diff --git a/.actions/assistant.py b/.actions/assistant.py new file mode 100644 index 0000000..bd134e2 --- /dev/null +++ b/.actions/assistant.py @@ -0,0 +1,474 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import glob +import logging +import os +import pathlib +import re +import shutil +import tarfile +import tempfile +import urllib.request +from distutils.version import LooseVersion +from itertools import chain +from os.path import dirname, isfile +from pathlib import Path +from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence, Tuple, Union + +from pkg_resources import parse_requirements, Requirement, yield_lines + +REQUIREMENT_FILES = { + "pytorch": ( + "requirements/pytorch/base.txt", + "requirements/pytorch/extra.txt", + "requirements/pytorch/strategies.txt", + "requirements/pytorch/examples.txt", + ), + "app": ( + "requirements/app/base.txt", + "requirements/app/ui.txt", + "requirements/app/cloud.txt", + ), + "fabric": ( + "requirements/fabric/base.txt", + "requirements/fabric/strategies.txt", + ), + "data": ( + "requirements/data/data.txt", + "requirements/data/cloud.txt", + "requirements/data/examples.txt", + ), +} +REQUIREMENT_FILES_ALL = list(chain(*REQUIREMENT_FILES.values())) + +_PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__)) + + +class _RequirementWithComment(Requirement): + strict_string = "# strict" + + def __init__(self, *args: Any, comment: str = "", pip_argument: Optional[str] = None, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.comment = comment + assert pip_argument is None or pip_argument # sanity check that it's not an empty str + self.pip_argument = pip_argument + self.strict = self.strict_string in comment.lower() + + def adjust(self, unfreeze: str) -> str: + """Remove version restrictions unless they are strict. + + >>> _RequirementWithComment("arrow<=1.2.2,>=1.2.0", comment="# anything").adjust("none") + 'arrow<=1.2.2,>=1.2.0' + >>> _RequirementWithComment("arrow<=1.2.2,>=1.2.0", comment="# strict").adjust("none") + 'arrow<=1.2.2,>=1.2.0 # strict' + >>> _RequirementWithComment("arrow<=1.2.2,>=1.2.0", comment="# my name").adjust("all") + 'arrow>=1.2.0' + >>> _RequirementWithComment("arrow>=1.2.0, <=1.2.2", comment="# strict").adjust("all") + 'arrow<=1.2.2,>=1.2.0 # strict' + >>> _RequirementWithComment("arrow").adjust("all") + 'arrow' + >>> _RequirementWithComment("arrow>=1.2.0, <=1.2.2", comment="# cool").adjust("major") + 'arrow<2.0,>=1.2.0' + >>> _RequirementWithComment("arrow>=1.2.0, <=1.2.2", comment="# strict").adjust("major") + 'arrow<=1.2.2,>=1.2.0 # strict' + >>> _RequirementWithComment("arrow>=1.2.0").adjust("major") + 'arrow>=1.2.0' + >>> _RequirementWithComment("arrow").adjust("major") + 'arrow' + """ + out = str(self) + if self.strict: + return f"{out} {self.strict_string}" + if unfreeze == "major": + for operator, version in self.specs: + if operator in ("<", "<="): + major = LooseVersion(version).version[0] + # replace upper bound with major version increased by one + return out.replace(f"{operator}{version}", f"<{major + 1}.0") + elif unfreeze == "all": + for operator, version in self.specs: + if operator in ("<", "<="): + # drop upper bound + return out.replace(f"{operator}{version},", "") + elif unfreeze != "none": + raise ValueError(f"Unexpected unfreeze: {unfreeze!r} value.") + return out + + +def _parse_requirements(strs: Union[str, Iterable[str]]) -> Iterator[_RequirementWithComment]: + """Adapted from `pkg_resources.parse_requirements` to include comments. + + >>> txt = ['# ignored', '', 'this # is an', '--piparg', 'example', 'foo # strict', 'thing', '-r different/file.txt'] + >>> [r.adjust('none') for r in _parse_requirements(txt)] + ['this', 'example', 'foo # strict', 'thing'] + >>> txt = '\\n'.join(txt) + >>> [r.adjust('none') for r in _parse_requirements(txt)] + ['this', 'example', 'foo # strict', 'thing'] + """ + lines = yield_lines(strs) + pip_argument = None + for line in lines: + # Drop comments -- a hash without a space may be in a URL. + if " #" in line: + comment_pos = line.find(" #") + line, comment = line[:comment_pos], line[comment_pos:] + else: + comment = "" + # If there is a line continuation, drop it, and append the next line. + if line.endswith("\\"): + line = line[:-2].strip() + try: + line += next(lines) + except StopIteration: + return + # If there's a pip argument, save it + if line.startswith("--"): + pip_argument = line + continue + if line.startswith("-r "): + # linked requirement files are unsupported + continue + yield _RequirementWithComment(line, comment=comment, pip_argument=pip_argument) + pip_argument = None + + +def load_requirements(path_dir: str, file_name: str = "base.txt", unfreeze: str = "all") -> List[str]: + """Loading requirements from a file. + + >>> path_req = os.path.join(_PROJECT_ROOT, "requirements") + >>> load_requirements(path_req, "docs.txt", unfreeze="major") # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + ['sphinx<...] + """ + assert unfreeze in {"none", "major", "all"} + path = Path(path_dir) / file_name + if not path.exists(): + logging.warning(f"Folder {path_dir} does not have any base requirements.") + return [] + assert path.exists(), (path_dir, file_name, path) + text = path.read_text() + return [req.adjust(unfreeze) for req in _parse_requirements(text)] + + +def load_readme_description(path_dir: str, homepage: str, version: str) -> str: + """Load readme as decribtion. + + >>> load_readme_description(_PROJECT_ROOT, "", "") # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + '...PyTorch Lightning is just organized PyTorch...' + """ + path_readme = os.path.join(path_dir, "README.md") + with open(path_readme, encoding="utf-8") as fo: + text = fo.read() + + # drop images from readme + text = text.replace( + "![PT to PL](docs/source-pytorch/_static/images/general/pl_quick_start_full_compressed.gif)", "" + ) + + # https://github.com/Lightning-AI/lightning/raw/master/docs/source/_static/images/lightning_module/pt_to_pl.png + github_source_url = os.path.join(homepage, "raw", version) + # replace relative repository path to absolute link to the release + # do not replace all "docs" as in the readme we reger some other sources with particular path to docs + text = text.replace( + "docs/source-pytorch/_static/", f"{os.path.join(github_source_url, 'docs/source-app/_static/')}" + ) + + # readthedocs badge + text = text.replace("badge/?version=stable", f"badge/?version={version}") + text = text.replace("pytorch-lightning.readthedocs.io/en/stable/", f"pytorch-lightning.readthedocs.io/en/{version}") + # codecov badge + text = text.replace("/branch/master/graph/badge.svg", f"/release/{version}/graph/badge.svg") + # github actions badge + text = text.replace("badge.svg?branch=master&event=push", f"badge.svg?tag={version}") + # azure pipelines badge + text = text.replace("?branchName=master", f"?branchName=refs%2Ftags%2F{version}") + + skip_begin = r"" + skip_end = r"" + # todo: wrap content as commented description + text = re.sub(rf"{skip_begin}.+?{skip_end}", "", text, flags=re.IGNORECASE + re.DOTALL) + + # # https://github.com/Borda/pytorch-lightning/releases/download/1.1.0a6/codecov_badge.png + # github_release_url = os.path.join(homepage, "releases", "download", version) + # # download badge and replace url with local file + # text = _parse_for_badge(text, github_release_url) + return text + + +def distribute_version(src_folder: str, ver_file: str = "version.info") -> None: + """Copy the global version to all packages.""" + ls_ver = glob.glob(os.path.join(src_folder, "*", "__version__.py")) + ver_template = os.path.join(src_folder, ver_file) + for fpath in ls_ver: + fpath = os.path.join(os.path.dirname(fpath), ver_file) + print("Distributing the version to", fpath) + if os.path.isfile(fpath): + os.remove(fpath) + shutil.copy2(ver_template, fpath) + + +def _download_frontend(pkg_path: str): + """Downloads an archive file for a specific release of the Lightning frontend and extracts it to the correct + directory.""" + + try: + frontend_dir = pathlib.Path(pkg_path, "ui") + download_dir = tempfile.mkdtemp() + + shutil.rmtree(frontend_dir, ignore_errors=True) + # TODO: remove this once lightning-ui package is ready as a dependency + frontend_release_url = "https://storage.googleapis.com/grid-packages/lightning-ui/v0.0.0/build.tar.gz" + response = urllib.request.urlopen(frontend_release_url) + + file = tarfile.open(fileobj=response, mode="r|gz") + file.extractall(path=download_dir) + + shutil.move(os.path.join(download_dir, "build"), frontend_dir) + print("The Lightning UI has successfully been downloaded!") + + # If installing from source without internet connection, we don't want to break the installation + except Exception: + print("The Lightning UI downloading has failed!") + + +def _load_aggregate_requirements(req_dir: str = "requirements", freeze_requirements: bool = False) -> None: + """Load all base requirements from all particular packages and prune duplicates. + + >>> _load_aggregate_requirements(os.path.join(_PROJECT_ROOT, "requirements")) + """ + requires = [ + load_requirements(d, unfreeze="none" if freeze_requirements else "major") + for d in glob.glob(os.path.join(req_dir, "*")) + # skip empty folder (git artifacts), and resolving Will's special issue + if os.path.isdir(d) and len(glob.glob(os.path.join(d, "*"))) > 0 and not os.path.basename(d).startswith("_") + ] + if not requires: + return + # TODO: add some smarter version aggregation per each package + requires = sorted(set(chain(*requires))) + with open(os.path.join(req_dir, "base.txt"), "w") as fp: + fp.writelines([ln + os.linesep for ln in requires] + [os.linesep]) + + +def _retrieve_files(directory: str, *ext: str) -> List[str]: + all_files = [] + for root, _, files in os.walk(directory): + for fname in files: + if not ext or any(os.path.split(fname)[1].lower().endswith(e) for e in ext): + all_files.append(os.path.join(root, fname)) + + return all_files + + +def _replace_imports(lines: List[str], mapping: List[Tuple[str, str]], lightning_by: str = "") -> List[str]: + """Replace imports of standalone package to lightning. + + >>> lns = [ + ... '"lightning_app"', + ... "lightning_app", + ... "lightning_app/", + ... "delete_cloud_lightning_apps", + ... "from lightning_app import", + ... "lightning_apps = []", + ... "lightning_app and pytorch_lightning are ours", + ... "def _lightning_app():", + ... ":class:`~lightning_app.core.flow.LightningFlow`", + ... "http://pytorch_lightning.ai", + ... "from lightning import __version__", + ... "@lightning.ai" + ... ] + >>> mapping = [("lightning_app", "lightning.app"), ("pytorch_lightning", "lightning.pytorch")] + >>> _replace_imports(lns, mapping, lightning_by="lightning_fabric") # doctest: +NORMALIZE_WHITESPACE + ['"lightning.app"', \ + 'lightning.app', \ + 'lightning_app/', \ + 'delete_cloud_lightning_apps', \ + 'from lightning.app import', \ + 'lightning_apps = []', \ + 'lightning.app and lightning.pytorch are ours', \ + 'def _lightning_app():', \ + ':class:`~lightning.app.core.flow.LightningFlow`', \ + 'http://pytorch_lightning.ai', \ + 'from lightning_fabric import __version__', \ + '@lightning.ai'] + """ + out = lines[:] + for source_import, target_import in mapping: + for i, ln in enumerate(out): + out[i] = re.sub( + rf"([^_/@]|^){source_import}([^_\w/]|$)", + rf"\1{target_import}\2", + ln, + ) + if lightning_by: # in addition, replace base package + out[i] = out[i].replace("from lightning import ", f"from {lightning_by} import ") + out[i] = out[i].replace("import lightning ", f"import {lightning_by} ") + return out + + +def copy_replace_imports( + source_dir: str, + source_imports: Sequence[str], + target_imports: Sequence[str], + target_dir: Optional[str] = None, + lightning_by: str = "", +) -> None: + """Copy package content with import adjustments.""" + print(f"Replacing imports: {locals()}") + assert len(source_imports) == len(target_imports), ( + "source and target imports must have the same length, " + f"source: {len(source_imports)}, target: {len(target_imports)}" + ) + if target_dir is None: + target_dir = source_dir + + ls = _retrieve_files(source_dir) + for fp in ls: + fp_new = fp.replace(source_dir, target_dir) + _, ext = os.path.splitext(fp) + if ext in (".png", ".jpg", ".ico"): + os.makedirs(dirname(fp_new), exist_ok=True) + if not isfile(fp_new): + shutil.copy(fp, fp_new) + continue + if ext in (".pyc",): + continue + # Try to parse everything else + with open(fp, encoding="utf-8") as fo: + try: + lines = fo.readlines() + except UnicodeDecodeError: + # a binary file, skip + print(f"Skipped replacing imports for {fp}") + continue + lines = _replace_imports(lines, list(zip(source_imports, target_imports)), lightning_by=lightning_by) + os.makedirs(os.path.dirname(fp_new), exist_ok=True) + with open(fp_new, "w", encoding="utf-8") as fo: + fo.writelines(lines) + + +def create_mirror_package(source_dir: str, package_mapping: Dict[str, str]) -> None: + # replace imports and copy the code + mapping = package_mapping.copy() + mapping.pop("lightning", None) # pop this key to avoid replacing `lightning` to `lightning.lightning` + + mapping = {f"lightning.{sp}": sl for sp, sl in mapping.items()} + for pkg_from, pkg_to in mapping.items(): + copy_replace_imports( + source_dir=os.path.join(source_dir, pkg_from.replace(".", os.sep)), + # pytorch_lightning uses lightning_fabric, so we need to replace all imports for all directories + source_imports=mapping.keys(), + target_imports=mapping.values(), + target_dir=os.path.join(source_dir, pkg_to.replace(".", os.sep)), + lightning_by=pkg_from, + ) + + +class AssistantCLI: + @staticmethod + def requirements_prune_pkgs(packages: Sequence[str], req_files: Sequence[str] = REQUIREMENT_FILES_ALL) -> None: + """Remove some packages from given requirement files.""" + if isinstance(req_files, str): + req_files = [req_files] + for req in req_files: + AssistantCLI._prune_packages(req, packages) + + @staticmethod + def _prune_packages(req_file: str, packages: Sequence[str]) -> None: + """Remove some packages from given requirement files.""" + path = Path(req_file) + assert path.exists() + text = path.read_text() + lines = text.splitlines() + final = [] + for line in lines: + ln_ = line.strip() + if not ln_ or ln_.startswith("#"): + final.append(line) + continue + req = list(parse_requirements(ln_))[0] + if req.name not in packages: + final.append(line) + print(final) + path.write_text("\n".join(final) + "\n") + + @staticmethod + def _replace_min(fname: str) -> None: + with open(fname, encoding="utf-8") as fo: + req = fo.read().replace(">=", "==") + with open(fname, "w", encoding="utf-8") as fw: + fw.write(req) + + @staticmethod + def replace_oldest_ver(requirement_fnames: Sequence[str] = REQUIREMENT_FILES_ALL) -> None: + """Replace the min package version by fixed one.""" + for fname in requirement_fnames: + print(fname) + AssistantCLI._replace_min(fname) + + @staticmethod + def copy_replace_imports( + source_dir: str, + source_import: str, + target_import: str, + target_dir: Optional[str] = None, + lightning_by: str = "", + ) -> None: + """Copy package content with import adjustments.""" + source_imports = source_import.strip().split(",") + target_imports = target_import.strip().split(",") + copy_replace_imports( + source_dir, source_imports, target_imports, target_dir=target_dir, lightning_by=lightning_by + ) + + @staticmethod + def pull_docs_files( + gh_user_repo: str, + target_dir: str = "docs/source-pytorch/XXX", + checkout: str = "tags/1.0.0", + source_dir: str = "docs/source", + ) -> None: + """Pull docs pages from external source and append to local docs.""" + import zipfile + + zip_url = f"https://github.com/{gh_user_repo}/archive/refs/{checkout}.zip" + + with tempfile.TemporaryDirectory() as tmp: + zip_file = os.path.join(tmp, "repo.zip") + urllib.request.urlretrieve(zip_url, zip_file) + + with zipfile.ZipFile(zip_file, "r") as zip_ref: + zip_ref.extractall(tmp) + + zip_dirs = [d for d in glob.glob(os.path.join(tmp, "*")) if os.path.isdir(d)] + # check that the extracted archive has only repo folder + assert len(zip_dirs) == 1 + repo_dir = zip_dirs[0] + + ls_pages = glob.glob(os.path.join(repo_dir, source_dir, "*.rst")) + ls_pages += glob.glob(os.path.join(repo_dir, source_dir, "**", "*.rst")) + for rst in ls_pages: + rel_rst = rst.replace(os.path.join(repo_dir, source_dir) + os.path.sep, "") + rel_dir = os.path.dirname(rel_rst) + os.makedirs(os.path.join(_PROJECT_ROOT, target_dir, rel_dir), exist_ok=True) + new_rst = os.path.join(_PROJECT_ROOT, target_dir, rel_rst) + if os.path.isfile(new_rst): + logging.warning(f"Page {new_rst} already exists in the local tree so it will be skipped.") + continue + shutil.copy(rst, new_rst) + + +if __name__ == "__main__": + import jsonargparse + + jsonargparse.CLI(AssistantCLI, as_positional=False) diff --git a/.actions/pull_legacy_checkpoints.sh b/.actions/pull_legacy_checkpoints.sh new file mode 100644 index 0000000..b61647a --- /dev/null +++ b/.actions/pull_legacy_checkpoints.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Run this script from the project root. +URL="https://pl-public-data.s3.amazonaws.com/legacy/checkpoints.zip" +mkdir -p tests/legacy +# wget is simpler but does not work on Windows +python -c "from urllib.request import urlretrieve; urlretrieve('$URL', 'tests/legacy/checkpoints.zip')" +ls -l tests/legacy/ + +unzip -o tests/legacy/checkpoints.zip -d tests/legacy/ +ls -l tests/legacy/checkpoints/ diff --git a/.actions/requirements.txt b/.actions/requirements.txt new file mode 100644 index 0000000..fdd6005 --- /dev/null +++ b/.actions/requirements.txt @@ -0,0 +1,2 @@ +jsonargparse>=4.16.0 +requests diff --git a/.github/workflows/deploy-gh-pages.yml b/.github/workflows/deploy-gh-pages.yml index 46998e5..879db68 100644 --- a/.github/workflows/deploy-gh-pages.yml +++ b/.github/workflows/deploy-gh-pages.yml @@ -1,47 +1,93 @@ -name: gh-pages deploy +name: Build Lightning Docs & Deploy to gh-pages on: push: branches: - master +defaults: + run: + shell: bash + +env: + FREEZE_REQUIREMENTS: "1" + TORCH_URL: "https://download.pytorch.org/whl/cpu/torch_stable.html" + PYPI_CACHE_DIR: "_pip-wheels" + jobs: - build: + make-html: runs-on: ubuntu-latest + container: + image: pytorchlightning/pytorch_lightning:docs + strategy: + fail-fast: false + matrix: + pkg-name: ["app", "fabric", "pytorch"] steps: - uses: actions/checkout@v3 + with: + submodules: true - - name: Set up Python 3.8 - uses: actions/setup-python@v3 + - name: pip wheels cache + uses: actions/cache/restore@v3 with: - python-version: '3.8' + path: ${{ env.PYPI_CACHE_DIR }} + key: pypi_wheels - - name: Upgrade pip - run: python3 -m pip install --upgrade pip + - name: Install package & dependencies + run: | + mkdir -p $PYPI_CACHE_DIR # in case cache was not hit + ls -lh $PYPI_CACHE_DIR + mkdir -p pypi_pkgs # in case template is not pulled + pip --version + pip install -U -r requirements.txt \ + -f pypi_pkgs/ -f $PYPI_CACHE_DIR -f ${TORCH_URL} + pip install -U -r requirements/${{ matrix.pkg-name }}/docs.txt \ + -f pypi_pkgs/ -f $PYPI_CACHE_DIR -f ${TORCH_URL} + pip list + shell: bash - - name: Get pip cache dir - id: pip-cache - run: echo "::set-output name=dir::$(pip cache dir)" + - name: Make Documentation + working-directory: ./docs/source-${{ matrix.pkg-name }} + run: make html --debug --jobs $(nproc) SPHINXOPTS="-W --keep-going" - - name: Cache dependencies - uses: actions/cache@v2 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- + - name: Keep artifact + id: keep-artifact + run: python -c "print('DAYS=' + str(7 if '${{ github.event_name }}'.startswith('pull_request') else 0))" >> $GITHUB_OUTPUT - - name: Install dependencies - run: pip install -r requirements.txt + - name: Upload built docs + uses: actions/upload-artifact@v3 + with: + name: docs-${{ matrix.pkg-name }}-${{ github.sha }} + path: docs/build/html/ + retention-days: ${{ steps.keep-artifact.outputs.DAYS }} - - name: Build Sphinx documents - run: make docs + - name: Dump handy wheels + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + continue-on-error: true + uses: ./.github/actions/pip-wheels + with: + wheel-dir: ${{ env.PYPI_CACHE_DIR }} + torch-url: ${{ env.TORCH_URL }} + cache-key: "pypi_wheels" + deploy-docs: + needs: [make-html] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + pkg-name: ["app", "fabric", "pytorch"] + steps: + - uses: actions/download-artifact@v3 + with: + name: docs-${{ matrix.pkg-name }}-${{ github.sha }} + path: docs/build/html/ - name: Deploy to gh-pages branch uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.DEPLOY_TOKEN }} - publish_dir: ./build/html + publish_dir: ./docs/build/html cname: docs.pytorchlightning.kr enable_jekyll: false force_orphan: true diff --git a/.gitignore b/.gitignore index 923c2a1..c9307d0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,23 +2,30 @@ .DS_Store run_configs/ model_weights/ -app/models/ pip-wheel-metadata/ lightning_logs/ .vscode/ -# Test-tube -test_tube_*/ - # Documentations -docs/source/api -docs/source/*.md -docs/source/generated -docs/source/*/generated -docs/source/notebooks -docs/source/_static/images/course_UvA-DL -docs/source/_static/images/lightning_examples +docs/venv*/ +docs/build*/ +docs/source-app/generated +docs/source-app/*/generated +docs/source-app/_static/fetched-s3-assets +docs/source-fabric/_static/fetched-s3-assets +docs/source-pytorch/api +docs/source-pytorch/*.md +docs/source-pytorch/generated +docs/source-pytorch/*/generated +docs/source-pytorch/notebooks +docs/source-pytorch/_static/images/course_UvA-DL +docs/source-pytorch/_static/images/lightning_examples +docs/source-pytorch/_static/fetched-s3-assets + +docs/source-fabric/*/generated +# C extensions +*.so # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -27,11 +34,6 @@ timit_data/ grid_generated* grid_ori* - - -# C extensions -*.so - # PyCharm .idea/ @@ -53,6 +55,18 @@ wheels/ *.egg-info/ .installed.cfg *.egg +src/*/version.info +src/lightning_app/* +src/lightning_fabric/* +src/pytorch_lightning/* +!src/*/__about__.py +!src/*/__main__.py +!src/*/__setup__.py +!src/*/__version__.py +!src/*/MANIFEST.in +!src/*/py.typed +!src/*/README.md +!src/*/shell-folder_code-lives-lightning.info # PyInstaller # Usually these files are written by a python script from a template @@ -113,6 +127,8 @@ celerybeat-schedule # dotenv .env +.env.staging +.env.local # virtualenv .venv @@ -134,13 +150,15 @@ ENV/ .mypy_cache/ # pytest .pytest_cache/ +# ruff +.ruff_cache/ # data .data/ Datasets/ mnist/ MNIST/ -legacy/checkpoints/ +tests/legacy/checkpoints/ *.gz *ubyte @@ -149,7 +167,6 @@ ml-runs/ mlruns/ *.zip *.ckpt -pytorch\ lightning test-reports/ wandb .forked/ @@ -163,3 +180,32 @@ cifar-10-batches-py # ctags tags .tags +src/lightning_app/ui/* +src/lightning/app/ui/* +*examples/template_react_ui* +hars* +artifacts/* +*docs/examples* + +# tutorials +our_model.tar +test.png +saved_models +data/ +!src/lightning/data/ +!examples/data/ +!tests/tests_pytorch/utilities/data/ +!requirements/data/ +.shared +.lightning +node_modules/ + +# examples +**/events.out.tfevents.* +examples/**/*.png + +# CI +.wheels/ + +# sourced notebooks from tutorials +_notebooks/.notebooks/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..828dd19 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "_notebooks"] + path = _notebooks + url = https://github.com/PyTorchKorea/lightning-tutorials-kr.git + branch = publication diff --git a/.lightningignore b/.lightningignore new file mode 100644 index 0000000..4ce8d52 --- /dev/null +++ b/.lightningignore @@ -0,0 +1,16 @@ +_notebooks +.azure +.github +.ipynb_checkpoints +.pytest_cache +.shared +.storage +.venv +.vscode +.git +artifacts +Datasets +dist +docs +examples +tests diff --git a/Makefile b/Makefile index 2159c7f..c303c68 100644 --- a/Makefile +++ b/Makefile @@ -1,25 +1,38 @@ -# Minimal makefile for Sphinx documentation -# +.PHONY: test clean docs -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SOURCEDIR = source -BUILDDIR = build +# to imitate SLURM set only single node +export SLURM_LOCALID=0 +# assume you have installed need packages +export SPHINX_MOCK_REQUIREMENTS=1 +# install only Lightning Trainer packages +export PACKAGE_NAME=pytorch -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -v +clean: + # clean all temp runs + rm -rf $(shell find . -name "mlruns") + rm -rf $(shell find . -name "lightning_log") + rm -rf $(shell find . -name "lightning_logs") + rm -rf _ckpt_* + rm -rf .mypy_cache + rm -rf .pytest_cache + rm -rf ./docs/build + rm -rf ./docs/source-fabric/api/generated + rm -rf ./docs/source-pytorch/notebooks + rm -rf ./docs/source-pytorch/generated + rm -rf ./docs/source-pytorch/*/generated + rm -rf ./docs/source-pytorch/api + rm -rf ./docs/source-app/generated + rm -rf ./docs/source-app/*/generated + rm -rf build + rm -rf dist + rm -rf *.egg-info docs: - make html - echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + git submodule update --init --recursive # get Notebook submodule + pip install -qq lightning # install (stable) Lightning from PyPI instead of src + pip install -qq -r requirements/app/base.txt + pip install -qq -r requirements/pytorch/docs.txt + cd docs/source-pytorch && $(MAKE) html --jobs $(nproc) && cd ../../ -.PHONY: all docs clean +update: + git submodule update --init --recursive --remote diff --git a/README.md b/README.md index 4979a18..70db3e5 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,633 @@ -# PyTorch-Lightning Docs +
-We are using Sphinx with Napoleon extension. -Moreover, we set Google style to follow with type convention. +Lightning -- [Napoleon formatting with Google style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) -- [ReStructured Text (reST)](https://docs.pylonsproject.org/projects/docs-style-guide/) -- [Paragraph-level markup](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#paragraphs) +
+
-See following short example of a sample function taking one position string and optional +**The Deep Learning framework to train, deploy, and ship AI products Lightning fast.** -```python -from typing import Optional +**NEW- Lightning 2.0 is featuring a clean and stable API!!** + +______________________________________________________________________ + +

+ Lightning.ai • + PyTorch Lightning • + Fabric • + Lightning Apps • + Docs • + Community • + Contribute • +

+ + + +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pytorch-lightning)](https://pypi.org/project/pytorch-lightning/) +[![PyPI Status](https://badge.fury.io/py/pytorch-lightning.svg)](https://badge.fury.io/py/pytorch-lightning) +[![PyPI Status](https://pepy.tech/badge/pytorch-lightning)](https://pepy.tech/project/pytorch-lightning) +[![Conda](https://img.shields.io/conda/v/conda-forge/lightning?label=conda&color=success)](https://anaconda.org/conda-forge/lightning) +[![DockerHub](https://img.shields.io/docker/pulls/pytorchlightning/pytorch_lightning.svg)](https://hub.docker.com/r/pytorchlightning/pytorch_lightning) +[![codecov](https://codecov.io/gh/Lightning-AI/lightning/branch/master/graph/badge.svg?token=SmzX8mnKlA)](https://codecov.io/gh/Lightning-AI/lightning) + +[![Discord](https://img.shields.io/discord/1077906959069626439?style=plastic)](https://discord.gg/VptPCZkGNa) +![GitHub commit activity](https://img.shields.io/github/commit-activity/w/lightning-ai/lightning) +[![license](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/Lightning-AI/lightning/blob/master/LICENSE) + + + +
+ +## Install Lightning + +Simple installation from PyPI + +```bash +pip install lightning +``` + -def my_func(param_a: int, param_b: Optional[float] = None) -> str: - """Sample function. +
+ Other installation options + - Args: - param_a: first parameter - param_b: second parameter +#### Install with optional dependencies - Return: - sum of both numbers +```bash +pip install lightning['extra'] +``` - Example:: +#### Conda - >>> my_func(1, 2) - 3 +```bash +conda install lightning -c conda-forge +``` - Note: - If you want to add something. - """ - p = param_b if param_b else 0 - return str(param_a + p) +#### Install stable version + +Install future release from the source + +```bash +pip install https://github.com/Lightning-AI/lightning/archive/refs/heads/release/stable.zip -U ``` -## Building Docs +#### Install bleeding-edge -When updating the docs, make sure to build them first locally and visually inspect the html files in your browser for -formatting errors. In certain cases, a missing blank line or a wrong indent can lead to a broken layout. -Run these commands +Install nightly from the source (no guarantees) ```bash -git submodule update --init --recursive -pip install -r requirements/docs.txt -make clean -cd docs -make html +pip install https://github.com/Lightning-AI/lightning/archive/refs/heads/master.zip -U ``` -and open `docs/build/html/index.html` in your browser. +or from testing PyPI + +```bash +pip install -iU https://test.pypi.org/simple/ pytorch-lightning +``` + +
+ + +______________________________________________________________________ + +## Lightning has 3 core packages -When you send a PR the continuous integration will run tests and build the docs. You can access a preview of the html pages in the -_Artifacts_ tab in CircleCI when you click on the task named _build-Docs_ of _ci-tests_ at the bottom of the PR page. +[PyTorch Lightning: Train and deploy PyTorch at scale](#pytorch-lightning-train-and-deploy-pytorch-at-scale). +
+[Lightning Fabric: Expert control](#lightning-fabric-expert-control). +
+[Lightning Apps: Build AI products and ML workflows](#lightning-apps-build-ai-products-and-ml-workflows). -Notes (Optional): +Lightning gives you granular control over how much abstraction you want to add over PyTorch. -- You need to have LaTeX installed for rendering math equations. You can for example install TeXLive by doing one of the following: - - on Ubuntu (Linux) run `apt-get install texlive` or otherwise follow the instructions on the TeXLive website - - use the [RTD docker image](https://hub.docker.com/r/readthedocs/build) +
+ +
-## Developing docs +______________________________________________________________________ -When developing the docs, building docs can be VERY slow locally because of the notebook tutorials. -To speed this up, enable this flag in before building docs: +# PyTorch Lightning: Train and Deploy PyTorch at Scale + +PyTorch Lightning is just organized PyTorch - Lightning disentangles PyTorch code to decouple the science from the engineering. + +![PT to PL](docs/source-pytorch/_static/images/general/pl_quick_start_full_compressed.gif) + +______________________________________________________________________ + +### Hello simple model + +```python +# main.py +# ! pip install torchvision +import torch, torch.nn as nn, torch.utils.data as data, torchvision as tv, torch.nn.functional as F +import lightning as L + +# -------------------------------- +# Step 1: Define a LightningModule +# -------------------------------- +# A LightningModule (nn.Module subclass) defines a full *system* +# (ie: an LLM, diffusion model, autoencoder, or simple image classifier). + + +class LitAutoEncoder(L.LightningModule): + def __init__(self): + super().__init__() + self.encoder = nn.Sequential(nn.Linear(28 * 28, 128), nn.ReLU(), nn.Linear(128, 3)) + self.decoder = nn.Sequential(nn.Linear(3, 128), nn.ReLU(), nn.Linear(128, 28 * 28)) + + def forward(self, x): + # in lightning, forward defines the prediction/inference actions + embedding = self.encoder(x) + return embedding + + def training_step(self, batch, batch_idx): + # training_step defines the train loop. It is independent of forward + x, y = batch + x = x.view(x.size(0), -1) + z = self.encoder(x) + x_hat = self.decoder(z) + loss = F.mse_loss(x_hat, x) + self.log("train_loss", loss) + return loss + + def configure_optimizers(self): + optimizer = torch.optim.Adam(self.parameters(), lr=1e-3) + return optimizer + + +# ------------------- +# Step 2: Define data +# ------------------- +dataset = tv.datasets.MNIST(".", download=True, transform=tv.transforms.ToTensor()) +train, val = data.random_split(dataset, [55000, 5000]) + +# ------------------- +# Step 3: Train +# ------------------- +autoencoder = LitAutoEncoder() +trainer = L.Trainer() +trainer.fit(autoencoder, data.DataLoader(train), data.DataLoader(val)) +``` + +Run the model on your terminal ```bash -# builds notebooks which is slow -export PL_FAST_DOCS_DEV=0 +pip install torchvision +python main.py +``` + +______________________________________________________________________ + +## Advanced features + +Lightning has over [40+ advanced features](https://lightning.ai/docs/pytorch/stable/common/trainer.html#trainer-flags) designed for professional AI research at scale. + +Here are some examples: + +
+ +
+ +
+ Train on 1000s of GPUs without code changes + +```python +# 8 GPUs +# no code changes needed +trainer = Trainer(accelerator="gpu", devices=8) -# fast notebook build which is fast -export PL_FAST_DOCS_DEV=1 +# 256 GPUs +trainer = Trainer(accelerator="gpu", devices=8, num_nodes=32) ``` -## docs CSS/theme +
+ +
+ Train on other accelerators like TPUs without code changes + +```python +# no code changes needed +trainer = Trainer(accelerator="tpu", devices=8) +``` + +
+ +
+ 16-bit precision + +```python +# no code changes needed +trainer = Trainer(precision=16) +``` + +
+ +
+ Experiment managers + +```python +from lightning import loggers + +# tensorboard +trainer = Trainer(logger=TensorBoardLogger("logs/")) + +# weights and biases +trainer = Trainer(logger=loggers.WandbLogger()) + +# comet +trainer = Trainer(logger=loggers.CometLogger()) + +# mlflow +trainer = Trainer(logger=loggers.MLFlowLogger()) + +# neptune +trainer = Trainer(logger=loggers.NeptuneLogger()) + +# ... and dozens more +``` + +
+ +
+ +Early Stopping + +```python +es = EarlyStopping(monitor="val_loss") +trainer = Trainer(callbacks=[es]) +``` + +
+ +
+ Checkpointing + +```python +checkpointing = ModelCheckpoint(monitor="val_loss") +trainer = Trainer(callbacks=[checkpointing]) +``` + +
+ +
+ Export to torchscript (JIT) (production use) + +```python +# torchscript +autoencoder = LitAutoEncoder() +torch.jit.save(autoencoder.to_torchscript(), "model.pt") +``` + +
+ +
+ Export to ONNX (production use) + +```python +# onnx +with tempfile.NamedTemporaryFile(suffix=".onnx", delete=False) as tmpfile: + autoencoder = LitAutoEncoder() + input_sample = torch.randn((1, 64)) + autoencoder.to_onnx(tmpfile.name, input_sample, export_params=True) + os.path.isfile(tmpfile.name) +``` + +
+ +______________________________________________________________________ + +## Advantages over unstructured PyTorch + +- Models become hardware agnostic +- Code is clear to read because engineering code is abstracted away +- Easier to reproduce +- Make fewer mistakes because lightning handles the tricky engineering +- Keeps all the flexibility (LightningModules are still PyTorch modules), but removes a ton of boilerplate +- Lightning has dozens of integrations with popular machine learning tools. +- [Tested rigorously with every new PR](https://github.com/Lightning-AI/lightning/tree/master/tests). We test every combination of PyTorch and Python supported versions, every OS, multi GPUs and even TPUs. +- Minimal running speed overhead (about 300 ms per epoch compared with pure PyTorch). + +______________________________________________________________________ + +
+ Read the PyTorch Lightning docs +
+ +______________________________________________________________________ + +# Lightning Fabric: Expert control. + +Run on any device at any scale with expert-level control over PyTorch training loop and scaling strategy. You can even write your own Trainer. + +Fabric is designed for the most complex models like foundation model scaling, LLMs, diffusion, transformers, reinforcement learning, active learning. Of any size. + + + + + + + + + +
What to changeResulting Fabric Code (copy me!)
+ + +```diff ++ import lightning as L + import torch; import torchvision as tv + + dataset = tv.datasets.CIFAR10("data", download=True, + train=True, + transform=tv.transforms.ToTensor()) + ++ fabric = L.Fabric() ++ fabric.launch() + + model = tv.models.resnet18() + optimizer = torch.optim.SGD(model.parameters(), lr=0.001) +- device = "cuda" if torch.cuda.is_available() else "cpu" +- model.to(device) ++ model, optimizer = fabric.setup(model, optimizer) + + dataloader = torch.utils.data.DataLoader(dataset, batch_size=8) ++ dataloader = fabric.setup_dataloaders(dataloader) + + model.train() + num_epochs = 10 + for epoch in range(num_epochs): + for batch in dataloader: + inputs, labels = batch +- inputs, labels = inputs.to(device), labels.to(device) + optimizer.zero_grad() + outputs = model(inputs) + loss = torch.nn.functional.cross_entropy(outputs, labels) +- loss.backward() ++ fabric.backward(loss) + optimizer.step() + print(loss.data) +``` + + + + + +```Python +import lightning as L +import torch; import torchvision as tv + +dataset = tv.datasets.CIFAR10("data", download=True, + train=True, + transform=tv.transforms.ToTensor()) + +fabric = L.Fabric() +fabric.launch() + +model = tv.models.resnet18() +optimizer = torch.optim.SGD(model.parameters(), lr=0.001) +model, optimizer = fabric.setup(model, optimizer) + +dataloader = torch.utils.data.DataLoader(dataset, batch_size=8) +dataloader = fabric.setup_dataloaders(dataloader) + +model.train() +num_epochs = 10 +for epoch in range(num_epochs): + for batch in dataloader: + inputs, labels = batch + optimizer.zero_grad() + outputs = model(inputs) + loss = torch.nn.functional.cross_entropy(outputs, labels) + fabric.backward(loss) + optimizer.step() + print(loss.data) +``` + + +
+ +## Key features + +
+ Easily switch from running on CPU to GPU (Apple Silicon, CUDA, …), TPU, multi-GPU or even multi-node training + +```python +# Use your available hardware +# no code changes needed +fabric = Fabric() + +# Run on GPUs (CUDA or MPS) +fabric = Fabric(accelerator="gpu") + +# 8 GPUs +fabric = Fabric(accelerator="gpu", devices=8) + +# 256 GPUs, multi-node +fabric = Fabric(accelerator="gpu", devices=8, num_nodes=32) + +# Run on TPUs +fabric = Fabric(accelerator="tpu") +``` + +
+ +
+ Use state-of-the-art distributed training strategies (DDP, FSDP, DeepSpeed) and mixed precision out of the box + +```python +# Use state-of-the-art distributed training techniques +fabric = Fabric(strategy="ddp") +fabric = Fabric(strategy="deepspeed") +fabric = Fabric(strategy="fsdp") + +# Switch the precision +fabric = Fabric(precision="16-mixed") +fabric = Fabric(precision="64") +``` + +
+ +
+ All the device logic boilerplate is handled for you + +```diff + # no more of this! +- model.to(device) +- batch.to(device) +``` + +
+ +
+ Build your own custom Trainer using Fabric primitives for training checkpointing, logging, and more + +```python +import lightning as L + + +class MyCustomTrainer: + def __init__(self, accelerator="auto", strategy="auto", devices="auto", precision="32-true"): + self.fabric = L.Fabric(accelerator=accelerator, strategy=strategy, devices=devices, precision=precision) + + def fit(self, model, optimizer, dataloader, max_epochs): + self.fabric.launch() + + model, optimizer = self.fabric.setup(model, optimizer) + dataloader = self.fabric.setup_dataloaders(dataloader) + model.train() + + for epoch in range(max_epochs): + for batch in dataloader: + input, target = batch + optimizer.zero_grad() + output = model(input) + loss = loss_fn(output, target) + self.fabric.backward(loss) + optimizer.step() +``` + +You can find a more extensive example in our [examples](examples/fabric/build_your_own_trainer) + +
+ +______________________________________________________________________ + +
+ Read the Lightning Fabric docs +
+ +______________________________________________________________________ + +# Lightning Apps: Build AI products and ML workflows + +Lightning Apps remove the cloud infrastructure boilerplate so you can focus on solving the research or business problems. Lightning Apps can run on the Lightning Cloud, your own cluster or a private cloud. + +
+ +
+ +## Hello Lightning app world + +```python +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f"train a model on {x}") + + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f"analyze model on {x}") + + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute("cpu")) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute("gpu")) + + def run(self): + self.train.run("CPU machine 1") + self.analyze.run("GPU machine 2") + + +app = L.LightningApp(WorkflowOrchestrator()) +``` + +Run on the cloud or locally + +```bash +# run on the cloud +lightning run app app.py --setup --cloud + +# run locally +lightning run app app.py +``` + +______________________________________________________________________ + +
+ Read the Lightning Apps docs +
+ +______________________________________________________________________ + +## Examples + +###### Self-supervised Learning + +- [CPC transforms](https://lightning-bolts.readthedocs.io/en/stable/transforms/self_supervised.html#cpc-transforms) +- [Moco v2 tranforms](https://lightning-bolts.readthedocs.io/en/stable/transforms/self_supervised.html#moco-v2-transforms) +- [SimCLR transforms](https://lightning-bolts.readthedocs.io/en/stable/transforms/self_supervised.html#simclr-transforms) + +###### Convolutional Architectures + +- [GPT-2](https://lightning-bolts.readthedocs.io/en/stable/models/convolutional.html#gpt-2) +- [UNet](https://lightning-bolts.readthedocs.io/en/stable/models/convolutional.html#unet) + +###### Reinforcement Learning + +- [DQN Loss](https://lightning-bolts.readthedocs.io/en/stable/losses.html#dqn-loss) +- [Double DQN Loss](https://lightning-bolts.readthedocs.io/en/stable/losses.html#double-dqn-loss) +- [Per DQN Loss](https://lightning-bolts.readthedocs.io/en/stable/losses.html#per-dqn-loss) + +###### GANs + +- [Basic GAN](https://lightning-bolts.readthedocs.io/en/stable/models/gans.html#basic-gan) +- [DCGAN](https://lightning-bolts.readthedocs.io/en/stable/models/gans.html#dcgan) + +###### Classic ML + +- [Logistic Regression](https://lightning-bolts.readthedocs.io/en/stable/models/classic_ml.html#logistic-regression) +- [Linear Regression](https://lightning-bolts.readthedocs.io/en/stable/models/classic_ml.html#linear-regression) + +______________________________________________________________________ + +## Continuous Integration + +Lightning is rigorously tested across multiple CPUs, GPUs, TPUs, IPUs, and HPUs and against major Python and PyTorch versions. + +###### \*Codecov is > 90%+ but build delays may show less + +
+ Current build statuses + +
+ +| System / PyTorch ver. | 1.11 | 1.12 | 1.13 | 2.0 | +| :--------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | +| Linux py3.9 \[GPUs\] | - | [![Build Status]()](https://dev.azure.com/Lightning-AI/lightning/_build/latest?definitionId=24&branchName=master) | [![Build Status]()](https://dev.azure.com/Lightning-AI/lightning/_build/latest?definitionId=24&branchName=master) | Soon | +| Linux py3.9 \[TPUs\] | - | [![Test PyTorch - TPU](https://github.com/Lightning-AI/lightning/actions/workflows/tpu-tests.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/tpu-tests.yml) | | Soon | +| Linux py3.8 \[IPUs\] | - | - | [![Build Status]()](https://dev.azure.com/Lightning-AI/lightning/_build/latest?definitionId=25&branchName=master) | Soon | +| Linux (multiple Python versions) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | Soon | +| OSX (multiple Python versions) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | Soon | +| Windows (multiple Python versions) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | [![Test PyTorch](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml/badge.svg)](https://github.com/Lightning-AI/lightning/actions/workflows/ci-tests-pytorch.yml) | Soon | + +
+
+ +______________________________________________________________________ + +## Community + +The lightning community is maintained by + +- [10+ core contributors](https://lightning.ai/docs/pytorch/latest/community/governance.html) who are all a mix of professional engineers, Research Scientists, and Ph.D. students from top AI labs. +- 800+ community contributors. + +Want to help us build Lightning and reduce boilerplate for thousands of researchers? [Learn how to make your first contribution here](https://lightning.ai/docs/pytorch/stable/generated/CONTRIBUTING.html) + +Lightning is also part of the [PyTorch ecosystem](https://pytorch.org/ecosystem/) which requires projects to have solid testing, documentation and support. + +### Asking for help + +If you have any questions please: -To change the CSS theme of the docs, go [here](https://github.com/PyTorchLightning/lightning_sphinx_theme). -Apologies in advance... this is a bit complex to build and requires basic understanding of javascript/npm. +1. [Read the docs](https://lightning.ai/docs). +1. [Search through existing Discussions](https://github.com/Lightning-AI/lightning/discussions), or [add a new question](https://github.com/Lightning-AI/lightning/discussions/new) +1. [Join our discord](https://discord.com/invite/tfXFetEZxv). diff --git a/__about__.py b/__about__.py deleted file mode 100644 index 0a309f9..0000000 --- a/__about__.py +++ /dev/null @@ -1,38 +0,0 @@ -import time - -_this_year = time.strftime("%Y") -__version__ = "1.7.0dev" -__author__ = "William Falcon et al." -__author_email__ = "waf2107@columbia.edu" -__license__ = "Apache-2.0" -__copyright__ = f"Copyright (c) 2018-{_this_year}, {__author__}." -__homepage__ = "https://github.com/PyTorchLightning/pytorch-lightning" -__docs_url__ = "https://pytorch-lightning.readthedocs.io/en/stable/" -# this has to be simple string, see: https://github.com/pypa/twine/issues/522 -__docs__ = ( - "PyTorch Lightning is the lightweight PyTorch wrapper for ML researchers." - " Scale your models. Write less boilerplate." -) -__long_docs__ = """ -Lightning is a way to organize your PyTorch code to decouple the science code from the engineering. - It's more of a style-guide than a framework. - -In Lightning, you organize your code into 3 distinct categories: - -1. Research code (goes in the LightningModule). -2. Engineering code (you delete, and is handled by the Trainer). -3. Non-essential research code (logging, etc. this goes in Callbacks). - -Although your research/production project might start simple, once you add things like GPU AND TPU training, - 16-bit precision, etc, you end up spending more time engineering than researching. - Lightning automates AND rigorously tests those parts for you. - -Overall, Lightning guarantees rigorously tested, correct, modern best practices for the automated parts. - -Documentation -------------- -- https://pytorch-lightning.readthedocs.io/en/latest -- https://pytorch-lightning.readthedocs.io/en/stable -""" - -__all__ = ["__author__", "__author_email__", "__copyright__", "__docs__", "__homepage__", "__license__", "__version__"] diff --git a/_notebooks/.actions/README.md b/_notebooks/.actions/README.md new file mode 100644 index 0000000..7eb3226 --- /dev/null +++ b/_notebooks/.actions/README.md @@ -0,0 +1,11 @@ +scripts for generating notebooks + +**GHA here** + +- generate notebooks +- flow to ban any added notebook in PR (fail if changes in .notebooks) + +**PL side** + +- git submodule with these examples +- gha cron to update submodule head diff --git a/_notebooks/.actions/assistant.py b/_notebooks/.actions/assistant.py new file mode 100644 index 0000000..f489308 --- /dev/null +++ b/_notebooks/.actions/assistant.py @@ -0,0 +1,713 @@ +import base64 +import json +import os +import re +from datetime import datetime +from shutil import copyfile +from textwrap import wrap +from typing import Any, Dict, List, Optional, Sequence, Tuple +from warnings import warn + +import fire +import requests +import tqdm +import yaml +from pip._internal.operations import freeze +from wcmatch import glob + +_PATH_HERE = os.path.dirname(__file__) +_PATH_ROOT = os.path.dirname(_PATH_HERE) +PATH_REQ_DEFAULT = os.path.join(_PATH_ROOT, "_requirements", "default.txt") +PATH_SCRIPT_RENDER = os.path.join(_PATH_HERE, "_ipynb-render.sh") +PATH_SCRIPT_TEST = os.path.join(_PATH_HERE, "_ipynb-test.sh") +# https://askubuntu.com/questions/909918/how-to-show-unzip-progress +UNZIP_PROGRESS_BAR = ' | awk \'BEGIN {ORS=" "} {if(NR%10==0)print "."}\'' +REPO_NAME = "lightning-tutorials" +COLAB_REPO_LINK = "https://colab.research.google.com/github/PytorchLightning" +BRANCH_DEFAULT = "main" +BRANCH_PUBLISHED = "publication" +DIR_NOTEBOOKS = ".notebooks" +URL_PL_DOWNLOAD = f"https://github.com/Lightning-AI/{REPO_NAME}/raw/{BRANCH_DEFAULT}" +TEMPLATE_HEADER = f"""# %%%% [markdown] +# +# # %(title)s +# +# * **Author:** %(author)s +# * **License:** %(license)s +# * **Generated:** %(generated)s +# +# %(description)s +# +# --- +# Open in [![Open In Colab](https://colab.research.google.com/assets/colab-badge.png){{height="20px" width="117px"}}]({COLAB_REPO_LINK}/{REPO_NAME}/blob/{BRANCH_PUBLISHED}/{DIR_NOTEBOOKS}/%(local_ipynb)s) +# +# Give us a ⭐ [on Github](https://www.github.com/Lightning-AI/lightning/) +# | Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/stable/) +# | Join us [on Slack](https://www.pytorchlightning.ai/community) + +""" +TEMPLATE_SETUP = """# %%%% [markdown] +# ## Setup +# This notebook requires some packages besides pytorch-lightning. + +# %%%% colab={} colab_type="code" id="LfrJLKPFyhsK" +# ! pip install --quiet %(requirements)s + +""" +TEMPLATE_FOOTER = """ +# %% [markdown] +# ## Congratulations - Time to Join the Community! +# +# Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning +# movement, you can do so in the following ways! +# +# ### Star [Lightning](https://github.com/Lightning-AI/lightning) on GitHub +# The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool +# tools we're building. +# +# ### Join our [Slack](https://www.pytorchlightning.ai/community)! +# The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself +# and share your interests in `#general` channel +# +# +# ### Contributions ! +# The best way to contribute to our community is to become a code contributor! At any time you can go to +# [Lightning](https://github.com/Lightning-AI/lightning) or [Bolt](https://github.com/Lightning-AI/lightning-bolts) +# GitHub Issues page and filter for "good first issue". +# +# * [Lightning good first issue](https://github.com/Lightning-AI/lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) +# * [Bolt good first issue](https://github.com/Lightning-AI/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) +# * You can also contribute your own notebooks with useful examples ! +# +# ### Great thanks from the entire Pytorch Lightning Team for your interest ! +# +# [![Pytorch Lightning](https://raw.githubusercontent.com/Lightning-AI/lightning/master/docs/source/_static/images/logo.png){height="60px" width="240px"}](https://pytorchlightning.ai) + +""" +TEMPLATE_CARD_ITEM = """ +.. customcarditem:: + :header: %(title)s + :card_description: %(short_description)s + :tags: %(tags)s +""" + + +def load_requirements(path_req: str = PATH_REQ_DEFAULT) -> list: + """Load the requirements from a file.""" + with open(path_req) as fp: + req = fp.readlines() + req = [r[: r.index("#")] if "#" in r else r for r in req] + req = [r.strip() for r in req] + req = [r for r in req if r] + return req + + +def get_running_cuda_version() -> str: + """Extract the version of actual CUDA for this runtime.""" + try: + import torch + + return torch.version.cuda or "" + except ImportError: + return "" + + +def get_running_torch_version(): + """Extract the version of actual PyTorch for this runtime.""" + try: + import torch + + ver = torch.__version__ + return ver[: ver.index("+")] if "+" in ver else ver + except ImportError: + return "" + + +_TORCH_VERSION = get_running_torch_version() +_CUDA_VERSION = get_running_cuda_version() +_RUNTIME_VERSIONS = dict( + TORCH_VERSION_FULL=_TORCH_VERSION, + TORCH_VERSION=_TORCH_VERSION[: _TORCH_VERSION.index("+")] if "+" in _TORCH_VERSION else _TORCH_VERSION, + TORCH_MAJOR_DOT_MINOR=".".join(_TORCH_VERSION.split(".")[:2]), + CUDA_VERSION=_CUDA_VERSION, + CUDA_MAJOR_MINOR=_CUDA_VERSION.replace(".", ""), + DEVICE=f"cu{_CUDA_VERSION.replace('.', '')}" if _CUDA_VERSION else "cpu", +) + + +class AssistantCLI: + """Collection of handy CLI commands.""" + + _LOCAL_ACCELERATOR = "cpu,gpu" if get_running_cuda_version() else "cpu" + DEVICE_ACCELERATOR = os.environ.get("ACCELERATOR", _LOCAL_ACCELERATOR).lower() + DATASETS_FOLDER = os.environ.get("PATH_DATASETS", "_datasets") + DRY_RUN = bool(int(os.environ.get("DRY_RUN", 0))) + _META_REQUIRED_FIELDS = ("title", "author", "license", "description") + _SKIP_DIRS = ( + ".actions", + ".azure", + ".datasets", + ".github", + "_docs", + "_TEMP", + "_requirements", + DIR_NOTEBOOKS, + ) + _META_FILE_REGEX = ".meta.{yaml,yml}" + _META_PIP_KEY = "pip__" + _META_ACCEL_DEFAULT = _LOCAL_ACCELERATOR.split(",") + + # Map directory names to tag names. Note that dashes will be replaced with spaces in rendered tags in the docs. + _DIR_TO_TAG = { + "course_UvA-DL": "UvA-DL-Course", + "lightning_examples": "Lightning-Examples", + "flash_tutorials": "Kaggle", + } + _BASH_SCRIPT_BASE = ("#!/bin/bash", "set -e", "") + _EXT_ARCHIVE_ZIP = (".zip",) + _EXT_ARCHIVE_TAR = (".tar", ".gz") + _EXT_ARCHIVE = _EXT_ARCHIVE_ZIP + _EXT_ARCHIVE_TAR + _AZURE_POOL = "lit-rtx-3090" + _AZURE_DOCKER = "pytorchlightning/pytorch_lightning:base-cuda-py3.9-torch1.12-cuda11.6.1" + + @staticmethod + def _find_meta(folder: str) -> str: + """Search for a meta file in given folder and return its path. + + Args: + folder: path to the folder with python script, meta and artefacts + """ + files = glob.glob(os.path.join(folder, AssistantCLI._META_FILE_REGEX), flags=glob.BRACE) + if len(files) == 1: + return files[0] + return "" + + @staticmethod + def _load_meta(folder: str, strict: bool = False) -> Optional[dict]: + """Loading meta-data for a particular notebook with given folder path. + + Args: + folder: path to the folder with python script, meta and artefacts + strict: raise error if meta is missing required feilds + """ + fpath = AssistantCLI._find_meta(folder) + assert fpath, f"Missing meta file in folder: {folder}" + meta = yaml.safe_load(open(fpath)) + + if strict: + meta_miss = [fl for fl in AssistantCLI._META_REQUIRED_FIELDS if fl not in meta] + if meta_miss: + raise ValueError(f"Meta file '{fpath}' is missing the following fields: {meta_miss}") + return meta + + @staticmethod + def _valid_conf_folder(folder: str) -> Tuple[str, str]: + """Validate notebook folder if it has required meta file and optional thumb. + + Args: + folder: path to the folder with python script, meta and artefacts + """ + meta_files = [os.path.join(folder, f".meta.{ext}") for ext in ("yml", "yaml")] + meta_files = [pf for pf in meta_files if os.path.isfile(pf)] + if len(meta_files) != 1: + raise FileExistsError(f"found {len(meta_files)} meta (yaml|yml) files in folder: {folder}") + thumb_files = glob.glob(os.path.join(folder, ".thumb.*")) + thumb_names = list(map(os.path.basename, thumb_files)) + if len(thumb_files) > 1: + raise FileExistsError(f"Too many thumb files ({thumb_names}) found in folder: {folder}") + thumb = thumb_files[0] if thumb_files else "" + return meta_files[0], thumb + + @staticmethod + def _valid_folder(folder: str, ext: str) -> Tuple[str, str, str]: + """Validate notebook folder if it has required meta file, python script or ipython notebook (depending on + the stage) and optional thumb. + + Args: + folder: path to the folder with python script, meta and artefacts + ext: extension determining the stage - ".py" for python script nad ".ipynb" for notebook + """ + files = glob.glob(os.path.join(folder, f"*{ext}")) + if len(files) != 1: + names = list(map(os.path.basename, files)) + raise FileNotFoundError(f"Missing required '{ext}' file in folder: {folder} among {names}") + meta_file, thumb_file = AssistantCLI._valid_conf_folder(folder) + return files[0], meta_file, thumb_file + + @staticmethod + def _valid_accelerator(folder: str) -> bool: + """Parse standard requirements from meta file. + + Args: + folder: path to the folder with python script, meta and artefacts + """ + meta = AssistantCLI._load_meta(folder) + meta_accels = [acc.lower() for acc in meta.get("accelerator", AssistantCLI._META_ACCEL_DEFAULT)] + device_accels = AssistantCLI.DEVICE_ACCELERATOR.lower().split(",") + return any(ac in meta_accels for ac in device_accels) + + @staticmethod + def _parse_requirements(folder: str) -> Tuple[str, str]: + """Parse standard requirements from meta file. + + Args: + folder: path to the folder with python script, meta and artefacts + """ + meta = AssistantCLI._load_meta(folder) + reqs = meta.get("requirements", []) + + meta_pip_args = { + k.replace(AssistantCLI._META_PIP_KEY, ""): v + for k, v in meta.items() + if k.startswith(AssistantCLI._META_PIP_KEY) + } + pip_args = ["--extra-index-url https://download.pytorch.org/whl/" + _RUNTIME_VERSIONS.get("DEVICE")] + for pip_key in meta_pip_args: + if not isinstance(meta_pip_args[pip_key], (list, tuple, set)): + meta_pip_args[pip_key] = [meta_pip_args[pip_key]] + for arg in meta_pip_args[pip_key]: + arg = arg % _RUNTIME_VERSIONS + pip_args.append(f"--{pip_key} {arg}") + + return " ".join([f'"{req}"' for req in reqs]), " ".join(pip_args) + + @staticmethod + def _bash_download_data(folder: str) -> List[str]: + """Generate sequence of commands for optional downloading dataset specified in the meta file. + + Args: + folder: path to the folder with python script, meta and artefacts + """ + meta = AssistantCLI._load_meta(folder) + datasets = meta.get("datasets", {}) + data_kaggle = datasets.get("kaggle", []) + cmd = [f"python -m kaggle competitions download -c {name}" for name in data_kaggle] + files = [f"{name}.zip" for name in data_kaggle] + data_web = datasets.get("web", []) + cmd += [f"wget {web} --progress=bar:force:noscroll --tries=3" for web in data_web] + files += [os.path.basename(web) for web in data_web] + for fn in files: + name, ext = os.path.splitext(fn) + if ext not in AssistantCLI._EXT_ARCHIVE: + continue + if ext in AssistantCLI._EXT_ARCHIVE_ZIP: + cmd += [f"unzip -o {fn} -d {AssistantCLI.DATASETS_FOLDER}/{name} {UNZIP_PROGRESS_BAR}"] + else: + cmd += [f"tar -zxvf {fn} --overwrite"] + cmd += [f"rm {fn}"] + cmd += [f"tree -L 2 {AssistantCLI.DATASETS_FOLDER}"] + return cmd + + @staticmethod + def bash_render(folder: str, output_file: str = PATH_SCRIPT_RENDER) -> Optional[str]: + """Prepare bash script for running rendering of a particular notebook. + + Args: + folder: name/path to a folder with notebook files + output_file: if defined, stream the commands to the file + + Returns: + string with nash script content + """ + cmd = list(AssistantCLI._BASH_SCRIPT_BASE) + [f"# Rendering: {folder}"] + if not AssistantCLI.DRY_RUN: + cmd += AssistantCLI._bash_download_data(folder) + ipynb_file, meta_file, thumb_file = AssistantCLI._valid_folder(folder, ext=".ipynb") + pub_ipynb = os.path.join(DIR_NOTEBOOKS, f"{folder}.ipynb") + pub_meta = pub_ipynb.replace(".ipynb", ".yaml") + pub_dir = os.path.dirname(pub_ipynb) + thumb_ext = os.path.splitext(thumb_file)[-1] if thumb_file else "." + pub_thumb = os.path.join(DIR_NOTEBOOKS, f"{folder}{thumb_ext}") if thumb_file else "" + cmd.append(f"mkdir -p {pub_dir}") + if AssistantCLI.DRY_RUN: + # dry run does not execute the notebooks just takes them as they are + cmd.append(f"cp {ipynb_file} {pub_ipynb}") + # copy and add meta config + cmd += [f"cp {meta_file} {pub_meta}", f"cat {pub_meta}", f"git add {pub_meta}"] + else: + pip_req, pip_args = AssistantCLI._parse_requirements(folder) + cmd += [f"pip install {pip_req} --quiet {pip_args}", "pip list"] + cmd.append(f"# available: {AssistantCLI.DEVICE_ACCELERATOR}\n") + if AssistantCLI._valid_accelerator(folder): + cmd.append(f"python -m papermill {ipynb_file} {pub_ipynb} --kernel python") + else: + warn("Invalid notebook's accelerator for this device. So no outputs will be generated.", RuntimeWarning) + cmd.append(f"cp {ipynb_file} {pub_ipynb}") + # Export the actual packages used in runtime + cmd.append(f"meta_file=$(python .actions/assistant.py update-env-details {folder})") + # copy and add to version the enriched meta config + cmd += ["echo $meta_file", "cat $meta_file", "git add $meta_file"] + # if thumb image is linked to the notebook, copy and version it too + if thumb_file: + cmd += [f"cp {thumb_file} {pub_thumb}", f"git add {pub_thumb}"] + # add the generated notebook to version + cmd.append(f"git add {pub_ipynb}") + if not output_file: + return os.linesep.join(cmd) + with open(output_file, "w") as fp: + fp.write(os.linesep.join(cmd)) + + @staticmethod + def bash_test(folder: str, output_file: str = PATH_SCRIPT_TEST) -> Optional[str]: + """Prepare bash script for running tests of a particular notebook. + + Args: + folder: name/path to a folder with notebook files + output_file: if defined, stream the commands to the file + + Returns: + string with nash script content + """ + cmd = list(AssistantCLI._BASH_SCRIPT_BASE) + [f"# Testing: {folder}"] + cmd += AssistantCLI._bash_download_data(folder) + ipynb_file, meta_file, _ = AssistantCLI._valid_folder(folder, ext=".ipynb") + + # prepare isolated environment with inheriting the global packages + path_venv = os.path.join(folder, "venv") + cmd += [ + f"python -m virtualenv --system-site-packages {path_venv}", + f"source {os.path.join(path_venv, 'bin', 'activate')}", + "pip --version", + ] + + cmd.append(f"# available: {AssistantCLI.DEVICE_ACCELERATOR}") + if AssistantCLI._valid_accelerator(folder): + # and install specific packages + pip_req, pip_args = AssistantCLI._parse_requirements(folder) + cmd += [f"pip install {pip_req} --quiet {pip_args}", "pip list"] + # Export the actual packages used in runtime + cmd.append(f"meta_file=$(python .actions/assistant.py update-env-details {folder} --base_path .)") + # show created meta config + cmd += ["echo $meta_file", "cat $meta_file"] + cmd.append(f"python -m pytest {ipynb_file} -v --nbval --nbval-cell-timeout=300") + else: + pub_ipynb = os.path.join(DIR_NOTEBOOKS, f"{folder}.ipynb") + pub_meta = pub_ipynb.replace(".ipynb", ".yaml") + # copy and add meta config + cmd += [ + f"mkdir -p {os.path.dirname(pub_meta)}", + f"cp {meta_file} {pub_meta}", + f"cat {pub_meta}", + f"git add {pub_meta}", + ] + warn("Invalid notebook's accelerator for this device. So no tests will be run!!!", RuntimeWarning) + # deactivate and clean local environment + cmd += ["deactivate", f"rm -rf {os.path.join(folder, 'venv')}"] + if not output_file: + return os.linesep.join(cmd) + with open(output_file, "w") as fp: + fp.write(os.linesep.join(cmd)) + + @staticmethod + def convert_ipynb(folder: str) -> None: + """Add template header and footer to the python base script. + + Args: + folder: folder with python script + """ + fpath, _, _ = AssistantCLI._valid_folder(folder, ext=".py") + with open(fpath) as fp: + py_script = fp.readlines() + + meta = AssistantCLI._load_meta(folder, strict=True) + meta.update( + dict(local_ipynb=f"{folder}.ipynb"), + generated=datetime.now().isoformat(), + ) + meta["description"] = meta["description"].replace(os.linesep, f"{os.linesep}# ") + + header = TEMPLATE_HEADER % meta + requires = set(load_requirements() + meta["requirements"]) + setup = TEMPLATE_SETUP % dict(requirements=" ".join([f'"{req}"' for req in requires])) + py_script = [header + setup] + py_script + [TEMPLATE_FOOTER] + + py_script = AssistantCLI._replace_images(py_script, folder) + + with open(fpath, "w") as fp: + fp.writelines(py_script) + + os.system(f'python -m jupytext --set-formats "ipynb,py:percent" {fpath}') + + @staticmethod + def _replace_images(lines: list, local_dir: str) -> list: + """Update images by URL to GitHub raw source. + + Args: + lines: string lines from python script + local_dir: relative path to the folder with script + """ + md = os.linesep.join([ln.rstrip() for ln in lines]) + p_imgs = [] + # todo: add a rule to replace this paths only i md sections + # because * is a greedy quantifier, trying to match as much as it can. Make it *? + p_imgs += re.findall(r"src=\"(.*?)\"", md) + p_imgs += re.findall(r"!\[.*?\]\((.*?)\)", md) + + # update all images + for p_img in set(p_imgs): + if p_img.startswith("http://") or p_img.startswith("https://"): + url_path = p_img + im = requests.get(p_img, stream=True).raw.read() + else: + url_path = "/".join([URL_PL_DOWNLOAD, local_dir, p_img]) + p_local_img = os.path.join(local_dir, p_img) + with open(p_local_img, "rb") as fp: + im = fp.read() + im_base64 = base64.b64encode(im).decode("utf-8") + _, ext = os.path.splitext(p_img) + md = md.replace(f'src="{p_img}"', f'src="{url_path}"') + md = md.replace(f"]({p_img})", f"](data:image/{ext[1:]};base64,{im_base64})") + + return [ln + os.linesep for ln in md.split(os.linesep)] + + @staticmethod + def _is_ipynb_parent_dir(dir_path: str) -> bool: + """Determine in recursive fasion of a folder is valid notebook file or any of sub-folders is.""" + if AssistantCLI._find_meta(dir_path): + return True + sub_dirs = [d for d in glob.glob(os.path.join(dir_path, "*")) if os.path.isdir(d)] + return any(AssistantCLI._is_ipynb_parent_dir(d) for d in sub_dirs) + + @staticmethod + def group_folders( + fpath_gitdiff: str, + fpath_change_folders: str = "changed-folders.txt", + fpath_drop_folders: str = "dropped-folders.txt", + fpath_actual_dirs: Sequence[str] = tuple(), + strict: bool = True, + root_path: str = "", + ) -> None: + """Parsing the raw git diff and group changes by folders. + + Args: + fpath_gitdiff: raw git changes + + Generate the git change list: + > head=$(git rev-parse origin/main) + > git diff --name-only $head --output=master-diff.txt + + fpath_change_folders: output file with changed folders + fpath_drop_folders: output file with deleted folders + fpath_actual_dirs: files with listed all folder in particular stat + strict: raise error if some folder outside skipped does not have valid meta file + root_path: path to the root tobe added for all local folder paths in files + + Example: + $ python assistant.py group-folders ../target-diff.txt \ + --fpath_actual_dirs "['../dirs-main.txt', '../dirs-publication.txt']" + """ + with open(fpath_gitdiff) as fp: + changed = [ln.strip() for ln in fp.readlines()] + dirs = [os.path.dirname(ln) for ln in changed] + # not empty paths + dirs = [ln for ln in dirs if ln] + + if fpath_actual_dirs: + assert isinstance(fpath_actual_dirs, list) + assert all(os.path.isfile(p) for p in fpath_actual_dirs) + dir_sets = [{ln.strip() for ln in open(fp).readlines()} for fp in fpath_actual_dirs] + # get only different + dirs += list(set.union(*dir_sets) - set.intersection(*dir_sets)) + + if root_path: + dirs = [os.path.join(root_path, d) for d in dirs] + # unique folders + dirs = set(dirs) + # drop folder with skip folder + dirs = [pd for pd in dirs if not any(nd in AssistantCLI._SKIP_DIRS for nd in pd.split(os.path.sep))] + # valid folder has meta + dirs_exist = [d for d in dirs if os.path.isdir(d)] + dirs_invalid = [d for d in dirs_exist if not AssistantCLI._find_meta(d)] + if strict and dirs_invalid: + msg = f"Following folders do not have valid `{AssistantCLI._META_FILE_REGEX}`" + warn(f"{msg}: \n {os.linesep.join(dirs_invalid)}") + # check if there is other valid folder in its tree + dirs_invalid = [pd for pd in dirs_invalid if not AssistantCLI._is_ipynb_parent_dir(pd)] + if dirs_invalid: + raise FileNotFoundError(f"{msg} nor sub-folder: \n {os.linesep.join(dirs_invalid)}") + + dirs_change = [d for d in dirs_exist if AssistantCLI._find_meta(d)] + with open(fpath_change_folders, "w") as fp: + fp.write(os.linesep.join(sorted(dirs_change))) + + dirs_drop = [d for d in dirs if not os.path.isdir(d)] + with open(fpath_drop_folders, "w") as fp: + fp.write(os.linesep.join(sorted(dirs_drop))) + + @staticmethod + def generate_matrix(fpath_change_folders: str) -> str: + """Generate Azure matrix with leaf for each changed notebook. + + Args: + fpath_change_folders: output of previous ``group_folders`` + """ + with open(fpath_change_folders) as fp: + folders = [ln.strip() for ln in fp.readlines()] + # set default so the matrix has at least one runner + if not folders: + return "" + mtx = {} + for ln in folders: + mtx[ln] = { + "notebook": ln, + # TODO: allow defining some custom pools with different devices + "agent-pool": AssistantCLI._AZURE_POOL, + # TODO: allow defining some custom images with with python or PT + "docker-image": AssistantCLI._AZURE_DOCKER, + } + return json.dumps(mtx) + + @staticmethod + def _get_card_item_cell(path_ipynb: str, path_meta: str, path_thumb: Optional[str]) -> Dict[str, Any]: + """Build the card item cell for the given notebook path.""" + meta = yaml.safe_load(open(path_meta)) + + # Clamp description length + wrapped_description = wrap( + meta.get("short_description", meta["description"]).strip().replace(os.linesep, " "), 175 + ) + suffix = "..." if len(wrapped_description) > 1 else "" + meta["short_description"] = wrapped_description[0] + suffix + + # Resolve some default tags based on accelerators and directory name + meta["tags"] = meta.get("tags", []) + + accelerators = meta.get("accelerator", ("CPU",)) + if ("GPU" in accelerators) or ("TPU" in accelerators): + meta["tags"].append("GPU/TPU") + + dirname = os.path.basename(os.path.dirname(path_ipynb)) + if dirname != ".notebooks": + meta["tags"].append(AssistantCLI._DIR_TO_TAG.get(dirname, dirname)) + + meta["tags"] = [tag.replace(" ", "-") for tag in meta["tags"]] + meta["tags"] = ",".join(meta["tags"]) + + # Build the notebook cell + rst_cell = TEMPLATE_CARD_ITEM % meta + + # Split lines + rst_cell_lines = rst_cell.strip().splitlines(True) + + if path_thumb is not None: + rst_cell_lines[-1] += "\n" + rst_cell_lines.append(f" :image: {path_thumb}") + + return { + "cell_type": "raw", + "metadata": {"raw_mimetype": "text/restructuredtext"}, + "source": rst_cell_lines, + } + + @staticmethod + def _resolve_path_thumb(path_ipynb: str, path_meta: str) -> Optional[str]: + """Find the thumbnail (assumes thumbnail to be any file that isn't metadata or notebook).""" + paths = list(set(glob.glob(path_ipynb.replace(".ipynb", ".*"))) - {path_ipynb, path_meta}) + if len(paths) == 0: + return None + assert len(paths) == 1, f"Found multiple possible thumbnail paths for notebook: {path_ipynb}." + path_thumb = paths[0] + path_thumb = path_thumb.split(os.path.sep) + path_thumb = os.path.sep.join(path_thumb[path_thumb.index(DIR_NOTEBOOKS) + 1 :]) + return path_thumb + + @staticmethod + def copy_notebooks( + path_root: str, + docs_root: str = "_docs/source", + path_docs_ipynb: str = "notebooks", + path_docs_images: str = "_static/images", + patterns: Sequence[str] = (".", "**"), + ) -> None: + """Copy all notebooks from a folder to doc folder. + + Args: + path_root: source path to the project root in these tutorials + docs_root: docs source directory + path_docs_ipynb: destination path to the notebooks' location relative to ``docs_root`` + path_docs_images: destination path to the images' location relative to ``docs_root`` + patterns: patterns to use when glob-ing notebooks + """ + ls_ipynb = [] + for sub in patterns: + ls_ipynb += glob.glob(os.path.join(path_root, DIR_NOTEBOOKS, sub, "*.ipynb")) + + os.makedirs(os.path.join(docs_root, path_docs_ipynb), exist_ok=True) + ipynb_content = [] + for path_ipynb in tqdm.tqdm(ls_ipynb): + ipynb = path_ipynb.split(os.path.sep) + sub_ipynb = os.path.sep.join(ipynb[ipynb.index(DIR_NOTEBOOKS) + 1 :]) + new_ipynb = os.path.join(docs_root, path_docs_ipynb, sub_ipynb) + os.makedirs(os.path.dirname(new_ipynb), exist_ok=True) + + path_meta = path_ipynb.replace(".ipynb", ".yaml") + path_thumb = AssistantCLI._resolve_path_thumb(path_ipynb, path_meta) + + if path_thumb is not None: + new_thumb = os.path.join(docs_root, path_docs_images, path_thumb) + old_path_thumb = os.path.join(path_root, DIR_NOTEBOOKS, path_thumb) + os.makedirs(os.path.dirname(new_thumb), exist_ok=True) + copyfile(old_path_thumb, new_thumb) + path_thumb = os.path.join(path_docs_images, path_thumb) + + print(f"{path_ipynb} -> {new_ipynb}") + + with open(path_ipynb) as f: + ipynb = json.load(f) + + ipynb["cells"].append(AssistantCLI._get_card_item_cell(path_ipynb, path_meta, path_thumb)) + + with open(new_ipynb, "w") as f: + json.dump(ipynb, f) + + ipynb_content.append(os.path.join("notebooks", sub_ipynb)) + + @staticmethod + def update_env_details(folder: str, base_path: str = DIR_NOTEBOOKS) -> str: + """Export the actual packages used in runtime. + + Args: + folder: path to the folder + base_path: + """ + meta = AssistantCLI._load_meta(folder) + # default is COU runtime + with open(PATH_REQ_DEFAULT) as fp: + req = fp.readlines() + req += meta.get("requirements", []) + req = [r.strip() for r in req] + + def _parse_package_name(pkg: str, keys: str = " !<=>[]@", egg_name: str = "#egg=") -> str: + """Parsing just the package name.""" + if egg_name in pkg: + pkg = pkg[pkg.index(egg_name) + len(egg_name) :] + if any(c in pkg for c in keys): + ix = min(pkg.index(c) for c in keys if c in pkg) + pkg = pkg[:ix] + return pkg + + require = {_parse_package_name(r) for r in req if r} + env = {_parse_package_name(p): p for p in freeze.freeze()} + meta["environment"] = [env[r] for r in require] + meta["published"] = datetime.now().isoformat() + + fmeta = os.path.join(base_path, folder) + ".yaml" + yaml.safe_dump(meta, stream=open(fmeta, "w"), sort_keys=False) + return fmeta + + @staticmethod + def list_dirs(folder: str = "", include_file_ext: str = "") -> str: + """List all sub-folders in a given tree including any ipynb.""" + dirs = glob.glob(os.path.join(folder, "*" + include_file_ext)) + dirs += glob.glob(os.path.join(folder, "**", "*" + include_file_ext)) + if include_file_ext: + _ignore_base_dir = lambda p: os.path.sep.join(p.split(os.path.sep)[1:]) # noqa: E731 + # Take the notebook as a folder (notebook are on teh same level as the raw tutorial file mix) + dirs = [os.path.splitext(_ignore_base_dir(p))[0] for p in dirs] + else: + dirs = [p for p in dirs if os.path.isdir(p)] + return os.linesep.join(sorted(dirs)) + + +if __name__ == "__main__": + fire.Fire(AssistantCLI) diff --git a/_notebooks/.actions/git-diff-sync.sh b/_notebooks/.actions/git-diff-sync.sh new file mode 100644 index 0000000..c461e6d --- /dev/null +++ b/_notebooks/.actions/git-diff-sync.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +set -e +printf "Detect changes for: $1 >> $2\n\n" + +b1="${1//'/'/'_'}" +printf "Branch alias: $b1\n" +# list all dirs in source branch +python .actions/assistant.py list_dirs > "dirs-$b1.txt" +cat "dirs-$b1.txt" + +head=$(git rev-parse origin/$2) +git diff --name-only $head --output=target-diff.txt +printf "\nRaw changes:\n" +cat target-diff.txt +# transfer the source CLI version +mkdir -p _TEMP +cp -r .actions/ _TEMP/.actions/ + +git checkout $2 +b2="${2//'/'/'_'}" +printf "Branch alias: $b2\n" +# recover the original CLI +#rm -rf .actions && mv _TEMP/.actions .actions +# list all dirs in target branch +python _TEMP/.actions/assistant.py list_dirs ".notebooks" --include_file_ext=".ipynb" > "dirs-$b2.txt" +cat "dirs-$b2.txt" + +printf "\n\n" +git merge --ff -s resolve origin/$1 + +python _TEMP/.actions/assistant.py group-folders target-diff.txt --fpath_actual_dirs "['dirs-$b1.txt', 'dirs-$b2.txt']" +printf "\n\nChanged folders:\n" +cat changed-folders.txt +printf "\n\nDropped folders:\n" +cat dropped-folders.txt +printf "\n" diff --git a/_notebooks/.actions/requires.txt b/_notebooks/.actions/requires.txt new file mode 100644 index 0000000..ff35f70 --- /dev/null +++ b/_notebooks/.actions/requires.txt @@ -0,0 +1,6 @@ +Fire +tqdm +PyYAML +wcmatch +requests +pip diff --git a/_notebooks/.actions/test_cli.py b/_notebooks/.actions/test_cli.py new file mode 100644 index 0000000..b73d686 --- /dev/null +++ b/_notebooks/.actions/test_cli.py @@ -0,0 +1,30 @@ +import os + +import pytest +from assistant import AssistantCLI + +_PATH_ROOT = os.path.dirname(os.path.dirname(__file__)) +_PATH_TEMPLATES = os.path.join(_PATH_ROOT, "templates") +_PATH_DIR_SIMPLE = os.path.join(_PATH_TEMPLATES, "simple") +_PATH_DIR_TITANIC = os.path.join(_PATH_TEMPLATES, "titanic") + + +def _path_in_dir(fname: str, folder: str = _PATH_ROOT) -> str: + return os.path.join(folder, fname) + + +@pytest.mark.parametrize( + "cmd,args", + [ + ("list_dirs", []), + ("list_dirs", [".", ".ipynb"]), + ("bash_render", [_PATH_DIR_SIMPLE]), + ("bash_test", [_PATH_DIR_SIMPLE]), + ("group_folders", [_path_in_dir("master-diff.txt"), _path_in_dir("dirs-b1.txt"), _path_in_dir("dirs-b2.txt")]), + ("convert_ipynb", [_PATH_DIR_SIMPLE]), + ("copy_notebooks", [_PATH_ROOT]), + ("update_env_details", [_PATH_DIR_SIMPLE]), + ], +) +def test_assistant_commands(cmd: str, args: list): + AssistantCLI().__getattribute__(cmd)(*args) diff --git a/_notebooks/.azure/ipynb-publish.yml b/_notebooks/.azure/ipynb-publish.yml new file mode 100644 index 0000000..4e99d21 --- /dev/null +++ b/_notebooks/.azure/ipynb-publish.yml @@ -0,0 +1,172 @@ +trigger: + # this shall process all these workflows in sequence even several PRs are merged shortly + batch: "true" + # publish notebooks only from default/main branch + branches: + include: [ main ] + +# no run on PR as this is exclusive for publishing notebooks +pr: none + +jobs: + + - job: sync_pub + pool: + vmImage: 'Ubuntu-20.04' + variables: + ACCELERATOR: CPU,GPU + PUB_BRANCH: publication + COMMIT_MSG: $(echo "$(Build.SourceVersionMessage)" | head -n 1) + COMMIT_HASH: "$(Build.SourceVersion)" + steps: + - bash: | + git config --global user.email "pipelines@azure.com" + git config --global user.name "Azure Pipelines" + printf "commit hash:\n $(COMMIT_HASH)\n" + printf "commit message:\n $(COMMIT_MSG)\n" + displayName: 'Set Git user' + - bash: | + set -e + git fetch --all + echo $(PUB_BRANCH) + git ls-remote --heads origin ${PUB_BRANCH} | grep ${PUB_BRANCH} >/dev/null + if [ "$?" == "1" ] ; then echo "Branch doesn't exist"; exit; fi + displayName: 'Git branch check' + + - bash: pip install -r .actions/requires.txt + displayName: 'Install dependencies' + - bash: | + current_branch=$(cut -d '/' -f3- <<< $(Build.SourceBranch)) + printf "$current_branch\n" + bash .actions/git-diff-sync.sh $current_branch $(PUB_BRANCH) + displayName: 'Compare changes & sync' + + - bash: | + notebooks=$(python .actions/assistant.py generate-matrix changed-folders.txt) + printf "Changed notebooks: $notebooks\n" + echo "##vso[task.setVariable variable=dirs;isOutput=true]$notebooks" + name: mtrx + displayName: 'Changed matrix' + + - bash: | + # remove notebooks which have moved + while IFS= read -r line; do + git rm .notebooks/$line.ipynb + git rm .notebooks/$line.yaml + done <<< $(cat dropped-folders.txt) + git status + git commit -m "prune: $(COMMIT_HASH)" + condition: gt(variables['dropped.folders'], 0) + displayName: 'Prune notebook' + + - bash: | + git status + git push https://$(PAT_GHOST)@github.com/Lightning-AI/tutorials.git $(PUB_BRANCH) + displayName: 'Finish push' + + - job: papermill + dependsOn: sync_pub + strategy: + # generated matrix with changed notebooks, include fields: "notebook", "agent-pool" and "docker-image" + matrix: $[ dependencies.sync_pub.outputs['mtrx.dirs'] ] + # Maximum number of jobs running in parallel, use 1 to run in sequence and reduce collisions + maxParallel: "1" + # how much time to give 'run always even if cancelled tasks' before stopping them + cancelTimeoutInMinutes: "2" + # how long to run the job before automatically cancelling + # When 0 is specified, the maximum limit is used: + # - For 360 minutes (6 hours) on Microsoft-hosted agents with a public project and public repository + # - For 60 minutes on Microsoft-hosted agents with a private project or private repository + timeoutInMinutes: "180" + + pool: "$(agent-pool)" + # this need to have installed docker in the base machine/image... + container: + image: "$(docker-image)" + options: "--gpus=all --shm-size=32g -v /usr/bin/docker:/tmp/docker:ro" + + variables: + ACCELERATOR: CPU,GPU + PUB_BRANCH: publication + PATH_DATASETS: "$(Build.Repository.LocalPath)/.datasets" + COMMIT_MSG: $(echo "$(Build.SourceVersionMessage)" | head -n 1) + COMMIT_HASH: "$(Build.SourceVersion)" + DEVICES: $( python -c 'print("$(Agent.Name)".split("_")[-1])' ) + + condition: ne(dependencies.sync_pub.outputs['mtrx.dirs'], '') + + steps: + - bash: | + echo "##vso[task.setvariable variable=CUDA_VISIBLE_DEVICES]$(DEVICES)" + echo "##vso[task.setvariable variable=CONTAINER_ID]$(head -1 /proc/self/cgroup|cut -d/ -f3)" + displayName: 'Set environment variables' + + - bash: | + lspci | egrep 'VGA|3D' + whereis nvidia + nvidia-smi + echo $CUDA_VISIBLE_DEVICES + echo $CONTAINER_ID + python --version + pip list + displayName: 'Image info & NVIDIA' + + - script: | + /tmp/docker exec -t -u 0 $CONTAINER_ID \ + sh -c "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confold" -y install sudo" + displayName: 'Install Sudo in container (thanks Microsoft!)' + + - bash: | + git config --global user.email "pipelines@azure.com" + git config --global user.name "Azure Pipelines" + printf "commit hash:\n $(COMMIT_HASH)\n" + printf "commit message:\n $(COMMIT_MSG)\n" + displayName: 'Set Git user' + - bash: | + set -e + git fetch --all + echo $(PUB_BRANCH) + git ls-remote --heads origin ${PUB_BRANCH} | grep ${PUB_BRANCH} >/dev/null + if [ "$?" == "1" ] ; then echo "Branch doesn't exist"; exit; fi + git checkout $(PUB_BRANCH) + git show-ref $(PUB_BRANCH) + git pull + displayName: 'Git check & switch branch' + + - bash: | + set -e + sudo apt-get update -q --fix-missing + sudo apt install -y tree ffmpeg + #pip install --upgrade pip + #pip --version + pip install -r requirements.txt -r _requirements/data.txt + pip list + displayName: 'Install dependencies' + + - bash: | + set -e + python -c "import torch ; mgpu = torch.cuda.device_count() ; assert mgpu > 0, f'GPU: {mgpu}'" + python -m papermill --version + displayName: 'Sanity check' + + - bash: python .actions/assistant.py convert-ipynb $(notebook) + displayName: 'Generate notebook' + + - bash: | + set -e + mkdir $(PATH_DATASETS) + python .actions/assistant.py bash-render $(notebook) + cat .actions/_ipynb-render.sh + bash .actions/_ipynb-render.sh + git status + git commit -m "publish [GPU]: $(notebook)" + env: + KAGGLE_USERNAME: $(KAGGLE_USERNAME) + KAGGLE_KEY: $(KAGGLE_KEY) + displayName: 'Render notebook' + + - bash: | + git status + git show-ref $(PUB_BRANCH) + git push https://$(PAT_GHOST)@github.com/Lightning-AI/tutorials.git $(PUB_BRANCH) + displayName: 'Finish push' diff --git a/_notebooks/.azure/ipynb-tests.yml b/_notebooks/.azure/ipynb-tests.yml new file mode 100644 index 0000000..f7c9d24 --- /dev/null +++ b/_notebooks/.azure/ipynb-tests.yml @@ -0,0 +1,106 @@ +trigger: none +pr: + branches: + include: [ main ] + autoCancel: "true" + drafts: "true" + +# Multi-job configuration +# - https://learn.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#multi-job-configuration + +jobs: + + - job: check_diff + pool: + vmImage: 'Ubuntu-20.04' + steps: + - bash: | + pip install -r .actions/requires.txt + pip list + displayName: 'Install dependencies' + + - bash: | + head=$(git rev-parse origin/main) + printf "Head: $head\n" + git diff --name-only $head --output=target-diff.txt + python .actions/assistant.py group-folders --fpath_gitdiff=target-diff.txt + printf "Changed folders:\n" + cat changed-folders.txt + displayName: 'Process folders' + + - bash: | + notebooks=$(python .actions/assistant.py generate-matrix changed-folders.txt) + printf "Changed notebooks: $notebooks\n" + echo "##vso[task.setVariable variable=dirs;isOutput=true]$notebooks" + name: mtrx + displayName: 'Changed matrix' + + - job: nbval + dependsOn: check_diff + strategy: + matrix: $[ dependencies.check_diff.outputs['mtrx.dirs'] ] + # how long to run the job before automatically cancelling + timeoutInMinutes: "95" + # how much time to give 'run always even if cancelled tasks' before stopping them + cancelTimeoutInMinutes: "2" + + pool: "$(agent-pool)" + # this need to have installed docker in the base image... + container: + image: "$(docker-image)" + options: "--gpus=all --shm-size=32g -v /usr/bin/docker:/tmp/docker:ro" + + variables: + ACCELERATOR: CPU,GPU + PATH_DATASETS: "$(Build.Repository.LocalPath)/.datasets" + DEVICES: $( python -c 'print("$(Agent.Name)".split("_")[-1])' ) + + condition: ne(dependencies.check_diff.outputs['mtrx.dirs'], '') + + steps: + + - bash: | + echo "##vso[task.setvariable variable=CUDA_VISIBLE_DEVICES]$(DEVICES)" + echo "##vso[task.setvariable variable=CONTAINER_ID]$(head -1 /proc/self/cgroup|cut -d/ -f3)" + displayName: 'Set environment variables' + + - bash: | + lspci | egrep 'VGA|3D' + whereis nvidia + nvidia-smi + echo $CUDA_VISIBLE_DEVICES + echo $CONTAINER_ID + python --version + pip list | grep torch + displayName: 'Image info & NVIDIA' + + - script: | + /tmp/docker exec -t -u 0 $CONTAINER_ID \ + sh -c "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confold" -y install sudo" + displayName: 'Install Sudo in container (thanks Microsoft!)' + + - bash: | + set -e + sudo apt-get update -q --fix-missing + sudo apt install -y tree ffmpeg + pip install -r requirements.txt -r _requirements/data.txt + pip list + displayName: 'Install dependencies' + + - bash: | + python -c "import torch ; mgpu = torch.cuda.device_count() ; assert mgpu > 0, f'GPU: {mgpu}'" + displayName: 'Sanity check' + + - bash: python .actions/assistant.py convert-ipynb $(notebook) + displayName: 'Generate notebook' + + - bash: | + set -e + mkdir $(PATH_DATASETS) + python .actions/assistant.py bash-test $(notebook) + cat .actions/_ipynb-test.sh + bash .actions/_ipynb-test.sh + env: + KAGGLE_USERNAME: $(KAGGLE_USERNAME) + KAGGLE_KEY: $(KAGGLE_KEY) + displayName: 'PyTest notebook' diff --git a/_notebooks/.codecov.yml b/_notebooks/.codecov.yml new file mode 100644 index 0000000..7196116 --- /dev/null +++ b/_notebooks/.codecov.yml @@ -0,0 +1,52 @@ +# see https://docs.codecov.io/docs/codecov-yaml +# Validation check: +# $ curl --data-binary @.codecov.yml https://codecov.io/validate + + +# https://docs.codecov.io/docs/codecovyml-reference +codecov: + bot: "codecov-io" + strict_yaml_branch: "yaml-config" + require_ci_to_pass: yes + notify: + # after_n_builds: 2 + wait_for_ci: yes + +coverage: + precision: 0 # 2 = xx.xx%, 0 = xx% + round: nearest # how coverage is rounded: down/up/nearest + range: 40...100 # custom range of coverage colors from red -> yellow -> green + status: + # https://codecov.readme.io/v1.0/docs/commit-status + project: + default: + target: 95% # specify the target coverage for each commit status + threshold: 30% # allow this little decrease on project + # https://github.com/codecov/support/wiki/Filtering-Branches + # branches: master + if_ci_failed: error + # https://github.com/codecov/support/wiki/Patch-Status + patch: + default: + threshold: 50% # allow this much decrease on patch + changes: false + +# https://docs.codecov.com/docs/github-checks#disabling-github-checks-patch-annotations +github_checks: + annotations: false + +parsers: + gcov: + branch_detection: + conditional: true + loop: true + macro: false + method: false + javascript: + enable_partials: false + +comment: + layout: header, diff + require_changes: false + behavior: default # update if exists else create new + # branches: * diff --git a/_notebooks/.gitattributes b/_notebooks/.gitattributes new file mode 100644 index 0000000..180a922 --- /dev/null +++ b/_notebooks/.gitattributes @@ -0,0 +1 @@ +*.ipynb filter=lfs diff=lfs merge=lfs -text diff --git a/_notebooks/.github/CODEOWNERS b/_notebooks/.github/CODEOWNERS new file mode 100644 index 0000000..24a71a1 --- /dev/null +++ b/_notebooks/.github/CODEOWNERS @@ -0,0 +1,24 @@ +# This is a comment. +# Each line is a file pattern followed by one or more owners. + +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @borda @rohitgr7 @carmocca @kaushikb11 @SeanNaren @ethanwharris + +# CI/CD and configs +/.actions/ @borda @ethanwharris +/.azure-*/ @borda @ethanwharris +/.github/ @borda @ethanwharris +/_requirements/ @borda @ethanwharris +*.yml @borda @ethanwharris +requirements.txt @borda @ethanwharris + +# Docs +/_docs/ @borda @ethanwharris @rohitgr7 +/.github/*.md @borda @ethanwharris @rohitgr7 +/.github/ISSUE_TEMPLATE/ @borda @ethanwharris @rohitgr7 + +/.github/CODEOWNERS @borda +/README.md @borda diff --git a/_notebooks/.github/CONTRIBUTING.md b/_notebooks/.github/CONTRIBUTING.md new file mode 100644 index 0000000..b2510fc --- /dev/null +++ b/_notebooks/.github/CONTRIBUTING.md @@ -0,0 +1,81 @@ +# Contributing + +Welcome to the PyTorch Lightning community! We're building the most advanced research platform on the planet to implement the latest, best practices that the amazing PyTorch team rolls out! + +## Design Principles + +We encourage all sorts of contributions you're interested in adding! When writing Tutorials, please follow these principles. + +#### Simple Internal Code + +It's useful for users to look at the code and understand very quickly what's happening. +Many users won't be engineers. Thus we need to value clear, simple code over condensed ninja moves. +While that's super cool, this isn't the project for that :) + +#### Force User Decisions To Best Practices + +There are 1,000 ways to do something. However, eventually one popular solution becomes standard practice, and everyone follows. +We try to find the best way to solve a particular problem, and then force our users to use it for readability and simplicity. + +When something becomes a best practice, we add it to the framework. This is usually something like bits of code in utils or in the model file that everyone keeps adding over and over again across projects. When this happens, bring that code inside the trainer and add a flag for it. + +#### Gain User Trust + +As a researcher, you can't have any part of your code going wrong. So, make thorough tests to ensure that every implementation of a new trick or subtle change is correct. + +#### Interoperability + +PyTorch Lightning Tutorials is highly interoperable with PyTorch Lightning and PyTorch. + +______________________________________________________________________ + +## Contribution Types + +We are always looking for help to implement new features or fixing bugs. + +A lot of good work has already been done in project mechanics (\_requirements/base.txt, setup.py, pep8, badges, ci, etc...) so we're in a good state there thanks to all sooner contributors! + +### Bug Fixes: + +1. If you find a bug please submit a GitHub issue. Make sure the title explains the issue. +1. Try to fix it or recommend a solution. +1. Submit a PR! + +_**Note**, even if you do not find the solution, sending a PR with a test covering the issue is a valid contribution, and we can help you or finish it with you :\]_ + +### New Models: + +PyTorch Lightning Tutorials shows several research models for ready usage. Following are general guidelines for adding new models. + +1. Workflows which are standard baselines +1. Whose results are reproduced properly either by us or by authors. +1. Do not reinvent the wheel, natively support torchvision, torchtext, torchaudio models. +1. Use open source licensed models. + +Please raise an issue before adding a new tutorial. There are tons of models that keep coming. It is very difficult to support every peace. + +______________________________________________________________________ + +## Guidelines + +For this section, we refer to read the [parent PL guidelines](https://pytorch-lightning.readthedocs.io/en/stable/CONTRIBUTING.html) + +**Reminder** + +All added or edited code shall be the own original work of the particular contributor. +If you use some third-party implementation, all such blocks/functions/modules shall be properly referred and if possible also agreed by code's author. For example - `This code is inspired from http://...`. +In case you adding new dependencies, make sure that they are compatible with the actual PyTorch Lightning license (each particular tutorial can have own licence). + +### Question & Answer + +1. **How can I help/contribute?** + + All help is extremely welcome - reporting bugs, fixing documentation, etc. To solve some issues you can start with label [good first issue](https://github.com/Lightning-AI/lightning-bolts/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) or chose something close to your domain with label. Before you start to implement anything check that the issue description that it is clear and self-assign the task to you (if it is not possible, just comment that you take it, and we assign it to you...). + +1. **Is there a recommendation for branch names?** + + We do not rely on the name convention so far you are working with your own fork. Anyway it would be nice to follow this convention `/_` where the types are: `bugfix`, `ipynb`, `docs`, ... + +1. **I have a model in other framework than PyTorch, how do I add it here?** + + Since PL Tutorials are aiming at Pytorch Lightning implementations we encourage staying with it. diff --git a/_notebooks/.github/ISSUE_TEMPLATE/bug_report.md b/_notebooks/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..fd7a397 --- /dev/null +++ b/_notebooks/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug / fix, help wanted +assignees: '' +--- + +## 🐛 Bug + + + +### To Reproduce + +Steps to reproduce the behavior: + +1. Run '....' +1. See error + + + +### Expected behavior + + + +### Additional context + + diff --git a/_notebooks/.github/ISSUE_TEMPLATE/config.yml b/_notebooks/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..c52bf19 --- /dev/null +++ b/_notebooks/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a Question + url: https://github.com/Lightning-AI/tutorials/discussions/new + about: Ask and answer Lightning related questions + - name: 💬 Slack + url: https://app.slack.com/client/TR9DVT48M/CQXV8BRH9/thread/CQXV8BRH9-1591382895.254600 + about: Chat with our community diff --git a/_notebooks/.github/ISSUE_TEMPLATE/feature_request.md b/_notebooks/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..11378b2 --- /dev/null +++ b/_notebooks/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,25 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' +--- + +## 🚀 Feature + +### Motivation + + + +### Pitch + + + +### Alternatives + + + +### Additional context + + diff --git a/_notebooks/.github/PULL_REQUEST_TEMPLATE.md b/_notebooks/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..429d86a --- /dev/null +++ b/_notebooks/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,18 @@ +## Before submitting + +- [ ] Was this **discussed/approved** via a Github issue? (no need for typos and docs improvements) +- [ ] Did you make sure to **update the docs**? +- [ ] Did you write any new **necessary tests**? + +## What does this PR do? + +Fixes # (issue) + +## PR review + +Anyone in the community is free to review the PR once the tests have passed. +If we didn't discuss your PR in Github issues there's a high chance it will not be merged. + +## Did you have fun? + +Make sure you had fun coding 🙃 diff --git a/_notebooks/.github/dependabot.yml b/_notebooks/.github/dependabot.yml new file mode 100644 index 0000000..a01cfc7 --- /dev/null +++ b/_notebooks/.github/dependabot.yml @@ -0,0 +1,30 @@ +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file +version: 2 +updates: + # Enable version updates for python + - package-ecosystem: "pip" + # Look for a `requirements` in the `root` directory + directory: "/_requirements" + # Check for updates once a week + schedule: + interval: "monthly" + # Labels on pull requests for version updates only + labels: ["ci/cd"] + pull-request-branch-name: + # Separate sections of the branch name with a hyphen + separator: "-" + # Allow up to 5 open pull requests for pip dependencies + open-pull-requests-limit: 5 + reviewers: + - "Lightning-AI/teams/core-lightning" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + labels: ["ci/cd"] + pull-request-branch-name: + separator: "-" + open-pull-requests-limit: 5 + reviewers: + - "Lightning-AI/core-lightning" diff --git a/_notebooks/.github/labeler.yml b/_notebooks/.github/labeler.yml new file mode 100644 index 0000000..621b7e0 --- /dev/null +++ b/_notebooks/.github/labeler.yml @@ -0,0 +1,7 @@ +documentation: + - _docs/**/* + +CI/CD: + - .actions/**/* + - .azure-*/**/* + - .github/**/* diff --git a/_notebooks/.github/mergify.yml b/_notebooks/.github/mergify.yml new file mode 100644 index 0000000..06f25ec --- /dev/null +++ b/_notebooks/.github/mergify.yml @@ -0,0 +1,32 @@ +pull_request_rules: + + - name: warn on conflicts + conditions: + - conflict + - -draft # filter-out GH draft PRs + - -label="has conflicts" + actions: + # comment: + # message: This pull request is now in conflict... :( + label: + add: [ "has conflicts" ] + + - name: resolved conflicts + conditions: + - -conflict + - label="has conflicts" + - -draft # filter-out GH draft PRs + - -merged # not merged yet + - -closed + actions: + label: + remove: [ "has conflicts" ] + + - name: add core reviewer + conditions: + # number of review approvals + - "#approved-reviews-by<2" + actions: + request_reviews: + users: + - Borda diff --git a/_notebooks/.github/stale.yml b/_notebooks/.github/stale.yml new file mode 100644 index 0000000..8dd7aca --- /dev/null +++ b/_notebooks/.github/stale.yml @@ -0,0 +1,26 @@ +# https://github.com/marketplace/stale + +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 14 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: won't fix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: true +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: true +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: true diff --git a/_notebooks/.github/workflows/ci_block-ipybn.yml b/_notebooks/.github/workflows/ci_block-ipybn.yml new file mode 100644 index 0000000..cde61ec --- /dev/null +++ b/_notebooks/.github/workflows/ci_block-ipybn.yml @@ -0,0 +1,13 @@ +name: Prevent adding/chnaging notebooks + +# see: https://help.github.com/en/actions/reference/events-that-trigger-workflows +on: # Trigger the workflow on PR to master + pull_request: + paths: + - ./**/*.ipynb + +jobs: + block-ipynb: + runs-on: ubuntu-latest + steps: + - run: exit 1 diff --git a/_notebooks/.github/workflows/ci_block-pub.yml b/_notebooks/.github/workflows/ci_block-pub.yml new file mode 100644 index 0000000..e0a2e81 --- /dev/null +++ b/_notebooks/.github/workflows/ci_block-pub.yml @@ -0,0 +1,12 @@ +name: Prevent modify publication + +# see: https://help.github.com/en/actions/reference/events-that-trigger-workflows +on: # Trigger the workflow on PR to master + pull_request: + branches: [publication] + +jobs: + block-ipynb: + runs-on: ubuntu-latest + steps: + - run: exit 1 diff --git a/_notebooks/.github/workflows/ci_checks.yml b/_notebooks/.github/workflows/ci_checks.yml new file mode 100644 index 0000000..99d3c0c --- /dev/null +++ b/_notebooks/.github/workflows/ci_checks.yml @@ -0,0 +1,18 @@ +name: General checks + +on: + push: + branches: + - "*" + - "**" + - "!publication" + pull_request: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref }} + cancel-in-progress: ${{ ! (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }} + +jobs: + + check-schema: + uses: Lightning-AI/utilities/.github/workflows/check-schema.yml@v0.7.1 diff --git a/_notebooks/.github/workflows/ci_docs.yml b/_notebooks/.github/workflows/ci_docs.yml new file mode 100644 index 0000000..7902d98 --- /dev/null +++ b/_notebooks/.github/workflows/ci_docs.yml @@ -0,0 +1,116 @@ +name: validate Docs + +on: # Trigger the workflow on push or pull request +# push: +# branches: [main] + pull_request: {} + schedule: + # At the end of every day + - cron: "0 0 * * *" + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + build-docs: + runs-on: ubuntu-latest + env: + PUB_BRANCH: publication + PATH_DATASETS: ${{ github.workspace }}/.datasets + timeout-minutes: 20 + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + with: + fetch-depth: 0 # fetch all history for all branches and tags + - uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: pip-${{ hashFiles('requirements.txt') }}-${{ hashFiles('_requirements/docs.txt') }} + restore-keys: pip- + + - name: Install dependencies + run: | + sudo apt-get update --fix-missing + sudo apt-get install -y tree + # install Texlive, see https://linuxconfig.org/how-to-install-latex-on-ubuntu-20-04-focal-fossa-linux + sudo apt-get install -y cmake pandoc texlive-latex-extra dvipng texlive-pictures + pip --version + pip install -q -r requirements.txt -r _requirements/docs.txt + pip list + shell: bash + + - name: Process folders + run: | + mkdir -p ${PATH_DATASETS} + head=$(git rev-parse origin/"${{ github.base_ref }}") + git diff --name-only $head --output=master-diff.txt + python .actions/assistant.py group-folders master-diff.txt + printf "Changed folders:\n" + cat changed-folders.txt + shell: bash + + - name: ">> output" + id: changed + run: python -c "lines = open('changed-folders.txt').readlines(); print(f'::set-output name=nb_dirs::{len(lines)}')" + + - uses: oleksiyrudenko/gha-git-credentials@v2.1 + with: + token: '${{ secrets.GITHUB_TOKEN }}' + global: true + - name: Sync to pub + run: git merge -s resolve origin/$PUB_BRANCH + + - name: Generate notebooks + if: steps.changed.outputs.nb_dirs != 0 + run: | + while IFS= read -r line; do + python .actions/assistant.py convert-ipynb $line + python .actions/assistant.py bash-render $line + cat .actions/_ipynb-render.sh + bash .actions/_ipynb-render.sh + done <<< $(cat changed-folders.txt) + env: + DRY_RUN: 1 + shell: bash + + - name: Copy notebooks + if: steps.changed.outputs.nb_dirs != 0 + run: | + while IFS= read -r line; do + dir=$(dirname $line) + mkdir -p changed-notebooks/${dir} + cp .notebooks/${line}.ipynb changed-notebooks/${dir}/ + done <<< $(cat changed-folders.txt) + tree changed-notebooks + shell: bash + + - uses: actions/upload-artifact@v3 + if: steps.changed.outputs.nb_dirs != 0 + with: + name: notebooks-${{ github.sha }} + path: changed-notebooks/ + + - name: Make Documentation + working-directory: ./_docs + run: make html --debug SPHINXOPTS="-W --keep-going" + + - name: Check External Links (Optional) + working-directory: ./_docs + run: make --jobs $(nproc) linkcheck + # ToDO: comment on PR if any link failed + continue-on-error: true + + - name: Upload built docs + uses: actions/upload-artifact@v3 + with: + name: docs-html-${{ github.sha }} + path: _docs/build/html/ + # Use always() to always run this step to publish test results when there are test failures + if: success() diff --git a/_notebooks/.github/workflows/ci_test-acts.yml b/_notebooks/.github/workflows/ci_test-acts.yml new file mode 100644 index 0000000..7f1d99a --- /dev/null +++ b/_notebooks/.github/workflows/ci_test-acts.yml @@ -0,0 +1,84 @@ +name: CI internal + +# see: https://help.github.com/en/actions/reference/events-that-trigger-workflows +on: # Trigger the workflow on push or pull request, but only for the main branch + push: {} + pull_request: + branches: [main] + +defaults: + run: + shell: bash + +jobs: + pytest-internal: + + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04 ] + python-version: ["3.8", "3.10"] + # Timeout: https://stackoverflow.com/a/59076067/4521646 + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # fetch all history for all branches and tags + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + # Note: This uses an internal pip API and may not always work + # https://github.com/actions/cache/blob/master/examples.md#multiple-oss-in-a-workflow + - name: Get pip cache dir + id: pip-cache + run: echo "::set-output name=dir::$(pip cache dir)" + + - name: pip cache + uses: actions/cache@v3 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-py${{ matrix.python-version }}-${{ hashFiles('.actions/requires.txt') }}-${{ hashFiles('requirements/default.txt') }} + restore-keys: ${{ runner.os }}-pip-py${{ matrix.python-version }}- + + - name: Install requirements + run: | + pip --version + pip install -q -r .actions/requires.txt + pip install -q "pytest==6.*" coverage jupytext + # this is needed to be able to run package version parsing test + pip install -q matplotlib -r _requirements/default.txt --find-links https://download.pytorch.org/whl/cpu/torch_stable.html + + - name: Prepare dummy inputs + run: | + jupytext --set-formats ipynb,py:percent templates/simple/template.py + jupytext --set-formats ipynb,py:percent templates/titanic/tutorial.py + # mv templates/simple/template.ipynb templates/simple.ipynb + git diff --name-only HEAD~3 > master-diff.txt + python .actions/assistant.py list_dirs "" > dirs-b1.txt + python .actions/assistant.py list_dirs --include_file_ext=".ipynb" > dirs-b2.txt + + - name: Testing + run: | + coverage run -m pytest .actions -v + + - name: Statistics + if: success() + run: | + coverage report + coverage xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + if: always() + # see: https://github.com/actions/toolkit/issues/399 + continue-on-error: true + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: coverage.xml + flags: pytest,${{ runner.os }} + name: CLI-coverage + fail_ci_if_error: false diff --git a/_notebooks/.github/workflows/docs-deploy.yml b/_notebooks/.github/workflows/docs-deploy.yml new file mode 100644 index 0000000..d132912 --- /dev/null +++ b/_notebooks/.github/workflows/docs-deploy.yml @@ -0,0 +1,58 @@ +name: Deploy Docs +on: + push: + branches: [publication] + workflow_dispatch: {} + workflow_run: + workflows: ["Publish notebook"] + types: + - completed + +jobs: + # https://github.com/marketplace/actions/deploy-to-github-pages + build-docs-deploy: + runs-on: ubuntu-20.04 + env: + PATH_DATASETS: ${{ github.workspace }}/.datasets + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + with: + ref: publication + - uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}-${{ hashFiles('_requirements/docs.txt') }} + restore-keys: ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + mkdir -p ${PATH_DATASETS} + # install Texlive, see https://linuxconfig.org/how-to-install-latex-on-ubuntu-20-04-focal-fossa-linux + sudo apt-get update + sudo apt-get install -y cmake pandoc + sudo apt-get install -y texlive-latex-extra dvipng texlive-pictures + pip --version + pip install --quiet --requirement _requirements/docs.txt + pip list + shell: bash + + - name: Make Documentation + working-directory: ./_docs + run: make html --jobs $(nproc) + + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4.4.1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages # The branch the action should deploy to. + folder: _docs/build/html # The folder the action should deploy. + clean: true # Automatically remove deleted files from the deploy branch + target-folder: docs # If you'd like to push the contents of the deployment folder into a specific directory + single-commit: true # you'd prefer to have a single commit on the deployment branch instead of full history + if: success() diff --git a/_notebooks/.gitignore b/_notebooks/.gitignore new file mode 100644 index 0000000..df8737c --- /dev/null +++ b/_notebooks/.gitignore @@ -0,0 +1,137 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +_docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +.idea/ + +# data artifacts +logs/ +lightning_logs/ +cifar-10-batches-py +*.tar.gz diff --git a/_notebooks/.jupytext.yml b/_notebooks/.jupytext.yml new file mode 100644 index 0000000..fa534dd --- /dev/null +++ b/_notebooks/.jupytext.yml @@ -0,0 +1,4 @@ +# todo: this seems to have no effect atm + +# Always pair ipynb notebooks to py:percent files +formats: ipynb,py:percent diff --git a/_notebooks/.pre-commit-config.yaml b/_notebooks/.pre-commit-config.yaml new file mode 100644 index 0000000..c65cb64 --- /dev/null +++ b/_notebooks/.pre-commit-config.yaml @@ -0,0 +1,66 @@ +default_language_version: + python: python3.8 + +ci: + autofix_prs: true + autoupdate_commit_msg: '[pre-commit.ci] pre-commit suggestions' + autoupdate_schedule: quarterly + # submodules: true + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-case-conflict + - id: check-json + - id: check-yaml + - id: check-toml + - id: check-added-large-files + args: ['--maxkb=250', '--enforce-all'] + - id: check-docstring-first + - id: detect-private-key + + - repo: https://github.com/asottile/pyupgrade + rev: v3.3.1 + hooks: + - id: pyupgrade + args: [--py37-plus] + name: Upgrade code + + - repo: https://github.com/PyCQA/docformatter + rev: v1.5.1 + hooks: + - id: docformatter + args: [--in-place, --wrap-summaries=115, --wrap-descriptions=120] + + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + + - repo: https://github.com/psf/black + rev: 22.12.0 + hooks: + - id: black + name: Format code + + - repo: https://github.com/asottile/yesqa + rev: v1.4.0 + hooks: + - id: yesqa + + - repo: https://github.com/executablebooks/mdformat + rev: 0.7.16 + hooks: + - id: mdformat + additional_dependencies: + - mdformat-gfm + - mdformat-black + - mdformat_frontmatter + + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 diff --git a/_notebooks/LICENSE b/_notebooks/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/_notebooks/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/_notebooks/Makefile b/_notebooks/Makefile new file mode 100644 index 0000000..764c44a --- /dev/null +++ b/_notebooks/Makefile @@ -0,0 +1,39 @@ +.PHONY: ipynb clean docs + +# META := $(wildcard **/.meta.yml) +META := $(shell find -regex ".*/.meta.y[a]?ml") +IPYNB := $(META:%/.meta.yml=%.ipynb) +IPYNB := $(IPYNB:%/.meta.yaml=%.ipynb) +export PATH_DATASETS=$(PWD)/.datasets + +init: + @echo $(PATH_DATASETS) + mkdir -p $(PATH_DATASETS) + +ipynb: init ${IPYNB} +# @echo $< + +%.ipynb: %/.meta.y*ml + @echo $< + python .actions/assistant.py convert-ipynb $(shell dirname $<) + python .actions/assistant.py bash-render $(shell dirname $<) + bash .actions/_ipynb-render.sh + +docs: clean + pip install --quiet -r _requirements/docs.txt + python -m sphinx -b html -W --keep-going _docs/source _docs/build + +clean: + rm -rf ./.datasets + # clean all temp runs + rm -rf ./_docs/build + rm -rf ./_docs/source/notebooks + rm -rf ./_docs/source/api + rm -f ./dirs-*.txt + rm -f ./*-folders.txt + rm -f ./*/**/*.ipynb + rm -rf ./*/**/.ipynb_checkpoints + rm -rf ./*/**/venv + rm -rf ./*/**/logs + rm -rf ./*/**/lightning_logs + rm -f ./*/**/requirements.txt diff --git a/_notebooks/README.md b/_notebooks/README.md new file mode 100644 index 0000000..5d8baa4 --- /dev/null +++ b/_notebooks/README.md @@ -0,0 +1,99 @@ +# PytorchLightning Tutorials + +[![CI internal](https://github.com/Lightning-AI/tutorials/actions/workflows/ci_test-acts.yml/badge.svg?event=push)](https://github.com/Lightning-AI/tutorials/actions/workflows/ci_test-acts.yml) +[![Build Status](https://dev.azure.com/Lightning-AI/Tutorials/_apis/build/status/Lightning-AI.tutorials%20%5Bpublish%5D?branchName=main)](https://dev.azure.com/Lightning-AI/Tutorials/_build/latest?definitionId=29&branchName=main) +[![codecov](https://codecov.io/gh/Lightning-AI/tutorials/branch/main/graph/badge.svg?token=C6T3XOOR56)](https://codecov.io/gh/Lightning-AI/tutorials) +[![Deploy Docs](https://github.com/Lightning-AI/tutorials/actions/workflows/docs-deploy.yml/badge.svg)](https://github.com/Lightning-AI/tutorials/actions/workflows/docs-deploy.yml) +[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/Lightning-AI/tutorials/main.svg)](https://results.pre-commit.ci/latest/github/Lightning-AI/tutorials/main) + +This is the Lightning Library - collection of Lightning related notebooks which are pulled back to the main repo as submodule and rendered inside the main documentations. +The key features/highlights: + +- we keep the repo **light-weighted** - notebooks are stored in rich script format +- all scripts/notebooks are tested to be **fully executable** +- fully **reproducible** by saving runtime env. details + +For more details read our blogpost - [Best Practices for Publishing PyTorch Lightning Tutorial Notebooks](https://devblog.pytorchlightning.ai/publishing-lightning-tutorials-cbea3eaa4b2c) + +## Adding/Editing notebooks + +This repo in main branch contain only python scripts with markdown extensions, and notebooks are generated in special publication branch, so no raw notebooks are accepted as PR. +On the other hand we highly recommend creating a notebooks and convert it script with [jupytext](https://jupytext.readthedocs.io/en/latest/) as + +```bash +jupytext --set-formats ipynb,py:percent my-notebook.ipynb +``` + +### Contribution structure + +The addition has to formed as new folder + +- the folder name is used for the future notebooks +- single python scripts with converted notebooks (name does not matter) +- metadata named `.meta.yaml` including following info: + ```yaml + title: Sample notebooks + author: [User](contact) + created: YYYY-MM-DD + updated: YYYY-MM-DD + license: CC BY-SA + # multi-line + description: | + This notebook will walk you through ... + requirements: + - package # with version if needed + # define supported - CPU|GPU|TPU + accelerator: + - CPU + ``` + +### Using datasets + +It is quite common to use some public or competition's dataset for your example. +We facilitate this via defining the data sources in the metafile. +There are two basic options, download a file from web or pul Kaggle dataset: + +```yaml +datasets: + web: + - https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz + kaggle: + - titanic +``` + +In both cases, the downloaded archive (Kaggle dataset is originally downloaded as zip file) is extracted to the default dataset folder under sub-folder with the same name as the downloaded file. +To get path to this dataset folder, please use environment variable `PATH_DATASETS`, so in your script use: + +```py +import os + +data_path = os.environ.get("PATH_DATASETS", "_datasets") +path_titanic = os.path.join(data_path, "titatnic") +``` + +**Warning:** some Kaggle datasets can be quite large and the process is - downloading and extracting, which means that particular runner needs to have double free space. For this reason, the CPU runner is limited to 3GB datasets. + +### Suggestions + +- For inserting images into text cells use MarkDown formatting, so we can insert inline images to the notebooks directly and drop eventual dependency on internet connection -> generated notebooks could be better shared offline +- If your images need special sizes, use `![Cation](my-image.png){height="60px" width="240px"}` +- If your notebook is computational or any other resource (CPU/RAM) demanding use only GPU accelerator option in meta config + +### Known limitations + +- Nothing major at this moment + +## Meantime notes + +On the back side of publishing workflow you can find in principle these three steps + +```bash +# 1) convert script to notebooks +jupytext --set-formats ipynb,py:percent notebook.py + +# 2) testing the created notebook +pytest -v notebook.ipynb --nbval + +# 3) generating notebooks outputs +papermill in-notebook.ipynb out-notebook.ipynb +``` diff --git a/_notebooks/_docs/.build_docs.sh b/_notebooks/_docs/.build_docs.sh new file mode 100644 index 0000000..0419bd6 --- /dev/null +++ b/_notebooks/_docs/.build_docs.sh @@ -0,0 +1,2 @@ +make clean +make html --debug --jobs $(nproc) diff --git a/_notebooks/_docs/Makefile b/_notebooks/_docs/Makefile new file mode 100644 index 0000000..197a2c7 --- /dev/null +++ b/_notebooks/_docs/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -W +SPHINXBUILD = python $(shell which sphinx-build) +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/make.bat b/_notebooks/_docs/make.bat similarity index 100% rename from make.bat rename to _notebooks/_docs/make.bat diff --git a/_notebooks/_docs/source/_static/images/icon.svg b/_notebooks/_docs/source/_static/images/icon.svg new file mode 100644 index 0000000..5ab3512 --- /dev/null +++ b/_notebooks/_docs/source/_static/images/icon.svg @@ -0,0 +1,62 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/_notebooks/_docs/source/_static/images/logo.png b/_notebooks/_docs/source/_static/images/logo.png new file mode 100644 index 0000000..a28606b Binary files /dev/null and b/_notebooks/_docs/source/_static/images/logo.png differ diff --git a/docs/_static/images/logo-lightning.svg b/_notebooks/_docs/source/_static/images/logo.svg old mode 100644 new mode 100755 similarity index 100% rename from docs/_static/images/logo-lightning.svg rename to _notebooks/_docs/source/_static/images/logo.svg diff --git a/_notebooks/_docs/source/_templates/theme_variables.jinja b/_notebooks/_docs/source/_templates/theme_variables.jinja new file mode 100644 index 0000000..95adb3e --- /dev/null +++ b/_notebooks/_docs/source/_templates/theme_variables.jinja @@ -0,0 +1,18 @@ +{%- set external_urls = { + 'github': 'https://github.com/Lightning-AI/lightning-sandbox', + 'github_issues': 'https://github.com/Lightning-AI/lightning-sandbox/issues', + 'contributing': 'https://github.com/Lightning-AI/lightning/blob/master/CONTRIBUTING.md', + 'governance': 'https://github.com/Lightning-AI/lightning/blob/master/governance.md', + 'docs': 'https://lightning-sandbox.rtfd.io/en/latest', + 'twitter': 'https://twitter.com/PyTorchLightnin', + 'discuss': 'https://pytorch-lightning.slack.com', + 'tutorials': 'https://pt-lightning-sandbox.readthedocs.io/en/latest/#tutorials', + 'previous_pytorch_versions': 'https://pt-lightning-sandbox.rtfd.io/en/latest/', + 'home': 'https://pt-lightning-sandbox.rtfd.io/en/latest/', + 'get_started': 'https://pt-lightning-sandbox.readthedocs.io/en/latest/introduction_guide.html', + 'features': 'https://pt-lightning-sandbox.rtfd.io/en/latest/', + 'blog': 'https://www.pytorchlightning.ai/blog', + 'resources': 'https://pt-lightning-sandbox.readthedocs.io/en/latest/#community-examples', + 'support': 'https://pt-lightning-sandbox.rtfd.io/en/latest/', +} +-%} diff --git a/_notebooks/_docs/source/conf.py b/_notebooks/_docs/source/conf.py new file mode 100644 index 0000000..862e46e --- /dev/null +++ b/_notebooks/_docs/source/conf.py @@ -0,0 +1,231 @@ +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +import os +import sys + +import pt_lightning_sphinx_theme + +_PATH_HERE = os.path.abspath(os.path.dirname(__file__)) +_PATH_ROOT = os.path.realpath(os.path.join(_PATH_HERE, "..", "..")) +sys.path.insert(0, os.path.abspath(_PATH_ROOT)) +sys.path.append(os.path.join(_PATH_ROOT, ".actions")) + +from assistant import AssistantCLI # noqa: E402 + +# -- Project information ----------------------------------------------------- + +# this name shall match the project name in Github as it is used for linking to code +project = "lightning-tutorials" +author = "PytorchLightning team" +copyright = f"Copyright (c) 2020-2021, {author}." +homepage = "https://www.pytorchlightning.ai" + +# # The short X.Y version +# version = about.__version__ +# # The full version, including alpha/beta/rc tags +# release = about.__version__ + +# Options for the linkcode extension +# ---------------------------------- +github_user = "PyTorchLightning" +github_repo = project + +# -- Project documents ------------------------------------------------------- + +AssistantCLI.copy_notebooks(_PATH_ROOT, _PATH_HERE) + +# with open(os.path.join(_PATH_HERE, 'ipynb_content.rst'), 'w') as fp: +# fp.write(os.linesep.join(ipynb_content)) + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. + +needs_sphinx = "4.0" + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.napoleon", + "sphinx.ext.imgmath", + "sphinx.ext.githubpages", + "nbsphinx", + "myst_parser", + "sphinx_paramlinks", + "pt_lightning_sphinx_theme.extensions.lightning", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# https://berkeley-stat159-f17.github.io/stat159-f17/lectures/14-sphinx..html#conf.py-(cont.) +# https://stackoverflow.com/questions/38526888/embed-ipython-notebook-in-sphinx-document +# I execute the notebooks manually in advance. If notebooks test the code, +# they should be run at build time. +nbsphinx_execute = "never" +nbsphinx_allow_errors = True +nbsphinx_requirejs_path = "" + +myst_update_mathjax = False + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_parsers = { + ".rst": "restructuredtext", + ".txt": "markdown", + ".md": "markdown", + ".ipynb": "nbsphinx", +} + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = "ko" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + "PULL_REQUEST_TEMPLATE.md", + "_build", + "**.ipynb_checkpoints", +] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "pt_lightning_sphinx_theme" +html_theme_path = [pt_lightning_sphinx_theme.get_html_theme_path()] + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. + +html_theme_options = { + "pytorch_project": homepage, + "canonical_url": homepage, + "collapse_navigation": False, + "display_version": True, + "logo_only": False, +} + +html_favicon = "_static/images/icon.svg" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_templates", "_static"] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = project + "-doc" + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', + # Latex figure (float) alignment + "figure_align": "htbp", +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, project + ".tex", project + " Documentation", author, "manual"), +] + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [(master_doc, project, project + " Documentation", [author], 1)] + +# -- Options for linkcheck builder ---------------------------------------------- +# regex pattern 0: allow linking to a specific selection state in +# tensorboard.dev links while continuing to validate the base experiment link +linkcheck_anchors_ignore = ["scalars.*&runSelectionState.*"] + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + project, + project + " Documentation", + author, + project, + "" "Miscellaneous", # about.__docs__, + ), +] + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ["search.html"] + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +# Example configuration for intersphinx: refer to the Python standard library. +# intersphinx_mapping = { +# "python": ("https://docs.python.org/3", None), +# "torch": ("https://pytorch.org/docs/stable/", None), +# "numpy": ("https://docs.scipy.org/doc/numpy/", None), +# } diff --git a/_notebooks/_docs/source/index.rst b/_notebooks/_docs/source/index.rst new file mode 100644 index 0000000..1564f99 --- /dev/null +++ b/_notebooks/_docs/source/index.rst @@ -0,0 +1,32 @@ +.. PyTorchLightning-Sandbox documentation master file, created by + sphinx-quickstart on Wed Mar 25 21:34:07 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Lightning-Sandbox documentation +=============================== + +.. tutoriallist:: + +.. raw:: html + +
+ +.. toctree:: + :maxdepth: 1 + :name: start + :caption: Start here + :glob: + + notebooks/**/* + +.. raw:: html + +
+ +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/_notebooks/_requirements/data.txt b/_notebooks/_requirements/data.txt new file mode 100644 index 0000000..08fe313 --- /dev/null +++ b/_notebooks/_requirements/data.txt @@ -0,0 +1,2 @@ +# fixed version to be able to call it as `python -m kaggle` +https://github.com/Borda/kaggle-api/archive/refs/heads/setup/python-m.zip diff --git a/_notebooks/_requirements/default.txt b/_notebooks/_requirements/default.txt new file mode 100644 index 0000000..42e7333 --- /dev/null +++ b/_notebooks/_requirements/default.txt @@ -0,0 +1,5 @@ +setuptools==67.4.0 +ipython[notebook]>=8.0.0, <8.12.0 +torch>=1.8.1, <1.14.0 +pytorch-lightning>=1.4, <2.0.0 +torchmetrics>=0.7, <0.12 diff --git a/_notebooks/_requirements/devel.txt b/_notebooks/_requirements/devel.txt new file mode 100644 index 0000000..e0ad0dc --- /dev/null +++ b/_notebooks/_requirements/devel.txt @@ -0,0 +1,6 @@ +virtualenv>=20.10 +jupytext>=1.10, <1.15 # converting +pytest>=6.0, <7.0 +# testing with own fork with extended cell timeout +https://github.com/Borda/nbval/archive/refs/heads/timeout-limit.zip +papermill>=2.3.4, <2.5.0 # render diff --git a/_notebooks/_requirements/docs.txt b/_notebooks/_requirements/docs.txt new file mode 100644 index 0000000..28b8790 --- /dev/null +++ b/_notebooks/_requirements/docs.txt @@ -0,0 +1,12 @@ +sphinx>=4.0, <5.0 +myst-parser>=0.15 +nbsphinx>=0.8 +pandoc>=1.0 +docutils>=0.16 +sphinx-paramlinks>=0.4.0 +ipython[notebook]>=8.0.0, <8.2.0 + +# https://github.com/Lightning-AI/lightning_sphinx_theme/archive/master.zip#egg=pt-lightning-sphinx-theme +https://github.com/PyTorchKorea/lightning_sphinx_theme/archive/master.zip#egg=pt-lightning-sphinx-theme + +-r ../.actions/requires.txt diff --git a/_notebooks/course_UvA-DL/01-introduction-to-pytorch/.meta.yml b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/.meta.yml new file mode 100644 index 0000000..1e5b5b9 --- /dev/null +++ b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/.meta.yml @@ -0,0 +1,15 @@ +title: "Tutorial 1: Introduction to PyTorch" +author: Phillip Lippe +created: 2021-08-27 +updated: 2023-03-14 +license: CC BY-SA +description: | + This tutorial will give a short introduction to PyTorch basics, and get you setup for writing your own neural networks. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - matplotlib + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/01-introduction-to-pytorch.jpg b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/01-introduction-to-pytorch.jpg rename to _notebooks/course_UvA-DL/01-introduction-to-pytorch/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/01-introduction-to-pytorch/Introduction_to_PyTorch.py b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/Introduction_to_PyTorch.py new file mode 100644 index 0000000..c692d14 --- /dev/null +++ b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/Introduction_to_PyTorch.py @@ -0,0 +1,990 @@ +# %% [markdown] +#
+# Welcome to our PyTorch tutorial for the Deep Learning course 2020 at the University of Amsterdam! +# The following notebook is meant to give a short introduction to PyTorch basics, and get you setup for writing your own neural networks. +# PyTorch is an open source machine learning framework that allows you to write your own neural networks and optimize them efficiently. +# However, PyTorch is not the only framework of its kind. +# Alternatives to PyTorch include [TensorFlow](https://www.tensorflow.org/), [JAX](https://github.com/google/jax) and [Caffe](http://caffe.berkeleyvision.org/). +# We choose to teach PyTorch at the University of Amsterdam because it is well established, has a huge developer community (originally developed by Facebook), is very flexible and especially used in research. +# Many current papers publish their code in PyTorch, and thus it is good to be familiar with PyTorch as well. +# Meanwhile, TensorFlow (developed by Google) is usually known for being a production-grade deep learning library. +# Still, if you know one machine learning framework in depth, it is very easy to learn another one because many of them use the same concepts and ideas. +# For instance, TensorFlow's version 2 was heavily inspired by the most popular features of PyTorch, making the frameworks even more similar. +# If you are already familiar with PyTorch and have created your own neural network projects, feel free to just skim this notebook. +# +# We are of course not the first ones to create a PyTorch tutorial. +# There are many great tutorials online, including the ["60-min blitz"](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html) on the official [PyTorch website](https://pytorch.org/tutorials/). +# Yet, we choose to create our own tutorial which is designed to give you the basics particularly necessary for the practicals, but still understand how PyTorch works under the hood. +# Over the next few weeks, we will also keep exploring new PyTorch features in the series of Jupyter notebook tutorials about deep learning. +# +# We will use a set of standard libraries that are often used in machine learning projects. +# If you are running this notebook on Google Colab, all libraries should be pre-installed. +# If you are running this notebook locally, make sure you have installed our `dl2020` environment ([link](https://github.com/uvadlc/uvadlc_practicals_2020/blob/master/environment.yml)) and have activated it. + +# %% +import time + +import matplotlib.pyplot as plt + +# %matplotlib inline +import matplotlib_inline.backend_inline +import numpy as np +import torch +import torch.nn as nn +import torch.utils.data as data +from matplotlib.colors import to_rgba +from torch import Tensor +from tqdm.notebook import tqdm # Progress bar + +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export + +# %% [markdown] +# ## The Basics of PyTorch +# +# We will start with reviewing the very basic concepts of PyTorch. +# As a prerequisite, we recommend to be familiar with the `numpy` package as most machine learning frameworks are based on very similar concepts. +# If you are not familiar with numpy yet, don't worry: here is a [tutorial](https://numpy.org/devdocs/user/quickstart.html) to go through. +# +# So, let's start with importing PyTorch. +# The package is called `torch`, based on its original framework [Torch](http://torch.ch/). +# As a first step, we can check its version: + +# %% +print("Using torch", torch.__version__) + +# %% [markdown] +# At the time of writing this tutorial (mid of August 2021), the current stable version is 1.9. +# You should therefore see the output `Using torch 1.9.0`, eventually with some extension for the CUDA version on Colab. +# In case you use the `dl2020` environment, you should see `Using torch 1.6.0` since the environment was provided in October 2020. +# It is recommended to update the PyTorch version to the newest one. +# If you see a lower version number than 1.6, make sure you have installed the correct the environment, or ask one of your TAs. +# In case PyTorch 1.10 or newer will be published during the time of the course, don't worry. +# The interface between PyTorch versions doesn't change too much, and hence all code should also be runnable with newer versions. +# +# As in every machine learning framework, PyTorch provides functions that are stochastic like generating random numbers. +# However, a very good practice is to setup your code to be reproducible with the exact same random numbers. +# This is why we set a seed below. + +# %% +torch.manual_seed(42) # Setting the seed + +# %% [markdown] +# ### Tensors +# +# Tensors are the PyTorch equivalent to Numpy arrays, with the addition to also have support for GPU acceleration (more on that later). +# The name "tensor" is a generalization of concepts you already know. +# For instance, a vector is a 1-D tensor, and a matrix a 2-D tensor. +# When working with neural networks, we will use tensors of various shapes and number of dimensions. +# +# Most common functions you know from numpy can be used on tensors as well. +# Actually, since numpy arrays are so similar to tensors, we can convert most tensors to numpy arrays (and back) but we don't need it too often. +# +# #### Initialization +# +# Let's first start by looking at different ways of creating a tensor. +# There are many possible options, the most simple one is to call +# `Tensor` passing the desired shape as input argument: + +# %% +x = Tensor(2, 3, 4) +print(x) + +# %% [markdown] +# The function `torch.Tensor` allocates memory for the desired tensor, but reuses any values that have already been in the memory. +# To directly assign values to the tensor during initialization, there are many alternatives including: +# +# * `torch.zeros`: Creates a tensor filled with zeros +# * `torch.ones`: Creates a tensor filled with ones +# * `torch.rand`: Creates a tensor with random values uniformly sampled between 0 and 1 +# * `torch.randn`: Creates a tensor with random values sampled from a normal distribution with mean 0 and variance 1 +# * `torch.arange`: Creates a tensor containing the values $N,N+1,N+2,...,M$ +# * `torch.Tensor` (input list): Creates a tensor from the list elements you provide + +# %% +# Create a tensor from a (nested) list +x = Tensor([[1, 2], [3, 4]]) +print(x) + +# %% +# Create a tensor with random values between 0 and 1 with the shape [2, 3, 4] +x = torch.rand(2, 3, 4) +print(x) + +# %% [markdown] +# You can obtain the shape of a tensor in the same way as in numpy (`x.shape`), or using the `.size` method: + +# %% +shape = x.shape +print("Shape:", x.shape) + +size = x.size() +print("Size:", size) + +dim1, dim2, dim3 = x.size() +print("Size:", dim1, dim2, dim3) + +# %% [markdown] +# #### Tensor to Numpy, and Numpy to Tensor +# +# Tensors can be converted to numpy arrays, and numpy arrays back to tensors. +# To transform a numpy array into a tensor, we can use the function `torch.from_numpy`: + +# %% +np_arr = np.array([[1, 2], [3, 4]]) +tensor = torch.from_numpy(np_arr) + +print("Numpy array:", np_arr) +print("PyTorch tensor:", tensor) + +# %% [markdown] +# To transform a PyTorch tensor back to a numpy array, we can use the function `.numpy()` on tensors: + +# %% +tensor = torch.arange(4) +np_arr = tensor.numpy() + +print("PyTorch tensor:", tensor) +print("Numpy array:", np_arr) + +# %% [markdown] +# The conversion of tensors to numpy require the tensor to be on the CPU, and not the GPU (more on GPU support in a later section). +# In case you have a tensor on GPU, you need to call `.cpu()` on the tensor beforehand. +# Hence, you get a line like `np_arr = tensor.cpu().numpy()`. + +# %% [markdown] +# #### Operations +# +# Most operations that exist in numpy, also exist in PyTorch. +# A full list of operations can be found in the [PyTorch documentation](https://pytorch.org/docs/stable/tensors.html#), but we will review the most important ones here. +# +# The simplest operation is to add two tensors: + +# %% +x1 = torch.rand(2, 3) +x2 = torch.rand(2, 3) +y = x1 + x2 + +print("X1", x1) +print("X2", x2) +print("Y", y) + +# %% [markdown] +# Calling `x1 + x2` creates a new tensor containing the sum of the two inputs. +# However, we can also use in-place operations that are applied directly on the memory of a tensor. +# We therefore change the values of `x2` without the chance to re-accessing the values of `x2` before the operation. +# An example is shown below: + +# %% +x1 = torch.rand(2, 3) +x2 = torch.rand(2, 3) +print("X1 (before)", x1) +print("X2 (before)", x2) + +x2.add_(x1) +print("X1 (after)", x1) +print("X2 (after)", x2) + +# %% [markdown] +# In-place operations are usually marked with a underscore postfix (for example `torch.add_` instead of `torch.add`). +# +# Another common operation aims at changing the shape of a tensor. +# A tensor of size (2,3) can be re-organized to any other shape with the same number of elements (e.g. a tensor of size (6), or (3,2), ...). +# In PyTorch, this operation is called `view`: + +# %% +x = torch.arange(6) +print("X", x) + +# %% +x = x.view(2, 3) +print("X", x) + +# %% +x = x.permute(1, 0) # Swapping dimension 0 and 1 +print("X", x) + +# %% [markdown] +# Other commonly used operations include matrix multiplications, which are essential for neural networks. +# Quite often, we have an input vector $\mathbf{x}$, which is transformed using a learned weight matrix $\mathbf{W}$. +# There are multiple ways and functions to perform matrix multiplication, some of which we list below: +# +# * `torch.matmul`: Performs the matrix product over two tensors, where the specific behavior depends on the dimensions. +# If both inputs are matrices (2-dimensional tensors), it performs the standard matrix product. +# For higher dimensional inputs, the function supports broadcasting (for details see the [documentation](https://pytorch.org/docs/stable/generated/torch.matmul.html?highlight=matmul#torch.matmul)). +# Can also be written as `a @ b`, similar to numpy. +# * `torch.mm`: Performs the matrix product over two matrices, but doesn't support broadcasting (see [documentation](https://pytorch.org/docs/stable/generated/torch.mm.html?highlight=torch%20mm#torch.mm)) +# * `torch.bmm`: Performs the matrix product with a support batch dimension. +# If the first tensor $T$ is of shape ($b\times n\times m$), and the second tensor $R$ ($b\times m\times p$), the output $O$ is of shape ($b\times n\times p$), and has been calculated by performing $b$ matrix multiplications of the submatrices of $T$ and $R$: $O_i = T_i @ R_i$ +# * `torch.einsum`: Performs matrix multiplications and more (i.e. sums of products) using the Einstein summation convention. +# Explanation of the Einstein sum can be found in assignment 1. +# +# Usually, we use `torch.matmul` or `torch.bmm`. We can try a matrix multiplication with `torch.matmul` below. + +# %% +x = torch.arange(6) +x = x.view(2, 3) +print("X", x) + +# %% +W = torch.arange(9).view(3, 3) # We can also stack multiple operations in a single line +print("W", W) + +# %% +h = torch.matmul(x, W) # Verify the result by calculating it by hand too! +print("h", h) + +# %% [markdown] +# #### Indexing +# +# We often have the situation where we need to select a part of a tensor. +# Indexing works just like in numpy, so let's try it: + +# %% +x = torch.arange(12).view(3, 4) +print("X", x) + +# %% +print(x[:, 1]) # Second column + +# %% +print(x[0]) # First row + +# %% +print(x[:2, -1]) # First two rows, last column + +# %% +print(x[1:3, :]) # Middle two rows + +# %% [markdown] +# ### Dynamic Computation Graph and Backpropagation +# +# One of the main reasons for using PyTorch in Deep Learning projects is that we can automatically get **gradients/derivatives** of functions that we define. +# We will mainly use PyTorch for implementing neural networks, and they are just fancy functions. +# If we use weight matrices in our function that we want to learn, then those are called the **parameters** or simply the **weights**. +# +# If our neural network would output a single scalar value, we would talk about taking the **derivative**, but you will see that quite often we will have **multiple** output variables ("values"); in that case we talk about **gradients**. +# It's a more general term. +# +# Given an input $\mathbf{x}$, we define our function by **manipulating** that input, usually by matrix-multiplications with weight matrices and additions with so-called bias vectors. +# As we manipulate our input, we are automatically creating a **computational graph**. +# This graph shows how to arrive at our output from our input. +# PyTorch is a **define-by-run** framework; this means that we can just do our manipulations, and PyTorch will keep track of that graph for us. +# Thus, we create a dynamic computation graph along the way. +# +# So, to recap: the only thing we have to do is to compute the **output**, and then we can ask PyTorch to automatically get the **gradients**. +# +# > **Note: Why do we want gradients? +# ** Consider that we have defined a function, a neural net, that is supposed to compute a certain output $y$ for an input vector $\mathbf{x}$. +# We then define an **error measure** that tells us how wrong our network is; how bad it is in predicting output $y$ from input $\mathbf{x}$. +# Based on this error measure, we can use the gradients to **update** the weights $\mathbf{W}$ that were responsible for the output, so that the next time we present input $\mathbf{x}$ to our network, the output will be closer to what we want. +# +# The first thing we have to do is to specify which tensors require gradients. +# By default, when we create a tensor, it does not require gradients. + +# %% +x = torch.ones((3,)) +print(x.requires_grad) + +# %% [markdown] +# We can change this for an existing tensor using the function `requires_grad_()` (underscore indicating that this is a in-place operation). +# Alternatively, when creating a tensor, you can pass the argument +# `requires_grad=True` to most initializers we have seen above. + +# %% +x.requires_grad_(True) +print(x.requires_grad) + +# %% [markdown] +# In order to get familiar with the concept of a computation graph, we will create one for the following function: +# +# $$y = \frac{1}{|x|}\sum_i \left[(x_i + 2)^2 + 3\right]$$ +# +# You could imagine that $x$ are our parameters, and we want to optimize (either maximize or minimize) the output $y$. +# For this, we want to obtain the gradients $\partial y / \partial \mathbf{x}$. +# For our example, we'll use $\mathbf{x}=[0,1,2]$ as our input. + +# %% +x = torch.arange(3, dtype=torch.float32, requires_grad=True) # Only float tensors can have gradients +print("X", x) + +# %% [markdown] +# Now let's build the computation graph step by step. +# You can combine multiple operations in a single line, but we will +# separate them here to get a better understanding of how each operation +# is added to the computation graph. + +# %% +a = x + 2 +b = a**2 +c = b + 3 +y = c.mean() +print("Y", y) + +# %% [markdown] +# Using the statements above, we have created a computation graph that looks similar to the figure below: +# +#
+# +# We calculate $a$ based on the inputs $x$ and the constant $2$, $b$ is $a$ squared, and so on. +# The visualization is an abstraction of the dependencies between inputs and outputs of the operations we have applied. +# Each node of the computation graph has automatically defined a function for calculating the gradients with respect to its inputs, `grad_fn`. +# You can see this when we printed the output tensor $y$. +# This is why the computation graph is usually visualized in the reverse direction (arrows point from the result to the inputs). +# We can perform backpropagation on the computation graph by calling the +# function `backward()` on the last output, which effectively calculates +# the gradients for each tensor that has the property +# `requires_grad=True`: + +# %% +y.backward() + +# %% [markdown] +# `x.grad` will now contain the gradient $\partial y/ \partial \mathcal{x}$, and this gradient indicates how a change in $\mathbf{x}$ will affect output $y$ given the current input $\mathbf{x}=[0,1,2]$: + +# %% +print(x.grad) + +# %% [markdown] +# We can also verify these gradients by hand. +# We will calculate the gradients using the chain rule, in the same way as PyTorch did it: +# +# $$\frac{\partial y}{\partial x_i} = \frac{\partial y}{\partial c_i}\frac{\partial c_i}{\partial b_i}\frac{\partial b_i}{\partial a_i}\frac{\partial a_i}{\partial x_i}$$ +# +# Note that we have simplified this equation to index notation, and by using the fact that all operation besides the mean do not combine the elements in the tensor. +# The partial derivatives are: +# +# $$ +# \frac{\partial a_i}{\partial x_i} = 1,\hspace{1cm} +# \frac{\partial b_i}{\partial a_i} = 2\cdot a_i\hspace{1cm} +# \frac{\partial c_i}{\partial b_i} = 1\hspace{1cm} +# \frac{\partial y}{\partial c_i} = \frac{1}{3} +# $$ +# +# Hence, with the input being $\mathbf{x}=[0,1,2]$, our gradients are $\partial y/\partial \mathbf{x}=[4/3,2,8/3]$. +# The previous code cell should have printed the same result. + +# %% [markdown] +# ### GPU support +# +# A crucial feature of PyTorch is the support of GPUs, short for Graphics Processing Unit. +# A GPU can perform many thousands of small operations in parallel, making it very well suitable for performing large matrix operations in neural networks. +# When comparing GPUs to CPUs, we can list the following main differences (credit: [Kevin Krewell, 2009](https://blogs.nvidia.com/blog/2009/12/16/whats-the-difference-between-a-cpu-and-a-gpu/)) +# +#
+# +# CPUs and GPUs have both different advantages and disadvantages, which is why many computers contain both components and use them for different tasks. +# In case you are not familiar with GPUs, you can read up more details in this [NVIDIA blog post](https://blogs.nvidia.com/blog/2009/12/16/whats-the-difference-between-a-cpu-and-a-gpu/) or [here](https://www.intel.com/content/www/us/en/products/docs/processors/what-is-a-gpu.html). +# +# GPUs can accelerate the training of your network up to a factor of $100$ which is essential for large neural networks. +# PyTorch implements a lot of functionality for supporting GPUs (mostly those of NVIDIA due to the libraries [CUDA](https://developer.nvidia.com/cuda-zone) and [cuDNN](https://developer.nvidia.com/cudnn)). +# First, let's check whether you have a GPU available: + +# %% +gpu_avail = torch.cuda.is_available() +print(f"Is the GPU available? {gpu_avail}") + +# %% [markdown] +# If you have a GPU on your computer but the command above returns False, make sure you have the correct CUDA-version installed. +# The `dl2020` environment comes with the CUDA-toolkit 10.1, which is selected for the Lisa supercomputer. +# Please change it if necessary (CUDA 10.2 is currently common). +# On Google Colab, make sure that you have selected a GPU in your runtime setup (in the menu, check under `Runtime -> Change runtime type`). +# +# By default, all tensors you create are stored on the CPU. +# We can push a tensor to the GPU by using the function `.to(...)`, or `.cuda()`. +# However, it is often a good practice to define a `device` object in your code which points to the GPU if you have one, and otherwise to the CPU. +# Then, you can write your code with respect to this device object, and it allows you to run the same code on both a CPU-only system, and one with a GPU. +# Let's try it below. +# We can specify the device as follows: + +# %% +device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") +print("Device", device) + +# %% [markdown] +# Now let's create a tensor and push it to the device: + +# %% +x = torch.zeros(2, 3) +x = x.to(device) +print("X", x) + +# %% [markdown] +# In case you have a GPU, you should now see the attribute `device='cuda:0'` being printed next to your tensor. +# The zero next to cuda indicates that this is the zero-th GPU device on your computer. +# PyTorch also supports multi-GPU systems, but this you will only need once you have very big networks to train (if interested, see the [PyTorch documentation](https://pytorch.org/docs/stable/distributed.html#distributed-basics)). +# We can also compare the runtime of a large matrix multiplication on the CPU with a operation on the GPU: + +# %% +x = torch.randn(5000, 5000) + +# CPU version +start_time = time.time() +_ = torch.matmul(x, x) +end_time = time.time() +print(f"CPU time: {(end_time - start_time):6.5f}s") + +# GPU version +if torch.cuda.is_available(): + x = x.to(device) + # CUDA is asynchronous, so we need to use different timing functions + start = torch.cuda.Event(enable_timing=True) + end = torch.cuda.Event(enable_timing=True) + start.record() + _ = torch.matmul(x, x) + end.record() + torch.cuda.synchronize() # Waits for everything to finish running on the GPU + print(f"GPU time: {0.001 * start.elapsed_time(end):6.5f}s") # Milliseconds to seconds + +# %% [markdown] +# Depending on the size of the operation and the CPU/GPU in your system, the speedup of this operation can be >50x. +# As `matmul` operations are very common in neural networks, we can already see the great benefit of training a NN on a GPU. +# The time estimate can be relatively noisy here because we haven't run it for multiple times. +# Feel free to extend this, but it also takes longer to run. +# +# When generating random numbers, the seed between CPU and GPU is not synchronized. +# Hence, we need to set the seed on the GPU separately to ensure a reproducible code. +# Note that due to different GPU architectures, running the same code on different GPUs does not guarantee the same random numbers. +# Still, we don't want that our code gives us a different output every time we run it on the exact same hardware. +# Hence, we also set the seed on the GPU: + +# %% +# GPU operations have a separate seed we also want to set +if torch.cuda.is_available(): + torch.cuda.manual_seed(42) + torch.cuda.manual_seed_all(42) + +# Additionally, some operations on a GPU are implemented stochastic for efficiency +# We want to ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +# %% [markdown] +# ## Learning by example: Continuous XOR +#
+# +# If we want to build a neural network in PyTorch, we could specify all our parameters (weight matrices, bias vectors) using `Tensors` (with `requires_grad=True`), ask PyTorch to calculate the gradients and then adjust the parameters. +# But things can quickly get cumbersome if we have a lot of parameters. +# In PyTorch, there is a package called `torch.nn` that makes building neural networks more convenient. +# +# We will introduce the libraries and all additional parts you might need to train a neural network in PyTorch, using a simple example classifier on a simple yet well known example: XOR. +# Given two binary inputs $x_1$ and $x_2$, the label to predict is $1$ if either $x_1$ or $x_2$ is $1$ while the other is $0$, or the label is $0$ in all other cases. +# The example became famous by the fact that a single neuron, i.e. a linear classifier, cannot learn this simple function. +# Hence, we will learn how to build a small neural network that can learn this function. +# To make it a little bit more interesting, we move the XOR into continuous space and introduce some gaussian noise on the binary inputs. +# Our desired separation of an XOR dataset could look as follows: +# +#
+ +# %% [markdown] +# ### The model +# +# The package `torch.nn` defines a series of useful classes like linear networks layers, activation functions, loss functions etc. +# A full list can be found [here](https://pytorch.org/docs/stable/nn.html). +# In case you need a certain network layer, check the documentation of the package first before writing the layer yourself as the package likely contains the code for it already. +# We import it below: + +# %% +# %% + +# %% [markdown] +# Additionally to `torch.nn`, there is also `torch.nn.functional`. +# It contains functions that are used in network layers. +# This is in contrast to `torch.nn` which defines them as `nn.Modules` (more on it below), and `torch.nn` actually uses a lot of functionalities from `torch.nn.functional`. +# Hence, the functional package is useful in many situations, and so we import it as well here. + +# %% [markdown] +# #### nn.Module +# +# In PyTorch, a neural network is built up out of modules. +# Modules can contain other modules, and a neural network is considered to be a module itself as well. +# The basic template of a module is as follows: + + +# %% +class MyModule(nn.Module): + def __init__(self): + super().__init__() + # Some init for my module + + def forward(self, x): + # Function for performing the calculation of the module. + pass + + +# %% [markdown] +# The forward function is where the computation of the module is taken place, and is executed when you call the module (`nn = MyModule(); nn(x)`). +# In the init function, we usually create the parameters of the module, using `nn.Parameter`, or defining other modules that are used in the forward function. +# The backward calculation is done automatically, but could be overwritten as well if wanted. +# +# #### Simple classifier +# We can now make use of the pre-defined modules in the `torch.nn` package, and define our own small neural network. +# We will use a minimal network with a input layer, one hidden layer with tanh as activation function, and a output layer. +# In other words, our networks should look something like this: +# +#
+# +# The input neurons are shown in blue, which represent the coordinates $x_1$ and $x_2$ of a data point. +# The hidden neurons including a tanh activation are shown in white, and the output neuron in red. +# In PyTorch, we can define this as follows: + + +# %% +class SimpleClassifier(nn.Module): + def __init__(self, num_inputs, num_hidden, num_outputs): + super().__init__() + # Initialize the modules we need to build the network + self.linear1 = nn.Linear(num_inputs, num_hidden) + self.act_fn = nn.Tanh() + self.linear2 = nn.Linear(num_hidden, num_outputs) + + def forward(self, x): + # Perform the calculation of the model to determine the prediction + x = self.linear1(x) + x = self.act_fn(x) + x = self.linear2(x) + return x + + +# %% [markdown] +# For the examples in this notebook, we will use a tiny neural network with two input neurons and four hidden neurons. +# As we perform binary classification, we will use a single output neuron. +# Note that we do not apply a sigmoid on the output yet. +# This is because other functions, especially the loss, are more efficient and precise to calculate on the original outputs instead of the sigmoid output. +# We will discuss the detailed reason later. + +# %% +model = SimpleClassifier(num_inputs=2, num_hidden=4, num_outputs=1) +# Printing a module shows all its submodules +print(model) + +# %% [markdown] +# Printing the model lists all submodules it contains. +# The parameters of a module can be obtained by using its `parameters()` functions, or `named_parameters()` to get a name to each parameter object. +# For our small neural network, we have the following parameters: + +# %% +for name, param in model.named_parameters(): + print(f"Parameter {name}, shape {param.shape}") + +# %% [markdown] +# Each linear layer has a weight matrix of the shape `[output, input]`, and a bias of the shape `[output]`. +# The tanh activation function does not have any parameters. +# Note that parameters are only registered for `nn.Module` objects that are direct object attributes, i.e. `self.a = ...`. +# If you define a list of modules, the parameters of those are not registered for the outer module and can cause some issues when you try to optimize your module. +# There are alternatives, like `nn.ModuleList`, `nn.ModuleDict` and `nn.Sequential`, that allow you to have different data structures of modules. +# We will use them in a few later tutorials and explain them there. + +# %% [markdown] +# ### The data +# +# PyTorch also provides a few functionalities to load the training and +# test data efficiently, summarized in the package `torch.utils.data`. + +# %% + +# %% [markdown] +# The data package defines two classes which are the standard interface for handling data in PyTorch: `data.Dataset`, and `data.DataLoader`. +# The dataset class provides an uniform interface to access the +# training/test data, while the data loader makes sure to efficiently load +# and stack the data points from the dataset into batches during training. + +# %% [markdown] +# #### The dataset class +# +# The dataset class summarizes the basic functionality of a dataset in a natural way. +# To define a dataset in PyTorch, we simply specify two functions: `__getitem__`, and `__len__`. +# The get-item function has to return the $i$-th data point in the dataset, while the len function returns the size of the dataset. +# For the XOR dataset, we can define the dataset class as follows: + +# %% + + +class XORDataset(data.Dataset): + def __init__(self, size, std=0.1): + """ + Inputs: + size - Number of data points we want to generate + std - Standard deviation of the noise (see generate_continuous_xor function) + """ + super().__init__() + self.size = size + self.std = std + self.generate_continuous_xor() + + def generate_continuous_xor(self): + # Each data point in the XOR dataset has two variables, x and y, that can be either 0 or 1 + # The label is their XOR combination, i.e. 1 if only x or only y is 1 while the other is 0. + # If x=y, the label is 0. + data = torch.randint(low=0, high=2, size=(self.size, 2), dtype=torch.float32) + label = (data.sum(dim=1) == 1).to(torch.long) + # To make it slightly more challenging, we add a bit of gaussian noise to the data points. + data += self.std * torch.randn(data.shape) + + self.data = data + self.label = label + + def __len__(self): + # Number of data point we have. Alternatively self.data.shape[0], or self.label.shape[0] + return self.size + + def __getitem__(self, idx): + # Return the idx-th data point of the dataset + # If we have multiple things to return (data point and label), we can return them as tuple + data_point = self.data[idx] + data_label = self.label[idx] + return data_point, data_label + + +# %% [markdown] +# Let's try to create such a dataset and inspect it: + +# %% +dataset = XORDataset(size=200) +print("Size of dataset:", len(dataset)) +print("Data point 0:", dataset[0]) + +# %% [markdown] +# To better relate to the dataset, we visualize the samples below. + + +# %% +def visualize_samples(data, label): + if isinstance(data, Tensor): + data = data.cpu().numpy() + if isinstance(label, Tensor): + label = label.cpu().numpy() + data_0 = data[label == 0] + data_1 = data[label == 1] + + plt.figure(figsize=(4, 4)) + plt.scatter(data_0[:, 0], data_0[:, 1], edgecolor="#333", label="Class 0") + plt.scatter(data_1[:, 0], data_1[:, 1], edgecolor="#333", label="Class 1") + plt.title("Dataset samples") + plt.ylabel(r"$x_2$") + plt.xlabel(r"$x_1$") + plt.legend() + + +# %% +visualize_samples(dataset.data, dataset.label) +plt.show() + +# %% [markdown] +# #### The data loader class +# +# The class `torch.utils.data.DataLoader` represents a Python iterable over a dataset with support for automatic batching, multi-process data loading and many more features. +# The data loader communicates with the dataset using the function `__getitem__`, and stacks its outputs as tensors over the first dimension to form a batch. +# In contrast to the dataset class, we usually don't have to define our own data loader class, but can just create an object of it with the dataset as input. +# Additionally, we can configure our data loader with the following input arguments (only a selection, see full list [here](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader)): +# +# * `batch_size`: Number of samples to stack per batch +# * `shuffle`: If True, the data is returned in a random order. +# This is important during training for introducing stochasticity. +# * `num_workers`: Number of subprocesses to use for data loading. +# The default, 0, means that the data will be loaded in the main process which can slow down training for datasets where loading a data point takes a considerable amount of time (e.g. large images). +# More workers are recommended for those, but can cause issues on Windows computers. +# For tiny datasets as ours, 0 workers are usually faster. +# * `pin_memory`: If True, the data loader will copy Tensors into CUDA pinned memory before returning them. +# This can save some time for large data points on GPUs. +# Usually a good practice to use for a training set, but not necessarily for validation and test to save memory on the GPU. +# * `drop_last`: If True, the last batch is dropped in case it is smaller than the specified batch size. +# This occurs when the dataset size is not a multiple of the batch size. +# Only potentially helpful during training to keep a consistent batch size. +# +# Let's create a simple data loader below: + +# %% +data_loader = data.DataLoader(dataset, batch_size=8, shuffle=True) + +# %% +# next(iter(...)) catches the first batch of the data loader +# If shuffle is True, this will return a different batch every time we run this cell +# For iterating over the whole dataset, we can simple use "for batch in data_loader: ..." +data_inputs, data_labels = next(iter(data_loader)) + +# The shape of the outputs are [batch_size, d_1,...,d_N] where d_1,...,d_N are the +# dimensions of the data point returned from the dataset class +print("Data inputs", data_inputs.shape, "\n", data_inputs) +print("Data labels", data_labels.shape, "\n", data_labels) + +# %% [markdown] +# ### Optimization +# +# After defining the model and the dataset, it is time to prepare the optimization of the model. +# During training, we will perform the following steps: +# +# 1. Get a batch from the data loader +# 2. Obtain the predictions from the model for the batch +# 3. Calculate the loss based on the difference between predictions and labels +# 4. Backpropagation: calculate the gradients for every parameter with respect to the loss +# 5. Update the parameters of the model in the direction of the gradients +# +# We have seen how we can do step 1, 2 and 4 in PyTorch. Now, we will look at step 3 and 5. + +# %% [markdown] +# #### Loss modules +# +# We can calculate the loss for a batch by simply performing a few tensor operations as those are automatically added to the computation graph. +# For instance, for binary classification, we can use Binary Cross Entropy (BCE) which is defined as follows: +# +# $$\mathcal{L}_{BCE} = -\sum_i \left[ y_i \log x_i + (1 - y_i) \log (1 - x_i) \right]$$ +# +# where $y$ are our labels, and $x$ our predictions, both in the range of $[0,1]$. +# However, PyTorch already provides a list of predefined loss functions which we can use (see [here](https://pytorch.org/docs/stable/nn.html#loss-functions) for a full list). +# For instance, for BCE, PyTorch has two modules: `nn.BCELoss()`, `nn.BCEWithLogitsLoss()`. +# While `nn.BCELoss` expects the inputs $x$ to be in the range $[0,1]$, i.e. the output of a sigmoid, `nn.BCEWithLogitsLoss` combines a sigmoid layer and the BCE loss in a single class. +# This version is numerically more stable than using a plain Sigmoid followed by a BCE loss because of the logarithms applied in the loss function. +# Hence, it is adviced to use loss functions applied on "logits" where possible (remember to not apply a sigmoid on the output of the model in this case!). +# For our model defined above, we therefore use the module `nn.BCEWithLogitsLoss`. + +# %% +loss_module = nn.BCEWithLogitsLoss() + +# %% [markdown] +# #### Stochastic Gradient Descent +# +# For updating the parameters, PyTorch provides the package `torch.optim` that has most popular optimizers implemented. +# We will discuss the specific optimizers and their differences later in the course, but will for now use the simplest of them: `torch.optim.SGD`. +# Stochastic Gradient Descent updates parameters by multiplying the gradients with a small constant, called learning rate, and subtracting those from the parameters (hence minimizing the loss). +# Therefore, we slowly move towards the direction of minimizing the loss. +# A good default value of the learning rate for a small network as ours is 0.1. + +# %% +# Input to the optimizer are the parameters of the model: model.parameters() +optimizer = torch.optim.SGD(model.parameters(), lr=0.1) + +# %% [markdown] +# The optimizer provides two useful functions: `optimizer.step()`, and `optimizer.zero_grad()`. +# The step function updates the parameters based on the gradients as explained above. +# The function `optimizer.zero_grad()` sets the gradients of all parameters to zero. +# While this function seems less relevant at first, it is a crucial pre-step before performing backpropagation. +# If we would call the `backward` function on the loss while the parameter gradients are non-zero from the previous batch, the new gradients would actually be added to the previous ones instead of overwriting them. +# This is done because a parameter might occur multiple times in a computation graph, and we need to sum the gradients in this case instead of replacing them. +# Hence, remember to call `optimizer.zero_grad()` before calculating the gradients of a batch. + +# %% [markdown] +# ### Training +# +# Finally, we are ready to train our model. +# As a first step, we create a slightly larger dataset and specify a data loader with a larger batch size. + +# %% +train_dataset = XORDataset(size=1000) +train_data_loader = data.DataLoader(train_dataset, batch_size=128, shuffle=True) + +# %% [markdown] +# Now, we can write a small training function. +# Remember our five steps: load a batch, obtain the predictions, calculate the loss, backpropagate, and update. +# Additionally, we have to push all data and model parameters to the device of our choice (GPU if available). +# For the tiny neural network we have, communicating the data to the GPU actually takes much more time than we could save from running the operation on GPU. +# For large networks, the communication time is significantly smaller than the actual runtime making a GPU crucial in these cases. +# Still, to practice, we will push the data to GPU here. + +# %% +# Push model to device. Has to be only done once +model.to(device) + +# %% [markdown] +# In addition, we set our model to training mode. +# This is done by calling `model.train()`. +# There exist certain modules that need to perform a different forward +# step during training than during testing (e.g. BatchNorm and Dropout), +# and we can switch between them using `model.train()` and `model.eval()`. + + +# %% +def train_model(model, optimizer, data_loader, loss_module, num_epochs=100): + # Set model to train mode + model.train() + + # Training loop + for epoch in tqdm(range(num_epochs)): + for data_inputs, data_labels in data_loader: + + # Step 1: Move input data to device (only strictly necessary if we use GPU) + data_inputs = data_inputs.to(device) + data_labels = data_labels.to(device) + + # Step 2: Run the model on the input data + preds = model(data_inputs) + preds = preds.squeeze(dim=1) # Output is [Batch size, 1], but we want [Batch size] + + # Step 3: Calculate the loss + loss = loss_module(preds, data_labels.float()) + + # Step 4: Perform backpropagation + # Before calculating the gradients, we need to ensure that they are all zero. + # The gradients would not be overwritten, but actually added to the existing ones. + optimizer.zero_grad() + # Perform backpropagation + loss.backward() + + # Step 5: Update the parameters + optimizer.step() + + +# %% +train_model(model, optimizer, train_data_loader, loss_module) + +# %% [markdown] +# #### Saving a model +# +# After finish training a model, we save the model to disk so that we can load the same weights at a later time. +# For this, we extract the so-called `state_dict` from the model which contains all learnable parameters. +# For our simple model, the state dict contains the following entries: + +# %% +state_dict = model.state_dict() +print(state_dict) + +# %% [markdown] +# To save the state dictionary, we can use `torch.save`: + +# %% +# torch.save(object, filename). For the filename, any extension can be used +torch.save(state_dict, "our_model.tar") + +# %% [markdown] +# To load a model from a state dict, we use the function `torch.load` to +# load the state dict from the disk, and the module function +# `load_state_dict` to overwrite our parameters with the new values: + +# %% +# Load state dict from the disk (make sure it is the same name as above) +state_dict = torch.load("our_model.tar") + +# Create a new model and load the state +new_model = SimpleClassifier(num_inputs=2, num_hidden=4, num_outputs=1) +new_model.load_state_dict(state_dict) + +# Verify that the parameters are the same +print("Original model\n", model.state_dict()) +print("\nLoaded model\n", new_model.state_dict()) + +# %% [markdown] +# A detailed tutorial on saving and loading models in PyTorch can be found +# [here](https://pytorch.org/tutorials/beginner/saving_loading_models.html). + +# %% [markdown] +# ### Evaluation +# +# Once we have trained a model, it is time to evaluate it on a held-out test set. +# As our dataset consist of randomly generated data points, we need to +# first create a test set with a corresponding data loader. + +# %% +test_dataset = XORDataset(size=500) +# drop_last -> Don't drop the last batch although it is smaller than 128 +test_data_loader = data.DataLoader(test_dataset, batch_size=128, shuffle=False, drop_last=False) + +# %% [markdown] +# As metric, we will use accuracy which is calculated as follows: +# +# $$acc = \frac{\#\text{correct predictions}}{\#\text{all predictions}} = \frac{TP+TN}{TP+TN+FP+FN}$$ +# +# where TP are the true positives, TN true negatives, FP false positives, and FN the fale negatives. +# +# When evaluating the model, we don't need to keep track of the computation graph as we don't intend to calculate the gradients. +# This reduces the required memory and speed up the model. +# In PyTorch, we can deactivate the computation graph using `with torch.no_grad(): ...`. +# Remember to additionally set the model to eval mode. + + +# %% +def eval_model(model, data_loader): + model.eval() # Set model to eval mode + true_preds, num_preds = 0.0, 0.0 + + with torch.no_grad(): # Deactivate gradients for the following code + for data_inputs, data_labels in data_loader: + + # Determine prediction of model on dev set + data_inputs, data_labels = data_inputs.to(device), data_labels.to(device) + preds = model(data_inputs) + preds = preds.squeeze(dim=1) + preds = torch.sigmoid(preds) # Sigmoid to map predictions between 0 and 1 + pred_labels = (preds >= 0.5).long() # Binarize predictions to 0 and 1 + + # Keep records of predictions for the accuracy metric (true_preds=TP+TN, num_preds=TP+TN+FP+FN) + true_preds += (pred_labels == data_labels).sum() + num_preds += data_labels.shape[0] + + acc = true_preds / num_preds + print(f"Accuracy of the model: {100.0*acc:4.2f}%") + + +# %% +eval_model(model, test_data_loader) + +# %% [markdown] +# If we trained our model correctly, we should see a score close to 100% accuracy. +# However, this is only possible because of our simple task, and +# unfortunately, we usually don't get such high scores on test sets of +# more complex tasks. + +# %% [markdown] +# #### Visualizing classification boundaries +# +# To visualize what our model has learned, we can perform a prediction for every data point in a range of $[-0.5, 1.5]$, and visualize the predicted class as in the sample figure at the beginning of this section. +# This shows where the model has created decision boundaries, and which points would be classified as $0$, and which as $1$. +# We therefore get a background image out of blue (class 0) and orange (class 1). +# The spots where the model is uncertain we will see a blurry overlap. +# The specific code is less relevant compared to the output figure which +# should hopefully show us a clear separation of classes: + + +# %% +@torch.no_grad() # Decorator, same effect as "with torch.no_grad(): ..." over the whole function. +def visualize_classification(model, data, label): + if isinstance(data, Tensor): + data = data.cpu().numpy() + if isinstance(label, Tensor): + label = label.cpu().numpy() + data_0 = data[label == 0] + data_1 = data[label == 1] + + plt.figure(figsize=(4, 4)) + plt.scatter(data_0[:, 0], data_0[:, 1], edgecolor="#333", label="Class 0") + plt.scatter(data_1[:, 0], data_1[:, 1], edgecolor="#333", label="Class 1") + plt.title("Dataset samples") + plt.ylabel(r"$x_2$") + plt.xlabel(r"$x_1$") + plt.legend() + + # Let's make use of a lot of operations we have learned above + model.to(device) + c0 = Tensor(to_rgba("C0")).to(device) + c1 = Tensor(to_rgba("C1")).to(device) + x1 = torch.arange(-0.5, 1.5, step=0.01, device=device) + x2 = torch.arange(-0.5, 1.5, step=0.01, device=device) + xx1, xx2 = torch.meshgrid(x1, x2) # Meshgrid function as in numpy + model_inputs = torch.stack([xx1, xx2], dim=-1) + preds = model(model_inputs) + preds = torch.sigmoid(preds) + # Specifying "None" in a dimension creates a new one + output_image = (1 - preds) * c0[None, None] + preds * c1[None, None] + output_image = ( + output_image.cpu().numpy() + ) # Convert to numpy array. This only works for tensors on CPU, hence first push to CPU + plt.imshow(output_image, origin="lower", extent=(-0.5, 1.5, -0.5, 1.5)) + plt.grid(False) + + +visualize_classification(model, dataset.data, dataset.label) +plt.show() + +# %% [markdown] +# The decision boundaries might not look exactly as in the figure in the preamble of this section which can be caused by running it on CPU or a different GPU architecture. +# Nevertheless, the result on the accuracy metric should be the approximately the same. + +# %% [markdown] +# ## Additional features we didn't get to discuss yet +# +# Finally, you are all set to start with your own PyTorch project! +# In summary, we have looked at how we can build neural networks in PyTorch, and train and test them on data. +# However, there is still much more to PyTorch we haven't discussed yet. +# In the comming series of Jupyter notebooks, we will discover more and more functionalities of PyTorch, so that you also get familiar to PyTorch concepts beyond the basics. +# If you are already interested in learning more of PyTorch, we recommend the official [tutorial website](https://pytorch.org/tutorials/) that contains many tutorials on various topics. +# Especially logging with Tensorboard ([tutorial +# here](https://pytorch.org/tutorials/intermediate/tensorboard_tutorial.html)) +# is a good practice that we will explore from Tutorial 5 on. diff --git a/_notebooks/course_UvA-DL/01-introduction-to-pytorch/comparison_CPU_GPU.png b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/comparison_CPU_GPU.png new file mode 100644 index 0000000..b7d94f9 Binary files /dev/null and b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/comparison_CPU_GPU.png differ diff --git a/_notebooks/course_UvA-DL/01-introduction-to-pytorch/continuous_xor.svg b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/continuous_xor.svg new file mode 100644 index 0000000..12bfd7f --- /dev/null +++ b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/continuous_xor.svg @@ -0,0 +1 @@ + diff --git a/_notebooks/course_UvA-DL/01-introduction-to-pytorch/pytorch_computation_graph.svg b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/pytorch_computation_graph.svg new file mode 100644 index 0000000..912788a --- /dev/null +++ b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/pytorch_computation_graph.svg @@ -0,0 +1 @@ +x2abc3y diff --git a/_notebooks/course_UvA-DL/01-introduction-to-pytorch/small_neural_network.svg b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/small_neural_network.svg new file mode 100644 index 0000000..4a55eac --- /dev/null +++ b/_notebooks/course_UvA-DL/01-introduction-to-pytorch/small_neural_network.svg @@ -0,0 +1 @@ +x1x2 diff --git a/_notebooks/course_UvA-DL/02-activation-functions/.meta.yml b/_notebooks/course_UvA-DL/02-activation-functions/.meta.yml new file mode 100644 index 0000000..8d6392a --- /dev/null +++ b/_notebooks/course_UvA-DL/02-activation-functions/.meta.yml @@ -0,0 +1,20 @@ +title: "Tutorial 2: Activation Functions" +author: Phillip Lippe +created: 2021-08-27 +updated: 2023-03-14 +license: CC BY-SA +description: | + In this tutorial, we will take a closer look at (popular) activation functions and investigate their effect on optimization properties in neural networks. + Activation functions are a crucial part of deep learning models as they add the non-linearity to neural networks. + There is a great variety of activation functions in the literature, and some are more beneficial than others. + The goal of this tutorial is to show the importance of choosing a good activation function (and how to do so), and what problems might occur if we don't. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/02-activation-functions.jpg b/_notebooks/course_UvA-DL/02-activation-functions/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/02-activation-functions.jpg rename to _notebooks/course_UvA-DL/02-activation-functions/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/02-activation-functions/Activation_Functions.py b/_notebooks/course_UvA-DL/02-activation-functions/Activation_Functions.py new file mode 100644 index 0000000..1abd126 --- /dev/null +++ b/_notebooks/course_UvA-DL/02-activation-functions/Activation_Functions.py @@ -0,0 +1,800 @@ +# %% [markdown] +#
+# Before we start, we import our standard libraries and set up basic functions: + +# %% +import json +import math +import os +import urllib.request +import warnings +from urllib.error import HTTPError + +import matplotlib.pyplot as plt + +# %matplotlib inline +import matplotlib_inline.backend_inline +import numpy as np +import seaborn as sns +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data as data +import torchvision +from torchvision import transforms +from torchvision.datasets import FashionMNIST +from tqdm.notebook import tqdm + +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +sns.set() + +# %% [markdown] +# We will define a function to set a seed on all libraries we might interact with in this tutorial (here numpy and torch). +# This allows us to make our training reproducible. +# However, note that in contrast to the CPU, the same seed on different GPU architectures can give different results. +# All models here have been trained on an NVIDIA GTX1080Ti. +# +# Additionally, the following cell defines two paths: `DATASET_PATH` and `CHECKPOINT_PATH`. +# The dataset path is the directory where we will download datasets used in the notebooks. +# It is recommended to store all datasets from PyTorch in one joined directory to prevent duplicate downloads. +# The checkpoint path is the directory where we will store trained model weights and additional files. +# The needed files will be automatically downloaded. +# In case you are on Google Colab, it is recommended to change the +# directories to start from the current directory (i.e. remove `../` for +# both dataset and checkpoint path). + +# %% +# Path to the folder where the datasets are/should be downloaded (e.g. MNIST) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/Activation_Functions/") + + +# Function for setting the seed +def set_seed(seed): + np.random.seed(seed) + torch.manual_seed(seed) + if torch.cuda.is_available(): # GPU operation have separate seed + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + + +set_seed(42) + +# Additionally, some operations on a GPU are implemented stochastic for efficiency +# We want to ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +# Fetching the device that will be used throughout this notebook +device = torch.device("cpu") if not torch.cuda.is_available() else torch.device("cuda:0") +print("Using device", device) + +# %% [markdown] +# The following cell downloads all pretrained models we will use in this notebook. +# The files are stored on a separate [repository](https://github.com/phlippe/saved_models) to reduce the size of the notebook repository, especially for building the documentation on ReadTheDocs. +# In case the download below fails, you can download the models from a [Google Drive folder](https://drive.google.com/drive/folders/1sFpZUpDJVjiYEvIqISqfkFizfsTnPf4s?usp=sharing). +# Please let me (Phillip) know if an error occurs so it can be fixed for all students. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/" +# Files to download +pretrained_files = [ + "FashionMNIST_elu.config", + "FashionMNIST_elu.tar", + "FashionMNIST_leakyrelu.config", + "FashionMNIST_leakyrelu.tar", + "FashionMNIST_relu.config", + "FashionMNIST_relu.tar", + "FashionMNIST_sigmoid.config", + "FashionMNIST_sigmoid.tar", + "FashionMNIST_swish.config", + "FashionMNIST_swish.tar", + "FashionMNIST_tanh.config", + "FashionMNIST_tanh.tar", +] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print(f"Downloading {file_url}...") + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# ## Common activation functions + +# %% [markdown] +# As a first step, we will implement some common activation functions by ourselves. +# Of course, most of them can also be found in the `torch.nn` package (see the [documentation](https://pytorch.org/docs/stable/nn.html#non-linear-activations-weighted-sum-nonlinearity) for an overview). +# However, we'll write our own functions here for a better understanding and insights. +# +# For an easier time of comparing various activation functions, we start +# with defining a base class from which all our future modules will +# inherit: + + +# %% +class ActivationFunction(nn.Module): + def __init__(self): + super().__init__() + self.name = self.__class__.__name__ + self.config = {"name": self.name} + + +# %% [markdown] +# Every activation function will be an `nn.Module` so that we can integrate them nicely in a network. +# We will use the `config` dictionary to store adjustable parameters for some activation functions. +# +# Next, we implement two of the "oldest" activation functions that are still commonly used for various tasks: sigmoid and tanh. +# Both the sigmoid and tanh activation can be also found as PyTorch functions (`torch.sigmoid`, `torch.tanh`) or as modules (`nn.Sigmoid`, `nn.Tanh`). +# Here, we implement them by hand: + +# %% +class Sigmoid(ActivationFunction): + def forward(self, x): + return 1 / (1 + torch.exp(-x)) + + +class Tanh(ActivationFunction): + def forward(self, x): + x_exp, neg_x_exp = torch.exp(x), torch.exp(-x) + return (x_exp - neg_x_exp) / (x_exp + neg_x_exp) + + +# %% [markdown] +# Another popular activation function that has allowed the training of deeper networks, is the Rectified Linear Unit (ReLU). +# Despite its simplicity of being a piecewise linear function, ReLU has one major benefit compared to sigmoid and tanh: a strong, stable gradient for a large range of values. +# Based on this idea, a lot of variations of ReLU have been proposed, of which we will implement the following three: LeakyReLU, ELU, and Swish. +# LeakyReLU replaces the zero settings in the negative part with a smaller slope to allow gradients to flow also in this part of the input. +# Similarly, ELU replaces the negative part with an exponential decay. +# The third, most recently proposed activation function is Swish, which is actually the result of a large experiment with the purpose of finding the "optimal" activation function. +# Compared to the other activation functions, Swish is both smooth and non-monotonic (i.e. contains a change of sign in the gradient). +# This has been shown to prevent dead neurons as in standard ReLU activation, especially for deep networks. +# If interested, a more detailed discussion of the benefits of Swish can be found in [this paper](https://arxiv.org/abs/1710.05941) [1]. +# +# Let's implement the four activation functions below: + +# %% +class ReLU(ActivationFunction): + def forward(self, x): + return x * (x > 0).float() + + +class LeakyReLU(ActivationFunction): + def __init__(self, alpha=0.1): + super().__init__() + self.config["alpha"] = alpha + + def forward(self, x): + return torch.where(x > 0, x, self.config["alpha"] * x) + + +class ELU(ActivationFunction): + def forward(self, x): + return torch.where(x > 0, x, torch.exp(x) - 1) + + +class Swish(ActivationFunction): + def forward(self, x): + return x * torch.sigmoid(x) + + +# %% [markdown] +# For later usage, we summarize all our activation functions in a dictionary mapping the name to the class object. +# In case you implement a new activation function by yourself, add it here to include it in future comparisons as well: + +# %% +act_fn_by_name = {"sigmoid": Sigmoid, "tanh": Tanh, "relu": ReLU, "leakyrelu": LeakyReLU, "elu": ELU, "swish": Swish} + +# %% [markdown] +# ### Visualizing activation functions +# +# To get an idea of what each activation function actually does, we will visualize them in the following. +# Next to the actual activation value, the gradient of the function is an important aspect as it is crucial for optimizing the neural network. +# PyTorch allows us to compute the gradients simply by calling the `backward` function: + + +# %% +def get_grads(act_fn, x): + """Computes the gradients of an activation function at specified positions. + + Args: + act_fn: An object of the class "ActivationFunction" with an implemented forward pass. + x: 1D input tensor. + Returns: + A tensor with the same size of x containing the gradients of act_fn at x. + """ + x = x.clone().requires_grad_() # Mark the input as tensor for which we want to store gradients + out = act_fn(x) + out.sum().backward() # Summing results in an equal gradient flow to each element in x + return x.grad # Accessing the gradients of x by "x.grad" + + +# %% [markdown] +# Now we can visualize all our activation functions including their gradients: + + +# %% +def vis_act_fn(act_fn, ax, x): + # Run activation function + y = act_fn(x) + y_grads = get_grads(act_fn, x) + # Push x, y and gradients back to cpu for plotting + x, y, y_grads = x.cpu().numpy(), y.cpu().numpy(), y_grads.cpu().numpy() + # Plotting + ax.plot(x, y, linewidth=2, label="ActFn") + ax.plot(x, y_grads, linewidth=2, label="Gradient") + ax.set_title(act_fn.name) + ax.legend() + ax.set_ylim(-1.5, x.max()) + + +# Add activation functions if wanted +act_fns = [act_fn() for act_fn in act_fn_by_name.values()] +x = torch.linspace(-5, 5, 1000) # Range on which we want to visualize the activation functions +# Plotting +cols = 2 +rows = math.ceil(len(act_fns) / float(cols)) +fig, ax = plt.subplots(rows, cols, figsize=(cols * 4, rows * 4)) +for i, act_fn in enumerate(act_fns): + vis_act_fn(act_fn, ax[divmod(i, cols)], x) +fig.subplots_adjust(hspace=0.3) +plt.show() + +# %% [markdown] +# ## Analysing the effect of activation functions +#
+ + +# %% [markdown] +# After implementing and visualizing the activation functions, we are aiming to gain insights into their effect. +# We do this by using a simple neural network trained on +# [FashionMNIST](https://github.com/zalandoresearch/fashion-mnist) and +# examine various aspects of the model, including the performance and +# gradient flow. + +# %% [markdown] +# ### Setup + +# %% [markdown] +# Firstly, let's set up a neural network. +# The chosen network views the images as 1D tensors and pushes them through a sequence of linear layers and a specified activation function. +# Feel free to experiment with other network architectures. + + +# %% +class BaseNetwork(nn.Module): + def __init__(self, act_fn, input_size=784, num_classes=10, hidden_sizes=[512, 256, 256, 128]): + """ + Args: + act_fn: Object of the activation function that should be used as non-linearity in the network. + input_size: Size of the input images in pixels + num_classes: Number of classes we want to predict + hidden_sizes: A list of integers specifying the hidden layer sizes in the NN + """ + super().__init__() + + # Create the network based on the specified hidden sizes + layers = [] + layer_sizes = [input_size] + hidden_sizes + layer_size_last = layer_sizes[0] + for layer_size in layer_sizes[1:]: + layers += [nn.Linear(layer_size_last, layer_size), act_fn] + layer_size_last = layer_size + layers += [nn.Linear(layer_sizes[-1], num_classes)] + # nn.Sequential summarizes a list of modules into a single module, applying them in sequence + self.layers = nn.Sequential(*layers) + + # We store all hyperparameters in a dictionary for saving and loading of the model + self.config = { + "act_fn": act_fn.config, + "input_size": input_size, + "num_classes": num_classes, + "hidden_sizes": hidden_sizes, + } + + def forward(self, x): + x = x.view(x.size(0), -1) # Reshape images to a flat vector + out = self.layers(x) + return out + + +# %% [markdown] +# We also add functions for loading and saving the model. +# The hyperparameters are stored in a configuration file (simple json file): + + +# %% +def _get_config_file(model_path, model_name): + # Name of the file for storing hyperparameter details + return os.path.join(model_path, model_name + ".config") + + +def _get_model_file(model_path, model_name): + # Name of the file for storing network parameters + return os.path.join(model_path, model_name + ".tar") + + +def load_model(model_path, model_name, net=None): + """Loads a saved model from disk. + + Args: + model_path: Path of the checkpoint directory + model_name: Name of the model (str) + net: (Optional) If given, the state dict is loaded into this model. Otherwise, a new model is created. + """ + config_file, model_file = _get_config_file(model_path, model_name), _get_model_file(model_path, model_name) + assert os.path.isfile( + config_file + ), f'Could not find the config file "{config_file}". Are you sure this is the correct path and you have your model config stored here?' + assert os.path.isfile( + model_file + ), f'Could not find the model file "{model_file}". Are you sure this is the correct path and you have your model stored here?' + with open(config_file) as f: + config_dict = json.load(f) + if net is None: + act_fn_name = config_dict["act_fn"].pop("name").lower() + act_fn = act_fn_by_name[act_fn_name](**config_dict.pop("act_fn")) + net = BaseNetwork(act_fn=act_fn, **config_dict) + net.load_state_dict(torch.load(model_file, map_location=device)) + return net + + +def save_model(model, model_path, model_name): + """Given a model, we save the state_dict and hyperparameters. + + Args: + model: Network object to save parameters from + model_path: Path of the checkpoint directory + model_name: Name of the model (str) + """ + config_dict = model.config + os.makedirs(model_path, exist_ok=True) + config_file, model_file = _get_config_file(model_path, model_name), _get_model_file(model_path, model_name) + with open(config_file, "w") as f: + json.dump(config_dict, f) + torch.save(model.state_dict(), model_file) + + +# %% [markdown] +# We also set up the dataset we want to train it on, namely [FashionMNIST](https://github.com/zalandoresearch/fashion-mnist). +# FashionMNIST is a more complex version of MNIST and contains black-and-white images of clothes instead of digits. +# The 10 classes include trousers, coats, shoes, bags and more. +# To load this dataset, we will make use of yet another PyTorch package, namely `torchvision` ([documentation](https://pytorch.org/vision/stable/index.html)). +# The `torchvision` package consists of popular datasets, model architectures, and common image transformations for computer vision. +# We will use the package for many of the notebooks in this course to simplify our dataset handling. +# +# Let's load the dataset below, and visualize a few images to get an impression of the data. + +# %% + +# Transformations applied on each image => first make them a tensor, then normalize them in the range -1 to 1 +transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) + +# Loading the training dataset. We need to split it into a training and validation part +train_dataset = FashionMNIST(root=DATASET_PATH, train=True, transform=transform, download=True) +train_set, val_set = torch.utils.data.random_split(train_dataset, [50000, 10000]) + +# Loading the test set +test_set = FashionMNIST(root=DATASET_PATH, train=False, transform=transform, download=True) + +# %% [markdown] +# We define a set of data loaders that we can use for various purposes later. +# Note that for actually training a model, we will use different data loaders +# with a lower batch size. + +# %% +train_loader = data.DataLoader(train_set, batch_size=1024, shuffle=True, drop_last=False) +val_loader = data.DataLoader(val_set, batch_size=1024, shuffle=False, drop_last=False) +test_loader = data.DataLoader(test_set, batch_size=1024, shuffle=False, drop_last=False) + +# %% +exmp_imgs = [train_set[i][0] for i in range(16)] +# Organize the images into a grid for nicer visualization +img_grid = torchvision.utils.make_grid(torch.stack(exmp_imgs, dim=0), nrow=4, normalize=True, pad_value=0.5) +img_grid = img_grid.permute(1, 2, 0) + +plt.figure(figsize=(8, 8)) +plt.title("FashionMNIST examples") +plt.imshow(img_grid) +plt.axis("off") +plt.show() +plt.close() + +# %% [markdown] +# ### Visualizing the gradient flow after initialization +# +# As mentioned previously, one important aspect of activation functions is how they propagate gradients through the network. +# Imagine we have a very deep neural network with more than 50 layers. +# The gradients for the input layer, i.e. the very first layer, have passed >50 times the activation function, but we still want them to be of a reasonable size. +# If the gradient through the activation function is (in expectation) considerably smaller than 1, our gradients will vanish until they reach the input layer. +# If the gradient through the activation function is larger than 1, the gradients exponentially increase and might explode. +# +# To get a feeling of how every activation function influences the +# gradients, we can look at a freshly initialized network and measure the +# gradients for each parameter for a batch of 256 images: + + +# %% +def visualize_gradients(net, color="C0"): + """ + Args: + net: Object of class BaseNetwork + color: Color in which we want to visualize the histogram (for easier separation of activation functions) + """ + net.eval() + small_loader = data.DataLoader(train_set, batch_size=256, shuffle=False) + imgs, labels = next(iter(small_loader)) + imgs, labels = imgs.to(device), labels.to(device) + + # Pass one batch through the network, and calculate the gradients for the weights + net.zero_grad() + preds = net(imgs) + loss = F.cross_entropy(preds, labels) + loss.backward() + # We limit our visualization to the weight parameters and exclude the bias to reduce the number of plots + grads = { + name: params.grad.data.view(-1).cpu().clone().numpy() + for name, params in net.named_parameters() + if "weight" in name + } + net.zero_grad() + + # Plotting + columns = len(grads) + fig, ax = plt.subplots(1, columns, figsize=(columns * 3.5, 2.5)) + fig_index = 0 + for key in grads: + key_ax = ax[fig_index % columns] + sns.histplot(data=grads[key], bins=30, ax=key_ax, color=color, kde=True) + key_ax.set_title(str(key)) + key_ax.set_xlabel("Grad magnitude") + fig_index += 1 + fig.suptitle( + f"Gradient magnitude distribution for activation function {net.config['act_fn']['name']}", fontsize=14, y=1.05 + ) + fig.subplots_adjust(wspace=0.45) + plt.show() + plt.close() + + +# %% +# Seaborn prints warnings if histogram has small values. We can ignore them for now +warnings.filterwarnings("ignore") +# Create a plot for every activation function +for i, act_fn_name in enumerate(act_fn_by_name): + # Setting the seed ensures that we have the same weight initialization for each activation function + set_seed(42) + act_fn = act_fn_by_name[act_fn_name]() + net_actfn = BaseNetwork(act_fn=act_fn).to(device) + visualize_gradients(net_actfn, color=f"C{i}") + +# %% [markdown] +# The sigmoid activation function shows a clearly undesirable behavior. +# While the gradients for the output layer are very large with up to 0.1, the input layer has the lowest gradient norm across all activation functions with only 1e-5. +# This is due to its small maximum gradient of 1/4, and finding a suitable learning rate across all layers is not possible in this setup. +# All the other activation functions show to have similar gradient norms across all layers. +# Interestingly, the ReLU activation has a spike around 0 which is caused by its zero-part on the left, and dead neurons (we will take a closer look at this later on). +# +# Note that additionally to the activation, the initialization of the weight parameters can be crucial. +# By default, PyTorch uses the [Kaiming](https://pytorch.org/docs/stable/nn.init.html#torch.nn.init.kaiming_uniform_) initialization for linear layers optimized for Tanh activations. +# In Tutorial 4, we will take a closer look at initialization, but assume +# for now that the Kaiming initialization works for all activation +# functions reasonably well. + +# %% [markdown] +# ### Training a model +# +# Next, we want to train our model with different activation functions on FashionMNIST and compare the gained performance. +# All in all, our final goal is to achieve the best possible performance on a dataset of our choice. +# Therefore, we write a training loop in the next cell including a +# validation after every epoch and a final test on the best model: + + +# %% +def train_model(net, model_name, max_epochs=50, patience=7, batch_size=256, overwrite=False): + """Train a model on the training set of FashionMNIST. + + Args: + net: Object of BaseNetwork + model_name: (str) Name of the model, used for creating the checkpoint names + max_epochs: Number of epochs we want to (maximally) train for + patience: If the performance on the validation set has not improved for #patience epochs, we stop training early + batch_size: Size of batches used in training + overwrite: Determines how to handle the case when there already exists a checkpoint. If True, it will be overwritten. Otherwise, we skip training. + """ + file_exists = os.path.isfile(_get_model_file(CHECKPOINT_PATH, model_name)) + if file_exists and not overwrite: + print("Model file already exists. Skipping training...") + else: + if file_exists: + print("Model file exists, but will be overwritten...") + + # Defining optimizer, loss and data loader + optimizer = optim.SGD(net.parameters(), lr=1e-2, momentum=0.9) # Default parameters, feel free to change + loss_module = nn.CrossEntropyLoss() + train_loader_local = data.DataLoader( + train_set, batch_size=batch_size, shuffle=True, drop_last=True, pin_memory=True + ) + + val_scores = [] + best_val_epoch = -1 + for epoch in range(max_epochs): + ############ + # Training # + ############ + net.train() + true_preds, count = 0.0, 0 + for imgs, labels in tqdm(train_loader_local, desc=f"Epoch {epoch+1}", leave=False): + imgs, labels = imgs.to(device), labels.to(device) # To GPU + optimizer.zero_grad() # Zero-grad can be placed anywhere before "loss.backward()" + preds = net(imgs) + loss = loss_module(preds, labels) + loss.backward() + optimizer.step() + # Record statistics during training + true_preds += (preds.argmax(dim=-1) == labels).sum() + count += labels.shape[0] + train_acc = true_preds / count + + ############## + # Validation # + ############## + val_acc = test_model(net, val_loader) + val_scores.append(val_acc) + print( + f"[Epoch {epoch+1:2i}] Training accuracy: {train_acc*100.0:05.2f}%, Validation accuracy: {val_acc*100.0:05.2f}%" + ) + + if len(val_scores) == 1 or val_acc > val_scores[best_val_epoch]: + print("\t (New best performance, saving model...)") + save_model(net, CHECKPOINT_PATH, model_name) + best_val_epoch = epoch + elif best_val_epoch <= epoch - patience: + print(f"Early stopping due to no improvement over the last {patience} epochs") + break + + # Plot a curve of the validation accuracy + plt.plot([i for i in range(1, len(val_scores) + 1)], val_scores) + plt.xlabel("Epochs") + plt.ylabel("Validation accuracy") + plt.title(f"Validation performance of {model_name}") + plt.show() + plt.close() + + load_model(CHECKPOINT_PATH, model_name, net=net) + test_acc = test_model(net, test_loader) + print((f" Test accuracy: {test_acc*100.0:4.2f}% ").center(50, "=") + "\n") + return test_acc + + +def test_model(net, data_loader): + """Test a model on a specified dataset. + + Args: + net: Trained model of type BaseNetwork + data_loader: DataLoader object of the dataset to test on (validation or test) + """ + net.eval() + true_preds, count = 0.0, 0 + for imgs, labels in data_loader: + imgs, labels = imgs.to(device), labels.to(device) + with torch.no_grad(): + preds = net(imgs).argmax(dim=-1) + true_preds += (preds == labels).sum().item() + count += labels.shape[0] + test_acc = true_preds / count + return test_acc + + +# %% [markdown] +# We train one model for each activation function. +# We recommend using the pretrained models to save time if you are running this notebook on CPU. + +# %% +for act_fn_name in act_fn_by_name: + print(f"Training BaseNetwork with {act_fn_name} activation...") + set_seed(42) + act_fn = act_fn_by_name[act_fn_name]() + net_actfn = BaseNetwork(act_fn=act_fn).to(device) + train_model(net_actfn, f"FashionMNIST_{act_fn_name}", overwrite=False) + +# %% [markdown] +# Not surprisingly, the model using the sigmoid activation function shows to fail and does not improve upon random performance (10 classes => 1/10 for random chance). +# +# All the other activation functions gain similar performance. +# To have a more accurate conclusion, we would have to train the models for multiple seeds and look at the averages. +# However, the "optimal" activation function also depends on many other factors (hidden sizes, number of layers, type of layers, task, dataset, optimizer, learning rate, etc.) +# so that a thorough grid search would not be useful in our case. +# In the literature, activation functions that have shown to work well +# with deep networks are all types of ReLU functions we experiment with +# here, with small gains for specific activation functions in specific +# networks. + +# %% [markdown] +# ### Visualizing the activation distribution + +# %% [markdown] +# After we have trained the models, we can look at the actual activation values that find inside the model. +# For instance, how many neurons are set to zero in ReLU? +# Where do we find most values in Tanh? +# To answer these questions, we can write a simple function which takes a +# trained model, applies it to a batch of images, and plots the histogram +# of the activations inside the network: + + +# %% +def visualize_activations(net, color="C0"): + activations = {} + + net.eval() + small_loader = data.DataLoader(train_set, batch_size=1024) + imgs, labels = next(iter(small_loader)) + with torch.no_grad(): + layer_index = 0 + imgs = imgs.to(device) + imgs = imgs.view(imgs.size(0), -1) + # We need to manually loop through the layers to save all activations + for layer_index, layer in enumerate(net.layers[:-1]): + imgs = layer(imgs) + activations[layer_index] = imgs.view(-1).cpu().numpy() + + # Plotting + columns = 4 + rows = math.ceil(len(activations) / columns) + fig, ax = plt.subplots(rows, columns, figsize=(columns * 2.7, rows * 2.5)) + fig_index = 0 + for key in activations: + key_ax = ax[fig_index // columns][fig_index % columns] + sns.histplot(data=activations[key], bins=50, ax=key_ax, color=color, kde=True, stat="density") + key_ax.set_title(f"Layer {key} - {net.layers[key].__class__.__name__}") + fig_index += 1 + fig.suptitle(f"Activation distribution for activation function {net.config['act_fn']['name']}", fontsize=14) + fig.subplots_adjust(hspace=0.4, wspace=0.4) + plt.show() + plt.close() + + +# %% +for i, act_fn_name in enumerate(act_fn_by_name): + net_actfn = load_model(model_path=CHECKPOINT_PATH, model_name=f"FashionMNIST_{act_fn_name}").to(device) + visualize_activations(net_actfn, color=f"C{i}") + +# %% [markdown] +# As the model with sigmoid activation was not able to train properly, the activations are also less informative and all gathered around 0.5 (the activation at input 0). +# +# The tanh shows a more diverse behavior. +# While for the input layer we experience a larger amount of neurons to be close to -1 and 1, where the gradients are close to zero, the activations in the two consecutive layers are closer to zero. +# This is probably because the input layers look for specific features in the input image, and the consecutive layers combine those together. +# The activations for the last layer are again more biased to the extreme points because the classification layer can be seen as a weighted average of those values (the gradients push the activations to those extremes). +# +# The ReLU has a strong peak at 0, as we initially expected. +# The effect of having no gradients for negative values is that the network does not have a Gaussian-like distribution after the linear layers, but a longer tail towards the positive values. +# The LeakyReLU shows a very similar behavior while ELU follows again a more Gaussian-like distribution. +# The Swish activation seems to lie in between, although it is worth noting that Swish uses significantly higher values than other activation functions (up to 20). +# +# As all activation functions show slightly different behavior although +# obtaining similar performance for our simple network, it becomes +# apparent that the selection of the "optimal" activation function really +# depends on many factors, and is not the same for all possible networks. + +# %% [markdown] +# ### Finding dead neurons in ReLU networks + +# %% [markdown] +# One known drawback of the ReLU activation is the occurrence of "dead neurons", i.e. neurons with no gradient for any training input. +# The issue of dead neurons is that as no gradient is provided for the layer, we cannot train the parameters of this neuron in the previous layer to obtain output values besides zero. +# For dead neurons to happen, the output value of a specific neuron of the linear layer before the ReLU has to be negative for all input images. +# Considering the large number of neurons we have in a neural network, it is not unlikely for this to happen. +# +# To get a better understanding of how much of a problem this is, and when we need to be careful, we will measure how many dead neurons different networks have. +# For this, we implement a function which runs the network on the whole +# training set and records whether a neuron is exactly 0 for all data +# points or not: + + +# %% +@torch.no_grad() +def measure_number_dead_neurons(net): + """Function to measure the number of dead neurons in a trained neural network. + + For each neuron, we create a boolean variable initially set to 1. If it has an activation unequals 0 at any time, we + set this variable to 0. After running through the whole training set, only dead neurons will have a 1. + """ + neurons_dead = [ + torch.ones(layer.weight.shape[0], device=device, dtype=torch.bool) + for layer in net.layers[:-1] + if isinstance(layer, nn.Linear) + ] # Same shapes as hidden size in BaseNetwork + + net.eval() + for imgs, labels in tqdm(train_loader, leave=False): # Run through whole training set + layer_index = 0 + imgs = imgs.to(device) + imgs = imgs.view(imgs.size(0), -1) + for layer in net.layers[:-1]: + imgs = layer(imgs) + if isinstance(layer, ActivationFunction): + # Are all activations == 0 in the batch, and we did not record the opposite in the last batches? + neurons_dead[layer_index] = torch.logical_and(neurons_dead[layer_index], (imgs == 0).all(dim=0)) + layer_index += 1 + number_neurons_dead = [t.sum().item() for t in neurons_dead] + print("Number of dead neurons:", number_neurons_dead) + print( + "In percentage:", + ", ".join( + [f"{(100.0 * num_dead / tens.shape[0]):4.2f}%" for tens, num_dead in zip(neurons_dead, number_neurons_dead)] + ), + ) + + +# %% [markdown] +# First, we can measure the number of dead neurons for an untrained network: + +# %% +set_seed(42) +net_relu = BaseNetwork(act_fn=ReLU()).to(device) +measure_number_dead_neurons(net_relu) + +# %% [markdown] +# We see that only a minor amount of neurons are dead, but that they increase with the depth of the layer. +# However, this is not a problem for the small number of dead neurons we have as the input to later layers is changed due to updates to the weights of previous layers. +# Therefore, dead neurons in later layers can potentially become "alive"/active again. +# +# How does this look like for a trained network (with the same initialization)? + +# %% +net_relu = load_model(model_path=CHECKPOINT_PATH, model_name="FashionMNIST_relu").to(device) +measure_number_dead_neurons(net_relu) + +# %% [markdown] +# The number of dead neurons indeed decreased in the later layers. +# However, it should be noted that dead neurons are especially problematic in the input layer. +# As the input does not change over epochs (the training set is kept as it is), training the network cannot turn those neurons back active. +# Still, the input data has usually a sufficiently high standard deviation to reduce the risk of dead neurons. +# +# Finally, we check how the number of dead neurons behaves with increasing layer depth. +# For instance, let's take the following 10-layer neural network: + +# %% +set_seed(42) +net_relu = BaseNetwork( + act_fn=ReLU(), + hidden_sizes=[256, 256, 256, 256, 256, 128, 128, 128, 128, 128], +).to(device) +measure_number_dead_neurons(net_relu) + +# %% [markdown] +# The number of dead neurons is significantly higher than before which harms the gradient flow especially in the first iterations. +# For instance, more than 56% of the neurons in the pre-last layer are dead which creates a considerable bottleneck. +# Hence, it is advisible to use other nonlinearities like Swish for very deep networks. + +# %% [markdown] +# ## Conclusion +# +# In this notebook, we have reviewed a set of six activation functions (sigmoid, tanh, ReLU, LeakyReLU, ELU, and Swish) in neural networks, and discussed how they influence the gradient distribution across layers. +# Sigmoid tends to fail deep neural networks as the highest gradient it provides is 0.25 leading to vanishing gradients in early layers. +# All ReLU-based activation functions have shown to perform well, and besides the original ReLU, do not have the issue of dead neurons. +# When implementing your own neural network, it is recommended to start +# with a ReLU-based network and select the specific activation function +# based on the properties of the network. + +# %% [markdown] +# ## References +# +# [1] Ramachandran, Prajit, Barret Zoph, and Quoc V. Le. +# "Searching for activation functions." +# arXiv preprint arXiv:1710.05941 (2017). +# [Paper link](https://arxiv.org/abs/1710.05941) diff --git a/_notebooks/course_UvA-DL/03-initialization-and-optimization/.meta.yml b/_notebooks/course_UvA-DL/03-initialization-and-optimization/.meta.yml new file mode 100644 index 0000000..dee86a0 --- /dev/null +++ b/_notebooks/course_UvA-DL/03-initialization-and-optimization/.meta.yml @@ -0,0 +1,24 @@ +title: "Tutorial 3: Initialization and Optimization" +author: Phillip Lippe +created: 2021-08-27 +updated: 2023-03-14 +license: CC BY-SA +tags: + - Image + - Initialization + - Optimizers +description: | + In this tutorial, we will review techniques for optimization and initialization of neural networks. + When increasing the depth of neural networks, there are various challenges we face. + Most importantly, we need to have a stable gradient flow through the network, as otherwise, we might encounter vanishing or exploding gradients. + This is why we will take a closer look at the following concepts: initialization and optimization. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/03-initialization-and-optimization.jpg b/_notebooks/course_UvA-DL/03-initialization-and-optimization/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/03-initialization-and-optimization.jpg rename to _notebooks/course_UvA-DL/03-initialization-and-optimization/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/03-initialization-and-optimization/Initialization_and_Optimization.py b/_notebooks/course_UvA-DL/03-initialization-and-optimization/Initialization_and_Optimization.py new file mode 100644 index 0000000..4fd6e47 --- /dev/null +++ b/_notebooks/course_UvA-DL/03-initialization-and-optimization/Initialization_and_Optimization.py @@ -0,0 +1,1157 @@ +# %% [markdown] +#
+# In the first half of the notebook, we will review different initialization techniques, and go step by step from the simplest initialization to methods that are nowadays used in very deep networks. +# In the second half, we focus on optimization comparing the optimizers SGD, SGD with Momentum, and Adam. +# +# Let's start with importing our standard libraries: + +# %% +import copy +import json +import math +import os +import urllib.request +from urllib.error import HTTPError + +import lightning as L +import matplotlib.pyplot as plt + +# %matplotlib inline +import matplotlib_inline.backend_inline +import numpy as np +import seaborn as sns +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils.data as data +from matplotlib import cm +from torchvision import transforms +from torchvision.datasets import FashionMNIST +from tqdm.notebook import tqdm + +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +sns.set() + +# %% [markdown] +# Instead of the `set_seed` function as in Tutorial 3, we can use Lightning's build-in function `L.seed_everything`. +# We will reuse the path variables `DATASET_PATH` and `CHECKPOINT_PATH` as in Tutorial 3. +# Adjust the paths if necessary. + +# %% +# Path to the folder where the datasets are/should be downloaded (e.g. MNIST) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/InitOptim/") + +# Seed everything +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +# Fetching the device that will be used throughout this notebook +device = torch.device("cpu") if not torch.cuda.is_available() else torch.device("cuda:0") +print("Using device", device) + +# %% [markdown] +# In the last part of the notebook, we will train models using three different optimizers. +# The pretrained models for those are downloaded below. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/" +# Files to download +pretrained_files = [ + "FashionMNIST_SGD.config", + "FashionMNIST_SGD_results.json", + "FashionMNIST_SGD.tar", + "FashionMNIST_SGDMom.config", + "FashionMNIST_SGDMom_results.json", + "FashionMNIST_SGDMom.tar", + "FashionMNIST_Adam.config", + "FashionMNIST_Adam_results.json", + "FashionMNIST_Adam.tar", +] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print(f"Downloading {file_url}...") + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# ## Preparation + +# %% [markdown] +# Throughout this notebook, we will use a deep fully connected network, similar to our previous tutorial. +# We will also again apply the network to FashionMNIST, so you can relate to the results of Tutorial 3. +# We start by loading the FashionMNIST dataset: + +# %% + +# Transformations applied on each image => first make them a tensor, then normalize them with mean 0 and std 1 +transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.2861,), (0.3530,))]) + +# Loading the training dataset. We need to split it into a training and validation part +train_dataset = FashionMNIST(root=DATASET_PATH, train=True, transform=transform, download=True) +train_set, val_set = torch.utils.data.random_split(train_dataset, [50000, 10000]) + +# Loading the test set +test_set = FashionMNIST(root=DATASET_PATH, train=False, transform=transform, download=True) + +# %% [markdown] +# We define a set of data loaders that we can use for various purposes later. +# Note that for actually training a model, we will use different data loaders +# with a lower batch size. + +# %% +train_loader = data.DataLoader(train_set, batch_size=1024, shuffle=True, drop_last=False) +val_loader = data.DataLoader(val_set, batch_size=1024, shuffle=False, drop_last=False) +test_loader = data.DataLoader(test_set, batch_size=1024, shuffle=False, drop_last=False) + +# %% [markdown] +# In comparison to the previous tutorial, we have changed the parameters of the normalization transformation `transforms.Normalize`. +# The normalization is now designed to give us an expected mean of 0 and a standard deviation of 1 across pixels. +# This will be particularly relevant for the discussion about initialization we will look at below, and hence we change it here. +# It should be noted that in most classification tasks, both normalization techniques (between -1 and 1 or mean 0 and stddev 1) have shown to work well. +# We can calculate the normalization parameters by determining the mean and standard deviation on the original images: + +# %% +print("Mean", (train_dataset.data.float() / 255.0).mean().item()) +print("Std", (train_dataset.data.float() / 255.0).std().item()) + +# %% [markdown] +# We can verify the transformation by looking at the statistics of a single batch: + +# %% +imgs, _ = next(iter(train_loader)) +print(f"Mean: {imgs.mean().item():5.3f}") +print(f"Standard deviation: {imgs.std().item():5.3f}") +print(f"Maximum: {imgs.max().item():5.3f}") +print(f"Minimum: {imgs.min().item():5.3f}") + +# %% [markdown] +# Note that the maximum and minimum are not 1 and -1 anymore, but shifted towards the positive values. +# This is because FashionMNIST contains a lot of black pixels, similar to MNIST. +# +# Next, we create a linear neural network. We use the same setup as in the previous tutorial. + + +# %% +class BaseNetwork(nn.Module): + def __init__(self, act_fn, input_size=784, num_classes=10, hidden_sizes=[512, 256, 256, 128]): + """ + Args: + act_fn: Object of the activation function that should be used as non-linearity in the network. + input_size: Size of the input images in pixels + num_classes: Number of classes we want to predict + hidden_sizes: A list of integers specifying the hidden layer sizes in the NN + """ + super().__init__() + + # Create the network based on the specified hidden sizes + layers = [] + layer_sizes = [input_size] + hidden_sizes + for layer_index in range(1, len(layer_sizes)): + layers += [nn.Linear(layer_sizes[layer_index - 1], layer_sizes[layer_index]), act_fn] + layers += [nn.Linear(layer_sizes[-1], num_classes)] + # A module list registers a list of modules as submodules (e.g. for parameters) + self.layers = nn.ModuleList(layers) + + self.config = { + "act_fn": act_fn.__class__.__name__, + "input_size": input_size, + "num_classes": num_classes, + "hidden_sizes": hidden_sizes, + } + + def forward(self, x): + x = x.view(x.size(0), -1) + for layer in self.layers: + x = layer(x) + return x + + +# %% [markdown] +# For the activation functions, we make use of PyTorch's `torch.nn` library instead of implementing ourselves. +# However, we also define an `Identity` activation function. +# Although this activation function would significantly limit the +# network's modeling capabilities, we will use it in the first steps of +# our discussion about initialization (for simplicity). + + +# %% +class Identity(nn.Module): + def forward(self, x): + return x + + +act_fn_by_name = {"tanh": nn.Tanh, "relu": nn.ReLU, "identity": Identity} + +# %% [markdown] +# Finally, we define a few plotting functions that we will use for our discussions. +# These functions help us to (1) visualize the weight/parameter distribution inside a network, (2) visualize the gradients that the parameters at different layers receive, and (3) the activations, i.e. the output of the linear layers. +# The detailed code is not important, but feel free to take a closer look if interested. + +# %% +############################################################## + + +def plot_dists(val_dict, color="C0", xlabel=None, stat="count", use_kde=True): + columns = len(val_dict) + fig, ax = plt.subplots(1, columns, figsize=(columns * 3, 2.5)) + fig_index = 0 + for key in sorted(val_dict.keys()): + key_ax = ax[fig_index % columns] + sns.histplot( + val_dict[key], + ax=key_ax, + color=color, + bins=50, + stat=stat, + kde=use_kde and ((val_dict[key].max() - val_dict[key].min()) > 1e-8), + ) # Only plot kde if there is variance + hidden_dim_str = ( + r"(%i $\to$ %i)" % (val_dict[key].shape[1], val_dict[key].shape[0]) if len(val_dict[key].shape) > 1 else "" + ) + key_ax.set_title(f"{key} {hidden_dim_str}") + if xlabel is not None: + key_ax.set_xlabel(xlabel) + fig_index += 1 + fig.subplots_adjust(wspace=0.4) + return fig + + +############################################################## + + +def visualize_weight_distribution(model, color="C0"): + weights = {} + for name, param in model.named_parameters(): + if name.endswith(".bias"): + continue + key_name = f"Layer {name.split('.')[1]}" + weights[key_name] = param.detach().view(-1).cpu().numpy() + + # Plotting + fig = plot_dists(weights, color=color, xlabel="Weight vals") + fig.suptitle("Weight distribution", fontsize=14, y=1.05) + plt.show() + plt.close() + + +############################################################## + + +def visualize_gradients(model, color="C0", print_variance=False): + """ + Args: + net: Object of class BaseNetwork + color: Color in which we want to visualize the histogram (for easier separation of activation functions) + """ + model.eval() + small_loader = data.DataLoader(train_set, batch_size=1024, shuffle=False) + imgs, labels = next(iter(small_loader)) + imgs, labels = imgs.to(device), labels.to(device) + + # Pass one batch through the network, and calculate the gradients for the weights + model.zero_grad() + preds = model(imgs) + loss = F.cross_entropy(preds, labels) # Same as nn.CrossEntropyLoss, but as a function instead of module + loss.backward() + # We limit our visualization to the weight parameters and exclude the bias to reduce the number of plots + grads = { + name: params.grad.view(-1).cpu().clone().numpy() + for name, params in model.named_parameters() + if "weight" in name + } + model.zero_grad() + + # Plotting + fig = plot_dists(grads, color=color, xlabel="Grad magnitude") + fig.suptitle("Gradient distribution", fontsize=14, y=1.05) + plt.show() + plt.close() + + if print_variance: + for key in sorted(grads.keys()): + print(f"{key} - Variance: {np.var(grads[key])}") + + +############################################################## + + +def visualize_activations(model, color="C0", print_variance=False): + model.eval() + small_loader = data.DataLoader(train_set, batch_size=1024, shuffle=False) + imgs, labels = next(iter(small_loader)) + imgs, labels = imgs.to(device), labels.to(device) + + # Pass one batch through the network, and calculate the gradients for the weights + feats = imgs.view(imgs.shape[0], -1) + activations = {} + with torch.no_grad(): + for layer_index, layer in enumerate(model.layers): + feats = layer(feats) + if isinstance(layer, nn.Linear): + activations[f"Layer {layer_index}"] = feats.view(-1).detach().cpu().numpy() + + # Plotting + fig = plot_dists(activations, color=color, stat="density", xlabel="Activation vals") + fig.suptitle("Activation distribution", fontsize=14, y=1.05) + plt.show() + plt.close() + + if print_variance: + for key in sorted(activations.keys()): + print(f"{key} - Variance: {np.var(activations[key])}") + + +############################################################## + +# %% [markdown] +# ## Initialization +# +# Before starting our discussion about initialization, it should be noted that there exist many very good blog posts about the topic of neural network initialization (for example [deeplearning.ai](https://www.deeplearning.ai/ai-notes/initialization/), or a more [math-focused blog post](https://pouannes.github.io/blog/initialization)). +# In case something remains unclear after this tutorial, we recommend skimming through these blog posts as well. +# +# When initializing a neural network, there are a few properties we would like to have. +# First, the variance of the input should be propagated through the model to the last layer, so that we have a similar standard deviation for the output neurons. +# If the variance would vanish the deeper we go in our model, it becomes much harder to optimize the model as the input to the next layer is basically a single constant value. +# Similarly, if the variance increases, it is likely to explode (i.e. head to infinity) the deeper we design our model. +# The second property we look out for in initialization techniques is a gradient distribution with equal variance across layers. +# If the first layer receives much smaller gradients than the last layer, we will have difficulties in choosing an appropriate learning rate. +# +# As a starting point for finding a good method, we will analyze different initialization based on our linear neural network with no activation function (i.e. an identity). +# We do this because initializations depend on the specific activation +# function used in the network, and we can adjust the initialization +# schemes later on for our specific choice. + +# %% +model = BaseNetwork(act_fn=Identity()).to(device) + +# %% [markdown] +# ### Constant initialization +# +# The first initialization we can consider is to initialize all weights with the same constant value. +# Intuitively, setting all weights to zero is not a good idea as the propagated gradient will be zero. +# However, what happens if we set all weights to a value slightly larger or smaller than 0? +# To find out, we can implement a function for setting all parameters below and visualize the gradients. + + +# %% +def const_init(model, fill=0.0): + for name, param in model.named_parameters(): + param.data.fill_(fill) + + +const_init(model, fill=0.005) +visualize_gradients(model) +visualize_activations(model, print_variance=True) + +# %% [markdown] +# As we can see, only the first and the last layer have diverse gradient distributions while the other three layers have the same gradient for all weights (note that this value is unequal 0, but often very close to it). +# Having the same gradient for parameters that have been initialized with the same values means that we will always have the same value for those parameters. +# This would make our layer useless and reduce our effective number of parameters to 1. +# Thus, we cannot use a constant initialization to train our networks. + +# %% [markdown] +# ### Constant variance +# +# From the experiment above, we have seen that a constant value is not working. +# So instead, how about we initialize the parameters by randomly sampling from a distribution like a Gaussian? +# The most intuitive way would be to choose one variance that is used for all layers in the network. +# Let's implement it below, and visualize the activation distribution across layers. + + +# %% +def var_init(model, std=0.01): + for name, param in model.named_parameters(): + param.data.normal_(mean=0.0, std=std) + + +var_init(model, std=0.01) +visualize_activations(model, print_variance=True) + +# %% [markdown] +# The variance of the activation becomes smaller and smaller across layers, and almost vanishes in the last layer. +# Alternatively, we could use a higher standard deviation: + +# %% +var_init(model, std=0.1) +visualize_activations(model, print_variance=True) + +# %% [markdown] +# With a higher standard deviation, the activations are likely to explode. +# You can play around with the specific standard deviation values, but it will be hard to find one that gives us a good activation distribution across layers and is very specific to our model. +# If we would change the hidden sizes or number of layers, you would have +# to search all over again, which is neither efficient nor recommended. + +# %% [markdown] +# ### How to find appropriate initialization values +# +# From our experiments above, we have seen that we need to sample the weights from a distribution, but are not sure which one exactly. +# As a next step, we will try to find the optimal initialization from the perspective of the activation distribution. +# For this, we state two requirements: +# +# 1. The mean of the activations should be zero +# 2. The variance of the activations should stay the same across every layer +# +# Suppose we want to design an initialization for the following layer: $y=Wx+b$ with $y\in\mathbb{R}^{d_y}$, $x\in\mathbb{R}^{d_x}$. +# Our goal is that the variance of each element of $y$ is the same as the input, i.e. $\text{Var}(y_i)=\text{Var}(x_i)=\sigma_x^{2}$, and that the mean is zero. +# We assume $x$ to also have a mean of zero, because, in deep neural networks, $y$ would be the input of another layer. +# This requires the bias and weight to have an expectation of 0. +# Actually, as $b$ is a single element per output neuron and is constant across different inputs, we set it to 0 overall. +# +# Next, we need to calculate the variance with which we need to initialize the weight parameters. +# Along the calculation, we will need to following variance rule: given two independent variables, the variance of their product is $\text{Var}(X\cdot Y) = \mathbb{E}(Y)^2\text{Var}(X) + \mathbb{E}(X)^2\text{Var}(Y) + \text{Var}(X)\text{Var}(Y) = \mathbb{E}(Y^2)\mathbb{E}(X^2)-\mathbb{E}(Y)^2\mathbb{E}(X)^2$ ($X$ and $Y$ are not refering to $x$ and $y$, but any random variable). +# +# The needed variance of the weights, $\text{Var}(w_{ij})$, is calculated as follows: +# +# $$ +# \begin{split} +# y_i & = \sum_{j} w_{ij}x_{j}\hspace{10mm}\text{Calculation of a single output neuron without bias}\\ +# \text{Var}(y_i) = \sigma_x^{2} & = \text{Var}\left(\sum_{j} w_{ij}x_{j}\right)\\ +# & = \sum_{j} \text{Var}(w_{ij}x_{j}) \hspace{10mm}\text{Inputs and weights are independent of each other}\\ +# & = \sum_{j} \text{Var}(w_{ij})\cdot\text{Var}(x_{j}) \hspace{10mm}\text{Variance rule (see above) with expectations being zero}\\ +# & = d_x \cdot \text{Var}(w_{ij})\cdot\text{Var}(x_{j}) \hspace{10mm}\text{Variance equal for all $d_x$ elements}\\ +# & = \sigma_x^{2} \cdot d_x \cdot \text{Var}(w_{ij})\\ +# \Rightarrow \text{Var}(w_{ij}) = \sigma_{W}^2 & = \frac{1}{d_x}\\ +# \end{split} +# $$ +# +# Thus, we should initialize the weight distribution with a variance of the inverse of the input dimension $d_x$. +# Let's implement it below and check whether this holds: + + +# %% +def equal_var_init(model): + for name, param in model.named_parameters(): + if name.endswith(".bias"): + param.data.fill_(0) + else: + param.data.normal_(std=1.0 / math.sqrt(param.shape[1])) + + +equal_var_init(model) +visualize_weight_distribution(model) +visualize_activations(model, print_variance=True) + +# %% [markdown] +# As we expected, the variance stays indeed constant across layers. +# Note that our initialization does not restrict us to a normal distribution, but allows any other distribution with a mean of 0 and variance of $1/d_x$. +# You often see that a uniform distribution is used for initialization. +# A small benefit of using a uniform instead of a normal distribution is that we can exclude the chance of initializing very large or small weights. +# +# Besides the variance of the activations, another variance we would like to stabilize is the one of the gradients. +# This ensures a stable optimization for deep networks. +# It turns out that we can do the same calculation as above starting from $\Delta x=W\Delta y$, and come to the conclusion that we should initialize our layers with $1/d_y$ where $d_y$ is the number of output neurons. +# You can do the calculation as a practice, or check a thorough explanation in [this blog post](https://pouannes.github.io/blog/initialization). +# As a compromise between both constraints, [Glorot and Bengio (2010)](http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf?hc_location=ufi) proposed to use the harmonic mean of both values. +# This leads us to the well-known Xavier initialization: +# +# $$W\sim \mathcal{N}\left(0,\frac{2}{d_x+d_y}\right)$$ +# +# If we use a uniform distribution, we would initialize the weights with: +# +# $$W\sim U\left[-\frac{\sqrt{6}}{\sqrt{d_x+d_y}}, \frac{\sqrt{6}}{\sqrt{d_x+d_y}}\right]$$ +# +# Let's shortly implement it and validate its effectiveness: + + +# %% +def xavier_init(model): + for name, param in model.named_parameters(): + if name.endswith(".bias"): + param.data.fill_(0) + else: + bound = math.sqrt(6) / math.sqrt(param.shape[0] + param.shape[1]) + param.data.uniform_(-bound, bound) + + +xavier_init(model) +visualize_gradients(model, print_variance=True) +visualize_activations(model, print_variance=True) + +# %% [markdown] +# We see that the Xavier initialization balances the variance of gradients and activations. +# Note that the significantly higher variance for the output layer is due to the large difference of input and output dimension ($128$ vs $10$). +# However, we currently assumed the activation function to be linear. +# So what happens if we add a non-linearity? +# In a tanh-based network, a common assumption is that for small values during the initial steps in training, the $\tanh$ works as a linear function such that we don't have to adjust our calculation. +# We can check if that is the case for us as well: + +# %% +model = BaseNetwork(act_fn=nn.Tanh()).to(device) +xavier_init(model) +visualize_gradients(model, print_variance=True) +visualize_activations(model, print_variance=True) + +# %% [markdown] +# Although the variance decreases over depth, it is apparent that the activation distribution becomes more focused on the low values. +# Therefore, our variance will stabilize around 0.25 if we would go even deeper. +# Hence, we can conclude that the Xavier initialization works well for Tanh networks. +# But what about ReLU networks? +# Here, we cannot take the previous assumption of the non-linearity becoming linear for small values. +# The ReLU activation function sets (in expectation) half of the inputs to 0 so that also the expectation of the input is not zero. +# However, as long as the expectation of $W$ is zero and $b=0$, the expectation of the output is zero. +# The part where the calculation of the ReLU initialization differs from the identity is when determining $\text{Var}(w_{ij}x_{j})$: +# +# $$\text{Var}(w_{ij}x_{j})=\underbrace{\mathbb{E}[w_{ij}^2]}_{=\text{Var}(w_{ij})}\mathbb{E}[x_{j}^2]-\underbrace{\mathbb{E}[w_{ij}]^2}_{=0}\mathbb{E}[x_{j}]^2=\text{Var}(w_{ij})\mathbb{E}[x_{j}^2]$$ +# +# If we assume now that $x$ is the output of a ReLU activation (from a previous layer, $x=max(0,\tilde{y})$), we can calculate the expectation as follows: +# +# +# $$ +# \begin{split} +# \mathbb{E}[x^2] & =\mathbb{E}[\max(0,\tilde{y})^2]\\ +# & =\frac{1}{2}\mathbb{E}[{\tilde{y}}^2]\hspace{2cm}\tilde{y}\text{ is zero-centered and symmetric}\\ +# & =\frac{1}{2}\text{Var}(\tilde{y}) +# \end{split}$$ +# +# Thus, we see that we have an additional factor of 1/2 in the equation, so that our desired weight variance becomes $2/d_x$. +# This gives us the Kaiming initialization (see [He, K. et al. +# (2015)](https://arxiv.org/pdf/1502.01852.pdf)). +# Note that the Kaiming initialization does not use the harmonic mean between input and output size. +# In their paper (Section 2.2, Backward Propagation, last paragraph), they argue that using $d_x$ or $d_y$ both lead to stable gradients throughout the network, and only depend on the overall input and output size of the network. +# Hence, we can use here only the input $d_x$: + + +# %% +def kaiming_init(model): + for name, param in model.named_parameters(): + if name.endswith(".bias"): + param.data.fill_(0) + elif name.startswith("layers.0"): # The first layer does not have ReLU applied on its input + param.data.normal_(0, 1 / math.sqrt(param.shape[1])) + else: + param.data.normal_(0, math.sqrt(2) / math.sqrt(param.shape[1])) + + +model = BaseNetwork(act_fn=nn.ReLU()).to(device) +kaiming_init(model) +visualize_gradients(model, print_variance=True) +visualize_activations(model, print_variance=True) + +# %% [markdown] +# The variance stays stable across layers. +# We can conclude that the Kaiming initialization indeed works well for ReLU-based networks. +# Note that for Leaky-ReLU etc., we have to slightly adjust the factor of $2$ in the variance as half of the values are not set to zero anymore. +# PyTorch provides a function to calculate this factor for many activation +# function, see `torch.nn.init.calculate_gain` +# ([link](https://pytorch.org/docs/stable/nn.init.html#torch.nn.init.calculate_gain)). + +# %% [markdown] +# ## Optimization +# +#
+# +# Besides initialization, selecting a suitable optimization algorithm can be an important choice for deep neural networks. +# Before taking a closer look at them, we should define code for training the models. +# Most of the following code is copied from the previous tutorial, and only slightly altered to fit our needs. + + +# %% +def _get_config_file(model_path, model_name): + return os.path.join(model_path, model_name + ".config") + + +def _get_model_file(model_path, model_name): + return os.path.join(model_path, model_name + ".tar") + + +def _get_result_file(model_path, model_name): + return os.path.join(model_path, model_name + "_results.json") + + +def load_model(model_path, model_name, net=None): + config_file = _get_config_file(model_path, model_name) + model_file = _get_model_file(model_path, model_name) + assert os.path.isfile( + config_file + ), f'Could not find the config file "{config_file}". Are you sure this is the correct path and you have your model config stored here?' + assert os.path.isfile( + model_file + ), f'Could not find the model file "{model_file}". Are you sure this is the correct path and you have your model stored here?' + with open(config_file) as f: + config_dict = json.load(f) + if net is None: + act_fn_name = config_dict["act_fn"].pop("name").lower() + assert ( + act_fn_name in act_fn_by_name + ), f'Unknown activation function "{act_fn_name}". Please add it to the "act_fn_by_name" dict.' + act_fn = act_fn_by_name[act_fn_name]() + net = BaseNetwork(act_fn=act_fn, **config_dict) + net.load_state_dict(torch.load(model_file)) + return net + + +def save_model(model, model_path, model_name): + config_dict = model.config + os.makedirs(model_path, exist_ok=True) + config_file = _get_config_file(model_path, model_name) + model_file = _get_model_file(model_path, model_name) + with open(config_file, "w") as f: + json.dump(config_dict, f) + torch.save(model.state_dict(), model_file) + + +def train_model(net, model_name, optim_func, max_epochs=50, batch_size=256, overwrite=False): + """Train a model on the training set of FashionMNIST. + + Args: + net: Object of BaseNetwork + model_name: (str) Name of the model, used for creating the checkpoint names + max_epochs: Number of epochs we want to (maximally) train for + patience: If the performance on the validation set has not improved for #patience epochs, we stop training early + batch_size: Size of batches used in training + overwrite: Determines how to handle the case when there already exists a checkpoint. If True, it will be overwritten. Otherwise, we skip training. + """ + file_exists = os.path.isfile(_get_model_file(CHECKPOINT_PATH, model_name)) + if file_exists and not overwrite: + print(f'Model file of "{model_name}" already exists. Skipping training...') + with open(_get_result_file(CHECKPOINT_PATH, model_name)) as f: + results = json.load(f) + else: + if file_exists: + print("Model file exists, but will be overwritten...") + + # Defining optimizer, loss and data loader + optimizer = optim_func(net.parameters()) + loss_module = nn.CrossEntropyLoss() + train_loader_local = data.DataLoader( + train_set, batch_size=batch_size, shuffle=True, drop_last=True, pin_memory=True + ) + + results = None + val_scores = [] + train_losses, train_scores = [], [] + best_val_epoch = -1 + for epoch in range(max_epochs): + train_acc, val_acc, epoch_losses = epoch_iteration( + net, loss_module, optimizer, train_loader_local, val_loader, epoch + ) + train_scores.append(train_acc) + val_scores.append(val_acc) + train_losses += epoch_losses + + if len(val_scores) == 1 or val_acc > val_scores[best_val_epoch]: + print("\t (New best performance, saving model...)") + save_model(net, CHECKPOINT_PATH, model_name) + best_val_epoch = epoch + + if results is None: + load_model(CHECKPOINT_PATH, model_name, net=net) + test_acc = test_model(net, test_loader) + results = { + "test_acc": test_acc, + "val_scores": val_scores, + "train_losses": train_losses, + "train_scores": train_scores, + } + with open(_get_result_file(CHECKPOINT_PATH, model_name), "w") as f: + json.dump(results, f) + + # Plot a curve of the validation accuracy + sns.set() + plt.plot([i for i in range(1, len(results["train_scores"]) + 1)], results["train_scores"], label="Train") + plt.plot([i for i in range(1, len(results["val_scores"]) + 1)], results["val_scores"], label="Val") + plt.xlabel("Epochs") + plt.ylabel("Validation accuracy") + plt.ylim(min(results["val_scores"]), max(results["train_scores"]) * 1.01) + plt.title(f"Validation performance of {model_name}") + plt.legend() + plt.show() + plt.close() + + print((f" Test accuracy: {results['test_acc']*100.0:4.2f}% ").center(50, "=") + "\n") + return results + + +def epoch_iteration(net, loss_module, optimizer, train_loader_local, val_loader, epoch): + ############ + # Training # + ############ + net.train() + true_preds, count = 0.0, 0 + epoch_losses = [] + t = tqdm(train_loader_local, leave=False) + for imgs, labels in t: + imgs, labels = imgs.to(device), labels.to(device) + optimizer.zero_grad() + preds = net(imgs) + loss = loss_module(preds, labels) + loss.backward() + optimizer.step() + # Record statistics during training + true_preds += (preds.argmax(dim=-1) == labels).sum().item() + count += labels.shape[0] + t.set_description(f"Epoch {epoch+1}: loss={loss.item():4.2f}") + epoch_losses.append(loss.item()) + train_acc = true_preds / count + + ############## + # Validation # + ############## + val_acc = test_model(net, val_loader) + print( + f"[Epoch {epoch+1:2i}] Training accuracy: {train_acc*100.0:05.2f}%, Validation accuracy: {val_acc*100.0:05.2f}%" + ) + return train_acc, val_acc, epoch_losses + + +def test_model(net, data_loader): + """Test a model on a specified dataset. + + Args: + net: Trained model of type BaseNetwork + data_loader: DataLoader object of the dataset to test on (validation or test) + """ + net.eval() + true_preds, count = 0.0, 0 + for imgs, labels in data_loader: + imgs, labels = imgs.to(device), labels.to(device) + with torch.no_grad(): + preds = net(imgs).argmax(dim=-1) + true_preds += (preds == labels).sum().item() + count += labels.shape[0] + test_acc = true_preds / count + return test_acc + + +# %% [markdown] +# First, we need to understand what an optimizer actually does. +# The optimizer is responsible to update the network's parameters given the gradients. +# Hence, we effectively implement a function $w^{t} = f(w^{t-1}, g^{t}, ...)$ with $w$ being the parameters, and $g^{t} = \nabla_{w^{(t-1)}} \mathcal{L}^{(t)}$ the gradients at time step $t$. +# A common, additional parameter to this function is the learning rate, here denoted by $\eta$. +# Usually, the learning rate can be seen as the "step size" of the update. +# A higher learning rate means that we change the weights more in the direction of the gradients, a smaller means we take shorter steps. +# +# As most optimizers only differ in the implementation of $f$, we can define a template for an optimizer in PyTorch below. +# We take as input the parameters of a model and a learning rate. +# The function `zero_grad` sets the gradients of all parameters to zero, which we have to do before calling `loss.backward()`. +# Finally, the `step()` function tells the optimizer to update all weights based on their gradients. +# The template is setup below: + + +# %% +class OptimizerTemplate: + def __init__(self, params, lr): + self.params = list(params) + self.lr = lr + + def zero_grad(self): + # Set gradients of all parameters to zero + for p in self.params: + if p.grad is not None: + p.grad.detach_() # For second-order optimizers important + p.grad.zero_() + + @torch.no_grad() + def step(self): + # Apply update step to all parameters + for p in self.params: + if p.grad is None: # We skip parameters without any gradients + continue + self.update_param(p) + + def update_param(self, p): + # To be implemented in optimizer-specific classes + raise NotImplementedError + + +# %% [markdown] +# The first optimizer we are going to implement is the standard Stochastic Gradient Descent (SGD). +# SGD updates the parameters using the following equation: +# +# $$ +# \begin{split} +# w^{(t)} & = w^{(t-1)} - \eta \cdot g^{(t)} +# \end{split} +# $$ +# +# As simple as the equation is also our implementation of SGD: + + +# %% +class SGD(OptimizerTemplate): + def __init__(self, params, lr): + super().__init__(params, lr) + + def update_param(self, p): + p_update = -self.lr * p.grad + p.add_(p_update) # In-place update => saves memory and does not create computation graph + + +# %% [markdown] +# In the lecture, we also have discussed the concept of momentum which replaces the gradient in the update by an exponential average of all past gradients including the current one: +# +# $$ +# \begin{split} +# m^{(t)} & = \beta_1 m^{(t-1)} + (1 - \beta_1)\cdot g^{(t)}\\ +# w^{(t)} & = w^{(t-1)} - \eta \cdot m^{(t)}\\ +# \end{split} +# $$ +# +# Let's also implement it below: + + +# %% +class SGDMomentum(OptimizerTemplate): + def __init__(self, params, lr, momentum=0.0): + super().__init__(params, lr) + self.momentum = momentum # Corresponds to beta_1 in the equation above + self.param_momentum = {p: torch.zeros_like(p.data) for p in self.params} # Dict to store m_t + + def update_param(self, p): + self.param_momentum[p] = (1 - self.momentum) * p.grad + self.momentum * self.param_momentum[p] + p_update = -self.lr * self.param_momentum[p] + p.add_(p_update) + + +# %% [markdown] +# Finally, we arrive at Adam. +# Adam combines the idea of momentum with an adaptive learning rate, which is based on an exponential average of the squared gradients, i.e. the gradients norm. +# Furthermore, we add a bias correction for the momentum and adaptive learning rate for the first iterations: +# +# $$ +# \begin{split} +# m^{(t)} & = \beta_1 m^{(t-1)} + (1 - \beta_1)\cdot g^{(t)}\\ +# v^{(t)} & = \beta_2 v^{(t-1)} + (1 - \beta_2)\cdot \left(g^{(t)}\right)^2\\ +# \hat{m}^{(t)} & = \frac{m^{(t)}}{1-\beta^{t}_1}, \hat{v}^{(t)} = \frac{v^{(t)}}{1-\beta^{t}_2}\\ +# w^{(t)} & = w^{(t-1)} - \frac{\eta}{\sqrt{v^{(t)}} + \epsilon}\circ \hat{m}^{(t)}\\ +# \end{split} +# $$ +# +# Epsilon is a small constant used to improve numerical stability for very small gradient norms. +# Remember that the adaptive learning rate does not replace the learning +# rate hyperparameter $\eta$, but rather acts as an extra factor and +# ensures that the gradients of various parameters have a similar norm. + + +# %% +class Adam(OptimizerTemplate): + def __init__(self, params, lr, beta1=0.9, beta2=0.999, eps=1e-8): + super().__init__(params, lr) + self.beta1 = beta1 + self.beta2 = beta2 + self.eps = eps + self.param_step = {p: 0 for p in self.params} # Remembers "t" for each parameter for bias correction + self.param_momentum = {p: torch.zeros_like(p.data) for p in self.params} + self.param_2nd_momentum = {p: torch.zeros_like(p.data) for p in self.params} + + def update_param(self, p): + self.param_step[p] += 1 + + self.param_momentum[p] = (1 - self.beta1) * p.grad + self.beta1 * self.param_momentum[p] + self.param_2nd_momentum[p] = (1 - self.beta2) * (p.grad) ** 2 + self.beta2 * self.param_2nd_momentum[p] + + bias_correction_1 = 1 - self.beta1 ** self.param_step[p] + bias_correction_2 = 1 - self.beta2 ** self.param_step[p] + + p_2nd_mom = self.param_2nd_momentum[p] / bias_correction_2 + p_mom = self.param_momentum[p] / bias_correction_1 + p_lr = self.lr / (torch.sqrt(p_2nd_mom) + self.eps) + p_update = -p_lr * p_mom + + p.add_(p_update) + + +# %% [markdown] +# ### Comparing optimizers on model training +# +# After we have implemented three optimizers (SGD, SGD with momentum, and Adam), we can start to analyze and compare them. +# First, we test them on how well they can optimize a neural network on the FashionMNIST dataset. +# We use again our linear network, this time with a ReLU activation and the kaiming initialization, which we have found before to work well for ReLU-based networks. +# Note that the model is over-parameterized for this task, and we can achieve similar performance with a much smaller network (for example `100,100,100`). +# However, our main interest is in how well the optimizer can train *deep* +# neural networks, hence the over-parameterization. + +# %% +base_model = BaseNetwork(act_fn=nn.ReLU(), hidden_sizes=[512, 256, 256, 128]) +kaiming_init(base_model) + +# %% [markdown] +# For a fair comparison, we train the exact same model with the same seed with the three optimizers below. +# Feel free to change the hyperparameters if you want (however, you have to train your own model then). + +# %% +SGD_model = copy.deepcopy(base_model).to(device) +SGD_results = train_model( + SGD_model, "FashionMNIST_SGD", lambda params: SGD(params, lr=1e-1), max_epochs=40, batch_size=256 +) + +# %% +SGDMom_model = copy.deepcopy(base_model).to(device) +SGDMom_results = train_model( + SGDMom_model, + "FashionMNIST_SGDMom", + lambda params: SGDMomentum(params, lr=1e-1, momentum=0.9), + max_epochs=40, + batch_size=256, +) + +# %% +Adam_model = copy.deepcopy(base_model).to(device) +Adam_results = train_model( + Adam_model, "FashionMNIST_Adam", lambda params: Adam(params, lr=1e-3), max_epochs=40, batch_size=256 +) + +# %% [markdown] +# The result is that all optimizers perform similarly well with the given model. +# The differences are too small to find any significant conclusion. +# However, keep in mind that this can also be attributed to the initialization we chose. +# When changing the initialization to worse (e.g. constant initialization), Adam usually shows to be more robust because of its adaptive learning rate. +# To show the specific benefits of the optimizers, we will continue to +# look at some possible loss surfaces in which momentum and adaptive +# learning rate are crucial. + +# %% [markdown] +# ### Pathological curvatures +# +# A pathological curvature is a type of surface that is similar to ravines and is particularly tricky for plain SGD optimization. +# In words, pathological curvatures typically have a steep gradient in one direction with an optimum at the center, while in a second direction we have a slower gradient towards a (global) optimum. +# Let's first create an example surface of this and visualize it: + + +# %% +def pathological_curve_loss(w1, w2): + # Example of a pathological curvature. There are many more possible, feel free to experiment here! + x1_loss = torch.tanh(w1) ** 2 + 0.01 * torch.abs(w1) + x2_loss = torch.sigmoid(w2) + return x1_loss + x2_loss + + +# %% +def plot_curve( + curve_fn, x_range=(-5, 5), y_range=(-5, 5), plot_3d=False, cmap=cm.viridis, title="Pathological curvature" +): + fig = plt.figure() + ax = fig.gca() + if plot_3d: + ax = fig.add_subplot(projection="3d") + + x = torch.arange(x_range[0], x_range[1], (x_range[1] - x_range[0]) / 100.0) + y = torch.arange(y_range[0], y_range[1], (y_range[1] - y_range[0]) / 100.0) + x, y = torch.meshgrid([x, y]) + z = curve_fn(x, y) + x, y, z = x.numpy(), y.numpy(), z.numpy() + + if plot_3d: + ax.plot_surface(x, y, z, cmap=cmap, linewidth=1, color="#000", antialiased=False) + ax.set_zlabel("loss") + else: + ax.imshow(z.T[::-1], cmap=cmap, extent=(x_range[0], x_range[1], y_range[0], y_range[1])) + plt.title(title) + ax.set_xlabel(r"$w_1$") + ax.set_ylabel(r"$w_2$") + plt.tight_layout() + return ax + + +sns.reset_orig() +_ = plot_curve(pathological_curve_loss, plot_3d=True) +plt.show() + +# %% [markdown] +# In terms of optimization, you can image that $w_1$ and $w_2$ are weight parameters, and the curvature represents the loss surface over the space of $w_1$ and $w_2$. +# Note that in typical networks, we have many, many more parameters than two, and such curvatures can occur in multi-dimensional spaces as well. +# +# Ideally, our optimization algorithm would find the center of the ravine and focuses on optimizing the parameters towards the direction of $w_2$. +# However, if we encounter a point along the ridges, the gradient is much greater in $w_1$ than $w_2$, and we might end up jumping from one side to the other. +# Due to the large gradients, we would have to reduce our learning rate slowing down learning significantly. +# +# To test our algorithms, we can implement a simple function to train two parameters on such a surface: + + +# %% +def train_curve(optimizer_func, curve_func=pathological_curve_loss, num_updates=100, init=[5, 5]): + """ + Args: + optimizer_func: Constructor of the optimizer to use. Should only take a parameter list + curve_func: Loss function (e.g. pathological curvature) + num_updates: Number of updates/steps to take when optimizing + init: Initial values of parameters. Must be a list/tuple with two elements representing w_1 and w_2 + Returns: + Numpy array of shape [num_updates, 3] with [t,:2] being the parameter values at step t, and [t,2] the loss at t. + """ + weights = nn.Parameter(torch.FloatTensor(init), requires_grad=True) + optim = optimizer_func([weights]) + + list_points = [] + for _ in range(num_updates): + loss = curve_func(weights[0], weights[1]) + list_points.append(torch.cat([weights.data.detach(), loss.unsqueeze(dim=0).detach()], dim=0)) + optim.zero_grad() + loss.backward() + optim.step() + points = torch.stack(list_points, dim=0).numpy() + return points + + +# %% [markdown] +# Next, let's apply the different optimizers on our curvature. +# Note that we set a much higher learning rate for the optimization algorithms as you would in a standard neural network. +# This is because we only have 2 parameters instead of tens of thousands or even millions. + +# %% +SGD_points = train_curve(lambda params: SGD(params, lr=10)) +SGDMom_points = train_curve(lambda params: SGDMomentum(params, lr=10, momentum=0.9)) +Adam_points = train_curve(lambda params: Adam(params, lr=1)) + +# %% [markdown] +# To understand best how the different algorithms worked, we visualize the update step as a line plot through the loss surface. +# We will stick with a 2D representation for readability. + +# %% +all_points = np.concatenate([SGD_points, SGDMom_points, Adam_points], axis=0) +ax = plot_curve( + pathological_curve_loss, + x_range=(-np.absolute(all_points[:, 0]).max(), np.absolute(all_points[:, 0]).max()), + y_range=(all_points[:, 1].min(), all_points[:, 1].max()), + plot_3d=False, +) +ax.plot(SGD_points[:, 0], SGD_points[:, 1], color="red", marker="o", zorder=1, label="SGD") +ax.plot(SGDMom_points[:, 0], SGDMom_points[:, 1], color="blue", marker="o", zorder=2, label="SGDMom") +ax.plot(Adam_points[:, 0], Adam_points[:, 1], color="grey", marker="o", zorder=3, label="Adam") +plt.legend() +plt.show() + +# %% [markdown] +# We can clearly see that SGD is not able to find the center of the optimization curve and has a problem converging due to the steep gradients in $w_1$. +# In contrast, Adam and SGD with momentum nicely converge as the changing direction of $w_1$ is canceling itself out. +# On such surfaces, it is crucial to use momentum. + +# %% [markdown] +# ### Steep optima +# +# A second type of challenging loss surfaces are steep optima. +# In those, we have a larger part of the surface having very small gradients while around the optimum, we have very large gradients. +# For instance, take the following loss surfaces: + + +# %% +def bivar_gaussian(w1, w2, x_mean=0.0, y_mean=0.0, x_sig=1.0, y_sig=1.0): + norm = 1 / (2 * np.pi * x_sig * y_sig) + x_exp = (-1 * (w1 - x_mean) ** 2) / (2 * x_sig**2) + y_exp = (-1 * (w2 - y_mean) ** 2) / (2 * y_sig**2) + return norm * torch.exp(x_exp + y_exp) + + +def comb_func(w1, w2): + z = -bivar_gaussian(w1, w2, x_mean=1.0, y_mean=-0.5, x_sig=0.2, y_sig=0.2) + z -= bivar_gaussian(w1, w2, x_mean=-1.0, y_mean=0.5, x_sig=0.2, y_sig=0.2) + z -= bivar_gaussian(w1, w2, x_mean=-0.5, y_mean=-0.8, x_sig=0.2, y_sig=0.2) + return z + + +_ = plot_curve(comb_func, x_range=(-2, 2), y_range=(-2, 2), plot_3d=True, title="Steep optima") + +# %% [markdown] +# Most of the loss surface has very little to no gradients. +# However, close to the optima, we have very steep gradients. +# To reach the minimum when starting in a region with lower gradients, we expect an adaptive learning rate to be crucial. +# To verify this hypothesis, we can run our three optimizers on the surface: + +# %% +SGD_points = train_curve(lambda params: SGD(params, lr=0.5), comb_func, init=[0, 0]) +SGDMom_points = train_curve(lambda params: SGDMomentum(params, lr=1, momentum=0.9), comb_func, init=[0, 0]) +Adam_points = train_curve(lambda params: Adam(params, lr=0.2), comb_func, init=[0, 0]) + +all_points = np.concatenate([SGD_points, SGDMom_points, Adam_points], axis=0) +ax = plot_curve(comb_func, x_range=(-2, 2), y_range=(-2, 2), plot_3d=False, title="Steep optima") +ax.plot(SGD_points[:, 0], SGD_points[:, 1], color="red", marker="o", zorder=3, label="SGD", alpha=0.7) +ax.plot(SGDMom_points[:, 0], SGDMom_points[:, 1], color="blue", marker="o", zorder=2, label="SGDMom", alpha=0.7) +ax.plot(Adam_points[:, 0], Adam_points[:, 1], color="grey", marker="o", zorder=1, label="Adam", alpha=0.7) +ax.set_xlim(-2, 2) +ax.set_ylim(-2, 2) +plt.legend() +plt.show() + +# %% [markdown] +# SGD first takes very small steps until it touches the border of the optimum. +# First reaching a point around $(-0.75,-0.5)$, the gradient direction has changed and pushes the parameters to $(0.8,0.5)$ from which SGD cannot recover anymore (only with many, many steps). +# A similar problem has SGD with momentum, only that it continues the direction of the touch of the optimum. +# The gradients from this time step are so much larger than any other point that the momentum $m_t$ is overpowered by it. +# Finally, Adam is able to converge in the optimum showing the importance of adaptive learning rates. + +# %% [markdown] +# ### What optimizer to take +# +# After seeing the results on optimization, what is our conclusion? +# Should we always use Adam and never look at SGD anymore? +# The short answer: no. +# There are many papers saying that in certain situations, SGD (with momentum) generalizes better where Adam often tends to overfit [5,6]. +# This is related to the idea of finding wider optima. +# For instance, see the illustration of different optima below (credit: [Keskar et al., 2017](https://arxiv.org/pdf/1609.04836.pdf)): +# +#
+# +# The black line represents the training loss surface, while the dotted red line is the test loss. +# Finding sharp, narrow minima can be helpful for finding the minimal training loss. +# However, this doesn't mean that it also minimizes the test loss as especially flat minima have shown to generalize better. +# You can imagine that the test dataset has a slightly shifted loss surface due to the different examples than in the training set. +# A small change can have a significant influence for sharp minima, while flat minima are generally more robust to this change. +# +# In the next tutorial, we will see that some network types can still be better optimized with SGD and learning rate scheduling than Adam. +# Nevertheless, Adam is the most commonly used optimizer in Deep Learning +# as it usually performs better than other optimizers, especially for deep +# networks. + +# %% [markdown] +# ## Conclusion +# +# In this tutorial, we have looked at initialization and optimization techniques for neural networks. +# We have seen that a good initialization has to balance the preservation of the gradient variance as well as the activation variance. +# This can be achieved with the Xavier initialization for tanh-based networks, and the Kaiming initialization for ReLU-based networks. +# In optimization, concepts like momentum and adaptive learning rate can help with challenging loss surfaces but don't guarantee an increase in performance for neural networks. +# +# +# ## References +# +# [1] Glorot, Xavier, and Yoshua Bengio. +# "Understanding the difficulty of training deep feedforward neural networks." +# Proceedings of the thirteenth international conference on artificial intelligence and statistics. +# 2010. +# [link](http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf) +# +# [2] He, Kaiming, et al. +# "Delving deep into rectifiers: Surpassing human-level performance on imagenet classification." +# Proceedings of the IEEE international conference on computer vision. +# 2015. +# [link](https://www.cv-foundation.org/openaccess/content_iccv_2015/html/He_Delving_Deep_into_ICCV_2015_paper.html) +# +# [3] Kingma, Diederik P. & Ba, Jimmy. +# "Adam: A Method for Stochastic Optimization." +# Proceedings of the third international conference for learning representations (ICLR). +# 2015. +# [link](https://arxiv.org/abs/1412.6980) +# +# [4] Keskar, Nitish Shirish, et al. +# "On large-batch training for deep learning: Generalization gap and sharp minima." +# Proceedings of the fifth international conference for learning representations (ICLR). +# 2017. +# [link](https://arxiv.org/abs/1609.04836) +# +# [5] Wilson, Ashia C., et al. +# "The Marginal Value of Adaptive Gradient Methods in Machine Learning." +# Advances in neural information processing systems. +# 2017. +# [link](https://papers.nips.cc/paper/7003-the-marginal-value-of-adaptive-gradient-methods-in-machine-learning.pdf) +# +# [6] Ruder, Sebastian. +# "An overview of gradient descent optimization algorithms." +# arXiv preprint. +# 2017. +# [link](https://arxiv.org/abs/1609.04747) diff --git a/_notebooks/course_UvA-DL/03-initialization-and-optimization/flat_vs_sharp_minima.svg b/_notebooks/course_UvA-DL/03-initialization-and-optimization/flat_vs_sharp_minima.svg new file mode 100644 index 0000000..c7b6225 --- /dev/null +++ b/_notebooks/course_UvA-DL/03-initialization-and-optimization/flat_vs_sharp_minima.svg @@ -0,0 +1,1456 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/.meta.yaml b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/.meta.yaml new file mode 100644 index 0000000..dc7b7b0 --- /dev/null +++ b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/.meta.yaml @@ -0,0 +1,23 @@ +title: "Tutorial 4: Inception, ResNet and DenseNet" +author: Phillip Lippe +created: 2021-08-27 +updated: 2023-03-14 +license: CC BY-SA +tags: + - Image +description: | + In this tutorial, we will implement and discuss variants of modern CNN architectures. + There have been many different architectures been proposed over the past few years. + Some of the most impactful ones, and still relevant today, are the following: [GoogleNet](https://arxiv.org/abs/1409.4842)/Inception architecture (winner of ILSVRC 2014), [ResNet](https://arxiv.org/abs/1512.03385) (winner of ILSVRC 2015), and [DenseNet](https://arxiv.org/abs/1608.06993) (best paper award CVPR 2017). + All of them were state-of-the-art models when being proposed, and the core ideas of these networks are the foundations for most current state-of-the-art architectures. + Thus, it is important to understand these architectures in detail and learn how to implement them. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - tabulate + - lightning>=2.0.0rc0 +accelerator: + - GPU diff --git a/docs/_static/images/course_UvA-DL/04-inception-resnet-densenet.jpg b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/04-inception-resnet-densenet.jpg rename to _notebooks/course_UvA-DL/04-inception-resnet-densenet/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/Inception_ResNet_DenseNet.py b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/Inception_ResNet_DenseNet.py new file mode 100644 index 0000000..ffee8ff --- /dev/null +++ b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/Inception_ResNet_DenseNet.py @@ -0,0 +1,1207 @@ +# %% [markdown] +#
+# Let's start with importing our standard libraries here. + +# %% +import os +import urllib.request +from types import SimpleNamespace +from urllib.error import HTTPError + +import lightning as L +import matplotlib +import matplotlib.pyplot as plt +import matplotlib_inline.backend_inline +import numpy as np +import seaborn as sns +import tabulate +import torch +import torch.nn as nn +import torch.optim as optim +import torch.utils.data as data +import torchvision + +# %matplotlib inline +from IPython.display import HTML, display +from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint +from PIL import Image +from torchvision import transforms +from torchvision.datasets import CIFAR10 + +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +matplotlib.rcParams["lines.linewidth"] = 2.0 +sns.reset_orig() + +# PyTorch +# Torchvision + +# %% [markdown] +# We will use the same `set_seed` function as in the previous tutorials, as well as the path variables `DATASET_PATH` and `CHECKPOINT_PATH`. +# Adjust the paths if necessary. + +# %% +# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/ConvNets") + + +# Function for setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu") + +# %% [markdown] +# We also have pretrained models and Tensorboards (more on this later) for this tutorial, and download them below. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/" +# Files to download +pretrained_files = [ + "GoogleNet.ckpt", + "ResNet.ckpt", + "ResNetPreAct.ckpt", + "DenseNet.ckpt", + "tensorboards/GoogleNet/events.out.tfevents.googlenet", + "tensorboards/ResNet/events.out.tfevents.resnet", + "tensorboards/ResNetPreAct/events.out.tfevents.resnetpreact", + "tensorboards/DenseNet/events.out.tfevents.densenet", +] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if "/" in file_name: + os.makedirs(file_path.rsplit("/", 1)[0], exist_ok=True) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print(f"Downloading {file_url}...") + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# Throughout this tutorial, we will train and evaluate the models on the CIFAR10 dataset. +# This allows you to compare the results obtained here with the model you have implemented in the first assignment. +# As we have learned from the previous tutorial about initialization, it is important to have the data preprocessed with a zero mean. +# Therefore, as a first step, we will calculate the mean and standard deviation of the CIFAR dataset: + +# %% +train_dataset = CIFAR10(root=DATASET_PATH, train=True, download=True) +DATA_MEANS = (train_dataset.data / 255.0).mean(axis=(0, 1, 2)) +DATA_STD = (train_dataset.data / 255.0).std(axis=(0, 1, 2)) +print("Data mean", DATA_MEANS) +print("Data std", DATA_STD) + +# %% [markdown] +# We will use this information to define a `transforms.Normalize` module which will normalize our data accordingly. +# Additionally, we will use data augmentation during training. +# This reduces the risk of overfitting and helps CNNs to generalize better. +# Specifically, we will apply two random augmentations. +# +# First, we will flip each image horizontally by a chance of 50% (`transforms.RandomHorizontalFlip`). +# The object class usually does not change when flipping an image, and we don't expect any image information to be dependent on the horizontal orientation. +# This would be however different if we would try to detect digits or letters in an image, as those have a certain orientation. +# +# The second augmentation we use is called `transforms.RandomResizedCrop`. +# This transformation scales the image in a small range, while eventually changing the aspect ratio, and crops it afterward in the previous size. +# Therefore, the actual pixel values change while the content or overall semantics of the image stays the same. +# +# We will randomly split the training dataset into a training and a validation set. +# The validation set will be used for determining early stopping. +# After finishing the training, we test the models on the CIFAR test set. + +# %% +test_transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(DATA_MEANS, DATA_STD)]) +# For training, we add some augmentation. Networks are too powerful and would overfit. +train_transform = transforms.Compose( + [ + transforms.RandomHorizontalFlip(), + transforms.RandomResizedCrop((32, 32), scale=(0.8, 1.0), ratio=(0.9, 1.1)), + transforms.ToTensor(), + transforms.Normalize(DATA_MEANS, DATA_STD), + ] +) +# Loading the training dataset. We need to split it into a training and validation part +# We need to do a little trick because the validation set should not use the augmentation. +train_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=train_transform, download=True) +val_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=test_transform, download=True) +L.seed_everything(42) +train_set, _ = torch.utils.data.random_split(train_dataset, [45000, 5000]) +L.seed_everything(42) +_, val_set = torch.utils.data.random_split(val_dataset, [45000, 5000]) + +# Loading the test set +test_set = CIFAR10(root=DATASET_PATH, train=False, transform=test_transform, download=True) + +# We define a set of data loaders that we can use for various purposes later. +train_loader = data.DataLoader(train_set, batch_size=128, shuffle=True, drop_last=True, pin_memory=True, num_workers=4) +val_loader = data.DataLoader(val_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4) +test_loader = data.DataLoader(test_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4) + +# %% [markdown] +# To verify that our normalization works, we can print out the mean and standard deviation of the single batch. +# The mean should be close to 0 and the standard deviation close to 1 for each channel: + +# %% +imgs, _ = next(iter(train_loader)) +print("Batch mean", imgs.mean(dim=[0, 2, 3])) +print("Batch std", imgs.std(dim=[0, 2, 3])) + +# %% [markdown] +# Finally, let's visualize a few images from the training set, and how they look like after random data augmentation: + +# %% +NUM_IMAGES = 4 +images = [train_dataset[idx][0] for idx in range(NUM_IMAGES)] +orig_images = [Image.fromarray(train_dataset.data[idx]) for idx in range(NUM_IMAGES)] +orig_images = [test_transform(img) for img in orig_images] + +img_grid = torchvision.utils.make_grid(torch.stack(images + orig_images, dim=0), nrow=4, normalize=True, pad_value=0.5) +img_grid = img_grid.permute(1, 2, 0) + +plt.figure(figsize=(8, 8)) +plt.title("Augmentation examples on CIFAR10") +plt.imshow(img_grid) +plt.axis("off") +plt.show() +plt.close() + +# %% [markdown] +# ## PyTorch Lightning +# +# In this notebook and in many following ones, we will make use of the library [PyTorch Lightning](https://www.lightning.ai/docs/pytorch/stable). +# PyTorch Lightning is a framework that simplifies your code needed to train, evaluate, and test a model in PyTorch. +# It also handles logging into [TensorBoard](https://pytorch.org/tutorials/intermediate/tensorboard_tutorial.html), a visualization toolkit for ML experiments, and saving model checkpoints automatically with minimal code overhead from our side. +# This is extremely helpful for us as we want to focus on implementing different model architectures and spend little time on other code overhead. +# Note that at the time of writing/teaching, the framework has been released in version 1.3. +# Future versions might have a slightly changed interface and thus might not work perfectly with the code (we will try to keep it up-to-date as much as possible). +# +# Now, we will take the first step in PyTorch Lightning, and continue to explore the framework in our other tutorials. +# PyTorch Lightning comes with a lot of useful functions, such as one for setting the seed as we have seen before: + +# %% +# Setting the seed +L.seed_everything(42) + +# %% [markdown] +# Thus, in the future, we don't have to define our own `set_seed` function anymore. +# +# In PyTorch Lightning, we define `L.LightningModule`'s (inheriting from `Module`) that organize our code into 5 main sections: +# +# 1. Initialization (`__init__`), where we create all necessary parameters/models +# 2. Optimizers (`configure_optimizers`) where we create the optimizers, learning rate scheduler, etc. +# 3. +# Training loop (`training_step`) where we only have to define the loss calculation for a single batch (the loop of optimizer.zero_grad(), loss.backward() and optimizer.step(), as well as any logging/saving operation, is done in the background) +# 4. +# Validation loop (`validation_step`) where similarly to the training, we only have to define what should happen per step +# 5. Test loop (`test_step`) which is the same as validation, only on a test set. +# +# Therefore, we don't abstract the PyTorch code, but rather organize it and define some default operations that are commonly used. +# If you need to change something else in your training/validation/test loop, there are many possible functions you can overwrite (see the [docs](https://lightning.ai/docs/pytorch/stable/common/lightning_module.html) for details). +# +# Now we can look at an example of how a Lightning Module for training a CNN looks like: + + +# %% +class CIFARModule(L.LightningModule): + def __init__(self, model_name, model_hparams, optimizer_name, optimizer_hparams): + """ + Inputs: + model_name - Name of the model/CNN to run. Used for creating the model (see function below) + model_hparams - Hyperparameters for the model, as dictionary. + optimizer_name - Name of the optimizer to use. Currently supported: Adam, SGD + optimizer_hparams - Hyperparameters for the optimizer, as dictionary. This includes learning rate, weight decay, etc. + """ + super().__init__() + # Exports the hyperparameters to a YAML file, and create "self.hparams" namespace + self.save_hyperparameters() + # Create model + self.model = create_model(model_name, model_hparams) + # Create loss module + self.loss_module = nn.CrossEntropyLoss() + # Example input for visualizing the graph in Tensorboard + self.example_input_array = torch.zeros((1, 3, 32, 32), dtype=torch.float32) + + def forward(self, imgs): + # Forward function that is run when visualizing the graph + return self.model(imgs) + + def configure_optimizers(self): + # We will support Adam or SGD as optimizers. + if self.hparams.optimizer_name == "Adam": + # AdamW is Adam with a correct implementation of weight decay (see here + # for details: https://arxiv.org/pdf/1711.05101.pdf) + optimizer = optim.AdamW(self.parameters(), **self.hparams.optimizer_hparams) + elif self.hparams.optimizer_name == "SGD": + optimizer = optim.SGD(self.parameters(), **self.hparams.optimizer_hparams) + else: + assert False, f'Unknown optimizer: "{self.hparams.optimizer_name}"' + + # We will reduce the learning rate by 0.1 after 100 and 150 epochs + scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[100, 150], gamma=0.1) + return [optimizer], [scheduler] + + def training_step(self, batch, batch_idx): + # "batch" is the output of the training data loader. + imgs, labels = batch + preds = self.model(imgs) + loss = self.loss_module(preds, labels) + acc = (preds.argmax(dim=-1) == labels).float().mean() + + # Logs the accuracy per epoch to tensorboard (weighted average over batches) + self.log("train_acc", acc, on_step=False, on_epoch=True) + self.log("train_loss", loss) + return loss # Return tensor to call ".backward" on + + def validation_step(self, batch, batch_idx): + imgs, labels = batch + preds = self.model(imgs).argmax(dim=-1) + acc = (labels == preds).float().mean() + # By default logs it per epoch (weighted average over batches) + self.log("val_acc", acc) + + def test_step(self, batch, batch_idx): + imgs, labels = batch + preds = self.model(imgs).argmax(dim=-1) + acc = (labels == preds).float().mean() + # By default logs it per epoch (weighted average over batches), and returns it afterwards + self.log("test_acc", acc) + + +# %% [markdown] +# We see that the code is organized and clear, which helps if someone else tries to understand your code. +# +# Another important part of PyTorch Lightning is the concept of callbacks. +# Callbacks are self-contained functions that contain the non-essential logic of your Lightning Module. +# They are usually called after finishing a training epoch, but can also influence other parts of your training loop. +# For instance, we will use the following two pre-defined callbacks: `LearningRateMonitor` and `ModelCheckpoint`. +# The learning rate monitor adds the current learning rate to our TensorBoard, which helps to verify that our learning rate scheduler works correctly. +# The model checkpoint callback allows you to customize the saving routine of your checkpoints. +# For instance, how many checkpoints to keep, when to save, which metric to look out for, etc. +# We import them below: + +# %% +# Callbacks + +# %% [markdown] +# To allow running multiple different models with the same Lightning module, we define a function below that maps a model name to the model class. +# At this stage, the dictionary `model_dict` is empty, but we will fill it throughout the notebook with our new models. + +# %% +model_dict = {} + + +def create_model(model_name, model_hparams): + if model_name in model_dict: + return model_dict[model_name](**model_hparams) + else: + assert False, f'Unknown model name "{model_name}". Available models are: {str(model_dict.keys())}' + + +# %% [markdown] +# Similarly, to use the activation function as another hyperparameter in +# our model, we define a "name to function" dict below: + +# %% +act_fn_by_name = {"tanh": nn.Tanh, "relu": nn.ReLU, "leakyrelu": nn.LeakyReLU, "gelu": nn.GELU} + +# %% [markdown] +# If we pass the classes or objects directly as an argument to the Lightning module, we couldn't take advantage of PyTorch Lightning's automatically hyperparameter saving and loading. +# +# Besides the Lightning module, the second most important module in PyTorch Lightning is the `Trainer`. +# The trainer is responsible to execute the training steps defined in the Lightning module and completes the framework. +# Similar to the Lightning module, you can override any key part that you don't want to be automated, but the default settings are often the best practice to do. +# For a full overview, see the [documentation](https://lightning.ai/docs/pytorch/stable/common/trainer.html). +# The most important functions we use below are: +# +# * `trainer.fit`: Takes as input a lightning module, a training dataset, and an (optional) validation dataset. +# This function trains the given module on the training dataset with occasional validation (default once per epoch, can be changed) +# * `trainer.test`: Takes as input a model and a dataset on which we want to test. +# It returns the test metric on the dataset. +# +# For training and testing, we don't have to worry about things like setting the model to eval mode (`model.eval()`) as this is all done automatically. +# See below how we define a training function for our models: + + +# %% +def train_model(model_name, save_name=None, **kwargs): + """ + Inputs: + model_name - Name of the model you want to run. Is used to look up the class in "model_dict" + save_name (optional) - If specified, this name will be used for creating the checkpoint and logging directory. + """ + if save_name is None: + save_name = model_name + + # Create a PyTorch Lightning trainer with the generation callback + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, save_name), # Where to save models + # We run on a single GPU (if possible) + accelerator="auto", + devices=1, + # How many epochs to train for if no patience is set + max_epochs=180, + callbacks=[ + ModelCheckpoint( + save_weights_only=True, mode="max", monitor="val_acc" + ), # Save the best checkpoint based on the maximum val_acc recorded. Saves only weights and not optimizer + LearningRateMonitor("epoch"), + ], # Log learning rate every epoch + ) # In case your notebook crashes due to the progress bar, consider increasing the refresh rate + trainer.logger._log_graph = True # If True, we plot the computation graph in tensorboard + trainer.logger._default_hp_metric = None # Optional logging argument that we don't need + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, save_name + ".ckpt") + if os.path.isfile(pretrained_filename): + print(f"Found pretrained model at {pretrained_filename}, loading...") + # Automatically loads the model with the saved hyperparameters + model = CIFARModule.load_from_checkpoint(pretrained_filename) + else: + L.seed_everything(42) # To be reproducable + model = CIFARModule(model_name=model_name, **kwargs) + trainer.fit(model, train_loader, val_loader) + model = CIFARModule.load_from_checkpoint( + trainer.checkpoint_callback.best_model_path + ) # Load best checkpoint after training + + # Test best model on validation and test set + val_result = trainer.test(model, dataloaders=val_loader, verbose=False) + test_result = trainer.test(model, dataloaders=test_loader, verbose=False) + result = {"test": test_result[0]["test_acc"], "val": val_result[0]["test_acc"]} + + return model, result + + +# %% [markdown] +# Finally, we can focus on the Convolutional Neural Networks we want to +# implement today: GoogleNet, ResNet, and DenseNet. + +# %% [markdown] +# ## Inception +# +#
+# +# The [GoogleNet](https://arxiv.org/abs/1409.4842), proposed in 2014, won the ImageNet Challenge because of its usage of the Inception modules. +# In general, we will mainly focus on the concept of Inception in this tutorial instead of the specifics of the GoogleNet, as based on Inception, there have been many follow-up works ([Inception-v2](https://arxiv.org/abs/1512.00567), [Inception-v3](https://arxiv.org/abs/1512.00567), [Inception-v4](https://arxiv.org/abs/1602.07261), [Inception-ResNet](https://arxiv.org/abs/1602.07261),...). +# The follow-up works mainly focus on increasing efficiency and enabling very deep Inception networks. +# However, for a fundamental understanding, it is sufficient to look at the original Inception block. +# +# An Inception block applies four convolution blocks separately on the same feature map: a 1x1, 3x3, and 5x5 convolution, and a max pool operation. +# This allows the network to look at the same data with different receptive fields. +# Of course, learning only 5x5 convolution would be theoretically more powerful. +# However, this is not only more computation and memory heavy but also tends to overfit much easier. +# The overall inception block looks like below (figure credit - [Szegedy et al. ](https://arxiv.org/abs/1409.4842)): +# +#
+# +# The additional 1x1 convolutions before the 3x3 and 5x5 convolutions are used for dimensionality reduction. +# This is especially crucial as the feature maps of all branches are merged afterward, and we don't want any explosion of feature size. +# As 5x5 convolutions are 25 times more expensive than 1x1 convolutions, we can save a lot of computation and parameters by reducing the dimensionality before the large convolutions. +# +# We can now try to implement the Inception Block ourselves: + + +# %% +class InceptionBlock(nn.Module): + def __init__(self, c_in, c_red: dict, c_out: dict, act_fn): + """ + Inputs: + c_in - Number of input feature maps from the previous layers + c_red - Dictionary with keys "3x3" and "5x5" specifying the output of the dimensionality reducing 1x1 convolutions + c_out - Dictionary with keys "1x1", "3x3", "5x5", and "max" + act_fn - Activation class constructor (e.g. nn.ReLU) + """ + super().__init__() + + # 1x1 convolution branch + self.conv_1x1 = nn.Sequential( + nn.Conv2d(c_in, c_out["1x1"], kernel_size=1), nn.BatchNorm2d(c_out["1x1"]), act_fn() + ) + + # 3x3 convolution branch + self.conv_3x3 = nn.Sequential( + nn.Conv2d(c_in, c_red["3x3"], kernel_size=1), + nn.BatchNorm2d(c_red["3x3"]), + act_fn(), + nn.Conv2d(c_red["3x3"], c_out["3x3"], kernel_size=3, padding=1), + nn.BatchNorm2d(c_out["3x3"]), + act_fn(), + ) + + # 5x5 convolution branch + self.conv_5x5 = nn.Sequential( + nn.Conv2d(c_in, c_red["5x5"], kernel_size=1), + nn.BatchNorm2d(c_red["5x5"]), + act_fn(), + nn.Conv2d(c_red["5x5"], c_out["5x5"], kernel_size=5, padding=2), + nn.BatchNorm2d(c_out["5x5"]), + act_fn(), + ) + + # Max-pool branch + self.max_pool = nn.Sequential( + nn.MaxPool2d(kernel_size=3, padding=1, stride=1), + nn.Conv2d(c_in, c_out["max"], kernel_size=1), + nn.BatchNorm2d(c_out["max"]), + act_fn(), + ) + + def forward(self, x): + x_1x1 = self.conv_1x1(x) + x_3x3 = self.conv_3x3(x) + x_5x5 = self.conv_5x5(x) + x_max = self.max_pool(x) + x_out = torch.cat([x_1x1, x_3x3, x_5x5, x_max], dim=1) + return x_out + + +# %% [markdown] +# The GoogleNet architecture consists of stacking multiple Inception blocks with occasional max pooling to reduce the height and width of the feature maps. +# The original GoogleNet was designed for image sizes of ImageNet (224x224 pixels) and had almost 7 million parameters. +# As we train on CIFAR10 with image sizes of 32x32, we don't require such a heavy architecture, and instead, apply a reduced version. +# The number of channels for dimensionality reduction and output per filter (1x1, 3x3, 5x5, and max pooling) need to be manually specified and can be changed if interested. +# The general intuition is to have the most filters for the 3x3 +# convolutions, as they are powerful enough to take the context into +# account while requiring almost a third of the parameters of the 5x5 +# convolution. + + +# %% +class GoogleNet(nn.Module): + def __init__(self, num_classes=10, act_fn_name="relu", **kwargs): + super().__init__() + self.hparams = SimpleNamespace( + num_classes=num_classes, act_fn_name=act_fn_name, act_fn=act_fn_by_name[act_fn_name] + ) + self._create_network() + self._init_params() + + def _create_network(self): + # A first convolution on the original image to scale up the channel size + self.input_net = nn.Sequential( + nn.Conv2d(3, 64, kernel_size=3, padding=1), nn.BatchNorm2d(64), self.hparams.act_fn() + ) + # Stacking inception blocks + self.inception_blocks = nn.Sequential( + InceptionBlock( + 64, + c_red={"3x3": 32, "5x5": 16}, + c_out={"1x1": 16, "3x3": 32, "5x5": 8, "max": 8}, + act_fn=self.hparams.act_fn, + ), + InceptionBlock( + 64, + c_red={"3x3": 32, "5x5": 16}, + c_out={"1x1": 24, "3x3": 48, "5x5": 12, "max": 12}, + act_fn=self.hparams.act_fn, + ), + nn.MaxPool2d(3, stride=2, padding=1), # 32x32 => 16x16 + InceptionBlock( + 96, + c_red={"3x3": 32, "5x5": 16}, + c_out={"1x1": 24, "3x3": 48, "5x5": 12, "max": 12}, + act_fn=self.hparams.act_fn, + ), + InceptionBlock( + 96, + c_red={"3x3": 32, "5x5": 16}, + c_out={"1x1": 16, "3x3": 48, "5x5": 16, "max": 16}, + act_fn=self.hparams.act_fn, + ), + InceptionBlock( + 96, + c_red={"3x3": 32, "5x5": 16}, + c_out={"1x1": 16, "3x3": 48, "5x5": 16, "max": 16}, + act_fn=self.hparams.act_fn, + ), + InceptionBlock( + 96, + c_red={"3x3": 32, "5x5": 16}, + c_out={"1x1": 32, "3x3": 48, "5x5": 24, "max": 24}, + act_fn=self.hparams.act_fn, + ), + nn.MaxPool2d(3, stride=2, padding=1), # 16x16 => 8x8 + InceptionBlock( + 128, + c_red={"3x3": 48, "5x5": 16}, + c_out={"1x1": 32, "3x3": 64, "5x5": 16, "max": 16}, + act_fn=self.hparams.act_fn, + ), + InceptionBlock( + 128, + c_red={"3x3": 48, "5x5": 16}, + c_out={"1x1": 32, "3x3": 64, "5x5": 16, "max": 16}, + act_fn=self.hparams.act_fn, + ), + ) + # Mapping to classification output + self.output_net = nn.Sequential( + nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(128, self.hparams.num_classes) + ) + + def _init_params(self): + # Based on our discussion in Tutorial 4, we should initialize the + # convolutions according to the activation function + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, nonlinearity=self.hparams.act_fn_name) + elif isinstance(m, nn.BatchNorm2d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + def forward(self, x): + x = self.input_net(x) + x = self.inception_blocks(x) + x = self.output_net(x) + return x + + +# %% [markdown] +# Now, we can integrate our model to the model dictionary we defined above: + +# %% +model_dict["GoogleNet"] = GoogleNet + +# %% [markdown] +# The training of the model is handled by PyTorch Lightning, and we just have to define the command to start. +# Note that we train for almost 200 epochs, which takes about an hour on Lisa's default GPUs (GTX1080Ti). +# We would recommend using the saved models and train your own model if you are interested. + +# %% +googlenet_model, googlenet_results = train_model( + model_name="GoogleNet", + model_hparams={"num_classes": 10, "act_fn_name": "relu"}, + optimizer_name="Adam", + optimizer_hparams={"lr": 1e-3, "weight_decay": 1e-4}, +) + +# %% [markdown] +# We will compare the results later in the notebooks, but we can already print them here for a first glance: + +# %% +print("GoogleNet Results", googlenet_results) + +# %% [markdown] +# ### Tensorboard log +# +# A nice extra of PyTorch Lightning is the automatic logging into TensorBoard. +# To give you a better intuition of what TensorBoard can be used, we can look at the board that PyTorch Lightning has been generated when training the GoogleNet. +# TensorBoard provides an inline functionality for Jupyter notebooks, and we use it here: + +# %% +# Import tensorboard +# %load_ext tensorboard + +# %% +# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH! +# %tensorboard --logdir ../saved_models/tutorial5/tensorboards/GoogleNet/ + +# %% [markdown] +#
+# +# TensorBoard is organized in multiple tabs. +# The main tab is the scalar tab where we can log the development of single numbers. +# For example, we have plotted the training loss, accuracy, learning rate, etc. +# If we look at the training or validation accuracy, we can really see the impact of using a learning rate scheduler. +# Reducing the learning rate gives our model a nice increase in training performance. +# Similarly, when looking at the training loss, we see a sudden decrease at this point. +# However, the high numbers on the training set compared to validation indicate that our model was overfitting which is inevitable for such large networks. +# +# Another interesting tab in TensorBoard is the graph tab. +# It shows us the network architecture organized by building blocks from the input to the output. +# It basically shows the operations taken in the forward step of `CIFARModule`. +# Double-click on a module to open it. +# Feel free to explore the architecture from a different perspective. +# The graph visualization can often help you to validate that your model +# is actually doing what it is supposed to do, and you don't miss any +# layers in the computation graph. + +# %% [markdown] +# ## ResNet +# +# The [ResNet](https://arxiv.org/abs/1512.03385) paper is one of the [most cited AI papers](https://www.natureindex.com/news-blog/google-scholar-reveals-most-influential-papers-research-citations-twenty-twenty), and has been the foundation for neural networks with more than 1,000 layers. +# Despite its simplicity, the idea of residual connections is highly effective as it supports stable gradient propagation through the network. +# Instead of modeling $x_{l+1}=F(x_{l})$, we model $x_{l+1}=x_{l}+F(x_{l})$ where $F$ is a non-linear mapping (usually a sequence of NN modules likes convolutions, activation functions, and normalizations). +# If we do backpropagation on such residual connections, we obtain: +# +# $$\frac{\partial x_{l+1}}{\partial x_{l}} = \mathbf{I} + \frac{\partial F(x_{l})}{\partial x_{l}}$$ +# +# The bias towards the identity matrix guarantees a stable gradient propagation being less effected by $F$ itself. +# There have been many variants of ResNet proposed, which mostly concern the function $F$, or operations applied on the sum. +# In this tutorial, we look at two of them: the original ResNet block, and the [Pre-Activation ResNet block](https://arxiv.org/abs/1603.05027). +# We visually compare the blocks below (figure credit - [He et al. ](https://arxiv.org/abs/1603.05027)): +# +#
+# +# The original ResNet block applies a non-linear activation function, usually ReLU, after the skip connection. +# In contrast, the pre-activation ResNet block applies the non-linearity at the beginning of $F$. +# Both have their advantages and disadvantages. +# For very deep network, however, the pre-activation ResNet has shown to perform better as the gradient flow is guaranteed to have the identity matrix as calculated above, and is not harmed by any non-linear activation applied to it. +# For comparison, in this notebook, we implement both ResNet types as shallow networks. +# +# Let's start with the original ResNet block. +# The visualization above already shows what layers are included in $F$. +# One special case we have to handle is when we want to reduce the image dimensions in terms of width and height. +# The basic ResNet block requires $F(x_{l})$ to be of the same shape as $x_{l}$. +# Thus, we need to change the dimensionality of $x_{l}$ as well before adding to $F(x_{l})$. +# The original implementation used an identity mapping with stride 2 and padded additional feature dimensions with 0. +# However, the more common implementation is to use a 1x1 convolution with stride 2 as it allows us to change the feature dimensionality while being efficient in parameter and computation cost. +# The code for the ResNet block is relatively simple, and shown below: + +# %% + + +class ResNetBlock(nn.Module): + def __init__(self, c_in, act_fn, subsample=False, c_out=-1): + """ + Inputs: + c_in - Number of input features + act_fn - Activation class constructor (e.g. nn.ReLU) + subsample - If True, we want to apply a stride inside the block and reduce the output shape by 2 in height and width + c_out - Number of output features. Note that this is only relevant if subsample is True, as otherwise, c_out = c_in + """ + super().__init__() + if not subsample: + c_out = c_in + + # Network representing F + self.net = nn.Sequential( + nn.Conv2d( + c_in, c_out, kernel_size=3, padding=1, stride=1 if not subsample else 2, bias=False + ), # No bias needed as the Batch Norm handles it + nn.BatchNorm2d(c_out), + act_fn(), + nn.Conv2d(c_out, c_out, kernel_size=3, padding=1, bias=False), + nn.BatchNorm2d(c_out), + ) + + # 1x1 convolution with stride 2 means we take the upper left value, and transform it to new output size + self.downsample = nn.Conv2d(c_in, c_out, kernel_size=1, stride=2) if subsample else None + self.act_fn = act_fn() + + def forward(self, x): + z = self.net(x) + if self.downsample is not None: + x = self.downsample(x) + out = z + x + out = self.act_fn(out) + return out + + +# %% [markdown] +# The second block we implement is the pre-activation ResNet block. +# For this, we have to change the order of layer in `self.net`, and do not apply an activation function on the output. +# Additionally, the downsampling operation has to apply a non-linearity as well as the input, $x_l$, has not been processed by a non-linearity yet. +# Hence, the block looks as follows: + + +# %% +class PreActResNetBlock(nn.Module): + def __init__(self, c_in, act_fn, subsample=False, c_out=-1): + """ + Inputs: + c_in - Number of input features + act_fn - Activation class constructor (e.g. nn.ReLU) + subsample - If True, we want to apply a stride inside the block and reduce the output shape by 2 in height and width + c_out - Number of output features. Note that this is only relevant if subsample is True, as otherwise, c_out = c_in + """ + super().__init__() + if not subsample: + c_out = c_in + + # Network representing F + self.net = nn.Sequential( + nn.BatchNorm2d(c_in), + act_fn(), + nn.Conv2d(c_in, c_out, kernel_size=3, padding=1, stride=1 if not subsample else 2, bias=False), + nn.BatchNorm2d(c_out), + act_fn(), + nn.Conv2d(c_out, c_out, kernel_size=3, padding=1, bias=False), + ) + + # 1x1 convolution needs to apply non-linearity as well as not done on skip connection + self.downsample = ( + nn.Sequential(nn.BatchNorm2d(c_in), act_fn(), nn.Conv2d(c_in, c_out, kernel_size=1, stride=2, bias=False)) + if subsample + else None + ) + + def forward(self, x): + z = self.net(x) + if self.downsample is not None: + x = self.downsample(x) + out = z + x + return out + + +# %% [markdown] +# Similarly to the model selection, we define a dictionary to create a mapping from string to block class. +# We will use the string name as hyperparameter value in our model to choose between the ResNet blocks. +# Feel free to implement any other ResNet block type and add it here as well. + +# %% +resnet_blocks_by_name = {"ResNetBlock": ResNetBlock, "PreActResNetBlock": PreActResNetBlock} + +# %% [markdown] +# The overall ResNet architecture consists of stacking multiple ResNet blocks, of which some are downsampling the input. +# When talking about ResNet blocks in the whole network, we usually group them by the same output shape. +# Hence, if we say the ResNet has `[3,3,3]` blocks, it means that we have 3 times a group of 3 ResNet blocks, where a subsampling is taking place in the fourth and seventh block. +# The ResNet with `[3,3,3]` blocks on CIFAR10 is visualized below. +# +#
+# +# The three groups operate on the resolutions $32\times32$, $16\times16$ and $8\times8$ respectively. +# The blocks in orange denote ResNet blocks with downsampling. +# The same notation is used by many other implementations such as in the [torchvision library](https://pytorch.org/vision/0.11/models.html#torchvision.models.resnet18) from PyTorch. +# Thus, our code looks as follows: + + +# %% +class ResNet(nn.Module): + def __init__( + self, + num_classes=10, + num_blocks=[3, 3, 3], + c_hidden=[16, 32, 64], + act_fn_name="relu", + block_name="ResNetBlock", + **kwargs, + ): + """ + Inputs: + num_classes - Number of classification outputs (10 for CIFAR10) + num_blocks - List with the number of ResNet blocks to use. The first block of each group uses downsampling, except the first. + c_hidden - List with the hidden dimensionalities in the different blocks. Usually multiplied by 2 the deeper we go. + act_fn_name - Name of the activation function to use, looked up in "act_fn_by_name" + block_name - Name of the ResNet block, looked up in "resnet_blocks_by_name" + """ + super().__init__() + assert block_name in resnet_blocks_by_name + self.hparams = SimpleNamespace( + num_classes=num_classes, + c_hidden=c_hidden, + num_blocks=num_blocks, + act_fn_name=act_fn_name, + act_fn=act_fn_by_name[act_fn_name], + block_class=resnet_blocks_by_name[block_name], + ) + self._create_network() + self._init_params() + + def _create_network(self): + c_hidden = self.hparams.c_hidden + + # A first convolution on the original image to scale up the channel size + if self.hparams.block_class == PreActResNetBlock: # => Don't apply non-linearity on output + self.input_net = nn.Sequential(nn.Conv2d(3, c_hidden[0], kernel_size=3, padding=1, bias=False)) + else: + self.input_net = nn.Sequential( + nn.Conv2d(3, c_hidden[0], kernel_size=3, padding=1, bias=False), + nn.BatchNorm2d(c_hidden[0]), + self.hparams.act_fn(), + ) + + # Creating the ResNet blocks + blocks = [] + for block_idx, block_count in enumerate(self.hparams.num_blocks): + for bc in range(block_count): + # Subsample the first block of each group, except the very first one. + subsample = bc == 0 and block_idx > 0 + blocks.append( + self.hparams.block_class( + c_in=c_hidden[block_idx if not subsample else (block_idx - 1)], + act_fn=self.hparams.act_fn, + subsample=subsample, + c_out=c_hidden[block_idx], + ) + ) + self.blocks = nn.Sequential(*blocks) + + # Mapping to classification output + self.output_net = nn.Sequential( + nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(c_hidden[-1], self.hparams.num_classes) + ) + + def _init_params(self): + # Based on our discussion in Tutorial 4, we should initialize the convolutions according to the activation function + # Fan-out focuses on the gradient distribution, and is commonly used in ResNets + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity=self.hparams.act_fn_name) + elif isinstance(m, nn.BatchNorm2d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + def forward(self, x): + x = self.input_net(x) + x = self.blocks(x) + x = self.output_net(x) + return x + + +# %% [markdown] +# We also need to add the new ResNet class to our model dictionary: + +# %% +model_dict["ResNet"] = ResNet + +# %% [markdown] +# Finally, we can train our ResNet models. +# One difference to the GoogleNet training is that we explicitly use SGD with Momentum as optimizer instead of Adam. +# Adam often leads to a slightly worse accuracy on plain, shallow ResNets. +# It is not 100% clear why Adam performs worse in this context, but one possible explanation is related to ResNet's loss surface. +# ResNet has been shown to produce smoother loss surfaces than networks without skip connection (see [Li et al., 2018](https://arxiv.org/pdf/1712.09913.pdf) for details). +# A possible visualization of the loss surface with/out skip connections is below (figure credit - [Li et al. ](https://arxiv.org/pdf/1712.09913.pdf)): +# +#
+# +# The $x$ and $y$ axis shows a projection of the parameter space, and the $z$ axis shows the loss values achieved by different parameter values. +# On smooth surfaces like the one on the right, we might not require an adaptive learning rate as Adam provides. +# Instead, Adam can get stuck in local optima while SGD finds the wider minima that tend to generalize better. +# However, to answer this question in detail, we would need an extra tutorial because it is not easy to answer. +# For now, we conclude: for ResNet architectures, consider the optimizer to be an important hyperparameter, and try training with both Adam and SGD. +# Let's train the model below with SGD: + +# %% +resnet_model, resnet_results = train_model( + model_name="ResNet", + model_hparams={"num_classes": 10, "c_hidden": [16, 32, 64], "num_blocks": [3, 3, 3], "act_fn_name": "relu"}, + optimizer_name="SGD", + optimizer_hparams={"lr": 0.1, "momentum": 0.9, "weight_decay": 1e-4}, +) + +# %% [markdown] +# Let's also train the pre-activation ResNet as comparison: + +# %% +resnetpreact_model, resnetpreact_results = train_model( + model_name="ResNet", + model_hparams={ + "num_classes": 10, + "c_hidden": [16, 32, 64], + "num_blocks": [3, 3, 3], + "act_fn_name": "relu", + "block_name": "PreActResNetBlock", + }, + optimizer_name="SGD", + optimizer_hparams={"lr": 0.1, "momentum": 0.9, "weight_decay": 1e-4}, + save_name="ResNetPreAct", +) + +# %% [markdown] +# ### Tensorboard log +# +# Similarly to our GoogleNet model, we also have a TensorBoard log for the ResNet model. We can open it below. + +# %% +# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH! Feel free to change "ResNet" to "ResNetPreAct" +# %tensorboard --logdir ../saved_models/tutorial5/tensorboards/ResNet/ + +# %% [markdown] +#
+# +# Feel free to explore the TensorBoard yourself, including the computation graph. +# In general, we can see that with SGD, the ResNet has a higher training loss than the GoogleNet in the first stage of the training. +# After reducing the learning rate however, the model achieves even higher validation accuracies. +# We compare the precise scores at the end of the notebook. + +# %% [markdown] +# ## DenseNet +# +#
+# +# [DenseNet](https://arxiv.org/abs/1608.06993) is another architecture for enabling very deep neural networks and takes a slightly different perspective on residual connections. +# Instead of modeling the difference between layers, DenseNet considers residual connections as a possible way to reuse features across layers, removing any necessity to learn redundant feature maps. +# If we go deeper into the network, the model learns abstract features to recognize patterns. +# However, some complex patterns consist of a combination of abstract features (e.g. hand, face, etc. +# ), and low-level features (e.g. edges, basic color, etc.). +# To find these low-level features in the deep layers, standard CNNs have to learn copy such feature maps, which wastes a lot of parameter complexity. +# DenseNet provides an efficient way of reusing features by having each convolution depends on all previous input features, but add only a small amount of filters to it. +# See the figure below for an illustration (figure credit - [Hu et al. ](https://arxiv.org/abs/1608.06993)): +# +#
+# +# The last layer, called the transition layer, is responsible for reducing the dimensionality of the feature maps in height, width, and channel size. +# Although those technically break the identity backpropagation, there are only a few in a network so that it doesn't affect the gradient flow much. +# +# We split the implementation of the layers in DenseNet into three parts: a `DenseLayer`, and a `DenseBlock`, and a `TransitionLayer`. +# The module `DenseLayer` implements a single layer inside a dense block. +# It applies a 1x1 convolution for dimensionality reduction with a subsequential 3x3 convolution. +# The output channels are concatenated to the originals and returned. +# Note that we apply the Batch Normalization as the first layer of each block. +# This allows slightly different activations for the same features to different layers, depending on what is needed. +# Overall, we can implement it as follows: + + +# %% +class DenseLayer(nn.Module): + def __init__(self, c_in, bn_size, growth_rate, act_fn): + """ + Inputs: + c_in - Number of input channels + bn_size - Bottleneck size (factor of growth rate) for the output of the 1x1 convolution. Typically between 2 and 4. + growth_rate - Number of output channels of the 3x3 convolution + act_fn - Activation class constructor (e.g. nn.ReLU) + """ + super().__init__() + self.net = nn.Sequential( + nn.BatchNorm2d(c_in), + act_fn(), + nn.Conv2d(c_in, bn_size * growth_rate, kernel_size=1, bias=False), + nn.BatchNorm2d(bn_size * growth_rate), + act_fn(), + nn.Conv2d(bn_size * growth_rate, growth_rate, kernel_size=3, padding=1, bias=False), + ) + + def forward(self, x): + out = self.net(x) + out = torch.cat([out, x], dim=1) + return out + + +# %% [markdown] +# The module `DenseBlock` summarizes multiple dense layers applied in sequence. +# Each dense layer takes as input the original input concatenated with all previous layers' feature maps: + + +# %% +class DenseBlock(nn.Module): + def __init__(self, c_in, num_layers, bn_size, growth_rate, act_fn): + """ + Inputs: + c_in - Number of input channels + num_layers - Number of dense layers to apply in the block + bn_size - Bottleneck size to use in the dense layers + growth_rate - Growth rate to use in the dense layers + act_fn - Activation function to use in the dense layers + """ + super().__init__() + layers = [] + for layer_idx in range(num_layers): + # Input channels are original plus the feature maps from previous layers + layer_c_in = c_in + layer_idx * growth_rate + layers.append(DenseLayer(c_in=layer_c_in, bn_size=bn_size, growth_rate=growth_rate, act_fn=act_fn)) + self.block = nn.Sequential(*layers) + + def forward(self, x): + out = self.block(x) + return out + + +# %% [markdown] +# Finally, the `TransitionLayer` takes as input the final output of a dense block and reduces its channel dimensionality using a 1x1 convolution. +# To reduce the height and width dimension, we take a slightly different approach than in ResNet and apply an average pooling with kernel size 2 and stride 2. +# This is because we don't have an additional connection to the output that would consider the full 2x2 patch instead of a single value. +# Besides, it is more parameter efficient than using a 3x3 convolution with stride 2. +# Thus, the layer is implemented as follows: + + +# %% +class TransitionLayer(nn.Module): + def __init__(self, c_in, c_out, act_fn): + super().__init__() + self.transition = nn.Sequential( + nn.BatchNorm2d(c_in), + act_fn(), + nn.Conv2d(c_in, c_out, kernel_size=1, bias=False), + nn.AvgPool2d(kernel_size=2, stride=2), # Average the output for each 2x2 pixel group + ) + + def forward(self, x): + return self.transition(x) + + +# %% [markdown] +# Now we can put everything together and create our DenseNet. +# To specify the number of layers, we use a similar notation as in ResNets and pass on a list of ints representing the number of layers per block. +# After each dense block except the last one, we apply a transition layer to reduce the dimensionality by 2. + + +# %% +class DenseNet(nn.Module): + def __init__( + self, num_classes=10, num_layers=[6, 6, 6, 6], bn_size=2, growth_rate=16, act_fn_name="relu", **kwargs + ): + super().__init__() + self.hparams = SimpleNamespace( + num_classes=num_classes, + num_layers=num_layers, + bn_size=bn_size, + growth_rate=growth_rate, + act_fn_name=act_fn_name, + act_fn=act_fn_by_name[act_fn_name], + ) + self._create_network() + self._init_params() + + def _create_network(self): + c_hidden = self.hparams.growth_rate * self.hparams.bn_size # The start number of hidden channels + + # A first convolution on the original image to scale up the channel size + self.input_net = nn.Sequential( + # No batch norm or activation function as done inside the Dense layers + nn.Conv2d(3, c_hidden, kernel_size=3, padding=1) + ) + + # Creating the dense blocks, eventually including transition layers + blocks = [] + for block_idx, num_layers in enumerate(self.hparams.num_layers): + blocks.append( + DenseBlock( + c_in=c_hidden, + num_layers=num_layers, + bn_size=self.hparams.bn_size, + growth_rate=self.hparams.growth_rate, + act_fn=self.hparams.act_fn, + ) + ) + c_hidden = c_hidden + num_layers * self.hparams.growth_rate # Overall output of the dense block + if block_idx < len(self.hparams.num_layers) - 1: # Don't apply transition layer on last block + blocks.append(TransitionLayer(c_in=c_hidden, c_out=c_hidden // 2, act_fn=self.hparams.act_fn)) + c_hidden = c_hidden // 2 + + self.blocks = nn.Sequential(*blocks) + + # Mapping to classification output + self.output_net = nn.Sequential( + nn.BatchNorm2d(c_hidden), # The features have not passed a non-linearity until here. + self.hparams.act_fn(), + nn.AdaptiveAvgPool2d((1, 1)), + nn.Flatten(), + nn.Linear(c_hidden, self.hparams.num_classes), + ) + + def _init_params(self): + # Based on our discussion in Tutorial 4, we should initialize the + # convolutions according to the activation function + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, nonlinearity=self.hparams.act_fn_name) + elif isinstance(m, nn.BatchNorm2d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + def forward(self, x): + x = self.input_net(x) + x = self.blocks(x) + x = self.output_net(x) + return x + + +# %% [markdown] +# Let's also add the DenseNet to our model dictionary: + +# %% +model_dict["DenseNet"] = DenseNet + +# %% [markdown] +# Lastly, we train our network. +# In contrast to ResNet, DenseNet does not show any issues with Adam, and hence we train it with this optimizer. +# The other hyperparameters are chosen to result in a network with a similar parameter size as the ResNet and GoogleNet. +# Commonly, when designing very deep networks, DenseNet is more parameter +# efficient than ResNet while achieving a similar or even better +# performance. + +# %% +densenet_model, densenet_results = train_model( + model_name="DenseNet", + model_hparams={ + "num_classes": 10, + "num_layers": [6, 6, 6, 6], + "bn_size": 2, + "growth_rate": 16, + "act_fn_name": "relu", + }, + optimizer_name="Adam", + optimizer_hparams={"lr": 1e-3, "weight_decay": 1e-4}, +) + +# %% [markdown] +# ### Tensorboard log +# +# Finally, we also have another TensorBoard for the DenseNet training. We take a look at it below: + +# %% +# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH! Feel free to change "ResNet" to "ResNetPreAct" +# %tensorboard --logdir ../saved_models/tutorial5/tensorboards/DenseNet/ + +# %% [markdown] +#
+# +# The overall course of the validation accuracy and training loss resemble the training of GoogleNet, which is also related to training the network with Adam. +# Feel free to explore the training metrics yourself. + +# %% [markdown] +# ## Conclusion and Comparison +# +# After discussing each model separately, and training all of them, we can finally compare them. +# First, let's organize the results of all models in a table: + +# %% language="html" +# +# + +# %% +all_models = [ + ("GoogleNet", googlenet_results, googlenet_model), + ("ResNet", resnet_results, resnet_model), + ("ResNetPreAct", resnetpreact_results, resnetpreact_model), + ("DenseNet", densenet_results, densenet_model), +] +table = [ + [ + model_name, + f"{100.0*model_results['val']:4.2f}%", + f"{100.0*model_results['test']:4.2f}%", + f"{sum(np.prod(p.shape) for p in model.parameters()):,}", + ] + for model_name, model_results, model in all_models +] +display( + HTML( + tabulate.tabulate(table, tablefmt="html", headers=["Model", "Val Accuracy", "Test Accuracy", "Num Parameters"]) + ) +) + +# %% [markdown] +# First of all, we see that all models are performing reasonably well. +# Simple models as you have implemented them in the practical achieve considerably lower performance, which is beside the lower number of parameters also attributed to the architecture design choice. +# GoogleNet is the model to obtain the lowest performance on the validation and test set, although it is very close to DenseNet. +# A proper hyperparameter search over all the channel sizes in GoogleNet would likely improve the accuracy of the model to a similar level, but this is also expensive given a large number of hyperparameters. +# ResNet outperforms both DenseNet and GoogleNet by more than 1% on the validation set, while there is a minor difference between both versions, original and pre-activation. +# We can conclude that for shallow networks, the place of the activation function does not seem to be crucial, although papers have reported the contrary for very deep networks (e.g. [He et al. ](https://arxiv.org/abs/1603.05027)). +# +# In general, we can conclude that ResNet is a simple, but powerful architecture. +# If we would apply the models on more complex tasks with larger images and more layers inside the networks, we would likely see a bigger gap between GoogleNet and skip-connection architectures like ResNet and DenseNet. +# A comparison with deeper models on CIFAR10 can be for example found [here](https://github.com/kuangliu/pytorch-cifar). +# Interestingly, DenseNet outperforms the original ResNet on their setup but comes closely behind the Pre-Activation ResNet. +# The best model, a Dual Path Network ([Chen et. +# al](https://arxiv.org/abs/1707.01629)), is actually a combination of +# ResNet and DenseNet showing that both offer different advantages. + +# %% [markdown] +# ### Which model should I choose for my task? +# +# We have reviewed four different models. +# So, which one should we choose if have given a new task? +# Usually, starting with a ResNet is a good idea given the superior performance of the CIFAR dataset and its simple implementation. +# Besides, for the parameter number we have chosen here, ResNet is the fastest as DenseNet and GoogleNet have many more layers that are applied in sequence in our primitive implementation. +# However, if you have a really difficult task, such as semantic +# segmentation on HD images, more complex variants of ResNet and DenseNet +# are recommended. diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/densenet_block.svg b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/densenet_block.svg new file mode 100644 index 0000000..7b7c5a5 --- /dev/null +++ b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/densenet_block.svg @@ -0,0 +1 @@ + diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/inception_block.svg b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/inception_block.svg new file mode 100644 index 0000000..be62455 --- /dev/null +++ b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/inception_block.svg @@ -0,0 +1,1290 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_block.svg b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_block.svg new file mode 100644 index 0000000..f5977dc --- /dev/null +++ b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_block.svg @@ -0,0 +1,1194 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_loss_surface.png b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_loss_surface.png new file mode 100644 index 0000000..01a97d8 Binary files /dev/null and b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_loss_surface.png differ diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_notation.svg b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_notation.svg new file mode 100644 index 0000000..959a4f0 --- /dev/null +++ b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/resnet_notation.svg @@ -0,0 +1,3 @@ + + +
ResNet Block 1
ResNet Block 1
ResNet Block 2
ResNet Block 2
ResNet Block 3
ResNet Block 3
ResNet Block 4
ResNet Block 4
ResNet Block 5
ResNet Block 5
ResNet Block 6
ResNet Block 6
ResNet Block 7
ResNet Block 7
ResNet Block 8
ResNet Block 8
ResNet Block 9
ResNet Block 9
Group 1 (32x32)
Group 1 (32x32)
Group 2 (16x16)
Group 2 (16x16)
Group 3 (8x8)
Group 3 (8x8)
diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_DenseNet.png b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_DenseNet.png new file mode 100644 index 0000000..7302773 Binary files /dev/null and b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_DenseNet.png differ diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_GoogleNet.png b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_GoogleNet.png new file mode 100644 index 0000000..36341ce Binary files /dev/null and b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_GoogleNet.png differ diff --git a/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_ResNet.png b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_ResNet.png new file mode 100644 index 0000000..e40f156 Binary files /dev/null and b/_notebooks/course_UvA-DL/04-inception-resnet-densenet/tensorboard_screenshot_ResNet.png differ diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/.meta.yml b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/.meta.yml new file mode 100644 index 0000000..0c8a0ee --- /dev/null +++ b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/.meta.yml @@ -0,0 +1,24 @@ +title: "Tutorial 5: Transformers and Multi-Head Attention" +author: Phillip Lippe +created: 2021-06-30 +updated: 2023-03-14 +license: CC BY-SA +build: 0 +tags: + - Text +description: | + In this tutorial, we will discuss one of the most impactful architectures of the last 2 years: the Transformer model. + Since the paper Attention Is All You Need by Vaswani et al. had been published in 2017, + the Transformer architecture has continued to beat benchmarks in many domains, most importantly in Natural Language Processing. + Transformers with an incredible amount of parameters can generate long, convincing essays, and opened up new application fields of AI. + As the hype of the Transformer architecture seems not to come to an end in the next years, + it is important to understand how it works, and have implemented it yourself, which we will do in this notebook. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - GPU diff --git a/docs/_static/images/course_UvA-DL/05-transformers-and-MH-attention.jpg b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/05-transformers-and-MH-attention.jpg rename to _notebooks/course_UvA-DL/05-transformers-and-MH-attention/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/Transformers_MHAttention.py b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/Transformers_MHAttention.py new file mode 100644 index 0000000..f74ed35 --- /dev/null +++ b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/Transformers_MHAttention.py @@ -0,0 +1,1606 @@ +# %% [markdown] +#
+# Despite the huge success of Transformers in NLP, we will _not_ include the NLP domain in our notebook here. +# There are many courses at the University of Amsterdam that focus on Natural Language Processing +# and take a closer look at the application of the Transformer architecture in NLP +# ([NLP2](https://studiegids.uva.nl/xmlpages/page/2020-2021/zoek-vak/vak/79628), +# [Advanced Topics in Computational Semantics](https://studiegids.uva.nl/xmlpages/page/2020-2021/zoek-vak/vak/80162)). +# Furthermore, and most importantly, there is so much more to the Transformer architecture. +# NLP is the domain the Transformer architecture has been originally proposed for and had the greatest impact on, +# but it also accelerated research in other domains, recently even [Computer Vision](https://arxiv.org/abs/2010.11929). +# Thus, we focus here on what makes the Transformer and self-attention so powerful in general. +# In a second notebook, we will look at Vision Transformers, i.e. Transformers for image classification +# ([link to notebook](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial15/Vision_Transformer.html)). +# +# Below, we import our standard libraries. + +# %% +# Standard libraries +import math +import os +import urllib.request +from functools import partial +from urllib.error import HTTPError + +# PyTorch Lightning +import lightning as L + +# Plotting +import matplotlib +import matplotlib.pyplot as plt +import matplotlib_inline.backend_inline +import numpy as np +import seaborn as sns + +# PyTorch +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data as data + +# Torchvision +import torchvision +from lightning.pytorch.callbacks import ModelCheckpoint +from torchvision import transforms +from torchvision.datasets import CIFAR100 +from tqdm.notebook import tqdm + +plt.set_cmap("cividis") +# %matplotlib inline +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +matplotlib.rcParams["lines.linewidth"] = 2.0 +sns.reset_orig() + +# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/Transformers/") + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu") +print("Device:", device) + +# %% [markdown] +# Two pre-trained models are downloaded below. +# Make sure to have adjusted your `CHECKPOINT_PATH` before running this code if not already done. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial6/" +# Files to download +pretrained_files = ["ReverseTask.ckpt", "SetAnomalyTask.ckpt"] + +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if "/" in file_name: + os.makedirs(file_path.rsplit("/", 1)[0], exist_ok=True) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print("Downloading %s..." % file_url) + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file manually," + " or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# ## The Transformer architecture +# +# In the first part of this notebook, we will implement the Transformer architecture by hand. +# As the architecture is so popular, there already exists a Pytorch module `nn.Transformer` +# ([documentation](https://pytorch.org/docs/stable/generated/torch.nn.Transformer.html)) +# and a [tutorial](https://pytorch.org/tutorials/beginner/transformer_tutorial.html) +# on how to use it for next token prediction. +# However, we will implement it here ourselves, to get through to the smallest details. +# +# There are of course many more tutorials out there about attention and Transformers. +# Below, we list a few that are worth exploring if you are interested in the topic +# and might want yet another perspective on the topic after this one: +# +# * [Transformer: A Novel Neural Network Architecture for Language Understanding +# (Jakob Uszkoreit, 2017)](https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html) - The original Google blog post about the Transformer paper, focusing on the application in machine translation. +# * [The Illustrated Transformer (Jay Alammar, 2018)](http://jalammar.github.io/illustrated-transformer/) - A very popular and great blog post intuitively explaining the Transformer architecture with many nice visualizations. +# The focus is on NLP. +# * [Attention? +# Attention! +# (Lilian Weng, 2018)](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html) - A nice blog post summarizing attention mechanisms in many domains including vision. +# * [Illustrated: Self-Attention (Raimi Karim, 2019)](https://towardsdatascience.com/illustrated-self-attention-2d627e33b20a) - A nice visualization of the steps of self-attention. +# Recommended going through if the explanation below is too abstract for you. +# * [The Transformer family (Lilian Weng, 2020)](https://lilianweng.github.io/lil-log/2020/04/07/the-transformer-family.html) - A very detailed blog post reviewing more variants of Transformers besides the original one. + +# %% [markdown] +# ### What is Attention? +# +# The attention mechanism describes a recent new group of layers in neural networks that has attracted +# a lot of interest in the past few years, especially in sequence tasks. +# There are a lot of different possible definitions of "attention" in the literature, +# but the one we will use here is the following: _the attention mechanism describes a weighted average +# of (sequence) elements with the weights dynamically computed based on an input query and elements' keys_. +# So what does this exactly mean? +# The goal is to take an average over the features of multiple elements. +# However, instead of weighting each element equally, we want to weight them depending on their actual values. +# In other words, we want to dynamically decide on which inputs we want to "attend" more than others. +# In particular, an attention mechanism has usually four parts we need to specify: +# +# * **Query**: The query is a feature vector that describes what we are looking for in the sequence, i.e. what would we maybe want to pay attention to. +# * **Keys**: For each input element, we have a key which is again a feature vector. +# This feature vector roughly describes what the element is "offering", or when it might be important. +# The keys should be designed such that we can identify the elements we want to pay attention to based on the query. +# * **Values**: For each input element, we also have a value vector. +# This feature vector is the one we want to average over. +# * **Score function**: To rate which elements we want to pay attention to, we need to specify a score function $f_{attn}$. +# The score function takes the query and a key as input, and output the score/attention weight of the query-key pair. +# It is usually implemented by simple similarity metrics like a dot product, or a small MLP. +# +# +# The weights of the average are calculated by a softmax over all score function outputs. +# Hence, we assign those value vectors a higher weight whose corresponding key is most similar to the query. +# If we try to describe it with pseudo-math, we can write: +# +# $$ +# \alpha_i = \frac{\exp\left(f_{attn}\left(\text{key}_i, \text{query}\right)\right)}{\sum_j \exp\left(f_{attn}\left(\text{key}_j, \text{query}\right)\right)}, \hspace{5mm} \text{out} = \sum_i \alpha_i \cdot \text{value}_i +# $$ +# +# Visually, we can show the attention over a sequence of words as follows: +# +#
+# +# For every word, we have one key and one value vector. +# The query is compared to all keys with a score function (in this case the dot product) to determine the weights. +# The softmax is not visualized for simplicity. +# Finally, the value vectors of all words are averaged using the attention weights. +# +# Most attention mechanisms differ in terms of what queries they use, how the key and value vectors are defined, +# and what score function is used. +# The attention applied inside the Transformer architecture is called **self-attention**. +# In self-attention, each sequence element provides a key, value, and query. +# For each element, we perform an attention layer where based on its query, +# we check the similarity of the all sequence elements' keys, and returned a different, +# averaged value vector for each element. +# We will now go into a bit more detail by first looking at the specific implementation of the attention mechanism +# which is in the Transformer case the scaled dot product attention. + +# %% [markdown] +# ### Scaled Dot Product Attention +# +# The core concept behind self-attention is the scaled dot product attention. +# Our goal is to have an attention mechanism with which any element in a sequence can attend to any other while +# still being efficient to compute. +# The dot product attention takes as input a set of queries +# $Q\in\mathbb{R}^{T\times d_k}$, keys $K\in\mathbb{R}^{T\times d_k}$ +# and values $V\in\mathbb{R}^{T\times d_v}$ where $T$ is the sequence length, +# and $d_k$ and $d_v$ are the hidden dimensionality for queries/keys and values respectively. +# For simplicity, we neglect the batch dimension for now. +# The attention value from element $i$ to $j$ is based on its similarity of the query $Q_i$ and key $K_j$, +# using the dot product as the similarity metric. +# In math, we calculate the dot product attention as follows: +# +# $$\text{Attention}(Q,K,V)=\text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$ +# +# The matrix multiplication $QK^T$ performs the dot product for every possible pair of queries and keys, +# resulting in a matrix of the shape $T\times T$. +# Each row represents the attention logits for a specific element $i$ to all other elements in the sequence. +# On these, we apply a softmax and multiply with the value vector to obtain a weighted mean +# (the weights being determined by the attention). +# Another perspective on this attention mechanism offers the computation graph which is visualized below +# (figure credit - [Vaswani et al., 2017](https://arxiv.org/abs/1706.03762)). +# +#
+# +# One aspect we haven't discussed yet is the scaling factor of $1/\sqrt{d_k}$. +# This scaling factor is crucial to maintain an appropriate variance of attention values after initialization. +# Remember that we intialize our layers with the intention of having equal variance throughout the model, and hence, +# $Q$ and $K$ might also have a variance close to $1$. +# However, performing a dot product over two vectors with a variance $\sigma$ results +# in a scalar having $d_k$-times higher variance: +# +# $$q_i \sim \mathcal{N}(0,\sigma), k_i \sim \mathcal{N}(0,\sigma) \to \text{Var}\left(\sum_{i=1}^{d_k} q_i\cdot k_i\right) = \sigma\cdot d_k$$ +# +# +# If we do not scale down the variance back to $\sigma$, the softmax over the logits will already saturate +# to $1$ for one random element and $0$ for all others. +# The gradients through the softmax will be close to zero so that we can't learn the parameters appropriately. +# +# The block `Mask (opt. +# )` in the diagram above represents the optional masking of specific entries in the attention matrix. +# This is for instance used if we stack multiple sequences with different lengths into a batch. +# To still benefit from parallelization in PyTorch, we pad the sentences to the same length and mask out the padding +# tokens during the calculation of the attention values. +# This is usually done by setting the respective attention logits to a very low value. +# +# After we have discussed the details of the scaled dot product attention block, we can write a function below +# which computes the output features given the triple of queries, keys, and values: + + +# %% +def scaled_dot_product(q, k, v, mask=None): + d_k = q.size()[-1] + attn_logits = torch.matmul(q, k.transpose(-2, -1)) + attn_logits = attn_logits / math.sqrt(d_k) + if mask is not None: + attn_logits = attn_logits.masked_fill(mask == 0, -9e15) + attention = F.softmax(attn_logits, dim=-1) + values = torch.matmul(attention, v) + return values, attention + + +# %% [markdown] +# Note that our code above supports any additional dimensionality in front of the sequence length +# so that we can also use it for batches. +# However, for a better understanding, let's generate a few random queries, keys, and value vectors, +# and calculate the attention outputs: + +# %% +seq_len, d_k = 3, 2 +L.seed_everything(42) +q = torch.randn(seq_len, d_k) +k = torch.randn(seq_len, d_k) +v = torch.randn(seq_len, d_k) +values, attention = scaled_dot_product(q, k, v) +print("Q\n", q) +print("K\n", k) +print("V\n", v) +print("Values\n", values) +print("Attention\n", attention) + +# %% [markdown] +# Before continuing, make sure you can follow the calculation of the specific values here, and also check it by hand. +# It is important to fully understand how the scaled dot product attention is calculated. + +# %% [markdown] +# ### Multi-Head Attention +# +# The scaled dot product attention allows a network to attend over a sequence. +# However, often there are multiple different aspects a sequence element wants to attend to, +# and a single weighted average is not a good option for it. +# This is why we extend the attention mechanisms to multiple heads, +# i.e. multiple different query-key-value triplets on the same features. +# Specifically, given a query, key, and value matrix, we transform those into $h$ sub-queries, sub-keys, +# and sub-values, which we pass through the scaled dot product attention independently. +# Afterward, we concatenate the heads and combine them with a final weight matrix. +# Mathematically, we can express this operation as: +# +# $$ +# \begin{split} +# \text{Multihead}(Q,K,V) & = \text{Concat}(\text{head}_1,...,\text{head}_h)W^{O}\\ +# \text{where } \text{head}_i & = \text{Attention}(QW_i^Q,KW_i^K, VW_i^V) +# \end{split} +# $$ +# +# We refer to this as Multi-Head Attention layer with the learnable parameters +# $W_{1...h}^{Q}\in\mathbb{R}^{D\times d_k}$, +# $W_{1...h}^{K}\in\mathbb{R}^{D\times d_k}$, +# $W_{1...h}^{V}\in\mathbb{R}^{D\times d_v}$, +# and $W^{O}\in\mathbb{R}^{h\cdot d_k\times d_{out}}$ ($D$ being the input dimensionality). +# Expressed in a computational graph, we can visualize it as below +# (figure credit - [Vaswani et al., 2017](https://arxiv.org/abs/1706.03762)). +# +#
+# +# How are we applying a Multi-Head Attention layer in a neural network, +# where we don't have an arbitrary query, key, and value vector as input? +# Looking at the computation graph above, a simple but effective implementation is to set the current +# feature map in a NN, $X\in\mathbb{R}^{B\times T\times d_{\text{model}}}$, as $Q$, $K$ and $V$ +# ($B$ being the batch size, $T$ the sequence length, $d_{\text{model}}$ the hidden dimensionality of $X$). +# The consecutive weight matrices $W^{Q}$, $W^{K}$, and $W^{V}$ can transform $X$ to the corresponding +# feature vectors that represent the queries, keys, and values of the input. +# Using this approach, we can implement the Multi-Head Attention module below. + + +# %% +class MultiheadAttention(nn.Module): + def __init__(self, input_dim, embed_dim, num_heads): + super().__init__() + assert embed_dim % num_heads == 0, "Embedding dimension must be 0 modulo number of heads." + + self.embed_dim = embed_dim + self.num_heads = num_heads + self.head_dim = embed_dim // num_heads + + # Stack all weight matrices 1...h together for efficiency + # Note that in many implementations you see "bias=False" which is optional + self.qkv_proj = nn.Linear(input_dim, 3 * embed_dim) + self.o_proj = nn.Linear(embed_dim, embed_dim) + + self._reset_parameters() + + def _reset_parameters(self): + # Original Transformer initialization, see PyTorch documentation + nn.init.xavier_uniform_(self.qkv_proj.weight) + self.qkv_proj.bias.data.fill_(0) + nn.init.xavier_uniform_(self.o_proj.weight) + self.o_proj.bias.data.fill_(0) + + def forward(self, x, mask=None, return_attention=False): + batch_size, seq_length, embed_dim = x.size() + qkv = self.qkv_proj(x) + + # Separate Q, K, V from linear output + qkv = qkv.reshape(batch_size, seq_length, self.num_heads, 3 * self.head_dim) + qkv = qkv.permute(0, 2, 1, 3) # [Batch, Head, SeqLen, Dims] + q, k, v = qkv.chunk(3, dim=-1) + + # Determine value outputs + values, attention = scaled_dot_product(q, k, v, mask=mask) + values = values.permute(0, 2, 1, 3) # [Batch, SeqLen, Head, Dims] + values = values.reshape(batch_size, seq_length, embed_dim) + o = self.o_proj(values) + + if return_attention: + return o, attention + else: + return o + + +# %% [markdown] +# One crucial characteristic of the multi-head attention is that it is permutation-equivariant with respect to its inputs. +# This means that if we switch two input elements in the sequence, e.g. $X_1\leftrightarrow X_2$ +# (neglecting the batch dimension for now), the output is exactly the same besides the elements 1 and 2 switched. +# Hence, the multi-head attention is actually looking at the input not as a sequence, but as a set of elements. +# This property makes the multi-head attention block and the Transformer architecture so powerful and widely applicable! +# But what if the order of the input is actually important for solving the task, like language modeling? +# The answer is to encode the position in the input features, which we will take a closer look at later +# (topic _Positional encodings_ below). +# +# Before moving on to creating the Transformer architecture, we can compare the self-attention operation +# with our other common layer competitors for sequence data: convolutions and recurrent neural networks. +# Below you can find a table by [Vaswani et al. +# (2017)](https://arxiv.org/abs/1706.03762) on the complexity per layer, the number of sequential operations, +# and maximum path length. +# The complexity is measured by the upper bound of the number of operations to perform, while the maximum path +# length represents the maximum number of steps a forward or backward signal has to traverse to reach any other position. +# The lower this length, the better gradient signals can backpropagate for long-range dependencies. +# Let's take a look at the table below: +# +# +#
+# +# $n$ is the sequence length, $d$ is the representation dimension and $k$ is the kernel size of convolutions. +# In contrast to recurrent networks, the self-attention layer can parallelize all its operations making it much faster +# to execute for smaller sequence lengths. +# However, when the sequence length exceeds the hidden dimensionality, self-attention becomes more expensive than RNNs. +# One way of reducing the computational cost for long sequences is by restricting the self-attention to a neighborhood +# of inputs to attend over, denoted by $r$. +# Nevertheless, there has been recently a lot of work on more efficient Transformer architectures that still allow long +# dependencies, of which you can find an overview in the paper by [Tay et al. +# (2020)](https://arxiv.org/abs/2009.06732) if interested. + +# %% [markdown] +# ### Transformer Encoder +# +#
+# +# Next, we will look at how to apply the multi-head attention blog inside the Transformer architecture. +# Originally, the Transformer model was designed for machine translation. +# Hence, it got an encoder-decoder structure where the encoder takes as input the sentence in the original language +# and generates an attention-based representation. +# On the other hand, the decoder attends over the encoded information and generates the translated sentence +# in an autoregressive manner, as in a standard RNN. +# While this structure is extremely useful for Sequence-to-Sequence tasks with the necessity of autoregressive decoding, +# we will focus here on the encoder part. +# Many advances in NLP have been made using pure encoder-based Transformer models (if interested, models include the +# [BERT](https://arxiv.org/abs/1810.04805)-family, +# the [Vision Transformer](https://arxiv.org/abs/2010.11929), and more), +# and in our tutorial, we will also mainly focus on the encoder part. +# If you have understood the encoder architecture, the decoder is a very small step to implement as well. +# The full Transformer architecture looks as follows +# (figure credit - [Vaswani et al., 2017](https://arxiv.org/abs/1706.03762)). +# : +# +#
+# +# The encoder consists of $N$ identical blocks that are applied in sequence. +# Taking as input $x$, it is first passed through a Multi-Head Attention block as we have implemented above. +# The output is added to the original input using a residual connection, +# and we apply a consecutive Layer Normalization on the sum. +# Overall, it calculates $\text{LayerNorm}(x+\text{Multihead}(x,x,x))$ +# ($x$ being $Q$, $K$ and $V$ input to the attention layer). +# The residual connection is crucial in the Transformer architecture for two reasons: +# +# 1. +# Similar to ResNets, Transformers are designed to be very deep. +# Some models contain more than 24 blocks in the encoder. +# Hence, the residual connections are crucial for enabling a smooth gradient flow through the model. +# 2. +# Without the residual connection, the information about the original sequence is lost. +# Remember that the Multi-Head Attention layer ignores the position of elements in a sequence, +# and can only learn it based on the input features. +# Removing the residual connections would mean that this information is lost after the first attention layer +# (after initialization), and with a randomly initialized query and key vector, +# the output vectors for position $i$ has no relation to its original input. +# All outputs of the attention are likely to represent similar/same information, +# and there is no chance for the model to distinguish which information came from which input element. +# An alternative option to residual connection would be to fix at least one head to focus on its original input, +# but this is very inefficient and does not have the benefit of the improved gradient flow. +# +# The Layer Normalization also plays an important role in the Transformer architecture as it enables faster +# training and provides small regularization. +# Additionally, it ensures that the features are in a similar magnitude among the elements in the sequence. +# We are not using Batch Normalization because it depends on the batch size which is often small with Transformers +# (they require a lot of GPU memory), and BatchNorm has shown to perform particularly bad in language +# as the features of words tend to have a much higher variance (there are many, very rare words +# which need to be considered for a good distribution estimate). +# +# Additionally to the Multi-Head Attention, a small fully connected feed-forward network is added to the model, +# which is applied to each position separately and identically. +# Specifically, the model uses a Linear$\to$ReLU$\to$Linear MLP. +# The full transformation including the residual connection can be expressed as: +# +# $$ +# \begin{split} +# \text{FFN}(x) & = \max(0, xW_1+b_1)W_2 + b_2\\ +# x & = \text{LayerNorm}(x + \text{FFN}(x)) +# \end{split} +# $$ +# +# This MLP adds extra complexity to the model and allows transformations on each sequence element separately. +# You can imagine as this allows the model to "post-process" the new information added +# by the previous Multi-Head Attention, and prepare it for the next attention block. +# Usually, the inner dimensionality of the MLP is 2-8$\times$ larger than $d_{\text{model}}$, +# i.e. the dimensionality of the original input $x$. +# The general advantage of a wider layer instead of a narrow, multi-layer MLP is the faster, parallelizable execution. +# +# Finally, after looking at all parts of the encoder architecture, we can start implementing it below. +# We first start by implementing a single encoder block. +# Additionally to the layers described above, we will add dropout layers in the MLP and on the output +# of the MLP and Multi-Head Attention for regularization. + + +# %% +class EncoderBlock(nn.Module): + def __init__(self, input_dim, num_heads, dim_feedforward, dropout=0.0): + """ + Args: + input_dim: Dimensionality of the input + num_heads: Number of heads to use in the attention block + dim_feedforward: Dimensionality of the hidden layer in the MLP + dropout: Dropout probability to use in the dropout layers + """ + super().__init__() + + # Attention layer + self.self_attn = MultiheadAttention(input_dim, input_dim, num_heads) + + # Two-layer MLP + self.linear_net = nn.Sequential( + nn.Linear(input_dim, dim_feedforward), + nn.Dropout(dropout), + nn.ReLU(inplace=True), + nn.Linear(dim_feedforward, input_dim), + ) + + # Layers to apply in between the main layers + self.norm1 = nn.LayerNorm(input_dim) + self.norm2 = nn.LayerNorm(input_dim) + self.dropout = nn.Dropout(dropout) + + def forward(self, x, mask=None): + # Attention part + attn_out = self.self_attn(x, mask=mask) + x = x + self.dropout(attn_out) + x = self.norm1(x) + + # MLP part + linear_out = self.linear_net(x) + x = x + self.dropout(linear_out) + x = self.norm2(x) + + return x + + +# %% [markdown] +# Based on this block, we can implement a module for the full Transformer encoder. +# Additionally to a forward function that iterates through the sequence of encoder blocks, +# we also provide a function called `get_attention_maps`. +# The idea of this function is to return the attention probabilities for all Multi-Head Attention blocks in the encoder. +# This helps us in understanding, and in a sense, explaining the model. +# However, the attention probabilities should be interpreted with a grain of salt as it does not necessarily +# reflect the true interpretation of the model (there is a series of papers about this, +# including [Attention is not Explanation](https://arxiv.org/abs/1902.10186) +# and [Attention is not not Explanation](https://arxiv.org/abs/1908.04626)). + + +# %% +class TransformerEncoder(nn.Module): + def __init__(self, num_layers, **block_args): + super().__init__() + self.layers = nn.ModuleList([EncoderBlock(**block_args) for _ in range(num_layers)]) + + def forward(self, x, mask=None): + for layer in self.layers: + x = layer(x, mask=mask) + return x + + def get_attention_maps(self, x, mask=None): + attention_maps = [] + for layer in self.layers: + _, attn_map = layer.self_attn(x, mask=mask, return_attention=True) + attention_maps.append(attn_map) + x = layer(x) + return attention_maps + + +# %% [markdown] +# ### Positional encoding +# +# We have discussed before that the Multi-Head Attention block is permutation-equivariant, +# and cannot distinguish whether an input comes before another one in the sequence or not. +# In tasks like language understanding, however, the position is important for interpreting the input words. +# The position information can therefore be added via the input features. +# We could learn a embedding for every possible position, but this would not generalize to a dynamical +# input sequence length. +# Hence, the better option is to use feature patterns that the network can identify from the features +# and potentially generalize to larger sequences. +# The specific pattern chosen by Vaswani et al. +# are sine and cosine functions of different frequencies, as follows: +# +# $$ +# PE_{(pos,i)} = \begin{cases} +# \sin\left(\frac{pos}{10000^{i/d_{\text{model}}}}\right) & \text{if}\hspace{3mm} i \text{ mod } 2=0\\ +# \cos\left(\frac{pos}{10000^{(i-1)/d_{\text{model}}}}\right) & \text{otherwise}\\ +# \end{cases} +# $$ +# +# $PE_{(pos,i)}$ represents the position encoding at position $pos$ in the sequence, and hidden dimensionality $i$. +# These values, concatenated for all hidden dimensions, are added to the original input features +# (in the Transformer visualization above, see "Positional encoding"), and constitute the position information. +# We distinguish between even ($i \text{ mod } 2=0$) and uneven ($i \text{ mod } 2=1$) +# hidden dimensionalities where we apply a sine/cosine respectively. +# The intuition behind this encoding is that you can represent $PE_{(pos+k,:)}$ as a linear function +# of $PE_{(pos,:)}$, which might allow the model to easily attend to relative positions. +# The wavelengths in different dimensions range from $2\pi$ to $10000\cdot 2\pi$. +# +# The positional encoding is implemented below. +# The code is taken from the [PyTorch tutorial](https://pytorch.org/tutorials/beginner/transformer_tutorial.html#define-the-model) +# about Transformers on NLP and adjusted for our purposes. + + +# %% +class PositionalEncoding(nn.Module): + def __init__(self, d_model, max_len=5000): + """ + Args + d_model: Hidden dimensionality of the input. + max_len: Maximum length of a sequence to expect. + """ + super().__init__() + + # Create matrix of [SeqLen, HiddenDim] representing the positional encoding for max_len inputs + pe = torch.zeros(max_len, d_model) + position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) + div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) + pe[:, 0::2] = torch.sin(position * div_term) + pe[:, 1::2] = torch.cos(position * div_term) + pe = pe.unsqueeze(0) + + # register_buffer => Tensor which is not a parameter, but should be part of the modules state. + # Used for tensors that need to be on the same device as the module. + # persistent=False tells PyTorch to not add the buffer to the state dict (e.g. when we save the model) + self.register_buffer("pe", pe, persistent=False) + + def forward(self, x): + x = x + self.pe[:, : x.size(1)] + return x + + +# %% [markdown] +# To understand the positional encoding, we can visualize it below. +# We will generate an image of the positional encoding over hidden dimensionality and position in a sequence. +# Each pixel, therefore, represents the change of the input feature we perform to encode the specific position. +# Let's do it below. + +# %% +encod_block = PositionalEncoding(d_model=48, max_len=96) +pe = encod_block.pe.squeeze().T.cpu().numpy() + +fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8, 3)) +pos = ax.imshow(pe, cmap="RdGy", extent=(1, pe.shape[1] + 1, pe.shape[0] + 1, 1)) +fig.colorbar(pos, ax=ax) +ax.set_xlabel("Position in sequence") +ax.set_ylabel("Hidden dimension") +ax.set_title("Positional encoding over hidden dimensions") +ax.set_xticks([1] + [i * 10 for i in range(1, 1 + pe.shape[1] // 10)]) +ax.set_yticks([1] + [i * 10 for i in range(1, 1 + pe.shape[0] // 10)]) +plt.show() + +# %% [markdown] +# You can clearly see the sine and cosine waves with different wavelengths that encode the position +# in the hidden dimensions. +# Specifically, we can look at the sine/cosine wave for each hidden dimension separately, +# to get a better intuition of the pattern. +# Below we visualize the positional encoding for the hidden dimensions $1$, $2$, $3$ and $4$. + +# %% +sns.set_theme() +fig, ax = plt.subplots(2, 2, figsize=(12, 4)) +ax = [a for a_list in ax for a in a_list] +for i in range(len(ax)): + ax[i].plot(np.arange(1, 17), pe[i, :16], color="C%i" % i, marker="o", markersize=6, markeredgecolor="black") + ax[i].set_title("Encoding in hidden dimension %i" % (i + 1)) + ax[i].set_xlabel("Position in sequence", fontsize=10) + ax[i].set_ylabel("Positional encoding", fontsize=10) + ax[i].set_xticks(np.arange(1, 17)) + ax[i].tick_params(axis="both", which="major", labelsize=10) + ax[i].tick_params(axis="both", which="minor", labelsize=8) + ax[i].set_ylim(-1.2, 1.2) +fig.subplots_adjust(hspace=0.8) +sns.reset_orig() +plt.show() + +# %% [markdown] +# As we can see, the patterns between the hidden dimension $1$ and $2$ only differ in the starting angle. +# The wavelength is $2\pi$, hence the repetition after position $6$. +# The hidden dimensions $2$ and $3$ have about twice the wavelength. + +# %% [markdown] +# ### Learning rate warm-up +# +# One commonly used technique for training a Transformer is learning rate warm-up. +# This means that we gradually increase the learning rate from 0 on to our originally specified +# learning rate in the first few iterations. +# Thus, we slowly start learning instead of taking very large steps from the beginning. +# In fact, training a deep Transformer without learning rate warm-up can make the model diverge +# and achieve a much worse performance on training and testing. +# Take for instance the following plot by [Liu et al. +# (2019)](https://arxiv.org/pdf/1908.03265.pdf) comparing Adam-vanilla (i.e. Adam without warm-up) +# vs Adam with a warm-up: +# +#
+# +# Clearly, the warm-up is a crucial hyperparameter in the Transformer architecture. +# Why is it so important? +# There are currently two common explanations. +# Firstly, Adam uses the bias correction factors which however can lead to a higher variance in the adaptive +# learning rate during the first iterations. +# Improved optimizers like [RAdam](https://arxiv.org/abs/1908.03265) have been shown to overcome this issue, +# not requiring warm-up for training Transformers. +# Secondly, the iteratively applied Layer Normalization across layers can lead to very high gradients during +# the first iterations, which can be solved by using Pre-Layer Normalization +# (similar to Pre-Activation ResNet), or replacing Layer Normalization by other techniques +# (Adaptive Normalization, +# [Power Normalization](https://arxiv.org/abs/2003.07845)). +# +# Nevertheless, many applications and papers still use the original Transformer architecture with Adam, +# because warm-up is a simple, yet effective way of solving the gradient problem in the first iterations. +# There are many different schedulers we could use. +# For instance, the original Transformer paper used an exponential decay scheduler with a warm-up. +# However, the currently most popular scheduler is the cosine warm-up scheduler, +# which combines warm-up with a cosine-shaped learning rate decay. +# We can implement it below, and visualize the learning rate factor over epochs. + + +# %% +class CosineWarmupScheduler(optim.lr_scheduler._LRScheduler): + def __init__(self, optimizer, warmup, max_iters): + self.warmup = warmup + self.max_num_iters = max_iters + super().__init__(optimizer) + + def get_lr(self): + lr_factor = self.get_lr_factor(epoch=self.last_epoch) + return [base_lr * lr_factor for base_lr in self.base_lrs] + + def get_lr_factor(self, epoch): + lr_factor = 0.5 * (1 + np.cos(np.pi * epoch / self.max_num_iters)) + if epoch <= self.warmup: + lr_factor *= epoch * 1.0 / self.warmup + return lr_factor + + +# %% +# Needed for initializing the lr scheduler +p = nn.Parameter(torch.empty(4, 4)) +optimizer = optim.Adam([p], lr=1e-3) +lr_scheduler = CosineWarmupScheduler(optimizer=optimizer, warmup=100, max_iters=2000) + +# Plotting +epochs = list(range(2000)) +sns.set() +plt.figure(figsize=(8, 3)) +plt.plot(epochs, [lr_scheduler.get_lr_factor(e) for e in epochs]) +plt.ylabel("Learning rate factor") +plt.xlabel("Iterations (in batches)") +plt.title("Cosine Warm-up Learning Rate Scheduler") +plt.show() +sns.reset_orig() + +# %% [markdown] +# In the first 100 iterations, we increase the learning rate factor from 0 to 1, +# whereas for all later iterations, we decay it using the cosine wave. +# Pre-implementations of this scheduler can be found in the popular NLP Transformer library +# [huggingface](https://huggingface.co/transformers/main_classes/optimizer_schedules.html?highlight=cosine#transformers.get_cosine_schedule_with_warmup). + +# %% [markdown] +# ### PyTorch Lightning Module +# +# Finally, we can embed the Transformer architecture into a PyTorch lightning module. +# From Tutorial 5, you know that PyTorch Lightning simplifies our training and test code, +# as well as structures the code nicely in separate functions. +# We will implement a template for a classifier based on the Transformer encoder. +# Thereby, we have a prediction output per sequence element. +# If we would need a classifier over the whole sequence, the common approach is to add an additional +# `[CLS]` token to the sequence, representing the classifier token. +# However, here we focus on tasks where we have an output per element. +# +# Additionally to the Transformer architecture, we add a small input network (maps input dimensions to model dimensions), +# the positional encoding, and an output network (transforms output encodings to predictions). +# We also add the learning rate scheduler, which takes a step each iteration instead of once per epoch. +# This is needed for the warmup and the smooth cosine decay. +# The training, validation, and test step is left empty for now and will be filled for our task-specific models. + + +# %% +class TransformerPredictor(L.LightningModule): + def __init__( + self, + input_dim, + model_dim, + num_classes, + num_heads, + num_layers, + lr, + warmup, + max_iters, + dropout=0.0, + input_dropout=0.0, + ): + """ + Args: + input_dim: Hidden dimensionality of the input + model_dim: Hidden dimensionality to use inside the Transformer + num_classes: Number of classes to predict per sequence element + num_heads: Number of heads to use in the Multi-Head Attention blocks + num_layers: Number of encoder blocks to use. + lr: Learning rate in the optimizer + warmup: Number of warmup steps. Usually between 50 and 500 + max_iters: Number of maximum iterations the model is trained for. This is needed for the CosineWarmup scheduler + dropout: Dropout to apply inside the model + input_dropout: Dropout to apply on the input features + """ + super().__init__() + self.save_hyperparameters() + self._create_model() + + def _create_model(self): + # Input dim -> Model dim + self.input_net = nn.Sequential( + nn.Dropout(self.hparams.input_dropout), nn.Linear(self.hparams.input_dim, self.hparams.model_dim) + ) + # Positional encoding for sequences + self.positional_encoding = PositionalEncoding(d_model=self.hparams.model_dim) + # Transformer + self.transformer = TransformerEncoder( + num_layers=self.hparams.num_layers, + input_dim=self.hparams.model_dim, + dim_feedforward=2 * self.hparams.model_dim, + num_heads=self.hparams.num_heads, + dropout=self.hparams.dropout, + ) + # Output classifier per sequence lement + self.output_net = nn.Sequential( + nn.Linear(self.hparams.model_dim, self.hparams.model_dim), + nn.LayerNorm(self.hparams.model_dim), + nn.ReLU(inplace=True), + nn.Dropout(self.hparams.dropout), + nn.Linear(self.hparams.model_dim, self.hparams.num_classes), + ) + + def forward(self, x, mask=None, add_positional_encoding=True): + """ + Args: + x: Input features of shape [Batch, SeqLen, input_dim] + mask: Mask to apply on the attention outputs (optional) + add_positional_encoding: If True, we add the positional encoding to the input. + Might not be desired for some tasks. + """ + x = self.input_net(x) + if add_positional_encoding: + x = self.positional_encoding(x) + x = self.transformer(x, mask=mask) + x = self.output_net(x) + return x + + @torch.no_grad() + def get_attention_maps(self, x, mask=None, add_positional_encoding=True): + """Function for extracting the attention matrices of the whole Transformer for a single batch. + + Input arguments same as the forward pass. + """ + x = self.input_net(x) + if add_positional_encoding: + x = self.positional_encoding(x) + attention_maps = self.transformer.get_attention_maps(x, mask=mask) + return attention_maps + + def configure_optimizers(self): + optimizer = optim.Adam(self.parameters(), lr=self.hparams.lr) + + # We don't return the lr scheduler because we need to apply it per iteration, not per epoch + self.lr_scheduler = CosineWarmupScheduler( + optimizer, warmup=self.hparams.warmup, max_iters=self.hparams.max_iters + ) + return optimizer + + def optimizer_step(self, *args, **kwargs): + super().optimizer_step(*args, **kwargs) + self.lr_scheduler.step() # Step per iteration + + def training_step(self, batch, batch_idx): + raise NotImplementedError + + def validation_step(self, batch, batch_idx): + raise NotImplementedError + + def test_step(self, batch, batch_idx): + raise NotImplementedError + + +# %% [markdown] +# ## Experiments +# +#
+# +# After having finished the implementation of the Transformer architecture, we can start experimenting +# and apply it to various tasks. +# In this notebook, we will focus on two tasks: parallel Sequence-to-Sequence, and set anomaly detection. +# The two tasks focus on different properties of the Transformer architecture, and we go through them below. +# +# ### Sequence to Sequence +# +# A Sequence-to-Sequence task represents a task where the input _and_ the output is a sequence, +# not necessarily of the same length. +# Popular tasks in this domain include machine translation and summarization. +# For this, we usually have a Transformer encoder for interpreting the input sequence, +# and a decoder for generating the output in an autoregressive manner. +# Here, however, we will go back to a much simpler example task and use only the encoder. +# Given a sequence of $N$ numbers between $0$ and $M$, the task is to reverse the input sequence. +# In Numpy notation, if our input is $x$, the output should be $x$[::-1]. +# Although this task sounds very simple, RNNs can have issues with such because the task requires long-term dependencies. +# Transformers are built to support such, and hence, we expect it to perform very well. +# +# First, let's create a dataset class below. + + +# %% +class ReverseDataset(data.Dataset): + def __init__(self, num_categories, seq_len, size): + super().__init__() + self.num_categories = num_categories + self.seq_len = seq_len + self.size = size + + self.data = torch.randint(self.num_categories, size=(self.size, self.seq_len)) + + def __len__(self): + return self.size + + def __getitem__(self, idx): + inp_data = self.data[idx] + labels = torch.flip(inp_data, dims=(0,)) + return inp_data, labels + + +# %% [markdown] +# We create an arbitrary number of random sequences of numbers between 0 and `num_categories-1`. +# The label is simply the tensor flipped over the sequence dimension. +# We can create the corresponding data loaders below. + +# %% +dataset = partial(ReverseDataset, 10, 16) +train_loader = data.DataLoader(dataset(50000), batch_size=128, shuffle=True, drop_last=True, pin_memory=True) +val_loader = data.DataLoader(dataset(1000), batch_size=128) +test_loader = data.DataLoader(dataset(10000), batch_size=128) + +# %% [markdown] +# Let's look at an arbitrary sample of the dataset: + +# %% +inp_data, labels = train_loader.dataset[0] +print("Input data:", inp_data) +print("Labels: ", labels) + +# %% [markdown] +# During training, we pass the input sequence through the Transformer encoder and predict the output for each input token. +# We use the standard Cross-Entropy loss to perform this. +# Every number is represented as a one-hot vector. +# Remember that representing the categories as single scalars decreases the expressiveness of the model extremely +# as $0$ and $1$ are not closer related than $0$ and $9$ in our example. +# An alternative to a one-hot vector is using a learned embedding vector as it is provided by the PyTorch module `nn.Embedding`. +# However, using a one-hot vector with an additional linear layer as in our case has the same effect +# as an embedding layer (`self.input_net` maps one-hot vector to a dense vector, +# where each row of the weight matrix represents the embedding for a specific category). +# +# To implement the training dynamic, we create a new class inheriting from `TransformerPredictor` +# and overwriting the training, validation and test step functions. + + +# %% +class ReversePredictor(TransformerPredictor): + def _calculate_loss(self, batch, mode="train"): + # Fetch data and transform categories to one-hot vectors + inp_data, labels = batch + inp_data = F.one_hot(inp_data, num_classes=self.hparams.num_classes).float() + + # Perform prediction and calculate loss and accuracy + preds = self.forward(inp_data, add_positional_encoding=True) + loss = F.cross_entropy(preds.view(-1, preds.size(-1)), labels.view(-1)) + acc = (preds.argmax(dim=-1) == labels).float().mean() + + # Logging + self.log("%s_loss" % mode, loss) + self.log("%s_acc" % mode, acc) + return loss, acc + + def training_step(self, batch, batch_idx): + loss, _ = self._calculate_loss(batch, mode="train") + return loss + + def validation_step(self, batch, batch_idx): + _ = self._calculate_loss(batch, mode="val") + + def test_step(self, batch, batch_idx): + _ = self._calculate_loss(batch, mode="test") + + +# %% [markdown] +# Finally, we can create a training function similar to the one we have seen in Tutorial 5 for PyTorch Lightning. +# We create a `L.Trainer` object, running for $N$ epochs, logging in TensorBoard, and saving our best model based on the validation. +# Afterward, we test our models on the test set. +# An additional parameter we pass to the trainer here is `gradient_clip_val`. +# This clips the norm of the gradients for all parameters before taking an optimizer step and prevents the model +# from diverging if we obtain very high gradients at, for instance, sharp loss surfaces (see many good blog posts +# on gradient clipping, like [DeepAI glossary](https://deepai.org/machine-learning-glossary-and-terms/gradient-clipping)). +# For Transformers, gradient clipping can help to further stabilize the training during the first few iterations, and also afterward. +# In plain PyTorch, you can apply gradient clipping via `torch.nn.utils.clip_grad_norm_(...)` +# (see [documentation](https://pytorch.org/docs/stable/generated/torch.nn.utils.clip_grad_norm_.html#torch.nn.utils.clip_grad_norm_)). +# The clip value is usually between 0.5 and 10, depending on how harsh you want to clip large gradients. +# After having explained this, let's implement the training function: + + +# %% +def train_reverse(**kwargs): + # Create a PyTorch Lightning trainer with the generation callback + root_dir = os.path.join(CHECKPOINT_PATH, "ReverseTask") + os.makedirs(root_dir, exist_ok=True) + trainer = L.Trainer( + default_root_dir=root_dir, + callbacks=[ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc")], + accelerator="auto", + devices=1, + max_epochs=10, + gradient_clip_val=5, + ) + trainer.logger._default_hp_metric = None # Optional logging argument that we don't need + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "ReverseTask.ckpt") + if os.path.isfile(pretrained_filename): + print("Found pretrained model, loading...") + model = ReversePredictor.load_from_checkpoint(pretrained_filename) + else: + model = ReversePredictor(max_iters=trainer.max_epochs * len(train_loader), **kwargs) + trainer.fit(model, train_loader, val_loader) + + # Test best model on validation and test set + val_result = trainer.test(model, dataloaders=val_loader, verbose=False) + test_result = trainer.test(model, dataloaders=test_loader, verbose=False) + result = {"test_acc": test_result[0]["test_acc"], "val_acc": val_result[0]["test_acc"]} + + model = model.to(device) + return model, result + + +# %% [markdown] +# Finally, we can train the model. +# In this setup, we will use a single encoder block and a single head in the Multi-Head Attention. +# This is chosen because of the simplicity of the task, and in this case, the attention can actually be interpreted +# as an "explanation" of the predictions (compared to the other papers above dealing with deep Transformers). + +# %% +reverse_model, reverse_result = train_reverse( + input_dim=train_loader.dataset.num_categories, + model_dim=32, + num_heads=1, + num_classes=train_loader.dataset.num_categories, + num_layers=1, + dropout=0.0, + lr=5e-4, + warmup=50, +) + +# %% [markdown] +# The warning of PyTorch Lightning regarding the number of workers can be ignored for now. +# As the data set is so simple and the `__getitem__` finishes a neglectable time, we don't need subprocesses +# to provide us the data (in fact, more workers can slow down the training as we have communication overhead among processes/threads). +# First, let's print the results: + +# %% +print("Val accuracy: %4.2f%%" % (100.0 * reverse_result["val_acc"])) +print("Test accuracy: %4.2f%%" % (100.0 * reverse_result["test_acc"])) + +# %% [markdown] +# As we would have expected, the Transformer can correctly solve the task. +# However, how does the attention in the Multi-Head Attention block looks like for an arbitrary input? +# Let's try to visualize it below. + +# %% +data_input, labels = next(iter(val_loader)) +inp_data = F.one_hot(data_input, num_classes=reverse_model.hparams.num_classes).float() +inp_data = inp_data.to(device) +attention_maps = reverse_model.get_attention_maps(inp_data) + +# %% [markdown] +# The object `attention_maps` is a list of length $N$ where $N$ is the number of layers. +# Each element is a tensor of shape [Batch, Heads, SeqLen, SeqLen], which we can verify below. + +# %% +attention_maps[0].shape + +# %% [markdown] +# Next, we will write a plotting function that takes as input the sequences, attention maps, and an index +# indicating for which batch element we want to visualize the attention map. +# We will create a plot where over rows, we have different layers, while over columns, we show the different heads. +# Remember that the softmax has been applied for each row separately. + + +# %% +def plot_attention_maps(input_data, attn_maps, idx=0): + if input_data is not None: + input_data = input_data[idx].detach().cpu().numpy() + else: + input_data = np.arange(attn_maps[0][idx].shape[-1]) + attn_maps = [m[idx].detach().cpu().numpy() for m in attn_maps] + + num_heads = attn_maps[0].shape[0] + num_layers = len(attn_maps) + seq_len = input_data.shape[0] + fig_size = 4 if num_heads == 1 else 3 + fig, ax = plt.subplots(num_layers, num_heads, figsize=(num_heads * fig_size, num_layers * fig_size)) + if num_layers == 1: + ax = [ax] + if num_heads == 1: + ax = [[a] for a in ax] + for row in range(num_layers): + for column in range(num_heads): + ax[row][column].imshow(attn_maps[row][column], origin="lower", vmin=0) + ax[row][column].set_xticks(list(range(seq_len))) + ax[row][column].set_xticklabels(input_data.tolist()) + ax[row][column].set_yticks(list(range(seq_len))) + ax[row][column].set_yticklabels(input_data.tolist()) + ax[row][column].set_title("Layer %i, Head %i" % (row + 1, column + 1)) + fig.subplots_adjust(hspace=0.5) + plt.show() + + +# %% [markdown] +# Finally, we can plot the attention map of our trained Transformer on the reverse task: + +# %% +plot_attention_maps(data_input, attention_maps, idx=0) + +# %% [markdown] +# The model has learned to attend to the token that is on the flipped index of itself. +# Hence, it actually does what we intended it to do. +# We see that it however also pays some attention to values close to the flipped index. +# This is because the model doesn't need the perfect, hard attention to solve this problem, +# but is fine with this approximate, noisy attention map. +# The close-by indices are caused by the similarity of the positional encoding, +# which we also intended with the positional encoding. + +# %% [markdown] +# ### Set Anomaly Detection +# +# Besides sequences, sets are another data structure that is relevant for many applications. +# In contrast to sequences, elements are unordered in a set. +# RNNs can only be applied on sets by assuming an order in the data, which however biases the model towards +# a non-existing order in the data. +# [Vinyals et al. +# (2015)](https://arxiv.org/abs/1511.06391) and other papers have shown that the assumed order can have a significant +# impact on the model's performance, and hence, we should try to not use RNNs on sets. +# Ideally, our model should be permutation-equivariant/invariant such that the output is the same no matter how we sort the elements in a set. +# +# Transformers offer the perfect architecture for this as the Multi-Head Attention is permutation-equivariant, and thus, +# outputs the same values no matter in what order we enter the inputs (inputs and outputs are permuted equally). +# The task we are looking at for sets is _Set Anomaly Detection_ which means that we try to find the element(s) +# in a set that does not fit the others. +# In the research community, the common application of anomaly detection is performed on a set of images, +# where $N-1$ images belong to the same category/have the same high-level features while one belongs to another category. +# Note that category does not necessarily have to relate to a class in a standard classification problem, +# but could be the combination of multiple features. +# For instance, on a face dataset, this could be people with glasses, male, beard, etc. +# An example of distinguishing different animals can be seen below. +# The first four images show foxes, while the last represents a different animal. +# We want to recognize that the last image shows a different animal, but it is not relevant which class of animal it is. +# +#
+# +# In this tutorial, we will use the CIFAR100 dataset. +# CIFAR100 has 600 images for 100 classes each with a resolution of 32x32, similar to CIFAR10. +# The larger amount of classes requires the model to attend to specific features in the images instead +# of coarse features as in CIFAR10, therefore making the task harder. +# We will show the model a set of 9 images of one class, and 1 image from another class. +# The task is to find the image that is from a different class than the other images. +# Using the raw images directly as input to the Transformer is not a good idea, because it is not translation +# invariant as a CNN, and would need to learn to detect image features from high-dimensional input first of all. +# Instead, we will use a pre-trained ResNet34 model from the torchvision package to obtain high-level, +# low-dimensional features of the images. +# The ResNet model has been pre-trained on the [ImageNet](http://image-net.org/) dataset which contains +# 1 million images of 1k classes and varying resolutions. +# However, during training and testing, the images are usually scaled to a resolution of 224x224, +# and hence we rescale our CIFAR images to this resolution as well. +# Below, we will load the dataset, and prepare the data for being processed by the ResNet model. + +# %% +# ImageNet statistics +DATA_MEANS = np.array([0.485, 0.456, 0.406]) +DATA_STD = np.array([0.229, 0.224, 0.225]) +# As torch tensors for later preprocessing +TORCH_DATA_MEANS = torch.from_numpy(DATA_MEANS).view(1, 3, 1, 1) +TORCH_DATA_STD = torch.from_numpy(DATA_STD).view(1, 3, 1, 1) + +# Resize to 224x224, and normalize to ImageNet statistic +transform = transforms.Compose( + [transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(DATA_MEANS, DATA_STD)] +) +# Loading the training dataset. +train_set = CIFAR100(root=DATASET_PATH, train=True, transform=transform, download=True) + +# Loading the test set +test_set = CIFAR100(root=DATASET_PATH, train=False, transform=transform, download=True) + +# %% [markdown] +# Next, we want to run the pre-trained ResNet model on the images, and extract the features before the classification layer. +# These are the most high-level features, and should sufficiently describe the images. +# CIFAR100 has some similarity to ImageNet, and thus we are not retraining the ResNet model in any form. +# However, if you would want to get the best performance and have a very large dataset, +# it would be better to add the ResNet to the computation graph during training and finetune its parameters as well. +# As we don't have a large enough dataset and want to train our model efficiently, we will extract the features beforehand. +# Let's load and prepare the model below. + +# %% +os.environ["TORCH_HOME"] = CHECKPOINT_PATH +pretrained_model = torchvision.models.resnet34(pretrained=True) +# Remove classification layer +# In some models, it is called "fc", others have "classifier" +# Setting both to an empty sequential represents an identity map of the final features. +pretrained_model.fc = nn.Sequential() +pretrained_model.classifier = nn.Sequential() +# To GPU +pretrained_model = pretrained_model.to(device) + +# Only eval, no gradient required +pretrained_model.eval() +for p in pretrained_model.parameters(): + p.requires_grad = False + +# %% [markdown] +# We will now write a extraction function for the features below. +# This cell requires access to a GPU, as the model is rather deep and the images relatively large. +# The GPUs on GoogleColab are sufficient, but running this cell can take 2-3 minutes. +# Once it is run, the features are exported on disk so they don't have to be recalculated every time you run the notebook. +# However, this requires >150MB free disk space. +# So it is recommended to run this only on a local computer if you have enough free disk and a GPU (GoogleColab is fine for this). +# If you do not have a GPU, you can download the features from the +# [GoogleDrive folder](https://drive.google.com/drive/folders/1DF7POc6j03pRiWQPWSl5QJX5iY-xK0sV?usp=sharing). + + +# %% +@torch.no_grad() +def extract_features(dataset, save_file): + if not os.path.isfile(save_file): + data_loader = data.DataLoader(dataset, batch_size=128, shuffle=False, drop_last=False, num_workers=4) + extracted_features = [] + for imgs, _ in tqdm(data_loader): + imgs = imgs.to(device) + feats = pretrained_model(imgs) + extracted_features.append(feats) + extracted_features = torch.cat(extracted_features, dim=0) + extracted_features = extracted_features.detach().cpu() + torch.save(extracted_features, save_file) + else: + extracted_features = torch.load(save_file) + return extracted_features + + +train_feat_file = os.path.join(CHECKPOINT_PATH, "train_set_features.tar") +train_set_feats = extract_features(train_set, train_feat_file) + +test_feat_file = os.path.join(CHECKPOINT_PATH, "test_set_features.tar") +test_feats = extract_features(test_set, test_feat_file) + +# %% [markdown] +# Let's verify the feature shapes below. +# The training should have 50k elements, and the test 10k images. +# The feature dimension is 512 for the ResNet34. +# If you experiment with other models, you likely see a different feature dimension. + +# %% +print("Train:", train_set_feats.shape) +print("Test: ", test_feats.shape) + +# %% [markdown] +# As usual, we want to create a validation set to detect when we should stop training. +# In this case, we will split the training set into 90% training, 10% validation. +# However, the difficulty is here that we need to ensure that the validation set has the same number of images for all 100 labels. +# Otherwise, we have a class imbalance which is not good for creating the image sets. +# Hence, we take 10% of the images for each class, and move them into the validation set. +# The code below does exactly this. + +# %% +# Split train into train+val +# Get labels from train set +labels = train_set.targets + +# Get indices of images per class +labels = torch.LongTensor(labels) +num_labels = labels.max() + 1 +sorted_indices = torch.argsort(labels).reshape(num_labels, -1) # [classes, num_imgs per class] + +# Determine number of validation images per class +num_val_exmps = sorted_indices.shape[1] // 10 + +# Get image indices for validation and training +val_indices = sorted_indices[:, :num_val_exmps].reshape(-1) +train_indices = sorted_indices[:, num_val_exmps:].reshape(-1) + +# Group corresponding image features and labels +train_feats, train_labels = train_set_feats[train_indices], labels[train_indices] +val_feats, val_labels = train_set_feats[val_indices], labels[val_indices] + +# %% [markdown] +# Now we can prepare a dataset class for the set anomaly task. +# We define an epoch to be the sequence in which each image has been exactly once as an "anomaly". +# Hence, the length of the dataset is the number of images in it. +# For the training set, each time we access an item with `__getitem__`, we sample a random, +# different class than the image at the corresponding index `idx` has. +# In a second step, we sample $N-1$ images of this sampled class. +# The set of 10 images is finally returned. +# The randomness in the `__getitem__` allows us to see a slightly different set during each iteration. +# However, we can't use the same strategy for the test set as we want the test dataset to be the same every time we iterate over it. +# Hence, we sample the sets in the `__init__` method, and return those in `__getitem__`. +# The code below implements exactly this dynamic. + + +# %% +class SetAnomalyDataset(data.Dataset): + def __init__(self, img_feats, labels, set_size=10, train=True): + """ + Args: + img_feats: Tensor of shape [num_imgs, img_dim]. Represents the high-level features. + labels: Tensor of shape [num_imgs], containing the class labels for the images + set_size: Number of elements in a set. N-1 are sampled from one class, and one from another one. + train: If True, a new set will be sampled every time __getitem__ is called. + """ + super().__init__() + self.img_feats = img_feats + self.labels = labels + self.set_size = set_size - 1 # The set size is here the size of correct images + self.train = train + + # Tensors with indices of the images per class + self.num_labels = labels.max() + 1 + self.img_idx_by_label = torch.argsort(self.labels).reshape(self.num_labels, -1) + + if not train: + self.test_sets = self._create_test_sets() + + def _create_test_sets(self): + # Pre-generates the sets for each image for the test set + test_sets = [] + num_imgs = self.img_feats.shape[0] + np.random.seed(42) + test_sets = [self.sample_img_set(self.labels[idx]) for idx in range(num_imgs)] + test_sets = torch.stack(test_sets, dim=0) + return test_sets + + def sample_img_set(self, anomaly_label): + """Samples a new set of images, given the label of the anomaly. + + The sampled images come from a different class than anomaly_label + """ + # Sample class from 0,...,num_classes-1 while skipping anomaly_label as class + set_label = np.random.randint(self.num_labels - 1) + if set_label >= anomaly_label: + set_label += 1 + + # Sample images from the class determined above + img_indices = np.random.choice(self.img_idx_by_label.shape[1], size=self.set_size, replace=False) + img_indices = self.img_idx_by_label[set_label, img_indices] + return img_indices + + def __len__(self): + return self.img_feats.shape[0] + + def __getitem__(self, idx): + anomaly = self.img_feats[idx] + if self.train: # If train => sample + img_indices = self.sample_img_set(self.labels[idx]) + else: # If test => use pre-generated ones + img_indices = self.test_sets[idx] + + # Concatenate images. The anomaly is always the last image for simplicity + img_set = torch.cat([self.img_feats[img_indices], anomaly[None]], dim=0) + indices = torch.cat([img_indices, torch.LongTensor([idx])], dim=0) + label = img_set.shape[0] - 1 + + # We return the indices of the images for visualization purpose. "Label" is the index of the anomaly + return img_set, indices, label + + +# %% [markdown] +# Next, we can setup our datasets and data loaders below. +# Here, we will use a set size of 10, i.e. 9 images from one category + 1 anomaly. +# Feel free to change it if you want to experiment with the sizes. + +# %% +SET_SIZE = 10 +test_labels = torch.LongTensor(test_set.targets) + +train_anom_dataset = SetAnomalyDataset(train_feats, train_labels, set_size=SET_SIZE, train=True) +val_anom_dataset = SetAnomalyDataset(val_feats, val_labels, set_size=SET_SIZE, train=False) +test_anom_dataset = SetAnomalyDataset(test_feats, test_labels, set_size=SET_SIZE, train=False) + +train_anom_loader = data.DataLoader( + train_anom_dataset, batch_size=64, shuffle=True, drop_last=True, num_workers=4, pin_memory=True +) +val_anom_loader = data.DataLoader(val_anom_dataset, batch_size=64, shuffle=False, drop_last=False, num_workers=4) +test_anom_loader = data.DataLoader(test_anom_dataset, batch_size=64, shuffle=False, drop_last=False, num_workers=4) + +# %% [markdown] +# To understand the dataset a little better, we can plot below a few sets from the test dataset. +# Each row shows a different input set, where the first 9 are from the same class. + + +# %% +def visualize_exmp(indices, orig_dataset): + images = [orig_dataset[idx][0] for idx in indices.reshape(-1)] + images = torch.stack(images, dim=0) + images = images * TORCH_DATA_STD + TORCH_DATA_MEANS + + img_grid = torchvision.utils.make_grid(images, nrow=SET_SIZE, normalize=True, pad_value=0.5, padding=16) + img_grid = img_grid.permute(1, 2, 0) + + plt.figure(figsize=(12, 8)) + plt.title("Anomaly examples on CIFAR100") + plt.imshow(img_grid) + plt.axis("off") + plt.show() + plt.close() + + +_, indices, _ = next(iter(test_anom_loader)) +visualize_exmp(indices[:4], test_set) + +# %% [markdown] +# We can already see that for some sets the task might be easier than for others. +# Difficulties can especially arise if the anomaly is in a different, but yet visually similar class +# (e.g. train vs bus, flour vs worm, etc. +# ). +# +# After having prepared the data, we can look closer at the model. +# Here, we have a classification of the whole set. +# For the prediction to be permutation-equivariant, we will output one logit for each image. +# Over these logits, we apply a softmax and train the anomaly image to have the highest score/probability. +# This is a bit different than a standard classification layer as the softmax is applied over images, +# not over output classes in the classical sense. +# However, if we swap two images in their position, we effectively swap their position in the output softmax. +# Hence, the prediction is equivariant with respect to the input. +# We implement this idea below in the subclass of the Transformer Lightning module. + + +# %% +class AnomalyPredictor(TransformerPredictor): + def _calculate_loss(self, batch, mode="train"): + img_sets, _, labels = batch + # No positional encodings as it is a set, not a sequence! + preds = self.forward(img_sets, add_positional_encoding=False) + preds = preds.squeeze(dim=-1) # Shape: [Batch_size, set_size] + loss = F.cross_entropy(preds, labels) # Softmax/CE over set dimension + acc = (preds.argmax(dim=-1) == labels).float().mean() + self.log("%s_loss" % mode, loss) + self.log("%s_acc" % mode, acc, on_step=False, on_epoch=True) + return loss, acc + + def training_step(self, batch, batch_idx): + loss, _ = self._calculate_loss(batch, mode="train") + return loss + + def validation_step(self, batch, batch_idx): + _ = self._calculate_loss(batch, mode="val") + + def test_step(self, batch, batch_idx): + _ = self._calculate_loss(batch, mode="test") + + +# %% [markdown] +# Finally, we write our train function below. +# It has the exact same structure as the reverse task one, hence not much of an explanation is needed here. + + +# %% +def train_anomaly(**kwargs): + # Create a PyTorch Lightning trainer with the generation callback + root_dir = os.path.join(CHECKPOINT_PATH, "SetAnomalyTask") + os.makedirs(root_dir, exist_ok=True) + trainer = L.Trainer( + default_root_dir=root_dir, + callbacks=[ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc")], + accelerator="auto", + devices=1, + max_epochs=100, + gradient_clip_val=2, + ) + trainer.logger._default_hp_metric = None # Optional logging argument that we don't need + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "SetAnomalyTask.ckpt") + if os.path.isfile(pretrained_filename): + print("Found pretrained model, loading...") + model = AnomalyPredictor.load_from_checkpoint(pretrained_filename) + else: + model = AnomalyPredictor(max_iters=trainer.max_epochs * len(train_anom_loader), **kwargs) + trainer.fit(model, train_anom_loader, val_anom_loader) + model = AnomalyPredictor.load_from_checkpoint(trainer.checkpoint_callback.best_model_path) + + # Test best model on validation and test set + train_result = trainer.test(model, dataloaders=train_anom_loader, verbose=False) + val_result = trainer.test(model, dataloaders=val_anom_loader, verbose=False) + test_result = trainer.test(model, dataloaders=test_anom_loader, verbose=False) + result = { + "test_acc": test_result[0]["test_acc"], + "val_acc": val_result[0]["test_acc"], + "train_acc": train_result[0]["test_acc"], + } + + model = model.to(device) + return model, result + + +# %% [markdown] +# Let's finally train our model. +# We will use 4 layers with 4 attention heads each. +# The hidden dimensionality of the model is 256, and we use a dropout of 0.1 throughout the model for good regularization. +# Note that we also apply the dropout on the input features, as this makes the model more robust against +# image noise and generalizes better. +# Again, we use warmup to slowly start our model training. + +# %% +anomaly_model, anomaly_result = train_anomaly( + input_dim=train_anom_dataset.img_feats.shape[-1], + model_dim=256, + num_heads=4, + num_classes=1, + num_layers=4, + dropout=0.1, + input_dropout=0.1, + lr=5e-4, + warmup=100, +) + +# %% [markdown] +# We can print the achieved accuracy below. + +# %% +print("Train accuracy: %4.2f%%" % (100.0 * anomaly_result["train_acc"])) +print("Val accuracy: %4.2f%%" % (100.0 * anomaly_result["val_acc"])) +print("Test accuracy: %4.2f%%" % (100.0 * anomaly_result["test_acc"])) + +# %% [markdown] +# With ~94% validation and test accuracy, the model generalizes quite well. +# It should be noted that you might see slightly different scores depending on what computer/device you are running this notebook. +# This is because despite setting the seed before generating the test dataset, it is not the same across platforms and numpy versions. +# Nevertheless, we can conclude that the model performs quite well and can solve the task for most sets. +# Before trying to interpret the model, let's verify that our model is permutation-equivariant, +# and assigns the same predictions for different permutations of the input set. +# For this, we sample a batch from the test set and run it through the model to obtain the probabilities. + +# %% +inp_data, indices, labels = next(iter(test_anom_loader)) +inp_data = inp_data.to(device) + +anomaly_model.eval() + +with torch.no_grad(): + preds = anomaly_model.forward(inp_data, add_positional_encoding=False) + preds = F.softmax(preds.squeeze(dim=-1), dim=-1) + + # Permut input data + permut = np.random.permutation(inp_data.shape[1]) + perm_inp_data = inp_data[:, permut] + perm_preds = anomaly_model.forward(perm_inp_data, add_positional_encoding=False) + perm_preds = F.softmax(perm_preds.squeeze(dim=-1), dim=-1) + +assert (preds[:, permut] - perm_preds).abs().max() < 1e-5, "Predictions are not permutation equivariant" + +print("Preds\n", preds[0, permut].cpu().numpy()) +print("Permuted preds\n", perm_preds[0].cpu().numpy()) + +# %% [markdown] +# You can see that the predictions are almost exactly the same, and only differ because of slight numerical +# differences inside the network operation. +# +# To interpret the model a little more, we can plot the attention maps inside the model. +# This will give us an idea of what information the model is sharing/communicating between images, +# and what each head might represent. +# First, we need to extract the attention maps for the test batch above, and determine the discrete predictions for simplicity. + +# %% +attention_maps = anomaly_model.get_attention_maps(inp_data, add_positional_encoding=False) +predictions = preds.argmax(dim=-1) + +# %% [markdown] +# Below we write a plot function which plots the images in the input set, the prediction of the model, +# and the attention maps of the different heads on layers of the transformer. +# Feel free to explore the attention maps for different input examples as well. + + +# %% +def visualize_prediction(idx): + visualize_exmp(indices[idx : idx + 1], test_set) + print("Prediction:", predictions[idx].item()) + plot_attention_maps(input_data=None, attn_maps=attention_maps, idx=idx) + + +visualize_prediction(0) + +# %% [markdown] +# Depending on the random seed, you might see a slightly different input set. +# For the version on the website, we compare 9 tree images with a volcano. +# We see that multiple heads, for instance, Layer 2 Head 1, Layer 2 Head 3, and Layer 3 Head 1 focus on the last image. +# Additionally, the heads in Layer 4 all seem to ignore the last image and assign a very low attention probability to it. +# This shows that the model has indeed recognized that the image doesn't fit the setting, and hence predicted it to be the anomaly. +# Layer 3 Head 2-4 seems to take a slightly weighted average of all images. +# That might indicate that the model extracts the "average" information of all images, to compare it to the image features itself. +# +# Let's try to find where the model actually makes a mistake. +# We can do this by identifying the sets where the model predicts something else than 9, as in the dataset, +# we ensured that the anomaly is always at the last position in the set. + +# %% +mistakes = torch.where(predictions != 9)[0].cpu().numpy() +print("Indices with mistake:", mistakes) + +# %% [markdown] +# As our model achieves ~94% accuracy, we only have very little number of mistakes in a batch of 64 sets. +# Still, let's visualize one of them, for example the last one: + +# %% +visualize_prediction(mistakes[-1]) +print("Probabilities:") +for i, p in enumerate(preds[mistakes[-1]].cpu().numpy()): + print("Image %i: %4.2f%%" % (i, 100.0 * p)) + +# %% [markdown] +# In this example, the model confuses a palm tree with a building, giving a probability of ~90% to image 2, and 8% to the actual anomaly. +# However, the difficulty here is that the picture of the building has been taken at a similar angle as the palms. +# Meanwhile, image 2 shows a rather unusual palm with a different color palette, which is why the model fails here. +# Nevertheless, in general, the model performs quite well. + +# %% [markdown] +# ## Conclusion +# +# In this tutorial, we took a closer look at the Multi-Head Attention layer which uses a scaled dot product between +# queries and keys to find correlations and similarities between input elements. +# The Transformer architecture is based on the Multi-Head Attention layer and applies multiple of them in a ResNet-like block. +# The Transformer is a very important, recent architecture that can be applied to many tasks and datasets. +# Although it is best known for its success in NLP, there is so much more to it. +# We have seen its application on sequence-to-sequence tasks and set anomaly detection. +# Its property of being permutation-equivariant if we do not provide any positional encodings, allows it to generalize to many settings. +# Hence, it is important to know the architecture, but also its possible issues such as the gradient problem during +# the first iterations solved by learning rate warm-up. +# If you are interested in continuing with the study of the Transformer architecture, +# please have a look at the blog posts listed at the beginning of the tutorial notebook. diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/attention_example.svg b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/attention_example.svg new file mode 100644 index 0000000..bd290ad --- /dev/null +++ b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/attention_example.svg @@ -0,0 +1 @@ + diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/cifar100_example_anomaly.png b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/cifar100_example_anomaly.png new file mode 100644 index 0000000..7e06e5a Binary files /dev/null and b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/cifar100_example_anomaly.png differ diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/comparison_conv_rnn.svg b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/comparison_conv_rnn.svg new file mode 100644 index 0000000..7af315f --- /dev/null +++ b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/comparison_conv_rnn.svg @@ -0,0 +1,1803 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/multihead_attention.svg b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/multihead_attention.svg new file mode 100644 index 0000000..1019553 --- /dev/null +++ b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/multihead_attention.svg @@ -0,0 +1,282 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/scaled_dot_product_attn.svg b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/scaled_dot_product_attn.svg new file mode 100644 index 0000000..7ca74ea --- /dev/null +++ b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/scaled_dot_product_attn.svg @@ -0,0 +1,346 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/transformer_architecture.svg b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/transformer_architecture.svg new file mode 100644 index 0000000..5b6b0f4 --- /dev/null +++ b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/transformer_architecture.svg @@ -0,0 +1,112 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + Encoder + Decoder + + diff --git a/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/warmup_loss_plot.svg b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/warmup_loss_plot.svg new file mode 100644 index 0000000..e38f81c --- /dev/null +++ b/_notebooks/course_UvA-DL/05-transformers-and-MH-attention/warmup_loss_plot.svg @@ -0,0 +1,1573 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/.meta.yml b/_notebooks/course_UvA-DL/06-graph-neural-networks/.meta.yml new file mode 100644 index 0000000..cfd63af --- /dev/null +++ b/_notebooks/course_UvA-DL/06-graph-neural-networks/.meta.yml @@ -0,0 +1,31 @@ +title: "Tutorial 6: Basics of Graph Neural Networks" +author: Phillip Lippe +created: 2021-06-07 +updated: 2023-03-14 +license: CC BY-SA +build: 0 +tags: + - Graph +description: | + In this tutorial, we will discuss the application of neural networks on graphs. + Graph Neural Networks (GNNs) have recently gained increasing popularity in both applications and research, + including domains such as social networks, knowledge graphs, recommender systems, and bioinformatics. + While the theory and math behind GNNs might first seem complicated, + the implementation of those models is quite simple and helps in understanding the methodology. + Therefore, we will discuss the implementation of basic network layers of a GNN, + namely graph convolutions, and attention layers. + Finally, we will apply a GNN on semi-supervised node classification and molecule categorization. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torch-scatter + - torch-sparse + - torch-cluster + - torch-spline-conv + - torch-geometric + - lightning>=2.0.0rc0 +pip__find-link: + # - https://pytorch-geometric.com/whl/torch-1.8.0+cu101.html + - https://pytorch-geometric.com/whl/torch-%(TORCH_MAJOR_DOT_MINOR)s.0+%(DEVICE)s.html +accelerator: + - GPU diff --git a/docs/_static/images/course_UvA-DL/06-graph-neural-networks.jpg b/_notebooks/course_UvA-DL/06-graph-neural-networks/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/06-graph-neural-networks.jpg rename to _notebooks/course_UvA-DL/06-graph-neural-networks/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/GNN_overview.py b/_notebooks/course_UvA-DL/06-graph-neural-networks/GNN_overview.py new file mode 100644 index 0000000..443f442 --- /dev/null +++ b/_notebooks/course_UvA-DL/06-graph-neural-networks/GNN_overview.py @@ -0,0 +1,993 @@ +# %% [markdown] +#
+# We start by importing our standard libraries below. + +# %% +# Standard libraries +import os + +# For downloading pre-trained models +import urllib.request +from urllib.error import HTTPError + +# PyTorch Lightning +import lightning as L + +# PyTorch +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim + +# PyTorch geometric +import torch_geometric +import torch_geometric.data as geom_data +import torch_geometric.nn as geom_nn + +# PL callbacks +from lightning.pytorch.callbacks import ModelCheckpoint +from torch import Tensor + +AVAIL_GPUS = min(1, torch.cuda.device_count()) +BATCH_SIZE = 256 if AVAIL_GPUS else 64 +# Path to the folder where the datasets are/should be downloaded +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/GNNs/") + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +# %% [markdown] +# We also have a few pre-trained models we download below. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial7/" +# Files to download +pretrained_files = ["NodeLevelMLP.ckpt", "NodeLevelGNN.ckpt", "GraphLevelGraphConv.ckpt"] + +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if "/" in file_name: + os.makedirs(file_path.rsplit("/", 1)[0], exist_ok=True) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print("Downloading %s..." % file_url) + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder," + " or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# ## Graph Neural Networks + +# %% [markdown] +# ### Graph representation +# +# Before starting the discussion of specific neural network operations on graphs, we should consider how to represent a graph. +# Mathematically, a graph $\mathcal{G}$ is defined as a tuple of a set of nodes/vertices $V$, and a set of edges/links $E$: $\mathcal{G}=(V,E)$. +# Each edge is a pair of two vertices, and represents a connection between them. +# For instance, let's look at the following graph: +# +#
+# +# The vertices are $V=\{1,2,3,4\}$, and edges $E=\{(1,2), (2,3), (2,4), (3,4)\}$. +# Note that for simplicity, we assume the graph to be undirected and hence don't add mirrored pairs like $(2,1)$. +# In application, vertices and edge can often have specific attributes, and edges can even be directed. +# The question is how we could represent this diversity in an efficient way for matrix operations. +# Usually, for the edges, we decide between two variants: an adjacency matrix, or a list of paired vertex indices. +# +# The **adjacency matrix** $A$ is a square matrix whose elements indicate whether pairs of vertices are adjacent, +# i.e. connected, or not. +# In the simplest case, $A_{ij}$ is 1 if there is a connection from node $i$ to $j$, and otherwise 0. +# If we have edge attributes or different categories of edges in a graph, this information can be added to the matrix as well. +# For an undirected graph, keep in mind that $A$ is a symmetric matrix ($A_{ij}=A_{ji}$). +# For the example graph above, we have the following adjacency matrix: +# +# $$ +# A = \begin{bmatrix} +# 0 & 1 & 0 & 0\\ +# 1 & 0 & 1 & 1\\ +# 0 & 1 & 0 & 1\\ +# 0 & 1 & 1 & 0 +# \end{bmatrix} +# $$ +# +# While expressing a graph as a list of edges is more efficient in terms of memory and (possibly) computation, +# using an adjacency matrix is more intuitive and simpler to implement. +# In our implementations below, we will rely on the adjacency matrix to keep the code simple. +# However, common libraries use edge lists, which we will discuss later more. +# Alternatively, we could also use the list of edges to define a sparse adjacency matrix with which we can work +# as if it was a dense matrix, but allows more memory-efficient operations. +# PyTorch supports this with the sub-package `torch.sparse` +# ([documentation](https://pytorch.org/docs/stable/sparse.html)) which is however still in a beta-stage +# (API might change in future). + +# %% [markdown] +# ### Graph Convolutions +# +# Graph Convolutional Networks have been introduced by [Kipf et al. ](https://openreview.net/pdf?id=SJU4ayYgl) +# in 2016 at the University of Amsterdam. +# He also wrote a great [blog post](https://tkipf.github.io/graph-convolutional-networks/) about this topic, +# which is recommended if you want to read about GCNs from a different perspective. +# GCNs are similar to convolutions in images in the sense that the "filter" parameters are typically shared over all locations in the graph. +# At the same time, GCNs rely on message passing methods, which means that vertices exchange information with the neighbors, +# and send "messages" to each other. +# Before looking at the math, we can try to visually understand how GCNs work. +# The first step is that each node creates a feature vector that represents the message it wants to send to all its neighbors. +# In the second step, the messages are sent to the neighbors, so that a node receives one message per adjacent node. +# Below we have visualized the two steps for our example graph. +# +#
+# +# If we want to formulate that in more mathematical terms, we need to first decide how to combine +# all the messages a node receives. +# As the number of messages vary across nodes, we need an operation that works for any number. +# Hence, the usual way to go is to sum or take the mean. +# Given the previous features of nodes $H^{(l)}$, the GCN layer is defined as follows: +# +# $$H^{(l+1)} = \sigma\left(\hat{D}^{-1/2}\hat{A}\hat{D}^{-1/2}H^{(l)}W^{(l)}\right)$$ +# +# $W^{(l)}$ is the weight parameters with which we transform the input features into messages ($H^{(l)}W^{(l)}$). +# To the adjacency matrix $A$ we add the identity matrix so that each node sends its own message also to itself: +# $\hat{A}=A+I$. +# Finally, to take the average instead of summing, we calculate the matrix $\hat{D}$ which is a diagonal +# matrix with $D_{ii}$ denoting the number of neighbors node $i$ has. +# $\sigma$ represents an arbitrary activation function, and not necessarily the sigmoid (usually a ReLU-based +# activation function is used in GNNs). +# +# When implementing the GCN layer in PyTorch, we can take advantage of the flexible operations on tensors. +# Instead of defining a matrix $\hat{D}$, we can simply divide the summed messages by the number of neighbors afterward. +# Additionally, we replace the weight matrix with a linear layer, which additionally allows us to add a bias. +# Written as a PyTorch module, the GCN layer is defined as follows: + + +# %% +class GCNLayer(nn.Module): + def __init__(self, c_in, c_out): + super().__init__() + self.projection = nn.Linear(c_in, c_out) + + def forward(self, node_feats, adj_matrix): + """ + Args: + node_feats: Tensor with node features of shape [batch_size, num_nodes, c_in] + adj_matrix: Batch of adjacency matrices of the graph. If there is an edge from i to j, + adj_matrix[b,i,j]=1 else 0. Supports directed edges by non-symmetric matrices. + Assumes to already have added the identity connections. + Shape: [batch_size, num_nodes, num_nodes] + """ + # Num neighbours = number of incoming edges + num_neighbours = adj_matrix.sum(dim=-1, keepdims=True) + node_feats = self.projection(node_feats) + node_feats = torch.bmm(adj_matrix, node_feats) + node_feats = node_feats / num_neighbours + return node_feats + + +# %% [markdown] +# To further understand the GCN layer, we can apply it to our example graph above. +# First, let's specify some node features and the adjacency matrix with added self-connections: + +# %% +node_feats = torch.arange(8, dtype=torch.float32).view(1, 4, 2) +adj_matrix = Tensor([[[1, 1, 0, 0], [1, 1, 1, 1], [0, 1, 1, 1], [0, 1, 1, 1]]]) + +print("Node features:\n", node_feats) +print("\nAdjacency matrix:\n", adj_matrix) + +# %% [markdown] +# Next, let's apply a GCN layer to it. +# For simplicity, we initialize the linear weight matrix as an identity matrix so that the input features are equal to the messages. +# This makes it easier for us to verify the message passing operation. + +# %% +layer = GCNLayer(c_in=2, c_out=2) +layer.projection.weight.data = Tensor([[1.0, 0.0], [0.0, 1.0]]) +layer.projection.bias.data = Tensor([0.0, 0.0]) + +with torch.no_grad(): + out_feats = layer(node_feats, adj_matrix) + +print("Adjacency matrix", adj_matrix) +print("Input features", node_feats) +print("Output features", out_feats) + +# %% [markdown] +# As we can see, the first node's output values are the average of itself and the second node. +# Similarly, we can verify all other nodes. +# However, in a GNN, we would also want to allow feature exchange between nodes beyond its neighbors. +# This can be achieved by applying multiple GCN layers, which gives us the final layout of a GNN. +# The GNN can be build up by a sequence of GCN layers and non-linearities such as ReLU. +# For a visualization, see below (figure credit - [Thomas Kipf, 2016](https://tkipf.github.io/graph-convolutional-networks/)). +# +#
+# +# However, one issue we can see from looking at the example above is that the output features for nodes 3 and 4 are +# the same because they have the same adjacent nodes (including itself). +# Therefore, GCN layers can make the network forget node-specific information if we just take a mean over all messages. +# Multiple possible improvements have been proposed. +# While the simplest option might be using residual connections, the more common approach is to either weigh +# the self-connections higher or define a separate weight matrix for the self-connections. +# Alternatively, we can use a well-known concept: attention. + +# %% [markdown] +# ### Graph Attention +# +# Attention describes a weighted average of multiple elements with the weights dynamically computed based on an input +# query and elements' keys (if you don't know what attention is, it is recommended to at least go through +# the very first section called [What is Attention?](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial6/Transformers_and_MHAttention.html#What-is-Attention?)). +# This concept can be similarly applied to graphs, one of such is the Graph Attention Network +# (called GAT, proposed by [Velickovic et al., 2017](https://arxiv.org/abs/1710.10903)). +# Similarly to the GCN, the graph attention layer creates a message for each node using a linear layer/weight matrix. +# For the attention part, it uses the message from the node itself as a query, and the messages to average as both +# keys and values (note that this also includes the message to itself). +# The score function $f_{attn}$ is implemented as a one-layer MLP which maps the query and key to a single value. +# The MLP looks as follows (figure credit - [Velickovic et al. ](https://arxiv.org/abs/1710.10903)): +# +#
+# +# $h_i$ and $h_j$ are the original features from node $i$ and $j$ respectively, and represent the messages +# of the layer with $\mathbf{W}$ as weight matrix. +# $\mathbf{a}$ is the weight matrix of the MLP, which has the shape $[1,2\times d_{\text{message}}]$, +# and $\alpha_{ij}$ the final attention weight from node $i$ to $j$. +# The calculation can be described as follows: +# +# $$\alpha_{ij} = \frac{\exp\left(\text{LeakyReLU}\left(\mathbf{a}\left[\mathbf{W}h_i||\mathbf{W}h_j\right]\right)\right)}{\sum_{k\in\mathcal{N}_i} \exp\left(\text{LeakyReLU}\left(\mathbf{a}\left[\mathbf{W}h_i||\mathbf{W}h_k\right]\right)\right)}$$ +# +# The operator $||$ represents the concatenation, and $\mathcal{N}_i$ the indices of the neighbors of node $i$. +# Note that in contrast to usual practice, we apply a non-linearity (here LeakyReLU) before the softmax over elements. +# Although it seems like a minor change at first, it is crucial for the attention to depend on the original input. +# Specifically, let's remove the non-linearity for a second, and try to simplify the expression: +# +# $$ +# \begin{split} +# \alpha_{ij} & = \frac{\exp\left(\mathbf{a}\left[\mathbf{W}h_i||\mathbf{W}h_j\right]\right)}{\sum_{k\in\mathcal{N}_i} \exp\left(\mathbf{a}\left[\mathbf{W}h_i||\mathbf{W}h_k\right]\right)}\\[5pt] +# & = \frac{\exp\left(\mathbf{a}_{:,:d/2}\mathbf{W}h_i+\mathbf{a}_{:,d/2:}\mathbf{W}h_j\right)}{\sum_{k\in\mathcal{N}_i} \exp\left(\mathbf{a}_{:,:d/2}\mathbf{W}h_i+\mathbf{a}_{:,d/2:}\mathbf{W}h_k\right)}\\[5pt] +# & = \frac{\exp\left(\mathbf{a}_{:,:d/2}\mathbf{W}h_i\right)\cdot\exp\left(\mathbf{a}_{:,d/2:}\mathbf{W}h_j\right)}{\sum_{k\in\mathcal{N}_i} \exp\left(\mathbf{a}_{:,:d/2}\mathbf{W}h_i\right)\cdot\exp\left(\mathbf{a}_{:,d/2:}\mathbf{W}h_k\right)}\\[5pt] +# & = \frac{\exp\left(\mathbf{a}_{:,d/2:}\mathbf{W}h_j\right)}{\sum_{k\in\mathcal{N}_i} \exp\left(\mathbf{a}_{:,d/2:}\mathbf{W}h_k\right)}\\ +# \end{split} +# $$ +# +# We can see that without the non-linearity, the attention term with $h_i$ actually cancels itself out, +# resulting in the attention being independent of the node itself. +# Hence, we would have the same issue as the GCN of creating the same output features for nodes with the same neighbors. +# This is why the LeakyReLU is crucial and adds some dependency on $h_i$ to the attention. +# +# Once we obtain all attention factors, we can calculate the output features for each node by performing +# the weighted average: +# +# $$h_i'=\sigma\left(\sum_{j\in\mathcal{N}_i}\alpha_{ij}\mathbf{W}h_j\right)$$ +# +# $\sigma$ is yet another non-linearity, as in the GCN layer. +# Visually, we can represent the full message passing in an attention layer as follows +# (figure credit - [Velickovic et al. ](https://arxiv.org/abs/1710.10903)): +# +#
+# +# To increase the expressiveness of the graph attention network, [Velickovic et al. ](https://arxiv.org/abs/1710.10903) +# proposed to extend it to multiple heads similar to the Multi-Head Attention block in Transformers. +# This results in $N$ attention layers being applied in parallel. +# In the image above, it is visualized as three different colors of arrows (green, blue, and purple) +# that are afterward concatenated. +# The average is only applied for the very final prediction layer in a network. +# +# After having discussed the graph attention layer in detail, we can implement it below: + + +# %% +class GATLayer(nn.Module): + def __init__(self, c_in, c_out, num_heads=1, concat_heads=True, alpha=0.2): + """ + Args: + c_in: Dimensionality of input features + c_out: Dimensionality of output features + num_heads: Number of heads, i.e. attention mechanisms to apply in parallel. The + output features are equally split up over the heads if concat_heads=True. + concat_heads: If True, the output of the different heads is concatenated instead of averaged. + alpha: Negative slope of the LeakyReLU activation. + """ + super().__init__() + self.num_heads = num_heads + self.concat_heads = concat_heads + if self.concat_heads: + assert c_out % num_heads == 0, "Number of output features must be a multiple of the count of heads." + c_out = c_out // num_heads + + # Sub-modules and parameters needed in the layer + self.projection = nn.Linear(c_in, c_out * num_heads) + self.a = nn.Parameter(Tensor(num_heads, 2 * c_out)) # One per head + self.leakyrelu = nn.LeakyReLU(alpha) + + # Initialization from the original implementation + nn.init.xavier_uniform_(self.projection.weight.data, gain=1.414) + nn.init.xavier_uniform_(self.a.data, gain=1.414) + + def forward(self, node_feats, adj_matrix, print_attn_probs=False): + """ + Args: + node_feats: Input features of the node. Shape: [batch_size, c_in] + adj_matrix: Adjacency matrix including self-connections. Shape: [batch_size, num_nodes, num_nodes] + print_attn_probs: If True, the attention weights are printed during the forward pass + (for debugging purposes) + """ + batch_size, num_nodes = node_feats.size(0), node_feats.size(1) + + # Apply linear layer and sort nodes by head + node_feats = self.projection(node_feats) + node_feats = node_feats.view(batch_size, num_nodes, self.num_heads, -1) + + # We need to calculate the attention logits for every edge in the adjacency matrix + # Doing this on all possible combinations of nodes is very expensive + # => Create a tensor of [W*h_i||W*h_j] with i and j being the indices of all edges + # Returns indices where the adjacency matrix is not 0 => edges + edges = adj_matrix.nonzero(as_tuple=False) + node_feats_flat = node_feats.view(batch_size * num_nodes, self.num_heads, -1) + edge_indices_row = edges[:, 0] * num_nodes + edges[:, 1] + edge_indices_col = edges[:, 0] * num_nodes + edges[:, 2] + a_input = torch.cat( + [ + torch.index_select(input=node_feats_flat, index=edge_indices_row, dim=0), + torch.index_select(input=node_feats_flat, index=edge_indices_col, dim=0), + ], + dim=-1, + ) # Index select returns a tensor with node_feats_flat being indexed at the desired positions + + # Calculate attention MLP output (independent for each head) + attn_logits = torch.einsum("bhc,hc->bh", a_input, self.a) + attn_logits = self.leakyrelu(attn_logits) + + # Map list of attention values back into a matrix + attn_matrix = attn_logits.new_zeros(adj_matrix.shape + (self.num_heads,)).fill_(-9e15) + attn_matrix[adj_matrix[..., None].repeat(1, 1, 1, self.num_heads) == 1] = attn_logits.reshape(-1) + + # Weighted average of attention + attn_probs = F.softmax(attn_matrix, dim=2) + if print_attn_probs: + print("Attention probs\n", attn_probs.permute(0, 3, 1, 2)) + node_feats = torch.einsum("bijh,bjhc->bihc", attn_probs, node_feats) + + # If heads should be concatenated, we can do this by reshaping. Otherwise, take mean + if self.concat_heads: + node_feats = node_feats.reshape(batch_size, num_nodes, -1) + else: + node_feats = node_feats.mean(dim=2) + + return node_feats + + +# %% [markdown] +# Again, we can apply the graph attention layer on our example graph above to understand the dynamics better. +# As before, the input layer is initialized as an identity matrix, but we set $\mathbf{a}$ +# to be a vector of arbitrary numbers to obtain different attention values. +# We use two heads to show the parallel, independent attention mechanisms working in the layer. + +# %% +layer = GATLayer(2, 2, num_heads=2) +layer.projection.weight.data = Tensor([[1.0, 0.0], [0.0, 1.0]]) +layer.projection.bias.data = Tensor([0.0, 0.0]) +layer.a.data = Tensor([[-0.2, 0.3], [0.1, -0.1]]) + +with torch.no_grad(): + out_feats = layer(node_feats, adj_matrix, print_attn_probs=True) + +print("Adjacency matrix", adj_matrix) +print("Input features", node_feats) +print("Output features", out_feats) + +# %% [markdown] +# We recommend that you try to calculate the attention matrix at least for one head and one node for yourself. +# The entries are 0 where there does not exist an edge between $i$ and $j$. +# For the others, we see a diverse set of attention probabilities. +# Moreover, the output features of node 3 and 4 are now different although they have the same neighbors. + +# %% [markdown] +# ## PyTorch Geometric +# +# We had mentioned before that implementing graph networks with adjacency matrix is simple and straight-forward +# but can be computationally expensive for large graphs. +# Many real-world graphs can reach over 200k nodes, for which adjacency matrix-based implementations fail. +# There are a lot of optimizations possible when implementing GNNs, and luckily, there exist packages that provide such layers. +# The most popular packages for PyTorch are [PyTorch Geometric](https://pytorch-geometric.readthedocs.io/en/latest/) +# and the [Deep Graph Library](https://www.dgl.ai/) (the latter being actually framework agnostic). +# Which one to use depends on the project you are planning to do and personal taste. +# In this tutorial, we will look at PyTorch Geometric as part of the PyTorch family. +# +# PyTorch Geometric provides us a set of common graph layers, including the GCN and GAT layer we implemented above. +# Additionally, similar to PyTorch's torchvision, it provides the common graph datasets and transformations +# on those to simplify training. +# Compared to our implementation above, PyTorch Geometric uses a list of index pairs to represent the edges. +# The details of this library will be explored further in our experiments. +# +# In our tasks below, we want to allow us to pick from a multitude of graph layers. +# Thus, we define again below a dictionary to access those using a string: + +# %% +gnn_layer_by_name = {"GCN": geom_nn.GCNConv, "GAT": geom_nn.GATConv, "GraphConv": geom_nn.GraphConv} + +# %% [markdown] +# Additionally to GCN and GAT, we added the layer `geom_nn.GraphConv` +# ([documentation](https://pytorch-geometric.readthedocs.io/en/latest/modules/nn.html#torch_geometric.nn.conv.GraphConv)). +# GraphConv is a GCN with a separate weight matrix for the self-connections. +# Mathematically, this would be: +# +# $$ +# \mathbf{x}_i^{(l+1)} = \mathbf{W}^{(l + 1)}_1 \mathbf{x}_i^{(l)} + \mathbf{W}^{(\ell + 1)}_2 \sum_{j \in \mathcal{N}_i} \mathbf{x}_j^{(l)} +# $$ +# +# In this formula, the neighbor's messages are added instead of averaged. +# However, PyTorch Geometric provides the argument `aggr` to switch between summing, averaging, and max pooling. + +# %% [markdown] +# ## Experiments on graph structures +# +#
+# +# Tasks on graph-structured data can be grouped into three groups: node-level, edge-level and graph-level. +# The different levels describe on which level we want to perform classification/regression. +# We will discuss all three types in more detail below. + +# %% [markdown] +# ### Node-level tasks: Semi-supervised node classification +# +# Node-level tasks have the goal to classify nodes in a graph. +# Usually, we have given a single, large graph with >1000 nodes of which a certain amount of nodes are labeled. +# We learn to classify those labeled examples during training and try to generalize to the unlabeled nodes. +# +# A popular example that we will use in this tutorial is the Cora dataset, a citation network among papers. +# The Cora consists of 2708 scientific publications with links between each other representing +# the citation of one paper by another. +# The task is to classify each publication into one of seven classes. +# Each publication is represented by a bag-of-words vector. +# This means that we have a vector of 1433 elements for each publication, where a 1 at feature $i$ indicates +# that the $i$-th word of a pre-defined dictionary is in the article. +# Binary bag-of-words representations are commonly used when we need very simple encodings, +# and already have an intuition of what words to expect in a network. +# There exist much better approaches, but we will leave this to the NLP courses to discuss. +# +# We will load the dataset below: + +# %% +cora_dataset = torch_geometric.datasets.Planetoid(root=DATASET_PATH, name="Cora") + +# %% [markdown] +# Let's look at how PyTorch Geometric represents the graph data. +# Note that although we have a single graph, PyTorch Geometric returns a dataset for compatibility to other datasets. + +# %% +cora_dataset[0] + +# %% [markdown] +# The graph is represented by a `Data` object +# ([documentation](https://pytorch-geometric.readthedocs.io/en/latest/modules/data.html#torch_geometric.data.Data)) +# which we can access as a standard Python namespace. +# The edge index tensor is the list of edges in the graph and contains the mirrored version of each edge for undirected graphs. +# The `train_mask`, `val_mask`, and `test_mask` are boolean masks that indicate which nodes we should use for training, +# validation, and testing. +# The `x` tensor is the feature tensor of our 2708 publications, and `y` the labels for all nodes. +# +# After having seen the data, we can implement a simple graph neural network. +# The GNN applies a sequence of graph layers (GCN, GAT, or GraphConv), ReLU as activation function, +# and dropout for regularization. +# See below for the specific implementation. + + +# %% +class GNNModel(nn.Module): + def __init__( + self, + c_in, + c_hidden, + c_out, + num_layers=2, + layer_name="GCN", + dp_rate=0.1, + **kwargs, + ): + """ + Args: + c_in: Dimension of input features + c_hidden: Dimension of hidden features + c_out: Dimension of the output features. Usually number of classes in classification + num_layers: Number of "hidden" graph layers + layer_name: String of the graph layer to use + dp_rate: Dropout rate to apply throughout the network + kwargs: Additional arguments for the graph layer (e.g. number of heads for GAT) + """ + super().__init__() + gnn_layer = gnn_layer_by_name[layer_name] + + layers = [] + in_channels, out_channels = c_in, c_hidden + for l_idx in range(num_layers - 1): + layers += [ + gnn_layer(in_channels=in_channels, out_channels=out_channels, **kwargs), + nn.ReLU(inplace=True), + nn.Dropout(dp_rate), + ] + in_channels = c_hidden + layers += [gnn_layer(in_channels=in_channels, out_channels=c_out, **kwargs)] + self.layers = nn.ModuleList(layers) + + def forward(self, x, edge_index): + """ + Args: + x: Input features per node + edge_index: List of vertex index pairs representing the edges in the graph (PyTorch geometric notation) + """ + for layer in self.layers: + # For graph layers, we need to add the "edge_index" tensor as additional input + # All PyTorch Geometric graph layer inherit the class "MessagePassing", hence + # we can simply check the class type. + if isinstance(layer, geom_nn.MessagePassing): + x = layer(x, edge_index) + else: + x = layer(x) + return x + + +# %% [markdown] +# Good practice in node-level tasks is to create an MLP baseline that is applied to each node independently. +# This way we can verify whether adding the graph information to the model indeed improves the prediction, or not. +# It might also be that the features per node are already expressive enough to clearly point towards a specific class. +# To check this, we implement a simple MLP below. + + +# %% +class MLPModel(nn.Module): + def __init__(self, c_in, c_hidden, c_out, num_layers=2, dp_rate=0.1): + """ + Args: + c_in: Dimension of input features + c_hidden: Dimension of hidden features + c_out: Dimension of the output features. Usually number of classes in classification + num_layers: Number of hidden layers + dp_rate: Dropout rate to apply throughout the network + """ + super().__init__() + layers = [] + in_channels, out_channels = c_in, c_hidden + for l_idx in range(num_layers - 1): + layers += [nn.Linear(in_channels, out_channels), nn.ReLU(inplace=True), nn.Dropout(dp_rate)] + in_channels = c_hidden + layers += [nn.Linear(in_channels, c_out)] + self.layers = nn.Sequential(*layers) + + def forward(self, x, *args, **kwargs): + """ + Args: + x: Input features per node + """ + return self.layers(x) + + +# %% [markdown] +# Finally, we can merge the models into a PyTorch Lightning module which handles the training, +# validation, and testing for us. + + +# %% +class NodeLevelGNN(L.LightningModule): + def __init__(self, model_name, **model_kwargs): + super().__init__() + # Saving hyperparameters + self.save_hyperparameters() + + if model_name == "MLP": + self.model = MLPModel(**model_kwargs) + else: + self.model = GNNModel(**model_kwargs) + self.loss_module = nn.CrossEntropyLoss() + + def forward(self, data, mode="train"): + x, edge_index = data.x, data.edge_index + x = self.model(x, edge_index) + + # Only calculate the loss on the nodes corresponding to the mask + if mode == "train": + mask = data.train_mask + elif mode == "val": + mask = data.val_mask + elif mode == "test": + mask = data.test_mask + else: + assert False, "Unknown forward mode: %s" % mode + + loss = self.loss_module(x[mask], data.y[mask]) + acc = (x[mask].argmax(dim=-1) == data.y[mask]).sum().float() / mask.sum() + return loss, acc + + def configure_optimizers(self): + # We use SGD here, but Adam works as well + optimizer = optim.SGD(self.parameters(), lr=0.1, momentum=0.9, weight_decay=2e-3) + return optimizer + + def training_step(self, batch, batch_idx): + loss, acc = self.forward(batch, mode="train") + self.log("train_loss", loss) + self.log("train_acc", acc) + return loss + + def validation_step(self, batch, batch_idx): + _, acc = self.forward(batch, mode="val") + self.log("val_acc", acc) + + def test_step(self, batch, batch_idx): + _, acc = self.forward(batch, mode="test") + self.log("test_acc", acc) + + +# %% [markdown] +# Additionally to the Lightning module, we define a training function below. +# As we have a single graph, we use a batch size of 1 for the data loader and share the same data loader for the train, +# validation, and test set (the mask is picked inside the Lightning module). +# Besides, we set the argument `enable_progress_bar` to False as it usually shows the progress per epoch, +# but an epoch only consists of a single step. +# If you have downloaded the pre-trained models in the beginning of the tutorial, we load those instead of training from scratch. +# Finally, we test the model and return the results. + + +# %% +def train_node_classifier(model_name, dataset, **model_kwargs): + L.seed_everything(42) + node_data_loader = geom_data.DataLoader(dataset, batch_size=1) + + # Create a PyTorch Lightning trainer + root_dir = os.path.join(CHECKPOINT_PATH, "NodeLevel" + model_name) + os.makedirs(root_dir, exist_ok=True) + trainer = L.Trainer( + default_root_dir=root_dir, + callbacks=[ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc")], + accelerator="auto", + devices=AVAIL_GPUS, + max_epochs=200, + enable_progress_bar=False, + ) # 0 because epoch size is 1 + trainer.logger._default_hp_metric = None # Optional logging argument that we don't need + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "NodeLevel%s.ckpt" % model_name) + if os.path.isfile(pretrained_filename): + print("Found pretrained model, loading...") + model = NodeLevelGNN.load_from_checkpoint(pretrained_filename) + else: + L.seed_everything() + model = NodeLevelGNN( + model_name=model_name, c_in=dataset.num_node_features, c_out=dataset.num_classes, **model_kwargs + ) + trainer.fit(model, node_data_loader, node_data_loader) + model = NodeLevelGNN.load_from_checkpoint(trainer.checkpoint_callback.best_model_path) + + # Test best model on the test set + test_result = trainer.test(model, dataloaders=node_data_loader, verbose=False) + batch = next(iter(node_data_loader)) + batch = batch.to(model.device) + _, train_acc = model.forward(batch, mode="train") + _, val_acc = model.forward(batch, mode="val") + result = {"train": train_acc, "val": val_acc, "test": test_result[0]["test_acc"]} + return model, result + + +# %% [markdown] +# Now, we can train our models. First, let's train the simple MLP: + + +# %% +# Small function for printing the test scores +def print_results(result_dict): + if "train" in result_dict: + print("Train accuracy: %4.2f%%" % (100.0 * result_dict["train"])) + if "val" in result_dict: + print("Val accuracy: %4.2f%%" % (100.0 * result_dict["val"])) + print("Test accuracy: %4.2f%%" % (100.0 * result_dict["test"])) + + +# %% +node_mlp_model, node_mlp_result = train_node_classifier( + model_name="MLP", dataset=cora_dataset, c_hidden=16, num_layers=2, dp_rate=0.1 +) + +print_results(node_mlp_result) + +# %% [markdown] +# Although the MLP can overfit on the training dataset because of the high-dimensional input features, +# it does not perform too well on the test set. +# Let's see if we can beat this score with our graph networks: + +# %% +node_gnn_model, node_gnn_result = train_node_classifier( + model_name="GNN", layer_name="GCN", dataset=cora_dataset, c_hidden=16, num_layers=2, dp_rate=0.1 +) +print_results(node_gnn_result) + +# %% [markdown] +# As we would have hoped for, the GNN model outperforms the MLP by quite a margin. +# This shows that using the graph information indeed improves our predictions and lets us generalizes better. +# +# The hyperparameters in the model have been chosen to create a relatively small network. +# This is because the first layer with an input dimension of 1433 can be relatively expensive to perform for large graphs. +# In general, GNNs can become relatively expensive for very big graphs. +# This is why such GNNs either have a small hidden size or use a special batching strategy +# where we sample a connected subgraph of the big, original graph. + +# %% [markdown] +# ### Edge-level tasks: Link prediction +# +# In some applications, we might have to predict on an edge-level instead of node-level. +# The most common edge-level task in GNN is link prediction. +# Link prediction means that given a graph, we want to predict whether there will be/should be an edge between two nodes or not. +# For example, in a social network, this is used by Facebook and co to propose new friends to you. +# Again, graph level information can be crucial to perform this task. +# The output prediction is usually done by performing a similarity metric on the pair of node features, +# which should be 1 if there should be a link, and otherwise close to 0. +# To keep the tutorial short, we will not implement this task ourselves. +# Nevertheless, there are many good resources out there if you are interested in looking closer at this task. +# Tutorials and papers for this topic include: +# +# * [PyTorch Geometric example](https://github.com/rusty1s/pytorch_geometric/blob/master/examples/link_pred.py) +# * [Graph Neural Networks: A Review of Methods and Applications](https://arxiv.org/pdf/1812.08434.pdf), Zhou et al. +# 2019 +# * [Link Prediction Based on Graph Neural Networks](https://papers.nips.cc/paper/2018/file/53f0d7c537d99b3824f0f99d62ea2428-Paper.pdf), Zhang and Chen, 2018. + +# %% [markdown] +# ### Graph-level tasks: Graph classification +# +# Finally, in this part of the tutorial, we will have a closer look at how to apply GNNs to the task of graph classification. +# The goal is to classify an entire graph instead of single nodes or edges. +# Therefore, we are also given a dataset of multiple graphs that we need to classify based on some structural graph properties. +# The most common task for graph classification is molecular property prediction, in which molecules are represented as graphs. +# Each atom is linked to a node, and edges in the graph are the bonds between atoms. +# For example, look at the figure below. +# +#
+# +# On the left, we have an arbitrary, small molecule with different atoms, whereas the right part of the image shows the graph representation. +# The atom types are abstracted as node features (e.g. a one-hot vector), and the different bond types are used as edge features. +# For simplicity, we will neglect the edge attributes in this tutorial, but you can include by using methods like the +# [Relational Graph Convolution](https://arxiv.org/abs/1703.06103) that uses a different weight matrix for each edge type. +# +# The dataset we will use below is called the MUTAG dataset. +# It is a common small benchmark for graph classification algorithms, and contain 188 graphs with 18 nodes +# and 20 edges on average for each graph. +# The graph nodes have 7 different labels/atom types, and the binary graph labels represent "their mutagenic effect +# on a specific gram negative bacterium" (the specific meaning of the labels are not too important here). +# The dataset is part of a large collection of different graph classification datasets, known as the +# [TUDatasets](https://chrsmrrs.github.io/datasets/), which is directly accessible +# via `torch_geometric.datasets.TUDataset` ([documentation](https://pytorch-geometric.readthedocs.io/en/latest/modules/datasets.html#torch_geometric.datasets.TUDataset)) in PyTorch Geometric. +# We can load the dataset below. + +# %% +tu_dataset = torch_geometric.datasets.TUDataset(root=DATASET_PATH, name="MUTAG") + +# %% [markdown] +# Let's look at some statistics for the dataset: + +# %% +print("Data object:", tu_dataset.data) +print("Length:", len(tu_dataset)) +print("Average label: %4.2f" % (tu_dataset.data.y.float().mean().item())) + +# %% [markdown] +# The first line shows how the dataset stores different graphs. +# The nodes, edges, and labels of each graph are concatenated to one tensor, and the dataset stores the indices +# where to split the tensors correspondingly. +# The length of the dataset is the number of graphs we have, and the "average label" +# denotes the percentage of the graph with label 1. +# As long as the percentage is in the range of 0.5, we have a relatively balanced dataset. +# It happens quite often that graph datasets are very imbalanced, hence checking the class balance +# is always a good thing to do. +# +# Next, we will split our dataset into a training and test part. +# Note that we do not use a validation set this time because of the small size of the dataset. +# Therefore, our model might overfit slightly on the validation set due to the noise of the evaluation, +# but we still get an estimate of the performance on untrained data. + +# %% +torch.manual_seed(42) +tu_dataset.shuffle() +train_dataset = tu_dataset[:150] +test_dataset = tu_dataset[150:] + +# %% [markdown] +# When using a data loader, we encounter a problem with batching $N$ graphs. +# Each graph in the batch can have a different number of nodes and edges, and hence we would require a lot of padding to obtain a single tensor. +# Torch geometric uses a different, more efficient approach: we can view the $N$ graphs in a batch as a single large graph with concatenated node and edge list. +# As there is no edge between the $N$ graphs, running GNN layers on the large graph gives us the same output as running the GNN on each graph separately. +# Visually, this batching strategy is visualized below (figure credit - PyTorch Geometric team, +# [tutorial here](https://colab.research.google.com/drive/1I8a0DfQ3fI7Njc62__mVXUlcAleUclnb)). +# +#
+# +# The adjacency matrix is zero for any nodes that come from two different graphs, and otherwise according to the adjacency matrix of the individual graph. +# Luckily, this strategy is already implemented in torch geometric, and hence we can use the corresponding data loader: + +# %% +graph_train_loader = geom_data.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True) +graph_val_loader = geom_data.DataLoader(test_dataset, batch_size=BATCH_SIZE) # Additional loader for a larger datasets +graph_test_loader = geom_data.DataLoader(test_dataset, batch_size=BATCH_SIZE) + +# %% [markdown] +# Let's load a batch below to see the batching in action: + +# %% +batch = next(iter(graph_test_loader)) +print("Batch:", batch) +print("Labels:", batch.y[:10]) +print("Batch indices:", batch.batch[:40]) + +# %% [markdown] +# We have 38 graphs stacked together for the test dataset. +# The batch indices, stored in `batch`, show that the first 12 nodes belong to the first graph, +# the next 22 to the second graph, and so on. +# These indices are important for performing the final prediction. +# To perform a prediction over a whole graph, we usually perform a pooling operation over all nodes after running the GNN model. +# In this case, we will use the average pooling. +# Hence, we need to know which nodes should be included in which average pool. +# Using this pooling, we can already create our graph network below. +# Specifically, we re-use our class `GNNModel` from before, +# and simply add an average pool and single linear layer for the graph prediction task. + + +# %% +class GraphGNNModel(nn.Module): + def __init__(self, c_in, c_hidden, c_out, dp_rate_linear=0.5, **kwargs): + """ + Args: + c_in: Dimension of input features + c_hidden: Dimension of hidden features + c_out: Dimension of output features (usually number of classes) + dp_rate_linear: Dropout rate before the linear layer (usually much higher than inside the GNN) + kwargs: Additional arguments for the GNNModel object + """ + super().__init__() + self.GNN = GNNModel(c_in=c_in, c_hidden=c_hidden, c_out=c_hidden, **kwargs) # Not our prediction output yet! + self.head = nn.Sequential(nn.Dropout(dp_rate_linear), nn.Linear(c_hidden, c_out)) + + def forward(self, x, edge_index, batch_idx): + """ + Args: + x: Input features per node + edge_index: List of vertex index pairs representing the edges in the graph (PyTorch geometric notation) + batch_idx: Index of batch element for each node + """ + x = self.GNN(x, edge_index) + x = geom_nn.global_mean_pool(x, batch_idx) # Average pooling + x = self.head(x) + return x + + +# %% [markdown] +# Finally, we can create a PyTorch Lightning module to handle the training. +# It is similar to the modules we have seen before and does nothing surprising in terms of training. +# As we have a binary classification task, we use the Binary Cross Entropy loss. + + +# %% +class GraphLevelGNN(L.LightningModule): + def __init__(self, **model_kwargs): + super().__init__() + # Saving hyperparameters + self.save_hyperparameters() + + self.model = GraphGNNModel(**model_kwargs) + self.loss_module = nn.BCEWithLogitsLoss() if self.hparams.c_out == 1 else nn.CrossEntropyLoss() + + def forward(self, data, mode="train"): + x, edge_index, batch_idx = data.x, data.edge_index, data.batch + x = self.model(x, edge_index, batch_idx) + x = x.squeeze(dim=-1) + + if self.hparams.c_out == 1: + preds = (x > 0).float() + data.y = data.y.float() + else: + preds = x.argmax(dim=-1) + loss = self.loss_module(x, data.y) + acc = (preds == data.y).sum().float() / preds.shape[0] + return loss, acc + + def configure_optimizers(self): + # High lr because of small dataset and small model + optimizer = optim.AdamW(self.parameters(), lr=1e-2, weight_decay=0.0) + return optimizer + + def training_step(self, batch, batch_idx): + loss, acc = self.forward(batch, mode="train") + self.log("train_loss", loss) + self.log("train_acc", acc) + return loss + + def validation_step(self, batch, batch_idx): + _, acc = self.forward(batch, mode="val") + self.log("val_acc", acc) + + def test_step(self, batch, batch_idx): + _, acc = self.forward(batch, mode="test") + self.log("test_acc", acc) + + +# %% [markdown] +# Below we train the model on our dataset. It resembles the typical training functions we have seen so far. + + +# %% +def train_graph_classifier(model_name, **model_kwargs): + L.seed_everything(42) + + # Create a PyTorch Lightning trainer with the generation callback + root_dir = os.path.join(CHECKPOINT_PATH, "GraphLevel" + model_name) + os.makedirs(root_dir, exist_ok=True) + trainer = L.Trainer( + default_root_dir=root_dir, + callbacks=[ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc")], + accelerator="cuda", + devices=AVAIL_GPUS, + max_epochs=500, + enable_progress_bar=False, + ) + trainer.logger._default_hp_metric = None + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "GraphLevel%s.ckpt" % model_name) + if os.path.isfile(pretrained_filename): + print("Found pretrained model, loading...") + model = GraphLevelGNN.load_from_checkpoint(pretrained_filename) + else: + L.seed_everything(42) + model = GraphLevelGNN( + c_in=tu_dataset.num_node_features, + c_out=1 if tu_dataset.num_classes == 2 else tu_dataset.num_classes, + **model_kwargs, + ) + trainer.fit(model, graph_train_loader, graph_val_loader) + model = GraphLevelGNN.load_from_checkpoint(trainer.checkpoint_callback.best_model_path) + + # Test best model on validation and test set + train_result = trainer.test(model, dataloaders=graph_train_loader, verbose=False) + test_result = trainer.test(model, dataloaders=graph_test_loader, verbose=False) + result = {"test": test_result[0]["test_acc"], "train": train_result[0]["test_acc"]} + return model, result + + +# %% [markdown] +# Finally, let's perform the training and testing. +# Feel free to experiment with different GNN layers, hyperparameters, etc. + +# %% +model, result = train_graph_classifier( + model_name="GraphConv", c_hidden=256, layer_name="GraphConv", num_layers=3, dp_rate_linear=0.5, dp_rate=0.0 +) + +# %% +print("Train performance: %4.2f%%" % (100.0 * result["train"])) +print("Test performance: %4.2f%%" % (100.0 * result["test"])) + +# %% [markdown] +# The test performance shows that we obtain quite good scores on an unseen part of the dataset. +# It should be noted that as we have been using the test set for validation as well, we might have overfitted slightly to this set. +# Nevertheless, the experiment shows us that GNNs can be indeed powerful to predict the properties of graphs and/or molecules. + +# %% [markdown] +# ## Conclusion +# +# In this tutorial, we have seen the application of neural networks to graph structures. +# We looked at how a graph can be represented (adjacency matrix or edge list), +# and discussed the implementation of common graph layers: GCN and GAT. +# The implementations showed the practical side of the layers, which is often easier than the theory. +# Finally, we experimented with different tasks, on node-, edge- and graph-level. +# Overall, we have seen that including graph information in the predictions can be crucial for achieving high performance. +# There are a lot of applications that benefit from GNNs, +# and the importance of these networks will likely increase over the next years. diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/example_graph.svg b/_notebooks/course_UvA-DL/06-graph-neural-networks/example_graph.svg new file mode 100644 index 0000000..1d85c94 --- /dev/null +++ b/_notebooks/course_UvA-DL/06-graph-neural-networks/example_graph.svg @@ -0,0 +1,3 @@ + + +
1
1
4
4
3
3
2
2
diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/gcn_network.png b/_notebooks/course_UvA-DL/06-graph-neural-networks/gcn_network.png new file mode 100644 index 0000000..d191107 Binary files /dev/null and b/_notebooks/course_UvA-DL/06-graph-neural-networks/gcn_network.png differ diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_attention.jpeg b/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_attention.jpeg new file mode 100644 index 0000000..4f382cb Binary files /dev/null and b/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_attention.jpeg differ diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_attention_MLP.svg b/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_attention_MLP.svg new file mode 100644 index 0000000..66d219d --- /dev/null +++ b/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_attention_MLP.svg @@ -0,0 +1,553 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_message_passing.svg b/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_message_passing.svg new file mode 100644 index 0000000..35f9915 --- /dev/null +++ b/_notebooks/course_UvA-DL/06-graph-neural-networks/graph_message_passing.svg @@ -0,0 +1,3 @@ + + +
1
1
4
4
3
3
2
2
1
1
4
4
3
3
2
2
Message passing
Message passing
diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/molecule_graph.svg b/_notebooks/course_UvA-DL/06-graph-neural-networks/molecule_graph.svg new file mode 100644 index 0000000..d5c8e1d --- /dev/null +++ b/_notebooks/course_UvA-DL/06-graph-neural-networks/molecule_graph.svg @@ -0,0 +1,434 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/06-graph-neural-networks/torch_geometric_stacking_graphs.png b/_notebooks/course_UvA-DL/06-graph-neural-networks/torch_geometric_stacking_graphs.png new file mode 100644 index 0000000..14bccb9 Binary files /dev/null and b/_notebooks/course_UvA-DL/06-graph-neural-networks/torch_geometric_stacking_graphs.png differ diff --git a/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/.meta.yml b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/.meta.yml new file mode 100644 index 0000000..a5d7c01 --- /dev/null +++ b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/.meta.yml @@ -0,0 +1,28 @@ +title: "Tutorial 7: Deep Energy-Based Generative Models" +author: Phillip Lippe +created: 2021-07-12 +updated: 2023-03-14 +license: CC BY-SA +build: 0 +tags: + - Image +description: | + In this tutorial, we will look at energy-based deep learning models, and focus on their application as generative models. + Energy models have been a popular tool before the huge deep learning hype around 2012 hit. + However, in recent years, energy-based models have gained increasing attention because of improved training methods and tricks being proposed. + Although they are still in a research stage, they have shown to outperform strong Generative Adversarial Networks + in certain cases which have been the state of the art of generating images + ([blog post](https://ajolicoeur.wordpress.com/the-new-contender-to-gans-score-matching-with-langevin-sampling/)about strong energy-based models, + [blog post](https://medium.com/syncedreview/nvidia-open-sources-hyper-realistic-face-generator-stylegan-f346e1a73826) about the power of GANs). + Hence, it is important to be aware of energy-based models, and as the theory can be abstract sometimes, + we will show the idea of energy-based models with a lot of examples. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - tensorboard + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/07-deep-energy-based-generative-models.jpg b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/07-deep-energy-based-generative-models.jpg rename to _notebooks/course_UvA-DL/07-deep-energy-based-generative-models/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/Deep_Energy_Models.py b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/Deep_Energy_Models.py new file mode 100644 index 0000000..6cd07a4 --- /dev/null +++ b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/Deep_Energy_Models.py @@ -0,0 +1,887 @@ +# %% [markdown] +#
+# First, let's import our standard libraries below. + +# %% +# Standard libraries +import os +import random +import urllib.request +from urllib.error import HTTPError + +# PyTorch Lightning +import lightning as L + +# Plotting +import matplotlib +import matplotlib.pyplot as plt + +# %matplotlib inline +import matplotlib_inline.backend_inline +import numpy as np + +# PyTorch +import torch +import torch.nn as nn +import torch.optim as optim +import torch.utils.data as data + +# Torchvision +import torchvision +from lightning.pytorch.callbacks import Callback, LearningRateMonitor, ModelCheckpoint +from torchvision import transforms +from torchvision.datasets import MNIST + +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +matplotlib.rcParams["lines.linewidth"] = 2.0 + +# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/tutorial8") + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu") + +# %% [markdown] +# We also have pre-trained models that we download below. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial8/" +# Files to download +pretrained_files = ["MNIST.ckpt", "tensorboards/events.out.tfevents.MNIST"] + +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if "/" in file_name: + os.makedirs(file_path.rsplit("/", 1)[0], exist_ok=True) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print("Downloading %s..." % file_url) + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the files manually," + " or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# ## Energy Models +# +# In the first part of this tutorial, we will review the theory of the energy-based models +# (the same theory has been discussed in Lecture 8). +# While most of the previous models had the goal of classification or regression, +# energy-based models are motivated from a different perspective: density estimation. +# Given a dataset with a lot of elements, we want to estimate the probability distribution over the whole data space. +# As an example, if we model images from CIFAR10, our goal would be to have a probability distribution +# over all possible images of size $32\times32\times3$ where those images have a high likelihood +# that look realistic and are one of the 10 CIFAR classes. +# Simple methods like interpolation between images don't work because images are extremely high-dimensional +# (especially for large HD images). +# Hence, we turn to deep learning methods that have performed well on complex data. +# +# However, how do we predict a probability distribution $p(\mathbf{x})$ over so many dimensions using a simple neural network? +# The problem is that we cannot just predict a score between 0 and 1, +# because a probability distribution over data needs to fulfill two properties: +# +# 1. +# The probability distribution needs to assign any possible value of +# $\mathbf{x}$ a non-negative value: $p(\mathbf{x}) \geq 0$. +# 2. +# The probability density must sum/integrate to 1 over **all** possible inputs: +# $\int_{\mathbf{x}} p(\mathbf{x}) d\mathbf{x} = 1$. +# +# Luckily, there are actually many approaches for this, and one of them are energy-based models. +# The fundamental idea of energy-based models is that you can turn any function +# that predicts values larger than zero into a probability distribution by dviding by its volume. +# Imagine we have a neural network, which has as output a single neuron, like in regression. +# We can call this network $E_{\theta}(\mathbf{x})$, where $\theta$ are our parameters of the network, +# and $\mathbf{x}$ the input data (e.g. an image). +# The output of $E_{\theta}$ is a scalar value between $-\infty$ and $\infty$. +# Now, we can use basic probability theory to *normalize* the scores of all possible inputs: +# +# $$ +# q_{\theta}(\mathbf{x}) = \frac{\exp\left(-E_{\theta}(\mathbf{x})\right)}{Z_{\theta}} \hspace{5mm}\text{where}\hspace{5mm} +# Z_{\theta} = \begin{cases} +# \int_{\mathbf{x}}\exp\left(-E_{\theta}(\mathbf{x})\right) d\mathbf{x} & \text{if }x\text{ is continuous}\\ +# \sum_{\mathbf{x}}\exp\left(-E_{\theta}(\mathbf{x})\right) & \text{if }x\text{ is discrete} +# \end{cases} +# $$ +# +# The $\exp$-function ensures that we assign a probability greater than zero to any possible input. +# We use a negative sign in front of $E$ because we call $E_{\theta}$ to be the energy function: +# data points with high likelihood have a low energy, while data points with low likelihood have a high energy. +# $Z_{\theta}$ is our normalization terms that ensures that the density integrates/sums to 1. +# We can show this by integrating over $q_{\theta}(\mathbf{x})$: +# +# $$ +# \int_{\mathbf{x}}q_{\theta}(\mathbf{x})d\mathbf{x} = +# \int_{\mathbf{x}}\frac{\exp\left(-E_{\theta}(\mathbf{x})\right)}{\int_{\mathbf{\tilde{x}}}\exp\left(-E_{\theta}(\mathbf{\tilde{x}})\right) d\mathbf{\tilde{x}}}d\mathbf{x} = +# \frac{\int_{\mathbf{x}}\exp\left(-E_{\theta}(\mathbf{x})\right)d\mathbf{x}}{\int_{\mathbf{\tilde{x}}}\exp\left(-E_{\theta}(\mathbf{\tilde{x}})\right) d\mathbf{\tilde{x}}} = 1 +# $$ +# +# Note that we call the probability distribution $q_{\theta}(\mathbf{x})$ because this is the learned distribution by the model, +# and is trained to be as close as possible to the *true*, unknown distribution $p(\mathbf{x})$. +# +# The main benefit of this formulation of the probability distribution is its great flexibility as we can choose +# $E_{\theta}$ in whatever way we like, without any constraints. +# Nevertheless, when looking at the equation above, we can see a fundamental issue: How do we calculate $Z_{\theta}$? +# There is no chance that we can calculate $Z_{\theta}$ analytically for high-dimensional input +# and/or larger neural networks, but the task requires us to know $Z_{\theta}$. +# Although we can't determine the exact likelihood of a point, there exist methods with which we can train energy-based models. +# Thus, we will look next at "Contrastive Divergence" for training the model. + +# %% [markdown] +# ### Contrastive Divergence +# +# When we train a model on generative modeling, it is usually done by maximum likelihood estimation. +# In other words, we try to maximize the likelihood of the examples in the training set. +# As the exact likelihood of a point cannot be determined due to the unknown normalization constant $Z_{\theta}$, +# we need to train energy-based models slightly different. +# We cannot just maximize the un-normalized probability $\exp(-E_{\theta}(\mathbf{x}_{\text{train}}))$ +# because there is no guarantee that $Z_{\theta}$ stays constant, or that $\mathbf{x}_{\text{train}}$ +# is becoming more likely than the others. +# However, if we base our training on comparing the likelihood of points, we can create a stable objective. +# Namely, we can re-write our maximum likelihood objective where we maximize the probability +# of $\mathbf{x}_{\text{train}}$ compared to a randomly sampled data point of our model: +# +# $$ +# \begin{split} +# \nabla_{\theta}\mathcal{L}_{\text{MLE}}(\mathbf{\theta};p) & = -\mathbb{E}_{p(\mathbf{x})}\left[\nabla_{\theta}\log q_{\theta}(\mathbf{x})\right]\\[5pt] +# & = \mathbb{E}_{p(\mathbf{x})}\left[\nabla_{\theta}E_{\theta}(\mathbf{x})\right] - \mathbb{E}_{q_{\theta}(\mathbf{x})}\left[\nabla_{\theta}E_{\theta}(\mathbf{x})\right] +# \end{split} +# $$ +# +# Note that the loss is still an objective we want to minimize. +# Thus, we try to minimize the energy for data points from the dataset, while maximizing the energy for randomly +# sampled data points from our model (how we sample will be explained below). +# Although this objective sounds intuitive, how is it actually derived from our original distribution $q_{\theta}(\mathbf{x})$? +# The trick is that we approximate $Z_{\theta}$ by a single Monte-Carlo sample. +# This gives us the exact same objective as written above. +# +# Visually, we can look at the objective as follows (figure credit +# - [Stefano Ermon and Aditya Grover](https://deepgenerativemodels.github.io/assets/slides/cs236_lecture11.pdf)): +# +#
+# +# $f_{\theta}$ represents $\exp(-E_{\theta}(\mathbf{x}))$ in our case. +# The point on the right, called "correct answer", represents a data point from the dataset +# (i.e. $x_{\text{train}}$), and the left point, "wrong answer", a sample from our model (i.e. $x_{\text{sample}}$). +# Thus, we try to "pull up" the probability of the data points in the dataset, +# while "pushing down" randomly sampled points. +# The two forces for pulling and pushing are in balance iff $q_{\theta}(\mathbf{x})=p(\mathbf{x})$. + +# %% [markdown] +# ### Sampling from Energy-Based Models +# +# For sampling from an energy-based model, we can apply a Markov Chain Monte Carlo using Langevin Dynamics. +# The idea of the algorithm is to start from a random point, and slowly move towards the direction +# of higher probability using the gradients of $E_{\theta}$. +# Nevertheless, this is not enough to fully capture the probability distribution. +# We need to add noise $\omega$ at each gradient step to the current sample. +# Under certain conditions such as that we perform the gradient steps an infinite amount of times, +# we would be able to create an exact sample from our modeled distribution. +# However, as this is not practically possible, we usually limit the chain to $K$ steps +# ($K$ a hyperparameter that needs to be finetuned). +# Overall, the sampling procedure can be summarized in the following algorithm: +# +#
+ +# %% [markdown] +# ### Applications of Energy-based models beyond generation +# +# Modeling the probability distribution for sampling new data is not the only application of energy-based models. +# Any application which requires us to compare two elements is much simpler to learn +# because we just need to go for the higher energy. +# A couple of examples are shown below (figure credit +# - [Stefano Ermon and Aditya Grover](https://deepgenerativemodels.github.io/assets/slides/cs236_lecture11.pdf)). +# A classification setup like object recognition or sequence labeling can be considered as an energy-based +# task as we just need to find the $Y$ input that minimizes the output $E(X, Y)$ (hence maximizes probability). +# Similarly, a popular application of energy-based models is denoising of images. +# Given an image $X$ with a lot of noise, we try to minimize the energy by finding the true input image $Y$. +# +#
+# +# Nonetheless, we will focus on generative modeling here as in the next couple of lectures, +# we will discuss more generative deep learning approaches. + +# %% [markdown] +# ## Image generation +# +#
+# +# As an example for energy-based models, we will train a model on image generation. +# Specifically, we will look at how we can generate MNIST digits with a very simple CNN model. +# However, it should be noted that energy models are not easy to train and often diverge +# if the hyperparameters are not well tuned. +# We will rely on training tricks proposed in the paper +# [Implicit Generation and Generalization in Energy-Based Models](https://arxiv.org/abs/1903.08689) +# by Yilun Du and Igor Mordatch ([blog](https://openai.com/blog/energy-based-models/)). +# The important part of this notebook is however to see how the theory above can actually be used in a model. +# +# ### Dataset +# +# First, we can load the MNIST dataset below. +# Note that we need to normalize the images between -1 and 1 instead of mean 0 and std 1 because during sampling, +# we have to limit the input space. +# Scaling between -1 and 1 makes it easier to implement it. + +# %% +# Transformations applied on each image => make them a tensor and normalize between -1 and 1 +transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) + +# Loading the training dataset. We need to split it into a training and validation part +train_set = MNIST(root=DATASET_PATH, train=True, transform=transform, download=True) + +# Loading the test set +test_set = MNIST(root=DATASET_PATH, train=False, transform=transform, download=True) + +# We define a set of data loaders that we can use for various purposes later. +# Note that for actually training a model, we will use different data loaders +# with a lower batch size. +train_loader = data.DataLoader(train_set, batch_size=128, shuffle=True, drop_last=True, num_workers=4, pin_memory=True) +test_loader = data.DataLoader(test_set, batch_size=256, shuffle=False, drop_last=False, num_workers=4) + +# %% [markdown] +# ### CNN Model +# +# First, we implement our CNN model. +# The MNIST images are of size 28x28, hence we only need a small model. +# As an example, we will apply several convolutions with stride 2 that downscale the images. +# If you are interested, you can also use a deeper model such as a small ResNet, but for simplicity, +# we will stick with the tiny network. +# +# It is a good practice to use a smooth activation function like Swish instead of ReLU in the energy model. +# This is because we will rely on the gradients we get back with respect to the input image, which should not be sparse. + + +# %% +class CNNModel(nn.Module): + def __init__(self, hidden_features=32, out_dim=1, **kwargs): + super().__init__() + # We increase the hidden dimension over layers. Here pre-calculated for simplicity. + c_hid1 = hidden_features // 2 + c_hid2 = hidden_features + c_hid3 = hidden_features * 2 + + # Series of convolutions and Swish activation functions + self.cnn_layers = nn.Sequential( + nn.Conv2d(1, c_hid1, kernel_size=5, stride=2, padding=4), # [16x16] - Larger padding to get 32x32 image + nn.SiLU(), + nn.Conv2d(c_hid1, c_hid2, kernel_size=3, stride=2, padding=1), # [8x8] + nn.SiLU(), + nn.Conv2d(c_hid2, c_hid3, kernel_size=3, stride=2, padding=1), # [4x4] + nn.SiLU(), + nn.Conv2d(c_hid3, c_hid3, kernel_size=3, stride=2, padding=1), # [2x2] + nn.SiLU(), + nn.Flatten(), + nn.Linear(c_hid3 * 4, c_hid3), + nn.SiLU(), + nn.Linear(c_hid3, out_dim), + ) + + def forward(self, x): + x = self.cnn_layers(x).squeeze(dim=-1) + return x + + +# %% [markdown] +# In the rest of the notebook, the output of the model will actually not represent +# $E_{\theta}(\mathbf{x})$, but $-E_{\theta}(\mathbf{x})$. +# This is a standard implementation practice for energy-based models, as some people also write the energy probability +# density as $q_{\theta}(\mathbf{x}) = \frac{\exp\left(f_{\theta}(\mathbf{x})\right)}{Z_{\theta}}$. +# In that case, the model would actually represent $f_{\theta}(\mathbf{x})$. +# In the training loss etc., we need to be careful to not switch up the signs. + +# %% [markdown] +# ### Sampling buffer +# +# In the next part, we look at the training with sampled elements. +# To use the contrastive divergence objective, we need to generate samples during training. +# Previous work has shown that due to the high dimensionality of images, we need a lot of iterations +# inside the MCMC sampling to obtain reasonable samples. +# However, there is a training trick that significantly reduces the sampling cost: using a sampling buffer. +# The idea is that we store the samples of the last couple of batches in a buffer, +# and re-use those as the starting point of the MCMC algorithm for the next batches. +# This reduces the sampling cost because the model requires a significantly +# lower number of steps to converge to reasonable samples. +# However, to not solely rely on previous samples and allow novel samples as well, +# we re-initialize 5% of our samples from scratch (random noise between -1 and 1). +# +# Below, we implement the sampling buffer. +# The function `sample_new_exmps` returns a new batch of "fake" images. +# We refer to those as fake images because they have been generated, but are not actually part of the dataset. +# As mentioned before, we use initialize 5% randomly, and 95% are randomly picked from our buffer. +# On this initial batch, we perform MCMC for 60 iterations to improve the image quality +# and come closer to samples from $q_{\theta}(\mathbf{x})$. +# In the function `generate_samples`, we implemented the MCMC for images. +# Note that the hyperparameters of `step_size`, `steps`, the noise standard deviation +# $\sigma$ are specifically set for MNIST, and need to be finetuned for a different dataset if you want to use such. + + +# %% +class Sampler: + def __init__(self, model, img_shape, sample_size, max_len=8192): + """ + Args: + model: Neural network to use for modeling E_theta + img_shape: Shape of the images to model + sample_size: Batch size of the samples + max_len: Maximum number of data points to keep in the buffer + """ + super().__init__() + self.model = model + self.img_shape = img_shape + self.sample_size = sample_size + self.max_len = max_len + self.examples = [(torch.rand((1,) + img_shape) * 2 - 1) for _ in range(self.sample_size)] + + def sample_new_exmps(self, steps=60, step_size=10): + """Function for getting a new batch of "fake" images. + + Args: + steps: Number of iterations in the MCMC algorithm + step_size: Learning rate nu in the algorithm above + """ + # Choose 95% of the batch from the buffer, 5% generate from scratch + n_new = np.random.binomial(self.sample_size, 0.05) + rand_imgs = torch.rand((n_new,) + self.img_shape) * 2 - 1 + old_imgs = torch.cat(random.choices(self.examples, k=self.sample_size - n_new), dim=0) + inp_imgs = torch.cat([rand_imgs, old_imgs], dim=0).detach().to(device) + + # Perform MCMC sampling + inp_imgs = Sampler.generate_samples(self.model, inp_imgs, steps=steps, step_size=step_size) + + # Add new images to the buffer and remove old ones if needed + self.examples = list(inp_imgs.to(torch.device("cpu")).chunk(self.sample_size, dim=0)) + self.examples + self.examples = self.examples[: self.max_len] + return inp_imgs + + @staticmethod + def generate_samples(model, inp_imgs, steps=60, step_size=10, return_img_per_step=False): + """Function for sampling images for a given model. + + Args: + model: Neural network to use for modeling E_theta + inp_imgs: Images to start from for sampling. If you want to generate new images, enter noise between -1 and 1. + steps: Number of iterations in the MCMC algorithm. + step_size: Learning rate nu in the algorithm above + return_img_per_step: If True, we return the sample at every iteration of the MCMC + """ + # Before MCMC: set model parameters to "required_grad=False" + # because we are only interested in the gradients of the input. + is_training = model.training + model.eval() + for p in model.parameters(): + p.requires_grad = False + inp_imgs.requires_grad = True + + # Enable gradient calculation if not already the case + had_gradients_enabled = torch.is_grad_enabled() + torch.set_grad_enabled(True) + + # We use a buffer tensor in which we generate noise each loop iteration. + # More efficient than creating a new tensor every iteration. + noise = torch.randn(inp_imgs.shape, device=inp_imgs.device) + + # List for storing generations at each step (for later analysis) + imgs_per_step = [] + + # Loop over K (steps) + for _ in range(steps): + # Part 1: Add noise to the input. + noise.normal_(0, 0.005) + inp_imgs.data.add_(noise.data) + inp_imgs.data.clamp_(min=-1.0, max=1.0) + + # Part 2: calculate gradients for the current input. + out_imgs = -model(inp_imgs) + out_imgs.sum().backward() + inp_imgs.grad.data.clamp_(-0.03, 0.03) # For stabilizing and preventing too high gradients + + # Apply gradients to our current samples + inp_imgs.data.add_(-step_size * inp_imgs.grad.data) + inp_imgs.grad.detach_() + inp_imgs.grad.zero_() + inp_imgs.data.clamp_(min=-1.0, max=1.0) + + if return_img_per_step: + imgs_per_step.append(inp_imgs.clone().detach()) + + # Reactivate gradients for parameters for training + for p in model.parameters(): + p.requires_grad = True + model.train(is_training) + + # Reset gradient calculation to setting before this function + torch.set_grad_enabled(had_gradients_enabled) + + if return_img_per_step: + return torch.stack(imgs_per_step, dim=0) + else: + return inp_imgs + + +# %% [markdown] +# The idea of the buffer becomes a bit clearer in the following algorithm. + +# %% [markdown] +# ### Training algorithm +# +# With the sampling buffer being ready, we can complete our training algorithm. +# Below is shown a summary of the full training algorithm of an energy model on image modeling: +# +#
+# +# The first few statements in each training iteration concern the sampling of the real and fake data, +# as we have seen above with the sample buffer. +# Next, we calculate the contrastive divergence objective using our energy model $E_{\theta}$. +# However, one additional training trick we need is to add a regularization loss on the output of $E_{\theta}$. +# As the output of the network is not constrained and adding a large bias or not to the output +# doesn't change the contrastive divergence loss, we need to ensure somehow else that the output values are in a reasonable range. +# Without the regularization loss, the output values will fluctuate in a very large range. +# With this, we ensure that the values for the real data are around 0, and the fake data likely slightly lower +# (for noise or outliers the score can be still significantly lower). +# As the regularization loss is less important than the Contrastive Divergence, we have a weight factor +# $\alpha$ which is usually quite some smaller than 1. +# Finally, we perform an update step with an optimizer on the combined loss and add the new samples to the buffer. +# +# Below, we put this training dynamic into a PyTorch Lightning module: + + +# %% +class DeepEnergyModel(L.LightningModule): + def __init__(self, img_shape, batch_size, alpha=0.1, lr=1e-4, beta1=0.0, **CNN_args): + super().__init__() + self.save_hyperparameters() + + self.cnn = CNNModel(**CNN_args) + self.sampler = Sampler(self.cnn, img_shape=img_shape, sample_size=batch_size) + self.example_input_array = torch.zeros(1, *img_shape) + + def forward(self, x): + z = self.cnn(x) + return z + + def configure_optimizers(self): + # Energy models can have issues with momentum as the loss surfaces changes with its parameters. + # Hence, we set it to 0 by default. + optimizer = optim.Adam(self.parameters(), lr=self.hparams.lr, betas=(self.hparams.beta1, 0.999)) + scheduler = optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.97) # Exponential decay over epochs + return [optimizer], [scheduler] + + def training_step(self, batch, batch_idx): + # We add minimal noise to the original images to prevent the model from focusing on purely "clean" inputs + real_imgs, _ = batch + small_noise = torch.randn_like(real_imgs) * 0.005 + real_imgs.add_(small_noise).clamp_(min=-1.0, max=1.0) + + # Obtain samples + fake_imgs = self.sampler.sample_new_exmps(steps=60, step_size=10) + + # Predict energy score for all images + inp_imgs = torch.cat([real_imgs, fake_imgs], dim=0) + real_out, fake_out = self.cnn(inp_imgs).chunk(2, dim=0) + + # Calculate losses + reg_loss = self.hparams.alpha * (real_out**2 + fake_out**2).mean() + cdiv_loss = fake_out.mean() - real_out.mean() + loss = reg_loss + cdiv_loss + + # Logging + self.log("loss", loss) + self.log("loss_regularization", reg_loss) + self.log("loss_contrastive_divergence", cdiv_loss) + self.log("metrics_avg_real", real_out.mean()) + self.log("metrics_avg_fake", fake_out.mean()) + return loss + + def validation_step(self, batch, batch_idx): + # For validating, we calculate the contrastive divergence between purely random images and unseen examples + # Note that the validation/test step of energy-based models depends on what we are interested in the model + real_imgs, _ = batch + fake_imgs = torch.rand_like(real_imgs) * 2 - 1 + + inp_imgs = torch.cat([real_imgs, fake_imgs], dim=0) + real_out, fake_out = self.cnn(inp_imgs).chunk(2, dim=0) + + cdiv = fake_out.mean() - real_out.mean() + self.log("val_contrastive_divergence", cdiv) + self.log("val_fake_out", fake_out.mean()) + self.log("val_real_out", real_out.mean()) + + +# %% [markdown] +# We do not implement a test step because energy-based, generative models are usually not evaluated on a test set. +# The validation step however is used to get an idea of the difference between ennergy/likelihood +# of random images to unseen examples of the dataset. + +# %% [markdown] +# ### Callbacks +# +# To track the performance of our model during training, we will make extensive use of PyTorch Lightning's callback framework. +# Remember that callbacks can be used for running small functions at any point of the training, +# for instance after finishing an epoch. +# Here, we will use three different callbacks we define ourselves. +# +# The first callback, called `GenerateCallback`, is used for adding image generations to the model during training. +# After every $N$ epochs (usually $N=5$ to reduce output to TensorBoard), we take a small batch +# of random images and perform many MCMC iterations until the model's generation converges. +# Compared to the training that used 60 iterations, we use 256 here because +# (1) we only have to do it once compared to the training that has to do it every iteration, and +# (2) we do not start from a buffer here, but from scratch. +# It is implemented as follows: + + +# %% +class GenerateCallback(Callback): + def __init__(self, batch_size=8, vis_steps=8, num_steps=256, every_n_epochs=5): + super().__init__() + self.batch_size = batch_size # Number of images to generate + self.vis_steps = vis_steps # Number of steps within generation to visualize + self.num_steps = num_steps # Number of steps to take during generation + # Only save those images every N epochs (otherwise tensorboard gets quite large) + self.every_n_epochs = every_n_epochs + + def on_epoch_end(self, trainer, pl_module): + # Skip for all other epochs + if trainer.current_epoch % self.every_n_epochs == 0: + # Generate images + imgs_per_step = self.generate_imgs(pl_module) + # Plot and add to tensorboard + for i in range(imgs_per_step.shape[1]): + step_size = self.num_steps // self.vis_steps + imgs_to_plot = imgs_per_step[step_size - 1 :: step_size, i] + grid = torchvision.utils.make_grid( + imgs_to_plot, nrow=imgs_to_plot.shape[0], normalize=True, range=(-1, 1) + ) + trainer.logger.experiment.add_image("generation_%i" % i, grid, global_step=trainer.current_epoch) + + def generate_imgs(self, pl_module): + pl_module.eval() + start_imgs = torch.rand((self.batch_size,) + pl_module.hparams["img_shape"]).to(pl_module.device) + start_imgs = start_imgs * 2 - 1 + imgs_per_step = Sampler.generate_samples( + pl_module.cnn, start_imgs, steps=self.num_steps, step_size=10, return_img_per_step=True + ) + pl_module.train() + return imgs_per_step + + +# %% [markdown] +# The second callback is called `SamplerCallback`, and simply adds a randomly picked subset of images +# in the sampling buffer to the TensorBoard. +# This helps to understand what images are currently shown to the model as "fake". + + +# %% +class SamplerCallback(Callback): + def __init__(self, num_imgs=32, every_n_epochs=5): + super().__init__() + self.num_imgs = num_imgs # Number of images to plot + # Only save those images every N epochs (otherwise tensorboard gets quite large) + self.every_n_epochs = every_n_epochs + + def on_epoch_end(self, trainer, pl_module): + if trainer.current_epoch % self.every_n_epochs == 0: + exmp_imgs = torch.cat(random.choices(pl_module.sampler.examples, k=self.num_imgs), dim=0) + grid = torchvision.utils.make_grid(exmp_imgs, nrow=4, normalize=True, range=(-1, 1)) + trainer.logger.experiment.add_image("sampler", grid, global_step=trainer.current_epoch) + + +# %% [markdown] +# Finally, our last callback is `OutlierCallback`. +# This callback evaluates the model by recording the (negative) energy assigned to random noise. +# While our training loss is almost constant across iterations, +# this score is likely showing the progress of the model to detect "outliers". + + +# %% +class OutlierCallback(Callback): + def __init__(self, batch_size=1024): + super().__init__() + self.batch_size = batch_size + + def on_epoch_end(self, trainer, pl_module): + with torch.no_grad(): + pl_module.eval() + rand_imgs = torch.rand((self.batch_size,) + pl_module.hparams["img_shape"]).to(pl_module.device) + rand_imgs = rand_imgs * 2 - 1.0 + rand_out = pl_module.cnn(rand_imgs).mean() + pl_module.train() + + trainer.logger.experiment.add_scalar("rand_out", rand_out, global_step=trainer.current_epoch) + + +# %% [markdown] +# ### Running the model +# +# Finally, we can add everything together to create our final training function. +# The function is very similar to any other PyTorch Lightning training function we have seen so far. +# However, there is the small difference of that we do not test the model on a test set +# because we will analyse the model afterward by checking its prediction and ability to perform outlier detection. + + +# %% +def train_model(**kwargs): + # Create a PyTorch Lightning trainer with the generation callback + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, "MNIST"), + accelerator="auto", + devices=1, + max_epochs=60, + gradient_clip_val=0.1, + callbacks=[ + ModelCheckpoint(save_weights_only=True, mode="min", monitor="val_contrastive_divergence"), + GenerateCallback(every_n_epochs=5), + SamplerCallback(every_n_epochs=5), + OutlierCallback(), + LearningRateMonitor("epoch"), + ], + ) + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "MNIST.ckpt") + if os.path.isfile(pretrained_filename): + print("Found pretrained model, loading...") + model = DeepEnergyModel.load_from_checkpoint(pretrained_filename) + else: + L.seed_everything(42) + model = DeepEnergyModel(**kwargs) + trainer.fit(model, train_loader, test_loader) + model = DeepEnergyModel.load_from_checkpoint(trainer.checkpoint_callback.best_model_path) + # No testing as we are more interested in other properties + return model + + +# %% +model = train_model(img_shape=(1, 28, 28), batch_size=train_loader.batch_size, lr=1e-4, beta1=0.0) + +# %% [markdown] +# ## Analysis +# +# In the last part of the notebook, we will try to take the trained energy-based generative model, +# and analyse its properties. + +# %% [markdown] +# ### TensorBoard +# +# The first thing we can look at is the TensorBoard generate during training. +# This can help us to understand the training dynamic even better, and shows potential issues. +# Let's load the TensorBoard below: + +# %% +# Uncomment the following two lines to open a tensorboard in the notebook. +# Adjust the path to your CHECKPOINT_PATH if needed. +# %load_ext tensorboard +# %tensorboard --logdir ../saved_models/tutorial8/tensorboards/ + +# %% [markdown] +#
+ +# %% [markdown] +# We see that the contrastive divergence as well as the regularization converge quickly to 0. +# However, the training continues although the loss is always close to zero. +# This is because our "training" data changes with the model by sampling. +# The progress of training can be best measured by looking at the samples across iterations, +# and the score for random images that decreases constantly over time. + +# %% [markdown] +# ### Image Generation +# +# Another way of evaluating generative models is by sampling a few generated images. +# Generative models need to be good at generating realistic images as this truely shows that they have modeled the true data distribution. +# Thus, let's sample a few images of the model below: + +# %% +model.to(device) +L.seed_everything(43) +callback = GenerateCallback(batch_size=4, vis_steps=8, num_steps=256) +imgs_per_step = callback.generate_imgs(model) +imgs_per_step = imgs_per_step.cpu() + +# %% [markdown] +# The characteristic of sampling with energy-based models is that they require the iterative MCMC algorithm. +# To gain an insight in how the images change over iterations, we plot a few intermediate samples in the MCMC as well: + +# %% +for i in range(imgs_per_step.shape[1]): + step_size = callback.num_steps // callback.vis_steps + imgs_to_plot = imgs_per_step[step_size - 1 :: step_size, i] + imgs_to_plot = torch.cat([imgs_per_step[0:1, i], imgs_to_plot], dim=0) + grid = torchvision.utils.make_grid( + imgs_to_plot, nrow=imgs_to_plot.shape[0], normalize=True, range=(-1, 1), pad_value=0.5, padding=2 + ) + grid = grid.permute(1, 2, 0) + plt.figure(figsize=(8, 8)) + plt.imshow(grid) + plt.xlabel("Generation iteration") + plt.xticks( + [(imgs_per_step.shape[-1] + 2) * (0.5 + j) for j in range(callback.vis_steps + 1)], + labels=[1] + list(range(step_size, imgs_per_step.shape[0] + 1, step_size)), + ) + plt.yticks([]) + plt.show() + +# %% [markdown] +# We see that although starting from noise in the very first step, the sampling algorithm obtains reasonable shapes after only 32 steps. +# Over the next 200 steps, the shapes become clearer and changed towards realistic digits. +# The specific samples can differ when you run the code on Colab, hence the following description is specific to the plots shown on the website. +# The first row shows an 8, where we remove unnecessary white parts over iterations. +# The transformation across iterations can be seen at best for the second sample, which creates a digit of 2. +# While the first sample after 32 iterations looks a bit like a digit, but not really, +# the sample is transformed more and more to a typical image of the digit 2. + +# %% [markdown] +# ### Out-of-distribution detection +# +# A very common and strong application of energy-based models is out-of-distribution detection +# (sometimes referred to as "anomaly" detection). +# As more and more deep learning models are applied in production and applications, +# a crucial aspect of these models is to know what the models don't know. +# Deep learning models are usually overconfident, meaning that they classify even random images sometimes with 100% probability. +# Clearly, this is not something that we want to see in applications. +# Energy-based models can help with this problem because they are trained to detect images that do not fit the training dataset distribution. +# Thus, in those applications, you could train an energy-based model along with the classifier, +# and only output predictions if the energy-based models assign a (unnormalized) probability higher than $\delta$ to the image. +# You can actually combine classifiers and energy-based objectives in a single model, +# as proposed in this [paper](https://arxiv.org/abs/1912.03263). +# +# In this part of the analysis, we want to test the out-of-distribution capability of our energy-based model. +# Remember that a lower output of the model denotes a low probability. +# Thus, we hope to see low scores if we enter random noise to the model: + +# %% +with torch.no_grad(): + rand_imgs = torch.rand((128,) + model.hparams.img_shape).to(model.device) + rand_imgs = rand_imgs * 2 - 1.0 + rand_out = model.cnn(rand_imgs).mean() + print("Average score for random images: %4.2f" % (rand_out.item())) + +# %% [markdown] +# As we hoped, the model assigns very low probability to those noisy images. +# As another reference, let's look at predictions for a batch of images from the training set: + +# %% +with torch.no_grad(): + train_imgs, _ = next(iter(train_loader)) + train_imgs = train_imgs.to(model.device) + train_out = model.cnn(train_imgs).mean() + print("Average score for training images: %4.2f" % (train_out.item())) + +# %% [markdown] +# The scores are close to 0 because of the regularization objective that was added to the training. +# So clearly, the model can distinguish between noise and real digits. +# However, what happens if we change the training images a little, and see which ones gets a very low score? + + +# %% +@torch.no_grad() +def compare_images(img1, img2): + imgs = torch.stack([img1, img2], dim=0).to(model.device) + score1, score2 = model.cnn(imgs).cpu().chunk(2, dim=0) + grid = torchvision.utils.make_grid( + [img1.cpu(), img2.cpu()], nrow=2, normalize=True, range=(-1, 1), pad_value=0.5, padding=2 + ) + grid = grid.permute(1, 2, 0) + plt.figure(figsize=(4, 4)) + plt.imshow(grid) + plt.xticks([(img1.shape[2] + 2) * (0.5 + j) for j in range(2)], labels=["Original image", "Transformed image"]) + plt.yticks([]) + plt.show() + print("Score original image: %4.2f" % score1) + print("Score transformed image: %4.2f" % score2) + + +# %% [markdown] +# We use a random test image for this. Feel free to change it to experiment with the model yourself. + +# %% +test_imgs, _ = next(iter(test_loader)) +exmp_img = test_imgs[0].to(model.device) + +# %% [markdown] +# The first transformation is to add some random noise to the image: + +# %% +img_noisy = exmp_img + torch.randn_like(exmp_img) * 0.3 +img_noisy.clamp_(min=-1.0, max=1.0) +compare_images(exmp_img, img_noisy) + +# %% [markdown] +# We can see that the score considerably drops. +# Hence, the model can detect random Gaussian noise on the image. +# This is also to expect as initially, the "fake" samples are pure noise images. +# +# Next, we flip an image and check how this influences the score: + +# %% +img_flipped = exmp_img.flip(dims=(1, 2)) +compare_images(exmp_img, img_flipped) + +# %% [markdown] +# If the digit can only be read in this way, for example, the 7, then we can see that the score drops. +# However, the score only drops slightly. +# This is likely because of the small size of our model. +# Keep in mind that generative modeling is a much harder task than classification, +# as we do not only need to distinguish between classes but learn **all** details/characteristics of the digits. +# With a deeper model, this could eventually be captured better (but at the cost of greater training instability). +# +# Finally, we check what happens if we reduce the digit significantly in size: + +# %% +img_tiny = torch.zeros_like(exmp_img) - 1 +img_tiny[:, exmp_img.shape[1] // 2 :, exmp_img.shape[2] // 2 :] = exmp_img[:, ::2, ::2] +compare_images(exmp_img, img_tiny) + +# %% [markdown] +# The score again drops but not by a large margin, although digits in the MNIST dataset usually are much larger. +# +# Overall, we can conclude that our model is good for detecting Gaussian noise and smaller transformations to existing digits. +# Nonetheless, to obtain a very good out-of-distribution model, we would need to train deeper models and for more iterations. + +# %% [markdown] +# ### Instability +# +# Finally, we should discuss the possible instabilities of energy-based models, +# in particular for the example of image generation that we have implemented in this notebook. +# In the process of hyperparameter search for this notebook, there have been several models that diverged. +# Divergence in energy-based models means that the models assign a high probability to examples of the training set which is a good thing. +# However, at the same time, the sampling algorithm fails and only generates noise images that obtain minimal probability scores. +# This happens because the model has created many local maxima in which the generated noise images fall. +# The energy surface over which we calculate the gradients to reach data points with high probability has "diverged" and is not useful for our MCMC sampling. +# +# Besides finding the optimal hyperparameters, a common trick in energy-based models is to reload stable checkpoints. +# If we detect that the model is diverging, we stop the training, load the model from one epoch ago where it did not diverge yet. +# Afterward, we continue training and hope that with a different seed the model is not diverging again. +# Nevertheless, this should be considered as the "last hope" for stabilizing the models, +# and careful hyperparameter tuning is the better way to do so. +# Sensitive hyperparameters include `step_size`, `steps` and the noise standard deviation in the sampler, +# and the learning rate and feature dimensionality in the CNN model. + +# %% [markdown] +# ## Conclusion +# +# In this tutorial, we have discussed energy-based models for generative modeling. +# The concept relies on the idea that any strictly positive function can be turned into a probability +# distribution by normalizing over the whole dataset. +# As this is not reasonable to calculate for high dimensional data like images, +# we train the model using contrastive divergence and sampling via MCMC. +# While the idea allows us to turn any neural network into an energy-based model, +# we have seen that there are multiple training tricks needed to stabilize the training. +# Furthermore, the training time of these models is relatively long as, during every training iteration, +# we need to sample new "fake" images, even with a sampling buffer. +# In the next lectures and assignment, we will see different generative models (e.g. VAE, GAN, NF) +# that allow us to do generative modeling more stably, but with the cost of more parameters. diff --git a/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/contrastive_divergence.svg b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/contrastive_divergence.svg new file mode 100644 index 0000000..bc084ae --- /dev/null +++ b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/contrastive_divergence.svg @@ -0,0 +1,84 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/energy_models_application.png b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/energy_models_application.png new file mode 100644 index 0000000..71fee99 Binary files /dev/null and b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/energy_models_application.png differ diff --git a/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/sampling.svg b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/sampling.svg new file mode 100644 index 0000000..b6fbd2a --- /dev/null +++ b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/sampling.svg @@ -0,0 +1,2562 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/tensorboard_screenshot.png b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/tensorboard_screenshot.png new file mode 100644 index 0000000..e514684 Binary files /dev/null and b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/tensorboard_screenshot.png differ diff --git a/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/training_algorithm.svg b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/training_algorithm.svg new file mode 100644 index 0000000..b1c76d6 --- /dev/null +++ b/_notebooks/course_UvA-DL/07-deep-energy-based-generative-models/training_algorithm.svg @@ -0,0 +1,5567 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/08-deep-autoencoders/.meta.yml b/_notebooks/course_UvA-DL/08-deep-autoencoders/.meta.yml new file mode 100644 index 0000000..5c4df67 --- /dev/null +++ b/_notebooks/course_UvA-DL/08-deep-autoencoders/.meta.yml @@ -0,0 +1,28 @@ +title: "Tutorial 8: Deep Autoencoders" +author: Phillip Lippe +created: 2021-07-12 +updated: 2023-03-14 +license: CC BY-SA +build: 0 +tags: + - Image +description: | + In this tutorial, we will take a closer look at autoencoders (AE). + Autoencoders are trained on encoding input data such as images into a smaller feature vector, + and afterward, reconstruct it by a second neural network, called a decoder. + The feature vector is called the "bottleneck" of the network as we aim to compress the input data into a smaller amount of features. + This property is useful in many applications, in particular in compressing data or comparing images on a metric beyond pixel-level comparisons. + Besides learning about the autoencoder framework, we will also see the "deconvolution" + (or transposed convolution) operator in action for scaling up feature maps in height and width. + Such deconvolution networks are necessary wherever we start from a small feature vector + and need to output an image of full size (e.g. in VAE, GANs, or super-resolution applications). + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/08-deep-autoencoders.jpg b/_notebooks/course_UvA-DL/08-deep-autoencoders/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/08-deep-autoencoders.jpg rename to _notebooks/course_UvA-DL/08-deep-autoencoders/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/08-deep-autoencoders/Deep_Autoencoders.py b/_notebooks/course_UvA-DL/08-deep-autoencoders/Deep_Autoencoders.py new file mode 100644 index 0000000..da289b9 --- /dev/null +++ b/_notebooks/course_UvA-DL/08-deep-autoencoders/Deep_Autoencoders.py @@ -0,0 +1,713 @@ +# %% [markdown] +#
+ +# %% +import os +import urllib.request +from urllib.error import HTTPError + +import lightning as L +import matplotlib +import matplotlib.pyplot as plt +import matplotlib_inline.backend_inline +import seaborn as sns +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data as data +import torchvision +from lightning.pytorch.callbacks import Callback, LearningRateMonitor, ModelCheckpoint +from torch.utils.tensorboard import SummaryWriter +from torchvision import transforms +from torchvision.datasets import CIFAR10 +from tqdm.notebook import tqdm + +# %matplotlib inline +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +matplotlib.rcParams["lines.linewidth"] = 2.0 +sns.reset_orig() +sns.set() + +# Tensorboard extension (for visualization purposes later) +# %load_ext tensorboard + +# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/tutorial9") + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu") +print("Device:", device) + +# %% [markdown] +# We have 4 pretrained models that we have to download. +# Remember the adjust the variables `DATASET_PATH` and `CHECKPOINT_PATH` if needed. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial9/" +# Files to download +pretrained_files = ["cifar10_64.ckpt", "cifar10_128.ckpt", "cifar10_256.ckpt", "cifar10_384.ckpt"] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print("Downloading %s..." % file_url) + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the files manually," + " or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# In this tutorial, we work with the CIFAR10 dataset. +# In CIFAR10, each image has 3 color channels and is 32x32 pixels large. +# As autoencoders do not have the constrain of modeling images probabilistic, we can work on more complex image data +# (i.e. 3 color channels instead of black-and-white) much easier than for VAEs. +# In case you have downloaded CIFAR10 already in a different directory, make sure to set DATASET_PATH +# accordingly to prevent another download. +# +# In contrast to previous tutorials on CIFAR10 like +# [Tutorial 5](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial5/Inception_ResNet_DenseNet.html) +# (CNN classification), we do not normalize the data explicitly with a mean of 0 and std of 1, +# but roughly estimate it scaling the data between -1 and 1. +# This is because limiting the range will make our task of predicting/reconstructing images easier. + +# %% +# Transformations applied on each image => only make them a tensor +transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) + +# Loading the training dataset. We need to split it into a training and validation part +train_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=transform, download=True) +L.seed_everything(42) +train_set, val_set = torch.utils.data.random_split(train_dataset, [45000, 5000]) + +# Loading the test set +test_set = CIFAR10(root=DATASET_PATH, train=False, transform=transform, download=True) + +# We define a set of data loaders that we can use for various purposes later. +train_loader = data.DataLoader(train_set, batch_size=256, shuffle=True, drop_last=True, pin_memory=True, num_workers=4) +val_loader = data.DataLoader(val_set, batch_size=256, shuffle=False, drop_last=False, num_workers=4) +test_loader = data.DataLoader(test_set, batch_size=256, shuffle=False, drop_last=False, num_workers=4) + + +def get_train_images(num): + return torch.stack([train_dataset[i][0] for i in range(num)], dim=0) + + +# %% [markdown] +# ## Building the autoencoder +# +# In general, an autoencoder consists of an **encoder** that maps the input $x$ to a lower-dimensional feature vector $z$, +# and a **decoder** that reconstructs the input $\hat{x}$ from $z$. +# We train the model by comparing $x$ to $\hat{x}$ and optimizing the parameters to increase the similarity between $x$ and $\hat{x}$. +# See below for a small illustration of the autoencoder framework. + +# %% [markdown] +#
+ +# %% [markdown] +# We first start by implementing the encoder. +# The encoder effectively consists of a deep convolutional network, where we scale down the image layer-by-layer using strided convolutions. +# After downscaling the image three times, we flatten the features and apply linear layers. +# The latent representation $z$ is therefore a vector of size *d* which can be flexibly selected. + + +# %% +class Encoder(nn.Module): + def __init__(self, num_input_channels: int, base_channel_size: int, latent_dim: int, act_fn: object = nn.GELU): + """ + Args: + num_input_channels : Number of input channels of the image. For CIFAR, this parameter is 3 + base_channel_size : Number of channels we use in the first convolutional layers. Deeper layers might use a duplicate of it. + latent_dim : Dimensionality of latent representation z + act_fn : Activation function used throughout the encoder network + """ + super().__init__() + c_hid = base_channel_size + self.net = nn.Sequential( + nn.Conv2d(num_input_channels, c_hid, kernel_size=3, padding=1, stride=2), # 32x32 => 16x16 + act_fn(), + nn.Conv2d(c_hid, c_hid, kernel_size=3, padding=1), + act_fn(), + nn.Conv2d(c_hid, 2 * c_hid, kernel_size=3, padding=1, stride=2), # 16x16 => 8x8 + act_fn(), + nn.Conv2d(2 * c_hid, 2 * c_hid, kernel_size=3, padding=1), + act_fn(), + nn.Conv2d(2 * c_hid, 2 * c_hid, kernel_size=3, padding=1, stride=2), # 8x8 => 4x4 + act_fn(), + nn.Flatten(), # Image grid to single feature vector + nn.Linear(2 * 16 * c_hid, latent_dim), + ) + + def forward(self, x): + return self.net(x) + + +# %% [markdown] +# Note that we do not apply Batch Normalization here. +# This is because we want the encoding of each image to be independent of all the other images. +# Otherwise, we might introduce correlations into the encoding or decoding that we do not want to have. +# In some implementations, you still can see Batch Normalization being used, because it can also serve as a form of regularization. +# Nevertheless, the better practice is to go with other normalization techniques if necessary like Instance Normalization or Layer Normalization. +# Given the small size of the model, we can neglect normalization for now. + +# %% [markdown] +# The decoder is a mirrored, flipped version of the encoder. +# The only difference is that we replace strided convolutions by transposed convolutions +# (i.e. deconvolutions) to upscale the features. +# Transposed convolutions can be imagined as adding the stride to the input instead of the output, +# and can thus upscale the input. +# For an illustration of a `nn.ConvTranspose2d` layer with kernel size 3, stride 2, and padding 1, +# see below (figure credit - [Vincent Dumoulin and Francesco Visin](https://arxiv.org/abs/1603.07285)): +# +#
+# +# You see that for an input of size $3\times3$, we obtain an output of $5\times5$. +# However, to truly have a reverse operation of the convolution, +# we need to ensure that the layer scales the input shape by a factor of 2 (e.g. $4\times4\to8\times8$). +# For this, we can specify the parameter `output_padding` which adds additional values to the output shape. +# Note that we do not perform zero-padding with this, but rather increase the output shape for calculation. +# +# Overall, the decoder can be implemented as follows: + + +# %% +class Decoder(nn.Module): + def __init__(self, num_input_channels: int, base_channel_size: int, latent_dim: int, act_fn: object = nn.GELU): + """ + Args: + num_input_channels : Number of channels of the image to reconstruct. For CIFAR, this parameter is 3 + base_channel_size : Number of channels we use in the last convolutional layers. Early layers might use a duplicate of it. + latent_dim : Dimensionality of latent representation z + act_fn : Activation function used throughout the decoder network + """ + super().__init__() + c_hid = base_channel_size + self.linear = nn.Sequential(nn.Linear(latent_dim, 2 * 16 * c_hid), act_fn()) + self.net = nn.Sequential( + nn.ConvTranspose2d( + 2 * c_hid, 2 * c_hid, kernel_size=3, output_padding=1, padding=1, stride=2 + ), # 4x4 => 8x8 + act_fn(), + nn.Conv2d(2 * c_hid, 2 * c_hid, kernel_size=3, padding=1), + act_fn(), + nn.ConvTranspose2d(2 * c_hid, c_hid, kernel_size=3, output_padding=1, padding=1, stride=2), # 8x8 => 16x16 + act_fn(), + nn.Conv2d(c_hid, c_hid, kernel_size=3, padding=1), + act_fn(), + nn.ConvTranspose2d( + c_hid, num_input_channels, kernel_size=3, output_padding=1, padding=1, stride=2 + ), # 16x16 => 32x32 + nn.Tanh(), # The input images is scaled between -1 and 1, hence the output has to be bounded as well + ) + + def forward(self, x): + x = self.linear(x) + x = x.reshape(x.shape[0], -1, 4, 4) + x = self.net(x) + return x + + +# %% [markdown] +# The encoder and decoder networks we chose here are relatively simple. +# Usually, more complex networks are applied, especially when using a ResNet-based architecture. +# For example, see [VQ-VAE](https://arxiv.org/abs/1711.00937) and +# [NVAE](https://arxiv.org/abs/2007.03898) (although the papers discuss architectures for VAEs, +# they can equally be applied to standard autoencoders). +# +# In a final step, we add the encoder and decoder together into the autoencoder architecture. +# We define the autoencoder as PyTorch Lightning Module to simplify the needed training code: + + +# %% +class Autoencoder(L.LightningModule): + def __init__( + self, + base_channel_size: int, + latent_dim: int, + encoder_class: object = Encoder, + decoder_class: object = Decoder, + num_input_channels: int = 3, + width: int = 32, + height: int = 32, + ): + super().__init__() + # Saving hyperparameters of autoencoder + self.save_hyperparameters() + # Creating encoder and decoder + self.encoder = encoder_class(num_input_channels, base_channel_size, latent_dim) + self.decoder = decoder_class(num_input_channels, base_channel_size, latent_dim) + # Example input array needed for visualizing the graph of the network + self.example_input_array = torch.zeros(2, num_input_channels, width, height) + + def forward(self, x): + """The forward function takes in an image and returns the reconstructed image.""" + z = self.encoder(x) + x_hat = self.decoder(z) + return x_hat + + def _get_reconstruction_loss(self, batch): + """Given a batch of images, this function returns the reconstruction loss (MSE in our case)""" + x, _ = batch # We do not need the labels + x_hat = self.forward(x) + loss = F.mse_loss(x, x_hat, reduction="none") + loss = loss.sum(dim=[1, 2, 3]).mean(dim=[0]) + return loss + + def configure_optimizers(self): + optimizer = optim.Adam(self.parameters(), lr=1e-3) + # Using a scheduler is optional but can be helpful. + # The scheduler reduces the LR if the validation performance hasn't improved for the last N epochs + scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode="min", factor=0.2, patience=20, min_lr=5e-5) + return {"optimizer": optimizer, "lr_scheduler": scheduler, "monitor": "val_loss"} + + def training_step(self, batch, batch_idx): + loss = self._get_reconstruction_loss(batch) + self.log("train_loss", loss) + return loss + + def validation_step(self, batch, batch_idx): + loss = self._get_reconstruction_loss(batch) + self.log("val_loss", loss) + + def test_step(self, batch, batch_idx): + loss = self._get_reconstruction_loss(batch) + self.log("test_loss", loss) + + +# %% [markdown] +# For the loss function, we use the mean squared error (MSE). +# The mean squared error pushes the network to pay special attention to those pixel values its estimate is far away. +# Predicting 127 instead of 128 is not important when reconstructing, but confusing 0 with 128 is much worse. +# Note that in contrast to VAEs, we do not predict the probability per pixel value, but instead use a distance measure. +# This saves a lot of parameters and simplifies training. +# To get a better intuition per pixel, we report the summed squared error averaged over the batch dimension +# (any other mean/sum leads to the same result/parameters). +# +# However, MSE has also some considerable disadvantages. +# Usually, MSE leads to blurry images where small noise/high-frequent patterns are removed as those cause a very low error. +# To ensure realistic images to be reconstructed, one could combine Generative Adversarial Networks +# (lecture 10) with autoencoders as done in several works (e.g. see [here](https://arxiv.org/abs/1704.02304), +# [here](https://arxiv.org/abs/1511.05644) or these [slides](http://elarosca.net/slides/iccv_autoencoder_gans.pdf)). +# Additionally, comparing two images using MSE does not necessarily reflect their visual similarity. +# For instance, suppose the autoencoder reconstructs an image shifted by one pixel to the right and bottom. +# Although the images are almost identical, we can get a higher loss than predicting a constant pixel value for half of the image (see code below). +# An example solution for this issue includes using a separate, pre-trained CNN, +# and use a distance of visual features in lower layers as a distance measure instead of the original pixel-level comparison. + + +# %% +def compare_imgs(img1, img2, title_prefix=""): + # Calculate MSE loss between both images + loss = F.mse_loss(img1, img2, reduction="sum") + # Plot images for visual comparison + grid = torchvision.utils.make_grid(torch.stack([img1, img2], dim=0), nrow=2, normalize=True, range=(-1, 1)) + grid = grid.permute(1, 2, 0) + plt.figure(figsize=(4, 2)) + plt.title(f"{title_prefix} Loss: {loss.item():4.2f}") + plt.imshow(grid) + plt.axis("off") + plt.show() + + +for i in range(2): + # Load example image + img, _ = train_dataset[i] + img_mean = img.mean(dim=[1, 2], keepdims=True) + + # Shift image by one pixel + SHIFT = 1 + img_shifted = torch.roll(img, shifts=SHIFT, dims=1) + img_shifted = torch.roll(img_shifted, shifts=SHIFT, dims=2) + img_shifted[:, :1, :] = img_mean + img_shifted[:, :, :1] = img_mean + compare_imgs(img, img_shifted, "Shifted -") + + # Set half of the image to zero + img_masked = img.clone() + img_masked[:, : img_masked.shape[1] // 2, :] = img_mean + compare_imgs(img, img_masked, "Masked -") + +# %% [markdown] +# ### Training the model +# +# During the training, we want to keep track of the learning progress by seeing reconstructions made by our model. +# For this, we implement a callback object in PyTorch Lightning which will add reconstructions every $N$ epochs to our tensorboard: + + +# %% +class GenerateCallback(Callback): + def __init__(self, input_imgs, every_n_epochs=1): + super().__init__() + self.input_imgs = input_imgs # Images to reconstruct during training + # Only save those images every N epochs (otherwise tensorboard gets quite large) + self.every_n_epochs = every_n_epochs + + def on_train_epoch_end(self, trainer, pl_module): + if trainer.current_epoch % self.every_n_epochs == 0: + # Reconstruct images + input_imgs = self.input_imgs.to(pl_module.device) + with torch.no_grad(): + pl_module.eval() + reconst_imgs = pl_module(input_imgs) + pl_module.train() + # Plot and add to tensorboard + imgs = torch.stack([input_imgs, reconst_imgs], dim=1).flatten(0, 1) + grid = torchvision.utils.make_grid(imgs, nrow=2, normalize=True, range=(-1, 1)) + trainer.logger.experiment.add_image("Reconstructions", grid, global_step=trainer.global_step) + + +# %% [markdown] +# We will now write a training function that allows us to train the autoencoder with different latent dimensionality +# and returns both the test and validation score. +# We provide pre-trained models and recommend you using those, especially when you work on a computer without GPU. +# Of course, feel free to train your own models on Lisa. + + +# %% +def train_cifar(latent_dim): + # Create a PyTorch Lightning trainer with the generation callback + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, "cifar10_%i" % latent_dim), + accelerator="auto", + devices=1, + max_epochs=500, + callbacks=[ + ModelCheckpoint(save_weights_only=True), + GenerateCallback(get_train_images(8), every_n_epochs=10), + LearningRateMonitor("epoch"), + ], + ) + trainer.logger._log_graph = True # If True, we plot the computation graph in tensorboard + trainer.logger._default_hp_metric = None # Optional logging argument that we don't need + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "cifar10_%i.ckpt" % latent_dim) + if os.path.isfile(pretrained_filename): + print("Found pretrained model, loading...") + model = Autoencoder.load_from_checkpoint(pretrained_filename) + else: + model = Autoencoder(base_channel_size=32, latent_dim=latent_dim) + trainer.fit(model, train_loader, val_loader) + # Test best model on validation and test set + val_result = trainer.test(model, dataloaders=val_loader, verbose=False) + test_result = trainer.test(model, dataloaders=test_loader, verbose=False) + result = {"test": test_result, "val": val_result} + return model, result + + +# %% [markdown] +# ### Comparing latent dimensionality +# +#
+# +# When training an autoencoder, we need to choose a dimensionality for the latent representation $z$. +# The higher the latent dimensionality, the better we expect the reconstruction to be. +# However, the idea of autoencoders is to *compress* data. +# Hence, we are also interested in keeping the dimensionality low. +# To find the best tradeoff, we can train multiple models with different latent dimensionalities. +# The original input has $32\times 32\times 3 = 3072$ pixels. +# Keeping this in mind, a reasonable choice for the latent dimensionality might be between 64 and 384: + +# %% +model_dict = {} +for latent_dim in [64, 128, 256, 384]: + model_ld, result_ld = train_cifar(latent_dim) + model_dict[latent_dim] = {"model": model_ld, "result": result_ld} + +# %% [markdown] +# After training the models, we can plot the reconstruction loss over the latent dimensionality to get an intuition +# how these two properties are correlated: + +# %% +latent_dims = sorted(k for k in model_dict) +val_scores = [model_dict[k]["result"]["val"][0]["test_loss"] for k in latent_dims] + +fig = plt.figure(figsize=(6, 4)) +plt.plot( + latent_dims, val_scores, "--", color="#000", marker="*", markeredgecolor="#000", markerfacecolor="y", markersize=16 +) +plt.xscale("log") +plt.xticks(latent_dims, labels=latent_dims) +plt.title("Reconstruction error over latent dimensionality", fontsize=14) +plt.xlabel("Latent dimensionality") +plt.ylabel("Reconstruction error") +plt.minorticks_off() +plt.ylim(0, 100) +plt.show() + +# %% [markdown] +# As we initially expected, the reconstruction loss goes down with increasing latent dimensionality. +# For our model and setup, the two properties seem to be exponentially (or double exponentially) correlated. +# To understand what these differences in reconstruction error mean, we can visualize example reconstructions of the four models: + + +# %% +def visualize_reconstructions(model, input_imgs): + # Reconstruct images + model.eval() + with torch.no_grad(): + reconst_imgs = model(input_imgs.to(model.device)) + reconst_imgs = reconst_imgs.cpu() + + # Plotting + imgs = torch.stack([input_imgs, reconst_imgs], dim=1).flatten(0, 1) + grid = torchvision.utils.make_grid(imgs, nrow=4, normalize=True, range=(-1, 1)) + grid = grid.permute(1, 2, 0) + plt.figure(figsize=(7, 4.5)) + plt.title("Reconstructed from %i latents" % (model.hparams.latent_dim)) + plt.imshow(grid) + plt.axis("off") + plt.show() + + +# %% +input_imgs = get_train_images(4) +for latent_dim in model_dict: + visualize_reconstructions(model_dict[latent_dim]["model"], input_imgs) + +# %% [markdown] +# Clearly, the smallest latent dimensionality can only save information about the rough shape and color of the object, +# but the reconstructed image is extremely blurry and it is hard to recognize the original object in the reconstruction. +# With 128 features, we can recognize some shapes again although the picture remains blurry. +# The models with the highest two dimensionalities reconstruct the images quite well. +# The difference between 256 and 384 is marginal at first sight but can be noticed when comparing, for instance, +# the backgrounds of the first image (the 384 features model more of the pattern than 256). + +# %% [markdown] +# ### Out-of-distribution images +# +# Before continuing with the applications of autoencoder, we can actually explore some limitations of our autoencoder. +# For example, what happens if we try to reconstruct an image that is clearly out of the distribution of our dataset? +# We expect the decoder to have learned some common patterns in the dataset, +# and thus might in particular fail to reconstruct images that do not follow these patterns. +# +# The first experiment we can try is to reconstruct noise. +# We, therefore, create two images whose pixels are randomly sampled from a uniform distribution over pixel values, +# and visualize the reconstruction of the model (feel free to test different latent dimensionalities): + +# %% +rand_imgs = torch.rand(2, 3, 32, 32) * 2 - 1 +visualize_reconstructions(model_dict[256]["model"], rand_imgs) + +# %% [markdown] +# The reconstruction of the noise is quite poor, and seems to introduce some rough patterns. +# As the input does not follow the patterns of the CIFAR dataset, the model has issues reconstructing it accurately. +# +# We can also check how well the model can reconstruct other manually-coded patterns: + +# %% +plain_imgs = torch.zeros(4, 3, 32, 32) + +# Single color channel +plain_imgs[1, 0] = 1 +# Checkboard pattern +plain_imgs[2, :, :16, :16] = 1 +plain_imgs[2, :, 16:, 16:] = -1 +# Color progression +xx, yy = torch.meshgrid(torch.linspace(-1, 1, 32), torch.linspace(-1, 1, 32)) +plain_imgs[3, 0, :, :] = xx +plain_imgs[3, 1, :, :] = yy + +visualize_reconstructions(model_dict[256]["model"], plain_imgs) + +# %% [markdown] +# The plain, constant images are reconstructed relatively good although the single color channel contains some noticeable noise. +# The hard borders of the checkboard pattern are not as sharp as intended, as well as the color progression, +# both because such patterns never occur in the real-world pictures of CIFAR. +# +# In general, autoencoders tend to fail reconstructing high-frequent noise (i.e. sudden, big changes across few pixels) +# due to the choice of MSE as loss function (see our previous discussion about loss functions in autoencoders). +# Small misalignments in the decoder can lead to huge losses so that the model settles for the expected value/mean in these regions. +# For low-frequent noise, a misalignment of a few pixels does not result in a big difference to the original image. +# However, the larger the latent dimensionality becomes, the more of this high-frequent noise can be accurately reconstructed. + +# %% [markdown] +# ### Generating new images +# +# Variational autoencoders are a generative version of the autoencoders because we regularize the latent space to follow a Gaussian distribution. +# However, in vanilla autoencoders, we do not have any restrictions on the latent vector. +# So what happens if we would actually input a randomly sampled latent vector into the decoder? +# Let's find it out below: + +# %% +model = model_dict[256]["model"] +latent_vectors = torch.randn(8, model.hparams.latent_dim, device=model.device) +with torch.no_grad(): + imgs = model.decoder(latent_vectors) + imgs = imgs.cpu() + +grid = torchvision.utils.make_grid(imgs, nrow=4, normalize=True, range=(-1, 1), pad_value=0.5) +grid = grid.permute(1, 2, 0) +plt.figure(figsize=(8, 5)) +plt.imshow(grid) +plt.axis("off") +plt.show() + +# %% [markdown] +# As we can see, the generated images more look like art than realistic images. +# As the autoencoder was allowed to structure the latent space in whichever way it suits the reconstruction best, +# there is no incentive to map every possible latent vector to realistic images. +# Furthermore, the distribution in latent space is unknown to us and doesn't necessarily follow a multivariate normal distribution. +# Thus, we can conclude that vanilla autoencoders are indeed not generative. + +# %% [markdown] +# ## Finding visually similar images +# +# One application of autoencoders is to build an image-based search engine to retrieve visually similar images. +# This can be done by representing all images as their latent dimensionality, and find the closest $K$ images in this domain. +# The first step to such a search engine is to encode all images into $z$. +# In the following, we will use the training set as a search corpus, and the test set as queries to the system. +# +# (Warning: the following cells can be computationally heavy for a weak CPU-only system. +# If you do not have a strong computer and are not on Google Colab, +# you might want to skip the execution of the following cells and rely on the results shown in the filled notebook) + +# %% +# We use the following model throughout this section. +# If you want to try a different latent dimensionality, change it here! +model = model_dict[128]["model"] + + +# %% +def embed_imgs(model, data_loader): + # Encode all images in the data_laoder using model, and return both images and encodings + img_list, embed_list = [], [] + model.eval() + for imgs, _ in tqdm(data_loader, desc="Encoding images", leave=False): + with torch.no_grad(): + z = model.encoder(imgs.to(model.device)) + img_list.append(imgs) + embed_list.append(z) + return (torch.cat(img_list, dim=0), torch.cat(embed_list, dim=0)) + + +train_img_embeds = embed_imgs(model, train_loader) +test_img_embeds = embed_imgs(model, test_loader) + +# %% [markdown] +# After encoding all images, we just need to write a function that finds the closest $K$ images and returns (or plots) those: + + +# %% +def find_similar_images(query_img, query_z, key_embeds, K=8): + # Find closest K images. We use the euclidean distance here but other like cosine distance can also be used. + dist = torch.cdist(query_z[None, :], key_embeds[1], p=2) + dist = dist.squeeze(dim=0) + dist, indices = torch.sort(dist) + # Plot K closest images + imgs_to_display = torch.cat([query_img[None], key_embeds[0][indices[:K]]], dim=0) + grid = torchvision.utils.make_grid(imgs_to_display, nrow=K + 1, normalize=True, range=(-1, 1)) + grid = grid.permute(1, 2, 0) + plt.figure(figsize=(12, 3)) + plt.imshow(grid) + plt.axis("off") + plt.show() + + +# %% +# Plot the closest images for the first N test images as example +for i in range(8): + find_similar_images(test_img_embeds[0][i], test_img_embeds[1][i], key_embeds=train_img_embeds) + +# %% [markdown] +# Based on our autoencoder, we see that we are able to retrieve many similar images to the test input. +# In particular, in row 4, we can spot that some test images might not be that different +# from the training set as we thought (same poster, just different scaling/color scaling). +# We also see that although we haven't given the model any labels, +# it can cluster different classes in different parts of the latent space (airplane + ship, animals, etc.). +# This is why autoencoders can also be used as a pre-training strategy for deep networks, +# especially when we have a large set of unlabeled images (often the case). +# However, it should be noted that the background still plays a big role in autoencoders while it doesn't for classification. +# Hence, we don't get "perfect" clusters and need to finetune such models for classification. + +# %% [markdown] +# ### Tensorboard clustering +# +# Another way of exploring the similarity of images in the latent space is by dimensionality-reduction methods like PCA or T-SNE. +# Luckily, Tensorboard provides a nice interface for this and we can make use of it in the following: + +# %% +# We use the following model throughout this section. +# If you want to try a different latent dimensionality, change it here! +model = model_dict[128]["model"] + +# %% +# Create a summary writer +writer = SummaryWriter("tensorboard/") + +# %% [markdown] +# The function `add_embedding` allows us to add high-dimensional feature vectors to TensorBoard on which we can perform clustering. +# What we have to provide in the function are the feature vectors, additional metadata such as the labels, +# and the original images so that we can identify a specific image in the clustering. + +# %% +# In case you obtain the following error in the next cell, execute the import statements and last line in this cell +# AttributeError: module 'tensorflow._api.v2.io.gfile' has no attribute 'get_filesystem' + +# import tensorflow as tf +# import tensorboard as tb +# tf.io.gfile = tb.compat.tensorflow_stub.io.gfile + +# %% +# Note: the embedding projector in tensorboard is computationally heavy. +# Reduce the image amount below if your computer struggles with visualizing all 10k points +NUM_IMGS = len(test_set) + +writer.add_embedding( + test_img_embeds[1][:NUM_IMGS], # Encodings per image + metadata=[test_set[i][1] for i in range(NUM_IMGS)], # Adding the labels per image to the plot + label_img=(test_img_embeds[0][:NUM_IMGS] + 1) / 2.0, +) # Adding the original images to the plot + +# %% [markdown] +# Finally, we can run tensorboard to explore similarities among images: + +# %% +# Uncomment the next line to start the tensorboard +# %tensorboard --logdir tensorboard/ + +# %% [markdown] +# You should be able to see something similar as in the following image. +# In case the projector stays empty, try to start the TensorBoard outside of the Jupyter notebook. +# +#
+# +# Overall, we can see that the model indeed clustered images together that are visually similar. +# Especially the background color seems to be a crucial factor in the encoding. +# This correlates to the chosen loss function, here Mean Squared Error on pixel-level +# because the background is responsible for more than half of the pixels in an average image. +# Hence, the model learns to focus on it. +# Nevertheless, we can see that the encodings also separate a couple of classes in the latent space although it hasn't seen any labels. +# This shows again that autoencoding can also be used as a "pre-training"/transfer learning task before classification. + +# %% +# Closing the summary writer +writer.close() + +# %% [markdown] +# ## Conclusion +# +# In this tutorial, we have implemented our own autoencoder on small RGB images and explored various properties of the model. +# In contrast to variational autoencoders, vanilla AEs are not generative and can work on MSE loss functions. +# This makes them often easier to train. +# Both versions of AE can be used for dimensionality reduction, as we have seen for finding visually similar images beyond pixel distances. +# Despite autoencoders gaining less interest in the research community due to their more "theoretically" +# challenging counterpart of VAEs, autoencoders still find usage in a lot of applications like denoising and compression. +# Hence, AEs are an essential tool that every Deep Learning engineer/researcher should be familiar with. diff --git a/_notebooks/course_UvA-DL/08-deep-autoencoders/autoencoder_visualization.svg b/_notebooks/course_UvA-DL/08-deep-autoencoders/autoencoder_visualization.svg new file mode 100644 index 0000000..f6c0411 --- /dev/null +++ b/_notebooks/course_UvA-DL/08-deep-autoencoders/autoencoder_visualization.svg @@ -0,0 +1,3 @@ + + +
Encoder
Encoder
Decoder
Decoder
Input 𝑥
Input 𝑥
𝑧
𝑧
Reconstruction 𝑥
Reconstruction 𝑥
ˆ
ˆ
diff --git a/_notebooks/course_UvA-DL/08-deep-autoencoders/deconvolution.gif b/_notebooks/course_UvA-DL/08-deep-autoencoders/deconvolution.gif new file mode 100644 index 0000000..894c1d6 Binary files /dev/null and b/_notebooks/course_UvA-DL/08-deep-autoencoders/deconvolution.gif differ diff --git a/_notebooks/course_UvA-DL/08-deep-autoencoders/tensorboard_projector_screenshot.jpeg b/_notebooks/course_UvA-DL/08-deep-autoencoders/tensorboard_projector_screenshot.jpeg new file mode 100644 index 0000000..c638110 Binary files /dev/null and b/_notebooks/course_UvA-DL/08-deep-autoencoders/tensorboard_projector_screenshot.jpeg differ diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/.meta.yml b/_notebooks/course_UvA-DL/09-normalizing-flows/.meta.yml new file mode 100644 index 0000000..d366a99 --- /dev/null +++ b/_notebooks/course_UvA-DL/09-normalizing-flows/.meta.yml @@ -0,0 +1,31 @@ +title: "Tutorial 9: Normalizing Flows for Image Modeling" +author: Phillip Lippe +created: 2021-06-07 +updated: 2023-03-14 +license: CC BY-SA +build: 0 +tags: + - Image +description: | + In this tutorial, we will take a closer look at complex, deep normalizing flows. + The most popular, current application of deep normalizing flows is to model datasets of images. + As for other generative models, images are a good domain to start working on because + (1) CNNs are widely studied and strong models exist, + (2) images are high-dimensional and complex, + and (3) images are discrete integers. + In this tutorial, we will review current advances in normalizing flows for image modeling, + and get hands-on experience on coding normalizing flows. + Note that normalizing flows are commonly parameter heavy and therefore computationally expensive. + We will use relatively simple and shallow flows to save computational cost and allow you to run the notebook on CPU, + but keep in mind that a simple way to improve the scores of the flows we study here is to make them deeper. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - tabulate + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/09-normalizing-flows.jpg b/_notebooks/course_UvA-DL/09-normalizing-flows/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/09-normalizing-flows.jpg rename to _notebooks/course_UvA-DL/09-normalizing-flows/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/NF_image_modeling.py b/_notebooks/course_UvA-DL/09-normalizing-flows/NF_image_modeling.py new file mode 100644 index 0000000..28821f6 --- /dev/null +++ b/_notebooks/course_UvA-DL/09-normalizing-flows/NF_image_modeling.py @@ -0,0 +1,1417 @@ +# %% [markdown] +#
+# Throughout this notebook, we make use of [PyTorch Lightning](https://lightning.ai/docs/pytorch/stable/). +# The first cell imports our usual libraries. + +# %% +import math +import os +import time +import urllib.request +from urllib.error import HTTPError + +import lightning as L +import matplotlib +import matplotlib.pyplot as plt +import matplotlib_inline.backend_inline +import numpy as np +import seaborn as sns +import tabulate +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data as data +import torchvision +from IPython.display import HTML, display +from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint +from matplotlib.colors import to_rgb +from torch import Tensor +from torchvision import transforms +from torchvision.datasets import MNIST +from tqdm.notebook import tqdm + +# %matplotlib inline +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +matplotlib.rcParams["lines.linewidth"] = 2.0 +sns.reset_orig() + +# Path to the folder where the datasets are/should be downloaded (e.g. MNIST) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/tutorial11") + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +# Fetching the device that will be used throughout this notebook +device = torch.device("cpu") if not torch.cuda.is_available() else torch.device("cuda:0") +print("Using device", device) + +# %% [markdown] +# Again, we have a few pretrained models. We download them below to the specified path above. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial11/" +# Files to download +pretrained_files = ["MNISTFlow_simple.ckpt", "MNISTFlow_vardeq.ckpt", "MNISTFlow_multiscale.ckpt"] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print("Downloading %s..." % file_url) + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# We will use the MNIST dataset in this notebook. +# MNIST constitutes, despite its simplicity, a challenge for small generative models as it requires the global understanding of an image. +# At the same time, we can easily judge whether generated images come from the same distribution as the dataset +# (i.e. represent real digits), or not. +# +# To deal better with the discrete nature of the images, we transform them +# from a range of 0-1 to a range of 0-255 as integers. + + +# %% +# Convert images from 0-1 to 0-255 (integers) +def discretize(sample): + return (sample * 255).to(torch.int32) + + +# Transformations applied on each image => make them a tensor and discretize +transform = transforms.Compose([transforms.ToTensor(), discretize]) + +# Loading the training dataset. We need to split it into a training and validation part +train_dataset = MNIST(root=DATASET_PATH, train=True, transform=transform, download=True) +L.seed_everything(42) +train_set, val_set = torch.utils.data.random_split(train_dataset, [50000, 10000]) + +# Loading the test set +test_set = MNIST(root=DATASET_PATH, train=False, transform=transform, download=True) + +# We define a set of data loaders that we can use for various purposes later. +# Note that for actually training a model, we will use different data loaders +# with a lower batch size. +train_loader = data.DataLoader(train_set, batch_size=256, shuffle=False, drop_last=False) +val_loader = data.DataLoader(val_set, batch_size=64, shuffle=False, drop_last=False, num_workers=4) +test_loader = data.DataLoader(test_set, batch_size=64, shuffle=False, drop_last=False, num_workers=4) + +# %% [markdown] +# In addition, we will define below a function to simplify the visualization of images/samples. +# Some training examples of the MNIST dataset is shown below. + + +# %% +def show_imgs(imgs, title=None, row_size=4): + # Form a grid of pictures (we use max. 8 columns) + num_imgs = imgs.shape[0] if isinstance(imgs, Tensor) else len(imgs) + is_int = imgs.dtype == torch.int32 if isinstance(imgs, Tensor) else imgs[0].dtype == torch.int32 + nrow = min(num_imgs, row_size) + ncol = int(math.ceil(num_imgs / nrow)) + imgs = torchvision.utils.make_grid(imgs, nrow=nrow, pad_value=128 if is_int else 0.5) + np_imgs = imgs.cpu().numpy() + # Plot the grid + plt.figure(figsize=(1.5 * nrow, 1.5 * ncol)) + plt.imshow(np.transpose(np_imgs, (1, 2, 0)), interpolation="nearest") + plt.axis("off") + if title is not None: + plt.title(title) + plt.show() + plt.close() + + +show_imgs([train_set[i][0] for i in range(8)]) + +# %% [markdown] +# ## Normalizing Flows as generative model +# +# In the previous lectures, we have seen Energy-based models, Variational Autoencoders (VAEs) +# and Generative Adversarial Networks (GANs) as example of generative models. +# However, none of them explicitly learn the probability density function $p(x)$ of the real input data. +# While VAEs model a lower bound, energy-based models only implicitly learn the probability density. +# GANs on the other hand provide us a sampling mechanism for generating new data, without offering a likelihood estimate. +# The generative model we will look at here, called Normalizing Flows, actually models the true data distribution +# $p(x)$ and provides us with an exact likelihood estimate. +# Below, we can visually compare VAEs, GANs and Flows +# (figure credit - [Lilian Weng](https://lilianweng.github.io/lil-log/2018/10/13/flow-based-deep-generative-models.html)): +# +#
+# +# The major difference compared to VAEs is that flows use *invertible* functions $f$ +# to map the input data $x$ to a latent representation $z$. +# To realize this, $z$ must be of the same shape as $x$. +# This is in contrast to VAEs where $z$ is usually much lower dimensional than the original input data. +# However, an invertible mapping also means that for every data point $x$, we have a corresponding latent representation +# $z$ which allows us to perform lossless reconstruction ($z$ to $x$). +# In the visualization above, this means that $x=x'$ for flows, no matter what invertible function $f$ and input $x$ we choose. +# +# Nonetheless, how are normalizing flows modeling a probability density with an invertible function? +# The answer to this question is the rule for change of variables. +# Specifically, given a prior density $p_z(z)$ (e.g. Gaussian) and an invertible function $f$, +# we can determine $p_x(x)$ as follows: +# +# $$ +# \begin{split} +# \int p_x(x) dx & = \int p_z(z) dz = 1 \hspace{1cm}\text{(by definition of a probability distribution)}\\ +# \Leftrightarrow p_x(x) & = p_z(z) \left|\frac{dz}{dx}\right| = p_z(f(x)) \left|\frac{df(x)}{dx}\right| +# \end{split} +# $$ +# +# Hence, in order to determine the probability of $x$, we only need to determine its probability in latent space, +# and get the derivate of $f$. +# Note that this is for a univariate distribution, and $f$ is required to be invertible and smooth. +# For a multivariate case, the derivative becomes a Jacobian of which we need to take the determinant. +# As we usually use the log-likelihood as objective, we write the multivariate term with logarithms below: +# +# $$ +# \log p_x(\mathbf{x}) = \log p_z(f(\mathbf{x})) + \log{} \left|\det \frac{df(\mathbf{x})}{d\mathbf{x}}\right| +# $$ +# +# Although we now know how a normalizing flow obtains its likelihood, it might not be clear what a normalizing flow does intuitively. +# For this, we should look from the inverse perspective of the flow starting with the prior probability density $p_z(z)$. +# If we apply an invertible function on it, we effectively "transform" its probability density. +# For instance, if $f^{-1}(z)=z+1$, we shift the density by one while still remaining a valid probability distribution, +# and being invertible. +# We can also apply more complex transformations, like scaling: $f^{-1}(z)=2z+1$, but there you might see a difference. +# When you scale, you also change the volume of the probability density, as for example on uniform distributions +# (figure credit - [Eric Jang](https://blog.evjang.com/2018/01/nf1.html)): +# +#
+# +# You can see that the height of $p(y)$ should be lower than $p(x)$ after scaling. +# This change in volume represents $\left|\frac{df(x)}{dx}\right|$ in our equation above, +# and ensures that even after scaling, we still have a valid probability distribution. +# We can go on with making our function $f$ more complex. +# However, the more complex $f$ becomes, the harder it will be to find the inverse $f^{-1}$ of it, +# and to calculate the log-determinant of the Jacobian $\log{} \left|\det \frac{df(\mathbf{x})}{d\mathbf{x}}\right|$. +# An easier trick to stack multiple invertible functions $f_{1,...,K}$ after each other, as all together, +# they still represent a single, invertible function. +# Using multiple, learnable invertible functions, a normalizing flow attempts to transform +# $p_z(z)$ slowly into a more complex distribution which should finally be $p_x(x)$. +# We visualize the idea below +# (figure credit - [Lilian Weng](https://lilianweng.github.io/lil-log/2018/10/13/flow-based-deep-generative-models.html)): +# +#
+# +# Starting from $z_0$, which follows the prior Gaussian distribution, we sequentially apply the invertible +# functions $f_1,f_2,...,f_K$, until $z_K$ represents $x$. +# Note that in the figure above, the functions $f$ represent the inverted function from $f$ we had above +# (here: $f:Z\to X$, above: $f:X\to Z$). +# This is just a different notation and has no impact on the actual flow design because all $f$ need to be invertible anyways. +# When we estimate the log likelihood of a data point $x$ as in the equations above, +# we run the flows in the opposite direction than visualized above. +# Multiple flow layers have been proposed that use a neural network as learnable parameters, +# such as the planar and radial flow. +# However, we will focus here on flows that are commonly used in image +# modeling, and will discuss them in the rest of the notebook along with +# the details of how to train a normalizing flow. + +# %% [markdown] +# ## Normalizing Flows on images +# +#
+# +# To become familiar with normalizing flows, especially for the application of image modeling, +# it is best to discuss the different elements in a flow along with the implementation. +# As a general concept, we want to build a normalizing flow that maps an input image (here MNIST) to an equally sized latent space: +# +#
+# +# As a first step, we will implement a template of a normalizing flow in PyTorch Lightning. +# During training and validation, a normalizing flow performs density estimation in the forward direction. +# For this, we apply a series of flow transformations on the input $x$ and estimate the probability +# of the input by determining the probability of the transformed point $z$ given a prior, +# and the change of volume caused by the transformations. +# During inference, we can do both density estimation and sampling new points by inverting the flow transformations. +# Therefore, we define a function `_get_likelihood` which performs density estimation, +# and `sample` to generate new examples. +# The functions `training_step`, `validation_step` and `test_step` all make use of `_get_likelihood`. +# +# The standard metric used in generative models, and in particular normalizing flows, is bits per dimensions (bpd). +# Bpd is motivated from an information theory perspective and describes how many bits we would need to encode a particular example in our modeled distribution. +# The less bits we need, the more likely the example in our distribution. +# When we test for the bits per dimension of our test dataset, we can judge whether our model generalizes to new samples of the dataset and didn't memorize the training dataset. +# In order to calculate the bits per dimension score, we can rely on the negative log-likelihood and change the log base (as bits are binary while NLL is usually exponential): +# +# $$\text{bpd} = \text{nll} \cdot \log_2\left(\exp(1)\right) \cdot \left(\prod d_i\right)^{-1}$$ +# +# where $d_1,...,d_K$ are the dimensions of the input. +# For images, this would be the height, width and channel number. +# We divide the log likelihood by these extra dimensions to have a metric which we can compare for different image resolutions. +# In the original image space, MNIST examples have a bits per dimension +# score of 8 (we need 8 bits to encode each pixel as there are 256 +# possible values). + + +# %% +class ImageFlow(L.LightningModule): + def __init__(self, flows, import_samples=8): + """ + Args: + flows: A list of flows (each a nn.Module) that should be applied on the images. + import_samples: Number of importance samples to use during testing (see explanation below). Can be changed at any time + """ + super().__init__() + self.flows = nn.ModuleList(flows) + self.import_samples = import_samples + # Create prior distribution for final latent space + self.prior = torch.distributions.normal.Normal(loc=0.0, scale=1.0) + # Example input for visualizing the graph + self.example_input_array = train_set[0][0].unsqueeze(dim=0) + + def forward(self, imgs): + # The forward function is only used for visualizing the graph + return self._get_likelihood(imgs) + + def encode(self, imgs): + # Given a batch of images, return the latent representation z and ldj of the transformations + z, ldj = imgs, torch.zeros(imgs.shape[0], device=self.device) + for flow in self.flows: + z, ldj = flow(z, ldj, reverse=False) + return z, ldj + + def _get_likelihood(self, imgs, return_ll=False): + """Given a batch of images, return the likelihood of those. + + If return_ll is True, this function returns the log likelihood of the input. Otherwise, the ouptut metric is + bits per dimension (scaled negative log likelihood) + """ + z, ldj = self.encode(imgs) + log_pz = self.prior.log_prob(z).sum(dim=[1, 2, 3]) + log_px = ldj + log_pz + nll = -log_px + # Calculating bits per dimension + bpd = nll * np.log2(np.exp(1)) / np.prod(imgs.shape[1:]) + return bpd.mean() if not return_ll else log_px + + @torch.no_grad() + def sample(self, img_shape, z_init=None): + """Sample a batch of images from the flow.""" + # Sample latent representation from prior + if z_init is None: + z = self.prior.sample(sample_shape=img_shape).to(device) + else: + z = z_init.to(device) + + # Transform z to x by inverting the flows + ldj = torch.zeros(img_shape[0], device=device) + for flow in reversed(self.flows): + z, ldj = flow(z, ldj, reverse=True) + return z + + def configure_optimizers(self): + optimizer = optim.Adam(self.parameters(), lr=1e-3) + # An scheduler is optional, but can help in flows to get the last bpd improvement + scheduler = optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.99) + return [optimizer], [scheduler] + + def training_step(self, batch, batch_idx): + # Normalizing flows are trained by maximum likelihood => return bpd + loss = self._get_likelihood(batch[0]) + self.log("train_bpd", loss) + return loss + + def validation_step(self, batch, batch_idx): + loss = self._get_likelihood(batch[0]) + self.log("val_bpd", loss) + + def test_step(self, batch, batch_idx): + # Perform importance sampling during testing => estimate likelihood M times for each image + samples = [] + for _ in range(self.import_samples): + img_ll = self._get_likelihood(batch[0], return_ll=True) + samples.append(img_ll) + img_ll = torch.stack(samples, dim=-1) + + # To average the probabilities, we need to go from log-space to exp, and back to log. + # Logsumexp provides us a stable implementation for this + img_ll = torch.logsumexp(img_ll, dim=-1) - np.log(self.import_samples) + + # Calculate final bpd + bpd = -img_ll * np.log2(np.exp(1)) / np.prod(batch[0].shape[1:]) + bpd = bpd.mean() + + self.log("test_bpd", bpd) + + +# %% [markdown] +# The `test_step` function differs from the training and validation step in that it makes use of importance sampling. +# We will discuss the motiviation and details behind this after +# understanding how flows model discrete images in continuous space. + +# %% [markdown] +# ### Dequantization +# +# Normalizing flows rely on the rule of change of variables, which is naturally defined in continuous space. +# Applying flows directly on discrete data leads to undesired density models where arbitrarly high likelihood are placed on a few, particular values. +# See the illustration below: +# +#
+# +# The black points represent the discrete points, and the green volume the density modeled by a normalizing flow in continuous space. +# The flow would continue to increase the likelihood for $x=0,1,2,3$ while having no volume on any other point. +# Remember that in continuous space, we have the constraint that the overall volume of the probability density must be 1 ($\int p(x)dx=1$). +# Otherwise, we don't model a probability distribution anymore. +# However, the discrete points $x=0,1,2,3$ represent delta peaks with no width in continuous space. +# This is why the flow can place an infinite high likelihood on these few points while still representing a distribution in continuous space. +# Nonetheless, the learned density does not tell us anything about the distribution among the discrete points, +# as in discrete space, the likelihoods of those four points would have to sum to 1, not to infinity. +# +# To prevent such degenerated solutions, a common solution is to add a small amount of noise to each discrete value, which is also referred to as dequantization. +# Considering $x$ as an integer (as it is the case for images), the dequantized representation $v$ can be formulated as $v=x+u$ where $u\in[0,1)^D$. +# Thus, the discrete value $1$ is modeled by a distribution over the interval $[1.0, 2.0)$, the value $2$ by an volume over $[2.0, 3.0)$, etc. +# Our objective of modeling $p(x)$ becomes: +# +# $$ p(x) = \int p(x+u)du = \int \frac{q(u|x)}{q(u|x)}p(x+u)du = \mathbb{E}_{u\sim q(u|x)}\left[\frac{p(x+u)}{q(u|x)} \right]$$ +# +# with $q(u|x)$ being the noise distribution. +# For now, we assume it to be uniform, which can also be written as $p(x)=\mathbb{E}_{u\sim U(0,1)^D}\left[p(x+u) \right]$. +# +# In the following, we will implement Dequantization as a flow transformation itself. +# After adding noise to the discrete values, we additionally transform the volume into a Gaussian-like shape. +# This is done by scaling $x+u$ between $0$ and $1$, and applying the invert of the sigmoid function $\sigma(z)^{-1} = \log z - \log 1-z$. +# If we would not do this, we would face two problems: +# +# 1. +# The input is scaled between 0 and 256 while the prior distribution is a Gaussian with mean $0$ and standard deviation $1$. +# In the first iterations after initializing the parameters of the flow, we would have extremely low likelihoods for large values like $256$. +# This would cause the training to diverge instantaneously. +# 2. +# As the output distribution is a Gaussian, it is beneficial for the flow to have a similarly shaped input distribution. +# This will reduce the modeling complexity that is required by the flow. +# +# Overall, we can implement dequantization as follows: + + +# %% +class Dequantization(nn.Module): + def __init__(self, alpha=1e-5, quants=256): + """ + Args: + alpha: small constant that is used to scale the original input. + Prevents dealing with values very close to 0 and 1 when inverting the sigmoid + quants: Number of possible discrete values (usually 256 for 8-bit image) + """ + super().__init__() + self.alpha = alpha + self.quants = quants + + def forward(self, z, ldj, reverse=False): + if not reverse: + z, ldj = self.dequant(z, ldj) + z, ldj = self.sigmoid(z, ldj, reverse=True) + else: + z, ldj = self.sigmoid(z, ldj, reverse=False) + z = z * self.quants + ldj += np.log(self.quants) * np.prod(z.shape[1:]) + z = torch.floor(z).clamp(min=0, max=self.quants - 1).to(torch.int32) + return z, ldj + + def sigmoid(self, z, ldj, reverse=False): + # Applies an invertible sigmoid transformation + if not reverse: + ldj += (-z - 2 * F.softplus(-z)).sum(dim=[1, 2, 3]) + z = torch.sigmoid(z) + else: + z = z * (1 - self.alpha) + 0.5 * self.alpha # Scale to prevent boundaries 0 and 1 + ldj += np.log(1 - self.alpha) * np.prod(z.shape[1:]) + ldj += (-torch.log(z) - torch.log(1 - z)).sum(dim=[1, 2, 3]) + z = torch.log(z) - torch.log(1 - z) + return z, ldj + + def dequant(self, z, ldj): + # Transform discrete values to continuous volumes + z = z.to(torch.float32) + z = z + torch.rand_like(z).detach() + z = z / self.quants + ldj -= np.log(self.quants) * np.prod(z.shape[1:]) + return z, ldj + + +# %% [markdown] +# A good check whether a flow is correctly implemented or not, is to verify that it is invertible. +# Hence, we will dequantize a randomly chosen training image, and then quantize it again. +# We would expect that we would get the exact same image out: + +# %% +# Testing invertibility of dequantization layer +L.seed_everything(42) +orig_img = train_set[0][0].unsqueeze(dim=0) +ldj = torch.zeros( + 1, +) +dequant_module = Dequantization() +deq_img, ldj = dequant_module(orig_img, ldj, reverse=False) +reconst_img, ldj = dequant_module(deq_img, ldj, reverse=True) + +d1, d2 = torch.where(orig_img.squeeze() != reconst_img.squeeze()) +if len(d1) != 0: + print("Dequantization was not invertible.") + for i in range(d1.shape[0]): + print("Original value:", orig_img[0, 0, d1[i], d2[i]].item()) + print("Reconstructed value:", reconst_img[0, 0, d1[i], d2[i]].item()) +else: + print("Successfully inverted dequantization") + +# Layer is not strictly invertible due to float precision constraints +# assert (orig_img == reconst_img).all().item() + +# %% [markdown] +# In contrast to our expectation, the test fails. +# However, this is no reason to doubt our implementation here as only one single value is not equal to the original. +# This is caused due to numerical inaccuracies in the sigmoid invert. +# While the input space to the inverted sigmoid is scaled between 0 and 1, the output space is between $-\infty$ and $\infty$. +# And as we use 32 bits to represent the numbers (in addition to applying logs over and over again), +# such inaccuries can occur and should not be worrisome. +# Nevertheless, it is good to be aware of them, and can be improved by using a double tensor (float64). +# +# Finally, we can take our dequantization and actually visualize the +# distribution it transforms the discrete values into: + +# %% + + +def visualize_dequantization(quants, prior=None): + """Function for visualizing the dequantization values of discrete values in continuous space.""" + # Prior over discrete values. If not given, a uniform is assumed + if prior is None: + prior = np.ones(quants, dtype=np.float32) / quants + prior = prior / prior.sum() # Ensure proper categorical distribution + + inp = torch.arange(-4, 4, 0.01).view(-1, 1, 1, 1) # Possible continuous values we want to consider + ldj = torch.zeros(inp.shape[0]) + dequant_module = Dequantization(quants=quants) + # Invert dequantization on continuous values to find corresponding discrete value + out, ldj = dequant_module.forward(inp, ldj, reverse=True) + inp, out, prob = inp.squeeze().numpy(), out.squeeze().numpy(), ldj.exp().numpy() + prob = prob * prior[out] # Probability scaled by categorical prior + + # Plot volumes and continuous distribution + sns.set_style("white") + _ = plt.figure(figsize=(6, 3)) + x_ticks = [] + for v in np.unique(out): + indices = np.where(out == v) + color = to_rgb("C%i" % v) + plt.fill_between(inp[indices], prob[indices], np.zeros(indices[0].shape[0]), color=color + (0.5,), label=str(v)) + plt.plot([inp[indices[0][0]]] * 2, [0, prob[indices[0][0]]], color=color) + plt.plot([inp[indices[0][-1]]] * 2, [0, prob[indices[0][-1]]], color=color) + x_ticks.append(inp[indices[0][0]]) + x_ticks.append(inp.max()) + plt.xticks(x_ticks, ["%.1f" % x for x in x_ticks]) + plt.plot(inp, prob, color=(0.0, 0.0, 0.0)) + # Set final plot properties + plt.ylim(0, prob.max() * 1.1) + plt.xlim(inp.min(), inp.max()) + plt.xlabel("z") + plt.ylabel("Probability") + plt.title("Dequantization distribution for %i discrete values" % quants) + plt.legend() + plt.show() + plt.close() + + +visualize_dequantization(quants=8) + +# %% [markdown] +# The visualized distribution show the sub-volumes that are assigned to the different discrete values. +# The value $0$ has its volume between $[-\infty, -1.9)$, the value $1$ is represented by the interval $[-1.9, -1.1)$, etc. +# The volume for each discrete value has the same probability mass. +# That's why the volumes close to the center (e.g. 3 and 4) have a smaller area on the z-axis as others +# ($z$ is being used to denote the output of the whole dequantization flow). +# +# Effectively, the consecutive normalizing flow models discrete images by the following objective: +# +# $$\log p(x) = \log \mathbb{E}_{u\sim q(u|x)}\left[\frac{p(x+u)}{q(u|x)} \right] \geq \mathbb{E}_{u}\left[\log \frac{p(x+u)}{q(u|x)} \right]$$ +# +# Although normalizing flows are exact in likelihood, we have a lower bound. +# Specifically, this is an example of the Jensen inequality because we need to move the log into the expectation so we can use Monte-carlo estimates. +# In general, this bound is considerably smaller than the ELBO in variational autoencoders. +# Actually, we can reduce the bound ourselves by estimating the expectation not by one, but by $M$ samples. +# In other words, we can apply importance sampling which leads to the following inequality: +# +# $$\log p(x) = \log \mathbb{E}_{u\sim q(u|x)}\left[\frac{p(x+u)}{q(u|x)} \right] \geq \mathbb{E}_{u}\left[\log \frac{1}{M} \sum_{m=1}^{M} \frac{p(x+u_m)}{q(u_m|x)} \right] \geq \mathbb{E}_{u}\left[\log \frac{p(x+u)}{q(u|x)} \right]$$ +# +# The importance sampling $\frac{1}{M} \sum_{m=1}^{M} \frac{p(x+u_m)}{q(u_m|x)}$ becomes +# $\mathbb{E}_{u\sim q(u|x)}\left[\frac{p(x+u)}{q(u|x)} \right]$ if $M\to \infty$, +# so that the more samples we use, the tighter the bound is. +# During testing, we can make use of this property and have it implemented in `test_step` in `ImageFlow`. +# In theory, we could also use this tighter bound during training. +# However, related work has shown that this does not necessarily lead to +# an improvement given the additional computational cost, and it is more +# efficient to stick with a single estimate [5]. + +# %% [markdown] +# ### Variational Dequantization +# +# Dequantization uses a uniform distribution for the noise $u$ which effectively leads to images being represented as hypercubes +# (cube in high dimensions) with sharp borders. +# However, modeling such sharp borders is not easy for a flow as it uses smooth transformations to convert it into a Gaussian distribution. +# +# Another way of looking at it is if we change the prior distribution in the previous visualization. +# Imagine we have independent Gaussian noise on pixels which is commonly the case for any real-world taken picture. +# Therefore, the flow would have to model a distribution as above, but with the individual volumes scaled as follows: + +# %% +visualize_dequantization(quants=8, prior=np.array([0.075, 0.2, 0.4, 0.2, 0.075, 0.025, 0.0125, 0.0125])) + +# %% [markdown] +# Transforming such a probability into a Gaussian is a difficult task, especially with such hard borders. +# Dequantization has therefore been extended to more sophisticated, learnable distributions beyond uniform in a variational framework. +# In particular, if we remember the learning objective +# $\log p(x) = \log \mathbb{E}_{u}\left[\frac{p(x+u)}{q(u|x)} \right]$, +# the uniform distribution can be replaced by a learned distribution $q_{\theta}(u|x)$ with support over $u\in[0,1)^D$. +# This approach is called Variational Dequantization and has been proposed by Ho et al. +# [3]. +# How can we learn such a distribution? +# We can use a second normalizing flow that takes $x$ as external input and learns a flexible distribution over $u$. +# To ensure a support over $[0,1)^D$, we can apply a sigmoid activation function as final flow transformation. +# +# Inheriting the original dequantization class, we can implement variational dequantization as follows: + + +# %% +class VariationalDequantization(Dequantization): + def __init__(self, var_flows, alpha=1e-5): + """ + Args: + var_flows: A list of flow transformations to use for modeling q(u|x) + alpha: Small constant, see Dequantization for details + """ + super().__init__(alpha=alpha) + self.flows = nn.ModuleList(var_flows) + + def dequant(self, z, ldj): + z = z.to(torch.float32) + img = (z / 255.0) * 2 - 1 # We condition the flows on x, i.e. the original image + + # Prior of u is a uniform distribution as before + # As most flow transformations are defined on [-infinity,+infinity], we apply an inverse sigmoid first. + deq_noise = torch.rand_like(z).detach() + deq_noise, ldj = self.sigmoid(deq_noise, ldj, reverse=True) + for flow in self.flows: + deq_noise, ldj = flow(deq_noise, ldj, reverse=False, orig_img=img) + deq_noise, ldj = self.sigmoid(deq_noise, ldj, reverse=False) + + # After the flows, apply u as in standard dequantization + z = (z + deq_noise) / 256.0 + ldj -= np.log(256.0) * np.prod(z.shape[1:]) + return z, ldj + + +# %% [markdown] +# Variational dequantization can be used as a substitute for dequantization. +# We will compare dequantization and variational dequantization in later experiments. + +# %% [markdown] +# ### Coupling layers +# +#
+# +# Next, we look at possible transformations to apply inside the flow. +# A recent popular flow layer, which works well in combination with deep neural networks, +# is the coupling layer introduced by Dinh et al. +# [1]. +# The input $z$ is arbitrarily split into two parts, $z_{1:j}$ and $z_{j+1:d}$, of which the first remains unchanged by the flow. +# Yet, $z_{1:j}$ is used to parameterize the transformation for the second part, $z_{j+1:d}$. +# Various transformations have been proposed in recent time [3,4], but here we will settle for the simplest and most efficient one: affine coupling. +# In this coupling layer, we apply an affine transformation by shifting the input by a bias $\mu$ and scale it by $\sigma$. +# In other words, our transformation looks as follows: +# +# $$z'_{j+1:d} = \mu_{\theta}(z_{1:j}) + \sigma_{\theta}(z_{1:j}) \odot z_{j+1:d}$$ +# +# The functions $\mu$ and $\sigma$ are implemented as a shared neural network, +# and the sum and multiplication are performed element-wise. +# The LDJ is thereby the sum of the logs of the scaling factors: $\sum_i \left[\log \sigma_{\theta}(z_{1:j})\right]_i$. +# Inverting the layer can as simply be done as subtracting the bias and dividing by the scale: +# +# $$z_{j+1:d} = \left(z'_{j+1:d} - \mu_{\theta}(z_{1:j})\right) / \sigma_{\theta}(z_{1:j})$$ +# +# We can also visualize the coupling layer in form of a computation graph, +# where $z_1$ represents $z_{1:j}$, and $z_2$ represents $z_{j+1:d}$: +# +#
+# +# In our implementation, we will realize the splitting of variables as masking. +# The variables to be transformed, $z_{j+1:d}$, are masked when passing $z$ to the shared network to predict the transformation parameters. +# When applying the transformation, we mask the parameters for $z_{1:j}$ +# so that we have an identity operation for those variables: + + +# %% +class CouplingLayer(nn.Module): + def __init__(self, network, mask, c_in): + """Coupling layer inside a normalizing flow. + + Args: + network: A PyTorch nn.Module constituting the deep neural network for mu and sigma. + Output shape should be twice the channel size as the input. + mask: Binary mask (0 or 1) where 0 denotes that the element should be transformed, + while 1 means the latent will be used as input to the NN. + c_in: Number of input channels + """ + super().__init__() + self.network = network + self.scaling_factor = nn.Parameter(torch.zeros(c_in)) + # Register mask as buffer as it is a tensor which is not a parameter, + # but should be part of the modules state. + self.register_buffer("mask", mask) + + def forward(self, z, ldj, reverse=False, orig_img=None): + """ + Args: + z: Latent input to the flow + ldj: The current ldj of the previous flows. + The ldj of this layer will be added to this tensor. + reverse: If True, we apply the inverse of the layer. + orig_img (optional): Only needed in VarDeq. Allows external + input to condition the flow on (e.g. original image) + """ + # Apply network to masked input + z_in = z * self.mask + if orig_img is None: + nn_out = self.network(z_in) + else: + nn_out = self.network(torch.cat([z_in, orig_img], dim=1)) + s, t = nn_out.chunk(2, dim=1) + + # Stabilize scaling output + s_fac = self.scaling_factor.exp().view(1, -1, 1, 1) + s = torch.tanh(s / s_fac) * s_fac + + # Mask outputs (only transform the second part) + s = s * (1 - self.mask) + t = t * (1 - self.mask) + + # Affine transformation + if not reverse: + # Whether we first shift and then scale, or the other way round, + # is a design choice, and usually does not have a big impact + z = (z + t) * torch.exp(s) + ldj += s.sum(dim=[1, 2, 3]) + else: + z = (z * torch.exp(-s)) - t + ldj -= s.sum(dim=[1, 2, 3]) + + return z, ldj + + +# %% [markdown] +# For stabilization purposes, we apply a $\tanh$ activation function on the scaling output. +# This prevents sudden large output values for the scaling that can destabilize training. +# To still allow scaling factors smaller or larger than -1 and 1 respectively, +# we have a learnable parameter per dimension, called `scaling_factor`. +# This scales the tanh to different limits. +# Below, we visualize the effect of the scaling factor on the output activation of the scaling terms: + +# %% +with torch.no_grad(): + x = torch.arange(-5, 5, 0.01) + scaling_factors = [0.5, 1, 2] + sns.set() + fig, ax = plt.subplots(1, 3, figsize=(12, 3)) + for i, scale in enumerate(scaling_factors): + y = torch.tanh(x / scale) * scale + ax[i].plot(x.numpy(), y.numpy()) + ax[i].set_title("Scaling factor: " + str(scale)) + ax[i].set_ylim(-3, 3) + plt.subplots_adjust(wspace=0.4) + sns.reset_orig() + plt.show() + +# %% [markdown] +# Coupling layers generalize to any masking technique we could think of. +# However, the most common approach for images is to split the input $z$ in half, using a checkerboard mask or channel mask. +# A checkerboard mask splits the variables across the height and width dimensions and assigns each other pixel to $z_{j+1:d}$. +# Thereby, the mask is shared across channels. +# In contrast, the channel mask assigns half of the channels to $z_{j+1:d}$, and the other half to $z_{1:j+1}$. +# Note that when we apply multiple coupling layers, we invert the masking for each other layer so that each variable is transformed a similar amount of times. +# +# Let's implement a function that creates a checkerboard mask and a channel mask for us: + + +# %% +def create_checkerboard_mask(h, w, invert=False): + x, y = torch.arange(h, dtype=torch.int32), torch.arange(w, dtype=torch.int32) + xx, yy = torch.meshgrid(x, y) + mask = torch.fmod(xx + yy, 2) + mask = mask.to(torch.float32).view(1, 1, h, w) + if invert: + mask = 1 - mask + return mask + + +def create_channel_mask(c_in, invert=False): + mask = torch.cat([torch.ones(c_in // 2, dtype=torch.float32), torch.zeros(c_in - c_in // 2, dtype=torch.float32)]) + mask = mask.view(1, c_in, 1, 1) + if invert: + mask = 1 - mask + return mask + + +# %% [markdown] +# We can also visualize the corresponding masks for an image of size $8\times 8\times 2$ (2 channels): + +# %% +checkerboard_mask = create_checkerboard_mask(h=8, w=8).expand(-1, 2, -1, -1) +channel_mask = create_channel_mask(c_in=2).expand(-1, -1, 8, 8) + +show_imgs(checkerboard_mask.transpose(0, 1), "Checkerboard mask") +show_imgs(channel_mask.transpose(0, 1), "Channel mask") + +# %% [markdown] +# As a last aspect of coupling layers, we need to decide for the deep neural network we want to apply in the coupling layers. +# The input to the layers is an image, and hence we stick with a CNN. +# Because the input to a transformation depends on all transformations before, +# it is crucial to ensure a good gradient flow through the CNN back to the input, +# which can be optimally achieved by a ResNet-like architecture. +# Specifically, we use a Gated ResNet that adds a $\sigma$-gate to the skip connection, +# similarly to the input gate in LSTMs. +# The details are not necessarily important here, and the network is +# strongly inspired from Flow++ [3] in case you are interested in building +# even stronger models. + + +# %% +class ConcatELU(nn.Module): + """Activation function that applies ELU in both direction (inverted and plain). + + Allows non-linearity while providing strong gradients for any input (important for final convolution) + """ + + def forward(self, x): + return torch.cat([F.elu(x), F.elu(-x)], dim=1) + + +class LayerNormChannels(nn.Module): + def __init__(self, c_in, eps=1e-5): + """ + This module applies layer norm across channels in an image. + Inputs: + c_in - Number of channels of the input + eps - Small constant to stabilize std + """ + super().__init__() + self.gamma = nn.Parameter(torch.ones(1, c_in, 1, 1)) + self.beta = nn.Parameter(torch.zeros(1, c_in, 1, 1)) + self.eps = eps + + def forward(self, x): + mean = x.mean(dim=1, keepdim=True) + var = x.var(dim=1, unbiased=False, keepdim=True) + y = (x - mean) / torch.sqrt(var + self.eps) + y = y * self.gamma + self.beta + return y + + +class GatedConv(nn.Module): + def __init__(self, c_in, c_hidden): + """ + This module applies a two-layer convolutional ResNet block with input gate + Args: + c_in: Number of channels of the input + c_hidden: Number of hidden dimensions we want to model (usually similar to c_in) + """ + super().__init__() + self.net = nn.Sequential( + ConcatELU(), + nn.Conv2d(2 * c_in, c_hidden, kernel_size=3, padding=1), + ConcatELU(), + nn.Conv2d(2 * c_hidden, 2 * c_in, kernel_size=1), + ) + + def forward(self, x): + out = self.net(x) + val, gate = out.chunk(2, dim=1) + return x + val * torch.sigmoid(gate) + + +class GatedConvNet(nn.Module): + def __init__(self, c_in, c_hidden=32, c_out=-1, num_layers=3): + """Module that summarizes the previous blocks to a full convolutional neural network. + + Args: + c_in: Number of input channels + c_hidden: Number of hidden dimensions to use within the network + c_out: Number of output channels. If -1, 2 times the input channels are used (affine coupling) + num_layers: Number of gated ResNet blocks to apply + """ + super().__init__() + c_out = c_out if c_out > 0 else 2 * c_in + layers = [] + layers += [nn.Conv2d(c_in, c_hidden, kernel_size=3, padding=1)] + for layer_index in range(num_layers): + layers += [GatedConv(c_hidden, c_hidden), LayerNormChannels(c_hidden)] + layers += [ConcatELU(), nn.Conv2d(2 * c_hidden, c_out, kernel_size=3, padding=1)] + self.nn = nn.Sequential(*layers) + + self.nn[-1].weight.data.zero_() + self.nn[-1].bias.data.zero_() + + def forward(self, x): + return self.nn(x) + + +# %% [markdown] +# ### Training loop +# +# Finally, we can add Dequantization, Variational Dequantization and Coupling Layers together to build our full normalizing flow on MNIST images. +# We apply 8 coupling layers in the main flow, and 4 for variational dequantization if applied. +# We apply a checkerboard mask throughout the network as with a single channel (black-white images), +# we cannot apply channel mask. +# The overall architecture is visualized below. +# +# +#
+ + +# %% +def create_simple_flow(use_vardeq=True): + flow_layers = [] + if use_vardeq: + vardeq_layers = [ + CouplingLayer( + network=GatedConvNet(c_in=2, c_out=2, c_hidden=16), + mask=create_checkerboard_mask(h=28, w=28, invert=(i % 2 == 1)), + c_in=1, + ) + for i in range(4) + ] + flow_layers += [VariationalDequantization(var_flows=vardeq_layers)] + else: + flow_layers += [Dequantization()] + + for i in range(8): + flow_layers += [ + CouplingLayer( + network=GatedConvNet(c_in=1, c_hidden=32), + mask=create_checkerboard_mask(h=28, w=28, invert=(i % 2 == 1)), + c_in=1, + ) + ] + + flow_model = ImageFlow(flow_layers).to(device) + return flow_model + + +# %% [markdown] +# For implementing the training loop, we use the framework of PyTorch Lightning and reduce the code overhead. +# If interested, you can take a look at the generated tensorboard file, +# in particularly the graph to see an overview of flow transformations that are applied. +# Note that we again provide pre-trained models (see later on in the notebook) +# as normalizing flows are particularly expensive to train. +# We have also run validation and testing as this can take some time as well with the added importance sampling. + + +# %% +def train_flow(flow, model_name="MNISTFlow"): + # Create a PyTorch Lightning trainer + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, model_name), + accelerator="auto", + devices=1, + max_epochs=200, + gradient_clip_val=1.0, + callbacks=[ + ModelCheckpoint(save_weights_only=True, mode="min", monitor="val_bpd"), + LearningRateMonitor("epoch"), + ], + ) + trainer.logger._log_graph = True + trainer.logger._default_hp_metric = None # Optional logging argument that we don't need + + train_data_loader = data.DataLoader( + train_set, batch_size=128, shuffle=True, drop_last=True, pin_memory=True, num_workers=8 + ) + result = None + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, model_name + ".ckpt") + if os.path.isfile(pretrained_filename): + print("Found pretrained model, loading...") + ckpt = torch.load(pretrained_filename, map_location=device) + flow.load_state_dict(ckpt["state_dict"]) + result = ckpt.get("result", None) + else: + print("Start training", model_name) + trainer.fit(flow, train_data_loader, val_loader) + + # Test best model on validation and test set if no result has been found + # Testing can be expensive due to the importance sampling. + if result is None: + val_result = trainer.test(flow, dataloaders=val_loader, verbose=False) + start_time = time.time() + test_result = trainer.test(flow, dataloaders=test_loader, verbose=False) + duration = time.time() - start_time + result = {"test": test_result, "val": val_result, "time": duration / len(test_loader) / flow.import_samples} + + return flow, result + + +# %% [markdown] +# ## Multi-scale architecture +# +#
+# +# One disadvantage of normalizing flows is that they operate on the exact same dimensions as the input. +# If the input is high-dimensional, so is the latent space, which requires larger computational cost to learn suitable transformations. +# However, particularly in the image domain, many pixels contain less information in the sense +# that we could remove them without loosing the semantical information of the image. +# +# Based on this intuition, deep normalizing flows on images commonly apply a multi-scale architecture [1]. +# After the first $N$ flow transformations, we split off half of the latent dimensions and directly evaluate them on the prior. +# The other half is run through $N$ more flow transformations, and depending on the size of the input, +# we split it again in half or stop overall at this position. +# The two operations involved in this setup is `Squeeze` and `Split` which +# we will review more closely and implement below. + +# %% [markdown] +# ### Squeeze and Split +# +# When we want to remove half of the pixels in an image, we have the problem of deciding which variables to cut, +# and how to rearrange the image. +# Thus, the squeezing operation is commonly used before split, which divides the image into subsquares +# of shape $2\times 2\times C$, and reshapes them into $1\times 1\times 4C$ blocks. +# Effectively, we reduce the height and width of the image by a factor of 2 while scaling the number of channels by 4. +# Afterwards, we can perform the split operation over channels without the need of rearranging the pixels. +# The smaller scale also makes the overall architecture more efficient. +# Visually, the squeeze operation should transform the input as follows: +# +#
+# +# The input of $4\times 4\times 1$ is scaled to $2\times 2\times 4$ following +# the idea of grouping the pixels in $2\times 2\times 1$ subsquares. +# Next, let's try to implement this layer: + + +# %% +class SqueezeFlow(nn.Module): + def forward(self, z, ldj, reverse=False): + B, C, H, W = z.shape + if not reverse: + # Forward direction: H x W x C => H/2 x W/2 x 4C + z = z.reshape(B, C, H // 2, 2, W // 2, 2) + z = z.permute(0, 1, 3, 5, 2, 4) + z = z.reshape(B, 4 * C, H // 2, W // 2) + else: + # Reverse direction: H/2 x W/2 x 4C => H x W x C + z = z.reshape(B, C // 4, 2, 2, H, W) + z = z.permute(0, 1, 4, 2, 5, 3) + z = z.reshape(B, C // 4, H * 2, W * 2) + return z, ldj + + +# %% [markdown] +# Before moving on, we can verify our implementation by comparing our output with the example figure above: + +# %% +sq_flow = SqueezeFlow() +rand_img = torch.arange(1, 17).view(1, 1, 4, 4) +print("Image (before)\n", rand_img) +forward_img, _ = sq_flow(rand_img, ldj=None, reverse=False) +print("\nImage (forward)\n", forward_img.permute(0, 2, 3, 1)) # Permute for readability +reconst_img, _ = sq_flow(forward_img, ldj=None, reverse=True) +print("\nImage (reverse)\n", reconst_img) + +# %% [markdown] +# The split operation divides the input into two parts, and evaluates one part directly on the prior. +# So that our flow operation fits to the implementation of the previous layers, +# we will return the prior probability of the first part as the log determinant jacobian of the layer. +# It has the same effect as if we would combine all variable splits at the +# end of the flow, and evaluate them together on the prior. + + +# %% +class SplitFlow(nn.Module): + def __init__(self): + super().__init__() + self.prior = torch.distributions.normal.Normal(loc=0.0, scale=1.0) + + def forward(self, z, ldj, reverse=False): + if not reverse: + z, z_split = z.chunk(2, dim=1) + ldj += self.prior.log_prob(z_split).sum(dim=[1, 2, 3]) + else: + z_split = self.prior.sample(sample_shape=z.shape).to(device) + z = torch.cat([z, z_split], dim=1) + ldj -= self.prior.log_prob(z_split).sum(dim=[1, 2, 3]) + return z, ldj + + +# %% [markdown] +# ### Building a multi-scale flow +# +# After defining the squeeze and split operation, we are finally able to build our own multi-scale flow. +# Deep normalizing flows such as Glow and Flow++ [2,3] often apply a split operation directly after squeezing. +# However, with shallow flows, we need to be more thoughtful about where to place the split operation as we need at least a minimum amount of transformations on each variable. +# Our setup is inspired by the original RealNVP architecture [1] which is shallower than other, +# more recent state-of-the-art architectures. +# +# Hence, for the MNIST dataset, we will apply the first squeeze operation after two coupling layers, but don't apply a split operation yet. +# Because we have only used two coupling layers and each the variable has been only transformed once, a split operation would be too early. +# We apply two more coupling layers before finally applying a split flow and squeeze again. +# The last four coupling layers operate on a scale of $7\times 7\times 8$. +# The full flow architecture is shown below. +# +#
+# +# Note that while the feature maps inside the coupling layers reduce with the height and width of the input, +# the increased number of channels is not directly considered. +# To counteract this, we increase the hidden dimensions for the coupling layers on the squeezed input. +# The dimensions are often scaled by 2 as this approximately increases the computation cost by 4 canceling with the squeezing operation. +# However, we will choose the hidden dimensionalities $32, 48, 64$ for the +# three scales respectively to keep the number of parameters reasonable +# and show the efficiency of multi-scale architectures. + + +# %% +def create_multiscale_flow(): + flow_layers = [] + + vardeq_layers = [ + CouplingLayer( + network=GatedConvNet(c_in=2, c_out=2, c_hidden=16), + mask=create_checkerboard_mask(h=28, w=28, invert=(i % 2 == 1)), + c_in=1, + ) + for i in range(4) + ] + flow_layers += [VariationalDequantization(vardeq_layers)] + + flow_layers += [ + CouplingLayer( + network=GatedConvNet(c_in=1, c_hidden=32), + mask=create_checkerboard_mask(h=28, w=28, invert=(i % 2 == 1)), + c_in=1, + ) + for i in range(2) + ] + flow_layers += [SqueezeFlow()] + for i in range(2): + flow_layers += [ + CouplingLayer( + network=GatedConvNet(c_in=4, c_hidden=48), mask=create_channel_mask(c_in=4, invert=(i % 2 == 1)), c_in=4 + ) + ] + flow_layers += [SplitFlow(), SqueezeFlow()] + for i in range(4): + flow_layers += [ + CouplingLayer( + network=GatedConvNet(c_in=8, c_hidden=64), mask=create_channel_mask(c_in=8, invert=(i % 2 == 1)), c_in=8 + ) + ] + + flow_model = ImageFlow(flow_layers).to(device) + return flow_model + + +# %% [markdown] +# We can show the difference in number of parameters below: + + +# %% +def print_num_params(model): + num_params = sum(np.prod(p.shape) for p in model.parameters()) + print(f"Number of parameters: {num_params:,}") + + +print_num_params(create_simple_flow(use_vardeq=False)) +print_num_params(create_simple_flow(use_vardeq=True)) +print_num_params(create_multiscale_flow()) + +# %% [markdown] +# Although the multi-scale flow has almost 3 times the parameters of the single scale flow, +# it is not necessarily more computationally expensive than its counterpart. +# We will compare the runtime in the following experiments as well. + +# %% [markdown] +# ## Analysing the flows +# +# In the last part of the notebook, we will train all the models we have implemented above, +# and try to analyze the effect of the multi-scale architecture and variational dequantization. +# +# ### Training flow variants +# +# Before we can analyse the flow models, we need to train them first. +# We provide pre-trained models that contain the validation and test performance, and run-time information. +# As flow models are computationally expensive, we advice you to rely on +# those pretrained models for a first run through the notebook. + +# %% +flow_dict = {"simple": {}, "vardeq": {}, "multiscale": {}} +flow_dict["simple"]["model"], flow_dict["simple"]["result"] = train_flow( + create_simple_flow(use_vardeq=False), model_name="MNISTFlow_simple" +) +flow_dict["vardeq"]["model"], flow_dict["vardeq"]["result"] = train_flow( + create_simple_flow(use_vardeq=True), model_name="MNISTFlow_vardeq" +) +flow_dict["multiscale"]["model"], flow_dict["multiscale"]["result"] = train_flow( + create_multiscale_flow(), model_name="MNISTFlow_multiscale" +) + +# %% [markdown] +# ### Density modeling and sampling +# +# Firstly, we can compare the models on their quantitative results. +# The following table shows all important statistics. +# The inference time specifies the time needed to determine the +# probability for a batch of 64 images for each model, and the sampling +# time the duration it took to sample a batch of 64 images. + +# %% language="html" +# +# + +# %% + +table = [ + [ + key, + "%4.3f bpd" % flow_dict[key]["result"]["val"][0]["test_bpd"], + "%4.3f bpd" % flow_dict[key]["result"]["test"][0]["test_bpd"], + "%2.0f ms" % (1000 * flow_dict[key]["result"]["time"]), + "%2.0f ms" % (1000 * flow_dict[key]["result"].get("samp_time", 0)), + "{:,}".format(sum(np.prod(p.shape) for p in flow_dict[key]["model"].parameters())), + ] + for key in flow_dict +] +display( + HTML( + tabulate.tabulate( + table, + tablefmt="html", + headers=["Model", "Validation Bpd", "Test Bpd", "Inference time", "Sampling time", "Num Parameters"], + ) + ) +) + +# %% [markdown] +# As we have intially expected, using variational dequantization improves upon standard dequantization in terms of bits per dimension. +# Although the difference with 0.04bpd doesn't seem impressive first, it is a considerably step for generative models +# (most state-of-the-art models improve upon previous models in a range of 0.02-0.1bpd on CIFAR with three times as high bpd). +# While it takes longer to evaluate the probability of an image due to the variational dequantization, +# which also leads to a longer training time, it does not have an effect on the sampling time. +# This is because inverting variational dequantization is the same as dequantization: finding the next lower integer. +# +# When we compare the two models to multi-scale architecture, we can see that the bits per dimension score again dropped by about 0.04bpd. +# Additionally, the inference time and sampling time improved notably despite having more parameters. +# Thus, we see that the multi-scale flow is not only stronger for density modeling, but also more efficient. +# +# Next, we can test the sampling quality of the models. +# We should note that the samples for variational dequantization and standard dequantization are very similar, +# and hence we visualize here only the ones for variational dequantization and the multi-scale model. +# However, feel free to also test out the `"simple"` model. +# The seeds are set to obtain reproducable generations and are not cherry picked. + +# %% +L.seed_everything(44) +samples = flow_dict["vardeq"]["model"].sample(img_shape=[16, 1, 28, 28]) +show_imgs(samples.cpu()) + +# %% +L.seed_everything(44) +samples = flow_dict["multiscale"]["model"].sample(img_shape=[16, 8, 7, 7]) +show_imgs(samples.cpu()) + +# %% [markdown] +# From the few samples, we can see a clear difference between the simple and the multi-scale model. +# The single-scale model has only learned local, small correlations while the multi-scale model was able to learn full, +# global relations that form digits. +# This show-cases another benefit of the multi-scale model. +# In contrast to VAEs, the outputs are sharp as normalizing flows can naturally model complex, +# multi-modal distributions while VAEs have the independent decoder output noise. +# Nevertheless, the samples from this flow are far from perfect as not all samples show true digits. + +# %% [markdown] +# ### Interpolation in latent space +# +# Another popular test for the smoothness of the latent space of generative models is to interpolate between two training examples. +# As normalizing flows are strictly invertible, we can guarantee that any image is represented in the latent space. +# We again compare the variational dequantization model with the multi-scale model below. + + +# %% +@torch.no_grad() +def interpolate(model, img1, img2, num_steps=8): + """ + Args: + model: object of ImageFlow class that represents the (trained) flow model + img1, img2: Image tensors of shape [1, 28, 28]. Images between which should be interpolated. + num_steps: Number of interpolation steps. 8 interpolation steps mean 6 intermediate pictures besides img1 and img2 + """ + imgs = torch.stack([img1, img2], dim=0).to(model.device) + z, _ = model.encode(imgs) + alpha = torch.linspace(0, 1, steps=num_steps, device=z.device).view(-1, 1, 1, 1) + interpolations = z[0:1] * alpha + z[1:2] * (1 - alpha) + interp_imgs = model.sample(interpolations.shape[:1] + imgs.shape[1:], z_init=interpolations) + show_imgs(interp_imgs, row_size=8) + + +exmp_imgs, _ = next(iter(train_loader)) + +# %% +L.seed_everything(42) +for i in range(2): + interpolate(flow_dict["vardeq"]["model"], exmp_imgs[2 * i], exmp_imgs[2 * i + 1]) + +# %% +L.seed_everything(42) +for i in range(2): + interpolate(flow_dict["multiscale"]["model"], exmp_imgs[2 * i], exmp_imgs[2 * i + 1]) + +# %% [markdown] +# The interpolations of the multi-scale model result in more realistic digits +# (first row $7\leftrightarrow 8\leftrightarrow 6$, second row $9\leftrightarrow 4\leftrightarrow 6$), +# while the variational dequantization model focuses on local patterns that globally do not form a digit. +# For the multi-scale model, we actually did not do the "true" interpolation between the two images +# as we did not consider the variables that were split along the flow (they have been sampled randomly for all samples). +# However, as we will see in the next experiment, the early variables do not effect the overall image much. + +# %% [markdown] +# ### Visualization of latents in different levels of multi-scale +# +# In the following we will focus more on the multi-scale flow. +# We want to analyse what information is being stored in the variables split at early layers, +# and what information for the final variables. +# For this, we sample 8 images where each of them share the same final latent variables, +# but differ in the other part of the latent variables. +# Below we visualize three examples of this: + +# %% +L.seed_everything(44) +for _ in range(3): + z_init = flow_dict["multiscale"]["model"].prior.sample(sample_shape=[1, 8, 7, 7]) + z_init = z_init.expand(8, -1, -1, -1) + samples = flow_dict["multiscale"]["model"].sample(img_shape=z_init.shape, z_init=z_init) + show_imgs(samples.cpu()) + +# %% [markdown] +# We see that the early split variables indeed have a smaller effect on the image. +# Still, small differences can be spot when we look carefully at the borders of the digits. +# For instance, the hole at the top of the 8 changes for different samples although all of them represent the same coarse structure. +# This shows that the flow indeed learns to separate the higher-level +# information in the final variables, while the early split ones contain +# local noise patterns. + +# %% [markdown] +# ### Visualizing Dequantization +# +# As a final part of this notebook, we will look at the effect of variational dequantization. +# We have motivated variational dequantization by the issue of sharp edges/boarders being difficult to model, +# and a flow would rather prefer smooth, prior-like distributions. +# To check how what noise distribution $q(u|x)$ the flows in the +# variational dequantization module have learned, we can plot a histogram +# of output values from the dequantization and variational dequantization +# module. + + +# %% +def visualize_dequant_distribution(model: ImageFlow, imgs: Tensor, title: str = None): + """ + Args: + model: The flow of which we want to visualize the dequantization distribution + imgs: Example training images of which we want to visualize the dequantization distribution + """ + imgs = imgs.to(device) + ldj = torch.zeros(imgs.shape[0], dtype=torch.float32).to(device) + with torch.no_grad(): + dequant_vals = [] + for _ in tqdm(range(8), leave=False): + d, _ = model.flows[0](imgs, ldj, reverse=False) + dequant_vals.append(d) + dequant_vals = torch.cat(dequant_vals, dim=0) + dequant_vals = dequant_vals.view(-1).cpu().numpy() + sns.set() + plt.figure(figsize=(10, 3)) + plt.hist(dequant_vals, bins=256, color=to_rgb("C0") + (0.5,), edgecolor="C0", density=True) + if title is not None: + plt.title(title) + plt.show() + plt.close() + + +sample_imgs, _ = next(iter(train_loader)) + +# %% +visualize_dequant_distribution(flow_dict["simple"]["model"], sample_imgs, title="Dequantization") + +# %% +visualize_dequant_distribution(flow_dict["vardeq"]["model"], sample_imgs, title="Variational dequantization") + +# %% [markdown] +# The dequantization distribution in the first plot shows that the MNIST images have a strong bias towards 0 (black), +# and the distribution of them have a sharp border as mentioned before. +# The variational dequantization module has indeed learned a much smoother distribution with a Gaussian-like curve which can be modeled much better. +# For the other values, we would need to visualize the distribution $q(u|x)$ on a deeper level, depending on $x$. +# However, as all $u$'s interact and depend on each other, we would need +# to visualize a distribution in 784 dimensions, which is not that +# intuitive anymore. + +# %% [markdown] +# ## Conclusion +# +# In conclusion, we have seen how to implement our own normalizing flow, and what difficulties arise if we want to apply them on images. +# Dequantization is a crucial step in mapping the discrete images into continuous space to prevent underisable delta-peak solutions. +# While dequantization creates hypercubes with hard border, variational dequantization allows us to fit a flow much better on the data. +# This allows us to obtain a lower bits per dimension score, while not affecting the sampling speed. +# The most common flow element, the coupling layer, is simple to implement, and yet effective. +# Furthermore, multi-scale architectures help to capture the global image context while allowing us to efficiently scale up the flow. +# Normalizing flows are an interesting alternative to VAEs as they allow an exact likelihood estimate in continuous space, +# and we have the guarantee that every possible input $x$ has a corresponding latent vector $z$. +# However, even beyond continuous inputs and images, flows can be applied and allow us to exploit +# the data structure in latent space, as e.g. on graphs for the task of molecule generation [6]. +# Recent advances in [Neural ODEs](https://arxiv.org/pdf/1806.07366.pdf) allow a flow with infinite number of layers, +# called Continuous Normalizing Flows, whose potential is yet to fully explore. +# Overall, normalizing flows are an exciting research area which will continue over the next couple of years. + +# %% [markdown] +# ## References +# +# [1] Dinh, L., Sohl-Dickstein, J., and Bengio, S. (2017). +# “Density estimation using Real NVP,” In: 5th International Conference on Learning Representations, ICLR 2017. +# [Link](https://arxiv.org/abs/1605.08803) +# +# [2] Kingma, D. P., and Dhariwal, P. (2018). +# “Glow: Generative Flow with Invertible 1x1 Convolutions,” In: Advances in Neural Information Processing Systems, vol. +# 31, pp. +# 10215--10224. +# [Link](http://papers.nips.cc/paper/8224-glow-generative-flow-with-invertible-1x1-convolutions.pdf) +# +# [3] Ho, J., Chen, X., Srinivas, A., Duan, Y., and Abbeel, P. (2019). +# “Flow++: Improving Flow-Based Generative Models with Variational Dequantization and Architecture Design,” +# in Proceedings of the 36th International Conference on Machine Learning, vol. +# 97, pp. +# 2722–2730. +# [Link](https://arxiv.org/abs/1902.00275) +# +# [4] Durkan, C., Bekasov, A., Murray, I., and Papamakarios, G. (2019). +# “Neural Spline Flows,” In: Advances in Neural Information Processing Systems, pp. +# 7509–7520. +# [Link](http://papers.neurips.cc/paper/8969-neural-spline-flows.pdf) +# +# [5] Hoogeboom, E., Cohen, T. S., and Tomczak, J. M. (2020). +# “Learning Discrete Distributions by Dequantization,” arXiv preprint arXiv2001.11235v1. +# [Link](https://arxiv.org/abs/2001.11235) +# +# [6] Lippe, P., and Gavves, E. (2021). +# “Categorical Normalizing Flows via Continuous Transformations,” +# In: International Conference on Learning Representations, ICLR 2021. +# [Link](https://openreview.net/pdf?id=-GLNZeVDuik) diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/Squeeze_operation.svg b/_notebooks/course_UvA-DL/09-normalizing-flows/Squeeze_operation.svg new file mode 100644 index 0000000..cf66772 --- /dev/null +++ b/_notebooks/course_UvA-DL/09-normalizing-flows/Squeeze_operation.svg @@ -0,0 +1,3 @@ + + +
16
16
15
15
12
12
14
14
13
13
10
10
8
8
7
7
4
4
6
6
5
5
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
11
11
12
12
13
13
14
14
15
15
16
16
2
2
3
3
9
9
11
11
1
1
H x W x C
H x W x C
H/2 x W/2 x 4C
H/2 x W/2 x 4C
diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/comparison_GAN_VAE_NF.png b/_notebooks/course_UvA-DL/09-normalizing-flows/comparison_GAN_VAE_NF.png new file mode 100644 index 0000000..fffe9d2 Binary files /dev/null and b/_notebooks/course_UvA-DL/09-normalizing-flows/comparison_GAN_VAE_NF.png differ diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/coupling_flow.svg b/_notebooks/course_UvA-DL/09-normalizing-flows/coupling_flow.svg new file mode 100644 index 0000000..817758c --- /dev/null +++ b/_notebooks/course_UvA-DL/09-normalizing-flows/coupling_flow.svg @@ -0,0 +1,926 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/dequantization_issue.svg b/_notebooks/course_UvA-DL/09-normalizing-flows/dequantization_issue.svg new file mode 100644 index 0000000..f254085 --- /dev/null +++ b/_notebooks/course_UvA-DL/09-normalizing-flows/dequantization_issue.svg @@ -0,0 +1,417 @@ + + + + + + + + 2020-09-10T14:01:01.731212 + image/svg+xml + + + Matplotlib v3.3.0, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/image_to_gaussian.svg b/_notebooks/course_UvA-DL/09-normalizing-flows/image_to_gaussian.svg new file mode 100644 index 0000000..f287768 --- /dev/null +++ b/_notebooks/course_UvA-DL/09-normalizing-flows/image_to_gaussian.svg @@ -0,0 +1,3 @@ + + +
p(z)
p(z)
1x28x28
1x28x28
p(x)
p(x)
1x28x28
1x28x28
Normalizing Flow
Normalizing F...
diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/multiscale_flow.svg b/_notebooks/course_UvA-DL/09-normalizing-flows/multiscale_flow.svg new file mode 100644 index 0000000..db7af24 --- /dev/null +++ b/_notebooks/course_UvA-DL/09-normalizing-flows/multiscale_flow.svg @@ -0,0 +1,3 @@ + + +
(Variational) Dequantization
(Variational) Dequantizati...
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
p(x)
p(x)
p(z)
p(z)
1x28x28
1x28x28
1x28x28
1x28x28
Squeeze flow
Squeeze flow
1x28x28
=>
 4x14x14
1x28x28...
Squeeze flow
Squeeze flow
Split flow
Split flow
2x14x14
2x14x14
2x14x14
=>
8x7x7
2x14x14...
Concatenate and reshape
Concatenate and reshape
diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/normalizing_flow_layout.png b/_notebooks/course_UvA-DL/09-normalizing-flows/normalizing_flow_layout.png new file mode 100644 index 0000000..9a164b9 Binary files /dev/null and b/_notebooks/course_UvA-DL/09-normalizing-flows/normalizing_flow_layout.png differ diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/uniform_flow.png b/_notebooks/course_UvA-DL/09-normalizing-flows/uniform_flow.png new file mode 100644 index 0000000..34a08cf Binary files /dev/null and b/_notebooks/course_UvA-DL/09-normalizing-flows/uniform_flow.png differ diff --git a/_notebooks/course_UvA-DL/09-normalizing-flows/vanilla_flow.svg b/_notebooks/course_UvA-DL/09-normalizing-flows/vanilla_flow.svg new file mode 100644 index 0000000..6220752 --- /dev/null +++ b/_notebooks/course_UvA-DL/09-normalizing-flows/vanilla_flow.svg @@ -0,0 +1,3 @@ + + +
(Variational) Dequantization
(Variational) Dequantizati...
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
Coupling layer
p(x)
p(x)
p(z)
p(z)
1x28x28
1x28x28
1x28x28
1x28x28
diff --git a/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/.meta.yml b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/.meta.yml new file mode 100644 index 0000000..ac181f2 --- /dev/null +++ b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/.meta.yml @@ -0,0 +1,23 @@ +title: "Tutorial 10: Autoregressive Image Modeling" +author: Phillip Lippe +created: 2021-07-12 +updated: 2023-03-14 +license: CC BY-SA +build: 0 +tags: + - Image +description: | + In this tutorial, we implement an autoregressive likelihood model for the task of image modeling. + Autoregressive models are naturally strong generative models that constitute one of the current + state-of-the-art architectures on likelihood-based image modeling, + and are also the basis for large language generation models such as GPT3. + We will focus on the PixelCNN architecture in this tutorial, and apply it to MNIST modeling. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - GPU diff --git a/docs/_static/images/course_UvA-DL/10-autoregressive-image-modeling.jpg b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/10-autoregressive-image-modeling.jpg rename to _notebooks/course_UvA-DL/10-autoregressive-image-modeling/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/Autoregressive_Image_Modeling.py b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/Autoregressive_Image_Modeling.py new file mode 100644 index 0000000..5adb928 --- /dev/null +++ b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/Autoregressive_Image_Modeling.py @@ -0,0 +1,977 @@ +# %% [markdown] +#
+# +# Similar to the language generation you have seen in assignment 2, autoregressive models work on images by modeling the likelihood of a pixel given all previous ones. +# For instance, in the picture below, we model the pixel $x_i$ as a conditional probability distribution +# based on all previous (here blue) pixels (figure credit - [Aaron van den Oord et al. ](https://arxiv.org/abs/1601.06759)): +# +#
+# +# Generally, autoregressive model over high-dimensional data $\mathbf{x}$ factor the joint distribution as the following product of conditionals: +# +# $$p(\mathbf{x})=p(x_1, ..., x_n)=\prod_{i=1}^{n} p(x_i|x_1,...,x_{i-1})$$ +# +# Learning these conditionals is often much simpler than learning the joint distribution $p(\mathbf{x})$ all together. +# However, disadvantages of autoregressive models include slow sampling, especially for large images, +# as we need height-times-width forward passes through the model. +# In addition, for some applications, we require a latent space as modeled in VAEs and Normalizing Flows. +# For instance, in autoregressive models, we cannot interpolate between two images because of the lack of a latent representation. +# We will explore and discuss these benefits and drawbacks alongside with our implementation. +# +# Our implementation will focus on the [PixelCNN](https://arxiv.org/pdf/1606.05328.pdf) [2] model which has been discussed in detail in the lecture. +# Most current SOTA models use PixelCNN as their fundamental architecture, +# and various additions have been proposed to improve the performance +# (e.g. [PixelCNN++](https://arxiv.org/pdf/1701.05517.pdf) and [PixelSNAIL](http://proceedings.mlr.press/v80/chen18h/chen18h.pdf)). +# Hence, implementing PixelCNN is a good starting point for our short tutorial. +# +# First of all, we need to import our standard libraries. Similarly as in +# the last couple of tutorials, we will use [PyTorch +# Lightning](https://lightning.ai/docs/pytorch/stable/) here as +# well. + +# %% + +import math +import os +import urllib.request +from urllib.error import HTTPError + +import lightning as L + +# Imports for plotting +import matplotlib.pyplot as plt +import matplotlib_inline.backend_inline +import numpy as np +import seaborn as sns +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data as data +import torchvision +from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint +from matplotlib.colors import to_rgb +from torch import Tensor +from torchvision import transforms +from torchvision.datasets import MNIST +from tqdm.notebook import tqdm + +plt.set_cmap("cividis") +# %matplotlib inline +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export + +# Path to the folder where the datasets are/should be downloaded (e.g. MNIST) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/tutorial12") + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.determinstic = True +torch.backends.cudnn.benchmark = False + +# Fetching the device that will be used throughout this notebook +device = torch.device("cpu") if not torch.cuda.is_available() else torch.device("cuda:0") +print("Using device", device) + +# %% [markdown] +# We again provide a pretrained model, which is downloaded below: + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial12/" +# Files to download +pretrained_files = ["PixelCNN.ckpt"] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print("Downloading %s..." % file_url) + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# Similar to the Normalizing Flows in Tutorial 11, we will work on the +# MNIST dataset and use 8-bits per pixel (values between 0 and 255). The +# dataset is loaded below: + + +# %% +# Convert images from 0-1 to 0-255 (integers). We use the long datatype as we will use the images as labels as well +def discretize(sample): + return (sample * 255).to(torch.long) + + +# Transformations applied on each image => only make them a tensor +transform = transforms.Compose([transforms.ToTensor(), discretize]) + +# Loading the training dataset. We need to split it into a training and validation part +train_dataset = MNIST(root=DATASET_PATH, train=True, transform=transform, download=True) +L.seed_everything(42) +train_set, val_set = torch.utils.data.random_split(train_dataset, [50000, 10000]) + +# Loading the test set +test_set = MNIST(root=DATASET_PATH, train=False, transform=transform, download=True) + +# We define a set of data loaders that we can use for various purposes later. +train_loader = data.DataLoader(train_set, batch_size=128, shuffle=True, drop_last=True, pin_memory=True, num_workers=4) +val_loader = data.DataLoader(val_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4) +test_loader = data.DataLoader(test_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4) + +# %% [markdown] +# A good practice is to always visualize some data examples to get an intuition of the data: + + +# %% +def show_imgs(imgs): + num_imgs = imgs.shape[0] if isinstance(imgs, Tensor) else len(imgs) + nrow = min(num_imgs, 4) + ncol = int(math.ceil(num_imgs / nrow)) + imgs = torchvision.utils.make_grid(imgs, nrow=nrow, pad_value=128) + imgs = imgs.clamp(min=0, max=255) + np_imgs = imgs.cpu().numpy() + plt.figure(figsize=(1.5 * nrow, 1.5 * ncol)) + plt.imshow(np.transpose(np_imgs, (1, 2, 0)), interpolation="nearest") + plt.axis("off") + plt.show() + plt.close() + + +show_imgs([train_set[i][0] for i in range(8)]) + +# %% [markdown] +# ## Masked autoregressive convolutions +# +# The core module of PixelCNN is its masked convolutions. +# In contrast to language models, we don't apply an LSTM on each pixel one-by-one. +# This would be inefficient because images are grids instead of sequences. +# Thus, it is better to rely on convolutions that have shown great success in deep CNN classification models. +# +# Nevertheless, we cannot just apply standard convolutions without any changes. +# Remember that during training of autoregressive models, we want to use teacher forcing which both helps the model training, and significantly reduces the time needed for training. +# For image modeling, teacher forcing is implemented by using a training image as input to the model, and we want to obtain as output the prediction for each pixel based on *only* its predecessors. +# Thus, we need to ensure that the prediction for a specific pixel can only be influenced by its predecessors and not by its own value or any "future" pixels. +# For this, we apply convolutions with a mask. +# +# Which mask we use depends on the ordering of pixels we decide on, i.e. which is the first pixel we predict, +# which is the second one, etc. +# The most commonly used ordering is to denote the upper left pixel as the start pixel, +# and sort the pixels row by row, as shown in the visualization at the top of the tutorial. +# Thus, the second pixel is on the right of the first one (first row, second column), +# and once we reach the end of the row, we start in the second row, first column. +# If we now want to apply this to our convolutions, we need to ensure that the prediction of pixel 1 +# is not influenced by its own "true" input, and all pixels on its right and in any lower row. +# In convolutions, this means that we want to set those entries of the weight matrix to zero that take pixels on the right and below into account. +# As an example for a 5x5 kernel, see a mask below (figure credit - [Aaron van den Oord](https://arxiv.org/pdf/1606.05328.pdf)): +# +#
+# +# Before looking into the application of masked convolutions in PixelCNN +# in detail, let's first implement a module that allows us to apply an +# arbitrary mask to a convolution: + + +# %% +class MaskedConvolution(nn.Module): + def __init__(self, c_in, c_out, mask, **kwargs): + """Implements a convolution with mask applied on its weights. + + Args: + c_in: Number of input channels + c_out: Number of output channels + mask: Tensor of shape [kernel_size_H, kernel_size_W] with 0s where + the convolution should be masked, and 1s otherwise. + kwargs: Additional arguments for the convolution + """ + super().__init__() + # For simplicity: calculate padding automatically + kernel_size = (mask.shape[0], mask.shape[1]) + dilation = 1 if "dilation" not in kwargs else kwargs["dilation"] + padding = tuple(dilation * (kernel_size[i] - 1) // 2 for i in range(2)) + # Actual convolution + self.conv = nn.Conv2d(c_in, c_out, kernel_size, padding=padding, **kwargs) + + # Mask as buffer => it is no parameter but still a tensor of the module + # (must be moved with the devices) + self.register_buffer("mask", mask[None, None]) + + def forward(self, x): + self.conv.weight.data *= self.mask # Ensures zero's at masked positions + return self.conv(x) + + +# %% [markdown] +# ### Vertical and horizontal convolution stacks +# +# To build our own autoregressive image model, we could simply stack a few masked convolutions on top of each other. +# This was actually the case for the original PixelCNN model, discussed in the paper +# [Pixel Recurrent Neural Networks](https://arxiv.org/pdf/1601.06759.pdf), but this leads to a considerable issue. +# When sequentially applying a couple of masked convolutions, the receptive field of a pixel +# show to have a "blind spot" on the right upper side, as shown in the figure below +# (figure credit - [Aaron van den Oord et al. ](https://arxiv.org/pdf/1606.05328.pdf)): +# +#
+# +# Although a pixel should be able to take into account all other pixels above and left of it, +# a stack of masked convolutions does not allow us to look to the upper pixels on the right. +# This is because the features of the pixels above, which we use for convolution, +# do not contain any information of the pixels on the right of the same row. +# If they would, we would be "cheating" and actually looking into the future. +# To overcome this issue, van den Oord et. +# al [2] proposed to split the convolutions into a vertical and a horizontal stack. +# The vertical stack looks at all pixels above the current one, while the horizontal takes into account all on the left. +# While keeping both of them separate, we can actually look at the pixels on the right with the vertical stack without breaking any of our assumptions. +# The two convolutions are also shown in the figure above. +# +# Let us implement them here as follows: + + +# %% +class VerticalStackConvolution(MaskedConvolution): + def __init__(self, c_in, c_out, kernel_size=3, mask_center=False, **kwargs): + # Mask out all pixels below. For efficiency, we could also reduce the kernel + # size in height, but for simplicity, we stick with masking here. + mask = torch.ones(kernel_size, kernel_size) + mask[kernel_size // 2 + 1 :, :] = 0 + + # For the very first convolution, we will also mask the center row + if mask_center: + mask[kernel_size // 2, :] = 0 + + super().__init__(c_in, c_out, mask, **kwargs) + + +class HorizontalStackConvolution(MaskedConvolution): + def __init__(self, c_in, c_out, kernel_size=3, mask_center=False, **kwargs): + # Mask out all pixels on the left. Note that our kernel has a size of 1 + # in height because we only look at the pixel in the same row. + mask = torch.ones(1, kernel_size) + mask[0, kernel_size // 2 + 1 :] = 0 + + # For the very first convolution, we will also mask the center pixel + if mask_center: + mask[0, kernel_size // 2] = 0 + + super().__init__(c_in, c_out, mask, **kwargs) + + +# %% [markdown] +# Note that we have an input argument called `mask_center`. Remember that +# the input to the model is the actual input image. Hence, the very first +# convolution we apply cannot use the center pixel as input, but must be +# masked. All consecutive convolutions, however, should use the center +# pixel as we otherwise lose the features of the previous layer. Hence, +# the input argument `mask_center` is True for the very first +# convolutions, and False for all others. + +# %% [markdown] +# ### Visualizing the receptive field +# +# To validate our implementation of masked convolutions, we can visualize the receptive field we obtain with such convolutions. +# We should see that with increasing number of convolutional layers, the receptive field grows in both vertical and horizontal direction, without the issue of a blind spot. +# The receptive field can be empirically measured by backpropagating an arbitrary loss for the output features of a speicifc pixel with respect to the input. +# We implement this idea below, and visualize the receptive field below. + +# %% +inp_img = torch.zeros(1, 1, 11, 11) +inp_img.requires_grad_() + + +def show_center_recep_field(img, out): + """Calculates the gradients of the input with respect to the output center pixel, and visualizes the overall + receptive field. + + Args: + img: Input image for which we want to calculate the receptive field on. + out: Output features/loss which is used for backpropagation, and should be + the output of the network/computation graph. + """ + # Determine gradients + loss = out[0, :, img.shape[2] // 2, img.shape[3] // 2].sum() # L1 loss for simplicity + # Retain graph as we want to stack multiple layers and show the receptive field of all of them + loss.backward(retain_graph=True) + img_grads = img.grad.abs() + img.grad.fill_(0) # Reset grads + + # Plot receptive field + img = img_grads.squeeze().cpu().numpy() + fig, ax = plt.subplots(1, 2) + _ = ax[0].imshow(img) + ax[1].imshow(img > 0) + # Mark the center pixel in red if it doesn't have any gradients (should be + # the case for standard autoregressive models) + show_center = img[img.shape[0] // 2, img.shape[1] // 2] == 0 + if show_center: + center_pixel = np.zeros(img.shape + (4,)) + center_pixel[center_pixel.shape[0] // 2, center_pixel.shape[1] // 2, :] = np.array([1.0, 0.0, 0.0, 1.0]) + for i in range(2): + ax[i].axis("off") + if show_center: + ax[i].imshow(center_pixel) + ax[0].set_title("Weighted receptive field") + ax[1].set_title("Binary receptive field") + plt.show() + plt.close() + + +show_center_recep_field(inp_img, inp_img) + +# %% [markdown] +# Let's first visualize the receptive field of a horizontal convolution +# without the center pixel. We use a small, arbitrary input image +# ($11\times 11$ pixels), and calculate the loss for the center pixel. For +# simplicity, we initialize all weights with 1 and the bias with 0, and +# use a single channel. This is sufficient for our visualization purposes. + +# %% +horiz_conv = HorizontalStackConvolution(c_in=1, c_out=1, kernel_size=3, mask_center=True) +horiz_conv.conv.weight.data.fill_(1) +horiz_conv.conv.bias.data.fill_(0) +horiz_img = horiz_conv(inp_img) +show_center_recep_field(inp_img, horiz_img) + +# %% [markdown] +# The receptive field is shown in yellow, the center pixel in red, and all other pixels outside of the receptive field are dark blue. +# As expected, the receptive field of a single horizontal convolution with the center pixel masked and a $3\times3$ kernel is only the pixel on the left. +# If we use a larger kernel size, more pixels would be taken into account on the left. +# +# Next, let's take a look at the vertical convolution: + +# %% +vert_conv = VerticalStackConvolution(c_in=1, c_out=1, kernel_size=3, mask_center=True) +vert_conv.conv.weight.data.fill_(1) +vert_conv.conv.bias.data.fill_(0) +vert_img = vert_conv(inp_img) +show_center_recep_field(inp_img, vert_img) + +# %% [markdown] +# The vertical convolution takes all pixels above into account. Combining +# these two, we get the L-shaped receptive field of the original masked +# convolution: + +# %% +horiz_img = vert_img + horiz_img +show_center_recep_field(inp_img, horiz_img) + +# %% [markdown] +# If we stack multiple horizontal and vertical convolutions, we need to take two aspects into account: +# +# 1. +# The center should not be masked anymore for the following convolutions as the features at the pixel's position are already independent of its actual value. +# If it is hard to imagine why we can do this, just change the value below to `mask_center=True` and see what happens. +# 2. +# The vertical convolution is not allowed to work on features from the horizontal convolution. +# In the feature map of the horizontal convolutions, a pixel contains information about all of the "true" pixels on the left. +# If we apply a vertical convolution which also uses features from the right, we effectively expand our receptive field to the true input which we want to prevent. +# Thus, the feature maps can only be merged for the horizontal convolution. +# +# Using this, we can stack the convolutions in the following way. We have +# two feature streams: one for the vertical stack, and one for the +# horizontal stack. The horizontal convolutions can operate on the joint +# features of the previous horizontals and vertical convolutions, while +# the vertical stack only takes its own previous features as input. For a +# quick implementation, we can therefore sum the horizontal and vertical +# output features at each layer, and use those as final output features to +# calculate the loss on. An implementation of 4 consecutive layers is +# shown below. Note that we reuse the features from the other convolutions +# with `mask_center=True` from above. + +# %% +# Initialize convolutions with equal weight to all input pixels +horiz_conv = HorizontalStackConvolution(c_in=1, c_out=1, kernel_size=3, mask_center=False) +horiz_conv.conv.weight.data.fill_(1) +horiz_conv.conv.bias.data.fill_(0) +vert_conv = VerticalStackConvolution(c_in=1, c_out=1, kernel_size=3, mask_center=False) +vert_conv.conv.weight.data.fill_(1) +vert_conv.conv.bias.data.fill_(0) + +# We reuse our convolutions for the 4 layers here. Note that in a standard network, +# we don't do that, and instead learn 4 separate convolution. As this cell is only for +# visualization purposes, we reuse the convolutions for all layers. +for l_idx in range(4): + vert_img = vert_conv(vert_img) + horiz_img = horiz_conv(horiz_img) + vert_img + print("Layer %i" % (l_idx + 2)) + show_center_recep_field(inp_img, horiz_img) + +# %% [markdown] +# The receptive field above it visualized for the horizontal stack, which includes the features of the vertical convolutions. +# It grows over layers without any blind spot as we had before. +# The difference between "weighted" and "binary" receptive field is that for the latter, we check whether there are any gradients flowing back to this pixel. +# This indicates that the center pixel indeed can use information from this pixel. +# Nevertheless, due to the convolution weights, some pixels have a stronger effect on the prediction than others. +# This is visualized in the weighted receptive field by plotting the gradient magnitude for each pixel instead of a binary yes/no. +# +# +# Another receptive field we can check is the one for the vertical stack +# as the one above is for the horizontal stack. Let's visualize it below: + +# %% +show_center_recep_field(inp_img, vert_img) + +# %% [markdown] +# As we have discussed before, the vertical stack only looks at pixels above the one we want to predict. +# Hence, we can validate that our implementation works as we initially expected it to. +# As a final step, let's clean up the computation graph we still had kept +# in memory for the visualization of the receptive field: + +# %% +del inp_img, horiz_conv, vert_conv + +# %% [markdown] +# ## Gated PixelCNN +# +#
+# +# In the next step, we will use the masked convolutions to build a full autoregressive model, called Gated PixelCNN. +# The difference between the original PixelCNN and Gated PixelCNN is the use of separate horizontal and vertical stacks. +# However, in literature, you often see that people refer to the Gated PixelCNN simply as "PixelCNN". +# Hence, in the following, if we say "PixelCNN", we usually mean the gated version. +# What "Gated" refers to in the model name is explained next. +# +# ### Gated Convolutions +# +# For visualizing the receptive field, we assumed a very simplified stack of vertical and horizontal convolutions. +# Obviously, there are more sophisticated ways of doing it, and PixelCNN uses gated convolutions for this. +# Specifically, the Gated Convolution block in PixelCNN looks as follows +# (figure credit - [Aaron van den Oord et al. ](https://arxiv.org/pdf/1606.05328.pdf)): +# +#
+# +# The left path is the vertical stack (the $N\times N$ convolution is masked correspondingly), +# and the right path is the horizontal stack. +# Gated convolutions are implemented by having a twice as large output channel size, +# and combine them by a element-wise multiplication of $\tanh$ and a sigmoid. +# For a linear layer, we can express a gated activation unit as follows: +# +# $$\mathbf{y} = \tanh\left(\mathbf{W}_{f}\mathbf{x}\right)\odot\sigma\left(\mathbf{W}_{g}\mathbf{x}\right)$$ +# +# For simplicity, biases have been neglected and the linear layer split into two part, $\mathbf{W}_{f}$ and $\mathbf{W}_{g}$. +# This concept resembles the input and modulation gate in an LSTM, and has been used in many other architectures as well. +# The main motivation behind this gated activation is that it might allow to model more complex interactions and simplifies learning. +# But as in any other architecture, this is mostly a design choice and can be considered a hyperparameters. +# +# Besides the gated convolutions, we also see that the horizontal stack uses a residual connection while the vertical stack does not. +# This is because we use the output of the horizontal stack for prediction. +# Each convolution in the vertical stack also receives a strong gradient signal +# as it is only two $1\times 1$ convolutions away from the residual connection, +# and does not require another residual connection to all its earleri layers. +# +# The implementation in PyTorch is fairly straight forward for this block, +# because the visualization above gives us a computation graph to follow: + + +# %% +class GatedMaskedConv(nn.Module): + def __init__(self, c_in, **kwargs): + """Gated Convolution block implemented the computation graph shown above.""" + super().__init__() + self.conv_vert = VerticalStackConvolution(c_in, c_out=2 * c_in, **kwargs) + self.conv_horiz = HorizontalStackConvolution(c_in, c_out=2 * c_in, **kwargs) + self.conv_vert_to_horiz = nn.Conv2d(2 * c_in, 2 * c_in, kernel_size=1, padding=0) + self.conv_horiz_1x1 = nn.Conv2d(c_in, c_in, kernel_size=1, padding=0) + + def forward(self, v_stack, h_stack): + # Vertical stack (left) + v_stack_feat = self.conv_vert(v_stack) + v_val, v_gate = v_stack_feat.chunk(2, dim=1) + v_stack_out = torch.tanh(v_val) * torch.sigmoid(v_gate) + + # Horizontal stack (right) + h_stack_feat = self.conv_horiz(h_stack) + h_stack_feat = h_stack_feat + self.conv_vert_to_horiz(v_stack_feat) + h_val, h_gate = h_stack_feat.chunk(2, dim=1) + h_stack_feat = torch.tanh(h_val) * torch.sigmoid(h_gate) + h_stack_out = self.conv_horiz_1x1(h_stack_feat) + h_stack_out = h_stack_out + h_stack + + return v_stack_out, h_stack_out + + +# %% [markdown] +# ### Building the model +# +# Using the gated convolutions, we can now build our PixelCNN model. +# The architecture consists of multiple stacked GatedMaskedConv blocks, where we add an additional dilation factor to a few convolutions. +# This is used to increase the receptive field of the model and allows to take a larger context into accout during generation. +# As a reminder, dilation on a convolution works looks as follows +# (figure credit - [Vincent Dumoulin and Francesco Visin](https://arxiv.org/pdf/1603.07285.pdf)): +# +#
+# +# Note that the smaller output size is only because the animation assumes no padding. +# In our implementation, we will pad the input image correspondingly. +# Alternatively to dilated convolutions, we could downsample the input and use a encoder-decoder architecture as in PixelCNN++ [3]. +# This is especially beneficial if we want to build a very deep autoregressive model. +# Nonetheless, as we seek to train a reasonably small model, dilated convolutions are the more efficient option to use here. +# +# Below, we implement the PixelCNN model as a PyTorch Lightning module. +# Besides the stack of gated convolutions, we also have the initial +# horizontal and vertical convolutions which mask the center pixel, and a +# final $1\times 1$ convolution which maps the output features to class +# predictions. To determine the likelihood of a batch of images, we first +# create our initial features using the masked horizontal and vertical +# input convolution. Next, we forward the features through the stack of +# gated convolutions. Finally, we take the output features of the +# horizontal stack, and apply the $1\times 1$ convolution for +# classification. We use the bits per dimension metric for the likelihood, +# similarly to Tutorial 11 and assignment 3. + + +# %% +class PixelCNN(L.LightningModule): + def __init__(self, c_in, c_hidden): + super().__init__() + self.save_hyperparameters() + + # Initial convolutions skipping the center pixel + self.conv_vstack = VerticalStackConvolution(c_in, c_hidden, mask_center=True) + self.conv_hstack = HorizontalStackConvolution(c_in, c_hidden, mask_center=True) + # Convolution block of PixelCNN. We use dilation instead of downscaling + self.conv_layers = nn.ModuleList( + [ + GatedMaskedConv(c_hidden), + GatedMaskedConv(c_hidden, dilation=2), + GatedMaskedConv(c_hidden), + GatedMaskedConv(c_hidden, dilation=4), + GatedMaskedConv(c_hidden), + GatedMaskedConv(c_hidden, dilation=2), + GatedMaskedConv(c_hidden), + ] + ) + # Output classification convolution (1x1) + self.conv_out = nn.Conv2d(c_hidden, c_in * 256, kernel_size=1, padding=0) + + self.example_input_array = train_set[0][0][None] + + def forward(self, x): + """Forward image through model and return logits for each pixel. + + Args: + x: Image tensor with integer values between 0 and 255. + """ + # Scale input from 0 to 255 back to -1 to 1 + x = (x.float() / 255.0) * 2 - 1 + + # Initial convolutions + v_stack = self.conv_vstack(x) + h_stack = self.conv_hstack(x) + # Gated Convolutions + for layer in self.conv_layers: + v_stack, h_stack = layer(v_stack, h_stack) + # 1x1 classification convolution + # Apply ELU before 1x1 convolution for non-linearity on residual connection + out = self.conv_out(F.elu(h_stack)) + + # Output dimensions: [Batch, Classes, Channels, Height, Width] + out = out.reshape(out.shape[0], 256, out.shape[1] // 256, out.shape[2], out.shape[3]) + return out + + def calc_likelihood(self, x): + # Forward pass with bpd likelihood calculation + pred = self.forward(x) + nll = F.cross_entropy(pred, x, reduction="none") + bpd = nll.mean(dim=[1, 2, 3]) * np.log2(np.exp(1)) + return bpd.mean() + + @torch.no_grad() + def sample(self, img_shape, img=None): + """Sampling function for the autoregressive model. + + Args: + img_shape: Shape of the image to generate (B,C,H,W) + img (optional): If given, this tensor will be used as + a starting image. The pixels to fill + should be -1 in the input tensor. + """ + # Create empty image + if img is None: + img = torch.zeros(img_shape, dtype=torch.long).to(device) - 1 + # Generation loop + for h in tqdm(range(img_shape[2]), leave=False): + for w in range(img_shape[3]): + for c in range(img_shape[1]): + # Skip if not to be filled (-1) + if (img[:, c, h, w] != -1).all().item(): + continue + # For efficiency, we only have to input the upper part of the image + # as all other parts will be skipped by the masked convolutions anyways + pred = self.forward(img[:, :, : h + 1, :]) + probs = F.softmax(pred[:, :, c, h, w], dim=-1) + img[:, c, h, w] = torch.multinomial(probs, num_samples=1).squeeze(dim=-1) + return img + + def configure_optimizers(self): + optimizer = optim.Adam(self.parameters(), lr=1e-3) + scheduler = optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.99) + return [optimizer], [scheduler] + + def training_step(self, batch, batch_idx): + loss = self.calc_likelihood(batch[0]) + self.log("train_bpd", loss) + return loss + + def validation_step(self, batch, batch_idx): + loss = self.calc_likelihood(batch[0]) + self.log("val_bpd", loss) + + def test_step(self, batch, batch_idx): + loss = self.calc_likelihood(batch[0]) + self.log("test_bpd", loss) + + +# %% [markdown] +# To sample from the autoregressive model, we need to iterate over all dimensions of the input. +# We start with an empty image, and fill the pixels one by one, starting from the upper left corner. +# Note that as for predicting $x_i$, all pixels below it have no influence on the prediction. +# Hence, we can cut the image in height without changing the prediction while increasing efficiency. +# Nevertheless, all the loops in the sampling function already show that it will take us quite some time to sample. +# A lot of computation could be reused across loop iterations as those the features on the already predicted pixels will not change over iterations. +# Nevertheless, this takes quite some effort to implement, and is often not done in implementations because in the end, +# autoregressive sampling remains sequential and slow. +# Hence, we settle with the default implementation here. +# +# Before training the model, we can check the full receptive field of the model on an MNIST image of size $28\times 28$: + +# %% +test_model = PixelCNN(c_in=1, c_hidden=64) +inp = torch.zeros(1, 1, 28, 28) +inp.requires_grad_() +out = test_model(inp) +show_center_recep_field(inp, out.squeeze(dim=2)) +del inp, out, test_model + +# %% [markdown] +# The visualization shows that for predicting any pixel, we can take almost half of the image into account. +# However, keep in mind that this is the "theoretical" receptive field and not necessarily +# the [effective receptive field](https://arxiv.org/pdf/1701.04128.pdf), which is usually much smaller. +# For a stronger model, we should therefore try to increase the receptive +# field even further. Especially, for the pixel on the bottom right, the +# very last pixel, we would be allowed to take into account the whole +# image. However, our current receptive field only spans across 1/4 of the +# image. An encoder-decoder architecture can help with this, but it also +# shows that we require a much deeper, more complex network in +# autoregressive models than in VAEs or energy-based models. + +# %% [markdown] +# ### Training loop +# +# To train the model, we again can rely on PyTorch Lightning and write a +# function below for loading the pretrained model if it exists. To reduce +# the computational cost, we have saved the validation and test score in +# the checkpoint already: + + +# %% +def train_model(**kwargs): + # Create a PyTorch Lightning trainer with the generation callback + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, "PixelCNN"), + accelerator="auto", + devices=1, + max_epochs=150, + callbacks=[ + ModelCheckpoint(save_weights_only=True, mode="min", monitor="val_bpd"), + LearningRateMonitor("epoch"), + ], + ) + result = None + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "PixelCNN.ckpt") + if os.path.isfile(pretrained_filename): + print("Found pretrained model, loading...") + model = PixelCNN.load_from_checkpoint(pretrained_filename) + ckpt = torch.load(pretrained_filename, map_location=device) + result = ckpt.get("result", None) + else: + model = PixelCNN(**kwargs) + trainer.fit(model, train_loader, val_loader) + model = model.to(device) + + if result is None: + # Test best model on validation and test set + val_result = trainer.test(model, dataloaders=val_loader, verbose=False) + test_result = trainer.test(model, dataloaders=test_loader, verbose=False) + result = {"test": test_result, "val": val_result} + return model, result + + +# %% [markdown] +# Training the model is time consuming and we recommend using the provided pre-trained model for going through this notebook. +# However, feel free to play around with the hyperparameter like number of layers etc. +# if you want to get a feeling for those. +# +# When calling the training function with a pre-trained model, we automatically load it and print its test performance: + +# %% +model, result = train_model(c_in=1, c_hidden=64) +test_res = result["test"][0] +print( + "Test bits per dimension: %4.3fbpd" % (test_res["test_loss"] if "test_loss" in test_res else test_res["test_bpd"]) +) + +# %% [markdown] +# With a test performance of 0.809bpd, the PixelCNN significantly outperforms the normalizing flows we have seen in Tutorial 11. +# Considering image modeling as an autoregressive problem simplifies the learning process as predicting +# one pixel given the ground truth of all others is much easier than predicting all pixels at once. +# In addition, PixelCNN can explicitly predict the pixel values by a discrete softmax while +# Normalizing Flows have to learn transformations in continuous latent space. +# These two aspects allow the PixelCNN to achieve a notably better performance. +# +# To fully compare the models, let's also measure the number of parameters of the PixelCNN: + +# %% +num_params = sum(np.prod(param.shape) for param in model.parameters()) +print(f"Number of parameters: {num_params:,}") + +# %% [markdown] +# Compared to the multi-scale normalizing flows, the PixelCNN has considerably less parameters. +# Of course, the number of parameters depend on our hyperparameter choices. +# Nevertheless, in general, it can be said that autoregressive models +# require considerably less parameters than normalizing flows to reach +# good performance, based on the reasons stated above. Still, +# autoregressive models are much slower in sampling than normalizing +# flows, which limits their possible applications. + +# %% [markdown] +# ## Sampling +# +# One way of qualitatively analysing generative models is by looking at the actual samples. +# Let's therefore use our sampling function to generate a few digits: + +# %% +L.seed_everything(1) +samples = model.sample(img_shape=(16, 1, 28, 28)) +show_imgs(samples.cpu()) + +# %% [markdown] +# Most of the samples can be identified as digits, and overall we achieve a better quality than we had in normalizing flows. +# This goes along with the lower likelihood we achieved with autoregressive models. +# Nevertheless, we also see that there is still place for improvement +# as a considerable amount of samples cannot be identified (for example the first row). +# Deeper autoregressive models are expected to achieve better quality, +# as they can take more context into account for generating the pixels. +# +# Note that on Google Colab, you might see different results, specifically with a white line at the top. +# After some debugging, it seemed that the difference occurs inside the dilated convolution, +# as it gives different results for different batch sizes. +# However, it is hard to debug this further as it might be a bug of the installed PyTorch version on Google Colab. +# +# The trained model itself is not restricted to any specific image size. +# However, what happens if we actually sample a larger image than we had +# seen in our training dataset? Let's try below to sample images of size +# $64\times64$ instead of $28\times28$: + +# %% +L.seed_everything(1) +samples = model.sample(img_shape=(8, 1, 64, 64)) +show_imgs(samples.cpu()) + +# %% [markdown] +# The larger images show that changing the size of the image during testing confuses the model +# and generates abstract figures (you can sometimes spot a digit in the upper left corner). +# In addition, sampling for images of 64x64 pixels take more than a minute on a GPU. +# Clearly, autoregressive models cannot be scaled to large images without changing the sampling procedure such as with [forecasting](https://arxiv.org/abs/2002.09928). +# Our implementation is also not the most efficient as many computations can be stored and reused throughout the sampling process. +# Nevertheless, the sampling procedure stays sequential which is +# inherently slower than parallel generation like done in normalizing +# flows. + +# %% [markdown] +# ### Autocompletion +# +# One common application done with autoregressive models is +# auto-completing an image. As autoregressive models predict pixels one by +# one, we can set the first $N$ pixels to predefined values and check how +# the model completes the image. For implementing this, we just need to +# skip the iterations in the sampling loop that already have a value +# unequals -1. See above in our PyTorch Lightning module for the specific +# implementation. In the cell below, we randomly take three images from +# the training set, mask about the lower half of the image, and let the +# model autocomplete it. To see the diversity of samples, we do this 12 +# times for each image: + + +# %% +def autocomplete_image(img): + # Remove lower half of the image + img_init = img.clone() + img_init[:, 10:, :] = -1 + print("Original image and input image to sampling:") + show_imgs([img, img_init]) + # Generate 12 example completions + img_init = img_init.unsqueeze(dim=0).expand(12, -1, -1, -1).to(device) + L.seed_everything(1) + img_generated = model.sample(img_init.shape, img_init) + print("Autocompletion samples:") + show_imgs(img_generated) + + +for i in range(1, 4): + img = train_set[i][0] + autocomplete_image(img) + +# %% [markdown] +# For the first two digits (7 and 6), we see that the 12 samples all +# result in a shape which resemble the original digit. Nevertheless, there +# are some style difference in writing the 7, and some deformed sixes in +# the samples. When autocompleting the 9 below, we see that the model can +# fit multiple digits to it. We obtain diverse samples from 0, 3, 8 and 9. +# This shows that despite having no latent space, we can still obtain +# diverse samples from an autoregressive model. + +# %% [markdown] +# ### Visualization of the predictive distribution (softmax) +# +# Autoregressive models use a softmax over 256 values to predict the next pixel. +# This gives the model a large flexibility as the probabilities for each pixel value can be learned independently if necessary. +# However, the values are actually not independent because the values 32 and 33 are much closer than 32 and 255. +# In the following, we visualize the softmax distribution that the model predicts to gain insights how it has learned the relationships of close-by pixels. +# +# To do this, we first run the model on a batch of images and store the output softmax distributions: + +# %% +det_loader = data.DataLoader(train_set, batch_size=128, shuffle=False, drop_last=False) +imgs, _ = next(iter(det_loader)) +imgs = imgs.to(device) +with torch.no_grad(): + out = model(imgs) + out = F.softmax(out, dim=1) + mean_out = out.mean(dim=[0, 2, 3, 4]).cpu().numpy() + out = out.cpu().numpy() + +# %% [markdown] +# Before diving into the model, let's visualize the distribution of the pixel values in the whole dataset: + +# %% +sns.set() +plot_args = {"color": to_rgb("C0") + (0.5,), "edgecolor": "C0", "linewidth": 0.5, "width": 1.0} +plt.hist(imgs.view(-1).cpu().numpy(), bins=256, density=True, **plot_args) +plt.yscale("log") +plt.xticks([0, 64, 128, 192, 256]) +plt.show() +plt.close() + +# %% [markdown] +# As we would expect from the seen images, the pixel value 0 (black) is the dominant value, followed by a batch of values between 250 and 255. +# Note that we use a log scale on the y-axis due to the big imbalance in the dataset. +# Interestingly, the pixel values 64, 128 and 191 also stand out which is likely due to the quantization used during the creation of the dataset. +# For RGB images, we would also see two peaks around 0 and 255, +# but the values in between would be much more frequent than in MNIST +# (see Figure 1 in the [PixelCNN++](https://arxiv.org/pdf/1701.05517.pdf) for a visualization on CIFAR10). +# +# Next, we can visualize the distribution our model predicts (in average): + +# %% +plt.bar(np.arange(mean_out.shape[0]), mean_out, **plot_args) +plt.yscale("log") +plt.xticks([0, 64, 128, 192, 256]) +plt.show() +plt.close() + +# %% [markdown] +# This distribution is very close to the actual dataset distribution. +# This is in general a good sign, but we can see a slightly smoother histogram than above. +# +# Finally, to take a closer look at learned value relations, we can +# visualize the distribution for individual pixel predictions to get a +# better intuition. For this, we pick 4 random images and pixels, and +# visualize their distribution below: + +# %% +fig, ax = plt.subplots(2, 2, figsize=(10, 6)) +for i in range(4): + ax_sub = ax[i // 2][i % 2] + ax_sub.bar(np.arange(out.shape[1], dtype=np.int32), out[i + 4, :, 0, 14, 14], **plot_args) + ax_sub.set_yscale("log") + ax_sub.set_xticks([0, 64, 128, 192, 256]) +plt.show() +plt.close() + +# %% [markdown] +# Overall we see a very diverse set of distributions, with a usual peak +# for 0 and close to 1. However, the distributions in the first row show a +# potentially undesirable behavior. For instance, the value 242 has a +# 1000x lower likelihood than 243 although they are extremely close and +# can often not be distinguished. This shows that the model might have not +# generlized well over pixel values. The better solution to this problem +# is to use discrete logitics mixtures instead of a softmax distribution. +# A discrete logistic distribution can be imagined as discretized, binned +# Gaussians. Using a mixture of discrete logistics instead of a softmax +# introduces an inductive bias to the model to assign close-by values +# similar likelihoods. We can visualize a discrete logistic below: + +# %% +mu = Tensor([128]) +sigma = Tensor([2.0]) + + +def discrete_logistic(x, mu, sigma): + return torch.sigmoid((x + 0.5 - mu) / sigma) - torch.sigmoid((x - 0.5 - mu) / sigma) + + +x = torch.arange(256) +p = discrete_logistic(x, mu, sigma) + +# Visualization +plt.figure(figsize=(6, 3)) +plt.bar(x.numpy(), p.numpy(), **plot_args) +plt.xlim(96, 160) +plt.title("Discrete logistic distribution") +plt.xlabel("Pixel value") +plt.ylabel("Probability") +plt.show() +plt.close() + +# %% [markdown] +# Instead of the softmax, the model would output mean and standard +# deviations for the $K$ logistics we use in the mixture. This is one of +# the improvements in autoregressive models that PixelCNN++ [3] has +# introduced compared to the original PixelCNN. + +# %% [markdown] +# ## Conclusion +# +# In this tutorial, we have looked at autoregressive image modeling, and +# implemented the PixelCNN architecture. With the usage of masked +# convolutions, we are able to apply a convolutional network in which a +# pixel is only influenced by all its predecessors. Separating the masked +# convolution into a horizontal and vertical stack allowed us to remove +# the known blind spot on the right upper row of a pixel. In experiments, +# autoregressive models outperformed normalizing flows in terms of bits +# per dimension, but are much slower to sample from. Improvements, that we +# have not implemented ourselves here, are discrete logistic mixtures, a +# downsampling architecture, and changing the pixel order in a diagonal +# fashion (see PixelSNAIL). Overall, autoregressive models are another, +# strong family of generative models, which however are mostly used in +# sequence tasks because of their linear scaling in sampling time than +# quadratic as on images. + +# %% [markdown] +# ## References +# [1] van den Oord, A., et al. +# "Pixel Recurrent Neural Networks." +# arXiv preprint arXiv:1601.06759 (2016). +# [Link](https://arxiv.org/abs/1601.06759) +# +# [2] van den Oord, A., et al. +# "Conditional Image Generation with PixelCNN Decoders." +# In Advances in Neural Information Processing Systems 29, pp. +# 4790–4798 (2016). +# [Link](http://papers.nips.cc/paper/6527-conditional-image-generation-with-pixelcnn-decoders.pdf) +# +# [3] Salimans, Tim, et al. +# "PixelCNN++: Improving the PixelCNN with Discretized Logistic Mixture Likelihood and Other Modifications." +# arXiv preprint arXiv:1701.05517 (2017). +# [Link](https://arxiv.org/abs/1701.05517) diff --git a/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/PixelCNN_GatedConv.svg b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/PixelCNN_GatedConv.svg new file mode 100644 index 0000000..2104663 --- /dev/null +++ b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/PixelCNN_GatedConv.svg @@ -0,0 +1,2195 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/autoregressive_image_modeling.svg b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/autoregressive_image_modeling.svg new file mode 100644 index 0000000..8584a8b --- /dev/null +++ b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/autoregressive_image_modeling.svg @@ -0,0 +1,961 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/masked_convolution.svg b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/masked_convolution.svg new file mode 100644 index 0000000..925dc48 --- /dev/null +++ b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/masked_convolution.svg @@ -0,0 +1,923 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/pixelcnn_blind_spot.svg b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/pixelcnn_blind_spot.svg new file mode 100644 index 0000000..18ade2f --- /dev/null +++ b/_notebooks/course_UvA-DL/10-autoregressive-image-modeling/pixelcnn_blind_spot.svg @@ -0,0 +1,1091 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/11-vision-transformer/.meta.yml b/_notebooks/course_UvA-DL/11-vision-transformer/.meta.yml new file mode 100644 index 0000000..171d877 --- /dev/null +++ b/_notebooks/course_UvA-DL/11-vision-transformer/.meta.yml @@ -0,0 +1,23 @@ +title: "Tutorial 11: Vision Transformers" +author: Phillip Lippe +created: 2021-08-21 +updated: 2023-03-14 +license: CC BY-SA +description: | + In this tutorial, we will take a closer look at a recent new trend: Transformers for Computer Vision. + Since [Alexey Dosovitskiy et al.](https://openreview.net/pdf?id=YicbFdNTTy) successfully applied a Transformer on a variety of image recognition benchmarks, there have been an incredible amount of follow-up works showing that CNNs might not be optimal architecture for Computer Vision anymore. + But how do Vision Transformers work exactly, and what benefits and drawbacks do they offer in contrast to CNNs? + We will answer these questions by implementing a Vision Transformer ourselves, and train it on the popular, small dataset CIFAR10. + We will compare these results to popular convolutional architectures such as Inception, ResNet and DenseNet. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +tags: + - Image +requirements: + - torchvision + - matplotlib + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/11-vision-transformer.jpg b/_notebooks/course_UvA-DL/11-vision-transformer/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/11-vision-transformer.jpg rename to _notebooks/course_UvA-DL/11-vision-transformer/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/11-vision-transformer/Vision_Transformer.py b/_notebooks/course_UvA-DL/11-vision-transformer/Vision_Transformer.py new file mode 100644 index 0000000..9d8cef8 --- /dev/null +++ b/_notebooks/course_UvA-DL/11-vision-transformer/Vision_Transformer.py @@ -0,0 +1,530 @@ +# %% [markdown] +#
+# Let's start with importing our standard set of libraries. + +# %% +import os +import urllib.request +from urllib.error import HTTPError + +import lightning as L +import matplotlib +import matplotlib.pyplot as plt +import matplotlib_inline.backend_inline +import seaborn as sns +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data as data +import torchvision +from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint +from torchvision import transforms +from torchvision.datasets import CIFAR10 + +plt.set_cmap("cividis") +# %matplotlib inline +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +matplotlib.rcParams["lines.linewidth"] = 2.0 +sns.reset_orig() + +# %load_ext tensorboard + +# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/VisionTransformers/") + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu") +print("Device:", device) + +# %% [markdown] +# We provide a pre-trained Vision Transformer which we download in the next cell. +# However, Vision Transformers can be relatively quickly trained on CIFAR10 with an overall training time of less than an hour on an NVIDIA TitanRTX. +# Feel free to experiment with training your own Transformer once you went through the whole notebook. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/" +# Files to download +pretrained_files = [ + "tutorial15/ViT.ckpt", + "tutorial15/tensorboards/ViT/events.out.tfevents.ViT", + "tutorial5/tensorboards/ResNet/events.out.tfevents.resnet", +] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name.split("/", 1)[1]) + if "/" in file_name.split("/", 1)[1]: + os.makedirs(file_path.rsplit("/", 1)[0], exist_ok=True) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print("Downloading %s..." % file_url) + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# We load the CIFAR10 dataset below. +# We use the same setup of the datasets and data augmentations as for the CNNs in Tutorial 5 to keep a fair comparison. +# The constants in the `transforms.Normalize` correspond to the values +# that scale and shift the data to a zero mean and standard deviation of +# one. + +# %% +test_transform = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize([0.49139968, 0.48215841, 0.44653091], [0.24703223, 0.24348513, 0.26158784]), + ] +) +# For training, we add some augmentation. Networks are too powerful and would overfit. +train_transform = transforms.Compose( + [ + transforms.RandomHorizontalFlip(), + transforms.RandomResizedCrop((32, 32), scale=(0.8, 1.0), ratio=(0.9, 1.1)), + transforms.ToTensor(), + transforms.Normalize([0.49139968, 0.48215841, 0.44653091], [0.24703223, 0.24348513, 0.26158784]), + ] +) +# Loading the training dataset. We need to split it into a training and validation part +# We need to do a little trick because the validation set should not use the augmentation. +train_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=train_transform, download=True) +val_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=test_transform, download=True) +L.seed_everything(42) +train_set, _ = torch.utils.data.random_split(train_dataset, [45000, 5000]) +L.seed_everything(42) +_, val_set = torch.utils.data.random_split(val_dataset, [45000, 5000]) + +# Loading the test set +test_set = CIFAR10(root=DATASET_PATH, train=False, transform=test_transform, download=True) + +# We define a set of data loaders that we can use for various purposes later. +train_loader = data.DataLoader(train_set, batch_size=128, shuffle=True, drop_last=True, pin_memory=True, num_workers=4) +val_loader = data.DataLoader(val_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4) +test_loader = data.DataLoader(test_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4) + +# Visualize some examples +NUM_IMAGES = 4 +CIFAR_images = torch.stack([val_set[idx][0] for idx in range(NUM_IMAGES)], dim=0) +img_grid = torchvision.utils.make_grid(CIFAR_images, nrow=4, normalize=True, pad_value=0.9) +img_grid = img_grid.permute(1, 2, 0) + +plt.figure(figsize=(8, 8)) +plt.title("Image examples of the CIFAR10 dataset") +plt.imshow(img_grid) +plt.axis("off") +plt.show() +plt.close() + +# %% [markdown] +# ## Transformers for image classification +# +# Transformers have been originally proposed to process sets since it is a permutation-equivariant architecture, i.e., producing the same output permuted if the input is permuted. +# To apply Transformers to sequences, we have simply added a positional encoding to the input feature vectors, and the model learned by itself what to do with it. +# So, why not do the same thing on images? +# This is exactly what [Alexey Dosovitskiy et al. ](https://openreview.net/pdf?id=YicbFdNTTy) proposed in their paper "An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale". +# Specifically, the Vision Transformer is a model for image classification that views images as sequences of smaller patches. +# As a preprocessing step, we split an image of, for example, $48\times 48$ pixels into 9 $16\times 16$ patches. +# Each of those patches is considered to be a "word"/"token", and projected to a feature space. +# With adding positional encodings and a token for classification on top, we can apply a Transformer as usual to this sequence and start training it for our task. +# A nice GIF visualization of the architecture is shown below (figure credit - [Phil Wang](https://github.com/lucidrains/vit-pytorch/blob/main/images/vit.gif)): +# +#
+# +# We will walk step by step through the Vision Transformer, and implement all parts by ourselves. +# First, let's implement the image preprocessing: an image of size $N\times N$ has to be split into $(N/M)^2$ patches of size $M\times M$. +# These represent the input words to the Transformer. + + +# %% +def img_to_patch(x, patch_size, flatten_channels=True): + """ + Inputs: + x - Tensor representing the image of shape [B, C, H, W] + patch_size - Number of pixels per dimension of the patches (integer) + flatten_channels - If True, the patches will be returned in a flattened format + as a feature vector instead of a image grid. + """ + B, C, H, W = x.shape + x = x.reshape(B, C, H // patch_size, patch_size, W // patch_size, patch_size) + x = x.permute(0, 2, 4, 1, 3, 5) # [B, H', W', C, p_H, p_W] + x = x.flatten(1, 2) # [B, H'*W', C, p_H, p_W] + if flatten_channels: + x = x.flatten(2, 4) # [B, H'*W', C*p_H*p_W] + return x + + +# %% [markdown] +# Let's take a look at how that works for our CIFAR examples above. +# For our images of size $32\times 32$, we choose a patch size of 4. +# Hence, we obtain sequences of 64 patches of size $4\times 4$. +# We visualize them below: + +# %% +img_patches = img_to_patch(CIFAR_images, patch_size=4, flatten_channels=False) + +fig, ax = plt.subplots(CIFAR_images.shape[0], 1, figsize=(14, 3)) +fig.suptitle("Images as input sequences of patches") +for i in range(CIFAR_images.shape[0]): + img_grid = torchvision.utils.make_grid(img_patches[i], nrow=64, normalize=True, pad_value=0.9) + img_grid = img_grid.permute(1, 2, 0) + ax[i].imshow(img_grid) + ax[i].axis("off") +plt.show() +plt.close() + +# %% [markdown] +# Compared to the original images, it is much harder to recognize the objects from those patch lists now. +# Still, this is the input we provide to the Transformer for classifying the images. +# The model has to learn itself how it has to combine the patches to recognize the objects. +# The inductive bias in CNNs that an image is grid of pixels, is lost in this input format. +# +# After we have looked at the preprocessing, we can now start building the Transformer model. +# Since we have discussed the fundamentals of Multi-Head Attention in [Tutorial 6](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial6/Transformers_and_MHAttention.html), we will use the PyTorch module `nn.MultiheadAttention` ([docs](https://pytorch.org/docs/stable/generated/torch.nn.MultiheadAttention.html?highlight=multihead#torch.nn.MultiheadAttention)) here. +# Further, we use the Pre-Layer Normalization version of the Transformer blocks proposed by [Ruibin Xiong et al. ](http://proceedings.mlr.press/v119/xiong20b/xiong20b.pdf) in 2020. +# The idea is to apply Layer Normalization not in between residual blocks, but instead as a first layer in the residual blocks. +# This reorganization of the layers supports better gradient flow and removes the necessity of a warm-up stage. +# A visualization of the difference between the standard Post-LN and the Pre-LN version is shown below. +# +#
+# +# The implementation of the Pre-LN attention block looks as follows: + + +# %% +class AttentionBlock(nn.Module): + def __init__(self, embed_dim, hidden_dim, num_heads, dropout=0.0): + """ + Inputs: + embed_dim - Dimensionality of input and attention feature vectors + hidden_dim - Dimensionality of hidden layer in feed-forward network + (usually 2-4x larger than embed_dim) + num_heads - Number of heads to use in the Multi-Head Attention block + dropout - Amount of dropout to apply in the feed-forward network + """ + super().__init__() + + self.layer_norm_1 = nn.LayerNorm(embed_dim) + self.attn = nn.MultiheadAttention(embed_dim, num_heads) + self.layer_norm_2 = nn.LayerNorm(embed_dim) + self.linear = nn.Sequential( + nn.Linear(embed_dim, hidden_dim), + nn.GELU(), + nn.Dropout(dropout), + nn.Linear(hidden_dim, embed_dim), + nn.Dropout(dropout), + ) + + def forward(self, x): + inp_x = self.layer_norm_1(x) + x = x + self.attn(inp_x, inp_x, inp_x)[0] + x = x + self.linear(self.layer_norm_2(x)) + return x + + +# %% [markdown] +# Now we have all modules ready to build our own Vision Transformer. +# Besides the Transformer encoder, we need the following modules: +# +# * A **linear projection** layer that maps the input patches to a feature vector of larger size. +# It is implemented by a simple linear layer that takes each $M\times M$ patch independently as input. +# * A **classification token** that is added to the input sequence. +# We will use the output feature vector of the classification token (CLS token in short) for determining the classification prediction. +# * Learnable **positional encodings** that are added to the tokens before being processed by the Transformer. +# Those are needed to learn position-dependent information, and convert the set to a sequence. +# Since we usually work with a fixed resolution, we can learn the positional encodings instead of having the pattern of sine and cosine functions. +# * A **MLP head** that takes the output feature vector of the CLS token, and maps it to a classification prediction. +# This is usually implemented by a small feed-forward network or even a single linear layer. +# +# With those components in mind, let's implement the full Vision Transformer below: + + +# %% +class VisionTransformer(nn.Module): + def __init__( + self, + embed_dim, + hidden_dim, + num_channels, + num_heads, + num_layers, + num_classes, + patch_size, + num_patches, + dropout=0.0, + ): + """ + Inputs: + embed_dim - Dimensionality of the input feature vectors to the Transformer + hidden_dim - Dimensionality of the hidden layer in the feed-forward networks + within the Transformer + num_channels - Number of channels of the input (3 for RGB) + num_heads - Number of heads to use in the Multi-Head Attention block + num_layers - Number of layers to use in the Transformer + num_classes - Number of classes to predict + patch_size - Number of pixels that the patches have per dimension + num_patches - Maximum number of patches an image can have + dropout - Amount of dropout to apply in the feed-forward network and + on the input encoding + """ + super().__init__() + + self.patch_size = patch_size + + # Layers/Networks + self.input_layer = nn.Linear(num_channels * (patch_size**2), embed_dim) + self.transformer = nn.Sequential( + *(AttentionBlock(embed_dim, hidden_dim, num_heads, dropout=dropout) for _ in range(num_layers)) + ) + self.mlp_head = nn.Sequential(nn.LayerNorm(embed_dim), nn.Linear(embed_dim, num_classes)) + self.dropout = nn.Dropout(dropout) + + # Parameters/Embeddings + self.cls_token = nn.Parameter(torch.randn(1, 1, embed_dim)) + self.pos_embedding = nn.Parameter(torch.randn(1, 1 + num_patches, embed_dim)) + + def forward(self, x): + # Preprocess input + x = img_to_patch(x, self.patch_size) + B, T, _ = x.shape + x = self.input_layer(x) + + # Add CLS token and positional encoding + cls_token = self.cls_token.repeat(B, 1, 1) + x = torch.cat([cls_token, x], dim=1) + x = x + self.pos_embedding[:, : T + 1] + + # Apply Transforrmer + x = self.dropout(x) + x = x.transpose(0, 1) + x = self.transformer(x) + + # Perform classification prediction + cls = x[0] + out = self.mlp_head(cls) + return out + + +# %% [markdown] +# Finally, we can put everything into a PyTorch Lightning Module as usual. +# We use `torch.optim.AdamW` as the optimizer, which is Adam with a corrected weight decay implementation. +# Since we use the Pre-LN Transformer version, we do not need to use a learning rate warmup stage anymore. +# Instead, we use the same learning rate scheduler as the CNNs in our previous tutorial on image classification. + + +# %% +class ViT(L.LightningModule): + def __init__(self, model_kwargs, lr): + super().__init__() + self.save_hyperparameters() + self.model = VisionTransformer(**model_kwargs) + self.example_input_array = next(iter(train_loader))[0] + + def forward(self, x): + return self.model(x) + + def configure_optimizers(self): + optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr) + lr_scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[100, 150], gamma=0.1) + return [optimizer], [lr_scheduler] + + def _calculate_loss(self, batch, mode="train"): + imgs, labels = batch + preds = self.model(imgs) + loss = F.cross_entropy(preds, labels) + acc = (preds.argmax(dim=-1) == labels).float().mean() + + self.log("%s_loss" % mode, loss) + self.log("%s_acc" % mode, acc) + return loss + + def training_step(self, batch, batch_idx): + loss = self._calculate_loss(batch, mode="train") + return loss + + def validation_step(self, batch, batch_idx): + self._calculate_loss(batch, mode="val") + + def test_step(self, batch, batch_idx): + self._calculate_loss(batch, mode="test") + + +# %% [markdown] +# ## Experiments +# +# Commonly, Vision Transformers are applied to large-scale image classification benchmarks such as ImageNet to leverage their full potential. +# However, here we take a step back and ask: can Vision Transformer also succeed on classical, small benchmarks such as CIFAR10? +# To find this out, we train a Vision Transformer from scratch on the CIFAR10 dataset. +# Let's first create a training function for our PyTorch Lightning module +# which also loads the pre-trained model if you have downloaded it above. + + +# %% +def train_model(**kwargs): + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, "ViT"), + accelerator="auto", + devices=1, + max_epochs=180, + callbacks=[ + ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc"), + LearningRateMonitor("epoch"), + ], + ) + trainer.logger._log_graph = True # If True, we plot the computation graph in tensorboard + trainer.logger._default_hp_metric = None # Optional logging argument that we don't need + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "ViT.ckpt") + if os.path.isfile(pretrained_filename): + print("Found pretrained model at %s, loading..." % pretrained_filename) + # Automatically loads the model with the saved hyperparameters + model = ViT.load_from_checkpoint(pretrained_filename) + else: + L.seed_everything(42) # To be reproducable + model = ViT(**kwargs) + trainer.fit(model, train_loader, val_loader) + # Load best checkpoint after training + model = ViT.load_from_checkpoint(trainer.checkpoint_callback.best_model_path) + + # Test best model on validation and test set + val_result = trainer.test(model, dataloaders=val_loader, verbose=False) + test_result = trainer.test(model, dataloaders=test_loader, verbose=False) + result = {"test": test_result[0]["test_acc"], "val": val_result[0]["test_acc"]} + + return model, result + + +# %% [markdown] +# Now, we can already start training our model. +# As seen in our implementation, we have couple of hyperparameter that we have to choose. +# When creating this notebook, we have performed a small grid search over hyperparameters and listed the best hyperparameters in the cell below. +# Nevertheless, it is worth to discuss the influence that each hyperparameter has, and what intuition we have for choosing its value. +# +# First, let's consider the patch size. +# The smaller we make the patches, the longer the input sequences to the Transformer become. +# While in general, this allows the Transformer to model more complex functions, it requires a longer computation time due to its quadratic memory usage in the attention layer. +# Furthermore, small patches can make the task more difficult since the Transformer has to learn which patches are close-by, and which are far away. +# We experimented with patch sizes of 2, 4 and 8 which gives us the input sequence lengths of 256, 64, and 16 respectively. +# We found 4 to result in the best performance, and hence pick it below. +# +# Next, the embedding and hidden dimensionality have a similar impact to a Transformer as to an MLP. +# The larger the sizes, the more complex the model becomes, and the longer it takes to train. +# In Transformer however, we have one more aspect to consider: the query-key sizes in the Multi-Head Attention layers. +# Each key has the feature dimensionality of `embed_dim/num_heads`. +# Considering that we have an input sequence length of 64, a minimum reasonable size for the key vectors is 16 or 32. +# Lower dimensionalities can restrain the possible attention maps too much. +# We observed that more than 8 heads are not necessary for the Transformer, and therefore pick a embedding dimensionality of `256`. +# The hidden dimensionality in the feed-forward networks is usually 2-4x larger than the embedding dimensionality, and thus we pick `512`. +# +# Finally, the learning rate for Transformers is usually relatively small, and in papers, a common value to use is 3e-5. +# However, since we work with a smaller dataset and have a potentially easier task, we found that we are able to increase the learning rate to 3e-4 without any problems. +# To reduce overfitting, we use a dropout value of 0.2. +# Remember that we also use small image augmentations as regularization during training. +# +# Feel free to explore the hyperparameters yourself by changing the values below. +# In general, the Vision Transformer did not show to be too sensitive to +# the hyperparameter choices on the CIFAR10 dataset. + +# %% +model, results = train_model( + model_kwargs={ + "embed_dim": 256, + "hidden_dim": 512, + "num_heads": 8, + "num_layers": 6, + "patch_size": 4, + "num_channels": 3, + "num_patches": 64, + "num_classes": 10, + "dropout": 0.2, + }, + lr=3e-4, +) +print("ViT results", results) + +# %% [markdown] +# The Vision Transformer achieves a validation and test performance of about 75%. +# In comparison, almost all CNN architectures that we have tested in [Tutorial 5](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial5/Inception_ResNet_DenseNet.html) obtained a classification performance of around 90%. +# This is a considerable gap and shows that although Vision Transformers perform strongly on ImageNet with potential pretraining, they cannot come close to simple CNNs on CIFAR10 when being trained from scratch. +# The differences between a CNN and Transformer can be well observed in the training curves. +# Let's look at them in a tensorboard below: + +# %% +# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH! +# %tensorboard --logdir ../saved_models/tutorial15/tensorboards/ + +# %% [markdown] +#
+ +# %% [markdown] +# The tensorboard compares the Vision Transformer to a ResNet trained on CIFAR10. +# When looking at the training losses, we see that the ResNet learns much more quickly in the first iterations. +# While the learning rate might have an influence on the initial learning speed, we see the same trend in the validation accuracy. +# The ResNet achieves the best performance of the Vision Transformer after just 5 epochs (2000 iterations). +# Further, while the ResNet training loss and validation accuracy have a similar trend, the validation performance of the Vision Transformers only marginally changes after 10k iterations while the training loss has almost just started going down. +# Yet, the Vision Transformer is also able to achieve a close-to 100% accuracy on the training set. +# +# All those observed phenomenons can be explained with a concept that we have visited before: inductive biases. +# Convolutional Neural Networks have been designed with the assumption that images are translation invariant. +# Hence, we apply convolutions with shared filters across the image. +# Furthermore, a CNN architecture integrates the concept of distance in an image: two pixels that are close to each other are more related than two distant pixels. +# Local patterns are combined into larger patterns, until we perform our classification prediction. +# All those aspects are inductive biases of a CNN. +# In contrast, a Vision Transformer does not know which two pixels are close to each other, and which are far apart. +# It has to learn this information solely from the sparse learning signal of the classification task. +# This is a huge disadvantage when we have a small dataset since such information is crucial for generalizing to an unseen test dataset. +# With large enough datasets and/or good pre-training, a Transformer can learn this information without the need of inductive biases, and instead is more flexible than a CNN. +# Especially long-distance relations between local patterns can be difficult to process in CNNs, while in Transformers, all patches have the distance of one. +# This is why Vision Transformers are so strong on large-scale datasets +# such as ImageNet, but underperform a lot when being applied to a small +# dataset such as CIFAR10. + +# %% [markdown] +# ## Conclusion +# +# In this tutorial, we have implemented our own Vision Transformer from scratch and applied it on the task of image classification. +# Vision Transformers work by splitting an image into a sequence of smaller patches, use those as input to a standard Transformer encoder. +# While Vision Transformers achieved outstanding results on large-scale image recognition benchmarks such as ImageNet, they considerably underperform when being trained from scratch on small-scale datasets like CIFAR10. +# The reason is that in contrast to CNNs, Transformers do not have the inductive biases of translation invariance and the feature hierachy (i.e. larger patterns consist of many smaller patterns). +# However, these aspects can be learned when enough data is provided, or the model has been pre-trained on other large-scale tasks. +# Considering that Vision Transformers have just been proposed end of 2020, there is likely a lot more to come on Transformers for Computer Vision. +# +# +# ### References +# +# Dosovitskiy, Alexey, et al. +# "An image is worth 16x16 words: Transformers for image recognition at scale." +# International Conference on Representation Learning (2021). +# [link](https://arxiv.org/pdf/2010.11929.pdf) +# +# Chen, Xiangning, et al. +# "When Vision Transformers Outperform ResNets without Pretraining or Strong Data Augmentations." +# arXiv preprint arXiv:2106.01548 (2021). +# [link](https://arxiv.org/abs/2106.01548) +# +# Tolstikhin, Ilya, et al. +# "MLP-mixer: An all-MLP Architecture for Vision." +# arXiv preprint arXiv:2105.01601 (2021). +# [link](https://arxiv.org/abs/2105.01601) +# +# Xiong, Ruibin, et al. +# "On layer normalization in the transformer architecture." +# International Conference on Machine Learning. +# PMLR, 2020. +# [link](http://proceedings.mlr.press/v119/xiong20b/xiong20b.pdf) diff --git a/_notebooks/course_UvA-DL/11-vision-transformer/pre_layer_norm.svg b/_notebooks/course_UvA-DL/11-vision-transformer/pre_layer_norm.svg new file mode 100644 index 0000000..cb7fbd4 --- /dev/null +++ b/_notebooks/course_UvA-DL/11-vision-transformer/pre_layer_norm.svg @@ -0,0 +1,810 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/11-vision-transformer/tensorboard_screenshot.png b/_notebooks/course_UvA-DL/11-vision-transformer/tensorboard_screenshot.png new file mode 100644 index 0000000..626e930 Binary files /dev/null and b/_notebooks/course_UvA-DL/11-vision-transformer/tensorboard_screenshot.png differ diff --git a/_notebooks/course_UvA-DL/11-vision-transformer/vit_architecture.png b/_notebooks/course_UvA-DL/11-vision-transformer/vit_architecture.png new file mode 100644 index 0000000..446aa44 Binary files /dev/null and b/_notebooks/course_UvA-DL/11-vision-transformer/vit_architecture.png differ diff --git a/_notebooks/course_UvA-DL/12-meta-learning/.meta.yml b/_notebooks/course_UvA-DL/12-meta-learning/.meta.yml new file mode 100644 index 0000000..18f459d --- /dev/null +++ b/_notebooks/course_UvA-DL/12-meta-learning/.meta.yml @@ -0,0 +1,29 @@ +title: "Tutorial 12: Meta-Learning - Learning to Learn" +author: Phillip Lippe +created: 2021-08-21 +updated: 2023-03-14 +license: CC BY-SA +tags: + - Few-shot-learning + - MAML + - ProtoNet +description: | + In this tutorial, we will discuss algorithms that learn models which can quickly adapt to new classes and/or tasks with few samples. + This area of machine learning is called _Meta-Learning_ aiming at "learning to learn". + Learning from very few examples is a natural task for humans. In contrast to current deep learning models, we need to see only a few examples of a police car or firetruck to recognize them in daily traffic. + This is crucial ability since in real-world application, it is rarely the case that the data stays static and does not change over time. + For example, an object detection system for mobile phones trained on data from 2000 will have troubles detecting today's common mobile phones, and thus, needs to adapt to new data without excessive label effort. + The optimization techniques we have discussed so far struggle with this because they only aim at obtaining a good performance on a test set that had similar data. + However, what if the test set has classes that we do not have in the training set? + Or what if we want to test the model on a completely different task? + We will discuss and implement three common Meta-Learning algorithms for such situations. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/12-meta-learning.jpg b/_notebooks/course_UvA-DL/12-meta-learning/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/12-meta-learning.jpg rename to _notebooks/course_UvA-DL/12-meta-learning/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/12-meta-learning/MAML_algorithm.svg b/_notebooks/course_UvA-DL/12-meta-learning/MAML_algorithm.svg new file mode 100644 index 0000000..1812df4 --- /dev/null +++ b/_notebooks/course_UvA-DL/12-meta-learning/MAML_algorithm.svg @@ -0,0 +1,5318 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/12-meta-learning/MAML_figure.svg b/_notebooks/course_UvA-DL/12-meta-learning/MAML_figure.svg new file mode 100644 index 0000000..9e5838b --- /dev/null +++ b/_notebooks/course_UvA-DL/12-meta-learning/MAML_figure.svg @@ -0,0 +1,935 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/12-meta-learning/Meta_Learning.py b/_notebooks/course_UvA-DL/12-meta-learning/Meta_Learning.py new file mode 100644 index 0000000..5fdd66a --- /dev/null +++ b/_notebooks/course_UvA-DL/12-meta-learning/Meta_Learning.py @@ -0,0 +1,1332 @@ +# %% [markdown] +#
+# Meta-Learning offers solutions to these situations, and we will discuss three popular algorithms: __Prototypical Networks__ ([Snell et al., 2017](https://arxiv.org/pdf/1703.05175.pdf)), __Model-Agnostic Meta-Learning / MAML__ ([Finn et al., 2017](http://proceedings.mlr.press/v70/finn17a.html)), and __Proto-MAML__ ([Triantafillou et al., 2020](https://openreview.net/pdf?id=rkgAGAVKPr)). +# We will focus on the task of few-shot classification where the training and test set have distinct sets of classes. +# For instance, we would train the model on the binary classifications of cats-birds and flowers-bikes, but during test time, the model would need to learn from 4 examples each the difference between dogs and otters, two classes we have not seen during training (Figure credit - [Lilian Weng](https://lilianweng.github.io/lil-log/2018/11/30/meta-learning.html)). +# +#
+# +# A different setup, which is very common in Reinforcement Learning and recently Natural Language Processing, is to aim at few-shot learning of a completely new task. +# For example, an robot agent that learned to run, jump and pick up boxes, should quickly adapt to collecting and stacking boxes. +# In NLP, we can think of a model which was trained sentiment classification, hatespeech detection and sarcasm classification, to adapt to classifying the emotion of a text. +# All methods we will discuss in this notebook can be easily applied to these settings since we only use a different definition of a 'task'. +# For few-shot classification, we consider a task to distinguish between $M$ novel classes. +# Here, we would not only have novel classes, but also a completely different dataset. +# +# First of all, let's start with importing our standard libraries. We will again be using PyTorch Lightning. + +# %% +import json +import os +import random +import urllib.request +from collections import defaultdict +from copy import deepcopy +from statistics import mean, stdev +from urllib.error import HTTPError + +import lightning as L +import matplotlib +import matplotlib.pyplot as plt +import matplotlib_inline.backend_inline +import numpy as np +import seaborn as sns +import torch +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data as data +import torchvision +from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint +from PIL import Image +from torchvision import transforms +from torchvision.datasets import CIFAR100, SVHN +from tqdm.auto import tqdm + +plt.set_cmap("cividis") +# %matplotlib inline +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +matplotlib.rcParams["lines.linewidth"] = 2.0 +sns.reset_orig() + +# Import tensorboard +# %load_ext tensorboard + +# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/MetaLearning/") + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu") +print("Device:", device) + +# %% [markdown] +# Training the models in this notebook can take between 2 and 8 hours, and the evaluation time of some algorithms is in the span of couples of minutes. +# Hence, we download pre-trained models and results below. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial16/" +# Files to download +pretrained_files = [ + "ProtoNet.ckpt", + "ProtoMAML.ckpt", + "tensorboards/ProtoNet/events.out.tfevents.ProtoNet", + "tensorboards/ProtoMAML/events.out.tfevents.ProtoMAML", + "protomaml_fewshot.json", + "protomaml_svhn_fewshot.json", +] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if "/" in file_name: + os.makedirs(file_path.rsplit("/", 1)[0], exist_ok=True) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print("Downloading %s..." % file_url) + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# ## Few-shot classification +# +# We start our implementation by discussing the dataset setup. +# In this notebook, we will use CIFAR100 which we have already seen in Tutorial 6. +# CIFAR100 has 100 classes each with 600 images of size $32\times 32$ pixels. +# Instead of splitting the training, validation and test set over examples, we will split them over classes: we will use 80 classes for training, and 10 for validation and 10 for testing. +# Our overall goal is to obtain a model that can distinguish between the 10 test classes with seeing very little examples. +# First, let's load the dataset and visualize some examples. + +# %% +# Loading CIFAR100 dataset +cifar_train_set = CIFAR100(root=DATASET_PATH, train=True, download=True, transform=transforms.ToTensor()) +cifar_test_set = CIFAR100(root=DATASET_PATH, train=False, download=True, transform=transforms.ToTensor()) + +# %% +# Visualize some examples +NUM_IMAGES = 12 +cifar_images = [cifar_train_set[np.random.randint(len(cifar_train_set))][0] for idx in range(NUM_IMAGES)] +cifar_images = torch.stack(cifar_images, dim=0) +img_grid = torchvision.utils.make_grid(cifar_images, nrow=6, normalize=True, pad_value=0.9) +img_grid = img_grid.permute(1, 2, 0) + +plt.figure(figsize=(8, 8)) +plt.title("Image examples of the CIFAR100 dataset") +plt.imshow(img_grid) +plt.axis("off") +plt.show() +plt.close() + +# %% [markdown] +# ### Data preprocessing +# +# Next, we need to prepare the dataset in the training, validation and test split as mentioned before. +# The torchvision package gives us the training and test set as two separate dataset objects. +# The next code cells will merge the original training and test set, and then create the new train-val-test split. + +# %% +# Merging original training and test set +cifar_all_images = np.concatenate([cifar_train_set.data, cifar_test_set.data], axis=0) +cifar_all_targets = torch.LongTensor(cifar_train_set.targets + cifar_test_set.targets) + +# %% [markdown] +# To have an easier time handling the dataset, we define our own, simple dataset class below. +# It takes a set of images, labels/targets, and image transformations, and +# returns the corresponding images and labels element-wise. + + +# %% +class ImageDataset(data.Dataset): + def __init__(self, imgs, targets, img_transform=None): + """ + Inputs: + imgs - Numpy array of shape [N,32,32,3] containing all images. + targets - PyTorch array of shape [N] containing all labels. + img_transform - A torchvision transformation that should be applied + to the images before returning. If none, no transformation + is applied. + """ + super().__init__() + self.img_transform = img_transform + self.imgs = imgs + self.targets = targets + + def __getitem__(self, idx): + img, target = self.imgs[idx], self.targets[idx] + img = Image.fromarray(img) + + if self.img_transform is not None: + img = self.img_transform(img) + + return img, target + + def __len__(self): + return self.imgs.shape[0] + + +# %% [markdown] +# Now, we can create the class splits. +# We will assign the classes randomly to training, validation and test, and use a 80%-10%-10% split. + +# %% +L.seed_everything(0) # Set seed for reproducibility +classes = torch.randperm(100) # Returns random permutation of numbers 0 to 99 +train_classes, val_classes, test_classes = classes[:80], classes[80:90], classes[90:] + +# %% [markdown] +# To get an intuition of the validation and test classes, we print the class names below: + +# %% +# Printing validation and test classes +idx_to_class = {val: key for key, val in cifar_train_set.class_to_idx.items()} +print("Validation classes:", [idx_to_class[c.item()] for c in val_classes]) +print("Test classes:", [idx_to_class[c.item()] for c in test_classes]) + +# %% [markdown] +# As we can see, the classes have quite some variety and some classes might be easier to distinguish than others. +# For instance, in the test classes, 'pickup_truck' is the only vehicle while the classes 'mushroom', 'worm' and 'forest' might be harder to keep apart. +# Remember that we want to learn the classification of those ten classes from 80 other classes in our training set, and few examples from the actual test classes. +# We will experiment with the number of examples per class. +# +# Finally, we can create the training, validation and test dataset according to our split above. +# For this, we create dataset objects of our previously defined class `ImageDataset`. + + +# %% +def dataset_from_labels(imgs, targets, class_set, **kwargs): + class_mask = (targets[:, None] == class_set[None, :]).any(dim=-1) + return ImageDataset(imgs=imgs[class_mask], targets=targets[class_mask], **kwargs) + + +# %% [markdown] +# As in our experiments before on CIFAR in Tutorial 5, 6 and 9, we normalize the dataset. +# Additionally, we use small augmentations during training to prevent overfitting. + +# %% +DATA_MEANS = (cifar_train_set.data / 255.0).mean(axis=(0, 1, 2)) +DATA_STD = (cifar_train_set.data / 255.0).std(axis=(0, 1, 2)) + +test_transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(DATA_MEANS, DATA_STD)]) +# For training, we add some augmentation. +train_transform = transforms.Compose( + [ + transforms.RandomHorizontalFlip(), + transforms.RandomResizedCrop((32, 32), scale=(0.8, 1.0), ratio=(0.9, 1.1)), + transforms.ToTensor(), + transforms.Normalize(DATA_MEANS, DATA_STD), + ] +) + +train_set = dataset_from_labels(cifar_all_images, cifar_all_targets, train_classes, img_transform=train_transform) +val_set = dataset_from_labels(cifar_all_images, cifar_all_targets, val_classes, img_transform=test_transform) +test_set = dataset_from_labels(cifar_all_images, cifar_all_targets, test_classes, img_transform=test_transform) + +# %% [markdown] +# ### Data sampling +# +# The strategy of how to use the available training data for learning few-shot adaptation is crucial in meta-learning. +# All three algorithms that we discuss here have a similar idea: simulate few-shot learning during training. +# Specifically, at each training step, we randomly select a small number of classes, and sample a small number of examples for each class. +# This represents our few-shot training batch, which we also refer to as **support set**. +# Additionally, we sample a second set of examples from the same classes, and refer to this batch as **query set**. +# Our training objective is to classify the query set correctly from seeing the support set and its corresponding labels. +# The main difference between our three methods (ProtoNet, MAML, and Proto-MAML) is in how they use the support set to adapt to the training classes. +# +# This subsection summarizes the code that is needed to create such training batches. +# In PyTorch, we can specify the data sampling procedure by so-called `Sampler` ([documentation](https://pytorch.org/docs/stable/data.html#data-loading-order-and-sampler)). +# Samplers are iteratable objects that return indices in the order in which the data elements should be sampled. +# In our previous notebooks, we usually used the option `shuffle=True` in the `data.DataLoader` objects which creates a sampler returning the data indices in a random order. +# Here, we focus on samplers that return batches of indices that correspond to support and query set batches. +# Below, we implement such a sampler. + + +# %% +class FewShotBatchSampler: + def __init__(self, dataset_targets, N_way, K_shot, include_query=False, shuffle=True, shuffle_once=False): + """ + Inputs: + dataset_targets - PyTorch tensor of the labels of the data elements. + N_way - Number of classes to sample per batch. + K_shot - Number of examples to sample per class in the batch. + include_query - If True, returns batch of size N_way*K_shot*2, which + can be split into support and query set. Simplifies + the implementation of sampling the same classes but + distinct examples for support and query set. + shuffle - If True, examples and classes are newly shuffled in each + iteration (for training) + shuffle_once - If True, examples and classes are shuffled once in + the beginning, but kept constant across iterations + (for validation) + """ + super().__init__() + self.dataset_targets = dataset_targets + self.N_way = N_way + self.K_shot = K_shot + self.shuffle = shuffle + self.include_query = include_query + if self.include_query: + self.K_shot *= 2 + self.batch_size = self.N_way * self.K_shot # Number of overall images per batch + + # Organize examples by class + self.classes = torch.unique(self.dataset_targets).tolist() + self.num_classes = len(self.classes) + self.indices_per_class = {} + self.batches_per_class = {} # Number of K-shot batches that each class can provide + for c in self.classes: + self.indices_per_class[c] = torch.where(self.dataset_targets == c)[0] + self.batches_per_class[c] = self.indices_per_class[c].shape[0] // self.K_shot + + # Create a list of classes from which we select the N classes per batch + self.iterations = sum(self.batches_per_class.values()) // self.N_way + self.class_list = [c for c in self.classes for _ in range(self.batches_per_class[c])] + if shuffle_once or self.shuffle: + self.shuffle_data() + else: + # For testing, we iterate over classes instead of shuffling them + sort_idxs = [ + i + p * self.num_classes for i, c in enumerate(self.classes) for p in range(self.batches_per_class[c]) + ] + self.class_list = np.array(self.class_list)[np.argsort(sort_idxs)].tolist() + + def shuffle_data(self): + # Shuffle the examples per class + for c in self.classes: + perm = torch.randperm(self.indices_per_class[c].shape[0]) + self.indices_per_class[c] = self.indices_per_class[c][perm] + # Shuffle the class list from which we sample. Note that this way of shuffling + # does not prevent to choose the same class twice in a batch. However, for + # training and validation, this is not a problem. + random.shuffle(self.class_list) + + def __iter__(self): + # Shuffle data + if self.shuffle: + self.shuffle_data() + + # Sample few-shot batches + start_index = defaultdict(int) + for it in range(self.iterations): + class_batch = self.class_list[it * self.N_way : (it + 1) * self.N_way] # Select N classes for the batch + index_batch = [] + for c in class_batch: # For each class, select the next K examples and add them to the batch + index_batch.extend(self.indices_per_class[c][start_index[c] : start_index[c] + self.K_shot]) + start_index[c] += self.K_shot + if self.include_query: # If we return support+query set, sort them so that they are easy to split + index_batch = index_batch[::2] + index_batch[1::2] + yield index_batch + + def __len__(self): + return self.iterations + + +# %% [markdown] +# Now, we can create our intended data loaders by passing an object of `FewShotBatchSampler` as `batch_sampler=...` input to the PyTorch data loader object. +# For our experiments, we will use a 5-class 4-shot training setting. +# This means that each support set contains 5 classes with 4 examples each, i.e., 20 images overall. +# Usually, it is good to keep the number of shots equal to the number that you aim to test on. +# However, we will experiment later with different number of shots, and hence, we pick 4 as a compromise for now. +# To get the best performing model, it is recommended to consider the +# number of training shots as hyperparameter in a grid search. + +# %% +N_WAY = 5 +K_SHOT = 4 +train_data_loader = data.DataLoader( + train_set, + batch_sampler=FewShotBatchSampler(train_set.targets, include_query=True, N_way=N_WAY, K_shot=K_SHOT, shuffle=True), + num_workers=4, +) +val_data_loader = data.DataLoader( + val_set, + batch_sampler=FewShotBatchSampler( + val_set.targets, include_query=True, N_way=N_WAY, K_shot=K_SHOT, shuffle=False, shuffle_once=True + ), + num_workers=4, +) + +# %% [markdown] +# For simplicity, we implemented the sampling of a support and query set as sampling a support set with twice the number of examples. +# After sampling a batch from the data loader, we need to split it into a support and query set. +# We can summarize this step in the following function: + + +# %% +def split_batch(imgs, targets): + support_imgs, query_imgs = imgs.chunk(2, dim=0) + support_targets, query_targets = targets.chunk(2, dim=0) + return support_imgs, query_imgs, support_targets, query_targets + + +# %% [markdown] +# Finally, to ensure that our implementation of the data sampling process is correct, we can sample a batch and visualize its support and query set. +# What we would like to see is that the support and query set have the same classes, but distinct examples. + +# %% +imgs, targets = next(iter(val_data_loader)) # We use the validation set since it does not apply augmentations +support_imgs, query_imgs, _, _ = split_batch(imgs, targets) +support_grid = torchvision.utils.make_grid(support_imgs, nrow=K_SHOT, normalize=True, pad_value=0.9) +support_grid = support_grid.permute(1, 2, 0) +query_grid = torchvision.utils.make_grid(query_imgs, nrow=K_SHOT, normalize=True, pad_value=0.9) +query_grid = query_grid.permute(1, 2, 0) + +fig, ax = plt.subplots(1, 2, figsize=(8, 5)) +ax[0].imshow(support_grid) +ax[0].set_title("Support set") +ax[0].axis("off") +ax[1].imshow(query_grid) +ax[1].set_title("Query set") +ax[1].axis("off") +fig.suptitle("Few Shot Batch", weight="bold") +fig.show() +plt.close(fig) + +# %% [markdown] +# As we can see, the support and query set have the same five classes, but different examples. +# The models will be tasked to classify the examples in the query set by learning from the support set and its labels. +# With the data sampling in place, we can now start to implement our first meta-learning model: Prototypical Networks. + +# %% [markdown] +# ## Prototypical Networks +# +#
+ +# %% [markdown] +# The Prototypical Network, or ProtoNet for short, is a metric-based meta-learning algorithm which operates similar to a nearest neighbor classification. +# Metric-based meta-learning methods classify a new example $\mathbf{x}$ based on some distance function $d_{\varphi}$ between $x$ and all elements in the support set. +# ProtoNets implements this idea with the concept of prototypes in a learned feature space. +# First, ProtoNet uses an embedding function $f_{\theta}$ to encode each input in the support set into a $L$-dimensional feature vector. +# Next, for each class $c$, we collect the feature vectors of all examples with label $c$, and average their feature vectors. +# Formally, we can define this as: +# +# $$\mathbf{v}_c=\frac{1}{|S_c|}\sum_{(\mathbf{x}_i,y_i)\in S_c}f_{\theta}(\mathbf{x}_i)$$ +# +# where $S_c$ is the part of the support set $S$ for which $y_i=c$, and $\mathbf{v}_c$ represents the _prototype_ of class $c$. +# The prototype calculation is visualized below for a 2-dimensional feature space and 3 classes (Figure credit - [Snell et al.](https://arxiv.org/pdf/1703.05175.pdf)). +# The colored dots represent encoded support elements with color-corresponding class label, and the black dots next to the class label are the averaged prototypes. +# +#
+# +# Based on these prototypes, we want to classify a new example. +# Remember that since we want to learn the encoding function $f_{\theta}$, this classification must be differentiable and hence, we need to define a probability distribution across classes. +# For this, we will make use of the distance function $d_{\varphi}$: the closer a new example $\mathbf{x}$ is to a prototype $\mathbf{v}_c$, the higher the probability for $\mathbf{x}$ belonging to class $c$. +# Formally, we can simply use a softmax over the distances of $\mathbf{x}$ to all class prototypes: +# +# $$p(y=c\vert\mathbf{x})=\text{softmax}(-d_{\varphi}(f_{\theta}(\mathbf{x}), \mathbf{v}_c))=\frac{\exp\left(-d_{\varphi}(f_{\theta}(\mathbf{x}), \mathbf{v}_c)\right)}{\sum_{c'\in \mathcal{C}}\exp\left(-d_{\varphi}(f_{\theta}(\mathbf{x}), \mathbf{v}_{c'})\right)}$$ +# +# Note that the negative sign is necessary since we want to increase the probability for close-by vectors and have a low probability for distant vectors. +# We train the network $f_{\theta}$ based on the cross entropy error of the training query set examples. +# Thereby, the gradient flows through both the prototypes $\mathbf{v}_c$ and the query set encodings $f_{\theta}(\mathbf{x})$. +# For the distance function $d_{\varphi}$, we can choose any function as long as it is differentiable with respect to both of its inputs. +# The most common function, which we also use here, is the squared +# euclidean distance, but there has been several works on different +# distance functions as well. + +# %% [markdown] +# ### ProtoNet implementation + +# %% [markdown] +# Now that we know how a ProtoNet works in principle, let's look at how we can apply to our specific problem of few-shot image classification, and implement it below. +# First, we need to define the encoder function $f_{\theta}$. +# Since we work with CIFAR images, we can take a look back at Tutorial 5 where we compared common Computer Vision architectures, and choose one of the best performing ones. +# Here, we go with a DenseNet since it is in general more parameter efficient than ResNet. +# Luckily, we do not need to implement DenseNet ourselves again and can rely on torchvision's model package instead. +# We use common hyperparameters of 64 initial feature channels, add 32 per block, and use a bottleneck size of 64 (i.e. 2 times the growth rate). +# We use 4 stages of 6 layers each, which results in overall about 1 million parameters. +# Note that the torchvision package assumes that the last layer is used for classification and hence calls its output size `num_classes`. +# However, we can instead just use it as the feature space of ProtoNet, and choose an arbitrary dimensionality. +# We will use the same network for other algorithms in this notebook to ensure a fair comparison. + + +# %% +def get_convnet(output_size): + convnet = torchvision.models.DenseNet( + growth_rate=32, + block_config=(6, 6, 6, 6), + bn_size=2, + num_init_features=64, + num_classes=output_size, # Output dimensionality + ) + return convnet + + +# %% [markdown] +# Next, we can look at implementing ProtoNet. +# We will define it as PyTorch Lightning module to use all functionalities of PyTorch Lightning. +# The first step during training is to encode all images in a batch with our network. +# Next, we calculate the class prototypes from the support set (function `calculate_prototypes`), and classify the query set examples according to the prototypes (function `classify_feats`). +# Keep in mind that we use the data sampling described before, such that the support and query set are stacked together in the batch. +# Thus, we use our previously defined function `split_batch` to split them apart. +# The full code can be found below. + + +# %% +class ProtoNet(L.LightningModule): + def __init__(self, proto_dim, lr): + """Inputs. + + proto_dim - Dimensionality of prototype feature space + lr - Learning rate of Adam optimizer + """ + super().__init__() + self.save_hyperparameters() + self.model = get_convnet(output_size=self.hparams.proto_dim) + + def configure_optimizers(self): + optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr) + scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[140, 180], gamma=0.1) + return [optimizer], [scheduler] + + @staticmethod + def calculate_prototypes(features, targets): + # Given a stack of features vectors and labels, return class prototypes + # features - shape [N, proto_dim], targets - shape [N] + classes, _ = torch.unique(targets).sort() # Determine which classes we have + prototypes = [] + for c in classes: + p = features[torch.where(targets == c)[0]].mean(dim=0) # Average class feature vectors + prototypes.append(p) + prototypes = torch.stack(prototypes, dim=0) + # Return the 'classes' tensor to know which prototype belongs to which class + return prototypes, classes + + def classify_feats(self, prototypes, classes, feats, targets): + # Classify new examples with prototypes and return classification error + dist = torch.pow(prototypes[None, :] - feats[:, None], 2).sum(dim=2) # Squared euclidean distance + preds = F.log_softmax(-dist, dim=1) + labels = (classes[None, :] == targets[:, None]).long().argmax(dim=-1) + acc = (preds.argmax(dim=1) == labels).float().mean() + return preds, labels, acc + + def calculate_loss(self, batch, mode): + # Determine training loss for a given support and query set + imgs, targets = batch + features = self.model(imgs) # Encode all images of support and query set + support_feats, query_feats, support_targets, query_targets = split_batch(features, targets) + prototypes, classes = ProtoNet.calculate_prototypes(support_feats, support_targets) + preds, labels, acc = self.classify_feats(prototypes, classes, query_feats, query_targets) + loss = F.cross_entropy(preds, labels) + + self.log("%s_loss" % mode, loss) + self.log("%s_acc" % mode, acc) + return loss + + def training_step(self, batch, batch_idx): + return self.calculate_loss(batch, mode="train") + + def validation_step(self, batch, batch_idx): + self.calculate_loss(batch, mode="val") + + +# %% [markdown] +# For validation, we use the same principle as training and sample support and query sets from the hold-out 10 classes. +# However, this gives us noisy scores depending on which query sets are chosen to which support sets. +# This is why we will use a different strategy during testing. +# For validation, our training strategy is sufficient since it is much +# faster than testing, and gives a good estimate of the training +# generalization as long as we keep the support-query sets constant across +# validation iterations. + +# %% [markdown] +# ### Training +# +# After implementing the model, we can already start training it. +# We use our common PyTorch Lightning training function, and train the model for 200 epochs. +# The training function takes `model_class` as input argument, i.e. the +# PyTorch Lightning module class that should be trained, since we will +# reuse this function for other algorithms as well. + + +# %% +def train_model(model_class, train_loader, val_loader, **kwargs): + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, model_class.__name__), + accelerator="auto", + devices=1, + max_epochs=200, + callbacks=[ + ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc"), + LearningRateMonitor("epoch"), + ], + enable_progress_bar=False, + ) + trainer.logger._default_hp_metric = None + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, model_class.__name__ + ".ckpt") + if os.path.isfile(pretrained_filename): + print("Found pretrained model at %s, loading..." % pretrained_filename) + # Automatically loads the model with the saved hyperparameters + model = model_class.load_from_checkpoint(pretrained_filename) + else: + L.seed_everything(42) # To be reproducable + model = model_class(**kwargs) + trainer.fit(model, train_loader, val_loader) + model = model_class.load_from_checkpoint( + trainer.checkpoint_callback.best_model_path + ) # Load best checkpoint after training + + return model + + +# %% [markdown] +# Below is the training call for our ProtoNet. +# We use a 64-dimensional feature space. +# Larger feature spaces showed to give noisier results since the squared euclidean distance becomes proportionally larger in expectation, and smaller feature spaces might not allow for enough flexibility. +# We recommend to load the pre-trained model here at first, but feel free +# to play around with the hyperparameters yourself. + +# %% +protonet_model = train_model( + ProtoNet, proto_dim=64, lr=2e-4, train_loader=train_data_loader, val_loader=val_data_loader +) + +# %% [markdown] +# We can also take a closer look at the TensorBoard below. + +# %% +# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH if needed +# # %tensorboard --logdir ../saved_models/tutorial16/tensorboards/ProtoNet/ + +# %% [markdown] +#
+# +# In contrast to standard supervised learning, we see that ProtoNet does not overfit as much as we would expect. +# The validation accuracy is of course lower than the average training, but the training loss does not stick close to zero. +# This is because no training batch is as the other, and we also mix new examples in the support set and query set. +# This gives us slightly different prototypes in every iteration, and makes it harder for the network to fully overfit. + +# %% [markdown] +# ### Testing +# +# Our goal of meta-learning is to obtain a model that can quickly adapt to a new task, or in this case, new classes to distinguish between. +# To test this, we will use our trained ProtoNet and adapt it to the 10 test classes. +# Thereby, we pick $k$ examples per class from which we determine the prototypes, and test the classification accuracy on all other examples. +# This can be seen as using the $k$ examples per class as support set, and the rest of the dataset as a query set. +# We iterate through the dataset such that each example has been once included in a support set. +# The average performance over all support sets tells us how well we can expect ProtoNet to perform when seeing only $k$ examples per class. +# During training, we used $k=4$. +# In testing, we will experiment with $k=\{2,4,8,16,32\}$ to get a better sense of how $k$ influences the results. +# We would expect that we achieve higher accuracies the more examples we have in the support set, but we don't know how it scales. +# Hence, let's first implement a function that executes the testing procedure for a given $k$: + + +# %% +@torch.no_grad() +def test_proto_net(model, dataset, data_feats=None, k_shot=4): + """Inputs. + + model - Pretrained ProtoNet model + dataset - The dataset on which the test should be performed. + Should be instance of ImageDataset + data_feats - The encoded features of all images in the dataset. + If None, they will be newly calculated, and returned + for later usage. + k_shot - Number of examples per class in the support set. + """ + model = model.to(device) + model.eval() + num_classes = dataset.targets.unique().shape[0] + exmps_per_class = dataset.targets.shape[0] // num_classes # We assume uniform example distribution here + + # The encoder network remains unchanged across k-shot settings. Hence, we only need + # to extract the features for all images once. + if data_feats is None: + # Dataset preparation + dataloader = data.DataLoader(dataset, batch_size=128, num_workers=4, shuffle=False, drop_last=False) + + img_features = [] + img_targets = [] + for imgs, targets in tqdm(dataloader, "Extracting image features", leave=False): + imgs = imgs.to(device) + feats = model.model(imgs) + img_features.append(feats.detach().cpu()) + img_targets.append(targets) + img_features = torch.cat(img_features, dim=0) + img_targets = torch.cat(img_targets, dim=0) + # Sort by classes, so that we obtain tensors of shape [num_classes, exmps_per_class, ...] + # Makes it easier to process later + img_targets, sort_idx = img_targets.sort() + img_targets = img_targets.reshape(num_classes, exmps_per_class).transpose(0, 1) + img_features = img_features[sort_idx].reshape(num_classes, exmps_per_class, -1).transpose(0, 1) + else: + img_features, img_targets = data_feats + + # We iterate through the full dataset in two manners. First, to select the k-shot batch. + # Second, the evaluate the model on all other examples + accuracies = [] + for k_idx in tqdm(range(0, img_features.shape[0], k_shot), "Evaluating prototype classification", leave=False): + # Select support set and calculate prototypes + k_img_feats = img_features[k_idx : k_idx + k_shot].flatten(0, 1) + k_targets = img_targets[k_idx : k_idx + k_shot].flatten(0, 1) + prototypes, proto_classes = model.calculate_prototypes(k_img_feats, k_targets) + # Evaluate accuracy on the rest of the dataset + batch_acc = 0 + for e_idx in range(0, img_features.shape[0], k_shot): + if k_idx == e_idx: # Do not evaluate on the support set examples + continue + e_img_feats = img_features[e_idx : e_idx + k_shot].flatten(0, 1) + e_targets = img_targets[e_idx : e_idx + k_shot].flatten(0, 1) + _, _, acc = model.classify_feats(prototypes, proto_classes, e_img_feats, e_targets) + batch_acc += acc.item() + batch_acc /= img_features.shape[0] // k_shot - 1 + accuracies.append(batch_acc) + + return (mean(accuracies), stdev(accuracies)), (img_features, img_targets) + + +# %% [markdown] +# Testing ProtoNet is relatively quick if we have processed all images once. Hence, we can do in this notebook: + +# %% +protonet_accuracies = dict() +data_feats = None +for k in [2, 4, 8, 16, 32]: + protonet_accuracies[k], data_feats = test_proto_net(protonet_model, test_set, data_feats=data_feats, k_shot=k) + print( + "Accuracy for k=%i: %4.2f%% (+-%4.2f%%)" + % (k, 100.0 * protonet_accuracies[k][0], 100 * protonet_accuracies[k][1]) + ) + +# %% [markdown] +# Before discussing the results above, let's first plot the accuracies over number of examples in the support set: + + +# %% +def plot_few_shot(acc_dict, name, color=None, ax=None): + sns.set() + if ax is None: + fig, ax = plt.subplots(1, 1, figsize=(5, 3)) + ks = sorted(list(acc_dict.keys())) + mean_accs = [acc_dict[k][0] for k in ks] + std_accs = [acc_dict[k][1] for k in ks] + ax.plot(ks, mean_accs, marker="o", markeredgecolor="k", markersize=6, label=name, color=color) + ax.fill_between( + ks, + [m - s for m, s in zip(mean_accs, std_accs)], + [m + s for m, s in zip(mean_accs, std_accs)], + alpha=0.2, + color=color, + ) + ax.set_xticks(ks) + ax.set_xlim([ks[0] - 1, ks[-1] + 1]) + ax.set_xlabel("Number of shots per class", weight="bold") + ax.set_ylabel("Accuracy", weight="bold") + if len(ax.get_title()) == 0: + ax.set_title("Few-Shot Performance " + name, weight="bold") + else: + ax.set_title(ax.get_title() + " and " + name, weight="bold") + ax.legend() + return ax + + +# %% +ax = plot_few_shot(protonet_accuracies, name="ProtoNet", color="C1") +plt.show() +plt.close() + +# %% [markdown] +# As we initially expected, the performance of ProtoNet indeed increases the more samples we have. +# However, even with just two samples per class, we classify almost half of the images correctly, which is well above random accuracy (10%). +# The curve shows an exponentially dampend trend, meaning that adding 2 extra examples to $k=2$ has a much higher impact than adding 2 extra samples if we already have $k=16$. +# Nonetheless, we can say that ProtoNet adapts fairly well to new classes. + +# %% [markdown] +# ## MAML and ProtoMAML +# +#
+ +# %% [markdown] +# The second meta-learning algorithm we will look at is MAML, short for Model-Agnostic Meta-Learning. +# MAML is an optimization-based meta-learning algorithm, which means that it tries to adjust the standard optimization procedure to a few-shot setting. +# The idea of MAML is relatively simple: given a model, support and query set during training, we optimize the model for $m$ steps on the support set, and evaluate the gradients of the query loss with respect to the original model's parameters. +# For the same model, we do it for a few different support-query sets and accumulate the gradients. +# This results in learning a model that provides a good initialization for being quickly adapted to the training tasks. +# If we denote the model parameters with $\theta$, we can visualize the procedure as follows (Figure credit - [Finn et al. ](http://proceedings.mlr.press/v70/finn17a.html)). +# +#
+ +# %% [markdown] +# The full algorithm of MAML is therefore as follows. +# At each training step, we sample a batch of tasks, i.e., a batch of support-query set pairs. +# For each task $\mathcal{T}_i$, we optimize a model $f_{\theta}$ on the support set via SGD, and denote this model as $f_{\theta_i'}$. +# We refer to this optimization as _inner loop_. +# Using this new model, we calculate the gradients of the original parameters, $\theta$, with respect to the query loss on $f_{\theta_i'}$. +# These gradients are accumulated over all tasks, and used to update $\theta$. +# This is called _outer loop_ since we iterate over tasks. +# The full MAML algorithm is summarized below (Figure credit - [Finn et al. ](http://proceedings.mlr.press/v70/finn17a.html)). +# +#
+ +# %% [markdown] +# To obtain gradients for the initial parameters $\theta$ from the optimized model $f_{\theta_i'}$, we actually need second-order gradients, i.e. gradients of gradients, as the support set gradients depend on $\theta$ as well. +# This makes MAML computationally expensive, especially when using mulitple inner loop steps. +# A simpler, yet almost equally well performing alternative is First-Order MAML (FOMAML) which only uses first-order gradients. +# This means that the second-order gradients are ignored, and we can calculate the outer loop gradients (line 10 in algorithm 2) simply by calculating the gradients with respect to $\theta_i'$, and use those as update to $\theta$. +# Hence, the new update rule becomes: +# $$\theta\leftarrow\theta-\beta\sum_{\mathcal{T}_i\sim p(\mathcal{T})}\nabla_{\theta_i'}\mathcal{L}_{\mathcal{T}_i}(f_{\theta_i'})$$ +# Note the change of $\theta$ to $\theta_i'$ for $\nabla$. + +# %% [markdown] +# ### ProtoMAML +# +# A problem of MAML is how to design the output classification layer. +# In case all tasks have different number of classes, we need to initialize the output layer with zeros or randomly in every iteration. +# Even if we always have the same number of classes, we just start from random predictions. +# This requires several inner loop steps to reach a reasonable classification result. +# To overcome this problem, Triantafillou et al. +# (2020) propose to combine the merits of Prototypical Networks and MAML. +# Specifically, we can use prototypes to initialize our output layer to have a strong initialization. +# Thereby, it can be shown that the softmax over euclidean distances can be reformulated as a linear layer with softmax. +# To see this, let's first write out the negative euclidean distance between a feature vector $f_{\theta}(\mathbf{x}^{*})$ of a new data point $\mathbf{x}^{*}$ to a prototype $\mathbf{v}_c$ of class $c$: +# $$ +# -||f_{\theta}(\mathbf{x}^{*})-\mathbf{v}_c||^2=-f_{\theta}(\mathbf{x}^{*})^Tf_{\theta}(\mathbf{x}^{*})+2\mathbf{v}_c^{T}f_{\theta}(\mathbf{x}^{*})-\mathbf{v}_c^T\mathbf{v}_c +# $$ +# +# We perform the classification across all classes $c\in\mathcal{C}$ and take a softmax on the distance. +# Hence, any term that is same for all classes can be removed without changing the output probabilities. +# In the equation above, this is true for $-f_{\theta}(\mathbf{x}^{*})^Tf_{\theta}(\mathbf{x}^{*})$ since it is independent of any class prototype. +# Thus, we can write: +# +# $$ +# -||f_{\theta}(\mathbf{x}^{*})-\mathbf{v}_c||^2=2\mathbf{v}_c^{T}f_{\theta}(\mathbf{x}^{*})-||\mathbf{v}_c||^2+\text{constant} +# $$ +# +# Taking a second look at the equation above, it looks a lot like a linear layer. +# For this, we use $\mathbf{W}_{c,\cdot}=2\mathbf{v}_c$ and $b_c=-||\mathbf{v}_c||^2$ which gives us the linear layer $\mathbf{W}f_{\theta}(\mathbf{x}^{*})+\mathbf{b}$. +# Hence, if we initialize the output weight with twice the prototypes, and the biases by the negative squared L2 norm of the prototypes, we start with a Prototypical Network. +# MAML allows us to adapt this layer and the rest of the network further. +# +# In the following, we will implement First-Order ProtoMAML for few-shot classification. +# The implementation of MAML would be the same except the output layer initialization. + +# %% [markdown] +# ### ProtoMAML implementation +# +# For implementing ProtoMAML, we can follow Algorithm 2 with minor modifications. +# At each training step, we first sample a batch of tasks, and a support and query set for each task. +# In our case of few-shot classification, this means that we simply sample multiple support-query set pairs from our sampler. +# For each task, we finetune our current model on the support set. +# However, since we need to remember the original parameters for the other tasks, the outer loop gradient update and future training steps, we need to create a copy of our model, and finetune only the copy. +# We can copy a model by using standard Python functions like `deepcopy`. +# The inner loop is implemented in the function `adapt_few_shot` in the PyTorch Lightning module below. +# +# After finetuning the model, we apply it on the query set and calculate the first-order gradients with respect to the original parameters $\theta$. +# In contrast to simple MAML, we also have to consider the gradients with respect to the output layer initialization, i.e. the prototypes, since they directly rely on $\theta$. +# To realize this efficiently, we take two steps. +# First, we calculate the prototypes by applying the original model, i.e. not the copied model, on the support elements. +# When initializing the output layer, we detach the prototypes to stop the gradients. +# This is because in the inner loop itself, we do not want to consider gradients through the prototypes back to the original model. +# However, after the inner loop is finished, we re-attach the computation graph of the prototypes by writing `output_weight = (output_weight - init_weight).detach() + init_weight`. +# While this line does not change the value of the variable `output_weight`, it adds its dependency on the prototype initialization `init_weight`. +# Thus, if we call `.backward` on `output_weight`, we will automatically calculate the first-order gradients with respect to the prototype initialization in the original model. +# +# After calculating all gradients and summing them together in the original model, we can take a standard optimizer step. +# PyTorch Lightning's method is however designed to return a loss-tensor on which we call `.backward` first. +# Since this is not possible here, we need to perform the optimization step ourselves. +# All details can be found in the code below. +# +# For implementing (Proto-)MAML with second-order gradients, it is recommended to use libraries such as [$\nabla$higher](https://github.com/facebookresearch/higher) from Facebook AI Research. +# For simplicity, we stick with first-order methods here. + + +# %% +class ProtoMAML(L.LightningModule): + def __init__(self, proto_dim, lr, lr_inner, lr_output, num_inner_steps): + """Inputs. + + proto_dim - Dimensionality of prototype feature space + lr - Learning rate of the outer loop Adam optimizer + lr_inner - Learning rate of the inner loop SGD optimizer + lr_output - Learning rate for the output layer in the inner loop + num_inner_steps - Number of inner loop updates to perform + """ + super().__init__() + self.save_hyperparameters() + self.model = get_convnet(output_size=self.hparams.proto_dim) + + def configure_optimizers(self): + optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr) + scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[140, 180], gamma=0.1) + return [optimizer], [scheduler] + + def run_model(self, local_model, output_weight, output_bias, imgs, labels): + # Execute a model with given output layer weights and inputs + feats = local_model(imgs) + preds = F.linear(feats, output_weight, output_bias) + loss = F.cross_entropy(preds, labels) + acc = (preds.argmax(dim=1) == labels).float() + return loss, preds, acc + + def adapt_few_shot(self, support_imgs, support_targets): + # Determine prototype initialization + support_feats = self.model(support_imgs) + prototypes, classes = ProtoNet.calculate_prototypes(support_feats, support_targets) + support_labels = (classes[None, :] == support_targets[:, None]).long().argmax(dim=-1) + # Create inner-loop model and optimizer + local_model = deepcopy(self.model) + local_model.train() + local_optim = optim.SGD(local_model.parameters(), lr=self.hparams.lr_inner) + local_optim.zero_grad() + # Create output layer weights with prototype-based initialization + init_weight = 2 * prototypes + init_bias = -torch.norm(prototypes, dim=1) ** 2 + output_weight = init_weight.detach().requires_grad_() + output_bias = init_bias.detach().requires_grad_() + + # Optimize inner loop model on support set + for _ in range(self.hparams.num_inner_steps): + # Determine loss on the support set + loss, _, _ = self.run_model(local_model, output_weight, output_bias, support_imgs, support_labels) + # Calculate gradients and perform inner loop update + loss.backward() + local_optim.step() + # Update output layer via SGD + output_weight.data -= self.hparams.lr_output * output_weight.grad + output_bias.data -= self.hparams.lr_output * output_bias.grad + # Reset gradients + local_optim.zero_grad() + output_weight.grad.fill_(0) + output_bias.grad.fill_(0) + + # Re-attach computation graph of prototypes + output_weight = (output_weight - init_weight).detach() + init_weight + output_bias = (output_bias - init_bias).detach() + init_bias + + return local_model, output_weight, output_bias, classes + + def outer_loop(self, batch, mode="train"): + accuracies = [] + losses = [] + self.model.zero_grad() + + # Determine gradients for batch of tasks + for task_batch in batch: + imgs, targets = task_batch + support_imgs, query_imgs, support_targets, query_targets = split_batch(imgs, targets) + # Perform inner loop adaptation + local_model, output_weight, output_bias, classes = self.adapt_few_shot(support_imgs, support_targets) + # Determine loss of query set + query_labels = (classes[None, :] == query_targets[:, None]).long().argmax(dim=-1) + loss, preds, acc = self.run_model(local_model, output_weight, output_bias, query_imgs, query_labels) + # Calculate gradients for query set loss + if mode == "train": + loss.backward() + + for p_global, p_local in zip(self.model.parameters(), local_model.parameters()): + p_global.grad += p_local.grad # First-order approx. -> add gradients of finetuned and base model + + accuracies.append(acc.mean().detach()) + losses.append(loss.detach()) + + # Perform update of base model + if mode == "train": + opt = self.optimizers() + opt.step() + opt.zero_grad() + + self.log("%s_loss" % mode, sum(losses) / len(losses)) + self.log("%s_acc" % mode, sum(accuracies) / len(accuracies)) + + def training_step(self, batch, batch_idx): + self.outer_loop(batch, mode="train") + return None # Returning None means we skip the default training optimizer steps by PyTorch Lightning + + def validation_step(self, batch, batch_idx): + # Validation requires to finetune a model, hence we need to enable gradients + torch.set_grad_enabled(True) + self.outer_loop(batch, mode="val") + torch.set_grad_enabled(False) + + +# %% [markdown] +# ### Training +# +# To train ProtoMAML, we need to change our sampling slightly. +# Instead of a single support-query set batch, we need to sample multiple. +# To implement this, we yet use another Sampler which combines multiple batches from a `FewShotBatchSampler`, and returns it afterwards. +# Additionally, we define a `collate_fn` for our data loader which takes the stack of support-query set images, and returns the tasks as a list. +# This makes it easier to process in our PyTorch Lightning module before. +# The implementation of the sampler can be found below. + + +# %% +class TaskBatchSampler: + def __init__(self, dataset_targets, batch_size, N_way, K_shot, include_query=False, shuffle=True): + """ + Inputs: + dataset_targets - PyTorch tensor of the labels of the data elements. + batch_size - Number of tasks to aggregate in a batch + N_way - Number of classes to sample per batch. + K_shot - Number of examples to sample per class in the batch. + include_query - If True, returns batch of size N_way*K_shot*2, which + can be split into support and query set. Simplifies + the implementation of sampling the same classes but + distinct examples for support and query set. + shuffle - If True, examples and classes are newly shuffled in each + iteration (for training) + """ + super().__init__() + self.batch_sampler = FewShotBatchSampler(dataset_targets, N_way, K_shot, include_query, shuffle) + self.task_batch_size = batch_size + self.local_batch_size = self.batch_sampler.batch_size + + def __iter__(self): + # Aggregate multiple batches before returning the indices + batch_list = [] + for batch_idx, batch in enumerate(self.batch_sampler): + batch_list.extend(batch) + if (batch_idx + 1) % self.task_batch_size == 0: + yield batch_list + batch_list = [] + + def __len__(self): + return len(self.batch_sampler) // self.task_batch_size + + def get_collate_fn(self): + # Returns a collate function that converts one big tensor into a list of task-specific tensors + def collate_fn(item_list): + imgs = torch.stack([img for img, target in item_list], dim=0) + targets = torch.stack([target for img, target in item_list], dim=0) + imgs = imgs.chunk(self.task_batch_size, dim=0) + targets = targets.chunk(self.task_batch_size, dim=0) + return list(zip(imgs, targets)) + + return collate_fn + + +# %% [markdown] +# The creation of the data loaders is with this sampler straight-forward. +# Note that since many images need to loaded for a training batch, it is recommended to use less workers than usual. + +# %% +# Training constant (same as for ProtoNet) +N_WAY = 5 +K_SHOT = 4 + +# Training set +train_protomaml_sampler = TaskBatchSampler( + train_set.targets, include_query=True, N_way=N_WAY, K_shot=K_SHOT, batch_size=16 +) +train_protomaml_loader = data.DataLoader( + train_set, batch_sampler=train_protomaml_sampler, collate_fn=train_protomaml_sampler.get_collate_fn(), num_workers=2 +) + +# Validation set +val_protomaml_sampler = TaskBatchSampler( + val_set.targets, + include_query=True, + N_way=N_WAY, + K_shot=K_SHOT, + batch_size=1, # We do not update the parameters, hence the batch size is irrelevant here + shuffle=False, +) +val_protomaml_loader = data.DataLoader( + val_set, batch_sampler=val_protomaml_sampler, collate_fn=val_protomaml_sampler.get_collate_fn(), num_workers=2 +) + +# %% [markdown] +# Now, we are ready to train our ProtoMAML. +# We use the same feature space size as for ProtoNet, but can use a higher learning rate since the outer loop gradients are accumulated over 16 batches. +# The inner loop learning rate is set to 0.1, which is much higher than the outer loop lr because we use SGD in the inner loop instead of Adam. +# Commonly, the learning rate for the output layer is higher than the base model is the base model is very deep or pre-trained. +# However, for our setup, we observed no noticable impact of using a different learning rate than the base model. +# The number of inner loop updates is another crucial hyperparmaeter, and depends on the similarity of our training tasks. +# Since all tasks are on images from the same dataset, we notice that a single inner loop update achieves similar performance as 3 or 5 while training considerably faster. +# However, especially in RL and NLP, larger number of inner loop steps are often needed. + +# %% +protomaml_model = train_model( + ProtoMAML, + proto_dim=64, + lr=1e-3, + lr_inner=0.1, + lr_output=0.1, + num_inner_steps=1, # Often values between 1 and 10 + train_loader=train_protomaml_loader, + val_loader=val_protomaml_loader, +) + +# %% [markdown] +# Let's have a look at the training TensorBoard. + +# %% +# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH if needed +# # %tensorboard --logdir ../saved_models/tutorial16/tensorboards/ProtoMAML/ + +# %% [markdown] +#
+# +# One obvious difference to ProtoNet is that the loss curves look much less noisy. +# This is because we average the outer loop gradients over multiple tasks, and thus have a smoother training curve. +# Additionally, we only have 15k training iterations after 200 epochs. +# This is again because of the task batches, which cause 16 times less iterations. +# However, each iteration has seen 16 times more data in this experiment. +# Thus, we still have a fair comparison between ProtoMAML and ProtoNet. +# At first sight on the validation accuracy, one would assume that +# ProtoNet performs superior to ProtoMAML, but we have to verify that with +# proper testing below. + +# %% [markdown] +# ### Testing +# +# We test ProtoMAML in the same manner as ProtoNet, namely by picking random examples in the test set as support sets and use the rest of the dataset as query set. +# Instead of just calculating the prototypes for all examples, we need to finetune a separate model for each support set. +# This is why this process is more expensive than ProtoNet, and in our case, testing $k=\{2,4,8,16,32\}$ can take almost an hour. +# Hence, we provide evaluation files besides the pretrained models. + + +# %% +def test_protomaml(model, dataset, k_shot=4): + L.seed_everything(42) + model = model.to(device) + num_classes = dataset.targets.unique().shape[0] + + # Data loader for full test set as query set + full_dataloader = data.DataLoader(dataset, batch_size=128, num_workers=4, shuffle=False, drop_last=False) + # Data loader for sampling support sets + sampler = FewShotBatchSampler( + dataset.targets, include_query=False, N_way=num_classes, K_shot=k_shot, shuffle=False, shuffle_once=False + ) + sample_dataloader = data.DataLoader(dataset, batch_sampler=sampler, num_workers=2) + + # We iterate through the full dataset in two manners. First, to select the k-shot batch. + # Second, the evaluate the model on all other examples + accuracies = [] + for (support_imgs, support_targets), support_indices in tqdm( + zip(sample_dataloader, sampler), "Performing few-shot finetuning" + ): + support_imgs = support_imgs.to(device) + support_targets = support_targets.to(device) + # Finetune new model on support set + local_model, output_weight, output_bias, classes = model.adapt_few_shot(support_imgs, support_targets) + with torch.no_grad(): # No gradients for query set needed + local_model.eval() + batch_acc = torch.zeros((0,), dtype=torch.float32, device=device) + # Evaluate all examples in test dataset + for query_imgs, query_targets in full_dataloader: + query_imgs = query_imgs.to(device) + query_targets = query_targets.to(device) + query_labels = (classes[None, :] == query_targets[:, None]).long().argmax(dim=-1) + _, _, acc = model.run_model(local_model, output_weight, output_bias, query_imgs, query_labels) + batch_acc = torch.cat([batch_acc, acc.detach()], dim=0) + # Exclude support set elements + for s_idx in support_indices: + batch_acc[s_idx] = 0 + batch_acc = batch_acc.sum().item() / (batch_acc.shape[0] - len(support_indices)) + accuracies.append(batch_acc) + return mean(accuracies), stdev(accuracies) + + +# %% [markdown] +# In contrast to training, it is recommended to use many more inner loop updates during testing. +# During training, we are not interested in getting the best model from the inner loop, but the model which can provide the best gradients. +# Hence, one update might be already sufficient in training, but for testing, it was often observed that larger number of updates can give a considerable performance boost. +# Thus, we change the inner loop updates to 200 before testing. + +# %% +protomaml_model.hparams.num_inner_steps = 200 + +# %% [markdown] +# Now, we can test our model. +# For the pre-trained models, we provide a json file with the results to reduce evaluation time. + +# %% +protomaml_result_file = os.path.join(CHECKPOINT_PATH, "protomaml_fewshot.json") + +if os.path.isfile(protomaml_result_file): + # Load pre-computed results + with open(protomaml_result_file) as f: + protomaml_accuracies = json.load(f) + protomaml_accuracies = {int(k): v for k, v in protomaml_accuracies.items()} +else: + # Perform same experiments as for ProtoNet + protomaml_accuracies = dict() + for k in [2, 4, 8, 16, 32]: + protomaml_accuracies[k] = test_protomaml(protomaml_model, test_set, k_shot=k) + # Export results + with open(protomaml_result_file, "w") as f: + json.dump(protomaml_accuracies, f, indent=4) + +for k in protomaml_accuracies: + print( + "Accuracy for k=%i: %4.2f%% (+-%4.2f%%)" + % (k, 100.0 * protomaml_accuracies[k][0], 100.0 * protomaml_accuracies[k][1]) + ) + +# %% [markdown] +# Again, let's plot the results in our plot from before. + +# %% +ax = plot_few_shot(protonet_accuracies, name="ProtoNet", color="C1") +plot_few_shot(protomaml_accuracies, name="ProtoMAML", color="C2", ax=ax) +plt.show() +plt.close() + +# %% [markdown] +# We can observe that ProtoMAML is indeed able to outperform ProtoNet for $k>4$. +# This is because with more samples, it becomes more relevant to also adapt the base model's parameters. +# Meanwhile, for $k=2$, ProtoMAML achieves lower performance than ProtoNet. +# This is likely also related to choosing 200 inner loop updates since with more updates, there exists the risk of overfitting. +# Nonetheless, the high standard deviation for $k=2$ makes it hard to take any statistically valid conclusion. +# +# Overall, we can conclude that ProtoMAML slightly outperforms ProtoNet for larger shot counts. +# However, one disadvantage of ProtoMAML is its much longer training and testing time. +# ProtoNet provides a simple, efficient, yet strong baseline for +# ProtoMAML, and might be the better solution in situations where limited +# resources are available. + +# %% [markdown] +# ## Domain adaptation +# +# So far, we have evaluated our meta-learning algorithms on the same dataset on which we have trained them. +# However, meta-learning algorithms are especially interesting when we want to move from one to another dataset. +# So, what happens if we apply them on a quite different dataset than CIFAR? +# This is what we try out below, and evaluate ProtoNet and ProtoMAML on the SVHN dataset. + +# %% [markdown] +# ### SVHN dataset +# +# The Street View House Numbers (SVHN) dataset is a real-world image dataset for house number detection. +# It is similar to MNIST by having the classes 0 to 9, but is more difficult due to its real-world setting and possible distracting numbers left and right. +# Let's first load the dataset, and visualize some images to get an impression of the dataset. + +# %% +SVHN_test_dataset = SVHN(root=DATASET_PATH, split="test", download=True, transform=transforms.ToTensor()) + +# %% +# Visualize some examples +NUM_IMAGES = 12 +SVHN_images = [SVHN_test_dataset[np.random.randint(len(SVHN_test_dataset))][0] for idx in range(NUM_IMAGES)] +SVHN_images = torch.stack(SVHN_images, dim=0) +img_grid = torchvision.utils.make_grid(SVHN_images, nrow=6, normalize=True, pad_value=0.9) +img_grid = img_grid.permute(1, 2, 0) + +plt.figure(figsize=(8, 8)) +plt.title("Image examples of the SVHN dataset") +plt.imshow(img_grid) +plt.axis("off") +plt.show() +plt.close() + +# %% [markdown] +# Each image is labeled with one class between 0 and 9 representing the main digit in the image. +# Can our ProtoNet and ProtoMAML learn to classify the digits from only a few examples? +# This is what we will test out below. +# The images have the same size as CIFAR, so that we can use the images without changes. +# We first prepare the dataset, for which we take the first 500 images per class. +# For this dataset, we use our test functions as before to get an estimated performance for different number of shots. + +# %% +imgs = np.transpose(SVHN_test_dataset.data, (0, 2, 3, 1)) +targets = SVHN_test_dataset.labels +# Limit number of examples to 500 to reduce test time +min_label_count = min(500, np.bincount(SVHN_test_dataset.labels).min()) + +idxs = np.concatenate([np.where(targets == c)[0][:min_label_count] for c in range(1 + targets.max())], axis=0) +imgs = imgs[idxs] +targets = torch.from_numpy(targets[idxs]).long() + +svhn_fewshot_dataset = ImageDataset(imgs, targets, img_transform=test_transform) +svhn_fewshot_dataset.imgs.shape + +# %% [markdown] +# ### Experiments +# +# First, we can apply ProtoNet to the SVHN dataset: + +# %% +protonet_svhn_accuracies = dict() +data_feats = None +for k in [2, 4, 8, 16, 32]: + protonet_svhn_accuracies[k], data_feats = test_proto_net( + protonet_model, svhn_fewshot_dataset, data_feats=data_feats, k_shot=k + ) + print( + "Accuracy for k=%i: %4.2f%% (+-%4.2f%%)" + % (k, 100.0 * protonet_svhn_accuracies[k][0], 100 * protonet_svhn_accuracies[k][1]) + ) + +# %% [markdown] +# It becomes clear that the results are much lower than the ones on CIFAR, and just slightly above random for $k=2$. +# How about ProtoMAML? +# We provide again evaluation files since the evaluation can take several minutes to complete. + +# %% +protomaml_result_file = os.path.join(CHECKPOINT_PATH, "protomaml_svhn_fewshot.json") + +if os.path.isfile(protomaml_result_file): + # Load pre-computed results + with open(protomaml_result_file) as f: + protomaml_svhn_accuracies = json.load(f) + protomaml_svhn_accuracies = {int(k): v for k, v in protomaml_svhn_accuracies.items()} +else: + # Perform same experiments as for ProtoNet + protomaml_svhn_accuracies = dict() + for k in [2, 4, 8, 16, 32]: + protomaml_svhn_accuracies[k] = test_protomaml(protomaml_model, svhn_fewshot_dataset, k_shot=k) + # Export results + with open(protomaml_result_file, "w") as f: + json.dump(protomaml_svhn_accuracies, f, indent=4) + +for k in protomaml_svhn_accuracies: + print( + "Accuracy for k=%i: %4.2f%% (+-%4.2f%%)" + % (k, 100.0 * protomaml_svhn_accuracies[k][0], 100.0 * protomaml_svhn_accuracies[k][1]) + ) + +# %% [markdown] +# While ProtoMAML shows similar performance than ProtoNet for $k\leq 4$, it considerably outperforms ProtoNet for more than 8 shots. +# This is because we can adapt the base model, which is crucial when the data does not fit the original training data. +# For $k=32$, ProtoMAML achieves $13\%$ higher classification accuracy than ProtoNet which already starts to flatten out. +# We can see the trend more clearly in our plot below. + +# %% +ax = plot_few_shot(protonet_svhn_accuracies, name="ProtoNet", color="C1") +plot_few_shot(protomaml_svhn_accuracies, name="ProtoMAML", color="C2", ax=ax) +plt.show() +plt.close() + +# %% [markdown] +# ## Conclusion +# +# In this notebook, we have discussed meta-learning algorithms that learn to adapt to new classes and/or tasks with just a few samples. +# We have discussed three popular algorithms, namely ProtoNet, MAML and ProtoMAML. +# On the few-shot image classification task of CIFAR100, ProtoNet and ProtoMAML showed to perform similarly well, with slight benefits of ProtoMAML for larger shot sizes. +# However, for out-of-distribution data (SVHN), the ability to optimize the base model showed to be crucial and gave ProtoMAML considerable performance gains over ProtoNet. +# Nonetheless, ProtoNet offers other advantages compared to ProtoMAML, namely a very cheap training and test cost as well as a simpler implementation. +# Hence, it is recommended to consider whether the additionally complexity +# of ProtoMAML is worth the extra training computation cost, or whether +# ProtoNet is already sufficient for the task at hand. + +# %% [markdown] +# ### References +# +# [1] Snell, Jake, Kevin Swersky, and Richard S. Zemel. +# "Prototypical networks for few-shot learning." +# NeurIPS 2017. +# ([link](https://arxiv.org/pdf/1703.05175.pdf)) +# +# [2] Chelsea Finn, Pieter Abbeel, Sergey Levine. +# "Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks." +# ICML 2017. +# ([link](http://proceedings.mlr.press/v70/finn17a.html)) +# +# [3] Triantafillou, Eleni, Tyler Zhu, Vincent Dumoulin, Pascal Lamblin, Utku Evci, Kelvin Xu, Ross Goroshin et al. +# "Meta-dataset: A dataset of datasets for learning to learn from few examples." +# ICLR 2020. +# ([link](https://openreview.net/pdf?id=rkgAGAVKPr)) diff --git a/_notebooks/course_UvA-DL/12-meta-learning/few-shot-classification.png b/_notebooks/course_UvA-DL/12-meta-learning/few-shot-classification.png new file mode 100644 index 0000000..d0146d1 Binary files /dev/null and b/_notebooks/course_UvA-DL/12-meta-learning/few-shot-classification.png differ diff --git a/_notebooks/course_UvA-DL/12-meta-learning/protonet_classification.svg b/_notebooks/course_UvA-DL/12-meta-learning/protonet_classification.svg new file mode 100644 index 0000000..716fbe3 --- /dev/null +++ b/_notebooks/course_UvA-DL/12-meta-learning/protonet_classification.svg @@ -0,0 +1,612 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/12-meta-learning/tensorboard_screenshot_ProtoMAML.png b/_notebooks/course_UvA-DL/12-meta-learning/tensorboard_screenshot_ProtoMAML.png new file mode 100644 index 0000000..13f2993 Binary files /dev/null and b/_notebooks/course_UvA-DL/12-meta-learning/tensorboard_screenshot_ProtoMAML.png differ diff --git a/_notebooks/course_UvA-DL/12-meta-learning/tensorboard_screenshot_ProtoNet.png b/_notebooks/course_UvA-DL/12-meta-learning/tensorboard_screenshot_ProtoNet.png new file mode 100644 index 0000000..e420ef8 Binary files /dev/null and b/_notebooks/course_UvA-DL/12-meta-learning/tensorboard_screenshot_ProtoNet.png differ diff --git a/_notebooks/course_UvA-DL/13-contrastive-learning/.meta.yml b/_notebooks/course_UvA-DL/13-contrastive-learning/.meta.yml new file mode 100644 index 0000000..6f9832f --- /dev/null +++ b/_notebooks/course_UvA-DL/13-contrastive-learning/.meta.yml @@ -0,0 +1,26 @@ +title: "Tutorial 13: Self-Supervised Contrastive Learning with SimCLR" +author: Phillip Lippe +created: 2021-08-30 +updated: 2023-03-14 +license: CC BY-SA +tags: + - Image + - Self-Supervised + - Contrastive-Learning +description: | + In this tutorial, we will take a closer look at self-supervised contrastive learning. + Self-supervised learning, or also sometimes called unsupervised learning, describes the scenario where we have given input data, but no accompanying labels to train in a classical supervised way. + However, this data still contains a lot of information from which we can learn: how are the images different from each other? + What patterns are descriptive for certain images? + Can we cluster the images? + To get an insight into these questions, we will implement a popular, simple contrastive learning method, SimCLR, and apply it to the STL10 dataset. + This notebook is part of a lecture series on Deep Learning at the University of Amsterdam. + The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io. +requirements: + - torchvision + - matplotlib + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/images/course_UvA-DL/13-contrastive-learning.jpg b/_notebooks/course_UvA-DL/13-contrastive-learning/.thumb.jpg similarity index 100% rename from docs/_static/images/course_UvA-DL/13-contrastive-learning.jpg rename to _notebooks/course_UvA-DL/13-contrastive-learning/.thumb.jpg diff --git a/_notebooks/course_UvA-DL/13-contrastive-learning/SimCLR.py b/_notebooks/course_UvA-DL/13-contrastive-learning/SimCLR.py new file mode 100644 index 0000000..1bc97bb --- /dev/null +++ b/_notebooks/course_UvA-DL/13-contrastive-learning/SimCLR.py @@ -0,0 +1,841 @@ +# %% [markdown] +#
+# Methods for self-supervised learning try to learn as much as possible from the data alone, so it can quickly be finetuned for a specific classification task. +# The benefit of self-supervised learning is that a large dataset can often easily be obtained. +# For instance, if we want to train a vision model on semantic segmentation for autonomous driving, we can collect large amounts of data by simply installing a camera in a car, and driving through a city for an hour. +# In contrast, if we would want to do supervised learning, we would have to manually label all those images before training a model. +# This is extremely expensive, and would likely take a couple of months to manually label the same amount of data. +# Further, self-supervised learning can provide an alternative to transfer learning from models pretrained on ImageNet since we could pretrain a model on a specific dataset/situation, e.g. traffic scenarios for autonomous driving. +# +# Within the last two years, a lot of new approaches have been proposed for self-supervised learning, in particular for images, that have resulted in great improvements over supervised models when few labels are available. +# The subfield that we will focus on in this tutorial is contrastive learning. +# Contrastive learning is motivated by the question mentioned above: how are images different from each other? +# Specifically, contrastive learning methods train a model to cluster an image and its slightly augmented version in latent space, while the distance to other images should be maximized. +# A very recent and simple method for this is [SimCLR](https://arxiv.org/abs/2006.10029), which is visualized below (figure credit - [Ting Chen et al. ](https://simclr.github.io/)). +# +#
![simclr contrastive learning](simclr_contrastive_learning.png){width="500px"}
+# +# The general setup is that we are given a dataset of images without any labels, and want to train a model on this data such that it can quickly adapt to any image recognition task afterward. +# During each training iteration, we sample a batch of images as usual. +# For each image, we create two versions by applying data augmentation techniques like cropping, Gaussian noise, blurring, etc. +# An example of such is shown on the left with the image of the dog. +# We will go into the details and effects of the chosen augmentation techniques later. +# On those images, we apply a CNN like ResNet and obtain as output a 1D feature vector on which we apply a small MLP. +# The output features of the two augmented images are then trained to be close to each other, while all other images in that batch should be as different as possible. +# This way, the model has to learn to recognize the content of the image that remains unchanged under the data augmentations, such as objects which we usually care about in supervised tasks. +# +# We will now implement this framework ourselves and discuss further details along the way. +# Let's first start with importing our standard libraries below: + +# %% +import os +import urllib.request +from copy import deepcopy +from urllib.error import HTTPError + +import lightning as L +import matplotlib +import matplotlib.pyplot as plt +import matplotlib_inline.backend_inline +import seaborn as sns +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torch.utils.data as data +import torchvision +from lightning.pytorch.callbacks import LearningRateMonitor, ModelCheckpoint +from torchvision import transforms +from torchvision.datasets import STL10 +from tqdm.notebook import tqdm + +plt.set_cmap("cividis") +# %matplotlib inline +matplotlib_inline.backend_inline.set_matplotlib_formats("svg", "pdf") # For export +matplotlib.rcParams["lines.linewidth"] = 2.0 +sns.set() + +# Import tensorboard +# %load_ext tensorboard + +# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10) +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") +# Path to the folder where the pretrained models are saved +CHECKPOINT_PATH = os.environ.get("PATH_CHECKPOINT", "saved_models/ContrastiveLearning/") +# In this notebook, we use data loaders with heavier computational processing. It is recommended to use as many +# workers as possible in a data loader, which corresponds to the number of CPU cores +NUM_WORKERS = os.cpu_count() + +# Setting the seed +L.seed_everything(42) + +# Ensure that all operations are deterministic on GPU (if used) for reproducibility +torch.backends.cudnn.determinstic = True +torch.backends.cudnn.benchmark = False + +device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu") +print("Device:", device) +print("Number of workers:", NUM_WORKERS) + +# %% [markdown] +# As in many tutorials before, we provide pre-trained models. +# Note that those models are slightly larger as normal (~100MB overall) since we use the default ResNet-18 architecture. +# If you are running this notebook locally, make sure to have sufficient disk space available. + +# %% +# Github URL where saved models are stored for this tutorial +base_url = "https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/" +# Files to download +pretrained_files = [ + "SimCLR.ckpt", + "ResNet.ckpt", + "tensorboards/SimCLR/events.out.tfevents.SimCLR", + "tensorboards/classification/ResNet/events.out.tfevents.ResNet", +] +pretrained_files += [f"LogisticRegression_{size}.ckpt" for size in [10, 20, 50, 100, 200, 500]] +# Create checkpoint path if it doesn't exist yet +os.makedirs(CHECKPOINT_PATH, exist_ok=True) + +# For each file, check whether it already exists. If not, try downloading it. +for file_name in pretrained_files: + file_path = os.path.join(CHECKPOINT_PATH, file_name) + if "/" in file_name: + os.makedirs(file_path.rsplit("/", 1)[0], exist_ok=True) + if not os.path.isfile(file_path): + file_url = base_url + file_name + print(f"Downloading {file_url}...") + try: + urllib.request.urlretrieve(file_url, file_path) + except HTTPError as e: + print( + "Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\n", + e, + ) + +# %% [markdown] +# ## SimCLR +# +# We will start our exploration of contrastive learning by discussing the effect of different data augmentation techniques, and how we can implement an efficient data loader for such. +# Next, we implement SimCLR with PyTorch Lightning, and finally train it on a large, unlabeled dataset. + +# %% [markdown] +# ### Data Augmentation for Contrastive Learning +# +# To allow efficient training, we need to prepare the data loading such that we sample two different, random augmentations for each image in the batch. +# The easiest way to do this is by creating a transformation that, when being called, applies a set of data augmentations to an image twice. +# This is implemented in the class `ContrastiveTransformations` below: + + +# %% +class ContrastiveTransformations: + def __init__(self, base_transforms, n_views=2): + self.base_transforms = base_transforms + self.n_views = n_views + + def __call__(self, x): + return [self.base_transforms(x) for i in range(self.n_views)] + + +# %% [markdown] +# The contrastive learning framework can easily be extended to have more _positive_ examples by sampling more than two augmentations of the same image. +# However, the most efficient training is usually obtained by using only two. +# +# Next, we can look at the specific augmentations we want to apply. +# The choice of the data augmentation to use is the most crucial hyperparameter in SimCLR since it directly affects how the latent space is structured, and what patterns might be learned from the data. +# Let's first take a look at some of the most popular data augmentations (figure credit - [Ting Chen and Geoffrey Hinton](https://ai.googleblog.com/2020/04/advancing-self-supervised-and-semi.html)): +# +#
+# +# All of them can be used, but it turns out that two augmentations stand out in their importance: crop-and-resize, and color distortion. +# Interestingly, however, they only lead to strong performance if they have been used together as discussed by [Ting Chen et al. ](https://arxiv.org/abs/2006.10029) in their SimCLR paper. +# When performing randomly cropping and resizing, we can distinguish between two situations: (a) cropped image A provides a local view of cropped image B, or (b) cropped images C and D show neighboring views of the same image (figure credit - [Ting Chen and Geoffrey Hinton](https://ai.googleblog.com/2020/04/advancing-self-supervised-and-semi.html)). +# +#
+# +# While situation (a) requires the model to learn some sort of scale invariance to make crops A and B similar in latent space, situation (b) is more challenging since the model needs to recognize an object beyond its limited view. +# However, without color distortion, there is a loophole that the model can exploit, namely that different crops of the same image usually look very similar in color space. +# Consider the picture of the dog above. +# Simply from the color of the fur and the green color tone of the background, you can reason that two patches belong to the same image without actually recognizing the dog in the picture. +# In this case, the model might end up focusing only on the color histograms of the images, and ignore other more generalizable features. +# If, however, we distort the colors in the two patches randomly and independently of each other, the model cannot rely on this simple feature anymore. +# Hence, by combining random cropping and color distortions, the model can only match two patches by learning generalizable representations. +# +# Overall, for our experiments, we apply a set of 5 transformations following the original SimCLR setup: random horizontal flip, crop-and-resize, color distortion, random grayscale, and gaussian blur. +# In comparison to the [original implementation](https://github.com/google-research/simclr), we reduce the effect of the color jitter slightly (0.5 instead of 0.8 for brightness, contrast, and saturation, and 0.1 instead of 0.2 for hue). +# In our experiments, this setting obtained better performance and was faster and more stable to train. +# If, for instance, the brightness scale highly varies in a dataset, the +# original settings can be more beneficial since the model can't rely on +# this information anymore to distinguish between images. + +# %% +contrast_transforms = transforms.Compose( + [ + transforms.RandomHorizontalFlip(), + transforms.RandomResizedCrop(size=96), + transforms.RandomApply([transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.1)], p=0.8), + transforms.RandomGrayscale(p=0.2), + transforms.GaussianBlur(kernel_size=9), + transforms.ToTensor(), + transforms.Normalize((0.5,), (0.5,)), + ] +) + +# %% [markdown] +# After discussing the data augmentation techniques, we can now focus on the dataset. +# In this tutorial, we will use the [STL10 dataset](https://cs.stanford.edu/~acoates/stl10/), which, similarly to CIFAR10, contains images of 10 classes: airplane, bird, car, cat, deer, dog, horse, monkey, ship, truck. +# However, the images have a higher resolution, namely $96\times 96$ pixels, and we are only provided with 500 labeled images per class. +# Additionally, we have a much larger set of $100,000$ unlabeled images which are similar to the training images but are sampled from a wider range of animals and vehicles. +# This makes the dataset ideal to showcase the benefits that self-supervised learning offers. +# +# Luckily, the STL10 dataset is provided through torchvision. +# Keep in mind, however, that since this dataset is relatively large and has a considerably higher resolution than CIFAR10, it requires more disk space (~3GB) and takes a bit of time to download. +# For our initial discussion of self-supervised learning and SimCLR, we +# will create two data loaders with our contrastive transformations above: +# the `unlabeled_data` will be used to train our model via contrastive +# learning, and `train_data_contrast` will be used as a validation set in +# contrastive learning. + +# %% +unlabeled_data = STL10( + root=DATASET_PATH, + split="unlabeled", + download=True, + transform=ContrastiveTransformations(contrast_transforms, n_views=2), +) +train_data_contrast = STL10( + root=DATASET_PATH, + split="train", + download=True, + transform=ContrastiveTransformations(contrast_transforms, n_views=2), +) + +# %% [markdown] +# Finally, before starting with our implementation of SimCLR, let's look +# at some example image pairs sampled with our augmentations: + +# %% +# Visualize some examples +L.seed_everything(42) +NUM_IMAGES = 6 +imgs = torch.stack([img for idx in range(NUM_IMAGES) for img in unlabeled_data[idx][0]], dim=0) +img_grid = torchvision.utils.make_grid(imgs, nrow=6, normalize=True, pad_value=0.9) +img_grid = img_grid.permute(1, 2, 0) + +plt.figure(figsize=(10, 5)) +plt.title("Augmented image examples of the STL10 dataset") +plt.imshow(img_grid) +plt.axis("off") +plt.show() +plt.close() + +# %% [markdown] +# We see the wide variety of our data augmentation, including randomly cropping, grayscaling, gaussian blur, and color distortion. +# Thus, it remains a challenging task for the model to match two, independently augmented patches of the same image. + +# %% [markdown] +# ### SimCLR implementation +# +# Using the data loader pipeline above, we can now implement SimCLR. +# At each iteration, we get for every image $x$ two differently augmented versions, which we refer to as $\tilde{x}_i$ and $\tilde{x}_j$. +# Both of these images are encoded into a one-dimensional feature vector, between which we want to maximize similarity which minimizes it to all other images in the batch. +# The encoder network is split into two parts: a base encoder network $f(\cdot)$, and a projection head $g(\cdot)$. +# The base network is usually a deep CNN as we have seen in e.g. [Tutorial 5](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial5/Inception_ResNet_DenseNet.html) before, and is responsible for extracting a representation vector from the augmented data examples. +# In our experiments, we will use the common ResNet-18 architecture as $f(\cdot)$, and refer to the output as $f(\tilde{x}_i)=h_i$. +# The projection head $g(\cdot)$ maps the representation $h$ into a space where we apply the contrastive loss, i.e., compare similarities between vectors. +# It is often chosen to be a small MLP with non-linearities, and for simplicity, we follow the original SimCLR paper setup by defining it as a two-layer MLP with ReLU activation in the hidden layer. +# Note that in the follow-up paper, [SimCLRv2](https://arxiv.org/abs/2006.10029), the authors mention that larger/wider MLPs can boost the performance considerably. +# This is why we apply an MLP with four times larger hidden dimensions, but deeper MLPs showed to overfit on the given dataset. +# The general setup is visualized below (figure credit - [Ting Chen et al. ](https://arxiv.org/abs/2006.10029)): +# +#
+# +# After finishing the training with contrastive learning, we will remove the projection head $g(\cdot)$, and use $f(\cdot)$ as a pretrained feature extractor. +# The representations $z$ that come out of the projection head $g(\cdot)$ have been shown to perform worse than those of the base network $f(\cdot)$ when finetuning the network for a new task. +# This is likely because the representations $z$ are trained to become invariant to many features like the color that can be important for downstream tasks. +# Thus, $g(\cdot)$ is only needed for the contrastive learning stage. +# +# Now that the architecture is described, let's take a closer look at how we train the model. +# As mentioned before, we want to maximize the similarity between the representations of the two augmented versions of the same image, i.e., $z_i$ and $z_j$ in the figure above, while minimizing it to all other examples in the batch. +# SimCLR thereby applies the InfoNCE loss, originally proposed by [Aaron van den Oord et al. ](https://arxiv.org/abs/1807.03748) for contrastive learning. +# In short, the InfoNCE loss compares the similarity of $z_i$ and $z_j$ to the similarity of $z_i$ to any other representation in the batch by performing a softmax over the similarity values. +# The loss can be formally written as: +# $$ +# \ell_{i,j}=-\log \frac{\exp(\text{sim}(z_i,z_j)/\tau)}{\sum_{k=1}^{2N}\mathbb{1}_{[k\neq i]}\exp(\text{sim}(z_i,z_k)/\tau)}=-\text{sim}(z_i,z_j)/\tau+\log\left[\sum_{k=1}^{2N}\mathbb{1}_{[k\neq i]}\exp(\text{sim}(z_i,z_k)/\tau)\right] +# $$ +# The function $\text{sim}$ is a similarity metric, and the hyperparameter $\tau$ is called temperature determining how peaked the distribution is. +# Since many similarity metrics are bounded, the temperature parameter allows us to balance the influence of many dissimilar image patches versus one similar patch. +# The similarity metric that is used in SimCLR is cosine similarity, as defined below: +# $$ +# \text{sim}(z_i,z_j) = \frac{z_i^\top \cdot z_j}{||z_i||\cdot||z_j||} +# $$ +# The maximum cosine similarity possible is $1$, while the minimum is $-1$. +# In general, we will see that the features of two different images will converge to a cosine similarity around zero since the minimum, $-1$, would require $z_i$ and $z_j$ to be in the exact opposite direction in all feature dimensions, which does not allow for great flexibility. +# +# Finally, now that we have discussed all details, let's implement SimCLR below as a PyTorch Lightning module: + + +# %% +class SimCLR(L.LightningModule): + def __init__(self, hidden_dim, lr, temperature, weight_decay, max_epochs=500): + super().__init__() + self.save_hyperparameters() + assert self.hparams.temperature > 0.0, "The temperature must be a positive float!" + # Base model f(.) + self.convnet = torchvision.models.resnet18( + pretrained=False, num_classes=4 * hidden_dim + ) # num_classes is the output size of the last linear layer + # The MLP for g(.) consists of Linear->ReLU->Linear + self.convnet.fc = nn.Sequential( + self.convnet.fc, # Linear(ResNet output, 4*hidden_dim) + nn.ReLU(inplace=True), + nn.Linear(4 * hidden_dim, hidden_dim), + ) + + def configure_optimizers(self): + optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr, weight_decay=self.hparams.weight_decay) + lr_scheduler = optim.lr_scheduler.CosineAnnealingLR( + optimizer, T_max=self.hparams.max_epochs, eta_min=self.hparams.lr / 50 + ) + return [optimizer], [lr_scheduler] + + def info_nce_loss(self, batch, mode="train"): + imgs, _ = batch + imgs = torch.cat(imgs, dim=0) + + # Encode all images + feats = self.convnet(imgs) + # Calculate cosine similarity + cos_sim = F.cosine_similarity(feats[:, None, :], feats[None, :, :], dim=-1) + # Mask out cosine similarity to itself + self_mask = torch.eye(cos_sim.shape[0], dtype=torch.bool, device=cos_sim.device) + cos_sim.masked_fill_(self_mask, -9e15) + # Find positive example -> batch_size//2 away from the original example + pos_mask = self_mask.roll(shifts=cos_sim.shape[0] // 2, dims=0) + # InfoNCE loss + cos_sim = cos_sim / self.hparams.temperature + nll = -cos_sim[pos_mask] + torch.logsumexp(cos_sim, dim=-1) + nll = nll.mean() + + # Logging loss + self.log(mode + "_loss", nll) + # Get ranking position of positive example + comb_sim = torch.cat( + [cos_sim[pos_mask][:, None], cos_sim.masked_fill(pos_mask, -9e15)], # First position positive example + dim=-1, + ) + sim_argsort = comb_sim.argsort(dim=-1, descending=True).argmin(dim=-1) + # Logging ranking metrics + self.log(mode + "_acc_top1", (sim_argsort == 0).float().mean()) + self.log(mode + "_acc_top5", (sim_argsort < 5).float().mean()) + self.log(mode + "_acc_mean_pos", 1 + sim_argsort.float().mean()) + + return nll + + def training_step(self, batch, batch_idx): + return self.info_nce_loss(batch, mode="train") + + def validation_step(self, batch, batch_idx): + self.info_nce_loss(batch, mode="val") + + +# %% [markdown] +# Alternatively to performing the validation on the contrastive learning loss as well, we could also take a simple, small downstream task, and track the performance of the base network $f(\cdot)$ on that. +# However, in this tutorial, we will restrict ourselves to the STL10 +# dataset where we use the task of image classification on STL10 as our +# test task. + +# %% [markdown] +# ### Training +# +# Now that we have implemented SimCLR and the data loading pipeline, we are ready to train the model. +# We will use the same training function setup as usual. +# For saving the best model checkpoint, we track the metric `val_acc_top5`, which describes how often the correct image patch is within the top-5 most similar examples in the batch. +# This is usually less noisy than the top-1 metric, making it a better metric to choose the best model from. + + +# %% +def train_simclr(batch_size, max_epochs=500, **kwargs): + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, "SimCLR"), + accelerator="auto", + devices=1, + max_epochs=max_epochs, + callbacks=[ + ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc_top5"), + LearningRateMonitor("epoch"), + ], + ) + trainer.logger._default_hp_metric = None # Optional logging argument that we don't need + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "SimCLR.ckpt") + if os.path.isfile(pretrained_filename): + print(f"Found pretrained model at {pretrained_filename}, loading...") + # Automatically loads the model with the saved hyperparameters + model = SimCLR.load_from_checkpoint(pretrained_filename) + else: + train_loader = data.DataLoader( + unlabeled_data, + batch_size=batch_size, + shuffle=True, + drop_last=True, + pin_memory=True, + num_workers=NUM_WORKERS, + ) + val_loader = data.DataLoader( + train_data_contrast, + batch_size=batch_size, + shuffle=False, + drop_last=False, + pin_memory=True, + num_workers=NUM_WORKERS, + ) + L.seed_everything(42) # To be reproducable + model = SimCLR(max_epochs=max_epochs, **kwargs) + trainer.fit(model, train_loader, val_loader) + # Load best checkpoint after training + model = SimCLR.load_from_checkpoint(trainer.checkpoint_callback.best_model_path) + + return model + + +# %% [markdown] +# A common observation in contrastive learning is that the larger the batch size, the better the models perform. +# A larger batch size allows us to compare each image to more negative examples, leading to overall smoother loss gradients. +# However, in our case, we experienced that a batch size of 256 was sufficient to get good results. + +# %% +simclr_model = train_simclr( + batch_size=256, hidden_dim=128, lr=5e-4, temperature=0.07, weight_decay=1e-4, max_epochs=500 +) + +# %% [markdown] +# To get an intuition of how training with contrastive learning behaves, we can take a look at the TensorBoard below: + +# %% +# %tensorboard --logdir ../saved_models/tutorial17/tensorboards/SimCLR/ + +# %% [markdown] +#
![tensorboard simclr](tensorboard_simclr.png){width="1200px"}
+# +# One thing to note is that contrastive learning benefits a lot from long training. +# The shown plot above is from a training that took approx. +# 1 day on a NVIDIA TitanRTX. +# Training the model for even longer might reduce its loss further, but we did not experience any gains from it for the downstream task on image classification. +# In general, contrastive learning can also benefit from using larger models, if sufficient unlabeled data is available. + +# %% [markdown] +# ## Logistic Regression +# +#
+# After we have trained our model via contrastive learning, we can deploy it on downstream tasks and see how well it performs with little data. +# A common setup, which also verifies whether the model has learned generalized representations, is to perform Logistic Regression on the features. +# In other words, we learn a single, linear layer that maps the representations to a class prediction. +# Since the base network $f(\cdot)$ is not changed during the training process, the model can only perform well if the representations of $h$ describe all features that might be necessary for the task. +# Further, we do not have to worry too much about overfitting since we have very few parameters that are trained. +# Hence, we might expect that the model can perform well even with very little data. +# +# First, let's implement a simple Logistic Regression setup for which we assume that the images already have been encoded in their feature vectors. +# If very little data is available, it might be beneficial to dynamically encode the images during training so that we can also apply data augmentations. +# However, the way we implement it here is much more efficient and can be trained within a few seconds. +# Further, using data augmentations did not show any significant gain in this simple setup. + + +# %% +class LogisticRegression(L.LightningModule): + def __init__(self, feature_dim, num_classes, lr, weight_decay, max_epochs=100): + super().__init__() + self.save_hyperparameters() + # Mapping from representation h to classes + self.model = nn.Linear(feature_dim, num_classes) + + def configure_optimizers(self): + optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr, weight_decay=self.hparams.weight_decay) + lr_scheduler = optim.lr_scheduler.MultiStepLR( + optimizer, milestones=[int(self.hparams.max_epochs * 0.6), int(self.hparams.max_epochs * 0.8)], gamma=0.1 + ) + return [optimizer], [lr_scheduler] + + def _calculate_loss(self, batch, mode="train"): + feats, labels = batch + preds = self.model(feats) + loss = F.cross_entropy(preds, labels) + acc = (preds.argmax(dim=-1) == labels).float().mean() + + self.log(mode + "_loss", loss) + self.log(mode + "_acc", acc) + return loss + + def training_step(self, batch, batch_idx): + return self._calculate_loss(batch, mode="train") + + def validation_step(self, batch, batch_idx): + self._calculate_loss(batch, mode="val") + + def test_step(self, batch, batch_idx): + self._calculate_loss(batch, mode="test") + + +# %% [markdown] +# The data we use is the training and test set of STL10. +# The training contains 500 images per class, while the test set has 800 images per class. + +# %% +img_transforms = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) + +train_img_data = STL10(root=DATASET_PATH, split="train", download=True, transform=img_transforms) +test_img_data = STL10(root=DATASET_PATH, split="test", download=True, transform=img_transforms) + +print("Number of training examples:", len(train_img_data)) +print("Number of test examples:", len(test_img_data)) + +# %% [markdown] +# Next, we implement a small function to encode all images in our datasets. +# The output representations are then used as inputs to the Logistic Regression model. + + +# %% +@torch.no_grad() +def prepare_data_features(model, dataset): + # Prepare model + network = deepcopy(model.convnet) + network.fc = nn.Identity() # Removing projection head g(.) + network.eval() + network.to(device) + + # Encode all images + data_loader = data.DataLoader(dataset, batch_size=64, num_workers=NUM_WORKERS, shuffle=False, drop_last=False) + feats, labels = [], [] + for batch_imgs, batch_labels in tqdm(data_loader): + batch_imgs = batch_imgs.to(device) + batch_feats = network(batch_imgs) + feats.append(batch_feats.detach().cpu()) + labels.append(batch_labels) + + feats = torch.cat(feats, dim=0) + labels = torch.cat(labels, dim=0) + + # Sort images by labels + labels, idxs = labels.sort() + feats = feats[idxs] + + return data.TensorDataset(feats, labels) + + +# %% [markdown] +# Let's apply the function to both training and test set below. + +# %% +train_feats_simclr = prepare_data_features(simclr_model, train_img_data) +test_feats_simclr = prepare_data_features(simclr_model, test_img_data) + +# %% [markdown] +# Finally, we can write a training function as usual. +# We evaluate the model on the test set every 10 epochs to allow early +# stopping, but the low frequency of the validation ensures that we do not +# overfit too much on the test set. + + +# %% +def train_logreg(batch_size, train_feats_data, test_feats_data, model_suffix, max_epochs=100, **kwargs): + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, "LogisticRegression"), + accelerator="auto", + devices=1, + max_epochs=max_epochs, + callbacks=[ + ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc"), + LearningRateMonitor("epoch"), + ], + enable_progress_bar=False, + check_val_every_n_epoch=10, + ) + trainer.logger._default_hp_metric = None + + # Data loaders + train_loader = data.DataLoader( + train_feats_data, batch_size=batch_size, shuffle=True, drop_last=False, pin_memory=True, num_workers=0 + ) + test_loader = data.DataLoader( + test_feats_data, batch_size=batch_size, shuffle=False, drop_last=False, pin_memory=True, num_workers=0 + ) + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, f"LogisticRegression_{model_suffix}.ckpt") + if os.path.isfile(pretrained_filename): + print(f"Found pretrained model at {pretrained_filename}, loading...") + model = LogisticRegression.load_from_checkpoint(pretrained_filename) + else: + L.seed_everything(42) # To be reproducable + model = LogisticRegression(**kwargs) + trainer.fit(model, train_loader, test_loader) + model = LogisticRegression.load_from_checkpoint(trainer.checkpoint_callback.best_model_path) + + # Test best model on train and validation set + train_result = trainer.test(model, dataloaders=train_loader, verbose=False) + test_result = trainer.test(model, dataloaders=test_loader, verbose=False) + result = {"train": train_result[0]["test_acc"], "test": test_result[0]["test_acc"]} + + return model, result + + +# %% [markdown] +# Despite the training dataset of STL10 already only having 500 labeled images per class, we will perform experiments with even smaller datasets. +# Specifically, we train a Logistic Regression model for datasets with only 10, 20, 50, 100, 200, and all 500 examples per class. +# This gives us an intuition on how well the representations learned by contrastive learning can be transfered to a image recognition task like this classification. +# First, let's define a function to create the intended sub-datasets from the full training set: + + +# %% +def get_smaller_dataset(original_dataset, num_imgs_per_label): + new_dataset = data.TensorDataset( + *(t.unflatten(0, (10, 500))[:, :num_imgs_per_label].flatten(0, 1) for t in original_dataset.tensors) + ) + return new_dataset + + +# %% [markdown] +# Next, let's run all models. +# Despite us training 6 models, this cell could be run within a minute or two without the pretrained models. + +# %% +results = {} +for num_imgs_per_label in [10, 20, 50, 100, 200, 500]: + sub_train_set = get_smaller_dataset(train_feats_simclr, num_imgs_per_label) + _, small_set_results = train_logreg( + batch_size=64, + train_feats_data=sub_train_set, + test_feats_data=test_feats_simclr, + model_suffix=num_imgs_per_label, + feature_dim=train_feats_simclr.tensors[0].shape[1], + num_classes=10, + lr=1e-3, + weight_decay=1e-3, + ) + results[num_imgs_per_label] = small_set_results + +# %% [markdown] +# Finally, let's plot the results. + +# %% +dataset_sizes = sorted(k for k in results) +test_scores = [results[k]["test"] for k in dataset_sizes] + +fig = plt.figure(figsize=(6, 4)) +plt.plot( + dataset_sizes, + test_scores, + "--", + color="#000", + marker="*", + markeredgecolor="#000", + markerfacecolor="y", + markersize=16, +) +plt.xscale("log") +plt.xticks(dataset_sizes, labels=dataset_sizes) +plt.title("STL10 classification over dataset size", fontsize=14) +plt.xlabel("Number of images per class") +plt.ylabel("Test accuracy") +plt.minorticks_off() +plt.show() + +for k, score in zip(dataset_sizes, test_scores): + print(f"Test accuracy for {k:3d} images per label: {100*score:4.2f}%") + +# %% [markdown] +# As one would expect, the classification performance improves the more data we have. +# However, with only 10 images per class, we can already classify more than 60% of the images correctly. +# This is quite impressive, considering that the images are also higher dimensional than e.g. CIFAR10. +# With the full dataset, we achieve an accuracy of 81%. +# The increase between 50 to 500 images per class might suggest a linear increase in performance with an exponentially larger dataset. +# However, with even more data, we could also finetune $f(\cdot)$ in the training process, allowing for the representations to adapt more to the specific classification task given. +# +# To set the results above into perspective, we will train the base +# network, a ResNet-18, on the classification task from scratch. + +# %% [markdown] +# ## Baseline +# +# As a baseline to our results above, we will train a standard ResNet-18 with random initialization on the labeled training set of STL10. +# The results will give us an indication of the advantages that contrastive learning on unlabeled data has compared to using only supervised training. +# The implementation of the model is straightforward since the ResNet +# architecture is provided in the torchvision library. + + +# %% +class ResNet(L.LightningModule): + def __init__(self, num_classes, lr, weight_decay, max_epochs=100): + super().__init__() + self.save_hyperparameters() + self.model = torchvision.models.resnet18(pretrained=False, num_classes=num_classes) + + def configure_optimizers(self): + optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr, weight_decay=self.hparams.weight_decay) + lr_scheduler = optim.lr_scheduler.MultiStepLR( + optimizer, milestones=[int(self.hparams.max_epochs * 0.7), int(self.hparams.max_epochs * 0.9)], gamma=0.1 + ) + return [optimizer], [lr_scheduler] + + def _calculate_loss(self, batch, mode="train"): + imgs, labels = batch + preds = self.model(imgs) + loss = F.cross_entropy(preds, labels) + acc = (preds.argmax(dim=-1) == labels).float().mean() + + self.log(mode + "_loss", loss) + self.log(mode + "_acc", acc) + return loss + + def training_step(self, batch, batch_idx): + return self._calculate_loss(batch, mode="train") + + def validation_step(self, batch, batch_idx): + self._calculate_loss(batch, mode="val") + + def test_step(self, batch, batch_idx): + self._calculate_loss(batch, mode="test") + + +# %% [markdown] +# It is clear that the ResNet easily overfits on the training data since its parameter count is more than 1000 times larger than the dataset size. +# To make the comparison to the contrastive learning models fair, we apply data augmentations similar to the ones we used before: horizontal flip, crop-and-resize, grayscale, and gaussian blur. +# Color distortions as before are not used because the color distribution of an image showed to be an important feature for the classification. +# Hence, we observed no noticeable performance gains when adding color distortions to the set of augmentations. +# Similarly, we restrict the resizing operation before cropping to the max. +# 125% of its original resolution, instead of 1250% as done in SimCLR. +# This is because, for classification, the model needs to recognize the full object, while in contrastive learning, we only want to check whether two patches belong to the same image/object. +# Hence, the chosen augmentations below are overall weaker than in the contrastive learning case. + +# %% +train_transforms = transforms.Compose( + [ + transforms.RandomHorizontalFlip(), + transforms.RandomResizedCrop(size=96, scale=(0.8, 1.0)), + transforms.RandomGrayscale(p=0.2), + transforms.GaussianBlur(kernel_size=9, sigma=(0.1, 0.5)), + transforms.ToTensor(), + transforms.Normalize((0.5,), (0.5,)), + ] +) + +train_img_aug_data = STL10(root=DATASET_PATH, split="train", download=True, transform=train_transforms) + +# %% [markdown] +# The training function for the ResNet is almost identical to the Logistic Regression setup. +# Note that we allow the ResNet to perform validation every 2 epochs to +# also check whether the model overfits strongly in the first iterations +# or not. + + +# %% +def train_resnet(batch_size, max_epochs=100, **kwargs): + trainer = L.Trainer( + default_root_dir=os.path.join(CHECKPOINT_PATH, "ResNet"), + accelerator="auto", + devices=1, + max_epochs=max_epochs, + callbacks=[ + ModelCheckpoint(save_weights_only=True, mode="max", monitor="val_acc"), + LearningRateMonitor("epoch"), + ], + check_val_every_n_epoch=2, + ) + trainer.logger._default_hp_metric = None + + # Data loaders + train_loader = data.DataLoader( + train_img_aug_data, + batch_size=batch_size, + shuffle=True, + drop_last=True, + pin_memory=True, + num_workers=NUM_WORKERS, + ) + test_loader = data.DataLoader( + test_img_data, batch_size=batch_size, shuffle=False, drop_last=False, pin_memory=True, num_workers=NUM_WORKERS + ) + + # Check whether pretrained model exists. If yes, load it and skip training + pretrained_filename = os.path.join(CHECKPOINT_PATH, "ResNet.ckpt") + if os.path.isfile(pretrained_filename): + print("Found pretrained model at %s, loading..." % pretrained_filename) + model = ResNet.load_from_checkpoint(pretrained_filename) + else: + L.seed_everything(42) # To be reproducable + model = ResNet(**kwargs) + trainer.fit(model, train_loader, test_loader) + model = ResNet.load_from_checkpoint(trainer.checkpoint_callback.best_model_path) + + # Test best model on validation set + train_result = trainer.test(model, dataloaders=train_loader, verbose=False) + val_result = trainer.test(model, dataloaders=test_loader, verbose=False) + result = {"train": train_result[0]["test_acc"], "test": val_result[0]["test_acc"]} + + return model, result + + +# %% [markdown] +# Finally, let's train the model and check its results: + +# %% +resnet_model, resnet_result = train_resnet(batch_size=64, num_classes=10, lr=1e-3, weight_decay=2e-4, max_epochs=100) +print(f"Accuracy on training set: {100*resnet_result['train']:4.2f}%") +print(f"Accuracy on test set: {100*resnet_result['test']:4.2f}%") + +# %% [markdown] +# The ResNet trained from scratch achieves 73.31% on the test set. +# This is almost 8% less than the contrastive learning model, and even slightly less than SimCLR achieves with 1/10 of the data. +# This shows that self-supervised, contrastive learning provides +# considerable performance gains by leveraging large amounts of unlabeled +# data when little labeled data is available. + +# %% [markdown] +# ## Conclusion +# +# In this tutorial, we have discussed self-supervised contrastive learning and implemented SimCLR as an example method. +# We have applied it to the STL10 dataset and showed that it can learn generalizable representations that we can use to train simple classification models. +# With 500 images per label, it achieved an 8% higher accuracy than a similar model solely trained from supervision and performs on par with it when only using a tenth of the labeled data. +# Our experimental results are limited to a single dataset, but recent works such as [Ting Chen et al. ](https://arxiv.org/abs/2006.10029) showed similar trends for larger datasets like ImageNet. +# Besides the discussed hyperparameters, the size of the model seems to be important in contrastive learning as well. +# If a lot of unlabeled data is available, larger models can achieve much stronger results and come close to their supervised baselines. +# Further, there are also approaches for combining contrastive and supervised learning, leading to performance gains beyond supervision (see [Khosla et al.](https://arxiv.org/abs/2004.11362)). +# Moreover, contrastive learning is not the only approach to self-supervised learning that has come up in the last two years and showed great results. +# Other methods include distillation-based methods like [BYOL](https://arxiv.org/abs/2006.07733) and redundancy reduction techniques like [Barlow Twins](https://arxiv.org/abs/2103.03230). +# There is a lot more to explore in the self-supervised domain, and more, impressive steps ahead are to be expected. +# +# ### References +# +# [1] Chen, T., Kornblith, S., Norouzi, M., and Hinton, G. (2020). +# A simple framework for contrastive learning of visual representations. +# In International conference on machine learning (pp. +# 1597-1607). +# PMLR. +# ([link](https://arxiv.org/abs/2002.05709)) +# +# [2] Chen, T., Kornblith, S., Swersky, K., Norouzi, M., and Hinton, G. (2020). +# Big self-supervised models are strong semi-supervised learners. +# NeurIPS 2021 ([link](https://arxiv.org/abs/2006.10029)). +# +# [3] Oord, A. V. D., Li, Y., and Vinyals, O. +# (2018). +# Representation learning with contrastive predictive coding. +# arXiv preprint arXiv:1807.03748. +# ([link](https://arxiv.org/abs/1807.03748)) +# +# [4] Grill, J.B., Strub, F., Altché, F., Tallec, C., Richemond, P.H., Buchatskaya, E., Doersch, C., Pires, B.A., Guo, Z.D., Azar, M.G. +# and Piot, B. +# (2020). +# Bootstrap your own latent: A new approach to self-supervised learning. +# arXiv preprint arXiv:2006.07733. +# ([link](https://arxiv.org/abs/2006.07733)) +# +# [5] Khosla, P., Teterwak, P., Wang, C., Sarna, A., Tian, Y., Isola, P., Maschinot, A., Liu, C. and Krishnan, D. (2020). +# Supervised contrastive learning. +# arXiv preprint arXiv:2004.11362. +# ([link](https://arxiv.org/abs/2004.11362)) +# +# [6] Zbontar, J., Jing, L., Misra, I., LeCun, Y. and Deny, S. (2021). +# Barlow twins: Self-supervised learning via redundancy reduction. +# arXiv preprint arXiv:2103.03230. +# ([link](https://arxiv.org/abs/2103.03230)) diff --git a/_notebooks/course_UvA-DL/13-contrastive-learning/crop_views.svg b/_notebooks/course_UvA-DL/13-contrastive-learning/crop_views.svg new file mode 100644 index 0000000..6ea2ba9 --- /dev/null +++ b/_notebooks/course_UvA-DL/13-contrastive-learning/crop_views.svg @@ -0,0 +1 @@ + diff --git a/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_contrastive_learning.png b/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_contrastive_learning.png new file mode 100644 index 0000000..622b411 Binary files /dev/null and b/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_contrastive_learning.png differ diff --git a/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_data_augmentations.jpg b/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_data_augmentations.jpg new file mode 100644 index 0000000..57a440c Binary files /dev/null and b/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_data_augmentations.jpg differ diff --git a/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_network_setup.svg b/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_network_setup.svg new file mode 100644 index 0000000..687a62d --- /dev/null +++ b/_notebooks/course_UvA-DL/13-contrastive-learning/simclr_network_setup.svg @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_notebooks/course_UvA-DL/13-contrastive-learning/tensorboard_simclr.png b/_notebooks/course_UvA-DL/13-contrastive-learning/tensorboard_simclr.png new file mode 100644 index 0000000..bfe6859 Binary files /dev/null and b/_notebooks/course_UvA-DL/13-contrastive-learning/tensorboard_simclr.png differ diff --git a/_notebooks/flash_tutorials/electricity_forecasting/.meta.yml b/_notebooks/flash_tutorials/electricity_forecasting/.meta.yml new file mode 100644 index 0000000..ab165e1 --- /dev/null +++ b/_notebooks/flash_tutorials/electricity_forecasting/.meta.yml @@ -0,0 +1,24 @@ +title: Electricity Price Forecasting with N-BEATS +author: Ethan Harris (ethan@pytorchlightning.ai) +created: 2021-11-23 +updated: 2021-12-16 +license: CC BY-SA +build: 3 +tags: + - Tabular + - Forecasting + - Timeseries +description: | + This tutorial covers using Lightning Flash and it's integration with PyTorch Forecasting to train an autoregressive + model (N-BEATS) on hourly electricity pricing data. We show how the built-in interpretability tools from PyTorch + Forecasting can be used with Flash to plot the trend and daily seasonality in our data discovered by the model. We + also cover how features from PyTorch Lightning such as the learning rate finder can be used easily with Flash. As a + bonus, we show hat we can resample daily observations from the data to discover weekly trends instead. +requirements: + - pandas==1.1.5 + - lightning-flash[tabular]>=0.6.0 + - pytorch-lightning==1.3.6 # todo: update to latest + - numpy<1.24 +accelerator: + - GPU + - CPU diff --git a/_notebooks/flash_tutorials/electricity_forecasting/.thumb.svg b/_notebooks/flash_tutorials/electricity_forecasting/.thumb.svg new file mode 100644 index 0000000..3f8037e --- /dev/null +++ b/_notebooks/flash_tutorials/electricity_forecasting/.thumb.svg @@ -0,0 +1 @@ + diff --git a/_notebooks/flash_tutorials/electricity_forecasting/diagram.png b/_notebooks/flash_tutorials/electricity_forecasting/diagram.png new file mode 100644 index 0000000..47120db Binary files /dev/null and b/_notebooks/flash_tutorials/electricity_forecasting/diagram.png differ diff --git a/_notebooks/flash_tutorials/electricity_forecasting/electricity_forecasting.py b/_notebooks/flash_tutorials/electricity_forecasting/electricity_forecasting.py new file mode 100644 index 0000000..621cd30 --- /dev/null +++ b/_notebooks/flash_tutorials/electricity_forecasting/electricity_forecasting.py @@ -0,0 +1,311 @@ +# %% [markdown] +# In this tutorial we'll look at using [Lightning Flash](https://github.com/Lightning-AI/lightning-flash) and it's +# integration with [PyTorch Forecasting](https://github.com/jdb78/pytorch-forecasting) for autoregressive modelling of +# electricity prices using [the N-BEATS model](https://arxiv.org/abs/1905.10437). +# We'll start by using N-BEATS to uncover daily patterns (seasonality) from hourly observations and then show how we can +# resample daily averages to uncover weekly patterns too. +# +# Along the way, we'll see how the built-in tools from PyTorch Lightning, like the learning rate finder, can be used +# seamlessly with Flash to help make the process of putting a model together as smooth as possible. + +# %% + +import os +from typing import Any, Dict + +import flash +import matplotlib.pyplot as plt +import pandas as pd +import torch +from flash.core.data.utils import download_data +from flash.core.integrations.pytorch_forecasting import convert_predictions +from flash.tabular.forecasting import TabularForecaster, TabularForecastingData + +DATASET_PATH = os.environ.get("PATH_DATASETS", "data/") + +# %% [markdown] +# ## Loading the data +# +# We'll use the Spanish hourly energy demand generation and weather data set from Kaggle: +# https://www.kaggle.com/nicholasjhana/energy-consumption-generation-prices-and-weather +# +# First, download the data: + +# %% +download_data("https://pl-flash-data.s3.amazonaws.com/kaggle_electricity.zip", DATASET_PATH) + +# %% [markdown] +# ## Data loading +# +# To load the data, we start by loading the CSV file into a pandas DataFrame: + +# %% +df_energy_hourly = pd.read_csv(f"{DATASET_PATH}/energy_dataset.csv", parse_dates=["time"]) + +# %% [markdown] +# Before we can load the data into Flash, there are a few preprocessing steps we need to take. +# The first preprocessing step is to set the `time` field as the index (formatted as a datetime). +# The second step is to resample the data to the desired frequency in case it is different from the desired observation +# frequency. +# Since we are performing autoregressive modelling, we can remove all columns except for `"price actual"`. +# +# For the third preprocessing step, we need to create a "time_idx" column. +# The "time_idx" column should contain integers corresponding to the observation index (e.g. in our case the difference +# between two "time_idx" values is the number of hours between the observations). +# To do this we convert the datetime to an index by taking the nanoseconds value and dividing by the number of +# nanoseconds in a single unit of our chosen frequency. +# We then subtract the minimum value so it starts at zero (although it would still work without this step). +# +# The Flash `TabularForecastingData` (which uses the `TimeSeriesDataSet` from PyTorch Forecasting internally) also +# supports loading data from multiple time series (e.g. you may have electricity data from multiple countries). +# To indicate that our data is all from the same series, we add a `constant` column with a constant value of zero. +# +# Here's the full preprocessing function: + +# %% + + +def preprocess(df: pd.DataFrame, frequency: str = "1H") -> pd.DataFrame: + df["time"] = pd.to_datetime(df["time"], utc=True, infer_datetime_format=True) + df.set_index("time", inplace=True) + + df = df.resample(frequency).mean() + + df = df.filter(["price actual"]) + + df["time_idx"] = (df.index.view(int) / pd.Timedelta(frequency).value).astype(int) + df["time_idx"] -= df["time_idx"].min() + + df["constant"] = 0 + + return df + + +df_energy_hourly = preprocess(df_energy_hourly) + +# %% [markdown] +# ## Creating the Flash DataModule +# +# Now, we can create a `TabularForecastingData`. +# The role of the `TabularForecastingData` is to split up our time series into windows which include a region to encode +# (of size `max_encoder_length`) and a region to predict (of size `max_prediction_length`) which will be used to compute +# the loss. +# The size of the prediction window should be chosen depending on the kinds of trends we would like our model to +# uncover. +# In our case, we are interested in how electricity prices change throughout the day, so a one day prediction window +# (`max_prediction_length = 24`) makes sense here. +# The size of the encoding window can vary, however, in the [N-BEATS paper](https://arxiv.org/abs/1905.10437) the +# authors suggest using an encoder length of between two and ten times the prediction length. +# We therefore choose two days (`max_encoder_length = 48`) as the encoder length. + +# %% +max_prediction_length = 24 +max_encoder_length = 24 * 2 + +training_cutoff = df_energy_hourly["time_idx"].max() - max_prediction_length + +datamodule = TabularForecastingData.from_data_frame( + time_idx="time_idx", + target="price actual", + group_ids=["constant"], + max_encoder_length=max_encoder_length, + max_prediction_length=max_prediction_length, + time_varying_unknown_reals=["price actual"], + train_data_frame=df_energy_hourly[df_energy_hourly["time_idx"] <= training_cutoff], + val_data_frame=df_energy_hourly, + batch_size=256, +) + +# %% [markdown] +# ## Creating the Flash Task +# +# Now, we're ready to create a `TabularForecaster`. +# The N-BEATS model has two primary hyper-parameters:`"widths"`, and `"backcast_loss_ratio"`. +# In the [PyTorch Forecasting Documentation](https://pytorch-forecasting.readthedocs.io/en/latest/api/pytorch_forecasting.models.nbeats.NBeats.html), +# the authors recommend using `"widths"` of `[32, 512]`. +# In order to prevent overfitting with smaller datasets, a good rule of thumb is to limit the number of parameters of +# your model. +# For this reason, we use `"widths"` of `[16, 256]`. +# +# To understand the `"backcast_loss_ratio"`, let's take a look at this diagram of the model taken from +# [the arXiv paper](https://arxiv.org/abs/1905.10437): +# +# ![N-BEATS diagram](diagram.png) +# +# Each 'block' within the N-BEATS architecture includes a forecast output and a backcast which can each yield their own +# loss. +# The `"backcast_loss_ratio"` is the ratio of the backcast loss to the forecast loss. +# A value of `1.0` means that the loss function is simply the sum of the forecast and backcast losses. + +# %% +model = TabularForecaster( + datamodule.parameters, backbone="n_beats", backbone_kwargs={"widths": [16, 256], "backcast_loss_ratio": 1.0} +) + +# %% [markdown] +# ## Finding the learning rate +# +# Tabular models can be particularly sensitive to the choice of learning rate. +# Helpfully, PyTorch Lightning provides a built-in learning rate finder that suggests a suitable learning rate +# automatically. +# To use it, we first create our Trainer. +# We apply gradient clipping (a common technique for tabular tasks) with ``gradient_clip_val=0.01`` in order to help +# prevent our model from over-fitting. +# Here's how to find the learning rate: + +# %% +trainer = flash.Trainer( + max_epochs=3, + gpus=int(torch.cuda.is_available()), + gradient_clip_val=0.01, +) + +res = trainer.tuner.lr_find(model, datamodule=datamodule, min_lr=1e-5) +print(f"Suggested learning rate: {res.suggestion()}") +res.plot(show=True, suggest=True).show() + +# %% [markdown] +# Once the suggest learning rate has been found, we can update our model with it: + +# %% +model.learning_rate = res.suggestion() + +# %% [markdown] +# ## Training the model +# Now all we have to do is train the model! + +# %% +trainer.fit(model, datamodule=datamodule) + +# %% [markdown] +# ## Plot the interpretation +# +# An important feature of the N-BEATS model is that it can be configured to produce an interpretable prediction that is +# split into both a low frequency (trend) component and a high frequency (seasonality) component. +# For hourly observations, we might expect the trend component to show us how electricity prices are changing from one +# day to the next (for example, whether prices were generally higher or lower than yesterday). +# In contrast, the seasonality component would be expected to show us the general pattern in prices through the day +# (for example, if there is typically a peak in price around lunch time or a drop at night). +# +# It is often useful to visualize this decomposition and the `TabularForecaster` makes it simple. +# First, we load the best model from our training run and generate some predictions. +# Next, we convert the predictions to the format expected by PyTorch Forecasting using the `convert_predictions` utility +# function. +# Finally, we plot the interpretation using the `pytorch_forecasting_model` attribute. +# Here's the full function: + +# %% + + +def plot_interpretation(model_path: str, predict_df: pd.DataFrame, parameters: Dict[str, Any]): + model = TabularForecaster.load_from_checkpoint(model_path) + datamodule = TabularForecastingData.from_data_frame( + parameters=parameters, + predict_data_frame=predict_df, + batch_size=256, + ) + trainer = flash.Trainer(gpus=int(torch.cuda.is_available())) + predictions = trainer.predict(model, datamodule=datamodule) + predictions, inputs = convert_predictions(predictions) + model.pytorch_forecasting_model.plot_interpretation(inputs, predictions, idx=0) + plt.show() + + +# %% [markdown] +# And now we run the function to plot the trend and seasonality curves: + +# %% +# Todo: Make sure to uncomment the line below if you want to run predictions and visualize the graph +# plot_interpretation(trainer.checkpoint_callback.best_model_path, df_energy_hourly, datamodule.parameters) + +# %% [markdown] +# It worked! The plot shows that the `TabularForecaster` does a reasonable job of modelling the time series and also +# breaks it down into a trend component and a seasonality component (in this case showing daily fluctuations in +# electricity prices). +# +# ## Bonus: Weekly trends +# +# The type of seasonality that the model learns to detect is dictated by the frequency of observations and the length of +# the encoding / prediction window. +# We might imagine that our pipeline could be changed to instead uncover weekly trends if we resample daily +# observations from our data instead of hourly. +# +# We can use our preprocessing function to do this. +# First, we load the data as before then preprocess it (this time setting `frequency = "1D"`). + +# %% +df_energy_daily = pd.read_csv(f"{DATASET_PATH}/energy_dataset.csv", parse_dates=["time"]) +df_energy_daily = preprocess(df_energy_daily, frequency="1D") + +# %% [markdown] +# Now let's create our `TabularForecastingData` as before, this time with a four week encoding window and a one week +# prediction window. + +# %% +max_prediction_length = 1 * 7 +max_encoder_length = 4 * 7 + +training_cutoff = df_energy_daily["time_idx"].max() - max_prediction_length + +datamodule = TabularForecastingData.from_data_frame( + time_idx="time_idx", + target="price actual", + group_ids=["constant"], + max_encoder_length=max_encoder_length, + max_prediction_length=max_prediction_length, + time_varying_unknown_reals=["price actual"], + train_data_frame=df_energy_daily[df_energy_daily["time_idx"] <= training_cutoff], + val_data_frame=df_energy_daily, + batch_size=256, +) + +# %% [markdown] +# Now it's time to create a new model and trainer. +# We run for 24 times the number of epochs this time as we now have around 1/24th of the number of observations. +# This time, instead of using the learning rate finder we just set the learning rate manually: + +# %% +model = TabularForecaster( + datamodule.parameters, + backbone="n_beats", + backbone_kwargs={"widths": [16, 256], "backcast_loss_ratio": 1.0}, + learning_rate=5e-4, +) + +trainer = flash.Trainer( + max_epochs=3 * 24, + check_val_every_n_epoch=24, + gpus=int(torch.cuda.is_available()), + gradient_clip_val=0.01, +) + +# %% [markdown] +# Finally, we train the new model: + +# %% +trainer.fit(model, datamodule=datamodule) + +# %% [markdown] +# Now let's look at what it learned: + +# %% +# Todo: Make sure to uncomment the line below if you want to run predictions and visualize the graph +# plot_interpretation(trainer.checkpoint_callback.best_model_path, df_energy_daily, datamodule.parameters) + +# %% [markdown] +# Success! We can now also see weekly trends / seasonality uncovered by our new model. +# +# ## Closing thoughts and next steps! +# +# This tutorial has shown how Flash and PyTorch Forecasting can be used to train state-of-the-art auto-regressive +# forecasting models (such as N-BEATS). +# We've seen how we can influence the kinds of trends and patterns uncovered by the model by resampling the data and +# changing the hyper-parameters. +# +# There are plenty of ways you could take this tutorial further. +# For example, you could try a more complex model, such as the +# [temporal fusion transformer](https://pytorch-forecasting.readthedocs.io/en/latest/api/pytorch_forecasting.models.temporal_fusion_transformer.TemporalFusionTransformer.html), +# which can handle additional inputs (the kaggle data set we used also includes weather data). +# +# Alternatively, if you want to be a bit more adventurous, you could look at +# [some of the other problems that can solved with Lightning Flash](https://lightning-flash.readthedocs.io/en/stable/?badge=stable). diff --git a/_notebooks/flash_tutorials/image_classification/.meta.yml b/_notebooks/flash_tutorials/image_classification/.meta.yml new file mode 100644 index 0000000..e4f1cfc --- /dev/null +++ b/_notebooks/flash_tutorials/image_classification/.meta.yml @@ -0,0 +1,19 @@ +title: Image Classification on Hymenoptera Dataset +author: Ethan Harris (ethan@pytorchlightning.ai) +created: 2021-11-23 +updated: 2022-08-26 +license: CC BY-SA +build: 3 +tags: + - Image Classification + - Image +description: | + In this tutorial, we'll go over the basics of lightning Flash by finetuning/predictin with an ImageClassifier on [Hymenoptera Dataset](https://www.kaggle.com/ajayrana/hymenoptera-data) containing ants and bees images. +requirements: + - pytorch-lightning==1.6.* + - lightning-flash[image]>=0.7.0 + - torchmetrics<0.11 # todo: task argument is missing + - numpy<1.24 +accelerator: + - GPU + - CPU diff --git a/_notebooks/flash_tutorials/image_classification/image_classification.py b/_notebooks/flash_tutorials/image_classification/image_classification.py new file mode 100644 index 0000000..ba34a8b --- /dev/null +++ b/_notebooks/flash_tutorials/image_classification/image_classification.py @@ -0,0 +1,115 @@ +# %% [markdown] +# In this tutorial, we'll go over the basics of lightning Flash by finetuning/predictin with an ImageClassifier on [Hymenoptera Dataset](https://www.kaggle.com/ajayrana/hymenoptera-data) containing ants and bees images. +# +# # Finetuning +# +# Finetuning consists of four steps: +# +# - 1. Train a source neural network model on a source dataset. For computer vision, it is traditionally the [ImageNet dataset](http://www.image-net.org). As training is costly, library such as [Torchvision](https://pytorch.org/vision/stable/index.html) library supports popular pre-trainer model architectures . In this notebook, we will be using their [resnet-18](https://pytorch.org/hub/pytorch_vision_resnet/). +# +# - 2. Create a new neural network called the target model. Its architecture replicates the source model and parameters, expect the latest layer which is removed. This model without its latest layer is traditionally called a backbone +# +# - 3. Add new layers after the backbone where the latest output size is the number of target dataset categories. Those new layers, traditionally called head will be randomly initialized while backbone will conserve its pre-trained weights from ImageNet. +# +# - 4. Train the target model on a target dataset, such as Hymenoptera Dataset with ants and bees. However, freezing some layers at training start such as the backbone tends to be more stable. In Flash, it can easily be done with `trainer.finetune(..., strategy="freeze")`. It is also common to `freeze/unfreeze` the backbone. In `Flash`, it can be done with `trainer.finetune(..., strategy="freeze_unfreeze")`. If one wants more control on the unfreeze flow, Flash supports `trainer.finetune(..., strategy=MyFinetuningStrategy())` where `MyFinetuningStrategy` is subclassing `pytorch_lightning.callbacks.BaseFinetuning`. + +# %% + +import flash +from flash.core.data.utils import download_data +from flash.image import ImageClassificationData, ImageClassifier + +# %% [markdown] +# ## Download data +# The data are downloaded from a URL, and save in a 'data' directory. + +# %% +download_data("https://pl-flash-data.s3.amazonaws.com/hymenoptera_data.zip", "data/") + + +# %% [markdown] +# ## Load the data +# +# Flash Tasks have built-in DataModules that you can use to organize your data. Pass in a train, validation and test folders and Flash will take care of the rest. +# Creates a ImageClassificationData object from folders of images arranged in this way: +# +# train/dog/xxx.png +# train/dog/xxy.png +# train/dog/xxz.png +# train/cat/123.png +# train/cat/nsdf3.png +# train/cat/asd932.png + +# %% +datamodule = ImageClassificationData.from_folders( + train_folder="data/hymenoptera_data/train/", + val_folder="data/hymenoptera_data/val/", + test_folder="data/hymenoptera_data/test/", + batch_size=1, +) + + +# %% [markdown] +# ## Build the model +# Create the ImageClassifier task. By default, the ImageClassifier task uses a [resnet-18](https://pytorch.org/hub/pytorch_vision_resnet/) backbone to train or finetune your model. +# For [Hymenoptera Dataset](https://www.kaggle.com/ajayrana/hymenoptera-data) containing ants and bees images, ``datamodule.num_classes`` will be 2. +# Backbone can easily be changed with `ImageClassifier(backbone="resnet50")` or you could provide your own `ImageClassifier(backbone=my_backbone)` + +# %% +model = ImageClassifier(num_classes=datamodule.num_classes) + + +# %% [markdown] +# ## Create the trainer. Run once on data +# The trainer object can be used for training or fine-tuning tasks on new sets of data. +# You can pass in parameters to control the training routine- limit the number of epochs, run on GPUs or TPUs, etc. +# For more details, read the [Trainer Documentation](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.trainer.trainer.Trainer.html?highlight=Trainer). +# In this demo, we will limit the fine-tuning to run just one epoch using max_epochs=2. + +# %% +trainer = flash.Trainer(max_epochs=1) + + +# %% [markdown] +# ## Finetune the model + +# %% +trainer.finetune(model, datamodule=datamodule, strategy="freeze") + + +# %% [markdown] +# ## Test the model + +# %% +trainer.test(model, datamodule=datamodule) + + +# %% [markdown] +# ## Save it! + +# %% +trainer.save_checkpoint("image_classification_model.pt") + +# %% [markdown] +# ## Predicting +# **Load the model from a checkpoint** + +# %% +model = ImageClassifier.load_from_checkpoint( + "https://flash-weights.s3.amazonaws.com/0.7.0/image_classification_model.pt" +) + +# %% [markdown] +# **Predict what's on a few images! ants or bees?** + +# %% +datamodule = ImageClassificationData.from_files( + predict_files=[ + "data/hymenoptera_data/val/bees/65038344_52a45d090d.jpg", + "data/hymenoptera_data/val/bees/590318879_68cf112861.jpg", + "data/hymenoptera_data/val/ants/540543309_ddbb193ee5.jpg", + ], + batch_size=1, +) +predictions = trainer.predict(model, datamodule=datamodule) +print(predictions) diff --git a/_notebooks/flash_tutorials/tabular_classification/.meta.yml b/_notebooks/flash_tutorials/tabular_classification/.meta.yml new file mode 100644 index 0000000..8e885a9 --- /dev/null +++ b/_notebooks/flash_tutorials/tabular_classification/.meta.yml @@ -0,0 +1,18 @@ +title: Tabular Classification on Titanic Dataset +author: Ethan Harris (ethan@pytorchlightning.ai) +created: 2021-11-23 +updated: 2022-08-26 +license: CC BY-SA +build: 3 +tags: + - Tabular Classification + - Tabular +description: | + In this notebook, we'll go over the basics of lightning Flash by training a TabularClassifier on [Titanic Dataset](https://www.kaggle.com/c/titanic). +requirements: + - lightning-flash[tabular]>=0.6.0 + - pytorch-lightning==1.3.6 # todo: update to latest + - numpy<1.24 +accelerator: + - GPU + - CPU diff --git a/_notebooks/flash_tutorials/tabular_classification/tabular_classification.py b/_notebooks/flash_tutorials/tabular_classification/tabular_classification.py new file mode 100644 index 0000000..a2089ca --- /dev/null +++ b/_notebooks/flash_tutorials/tabular_classification/tabular_classification.py @@ -0,0 +1,98 @@ +# %% [markdown] +# In this notebook, we'll go over the basics of lightning Flash by training a TabularClassifier on [Titanic Dataset](https://www.kaggle.com/c/titanic). + +# # Training + +# %% + +import flash +from flash.core.data.utils import download_data +from flash.tabular import TabularClassificationData, TabularClassifier + +# %% [markdown] +# ## Download the data +# The data are downloaded from a URL, and save in a 'data' directory. + +# %% +download_data("https://pl-flash-data.s3.amazonaws.com/titanic.zip", "data/") + + +# %% [markdown] +# ## Load the data +# Flash Tasks have built-in DataModules that you can use to organize your data. Pass in a train, validation and test folders and Flash will take care of the rest. +# +# Creates a TabularData relies on [Pandas DataFrame](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html). + +# %% +datamodule = TabularClassificationData.from_csv( + ["Sex", "Age", "SibSp", "Parch", "Ticket", "Cabin", "Embarked"], + ["Fare"], + target_fields="Survived", + train_file="./data/titanic/titanic.csv", + test_file="./data/titanic/test.csv", + val_split=0.25, + batch_size=8, +) + + +# %% [markdown] +# ## Build the model +# +# Note: Categorical columns will be mapped to the embedding space. Embedding space is set of tensors to be trained associated to each categorical column. + +# %% +model = TabularClassifier.from_data(datamodule) + + +# %% [markdown] +# ## Create the trainer. Run 10 times on data + +# %% +trainer = flash.Trainer(max_epochs=10) + + +# %% [markdown] +# ## Train the model + +# %% +trainer.fit(model, datamodule=datamodule) + +# %% [markdown] +# ## Test model + +# %% +trainer.test(model, datamodule=datamodule) + + +# %% [markdown] +# ## Save it! + +# %% +trainer.save_checkpoint("tabular_classification_model.pt") + + +# %% [markdown] +# # Predicting +# ## Load the model from a checkpoint +# +# `TabularClassifier.load_from_checkpoint` supports both url or local_path to a checkpoint. If provided with an url, the checkpoint will first be downloaded and laoded to re-create the model. + +# %% +model = TabularClassifier.load_from_checkpoint( + "https://flash-weights.s3.amazonaws.com/0.7.0/tabular_classification_model.pt" +) + + +# %% [markdown] +# ## Generate predictions from a sheet file! Who would survive? +# +# `TabularClassifier.predict` support both DataFrame and path to `.csv` file. + +# %% +datamodule = TabularClassificationData.from_csv( + predict_file="data/titanic/titanic.csv", + parameters=datamodule.parameters, + batch_size=8, +) +predictions = trainer.predict(model, datamodule=datamodule) +print(predictions) diff --git a/_notebooks/flash_tutorials/text_classification/.meta.yml b/_notebooks/flash_tutorials/text_classification/.meta.yml new file mode 100644 index 0000000..1b1592a --- /dev/null +++ b/_notebooks/flash_tutorials/text_classification/.meta.yml @@ -0,0 +1,19 @@ +title: Finetuning a Text Classifier on IMDB Dataset +author: Ethan Harris (ethan@pytorchlightning.ai) +created: 2021-11-23 +updated: 2022-08-26 +license: CC BY-SA +build: 3 +tags: + - Text Classification + - Text +description: | + In this notebook, we'll go over the basics of lightning Flash by finetunig a TextClassifier on IMDB Dataset. +requirements: + - pytorch-lightning==1.6.* + - lightning-flash[text]>=0.7.0 + - torchmetrics<0.11 # todo: update to use task=... + - numpy<1.24 +accelerator: + - GPU + - CPU diff --git a/_notebooks/flash_tutorials/text_classification/text_classification.py b/_notebooks/flash_tutorials/text_classification/text_classification.py new file mode 100644 index 0000000..f83bac5 --- /dev/null +++ b/_notebooks/flash_tutorials/text_classification/text_classification.py @@ -0,0 +1,110 @@ +# %% [markdown] +# In this notebook, we'll go over the basics of lightning Flash by finetunig a TextClassifier on [IMDB Dataset](https://paperswithcode.com/dataset/imdb-movie-reviews). +# +# # Finetuning +# +# Finetuning consists of four steps: +# +# - 1. Train a source neural network model on a source dataset. For text classication, it is traditionally a transformer model such as BERT [Bidirectional Encoder Representations from Transformers](https://arxiv.org/abs/1810.04805) trained on wikipedia. +# As those model are costly to train, [Transformers](https://github.com/huggingface/transformers) or [FairSeq](https://github.com/pytorch/fairseq) libraries provides popular pre-trained model architectures for NLP. In this notebook, we will be using [tiny-bert](https://huggingface.co/prajjwal1/bert-tiny). +# +# - 2. Create a new neural network the target model. Its architecture replicates all model designs and their parameters on the source model, expect the latest layer which is removed. This model without its latest layers is traditionally called a backbone +# +# - 3. Add new layers after the backbone where the latest output size is the number of target dataset categories. Those new layers, traditionally called head, will be randomly initialized while backbone will conserve its pre-trained weights from ImageNet. +# +# - 4. Train the target model on a target dataset, such as Hymenoptera Dataset with ants and bees. However, freezing some layers at training start such as the backbone tends to be more stable. In Flash, it can easily be done with `trainer.finetune(..., strategy="freeze")`. It is also common to `freeze/unfreeze` the backbone. In `Flash`, it can be done with `trainer.finetune(..., strategy="freeze_unfreeze")`. If a one wants more control on the unfreeze flow, Flash supports `trainer.finetune(..., strategy=MyFinetuningStrategy())` where `MyFinetuningStrategy` is subclassing `pytorch_lightning.callbacks.BaseFinetuning`. + +# %% + +import flash +from flash.core.data.utils import download_data +from flash.text import TextClassificationData, TextClassifier + +# %% [markdown] +# ## Download the data +# The data are downloaded from a URL, and save in a 'data' directory. + +# %% +download_data("https://pl-flash-data.s3.amazonaws.com/imdb.zip", "data/") + + +# %% [markdown] +# ## Load the data +# +# Flash Tasks have built-in DataModules that you can use to organize your data. Pass in a train, validation and test folders and Flash will take care of the rest. +# Creates a TextClassificationData object from csv file. + +# %% +datamodule = TextClassificationData.from_csv( + "review", + "sentiment", + train_file="data/imdb/train.csv", + val_file="data/imdb/valid.csv", + test_file="data/imdb/test.csv", + batch_size=512, # just increased for the example to run fast +) + + +# %% [markdown] +# ## Build the model +# +# Create the TextClassifier task. By default, the TextClassifier task uses a [tiny-bert](https://huggingface.co/prajjwal1/bert-tiny) backbone to train or finetune your model demo. You could use any models from [transformers - Text Classification](https://huggingface.co/models?filter=text-classification,pytorch) +# +# Backbone can easily be changed with such as `TextClassifier(backbone='bert-tiny-mnli')` + +# %% +model = TextClassifier(num_classes=datamodule.num_classes, backbone="prajjwal1/bert-tiny") + + +# %% [markdown] +# ## Create the trainer. Run once on data + +# %% +trainer = flash.Trainer(max_epochs=1) + + +# %% [markdown] +# ## Fine-tune the model +# +# The backbone won't be freezed and the entire model will be finetuned on the imdb dataset + +# %% +trainer.finetune(model, datamodule=datamodule, strategy="freeze") + + +# %% [markdown] +# ## Test model + +# %% +trainer.test(model, datamodule=datamodule) + + +# %% [markdown] +# ## Save it! + +# %% +trainer.save_checkpoint("text_classification_model.pt") + + +# %% [markdown] +# ## Predicting +# **Load the model from a checkpoint** + +# %% +model = TextClassifier.load_from_checkpoint("text_classification_model.pt") + + +# %% [markdown] +# **Classify a few sentences! How was the movie?** + +# %% +datamodule = TextClassificationData.from_lists( + predict_data=[ + "Turgid dialogue, feeble characterization - Harvey Keitel a judge?.", + "The worst movie in the history of cinema.", + "I come from Bulgaria where it 's almost impossible to have a tornado.", + ], + batch_size=4, +) +predictions = trainer.predict(model, datamodule=datamodule) +print(predictions) diff --git a/_notebooks/lightning_examples/augmentation_kornia/.meta.yml b/_notebooks/lightning_examples/augmentation_kornia/.meta.yml new file mode 100644 index 0000000..7f5c11c --- /dev/null +++ b/_notebooks/lightning_examples/augmentation_kornia/.meta.yml @@ -0,0 +1,24 @@ +title: GPU and batched data augmentation with Kornia and PyTorch-Lightning +author: PL/Kornia team +created: 2021-06-11 +updated: 2023-03-15 +license: CC BY-SA +build: 0 +tags: + - Image +description: | + In this tutorial we will show how to combine both Kornia and PyTorch Lightning + to perform efficient data augmentation to train a simple model using the GPU in batch + mode without additional effort. +requirements: + - kornia + - lightning + - torchmetrics + - torchvision + - matplotlib + - pandas + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/docs/_static/icon.svg b/_notebooks/lightning_examples/augmentation_kornia/.thumb.svg similarity index 100% rename from docs/_static/icon.svg rename to _notebooks/lightning_examples/augmentation_kornia/.thumb.svg diff --git a/_notebooks/lightning_examples/augmentation_kornia/augmentation.py b/_notebooks/lightning_examples/augmentation_kornia/augmentation.py new file mode 100644 index 0000000..46ab969 --- /dev/null +++ b/_notebooks/lightning_examples/augmentation_kornia/augmentation.py @@ -0,0 +1,206 @@ +# %% +import os + +import lightning as L +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +import seaborn as sn +import torch +import torch.nn as nn +import torchmetrics +import torchvision +from IPython.display import display +from kornia import image_to_tensor, tensor_to_image +from kornia.augmentation import ColorJitter, RandomChannelShuffle, RandomHorizontalFlip, RandomThinPlateSpline +from lightning.pytorch.loggers import CSVLogger +from torch import Tensor +from torch.nn import functional as F +from torch.utils.data import DataLoader +from torchvision.datasets import CIFAR10 + +sn.set() + +# %% [markdown] +# ## Define Data Augmentations module +# +# [Kornia](https://github.com/kornia/kornia) is low level Computer Vision library that provides a dedicated module +# [`kornia.augmentation`](https://kornia.readthedocs.io/en/latest/augmentation.html) module implementing +# en extensive set of data augmentation techniques for image and video. +# +# Similar to Lightning, in Kornia it's promoted to encapsulate functionalities inside classes for readability +# and efficiency purposes. In this case, we define a data augmentaton pipeline subclassing a `nn.Module` +# where the augmentation_kornia (also subclassing `nn.Module`) are combined with other PyTorch components +# such as `nn.Sequential`. +# +# Checkout the different augmentation operators in Kornia docs and experiment yourself! + + +# %% +class DataAugmentation(nn.Module): + """Module to perform data augmentation using Kornia on torch tensors.""" + + def __init__(self, apply_color_jitter: bool = False) -> None: + super().__init__() + self._apply_color_jitter = apply_color_jitter + + self.transforms = nn.Sequential( + RandomHorizontalFlip(p=0.75), + RandomChannelShuffle(p=0.75), + RandomThinPlateSpline(p=0.75), + ) + + self.jitter = ColorJitter(0.5, 0.5, 0.5, 0.5) + + @torch.no_grad() # disable gradients for effiency + def forward(self, x: Tensor) -> Tensor: + x_out = self.transforms(x) # BxCxHxW + if self._apply_color_jitter: + x_out = self.jitter(x_out) + return x_out + + +# %% [markdown] +# ## Define a Pre-processing module +# +# In addition to the `DataAugmentation` modudle that will sample random parameters during the training stage, +# we define a `Preprocess` class to handle the conversion of the image type to properly work with `Tensor`. +# +# For this example we use `torchvision` CIFAR10 which return samples of `PIL.Image`, however, +# to take all the advantages of PyTorch and Kornia we need to cast the images into tensors. +# +# To do that we will use `kornia.image_to_tensor` which casts and permutes the images in the right format. + + +# %% +class Preprocess(nn.Module): + """Module to perform pre-process using Kornia on torch tensors.""" + + @torch.no_grad() # disable gradients for effiency + def forward(self, x) -> Tensor: + x_tmp: np.ndarray = np.array(x) # HxWxC + x_out: Tensor = image_to_tensor(x_tmp, keepdim=True) # CxHxW + return x_out.float() / 255.0 + + +# %% [markdown] +# ## Define PyTorch Lightning model +# +# The next step is to define our `LightningModule` to have a proper organisation of our training pipeline. +# This is a simple example just to show how to structure your baseline to be used as a reference, +# do not expect a high performance. +# +# Notice that the `Preprocess` class is injected into the dataset and will be applied per sample. +# +# The interesting part in the proposed approach happens inside the `training_step` where with just a single +# line of code we apply the data augmentation in batch and no need to worry about the device. +# This means that our `DataAugmentation` pipeline will automatically executed in the GPU. + + +# %% +class CoolSystem(L.LightningModule): + def __init__(self): + super().__init__() + # not the best model: expereiment yourself + self.model = torchvision.models.resnet18(pretrained=True) + self.preprocess = Preprocess() # per sample transforms + self.transform = DataAugmentation() # per batch augmentation_kornia + self.train_accuracy = torchmetrics.Accuracy(task="multiclass", num_classes=1000) + self.val_accuracy = torchmetrics.Accuracy(task="multiclass", num_classes=1000) + + def forward(self, x): + return self.model(x) + + def compute_loss(self, y_hat, y): + return F.cross_entropy(y_hat, y) + + def show_batch(self, win_size=(10, 10)): + def _to_vis(data): + return tensor_to_image(torchvision.utils.make_grid(data, nrow=8)) + + # get a batch from the training set: try with `val_datlaoader` :) + imgs, labels = next(iter(self.train_dataloader())) + imgs_aug = self.transform(imgs) # apply transforms + # use matplotlib to visualize + plt.figure(figsize=win_size) + plt.imshow(_to_vis(imgs)) + plt.figure(figsize=win_size) + plt.imshow(_to_vis(imgs_aug)) + + def on_after_batch_transfer(self, batch, dataloader_idx): + x, y = batch + if self.trainer.training: + x = self.transform(x) # => we perform GPU/Batched data augmentation + return x, y + + def training_step(self, batch, batch_idx): + x, y = batch + y_hat = self(x) + loss = self.compute_loss(y_hat, y) + self.train_accuracy.update(y_hat, y) + self.log("train_loss", loss, prog_bar=False) + self.log("train_acc", self.train_accuracy, prog_bar=False) + return loss + + def validation_step(self, batch, batch_idx): + x, y = batch + y_hat = self(x) + loss = self.compute_loss(y_hat, y) + self.val_accuracy.update(y_hat, y) + self.log("valid_loss", loss, prog_bar=False) + self.log("valid_acc", self.val_accuracy, prog_bar=True) + + def configure_optimizers(self): + optimizer = torch.optim.AdamW(self.model.parameters(), lr=1e-4) + scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, self.trainer.max_epochs, 0) + return [optimizer], [scheduler] + + def prepare_data(self): + CIFAR10(os.getcwd(), train=True, download=True, transform=self.preprocess) + CIFAR10(os.getcwd(), train=False, download=True, transform=self.preprocess) + + def train_dataloader(self): + dataset = CIFAR10(os.getcwd(), train=True, download=True, transform=self.preprocess) + loader = DataLoader(dataset, batch_size=32) + return loader + + def val_dataloader(self): + dataset = CIFAR10(os.getcwd(), train=False, download=True, transform=self.preprocess) + loader = DataLoader(dataset, batch_size=32) + return loader + + +# %% [markdown] +# ## Visualize images + +# %% +# init model +model = CoolSystem() + +# %% +model.show_batch(win_size=(14, 14)) + +# %% [markdown] +# ## Run training + +# %% +# Initialize a trainer +trainer = L.Trainer( + accelerator="auto", + devices=1, + max_epochs=10, + logger=CSVLogger(save_dir="logs/"), +) + +# Train the model ⚡ +trainer.fit(model) + +# %% [markdown] +# ### Visualize the training results + +# %% +metrics = pd.read_csv(f"{trainer.logger.log_dir}/metrics.csv") +del metrics["step"] +metrics.set_index("epoch", inplace=True) +display(metrics.dropna(axis=1, how="all").head()) +sn.relplot(data=metrics, kind="line") diff --git a/_notebooks/lightning_examples/barlow-twins/.meta.yml b/_notebooks/lightning_examples/barlow-twins/.meta.yml new file mode 100644 index 0000000..9a2227b --- /dev/null +++ b/_notebooks/lightning_examples/barlow-twins/.meta.yml @@ -0,0 +1,23 @@ +title: Barlow Twins Tutorial +author: Ananya Harsh Jha +created: 2021-09-19 +updated: 2023-03-15 +license: CC BY-SA +build: 0 +tags: + - Image + - Self-Supervised +description: | + This notebook describes the self-supervised learning method Barlow Twins. + Barlow Twins differs from other recently proposed algorithms as it doesn't + fall under the category of either contrastive learning, or methods like knowledge + distillation or clustering. The simplicity of the loss function and its effectiveness + in comparison to the current state of the art makes Barlow Twins an interesting + case study. +requirements: + - torchvision + - matplotlib + - lightning>=2.0.0rc0 +accelerator: + - GPU + - CPU diff --git a/_notebooks/lightning_examples/barlow-twins/barlow_twins.py b/_notebooks/lightning_examples/barlow-twins/barlow_twins.py new file mode 100644 index 0000000..85a3132 --- /dev/null +++ b/_notebooks/lightning_examples/barlow-twins/barlow_twins.py @@ -0,0 +1,437 @@ +# %% [markdown] +# ## Barlow Twins +# +# Barlow Twins finds itself in unique place amongst the current state-of-the-art self-supervised learning methods. It does not fall under the existing categories of contrastive learning, knowledge distillation or clustering based methods. Instead, it creates its own category of redundancy reductionand achieves competitive performance with a simple yet effective loss function. In this tutorial, we look at coding up a small version of Barlow Twins algorithm using PyTorch Lightning. + +# %% +from functools import partial +from typing import Sequence, Tuple, Union + +import lightning as L +import matplotlib.pyplot as plt +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +import torchvision.transforms as transforms +import torchvision.transforms.functional as VisionF +from lightning.pytorch.callbacks import Callback, ModelCheckpoint +from torch import Tensor +from torch.utils.data import DataLoader +from torchmetrics.functional import accuracy +from torchvision.datasets import CIFAR10 +from torchvision.models.resnet import resnet18 +from torchvision.utils import make_grid + +batch_size = 32 +num_workers = 0 # to run notebook on CPU +max_epochs = 200 +z_dim = 128 + + +# %% [markdown] +# ### Transforms +# +# We first define the data augmentation pipeline used in Barlow Twins. Here, we use pipeline proposed in SimCLR, which generates two copies/views of an input image by applying the following transformations in a sequence. +# +# First it takes a random crop of the image and resizes it to a fixed pre-specified size. Then, it applies a left-to-right random flip with a probability of 0.5. This step is followed by a composition of color jitter, conversion to grayscale with a probability of 0.2 and the application of a Gaussian blur filter. Finally, we normalize the image and convert it to a tensor. +# +# Within this transform, we add a third view for our online finetuner, which we explain later on. But, to explain things quickly here, we add a another transform to perform perform test our encoder on a downstream classification task. + +# %% +class BarlowTwinsTransform: + def __init__(self, train=True, input_height=224, gaussian_blur=True, jitter_strength=1.0, normalize=None): + self.input_height = input_height + self.gaussian_blur = gaussian_blur + self.jitter_strength = jitter_strength + self.normalize = normalize + self.train = train + + color_jitter = transforms.ColorJitter( + 0.8 * self.jitter_strength, + 0.8 * self.jitter_strength, + 0.8 * self.jitter_strength, + 0.2 * self.jitter_strength, + ) + + color_transform = [transforms.RandomApply([color_jitter], p=0.8), transforms.RandomGrayscale(p=0.2)] + + if self.gaussian_blur: + kernel_size = int(0.1 * self.input_height) + if kernel_size % 2 == 0: + kernel_size += 1 + + color_transform.append(transforms.RandomApply([transforms.GaussianBlur(kernel_size=kernel_size)], p=0.5)) + + self.color_transform = transforms.Compose(color_transform) + + if normalize is None: + self.final_transform = transforms.ToTensor() + else: + self.final_transform = transforms.Compose([transforms.ToTensor(), normalize]) + + self.transform = transforms.Compose( + [ + transforms.RandomResizedCrop(self.input_height), + transforms.RandomHorizontalFlip(p=0.5), + self.color_transform, + self.final_transform, + ] + ) + + self.finetune_transform = None + if self.train: + self.finetune_transform = transforms.Compose( + [ + transforms.RandomCrop(32, padding=4, padding_mode="reflect"), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + ] + ) + else: + self.finetune_transform = transforms.ToTensor() + + def __call__(self, sample): + return self.transform(sample), self.transform(sample), self.finetune_transform(sample) + + +# %% [markdown] +# ### Dataset +# +# We select CIFAR10 as the dataset to demonstrate the pre-training process for Barlow Twins. CIFAR10 images are 32x32 in size and we do not apply a Gaussian blur transformation on them. In this step, we create the training and validation dataloaders for CIFAR10. + +# %% +def cifar10_normalization(): + normalize = transforms.Normalize( + mean=[x / 255.0 for x in [125.3, 123.0, 113.9]], std=[x / 255.0 for x in [63.0, 62.1, 66.7]] + ) + return normalize + + +train_transform = BarlowTwinsTransform( + train=True, input_height=32, gaussian_blur=False, jitter_strength=0.5, normalize=cifar10_normalization() +) +train_dataset = CIFAR10(root=".", train=True, download=True, transform=train_transform) + +val_transform = BarlowTwinsTransform( + train=False, input_height=32, gaussian_blur=False, jitter_strength=0.5, normalize=cifar10_normalization() +) +val_dataset = CIFAR10(root=".", train=False, download=True, transform=train_transform) + +train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers, drop_last=True) +val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=num_workers, drop_last=True) + +# %% [markdown] +# ### Plot images +# +# To see how the CIFAR10 images look after the data augmentation pipeline, we load a few images from the dataloader and plot them here. + +# %% +for batch in val_loader: + (img1, img2, _), label = batch + break + +img_grid = make_grid(img1, normalize=True) + + +def show(imgs): + if not isinstance(imgs, list): + imgs = [imgs] + fix, axs = plt.subplots(ncols=len(imgs), squeeze=False) + for i, img in enumerate(imgs): + img = img.detach() + img = VisionF.to_pil_image(img) + axs[0, i].imshow(np.asarray(img)) + axs[0, i].set(xticklabels=[], yticklabels=[], xticks=[], yticks=[]) + + +show(img_grid) + + +# %% [markdown] +# ### Barlow Twins Loss +# +# Here we define the loss function for Barlow Twins. It first normalizes the D dimensinonal vectors from the projection head and then computes the DxD cross-correlation matrix between the normalized vectors of the 2 views of each image. +# +# Then it splits this cross-correlation matrix into two parts. The first part, the diagonal of this matrix is brought closer to 1, which pushes up the cosine similarity between the latent vectors of two views of each image, thus making the backbone invariant to the transformations applied to the views. The second part of the loss pushes the non-diagonal elements of the cross-corrlelation matrix closes to 0. This reduces the redundancy between the different dimensions of the latent vector. + +# %% +class BarlowTwinsLoss(nn.Module): + def __init__(self, batch_size, lambda_coeff=5e-3, z_dim=128): + super().__init__() + + self.z_dim = z_dim + self.batch_size = batch_size + self.lambda_coeff = lambda_coeff + + def off_diagonal_ele(self, x): + # taken from: https://github.com/facebookresearch/barlowtwins/blob/main/main.py + # return a flattened view of the off-diagonal elements of a square matrix + n, m = x.shape + assert n == m + return x.flatten()[:-1].view(n - 1, n + 1)[:, 1:].flatten() + + def forward(self, z1, z2): + # N x D, where N is the batch size and D is output dim of projection head + z1_norm = (z1 - torch.mean(z1, dim=0)) / torch.std(z1, dim=0) + z2_norm = (z2 - torch.mean(z2, dim=0)) / torch.std(z2, dim=0) + + cross_corr = torch.matmul(z1_norm.T, z2_norm) / self.batch_size + + on_diag = torch.diagonal(cross_corr).add_(-1).pow_(2).sum() + off_diag = self.off_diagonal_ele(cross_corr).pow_(2).sum() + + return on_diag + self.lambda_coeff * off_diag + + +# %% [markdown] +# ### Backbone +# +# This is a standard Resnet backbone that we pre-train using the Barlow Twins method. To accommodate the 32x32 CIFAR10 images, we replace the first 7x7 convolution of the Resnet backbone by a 3x3 filter. We also remove the first Maxpool layer from the network for CIFAR10 images. + +# %% +encoder = resnet18() + +# for CIFAR10, replace the first 7x7 conv with smaller 3x3 conv and remove the first maxpool +encoder.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False) +encoder.maxpool = nn.MaxPool2d(kernel_size=1, stride=1) + +# replace classification fc layer of Resnet to obtain representations from the backbone +encoder.fc = nn.Identity() + + +# %% [markdown] +# ### Projection head +# +# Unlike SimCLR and BYOL, the downstream performance of Barlow Twins greatly benefits from having a larger projection head after the backbone network. The paper utilizes a 3 layer MLP with 8192 hidden dimensions and 8192 as the output dimenion of the projection head. For the purposes of the tutorial, we use a smaller projection head. But, it is imperative to mention here that in practice, Barlow Twins needs to be trained using a bigger projection head as it is highly sensitive to its architecture and output dimensionality. + +# %% +class ProjectionHead(nn.Module): + def __init__(self, input_dim=2048, hidden_dim=2048, output_dim=128): + super().__init__() + + self.projection_head = nn.Sequential( + nn.Linear(input_dim, hidden_dim, bias=True), + nn.BatchNorm1d(hidden_dim), + nn.ReLU(), + nn.Linear(hidden_dim, output_dim, bias=False), + ) + + def forward(self, x): + return self.projection_head(x) + + +# %% [markdown] +# ### Learning rate warmup +# +# For the purposes of this tutorial, we keep things simple and use a linear warmup schedule with Adam optimizer. In our previous experiments we have found that linear warmup part is much more important for the final performance of a model than the cosine decay component of the schedule. + +# %% +def fn(warmup_steps, step): + if step < warmup_steps: + return float(step) / float(max(1, warmup_steps)) + else: + return 1.0 + + +def linear_warmup_decay(warmup_steps): + return partial(fn, warmup_steps) + + +# %% [markdown] +# ### Barlow Twins Lightning Module +# +# We keep the LightningModule for Barlow Twins neat and simple. It takes in an backbone encoder and initializes the projection head and the loss function. We configure the optimizer and the learning rate scheduler in the ``configure_optimizers`` method. + +# %% +class BarlowTwins(L.LightningModule): + def __init__( + self, + encoder, + encoder_out_dim, + num_training_samples, + batch_size, + lambda_coeff=5e-3, + z_dim=128, + learning_rate=1e-4, + warmup_epochs=10, + max_epochs=200, + ): + super().__init__() + + self.encoder = encoder + self.projection_head = ProjectionHead(input_dim=encoder_out_dim, hidden_dim=encoder_out_dim, output_dim=z_dim) + self.loss_fn = BarlowTwinsLoss(batch_size=batch_size, lambda_coeff=lambda_coeff, z_dim=z_dim) + + self.learning_rate = learning_rate + self.warmup_epochs = warmup_epochs + self.max_epochs = max_epochs + + self.train_iters_per_epoch = num_training_samples // batch_size + + def forward(self, x): + return self.encoder(x) + + def shared_step(self, batch): + (x1, x2, _), _ = batch + + z1 = self.projection_head(self.encoder(x1)) + z2 = self.projection_head(self.encoder(x2)) + + return self.loss_fn(z1, z2) + + def training_step(self, batch, batch_idx): + loss = self.shared_step(batch) + self.log("train_loss", loss, on_step=True, on_epoch=False) + return loss + + def validation_step(self, batch, batch_idx): + loss = self.shared_step(batch) + self.log("val_loss", loss, on_step=False, on_epoch=True) + + def configure_optimizers(self): + optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate) + + warmup_steps = self.train_iters_per_epoch * self.warmup_epochs + + scheduler = { + "scheduler": torch.optim.lr_scheduler.LambdaLR( + optimizer, + linear_warmup_decay(warmup_steps), + ), + "interval": "step", + "frequency": 1, + } + + return [optimizer], [scheduler] + + +# %% [markdown] +# ### Evaluation +# +# We define a callback which appends a linear layer on top of the encoder and trains the classification evaluation head in an online manner. We make sure not to backpropagate the gradients back to the encoder while tuning the linear layer. This technique was used in SimCLR as well and they showed that the final downstream classification peformance is pretty much similar to the results on online finetuning as the training progresses. + +# %% +class OnlineFineTuner(Callback): + def __init__( + self, + encoder_output_dim: int, + num_classes: int, + ) -> None: + super().__init__() + + self.optimizer: torch.optim.Optimizer + + self.encoder_output_dim = encoder_output_dim + self.num_classes = num_classes + + def on_fit_start(self, trainer: L.Trainer, pl_module: L.LightningModule) -> None: + # add linear_eval layer and optimizer + pl_module.online_finetuner = nn.Linear(self.encoder_output_dim, self.num_classes).to(pl_module.device) + self.optimizer = torch.optim.Adam(pl_module.online_finetuner.parameters(), lr=1e-4) + + def extract_online_finetuning_view( + self, batch: Sequence, device: Union[str, torch.device] + ) -> Tuple[Tensor, Tensor]: + (_, _, finetune_view), y = batch + finetune_view = finetune_view.to(device) + y = y.to(device) + + return finetune_view, y + + def on_train_batch_end( + self, + trainer: L.Trainer, + pl_module: L.LightningModule, + outputs: Sequence, + batch: Sequence, + batch_idx: int, + ) -> None: + x, y = self.extract_online_finetuning_view(batch, pl_module.device) + + with torch.no_grad(): + feats = pl_module(x) + + feats = feats.detach() + preds = pl_module.online_finetuner(feats) + loss = F.cross_entropy(preds, y) + + loss.backward() + self.optimizer.step() + self.optimizer.zero_grad() + + acc = accuracy(F.softmax(preds, dim=1), y, task="multiclass", num_classes=10) + pl_module.log("online_train_acc", acc, on_step=True, on_epoch=False) + pl_module.log("online_train_loss", loss, on_step=True, on_epoch=False) + + def on_validation_batch_end( + self, + trainer: L.Trainer, + pl_module: L.LightningModule, + outputs: Sequence, + batch: Sequence, + batch_idx: int, + ) -> None: + x, y = self.extract_online_finetuning_view(batch, pl_module.device) + + with torch.no_grad(): + feats = pl_module(x) + + feats = feats.detach() + preds = pl_module.online_finetuner(feats) + loss = F.cross_entropy(preds, y) + + acc = accuracy(F.softmax(preds, dim=1), y, task="multiclass", num_classes=10) + pl_module.log("online_val_acc", acc, on_step=False, on_epoch=True, sync_dist=True) + pl_module.log("online_val_loss", loss, on_step=False, on_epoch=True, sync_dist=True) + + +# %% [markdown] +# Finally, we define the trainer for training the model. We pass in the ``train_loader`` and ``val_loader`` we had initialized earlier to the ``fit`` function. + +# %% +encoder_out_dim = 512 + +model = BarlowTwins( + encoder=encoder, + encoder_out_dim=encoder_out_dim, + num_training_samples=len(train_dataset), + batch_size=batch_size, + z_dim=z_dim, +) + +online_finetuner = OnlineFineTuner(encoder_output_dim=encoder_out_dim, num_classes=10) +checkpoint_callback = ModelCheckpoint(every_n_epochs=100, save_top_k=-1, save_last=True) + +trainer = L.Trainer( + max_epochs=max_epochs, + accelerator="auto", + devices=1, + callbacks=[online_finetuner, checkpoint_callback], +) + +# uncomment this to train the model +# this is done for the tutorial so that the notebook compiles +# trainer.fit(model, train_loader, val_loader) + +# %% [markdown] +# ### Using the trained encoder for downstream tasks +# +# Once the encoder is pretrained on CIFAR10, we can use it to get image embeddings and use them further downstream on tasks like classification, detection, segmentation etc. +# +# In this tutorial, we did not completely train our encoder for 100s of epochs using the Barlow Twins pretraining method. So, we will load the pretrained encoder weights from a checkpoint and show the image embeddings obtained from that. +# +# To create this checkpoint, the encoder was pretrained for 200 epochs, and obtained a online finetune accuracy of x% on CIFAR-10. + +# %% +# ckpt_model = torch.load('') # upload checkpoint to aws +# encoder = ckpt_model.encoder +encoder = model.encoder + +downstream_dataset = CIFAR10(root=".", train=False, transform=transforms.ToTensor()) +dataloader = DataLoader(downstream_dataset, batch_size=4, shuffle=False) + +for batch in dataloader: + img, label = batch + print(encoder(img).shape) + break diff --git a/_notebooks/lightning_examples/basic-gan/.meta.yaml b/_notebooks/lightning_examples/basic-gan/.meta.yaml new file mode 100644 index 0000000..dca0517 --- /dev/null +++ b/_notebooks/lightning_examples/basic-gan/.meta.yaml @@ -0,0 +1,20 @@ +title: PyTorch Lightning Basic GAN Tutorial +author: PL team +created: 2020-12-21 +updated: 2023-03-15 +license: CC BY-SA +build: 0 +tags: + - Image +description: | + How to train a GAN! + + Main takeaways: + 1. Generator and discriminator are arbitrary PyTorch modules. + 2. training_step does both the generator and discriminator training. +requirements: + - torchvision + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/_notebooks/lightning_examples/basic-gan/gan.py b/_notebooks/lightning_examples/basic-gan/gan.py new file mode 100644 index 0000000..b8e3827 --- /dev/null +++ b/_notebooks/lightning_examples/basic-gan/gan.py @@ -0,0 +1,266 @@ +# %% +import os + +import lightning as L +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +import torchvision +import torchvision.transforms as transforms +from torch.utils.data import DataLoader, random_split +from torchvision.datasets import MNIST + +PATH_DATASETS = os.environ.get("PATH_DATASETS", ".") +BATCH_SIZE = 256 if torch.cuda.is_available() else 64 +NUM_WORKERS = int(os.cpu_count() / 2) + +# %% [markdown] +# ### MNIST DataModule +# +# Below, we define a DataModule for the MNIST Dataset. To learn more about DataModules, check out our tutorial +# on them or see the [latest release docs](https://lightning.ai/docs/pytorch/stable/data/datamodule.html). + + +# %% +class MNISTDataModule(L.LightningDataModule): + def __init__( + self, + data_dir: str = PATH_DATASETS, + batch_size: int = BATCH_SIZE, + num_workers: int = NUM_WORKERS, + ): + super().__init__() + self.data_dir = data_dir + self.batch_size = batch_size + self.num_workers = num_workers + + self.transform = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,)), + ] + ) + + self.dims = (1, 28, 28) + self.num_classes = 10 + + def prepare_data(self): + # download + MNIST(self.data_dir, train=True, download=True) + MNIST(self.data_dir, train=False, download=True) + + def setup(self, stage=None): + # Assign train/val datasets for use in dataloaders + if stage == "fit" or stage is None: + mnist_full = MNIST(self.data_dir, train=True, transform=self.transform) + self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) + + # Assign test dataset for use in dataloader(s) + if stage == "test" or stage is None: + self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform) + + def train_dataloader(self): + return DataLoader( + self.mnist_train, + batch_size=self.batch_size, + num_workers=self.num_workers, + ) + + def val_dataloader(self): + return DataLoader(self.mnist_val, batch_size=self.batch_size, num_workers=self.num_workers) + + def test_dataloader(self): + return DataLoader(self.mnist_test, batch_size=self.batch_size, num_workers=self.num_workers) + + +# %% [markdown] +# ### A. Generator + + +# %% +class Generator(nn.Module): + def __init__(self, latent_dim, img_shape): + super().__init__() + self.img_shape = img_shape + + def block(in_feat, out_feat, normalize=True): + layers = [nn.Linear(in_feat, out_feat)] + if normalize: + layers.append(nn.BatchNorm1d(out_feat, 0.8)) + layers.append(nn.LeakyReLU(0.2, inplace=True)) + return layers + + self.model = nn.Sequential( + *block(latent_dim, 128, normalize=False), + *block(128, 256), + *block(256, 512), + *block(512, 1024), + nn.Linear(1024, int(np.prod(img_shape))), + nn.Tanh(), + ) + + def forward(self, z): + img = self.model(z) + img = img.view(img.size(0), *self.img_shape) + return img + + +# %% [markdown] +# ### B. Discriminator + + +# %% +class Discriminator(nn.Module): + def __init__(self, img_shape): + super().__init__() + + self.model = nn.Sequential( + nn.Linear(int(np.prod(img_shape)), 512), + nn.LeakyReLU(0.2, inplace=True), + nn.Linear(512, 256), + nn.LeakyReLU(0.2, inplace=True), + nn.Linear(256, 1), + nn.Sigmoid(), + ) + + def forward(self, img): + img_flat = img.view(img.size(0), -1) + validity = self.model(img_flat) + + return validity + + +# %% [markdown] +# ### C. GAN +# +# #### A couple of cool features to check out in this example... +# +# - We use `some_tensor.type_as(another_tensor)` to make sure we initialize new tensors on the right device (i.e. GPU, CPU). +# - Lightning will put your dataloader data on the right device automatically +# - In this example, we pull from latent dim on the fly, so we need to dynamically add tensors to the right device. +# - `type_as` is the way we recommend to do this. +# - This example shows how to use multiple dataloaders in your `LightningModule`. + + +# %% +class GAN(L.LightningModule): + def __init__( + self, + channels, + width, + height, + latent_dim: int = 100, + lr: float = 0.0002, + b1: float = 0.5, + b2: float = 0.999, + batch_size: int = BATCH_SIZE, + **kwargs, + ): + super().__init__() + self.save_hyperparameters() + self.automatic_optimization = False + + # networks + data_shape = (channels, width, height) + self.generator = Generator(latent_dim=self.hparams.latent_dim, img_shape=data_shape) + self.discriminator = Discriminator(img_shape=data_shape) + + self.validation_z = torch.randn(8, self.hparams.latent_dim) + + self.example_input_array = torch.zeros(2, self.hparams.latent_dim) + + def forward(self, z): + return self.generator(z) + + def adversarial_loss(self, y_hat, y): + return F.binary_cross_entropy(y_hat, y) + + def training_step(self, batch): + imgs, _ = batch + + optimizer_g, optimizer_d = self.optimizers() + + # sample noise + z = torch.randn(imgs.shape[0], self.hparams.latent_dim) + z = z.type_as(imgs) + + # train generator + # generate images + self.toggle_optimizer(optimizer_g) + self.generated_imgs = self(z) + + # log sampled images + sample_imgs = self.generated_imgs[:6] + grid = torchvision.utils.make_grid(sample_imgs) + self.logger.experiment.add_image("generated_images", grid, 0) + + # ground truth result (ie: all fake) + # put on GPU because we created this tensor inside training_loop + valid = torch.ones(imgs.size(0), 1) + valid = valid.type_as(imgs) + + # adversarial loss is binary cross-entropy + g_loss = self.adversarial_loss(self.discriminator(self(z)), valid) + self.log("g_loss", g_loss, prog_bar=True) + self.manual_backward(g_loss) + optimizer_g.step() + optimizer_g.zero_grad() + self.untoggle_optimizer(optimizer_g) + + # train discriminator + # Measure discriminator's ability to classify real from generated samples + self.toggle_optimizer(optimizer_d) + + # how well can it label as real? + valid = torch.ones(imgs.size(0), 1) + valid = valid.type_as(imgs) + + real_loss = self.adversarial_loss(self.discriminator(imgs), valid) + + # how well can it label as fake? + fake = torch.zeros(imgs.size(0), 1) + fake = fake.type_as(imgs) + + fake_loss = self.adversarial_loss(self.discriminator(self(z).detach()), fake) + + # discriminator loss is the average of these + d_loss = (real_loss + fake_loss) / 2 + self.log("d_loss", d_loss, prog_bar=True) + self.manual_backward(d_loss) + optimizer_d.step() + optimizer_d.zero_grad() + self.untoggle_optimizer(optimizer_d) + + def configure_optimizers(self): + lr = self.hparams.lr + b1 = self.hparams.b1 + b2 = self.hparams.b2 + + opt_g = torch.optim.Adam(self.generator.parameters(), lr=lr, betas=(b1, b2)) + opt_d = torch.optim.Adam(self.discriminator.parameters(), lr=lr, betas=(b1, b2)) + return [opt_g, opt_d], [] + + def on_validation_epoch_end(self): + z = self.validation_z.type_as(self.generator.model[0].weight) + + # log sampled images + sample_imgs = self(z) + grid = torchvision.utils.make_grid(sample_imgs) + self.logger.experiment.add_image("generated_images", grid, self.current_epoch) + + +# %% +dm = MNISTDataModule() +model = GAN(*dm.dims) +trainer = L.Trainer( + accelerator="auto", + devices=1, + max_epochs=5, +) +trainer.fit(model, dm) + +# %% +# Start tensorboard. +# %load_ext tensorboard +# %tensorboard --logdir lightning_logs/ diff --git a/_notebooks/lightning_examples/cifar10-baseline/.meta.yml b/_notebooks/lightning_examples/cifar10-baseline/.meta.yml new file mode 100644 index 0000000..6862531 --- /dev/null +++ b/_notebooks/lightning_examples/cifar10-baseline/.meta.yml @@ -0,0 +1,17 @@ +title: PyTorch Lightning CIFAR10 ~94% Baseline Tutorial +author: PL team +created: 2020-12-21 +updated: 2023-03-15 +license: CC BY-SA +build: 0 +tags: + - Image +description: > + Train a Resnet to 94% accuracy on Cifar10! +requirements: + - torchvision + - pandas + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - GPU diff --git a/_notebooks/lightning_examples/cifar10-baseline/baseline.py b/_notebooks/lightning_examples/cifar10-baseline/baseline.py new file mode 100644 index 0000000..9abc859 --- /dev/null +++ b/_notebooks/lightning_examples/cifar10-baseline/baseline.py @@ -0,0 +1,255 @@ +# %% +# Run this if you intend to use TPUs +# # !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.8-cp37-cp37m-linux_x86_64.whl + +# %% +import os + +import lightning as L +import pandas as pd +import seaborn as sn +import torch +import torch.nn as nn +import torch.nn.functional as F +import torchvision +from IPython.display import display +from lightning.pytorch.callbacks import LearningRateMonitor +from lightning.pytorch.loggers import CSVLogger +from torch.optim.lr_scheduler import OneCycleLR +from torch.optim.swa_utils import AveragedModel, update_bn +from torch.utils.data import DataLoader, random_split +from torchmetrics.functional import accuracy +from torchvision.datasets import CIFAR10 + +L.seed_everything(7) + +PATH_DATASETS = os.environ.get("PATH_DATASETS", ".") +BATCH_SIZE = 256 if torch.cuda.is_available() else 64 +NUM_WORKERS = int(os.cpu_count() / 2) + +# %% [markdown] +# ### CIFAR10 DataLoaders +# + +# %% + +cifar10_normalization = torchvision.transforms.Normalize( + mean=[x / 255.0 for x in [125.3, 123.0, 113.9]], + std=[x / 255.0 for x in [63.0, 62.1, 66.7]], +) + + +def split_dataset(dataset, val_split=0.2, train=True): + """Splits the dataset into train and validation set.""" + len_dataset = len(dataset) + splits = get_splits(len_dataset, val_split) + dataset_train, dataset_val = random_split(dataset, splits, generator=torch.Generator().manual_seed(42)) + + if train: + return dataset_train + return dataset_val + + +def get_splits(len_dataset, val_split): + """Computes split lengths for train and validation set.""" + if isinstance(val_split, int): + train_len = len_dataset - val_split + splits = [train_len, val_split] + elif isinstance(val_split, float): + val_len = int(val_split * len_dataset) + train_len = len_dataset - val_len + splits = [train_len, val_len] + else: + raise ValueError(f"Unsupported type {type(val_split)}") + + return splits + + +train_transforms = torchvision.transforms.Compose( + [ + torchvision.transforms.RandomCrop(32, padding=4), + torchvision.transforms.RandomHorizontalFlip(), + torchvision.transforms.ToTensor(), + cifar10_normalization, + ] +) +test_transforms = torchvision.transforms.Compose( + [ + torchvision.transforms.ToTensor(), + cifar10_normalization, + ] +) + +dataset_train = CIFAR10(PATH_DATASETS, train=True, download=True, transform=train_transforms) +dataset_val = CIFAR10(PATH_DATASETS, train=True, download=True, transform=test_transforms) +dataset_train = split_dataset(dataset_train) +dataset_val = split_dataset(dataset_val, train=False) +dataset_test = CIFAR10(PATH_DATASETS, train=False, download=True, transform=test_transforms) + +train_dataloader = DataLoader(dataset_train, batch_size=BATCH_SIZE, shuffle=True, num_workers=NUM_WORKERS) +val_dataloader = DataLoader(dataset_val, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS) +test_dataloader = DataLoader(dataset_test, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS) + + +# %% [markdown] +# ### Resnet +# Modify the pre-existing Resnet architecture from TorchVision. The pre-existing architecture is based on ImageNet +# images (224x224) as input. So we need to modify it for CIFAR10 images (32x32). + + +# %% +def create_model(): + model = torchvision.models.resnet18(pretrained=False, num_classes=10) + model.conv1 = nn.Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False) + model.maxpool = nn.Identity() + return model + + +# %% [markdown] +# ### Lightning Module +# Check out the [`configure_optimizers`](https://lightning.ai/docs/pytorch/stable/common/lightning_module.html#configure-optimizers) +# method to use custom Learning Rate schedulers. The OneCycleLR with SGD will get you to around 92-93% accuracy +# in 20-30 epochs and 93-94% accuracy in 40-50 epochs. Feel free to experiment with different +# LR schedules from https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate + + +# %% +class LitResnet(L.LightningModule): + def __init__(self, lr=0.05): + super().__init__() + + self.save_hyperparameters() + self.model = create_model() + + def forward(self, x): + out = self.model(x) + return F.log_softmax(out, dim=1) + + def training_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + self.log("train_loss", loss) + return loss + + def evaluate(self, batch, stage=None): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + preds = torch.argmax(logits, dim=1) + acc = accuracy(preds, y, task="multiclass", num_classes=10) + + if stage: + self.log(f"{stage}_loss", loss, prog_bar=True) + self.log(f"{stage}_acc", acc, prog_bar=True) + + def validation_step(self, batch, batch_idx): + self.evaluate(batch, "val") + + def test_step(self, batch, batch_idx): + self.evaluate(batch, "test") + + def configure_optimizers(self): + optimizer = torch.optim.SGD( + self.parameters(), + lr=self.hparams.lr, + momentum=0.9, + weight_decay=5e-4, + ) + steps_per_epoch = 45000 // BATCH_SIZE + scheduler_dict = { + "scheduler": OneCycleLR( + optimizer, + 0.1, + epochs=self.trainer.max_epochs, + steps_per_epoch=steps_per_epoch, + ), + "interval": "step", + } + return {"optimizer": optimizer, "lr_scheduler": scheduler_dict} + + +# %% +model = LitResnet(lr=0.05) + +trainer = L.Trainer( + max_epochs=30, + accelerator="auto", + devices=1, + logger=CSVLogger(save_dir="logs/"), + callbacks=[LearningRateMonitor(logging_interval="step")], +) + +trainer.fit(model, train_dataloader, val_dataloaders=val_dataloader) +trainer.test(model, test_dataloader) + +# %% + +metrics = pd.read_csv(f"{trainer.logger.log_dir}/metrics.csv") +del metrics["step"] +metrics.set_index("epoch", inplace=True) +display(metrics.dropna(axis=1, how="all").head()) +sn.relplot(data=metrics, kind="line") + +# %% [markdown] +# ### Bonus: Use [Stochastic Weight Averaging](https://arxiv.org/abs/1803.05407) to get a boost on performance +# +# Use SWA from torch.optim to get a quick performance boost. Also shows a couple of cool features from Lightning: +# - Use `training_epoch_end` to run code after the end of every epoch +# - Use a pretrained model directly with this wrapper for SWA + + +# %% +class SWAResnet(LitResnet): + def __init__(self, trained_model, lr=0.01): + super().__init__() + + self.save_hyperparameters("lr") + self.model = trained_model + self.swa_model = AveragedModel(self.model) + + def forward(self, x): + out = self.swa_model(x) + return F.log_softmax(out, dim=1) + + def on_train_epoch_end(self): + self.swa_model.update_parameters(self.model) + + def validation_step(self, batch, batch_idx, stage=None): + x, y = batch + logits = F.log_softmax(self.model(x), dim=1) + loss = F.nll_loss(logits, y) + preds = torch.argmax(logits, dim=1) + acc = accuracy(preds, y, task="multiclass", num_classes=10) + + self.log("val_loss", loss, prog_bar=True) + self.log("val_acc", acc, prog_bar=True) + + def configure_optimizers(self): + optimizer = torch.optim.SGD(self.model.parameters(), lr=self.hparams.lr, momentum=0.9, weight_decay=5e-4) + return optimizer + + def on_train_end(self): + update_bn(self.trainer.datamodule.train_dataloader(), self.swa_model, device=self.device) + + +# %% +swa_model = SWAResnet(model.model, lr=0.01) + +swa_trainer = L.Trainer( + max_epochs=20, + accelerator="auto", + devices=1, + logger=CSVLogger(save_dir="logs/"), +) + +swa_trainer.fit(swa_model, train_dataloader, val_dataloader=val_dataloader) +swa_trainer.test(swa_model, test_dataloader) + +# %% + +metrics = pd.read_csv(f"{trainer.logger.log_dir}/metrics.csv") +del metrics["step"] +metrics.set_index("epoch", inplace=True) +display(metrics.dropna(axis=1, how="all").head()) +sn.relplot(data=metrics, kind="line") diff --git a/_notebooks/lightning_examples/datamodules/.meta.yml b/_notebooks/lightning_examples/datamodules/.meta.yml new file mode 100644 index 0000000..5c8fc0b --- /dev/null +++ b/_notebooks/lightning_examples/datamodules/.meta.yml @@ -0,0 +1,16 @@ +title: PyTorch Lightning DataModules +author: PL team +created: 2020-12-21 +updated: 2023-03-15 +license: CC BY-SA +build: 0 +description: This notebook will walk you through how to start using Datamodules. With + the release of `pytorch-lightning` version 0.9.0, we have included a new class called + `LightningDataModule` to help you decouple data related hooks from your `LightningModule`. + The most up-to-date documentation on datamodules can be found + [here](https://lightning.ai/docs/pytorch/stable/data/datamodule.html). +requirements: + - torchvision +accelerator: + - CPU + - GPU diff --git a/_notebooks/lightning_examples/datamodules/datamodules.py b/_notebooks/lightning_examples/datamodules/datamodules.py new file mode 100644 index 0000000..dd5b655 --- /dev/null +++ b/_notebooks/lightning_examples/datamodules/datamodules.py @@ -0,0 +1,339 @@ +# %% [markdown] +# ## Introduction +# +# First, we'll go over a regular `LightningModule` implementation without the use of a `LightningDataModule` + +# %% +import os + +import lightning as L +import torch +import torch.nn.functional as F +from torch import nn +from torch.utils.data import DataLoader, random_split +from torchmetrics.functional import accuracy +from torchvision import transforms + +# Note - you must have torchvision installed for this example +from torchvision.datasets import CIFAR10, MNIST + +PATH_DATASETS = os.environ.get("PATH_DATASETS", ".") +BATCH_SIZE = 256 if torch.cuda.is_available() else 64 + +# %% [markdown] +# ### Defining the LitMNISTModel +# +# Below, we reuse a `LightningModule` from our hello world tutorial that classifies MNIST Handwritten Digits. +# +# Unfortunately, we have hardcoded dataset-specific items within the model, +# forever limiting it to working with MNIST Data. 😢 +# +# This is fine if you don't plan on training/evaluating your model on different datasets. +# However, in many cases, this can become bothersome when you want to try out your architecture with different datasets. + + +# %% +class LitMNIST(L.LightningModule): + def __init__(self, data_dir=PATH_DATASETS, hidden_size=64, learning_rate=2e-4): + super().__init__() + + # We hardcode dataset specific stuff here. + self.data_dir = data_dir + self.num_classes = 10 + self.dims = (1, 28, 28) + channels, width, height = self.dims + self.transform = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,)), + ] + ) + + self.hidden_size = hidden_size + self.learning_rate = learning_rate + + # Build model + self.model = nn.Sequential( + nn.Flatten(), + nn.Linear(channels * width * height, hidden_size), + nn.ReLU(), + nn.Dropout(0.1), + nn.Linear(hidden_size, hidden_size), + nn.ReLU(), + nn.Dropout(0.1), + nn.Linear(hidden_size, self.num_classes), + ) + + def forward(self, x): + x = self.model(x) + return F.log_softmax(x, dim=1) + + def training_step(self, batch): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + return loss + + def validation_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + preds = torch.argmax(logits, dim=1) + acc = accuracy(preds, y, task="multiclass", num_classes=10) + self.log("val_loss", loss, prog_bar=True) + self.log("val_acc", acc, prog_bar=True) + + def configure_optimizers(self): + optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate) + return optimizer + + #################### + # DATA RELATED HOOKS + #################### + + def prepare_data(self): + # download + MNIST(self.data_dir, train=True, download=True) + MNIST(self.data_dir, train=False, download=True) + + def setup(self, stage=None): + # Assign train/val datasets for use in dataloaders + if stage == "fit" or stage is None: + mnist_full = MNIST(self.data_dir, train=True, transform=self.transform) + self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) + + # Assign test dataset for use in dataloader(s) + if stage == "test" or stage is None: + self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform) + + def train_dataloader(self): + return DataLoader(self.mnist_train, batch_size=128) + + def val_dataloader(self): + return DataLoader(self.mnist_val, batch_size=128) + + def test_dataloader(self): + return DataLoader(self.mnist_test, batch_size=128) + + +# %% [markdown] +# ### Training the ListMNIST Model + +# %% +model = LitMNIST() +trainer = L.Trainer( + max_epochs=2, + accelerator="auto", + devices=1, +) +trainer.fit(model) + +# %% [markdown] +# ## Using DataModules +# +# DataModules are a way of decoupling data-related hooks from the `LightningModule +# ` so you can develop dataset agnostic models. + +# %% [markdown] +# ### Defining The MNISTDataModule +# +# Let's go over each function in the class below and talk about what they're doing: +# +# 1. ```__init__``` +# - Takes in a `data_dir` arg that points to where you have downloaded/wish to download the MNIST dataset. +# - Defines a transform that will be applied across train, val, and test dataset splits. +# - Defines default `self.dims`. +# +# +# 2. ```prepare_data``` +# - This is where we can download the dataset. We point to our desired dataset and ask torchvision's `MNIST` dataset class to download if the dataset isn't found there. +# - **Note we do not make any state assignments in this function** (i.e. `self.something = ...`) +# +# 3. ```setup``` +# - Loads in data from file and prepares PyTorch tensor datasets for each split (train, val, test). +# - Setup expects a 'stage' arg which is used to separate logic for 'fit' and 'test'. +# - If you don't mind loading all your datasets at once, you can set up a condition to allow for both 'fit' related setup and 'test' related setup to run whenever `None` is passed to `stage`. +# - **Note this runs across all GPUs and it *is* safe to make state assignments here** +# +# +# 4. ```x_dataloader``` +# - `train_dataloader()`, `val_dataloader()`, and `test_dataloader()` all return PyTorch `DataLoader` instances that are created by wrapping their respective datasets that we prepared in `setup()` + + +# %% +class MNISTDataModule(L.LightningDataModule): + def __init__(self, data_dir: str = PATH_DATASETS): + super().__init__() + self.data_dir = data_dir + self.transform = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,)), + ] + ) + + self.dims = (1, 28, 28) + self.num_classes = 10 + + def prepare_data(self): + # download + MNIST(self.data_dir, train=True, download=True) + MNIST(self.data_dir, train=False, download=True) + + def setup(self, stage=None): + # Assign train/val datasets for use in dataloaders + if stage == "fit" or stage is None: + mnist_full = MNIST(self.data_dir, train=True, transform=self.transform) + self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) + + # Assign test dataset for use in dataloader(s) + if stage == "test" or stage is None: + self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform) + + def train_dataloader(self): + return DataLoader(self.mnist_train, batch_size=BATCH_SIZE) + + def val_dataloader(self): + return DataLoader(self.mnist_val, batch_size=BATCH_SIZE) + + def test_dataloader(self): + return DataLoader(self.mnist_test, batch_size=BATCH_SIZE) + + +# %% [markdown] +# ### Defining the dataset agnostic `LitModel` +# +# Below, we define the same model as the `LitMNIST` model we made earlier. +# +# However, this time our model has the freedom to use any input data that we'd like 🔥. + + +# %% +class LitModel(L.LightningModule): + def __init__(self, channels, width, height, num_classes, hidden_size=64, learning_rate=2e-4): + super().__init__() + + # We take in input dimensions as parameters and use those to dynamically build model. + self.channels = channels + self.width = width + self.height = height + self.num_classes = num_classes + self.hidden_size = hidden_size + self.learning_rate = learning_rate + + self.model = nn.Sequential( + nn.Flatten(), + nn.Linear(channels * width * height, hidden_size), + nn.ReLU(), + nn.Dropout(0.1), + nn.Linear(hidden_size, hidden_size), + nn.ReLU(), + nn.Dropout(0.1), + nn.Linear(hidden_size, num_classes), + ) + + def forward(self, x): + x = self.model(x) + return F.log_softmax(x, dim=1) + + def training_step(self, batch): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + return loss + + def validation_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + preds = torch.argmax(logits, dim=1) + acc = accuracy(preds, y, task="multiclass", num_classes=10) + self.log("val_loss", loss, prog_bar=True) + self.log("val_acc", acc, prog_bar=True) + + def configure_optimizers(self): + optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate) + return optimizer + + +# %% [markdown] +# ### Training the `LitModel` using the `MNISTDataModule` +# +# Now, we initialize and train the `LitModel` using the `MNISTDataModule`'s configuration settings and dataloaders. + +# %% +# Init DataModule +dm = MNISTDataModule() +# Init model from datamodule's attributes +model = LitModel(*dm.dims, dm.num_classes) +# Init trainer +trainer = L.Trainer( + max_epochs=3, + accelerator="auto", + devices=1, +) +# Pass the datamodule as arg to trainer.fit to override model hooks :) +trainer.fit(model, dm) + +# %% [markdown] +# ### Defining the CIFAR10 DataModule +# +# Lets prove the `LitModel` we made earlier is dataset agnostic by defining a new datamodule for the CIFAR10 dataset. + + +# %% +class CIFAR10DataModule(L.LightningDataModule): + def __init__(self, data_dir: str = "./"): + super().__init__() + self.data_dir = data_dir + self.transform = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), + ] + ) + + self.dims = (3, 32, 32) + self.num_classes = 10 + + def prepare_data(self): + # download + CIFAR10(self.data_dir, train=True, download=True) + CIFAR10(self.data_dir, train=False, download=True) + + def setup(self, stage=None): + # Assign train/val datasets for use in dataloaders + if stage == "fit" or stage is None: + cifar_full = CIFAR10(self.data_dir, train=True, transform=self.transform) + self.cifar_train, self.cifar_val = random_split(cifar_full, [45000, 5000]) + + # Assign test dataset for use in dataloader(s) + if stage == "test" or stage is None: + self.cifar_test = CIFAR10(self.data_dir, train=False, transform=self.transform) + + def train_dataloader(self): + return DataLoader(self.cifar_train, batch_size=BATCH_SIZE) + + def val_dataloader(self): + return DataLoader(self.cifar_val, batch_size=BATCH_SIZE) + + def test_dataloader(self): + return DataLoader(self.cifar_test, batch_size=BATCH_SIZE) + + +# %% [markdown] +# ### Training the `LitModel` using the `CIFAR10DataModule` +# +# Our model isn't very good, so it will perform pretty badly on the CIFAR10 dataset. +# +# The point here is that we can see that our `LitModel` has no problem using a different datamodule as its input data. + +# %% +dm = CIFAR10DataModule() +model = LitModel(*dm.dims, dm.num_classes, hidden_size=256) +trainer = L.Trainer( + max_epochs=5, + accelerator="auto", + devices=1, +) +trainer.fit(model, dm) diff --git a/_notebooks/lightning_examples/finetuning-scheduler/.meta.yml b/_notebooks/lightning_examples/finetuning-scheduler/.meta.yml new file mode 100644 index 0000000..6264155 --- /dev/null +++ b/_notebooks/lightning_examples/finetuning-scheduler/.meta.yml @@ -0,0 +1,21 @@ +title: Fine-Tuning Scheduler +author: "[Dan Dale](https://github.com/speediedan)" +created: 2021-11-29 +updated: 2023-01-24 +license: CC BY-SA +build: 0 +tags: + - Fine-Tuning +description: | + This notebook introduces the [Fine-Tuning Scheduler](https://finetuning-scheduler.readthedocs.io/en/stable/index.html) extension + and demonstrates the use of it to fine-tune a small foundation model on the + [RTE](https://huggingface.co/datasets/viewer/?dataset=super_glue&config=rte) task of + [SuperGLUE](https://super.gluebenchmark.com/) with iterative early-stopping defined according to a user-specified + schedule. It uses Hugging Face's ``datasets`` and ``transformers`` libraries to retrieve the relevant benchmark data + and foundation model weights. The required dependencies are installed via the finetuning-scheduler ``[examples]`` extra. +requirements: + - finetuning-scheduler[examples]>=0.4.0 + - datasets<2.8.0 # todo: AttributeError: module 'datasets.arrow_dataset' has no attribute 'Batch' + - lightning>=2.0.0rc0 +accelerator: + - GPU diff --git a/_notebooks/lightning_examples/finetuning-scheduler/RteBoolqModule_ft_schedule_deberta_base.yaml b/_notebooks/lightning_examples/finetuning-scheduler/RteBoolqModule_ft_schedule_deberta_base.yaml new file mode 100644 index 0000000..62bdbae --- /dev/null +++ b/_notebooks/lightning_examples/finetuning-scheduler/RteBoolqModule_ft_schedule_deberta_base.yaml @@ -0,0 +1,18 @@ + +0: + params: + - model.classifier.bias + - model.classifier.weight + - model.pooler.dense.bias + - model.pooler.dense.weight + - model.deberta.encoder.LayerNorm.bias + - model.deberta.encoder.LayerNorm.weight + - model.deberta.encoder.rel_embeddings.weight + - model.deberta.encoder.layer.{0,11}.(output|attention|intermediate).* +1: + params: + - model.deberta.embeddings.LayerNorm.bias + - model.deberta.embeddings.LayerNorm.weight +2: + params: + - model.deberta.embeddings.word_embeddings.weight diff --git a/_notebooks/lightning_examples/finetuning-scheduler/emphasized_yaml.png b/_notebooks/lightning_examples/finetuning-scheduler/emphasized_yaml.png new file mode 100644 index 0000000..492be1d Binary files /dev/null and b/_notebooks/lightning_examples/finetuning-scheduler/emphasized_yaml.png differ diff --git a/_notebooks/lightning_examples/finetuning-scheduler/finetuning-scheduler.py b/_notebooks/lightning_examples/finetuning-scheduler/finetuning-scheduler.py new file mode 100644 index 0000000..956897d --- /dev/null +++ b/_notebooks/lightning_examples/finetuning-scheduler/finetuning-scheduler.py @@ -0,0 +1,708 @@ +# %% [markdown] +# ## Scheduled Fine-Tuning with the Fine-Tuning Scheduler Extension +# +# ![Fine-Tuning Scheduler logo](logo_fts.png){height="55px" width="401px"} +# +# The [Fine-Tuning Scheduler](https://finetuning-scheduler.readthedocs.io/en/stable/index.html) extension accelerates and enhances model experimentation with flexible fine-tuning schedules. +# +# Training with the extension is simple and confers a host of benefits: +# +# - it dramatically increases fine-tuning flexibility +# - expedites and facilitates exploration of model tuning dynamics +# - enables marginal performance improvements of fine-tuned models +# +# Setup is straightforward, just install from PyPI! Since this notebook-based example requires a few additional packages (e.g. +# ``transformers``, ``sentencepiece``), we installed the ``finetuning-scheduler`` package with the ``[examples]`` extra above. +# Once the ``finetuning-scheduler`` package is installed, the [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) callback is available for use with PyTorch Lightning. +# For additional installation options, please see the Fine-Tuning Scheduler [README](https://github.com/speediedan/finetuning-scheduler/blob/main/README.md). +# +# +# +#
+# +# Fundamentally, [Fine-Tuning Scheduler](https://finetuning-scheduler.readthedocs.io/en/stable/index.html) enables +# scheduled, multi-phase, fine-tuning of foundation models. Gradual unfreezing (i.e. thawing) can help maximize +# foundation model knowledge retention while allowing (typically upper layers of) the model to +# optimally adapt to new tasks during transfer learning [1, 2, 3](#f1) +# +#
+# +# The [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) callback orchestrates the gradual unfreezing +# of models via a fine-tuning schedule that is either implicitly generated (the default) or explicitly provided by the user +# (more computationally efficient). Fine-tuning phase transitions are driven by +# [FTSEarlyStopping](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSEarlyStopping) +# criteria (a multi-phase extension of ``EarlyStopping`` packaged with FinetuningScheduler), user-specified epoch transitions or a composition of the two (the default mode). +# A [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) training session completes when the +# final phase of the schedule has its stopping criteria met. See +# the [early stopping documentation](https://lightning.ai/docs/pytorch/stable/api/pytorch_lightning.callbacks.EarlyStopping.html) for more details on that callback's configuration. +# +# ![FinetuningScheduler explicit loss animation](fts_explicit_loss_anim.gif){height="272px" width="376px"} + +# %% [markdown] +# +# ## Basic Usage +# +#
+# +# If no fine-tuning schedule is provided by the user, [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) will generate a +# [default schedule](#The-Default-Finetuning-Schedule) and proceed to fine-tune according to the generated schedule, +# using default [FTSEarlyStopping](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSEarlyStopping) and [FTSCheckpoint](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSCheckpoint) callbacks with ``monitor=val_loss``. +# +#
+# +# ```python +# from pytorch_lightning import Trainer +# from finetuning_scheduler import FinetuningScheduler +# trainer = Trainer(callbacks=[FinetuningScheduler()]) +# ``` + +# %% [markdown] +# ## The Default Fine-Tuning Schedule +# +# Schedule definition is facilitated via the [gen_ft_schedule](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.ScheduleImplMixin.gen_ft_schedule) method which dumps a default fine-tuning schedule (by default using a naive, 2-parameters per level heuristic) which can be adjusted as +# desired by the user and/or subsequently passed to the callback. Using the default/implicitly generated schedule will likely be less computationally efficient than a user-defined fine-tuning schedule but is useful for exploring a model's fine-tuning behavior and can serve as a good baseline for subsequent explicit schedule refinement. +# While the current version of [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) only supports single optimizer and (optional) lr_scheduler configurations, per-phase maximum learning rates can be set as demonstrated in the next section. + +# %% [markdown] +# ## Specifying a Fine-Tuning Schedule +# +# To specify a fine-tuning schedule, it's convenient to first generate the default schedule and then alter the thawed/unfrozen parameter groups associated with each fine-tuning phase as desired. Fine-tuning phases are zero-indexed and executed in ascending order. +# +# 1. First, generate the default schedule to ``Trainer.log_dir``. It will be named after your +# ``LightningModule`` subclass with the suffix ``_ft_schedule.yaml``. +# +# ```python +# from pytorch_lightning import Trainer +# from finetuning_scheduler import FinetuningScheduler +# trainer = Trainer(callbacks=[FinetuningScheduler(gen_ft_sched_only=True)]) +# ``` +# +# 2. Alter the schedule as desired. +# +# ![side_by_side_yaml](side_by_side_yaml.png){height="327px" width="800px"} +# +# 3. Once the fine-tuning schedule has been altered as desired, pass it to +# [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) to commence scheduled training: +# +# ```python +# from pytorch_lightning import Trainer +# from finetuning_scheduler import FinetuningScheduler +# +# trainer = Trainer(callbacks=[FinetuningScheduler(ft_schedule="/path/to/my/schedule/my_schedule.yaml")]) +# ``` + +# %% [markdown] +# ## Early-Stopping and Epoch-Driven Phase Transition Criteria +# +# +# By default, [FTSEarlyStopping](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSEarlyStopping) and epoch-driven +# transition criteria are composed. If a ``max_transition_epoch`` is specified for a given phase, the next fine-tuning phase will begin at that epoch unless [FTSEarlyStopping](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSEarlyStopping) criteria are met first. +# If [FinetuningScheduler.epoch_transitions_only](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler.params.epoch_transitions_only) is ``True``, [FTSEarlyStopping](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSEarlyStopping) will not be used +# and transitions will be exclusively epoch-driven. +# +# +#
+# +# **Tip:** Use of regex expressions can be convenient for specifying more complex schedules. Also, a per-phase base maximum lr can be specified: +# +# ![emphasized_yaml](emphasized_yaml.png){height="380px" width="800px"} +# +#
+# +# +# +# The end-to-end example in this notebook ([Scheduled Fine-Tuning For SuperGLUE](#superglue)) uses [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) in explicit mode to fine-tune a small foundation model on the [RTE](https://huggingface.co/datasets/viewer/?dataset=super_glue&config=rte) task of [SuperGLUE](https://super.gluebenchmark.com/). +# Please see the [official Fine-Tuning Scheduler documentation](https://finetuning-scheduler.readthedocs.io/en/stable/index.html) if you are interested in a similar [CLI-based example](https://finetuning-scheduler.readthedocs.io/en/stable/index.html#example-scheduled-fine-tuning-for-superglue) using the LightningCLI. + +# %% [markdown] +# ## Resuming Scheduled Fine-Tuning Training Sessions +# +# Resumption of scheduled fine-tuning training is identical to the continuation of +# [other training sessions](https://lightning.ai/docs/pytorch/stable/common/trainer.html) with the caveat that the provided checkpoint must have been saved by a [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) session. +# [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) uses [FTSCheckpoint](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSCheckpoint) (an extension of ``ModelCheckpoint``) to maintain schedule state with special metadata. +# +# +# ```python +# from pytorch_lightning import Trainer +# from finetuning_scheduler import FinetuningScheduler +# trainer = Trainer(callbacks=[FinetuningScheduler()]) +# trainer.fit(..., ckpt_path="some/path/to/my_checkpoint.ckpt") +# ``` +# +# Training will resume at the depth/level of the provided checkpoint according to the specified schedule. Schedules can be altered between training sessions but schedule compatibility is left to the user for maximal flexibility. If executing a user-defined schedule, typically the same schedule should be provided for the original and resumed training sessions. +# +# By default ([FinetuningScheduler.restore_best](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html?highlight=restore_best#finetuning_scheduler.fts.FinetuningScheduler.params.restore_best) is ``True``), [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) will attempt to restore the best available checkpoint before fine-tuning depth transitions. +# +# ```python +# trainer = Trainer(callbacks=[FinetuningScheduler()]) +# trainer.fit(..., ckpt_path="some/path/to/my_kth_best_checkpoint.ckpt") +# ``` +# +# Note that similar to the behavior of [ModelCheckpoint](https://lightning.ai/docs/pytorch/stable/api/pytorch_lightning.callbacks.ModelCheckpoint.html), (specifically [this PR](https://github.com/Lightning-AI/lightning/pull/12045)), +# when resuming training with a different [FTSCheckpoint](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSCheckpoint) ``dirpath`` from the provided +# checkpoint, the new training session's checkpoint state will be re-initialized at the resumption depth with the provided checkpoint being set as the best checkpoint. + +# %% [markdown] +#
+# +# **Note:** Currently, [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) supports the following strategy types: +# +# - ``ddp`` (and alias ``ddp_find_unused_parameters_false``) +# - ``fsdp_native`` (and alias ``fsdp_native_full_shard_offload``) +# - ``ddp_spawn`` (and aliases ``ddp_fork``, ``ddp_notebook``) +# - ``dp`` +# - ``ddp_sharded`` (deprecated, to be removed in 2.0) +# - ``ddp_sharded_spawn`` (deprecated, to be removed in 2.0) +# +# Custom or officially unsupported strategies can be used by setting [FinetuningScheduler.allow_untested](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html?highlight=allow_untested#finetuning_scheduler.fts.FinetuningScheduler.params.allow_untested) to ``True``. +# Note that most currently unsupported strategies are so because they require varying degrees of modification to be compatible. For example, ``deepspeed`` will require a [StrategyAdapter](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.strategy_adapters.html#finetuning_scheduler.strategy_adapters.StrategyAdapter) to be written (similar to the one for ``FSDP``, [FSDPStrategyAdapter](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.strategy_adapters.html#finetuning_scheduler.strategy_adapters.FSDPStrategyAdapter)) before support can be added (PRs welcome!), +# while ``tpu_spawn`` would require an override of the current broadcast method to include python objects. +#
+ +# %% [markdown] +#
+# +# ## Scheduled Fine-Tuning For SuperGLUE +# +# The following example demonstrates the use of [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) to fine-tune a small foundation model on the [RTE](https://huggingface.co/datasets/viewer/?dataset=super_glue&config=rte) task of [SuperGLUE](https://super.gluebenchmark.com/). Iterative early-stopping will be applied according to a user-specified schedule. +# + +# %% +import os +import warnings +from datetime import datetime +from typing import Any, Dict, List, Optional + +from packaging.version import Version + +import sentencepiece as sp # noqa: F401 # isort: split +import datasets +import evaluate +import pytorch_lightning as pl +import torch +from datasets import logging as datasets_logging +from lightning_fabric.accelerators.cuda import is_cuda_available +from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint +from pytorch_lightning.loggers.tensorboard import TensorBoardLogger +from pytorch_lightning.utilities import rank_zero_warn +from torch.optim.lr_scheduler import CosineAnnealingWarmRestarts +from torch.utils.data import DataLoader +from transformers import AutoConfig, AutoModelForSequenceClassification, AutoTokenizer +from transformers import logging as transformers_logging +from transformers.tokenization_utils_base import BatchEncoding + +if Version(torch.__version__) == Version("1.12.0") or torch.__version__.startswith("1.12.0"): + # we need to use a patched version of AdamW to fix https://github.com/pytorch/pytorch/issues/80809 + # and allow examples to succeed with torch 1.12.0 (this torch bug is fixed in 1.12.1) + from fts_examples.patched_adamw import AdamW +else: + from torch.optim.adamw import AdamW + +# %% +# Import the `FinetuningScheduler` PyTorch Lightning extension module we want to use. This will import all necessary callbacks. +import finetuning_scheduler as fts # isort: split + +# set notebook-level variables +TASK_NUM_LABELS = {"boolq": 2, "rte": 2} +DEFAULT_TASK = "rte" + +# reduce hf logging verbosity to focus on tutorial-relevant code/messages +for hflogger in [transformers_logging, datasets_logging]: + hflogger.set_verbosity_error() +# ignore warnings related tokenizers_parallelism/DataLoader parallelism trade-off and +# expected logging behavior +for warnf in [ + r".*does not have many workers.*", + r".*The number of training samples.*", + r".*converting to a fast.*", + r".*number of training batches.*", +]: + warnings.filterwarnings("ignore", warnf) + + +# %% +class RteBoolqDataModule(pl.LightningDataModule): + """A ``LightningDataModule`` designed for both the RTE or BoolQ SuperGLUE Hugging Face datasets.""" + + TASK_TEXT_FIELD_MAP = {"rte": ("premise", "hypothesis"), "boolq": ("question", "passage")} + LOADER_COLUMNS = ( + "datasets_idx", + "input_ids", + "token_type_ids", + "attention_mask", + "start_positions", + "end_positions", + "labels", + ) + + def __init__( + self, + model_name_or_path: str, + task_name: str = DEFAULT_TASK, + max_seq_length: int = 128, + train_batch_size: int = 16, + eval_batch_size: int = 16, + tokenizers_parallelism: bool = True, + **dataloader_kwargs: Any, + ): + r"""Initialize the ``LightningDataModule`` designed for both the RTE or BoolQ SuperGLUE Hugging Face + datasets. + + Args: + model_name_or_path (str): + Can be either: + - A string, the ``model id`` of a pretrained model hosted inside a model repo on huggingface.co. + Valid model ids can be located at the root-level, like ``bert-base-uncased``, or namespaced + under a user or organization name, like ``dbmdz/bert-base-german-cased``. + - A path to a ``directory`` containing model weights saved using + :meth:`~transformers.PreTrainedModel.save_pretrained`, e.g., ``./my_model_directory/``. + task_name (str, optional): Name of the SuperGLUE task to execute. This module supports 'rte' or 'boolq'. + Defaults to DEFAULT_TASK which is 'rte'. + max_seq_length (int, optional): Length to which we will pad sequences or truncate input. Defaults to 128. + train_batch_size (int, optional): Training batch size. Defaults to 16. + eval_batch_size (int, optional): Batch size to use for validation and testing splits. Defaults to 16. + tokenizers_parallelism (bool, optional): Whether to use parallelism in the tokenizer. Defaults to True. + \**dataloader_kwargs: Arguments passed when initializing the dataloader. + """ + super().__init__() + task_name = task_name if task_name in TASK_NUM_LABELS.keys() else DEFAULT_TASK + self.text_fields = self.TASK_TEXT_FIELD_MAP[task_name] + self.dataloader_kwargs = { + "num_workers": dataloader_kwargs.get("num_workers", 0), + "pin_memory": dataloader_kwargs.get("pin_memory", False), + } + self.save_hyperparameters() + os.environ["TOKENIZERS_PARALLELISM"] = "true" if self.hparams.tokenizers_parallelism else "false" + self.tokenizer = AutoTokenizer.from_pretrained( + self.hparams.model_name_or_path, use_fast=True, local_files_only=False + ) + + def prepare_data(self): + """Load the SuperGLUE dataset.""" + # N.B. PL calls prepare_data from a single process (rank 0) so do not use it to assign + # state (e.g. self.x=y) + datasets.load_dataset("super_glue", self.hparams.task_name) + + def setup(self, stage): + """Setup our dataset splits for training/validation.""" + self.dataset = datasets.load_dataset("super_glue", self.hparams.task_name) + for split in self.dataset.keys(): + self.dataset[split] = self.dataset[split].map( + self._convert_to_features, batched=True, remove_columns=["label"] + ) + self.columns = [c for c in self.dataset[split].column_names if c in self.LOADER_COLUMNS] + self.dataset[split].set_format(type="torch", columns=self.columns) + + self.eval_splits = [x for x in self.dataset.keys() if "validation" in x] + + def train_dataloader(self): + return DataLoader(self.dataset["train"], batch_size=self.hparams.train_batch_size, **self.dataloader_kwargs) + + def val_dataloader(self): + return DataLoader(self.dataset["validation"], batch_size=self.hparams.eval_batch_size, **self.dataloader_kwargs) + + def _convert_to_features(self, example_batch: datasets.arrow_dataset.LazyDict) -> BatchEncoding: + """Convert raw text examples to a :class:`~transformers.tokenization_utils_base.BatchEncoding` container + (derived from python dict) of features that includes helpful methods for translating between word/character + space and token space. + + Args: + example_batch ([type]): The set of examples to convert to token space. + + Returns: + ``BatchEncoding``: A batch of encoded examples (note default tokenizer batch_size=1000). + """ + text_pairs = list(zip(example_batch[self.text_fields[0]], example_batch[self.text_fields[1]])) + # Tokenize the text/text pairs + features = self.tokenizer.batch_encode_plus( + text_pairs, max_length=self.hparams.max_seq_length, padding="longest", truncation=True + ) + # Rename label to labels to make it easier to pass to model forward + features["labels"] = example_batch["label"] + return features + + +# %% +class RteBoolqModule(pl.LightningModule): + """A ``LightningModule`` that can be used to fine-tune a foundation model on either the RTE or BoolQ SuperGLUE + tasks using Hugging Face implementations of a given model and the `SuperGLUE Hugging Face dataset.""" + + def __init__( + self, + model_name_or_path: str, + optimizer_init: Dict[str, Any], + lr_scheduler_init: Dict[str, Any], + model_cfg: Optional[Dict[str, Any]] = None, + task_name: str = DEFAULT_TASK, + experiment_tag: str = "default", + ): + """ + Args: + model_name_or_path (str): Path to pretrained model or identifier from https://huggingface.co/models. + optimizer_init (Dict[str, Any]): The desired optimizer configuration. + lr_scheduler_init (Dict[str, Any]): The desired learning rate scheduler config. + model_cfg (Optional[Dict[str, Any]], optional): Defines overrides of the default model config. Defaults to + ``None``. + task_name (str, optional): The SuperGLUE task to execute, one of ``'rte'``, ``'boolq'``. Defaults to "rte". + experiment_tag (str, optional): The tag to use for the experiment and tensorboard logs. Defaults to + "default". + """ + super().__init__() + if task_name not in TASK_NUM_LABELS.keys(): + rank_zero_warn(f"Invalid task_name {task_name!r}. Proceeding with the default task: {DEFAULT_TASK!r}") + task_name = DEFAULT_TASK + self.num_labels = TASK_NUM_LABELS[task_name] + self.model_cfg = model_cfg or {} + conf = AutoConfig.from_pretrained(model_name_or_path, num_labels=self.num_labels, local_files_only=False) + self.model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, config=conf) + self.model.config.update(self.model_cfg) # apply model config overrides + self.init_hparams = { + "optimizer_init": optimizer_init, + "lr_scheduler_init": lr_scheduler_init, + "model_config": self.model.config, + "model_name_or_path": model_name_or_path, + "task_name": task_name, + "experiment_id": f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_{experiment_tag}", + } + self.save_hyperparameters(self.init_hparams) + self.metric = evaluate.load("super_glue", self.hparams.task_name, experiment_id=self.hparams.experiment_id) + self.no_decay = ["bias", "LayerNorm.weight"] + + @property + def finetuningscheduler_callback(self) -> fts.FinetuningScheduler: + fts_callback = [c for c in self.trainer.callbacks if isinstance(c, fts.FinetuningScheduler)] + return fts_callback[0] if fts_callback else None + + def forward(self, **inputs): + return self.model(**inputs) + + def training_step(self, batch, batch_idx): + outputs = self(**batch) + loss = outputs[0] + self.log("train_loss", loss) + return loss + + def training_epoch_end(self, outputs: List[Any]) -> None: + if self.finetuningscheduler_callback: + self.log("finetuning_schedule_depth", float(self.finetuningscheduler_callback.curr_depth)) + + def validation_step(self, batch, batch_idx, dataloader_idx=0): + outputs = self(**batch) + val_loss, logits = outputs[:2] + if self.num_labels >= 1: + preds = torch.argmax(logits, axis=1) + elif self.num_labels == 1: + preds = logits.squeeze() + labels = batch["labels"] + self.log("val_loss", val_loss, prog_bar=True) + metric_dict = self.metric.compute(predictions=preds, references=labels) + self.log_dict(metric_dict, prog_bar=True) + + def _init_param_groups(self) -> List[Dict]: + """Initialize the parameter groups. Used to ensure weight_decay is not applied to our specified bias + parameters when we initialize the optimizer. + + Returns: + List[Dict]: A list of parameter group dictionaries. + """ + return [ + { + "params": [ + p + for n, p in self.model.named_parameters() + if not any(nd in n for nd in self.no_decay) and p.requires_grad + ], + "weight_decay": self.hparams.optimizer_init["weight_decay"], + }, + { + "params": [ + p + for n, p in self.model.named_parameters() + if any(nd in n for nd in self.no_decay) and p.requires_grad + ], + "weight_decay": 0.0, + }, + ] + + def configure_optimizers(self): + # the phase 0 parameters will have been set to require gradients during setup + # you can initialize the optimizer with a simple requires.grad filter as is often done, + # but in this case we pass a list of parameter groups to ensure weight_decay is + # not applied to the bias parameter (for completeness, in this case it won't make much + # performance difference) + optimizer = AdamW(params=self._init_param_groups(), **self.hparams.optimizer_init) + scheduler = { + "scheduler": CosineAnnealingWarmRestarts(optimizer, **self.hparams.lr_scheduler_init), + "interval": "epoch", + } + return [optimizer], [scheduler] + + +# %% [markdown] +# ### Our Training Sessions +# +# We'll be comparing three different fine-tuning training configurations. Every configuration in this example depends +# upon a shared set of defaults, only differing in their respective fine-tuning schedules. +# +# | Experiment Tag | Training Scenario Description | +# |:-----------------:| ---------------------------------------------------------------------- | +# | ``fts_explicit`` | Training with a fine-tuning schedule explicitly provided by the user | +# | ``nofts_baseline``| A baseline fine-tuning training session (without scheduled fine-tuning) | +# | ``fts_implicit`` | Training with an implicitly generated fine-tuning schedule (the default) | +# +# Let's begin by configuring the ``fts_explicit`` scenario. We'll subsequently run the other two scenarios for +# comparison. + +# %% +# Let's create a fine-tuning schedule for our model and run an explicitly scheduled fine-tuning training scenario with it +# Please see the [FinetuningScheduler documentation](https://finetuning-scheduler.readthedocs.io/en/stable/index.html) for a full description of the schedule format + + +ft_schedule_yaml = """ +0: + params: + - model.classifier.bias + - model.classifier.weight + - model.pooler.dense.bias + - model.pooler.dense.weight + - model.deberta.encoder.LayerNorm.bias + - model.deberta.encoder.LayerNorm.weight + - model.deberta.encoder.rel_embeddings.weight + - model.deberta.encoder.layer.{0,11}.(output|attention|intermediate).* +1: + params: + - model.deberta.embeddings.LayerNorm.bias + - model.deberta.embeddings.LayerNorm.weight +2: + params: + - model.deberta.embeddings.word_embeddings.weight +""" +ft_schedule_name = "RteBoolqModule_ft_schedule_deberta_base.yaml" +# Let's write the schedule to a file so we can simulate loading an explicitly defined fine-tuning +# schedule. +with open(ft_schedule_name, "w") as f: + f.write(ft_schedule_yaml) + +# %% +datasets.logging.disable_progress_bar() +pl.seed_everything(42) +dm = RteBoolqDataModule(model_name_or_path="microsoft/deberta-v3-base", tokenizers_parallelism=True) + +# %% [markdown] +# ### Optimizer Configuration +# +#
+# +# Though other optimizers can arguably yield some marginal advantage contingent on the context, +# the Adam optimizer (and the [AdamW version](https://pytorch.org/docs/stable/_modules/torch/optim/adamw.html#AdamW) which +# implements decoupled weight decay) remains robust to hyperparameter choices and is commonly used for fine-tuning +# foundation language models. See [(Sivaprasad et al., 2020)](#f2) and [(Mosbach, Andriushchenko & Klakow, 2020)](#f3) for theoretical and systematic empirical justifications of Adam and its use in fine-tuning +# large transformer-based language models. The values used here have some justification +# in the referenced literature but have been largely empirically determined and while a good +# starting point could be could be further tuned. +# +#
+ +# %% +optimizer_init = {"weight_decay": 1e-05, "eps": 1e-07, "lr": 1e-05} + +# %% [markdown] +# ### LR Scheduler Configuration +# +#
+# +# The [CosineAnnealingWarmRestarts scheduler](https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.CosineAnnealingWarmRestarts.html?highlight=cosineannealingwarm#torch.optim.lr_scheduler.CosineAnnealingWarmRestarts) nicely fits with our iterative fine-tuning since it does not depend upon a global max_epoch +# value. The importance of initial warmup is reduced due to the innate warmup effect of Adam bias correction [[5]](#f3) +# and the gradual thawing we are performing. Note that commonly used LR schedulers that depend on providing +# max_iterations/epochs (e.g. the +# [CosineWarmupScheduler](https://github.com/Lightning-AI/tutorials/blob/0c325829101d5a6ebf32ed99bbf5b09badf04a59/course_UvA-DL/05-transformers-and-MH-attention/Transformers_MHAttention.py#L688) +# used in other pytorch-lightning tutorials) also work with FinetuningScheduler. Though the LR scheduler is theoretically +# justified [(Loshchilov & Hutter, 2016)](#f4), the particular values provided here are primarily empircally driven. +# +# [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) also supports LR scheduler +# reinitialization in both explicit and implicit finetuning schedule modes. See the [advanced usage documentation](https://finetuning-scheduler.readthedocs.io/en/stable/advanced/lr_scheduler_reinitialization.html) for explanations and demonstration of the extension's support for more complex requirements. +#
+ + +# %% +lr_scheduler_init = {"T_0": 1, "T_mult": 2, "eta_min": 1e-07} + +# %% +# Load our lightning module... +lightning_module_kwargs = { + "model_name_or_path": "microsoft/deberta-v3-base", + "optimizer_init": optimizer_init, + "lr_scheduler_init": lr_scheduler_init, +} +model = RteBoolqModule(**lightning_module_kwargs, experiment_tag="fts_explicit") + +# %% [markdown] +# ### Callback Configuration +# +# The only callback required to invoke the [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) is the [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) callback itself. +# Default versions of [FTSCheckpoint](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSCheckpoint) and [FTSEarlyStopping](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts_supporters.html#finetuning_scheduler.fts_supporters.FTSEarlyStopping) +# (if not specifying ``epoch_only_transitions``) will be included ([as discussed above](#basic_usage)) if not provided +# in the callbacks list. For demonstration purposes I'm including example configurations of all three callbacks below. + +# %% +# let's save our callback configurations for the explicit scenario since we'll be reusing the same +# configurations for the implicit and nofts_baseline scenarios (except the config for the +# FinetuningScheduler callback itself of course in the case of nofts_baseline) +earlystopping_kwargs = {"monitor": "val_loss", "min_delta": 0.001, "patience": 2} +checkpoint_kwargs = {"monitor": "val_loss", "save_top_k": 1} +fts_kwargs = {"max_depth": 1} +callbacks = [ + fts.FinetuningScheduler(ft_schedule=ft_schedule_name, **fts_kwargs), + fts.FTSEarlyStopping(**earlystopping_kwargs), + fts.FTSCheckpoint(**checkpoint_kwargs), +] + +# %% +logger = TensorBoardLogger("lightning_logs", name="fts_explicit") +# optionally start tensorboard and monitor progress graphically while viewing multi-phase fine-tuning specific training +# logs in the cell output below by uncommenting the next 2 lines +# # %load_ext tensorboard +# # %tensorboard --logdir lightning_logs +# disable progress bar by default to focus on multi-phase training logs. Set to True to re-enable if desired +enable_progress_bar = False + +# %% + + +def train() -> None: + trainer = pl.Trainer( + enable_progress_bar=enable_progress_bar, + max_epochs=100, + precision=16, + accelerator="auto", + devices=1 if is_cuda_available() else None, + callbacks=callbacks, + logger=logger, + ) + trainer.fit(model, datamodule=dm) + + +print( + "Note given the computation associated w/ the multiple phases of fine-tuning demonstrated, this notebook is best used with an accelerator" +) +train() + +# %% [markdown] +# ### Running the Baseline and Implicit Fine-Tuning Scenarios +# +# Let's now compare our ``nofts_baseline`` and ``fts_implicit`` scenarios with the ``fts_explicit`` one we just ran. +# +# We'll need to update our callbacks list, using the core PL ``EarlyStopping`` and ``ModelCheckpoint`` callbacks for the +# ``nofts_baseline`` (which operate identically to their FTS analogs apart from the recursive training support). +# For both core PyTorch Lightning and user-registered callbacks, we can define our callbacks using a dictionary as we do +# with the LightningCLI. This allows us to avoid managing imports and support more complex configuration separated from +# code. +# +# Note that we'll be using identical callback configurations to the ``fts_explicit`` scenario. Keeping [max_depth](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html?highlight=max_depth#finetuning_scheduler.fts.FinetuningScheduler.params.max_depth) for +# the implicit schedule will limit fine-tuning to just the last 4 parameters of the model, which is only a small fraction +# of the parameters you'd want to tune for maximum performance. Since the implicit schedule is quite computationally +# intensive and most useful for exploring model behavior, leaving [max_depth](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html?highlight=max_depth#finetuning_scheduler.fts.FinetuningScheduler.params.max_depth) 1 allows us to demo implicit mode +# behavior while keeping the computational cost and runtime of this notebook reasonable. To review how a full implicit +# mode run compares to the ``nofts_baseline`` and ``fts_explicit`` scenarios, please see the the following +# [tensorboard experiment summary](https://tensorboard.dev/experiment/n7U8XhrzRbmvVzC4SQSpWw/). + + +# %% +nofts_callbacks = [EarlyStopping(**earlystopping_kwargs), ModelCheckpoint(**checkpoint_kwargs)] +fts_implicit_callbacks = [ + fts.FinetuningScheduler(**fts_kwargs), + fts.FTSEarlyStopping(**earlystopping_kwargs), + fts.FTSCheckpoint(**checkpoint_kwargs), +] +scenario_callbacks = {"nofts_baseline": nofts_callbacks, "fts_implicit": fts_implicit_callbacks} + +# %% +for scenario_name, scenario_callbacks in scenario_callbacks.items(): + model = RteBoolqModule(**lightning_module_kwargs, experiment_tag=scenario_name) + logger = TensorBoardLogger("lightning_logs", name=scenario_name) + callbacks = scenario_callbacks + print(f"Beginning training the '{scenario_name}' scenario") + train() + +# %% [markdown] +# ### Reviewing the Training Results +# +# See the [tensorboard experiment summaries](https://tensorboard.dev/experiment/n7U8XhrzRbmvVzC4SQSpWw/) to get a sense +# of the relative computational and performance tradeoffs associated with these [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) configurations. +# The summary compares a full ``fts_implicit`` execution to ``fts_explicit`` and ``nofts_baseline`` scenarios using DDP +# training with 2 GPUs. The full logs/schedules and detailed system configuration used for all three scenarios are available +# [here](https://drive.google.com/file/d/1LrUcisRLHeJgh_BDOOD_GUBPp5iHAkoR/view?usp=sharing) and the checkpoints +# produced in the scenarios [here](https://drive.google.com/file/d/1t7myBgcqcZ9ax_IT9QVk-vFH_l_o5UXB/view?usp=sharing) +# (caution, ~3.5GB). +# +# [![fts_explicit_accuracy](fts_explicit_accuracy.png){height="315px" width="492px"}](https://tensorboard.dev/experiment/n7U8XhrzRbmvVzC4SQSpWw/#scalars&_smoothingWeight=0&runSelectionState=eyJmdHNfZXhwbGljaXQiOnRydWUsIm5vZnRzX2Jhc2VsaW5lIjpmYWxzZSwiZnRzX2ltcGxpY2l0IjpmYWxzZX0%3D) +# [![nofts_baseline](nofts_baseline_accuracy.png){height="316px" width="505px"}](https://tensorboard.dev/experiment/n7U8XhrzRbmvVzC4SQSpWw/#scalars&_smoothingWeight=0&runSelectionState=eyJmdHNfZXhwbGljaXQiOmZhbHNlLCJub2Z0c19iYXNlbGluZSI6dHJ1ZSwiZnRzX2ltcGxpY2l0IjpmYWxzZX0%3D) +# +# Note that the results above may vary to a small degree from the tensorboard summaries generated by this notebook +# which used DP, 1 GPU and likely when you're running this, different versions of certain software components (e.g. pytorch, transformers). +# +# [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) expands the space of possible fine-tuning schedules and the composition of more sophisticated schedules can +# yield marginal fine-tuning performance gains. That stated, it should be emphasized the primary utility of [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) is to grant +# greater fine-tuning flexibility for model exploration in research. For example, glancing at DeBERTa-v3's implicit training +# run, a critical tuning transition point is immediately apparent: +# +# [![implicit_training_transition](implicit_training_transition.png){height="272px" width="494px"}](https://tensorboard.dev/experiment/n7U8XhrzRbmvVzC4SQSpWw/#scalars&_smoothingWeight=0&runSelectionState=eyJmdHNfZXhwbGljaXQiOmZhbHNlLCJub2Z0c19iYXNlbGluZSI6ZmFsc2UsImZ0c19pbXBsaWNpdCI6dHJ1ZX0%3D) +# +# Our `val_loss` begins a precipitous decline at step 3119 which corresponds to phase 17 in the schedule. Referring to our +# schedule, in phase 17 we're beginning tuning the attention parameters of our 10th encoder layer (of 11). Interesting! +# Though beyond the scope of this tutorial, it might be worth investigating these dynamics further and +# [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) allows one to do just that quite easily. +# +# %% [markdown] +# +# Note that though this example is intended to capture a common usage scenario, substantial variation is expected +# among use cases and models. +# In summary, [FinetuningScheduler](https://finetuning-scheduler.readthedocs.io/en/stable/api/finetuning_scheduler.fts.html#finetuning_scheduler.fts.FinetuningScheduler) provides increased fine-tuning flexibility that can be useful in a variety of +# contexts from exploring model tuning behavior to maximizing performance. +# %% [markdown] +# ## Footnotes +# +#
    +#
  1. +# +# [Howard, J., & Ruder, S. (2018)](https://arxiv.org/pdf/1801.06146.pdf). Fine-tuned Language +# Models for Text Classification. ArXiv, abs/1801.06146. [↩](#a1) +# +#
  2. +#
  3. +# +# [Chronopoulou, A., Baziotis, C., & Potamianos, A. (2019)](https://arxiv.org/pdf/1902.10547.pdf). +# An embarrassingly simple approach for transfer learning from pretrained language models. arXiv +# preprint arXiv:1902.10547. [↩](#a1) +# +#
  4. +#
  5. +# +# [Peters, M. E., Ruder, S., & Smith, N. A. (2019)](https://arxiv.org/pdf/1903.05987.pdf). To tune or not to +# tune? adapting pretrained representations to diverse tasks. arXiv preprint arXiv:1903.05987. [↩](#a1) +# +#
  6. +#
  7. +# +# [Sivaprasad, P. T., Mai, F., Vogels, T., Jaggi, M., & Fleuret, F. (2020)](https://arxiv.org/pdf/1910.11758.pdf). +# Optimizer benchmarking needs to account for hyperparameter tuning. In International Conference on Machine Learning +# (pp. 9036-9045). PMLR. [↩](#a2) +# +#
  8. +#
  9. +# +# [Mosbach, M., Andriushchenko, M., & Klakow, D. (2020)](https://arxiv.org/pdf/2006.04884.pdf). On the stability of +# fine-tuning bert: Misconceptions, explanations, and strong baselines. arXiv preprint arXiv:2006.04884. [↩](#a2) +# +#
  10. +#
  11. +# +# [Loshchilov, I., & Hutter, F. (2016)](https://arxiv.org/pdf/1608.03983.pdf). Sgdr: Stochastic gradient descent with +# warm restarts. arXiv preprint arXiv:1608.03983. [↩](#a3) +# +#
  12. +# +#
+ +# %% [markdown] +# diff --git a/_notebooks/lightning_examples/finetuning-scheduler/fts_explicit_accuracy.png b/_notebooks/lightning_examples/finetuning-scheduler/fts_explicit_accuracy.png new file mode 100644 index 0000000..b5d8f55 Binary files /dev/null and b/_notebooks/lightning_examples/finetuning-scheduler/fts_explicit_accuracy.png differ diff --git a/_notebooks/lightning_examples/finetuning-scheduler/fts_explicit_loss_anim.gif b/_notebooks/lightning_examples/finetuning-scheduler/fts_explicit_loss_anim.gif new file mode 100644 index 0000000..7451f65 Binary files /dev/null and b/_notebooks/lightning_examples/finetuning-scheduler/fts_explicit_loss_anim.gif differ diff --git a/_notebooks/lightning_examples/finetuning-scheduler/implicit_training_transition.png b/_notebooks/lightning_examples/finetuning-scheduler/implicit_training_transition.png new file mode 100644 index 0000000..6854dbf Binary files /dev/null and b/_notebooks/lightning_examples/finetuning-scheduler/implicit_training_transition.png differ diff --git a/_notebooks/lightning_examples/finetuning-scheduler/logo_fts.png b/_notebooks/lightning_examples/finetuning-scheduler/logo_fts.png new file mode 100644 index 0000000..02e14a3 Binary files /dev/null and b/_notebooks/lightning_examples/finetuning-scheduler/logo_fts.png differ diff --git a/_notebooks/lightning_examples/finetuning-scheduler/nofts_baseline_accuracy.png b/_notebooks/lightning_examples/finetuning-scheduler/nofts_baseline_accuracy.png new file mode 100644 index 0000000..b78f8c6 Binary files /dev/null and b/_notebooks/lightning_examples/finetuning-scheduler/nofts_baseline_accuracy.png differ diff --git a/_notebooks/lightning_examples/finetuning-scheduler/side_by_side_yaml.png b/_notebooks/lightning_examples/finetuning-scheduler/side_by_side_yaml.png new file mode 100644 index 0000000..3a32a1f Binary files /dev/null and b/_notebooks/lightning_examples/finetuning-scheduler/side_by_side_yaml.png differ diff --git a/_notebooks/lightning_examples/mnist-hello-world/.meta.yml b/_notebooks/lightning_examples/mnist-hello-world/.meta.yml new file mode 100644 index 0000000..ae9f221 --- /dev/null +++ b/_notebooks/lightning_examples/mnist-hello-world/.meta.yml @@ -0,0 +1,19 @@ +title: Introduction to PyTorch Lightning +author: PL team +created: 2020-12-21 +updated: 2023-05-15 +license: CC BY-SA +build: 0 +tags: + - Image +description: In this notebook, we'll go over the basics of lightning by preparing + models to train on the [MNIST Handwritten Digits dataset](https://en.wikipedia.org/wiki/MNIST_database). +requirements: + - torchvision + - torchmetrics >=0.11.0 + - pandas + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/_notebooks/lightning_examples/mnist-hello-world/hello-world.py b/_notebooks/lightning_examples/mnist-hello-world/hello-world.py new file mode 100644 index 0000000..cd6c6ef --- /dev/null +++ b/_notebooks/lightning_examples/mnist-hello-world/hello-world.py @@ -0,0 +1,238 @@ +# %% +import os + +import lightning as L +import pandas as pd +import seaborn as sn +import torch +from IPython.display import display +from lightning.pytorch.loggers import CSVLogger +from torch import nn +from torch.nn import functional as F +from torch.utils.data import DataLoader, random_split +from torchmetrics import Accuracy +from torchvision import transforms +from torchvision.datasets import MNIST + +PATH_DATASETS = os.environ.get("PATH_DATASETS", ".") +BATCH_SIZE = 256 if torch.cuda.is_available() else 64 + +# %% [markdown] +# ## Simplest example +# +# Here's the simplest most minimal example with just a training loop (no validation, no testing). +# +# **Keep in Mind** - A `LightningModule` *is* a PyTorch `nn.Module` - it just has a few more helpful features. + + +# %% +class MNISTModel(L.LightningModule): + def __init__(self): + super().__init__() + self.l1 = torch.nn.Linear(28 * 28, 10) + + def forward(self, x): + return torch.relu(self.l1(x.view(x.size(0), -1))) + + def training_step(self, batch, batch_nb): + x, y = batch + loss = F.cross_entropy(self(x), y) + return loss + + def configure_optimizers(self): + return torch.optim.Adam(self.parameters(), lr=0.02) + + +# %% [markdown] +# By using the `Trainer` you automatically get: +# 1. Tensorboard logging +# 2. Model checkpointing +# 3. Training and validation loop +# 4. early-stopping + +# %% +# Init our model +mnist_model = MNISTModel() + +# Init DataLoader from MNIST Dataset +train_ds = MNIST(PATH_DATASETS, train=True, download=True, transform=transforms.ToTensor()) +train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE) + +# Initialize a trainer +trainer = L.Trainer( + accelerator="auto", + devices=1, + max_epochs=3, +) + +# Train the model ⚡ +trainer.fit(mnist_model, train_loader) + +# %% [markdown] +# ## A more complete MNIST Lightning Module Example +# +# That wasn't so hard was it? +# +# Now that we've got our feet wet, let's dive in a bit deeper and write a more complete `LightningModule` for MNIST... +# +# This time, we'll bake in all the dataset specific pieces directly in the `LightningModule`. +# This way, we can avoid writing extra code at the beginning of our script every time we want to run it. +# +# --- +# +# ### Note what the following built-in functions are doing: +# +# 1. [prepare_data()](https://lightning.ai/docs/pytorch/stable/common/lightning_module.html#prepare-data) 💾 +# - This is where we can download the dataset. We point to our desired dataset and ask torchvision's `MNIST` dataset class to download if the dataset isn't found there. +# - **Note we do not make any state assignments in this function** (i.e. `self.something = ...`) +# +# 2. [setup(stage)](https://lightning.ai/docs/pytorch/stable/common/lightning_module.html#setup) ⚙️ +# - Loads in data from file and prepares PyTorch tensor datasets for each split (train, val, test). +# - Setup expects a 'stage' arg which is used to separate logic for 'fit' and 'test'. +# - If you don't mind loading all your datasets at once, you can set up a condition to allow for both 'fit' related setup and 'test' related setup to run whenever `None` is passed to `stage` (or ignore it altogether and exclude any conditionals). +# - **Note this runs across all GPUs and it *is* safe to make state assignments here** +# +# 3. [x_dataloader()](https://lightning.ai/docs/pytorch/stable/api/pytorch_lightning.core.hooks.DataHooks.html#pytorch_lightning.core.hooks.DataHooks.train_dataloader) ♻️ +# - `train_dataloader()`, `val_dataloader()`, and `test_dataloader()` all return PyTorch `DataLoader` instances that are created by wrapping their respective datasets that we prepared in `setup()` + + +# %% +class LitMNIST(L.LightningModule): + def __init__(self, data_dir=PATH_DATASETS, hidden_size=64, learning_rate=2e-4): + super().__init__() + + # Set our init args as class attributes + self.data_dir = data_dir + self.hidden_size = hidden_size + self.learning_rate = learning_rate + + # Hardcode some dataset specific attributes + self.num_classes = 10 + self.dims = (1, 28, 28) + channels, width, height = self.dims + self.transform = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,)), + ] + ) + + # Define PyTorch model + self.model = nn.Sequential( + nn.Flatten(), + nn.Linear(channels * width * height, hidden_size), + nn.ReLU(), + nn.Dropout(0.1), + nn.Linear(hidden_size, hidden_size), + nn.ReLU(), + nn.Dropout(0.1), + nn.Linear(hidden_size, self.num_classes), + ) + + self.val_accuracy = Accuracy(task="multiclass", num_classes=10) + self.test_accuracy = Accuracy(task="multiclass", num_classes=10) + + def forward(self, x): + x = self.model(x) + return F.log_softmax(x, dim=1) + + def training_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + return loss + + def validation_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + preds = torch.argmax(logits, dim=1) + self.val_accuracy.update(preds, y) + + # Calling self.log will surface up scalars for you in TensorBoard + self.log("val_loss", loss, prog_bar=True) + self.log("val_acc", self.val_accuracy, prog_bar=True) + + def test_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + preds = torch.argmax(logits, dim=1) + self.test_accuracy.update(preds, y) + + # Calling self.log will surface up scalars for you in TensorBoard + self.log("test_loss", loss, prog_bar=True) + self.log("test_acc", self.test_accuracy, prog_bar=True) + + def configure_optimizers(self): + optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate) + return optimizer + + #################### + # DATA RELATED HOOKS + #################### + + def prepare_data(self): + # download + MNIST(self.data_dir, train=True, download=True) + MNIST(self.data_dir, train=False, download=True) + + def setup(self, stage=None): + # Assign train/val datasets for use in dataloaders + if stage == "fit" or stage is None: + mnist_full = MNIST(self.data_dir, train=True, transform=self.transform) + self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) + + # Assign test dataset for use in dataloader(s) + if stage == "test" or stage is None: + self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform) + + def train_dataloader(self): + return DataLoader(self.mnist_train, batch_size=BATCH_SIZE) + + def val_dataloader(self): + return DataLoader(self.mnist_val, batch_size=BATCH_SIZE) + + def test_dataloader(self): + return DataLoader(self.mnist_test, batch_size=BATCH_SIZE) + + +# %% +model = LitMNIST() +trainer = L.Trainer( + accelerator="auto", + devices=1, + max_epochs=3, + logger=CSVLogger(save_dir="logs/"), +) +trainer.fit(model) + +# %% [markdown] +# ### Testing +# +# To test a model, call `trainer.test(model)`. +# +# Or, if you've just trained a model, you can just call `trainer.test()` and Lightning will automatically +# test using the best saved checkpoint (conditioned on val_loss). + +# %% +trainer.test() + +# %% [markdown] +# ### Bonus Tip +# +# You can keep calling `trainer.fit(model)` as many times as you'd like to continue training + +# %% +trainer.fit(model) + +# %% [markdown] +# In Colab, you can use the TensorBoard magic function to view the logs that Lightning has created for you! + +# %% + +metrics = pd.read_csv(f"{trainer.logger.log_dir}/metrics.csv") +del metrics["step"] +metrics.set_index("epoch", inplace=True) +display(metrics.dropna(axis=1, how="all").head()) +sn.relplot(data=metrics, kind="line") diff --git a/_notebooks/lightning_examples/mnist-tpu-training/.meta.yml b/_notebooks/lightning_examples/mnist-tpu-training/.meta.yml new file mode 100644 index 0000000..7c82362 --- /dev/null +++ b/_notebooks/lightning_examples/mnist-tpu-training/.meta.yml @@ -0,0 +1,16 @@ +title: TPU training with PyTorch Lightning +author: PL team +created: 2020-12-21 +updated: 2023-05-15 +license: CC BY-SA +build: 0 +tags: + - Image +description: In this notebook, we'll train a model on TPUs. Updating one Trainer flag is all you need for that. + The most up to documentation related to TPU training can be found + [here](https://lightning.ai/docs/pytorch/stable/accelerators/tpu.html). +requirements: + - torchvision + - lightning>=2.0.0rc0 +accelerator: + - TPU diff --git a/_notebooks/lightning_examples/mnist-tpu-training/mnist-tpu.py b/_notebooks/lightning_examples/mnist-tpu-training/mnist-tpu.py new file mode 100644 index 0000000..f0d7427 --- /dev/null +++ b/_notebooks/lightning_examples/mnist-tpu-training/mnist-tpu.py @@ -0,0 +1,174 @@ +# %% [markdown] +# ### Install Colab TPU compatible PyTorch/TPU wheels and dependencies + +# %% +# ! pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.8-cp37-cp37m-linux_x86_64.whl + +import lightning as L + +# %% +import torch +import torch.nn.functional as F +from torch import nn +from torch.utils.data import DataLoader, random_split +from torchmetrics.functional import accuracy +from torchvision import transforms + +# Note - you must have torchvision installed for this example +from torchvision.datasets import MNIST + +BATCH_SIZE = 1024 + +# %% [markdown] +# ### Defining The `MNISTDataModule` +# +# Below we define `MNISTDataModule`. You can learn more about datamodules +# in [docs](https://lightning.ai/docs/pytorch/stable/data/datamodule.html). + + +# %% +class MNISTDataModule(L.LightningDataModule): + def __init__(self, data_dir: str = "./"): + super().__init__() + self.data_dir = data_dir + self.transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) + + self.dims = (1, 28, 28) + self.num_classes = 10 + + def prepare_data(self): + # download + MNIST(self.data_dir, train=True, download=True) + MNIST(self.data_dir, train=False, download=True) + + def setup(self, stage=None): + # Assign train/val datasets for use in dataloaders + if stage == "fit" or stage is None: + mnist_full = MNIST(self.data_dir, train=True, transform=self.transform) + self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) + + # Assign test dataset for use in dataloader(s) + if stage == "test" or stage is None: + self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform) + + def train_dataloader(self): + return DataLoader(self.mnist_train, batch_size=BATCH_SIZE) + + def val_dataloader(self): + return DataLoader(self.mnist_val, batch_size=BATCH_SIZE) + + def test_dataloader(self): + return DataLoader(self.mnist_test, batch_size=BATCH_SIZE) + + +# %% [markdown] +# ### Defining the `LitModel` +# +# Below, we define the model `LitMNIST`. + + +# %% +class LitModel(L.LightningModule): + def __init__(self, channels, width, height, num_classes, hidden_size=64, learning_rate=2e-4): + super().__init__() + + self.save_hyperparameters() + + self.model = nn.Sequential( + nn.Flatten(), + nn.Linear(channels * width * height, hidden_size), + nn.ReLU(), + nn.Dropout(0.1), + nn.Linear(hidden_size, hidden_size), + nn.ReLU(), + nn.Dropout(0.1), + nn.Linear(hidden_size, num_classes), + ) + + def forward(self, x): + x = self.model(x) + return F.log_softmax(x, dim=1) + + def training_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + self.log("train_loss", loss) + return loss + + def validation_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + loss = F.nll_loss(logits, y) + preds = torch.argmax(logits, dim=1) + acc = accuracy(preds, y) + self.log("val_loss", loss, prog_bar=True) + self.log("val_acc", acc, prog_bar=True) + + def configure_optimizers(self): + optimizer = torch.optim.Adam(self.parameters(), lr=self.hparams.learning_rate) + return optimizer + + +# %% [markdown] +# ### TPU Training +# +# Lightning supports training on a single TPU core or 8 TPU cores. +# +# The Trainer parameter `devices` defines how many TPU cores to train on (1 or 8) / Single TPU core to train on [1] +# along with accelerator='tpu'. +# +# For Single TPU training, Just pass the TPU core ID [1-8] in a list. +# Setting `devices=[5]` will train on TPU core ID 5. + +# %% [markdown] +# Train on TPU core ID 5 with `devices=[5]`. + +# %% +# Init DataModule +dm = MNISTDataModule() +# Init model from datamodule's attributes +model = LitModel(*dm.size(), dm.num_classes) +# Init trainer +trainer = L.Trainer( + max_epochs=3, + accelerator="tpu", + devices=[5], +) +# Train +trainer.fit(model, dm) + +# %% [markdown] +# Train on single TPU core with `devices=1`. + +# %% +# Init DataModule +dm = MNISTDataModule() +# Init model from datamodule's attributes +model = LitModel(*dm.dims, dm.num_classes) +# Init trainer +trainer = L.Trainer( + max_epochs=3, + accelerator="tpu", + devices=1, +) +# Train +trainer.fit(model, dm) + +# %% [markdown] +# Train on 8 TPU cores with `accelerator='tpu'` and `devices=8`. +# You might have to restart the notebook to run it on 8 TPU cores after training on single TPU core. + +# %% +# Init DataModule +dm = MNISTDataModule() +# Init model from datamodule's attributes +model = LitModel(*dm.dims, dm.num_classes) +# Init trainer +trainer = L.Trainer( + max_epochs=3, + accelerator="tpu", + devices=8, +) +# Train +trainer.fit(model, dm) diff --git a/_notebooks/lightning_examples/reinforce-learning-DQN/.meta.yml b/_notebooks/lightning_examples/reinforce-learning-DQN/.meta.yml new file mode 100644 index 0000000..abf466a --- /dev/null +++ b/_notebooks/lightning_examples/reinforce-learning-DQN/.meta.yml @@ -0,0 +1,23 @@ +title: How to train a Deep Q Network +author: PL team +created: 2021-01-31 +updated: 2021-12-03 +license: CC BY-SA +build: 2 +tags: + - RL +description: | + Main takeaways: + + 1. RL has the same flow as previous models we have seen, with a few additions + 2. Handle unsupervised learning by using an IterableDataset where the dataset itself is constantly updated during training + 3. Each training step carries has the agent taking an action in the environment and storing the experience in the IterableDataset +requirements: + - gym + - pygame + - pandas + - seaborn + - lightning>=2.0.0rc0 +accelerator: + - CPU + - GPU diff --git a/_notebooks/lightning_examples/reinforce-learning-DQN/dqn.py b/_notebooks/lightning_examples/reinforce-learning-DQN/dqn.py new file mode 100644 index 0000000..357f203 --- /dev/null +++ b/_notebooks/lightning_examples/reinforce-learning-DQN/dqn.py @@ -0,0 +1,382 @@ +# %% +import os +from collections import OrderedDict, deque, namedtuple +from typing import Iterator, List, Tuple + +import gym +import numpy as np +import pandas as pd +import seaborn as sn +import torch +from IPython.core.display import display +from pytorch_lightning import LightningModule, Trainer +from pytorch_lightning.loggers import CSVLogger +from torch import Tensor, nn +from torch.optim import Adam, Optimizer +from torch.utils.data import DataLoader +from torch.utils.data.dataset import IterableDataset + +PATH_DATASETS = os.environ.get("PATH_DATASETS", ".") + + +# %% +class DQN(nn.Module): + """Simple MLP network.""" + + def __init__(self, obs_size: int, n_actions: int, hidden_size: int = 128): + """ + Args: + obs_size: observation/state size of the environment + n_actions: number of discrete actions available in the environment + hidden_size: size of hidden layers + """ + super().__init__() + self.net = nn.Sequential( + nn.Linear(obs_size, hidden_size), + nn.ReLU(), + nn.Linear(hidden_size, n_actions), + ) + + def forward(self, x): + return self.net(x.float()) + + +# %% [markdown] +# ### Memory + +# %% + +# Named tuple for storing experience steps gathered in training +Experience = namedtuple( + "Experience", + field_names=["state", "action", "reward", "done", "new_state"], +) + + +# %% +class ReplayBuffer: + """Replay Buffer for storing past experiences allowing the agent to learn from them. + + Args: + capacity: size of the buffer + """ + + def __init__(self, capacity: int) -> None: + self.buffer = deque(maxlen=capacity) + + def __len__(self) -> None: + return len(self.buffer) + + def append(self, experience: Experience) -> None: + """Add experience to the buffer. + + Args: + experience: tuple (state, action, reward, done, new_state) + """ + self.buffer.append(experience) + + def sample(self, batch_size: int) -> Tuple: + indices = np.random.choice(len(self.buffer), batch_size, replace=False) + states, actions, rewards, dones, next_states = zip(*(self.buffer[idx] for idx in indices)) + + return ( + np.array(states), + np.array(actions), + np.array(rewards, dtype=np.float32), + np.array(dones, dtype=bool), + np.array(next_states), + ) + + +# %% +class RLDataset(IterableDataset): + """Iterable Dataset containing the ExperienceBuffer which will be updated with new experiences during training. + + Args: + buffer: replay buffer + sample_size: number of experiences to sample at a time + """ + + def __init__(self, buffer: ReplayBuffer, sample_size: int = 200) -> None: + self.buffer = buffer + self.sample_size = sample_size + + def __iter__(self) -> Iterator[Tuple]: + states, actions, rewards, dones, new_states = self.buffer.sample(self.sample_size) + for i in range(len(dones)): + yield states[i], actions[i], rewards[i], dones[i], new_states[i] + + +# %% [markdown] +# ### Agent + + +# %% +class Agent: + """Base Agent class handeling the interaction with the environment.""" + + def __init__(self, env: gym.Env, replay_buffer: ReplayBuffer) -> None: + """ + Args: + env: training environment + replay_buffer: replay buffer storing experiences + """ + self.env = env + self.replay_buffer = replay_buffer + self.reset() + self.state = self.env.reset() + + def reset(self) -> None: + """Resents the environment and updates the state.""" + self.state = self.env.reset() + + def get_action(self, net: nn.Module, epsilon: float, device: str) -> int: + """Using the given network, decide what action to carry out using an epsilon-greedy policy. + + Args: + net: DQN network + epsilon: value to determine likelihood of taking a random action + device: current device + + Returns: + action + """ + if np.random.random() < epsilon: + action = self.env.action_space.sample() + else: + state = torch.tensor([self.state]) + + if device not in ["cpu"]: + state = state.cuda(device) + + q_values = net(state) + _, action = torch.max(q_values, dim=1) + action = int(action.item()) + + return action + + @torch.no_grad() + def play_step( + self, + net: nn.Module, + epsilon: float = 0.0, + device: str = "cpu", + ) -> Tuple[float, bool]: + """Carries out a single interaction step between the agent and the environment. + + Args: + net: DQN network + epsilon: value to determine likelihood of taking a random action + device: current device + + Returns: + reward, done + """ + + action = self.get_action(net, epsilon, device) + + # do step in the environment + new_state, reward, done, _ = self.env.step(action) + + exp = Experience(self.state, action, reward, done, new_state) + + self.replay_buffer.append(exp) + + self.state = new_state + if done: + self.reset() + return reward, done + + +# %% [markdown] +# ### DQN Lightning Module + + +# %% +class DQNLightning(LightningModule): + """Basic DQN Model.""" + + def __init__( + self, + batch_size: int = 16, + lr: float = 1e-2, + env: str = "CartPole-v0", + gamma: float = 0.99, + sync_rate: int = 10, + replay_size: int = 1000, + warm_start_size: int = 1000, + eps_last_frame: int = 1000, + eps_start: float = 1.0, + eps_end: float = 0.01, + episode_length: int = 200, + warm_start_steps: int = 1000, + ) -> None: + """ + Args: + batch_size: size of the batches") + lr: learning rate + env: gym environment tag + gamma: discount factor + sync_rate: how many frames do we update the target network + replay_size: capacity of the replay buffer + warm_start_size: how many samples do we use to fill our buffer at the start of training + eps_last_frame: what frame should epsilon stop decaying + eps_start: starting value of epsilon + eps_end: final value of epsilon + episode_length: max length of an episode + warm_start_steps: max episode reward in the environment + """ + super().__init__() + self.save_hyperparameters() + + self.env = gym.make(self.hparams.env) + obs_size = self.env.observation_space.shape[0] + n_actions = self.env.action_space.n + + self.net = DQN(obs_size, n_actions) + self.target_net = DQN(obs_size, n_actions) + + self.buffer = ReplayBuffer(self.hparams.replay_size) + self.agent = Agent(self.env, self.buffer) + self.total_reward = 0 + self.episode_reward = 0 + self.populate(self.hparams.warm_start_steps) + + def populate(self, steps: int = 1000) -> None: + """Carries out several random steps through the environment to initially fill up the replay buffer with + experiences. + + Args: + steps: number of random steps to populate the buffer with + """ + for _ in range(steps): + self.agent.play_step(self.net, epsilon=1.0) + + def forward(self, x: Tensor) -> Tensor: + """Passes in a state x through the network and gets the q_values of each action as an output. + + Args: + x: environment state + + Returns: + q values + """ + output = self.net(x) + return output + + def dqn_mse_loss(self, batch: Tuple[Tensor, Tensor]) -> Tensor: + """Calculates the mse loss using a mini batch from the replay buffer. + + Args: + batch: current mini batch of replay data + + Returns: + loss + """ + states, actions, rewards, dones, next_states = batch + + state_action_values = self.net(states).gather(1, actions.long().unsqueeze(-1)).squeeze(-1) + + with torch.no_grad(): + next_state_values = self.target_net(next_states).max(1)[0] + next_state_values[dones] = 0.0 + next_state_values = next_state_values.detach() + + expected_state_action_values = next_state_values * self.hparams.gamma + rewards + + return nn.MSELoss()(state_action_values, expected_state_action_values) + + def get_epsilon(self, start: int, end: int, frames: int) -> float: + if self.global_step > frames: + return end + return start - (self.global_step / frames) * (start - end) + + def training_step(self, batch: Tuple[Tensor, Tensor], nb_batch) -> OrderedDict: + """Carries out a single step through the environment to update the replay buffer. Then calculates loss + based on the minibatch recieved. + + Args: + batch: current mini batch of replay data + nb_batch: batch number + + Returns: + Training loss and log metrics + """ + device = self.get_device(batch) + epsilon = self.get_epsilon(self.hparams.eps_start, self.hparams.eps_end, self.hparams.eps_last_frame) + self.log("epsilon", epsilon) + + # step through environment with agent + reward, done = self.agent.play_step(self.net, epsilon, device) + self.episode_reward += reward + self.log("episode reward", self.episode_reward) + + # calculates training loss + loss = self.dqn_mse_loss(batch) + + if done: + self.total_reward = self.episode_reward + self.episode_reward = 0 + + # Soft update of target network + if self.global_step % self.hparams.sync_rate == 0: + self.target_net.load_state_dict(self.net.state_dict()) + + self.log_dict( + { + "reward": reward, + "train_loss": loss, + } + ) + self.log("total_reward", self.total_reward, prog_bar=True) + self.log("steps", self.global_step, logger=False, prog_bar=True) + + return loss + + def configure_optimizers(self) -> List[Optimizer]: + """Initialize Adam optimizer.""" + optimizer = Adam(self.net.parameters(), lr=self.hparams.lr) + return optimizer + + def __dataloader(self) -> DataLoader: + """Initialize the Replay Buffer dataset used for retrieving experiences.""" + dataset = RLDataset(self.buffer, self.hparams.episode_length) + dataloader = DataLoader( + dataset=dataset, + batch_size=self.hparams.batch_size, + ) + return dataloader + + def train_dataloader(self) -> DataLoader: + """Get train loader.""" + return self.__dataloader() + + def get_device(self, batch) -> str: + """Retrieve device currently being used by minibatch.""" + return batch[0].device.index if self.on_gpu else "cpu" + + +# %% [markdown] +# ### Trainer + +# %% + +model = DQNLightning() + +trainer = Trainer( + accelerator="auto", + devices=1 if torch.cuda.is_available() else None, # limiting got iPython runs + max_epochs=150, + val_check_interval=50, + logger=CSVLogger(save_dir="logs/"), +) + +trainer.fit(model) + +# %% + +metrics = pd.read_csv(f"{trainer.logger.log_dir}/metrics.csv") +del metrics["step"] +metrics.set_index("epoch", inplace=True) +display(metrics.dropna(axis=1, how="all").head()) +sn.relplot(data=metrics, kind="line") diff --git a/_notebooks/lightning_examples/text-transformers/.meta.yml b/_notebooks/lightning_examples/text-transformers/.meta.yml new file mode 100644 index 0000000..f34c5fe --- /dev/null +++ b/_notebooks/lightning_examples/text-transformers/.meta.yml @@ -0,0 +1,21 @@ +title: Finetune Transformers Models with PyTorch Lightning +author: PL team +created: 2021-01-31 +updated: 2022-02-08 +license: CC BY-SA +build: 1 +tags: + - Text +description: | + This notebook will use HuggingFace's `datasets` library to get data, which will be wrapped in a `LightningDataModule`. + Then, we write a class to perform text classification on any dataset from the [GLUE Benchmark](https://gluebenchmark.com/). + (We just show CoLA and MRPC due to constraint on compute/disk) +requirements: + - transformers + - datasets + - scipy + - scikit-learn + - torchtext>=0.9 + - lightning>=2.0.0rc0 +accelerator: + - GPU diff --git a/_notebooks/lightning_examples/text-transformers/text-transformers.py b/_notebooks/lightning_examples/text-transformers/text-transformers.py new file mode 100644 index 0000000..cc04b7f --- /dev/null +++ b/_notebooks/lightning_examples/text-transformers/text-transformers.py @@ -0,0 +1,322 @@ +# %% +from datetime import datetime +from typing import Optional + +import datasets +import torch +from pytorch_lightning import LightningDataModule, LightningModule, Trainer, seed_everything +from torch.utils.data import DataLoader +from transformers import ( + AdamW, + AutoConfig, + AutoModelForSequenceClassification, + AutoTokenizer, + get_linear_schedule_with_warmup, +) + +# %% [markdown] +# ## Training BERT with Lightning + +# %% [markdown] +# ### Lightning DataModule for GLUE + + +# %% +class GLUEDataModule(LightningDataModule): + task_text_field_map = { + "cola": ["sentence"], + "sst2": ["sentence"], + "mrpc": ["sentence1", "sentence2"], + "qqp": ["question1", "question2"], + "stsb": ["sentence1", "sentence2"], + "mnli": ["premise", "hypothesis"], + "qnli": ["question", "sentence"], + "rte": ["sentence1", "sentence2"], + "wnli": ["sentence1", "sentence2"], + "ax": ["premise", "hypothesis"], + } + + glue_task_num_labels = { + "cola": 2, + "sst2": 2, + "mrpc": 2, + "qqp": 2, + "stsb": 1, + "mnli": 3, + "qnli": 2, + "rte": 2, + "wnli": 2, + "ax": 3, + } + + loader_columns = [ + "datasets_idx", + "input_ids", + "token_type_ids", + "attention_mask", + "start_positions", + "end_positions", + "labels", + ] + + def __init__( + self, + model_name_or_path: str, + task_name: str = "mrpc", + max_seq_length: int = 128, + train_batch_size: int = 32, + eval_batch_size: int = 32, + **kwargs, + ): + super().__init__() + self.model_name_or_path = model_name_or_path + self.task_name = task_name + self.max_seq_length = max_seq_length + self.train_batch_size = train_batch_size + self.eval_batch_size = eval_batch_size + + self.text_fields = self.task_text_field_map[task_name] + self.num_labels = self.glue_task_num_labels[task_name] + self.tokenizer = AutoTokenizer.from_pretrained(self.model_name_or_path, use_fast=True) + + def setup(self, stage: str): + self.dataset = datasets.load_dataset("glue", self.task_name) + + for split in self.dataset.keys(): + self.dataset[split] = self.dataset[split].map( + self.convert_to_features, + batched=True, + remove_columns=["label"], + ) + self.columns = [c for c in self.dataset[split].column_names if c in self.loader_columns] + self.dataset[split].set_format(type="torch", columns=self.columns) + + self.eval_splits = [x for x in self.dataset.keys() if "validation" in x] + + def prepare_data(self): + datasets.load_dataset("glue", self.task_name) + AutoTokenizer.from_pretrained(self.model_name_or_path, use_fast=True) + + def train_dataloader(self): + return DataLoader(self.dataset["train"], batch_size=self.train_batch_size, shuffle=True) + + def val_dataloader(self): + if len(self.eval_splits) == 1: + return DataLoader(self.dataset["validation"], batch_size=self.eval_batch_size) + elif len(self.eval_splits) > 1: + return [DataLoader(self.dataset[x], batch_size=self.eval_batch_size) for x in self.eval_splits] + + def test_dataloader(self): + if len(self.eval_splits) == 1: + return DataLoader(self.dataset["test"], batch_size=self.eval_batch_size) + elif len(self.eval_splits) > 1: + return [DataLoader(self.dataset[x], batch_size=self.eval_batch_size) for x in self.eval_splits] + + def convert_to_features(self, example_batch, indices=None): + # Either encode single sentence or sentence pairs + if len(self.text_fields) > 1: + texts_or_text_pairs = list(zip(example_batch[self.text_fields[0]], example_batch[self.text_fields[1]])) + else: + texts_or_text_pairs = example_batch[self.text_fields[0]] + + # Tokenize the text/text pairs + features = self.tokenizer.batch_encode_plus( + texts_or_text_pairs, max_length=self.max_seq_length, pad_to_max_length=True, truncation=True + ) + + # Rename label to labels to make it easier to pass to model forward + features["labels"] = example_batch["label"] + + return features + + +# %% [markdown] +# **You could use this datamodule with standalone PyTorch if you wanted...** + +# %% +dm = GLUEDataModule("distilbert-base-uncased") +dm.prepare_data() +dm.setup("fit") +next(iter(dm.train_dataloader())) + +# %% [markdown] +# ### Transformer LightningModule + + +# %% +class GLUETransformer(LightningModule): + def __init__( + self, + model_name_or_path: str, + num_labels: int, + task_name: str, + learning_rate: float = 2e-5, + adam_epsilon: float = 1e-8, + warmup_steps: int = 0, + weight_decay: float = 0.0, + train_batch_size: int = 32, + eval_batch_size: int = 32, + eval_splits: Optional[list] = None, + **kwargs, + ): + super().__init__() + + self.save_hyperparameters() + + self.config = AutoConfig.from_pretrained(model_name_or_path, num_labels=num_labels) + self.model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, config=self.config) + self.metric = datasets.load_metric( + "glue", self.hparams.task_name, experiment_id=datetime.now().strftime("%d-%m-%Y_%H-%M-%S") + ) + + def forward(self, **inputs): + return self.model(**inputs) + + def training_step(self, batch, batch_idx): + outputs = self(**batch) + loss = outputs[0] + return loss + + def validation_step(self, batch, batch_idx, dataloader_idx=0): + outputs = self(**batch) + val_loss, logits = outputs[:2] + + if self.hparams.num_labels > 1: + preds = torch.argmax(logits, axis=1) + elif self.hparams.num_labels == 1: + preds = logits.squeeze() + + labels = batch["labels"] + + return {"loss": val_loss, "preds": preds, "labels": labels} + + def validation_epoch_end(self, outputs): + if self.hparams.task_name == "mnli": + for i, output in enumerate(outputs): + # matched or mismatched + split = self.hparams.eval_splits[i].split("_")[-1] + preds = torch.cat([x["preds"] for x in output]).detach().cpu().numpy() + labels = torch.cat([x["labels"] for x in output]).detach().cpu().numpy() + loss = torch.stack([x["loss"] for x in output]).mean() + self.log(f"val_loss_{split}", loss, prog_bar=True) + split_metrics = { + f"{k}_{split}": v for k, v in self.metric.compute(predictions=preds, references=labels).items() + } + self.log_dict(split_metrics, prog_bar=True) + return loss + + preds = torch.cat([x["preds"] for x in outputs]).detach().cpu().numpy() + labels = torch.cat([x["labels"] for x in outputs]).detach().cpu().numpy() + loss = torch.stack([x["loss"] for x in outputs]).mean() + self.log("val_loss", loss, prog_bar=True) + self.log_dict(self.metric.compute(predictions=preds, references=labels), prog_bar=True) + + def configure_optimizers(self): + """Prepare optimizer and schedule (linear warmup and decay)""" + model = self.model + no_decay = ["bias", "LayerNorm.weight"] + optimizer_grouped_parameters = [ + { + "params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)], + "weight_decay": self.hparams.weight_decay, + }, + { + "params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)], + "weight_decay": 0.0, + }, + ] + optimizer = AdamW(optimizer_grouped_parameters, lr=self.hparams.learning_rate, eps=self.hparams.adam_epsilon) + + scheduler = get_linear_schedule_with_warmup( + optimizer, + num_warmup_steps=self.hparams.warmup_steps, + num_training_steps=self.trainer.estimated_stepping_batches, + ) + scheduler = {"scheduler": scheduler, "interval": "step", "frequency": 1} + return [optimizer], [scheduler] + + +# %% [markdown] +# ## Training + +# %% [markdown] +# ### CoLA +# +# See an interactive view of the +# CoLA dataset in [NLP Viewer](https://huggingface.co/nlp/viewer/?dataset=glue&config=cola) + +# %% +seed_everything(42) + +dm = GLUEDataModule(model_name_or_path="albert-base-v2", task_name="cola") +dm.setup("fit") +model = GLUETransformer( + model_name_or_path="albert-base-v2", + num_labels=dm.num_labels, + eval_splits=dm.eval_splits, + task_name=dm.task_name, +) + +trainer = Trainer( + max_epochs=1, + accelerator="auto", + devices=1 if torch.cuda.is_available() else None, # limiting got iPython runs +) +trainer.fit(model, datamodule=dm) + +# %% [markdown] +# ### MRPC +# +# See an interactive view of the +# MRPC dataset in [NLP Viewer](https://huggingface.co/nlp/viewer/?dataset=glue&config=mrpc) + +# %% +seed_everything(42) + +dm = GLUEDataModule( + model_name_or_path="distilbert-base-cased", + task_name="mrpc", +) +dm.setup("fit") +model = GLUETransformer( + model_name_or_path="distilbert-base-cased", + num_labels=dm.num_labels, + eval_splits=dm.eval_splits, + task_name=dm.task_name, +) + +trainer = Trainer( + max_epochs=3, + accelerator="auto", + devices=1 if torch.cuda.is_available() else None, # limiting got iPython runs +) +trainer.fit(model, datamodule=dm) + +# %% [markdown] +# ### MNLI +# +# - The MNLI dataset is huge, so we aren't going to bother trying to train on it here. +# - We will skip over training and go straight to validation. +# +# See an interactive view of the +# MRPC dataset in [NLP Viewer](https://huggingface.co/nlp/viewer/?dataset=glue&config=mnli) + +# %% +dm = GLUEDataModule( + model_name_or_path="distilbert-base-cased", + task_name="mnli", +) +dm.setup("fit") +model = GLUETransformer( + model_name_or_path="distilbert-base-cased", + num_labels=dm.num_labels, + eval_splits=dm.eval_splits, + task_name=dm.task_name, +) + +trainer = Trainer( + max_epochs=3, + accelerator="auto", + devices=1 if torch.cuda.is_available() else None, # limiting got iPython runs +) +trainer.validate(model, dm) diff --git a/_notebooks/lightning_examples/warp-drive/.meta.yml b/_notebooks/lightning_examples/warp-drive/.meta.yml new file mode 100644 index 0000000..adf93f0 --- /dev/null +++ b/_notebooks/lightning_examples/warp-drive/.meta.yml @@ -0,0 +1,30 @@ +title: Multi-agent Reinforcement Learning With WarpDrive +author: Sunil Srinivasa (sunil.srinivasa@salesforce.com), Tian Lan (tian.lan@salesforce.com), Huan Wang (huan.wang@salesforce.com) and Stephan Zheng(stephan.zheng@salesforce.com) +created: 2022-03-01 +license: BSD 3-Clause "New" or "Revised" License +tags: + - Reinforcement Learning + - Multi-agent + - GPU +description: This notebook introduces multi-agent reinforcement learning (MARL) with WarpDrive (Lan et al. https://arxiv.org/abs/2108.13976). + WarpDrive is a flexible, lightweight, and easy-to-use open-source framework that implements end-to-end deep MARL on GPUs. + WarpDrive enables orders-of-magnitude speedups compared to CPU-GPU implementations, using the parallelization capability + of GPUs and several design choices to minimize communication overhead. WarpDrive also prioritizes user-friendliness - + it has utility functions to easily build MARL environments in CUDA and quality-of-life tools to run end-to-end MARL + using just a few lines of code, and is compatible with PyTorch. + + WarpDrive includes the following resources. + code - https://github.com/salesforce/warp-drive + documentation - http://opensource.salesforce.com/warp-drive/, and + white paper - https://arxiv.org/abs/2108.13976. + +requirements: + - rl-warp-drive==2.1 + - ffmpeg-python + # todo: after merging #155 we will relax this just to `torch<=1.10` and drop TV, TT, etc. + - torch==1.10.* + - torchvision==0.11.* + - torchtext==0.11.* + - lightning>=2.0.0rc0 +accelerator: + - GPU diff --git a/_notebooks/lightning_examples/warp-drive/multi_agent_rl.py b/_notebooks/lightning_examples/warp-drive/multi_agent_rl.py new file mode 100644 index 0000000..33efbe2 --- /dev/null +++ b/_notebooks/lightning_examples/warp-drive/multi_agent_rl.py @@ -0,0 +1,261 @@ +# %% [markdown] +# **⚠️ PLEASE NOTE:** +# This notebook runs on a GPU runtime. If running on Colab, choose Runtime > Change runtime type from the menu, then select `GPU` in the 'Hardware accelerator' dropdown menu. + +# %% [markdown] +# ## Introduction + +# %% [markdown] +# This tutorial provides a demonstration of a multi-agent Reinforcement Learning (RL) training loop with [WarpDrive](https://github.com/salesforce/warp-drive). WarpDrive is a flexible, lightweight, and easy-to-use RL framework that implements end-to-end deep multi-agent RL on a GPU (Graphics Processing Unit). Using the extreme parallelization capability of GPUs, it enables [orders-of-magnitude faster RL](https://arxiv.org/abs/2108.13976) compared to common implementations that blend CPU simulations and GPU models. WarpDrive is extremely efficient as it runs simulations across multiple agents and multiple environment replicas all in parallel and completely eliminates the back-and-forth data copying between the CPU and the GPU during every step. As such, WarpDrive +# - Can simulate 1000s of agents in each environment and thousands of environments in parallel, harnessing the extreme parallelism capability of GPUs. +# - Eliminates communication between CPU and GPU, and also within the GPU, as read and write operations occur in-place. +# - Is fully compatible with PyTorch, a highly flexible and very fast deep learning framework. +# - Implements parallel action sampling on CUDA C, which is ~3x faster than using PyTorch’s sampling methods. +# - Allows for large-scale distributed training on multiple GPUs. +# +# Below is an overview of WarpDrive’s layout of computational and data structures on a single GPU. +# ![](https://blog.salesforceairesearch.com/content/images/2021/08/warpdrive_framework_overview.png) +# Computations are organized into blocks, with multiple threads in each block. Each block runs a simulation environment and each thread +# simulates an agent in an environment. Blocks can access the shared GPU memory that stores simulation data and neural network policy models. A DataManager and FunctionManager enable defining multi-agent RL GPU-workflows with Python APIs. For more details, please read out white [paper](https://arxiv.org/abs/2108.13976). +# +# The Warpdrive framework comprises several utility functions that help easily implement any (OpenAI-)*gym-style* RL environment, and furthermore, provides quality-of-life tools to train it end-to-end using just a few lines of code. You may familiarize yourself with WarpDrive with the help of these [tutorials](https://github.com/salesforce/warp-drive/tree/master/tutorials). +# +# We invite everyone to **contribute to WarpDrive**, including adding new multi-agent environments, proposing new features and reporting issues on our open source [repository](https://github.com/salesforce/warp-drive). +# +# We have integrated WarpDrive with the [PyTorch Lightning](https://www.lightning.ai/) framework, which greatly reduces the trainer boilerplate code, and improves training modularity and flexibility. It abstracts away most of the engineering pieces of code, so users can focus on research and building models, and iterate on experiments really fast. PyTorch Lightning also provides support for easily running the model on any hardware, performing distributed training, model checkpointing, performance profiling, logging and visualization. +# +# Below, we demonstrate how to use WarpDrive and PyTorch Lightning together to train a game of [Tag](https://github.com/salesforce/warp-drive/blob/master/example_envs/tag_continuous/tag_continuous.py) where multiple *tagger* agents are trying to run after and tag multiple other *runner* agents. Here's a sample depiction of the game of Tag with $100$ runners and $5$ taggers. +# ![](https://blog.salesforceairesearch.com/content/images/2021/08/same_speed_50fps-1.gif) + +# %% [markdown] +# ## Dependencies + +# %% +import logging + +import torch +from example_envs.tag_continuous.tag_continuous import TagContinuous +from pytorch_lightning import Trainer +from warp_drive.env_wrapper import EnvWrapper +from warp_drive.training.pytorch_lightning import CUDACallback, PerfStatsCallback, WarpDriveModule + +# Uncomment below for enabling animation visualizations. +# from example_envs.utils.generate_rollout_animation import generate_tag_env_rollout_animation +# from IPython.display import HTML + + +# %% +assert torch.cuda.device_count() > 0, "This notebook only runs on a GPU!" + +# %% +# Set logger level e.g., DEBUG, INFO, WARNING, ERROR. +logging.getLogger().setLevel(logging.ERROR) + +# %% [markdown] +# ## Specify a set of run configurations for your experiments +# +# The run configuration is a dictionary comprising the environment parameters, the trainer and the policy network settings, as well as configurations for saving. +# +# For our experiment, we consider an environment wherein $5$ taggers and $100$ runners play the game of [Tag](https://github.com/salesforce/warp-drive/blob/master/example_envs/tag_continuous/tag_continuous.py) on a $20 \times 20$ plane. The game lasts $200$ timesteps. Each agent chooses it's own acceleration and turn actions at every timestep, and we use mechanics to determine how the agents move over the grid. When a tagger gets close to a runner, the runner is tagged, and is eliminated from the game. For the configuration below, the runners and taggers have the same unit skill levels, or top speeds. +# +# We train the agents using $50$ environments or simulations running in parallel. With WarpDrive, each simulation runs on separate GPU blocks. +# +# There are two separate policy networks used for the tagger and runner agents. Each network is a fully-connected model with two layers each of $256$ dimensions. We use the Advantage Actor Critic (A2C) algorithm for training. WarpDrive also currently provides the option to use the Proximal Policy Optimization (PPO) algorithm instead. + +# %% +run_config = dict( + name="tag_continuous", + # Environment settings. + env=dict( + # number of taggers in the environment + num_taggers=5, + # number of runners in the environment + num_runners=100, + # length of the (square) grid on which the game is played + grid_length=20.0, + # episode length in timesteps + episode_length=200, + # maximum acceleration + max_acceleration=0.1, + # minimum acceleration + min_acceleration=-0.1, + # maximum turn (in radians) + max_turn=2.35, # 3pi/4 radians + # minimum turn (in radians) + min_turn=-2.35, # -3pi/4 radians + # number of discretized accelerate actions + num_acceleration_levels=10, + # number of discretized turn actions + num_turn_levels=10, + # skill level for the tagger + skill_level_tagger=1.0, + # skill level for the runner + skill_level_runner=1.0, + # each agent sees the full (or partial) information of the world + use_full_observation=False, + # flag to indicate if a runner stays in the game after getting tagged + runner_exits_game_after_tagged=True, + # number of other agents each agent can see + # used in the case use_full_observation is False + num_other_agents_observed=10, + # positive reward for a tagger upon tagging a runner + tag_reward_for_tagger=10.0, + # negative reward for a runner upon getting tagged + tag_penalty_for_runner=-10.0, + # reward at the end of the game for a runner that isn't tagged + end_of_game_reward_for_runner=1.0, + # distance margin between a tagger and runner + # to consider the runner as being 'tagged' + tagging_distance=0.02, + ), + # Trainer settings. + trainer=dict( + # number of environment replicas (number of GPU blocks used) + num_envs=50, + # total batch size used for training per iteration (across all the environments) + train_batch_size=10000, + # total number of episodes to run the training for + # This can be set arbitrarily high! + num_episodes=500, + ), + # Policy network settings. + policy=dict( + runner=dict( + # flag indicating whether the model needs to be trained + to_train=True, + # algorithm used to train the policy + algorithm="A2C", + # discount rate + gamma=0.98, + # learning rate + lr=0.005, + # policy model settings + model=dict(type="fully_connected", fc_dims=[256, 256], model_ckpt_filepath=""), + ), + tagger=dict( + to_train=True, + algorithm="A2C", + gamma=0.98, + lr=0.002, + model=dict(type="fully_connected", fc_dims=[256, 256], model_ckpt_filepath=""), + ), + ), + # Checkpoint saving setting. + saving=dict( + # how often (in iterations) to print the metrics + metrics_log_freq=10, + # how often (in iterations) to save the model parameters + model_params_save_freq=5000, + # base folder used for saving + basedir="/tmp", + # experiment name + name="continuous_tag", + # experiment tag + tag="example", + ), +) + +# %% [markdown] +# ## Instantiate the WarpDrive Module +# +# In order to instantiate the WarpDrive module, we first use an environment wrapper to specify that the environment needs to be run on the GPU (via the `use_cuda` flag). Also, agents in the environment can share policy models; so we specify a dictionary to map each policy network model to the list of agent ids using that model. + +# %% +# Create a wrapped environment object via the EnvWrapper +# Ensure that env_backend is set to be "pycuda" or "numba"(in order to run on the GPU) +# WarpDrive v2 supports JIT compiled Numba backend now! +env_wrapper = EnvWrapper( + TagContinuous(**run_config["env"]), + num_envs=run_config["trainer"]["num_envs"], + env_backend="pycuda", +) + +# Agents can share policy models: this dictionary maps policy model names to agent ids. +policy_tag_to_agent_id_map = { + "tagger": list(env_wrapper.env.taggers), + "runner": list(env_wrapper.env.runners), +} + +wd_module = WarpDriveModule( + env_wrapper=env_wrapper, + config=run_config, + policy_tag_to_agent_id_map=policy_tag_to_agent_id_map, + verbose=True, +) + + +# %% [markdown] +# ## Visualizing an episode roll-out before training +# +# We have created a helper function (see below) to visualize an episode rollout. Internally, this function uses the WarpDrive module's `fetch_episode_states` API to fetch the data arrays on the GPU for the duration of an entire episode. Specifically, we fetch the state arrays pertaining to agents' x and y locations on the plane and indicators on which agents are still active in the game. Note that this function may be invoked at any time during training, and it will use the state of the policy models at that time to sample actions and generate the visualization. + +# %% [markdown] +# The animation below shows a sample realization of the game episode before training, i.e., with randomly chosen agent actions. The $5$ taggers are marked in pink, while the $100$ blue agents are the runners. Both the taggers and runners move around randomly and about half the runners remain at the end of the episode. + +# %% +# Uncomment below for enabling animation visualizations. +# anim = generate_tag_env_rollout_animation(wd_module, fps=25) +# HTML(anim.to_html5_video()) + +# %% [markdown] +# ## Create the Lightning Trainer +# +# Next, we create the trainer for training the WarpDrive model. We add the `performance stats` callbacks to the trainer to view the throughput performance of WarpDrive. + +# %% +log_freq = run_config["saving"]["metrics_log_freq"] + +# Define callbacks. +cuda_callback = CUDACallback(module=wd_module) +perf_stats_callback = PerfStatsCallback( + batch_size=wd_module.training_batch_size, + num_iters=wd_module.num_iters, + log_freq=log_freq, +) + +# Instantiate the PyTorch Lightning trainer with the callbacks. +# Also, set the number of gpus to 1, since this notebook uses just a single GPU. +num_gpus = 1 +num_episodes = run_config["trainer"]["num_episodes"] +episode_length = run_config["env"]["episode_length"] +training_batch_size = run_config["trainer"]["train_batch_size"] +num_epochs = int(num_episodes * episode_length / training_batch_size) + +trainer = Trainer( + accelerator="gpu", + devices=num_gpus, + callbacks=[cuda_callback, perf_stats_callback], + max_epochs=num_epochs, + log_every_n_steps=1, + reload_dataloaders_every_n_epochs=1, +) + +# %% +# Start tensorboard. +# %load_ext tensorboard +# %tensorboard --logdir lightning_logs/ + +# %% [markdown] +# ## Train the WarpDrive Module +# +# Finally, we invoke training. +# +# Note: please scroll up to the tensorboard cell to visualize the curves during training. + +# %% +trainer.fit(wd_module) + +# %% [markdown] +# ## Visualize an episode-rollout after training + +# %% +# Uncomment below for enabling animation visualizations. +# anim = generate_tag_env_rollout_animation(wd_module, fps=25) +# HTML(anim.to_html5_video()) + +# %% [markdown] +# Note: In the configuration above, we have set the trainer to only train on $500$ rollout episodes, but you can increase the `num_episodes` configuration parameter to train further. As more training happens, the runners learn to escape the taggers, and the taggers learn to chase after the runner. Sometimes, the taggers also collaborate to team-tag runners. A good number of episodes to train on (for the configuration we have used) is $2$M or higher. + +# %% +# Finally, close the WarpDrive module to clear up the CUDA memory heap +wd_module.graceful_close() diff --git a/_notebooks/pyproject.toml b/_notebooks/pyproject.toml new file mode 100644 index 0000000..8e49741 --- /dev/null +++ b/_notebooks/pyproject.toml @@ -0,0 +1,12 @@ +[tool.black] +# https://github.com/psf/black +line-length = 120 +exclude = "(.eggs|.git|.hg|.venv|_build|buck-out|build)" + +[tool.isort] +skip_glob = [] +profile = "black" +line_length = 120 + +[tool.autopep8] +ignore = ["E731"] diff --git a/_notebooks/requirements.txt b/_notebooks/requirements.txt new file mode 100644 index 0000000..9624fab --- /dev/null +++ b/_notebooks/requirements.txt @@ -0,0 +1,5 @@ +-r _requirements/devel.txt +-r .actions/requires.txt + +# default for all examples +-r _requirements/default.txt diff --git a/_notebooks/setup.cfg b/_notebooks/setup.cfg new file mode 100644 index 0000000..a7ed4be --- /dev/null +++ b/_notebooks/setup.cfg @@ -0,0 +1,49 @@ +[tool:pytest] +norecursedirs = + .git + .github + dist + build +addopts = + --strict + --doctest-modules + --color=yes + +[coverage:report] +exclude_lines = + pragma: no-cover + pass + + +[flake8] +max-line-length = 120 +exclude = + *.egg + build + temp +select = E,W,F +doctests = True +verbose = 2 +# https://pep8.readthedocs.io/en/latest/intro.html#error-codes +format = pylint +# see: https://www.flake8rules.com/ +ignore = + # line too long + E501 + # whitespace before ':' + E203 + + +# setup.cfg or tox.ini +[check-manifest] +ignore = + *.yml + .github + .github/* + + +[metadata] +license_file = LICENSE +description-file = README.md +# long_description = file:README.md +# long_description_content_type = text/markdown diff --git a/_notebooks/templates/img-classify/.meta.yml b/_notebooks/templates/img-classify/.meta.yml new file mode 100644 index 0000000..b26049d --- /dev/null +++ b/_notebooks/templates/img-classify/.meta.yml @@ -0,0 +1,21 @@ +title: Simple image classification with Lightning Flash +author: PL team +created: 2022-04-14 +updated: 2021-06-16 +license: CC BY-SA +build: 2 +tags: + - Image +description: | + This is a template to show simple image classification case if for some reason accelerator is required. +requirements: + - lightning-flash[image]>=0.7 + - numpy<1.24 + - pandas>=1.0 + - matplotlib>=3.0 + - seaborn +accelerator: + - GPU +datasets: + web: + - https://pl-flash-data.s3.amazonaws.com/hymenoptera_data.zip diff --git a/_notebooks/templates/img-classify/classify.py b/_notebooks/templates/img-classify/classify.py new file mode 100644 index 0000000..ba0273b --- /dev/null +++ b/_notebooks/templates/img-classify/classify.py @@ -0,0 +1,70 @@ +# %% +import os + +import flash +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sn +from flash.image import ImageClassificationData, ImageClassifier +from IPython.core.display import display +from pytorch_lightning.loggers import CSVLogger + +PATH_DATASETS = os.environ.get("PATH_DATASETS", ".") +# this dataset is automatically downloaded and extracted based on meta link +# this archive includes the one more level - folder with the same name +DATA_HYMENOPLERA = os.path.join(PATH_DATASETS, "hymenoptera_data", "hymenoptera_data") + +# %% [markdown] +# ## 1. Create the DataModule + +# %% +datamodule = ImageClassificationData.from_folders( + train_folder=f"{DATA_HYMENOPLERA}/train/", + val_folder=f"{DATA_HYMENOPLERA}/val/", + batch_size=1024, +) + +# %% [markdown] +# ## 2. Build the task + +# %% +model = ImageClassifier(backbone="resnet18", labels=datamodule.labels) + +# %% [markdown] +# ## 3. Create the trainer and finetune the model + +# %% +logger = CSVLogger(save_dir="logs/") +trainer = flash.Trainer(logger=logger, max_epochs=3, gpus=1) +trainer.finetune(model, datamodule=datamodule, strategy="freeze") + +# %% +metrics = pd.read_csv(f"{trainer.logger.log_dir}/metrics.csv") +del metrics["step"] +metrics.set_index("epoch", inplace=True) +display(metrics.dropna(axis=1, how="all").head()) + +g = sn.relplot(data=metrics, kind="line") +plt.gcf().set_size_inches(12, 4) +plt.grid() + +# %% [markdown] +# ## 4. Predict what's on a few images! ants or bees? + +# %% +datamodule = ImageClassificationData.from_files( + predict_files=[ + f"{DATA_HYMENOPLERA}/val/bees/65038344_52a45d090d.jpg", + f"{DATA_HYMENOPLERA}/val/bees/590318879_68cf112861.jpg", + f"{DATA_HYMENOPLERA}/val/ants/540543309_ddbb193ee5.jpg", + ], + batch_size=3, +) +predictions = trainer.predict(model, datamodule=datamodule, output="labels") +print(predictions) + +# %% [markdown] +# ## 5. Save the model! + +# %% +trainer.save_checkpoint("image_classification_model.pt") diff --git a/_notebooks/templates/simple/.meta.yml b/_notebooks/templates/simple/.meta.yml new file mode 100644 index 0000000..db12f93 --- /dev/null +++ b/_notebooks/templates/simple/.meta.yml @@ -0,0 +1,13 @@ +title: How to write a PyTorch Lightning tutorial +author: PL team +created: 2021-06-15 +updated: 2021-06-17 +license: CC +build: 10 +description: | + This is a template to show how to contribute a tutorial. +requirements: + - matplotlib +accelerator: + - CPU + - GPU diff --git a/_notebooks/templates/simple/.thumb.png b/_notebooks/templates/simple/.thumb.png new file mode 100644 index 0000000..e778f65 Binary files /dev/null and b/_notebooks/templates/simple/.thumb.png differ diff --git a/_notebooks/templates/simple/template.py b/_notebooks/templates/simple/template.py new file mode 100644 index 0000000..9f30ea0 --- /dev/null +++ b/_notebooks/templates/simple/template.py @@ -0,0 +1,45 @@ +# %% [markdown] +# ## Create a Markdown cell +# +# `# %% [markdown]` +# +# the content of single cell shall be connected with `# ` at each line, so for example: +# `# Add some text that will be rendered as markdown text.` + +# %% [markdown] +# ## Create a code cell +# +# `# %%` + +# %% +import torch + +print(torch.__version__) + +# %% [markdown] +# ## Add any Python codes +# Easy integration with Python ecosystem libraries component. +# +# For example create a simple plot with `matplotlib` with an image: +# +# ![test image](test.png) +# +# From: https://matplotlib.org/stable/gallery/lines_bars_and_markers/simple_plot.html + +# %% +import matplotlib.pyplot as plt # noqa: E402 +import numpy as np # noqa: E402 + +# Data for plotting +t = np.arange(0.0, 2.0, 0.01) +s = 1 + np.sin(2 * np.pi * t) + +fig, ax = plt.subplots() +ax.plot(t, s) + +ax.set(xlabel="time (s)", ylabel="voltage (mV)", title="About as simple as it gets, folks") +ax.grid() + +fig.savefig("test.png") +# render image to the notebooks +plt.show() diff --git a/_notebooks/templates/titanic/.meta.yml b/_notebooks/templates/titanic/.meta.yml new file mode 100644 index 0000000..dbe94d7 --- /dev/null +++ b/_notebooks/templates/titanic/.meta.yml @@ -0,0 +1,18 @@ +title: Solving Titanic dataset with Lightning Flash +author: PL team +created: 2021-10-15 +updated: 2021-12-10 +license: CC +build: 0 +description: | + This is a template to show how to contribute a tutorial. +requirements: + - https://github.com/PyTorchLightning/lightning-flash/archive/refs/tags/0.5.2.zip#egg=lightning-flash[tabular] + - matplotlib + - seaborn +accelerator: + - CPU + - GPU +datasets: + kaggle: + - titanic diff --git a/_notebooks/templates/titanic/tutorial.py b/_notebooks/templates/titanic/tutorial.py new file mode 100644 index 0000000..a82976f --- /dev/null +++ b/_notebooks/templates/titanic/tutorial.py @@ -0,0 +1,101 @@ +import os + +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sns +import torch +from flash import Trainer +from flash.tabular import TabularClassificationData, TabularClassifier + +# %% [markdown] +# ## 1. Create the DataModule +# +# ### Variable & Definition +# +# - survival: Survival (0 = No, 1 = Yes) +# - pclass: Ticket class (1 = 1st, 2 = 2nd, 3 = 3rd) +# - sex: Sex +# - Age: Age in years +# - sibsp: number of siblings / spouses aboard the Titanic +# - parch: number of parents / children aboard the Titanic +# - ticket: Ticket number +# - fare: Passenger fare +# - cabin: Cabin number +# - embarked: Port of Embarkation + +# %% +data_path = os.environ.get("PATH_DATASETS", "_datasets") +path_titanic = os.path.join(data_path, "titanic") +csv_train = os.path.join(path_titanic, "train.csv") +csv_test = os.path.join(path_titanic, "test.csv") + +df_train = pd.read_csv(csv_train) +df_train["Survived"].hist(bins=2) + +# %% +datamodule = TabularClassificationData.from_csv( + categorical_fields=["Sex", "Embarked", "Cabin"], + numerical_fields=["Fare", "Age", "Pclass", "SibSp", "Parch"], + target_fields="Survived", + train_file=csv_train, + val_split=0.1, + batch_size=8, +) + +# %% [markdown] +# ## 2. Build the task + +# %% +model = TabularClassifier.from_data( + datamodule, + learning_rate=0.1, + optimizer="Adam", + n_a=8, + gamma=0.3, +) + +# %% [markdown] +# ## 3. Create the trainer and train the model + +# %% +from pytorch_lightning.loggers import CSVLogger # noqa: E402] + +logger = CSVLogger(save_dir="logs/") +trainer = Trainer( + max_epochs=10, + gpus=torch.cuda.device_count(), + logger=logger, + accumulate_grad_batches=12, + gradient_clip_val=0.1, +) + +# %% + +trainer.fit(model, datamodule=datamodule) + +# %% + +metrics = pd.read_csv(f"{trainer.logger.log_dir}/metrics.csv") +metrics.set_index("step", inplace=True) +del metrics["epoch"] +sns.relplot(data=metrics, kind="line") +plt.gca().set_ylim([0, 1.25]) +plt.gcf().set_size_inches(10, 5) + +# %% [markdown] +# ## 4. Generate predictions from a CSV + +# %% +df_test = pd.read_csv(csv_test) + +predictions = model.predict(csv_test) +print(predictions[0]) + +# %% +import numpy as np # noqa: E402] + +assert len(df_test) == len(predictions) + +df_test["Survived"] = np.argmax(predictions, axis=-1) +df_test.set_index("PassengerId", inplace=True) +df_test["Survived"].hist(bins=5) diff --git a/docs/.buildinfo b/docs/.buildinfo deleted file mode 100644 index 1767b18..0000000 --- a/docs/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 9f51b0822c0fcb09f5289c381438e1cc -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index ba22efe..0000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -docs.pytorchlightning.kr diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..d59458b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,76 @@ +# PyTorch-Lightning Docs + +We are using Sphinx with Napoleon extension. +Moreover, we set Google style to follow with type convention. + +- [Napoleon formatting with Google style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) +- [ReStructured Text (reST)](https://docs.pylonsproject.org/projects/docs-style-guide/) +- [Paragraph-level markup](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#paragraphs) + +See following short example of a sample function taking one position string and optional + +```python +from typing import Optional + + +def my_func(param_a: int, param_b: Optional[float] = None) -> str: + """Sample function. + + Args: + param_a: first parameter + param_b: second parameter + + Return: + sum of both numbers + + Example:: + + >>> my_func(1, 2) + 3 + + Note: + If you want to add something. + """ + p = param_b if param_b else 0 + return str(param_a + p) +``` + +## Building Docs + +When updating the docs, make sure to build them first locally and visually inspect the html files in your browser for +formatting errors. In certain cases, a missing blank line or a wrong indent can lead to a broken layout. +Run these commands + +```bash +git submodule update --init --recursive +make docs +``` + +and open `docs/build/html/index.html` in your browser. + +When you send a PR the continuous integration will run tests and build the docs. + +Notes: + +- You need to have LaTeX installed for rendering math equations. You can for example install TeXLive with the necessary extras by doing one of the following: + - on Ubuntu (Linux) run `sudo apt-get update && sudo apt-get install -y texlive-latex-extra dvipng texlive-pictures` + - use the [RTD docker image](https://hub.docker.com/r/readthedocs/build) +- You need to have pandoc installed for rendering Jupyter Notebooks. On Ubuntu (Linux), you can run: `sudo apt-get install pandoc` + +## Developing docs + +When developing the docs, building docs can be VERY slow locally because of the notebook tutorials. +To speed this up, enable this flag in before building docs: + +```bash +# builds notebooks which is slow +export PL_FAST_DOCS_DEV=0 + +# fast notebook build which is fast +export PL_FAST_DOCS_DEV=1 +``` + +## docs CSS/theme + +To change the CSS theme of the docs, go [here](https://github.com/Lightning-AI/lightning_sphinx_theme). +Apologies in advance... this is a bit complex to build and requires basic understanding of javascript/npm. diff --git a/docs/_modules/index.html b/docs/_modules/index.html deleted file mode 100644 index b78bc49..0000000 --- a/docs/_modules/index.html +++ /dev/null @@ -1,689 +0,0 @@ - - - - - - - - - - - - - Overview: module code — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
- - -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/callbacks/base.html b/docs/_modules/pytorch_lightning/callbacks/base.html deleted file mode 100644 index 7c0586e..0000000 --- a/docs/_modules/pytorch_lightning/callbacks/base.html +++ /dev/null @@ -1,1048 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.callbacks.base — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.callbacks.base

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-r"""
-Base class used to build new callbacks.
-
-"""
-
-from typing import Any, Dict, List, Optional, Type
-
-import torch
-from torch.optim import Optimizer
-
-import pytorch_lightning as pl
-from pytorch_lightning.utilities.types import STEP_OUTPUT
-
-
-class Callback:
-    r"""
-    Abstract base class used to build new callbacks.
-
-    Subclass this class and override any of the relevant hooks
-    """
-
-    @property
-    def state_key(self) -> str:
-        """Identifier for the state of the callback.
-
-        Used to store and retrieve a callback's state from the checkpoint dictionary by
-        ``checkpoint["callbacks"][state_key]``. Implementations of a callback need to provide a unique state key if 1)
-        the callback has state and 2) it is desired to maintain the state of multiple instances of that callback.
-        """
-        return self.__class__.__qualname__
-
-    @property
-    def _legacy_state_key(self) -> Type["Callback"]:
-        """State key for checkpoints saved prior to version 1.5.0."""
-        return type(self)
-
-    def _generate_state_key(self, **kwargs: Any) -> str:
-        """Formats a set of key-value pairs into a state key string with the callback class name prefixed. Useful
-        for defining a :attr:`state_key`.
-
-        Args:
-            **kwargs: A set of key-value pairs. Must be serializable to :class:`str`.
-        """
-        return f"{self.__class__.__qualname__}{repr(kwargs)}"
-
-
[docs] def on_configure_sharded_model(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.6 - This callback hook was deprecated in v1.6 and will be removed in v1.8. Use `setup()` instead. - - Called before configure sharded model. - """
- - def on_before_accelerator_backend_setup(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.6 - This callback hook was deprecated in v1.6 and will be removed in v1.8. Use ``setup()`` instead. - - Called before accelerator is being setup. - """ - -
[docs] def setup(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", stage: Optional[str] = None) -> None: - """Called when fit, validate, test, predict, or tune begins."""
- -
[docs] def teardown(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", stage: Optional[str] = None) -> None: - """Called when fit, validate, test, predict, or tune ends."""
- -
[docs] def on_init_start(self, trainer: "pl.Trainer") -> None: - r""" - .. deprecated:: v1.6 - This callback hook was deprecated in v1.6 and will be removed in v1.8. - - Called when the trainer initialization begins, model has not yet been set. - """
- -
[docs] def on_init_end(self, trainer: "pl.Trainer") -> None: - r""" - .. deprecated:: v1.6 - This callback hook was deprecated in v1.6 and will be removed in v1.8. - - Called when the trainer initialization ends, model has not yet been set. - """
- -
[docs] def on_fit_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when fit begins."""
- -
[docs] def on_fit_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when fit ends."""
- -
[docs] def on_sanity_check_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the validation sanity check starts."""
- -
[docs] def on_sanity_check_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the validation sanity check ends."""
- -
[docs] def on_train_batch_start( - self, - trainer: "pl.Trainer", - pl_module: "pl.LightningModule", - batch: Any, - batch_idx: int, - unused: int = 0, - ) -> None: - """Called when the train batch begins."""
- -
[docs] def on_train_batch_end( - self, - trainer: "pl.Trainer", - pl_module: "pl.LightningModule", - outputs: STEP_OUTPUT, - batch: Any, - batch_idx: int, - unused: int = 0, - ) -> None: - """Called when the train batch ends."""
- -
[docs] def on_train_epoch_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the train epoch begins."""
- -
[docs] def on_train_epoch_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the train epoch ends. - - To access all batch outputs at the end of the epoch, either: - - 1. Implement `training_epoch_end` in the `LightningModule` and access outputs via the module OR - 2. Cache data across train batch hooks inside the callback implementation to post-process in this hook. - """
- -
[docs] def on_validation_epoch_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the val epoch begins."""
- -
[docs] def on_validation_epoch_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the val epoch ends."""
- -
[docs] def on_test_epoch_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the test epoch begins."""
- -
[docs] def on_test_epoch_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the test epoch ends."""
- -
[docs] def on_predict_epoch_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the predict epoch begins."""
- -
[docs] def on_predict_epoch_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", outputs: List[Any]) -> None: - """Called when the predict epoch ends."""
- - def on_epoch_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.6 - This callback hook was deprecated in v1.6 and will be removed in v1.8. Use - ``on_<train/validation/test>_epoch_start`` instead. - - Called when either of train/val/test epoch begins. - """ - -
[docs] def on_epoch_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.6 - This callback hook was deprecated in v1.6 and will be removed in v1.8. Use - ``on_<train/validation/test>_epoch_end`` instead. - - Called when either of train/val/test epoch ends. - """
- - def on_batch_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.6 - This callback hook was deprecated in v1.6 and will be removed in v1.8. Use - ``on_train_batch_start`` instead. - - Called when the training batch begins. - """ - - def on_batch_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.6 - This callback hook was deprecated in v1.6 and will be removed in v1.8. Use - ``on_train_batch_end`` instead. - - Called when the training batch ends. - """ - -
[docs] def on_validation_batch_start( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", batch: Any, batch_idx: int, dataloader_idx: int - ) -> None: - """Called when the validation batch begins."""
- -
[docs] def on_validation_batch_end( - self, - trainer: "pl.Trainer", - pl_module: "pl.LightningModule", - outputs: Optional[STEP_OUTPUT], - batch: Any, - batch_idx: int, - dataloader_idx: int, - ) -> None: - """Called when the validation batch ends."""
- -
[docs] def on_test_batch_start( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", batch: Any, batch_idx: int, dataloader_idx: int - ) -> None: - """Called when the test batch begins."""
- -
[docs] def on_test_batch_end( - self, - trainer: "pl.Trainer", - pl_module: "pl.LightningModule", - outputs: Optional[STEP_OUTPUT], - batch: Any, - batch_idx: int, - dataloader_idx: int, - ) -> None: - """Called when the test batch ends."""
- -
[docs] def on_predict_batch_start( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", batch: Any, batch_idx: int, dataloader_idx: int - ) -> None: - """Called when the predict batch begins."""
- -
[docs] def on_predict_batch_end( - self, - trainer: "pl.Trainer", - pl_module: "pl.LightningModule", - outputs: Any, - batch: Any, - batch_idx: int, - dataloader_idx: int, - ) -> None: - """Called when the predict batch ends."""
- -
[docs] def on_train_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the train begins."""
- -
[docs] def on_train_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the train ends."""
- - def on_pretrain_routine_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.6 - - This callback hook was deprecated in v1.6 and will be removed in v1.8. Use ``on_fit_start`` instead. - - Called when the pretrain routine begins. - """ - - def on_pretrain_routine_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.6 - - This callback hook was deprecated in v1.6 and will be removed in v1.8. Use ``on_fit_start`` instead. - - Called when the pretrain routine ends. - """ - -
[docs] def on_validation_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the validation loop begins."""
- -
[docs] def on_validation_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the validation loop ends."""
- -
[docs] def on_test_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the test begins."""
- -
[docs] def on_test_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the test ends."""
- -
[docs] def on_predict_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when the predict begins."""
- -
[docs] def on_predict_end(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called when predict ends."""
- -
[docs] def on_keyboard_interrupt(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - r""" - .. deprecated:: v1.5 - This callback hook was deprecated in v1.5 in favor of `on_exception` and will be removed in v1.7. - - Called when any trainer execution is interrupted by KeyboardInterrupt. - """
- -
[docs] def on_exception(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", exception: BaseException) -> None: - """Called when any trainer execution is interrupted by an exception."""
- -
[docs] def state_dict(self) -> Dict[str, Any]: - """Called when saving a checkpoint, implement to generate callback's ``state_dict``. - - Returns: - A dictionary containing callback state. - """ - return {}
- -
[docs] def load_state_dict(self, state_dict: Dict[str, Any]) -> None: - """Called when loading a checkpoint, implement to reload callback state given callback's ``state_dict``. - - Args: - state_dict: the callback state returned by ``state_dict``. - """ - pass
- -
[docs] def on_save_checkpoint( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", checkpoint: Dict[str, Any] - ) -> Optional[dict]: - r""" - Called when saving a checkpoint to give you a chance to store anything else you might want to save. - - Args: - trainer: the current :class:`~pytorch_lightning.trainer.Trainer` instance. - pl_module: the current :class:`~pytorch_lightning.core.lightning.LightningModule` instance. - checkpoint: the checkpoint dictionary that will be saved. - - Returns: - None or the callback state. Support for returning callback state will be removed in v1.8. - - .. deprecated:: v1.6 - Returning a value from this method was deprecated in v1.6 and will be removed in v1.8. - Implement ``Callback.state_dict`` instead to return state. - In v1.8 ``Callback.on_save_checkpoint`` can only return None. - """
- -
[docs] def on_load_checkpoint( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", callback_state: Dict[str, Any] - ) -> None: - r""" - Called when loading a model checkpoint, use to reload state. - - Args: - trainer: the current :class:`~pytorch_lightning.trainer.Trainer` instance. - pl_module: the current :class:`~pytorch_lightning.core.lightning.LightningModule` instance. - callback_state: the callback state returned by ``on_save_checkpoint``. - - Note: - The ``on_load_checkpoint`` won't be called with an undefined state. - If your ``on_load_checkpoint`` hook behavior doesn't rely on a state, - you will still need to override ``on_save_checkpoint`` to return a ``dummy state``. - - .. deprecated:: v1.6 - This callback hook will change its signature and behavior in v1.8. - If you wish to load the state of the callback, use ``Callback.load_state_dict`` instead. - In v1.8 ``Callback.on_load_checkpoint(checkpoint)`` will receive the entire loaded - checkpoint dictionary instead of only the callback state from the checkpoint. - """
- -
[docs] def on_before_backward(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", loss: torch.Tensor) -> None: - """Called before ``loss.backward()``."""
- -
[docs] def on_after_backward(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None: - """Called after ``loss.backward()`` and before optimizers are stepped."""
- -
[docs] def on_before_optimizer_step( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", optimizer: Optimizer, opt_idx: int - ) -> None: - """Called before ``optimizer.step()``."""
- -
[docs] def on_before_zero_grad(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", optimizer: Optimizer) -> None: - """Called before ``optimizer.zero_grad()``."""
-
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/core/datamodule.html b/docs/_modules/pytorch_lightning/core/datamodule.html deleted file mode 100644 index 4ca3aff..0000000 --- a/docs/_modules/pytorch_lightning/core/datamodule.html +++ /dev/null @@ -1,944 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.core.datamodule — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.core.datamodule

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""LightningDataModule for loading DataLoaders with ease."""
-from argparse import ArgumentParser, Namespace
-from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union
-
-from torch.utils.data import DataLoader, Dataset, IterableDataset
-
-from pytorch_lightning.core.hooks import CheckpointHooks, DataHooks
-from pytorch_lightning.core.mixins import HyperparametersMixin
-from pytorch_lightning.utilities import rank_zero_deprecation
-from pytorch_lightning.utilities.argparse import add_argparse_args, from_argparse_args, get_init_arguments_and_types
-
-
-class LightningDataModule(CheckpointHooks, DataHooks, HyperparametersMixin):
-    """A DataModule standardizes the training, val, test splits, data preparation and transforms. The main
-    advantage is consistent data splits, data preparation and transforms across models.
-
-    Example::
-
-        class MyDataModule(LightningDataModule):
-            def __init__(self):
-                super().__init__()
-            def prepare_data(self):
-                # download, split, etc...
-                # only called on 1 GPU/TPU in distributed
-            def setup(self, stage):
-                # make assignments here (val/train/test split)
-                # called on every process in DDP
-            def train_dataloader(self):
-                train_split = Dataset(...)
-                return DataLoader(train_split)
-            def val_dataloader(self):
-                val_split = Dataset(...)
-                return DataLoader(val_split)
-            def test_dataloader(self):
-                test_split = Dataset(...)
-                return DataLoader(test_split)
-            def teardown(self):
-                # clean up after fit or test
-                # called on every process in DDP
-    """
-
-    name: str = ...
-
-    def __init__(self, train_transforms=None, val_transforms=None, test_transforms=None, dims=None):
-        super().__init__()
-        if train_transforms is not None:
-            rank_zero_deprecation(
-                "DataModule property `train_transforms` was deprecated in v1.5 and will be removed in v1.7."
-            )
-        if val_transforms is not None:
-            rank_zero_deprecation(
-                "DataModule property `val_transforms` was deprecated in v1.5 and will be removed in v1.7."
-            )
-        if test_transforms is not None:
-            rank_zero_deprecation(
-                "DataModule property `test_transforms` was deprecated in v1.5 and will be removed in v1.7."
-            )
-        if dims is not None:
-            rank_zero_deprecation("DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.")
-        self._train_transforms = train_transforms
-        self._val_transforms = val_transforms
-        self._test_transforms = test_transforms
-        self._dims = dims if dims is not None else ()
-
-        # Pointer to the trainer object
-        self.trainer = None
-
-    @property
-    def train_transforms(self):
-        """Optional transforms (or collection of transforms) you can apply to train dataset.
-
-        .. deprecated:: v1.5     Will be removed in v1.7.0.
-        """
-
-        rank_zero_deprecation(
-            "DataModule property `train_transforms` was deprecated in v1.5 and will be removed in v1.7."
-        )
-        return self._train_transforms
-
-    @train_transforms.setter
-    def train_transforms(self, t):
-        rank_zero_deprecation(
-            "DataModule property `train_transforms` was deprecated in v1.5 and will be removed in v1.7."
-        )
-        self._train_transforms = t
-
-    @property
-    def val_transforms(self):
-        """Optional transforms (or collection of transforms) you can apply to validation dataset.
-
-        .. deprecated:: v1.5     Will be removed in v1.7.0.
-        """
-
-        rank_zero_deprecation(
-            "DataModule property `val_transforms` was deprecated in v1.5 and will be removed in v1.7."
-        )
-        return self._val_transforms
-
-    @val_transforms.setter
-    def val_transforms(self, t):
-        rank_zero_deprecation(
-            "DataModule property `val_transforms` was deprecated in v1.5 and will be removed in v1.7."
-        )
-        self._val_transforms = t
-
-    @property
-    def test_transforms(self):
-        """Optional transforms (or collection of transforms) you can apply to test dataset.
-
-        .. deprecated:: v1.5     Will be removed in v1.7.0.
-        """
-
-        rank_zero_deprecation(
-            "DataModule property `test_transforms` was deprecated in v1.5 and will be removed in v1.7."
-        )
-        return self._test_transforms
-
-    @test_transforms.setter
-    def test_transforms(self, t):
-        rank_zero_deprecation(
-            "DataModule property `test_transforms` was deprecated in v1.5 and will be removed in v1.7."
-        )
-        self._test_transforms = t
-
-    @property
-    def dims(self):
-        """A tuple describing the shape of your data. Extra functionality exposed in ``size``.
-
-        .. deprecated:: v1.5     Will be removed in v1.7.0.
-        """
-        rank_zero_deprecation("DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.")
-        return self._dims
-
-    @dims.setter
-    def dims(self, d):
-        rank_zero_deprecation("DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.")
-        self._dims = d
-
-    def size(self, dim=None) -> Union[Tuple, List[Tuple]]:
-        """Return the dimension of each input either as a tuple or list of tuples. You can index this just as you
-        would with a torch tensor.
-
-        .. deprecated:: v1.5     Will be removed in v1.7.0.
-        """
-        rank_zero_deprecation("DataModule property `size` was deprecated in v1.5 and will be removed in v1.7.")
-
-        if dim is not None:
-            return self.dims[dim]
-
-        return self.dims
-
-    @classmethod
-    def add_argparse_args(cls, parent_parser: ArgumentParser, **kwargs) -> ArgumentParser:
-        """Extends existing argparse by default `LightningDataModule` attributes."""
-        return add_argparse_args(cls, parent_parser, **kwargs)
-
-    @classmethod
-    def from_argparse_args(cls, args: Union[Namespace, ArgumentParser], **kwargs):
-        """Create an instance from CLI arguments.
-
-        Args:
-            args: The parser or namespace to take arguments from. Only known arguments will be
-                parsed and passed to the :class:`~pytorch_lightning.core.datamodule.LightningDataModule`.
-            **kwargs: Additional keyword arguments that may override ones in the parser or namespace.
-                These must be valid DataModule arguments.
-
-        Example::
-
-            parser = ArgumentParser(add_help=False)
-            parser = LightningDataModule.add_argparse_args(parser)
-            module = LightningDataModule.from_argparse_args(args)
-        """
-        return from_argparse_args(cls, args, **kwargs)
-
-    @classmethod
-    def get_init_arguments_and_types(cls) -> List[Tuple[str, Tuple, Any]]:
-        r"""Scans the DataModule signature and returns argument names, types and default values.
-
-        Returns:
-            List with tuples of 3 values:
-            (argument name, set with argument types, argument default value).
-        """
-        return get_init_arguments_and_types(cls)
-
-    @classmethod
-    def from_datasets(
-        cls,
-        train_dataset: Optional[Union[Dataset, Sequence[Dataset], Mapping[str, Dataset]]] = None,
-        val_dataset: Optional[Union[Dataset, Sequence[Dataset]]] = None,
-        test_dataset: Optional[Union[Dataset, Sequence[Dataset]]] = None,
-        batch_size: int = 1,
-        num_workers: int = 0,
-    ):
-        r"""
-        Create an instance from torch.utils.data.Dataset.
-
-        Args:
-            train_dataset: (optional) Dataset to be used for train_dataloader()
-            val_dataset: (optional) Dataset or list of Dataset to be used for val_dataloader()
-            test_dataset: (optional) Dataset or list of Dataset to be used for test_dataloader()
-            batch_size: Batch size to use for each dataloader. Default is 1.
-            num_workers: Number of subprocesses to use for data loading. 0 means that the
-                data will be loaded in the main process. Number of CPUs available.
-
-        """
-
-        def dataloader(ds: Dataset, shuffle: bool = False) -> DataLoader:
-            shuffle &= not isinstance(ds, IterableDataset)
-            return DataLoader(ds, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers, pin_memory=True)
-
-        def train_dataloader():
-            if isinstance(train_dataset, Mapping):
-                return {key: dataloader(ds, shuffle=True) for key, ds in train_dataset.items()}
-            if isinstance(train_dataset, Sequence):
-                return [dataloader(ds, shuffle=True) for ds in train_dataset]
-            return dataloader(train_dataset, shuffle=True)
-
-        def val_dataloader():
-            if isinstance(val_dataset, Sequence):
-                return [dataloader(ds) for ds in val_dataset]
-            return dataloader(val_dataset)
-
-        def test_dataloader():
-            if isinstance(test_dataset, Sequence):
-                return [dataloader(ds) for ds in test_dataset]
-            return dataloader(test_dataset)
-
-        datamodule = cls()
-        if train_dataset is not None:
-            datamodule.train_dataloader = train_dataloader
-        if val_dataset is not None:
-            datamodule.val_dataloader = val_dataloader
-        if test_dataset is not None:
-            datamodule.test_dataloader = test_dataloader
-        return datamodule
-
-
[docs] def state_dict(self) -> Dict[str, Any]: - """Called when saving a checkpoint, implement to generate and save datamodule state. - - Returns: - A dictionary containing datamodule state. - """ - return {}
- -
[docs] def load_state_dict(self, state_dict: Dict[str, Any]) -> None: - """Called when loading a checkpoint, implement to reload datamodule state given datamodule state_dict. - - Args: - state_dict: the datamodule state returned by ``state_dict``. - """ - pass
-
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/core/lightning.html b/docs/_modules/pytorch_lightning/core/lightning.html deleted file mode 100644 index e80c879..0000000 --- a/docs/_modules/pytorch_lightning/core/lightning.html +++ /dev/null @@ -1,2729 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.core.lightning — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.core.lightning

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""The LightningModule - an nn.Module with many additional features."""
-
-import collections
-import inspect
-import logging
-import numbers
-import os
-import tempfile
-from contextlib import contextmanager
-from pathlib import Path
-from typing import Any, Callable, Dict, List, Mapping, Optional, overload, Sequence, Tuple, Union
-
-import torch
-from torch import ScriptModule, Tensor
-from torch.nn import Module
-from torch.optim.optimizer import Optimizer
-from torchmetrics import Metric
-from typing_extensions import Literal
-
-import pytorch_lightning as pl
-from pytorch_lightning.callbacks.base import Callback
-from pytorch_lightning.callbacks.progress import base as progress_base
-from pytorch_lightning.core.hooks import CheckpointHooks, DataHooks, ModelHooks
-from pytorch_lightning.core.mixins import DeviceDtypeModuleMixin, HyperparametersMixin
-from pytorch_lightning.core.optimizer import LightningOptimizer
-from pytorch_lightning.core.saving import ModelIO
-from pytorch_lightning.loggers import LightningLoggerBase
-from pytorch_lightning.trainer.connectors.data_connector import _DataHookSelector
-from pytorch_lightning.trainer.connectors.logger_connector.fx_validator import _FxValidator
-from pytorch_lightning.utilities import _IS_WINDOWS, _TORCH_GREATER_EQUAL_1_10, GradClipAlgorithmType
-from pytorch_lightning.utilities.apply_func import apply_to_collection, convert_to_tensors
-from pytorch_lightning.utilities.cloud_io import get_filesystem
-from pytorch_lightning.utilities.distributed import distributed_available, sync_ddp
-from pytorch_lightning.utilities.exceptions import MisconfigurationException
-from pytorch_lightning.utilities.memory import get_model_size_mb
-from pytorch_lightning.utilities.model_summary import ModelSummary, summarize
-from pytorch_lightning.utilities.parsing import collect_init_args
-from pytorch_lightning.utilities.rank_zero import rank_zero_debug, rank_zero_deprecation, rank_zero_warn
-from pytorch_lightning.utilities.signature_utils import is_param_in_hook_signature
-from pytorch_lightning.utilities.types import _METRIC_COLLECTION, EPOCH_OUTPUT, LRSchedulerTypeUnion, STEP_OUTPUT
-from pytorch_lightning.utilities.warnings import WarningCache
-
-warning_cache = WarningCache()
-log = logging.getLogger(__name__)
-
-
-class LightningModule(
-    DeviceDtypeModuleMixin,
-    HyperparametersMixin,
-    ModelIO,
-    ModelHooks,
-    DataHooks,
-    CheckpointHooks,
-    Module,
-):
-    # Below is for property support of JIT
-    # since none of these are important when using JIT, we are going to ignore them.
-    __jit_unused_properties__ = (
-        [
-            "example_input_array",
-            "on_gpu",
-            "current_epoch",
-            "global_step",
-            "global_rank",
-            "local_rank",
-            "logger",
-            "loggers",
-            "model_size",
-            "automatic_optimization",
-            "truncated_bptt_steps",
-            "use_amp",
-        ]
-        + DeviceDtypeModuleMixin.__jit_unused_properties__
-        + HyperparametersMixin.__jit_unused_properties__
-    )
-
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        super().__init__(*args, **kwargs)
-
-        # see (https://github.com/pytorch/pytorch/blob/3e6bb5233f9ca2c5aa55d9cda22a7ee85439aa6e/
-        # torch/nn/modules/module.py#L227)
-        torch._C._log_api_usage_once(f"lightning.module.{self.__class__.__name__}")
-
-        # pointer to the trainer object
-        self.trainer: Optional["pl.Trainer"] = None
-
-        self._use_amp: bool = False
-
-        # the precision used
-        self.precision: int = 32
-
-        # optionally can be set by user
-        self._example_input_array = None
-        self._current_fx_name: Optional[str] = None
-        self._automatic_optimization: bool = True
-        self._truncated_bptt_steps: int = 0
-        self._param_requires_grad_state = {}
-        self._metric_attributes: Optional[Dict[int, str]] = None
-        self._should_prevent_trainer_and_dataloaders_deepcopy: bool = False
-        # TODO: remove in 1.8
-        self._running_torchscript = False
-
-        self._register_sharded_tensor_state_dict_hooks_if_available()
-
-    @overload
-    def optimizers(self, use_pl_optimizer: Literal[True] = True) -> Union[LightningOptimizer, List[LightningOptimizer]]:
-        ...
-
-    @overload
-    def optimizers(self, use_pl_optimizer: Literal[False]) -> Union[Optimizer, List[Optimizer]]:
-        ...
-
-    @overload
-    def optimizers(
-        self, use_pl_optimizer: bool
-    ) -> Union[Optimizer, LightningOptimizer, List[Optimizer], List[LightningOptimizer]]:
-        ...
-
-
[docs] def optimizers( - self, use_pl_optimizer: bool = True - ) -> Union[Optimizer, LightningOptimizer, List[Optimizer], List[LightningOptimizer]]: - """Returns the optimizer(s) that are being used during training. Useful for manual optimization. - - Args: - use_pl_optimizer: If ``True``, will wrap the optimizer(s) in a - :class:`~pytorch_lightning.core.optimizer.LightningOptimizer` for automatic handling of precision and - profiling. - - Returns: - A single optimizer, or a list of optimizers in case multiple ones are present. - """ - if use_pl_optimizer: - opts = list(self.trainer.strategy._lightning_optimizers.values()) - else: - opts = self.trainer.optimizers - - # single optimizer - if isinstance(opts, list) and len(opts) == 1 and isinstance(opts[0], (Optimizer, LightningOptimizer)): - return opts[0] - # multiple opts - return opts
- -
[docs] def lr_schedulers(self) -> Optional[Union[LRSchedulerTypeUnion, List[LRSchedulerTypeUnion]]]: - """Returns the learning rate scheduler(s) that are being used during training. Useful for manual - optimization. - - Returns: - A single scheduler, or a list of schedulers in case multiple ones are present, or ``None`` if no - schedulers were returned in :meth:`configure_optimizers`. - """ - if not self.trainer.lr_scheduler_configs: - return None - - # ignore other keys "interval", "frequency", etc. - lr_schedulers = [config.scheduler for config in self.trainer.lr_scheduler_configs] - - # single scheduler - if len(lr_schedulers) == 1: - return lr_schedulers[0] - - # multiple schedulers - return lr_schedulers
- - @property - def example_input_array(self) -> Any: - """The example input array is a specification of what the module can consume in the :meth:`forward` method. - The return type is interpreted as follows: - - - Single tensor: It is assumed the model takes a single argument, i.e., - ``model.forward(model.example_input_array)`` - - Tuple: The input array should be interpreted as a sequence of positional arguments, i.e., - ``model.forward(*model.example_input_array)`` - - Dict: The input array represents named keyword arguments, i.e., - ``model.forward(**model.example_input_array)`` - """ - return self._example_input_array - - @example_input_array.setter - def example_input_array(self, example: Any) -> None: - self._example_input_array = example - - @property - def current_epoch(self) -> int: - """The current epoch in the ``Trainer``, or 0 if not attached.""" - return self.trainer.current_epoch if self.trainer else 0 - - @property - def global_step(self) -> int: - """Total training batches seen across all epochs. - - If no Trainer is attached, this propery is 0. - """ - return self.trainer.global_step if self.trainer else 0 - - @property - def global_rank(self) -> int: - """The index of the current process across all nodes and devices.""" - return self.trainer.global_rank if self.trainer else 0 - - @property - def local_rank(self) -> int: - """The index of the current process within a single node.""" - return self.trainer.local_rank if self.trainer else 0 - - @property - def on_gpu(self): - """Returns ``True`` if this model is currently located on a GPU. - - Useful to set flags around the LightningModule for different CPU vs GPU behavior. - """ - return self.device.type == "cuda" - - @property - def automatic_optimization(self) -> bool: - """If set to ``False`` you are responsible for calling ``.backward()``, ``.step()``, ``.zero_grad()``.""" - return self._automatic_optimization - - @automatic_optimization.setter - def automatic_optimization(self, automatic_optimization: bool) -> None: - self._automatic_optimization = automatic_optimization - - @property - def truncated_bptt_steps(self) -> int: - """Enables `Truncated Backpropagation Through Time` in the Trainer when set to a positive integer. - - It represents - the number of times :meth:`training_step` gets called before backpropagation. If this is > 0, the - :meth:`training_step` receives an additional argument ``hiddens`` and is expected to return a hidden state. - """ - return self._truncated_bptt_steps - - @truncated_bptt_steps.setter - def truncated_bptt_steps(self, truncated_bptt_steps: int) -> None: - self._truncated_bptt_steps = truncated_bptt_steps - - @property - def logger(self) -> Optional[LightningLoggerBase]: - """Reference to the logger object in the Trainer.""" - return self.trainer.logger if self.trainer else None - - @property - def loggers(self) -> List[LightningLoggerBase]: - """Reference to the list of loggers in the Trainer.""" - return self.trainer.loggers if self.trainer else [] - - def _apply_batch_transfer_handler( - self, batch: Any, device: Optional[torch.device] = None, dataloader_idx: int = 0 - ) -> Any: - device = device or self.device - datahook_selector = ( - _DataHookSelector(self, None) if self.trainer is None else self.trainer._data_connector._datahook_selector - ) - - hook = datahook_selector.get_hook("on_before_batch_transfer") - batch = hook(batch, dataloader_idx) - hook = datahook_selector.get_hook("transfer_batch_to_device") - batch = hook(batch, device, dataloader_idx) - hook = datahook_selector.get_hook("on_after_batch_transfer") - batch = hook(batch, dataloader_idx) - return batch - -
[docs] def print(self, *args, **kwargs) -> None: - r""" - Prints only from process 0. Use this in any distributed mode to log only once. - - Args: - *args: The thing to print. The same as for Python's built-in print function. - **kwargs: The same as for Python's built-in print function. - - Example:: - - def forward(self, x): - self.print(x, 'in forward') - - """ - if self.trainer.is_global_zero: - progress_bar = self.trainer.progress_bar_callback - if progress_bar is not None and progress_bar.is_enabled: - progress_bar.print(*args, **kwargs) - else: - print(*args, **kwargs)
- -
[docs] def log( - self, - name: str, - value: _METRIC_COLLECTION, - prog_bar: bool = False, - logger: bool = True, - on_step: Optional[bool] = None, - on_epoch: Optional[bool] = None, - reduce_fx: Union[str, Callable] = "mean", - enable_graph: bool = False, - sync_dist: bool = False, - sync_dist_group: Optional[Any] = None, - add_dataloader_idx: bool = True, - batch_size: Optional[int] = None, - metric_attribute: Optional[str] = None, - rank_zero_only: bool = False, - ) -> None: - """Log a key, value pair. - - Example:: - - self.log('train_loss', loss) - - The default behavior per hook is documented here: :ref:`extensions/logging:Automatic Logging`. - - Args: - name: key to log. - value: value to log. Can be a ``float``, ``Tensor``, ``Metric``, or a dictionary of the former. - prog_bar: if ``True`` logs to the progress bar. - logger: if ``True`` logs to the logger. - on_step: if ``True`` logs at this step. The default value is determined by the hook. - See :ref:`extensions/logging:Automatic Logging` for details. - on_epoch: if ``True`` logs epoch accumulated metrics. The default value is determined by the hook. - See :ref:`extensions/logging:Automatic Logging` for details. - reduce_fx: reduction function over step values for end of epoch. :meth:`torch.mean` by default. - enable_graph: if ``True``, will not auto detach the graph. - sync_dist: if ``True``, reduces the metric across devices. Use with care as this may lead to a significant - communication overhead. - sync_dist_group: the DDP group to sync across. - add_dataloader_idx: if ``True``, appends the index of the current dataloader to - the name (when using multiple dataloaders). If False, user needs to give unique names for - each dataloader to not mix the values. - batch_size: Current batch_size. This will be directly inferred from the loaded batch, - but for some data structures you might need to explicitly provide it. - metric_attribute: To restore the metric state, Lightning requires the reference of the - :class:`torchmetrics.Metric` in your model. This is found automatically if it is a model attribute. - rank_zero_only: Whether the value will be logged only on rank 0. This will prevent synchronization which - would produce a deadlock as not all processes would perform this log call. - """ - # check for invalid values - apply_to_collection(value, dict, self.__check_not_nested, name) - apply_to_collection( - value, object, self.__check_allowed, name, value, wrong_dtype=(numbers.Number, Metric, Tensor, dict) - ) - - if self.trainer is None: - # not an error to support testing the `*_step` methods without a `Trainer` reference - rank_zero_warn( - "You are trying to `self.log()` but the `self.trainer` reference is not registered on the model yet." - " This is most likely because the model hasn't been passed to the `Trainer`" - ) - return - results = self.trainer._results - if results is None: - raise MisconfigurationException( - "You are trying to `self.log()` but the loop's result collection is not registered" - " yet. This is most likely because you are trying to log in a `predict` hook," - " but it doesn't support logging" - ) - if self._current_fx_name is None: - raise MisconfigurationException( - "You are trying to `self.log()` but it is not managed by the `Trainer` control flow" - ) - - on_step, on_epoch = _FxValidator.check_logging_and_get_default_levels( - self._current_fx_name, on_step=on_step, on_epoch=on_epoch - ) - - # make sure user doesn't introduce logic for multi-dataloaders - if "/dataloader_idx_" in name: - raise MisconfigurationException( - f"You called `self.log` with the key `{name}`" - " but it should not contain information about `dataloader_idx`" - ) - - value = apply_to_collection(value, numbers.Number, self.__to_tensor) - - if self.trainer._logger_connector.should_reset_tensors(self._current_fx_name): - # if we started a new epoch (running its first batch) the hook name has changed - # reset any tensors for the new hook name - results.reset(metrics=False, fx=self._current_fx_name) - - if metric_attribute is None and isinstance(value, Metric): - if self._metric_attributes is None: - # compute once - self._metric_attributes = { - id(module): name for name, module in self.named_modules() if isinstance(module, Metric) - } - if not self._metric_attributes: - raise MisconfigurationException( - "Could not find the `LightningModule` attribute for the `torchmetrics.Metric` logged." - " You can fix this by setting an attribute for the metric in your `LightningModule`." - ) - # try to find the passed metric in the LightningModule - metric_attribute = self._metric_attributes.get(id(value), None) - if metric_attribute is None: - raise MisconfigurationException( - "Could not find the `LightningModule` attribute for the `torchmetrics.Metric` logged." - f" You can fix this by calling `self.log({name}, ..., metric_attribute=name)` where `name` is one" - f" of {list(self._metric_attributes.values())}" - ) - - if ( - self.trainer.training - and is_param_in_hook_signature(self.training_step, "dataloader_iter", explicit=True) - and batch_size is None - ): - raise MisconfigurationException( - "With `def training_step(self, dataloader_iter)`, `self.log(..., batch_size=...)` should be provided." - ) - - results.log( - self._current_fx_name, - name, - value, - prog_bar=prog_bar, - logger=logger, - on_step=on_step, - on_epoch=on_epoch, - reduce_fx=reduce_fx, - enable_graph=enable_graph, - add_dataloader_idx=add_dataloader_idx, - batch_size=batch_size, - sync_dist=sync_dist and distributed_available(), - sync_dist_fn=self.trainer.strategy.reduce or sync_ddp, - sync_dist_group=sync_dist_group, - metric_attribute=metric_attribute, - rank_zero_only=rank_zero_only, - ) - - self.trainer._logger_connector._current_fx = self._current_fx_name
- -
[docs] def log_dict( - self, - dictionary: Mapping[str, _METRIC_COLLECTION], - prog_bar: bool = False, - logger: bool = True, - on_step: Optional[bool] = None, - on_epoch: Optional[bool] = None, - reduce_fx: Union[str, Callable] = "mean", - enable_graph: bool = False, - sync_dist: bool = False, - sync_dist_group: Optional[Any] = None, - add_dataloader_idx: bool = True, - batch_size: Optional[int] = None, - rank_zero_only: bool = False, - ) -> None: - """Log a dictionary of values at once. - - Example:: - - values = {'loss': loss, 'acc': acc, ..., 'metric_n': metric_n} - self.log_dict(values) - - Args: - dictionary: key value pairs. - The values can be a ``float``, ``Tensor``, ``Metric``, or a dictionary of the former. - prog_bar: if ``True`` logs to the progress base. - logger: if ``True`` logs to the logger. - on_step: if ``True`` logs at this step. - ``None`` auto-logs for training_step but not validation/test_step. - The default value is determined by the hook. - See :ref:`extensions/logging:Automatic Logging` for details. - on_epoch: if ``True`` logs epoch accumulated metrics. - ``None`` auto-logs for val/test step but not ``training_step``. - The default value is determined by the hook. - See :ref:`extensions/logging:Automatic Logging` for details. - reduce_fx: reduction function over step values for end of epoch. :meth:`torch.mean` by default. - enable_graph: if ``True``, will not auto-detach the graph - sync_dist: if ``True``, reduces the metric across GPUs/TPUs. Use with care as this may lead to a significant - communication overhead. - sync_dist_group: the ddp group to sync across. - add_dataloader_idx: if ``True``, appends the index of the current dataloader to - the name (when using multiple). If ``False``, user needs to give unique names for - each dataloader to not mix values. - batch_size: Current batch size. This will be directly inferred from the loaded batch, - but some data structures might need to explicitly provide it. - rank_zero_only: Whether the value will be logged only on rank 0. This will prevent synchronization which - would produce a deadlock as not all processes would perform this log call. - """ - for k, v in dictionary.items(): - self.log( - name=k, - value=v, - prog_bar=prog_bar, - logger=logger, - on_step=on_step, - on_epoch=on_epoch, - reduce_fx=reduce_fx, - enable_graph=enable_graph, - sync_dist=sync_dist, - sync_dist_group=sync_dist_group, - add_dataloader_idx=add_dataloader_idx, - batch_size=batch_size, - rank_zero_only=rank_zero_only, - )
- - @staticmethod - def __check_not_nested(value: dict, name: str) -> dict: - # self-imposed restriction. for simplicity - if any(isinstance(v, dict) for v in value.values()): - raise ValueError(f"`self.log({name}, {value})` was called, but nested dictionaries cannot be logged") - return value - - @staticmethod - def __check_allowed(v: Any, name: str, value: Any) -> None: - raise ValueError(f"`self.log({name}, {value})` was called, but `{type(v).__name__}` values cannot be logged") - - def __to_tensor(self, value: numbers.Number) -> torch.Tensor: - return torch.tensor(value, device=self.device) - - def log_grad_norm(self, grad_norm_dict: Dict[str, float]) -> None: - """Override this method to change the default behaviour of ``log_grad_norm``. - - If clipping gradients, the gradients will not have been clipped yet. - - Args: - grad_norm_dict: Dictionary containing current grad norm metrics - - Example:: - - # DEFAULT - def log_grad_norm(self, grad_norm_dict): - self.log_dict(grad_norm_dict, on_step=True, on_epoch=True, prog_bar=False, logger=True) - """ - self.log_dict(grad_norm_dict, on_step=True, on_epoch=True, prog_bar=False, logger=True) - -
[docs] def all_gather( - self, data: Union[torch.Tensor, Dict, List, Tuple], group: Optional[Any] = None, sync_grads: bool = False - ): - r""" - Allows users to call ``self.all_gather()`` from the LightningModule, thus making the ``all_gather`` operation - accelerator agnostic. ``all_gather`` is a function provided by accelerators to gather a tensor from several - distributed processes. - - Args: - data: int, float, tensor of shape (batch, ...), or a (possibly nested) collection thereof. - group: the process group to gather results from. Defaults to all processes (world) - sync_grads: flag that allows users to synchronize gradients for the all_gather operation - - Return: - A tensor of shape (world_size, batch, ...), or if the input was a collection - the output will also be a collection with tensors of this shape. - """ - group = group if group is not None else torch.distributed.group.WORLD - all_gather = self.trainer.strategy.all_gather - data = convert_to_tensors(data, device=self.device) - return apply_to_collection(data, torch.Tensor, all_gather, group=group, sync_grads=sync_grads)
- -
[docs] def forward(self, *args, **kwargs) -> Any: - r""" - Same as :meth:`torch.nn.Module.forward()`. - - Args: - *args: Whatever you decide to pass into the forward method. - **kwargs: Keyword arguments are also possible. - - Return: - Your model's output - """ - return super().forward(*args, **kwargs)
- -
[docs] def training_step(self, *args, **kwargs) -> STEP_OUTPUT: - r""" - Here you compute and return the training loss and some additional metrics for e.g. - the progress bar or logger. - - Args: - batch (:class:`~torch.Tensor` | (:class:`~torch.Tensor`, ...) | [:class:`~torch.Tensor`, ...]): - The output of your :class:`~torch.utils.data.DataLoader`. A tensor, tuple or list. - batch_idx (``int``): Integer displaying index of this batch - optimizer_idx (``int``): When using multiple optimizers, this argument will also be present. - hiddens (``Any``): Passed in if - :paramref:`~pytorch_lightning.core.lightning.LightningModule.truncated_bptt_steps` > 0. - - Return: - Any of. - - - :class:`~torch.Tensor` - The loss tensor - - ``dict`` - A dictionary. Can include any keys, but must include the key ``'loss'`` - - ``None`` - Training will skip to the next batch. This is only for automatic optimization. - This is not supported for multi-GPU, TPU, IPU, or DeepSpeed. - - In this step you'd normally do the forward pass and calculate the loss for a batch. - You can also do fancier things like multiple forward passes or something model specific. - - Example:: - - def training_step(self, batch, batch_idx): - x, y, z = batch - out = self.encoder(x) - loss = self.loss(out, x) - return loss - - If you define multiple optimizers, this step will be called with an additional - ``optimizer_idx`` parameter. - - .. code-block:: python - - # Multiple optimizers (e.g.: GANs) - def training_step(self, batch, batch_idx, optimizer_idx): - if optimizer_idx == 0: - # do training_step with encoder - ... - if optimizer_idx == 1: - # do training_step with decoder - ... - - - If you add truncated back propagation through time you will also get an additional - argument with the hidden states of the previous step. - - .. code-block:: python - - # Truncated back-propagation through time - def training_step(self, batch, batch_idx, hiddens): - # hiddens are the hidden states from the previous truncated backprop step - out, hiddens = self.lstm(data, hiddens) - loss = ... - return {"loss": loss, "hiddens": hiddens} - - Note: - The loss value shown in the progress bar is smoothed (averaged) over the last values, - so it differs from the actual loss returned in train/validation step. - """ - rank_zero_warn("`training_step` must be implemented to be used with the Lightning Trainer")
- -
[docs] def training_step_end(self, step_output: STEP_OUTPUT) -> STEP_OUTPUT: - """Use this when training with dp or ddp2 because :meth:`training_step` will operate on only part of the - batch. However, this is still optional and only needed for things like softmax or NCE loss. - - Note: - If you later switch to ddp or some other mode, this will still be called - so that you don't have to change your code - - .. code-block:: python - - # pseudocode - sub_batches = split_batches_for_dp(batch) - step_output = [training_step(sub_batch) for sub_batch in sub_batches] - training_step_end(step_output) - - Args: - step_output: What you return in `training_step` for each batch part. - - Return: - Anything - - When using dp/ddp2 distributed backends, only a portion of the batch is inside the training_step: - - .. code-block:: python - - def training_step(self, batch, batch_idx): - # batch is 1/num_gpus big - x, y = batch - - out = self(x) - - # softmax uses only a portion of the batch in the denominator - loss = self.softmax(out) - loss = nce_loss(loss) - return loss - - If you wish to do something with all the parts of the batch, then use this method to do it: - - .. code-block:: python - - def training_step(self, batch, batch_idx): - # batch is 1/num_gpus big - x, y = batch - - out = self.encoder(x) - return {"pred": out} - - - def training_step_end(self, training_step_outputs): - gpu_0_pred = training_step_outputs[0]["pred"] - gpu_1_pred = training_step_outputs[1]["pred"] - gpu_n_pred = training_step_outputs[n]["pred"] - - # this softmax now uses the full batch - loss = nce_loss([gpu_0_pred, gpu_1_pred, gpu_n_pred]) - return loss - - See Also: - See the :ref:`accelerators/gpu:Multi GPU Training` guide for more details. - """
- -
[docs] def training_epoch_end(self, outputs: EPOCH_OUTPUT) -> None: - """Called at the end of the training epoch with the outputs of all training steps. Use this in case you - need to do something with all the outputs returned by :meth:`training_step`. - - .. code-block:: python - - # the pseudocode for these calls - train_outs = [] - for train_batch in train_data: - out = training_step(train_batch) - train_outs.append(out) - training_epoch_end(train_outs) - - Args: - outputs: List of outputs you defined in :meth:`training_step`. If there are multiple optimizers or when - using ``truncated_bptt_steps > 0``, the lists have the dimensions - (n_batches, tbptt_steps, n_optimizers). Dimensions of length 1 are squeezed. - - Return: - None - - Note: - If this method is not overridden, this won't be called. - - .. code-block:: python - - def training_epoch_end(self, training_step_outputs): - # do something with all training_step outputs - for out in training_step_outputs: - ... - """
- -
[docs] def validation_step(self, *args, **kwargs) -> Optional[STEP_OUTPUT]: - r""" - Operates on a single batch of data from the validation set. - In this step you'd might generate examples or calculate anything of interest like accuracy. - - .. code-block:: python - - # the pseudocode for these calls - val_outs = [] - for val_batch in val_data: - out = validation_step(val_batch) - val_outs.append(out) - validation_epoch_end(val_outs) - - Args: - batch: The output of your :class:`~torch.utils.data.DataLoader`. - batch_idx: The index of this batch. - dataloader_idx: The index of the dataloader that produced this batch. - (only if multiple val dataloaders used) - - Return: - - Any object or value - - ``None`` - Validation will skip to the next batch - - .. code-block:: python - - # pseudocode of order - val_outs = [] - for val_batch in val_data: - out = validation_step(val_batch) - if defined("validation_step_end"): - out = validation_step_end(out) - val_outs.append(out) - val_outs = validation_epoch_end(val_outs) - - - .. code-block:: python - - # if you have one val dataloader: - def validation_step(self, batch, batch_idx): - ... - - - # if you have multiple val dataloaders: - def validation_step(self, batch, batch_idx, dataloader_idx=0): - ... - - Examples:: - - # CASE 1: A single validation dataset - def validation_step(self, batch, batch_idx): - x, y = batch - - # implement your own - out = self(x) - loss = self.loss(out, y) - - # log 6 example images - # or generated text... or whatever - sample_imgs = x[:6] - grid = torchvision.utils.make_grid(sample_imgs) - self.logger.experiment.add_image('example_images', grid, 0) - - # calculate acc - labels_hat = torch.argmax(out, dim=1) - val_acc = torch.sum(y == labels_hat).item() / (len(y) * 1.0) - - # log the outputs! - self.log_dict({'val_loss': loss, 'val_acc': val_acc}) - - If you pass in multiple val dataloaders, :meth:`validation_step` will have an additional argument. We recommend - setting the default value of 0 so that you can quickly switch between single and multiple dataloaders. - - .. code-block:: python - - # CASE 2: multiple validation dataloaders - def validation_step(self, batch, batch_idx, dataloader_idx=0): - # dataloader_idx tells you which dataset this is. - ... - - Note: - If you don't need to validate you don't need to implement this method. - - Note: - When the :meth:`validation_step` is called, the model has been put in eval mode - and PyTorch gradients have been disabled. At the end of validation, - the model goes back to training mode and gradients are enabled. - """
- -
[docs] def validation_step_end(self, *args, **kwargs) -> Optional[STEP_OUTPUT]: - """Use this when validating with dp or ddp2 because :meth:`validation_step` will operate on only part of - the batch. However, this is still optional and only needed for things like softmax or NCE loss. - - Note: - If you later switch to ddp or some other mode, this will still be called - so that you don't have to change your code. - - .. code-block:: python - - # pseudocode - sub_batches = split_batches_for_dp(batch) - step_output = [validation_step(sub_batch) for sub_batch in sub_batches] - validation_step_end(step_output) - - Args: - step_output: What you return in :meth:`validation_step` for each batch part. - - Return: - None or anything - - .. code-block:: python - - # WITHOUT validation_step_end - # if used in DP or DDP2, this batch is 1/num_gpus large - def validation_step(self, batch, batch_idx): - # batch is 1/num_gpus big - x, y = batch - - out = self.encoder(x) - loss = self.softmax(out) - loss = nce_loss(loss) - self.log("val_loss", loss) - - - # -------------- - # with validation_step_end to do softmax over the full batch - def validation_step(self, batch, batch_idx): - # batch is 1/num_gpus big - x, y = batch - - out = self(x) - return out - - - def validation_step_end(self, val_step_outputs): - for out in val_step_outputs: - ... - - See Also: - See the :ref:`accelerators/gpu:Multi GPU Training` guide for more details. - """
- -
[docs] def validation_epoch_end(self, outputs: Union[EPOCH_OUTPUT, List[EPOCH_OUTPUT]]) -> None: - """Called at the end of the validation epoch with the outputs of all validation steps. - - .. code-block:: python - - # the pseudocode for these calls - val_outs = [] - for val_batch in val_data: - out = validation_step(val_batch) - val_outs.append(out) - validation_epoch_end(val_outs) - - Args: - outputs: List of outputs you defined in :meth:`validation_step`, or if there - are multiple dataloaders, a list containing a list of outputs for each dataloader. - - Return: - None - - Note: - If you didn't define a :meth:`validation_step`, this won't be called. - - Examples: - With a single dataloader: - - .. code-block:: python - - def validation_epoch_end(self, val_step_outputs): - for out in val_step_outputs: - ... - - With multiple dataloaders, `outputs` will be a list of lists. The outer list contains - one entry per dataloader, while the inner list contains the individual outputs of - each validation step for that dataloader. - - .. code-block:: python - - def validation_epoch_end(self, outputs): - for dataloader_output_result in outputs: - dataloader_outs = dataloader_output_result.dataloader_i_outputs - - self.log("final_metric", final_value) - """
- -
[docs] def test_step(self, *args, **kwargs) -> Optional[STEP_OUTPUT]: - r""" - Operates on a single batch of data from the test set. - In this step you'd normally generate examples or calculate anything of interest - such as accuracy. - - .. code-block:: python - - # the pseudocode for these calls - test_outs = [] - for test_batch in test_data: - out = test_step(test_batch) - test_outs.append(out) - test_epoch_end(test_outs) - - Args: - batch: The output of your :class:`~torch.utils.data.DataLoader`. - batch_idx: The index of this batch. - dataloader_id: The index of the dataloader that produced this batch. - (only if multiple test dataloaders used). - - Return: - Any of. - - - Any object or value - - ``None`` - Testing will skip to the next batch - - .. code-block:: python - - # if you have one test dataloader: - def test_step(self, batch, batch_idx): - ... - - - # if you have multiple test dataloaders: - def test_step(self, batch, batch_idx, dataloader_idx=0): - ... - - Examples:: - - # CASE 1: A single test dataset - def test_step(self, batch, batch_idx): - x, y = batch - - # implement your own - out = self(x) - loss = self.loss(out, y) - - # log 6 example images - # or generated text... or whatever - sample_imgs = x[:6] - grid = torchvision.utils.make_grid(sample_imgs) - self.logger.experiment.add_image('example_images', grid, 0) - - # calculate acc - labels_hat = torch.argmax(out, dim=1) - test_acc = torch.sum(y == labels_hat).item() / (len(y) * 1.0) - - # log the outputs! - self.log_dict({'test_loss': loss, 'test_acc': test_acc}) - - If you pass in multiple test dataloaders, :meth:`test_step` will have an additional argument. We recommend - setting the default value of 0 so that you can quickly switch between single and multiple dataloaders. - - .. code-block:: python - - # CASE 2: multiple test dataloaders - def test_step(self, batch, batch_idx, dataloader_idx=0): - # dataloader_idx tells you which dataset this is. - ... - - Note: - If you don't need to test you don't need to implement this method. - - Note: - When the :meth:`test_step` is called, the model has been put in eval mode and - PyTorch gradients have been disabled. At the end of the test epoch, the model goes back - to training mode and gradients are enabled. - """
- -
[docs] def test_step_end(self, *args, **kwargs) -> Optional[STEP_OUTPUT]: - """Use this when testing with dp or ddp2 because :meth:`test_step` will operate on only part of the batch. - However, this is still optional and only needed for things like softmax or NCE loss. - - Note: - If you later switch to ddp or some other mode, this will still be called - so that you don't have to change your code. - - .. code-block:: python - - # pseudocode - sub_batches = split_batches_for_dp(batch) - step_output = [test_step(sub_batch) for sub_batch in sub_batches] - test_step_end(step_output) - - Args: - step_output: What you return in :meth:`test_step` for each batch part. - - Return: - None or anything - - .. code-block:: python - - # WITHOUT test_step_end - # if used in DP or DDP2, this batch is 1/num_gpus large - def test_step(self, batch, batch_idx): - # batch is 1/num_gpus big - x, y = batch - - out = self(x) - loss = self.softmax(out) - self.log("test_loss", loss) - - - # -------------- - # with test_step_end to do softmax over the full batch - def test_step(self, batch, batch_idx): - # batch is 1/num_gpus big - x, y = batch - - out = self.encoder(x) - return out - - - def test_step_end(self, output_results): - # this out is now the full size of the batch - all_test_step_outs = output_results.out - loss = nce_loss(all_test_step_outs) - self.log("test_loss", loss) - - See Also: - See the :ref:`accelerators/gpu:Multi GPU Training` guide for more details. - """
- -
[docs] def test_epoch_end(self, outputs: Union[EPOCH_OUTPUT, List[EPOCH_OUTPUT]]) -> None: - """Called at the end of a test epoch with the output of all test steps. - - .. code-block:: python - - # the pseudocode for these calls - test_outs = [] - for test_batch in test_data: - out = test_step(test_batch) - test_outs.append(out) - test_epoch_end(test_outs) - - Args: - outputs: List of outputs you defined in :meth:`test_step_end`, or if there - are multiple dataloaders, a list containing a list of outputs for each dataloader - - Return: - None - - Note: - If you didn't define a :meth:`test_step`, this won't be called. - - Examples: - With a single dataloader: - - .. code-block:: python - - def test_epoch_end(self, outputs): - # do something with the outputs of all test batches - all_test_preds = test_step_outputs.predictions - - some_result = calc_all_results(all_test_preds) - self.log(some_result) - - With multiple dataloaders, `outputs` will be a list of lists. The outer list contains - one entry per dataloader, while the inner list contains the individual outputs of - each test step for that dataloader. - - .. code-block:: python - - def test_epoch_end(self, outputs): - final_value = 0 - for dataloader_outputs in outputs: - for test_step_out in dataloader_outputs: - # do something - final_value += test_step_out - - self.log("final_metric", final_value) - """
- -
[docs] def predict_step(self, batch: Any, batch_idx: int, dataloader_idx: int = 0) -> Any: - """Step function called during :meth:`~pytorch_lightning.trainer.trainer.Trainer.predict`. By default, it - calls :meth:`~pytorch_lightning.core.lightning.LightningModule.forward`. Override to add any processing - logic. - - The :meth:`~pytorch_lightning.core.lightning.LightningModule.predict_step` is used - to scale inference on multi-devices. - - To prevent an OOM error, it is possible to use :class:`~pytorch_lightning.callbacks.BasePredictionWriter` - callback to write the predictions to disk or database after each batch or on epoch end. - - The :class:`~pytorch_lightning.callbacks.BasePredictionWriter` should be used while using a spawn - based accelerator. This happens for ``Trainer(strategy="ddp_spawn")`` - or training on 8 TPU cores with ``Trainer(accelerator="tpu", devices=8)`` as predictions won't be returned. - - Example :: - - class MyModel(LightningModule): - - def predicts_step(self, batch, batch_idx, dataloader_idx=0): - return self(batch) - - dm = ... - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=2) - predictions = trainer.predict(model, dm) - - - Args: - batch: Current batch. - batch_idx: Index of current batch. - dataloader_idx: Index of the current dataloader. - - Return: - Predicted output - """ - return self(batch)
- -
[docs] def configure_callbacks(self) -> Union[Sequence[Callback], Callback]: - """Configure model-specific callbacks. When the model gets attached, e.g., when ``.fit()`` or ``.test()`` - gets called, the list or a callback returned here will be merged with the list of callbacks passed to the - Trainer's ``callbacks`` argument. If a callback returned here has the same type as one or several callbacks - already present in the Trainer's callbacks list, it will take priority and replace them. In addition, - Lightning will make sure :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` callbacks - run last. - - Return: - A callback or a list of callbacks which will extend the list of callbacks in the Trainer. - - Example:: - - def configure_callbacks(self): - early_stop = EarlyStopping(monitor="val_acc", mode="max") - checkpoint = ModelCheckpoint(monitor="val_loss") - return [early_stop, checkpoint] - - Note: - Certain callback methods like :meth:`~pytorch_lightning.callbacks.base.Callback.on_init_start` - will never be invoked on the new callbacks returned here. - """ - return []
- -
[docs] def configure_optimizers(self): - r""" - Choose what optimizers and learning-rate schedulers to use in your optimization. - Normally you'd need one. But in the case of GANs or similar you might have multiple. - - Return: - Any of these 6 options. - - - **Single optimizer**. - - **List or Tuple** of optimizers. - - **Two lists** - The first list has multiple optimizers, and the second has multiple LR schedulers - (or multiple ``lr_scheduler_config``). - - **Dictionary**, with an ``"optimizer"`` key, and (optionally) a ``"lr_scheduler"`` - key whose value is a single LR scheduler or ``lr_scheduler_config``. - - **Tuple of dictionaries** as described above, with an optional ``"frequency"`` key. - - **None** - Fit will run without any optimizer. - - The ``lr_scheduler_config`` is a dictionary which contains the scheduler and its associated configuration. - The default configuration is shown below. - - .. code-block:: python - - lr_scheduler_config = { - # REQUIRED: The scheduler instance - "scheduler": lr_scheduler, - # The unit of the scheduler's step size, could also be 'step'. - # 'epoch' updates the scheduler on epoch end whereas 'step' - # updates it after a optimizer update. - "interval": "epoch", - # How many epochs/steps should pass between calls to - # `scheduler.step()`. 1 corresponds to updating the learning - # rate after every epoch/step. - "frequency": 1, - # Metric to to monitor for schedulers like `ReduceLROnPlateau` - "monitor": "val_loss", - # If set to `True`, will enforce that the value specified 'monitor' - # is available when the scheduler is updated, thus stopping - # training if not found. If set to `False`, it will only produce a warning - "strict": True, - # If using the `LearningRateMonitor` callback to monitor the - # learning rate progress, this keyword can be used to specify - # a custom logged name - "name": None, - } - - When there are schedulers in which the ``.step()`` method is conditioned on a value, such as the - :class:`torch.optim.lr_scheduler.ReduceLROnPlateau` scheduler, Lightning requires that the - ``lr_scheduler_config`` contains the keyword ``"monitor"`` set to the metric name that the scheduler - should be conditioned on. - - .. testcode:: - - # The ReduceLROnPlateau scheduler requires a monitor - def configure_optimizers(self): - optimizer = Adam(...) - return { - "optimizer": optimizer, - "lr_scheduler": { - "scheduler": ReduceLROnPlateau(optimizer, ...), - "monitor": "metric_to_track", - "frequency": "indicates how often the metric is updated" - # If "monitor" references validation metrics, then "frequency" should be set to a - # multiple of "trainer.check_val_every_n_epoch". - }, - } - - - # In the case of two optimizers, only one using the ReduceLROnPlateau scheduler - def configure_optimizers(self): - optimizer1 = Adam(...) - optimizer2 = SGD(...) - scheduler1 = ReduceLROnPlateau(optimizer1, ...) - scheduler2 = LambdaLR(optimizer2, ...) - return ( - { - "optimizer": optimizer1, - "lr_scheduler": { - "scheduler": scheduler1, - "monitor": "metric_to_track", - }, - }, - {"optimizer": optimizer2, "lr_scheduler": scheduler2}, - ) - - Metrics can be made available to monitor by simply logging it using - ``self.log('metric_to_track', metric_val)`` in your :class:`~pytorch_lightning.core.lightning.LightningModule`. - - Note: - The ``frequency`` value specified in a dict along with the ``optimizer`` key is an int corresponding - to the number of sequential batches optimized with the specific optimizer. - It should be given to none or to all of the optimizers. - There is a difference between passing multiple optimizers in a list, - and passing multiple optimizers in dictionaries with a frequency of 1: - - - In the former case, all optimizers will operate on the given batch in each optimization step. - - In the latter, only one optimizer will operate on the given batch at every step. - - This is different from the ``frequency`` value specified in the ``lr_scheduler_config`` mentioned above. - - .. code-block:: python - - def configure_optimizers(self): - optimizer_one = torch.optim.SGD(self.model.parameters(), lr=0.01) - optimizer_two = torch.optim.SGD(self.model.parameters(), lr=0.01) - return [ - {"optimizer": optimizer_one, "frequency": 5}, - {"optimizer": optimizer_two, "frequency": 10}, - ] - - In this example, the first optimizer will be used for the first 5 steps, - the second optimizer for the next 10 steps and that cycle will continue. - If an LR scheduler is specified for an optimizer using the ``lr_scheduler`` key in the above dict, - the scheduler will only be updated when its optimizer is being used. - - Examples:: - - # most cases. no learning rate scheduler - def configure_optimizers(self): - return Adam(self.parameters(), lr=1e-3) - - # multiple optimizer case (e.g.: GAN) - def configure_optimizers(self): - gen_opt = Adam(self.model_gen.parameters(), lr=0.01) - dis_opt = Adam(self.model_dis.parameters(), lr=0.02) - return gen_opt, dis_opt - - # example with learning rate schedulers - def configure_optimizers(self): - gen_opt = Adam(self.model_gen.parameters(), lr=0.01) - dis_opt = Adam(self.model_dis.parameters(), lr=0.02) - dis_sch = CosineAnnealing(dis_opt, T_max=10) - return [gen_opt, dis_opt], [dis_sch] - - # example with step-based learning rate schedulers - # each optimizer has its own scheduler - def configure_optimizers(self): - gen_opt = Adam(self.model_gen.parameters(), lr=0.01) - dis_opt = Adam(self.model_dis.parameters(), lr=0.02) - gen_sch = { - 'scheduler': ExponentialLR(gen_opt, 0.99), - 'interval': 'step' # called after each training step - } - dis_sch = CosineAnnealing(dis_opt, T_max=10) # called every epoch - return [gen_opt, dis_opt], [gen_sch, dis_sch] - - # example with optimizer frequencies - # see training procedure in `Improved Training of Wasserstein GANs`, Algorithm 1 - # https://arxiv.org/abs/1704.00028 - def configure_optimizers(self): - gen_opt = Adam(self.model_gen.parameters(), lr=0.01) - dis_opt = Adam(self.model_dis.parameters(), lr=0.02) - n_critic = 5 - return ( - {'optimizer': dis_opt, 'frequency': n_critic}, - {'optimizer': gen_opt, 'frequency': 1} - ) - - Note: - Some things to know: - - - Lightning calls ``.backward()`` and ``.step()`` on each optimizer and learning rate scheduler as needed. - - If you use 16-bit precision (``precision=16``), Lightning will automatically handle the optimizers. - - If you use multiple optimizers, :meth:`training_step` will have an additional ``optimizer_idx`` parameter. - - If you use :class:`torch.optim.LBFGS`, Lightning handles the closure function automatically for you. - - If you use multiple optimizers, gradients will be calculated only for the parameters of current optimizer - at each training step. - - If you need to control how often those optimizers step or override the default ``.step()`` schedule, - override the :meth:`optimizer_step` hook. - """ - rank_zero_warn("`configure_optimizers` must be implemented to be used with the Lightning Trainer")
- -
[docs] def manual_backward(self, loss: Tensor, *args, **kwargs) -> None: - """Call this directly from your :meth:`training_step` when doing optimizations manually. By using this, - Lightning can ensure that all the proper scaling gets applied when using mixed precision. - - See :ref:`manual optimization<common/optimization:Manual optimization>` for more examples. - - Example:: - - def training_step(...): - opt = self.optimizers() - loss = ... - opt.zero_grad() - # automatically applies scaling, etc... - self.manual_backward(loss) - opt.step() - - Args: - loss: The tensor on which to compute gradients. Must have a graph attached. - *args: Additional positional arguments to be forwarded to :meth:`~torch.Tensor.backward` - **kwargs: Additional keyword arguments to be forwarded to :meth:`~torch.Tensor.backward` - """ - self._verify_is_manual_optimization("manual_backward") - self.trainer.strategy.backward(loss, None, None, *args, **kwargs)
- -
[docs] def backward( - self, loss: Tensor, optimizer: Optional[Optimizer], optimizer_idx: Optional[int], *args, **kwargs - ) -> None: - """Called to perform backward on the loss returned in :meth:`training_step`. Override this hook with your - own implementation if you need to. - - Args: - loss: The loss tensor returned by :meth:`training_step`. If gradient accumulation is used, the loss here - holds the normalized value (scaled by 1 / accumulation steps). - optimizer: Current optimizer being used. ``None`` if using manual optimization. - optimizer_idx: Index of the current optimizer being used. ``None`` if using manual optimization. - - Example:: - - def backward(self, loss, optimizer, optimizer_idx): - loss.backward() - """ - loss.backward(*args, **kwargs)
- -
[docs] def toggle_optimizer(self, optimizer: Union[Optimizer, LightningOptimizer], optimizer_idx: int) -> None: - """Makes sure only the gradients of the current optimizer's parameters are calculated in the training step - to prevent dangling gradients in multiple-optimizer setup. - - This is only called automatically when automatic optimization is enabled and multiple optimizers are used. - It works with :meth:`untoggle_optimizer` to make sure ``param_requires_grad_state`` is properly reset. - - Args: - optimizer: The optimizer to toggle. - optimizer_idx: The index of the optimizer to toggle. - """ - # Iterate over all optimizer parameters to preserve their `requires_grad` information - # in case these are pre-defined during `configure_optimizers` - param_requires_grad_state = {} - for opt in self.trainer.optimizers: - for group in opt.param_groups: - for param in group["params"]: - # If a param already appear in param_requires_grad_state, continue - if param in param_requires_grad_state: - continue - param_requires_grad_state[param] = param.requires_grad - param.requires_grad = False - - # Then iterate over the current optimizer's parameters and set its `requires_grad` - # properties accordingly - for group in optimizer.param_groups: - for param in group["params"]: - param.requires_grad = param_requires_grad_state[param] - self._param_requires_grad_state = param_requires_grad_state
- -
[docs] def untoggle_optimizer(self, optimizer_idx: int) -> None: - """Resets the state of required gradients that were toggled with :meth:`toggle_optimizer`. - - This is only called automatically when automatic optimization is enabled and multiple optimizers are used. - - Args: - optimizer_idx: The index of the optimizer to untoggle. - """ - for opt_idx, opt in enumerate(self.trainer.optimizers): - if optimizer_idx != opt_idx: - for group in opt.param_groups: - for param in group["params"]: - if param in self._param_requires_grad_state: - param.requires_grad = self._param_requires_grad_state[param] - # save memory - self._param_requires_grad_state = {}
- - def clip_gradients( - self, - optimizer: Optimizer, - gradient_clip_val: Optional[Union[int, float]] = None, - gradient_clip_algorithm: Optional[str] = None, - ): - """Handles gradient clipping internally. - - Note: - Do not override this method. If you want to customize gradient clipping, consider - using :meth:`configure_gradient_clipping` method. - - Args: - optimizer: Current optimizer being used. - gradient_clip_val: The value at which to clip gradients. - gradient_clip_algorithm: The gradient clipping algorithm to use. Pass ``gradient_clip_algorithm="value"`` - to clip by value, and ``gradient_clip_algorithm="norm"`` to clip by norm. - """ - if gradient_clip_val is None: - gradient_clip_val = self.trainer.gradient_clip_val or 0.0 - elif self.trainer.gradient_clip_val is not None and self.trainer.gradient_clip_val != gradient_clip_val: - raise MisconfigurationException( - f"You have set `Trainer(gradient_clip_val={self.trainer.gradient_clip_val!r})`" - f" and have passed `clip_gradients(gradient_clip_val={gradient_clip_val!r})`." - " Please use only one of them." - ) - - if gradient_clip_algorithm is None: - gradient_clip_algorithm = self.trainer.gradient_clip_algorithm or "norm" - else: - gradient_clip_algorithm = gradient_clip_algorithm.lower() - if ( - self.trainer.gradient_clip_algorithm is not None - and self.trainer.gradient_clip_algorithm != gradient_clip_algorithm - ): - raise MisconfigurationException( - f"You have set `Trainer(gradient_clip_algorithm={self.trainer.gradient_clip_algorithm.value!r})`" - f" and have passed `clip_gradients(gradient_clip_algorithm={gradient_clip_algorithm!r})" - " Please use only one of them." - ) - - if not isinstance(gradient_clip_val, (int, float)): - raise TypeError(f"`gradient_clip_val` should be an int or a float. Got {gradient_clip_val}.") - - if not GradClipAlgorithmType.supported_type(gradient_clip_algorithm.lower()): - raise MisconfigurationException( - f"`gradient_clip_algorithm` {gradient_clip_algorithm} is invalid." - f" Allowed algorithms: {GradClipAlgorithmType.supported_types()}." - ) - - gradient_clip_algorithm = GradClipAlgorithmType(gradient_clip_algorithm) - self.trainer.precision_plugin.clip_gradients(optimizer, gradient_clip_val, gradient_clip_algorithm) - -
[docs] def configure_gradient_clipping( - self, - optimizer: Optimizer, - optimizer_idx: int, - gradient_clip_val: Optional[Union[int, float]] = None, - gradient_clip_algorithm: Optional[str] = None, - ): - """Perform gradient clipping for the optimizer parameters. Called before :meth:`optimizer_step`. - - Args: - optimizer: Current optimizer being used. - optimizer_idx: Index of the current optimizer being used. - gradient_clip_val: The value at which to clip gradients. By default value passed in Trainer - will be available here. - gradient_clip_algorithm: The gradient clipping algorithm to use. By default value - passed in Trainer will be available here. - - Example:: - - # Perform gradient clipping on gradients associated with discriminator (optimizer_idx=1) in GAN - def configure_gradient_clipping(self, optimizer, optimizer_idx, gradient_clip_val, gradient_clip_algorithm): - if optimizer_idx == 1: - # Lightning will handle the gradient clipping - self.clip_gradients( - optimizer, - gradient_clip_val=gradient_clip_val, - gradient_clip_algorithm=gradient_clip_algorithm - ) - else: - # implement your own custom logic to clip gradients for generator (optimizer_idx=0) - """ - self.clip_gradients( - optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm - )
- - def lr_scheduler_step( - self, - scheduler: LRSchedulerTypeUnion, - optimizer_idx: int, - metric: Optional[Any], - ) -> None: - r""" - Override this method to adjust the default way the - :class:`~pytorch_lightning.trainer.trainer.Trainer` calls each scheduler. - By default, Lightning calls ``step()`` and as shown in the example - for each scheduler based on its ``interval``. - - Args: - scheduler: Learning rate scheduler. - optimizer_idx: Index of the optimizer associated with this scheduler. - metric: Value of the monitor used for schedulers like ``ReduceLROnPlateau``. - - Examples:: - - # DEFAULT - def lr_scheduler_step(self, scheduler, optimizer_idx, metric): - if metric is None: - scheduler.step() - else: - scheduler.step(metric) - - # Alternative way to update schedulers if it requires an epoch value - def lr_scheduler_step(self, scheduler, optimizer_idx, metric): - scheduler.step(epoch=self.current_epoch) - - """ - if metric is None: - scheduler.step() - else: - scheduler.step(metric) - -
[docs] def optimizer_step( - self, - epoch: int, - batch_idx: int, - optimizer: Union[Optimizer, LightningOptimizer], - optimizer_idx: int = 0, - optimizer_closure: Optional[Callable[[], Any]] = None, - on_tpu: bool = False, - using_native_amp: bool = False, - using_lbfgs: bool = False, - ) -> None: - r""" - Override this method to adjust the default way the :class:`~pytorch_lightning.trainer.trainer.Trainer` calls - each optimizer. - - By default, Lightning calls ``step()`` and ``zero_grad()`` as shown in the example once per optimizer. - This method (and ``zero_grad()``) won't be called during the accumulation phase when - ``Trainer(accumulate_grad_batches != 1)``. Overriding this hook has no benefit with manual optimization. - - Args: - epoch: Current epoch - batch_idx: Index of current batch - optimizer: A PyTorch optimizer - optimizer_idx: If you used multiple optimizers, this indexes into that list. - optimizer_closure: The optimizer closure. This closure must be executed as it includes the - calls to ``training_step()``, ``optimizer.zero_grad()``, and ``backward()``. - on_tpu: ``True`` if TPU backward is required - using_native_amp: ``True`` if using native amp - using_lbfgs: True if the matching optimizer is :class:`torch.optim.LBFGS` - - Examples:: - - # DEFAULT - def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_idx, - optimizer_closure, on_tpu, using_native_amp, using_lbfgs): - optimizer.step(closure=optimizer_closure) - - # Alternating schedule for optimizer steps (i.e.: GANs) - def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_idx, - optimizer_closure, on_tpu, using_native_amp, using_lbfgs): - # update generator opt every step - if optimizer_idx == 0: - optimizer.step(closure=optimizer_closure) - - # update discriminator opt every 2 steps - if optimizer_idx == 1: - if (batch_idx + 1) % 2 == 0 : - optimizer.step(closure=optimizer_closure) - else: - # call the closure by itself to run `training_step` + `backward` without an optimizer step - optimizer_closure() - - # ... - # add as many optimizers as you want - - Here's another example showing how to use this for more advanced things such as - learning rate warm-up: - - .. code-block:: python - - # learning rate warm-up - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu, - using_native_amp, - using_lbfgs, - ): - # update params - optimizer.step(closure=optimizer_closure) - - # manually warm up lr without a scheduler - if self.trainer.global_step < 500: - lr_scale = min(1.0, float(self.trainer.global_step + 1) / 500.0) - for pg in optimizer.param_groups: - pg["lr"] = lr_scale * self.learning_rate - - """ - optimizer.step(closure=optimizer_closure)
- -
[docs] def optimizer_zero_grad(self, epoch: int, batch_idx: int, optimizer: Optimizer, optimizer_idx: int): - """Override this method to change the default behaviour of ``optimizer.zero_grad()``. - - Args: - epoch: Current epoch - batch_idx: Index of current batch - optimizer: A PyTorch optimizer - optimizer_idx: If you used multiple optimizers this indexes into that list. - - Examples:: - - # DEFAULT - def optimizer_zero_grad(self, epoch, batch_idx, optimizer, optimizer_idx): - optimizer.zero_grad() - - # Set gradients to `None` instead of zero to improve performance. - def optimizer_zero_grad(self, epoch, batch_idx, optimizer, optimizer_idx): - optimizer.zero_grad(set_to_none=True) - - See :meth:`torch.optim.Optimizer.zero_grad` for the explanation of the above example. - """ - optimizer.zero_grad()
- -
[docs] def tbptt_split_batch(self, batch: Any, split_size: int) -> List[Any]: - r""" - When using truncated backpropagation through time, each batch must be split along the - time dimension. Lightning handles this by default, but for custom behavior override - this function. - - Args: - batch: Current batch - split_size: The size of the split - - Return: - List of batch splits. Each split will be passed to :meth:`training_step` to enable truncated - back propagation through time. The default implementation splits root level Tensors and - Sequences at dim=1 (i.e. time dim). It assumes that each time dim is the same length. - - Examples:: - - def tbptt_split_batch(self, batch, split_size): - splits = [] - for t in range(0, time_dims[0], split_size): - batch_split = [] - for i, x in enumerate(batch): - if isinstance(x, torch.Tensor): - split_x = x[:, t:t + split_size] - elif isinstance(x, collections.Sequence): - split_x = [None] * len(x) - for batch_idx in range(len(x)): - split_x[batch_idx] = x[batch_idx][t:t + split_size] - batch_split.append(split_x) - splits.append(batch_split) - return splits - - Note: - Called in the training loop after - :meth:`~pytorch_lightning.callbacks.base.Callback.on_train_batch_start` - if :paramref:`~pytorch_lightning.core.lightning.LightningModule.truncated_bptt_steps` > 0. - Each returned batch split is passed separately to :meth:`training_step`. - """ - time_dims = [len(x[0]) for x in batch if isinstance(x, (torch.Tensor, collections.Sequence))] - assert len(time_dims) >= 1, "Unable to determine batch time dimension" - assert all(x == time_dims[0] for x in time_dims), "Batch time dimension length is ambiguous" - - splits = [] - for t in range(0, time_dims[0], split_size): - batch_split = [] - for i, x in enumerate(batch): - if isinstance(x, torch.Tensor): - split_x = x[:, t : t + split_size] - elif isinstance(x, collections.Sequence): - split_x = [None] * len(x) - for batch_idx in range(len(x)): - split_x[batch_idx] = x[batch_idx][t : t + split_size] - - batch_split.append(split_x) - - splits.append(batch_split) - - return splits
- - def summarize(self, max_depth: int = 1) -> ModelSummary: - """Summarize this LightningModule. - - .. deprecated:: v1.5 - This method was deprecated in v1.5 in favor of `pytorch_lightning.utilities.model_summary.summarize` - and will be removed in v1.7. - - Args: - max_depth: The maximum depth of layer nesting that the summary will include. A value of 0 turns the - layer summary off. Default: 1. - - Return: - The model summary object - """ - rank_zero_deprecation( - "The `LightningModule.summarize` method is deprecated in v1.5 and will be removed in v1.7. " - "Use `pytorch_lightning.utilities.model_summary.summarize` instead.", - stacklevel=6, - ) - - return summarize(self, max_depth) - -
[docs] def freeze(self) -> None: - r""" - Freeze all params for inference. - - Example:: - - model = MyLightningModule(...) - model.freeze() - - """ - for param in self.parameters(): - param.requires_grad = False - - self.eval()
- -
[docs] def unfreeze(self) -> None: - """Unfreeze all parameters for training. - - .. code-block:: python - - model = MyLightningModule(...) - model.unfreeze() - """ - for param in self.parameters(): - param.requires_grad = True - - self.train()
- - def get_progress_bar_dict(self) -> Dict[str, Union[int, str]]: - r""" - .. deprecated:: v1.5 - This method was deprecated in v1.5 in favor of - `pytorch_lightning.callbacks.progress.base.get_metrics` and will be removed in v1.7. - - Implement this to override the default items displayed in the progress bar. - By default it includes the average loss value, split index of BPTT (if used) - and the version of the experiment when using a logger. - - .. code-block:: - - Epoch 1: 4%|▎ | 40/1095 [00:03<01:37, 10.84it/s, loss=4.501, v_num=10] - - Here is an example how to override the defaults: - - .. code-block:: python - - def get_progress_bar_dict(self): - # don't show the version number - items = super().get_progress_bar_dict() - items.pop("v_num", None) - return items - - Return: - Dictionary with the items to be displayed in the progress bar. - """ - return progress_base.get_standard_metrics(self.trainer, self) - - def _verify_is_manual_optimization(self, fn_name): - if self.automatic_optimization: - raise MisconfigurationException( - f"to use {fn_name}, please disable automatic optimization:" - " set model property `automatic_optimization` as False" - ) - - @classmethod - def _auto_collect_arguments(cls, frame=None) -> Tuple[Dict, Dict]: - """Collect all module arguments in the current constructor and all child constructors. The child - constructors are all the ``__init__`` methods that reach the current class through (chained) - ``super().__init__()`` calls. - - Args: - frame: instance frame - - Returns: - self_arguments: arguments dictionary of the first instance - parents_arguments: arguments dictionary of the parent's instances - """ - if not frame: - frame = inspect.currentframe() - - frame_args = collect_init_args(frame.f_back, []) - self_arguments = frame_args[-1] - - # set hyper_parameters in child - self_arguments = self_arguments - parents_arguments = {} - - # add all arguments from parents - for args in frame_args[:-1]: - parents_arguments.update(args) - return self_arguments, parents_arguments - -
[docs] @torch.no_grad() - def to_onnx(self, file_path: Union[str, Path], input_sample: Optional[Any] = None, **kwargs): - """Saves the model in ONNX format. - - Args: - file_path: The path of the file the onnx model should be saved to. - input_sample: An input for tracing. Default: None (Use self.example_input_array) - **kwargs: Will be passed to torch.onnx.export function. - - Example: - >>> class SimpleModel(LightningModule): - ... def __init__(self): - ... super().__init__() - ... self.l1 = torch.nn.Linear(in_features=64, out_features=4) - ... - ... def forward(self, x): - ... return torch.relu(self.l1(x.view(x.size(0), -1))) - - >>> with tempfile.NamedTemporaryFile(suffix='.onnx', delete=False) as tmpfile: - ... model = SimpleModel() - ... input_sample = torch.randn((1, 64)) - ... model.to_onnx(tmpfile.name, input_sample, export_params=True) - ... os.path.isfile(tmpfile.name) - True - """ - mode = self.training - - if input_sample is None: - if self.example_input_array is None: - raise ValueError( - "Could not export to ONNX since neither `input_sample` nor" - " `model.example_input_array` attribute is set." - ) - input_sample = self.example_input_array - - input_sample = self._apply_batch_transfer_handler(input_sample) - - if not _TORCH_GREATER_EQUAL_1_10 and "example_outputs" not in kwargs: - self.eval() - if isinstance(input_sample, Tuple): - kwargs["example_outputs"] = self(*input_sample) - else: - kwargs["example_outputs"] = self(input_sample) - - torch.onnx.export(self, input_sample, file_path, **kwargs) - self.train(mode)
- -
[docs] @torch.no_grad() - def to_torchscript( - self, - file_path: Optional[Union[str, Path]] = None, - method: Optional[str] = "script", - example_inputs: Optional[Any] = None, - **kwargs, - ) -> Union[ScriptModule, Dict[str, ScriptModule]]: - """By default compiles the whole model to a :class:`~torch.jit.ScriptModule`. If you want to use tracing, - please provided the argument ``method='trace'`` and make sure that either the `example_inputs` argument is - provided, or the model has :attr:`example_input_array` set. If you would like to customize the modules that - are scripted you should override this method. In case you want to return multiple modules, we recommend - using a dictionary. - - Args: - file_path: Path where to save the torchscript. Default: None (no file saved). - method: Whether to use TorchScript's script or trace method. Default: 'script' - example_inputs: An input to be used to do tracing when method is set to 'trace'. - Default: None (uses :attr:`example_input_array`) - **kwargs: Additional arguments that will be passed to the :func:`torch.jit.script` or - :func:`torch.jit.trace` function. - - Note: - - Requires the implementation of the - :meth:`~pytorch_lightning.core.lightning.LightningModule.forward` method. - - The exported script will be set to evaluation mode. - - It is recommended that you install the latest supported version of PyTorch - to use this feature without limitations. See also the :mod:`torch.jit` - documentation for supported features. - - Example: - >>> class SimpleModel(LightningModule): - ... def __init__(self): - ... super().__init__() - ... self.l1 = torch.nn.Linear(in_features=64, out_features=4) - ... - ... def forward(self, x): - ... return torch.relu(self.l1(x.view(x.size(0), -1))) - ... - >>> model = SimpleModel() - >>> model.to_torchscript(file_path="model.pt") # doctest: +SKIP - >>> os.path.isfile("model.pt") # doctest: +SKIP - >>> torch.jit.save(model.to_torchscript(file_path="model_trace.pt", method='trace', # doctest: +SKIP - ... example_inputs=torch.randn(1, 64))) # doctest: +SKIP - >>> os.path.isfile("model_trace.pt") # doctest: +SKIP - True - - Return: - This LightningModule as a torchscript, regardless of whether `file_path` is - defined or not. - """ - mode = self.training - - self._running_torchscript = True - - if method == "script": - torchscript_module = torch.jit.script(self.eval(), **kwargs) - elif method == "trace": - # if no example inputs are provided, try to see if model has example_input_array set - if example_inputs is None: - if self.example_input_array is None: - raise ValueError( - "Choosing method=`trace` requires either `example_inputs`" - " or `model.example_input_array` to be defined." - ) - example_inputs = self.example_input_array - - # automatically send example inputs to the right device and use trace - example_inputs = self._apply_batch_transfer_handler(example_inputs) - torchscript_module = torch.jit.trace(func=self.eval(), example_inputs=example_inputs, **kwargs) - else: - raise ValueError(f"The 'method' parameter only supports 'script' or 'trace', but value given was: {method}") - - self.train(mode) - - if file_path is not None: - fs = get_filesystem(file_path) - with fs.open(file_path, "wb") as f: - torch.jit.save(torchscript_module, f) - - self._running_torchscript = False - - return torchscript_module
- - @property - def model_size(self) -> float: - """Returns the model size in MegaBytes (MB) - - Note: - This property will not return correct value for Deepspeed (stage 3) and fully-sharded training. - """ - if not self._running_torchscript: # remove with the deprecation removal - rank_zero_deprecation( - "The `LightningModule.model_size` property was deprecated in v1.5 and will be removed in v1.7." - " Please use the `pytorch_lightning.utilities.memory.get_model_size_mb`.", - stacklevel=5, - ) - return get_model_size_mb(self) - - @property - def use_amp(self) -> bool: - r""" - .. deprecated:: v1.6. - - This property was deprecated in v1.6 and will be removed in v1.8. - """ - if not self._running_torchscript: # remove with the deprecation removal - rank_zero_deprecation( - "`LightningModule.use_amp` was deprecated in v1.6 and will be removed in v1.8." - " Please use `Trainer.amp_backend`.", - stacklevel=5, - ) - return self._use_amp - - @use_amp.setter - def use_amp(self, use_amp: bool) -> None: - r""" - .. deprecated:: v1.6. - - This property was deprecated in v1.6 and will be removed in v1.8. - """ - if not self._running_torchscript: # remove with the deprecation removal - rank_zero_deprecation( - "`LightningModule.use_amp` was deprecated in v1.6 and will be removed in v1.8." - " Please use `Trainer.amp_backend`.", - stacklevel=5, - ) - self._use_amp = use_amp - -
[docs] def add_to_queue(self, queue: pl.strategies.launchers.spawn._FakeQueue) -> None: - """Appends the :attr:`trainer.callback_metrics` dictionary to the given queue. To avoid issues with memory - sharing, we cast the data to numpy. - - Args: - queue: the instance of the queue to append the data. - - .. deprecated:: v1.5 - This method was deprecated in v1.5 and will be removed in v1.7. - """
- -
[docs] def get_from_queue(self, queue: pl.strategies.launchers.spawn._FakeQueue) -> None: - """Retrieve the :attr:`trainer.callback_metrics` dictionary from the given queue. To preserve consistency, - we cast back the data to ``torch.Tensor``. - - Args: - queue: the instance of the queue from where to get the data. - - .. deprecated:: v1.5 - This method was deprecated in v1.5 and will be removed in v1.7. - """
- - @contextmanager - def _prevent_trainer_and_dataloaders_deepcopy(self) -> None: - self._should_prevent_trainer_and_dataloaders_deepcopy = True - yield - self._should_prevent_trainer_and_dataloaders_deepcopy = False - - def __getstate__(self) -> Dict[str, Any]: - state = dict(self.__dict__) - if self._should_prevent_trainer_and_dataloaders_deepcopy: - state["trainer"] = None - state.pop("train_dataloader", None) - state.pop("val_dataloader", None) - state.pop("test_dataloader", None) - state.pop("predict_dataloader", None) - return state - - def _register_sharded_tensor_state_dict_hooks_if_available(self) -> None: - """Adds ShardedTensor state dict hooks if ShardedTensors are supported. - - These hooks ensure that ShardedTensors are included when saving, and are loaded the LightningModule correctly. - """ - if not _TORCH_GREATER_EQUAL_1_10 or _IS_WINDOWS or not torch.distributed.is_available(): - rank_zero_debug("Could not register sharded tensor state dict hooks") - return - - from torch.distributed._sharded_tensor import pre_load_state_dict_hook, state_dict_hook - - self._register_state_dict_hook(state_dict_hook) - self._register_load_state_dict_pre_hook(pre_load_state_dict_hook, True) -
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/loggers/comet.html b/docs/_modules/pytorch_lightning/loggers/comet.html deleted file mode 100644 index c13671c..0000000 --- a/docs/_modules/pytorch_lightning/loggers/comet.html +++ /dev/null @@ -1,1026 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.loggers.comet — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.loggers.comet

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-Comet Logger
-------------
-"""
-
-import logging
-import os
-from argparse import Namespace
-from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Union
-
-import torch
-from torch import is_tensor
-
-import pytorch_lightning as pl
-from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_experiment
-from pytorch_lightning.utilities.exceptions import MisconfigurationException
-from pytorch_lightning.utilities.imports import _module_available
-from pytorch_lightning.utilities.logger import _add_prefix, _convert_params, _flatten_dict
-from pytorch_lightning.utilities.rank_zero import rank_zero_only
-
-log = logging.getLogger(__name__)
-_COMET_AVAILABLE = _module_available("comet_ml")
-
-if _COMET_AVAILABLE:
-    import comet_ml
-    from comet_ml import ExistingExperiment as CometExistingExperiment
-    from comet_ml import Experiment as CometExperiment
-    from comet_ml import OfflineExperiment as CometOfflineExperiment
-
-    try:
-        from comet_ml.api import API
-    except ModuleNotFoundError:  # pragma: no-cover
-        # For more information, see: https://www.comet.ml/docs/python-sdk/releases/#release-300
-        from comet_ml.papi import API  # pragma: no-cover
-else:
-    # needed for test mocks, these tests shall be updated
-    comet_ml = None
-    CometExperiment, CometExistingExperiment, CometOfflineExperiment = None, None, None
-    API = None
-
-
-
[docs]class CometLogger(LightningLoggerBase): - r""" - Log using `Comet.ml <https://www.comet.ml>`_. - - Install it with pip: - - .. code-block:: bash - - pip install comet-ml - - Comet requires either an API Key (online mode) or a local directory path (offline mode). - - **ONLINE MODE** - - .. code-block:: python - - import os - from pytorch_lightning import Trainer - from pytorch_lightning.loggers import CometLogger - - # arguments made to CometLogger are passed on to the comet_ml.Experiment class - comet_logger = CometLogger( - api_key=os.environ.get("COMET_API_KEY"), - workspace=os.environ.get("COMET_WORKSPACE"), # Optional - save_dir=".", # Optional - project_name="default_project", # Optional - rest_api_key=os.environ.get("COMET_REST_API_KEY"), # Optional - experiment_key=os.environ.get("COMET_EXPERIMENT_KEY"), # Optional - experiment_name="lightning_logs", # Optional - ) - trainer = Trainer(logger=comet_logger) - - **OFFLINE MODE** - - .. code-block:: python - - from pytorch_lightning.loggers import CometLogger - - # arguments made to CometLogger are passed on to the comet_ml.Experiment class - comet_logger = CometLogger( - save_dir=".", - workspace=os.environ.get("COMET_WORKSPACE"), # Optional - project_name="default_project", # Optional - rest_api_key=os.environ.get("COMET_REST_API_KEY"), # Optional - experiment_name="lightning_logs", # Optional - ) - trainer = Trainer(logger=comet_logger) - - Args: - api_key: Required in online mode. API key, found on Comet.ml. If not given, this - will be loaded from the environment variable COMET_API_KEY or ~/.comet.config - if either exists. - save_dir: Required in offline mode. The path for the directory to save local - comet logs. If given, this also sets the directory for saving checkpoints. - project_name: Optional. Send your experiment to a specific project. - Otherwise will be sent to Uncategorized Experiments. - If the project name does not already exist, Comet.ml will create a new project. - rest_api_key: Optional. Rest API key found in Comet.ml settings. - This is used to determine version number - experiment_name: Optional. String representing the name for this particular experiment on Comet.ml. - experiment_key: Optional. If set, restores from existing experiment. - offline: If api_key and save_dir are both given, this determines whether - the experiment will be in online or offline mode. This is useful if you use - save_dir to control the checkpoints directory and have a ~/.comet.config - file but still want to run offline experiments. - prefix: A string to put at the beginning of metric keys. - \**kwargs: Additional arguments like `workspace`, `log_code`, etc. used by - :class:`CometExperiment` can be passed as keyword arguments in this logger. - - Raises: - ModuleNotFoundError: - If required Comet package is not installed on the device. - MisconfigurationException: - If neither ``api_key`` nor ``save_dir`` are passed as arguments. - """ - - LOGGER_JOIN_CHAR = "-" - - def __init__( - self, - api_key: Optional[str] = None, - save_dir: Optional[str] = None, - project_name: Optional[str] = None, - rest_api_key: Optional[str] = None, - experiment_name: Optional[str] = None, - experiment_key: Optional[str] = None, - offline: bool = False, - prefix: str = "", - agg_key_funcs: Optional[Mapping[str, Callable[[Sequence[float]], float]]] = None, - agg_default_func: Optional[Callable[[Sequence[float]], float]] = None, - **kwargs, - ): - if comet_ml is None: - raise ModuleNotFoundError( - "You want to use `comet_ml` logger which is not installed yet, install it with `pip install comet-ml`." - ) - super().__init__(agg_key_funcs=agg_key_funcs, agg_default_func=agg_default_func) - self._experiment = None - - # Determine online or offline mode based on which arguments were passed to CometLogger - api_key = api_key or comet_ml.config.get_api_key(None, comet_ml.config.get_config()) - - if api_key is not None and save_dir is not None: - self.mode = "offline" if offline else "online" - self.api_key = api_key - self._save_dir = save_dir - elif api_key is not None: - self.mode = "online" - self.api_key = api_key - self._save_dir = None - elif save_dir is not None: - self.mode = "offline" - self._save_dir = save_dir - else: - # If neither api_key nor save_dir are passed as arguments, raise an exception - raise MisconfigurationException("CometLogger requires either api_key or save_dir during initialization.") - - log.info(f"CometLogger will be initialized in {self.mode} mode") - - self._project_name = project_name - self._experiment_key = experiment_key - self._experiment_name = experiment_name - self._prefix = prefix - self._kwargs = kwargs - self._future_experiment_key = None - - if rest_api_key is not None: - # Comet.ml rest API, used to determine version number - self.rest_api_key = rest_api_key - self.comet_api = API(self.rest_api_key) - else: - self.rest_api_key = None - self.comet_api = None - - self._kwargs = kwargs - - @property - @rank_zero_experiment - def experiment(self) -> Union[CometExperiment, CometExistingExperiment, CometOfflineExperiment]: - r""" - Actual Comet object. To use Comet features in your - :class:`~pytorch_lightning.core.lightning.LightningModule` do the following. - - Example:: - - self.logger.experiment.some_comet_function() - - """ - if self._experiment is not None: - return self._experiment - - if self._future_experiment_key is not None: - os.environ["COMET_EXPERIMENT_KEY"] = self._future_experiment_key - - try: - if self.mode == "online": - if self._experiment_key is None: - self._experiment = CometExperiment( - api_key=self.api_key, project_name=self._project_name, **self._kwargs - ) - self._experiment_key = self._experiment.get_key() - else: - self._experiment = CometExistingExperiment( - api_key=self.api_key, - project_name=self._project_name, - previous_experiment=self._experiment_key, - **self._kwargs, - ) - else: - self._experiment = CometOfflineExperiment( - offline_directory=self.save_dir, project_name=self._project_name, **self._kwargs - ) - finally: - if self._future_experiment_key is not None: - os.environ.pop("COMET_EXPERIMENT_KEY") - self._future_experiment_key = None - - if self._experiment_name: - self._experiment.set_name(self._experiment_name) - - return self._experiment - -
[docs] @rank_zero_only - def log_hyperparams(self, params: Union[Dict[str, Any], Namespace]) -> None: - params = _convert_params(params) - params = _flatten_dict(params) - self.experiment.log_parameters(params)
- -
[docs] @rank_zero_only - def log_metrics(self, metrics: Dict[str, Union[torch.Tensor, float]], step: Optional[int] = None) -> None: - assert rank_zero_only.rank == 0, "experiment tried to log from global_rank != 0" - # Comet.ml expects metrics to be a dictionary of detached tensors on CPU - metrics_without_epoch = metrics.copy() - for key, val in metrics_without_epoch.items(): - if is_tensor(val): - metrics_without_epoch[key] = val.cpu().detach() - - epoch = metrics_without_epoch.pop("epoch", None) - metrics_without_epoch = _add_prefix(metrics_without_epoch, self._prefix, self.LOGGER_JOIN_CHAR) - self.experiment.log_metrics(metrics_without_epoch, step=step, epoch=epoch)
- - def reset_experiment(self): - self._experiment = None - -
[docs] @rank_zero_only - def finalize(self, status: str) -> None: - r""" - When calling ``self.experiment.end()``, that experiment won't log any more data to Comet. - That's why, if you need to log any more data, you need to create an ExistingCometExperiment. - For example, to log data when testing your model after training, because when training is - finalized :meth:`CometLogger.finalize` is called. - - This happens automatically in the :meth:`~CometLogger.experiment` property, when - ``self._experiment`` is set to ``None``, i.e. ``self.reset_experiment()``. - """ - self.experiment.end() - self.reset_experiment()
- - @property - def save_dir(self) -> Optional[str]: - """Gets the save directory. - - Returns: - The path to the save directory. - """ - return self._save_dir - - @property - def name(self) -> str: - """Gets the project name. - - Returns: - The project name if it is specified, else "comet-default". - """ - # Don't create an experiment if we don't have one - if self._experiment is not None and self._experiment.project_name is not None: - return self._experiment.project_name - - if self._project_name is not None: - return self._project_name - - return "comet-default" - - @property - def version(self) -> str: - """Gets the version. - - Returns: - The first one of the following that is set in the following order - - 1. experiment id. - 2. experiment key. - 3. "COMET_EXPERIMENT_KEY" environment variable. - 4. future experiment key. - - If none are present generates a new guid. - """ - # Don't create an experiment if we don't have one - if self._experiment is not None: - return self._experiment.id - - if self._experiment_key is not None: - return self._experiment_key - - if "COMET_EXPERIMENT_KEY" in os.environ: - return os.environ["COMET_EXPERIMENT_KEY"] - - if self._future_experiment_key is not None: - return self._future_experiment_key - - # Pre-generate an experiment key - self._future_experiment_key = comet_ml.generate_guid() - - return self._future_experiment_key - - def __getstate__(self): - state = self.__dict__.copy() - - # Save the experiment id in case an experiment object already exists, - # this way we could create an ExistingExperiment pointing to the same - # experiment - state["_experiment_key"] = self._experiment.id if self._experiment is not None else None - - # Remove the experiment object as it contains hard to pickle objects - # (like network connections), the experiment object will be recreated if - # needed later - state["_experiment"] = None - return state - -
[docs] def log_graph(self, model: "pl.LightningModule", input_array=None) -> None: - if self._experiment is not None: - self._experiment.set_model_graph(model)
-
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/loggers/csv_logs.html b/docs/_modules/pytorch_lightning/loggers/csv_logs.html deleted file mode 100644 index 8d742fa..0000000 --- a/docs/_modules/pytorch_lightning/loggers/csv_logs.html +++ /dev/null @@ -1,928 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.loggers.csv_logs — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.loggers.csv_logs

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-CSV logger
-----------
-
-CSV logger for basic experiment logging that does not require opening ports
-
-"""
-import csv
-import logging
-import os
-from argparse import Namespace
-from typing import Any, Dict, Optional, Union
-
-import torch
-
-from pytorch_lightning.core.saving import save_hparams_to_yaml
-from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_experiment
-from pytorch_lightning.utilities.logger import _add_prefix, _convert_params
-from pytorch_lightning.utilities.rank_zero import rank_zero_only, rank_zero_warn
-
-log = logging.getLogger(__name__)
-
-
-class ExperimentWriter:
-    r"""
-    Experiment writer for CSVLogger.
-
-    Currently supports to log hyperparameters and metrics in YAML and CSV
-    format, respectively.
-
-    Args:
-        log_dir: Directory for the experiment logs
-    """
-
-    NAME_HPARAMS_FILE = "hparams.yaml"
-    NAME_METRICS_FILE = "metrics.csv"
-
-    def __init__(self, log_dir: str) -> None:
-        self.hparams = {}
-        self.metrics = []
-
-        self.log_dir = log_dir
-        if os.path.exists(self.log_dir) and os.listdir(self.log_dir):
-            rank_zero_warn(
-                f"Experiment logs directory {self.log_dir} exists and is not empty."
-                " Previous log files in this directory will be deleted when the new ones are saved!"
-            )
-        os.makedirs(self.log_dir, exist_ok=True)
-
-        self.metrics_file_path = os.path.join(self.log_dir, self.NAME_METRICS_FILE)
-
-    def log_hparams(self, params: Dict[str, Any]) -> None:
-        """Record hparams."""
-        self.hparams.update(params)
-
-    def log_metrics(self, metrics_dict: Dict[str, float], step: Optional[int] = None) -> None:
-        """Record metrics."""
-
-        def _handle_value(value):
-            if isinstance(value, torch.Tensor):
-                return value.item()
-            return value
-
-        if step is None:
-            step = len(self.metrics)
-
-        metrics = {k: _handle_value(v) for k, v in metrics_dict.items()}
-        metrics["step"] = step
-        self.metrics.append(metrics)
-
-    def save(self) -> None:
-        """Save recorded hparams and metrics into files."""
-        hparams_file = os.path.join(self.log_dir, self.NAME_HPARAMS_FILE)
-        save_hparams_to_yaml(hparams_file, self.hparams)
-
-        if not self.metrics:
-            return
-
-        last_m = {}
-        for m in self.metrics:
-            last_m.update(m)
-        metrics_keys = list(last_m.keys())
-
-        with open(self.metrics_file_path, "w", newline="") as f:
-            writer = csv.DictWriter(f, fieldnames=metrics_keys)
-            writer.writeheader()
-            writer.writerows(self.metrics)
-
-
-
[docs]class CSVLogger(LightningLoggerBase): - r""" - Log to local file system in yaml and CSV format. - - Logs are saved to ``os.path.join(save_dir, name, version)``. - - Example: - >>> from pytorch_lightning import Trainer - >>> from pytorch_lightning.loggers import CSVLogger - >>> logger = CSVLogger("logs", name="my_exp_name") - >>> trainer = Trainer(logger=logger) - - Args: - save_dir: Save directory - name: Experiment name. Defaults to ``'default'``. - version: Experiment version. If version is not specified the logger inspects the save - directory for existing versions, then automatically assigns the next available version. - prefix: A string to put at the beginning of metric keys. - flush_logs_every_n_steps: How often to flush logs to disk (defaults to every 100 steps). - """ - - LOGGER_JOIN_CHAR = "-" - - def __init__( - self, - save_dir: str, - name: Optional[str] = "lightning_logs", - version: Optional[Union[int, str]] = None, - prefix: str = "", - flush_logs_every_n_steps: int = 100, - ): - super().__init__() - self._save_dir = save_dir - self._name = name or "" - self._version = version - self._prefix = prefix - self._experiment = None - self._flush_logs_every_n_steps = flush_logs_every_n_steps - - @property - def root_dir(self) -> str: - """Parent directory for all checkpoint subdirectories. - - If the experiment name parameter is an empty string, no experiment subdirectory is used and the checkpoint will - be saved in "save_dir/version" - """ - return os.path.join(self.save_dir, self.name) - - @property - def log_dir(self) -> str: - """The log directory for this run. - - By default, it is named ``'version_${self.version}'`` but it can be overridden by passing a string value for the - constructor's version parameter instead of ``None`` or an int. - """ - # create a pseudo standard path - version = self.version if isinstance(self.version, str) else f"version_{self.version}" - log_dir = os.path.join(self.root_dir, version) - return log_dir - - @property - def save_dir(self) -> Optional[str]: - """The current directory where logs are saved. - - Returns: - The path to current directory where logs are saved. - """ - return self._save_dir - - @property - @rank_zero_experiment - def experiment(self) -> ExperimentWriter: - r""" - - Actual ExperimentWriter object. To use ExperimentWriter features in your - :class:`~pytorch_lightning.core.lightning.LightningModule` do the following. - - Example:: - - self.logger.experiment.some_experiment_writer_function() - - """ - if self._experiment: - return self._experiment - - os.makedirs(self.root_dir, exist_ok=True) - self._experiment = ExperimentWriter(log_dir=self.log_dir) - return self._experiment - -
[docs] @rank_zero_only - def log_hyperparams(self, params: Union[Dict[str, Any], Namespace]) -> None: - params = _convert_params(params) - self.experiment.log_hparams(params)
- -
[docs] @rank_zero_only - def log_metrics(self, metrics: Dict[str, float], step: Optional[int] = None) -> None: - metrics = _add_prefix(metrics, self._prefix, self.LOGGER_JOIN_CHAR) - self.experiment.log_metrics(metrics, step) - if step is not None and (step + 1) % self._flush_logs_every_n_steps == 0: - self.save()
- -
[docs] @rank_zero_only - def save(self) -> None: - super().save() - self.experiment.save()
- -
[docs] @rank_zero_only - def finalize(self, status: str) -> None: - self.save()
- - @property - def name(self) -> str: - """Gets the name of the experiment. - - Returns: - The name of the experiment. - """ - return self._name - - @property - def version(self) -> int: - """Gets the version of the experiment. - - Returns: - The version of the experiment if it is specified, else the next version. - """ - if self._version is None: - self._version = self._get_next_version() - return self._version - - def _get_next_version(self): - root_dir = self.root_dir - - if not os.path.isdir(root_dir): - log.warning("Missing logger folder: %s", root_dir) - return 0 - - existing_versions = [] - for d in os.listdir(root_dir): - if os.path.isdir(os.path.join(root_dir, d)) and d.startswith("version_"): - existing_versions.append(int(d.split("_")[1])) - - if len(existing_versions) == 0: - return 0 - - return max(existing_versions) + 1
-
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/loggers/mlflow.html b/docs/_modules/pytorch_lightning/loggers/mlflow.html deleted file mode 100644 index d22932b..0000000 --- a/docs/_modules/pytorch_lightning/loggers/mlflow.html +++ /dev/null @@ -1,959 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.loggers.mlflow — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.loggers.mlflow

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-MLflow Logger
--------------
-"""
-import logging
-import os
-import re
-from argparse import Namespace
-from time import time
-from typing import Any, Dict, Optional, Union
-
-from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_experiment
-from pytorch_lightning.utilities.imports import _module_available
-from pytorch_lightning.utilities.logger import _add_prefix, _convert_params, _flatten_dict
-from pytorch_lightning.utilities.rank_zero import rank_zero_only, rank_zero_warn
-
-log = logging.getLogger(__name__)
-LOCAL_FILE_URI_PREFIX = "file:"
-_MLFLOW_AVAILABLE = _module_available("mlflow")
-try:
-    import mlflow
-    from mlflow.tracking import context, MlflowClient
-    from mlflow.utils.mlflow_tags import MLFLOW_RUN_NAME
-# todo: there seems to be still some remaining import error with Conda env
-except ModuleNotFoundError:
-    _MLFLOW_AVAILABLE = False
-    mlflow, MlflowClient, context = None, None, None
-    MLFLOW_RUN_NAME = "mlflow.runName"
-
-# before v1.1.0
-if hasattr(context, "resolve_tags"):
-    from mlflow.tracking.context import resolve_tags
-
-
-# since v1.1.0
-elif hasattr(context, "registry"):
-    from mlflow.tracking.context.registry import resolve_tags
-else:
-
-    def resolve_tags(tags=None):
-        return tags
-
-
-
[docs]class MLFlowLogger(LightningLoggerBase): - """Log using `MLflow <https://mlflow.org>`_. - - Install it with pip: - - .. code-block:: bash - - pip install mlflow - - .. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.loggers import MLFlowLogger - - mlf_logger = MLFlowLogger(experiment_name="lightning_logs", tracking_uri="file:./ml-runs") - trainer = Trainer(logger=mlf_logger) - - Use the logger anywhere in your :class:`~pytorch_lightning.core.lightning.LightningModule` as follows: - - .. code-block:: python - - from pytorch_lightning import LightningModule - - - class LitModel(LightningModule): - def training_step(self, batch, batch_idx): - # example - self.logger.experiment.whatever_ml_flow_supports(...) - - def any_lightning_module_function_or_hook(self): - self.logger.experiment.whatever_ml_flow_supports(...) - - Args: - experiment_name: The name of the experiment. - run_name: Name of the new run. The `run_name` is internally stored as a ``mlflow.runName`` tag. - If the ``mlflow.runName`` tag has already been set in `tags`, the value is overridden by the `run_name`. - tracking_uri: Address of local or remote tracking server. - If not provided, defaults to `MLFLOW_TRACKING_URI` environment variable if set, otherwise it falls - back to `file:<save_dir>`. - tags: A dictionary tags for the experiment. - save_dir: A path to a local directory where the MLflow runs get saved. - Defaults to `./mlflow` if `tracking_uri` is not provided. - Has no effect if `tracking_uri` is provided. - prefix: A string to put at the beginning of metric keys. - artifact_location: The location to store run artifacts. If not provided, the server picks an appropriate - default. - run_id: The run identifier of the experiment. If not provided, a new run is started. - - Raises: - ModuleNotFoundError: - If required MLFlow package is not installed on the device. - """ - - LOGGER_JOIN_CHAR = "-" - - def __init__( - self, - experiment_name: str = "lightning_logs", - run_name: Optional[str] = None, - tracking_uri: Optional[str] = os.getenv("MLFLOW_TRACKING_URI"), - tags: Optional[Dict[str, Any]] = None, - save_dir: Optional[str] = "./mlruns", - prefix: str = "", - artifact_location: Optional[str] = None, - run_id: Optional[str] = None, - ): - if mlflow is None: - raise ModuleNotFoundError( - "You want to use `mlflow` logger which is not installed yet, install it with `pip install mlflow`." - ) - super().__init__() - if not tracking_uri: - tracking_uri = f"{LOCAL_FILE_URI_PREFIX}{save_dir}" - - self._experiment_name = experiment_name - self._experiment_id = None - self._tracking_uri = tracking_uri - self._run_name = run_name - self._run_id = run_id - self.tags = tags - self._prefix = prefix - self._artifact_location = artifact_location - - self._initialized = False - - self._mlflow_client = MlflowClient(tracking_uri) - - @property - @rank_zero_experiment - def experiment(self) -> MlflowClient: - r""" - Actual MLflow object. To use MLflow features in your - :class:`~pytorch_lightning.core.lightning.LightningModule` do the following. - - Example:: - - self.logger.experiment.some_mlflow_function() - - """ - - if self._initialized: - return self._mlflow_client - - if self._run_id is not None: - run = self._mlflow_client.get_run(self._run_id) - self._experiment_id = run.info.experiment_id - self._initialized = True - return self._mlflow_client - - if self._experiment_id is None: - expt = self._mlflow_client.get_experiment_by_name(self._experiment_name) - if expt is not None: - self._experiment_id = expt.experiment_id - else: - log.warning(f"Experiment with name {self._experiment_name} not found. Creating it.") - self._experiment_id = self._mlflow_client.create_experiment( - name=self._experiment_name, artifact_location=self._artifact_location - ) - - if self._run_id is None: - if self._run_name is not None: - self.tags = self.tags or {} - if MLFLOW_RUN_NAME in self.tags: - log.warning( - f"The tag {MLFLOW_RUN_NAME} is found in tags. The value will be overridden by {self._run_name}." - ) - self.tags[MLFLOW_RUN_NAME] = self._run_name - run = self._mlflow_client.create_run(experiment_id=self._experiment_id, tags=resolve_tags(self.tags)) - self._run_id = run.info.run_id - self._initialized = True - return self._mlflow_client - - @property - def run_id(self) -> str: - """Create the experiment if it does not exist to get the run id. - - Returns: - The run id. - """ - _ = self.experiment - return self._run_id - - @property - def experiment_id(self) -> str: - """Create the experiment if it does not exist to get the experiment id. - - Returns: - The experiment id. - """ - _ = self.experiment - return self._experiment_id - -
[docs] @rank_zero_only - def log_hyperparams(self, params: Union[Dict[str, Any], Namespace]) -> None: - params = _convert_params(params) - params = _flatten_dict(params) - for k, v in params.items(): - if len(str(v)) > 250: - rank_zero_warn( - f"Mlflow only allows parameters with up to 250 characters. Discard {k}={v}", category=RuntimeWarning - ) - continue - - self.experiment.log_param(self.run_id, k, v)
- -
[docs] @rank_zero_only - def log_metrics(self, metrics: Dict[str, float], step: Optional[int] = None) -> None: - assert rank_zero_only.rank == 0, "experiment tried to log from global_rank != 0" - - metrics = _add_prefix(metrics, self._prefix, self.LOGGER_JOIN_CHAR) - - timestamp_ms = int(time() * 1000) - for k, v in metrics.items(): - if isinstance(v, str): - log.warning(f"Discarding metric with string value {k}={v}.") - continue - - new_k = re.sub("[^a-zA-Z0-9_/. -]+", "", k) - if k != new_k: - rank_zero_warn( - "MLFlow only allows '_', '/', '.' and ' ' special characters in metric name." - f" Replacing {k} with {new_k}.", - category=RuntimeWarning, - ) - k = new_k - - self.experiment.log_metric(self.run_id, k, v, timestamp_ms, step)
- -
[docs] @rank_zero_only - def finalize(self, status: str = "FINISHED") -> None: - super().finalize(status) - status = "FINISHED" if status == "success" else status - if self.experiment.get_run(self.run_id): - self.experiment.set_terminated(self.run_id, status)
- - @property - def save_dir(self) -> Optional[str]: - """The root file directory in which MLflow experiments are saved. - - Return: - Local path to the root experiment directory if the tracking uri is local. - Otherwise returns `None`. - """ - if self._tracking_uri.startswith(LOCAL_FILE_URI_PREFIX): - return self._tracking_uri.lstrip(LOCAL_FILE_URI_PREFIX) - - @property - def name(self) -> str: - """Get the experiment id. - - Returns: - The experiment id. - """ - return self.experiment_id - - @property - def version(self) -> str: - """Get the run id. - - Returns: - The run id. - """ - return self.run_id
-
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/loggers/neptune.html b/docs/_modules/pytorch_lightning/loggers/neptune.html deleted file mode 100644 index 3e9bd1a..0000000 --- a/docs/_modules/pytorch_lightning/loggers/neptune.html +++ /dev/null @@ -1,1360 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.loggers.neptune — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.loggers.neptune

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-Neptune Logger
---------------
-"""
-__all__ = [
-    "NeptuneLogger",
-]
-
-import logging
-import os
-import warnings
-from argparse import Namespace
-from functools import reduce
-from typing import Any, Callable, Dict, Generator, Mapping, Optional, Sequence, Set, Union
-from weakref import ReferenceType
-
-import torch
-
-from pytorch_lightning import __version__
-from pytorch_lightning.callbacks.model_checkpoint import ModelCheckpoint
-from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_experiment
-from pytorch_lightning.utilities.imports import _NEPTUNE_AVAILABLE, _NEPTUNE_GREATER_EQUAL_0_9
-from pytorch_lightning.utilities.logger import _add_prefix, _convert_params, _sanitize_callable_params
-from pytorch_lightning.utilities.model_summary import ModelSummary
-from pytorch_lightning.utilities.rank_zero import rank_zero_only
-
-if _NEPTUNE_AVAILABLE and _NEPTUNE_GREATER_EQUAL_0_9:
-    try:
-        from neptune import new as neptune
-        from neptune.new.exceptions import NeptuneLegacyProjectException, NeptuneOfflineModeFetchException
-        from neptune.new.run import Run
-        from neptune.new.types import File as NeptuneFile
-    except ModuleNotFoundError:
-        import neptune
-        from neptune.exceptions import NeptuneLegacyProjectException, NeptuneOfflineModeFetchException
-        from neptune.run import Run
-        from neptune.types import File as NeptuneFile
-else:
-    # needed for test mocks, and function signatures
-    neptune, Run, NeptuneFile = None, None, None
-
-log = logging.getLogger(__name__)
-
-_INTEGRATION_VERSION_KEY = "source_code/integrations/pytorch-lightning"
-
-# kwargs used in previous NeptuneLogger version, now deprecated
-_LEGACY_NEPTUNE_INIT_KWARGS = [
-    "project_name",
-    "offline_mode",
-    "experiment_name",
-    "experiment_id",
-    "params",
-    "properties",
-    "upload_source_files",
-    "abort_callback",
-    "logger",
-    "upload_stdout",
-    "upload_stderr",
-    "send_hardware_metrics",
-    "run_monitoring_thread",
-    "handle_uncaught_exceptions",
-    "git_info",
-    "hostname",
-    "notebook_id",
-    "notebook_path",
-]
-
-# kwargs used in legacy NeptuneLogger from neptune-pytorch-lightning package
-_LEGACY_NEPTUNE_LOGGER_KWARGS = [
-    "base_namespace",
-    "close_after_fit",
-]
-
-
-
[docs]class NeptuneLogger(LightningLoggerBase): - r""" - Log using `Neptune <https://neptune.ai>`_. - - Install it with pip: - - .. code-block:: bash - - pip install neptune-client - - or conda: - - .. code-block:: bash - - conda install -c conda-forge neptune-client - - **Quickstart** - - Pass NeptuneLogger instance to the Trainer to log metadata with Neptune: - - .. code-block:: python - - - from pytorch_lightning import Trainer - from pytorch_lightning.loggers import NeptuneLogger - - neptune_logger = NeptuneLogger( - api_key="ANONYMOUS", # replace with your own - project="common/pytorch-lightning-integration", # format "<WORKSPACE/PROJECT>" - tags=["training", "resnet"], # optional - ) - trainer = Trainer(max_epochs=10, logger=neptune_logger) - - **How to use NeptuneLogger?** - - Use the logger anywhere in your :class:`~pytorch_lightning.core.lightning.LightningModule` as follows: - - .. code-block:: python - - from neptune.new.types import File - from pytorch_lightning import LightningModule - - - class LitModel(LightningModule): - def training_step(self, batch, batch_idx): - # log metrics - acc = ... - self.log("train/loss", loss) - - def any_lightning_module_function_or_hook(self): - # log images - img = ... - self.logger.experiment["train/misclassified_images"].log(File.as_image(img)) - - # generic recipe - metadata = ... - self.logger.experiment["your/metadata/structure"].log(metadata) - - Note that syntax: ``self.logger.experiment["your/metadata/structure"].log(metadata)`` is specific to Neptune - and it extends logger capabilities. Specifically, it allows you to log various types of metadata - like scores, files, images, interactive visuals, CSVs, etc. - Refer to the `Neptune docs <https://docs.neptune.ai/you-should-know/logging-metadata#essential-logging-methods>`_ - for more detailed explanations. - You can also use regular logger methods ``log_metrics()``, and ``log_hyperparams()`` with NeptuneLogger - as these are also supported. - - **Log after fitting or testing is finished** - - You can log objects after the fitting or testing methods are finished: - - .. code-block:: python - - neptune_logger = NeptuneLogger(project="common/pytorch-lightning-integration") - - trainer = pl.Trainer(logger=neptune_logger) - model = ... - datamodule = ... - trainer.fit(model, datamodule=datamodule) - trainer.test(model, datamodule=datamodule) - - # Log objects after `fit` or `test` methods - # model summary - neptune_logger.log_model_summary(model=model, max_depth=-1) - - # generic recipe - metadata = ... - neptune_logger.experiment["your/metadata/structure"].log(metadata) - - **Log model checkpoints** - - If you have :class:`~pytorch_lightning.callbacks.ModelCheckpoint` configured, - Neptune logger automatically logs model checkpoints. - Model weights will be uploaded to the: "model/checkpoints" namespace in the Neptune Run. - You can disable this option: - - .. code-block:: python - - neptune_logger = NeptuneLogger(project="common/pytorch-lightning-integration", log_model_checkpoints=False) - - **Pass additional parameters to the Neptune run** - - You can also pass ``neptune_run_kwargs`` to specify the run in the greater detail, like ``tags`` or ``description``: - - .. testcode:: - - from pytorch_lightning import Trainer - from pytorch_lightning.loggers import NeptuneLogger - - neptune_logger = NeptuneLogger( - project="common/pytorch-lightning-integration", - name="lightning-run", - description="mlp quick run with pytorch-lightning", - tags=["mlp", "quick-run"], - ) - trainer = Trainer(max_epochs=3, logger=neptune_logger) - - Check `run documentation <https://docs.neptune.ai/essentials/api-reference/run>`_ - for more info about additional run parameters. - - **Details about Neptune run structure** - - Runs can be viewed as nested dictionary-like structures that you can define in your code. - Thanks to this you can easily organize your metadata in a way that is most convenient for you. - - The hierarchical structure that you apply to your metadata will be reflected later in the UI. - - You can organize this way any type of metadata - images, parameters, metrics, model checkpoint, CSV files, etc. - - See Also: - - Read about - `what object you can log to Neptune <https://docs.neptune.ai/you-should-know/what-can-you-log-and-display>`_. - - Check `example run <https://app.neptune.ai/o/common/org/pytorch-lightning-integration/e/PTL-1/all>`_ - with multiple types of metadata logged. - - For more detailed info check - `user guide <https://docs.neptune.ai/integrations-and-supported-tools/model-training/pytorch-lightning>`_. - - Args: - api_key: Optional. - Neptune API token, found on https://neptune.ai upon registration. - Read: `how to find and set Neptune API token <https://docs.neptune.ai/administration/security-and-privacy/ - how-to-find-and-set-neptune-api-token>`_. - It is recommended to keep it in the `NEPTUNE_API_TOKEN` - environment variable and then you can drop ``api_key=None``. - project: Optional. - Name of a project in a form of "my_workspace/my_project" for example "tom/mask-rcnn". - If ``None``, the value of `NEPTUNE_PROJECT` environment variable will be taken. - You need to create the project in https://neptune.ai first. - name: Optional. Editable name of the run. - Run name appears in the "all metadata/sys" section in Neptune UI. - run: Optional. Default is ``None``. The Neptune ``Run`` object. - If specified, this `Run`` will be used for logging, instead of a new Run. - When run object is passed you can't specify other neptune properties. - log_model_checkpoints: Optional. Default is ``True``. Log model checkpoint to Neptune. - Works only if ``ModelCheckpoint`` is passed to the ``Trainer``. - prefix: Optional. Default is ``"training"``. Root namespace for all metadata logging. - \**neptune_run_kwargs: Additional arguments like ``tags``, ``description``, ``capture_stdout``, etc. - used when run is created. - - Raises: - ModuleNotFoundError: - If required Neptune package in version >=0.9 is not installed on the device. - TypeError: - If configured project has not been migrated to new structure yet. - ValueError: - If argument passed to the logger's constructor is incorrect. - """ - - LOGGER_JOIN_CHAR = "/" - PARAMETERS_KEY = "hyperparams" - ARTIFACTS_KEY = "artifacts" - - def __init__( - self, - *, # force users to call `NeptuneLogger` initializer with `kwargs` - api_key: Optional[str] = None, - project: Optional[str] = None, - name: Optional[str] = None, - run: Optional["Run"] = None, - log_model_checkpoints: Optional[bool] = True, - prefix: str = "training", - agg_key_funcs: Optional[Mapping[str, Callable[[Sequence[float]], float]]] = None, - agg_default_func: Optional[Callable[[Sequence[float]], float]] = None, - **neptune_run_kwargs, - ): - # verify if user passed proper init arguments - self._verify_input_arguments(api_key, project, name, run, neptune_run_kwargs) - if neptune is None: - raise ModuleNotFoundError( - "You want to use the `Neptune` logger which is not installed yet, install it with" - " `pip install neptune-client`." - ) - - super().__init__(agg_key_funcs=agg_key_funcs, agg_default_func=agg_default_func) - self._log_model_checkpoints = log_model_checkpoints - self._prefix = prefix - self._run_name = name - self._project_name = project - self._api_key = api_key - self._run_instance = run - self._neptune_run_kwargs = neptune_run_kwargs - self._run_short_id = None - - if self._run_instance is not None: - self._retrieve_run_data() - - # make sure that we've log integration version for outside `Run` instances - self._run_instance[_INTEGRATION_VERSION_KEY] = __version__ - - def _retrieve_run_data(self): - try: - self._run_instance.wait() - self._run_short_id = self._run_instance["sys/id"].fetch() - self._run_name = self._run_instance["sys/name"].fetch() - except NeptuneOfflineModeFetchException: - self._run_short_id = "OFFLINE" - self._run_name = "offline-name" - - @property - def _neptune_init_args(self): - args = {} - # Backward compatibility in case of previous version retrieval - try: - args = self._neptune_run_kwargs - except AttributeError: - pass - - if self._project_name is not None: - args["project"] = self._project_name - - if self._api_key is not None: - args["api_token"] = self._api_key - - if self._run_short_id is not None: - args["run"] = self._run_short_id - - # Backward compatibility in case of previous version retrieval - try: - if self._run_name is not None: - args["name"] = self._run_name - except AttributeError: - pass - - return args - - def _construct_path_with_prefix(self, *keys) -> str: - """Return sequence of keys joined by `LOGGER_JOIN_CHAR`, started with `_prefix` if defined.""" - if self._prefix: - return self.LOGGER_JOIN_CHAR.join([self._prefix, *keys]) - return self.LOGGER_JOIN_CHAR.join(keys) - - @staticmethod - def _verify_input_arguments( - api_key: Optional[str], - project: Optional[str], - name: Optional[str], - run: Optional["Run"], - neptune_run_kwargs: dict, - ): - legacy_kwargs_msg = ( - "Following kwargs are deprecated: {legacy_kwargs}.\n" - "If you are looking for the Neptune logger using legacy Python API," - " it's still available as part of neptune-contrib package:\n" - " - https://docs-legacy.neptune.ai/integrations/pytorch_lightning.html\n" - "The NeptuneLogger was re-written to use the neptune.new Python API\n" - " - https://neptune.ai/blog/neptune-new\n" - " - https://docs.neptune.ai/integrations-and-supported-tools/model-training/pytorch-lightning\n" - "You should use arguments accepted by either NeptuneLogger.init() or neptune.init()" - ) - - # check if user used legacy kwargs expected in `NeptuneLegacyLogger` - used_legacy_kwargs = [ - legacy_kwarg for legacy_kwarg in neptune_run_kwargs if legacy_kwarg in _LEGACY_NEPTUNE_INIT_KWARGS - ] - if used_legacy_kwargs: - raise ValueError(legacy_kwargs_msg.format(legacy_kwargs=used_legacy_kwargs)) - - # check if user used legacy kwargs expected in `NeptuneLogger` from neptune-pytorch-lightning package - used_legacy_neptune_kwargs = [ - legacy_kwarg for legacy_kwarg in neptune_run_kwargs if legacy_kwarg in _LEGACY_NEPTUNE_LOGGER_KWARGS - ] - if used_legacy_neptune_kwargs: - raise ValueError(legacy_kwargs_msg.format(legacy_kwargs=used_legacy_neptune_kwargs)) - - # check if user passed new client `Run` object - if run is not None and not isinstance(run, Run): - raise ValueError( - "Run parameter expected to be of type `neptune.new.Run`.\n" - "If you are looking for the Neptune logger using legacy Python API," - " it's still available as part of neptune-contrib package:\n" - " - https://docs-legacy.neptune.ai/integrations/pytorch_lightning.html\n" - "The NeptuneLogger was re-written to use the neptune.new Python API\n" - " - https://neptune.ai/blog/neptune-new\n" - " - https://docs.neptune.ai/integrations-and-supported-tools/model-training/pytorch-lightning\n" - ) - - # check if user passed redundant neptune.init arguments when passed run - any_neptune_init_arg_passed = any(arg is not None for arg in [api_key, project, name]) or neptune_run_kwargs - if run is not None and any_neptune_init_arg_passed: - raise ValueError( - "When an already initialized run object is provided" - " you can't provide other neptune.init() parameters.\n" - ) - - def __getstate__(self): - state = self.__dict__.copy() - # Run instance can't be pickled - state["_run_instance"] = None - return state - - def __setstate__(self, state): - self.__dict__ = state - self._run_instance = neptune.init(**self._neptune_init_args) - - @property - @rank_zero_experiment - def experiment(self) -> Run: - r""" - Actual Neptune run object. Allows you to use neptune logging features in your - :class:`~pytorch_lightning.core.lightning.LightningModule`. - - Example:: - - class LitModel(LightningModule): - def training_step(self, batch, batch_idx): - # log metrics - acc = ... - self.logger.experiment["train/acc"].log(acc) - - # log images - img = ... - self.logger.experiment["train/misclassified_images"].log(File.as_image(img)) - - Note that syntax: ``self.logger.experiment["your/metadata/structure"].log(metadata)`` - is specific to Neptune and it extends logger capabilities. - Specifically, it allows you to log various types of metadata like scores, files, - images, interactive visuals, CSVs, etc. Refer to the - `Neptune docs <https://docs.neptune.ai/you-should-know/logging-metadata#essential-logging-methods>`_ - for more detailed explanations. - You can also use regular logger methods ``log_metrics()``, and ``log_hyperparams()`` - with NeptuneLogger as these are also supported. - """ - return self.run - - @property - @rank_zero_experiment - def run(self) -> Run: - try: - if not self._run_instance: - self._run_instance = neptune.init(**self._neptune_init_args) - self._retrieve_run_data() - # make sure that we've log integration version for newly created - self._run_instance[_INTEGRATION_VERSION_KEY] = __version__ - - return self._run_instance - except NeptuneLegacyProjectException as e: - raise TypeError( - f"Project {self._project_name} has not been migrated to the new structure." - " You can still integrate it with the Neptune logger using legacy Python API" - " available as part of neptune-contrib package:" - " https://docs-legacy.neptune.ai/integrations/pytorch_lightning.html\n" - ) from e - -
[docs] @rank_zero_only - def log_hyperparams(self, params: Union[Dict[str, Any], Namespace]) -> None: # skipcq: PYL-W0221 - r""" - Log hyper-parameters to the run. - - Hyperparams will be logged under the "<prefix>/hyperparams" namespace. - - Note: - - You can also log parameters by directly using the logger instance: - ``neptune_logger.experiment["model/hyper-parameters"] = params_dict``. - - In this way you can keep hierarchical structure of the parameters. - - Args: - params: `dict`. - Python dictionary structure with parameters. - - Example:: - - from pytorch_lightning.loggers import NeptuneLogger - - PARAMS = { - "batch_size": 64, - "lr": 0.07, - "decay_factor": 0.97 - } - - neptune_logger = NeptuneLogger( - api_key="ANONYMOUS", - project="common/pytorch-lightning-integration" - ) - - neptune_logger.log_hyperparams(PARAMS) - """ - params = _convert_params(params) - params = _sanitize_callable_params(params) - - parameters_key = self.PARAMETERS_KEY - parameters_key = self._construct_path_with_prefix(parameters_key) - - self.run[parameters_key] = params
- -
[docs] @rank_zero_only - def log_metrics(self, metrics: Dict[str, Union[torch.Tensor, float]], step: Optional[int] = None) -> None: - """Log metrics (numeric values) in Neptune runs. - - Args: - metrics: Dictionary with metric names as keys and measured quantities as values. - step: Step number at which the metrics should be recorded, currently ignored. - """ - if rank_zero_only.rank != 0: - raise ValueError("run tried to log from global_rank != 0") - - metrics = _add_prefix(metrics, self._prefix, self.LOGGER_JOIN_CHAR) - - for key, val in metrics.items(): - # `step` is ignored because Neptune expects strictly increasing step values which - # Lightning does not always guarantee. - self.run[key].log(val)
- -
[docs] @rank_zero_only - def finalize(self, status: str) -> None: - if status: - self.run[self._construct_path_with_prefix("status")] = status - - super().finalize(status)
- - @property - def save_dir(self) -> Optional[str]: - """Gets the save directory of the experiment which in this case is ``None`` because Neptune does not save - locally. - - Returns: - the root directory where experiment logs get saved - """ - return os.path.join(os.getcwd(), ".neptune") - - @rank_zero_only - def log_model_summary(self, model, max_depth=-1): - model_str = str(ModelSummary(model=model, max_depth=max_depth)) - self.run[self._construct_path_with_prefix("model/summary")] = neptune.types.File.from_content( - content=model_str, extension="txt" - ) - -
[docs] @rank_zero_only - def after_save_checkpoint(self, checkpoint_callback: "ReferenceType[ModelCheckpoint]") -> None: - """Automatically log checkpointed model. Called after model checkpoint callback saves a new checkpoint. - - Args: - checkpoint_callback: the model checkpoint callback instance - """ - if not self._log_model_checkpoints: - return - - file_names = set() - checkpoints_namespace = self._construct_path_with_prefix("model/checkpoints") - - # save last model - if checkpoint_callback.last_model_path: - model_last_name = self._get_full_model_name(checkpoint_callback.last_model_path, checkpoint_callback) - file_names.add(model_last_name) - self.run[f"{checkpoints_namespace}/{model_last_name}"].upload(checkpoint_callback.last_model_path) - - # save best k models - for key in checkpoint_callback.best_k_models.keys(): - model_name = self._get_full_model_name(key, checkpoint_callback) - file_names.add(model_name) - self.run[f"{checkpoints_namespace}/{model_name}"].upload(key) - - # log best model path and checkpoint - if checkpoint_callback.best_model_path: - self.run[self._construct_path_with_prefix("model/best_model_path")] = checkpoint_callback.best_model_path - - model_name = self._get_full_model_name(checkpoint_callback.best_model_path, checkpoint_callback) - file_names.add(model_name) - self.run[f"{checkpoints_namespace}/{model_name}"].upload(checkpoint_callback.best_model_path) - - # remove old models logged to experiment if they are not part of best k models at this point - if self.run.exists(checkpoints_namespace): - exp_structure = self.run.get_structure() - uploaded_model_names = self._get_full_model_names_from_exp_structure(exp_structure, checkpoints_namespace) - - for file_to_drop in list(uploaded_model_names - file_names): - del self.run[f"{checkpoints_namespace}/{file_to_drop}"] - - # log best model score - if checkpoint_callback.best_model_score: - self.run[self._construct_path_with_prefix("model/best_model_score")] = ( - checkpoint_callback.best_model_score.cpu().detach().numpy() - )
- - @staticmethod - def _get_full_model_name(model_path: str, checkpoint_callback: "ReferenceType[ModelCheckpoint]") -> str: - """Returns model name which is string `model_path` appended to `checkpoint_callback.dirpath`.""" - expected_model_path = f"{checkpoint_callback.dirpath}{os.path.sep}" - if not model_path.startswith(expected_model_path): - raise ValueError(f"{model_path} was expected to start with {expected_model_path}.") - # Remove extension from filepath - filepath, _ = os.path.splitext(model_path[len(expected_model_path) :]) - - return filepath - - @classmethod - def _get_full_model_names_from_exp_structure(cls, exp_structure: dict, namespace: str) -> Set[str]: - """Returns all paths to properties which were already logged in `namespace`""" - structure_keys = namespace.split(cls.LOGGER_JOIN_CHAR) - uploaded_models_dict = reduce(lambda d, k: d[k], [exp_structure, *structure_keys]) - return set(cls._dict_paths(uploaded_models_dict)) - - @classmethod - def _dict_paths(cls, d: dict, path_in_build: str = None) -> Generator: - for k, v in d.items(): - path = f"{path_in_build}/{k}" if path_in_build is not None else k - if not isinstance(v, dict): - yield path - else: - yield from cls._dict_paths(v, path) - - @property - def name(self) -> str: - """Return the experiment name or 'offline-name' when exp is run in offline mode.""" - return self._run_name - - @property - def version(self) -> str: - """Return the experiment version. - - It's Neptune Run's short_id - """ - return self._run_short_id - - @staticmethod - def _signal_deprecated_api_usage(f_name, sample_code, raise_exception=False): - msg_suffix = ( - f"If you are looking for the Neptune logger using legacy Python API," - f" it's still available as part of neptune-contrib package:\n" - f" - https://docs-legacy.neptune.ai/integrations/pytorch_lightning.html\n" - f"The NeptuneLogger was re-written to use the neptune.new Python API\n" - f" - https://neptune.ai/blog/neptune-new\n" - f" - https://docs.neptune.ai/integrations-and-supported-tools/model-training/pytorch-lightning\n" - f"Instead of `logger.{f_name}` you can use:\n" - f"\t{sample_code}" - ) - - if not raise_exception: - warnings.warn( - "The function you've used is deprecated in v1.5.0 and will be removed in v1.7.0. " + msg_suffix - ) - else: - raise ValueError("The function you've used is deprecated.\n" + msg_suffix) - - @rank_zero_only - def log_metric(self, metric_name: str, metric_value: Union[torch.Tensor, float, str], step: Optional[int] = None): - key = f"{self._prefix}/{metric_name}" - self._signal_deprecated_api_usage("log_metric", f"logger.run['{key}'].log(42)") - if torch.is_tensor(metric_value): - metric_value = metric_value.cpu().detach() - - self.run[key].log(metric_value, step=step) - - @rank_zero_only - def log_text(self, log_name: str, text: str, step: Optional[int] = None) -> None: - key = f"{self._prefix}/{log_name}" - self._signal_deprecated_api_usage("log_text", f"logger.run['{key}].log('text')") - self.run[key].log(str(text), step=step) - - @rank_zero_only - def log_image(self, log_name: str, image: Union[str, Any], step: Optional[int] = None) -> None: - key = f"{self._prefix}/{log_name}" - self._signal_deprecated_api_usage("log_image", f"logger.run['{key}'].log(File('path_to_image'))") - if isinstance(image, str): - # if `img` is path to file, convert it to file object - image = NeptuneFile(image) - self.run[key].log(image, step=step) - - @rank_zero_only - def log_artifact(self, artifact: str, destination: Optional[str] = None) -> None: - key = f"{self._prefix}/{self.ARTIFACTS_KEY}/{artifact}" - self._signal_deprecated_api_usage("log_artifact", f"logger.run['{key}].log('path_to_file')") - self.run[key].log(destination) - - def set_property(self, *args, **kwargs): - self._signal_deprecated_api_usage( - "log_artifact", f"logger.run['{self._prefix}/{self.PARAMETERS_KEY}/key'].log(value)", raise_exception=True - ) - - def append_tags(self, *args, **kwargs): - self._signal_deprecated_api_usage( - "append_tags", "logger.run['sys/tags'].add(['foo', 'bar'])", raise_exception=True - )
-
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/loggers/tensorboard.html b/docs/_modules/pytorch_lightning/loggers/tensorboard.html deleted file mode 100644 index 328b8eb..0000000 --- a/docs/_modules/pytorch_lightning/loggers/tensorboard.html +++ /dev/null @@ -1,1004 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.loggers.tensorboard — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.loggers.tensorboard

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-TensorBoard Logger
-------------------
-"""
-
-import logging
-import os
-from argparse import Namespace
-from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Union
-
-import numpy as np
-import torch
-from torch.utils.tensorboard import SummaryWriter
-from torch.utils.tensorboard.summary import hparams
-
-import pytorch_lightning as pl
-from pytorch_lightning.core.saving import save_hparams_to_yaml
-from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_experiment
-from pytorch_lightning.utilities.cloud_io import get_filesystem
-from pytorch_lightning.utilities.imports import _OMEGACONF_AVAILABLE
-from pytorch_lightning.utilities.logger import _add_prefix, _convert_params, _flatten_dict
-from pytorch_lightning.utilities.logger import _sanitize_params as _utils_sanitize_params
-from pytorch_lightning.utilities.rank_zero import rank_zero_only, rank_zero_warn
-
-log = logging.getLogger(__name__)
-
-if _OMEGACONF_AVAILABLE:
-    from omegaconf import Container, OmegaConf
-
-
-
[docs]class TensorBoardLogger(LightningLoggerBase): - r""" - Log to local file system in `TensorBoard <https://www.tensorflow.org/tensorboard>`_ format. - - Implemented using :class:`~torch.utils.tensorboard.SummaryWriter`. Logs are saved to - ``os.path.join(save_dir, name, version)``. This is the default logger in Lightning, it comes - preinstalled. - - Example: - - .. testcode:: - - from pytorch_lightning import Trainer - from pytorch_lightning.loggers import TensorBoardLogger - - logger = TensorBoardLogger("tb_logs", name="my_model") - trainer = Trainer(logger=logger) - - Args: - save_dir: Save directory - name: Experiment name. Defaults to ``'default'``. If it is the empty string then no per-experiment - subdirectory is used. - version: Experiment version. If version is not specified the logger inspects the save - directory for existing versions, then automatically assigns the next available version. - If it is a string then it is used as the run-specific subdirectory name, - otherwise ``'version_${version}'`` is used. - log_graph: Adds the computational graph to tensorboard. This requires that - the user has defined the `self.example_input_array` attribute in their - model. - default_hp_metric: Enables a placeholder metric with key `hp_metric` when `log_hyperparams` is - called without a metric (otherwise calls to log_hyperparams without a metric are ignored). - prefix: A string to put at the beginning of metric keys. - sub_dir: Sub-directory to group TensorBoard logs. If a sub_dir argument is passed - then logs are saved in ``/save_dir/name/version/sub_dir/``. Defaults to ``None`` in which - logs are saved in ``/save_dir/name/version/``. - \**kwargs: Additional arguments used by :class:`SummaryWriter` can be passed as keyword - arguments in this logger. To automatically flush to disk, `max_queue` sets the size - of the queue for pending logs before flushing. `flush_secs` determines how many seconds - elapses before flushing. - - """ - NAME_HPARAMS_FILE = "hparams.yaml" - LOGGER_JOIN_CHAR = "-" - - def __init__( - self, - save_dir: str, - name: Optional[str] = "lightning_logs", - version: Optional[Union[int, str]] = None, - log_graph: bool = False, - default_hp_metric: bool = True, - prefix: str = "", - sub_dir: Optional[str] = None, - agg_key_funcs: Optional[Mapping[str, Callable[[Sequence[float]], float]]] = None, - agg_default_func: Optional[Callable[[Sequence[float]], float]] = None, - **kwargs, - ): - super().__init__(agg_key_funcs=agg_key_funcs, agg_default_func=agg_default_func) - self._save_dir = save_dir - self._name = name or "" - self._version = version - self._sub_dir = sub_dir - self._log_graph = log_graph - self._default_hp_metric = default_hp_metric - self._prefix = prefix - self._fs = get_filesystem(save_dir) - - self._experiment = None - self.hparams = {} - self._kwargs = kwargs - - @property - def root_dir(self) -> str: - """Parent directory for all tensorboard checkpoint subdirectories. - - If the experiment name parameter is an empty string, no experiment subdirectory is used and the checkpoint will - be saved in "save_dir/version" - """ - return os.path.join(self.save_dir, self.name) - - @property - def log_dir(self) -> str: - """The directory for this run's tensorboard checkpoint. - - By default, it is named ``'version_${self.version}'`` but it can be overridden by passing a string value for the - constructor's version parameter instead of ``None`` or an int. - """ - # create a pseudo standard path ala test-tube - version = self.version if isinstance(self.version, str) else f"version_{self.version}" - log_dir = os.path.join(self.root_dir, version) - if isinstance(self.sub_dir, str): - log_dir = os.path.join(log_dir, self.sub_dir) - log_dir = os.path.expandvars(log_dir) - log_dir = os.path.expanduser(log_dir) - return log_dir - - @property - def save_dir(self) -> Optional[str]: - """Gets the save directory where the TensorBoard experiments are saved. - - Returns: - The local path to the save directory where the TensorBoard experiments are saved. - """ - return self._save_dir - - @property - def sub_dir(self) -> Optional[str]: - """Gets the sub directory where the TensorBoard experiments are saved. - - Returns: - The local path to the sub directory where the TensorBoard experiments are saved. - """ - return self._sub_dir - - @property - @rank_zero_experiment - def experiment(self) -> SummaryWriter: - r""" - Actual tensorboard object. To use TensorBoard features in your - :class:`~pytorch_lightning.core.lightning.LightningModule` do the following. - - Example:: - - self.logger.experiment.some_tensorboard_function() - - """ - if self._experiment is not None: - return self._experiment - - assert rank_zero_only.rank == 0, "tried to init log dirs in non global_rank=0" - if self.root_dir: - self._fs.makedirs(self.root_dir, exist_ok=True) - self._experiment = SummaryWriter(log_dir=self.log_dir, **self._kwargs) - return self._experiment - -
[docs] @rank_zero_only - def log_hyperparams( - self, params: Union[Dict[str, Any], Namespace], metrics: Optional[Dict[str, Any]] = None - ) -> None: - """Record hyperparameters. TensorBoard logs with and without saved hyperparameters are incompatible, the - hyperparameters are then not displayed in the TensorBoard. Please delete or move the previously saved logs - to display the new ones with hyperparameters. - - Args: - params: a dictionary-like container with the hyperparameters - metrics: Dictionary with metric names as keys and measured quantities as values - """ - - params = _convert_params(params) - - # store params to output - if _OMEGACONF_AVAILABLE and isinstance(params, Container): - self.hparams = OmegaConf.merge(self.hparams, params) - else: - self.hparams.update(params) - - # format params into the suitable for tensorboard - params = _flatten_dict(params) - params = self._sanitize_params(params) - - if metrics is None: - if self._default_hp_metric: - metrics = {"hp_metric": -1} - elif not isinstance(metrics, dict): - metrics = {"hp_metric": metrics} - - if metrics: - self.log_metrics(metrics, 0) - exp, ssi, sei = hparams(params, metrics) - writer = self.experiment._get_file_writer() - writer.add_summary(exp) - writer.add_summary(ssi) - writer.add_summary(sei)
- -
[docs] @rank_zero_only - def log_metrics(self, metrics: Dict[str, float], step: Optional[int] = None) -> None: - assert rank_zero_only.rank == 0, "experiment tried to log from global_rank != 0" - - metrics = _add_prefix(metrics, self._prefix, self.LOGGER_JOIN_CHAR) - - for k, v in metrics.items(): - if isinstance(v, torch.Tensor): - v = v.item() - - if isinstance(v, dict): - self.experiment.add_scalars(k, v, step) - else: - try: - self.experiment.add_scalar(k, v, step) - # todo: specify the possible exception - except Exception as ex: - m = f"\n you tried to log {v} which is currently not supported. Try a dict or a scalar/tensor." - raise ValueError(m) from ex
- -
[docs] @rank_zero_only - def log_graph(self, model: "pl.LightningModule", input_array=None): - if self._log_graph: - if input_array is None: - input_array = model.example_input_array - - if input_array is not None: - input_array = model._apply_batch_transfer_handler(input_array) - model._running_torchscript = True - self.experiment.add_graph(model, input_array) - model._running_torchscript = False - else: - rank_zero_warn( - "Could not log computational graph since the" - " `model.example_input_array` attribute is not set" - " or `input_array` was not given", - )
- -
[docs] @rank_zero_only - def save(self) -> None: - super().save() - dir_path = self.log_dir - - # prepare the file path - hparams_file = os.path.join(dir_path, self.NAME_HPARAMS_FILE) - - # save the metatags file if it doesn't exist and the log directory exists - if self._fs.isdir(dir_path) and not self._fs.isfile(hparams_file): - save_hparams_to_yaml(hparams_file, self.hparams)
- -
[docs] @rank_zero_only - def finalize(self, status: str) -> None: - self.experiment.flush() - self.experiment.close() - self.save()
- - @property - def name(self) -> str: - """Get the name of the experiment. - - Returns: - The name of the experiment. - """ - return self._name - - @property - def version(self) -> int: - """Get the experiment version. - - Returns: - The experiment version if specified else the next version. - """ - if self._version is None: - self._version = self._get_next_version() - return self._version - - def _get_next_version(self): - root_dir = self.root_dir - - try: - listdir_info = self._fs.listdir(root_dir) - except OSError: - log.warning("Missing logger folder: %s", root_dir) - return 0 - - existing_versions = [] - for listing in listdir_info: - d = listing["name"] - bn = os.path.basename(d) - if self._fs.isdir(d) and bn.startswith("version_"): - dir_ver = bn.split("_")[1].replace("/", "") - existing_versions.append(int(dir_ver)) - if len(existing_versions) == 0: - return 0 - - return max(existing_versions) + 1 - - @staticmethod - def _sanitize_params(params: Dict[str, Any]) -> Dict[str, Any]: - params = _utils_sanitize_params(params) - # logging of arrays with dimension > 1 is not supported, sanitize as string - return {k: str(v) if isinstance(v, (torch.Tensor, np.ndarray)) and v.ndim > 1 else v for k, v in params.items()} - - def __getstate__(self): - state = self.__dict__.copy() - state["_experiment"] = None - return state
-
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/loggers/wandb.html b/docs/_modules/pytorch_lightning/loggers/wandb.html deleted file mode 100644 index ed249ab..0000000 --- a/docs/_modules/pytorch_lightning/loggers/wandb.html +++ /dev/null @@ -1,1197 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.loggers.wandb — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.loggers.wandb

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""
-Weights and Biases Logger
--------------------------
-"""
-import os
-from argparse import Namespace
-from pathlib import Path
-from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Union
-from weakref import ReferenceType
-
-import torch.nn as nn
-
-from pytorch_lightning.callbacks.model_checkpoint import ModelCheckpoint
-from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_experiment
-from pytorch_lightning.utilities.exceptions import MisconfigurationException
-from pytorch_lightning.utilities.imports import _WANDB_GREATER_EQUAL_0_10_22, _WANDB_GREATER_EQUAL_0_12_10
-from pytorch_lightning.utilities.logger import _add_prefix, _convert_params, _flatten_dict, _sanitize_callable_params
-from pytorch_lightning.utilities.rank_zero import rank_zero_only, rank_zero_warn
-
-try:
-    import wandb
-    from wandb.wandb_run import Run
-except ModuleNotFoundError:
-    # needed for test mocks, these tests shall be updated
-    wandb, Run = None, None
-
-
-
[docs]class WandbLogger(LightningLoggerBase): - r""" - Log using `Weights and Biases <https://docs.wandb.ai/integrations/lightning>`_. - - **Installation and set-up** - - Install with pip: - - .. code-block:: bash - - pip install wandb - - Create a `WandbLogger` instance: - - .. code-block:: python - - from pytorch_lightning.loggers import WandbLogger - - wandb_logger = WandbLogger(project="MNIST") - - Pass the logger instance to the `Trainer`: - - .. code-block:: python - - trainer = Trainer(logger=wandb_logger) - - A new W&B run will be created when training starts if you have not created one manually before with `wandb.init()`. - - **Log metrics** - - Log from :class:`~pytorch_lightning.core.lightning.LightningModule`: - - .. code-block:: python - - class LitModule(LightningModule): - def training_step(self, batch, batch_idx): - self.log("train/loss", loss) - - Use directly wandb module: - - .. code-block:: python - - wandb.log({"train/loss": loss}) - - **Log hyper-parameters** - - Save :class:`~pytorch_lightning.core.lightning.LightningModule` parameters: - - .. code-block:: python - - class LitModule(LightningModule): - def __init__(self, *args, **kwarg): - self.save_hyperparameters() - - Add other config parameters: - - .. code-block:: python - - # add one parameter - wandb_logger.experiment.config["key"] = value - - # add multiple parameters - wandb_logger.experiment.config.update({key1: val1, key2: val2}) - - # use directly wandb module - wandb.config["key"] = value - wandb.config.update() - - **Log gradients, parameters and model topology** - - Call the `watch` method for automatically tracking gradients: - - .. code-block:: python - - # log gradients and model topology - wandb_logger.watch(model) - - # log gradients, parameter histogram and model topology - wandb_logger.watch(model, log="all") - - # change log frequency of gradients and parameters (100 steps by default) - wandb_logger.watch(model, log_freq=500) - - # do not log graph (in case of errors) - wandb_logger.watch(model, log_graph=False) - - The `watch` method adds hooks to the model which can be removed at the end of training: - - .. code-block:: python - - wandb_logger.unwatch(model) - - **Log model checkpoints** - - Log model checkpoints at the end of training: - - .. code-block:: python - - wandb_logger = WandbLogger(log_model=True) - - Log model checkpoints as they get created during training: - - .. code-block:: python - - wandb_logger = WandbLogger(log_model="all") - - Custom checkpointing can be set up through :class:`~pytorch_lightning.callbacks.ModelCheckpoint`: - - .. code-block:: python - - # log model only if `val_accuracy` increases - wandb_logger = WandbLogger(log_model="all") - checkpoint_callback = ModelCheckpoint(monitor="val_accuracy", mode="max") - trainer = Trainer(logger=wandb_logger, callbacks=[checkpoint_callback]) - - `latest` and `best` aliases are automatically set to easily retrieve a model checkpoint: - - .. code-block:: python - - # reference can be retrieved in artifacts panel - # "VERSION" can be a version (ex: "v2") or an alias ("latest or "best") - checkpoint_reference = "USER/PROJECT/MODEL-RUN_ID:VERSION" - - # download checkpoint locally (if not already cached) - run = wandb.init(project="MNIST") - artifact = run.use_artifact(checkpoint_reference, type="model") - artifact_dir = artifact.download() - - # load checkpoint - model = LitModule.load_from_checkpoint(Path(artifact_dir) / "model.ckpt") - - **Log media** - - Log text with: - - .. code-block:: python - - # using columns and data - columns = ["input", "label", "prediction"] - data = [["cheese", "english", "english"], ["fromage", "french", "spanish"]] - wandb_logger.log_text(key="samples", columns=columns, data=data) - - # using a pandas DataFrame - wandb_logger.log_text(key="samples", dataframe=my_dataframe) - - Log images with: - - .. code-block:: python - - # using tensors, numpy arrays or PIL images - wandb_logger.log_image(key="samples", images=[img1, img2]) - - # adding captions - wandb_logger.log_image(key="samples", images=[img1, img2], caption=["tree", "person"]) - - # using file path - wandb_logger.log_image(key="samples", images=["img_1.jpg", "img_2.jpg"]) - - More arguments can be passed for logging segmentation masks and bounding boxes. Refer to - `Image Overlays documentation <https://docs.wandb.ai/guides/track/log/media#image-overlays>`_. - - **Log Tables** - - `W&B Tables <https://docs.wandb.ai/guides/data-vis>`_ can be used to log, query and analyze tabular data. - - They support any type of media (text, image, video, audio, molecule, html, etc) and are great for storing, - understanding and sharing any form of data, from datasets to model predictions. - - .. code-block:: python - - columns = ["caption", "image", "sound"] - data = [["cheese", wandb.Image(img_1), wandb.Audio(snd_1)], ["wine", wandb.Image(img_2), wandb.Audio(snd_2)]] - wandb_logger.log_table(key="samples", columns=columns, data=data) - - See Also: - - `Demo in Google Colab <http://wandb.me/lightning>`__ with hyperparameter search and model logging - - `W&B Documentation <https://docs.wandb.ai/integrations/lightning>`__ - - Args: - name: Display name for the run. - save_dir: Path where data is saved (wandb dir by default). - offline: Run offline (data can be streamed later to wandb servers). - id: Sets the version, mainly used to resume a previous run. - version: Same as id. - anonymous: Enables or explicitly disables anonymous logging. - project: The name of the project to which this run will belong. - log_model: Log checkpoints created by :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` - as W&B artifacts. `latest` and `best` aliases are automatically set. - - * if ``log_model == 'all'``, checkpoints are logged during training. - * if ``log_model == True``, checkpoints are logged at the end of training, except when - :paramref:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint.save_top_k` ``== -1`` - which also logs every checkpoint during training. - * if ``log_model == False`` (default), no checkpoint is logged. - - prefix: A string to put at the beginning of metric keys. - experiment: WandB experiment object. Automatically set when creating a run. - \**kwargs: Arguments passed to :func:`wandb.init` like `entity`, `group`, `tags`, etc. - - Raises: - ModuleNotFoundError: - If required WandB package is not installed on the device. - MisconfigurationException: - If both ``log_model`` and ``offline`` is set to ``True``. - - """ - - LOGGER_JOIN_CHAR = "-" - - def __init__( - self, - name: Optional[str] = None, - save_dir: Optional[str] = None, - offline: Optional[bool] = False, - id: Optional[str] = None, - anonymous: Optional[bool] = None, - version: Optional[str] = None, - project: Optional[str] = None, - log_model: Union[str, bool] = False, - experiment=None, - prefix: Optional[str] = "", - agg_key_funcs: Optional[Mapping[str, Callable[[Sequence[float]], float]]] = None, - agg_default_func: Optional[Callable[[Sequence[float]], float]] = None, - **kwargs, - ): - if wandb is None: - raise ModuleNotFoundError( - "You want to use `wandb` logger which is not installed yet," - " install it with `pip install wandb`." # pragma: no-cover - ) - - if offline and log_model: - raise MisconfigurationException( - f"Providing log_model={log_model} and offline={offline} is an invalid configuration" - " since model checkpoints cannot be uploaded in offline mode.\n" - "Hint: Set `offline=False` to log your model." - ) - - if log_model and not _WANDB_GREATER_EQUAL_0_10_22: - rank_zero_warn( - f"Providing log_model={log_model} requires wandb version >= 0.10.22" - " for logging associated model metadata.\n" - "Hint: Upgrade with `pip install --upgrade wandb`." - ) - - super().__init__(agg_key_funcs=agg_key_funcs, agg_default_func=agg_default_func) - self._offline = offline - self._log_model = log_model - self._prefix = prefix - self._experiment = experiment - self._logged_model_time = {} - self._checkpoint_callback = None - # set wandb init arguments - anonymous_lut = {True: "allow", False: None} - self._wandb_init = dict( - name=name, - project=project, - id=version or id, - dir=save_dir, - resume="allow", - anonymous=anonymous_lut.get(anonymous, anonymous), - ) - self._wandb_init.update(**kwargs) - # extract parameters - self._save_dir = self._wandb_init.get("dir") - self._name = self._wandb_init.get("name") - self._id = self._wandb_init.get("id") - # start wandb run (to create an attach_id for distributed modes) - if _WANDB_GREATER_EQUAL_0_12_10: - wandb.require("service") - _ = self.experiment - - def __getstate__(self): - state = self.__dict__.copy() - # args needed to reload correct experiment - if self._experiment is not None: - state["_id"] = getattr(self._experiment, "id", None) - state["_attach_id"] = getattr(self._experiment, "_attach_id", None) - state["_name"] = self._experiment.project_name() - - # cannot be pickled - state["_experiment"] = None - return state - - @property - @rank_zero_experiment - def experiment(self) -> Run: - r""" - - Actual wandb object. To use wandb features in your - :class:`~pytorch_lightning.core.lightning.LightningModule` do the following. - - Example:: - - .. code-block:: python - - self.logger.experiment.some_wandb_function() - - """ - if self._experiment is None: - if self._offline: - os.environ["WANDB_MODE"] = "dryrun" - - attach_id = getattr(self, "_attach_id", None) - if wandb.run is not None: - # wandb process already created in this instance - rank_zero_warn( - "There is a wandb run already in progress and newly created instances of `WandbLogger` will reuse" - " this run. If this is not desired, call `wandb.finish()` before instantiating `WandbLogger`." - ) - self._experiment = wandb.run - elif attach_id is not None and hasattr(wandb, "_attach"): - # attach to wandb process referenced - self._experiment = wandb._attach(attach_id) - else: - # create new wandb process - self._experiment = wandb.init(**self._wandb_init) - - # define default x-axis - if getattr(self._experiment, "define_metric", None): - self._experiment.define_metric("trainer/global_step") - self._experiment.define_metric("*", step_metric="trainer/global_step", step_sync=True) - - return self._experiment - - def watch(self, model: nn.Module, log: str = "gradients", log_freq: int = 100, log_graph: bool = True): - self.experiment.watch(model, log=log, log_freq=log_freq, log_graph=log_graph) - -
[docs] @rank_zero_only - def log_hyperparams(self, params: Union[Dict[str, Any], Namespace]) -> None: - params = _convert_params(params) - params = _flatten_dict(params) - params = _sanitize_callable_params(params) - self.experiment.config.update(params, allow_val_change=True)
- -
[docs] @rank_zero_only - def log_metrics(self, metrics: Dict[str, float], step: Optional[int] = None) -> None: - assert rank_zero_only.rank == 0, "experiment tried to log from global_rank != 0" - - metrics = _add_prefix(metrics, self._prefix, self.LOGGER_JOIN_CHAR) - if step is not None: - self.experiment.log({**metrics, "trainer/global_step": step}) - else: - self.experiment.log(metrics)
- -
[docs] @rank_zero_only - def log_table( - self, - key: str, - columns: List[str] = None, - data: List[List[Any]] = None, - dataframe: Any = None, - step: Optional[int] = None, - ) -> None: - """Log a Table containing any object type (text, image, audio, video, molecule, html, etc). - - Can be defined either with `columns` and `data` or with `dataframe`. - """ - - metrics = {key: wandb.Table(columns=columns, data=data, dataframe=dataframe)} - self.log_metrics(metrics, step)
- -
[docs] @rank_zero_only - def log_text( - self, - key: str, - columns: List[str] = None, - data: List[List[str]] = None, - dataframe: Any = None, - step: Optional[int] = None, - ) -> None: - """Log text as a Table. - - Can be defined either with `columns` and `data` or with `dataframe`. - """ - - self.log_table(key, columns, data, dataframe, step)
- -
[docs] @rank_zero_only - def log_image(self, key: str, images: List[Any], step: Optional[int] = None, **kwargs: str) -> None: - """Log images (tensors, numpy arrays, PIL Images or file paths). - - Optional kwargs are lists passed to each image (ex: caption, masks, boxes). - """ - if not isinstance(images, list): - raise TypeError(f'Expected a list as "images", found {type(images)}') - n = len(images) - for k, v in kwargs.items(): - if len(v) != n: - raise ValueError(f"Expected {n} items but only found {len(v)} for {k}") - kwarg_list = [{k: kwargs[k][i] for k in kwargs.keys()} for i in range(n)] - metrics = {key: [wandb.Image(img, **kwarg) for img, kwarg in zip(images, kwarg_list)]} - self.log_metrics(metrics, step)
- - @property - def save_dir(self) -> Optional[str]: - """Gets the save directory. - - Returns: - The path to the save directory. - """ - return self._save_dir - - @property - def name(self) -> Optional[str]: - """Gets the name of the experiment. - - Returns: - The name of the experiment if the experiment exists else the name given to the constructor. - """ - # don't create an experiment if we don't have one - return self._experiment.project_name() if self._experiment else self._name - - @property - def version(self) -> Optional[str]: - """Gets the id of the experiment. - - Returns: - The id of the experiment if the experiment exists else the id given to the constructor. - """ - # don't create an experiment if we don't have one - return self._experiment.id if self._experiment else self._id - -
[docs] def after_save_checkpoint(self, checkpoint_callback: "ReferenceType[ModelCheckpoint]") -> None: - # log checkpoints as artifacts - if self._log_model == "all" or self._log_model is True and checkpoint_callback.save_top_k == -1: - self._scan_and_log_checkpoints(checkpoint_callback) - elif self._log_model is True: - self._checkpoint_callback = checkpoint_callback
- -
[docs] @rank_zero_only - def finalize(self, status: str) -> None: - # log checkpoints as artifacts - if self._checkpoint_callback: - self._scan_and_log_checkpoints(self._checkpoint_callback)
- - def _scan_and_log_checkpoints(self, checkpoint_callback: "ReferenceType[ModelCheckpoint]") -> None: - # get checkpoints to be saved with associated score - checkpoints = { - checkpoint_callback.last_model_path: checkpoint_callback.current_score, - checkpoint_callback.best_model_path: checkpoint_callback.best_model_score, - **checkpoint_callback.best_k_models, - } - checkpoints = sorted((Path(p).stat().st_mtime, p, s) for p, s in checkpoints.items() if Path(p).is_file()) - checkpoints = [ - c for c in checkpoints if c[1] not in self._logged_model_time.keys() or self._logged_model_time[c[1]] < c[0] - ] - - # log iteratively all new checkpoints - for t, p, s in checkpoints: - metadata = ( - { - "score": s, - "original_filename": Path(p).name, - "ModelCheckpoint": { - k: getattr(checkpoint_callback, k) - for k in [ - "monitor", - "mode", - "save_last", - "save_top_k", - "save_weights_only", - "_every_n_train_steps", - ] - # ensure it does not break if `ModelCheckpoint` args change - if hasattr(checkpoint_callback, k) - }, - } - if _WANDB_GREATER_EQUAL_0_10_22 - else None - ) - artifact = wandb.Artifact(name=f"model-{self.experiment.id}", type="model", metadata=metadata) - artifact.add_file(p, name="model.ckpt") - aliases = ["latest", "best"] if p == checkpoint_callback.best_model_path else ["latest"] - self.experiment.log_artifact(artifact, aliases=aliases) - # remember logged models - timestamp needed in case filename didn't change (lastkckpt or custom name) - self._logged_model_time[p] = t
-
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/loops/base.html b/docs/_modules/pytorch_lightning/loops/base.html deleted file mode 100644 index b2f5283..0000000 --- a/docs/_modules/pytorch_lightning/loops/base.html +++ /dev/null @@ -1,1033 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.loops.base — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.loops.base

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-import inspect
-from abc import ABC, abstractmethod
-from typing import Any, Dict, Generic, Optional, Type, TypeVar, Union
-
-from deprecate import void
-from torchmetrics import Metric
-
-import pytorch_lightning as pl
-from pytorch_lightning.trainer.connectors.logger_connector.result import _ResultCollection
-from pytorch_lightning.trainer.progress import BaseProgress
-from pytorch_lightning.utilities.exceptions import MisconfigurationException
-from pytorch_lightning.utilities.imports import _fault_tolerant_training
-
-T = TypeVar("T")  # the output type of `run`
-
-
-class Loop(ABC, Generic[T]):
-    """Basic Loops interface. All classes derived from this must implement the following properties and methods:
-
-        * :attr:`done` (property): Condition to break the loop
-        * :attr:`reset` (method): Resets the internal state between multiple calls of :attr:`run`
-        * :attr:`advance` (method): Implements one step of the loop
-
-    This class implements the following loop structure:
-
-    .. code-block:: python
-
-        on_run_start()
-
-        while not done:
-            on_advance_start()
-            advance()
-            on_advance_end()
-
-        on_run_end()
-    """
-
-    def __init__(self) -> None:
-        self._restarting = False
-        self._trainer: Optional["pl.Trainer"] = None
-
-    @property
-    def trainer(self) -> "pl.Trainer":
-        if self._trainer is None:
-            raise RuntimeError("The loop is not attached to a Trainer.")
-        return self._trainer
-
-    @trainer.setter
-    def trainer(self, trainer: "pl.Trainer") -> None:
-        """Connects this loop's trainer and its children."""
-        if not isinstance(trainer, pl.Trainer):
-            raise MisconfigurationException(
-                f"Loop {self.__class__.__name__} should be connected to a `Trainer`, found: {trainer}."
-            )
-        self._trainer = trainer
-        for v in self.__dict__.values():
-            if isinstance(v, Loop):
-                v.trainer = trainer
-
-    @property
-    def restarting(self) -> bool:
-        """Whether the state of this loop was reloaded and it needs to restart."""
-        return self._restarting
-
-    @restarting.setter
-    def restarting(self, restarting: bool) -> None:
-        """Connects this loop's restarting value and its children."""
-        self._restarting = restarting
-        for loop in vars(self).values():
-            if isinstance(loop, Loop):
-                loop.restarting = restarting
-
-    @property
-    @abstractmethod
-    def done(self) -> bool:
-        """Property indicating when the loop is finished.
-
-        Example::
-
-            @property
-            def done(self):
-                return self.trainer.global_step >= self.trainer.max_steps
-        """
-
-    @property
-    def skip(self) -> bool:
-        """Determine whether to return immediately from the call to :meth:`run`.
-
-        Example::
-
-            @property
-            def skip(self):
-                return len(self.trainer.train_dataloader) == 0
-        """
-        return False
-
-    def connect(self, **kwargs: "Loop") -> None:
-        """Optionally connect one or multiple loops to this one.
-
-        Linked loops should form a tree.
-        """
-
-    def replace(self, **loops: Union["Loop", Type["Loop"]]) -> None:
-        """Optionally replace one or multiple of this loop's sub-loops.
-
-        This methods takes care of instantiating the class (if necessary) with all existing arguments, connecting all
-        sub-loops of the old loop to the new instance, setting the ``Trainer`` reference, and connecting the new loop to
-        the parent.
-
-        Args:
-            **loops: ``Loop`` subclasses or instances. The name used should match the loop attribute name you want to
-                replace.
-
-        Raises:
-            MisconfigurationException: When passing a ``Loop`` class, if the ``__init__`` arguments do not match those
-                of the Loop class it replaces.
-        """
-        new_loops = {}
-
-        for name, type_or_object in loops.items():
-            old_loop = getattr(self, name)
-
-            if isinstance(type_or_object, type):
-                # compare the signatures
-                old_parameters = inspect.signature(old_loop.__class__.__init__).parameters
-                current_parameters = inspect.signature(type_or_object.__init__).parameters
-                if old_parameters != current_parameters:
-                    raise MisconfigurationException(
-                        f"`{self.__class__.__name__}.replace({type_or_object.__name__})` can only be used if the"
-                        f" `__init__` signatures match but `{old_loop.__class__.__name__}` does not."
-                    )
-                # instantiate the loop
-                kwargs = {p: getattr(old_loop, p) for p in old_parameters if p != "self"}
-                loop = type_or_object(**kwargs)
-            else:
-                loop = type_or_object
-
-            # connect sub-loops
-            kwargs = {n: l for n, l in old_loop.__dict__.items() if isinstance(l, Loop)}
-            loop.connect(**kwargs)
-            # set the trainer reference
-            loop.trainer = self.trainer
-
-            new_loops[name] = loop
-        # connect to self
-        self.connect(**new_loops)
-
-    def on_skip(self) -> T:
-        """The function to run when :meth:`run` should be skipped, determined by the condition in :attr:`skip`.
-
-        Returns:
-            the default output value of :meth:`on_run_end`
-        """
-
-
[docs] def run(self, *args: Any, **kwargs: Any) -> T: - """The main entry point to the loop. - - Will frequently check the :attr:`done` condition and calls :attr:`advance` - until :attr:`done` evaluates to ``True``. - - Override this if you wish to change the default behavior. The default implementation is: - - Example:: - - def run(self, *args, **kwargs): - if self.skip: - return self.on_skip() - - self.reset() - self.on_run_start(*args, **kwargs) - - while not self.done: - self.advance(*args, **kwargs) - - output = self.on_run_end() - return output - - Returns: - The output of :attr:`on_run_end` (often outputs collected from each step of the loop) - """ - if self.skip: - return self.on_skip() - - self.reset() - - self.on_run_start(*args, **kwargs) - - while not self.done: - try: - self.on_advance_start(*args, **kwargs) - self.advance(*args, **kwargs) - self.on_advance_end() - self._restarting = False - except StopIteration: - break - self._restarting = False - - output = self.on_run_end() - return output
- -
[docs] @abstractmethod - def reset(self) -> None: - """Resets the internal state of the loop at the beginning of each call to :attr:`run`. - - Example:: - - def reset(self): - # reset your internal state or add custom logic - # if you expect run() to be called multiple times - self.current_iteration = 0 - self.outputs = [] - """
- - def on_run_start(self, *args: Any, **kwargs: Any) -> None: - """Hook to be called as the first thing after entering :attr:`run` (except the state reset). - - Accepts all arguments passed to :attr:`run`. - """ - void(*args, **kwargs) - - def on_advance_start(self, *args: Any, **kwargs: Any) -> None: - """Hook to be called each time before :attr:`advance` is called. - - Accepts all arguments passed to :attr`run`. - """ - void(*args, **kwargs) - -
[docs] @abstractmethod - def advance(self, *args: Any, **kwargs: Any) -> None: - """Performs a single step. - - Accepts all arguments passed to :attr:`run`. - - Example:: - - def advance(self, iterator): - batch = next(iterator) - loss = self.trainer.lightning_module.training_step(batch, batch_idx) - ... - """
- - def on_advance_end(self) -> None: - """Hook to be called each time after :attr:`advance` is called.""" - - def on_run_end(self) -> T: - """Hook to be called at the end of the run. - - Its return argument is returned from :attr:`run`. - """ - - def teardown(self) -> None: - """Use to release memory etc.""" - - def on_save_checkpoint(self) -> Dict: - """Called when saving a model checkpoint, use to persist loop state. - - Returns: - The current loop state. - """ - return {} - - def on_load_checkpoint(self, state_dict: Dict) -> None: - """Called when loading a model checkpoint, use to reload loop state.""" - - def state_dict(self, destination: Optional[Dict] = None, prefix: str = "") -> Dict: - """The state dict is determined by the state and progress of this loop and all its children. - - Args: - destination: An existing dictionary to update with this loop's state. By default a new dictionary - is returned. - prefix: A prefix for each key in the state dictionary - """ - if destination is None: - destination = {} - - destination[prefix + "state_dict"] = self.on_save_checkpoint() - - # do not get the mode from `self.trainer` because it might not have been attached yet - ft_enabled = _fault_tolerant_training() - for k, v in self.__dict__.items(): - key = prefix + k - if isinstance(v, BaseProgress): - destination[key] = v.state_dict() - elif isinstance(v, Loop): - v.state_dict(destination, key + ".") - elif ft_enabled and isinstance(v, _ResultCollection): - # sync / unsync metrics - v.sync() - destination[key] = v.state_dict() - v.unsync() - - return destination - - def load_state_dict( - self, - state_dict: Dict, - prefix: str = "", - metrics: Optional[Dict[str, Metric]] = None, - ) -> None: - """Loads the state of this loop and all its children.""" - self._load_from_state_dict(state_dict.copy(), prefix, metrics) - for k, v in self.__dict__.items(): - if isinstance(v, Loop): - v.load_state_dict(state_dict.copy(), prefix + k + ".") - self.restarting = True - - def _load_from_state_dict(self, state_dict: Dict, prefix: str, metrics: Optional[Dict[str, Metric]] = None) -> None: - for k, v in self.__dict__.items(): - key = prefix + k - if key not in state_dict: - # compatibility with old checkpoints - continue - - if isinstance(v, BaseProgress): - v.load_state_dict(state_dict[key]) - elif ( - isinstance(v, _ResultCollection) - and self.trainer is not None - and self.trainer.lightning_module is not None - ): - metric_attributes = { - name: module - for name, module in self.trainer.lightning_module.named_modules() - if isinstance(module, Metric) - } - if metrics: - metric_attributes.update(metrics) - - # The `_ResultCollection` objects have 2 types of metrics: `Tensor` and `torchmetrics.Metric`. - # When creating a checkpoint, the `Metric`s are dropped from the loop `state_dict` to serialize only - # Python primitives. However, their states are saved with the model's `state_dict`. - # On reload, we need to re-attach the `Metric`s back to the `_ResultCollection`. - # The references are provided through the `metric_attributes` dictionary. - v.load_state_dict(state_dict[key], metrics=metric_attributes, sync_fn=self.trainer.strategy.reduce) - - if not self.trainer.is_global_zero: - v.reset(metrics=False) - - if prefix + "state_dict" in state_dict: # compatibility with old checkpoints - self.on_load_checkpoint(state_dict[prefix + "state_dict"]) -
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_modules/pytorch_lightning/trainer/trainer.html b/docs/_modules/pytorch_lightning/trainer/trainer.html deleted file mode 100644 index a83010b..0000000 --- a/docs/_modules/pytorch_lightning/trainer/trainer.html +++ /dev/null @@ -1,3544 +0,0 @@ - - - - - - - - - - - - - pytorch_lightning.trainer.trainer — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Source code for pytorch_lightning.trainer.trainer

-# Copyright The PyTorch Lightning team.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-"""Trainer to automate the training."""
-import inspect
-import logging
-import math
-import os
-import traceback
-import warnings
-from argparse import ArgumentParser, Namespace
-from copy import deepcopy
-from datetime import timedelta
-from pathlib import Path
-from typing import Any, Callable, cast, Dict, Iterable, List, Optional, Type, Union
-from weakref import proxy
-
-import torch
-from packaging.version import Version
-from torch.optim import Optimizer
-from torch.utils.data import DataLoader
-
-import pytorch_lightning as pl
-from pytorch_lightning.accelerators import Accelerator, GPUAccelerator, HPUAccelerator, IPUAccelerator, TPUAccelerator
-from pytorch_lightning.callbacks import Callback, EarlyStopping, ModelCheckpoint, ProgressBarBase
-from pytorch_lightning.callbacks.prediction_writer import BasePredictionWriter
-from pytorch_lightning.core.datamodule import LightningDataModule
-from pytorch_lightning.core.optimizer import LightningOptimizer
-from pytorch_lightning.loggers import LightningLoggerBase
-from pytorch_lightning.loggers.base import DummyLogger, LoggerCollection
-from pytorch_lightning.loggers.tensorboard import TensorBoardLogger
-from pytorch_lightning.loops import PredictionLoop, TrainingEpochLoop
-from pytorch_lightning.loops.dataloader.evaluation_loop import EvaluationLoop
-from pytorch_lightning.loops.fit_loop import FitLoop
-from pytorch_lightning.loops.utilities import _parse_loop_limits, _reset_progress
-from pytorch_lightning.plugins import (
-    ApexMixedPrecisionPlugin,
-    NativeMixedPrecisionPlugin,
-    PLUGIN_INPUT,
-    PrecisionPlugin,
-)
-from pytorch_lightning.plugins.environments.slurm_environment import SLURMEnvironment
-from pytorch_lightning.profiler import (
-    AdvancedProfiler,
-    BaseProfiler,
-    PassThroughProfiler,
-    Profiler,
-    PyTorchProfiler,
-    SimpleProfiler,
-    XLAProfiler,
-)
-from pytorch_lightning.strategies import ParallelStrategy, Strategy
-from pytorch_lightning.strategies.ddp_spawn import DDPSpawnStrategy
-from pytorch_lightning.trainer.callback_hook import TrainerCallbackHookMixin
-from pytorch_lightning.trainer.configuration_validator import verify_loop_configurations
-from pytorch_lightning.trainer.connectors.accelerator_connector import AcceleratorConnector
-from pytorch_lightning.trainer.connectors.callback_connector import CallbackConnector
-from pytorch_lightning.trainer.connectors.checkpoint_connector import CheckpointConnector
-from pytorch_lightning.trainer.connectors.data_connector import DataConnector
-from pytorch_lightning.trainer.connectors.logger_connector import LoggerConnector
-from pytorch_lightning.trainer.connectors.logger_connector.result import _ResultCollection
-from pytorch_lightning.trainer.connectors.signal_connector import SignalConnector
-from pytorch_lightning.trainer.data_loading import TrainerDataLoadingMixin
-from pytorch_lightning.trainer.optimizers import TrainerOptimizersMixin
-from pytorch_lightning.trainer.states import RunningStage, TrainerFn, TrainerState, TrainerStatus
-from pytorch_lightning.trainer.supporters import CombinedLoader
-from pytorch_lightning.tuner.lr_finder import _LRFinder
-from pytorch_lightning.tuner.tuning import Tuner
-from pytorch_lightning.utilities import (
-    _HPU_AVAILABLE,
-    _IPU_AVAILABLE,
-    _TPU_AVAILABLE,
-    AMPType,
-    GradClipAlgorithmType,
-    parsing,
-)
-from pytorch_lightning.utilities.apply_func import apply_to_collection
-from pytorch_lightning.utilities.argparse import (
-    _defaults_from_env_vars,
-    add_argparse_args,
-    from_argparse_args,
-    parse_argparser,
-    parse_env_variables,
-)
-from pytorch_lightning.utilities.auto_restart import _add_capture_metadata_collate
-from pytorch_lightning.utilities.cloud_io import get_filesystem
-from pytorch_lightning.utilities.data import _auto_add_worker_init_fn, has_len_all_ranks
-from pytorch_lightning.utilities.distributed import distributed_available
-from pytorch_lightning.utilities.exceptions import ExitGracefullyException, MisconfigurationException
-from pytorch_lightning.utilities.imports import _fault_tolerant_training
-from pytorch_lightning.utilities.meta import is_on_meta_device, materialize_module
-from pytorch_lightning.utilities.model_helpers import is_overridden
-from pytorch_lightning.utilities.rank_zero import rank_zero_deprecation, rank_zero_info, rank_zero_warn
-from pytorch_lightning.utilities.seed import isolate_rng
-from pytorch_lightning.utilities.signature_utils import is_param_in_hook_signature
-from pytorch_lightning.utilities.types import (
-    _EVALUATE_OUTPUT,
-    _PATH,
-    _PREDICT_OUTPUT,
-    EVAL_DATALOADERS,
-    LRSchedulerConfig,
-    STEP_OUTPUT,
-    TRAIN_DATALOADERS,
-)
-from pytorch_lightning.utilities.warnings import PossibleUserWarning
-
-log = logging.getLogger(__name__)
-# warnings to ignore in trainer
-warnings.filterwarnings(
-    "ignore", message="torch.distributed.reduce_op is deprecated, please use torch.distributed.ReduceOp instead"
-)
-
-
-class Trainer(
-    TrainerCallbackHookMixin,  # TODO: Remove in v1.8
-    TrainerOptimizersMixin,  # TODO: Remove in v1.8
-    TrainerDataLoadingMixin,  # TODO: Remove in v1.8
-):
-
[docs] @_defaults_from_env_vars - def __init__( - self, - logger: Union[LightningLoggerBase, Iterable[LightningLoggerBase], bool] = True, - checkpoint_callback: Optional[bool] = None, - enable_checkpointing: bool = True, - callbacks: Optional[Union[List[Callback], Callback]] = None, - default_root_dir: Optional[str] = None, - gradient_clip_val: Optional[Union[int, float]] = None, - gradient_clip_algorithm: Optional[str] = None, - process_position: int = 0, - num_nodes: int = 1, - num_processes: Optional[int] = None, - devices: Optional[Union[List[int], str, int]] = None, - gpus: Optional[Union[List[int], str, int]] = None, - auto_select_gpus: bool = False, - tpu_cores: Optional[Union[List[int], str, int]] = None, - ipus: Optional[int] = None, - log_gpu_memory: Optional[str] = None, # TODO: Remove in 1.7 - progress_bar_refresh_rate: Optional[int] = None, # TODO: remove in v1.7 - enable_progress_bar: bool = True, - overfit_batches: Union[int, float] = 0.0, - track_grad_norm: Union[int, float, str] = -1, - check_val_every_n_epoch: int = 1, - fast_dev_run: Union[int, bool] = False, - accumulate_grad_batches: Optional[Union[int, Dict[int, int]]] = None, - max_epochs: Optional[int] = None, - min_epochs: Optional[int] = None, - max_steps: int = -1, - min_steps: Optional[int] = None, - max_time: Optional[Union[str, timedelta, Dict[str, int]]] = None, - limit_train_batches: Optional[Union[int, float]] = None, - limit_val_batches: Optional[Union[int, float]] = None, - limit_test_batches: Optional[Union[int, float]] = None, - limit_predict_batches: Optional[Union[int, float]] = None, - val_check_interval: Optional[Union[int, float]] = None, - flush_logs_every_n_steps: Optional[int] = None, - log_every_n_steps: int = 50, - accelerator: Optional[Union[str, Accelerator]] = None, - strategy: Optional[Union[str, Strategy]] = None, - sync_batchnorm: bool = False, - precision: Union[int, str] = 32, - enable_model_summary: bool = True, - weights_summary: Optional[str] = "top", - weights_save_path: Optional[str] = None, # TODO: Remove in 1.8 - num_sanity_val_steps: int = 2, - resume_from_checkpoint: Optional[Union[Path, str]] = None, - profiler: Optional[Union[BaseProfiler, str]] = None, - benchmark: Optional[bool] = None, - deterministic: bool = False, - reload_dataloaders_every_n_epochs: int = 0, - auto_lr_find: Union[bool, str] = False, - replace_sampler_ddp: bool = True, - detect_anomaly: bool = False, - auto_scale_batch_size: Union[str, bool] = False, - prepare_data_per_node: Optional[bool] = None, - plugins: Optional[Union[PLUGIN_INPUT, List[PLUGIN_INPUT]]] = None, - amp_backend: str = "native", - amp_level: Optional[str] = None, - move_metrics_to_cpu: bool = False, - multiple_trainloader_mode: str = "max_size_cycle", - stochastic_weight_avg: bool = False, - terminate_on_nan: Optional[bool] = None, - ) -> None: - r""" - Customize every aspect of training via flags. - - Args: - - accelerator: Supports passing different accelerator types ("cpu", "gpu", "tpu", "ipu", "hpu", "auto") - as well as custom accelerator instances. - - .. deprecated:: v1.5 - Passing training strategies (e.g., 'ddp') to ``accelerator`` has been deprecated in v1.5.0 - and will be removed in v1.7.0. Please use the ``strategy`` argument instead. - - accumulate_grad_batches: Accumulates grads every k batches or as set up in the dict. - Default: ``None``. - - amp_backend: The mixed precision backend to use ("native" or "apex"). - Default: ``'native''``. - - amp_level: The optimization level to use (O1, O2, etc...). By default it will be set to "O2" - if ``amp_backend`` is set to "apex". - - auto_lr_find: If set to True, will make trainer.tune() run a learning rate finder, - trying to optimize initial learning for faster convergence. trainer.tune() method will - set the suggested learning rate in self.lr or self.learning_rate in the LightningModule. - To use a different key set a string instead of True with the key name. - Default: ``False``. - - auto_scale_batch_size: If set to True, will `initially` run a batch size - finder trying to find the largest batch size that fits into memory. - The result will be stored in self.batch_size in the LightningModule. - Additionally, can be set to either `power` that estimates the batch size through - a power search or `binsearch` that estimates the batch size through a binary search. - Default: ``False``. - - auto_select_gpus: If enabled and ``gpus`` or ``devices`` is an integer, pick available - gpus automatically. This is especially useful when - GPUs are configured to be in "exclusive mode", such - that only one process at a time can access them. - Default: ``False``. - - benchmark: Sets ``torch.backends.cudnn.benchmark``. - Defaults to ``True`` if :paramref:`~pytorch_lightning.trainer.trainer.Trainer.deterministic` - is ``False``. Overwrite to manually set a different value. Default: ``None``. - - callbacks: Add a callback or list of callbacks. - Default: ``None``. - - checkpoint_callback: If ``True``, enable checkpointing. - Default: ``None``. - - .. deprecated:: v1.5 - ``checkpoint_callback`` has been deprecated in v1.5 and will be removed in v1.7. - Please consider using ``enable_checkpointing`` instead. - - enable_checkpointing: If ``True``, enable checkpointing. - It will configure a default ModelCheckpoint callback if there is no user-defined ModelCheckpoint in - :paramref:`~pytorch_lightning.trainer.trainer.Trainer.callbacks`. - Default: ``True``. - - check_val_every_n_epoch: Check val every n train epochs. - Default: ``1``. - - - default_root_dir: Default path for logs and weights when no logger/ckpt_callback passed. - Default: ``os.getcwd()``. - Can be remote file paths such as `s3://mybucket/path` or 'hdfs://path/' - - detect_anomaly: Enable anomaly detection for the autograd engine. - Default: ``False``. - - deterministic: If ``True``, sets whether PyTorch operations must use deterministic algorithms. - Default: ``False``. - - devices: Will be mapped to either `gpus`, `tpu_cores`, `num_processes` or `ipus`, - based on the accelerator type. - - fast_dev_run: Runs n if set to ``n`` (int) else 1 if set to ``True`` batch(es) - of train, val and test to find any bugs (ie: a sort of unit test). - Default: ``False``. - - flush_logs_every_n_steps: How often to flush logs to disk (defaults to every 100 steps). - - .. deprecated:: v1.5 - ``flush_logs_every_n_steps`` has been deprecated in v1.5 and will be removed in v1.7. - Please configure flushing directly in the logger instead. - - gpus: Number of GPUs to train on (int) or which GPUs to train on (list or str) applied per node - Default: ``None``. - - gradient_clip_val: The value at which to clip gradients. Passing ``gradient_clip_val=None`` disables - gradient clipping. If using Automatic Mixed Precision (AMP), the gradients will be unscaled before. - Default: ``None``. - - gradient_clip_algorithm: The gradient clipping algorithm to use. Pass ``gradient_clip_algorithm="value"`` - to clip by value, and ``gradient_clip_algorithm="norm"`` to clip by norm. By default it will - be set to ``"norm"``. - - limit_train_batches: How much of training dataset to check (float = fraction, int = num_batches). - Default: ``1.0``. - - limit_val_batches: How much of validation dataset to check (float = fraction, int = num_batches). - Default: ``1.0``. - - limit_test_batches: How much of test dataset to check (float = fraction, int = num_batches). - Default: ``1.0``. - - limit_predict_batches: How much of prediction dataset to check (float = fraction, int = num_batches). - Default: ``1.0``. - - logger: Logger (or iterable collection of loggers) for experiment tracking. A ``True`` value uses - the default ``TensorBoardLogger``. ``False`` will disable logging. If multiple loggers are - provided and the `save_dir` property of that logger is not set, local files (checkpoints, - profiler traces, etc.) are saved in ``default_root_dir`` rather than in the ``log_dir`` of any - of the individual loggers. - Default: ``True``. - - log_gpu_memory: None, 'min_max', 'all'. Might slow performance. - - .. deprecated:: v1.5 - Deprecated in v1.5.0 and will be removed in v1.7.0 - Please use the ``DeviceStatsMonitor`` callback directly instead. - - log_every_n_steps: How often to log within steps. - Default: ``50``. - - prepare_data_per_node: If True, each LOCAL_RANK=0 will call prepare data. - Otherwise only NODE_RANK=0, LOCAL_RANK=0 will prepare data - - .. deprecated:: v1.5 - Deprecated in v1.5.0 and will be removed in v1.7.0 - Please set ``prepare_data_per_node`` in ``LightningDataModule`` and/or - ``LightningModule`` directly instead. - - process_position: Orders the progress bar when running multiple models on same machine. - - .. deprecated:: v1.5 - ``process_position`` has been deprecated in v1.5 and will be removed in v1.7. - Please pass :class:`~pytorch_lightning.callbacks.progress.TQDMProgressBar` with ``process_position`` - directly to the Trainer's ``callbacks`` argument instead. - - progress_bar_refresh_rate: How often to refresh progress bar (in steps). Value ``0`` disables progress bar. - Ignored when a custom progress bar is passed to :paramref:`~Trainer.callbacks`. Default: None, means - a suitable value will be chosen based on the environment (terminal, Google COLAB, etc.). - - .. deprecated:: v1.5 - ``progress_bar_refresh_rate`` has been deprecated in v1.5 and will be removed in v1.7. - Please pass :class:`~pytorch_lightning.callbacks.progress.TQDMProgressBar` with ``refresh_rate`` - directly to the Trainer's ``callbacks`` argument instead. To disable the progress bar, - pass ``enable_progress_bar = False`` to the Trainer. - - enable_progress_bar: Whether to enable to progress bar by default. - Default: ``False``. - - profiler: To profile individual steps during training and assist in identifying bottlenecks. - Default: ``None``. - - overfit_batches: Overfit a fraction of training data (float) or a set number of batches (int). - Default: ``0.0``. - - plugins: Plugins allow modification of core behavior like ddp and amp, and enable custom lightning plugins. - Default: ``None``. - - precision: Double precision (64), full precision (32), half precision (16) or bfloat16 precision (bf16). - Can be used on CPU, GPU, TPUs, HPUs or IPUs. - Default: ``32``. - - max_epochs: Stop training once this number of epochs is reached. Disabled by default (None). - If both max_epochs and max_steps are not specified, defaults to ``max_epochs = 1000``. - To enable infinite training, set ``max_epochs = -1``. - - min_epochs: Force training for at least these many epochs. Disabled by default (None). - - max_steps: Stop training after this number of steps. Disabled by default (-1). If ``max_steps = -1`` - and ``max_epochs = None``, will default to ``max_epochs = 1000``. To enable infinite training, set - ``max_epochs`` to ``-1``. - - min_steps: Force training for at least these number of steps. Disabled by default (``None``). - - max_time: Stop training after this amount of time has passed. Disabled by default (``None``). - The time duration can be specified in the format DD:HH:MM:SS (days, hours, minutes seconds), as a - :class:`datetime.timedelta`, or a dictionary with keys that will be passed to - :class:`datetime.timedelta`. - - num_nodes: Number of GPU nodes for distributed training. - Default: ``1``. - - num_processes: Number of processes for distributed training with ``accelerator="cpu"``. - Default: ``1``. - - num_sanity_val_steps: Sanity check runs n validation batches before starting the training routine. - Set it to `-1` to run all batches in all validation dataloaders. - Default: ``2``. - - reload_dataloaders_every_n_epochs: Set to a non-negative integer to reload dataloaders every n epochs. - Default: ``0``. - - replace_sampler_ddp: Explicitly enables or disables sampler replacement. If not specified this - will toggled automatically when DDP is used. By default it will add ``shuffle=True`` for - train sampler and ``shuffle=False`` for val/test sampler. If you want to customize it, - you can set ``replace_sampler_ddp=False`` and add your own distributed sampler. - - resume_from_checkpoint: Path/URL of the checkpoint from which training is resumed. If there is - no checkpoint file at the path, an exception is raised. If resuming from mid-epoch checkpoint, - training will start from the beginning of the next epoch. - - .. deprecated:: v1.5 - ``resume_from_checkpoint`` is deprecated in v1.5 and will be removed in v2.0. - Please pass the path to ``Trainer.fit(..., ckpt_path=...)`` instead. - - strategy: Supports different training strategies with aliases - as well custom strategies. - Default: ``None``. - - sync_batchnorm: Synchronize batch norm layers between process groups/whole world. - Default: ``False``. - - terminate_on_nan: If set to True, will terminate training (by raising a `ValueError`) at the - end of each training batch, if any of the parameters or the loss are NaN or +/-inf. - - .. deprecated:: v1.5 - Trainer argument ``terminate_on_nan`` was deprecated in v1.5 and will be removed in 1.7. - Please use ``detect_anomaly`` instead. - - detect_anomaly: Enable anomaly detection for the autograd engine. - Default: ``False``. - - tpu_cores: How many TPU cores to train on (1 or 8) / Single TPU to train on (1) - Default: ``None``. - - ipus: How many IPUs to train on. - Default: ``None``. - - track_grad_norm: -1 no tracking. Otherwise tracks that p-norm. May be set to 'inf' infinity-norm. If using - Automatic Mixed Precision (AMP), the gradients will be unscaled before logging them. - Default: ``-1``. - - val_check_interval: How often to check the validation set. Pass a ``float`` in the range [0.0, 1.0] to check - after a fraction of the training epoch. Pass an ``int`` to check after a fixed number of training - batches. - Default: ``1.0``. - - enable_model_summary: Whether to enable model summarization by default. - Default: ``True``. - - weights_summary: Prints a summary of the weights when training begins. - - .. deprecated:: v1.5 - ``weights_summary`` has been deprecated in v1.5 and will be removed in v1.7. - To disable the summary, pass ``enable_model_summary = False`` to the Trainer. - To customize the summary, pass :class:`~pytorch_lightning.callbacks.model_summary.ModelSummary` - directly to the Trainer's ``callbacks`` argument. - - weights_save_path: Where to save weights if specified. Will override default_root_dir - for checkpoints only. Use this if for whatever reason you need the checkpoints - stored in a different place than the logs written in `default_root_dir`. - Can be remote file paths such as `s3://mybucket/path` or 'hdfs://path/' - Defaults to `default_root_dir`. - - .. deprecated:: v1.6 - ``weights_save_path`` has been deprecated in v1.6 and will be removed in v1.8. Please pass - ``dirpath`` directly to the :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` - callback. - - move_metrics_to_cpu: Whether to force internal logged metrics to be moved to cpu. - This can save some gpu memory, but can make training slower. Use with attention. - Default: ``False``. - - multiple_trainloader_mode: How to loop over the datasets when there are multiple train loaders. - In 'max_size_cycle' mode, the trainer ends one epoch when the largest dataset is traversed, - and smaller datasets reload when running out of their data. In 'min_size' mode, all the datasets - reload when reaching the minimum length of datasets. - Default: ``"max_size_cycle"``. - - stochastic_weight_avg: Whether to use `Stochastic Weight Averaging (SWA) - <https://pytorch.org/blog/pytorch-1.6-now-includes-stochastic-weight-averaging/>`_. - Default: ``False``. - - .. deprecated:: v1.5 - ``stochastic_weight_avg`` has been deprecated in v1.5 and will be removed in v1.7. - Please pass :class:`~pytorch_lightning.callbacks.stochastic_weight_avg.StochasticWeightAveraging` - directly to the Trainer's ``callbacks`` argument instead. - """ - super().__init__() - Trainer._log_api_event("init") - log.detail(f"{self.__class__.__name__}: Initializing trainer with parameters: {locals()}") - self.state = TrainerState() - - # init connectors - self._data_connector = DataConnector(self, multiple_trainloader_mode) - - self._accelerator_connector = AcceleratorConnector( - num_processes=num_processes, - devices=devices, - tpu_cores=tpu_cores, - ipus=ipus, - accelerator=accelerator, - strategy=strategy, - gpus=gpus, - num_nodes=num_nodes, - sync_batchnorm=sync_batchnorm, - benchmark=benchmark, - replace_sampler_ddp=replace_sampler_ddp, - deterministic=deterministic, - auto_select_gpus=auto_select_gpus, - precision=precision, - amp_type=amp_backend, - amp_level=amp_level, - plugins=plugins, - ) - self._logger_connector = LoggerConnector(self, log_gpu_memory) - self._callback_connector = CallbackConnector(self) - self._checkpoint_connector = CheckpointConnector(self, resume_from_checkpoint) - self._signal_connector = SignalConnector(self) - self.tuner = Tuner(self) - - min_steps, max_steps, min_epochs, max_epochs, max_time = _parse_loop_limits( - min_steps, max_steps, min_epochs, max_epochs, max_time - ) - fit_loop = FitLoop(min_epochs=min_epochs, max_epochs=max_epochs) - training_epoch_loop = TrainingEpochLoop(min_steps=min_steps, max_steps=max_steps) - fit_loop.connect(epoch_loop=training_epoch_loop) - - # default .fit() loop - self.fit_loop = fit_loop - - # default .validate() loop - self.validate_loop = EvaluationLoop() - - # default .test() loop - self.test_loop = EvaluationLoop() - - # default .predict() loop - self.predict_loop = PredictionLoop() - - # set when a checkpoint is loaded via `Trainer.{fit,validate,test,predict}`. - self._ckpt_path: Optional[str] = None - - # .validate(), predict() and .test() set these when they load a checkpoint. They will be removed in favor of - # the unified read-only `Trainer.ckpt_path` attribute in v1.8 - self._validated_ckpt_path: Optional[str] = None # TODO: remove in v1.8 - self._tested_ckpt_path: Optional[str] = None # TODO: remove in v1.8 - self._predicted_ckpt_path: Optional[str] = None # TODO: remove in v1.8 - - # todo: remove in v1.7 - self._weights_summary: Optional[str] = None - - # init callbacks - # Declare attributes to be set in _callback_connector on_trainer_init - self._callback_connector.on_trainer_init( - callbacks, - checkpoint_callback, - enable_checkpointing, - enable_progress_bar, - progress_bar_refresh_rate, - process_position, - default_root_dir, - weights_save_path, - enable_model_summary, - weights_summary, - stochastic_weight_avg, - max_time, - accumulate_grad_batches, - ) - - # hook - self._call_callback_hooks("on_init_start") - - # init data flags - self.check_val_every_n_epoch: int - self._data_connector.on_trainer_init( - check_val_every_n_epoch, - reload_dataloaders_every_n_epochs, - prepare_data_per_node, - ) - - if terminate_on_nan is not None: - rank_zero_deprecation( - "Trainer argument `terminate_on_nan` was deprecated in v1.5 and will be removed in 1.7." - " Please use `Trainer(detect_anomaly=True)` instead." - ) - if not isinstance(terminate_on_nan, bool): - raise TypeError(f"`terminate_on_nan` should be a bool, got {terminate_on_nan}.") - - # gradient clipping - if gradient_clip_val is not None and not isinstance(gradient_clip_val, (int, float)): - raise TypeError(f"`gradient_clip_val` should be an int or a float. Got {gradient_clip_val}.") - - if gradient_clip_algorithm is not None and not GradClipAlgorithmType.supported_type( - gradient_clip_algorithm.lower() - ): - raise MisconfigurationException( - f"`gradient_clip_algorithm` {gradient_clip_algorithm} is invalid. " - f"Allowed algorithms: {GradClipAlgorithmType.supported_types()}." - ) - - # gradient norm tracking - if track_grad_norm != -1 and not ( - (isinstance(track_grad_norm, (int, float)) or track_grad_norm == "inf") and float(track_grad_norm) > 0 - ): - raise MisconfigurationException( - f"`track_grad_norm` must be a positive number or 'inf' (infinity norm). Got {track_grad_norm}." - ) - - self._terminate_on_nan = terminate_on_nan - self.gradient_clip_val: Union[int, float] = gradient_clip_val - self.gradient_clip_algorithm: Optional[GradClipAlgorithmType] = ( - GradClipAlgorithmType(gradient_clip_algorithm.lower()) if gradient_clip_algorithm is not None else None - ) - self.track_grad_norm: float = float(track_grad_norm) - - self._detect_anomaly: bool = detect_anomaly - self._setup_on_init(num_sanity_val_steps) - - # configure tuner - self.tuner.on_trainer_init(auto_lr_find, auto_scale_batch_size) - - # configure profiler - self.__init_profiler(profiler) - - # init logger flags - self._loggers: List[LightningLoggerBase] - self._logger_connector.on_trainer_init(logger, flush_logs_every_n_steps, log_every_n_steps, move_metrics_to_cpu) - - # init debugging flags - self.val_check_interval: Union[int, float] - self._init_debugging_flags( - limit_train_batches, - limit_val_batches, - limit_test_batches, - limit_predict_batches, - val_check_interval, - overfit_batches, - fast_dev_run, - ) - - # Callback system - self._call_callback_hooks("on_init_end")
- - def _init_debugging_flags( - self, - limit_train_batches: Optional[Union[int, float]], - limit_val_batches: Optional[Union[int, float]], - limit_test_batches: Optional[Union[int, float]], - limit_predict_batches: Optional[Union[int, float]], - val_check_interval: Optional[Union[int, float]], - overfit_batches: Union[int, float], - fast_dev_run: Union[int, bool], - ) -> None: - if isinstance(fast_dev_run, int) and (fast_dev_run < 0): - raise MisconfigurationException( - f"fast_dev_run={fast_dev_run} is not a valid configuration. It should be >= 0." - ) - - self.fast_dev_run = fast_dev_run - - # set fast_dev_run=True when it is 1, used while logging - if fast_dev_run == 1: - self.fast_dev_run = True - - if fast_dev_run: - num_batches = int(fast_dev_run) - limit_train_batches = num_batches - limit_val_batches = num_batches - limit_test_batches = num_batches - limit_predict_batches = num_batches - self.fit_loop.max_steps = num_batches - self.num_sanity_val_steps = 0 - self.fit_loop.max_epochs = 1 - val_check_interval = 1.0 - self.check_val_every_n_epoch = 1 - self.loggers = [DummyLogger()] if self.loggers else [] - - rank_zero_info( - "Running in fast_dev_run mode: will run a full train," - f" val, test and prediction loop using {num_batches} batch(es)." - ) - - self.limit_train_batches = _determine_batch_limits(limit_train_batches, "limit_train_batches") - self.limit_val_batches = _determine_batch_limits(limit_val_batches, "limit_val_batches") - self.limit_test_batches = _determine_batch_limits(limit_test_batches, "limit_test_batches") - self.limit_predict_batches = _determine_batch_limits(limit_predict_batches, "limit_predict_batches") - self.val_check_interval = _determine_batch_limits(val_check_interval, "val_check_interval") - self.overfit_batches = _determine_batch_limits(overfit_batches, "overfit_batches") - self._determine_data_use_amount(self.overfit_batches) - - def _determine_data_use_amount(self, overfit_batches: float) -> None: - """Use less data for debugging purposes.""" - if overfit_batches > 0: - self.limit_train_batches = overfit_batches - self.limit_val_batches = 0 - - def _setup_on_init(self, num_sanity_val_steps: int) -> None: - self._log_device_info() - - self.should_stop = False - self.state = TrainerState() - self.num_training_batches = float("inf") - self.train_dataloader = None - - if num_sanity_val_steps == -1: - self.num_sanity_val_steps = float("inf") - else: - self.num_sanity_val_steps = num_sanity_val_steps - - self.num_sanity_val_batches = [] - self.num_test_batches = [] - self.num_val_batches = [] - self.test_dataloaders = None - self.val_dataloaders = None - self._last_train_dl_reload_epoch = float("-inf") - self._last_val_dl_reload_epoch = float("-inf") - - self.num_predict_batches = [] - - def _call_and_handle_interrupt(self, trainer_fn: Callable, *args: Any, **kwargs: Any) -> Any: - r""" - Error handling, intended to be used only for main trainer function entry points (fit, validate, test, predict) - as all errors should funnel through them - - Args: - trainer_fn: one of (fit, validate, test, predict) - *args: positional arguments to be passed to the `trainer_fn` - **kwargs: keyword arguments to be passed to `trainer_fn` - """ - try: - if self.strategy.launcher is not None: - return self.strategy.launcher.launch(trainer_fn, *args, trainer=self, **kwargs) - else: - return trainer_fn(*args, **kwargs) - # TODO: treat KeyboardInterrupt as BaseException (delete the code below) in v1.7 - except KeyboardInterrupt as exception: - rank_zero_warn("Detected KeyboardInterrupt, attempting graceful shutdown...") - # user could press Ctrl+c many times... only shutdown once - if not self.interrupted: - self.state.status = TrainerStatus.INTERRUPTED - self._call_callback_hooks("on_keyboard_interrupt") - self._call_callback_hooks("on_exception", exception) - except BaseException as exception: - self.state.status = TrainerStatus.INTERRUPTED - if distributed_available() and self.world_size > 1: - # try syncing remaining processes, kill otherwise - self.strategy.reconciliate_processes(traceback.format_exc()) - self._call_callback_hooks("on_exception", exception) - self._teardown() - # teardown might access the stage so we reset it after - self.state.stage = None - raise - -
[docs] def fit( - self, - model: "pl.LightningModule", - train_dataloaders: Optional[Union[TRAIN_DATALOADERS, LightningDataModule]] = None, - val_dataloaders: Optional[EVAL_DATALOADERS] = None, - datamodule: Optional[LightningDataModule] = None, - ckpt_path: Optional[str] = None, - ) -> None: - r""" - Runs the full optimization routine. - - Args: - model: Model to fit. - - train_dataloaders: A collection of :class:`torch.utils.data.DataLoader` or a - :class:`~pytorch_lightning.core.datamodule.LightningDataModule` specifying training samples. - In the case of multiple dataloaders, please see this :ref:`section <multiple-dataloaders>`. - - val_dataloaders: A :class:`torch.utils.data.DataLoader` or a sequence of them specifying validation samples. - - ckpt_path: Path/URL of the checkpoint from which training is resumed. If there is - no checkpoint file at the path, an exception is raised. If resuming from mid-epoch checkpoint, - training will start from the beginning of the next epoch. - - datamodule: An instance of :class:`~pytorch_lightning.core.datamodule.LightningDataModule`. - """ - self.strategy.model = model - self._call_and_handle_interrupt( - self._fit_impl, model, train_dataloaders, val_dataloaders, datamodule, ckpt_path - )
- - def _fit_impl( - self, - model: "pl.LightningModule", - train_dataloaders: Optional[Union[TRAIN_DATALOADERS, LightningDataModule]] = None, - val_dataloaders: Optional[EVAL_DATALOADERS] = None, - datamodule: Optional[LightningDataModule] = None, - ckpt_path: Optional[str] = None, - ) -> None: - Trainer._log_api_event("fit") - log.detail(f"{self.__class__.__name__}: trainer fit stage") - - self.state.fn = TrainerFn.FITTING - self.state.status = TrainerStatus.RUNNING - self.training = True - self._last_train_dl_reload_epoch = float("-inf") - self._last_val_dl_reload_epoch = float("-inf") - - # if a datamodule comes in as the second arg, then fix it for the user - if isinstance(train_dataloaders, LightningDataModule): - datamodule = train_dataloaders - train_dataloaders = None - # If you supply a datamodule you can't supply train_dataloader or val_dataloaders - if (train_dataloaders is not None or val_dataloaders is not None) and datamodule is not None: - raise MisconfigurationException( - "You cannot pass `train_dataloader` or `val_dataloaders` to `trainer.fit(datamodule=...)`" - ) - - # links data to the trainer - self._data_connector.attach_data( - model, train_dataloaders=train_dataloaders, val_dataloaders=val_dataloaders, datamodule=datamodule - ) - - # TODO: ckpt_path only in v2.0 - ckpt_path = ckpt_path or self.resume_from_checkpoint - self._ckpt_path = self.__set_ckpt_path( - ckpt_path, model_provided=True, model_connected=self.lightning_module is not None - ) - results = self._run(model, ckpt_path=self.ckpt_path) - - assert self.state.stopped - self.training = False - return results - -
[docs] def validate( - self, - model: Optional["pl.LightningModule"] = None, - dataloaders: Optional[Union[EVAL_DATALOADERS, LightningDataModule]] = None, - ckpt_path: Optional[str] = None, - verbose: bool = True, - datamodule: Optional[LightningDataModule] = None, - ) -> _EVALUATE_OUTPUT: - r""" - Perform one evaluation epoch over the validation set. - - Args: - model: The model to validate. - - dataloaders: A :class:`torch.utils.data.DataLoader` or a sequence of them, - or a :class:`~pytorch_lightning.core.datamodule.LightningDataModule` specifying validation samples. - - ckpt_path: Either ``best`` or path to the checkpoint you wish to validate. - If ``None`` and the model instance was passed, use the current weights. - Otherwise, the best model checkpoint from the previous ``trainer.fit`` call will be loaded - if a checkpoint callback is configured. - - verbose: If True, prints the validation results. - - datamodule: An instance of :class:`~pytorch_lightning.core.datamodule.LightningDataModule`. - - Returns: - List of dictionaries with metrics logged during the validation phase, e.g., in model- or callback hooks - like :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step`, - :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_epoch_end`, etc. - The length of the list corresponds to the number of validation dataloaders used. - """ - self.strategy.model = model or self.lightning_module - return self._call_and_handle_interrupt(self._validate_impl, model, dataloaders, ckpt_path, verbose, datamodule)
- - def _validate_impl( - self, - model: Optional["pl.LightningModule"] = None, - dataloaders: Optional[Union[EVAL_DATALOADERS, LightningDataModule]] = None, - ckpt_path: Optional[str] = None, - verbose: bool = True, - datamodule: Optional[LightningDataModule] = None, - ) -> _EVALUATE_OUTPUT: - # -------------------- - # SETUP HOOK - # -------------------- - Trainer._log_api_event("validate") - log.detail(f"{self.__class__.__name__}: trainer validate stage") - - self.state.fn = TrainerFn.VALIDATING - self.state.status = TrainerStatus.RUNNING - self.validating = True - - # if a datamodule comes in as the second arg, then fix it for the user - if isinstance(dataloaders, LightningDataModule): - datamodule = dataloaders - dataloaders = None - # If you supply a datamodule you can't supply val_dataloaders - if dataloaders is not None and datamodule: - raise MisconfigurationException("You cannot pass both `trainer.validate(dataloaders=..., datamodule=...)`") - - model_provided = model is not None - model = model or self.lightning_module - if model is None: - raise MisconfigurationException( - "`model` must be provided to `trainer.validate()` when it hasn't been passed in a previous run" - ) - - self.validate_loop.verbose = verbose - - # links data to the trainer - self._data_connector.attach_data(model, val_dataloaders=dataloaders, datamodule=datamodule) - - self._ckpt_path = self.__set_ckpt_path( - ckpt_path, model_provided=model_provided, model_connected=self.lightning_module is not None - ) - - self._validated_ckpt_path = self.ckpt_path # TODO: remove in v1.8 - - # run validate - results = self._run(model, ckpt_path=self.ckpt_path) - - assert self.state.stopped - self.validating = False - - return results - -
[docs] def test( - self, - model: Optional["pl.LightningModule"] = None, - dataloaders: Optional[Union[EVAL_DATALOADERS, LightningDataModule]] = None, - ckpt_path: Optional[str] = None, - verbose: bool = True, - datamodule: Optional[LightningDataModule] = None, - ) -> _EVALUATE_OUTPUT: - r""" - Perform one evaluation epoch over the test set. - It's separated from fit to make sure you never run on your test set until you want to. - - Args: - model: The model to test. - - dataloaders: A :class:`torch.utils.data.DataLoader` or a sequence of them, - or a :class:`~pytorch_lightning.core.datamodule.LightningDataModule` specifying test samples. - - ckpt_path: Either ``best`` or path to the checkpoint you wish to test. - If ``None`` and the model instance was passed, use the current weights. - Otherwise, the best model checkpoint from the previous ``trainer.fit`` call will be loaded - if a checkpoint callback is configured. - - verbose: If True, prints the test results. - - datamodule: An instance of :class:`~pytorch_lightning.core.datamodule.LightningDataModule`. - - Returns: - List of dictionaries with metrics logged during the test phase, e.g., in model- or callback hooks - like :meth:`~pytorch_lightning.core.lightning.LightningModule.test_step`, - :meth:`~pytorch_lightning.core.lightning.LightningModule.test_epoch_end`, etc. - The length of the list corresponds to the number of test dataloaders used. - """ - self.strategy.model = model or self.lightning_module - return self._call_and_handle_interrupt(self._test_impl, model, dataloaders, ckpt_path, verbose, datamodule)
- - def _test_impl( - self, - model: Optional["pl.LightningModule"] = None, - dataloaders: Optional[Union[EVAL_DATALOADERS, LightningDataModule]] = None, - ckpt_path: Optional[str] = None, - verbose: bool = True, - datamodule: Optional[LightningDataModule] = None, - ) -> _EVALUATE_OUTPUT: - # -------------------- - # SETUP HOOK - # -------------------- - Trainer._log_api_event("test") - log.detail(f"{self.__class__.__name__}: trainer test stage") - - self.state.fn = TrainerFn.TESTING - self.state.status = TrainerStatus.RUNNING - self.testing = True - - # if a datamodule comes in as the second arg, then fix it for the user - if isinstance(dataloaders, LightningDataModule): - datamodule = dataloaders - dataloaders = None - # If you supply a datamodule you can't supply test_dataloaders - if dataloaders is not None and datamodule: - raise MisconfigurationException("You cannot pass both `trainer.test(dataloaders=..., datamodule=...)`") - - model_provided = model is not None - model = model or self.lightning_module - if model is None: - raise MisconfigurationException( - "`model` must be provided to `trainer.test()` when it hasn't been passed in a previous run" - ) - - self.test_loop.verbose = verbose - - # links data to the trainer - self._data_connector.attach_data(model, test_dataloaders=dataloaders, datamodule=datamodule) - - self._ckpt_path = self.__set_ckpt_path( - ckpt_path, model_provided=model_provided, model_connected=self.lightning_module is not None - ) - - self._tested_ckpt_path = self.ckpt_path # TODO: remove in v1.8 - - # run test - results = self._run(model, ckpt_path=self.ckpt_path) - - assert self.state.stopped - self.testing = False - - return results - -
[docs] def predict( - self, - model: Optional["pl.LightningModule"] = None, - dataloaders: Optional[Union[EVAL_DATALOADERS, LightningDataModule]] = None, - datamodule: Optional[LightningDataModule] = None, - return_predictions: Optional[bool] = None, - ckpt_path: Optional[str] = None, - ) -> Optional[_PREDICT_OUTPUT]: - r""" - Run inference on your data. - This will call the model forward function to compute predictions. Useful to perform distributed - and batched predictions. Logging is disabled in the predict hooks. - - Args: - model: The model to predict with. - - dataloaders: A :class:`torch.utils.data.DataLoader` or a sequence of them, - or a :class:`~pytorch_lightning.core.datamodule.LightningDataModule` specifying prediction samples. - - datamodule: The datamodule with a predict_dataloader method that returns one or more dataloaders. - - return_predictions: Whether to return predictions. - ``True`` by default except when an accelerator that spawns processes is used (not supported). - - ckpt_path: Either ``best`` or path to the checkpoint you wish to predict. - If ``None`` and the model instance was passed, use the current weights. - Otherwise, the best model checkpoint from the previous ``trainer.fit`` call will be loaded - if a checkpoint callback is configured. - - Returns: - Returns a list of dictionaries, one for each provided dataloader containing their respective predictions. - """ - self.strategy.model = model or self.lightning_module - return self._call_and_handle_interrupt( - self._predict_impl, model, dataloaders, datamodule, return_predictions, ckpt_path - )
- - def _predict_impl( - self, - model: Optional["pl.LightningModule"] = None, - dataloaders: Optional[Union[EVAL_DATALOADERS, LightningDataModule]] = None, - datamodule: Optional[LightningDataModule] = None, - return_predictions: Optional[bool] = None, - ckpt_path: Optional[str] = None, - ) -> Optional[_PREDICT_OUTPUT]: - # -------------------- - # SETUP HOOK - # -------------------- - Trainer._log_api_event("predict") - log.detail(f"{self.__class__.__name__}: trainer predict stage") - - self.state.fn = TrainerFn.PREDICTING - self.state.status = TrainerStatus.RUNNING - self.predicting = True - - self.predict_loop.return_predictions = return_predictions - - # if a datamodule comes in as the second arg, then fix it for the user - if isinstance(dataloaders, LightningDataModule): - datamodule = dataloaders - dataloaders = None - if dataloaders is not None and datamodule: - raise MisconfigurationException("You cannot pass both `trainer.predict(dataloaders=..., datamodule=...)`") - - model_provided = model is not None - model = model or self.lightning_module - if model is None: - raise MisconfigurationException( - "`model` must be provided to `trainer.predict()` when it hasn't been passed in a previous run" - ) - - # links data to the trainer - self._data_connector.attach_data(model, predict_dataloaders=dataloaders, datamodule=datamodule) - - self._ckpt_path = self.__set_ckpt_path( - ckpt_path, model_provided=model_provided, model_connected=self.lightning_module is not None - ) - - self._predicted_ckpt_path = self.ckpt_path # TODO: remove in v1.8 - - results = self._run(model, ckpt_path=self.ckpt_path) - - assert self.state.stopped - self.predicting = False - - return results - -
[docs] def tune( - self, - model: "pl.LightningModule", - train_dataloaders: Optional[Union[TRAIN_DATALOADERS, LightningDataModule]] = None, - val_dataloaders: Optional[EVAL_DATALOADERS] = None, - datamodule: Optional[LightningDataModule] = None, - scale_batch_size_kwargs: Optional[Dict[str, Any]] = None, - lr_find_kwargs: Optional[Dict[str, Any]] = None, - ) -> Dict[str, Optional[Union[int, _LRFinder]]]: - r""" - Runs routines to tune hyperparameters before training. - - Args: - model: Model to tune. - - train_dataloaders: A collection of :class:`torch.utils.data.DataLoader` or a - :class:`~pytorch_lightning.core.datamodule.LightningDataModule` specifying training samples. - In the case of multiple dataloaders, please see this :ref:`section <multiple-dataloaders>`. - - val_dataloaders: A :class:`torch.utils.data.DataLoader` or a sequence of them specifying validation samples. - - datamodule: An instance of :class:`~pytorch_lightning.core.datamodule.LightningDataModule`. - - scale_batch_size_kwargs: Arguments for :func:`~pytorch_lightning.tuner.batch_size_scaling.scale_batch_size` - - lr_find_kwargs: Arguments for :func:`~pytorch_lightning.tuner.lr_finder.lr_find` - """ - Trainer._log_api_event("tune") - - self.state.fn = TrainerFn.TUNING - self.state.status = TrainerStatus.RUNNING - self.tuning = True - - # if a datamodule comes in as the second arg, then fix it for the user - if isinstance(train_dataloaders, LightningDataModule): - datamodule = train_dataloaders - train_dataloaders = None - # If you supply a datamodule you can't supply train_dataloader or val_dataloaders - if (train_dataloaders is not None or val_dataloaders is not None) and datamodule is not None: - raise MisconfigurationException( - "You cannot pass `train_dataloader` or `val_dataloaders` to `trainer.tune(datamodule=...)`" - ) - - # links data to the trainer - self._data_connector.attach_data( - model, train_dataloaders=train_dataloaders, val_dataloaders=val_dataloaders, datamodule=datamodule - ) - - with isolate_rng(): - result = self.tuner._tune( - model, scale_batch_size_kwargs=scale_batch_size_kwargs, lr_find_kwargs=lr_find_kwargs - ) - - assert self.state.stopped - self.tuning = False - - return result
- - def _restore_modules_and_callbacks(self, checkpoint_path: Optional[_PATH] = None) -> None: - # restore modules after setup - self._checkpoint_connector.resume_start(checkpoint_path) - self._checkpoint_connector._restore_quantization_callbacks() - self._checkpoint_connector.restore_model() - self._checkpoint_connector.restore_datamodule() - if self.state.fn == TrainerFn.FITTING: - # restore callback states - self._checkpoint_connector.restore_callbacks() - - def _run( - self, model: "pl.LightningModule", ckpt_path: Optional[str] = None - ) -> Optional[Union[_EVALUATE_OUTPUT, _PREDICT_OUTPUT]]: - # clean hparams - if hasattr(model, "hparams"): - parsing.clean_namespace(model.hparams) - - # attach model to the strategy - self.strategy.connect(model) - - self._callback_connector._attach_model_callbacks() - self._callback_connector._attach_model_logging_functions() - - verify_loop_configurations(self) - - # hook - log.detail(f"{self.__class__.__name__}: preparing data") - self._data_connector.prepare_data() - - # ---------------------------- - # SET UP TRAINING - # ---------------------------- - self._call_callback_hooks("on_before_accelerator_backend_setup") - log.detail(f"{self.__class__.__name__}: setting up strategy environment") - self.strategy.setup_environment() - self.__setup_profiler() - - self._call_setup_hook() # allow user to setup lightning_module in accelerator environment - - # check if we should delay restoring checkpoint till later - if not self.strategy.restore_checkpoint_after_setup: - log.detail(f"{self.__class__.__name__}: restoring module and callbacks from checkpoint path: {ckpt_path}") - self._restore_modules_and_callbacks(ckpt_path) - - log.detail(f"{self.__class__.__name__}: configuring sharded model") - self._call_configure_sharded_model() # allow user to setup in model sharded environment - - # ---------------------------- - # INSPECT THE CORE LOOPS - # ---------------------------- - rf""" - Lightning internal flow looks like this: - {Trainer.fit} or {Trainer.test} or {Trainer.predict} || - | || - spawn processes || - {self.strategy.setup_environment} || - | || - setup accelerator || - and strategy || LIGHTNING - | || - {self._run_stage} || FLOW - | || - {self._run_train} || DIRECTION - or {self._run_evaluate} || - or {self._run_predict} || - | || - results \/ - This is used to guide readers to the core loops: train, test, predict. - {self._run_predict} is the simplest to understand, use `Go to Definition` to read it :) - """ - - # ---------------------------- - # TRAIN - # ---------------------------- - - # reset logger connector - self._logger_connector.reset_results() - self._logger_connector.reset_metrics() - - # strategy will configure model and move it to the device - self.strategy.setup(self) - - # hook - if self.state.fn == TrainerFn.FITTING: - self._call_callback_hooks("on_fit_start") - self._call_lightning_module_hook("on_fit_start") - - self._log_hyperparams() - - if self.strategy.restore_checkpoint_after_setup: - log.detail(f"{self.__class__.__name__}: restoring module and callbacks from checkpoint path: {ckpt_path}") - self._restore_modules_and_callbacks(ckpt_path) - - # restore optimizers, etc. - log.detail(f"{self.__class__.__name__}: restoring training state") - self._checkpoint_connector.restore_training_state() - - self._checkpoint_connector.resume_end() - - results = self._run_stage() - - log.detail(f"{self.__class__.__name__}: trainer tearing down") - self._teardown() - - # ---------------------------- - # POST-Training CLEAN UP - # ---------------------------- - # hook - if self.state.fn == TrainerFn.FITTING: - self._call_callback_hooks("on_fit_end") - self._call_lightning_module_hook("on_fit_end") - - log.detail(f"{self.__class__.__name__}: calling teardown hooks") - self._call_teardown_hook() - - self.state.status = TrainerStatus.FINISHED - self.state.stage = None - - return results - - def _log_hyperparams(self) -> None: - if not self.loggers: - return - # log hyper-parameters - hparams_initial = None - - # save exp to get started (this is where the first experiment logs are written) - datamodule_log_hyperparams = self.datamodule._log_hyperparams if self.datamodule is not None else False - - if self.lightning_module._log_hyperparams and datamodule_log_hyperparams: - datamodule_hparams = self.datamodule.hparams_initial - lightning_hparams = self.lightning_module.hparams_initial - inconsistent_keys = [] - for key in lightning_hparams.keys() & datamodule_hparams.keys(): - lm_val, dm_val = lightning_hparams[key], datamodule_hparams[key] - if type(lm_val) != type(dm_val): - inconsistent_keys.append(key) - elif isinstance(lm_val, torch.Tensor) and id(lm_val) != id(dm_val): - inconsistent_keys.append(key) - elif lm_val != dm_val: - inconsistent_keys.append(key) - if inconsistent_keys: - raise MisconfigurationException( - f"Error while merging hparams: the keys {inconsistent_keys} are present " - "in both the LightningModule's and LightningDataModule's hparams " - "but have different values." - ) - hparams_initial = {**lightning_hparams, **datamodule_hparams} - elif self.lightning_module._log_hyperparams: - hparams_initial = self.lightning_module.hparams_initial - elif datamodule_log_hyperparams: - hparams_initial = self.datamodule.hparams_initial - - for logger in self.loggers: - if hparams_initial is not None: - logger.log_hyperparams(hparams_initial) - logger.log_graph(self.lightning_module) - logger.save() - - def _teardown(self): - """This is the Trainer's internal teardown, unrelated to the `teardown` hooks in LightningModule and - Callback; those are handled by :meth:`_call_teardown_hook`.""" - self.strategy.post_dispatch(self) - self.strategy.teardown() - loop = self._active_loop - # loop should never be `None` here but it can because we don't know the trainer stage with `ddp_spawn` - if loop is not None: - loop.teardown() - self._logger_connector.teardown() - self._signal_connector.teardown() - - def run_stage(self) -> None: - rank_zero_deprecation( - "`Trainer.run_stage` is deprecated in v1.6 and will be removed in v1.8. Use" - " `Trainer.{fit,validate,test,predict}` instead." - ) - return self._run_stage() - - def _run_stage(self): - self.strategy.barrier("run-stage") - self.strategy.dispatch(self) - - if self.evaluating: - return self._run_evaluate() - if self.predicting: - return self._run_predict() - return self._run_train() - - def _pre_training_routine(self): - # wait for all to join if on distributed - self.strategy.barrier("setup_training") - - # register signals - self._signal_connector.register_signal_handlers() - - # -------------------------- - # Pre-train - # -------------------------- - self._call_callback_hooks("on_pretrain_routine_start") - self._call_lightning_module_hook("on_pretrain_routine_start") - - self._call_callback_hooks("on_pretrain_routine_end") - self._call_lightning_module_hook("on_pretrain_routine_end") - - def _run_train(self) -> None: - self._pre_training_routine() - - with isolate_rng(): - self._run_sanity_check() - - # enable train mode - self.model.train() - torch.set_grad_enabled(True) - - self.fit_loop.trainer = self - with torch.autograd.set_detect_anomaly(self._detect_anomaly): - self.fit_loop.run() - - def _run_evaluate(self) -> _EVALUATE_OUTPUT: - assert self.evaluating - - # reload dataloaders - self._evaluation_loop._reload_evaluation_dataloaders() - - # reset trainer on this loop and all child loops in case user connected a custom loop - self._evaluation_loop.trainer = self - - with self.profiler.profile(f"run_{self.state.stage}_evaluation"), torch.no_grad(): - eval_loop_results = self._evaluation_loop.run() - - # remove the tensors from the eval results - for result in eval_loop_results: - if isinstance(result, dict): - for k, v in result.items(): - if isinstance(v, torch.Tensor): - result[k] = v.cpu().item() - - return eval_loop_results - - def _run_predict(self) -> Optional[_PREDICT_OUTPUT]: - self.reset_predict_dataloader(self.lightning_module) - # reset trainer on this loop and all child loops in case user connected a custom loop - self.predict_loop.trainer = self - with torch.no_grad(): - return self.predict_loop.run() - - def _run_sanity_check(self) -> None: - val_loop = self.fit_loop.epoch_loop.val_loop - - should_sanity_check = ( - self.enable_validation - and self.num_sanity_val_steps > 0 - # do not sanity check if restarting because it would mess up the loaded state - and not val_loop.restarting - ) - - # run tiny validation (if validation defined) - # to make sure program won't crash during val - if should_sanity_check: - stage = self.state.stage - self.sanity_checking = True - - # reset logger connector - self._logger_connector.reset_results() - self._logger_connector.reset_metrics() - - self._call_callback_hooks("on_sanity_check_start") - - # reload dataloaders - val_loop._reload_evaluation_dataloaders() - self.num_sanity_val_batches = [ - min(self.num_sanity_val_steps, val_batches) for val_batches in self.num_val_batches - ] - - # run eval step - with torch.no_grad(): - val_loop.run() - - self._call_callback_hooks("on_sanity_check_end") - - # reset logger connector - self._logger_connector.reset_results() - self._logger_connector.reset_metrics() - - # reset the progress tracking state after sanity checking. we don't need to set the state before - # because sanity check only runs when we are not restarting - _reset_progress(val_loop) - - # restore the previous stage when the sanity check if finished - self.state.stage = stage - - def __set_ckpt_path(self, ckpt_path: Optional[str], model_provided: bool, model_connected: bool) -> Optional[str]: - # fault-tolerance takes precedence - from pytorch_lightning.callbacks.fault_tolerance import _FaultToleranceCheckpoint - - ft_checkpoints = [cb for cb in self.callbacks if isinstance(cb, _FaultToleranceCheckpoint)] - if ft_checkpoints: - ft_ckpt_path = ft_checkpoints[0].ckpt_path - fs = get_filesystem(ft_ckpt_path) - if fs.exists(ft_ckpt_path): - return ft_ckpt_path - - if model_provided and ckpt_path is None: - # use passed model to function without loading weights - return - - fn = self.state.fn.value - - if model_connected and ckpt_path is None: - rank_zero_warn( - f"`.{fn}(ckpt_path=None)` was called without a model." - " The best model of the previous `fit` call will be used." - f" You can pass `{fn}(ckpt_path='best')` to use and best model" - " checkpoint and avoid this warning or" - " `ckpt_path=trainer.checkpoint_callback.last_model_path` to use the last model." - ) - ckpt_path = "best" - - if ckpt_path == "best": - if len(self.checkpoint_callbacks) > 1: - rank_zero_warn( - f'`.{fn}(ckpt_path="best")` is called with Trainer configured with multiple `ModelCheckpoint`' - " callbacks. It will use the best checkpoint path from first checkpoint callback." - ) - - if not self.checkpoint_callback: - raise MisconfigurationException( - f'`.{fn}(ckpt_path="best")` is set but `ModelCheckpoint` is not configured.' - ) - - if not self.checkpoint_callback.best_model_path: - if self.fast_dev_run: - raise MisconfigurationException( - f'You cannot execute `.{fn}(ckpt_path="best")` with `fast_dev_run=True`.' - f" Please pass an exact checkpoint path to `.{fn}(ckpt_path=...)`" - ) - raise MisconfigurationException( - f'`.{fn}(ckpt_path="best")` is set but `ModelCheckpoint` is not configured to save the best model.' - ) - # load best weights - ckpt_path = self.checkpoint_callback.best_model_path - - if not ckpt_path: - raise MisconfigurationException( - f"`.{fn}()` found no path for the best weights: {ckpt_path!r}. Please" - f" specify a path for a checkpoint `.{fn}(ckpt_path=PATH)`" - ) - return ckpt_path - - def _call_setup_hook(self) -> None: - fn = self.state.fn._setup_fn - - self.strategy.barrier("pre_setup") - - if self.datamodule is not None: - self.datamodule.setup(stage=fn) - self._call_callback_hooks("setup", stage=fn) - self._call_lightning_module_hook("setup", stage=fn) - - self.strategy.barrier("post_setup") - - def _call_configure_sharded_model(self) -> None: - with self.strategy.model_sharded_context(): - self._handle_meta_model() - self._call_lightning_module_hook("configure_sharded_model") - self._call_callback_hooks("on_configure_sharded_model") - - def _handle_meta_model(self) -> None: - if not is_on_meta_device(self.lightning_module): - return - - if isinstance(self.strategy, DDPSpawnStrategy): - raise MisconfigurationException("LightningModule on meta device isn't supported with spawn.") - - materialize_module(self.lightning_module) - # the trainer reference is lost during materialization - self.lightning_module.trainer = proxy(self) - - def _call_teardown_hook(self) -> None: - fn = self.state.fn._setup_fn - - if self.datamodule is not None: - self.datamodule.teardown(stage=fn) - - self._call_callback_hooks("teardown", stage=fn) - self._call_lightning_module_hook("teardown", stage=fn) - - self.lightning_module._current_fx_name = None - # these could have become stale if metrics are defined in `setup` - self.lightning_module._metric_attributes = None - - # todo: TPU 8 cores hangs in flush with TensorBoard. Might do for all loggers. - # It might be related to xla tensors blocked when moving the cpu kill loggers. - for logger in self.loggers: - logger.finalize("success") - - # summarize profile results - self.profiler.describe() - - def call_hook( - self, hook_name: str, *args: Any, pl_module: Optional["pl.LightningModule"] = None, **kwargs: Any - ) -> Any: - r""" - .. deprecated:: v1.6 - The Trainer's `call_hook` method was deprecated in v1.6 and will be removed in v1.8. - """ - rank_zero_deprecation("The Trainer's `call_hook` method was deprecated in v1.6 and will be removed in v1.8.") - pl_module = self.lightning_module or pl_module - if pl_module: - prev_fx_name = pl_module._current_fx_name - pl_module._current_fx_name = hook_name - - # always profile hooks - with self.profiler.profile(hook_name): - - # first call trainer hook - callback_fx = getattr(self, hook_name, None) - if callable(callback_fx): - callback_fx(*args, **kwargs) - - # next call hook in lightningModule - output = None - model_fx = getattr(pl_module, hook_name, None) - if callable(model_fx): - output = model_fx(*args, **kwargs) - - # call the strategy hook - if hook_name not in ("setup", "teardown", "on_train_start") and hasattr(self.strategy, hook_name): - strategy_hook = getattr(self.strategy, hook_name) - strategy_output = strategy_hook(*args, **kwargs) - output = strategy_output if output is None else output - - if pl_module: - # restore current_fx when nested context - pl_module._current_fx_name = prev_fx_name - - return output - - def _call_lightning_module_hook( - self, - hook_name: str, - *args: Any, - pl_module: Optional["pl.LightningModule"] = None, - **kwargs: Any, - ) -> Any: - pl_module = pl_module or self.lightning_module - - if pl_module is None: - raise TypeError("No Lightning Module is available to call hooks on") - - fn = getattr(pl_module, hook_name) - if not callable(fn): - return - - prev_fx_name = pl_module._current_fx_name - pl_module._current_fx_name = hook_name - - with self.profiler.profile(f"[LightningModule]{pl_module.__class__.__name__}.{hook_name}"): - output = fn(*args, **kwargs) - - # restore current_fx when nested context - pl_module._current_fx_name = prev_fx_name - - return output - - def _call_callback_hooks( - self, - hook_name: str, - *args: Any, - **kwargs: Any, - ) -> None: - log.debug(f"{self.__class__.__name__}: calling callback hook: {hook_name}") - # TODO: remove if block in v1.8 - if hook_name in ("on_init_start", "on_init_end"): - # these `Callback` hooks are the only ones that do not take a lightning module. - # we also don't profile bc profiler hasn't been set yet - for callback in self.callbacks: - fn = getattr(callback, hook_name) - if callable(fn): - fn(self, *args, **kwargs) - return - - pl_module = self.lightning_module - if pl_module: - prev_fx_name = pl_module._current_fx_name - pl_module._current_fx_name = hook_name - - # TODO: remove if block in v1.7 - if hook_name == "on_train_batch_start": - with self.profiler.profile(hook_name): - self._on_train_batch_start(*args, **kwargs) - elif hook_name == "on_train_batch_end": - with self.profiler.profile(hook_name): - self._on_train_batch_end(*args, **kwargs) - else: - for callback in self.callbacks: - fn = getattr(callback, hook_name) - if callable(fn): - with self.profiler.profile(f"[Callback]{callback.state_key}.{hook_name}"): - fn(self, self.lightning_module, *args, **kwargs) - - if pl_module: - # restore current_fx when nested context - pl_module._current_fx_name = prev_fx_name - - # TODO: Delete this in v1.7 (deprecations: #9816 and #11148) - def _on_train_batch_start(self, batch, batch_idx, dataloader_idx=0): - r"""Called when the training batch begins. This function is needed because of two different deprecations affecting - the original function in TrainerCallbackHookMixin: #9816 and #11148. - """ - for callback in self.callbacks: - if is_param_in_hook_signature(callback.on_train_batch_start, "dataloader_idx", explicit=True): - callback.on_train_batch_start(self, self.lightning_module, batch, batch_idx, 0) - else: - callback.on_train_batch_start(self, self.lightning_module, batch, batch_idx) - - # TODO: Delete this in v1.7 (deprecations: #9816 and #11148) - def _on_train_batch_end(self, outputs: STEP_OUTPUT, batch, batch_idx, dataloader_idx=0): - r"""Called when the training batch ends. This function is needed because of two different deprecations affecting - the original function in TrainerCallbackHookMixin: #9816 and #11148. - """ - for callback in self.callbacks: - if is_param_in_hook_signature(callback.on_train_batch_end, "dataloader_idx", explicit=True): - callback.on_train_batch_end(self, self.lightning_module, outputs, batch, batch_idx, 0) - else: - callback.on_train_batch_end(self, self.lightning_module, outputs, batch, batch_idx) - - def _call_callbacks_state_dict(self) -> Dict[str, dict]: - """Called when saving a model checkpoint, calls and returns every callback's `state_dict`, keyed by - `Callback.state_key`.""" - callback_state_dicts = {} - for callback in self.callbacks: - state_dict = callback.state_dict() - if state_dict: - callback_state_dicts[callback.state_key] = state_dict - return callback_state_dicts - - def _call_callbacks_on_save_checkpoint(self, checkpoint: Dict[str, Any]) -> None: - """Called when saving a model checkpoint, calls every callback's `on_save_checkpoint` hook. - - Will be removed in v1.8: If state is returned, we insert the callback state into - ``checkpoint["callbacks"][Callback.state_key]``. It overrides ``state_dict`` if already present. - """ - pl_module = self.lightning_module - if pl_module: - prev_fx_name = pl_module._current_fx_name - pl_module._current_fx_name = "on_save_checkpoint" - - for callback in self.callbacks: - with self.profiler.profile(f"[Callback]{callback.state_key}.on_save_checkpoint"): - state = callback.on_save_checkpoint(self, self.lightning_module, checkpoint) - if state: - rank_zero_deprecation( - f"Returning a value from `{callback.__class__.__name__}.on_save_checkpoint` is deprecated in v1.6" - " and will be removed in v1.8. Please override `Callback.state_dict`" - " to return state to be saved." - ) - checkpoint["callbacks"][callback.state_key] = state - - if pl_module: - # restore current_fx when nested context - pl_module._current_fx_name = prev_fx_name - - def _call_callbacks_on_load_checkpoint(self, checkpoint: Dict[str, Any]) -> None: - """Called when loading a model checkpoint. - - Calls every callback's `on_load_checkpoint` hook. We have a dedicated function for this rather than using - `_call_callback_hooks` because we have special logic for getting callback_states. - """ - pl_module = self.lightning_module - if pl_module: - prev_fx_name = pl_module._current_fx_name - pl_module._current_fx_name = "on_load_checkpoint" - - callback_states: Dict[Union[Type, str], Dict] = checkpoint.get("callbacks") - - if callback_states is None: - return - - is_legacy_ckpt = Version(checkpoint["pytorch-lightning_version"]) < Version("1.5.0dev") - current_callbacks_keys = {cb._legacy_state_key if is_legacy_ckpt else cb.state_key for cb in self.callbacks} - difference = callback_states.keys() - current_callbacks_keys - if difference: - rank_zero_warn( - "Be aware that when using `ckpt_path`," - " callbacks used to create the checkpoint need to be provided during `Trainer` instantiation." - f" Please add the following callbacks: {list(difference)}.", - ) - - for callback in self.callbacks: - state = callback_states.get(callback.state_key, callback_states.get(callback._legacy_state_key)) - if state: - state = deepcopy(state) - with self.profiler.profile(f"[Callback]{callback.state_key}.on_load_checkpoint"): - callback.on_load_checkpoint(self, self.lightning_module, state) - - if pl_module: - # restore current_fx when nested context - pl_module._current_fx_name = prev_fx_name - - def _call_callbacks_load_state_dict(self, checkpoint: Dict[str, Any]) -> None: - """Called when loading a model checkpoint, calls every callback's `load_state_dict`.""" - callback_states: Dict[Union[Type, str], Dict] = checkpoint.get("callbacks") - - if callback_states is None: - return - - for callback in self.callbacks: - state = callback_states.get(callback.state_key, callback_states.get(callback._legacy_state_key)) - if state: - state = deepcopy(state) - callback.load_state_dict(state) - - def _call_strategy_hook( - self, - hook_name: str, - *args: Any, - **kwargs: Any, - ) -> Any: - pl_module = self.lightning_module - prev_fx_name = pl_module._current_fx_name - pl_module._current_fx_name = hook_name - - fn = getattr(self.strategy, hook_name) - if not callable(fn): - return - - with self.profiler.profile(f"[Strategy]{self.strategy.__class__.__name__}.{hook_name}"): - output = fn(*args, **kwargs) - - # restore current_fx when nested context - pl_module._current_fx_name = prev_fx_name - - return output - - @staticmethod - def _log_api_event(event: str) -> None: - torch._C._log_api_usage_once("lightning.trainer." + event) - - def __init_profiler(self, profiler: Optional[Union[Profiler, str]]) -> None: - if isinstance(profiler, str): - PROFILERS = { - "simple": SimpleProfiler, - "advanced": AdvancedProfiler, - "pytorch": PyTorchProfiler, - "xla": XLAProfiler, - } - profiler = profiler.lower() - if profiler not in PROFILERS: - raise MisconfigurationException( - "When passing string value for the `profiler` parameter of `Trainer`," - f" it can only be one of {list(PROFILERS.keys())}" - ) - profiler_class = PROFILERS[profiler] - profiler = profiler_class() - self.profiler: Profiler = profiler or PassThroughProfiler() - - def __setup_profiler(self) -> None: - local_rank = self.local_rank if self.world_size > 1 else None - self.profiler._lightning_module = proxy(self.lightning_module) - self.profiler.setup(stage=self.state.fn._setup_fn, local_rank=local_rank, log_dir=self.log_dir) - - def _log_device_info(self) -> None: - rank_zero_info( - f"GPU available: {torch.cuda.is_available()}, used: {isinstance(self.accelerator, GPUAccelerator)}" - ) - - num_tpu_cores = self.num_devices if isinstance(self.accelerator, TPUAccelerator) else 0 - rank_zero_info(f"TPU available: {_TPU_AVAILABLE}, using: {num_tpu_cores} TPU cores") - - num_ipus = self.num_devices if isinstance(self.accelerator, IPUAccelerator) else 0 - rank_zero_info(f"IPU available: {_IPU_AVAILABLE}, using: {num_ipus} IPUs") - - num_hpus = self.num_devices if isinstance(self.accelerator, HPUAccelerator) else 0 - rank_zero_info(f"HPU available: {_HPU_AVAILABLE}, using: {num_hpus} HPUs") - - if torch.cuda.is_available() and not isinstance(self.accelerator, GPUAccelerator): - rank_zero_warn( - "GPU available but not used. Set `accelerator` and `devices` using" - f" `Trainer(accelerator='gpu', devices={GPUAccelerator.auto_device_count()})`.", - category=PossibleUserWarning, - ) - - if _TPU_AVAILABLE and not isinstance(self.accelerator, TPUAccelerator): - rank_zero_warn( - "TPU available but not used. Set `accelerator` and `devices` using" - f" `Trainer(accelerator='tpu', devices={TPUAccelerator.auto_device_count()})`." - ) - - if _IPU_AVAILABLE and not isinstance(self.accelerator, IPUAccelerator): - rank_zero_warn( - "IPU available but not used. Set `accelerator` and `devices` using" - f" `Trainer(accelerator='ipu', devices={IPUAccelerator.auto_device_count()})`." - ) - - if _HPU_AVAILABLE and not isinstance(self.accelerator, HPUAccelerator): - rank_zero_warn( - "HPU available but not used. Set `accelerator` and `devices` using" - f" `Trainer(accelerator='hpu', devices={HPUAccelerator.auto_device_count()})`." - ) - - """ - Data loading methods - """ - - def reset_train_dataloader(self, model: Optional["pl.LightningModule"] = None) -> None: - """Resets the train dataloader and initialises required variables (number of batches, when to validate, - etc.). - - Args: - model: The ``LightningModule`` if calling this outside of the trainer scope. - """ - source = self._data_connector._train_dataloader_source - pl_module = self.lightning_module or model - has_step = is_overridden("training_step", pl_module) - enable_training = self.limit_train_batches > 0 - if not (source.is_defined() and has_step and enable_training): - return - - self.train_dataloader = self._data_connector._request_dataloader(RunningStage.TRAINING, model=model) - - if self.overfit_batches > 0: - self.train_dataloader = self._data_connector._resolve_overfit_batches(self.train_dataloader) - - # automatically add samplers - self.train_dataloader = apply_to_collection( - self.train_dataloader, - (DataLoader, CombinedLoader), - self._data_connector._prepare_dataloader, - mode=RunningStage.TRAINING, - ) - loaders = ( - self.train_dataloader.loaders - if isinstance(self.train_dataloader, CombinedLoader) - else self.train_dataloader - ) - - # check the workers recursively - apply_to_collection(loaders, DataLoader, self._data_connector._worker_check, "train_dataloader") - - # add worker_init_fn for correct seeding in worker processes - apply_to_collection(loaders, DataLoader, _auto_add_worker_init_fn, rank=self.global_rank) - - # add collate_fn to collect metadata for fault tolerant training - if _fault_tolerant_training(): - apply_to_collection(loaders, DataLoader, _add_capture_metadata_collate) - - # wrap the sequence of train loaders to a CombinedLoader object for computing the num_training_batches - if not isinstance(self.train_dataloader, CombinedLoader): - self.train_dataloader = CombinedLoader(loaders, self._data_connector.multiple_trainloader_mode) - - module = model or self.lightning_module or self.datamodule - self.num_training_batches = ( - len(self.train_dataloader) - if has_len_all_ranks(self.train_dataloader, self.strategy, module) - else float("inf") - ) - - if isinstance(self.limit_train_batches, int): - self.num_training_batches = min(self.num_training_batches, int(self.limit_train_batches)) - elif self.num_training_batches != float("inf"): - self.num_training_batches = int(self.num_training_batches * self.limit_train_batches) - elif self.limit_train_batches != 1.0: - raise MisconfigurationException( - "When using an IterableDataset for `limit_train_batches`," - " `Trainer(limit_train_batches)` must be `1.0` or an int. An int k specifies" - " `num_training_batches` to use." - ) - - if isinstance(self.val_check_interval, int): - self.val_check_batch = self.val_check_interval - if self.val_check_batch > self.num_training_batches: - raise ValueError( - f"`val_check_interval` ({self.val_check_interval}) must be less than or equal " - f"to the number of the training batches ({self.num_training_batches}). " - "If you want to disable validation set `limit_val_batches` to 0.0 instead." - ) - else: - if not has_len_all_ranks(self.train_dataloader, self.strategy, module): - if self.val_check_interval == 1.0: - self.val_check_batch = float("inf") - else: - raise MisconfigurationException( - "When using an IterableDataset for `train_dataloader`," - " `Trainer(val_check_interval)` must be `1.0` or an int. An int k specifies" - " checking validation every k training batches." - ) - else: - self.val_check_batch = int(self.num_training_batches * self.val_check_interval) - self.val_check_batch = max(1, self.val_check_batch) - - if self.loggers and self.num_training_batches < self.log_every_n_steps: - rank_zero_warn( - f"The number of training batches ({self.num_training_batches}) is smaller than the logging interval" - f" Trainer(log_every_n_steps={self.log_every_n_steps}). Set a lower value for log_every_n_steps if" - " you want to see logs for the training epoch.", - category=PossibleUserWarning, - ) - - # store epoch of dataloader reset for reload_dataloaders_every_n_epochs - self._last_train_dl_reload_epoch = self.current_epoch - - def reset_val_dataloader(self, model: Optional["pl.LightningModule"] = None) -> None: - """Resets the validation dataloader and determines the number of batches. - - Args: - model: The ``LightningModule`` if called outside of the trainer scope. - """ - source = self._data_connector._val_dataloader_source - pl_module = self.lightning_module or model - has_step = is_overridden("validation_step", pl_module) - enable_validation = self.limit_val_batches > 0 - if source.is_defined() and has_step and enable_validation: - self.num_val_batches, self.val_dataloaders = self._data_connector._reset_eval_dataloader( - RunningStage.VALIDATING, model=pl_module - ) - - # store epoch of dataloader reset for reload_dataloaders_every_n_epochs - self._last_val_dl_reload_epoch = self.current_epoch - - def reset_test_dataloader(self, model: Optional["pl.LightningModule"] = None) -> None: - """Resets the test dataloader and determines the number of batches. - - Args: - model: The ``LightningModule`` if called outside of the trainer scope. - """ - source = self._data_connector._test_dataloader_source - pl_module = self.lightning_module or model - has_step = is_overridden("test_step", pl_module) - enable_testing = self.limit_test_batches > 0 - if source.is_defined() and has_step and enable_testing: - self.num_test_batches, self.test_dataloaders = self._data_connector._reset_eval_dataloader( - RunningStage.TESTING, model=pl_module - ) - - def reset_predict_dataloader(self, model: Optional["pl.LightningModule"] = None) -> None: - """Resets the predict dataloader and determines the number of batches. - - Args: - model: The ``LightningModule`` if called outside of the trainer scope. - """ - source = self._data_connector._predict_dataloader_source - pl_module = self.lightning_module or model - enable_prediction = self.limit_predict_batches > 0 - if source.is_defined() and enable_prediction: - self.num_predict_batches, self.predict_dataloaders = self._data_connector._reset_eval_dataloader( - RunningStage.PREDICTING, model=pl_module - ) - - def reset_train_val_dataloaders(self, model: Optional["pl.LightningModule"] = None) -> None: - """Resets train and val dataloaders if none are attached to the trainer. - - The val dataloader must be initialized before training loop starts, as the training loop - inspects the val dataloader to determine whether to run the evaluation loop. - Args: - model: The ``LightningModule`` if called outside of the trainer scope. - """ - if self.train_dataloader is None: - self.reset_train_dataloader(model=model) - if self.val_dataloaders is None: - self.reset_val_dataloader(model=model) - - """ - Accelerator properties - """ - - @property - def accelerator(self) -> Accelerator: - return self.strategy.accelerator - - @property - def strategy(self) -> Strategy: - return self._accelerator_connector.strategy - - @property - def training_type_plugin(self) -> Strategy: - rank_zero_deprecation( - "`Trainer.training_type_plugin` is deprecated in v1.6 and will be removed in v1.8. Use" - " `Trainer.strategy` instead." - ) - return self.strategy - - @property - def precision_plugin(self) -> PrecisionPlugin: - return self.strategy.precision_plugin - - @property - def global_rank(self) -> int: - return self.strategy.global_rank - - @property - def local_rank(self) -> int: - # some strategies define a local rank - return getattr(self.strategy, "local_rank", 0) - - @property - def node_rank(self) -> int: - # some strategies define a node rank - return getattr(self.strategy, "node_rank", 0) - - @property - def world_size(self) -> int: - # some strategies define a world size - return getattr(self.strategy, "world_size", 1) - - @property - def should_rank_save_checkpoint(self) -> bool: - rank_zero_deprecation( - "`Trainer.should_rank_save_checkpoint` is deprecated in v1.6 and will be removed in v1.8.", stacklevel=5 - ) - strategy = self.strategy - return ( - isinstance(strategy, pl.strategies.TPUSpawnStrategy) and strategy.local_rank == 0 or strategy.is_global_zero - ) - - @property - def num_nodes(self) -> int: - return getattr(self.strategy, "num_nodes", 1) - - @property - def device_ids(self) -> List[int]: - """List of device indexes per node.""" - devices = ( - self.strategy.parallel_devices - if isinstance(self.strategy, ParallelStrategy) - else [self.strategy.root_device] - ) - device_ids = [] - for idx, device in enumerate(devices): - if isinstance(device, torch.device): - device_ids.append(device.index or idx) - elif isinstance(device, int): - device_ids.append(device) - return device_ids - - @property - def num_devices(self) -> int: - """Number of devices the trainer uses per node.""" - return len(self.device_ids) - - @property - def num_processes(self) -> int: - rank_zero_deprecation( - "`Trainer.num_processes` is deprecated in v1.6 and will be removed in v1.8. " - "Please use `Trainer.num_devices` instead." - ) - return self.num_devices - - @property - def root_gpu(self) -> Optional[int]: - rank_zero_deprecation( - "`Trainer.root_gpu` is deprecated in v1.6 and will be removed in v1.8. " - "Please use `Trainer.strategy.root_device.index` instead." - ) - return self.strategy.root_device.index if isinstance(self.accelerator, GPUAccelerator) else None - - @property - def tpu_cores(self) -> int: - rank_zero_deprecation( - "`Trainer.tpu_cores` is deprecated in v1.6 and will be removed in v1.8. " - "Please use `Trainer.num_devices` instead." - ) - return self.num_devices if isinstance(self.accelerator, TPUAccelerator) else 0 - - @property - def ipus(self) -> int: - rank_zero_deprecation( - "`Trainer.ipus` was deprecated in v1.6 and will be removed in v1.8." - " Please use `Trainer.num_devices` instead." - ) - return self.num_devices if isinstance(self.accelerator, IPUAccelerator) else 0 - - @property - def num_gpus(self) -> int: - rank_zero_deprecation( - "`Trainer.num_gpus` was deprecated in v1.6 and will be removed in v1.8." - " Please use `Trainer.num_devices` instead." - ) - return self.num_devices if isinstance(self.accelerator, GPUAccelerator) else 0 - - @property - def devices(self) -> int: - rank_zero_deprecation( - "`Trainer.devices` was deprecated in v1.6 and will be removed in v1.8." - " Please use `Trainer.num_devices` or `Trainer.device_ids` to get device information instead." - ) - return self.num_devices - - @property - def data_parallel_device_ids(self) -> Optional[List[int]]: - rank_zero_deprecation( - "`Trainer.data_parallel_device_ids` was deprecated in v1.6 and will be removed in v1.8." - " Please use `Trainer.device_ids` instead." - ) - return self.device_ids if isinstance(self.accelerator, GPUAccelerator) else None - - @property - def lightning_module(self) -> "pl.LightningModule": - # TODO: this is actually an optional return - return self.strategy.lightning_module - - @property - def optimizers(self) -> List[Optimizer]: - return self.strategy.optimizers - - @optimizers.setter - def optimizers(self, new_optims: Optional[List[Optimizer]]) -> None: - self.strategy.optimizers = new_optims - - @property - def lightning_optimizers(self) -> Dict[int, LightningOptimizer]: - rank_zero_deprecation( - "`Trainer.lightning_optimizers` is deprecated in v1.6 and will be removed in v1.8", stacklevel=5 - ) - return self.strategy._lightning_optimizers - - @property - def lr_scheduler_configs(self) -> List[LRSchedulerConfig]: - return self.strategy.lr_scheduler_configs - - @property - def lr_schedulers(self) -> List[Dict[str, Any]]: - rank_zero_deprecation( - "`Trainer.lr_schedulers` is deprecated in v1.6 and will be removed in v1.8." - " You can use `trainer.lr_scheduler_configs` instead which contains dataclasses instead of dictionaries.", - stacklevel=5, - ) - from dataclasses import asdict - - return [asdict(config) for config in self.strategy.lr_scheduler_configs] - - @property - def optimizer_frequencies(self) -> List[int]: - return self.strategy.optimizer_frequencies - - @optimizer_frequencies.setter - def optimizer_frequencies(self, new_freqs: List[int]) -> None: - self.strategy.optimizer_frequencies = new_freqs - - @property - def amp_backend(self) -> Optional[AMPType]: - if isinstance(self.precision_plugin, ApexMixedPrecisionPlugin): - return AMPType.APEX - if isinstance(self.precision_plugin, NativeMixedPrecisionPlugin): - return AMPType.NATIVE - return None - - @property - def precision(self) -> Union[str, int]: - return self.strategy.precision_plugin.precision - - @property - def scaler(self) -> Optional[Any]: - return getattr(self.precision_plugin, "scaler", None) - - @property - def gpus(self) -> Optional[Union[List[int], str, int]]: - rank_zero_deprecation( - "`Trainer.gpus` was deprecated in v1.6 and will be removed in v1.8." - " Please use `Trainer.num_devices` or `Trainer.device_ids` to get device information instead." - ) - return self._accelerator_connector.gpus - - @property - def model(self) -> torch.nn.Module: - """The LightningModule, but possibly wrapped into DataParallel or DistributedDataParallel. - - To access the pure LightningModule, use - :meth:`~pytorch_lightning.trainer.trainer.Trainer.lightning_module` instead. - """ - return self.strategy.model - - @model.setter - def model(self, model: torch.nn.Module) -> None: - """Setter for the model, pass-through to accelerator and plugin where the model reference is stored. Used - by the Tuner to reset the state of Trainer and Accelerator. - - Args: - model: The LightningModule, possibly wrapped into DataParallel or DistributedDataParallel, depending - on the backend. - """ - self.strategy.model = model - - """ - General properties - """ - - @property - def log_dir(self) -> Optional[str]: - if len(self.loggers) == 1: - if isinstance(self.logger, TensorBoardLogger): - dirpath = self.logger.log_dir - else: - dirpath = self.logger.save_dir - else: - dirpath = self.default_root_dir - - dirpath = self.strategy.broadcast(dirpath) - return dirpath - - @property - def use_amp(self) -> bool: - rank_zero_deprecation( - "`Trainer.use_amp` is deprecated in v1.6.0 and will be removed in v1.8.0." - " Please use `Trainer.amp_backend` instead." - ) - return self.precision == 16 - - @property - def is_global_zero(self) -> bool: - return self.strategy.is_global_zero - - @property - def slurm_job_id(self) -> Optional[int]: - rank_zero_deprecation("Method `slurm_job_id` is deprecated in v1.6.0 and will be removed in v1.7.0.") - return SLURMEnvironment.job_id() - - @property - def distributed_sampler_kwargs(self) -> Optional[dict]: - if isinstance(self.strategy, ParallelStrategy): - return self.strategy.distributed_sampler_kwargs - - @property - def data_parallel(self) -> bool: - return isinstance(self.strategy, ParallelStrategy) - - @property - def progress_bar_dict(self) -> dict: - """Read-only for progress bar metrics.""" - rank_zero_deprecation( - "`trainer.progress_bar_dict` is deprecated in v1.5 and will be removed in v1.7." - " Use `ProgressBarBase.get_metrics` instead." - ) - ref_model = self.lightning_module - ref_model = cast(pl.LightningModule, ref_model) - if self.progress_bar_callback: - return self.progress_bar_callback.get_metrics(self, ref_model) - return self.progress_bar_metrics - - @property - def enable_validation(self) -> bool: - """Check if we should run validation during training.""" - return ( - self._data_connector._val_dataloader_source.is_defined() - and is_overridden("validation_step", self.lightning_module) - and self.limit_val_batches > 0 - ) - - @property - def default_root_dir(self) -> str: - """The default location to save artifacts of loggers, checkpoints etc. - - It is used as a fallback if logger or checkpoint callback do not define specific save paths. - """ - if get_filesystem(self._default_root_dir).protocol == "file": - return os.path.normpath(self._default_root_dir) - return self._default_root_dir - - @property - def weights_save_path(self) -> str: - """ - The default root location to save weights (checkpoints), e.g., when the - :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` does not define a file path. - - .. deprecated:: v1.6 - `Trainer.weights_save_path` has been deprecated in v1.6 and will be removed in v1.8. - """ - rank_zero_deprecation("`Trainer.weights_save_path` has been deprecated in v1.6 and will be removed in v1.8.") - return self._weights_save_path_internal - - # TODO: Remove _weights_save_path_internal in v1.8 - @property - def _weights_save_path_internal(self) -> str: - """This is an internal implementation of weights_save_path which allows weights_save_path to be used - internally by the framework without emitting a deprecation warning. - - To be removed in v1.8. - """ - if get_filesystem(self._weights_save_path).protocol == "file": - return os.path.normpath(self._weights_save_path) - return self._weights_save_path - - @property - def early_stopping_callback(self) -> Optional[EarlyStopping]: - """The first :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback in the - Trainer.callbacks list, or ``None`` if it doesn't exist.""" - callbacks = self.early_stopping_callbacks - return callbacks[0] if len(callbacks) > 0 else None - - @property - def early_stopping_callbacks(self) -> List[EarlyStopping]: - """A list of all instances of :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` found in - the Trainer.callbacks list.""" - return [c for c in self.callbacks if isinstance(c, EarlyStopping)] - - @property - def prediction_writer_callbacks(self) -> List[BasePredictionWriter]: - """A list of all instances of :class:`~pytorch_lightning.callbacks.prediction_writer.BasePredictionWriter` - found in the Trainer.callbacks list.""" - return [cb for cb in self.callbacks if isinstance(cb, BasePredictionWriter)] - - @property - def checkpoint_callback(self) -> Optional[ModelCheckpoint]: - """The first :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` callback in the - Trainer.callbacks list, or ``None`` if it doesn't exist.""" - callbacks = self.checkpoint_callbacks - return callbacks[0] if len(callbacks) > 0 else None - - @property - def checkpoint_callbacks(self) -> List[ModelCheckpoint]: - """A list of all instances of :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` found - in the Trainer.callbacks list.""" - return [c for c in self.callbacks if isinstance(c, ModelCheckpoint)] - - @property - def progress_bar_callback(self) -> Optional[ProgressBarBase]: - """An instance of :class:`~pytorch_lightning.callbacks.progress.base.ProgressBarBase` found in the - Trainer.callbacks list, or ``None`` if one doesn't exist.""" - for c in self.callbacks: - if isinstance(c, ProgressBarBase): - return c - return None - - @property - def resume_from_checkpoint(self) -> Optional[Union[str, Path]]: - resume_from_checkpoint = self._checkpoint_connector.resume_from_checkpoint_fit_path - if resume_from_checkpoint is not None: - rank_zero_deprecation( - "`trainer.resume_from_checkpoint` is deprecated in v1.5 and will be removed in v2.0." - " Specify the fit checkpoint path with `trainer.fit(ckpt_path=)` instead.", - stacklevel=5, - ) - - return resume_from_checkpoint - - @property - def ckpt_path(self) -> Optional[str]: - """Set to the path/URL of a checkpoint loaded via :meth:`~pytorch_lightning.trainer.trainer.Trainer.fit`, - :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate`, - :meth:`~pytorch_lightning.trainer.trainer.Trainer.test`, or - :meth:`~pytorch_lightning.trainer.trainer.Trainer.predict`. ``None`` otherwise.""" - return self._ckpt_path - - @property - def validated_ckpt_path(self) -> Optional[str]: - rank_zero_deprecation( - "The `Trainer.validated_ckpt_path` attribute was deprecated in v1.6 and will be removed in v1.8. The" - " path of a checkpoint loaded via `Trainer.{fit,validate,test,predict}` should be accessed via" - " `Trainer.ckpt_path` instead.", - stacklevel=5, - ) - return self._validated_ckpt_path - - @validated_ckpt_path.setter - def validated_ckpt_path(self, ckpt_path: Optional[str]) -> None: - rank_zero_deprecation( - "The `Trainer.validated_ckpt_path` attribute was deprecated in v1.6 and will be removed in v1.8. The" - " path of a checkpoint loaded via `Trainer.{fit,validate,test,predict}` should be accessed via the" - " read-only `Trainer.ckpt_path`.", - stacklevel=5, - ) - self._validated_ckpt_path = ckpt_path - - @property - def tested_ckpt_path(self) -> Optional[str]: - rank_zero_deprecation( - "The `Trainer.tested_ckpt_path` attribute was deprecated in v1.6 and will be removed in v1.8. The" - " path of a checkpoint loaded via `Trainer.{fit,validate,test,predict}` should be accessed via" - " `Trainer.ckpt_path` instead.", - stacklevel=5, - ) - return self._tested_ckpt_path - - @tested_ckpt_path.setter - def tested_ckpt_path(self, ckpt_path: Optional[str]) -> None: - rank_zero_deprecation( - "The `Trainer.tested_ckpt_path` attribute was deprecated in v1.6 and will be removed in v1.8. The" - " path of a checkpoint loaded via `Trainer.{fit,validate,test,predict}` should be accessed via the" - " read-only `Trainer.ckpt_path` instead.", - stacklevel=5, - ) - self._tested_ckpt_path = ckpt_path - - @property - def predicted_ckpt_path(self) -> Optional[str]: - rank_zero_deprecation( - "The `Trainer.predicted_ckpt_path` attribute was deprecated in v1.6 and will be removed in v1.8. The" - " path of a checkpoint loaded via `Trainer.{fit,validate,test,predict}` should be accessed via" - " `Trainer.ckpt_path` instead.", - stacklevel=5, - ) - return self._predicted_ckpt_path - - @predicted_ckpt_path.setter - def predicted_ckpt_path(self, ckpt_path: Optional[str]) -> None: - rank_zero_deprecation( - "The `Trainer.predicted_ckpt_path` attribute was deprecated in v1.6 and will be removed in v1.8. The" - " path of a checkpoint loaded via `Trainer.{fit,validate,test,predict}` should be accessed via the" - " read-only `Trainer.ckpt_path` instead.", - stacklevel=5, - ) - self._predicted_ckpt_path = ckpt_path - - def save_checkpoint( - self, filepath: _PATH, weights_only: bool = False, storage_options: Optional[Any] = None - ) -> None: - r""" - Runs routine to create a checkpoint. - - Args: - filepath: Path where checkpoint is saved. - weights_only: If ``True``, will only save the model weights. - storage_options: parameter for how to save to storage, passed to ``CheckpointIO`` plugin - - """ - self._checkpoint_connector.save_checkpoint(filepath, weights_only=weights_only, storage_options=storage_options) - - """ - Parsing properties - """ - - @classmethod - def default_attributes(cls) -> dict: - init_signature = inspect.signature(cls) - return {k: v.default for k, v in init_signature.parameters.items()} - - @classmethod - def get_deprecated_arg_names(cls) -> List: - """Returns a list with deprecated Trainer arguments.""" - depr_arg_names = [] - for name, val in cls.__dict__.items(): - if name.startswith("DEPRECATED") and isinstance(val, (tuple, list)): - depr_arg_names.extend(val) - return depr_arg_names - - @classmethod - def from_argparse_args(cls: Any, args: Union[Namespace, ArgumentParser], **kwargs) -> Any: - return from_argparse_args(cls, args, **kwargs) - - @classmethod - def parse_argparser(cls, arg_parser: Union[ArgumentParser, Namespace]) -> Namespace: - return parse_argparser(cls, arg_parser) - - @classmethod - def match_env_arguments(cls) -> Namespace: - return parse_env_variables(cls) - - @classmethod - def add_argparse_args(cls, parent_parser: ArgumentParser, **kwargs) -> ArgumentParser: - return add_argparse_args(cls, parent_parser, **kwargs) - - """ - State properties - """ - - @property - def interrupted(self) -> bool: - return self.state.status == TrainerStatus.INTERRUPTED - - @property - def training(self) -> bool: - return self.state.stage == RunningStage.TRAINING - - @training.setter - def training(self, val: bool) -> None: - if val: - self.state.stage = RunningStage.TRAINING - elif self.training: - self.state.stage = None - - @property - def testing(self) -> bool: - return self.state.stage == RunningStage.TESTING - - @testing.setter - def testing(self, val: bool) -> None: - if val: - self.state.stage = RunningStage.TESTING - elif self.testing: - self.state.stage = None - - @property - def predicting(self) -> bool: - return self.state.stage == RunningStage.PREDICTING - - @predicting.setter - def predicting(self, val: bool) -> None: - if val: - self.state.stage = RunningStage.PREDICTING - elif self.predicting: - self.state.stage = None - - @property - def tuning(self) -> bool: - return self.state.stage == RunningStage.TUNING - - @tuning.setter - def tuning(self, val: bool) -> None: - if val: - self.state.stage = RunningStage.TUNING - elif self.tuning: - self.state.stage = None - - @property - def validating(self) -> bool: - return self.state.stage == RunningStage.VALIDATING - - @validating.setter - def validating(self, val: bool) -> None: - if val: - self.state.stage = RunningStage.VALIDATING - elif self.validating: - self.state.stage = None - - @property - def evaluating(self) -> bool: - return self.state.stage and self.state.stage.evaluating - - @property - def sanity_checking(self) -> bool: - return self.state.stage == RunningStage.SANITY_CHECKING - - @sanity_checking.setter - def sanity_checking(self, val: bool) -> None: - if val: - self.state.stage = RunningStage.SANITY_CHECKING - elif self.sanity_checking: - self.state.stage = None - - """ - Loop properties - """ - - @property - def global_step(self) -> int: - """The number of optimizer steps taken (does not reset each epoch). - - This includes multiple optimizers and TBPTT steps (if enabled). - """ - return self.fit_loop.epoch_loop.global_step - - @property - def current_epoch(self) -> int: - """The current epoch, updated after the epoch end hooks are run.""" - return self.fit_loop.epoch_progress.current.completed - - @property - def max_epochs(self) -> int: - return self.fit_loop.max_epochs - - @property - def min_epochs(self) -> int: - return self.fit_loop.min_epochs - - @property - def max_steps(self) -> int: - return self.fit_loop.max_steps - - @property - def min_steps(self) -> Optional[int]: - return self.fit_loop.min_steps - - @property - def is_last_batch(self) -> bool: - return self.fit_loop.epoch_loop.batch_progress.is_last_batch - - @property - def fit_loop(self) -> FitLoop: - return self._fit_loop - - @fit_loop.setter - def fit_loop(self, loop: FitLoop): - """Attach a custom fit loop to this Trainer. - - It will run with - :meth:`~pytorch_lightning.trainer.trainer.Trainer.fit`. - """ - loop.trainer = self - self._fit_loop = loop - - @property - def validate_loop(self) -> EvaluationLoop: - return self._validate_loop - - @validate_loop.setter - def validate_loop(self, loop: EvaluationLoop): - """Attach a custom validation loop to this Trainer. - - It will run with - :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate`. Note that this loop is different from the one - running during training inside the :meth:`pytorch_lightning.trainer.trainer.Trainer.fit` call. - """ - loop.trainer = self - self._validate_loop = loop - - @property - def test_loop(self) -> EvaluationLoop: - return self._test_loop - - @test_loop.setter - def test_loop(self, loop: EvaluationLoop): - """Attach a custom test loop to this Trainer. - - It will run with - :meth:`~pytorch_lightning.trainer.trainer.Trainer.test`. - """ - loop.trainer = self - self._test_loop = loop - - @property - def predict_loop(self) -> PredictionLoop: - return self._predict_loop - - @predict_loop.setter - def predict_loop(self, loop: PredictionLoop): - """Attach a custom prediction loop to this Trainer. - - It will run with - :meth:`~pytorch_lightning.trainer.trainer.Trainer.predict`. - """ - loop.trainer = self - self._predict_loop = loop - - @property - def verbose_evaluate(self) -> bool: - rank_zero_deprecation( - "The `Trainer.verbose_evaluate` property has been deprecated and will be removed in v1.8. The current value" - " returned is the union of the validate and test loop values. You can choose which one to access with" - " `trainer.{validate,test}_loop.verbose`.", - stacklevel=5, - ) - return self.validate_loop.verbose or self.test_loop.verbose - - @verbose_evaluate.setter - def verbose_evaluate(self, verbose: bool) -> None: - rank_zero_deprecation( - "The `Trainer.verbose_evaluate` property has been deprecated and will be removed in v1.8. This will set" - " the value for both trainer.{validate,test}_loop.verbose`.", - stacklevel=5, - ) - self.validate_loop.verbose = verbose - self.test_loop.verbose = verbose - - @property - def _evaluation_loop(self) -> EvaluationLoop: - if self.state.fn in (TrainerFn.FITTING, TrainerFn.TUNING): - return self.fit_loop.epoch_loop.val_loop - if self.state.fn == TrainerFn.VALIDATING: - return self.validate_loop - if self.state.fn == TrainerFn.TESTING: - return self.test_loop - raise RuntimeError("The `Trainer._evaluation_loop` property isn't defined. Accessed outside of scope") - - @property - def _active_loop(self) -> Optional[Union[FitLoop, EvaluationLoop, PredictionLoop]]: - if self.training: - return self.fit_loop - if self.sanity_checking or self.evaluating: - return self._evaluation_loop - if self.predicting: - return self.predict_loop - - """ - Logging properties - """ - - @property - def logger(self) -> Optional[LightningLoggerBase]: - if len(self.loggers) == 0: - return None - if len(self.loggers) == 1: - return self.loggers[0] - else: - rank_zero_warn( - "Using trainer.logger when Trainer is configured to use multiple loggers." - " This behavior will change in v1.8 when LoggerCollection is removed, and" - " trainer.logger will return the first logger in trainer.loggers" - ) - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - return LoggerCollection(self.loggers) - - @logger.setter - def logger(self, logger: Optional[LightningLoggerBase]) -> None: - if not logger: - self.loggers = [] - elif isinstance(logger, LoggerCollection): - self.loggers = list(logger) - else: - self.loggers = [logger] - - @property - def loggers(self) -> List[LightningLoggerBase]: - return self._loggers - - @loggers.setter - def loggers(self, loggers: Optional[List[LightningLoggerBase]]) -> None: - self._loggers = loggers if loggers else [] - - @property - def callback_metrics(self) -> dict: - return self._logger_connector.callback_metrics - - @property - def logged_metrics(self) -> dict: - return self._logger_connector.logged_metrics - - @property - def progress_bar_metrics(self) -> dict: - return self._logger_connector.progress_bar_metrics - - @property - def _results(self) -> Optional[_ResultCollection]: - active_loop = self._active_loop - if active_loop is not None: - return active_loop._results - - def _exit_gracefully_on_signal(self) -> None: - if not _fault_tolerant_training() or not self._should_terminate_gracefully(): - return - raise ExitGracefullyException(0) - - def _should_terminate_gracefully(self) -> bool: - value = torch.tensor(int(self._terminate_gracefully), device=self.strategy.root_device) - return self.strategy.reduce(value, reduce_op="sum") > 0 - - @property - def weights_summary(self) -> Optional[str]: - rank_zero_deprecation("`Trainer.weights_summary` is deprecated in v1.5 and will be removed in v1.7.") - return self._weights_summary - - @weights_summary.setter - def weights_summary(self, val: Optional[str]) -> None: - rank_zero_deprecation("Setting `Trainer.weights_summary` is deprecated in v1.5 and will be removed in v1.7.") - self._weights_summary = val - - """ - Other - """ - - @property - def estimated_stepping_batches(self) -> Union[int, float]: - r""" - Estimated stepping batches for the complete training inferred from DataLoaders, gradient - accumulation factor and distributed setup. - - Examples:: - - def configure_optimizers(self): - optimizer = ... - scheduler = torch.optim.lr_scheduler.OneCycleLR( - optimizer, max_lr=1e-3, total_steps=self.trainer.estimated_stepping_batches - ) - return [optimizer], [scheduler] - - """ - accumulation_scheduler = self.accumulation_scheduler - - if accumulation_scheduler.epochs != [0]: - raise MisconfigurationException( - "Estimated stepping batches cannot be computed with different" - " `accumulate_grad_batches` at different epochs." - ) - - # infinite training - if self.max_epochs == -1 and self.max_steps == -1: - return float("inf") - - if self.train_dataloader is None: - rank_zero_info("Loading `train_dataloader` to estimate number of stepping batches.") - self.reset_train_dataloader() - - total_batches = self.num_training_batches - - # iterable dataset - if total_batches == float("inf"): - return self.max_steps - - self.accumulate_grad_batches = accumulation_scheduler.get_accumulate_grad_batches(self.current_epoch) - effective_batch_size = self.accumulate_grad_batches - max_estimated_steps = math.ceil(total_batches / effective_batch_size) * max(self.max_epochs, 1) - - max_estimated_steps = min(max_estimated_steps, self.max_steps) if self.max_steps != -1 else max_estimated_steps - return max_estimated_steps - - @property - def terminate_on_nan(self) -> bool: - rank_zero_deprecation("`Trainer.terminate_on_nan` is deprecated in v1.5 and will be removed in 1.7.") - return self._terminate_on_nan - - @terminate_on_nan.setter - def terminate_on_nan(self, val: bool) -> None: - rank_zero_deprecation( - f"Setting `Trainer.terminate_on_nan = {val}` is deprecated in v1.5 and will be removed in 1.7." - f" Please set `Trainer(detect_anomaly={val})` instead." - ) - self._terminate_on_nan = val # : 212 - - -def _determine_batch_limits(batches: Optional[Union[int, float]], name: str) -> Union[int, float]: - if batches is None: - # batches is optional to know if the user passed a value so that we can show the above info messages only to the - # users that set a value explicitly - return 1.0 - - # differentiating based on the type can be error-prone for users. show a message describing the chosen behaviour - if isinstance(batches, int) and batches == 1: - if name == "limit_train_batches": - message = "1 batch per epoch will be used." - elif name == "val_check_interval": - message = "validation will run after every batch." - else: - message = "1 batch will be used." - rank_zero_info(f"`Trainer({name}=1)` was configured so {message}") - elif isinstance(batches, float) and batches == 1.0: - if name == "limit_train_batches": - message = "100% of the batches per epoch will be used." - elif name == "val_check_interval": - message = "validation will run at the end of the training epoch." - else: - message = "100% of the batches will be used." - rank_zero_info(f"`Trainer({name}=1.0)` was configured so {message}.") - - if 0 <= batches <= 1: - return batches - if batches > 1 and batches % 1.0 == 0: - return int(batches) - raise MisconfigurationException( - f"You have passed invalid value {batches} for {name}, it has to be in [0.0, 1.0] or an int." - ) -
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/_sources/accelerators/accelerator_prepare.rst.txt b/docs/_sources/accelerators/accelerator_prepare.rst.txt deleted file mode 100644 index 38921f4..0000000 --- a/docs/_sources/accelerators/accelerator_prepare.rst.txt +++ /dev/null @@ -1,165 +0,0 @@ -:orphan: - -.. _gpu_prepare: - -######################################## -Hardware agnostic training (preparation) -######################################## - -To train on CPU/GPU/TPU without changing your code, we need to build a few good habits :) - ----- - -***************************** -Delete .cuda() or .to() calls -***************************** - -Delete any calls to .cuda() or .to(device). - -.. testcode:: - - # before lightning - def forward(self, x): - x = x.cuda(0) - layer_1.cuda(0) - x_hat = layer_1(x) - - - # after lightning - def forward(self, x): - x_hat = layer_1(x) - ----- - -********************************************** -Init tensors using type_as and register_buffer -********************************************** -When you need to create a new tensor, use ``type_as``. -This will make your code scale to any arbitrary number of GPUs or TPUs with Lightning. - -.. testcode:: - - # before lightning - def forward(self, x): - z = torch.Tensor(2, 3) - z = z.cuda(0) - - - # with lightning - def forward(self, x): - z = torch.Tensor(2, 3) - z = z.type_as(x) - -The :class:`~pytorch_lightning.core.lightning.LightningModule` knows what device it is on. You can access the reference via ``self.device``. -Sometimes it is necessary to store tensors as module attributes. However, if they are not parameters they will -remain on the CPU even if the module gets moved to a new device. To prevent that and remain device agnostic, -register the tensor as a buffer in your modules' ``__init__`` method with :meth:`~torch.nn.Module.register_buffer`. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self): - ... - self.register_buffer("sigma", torch.eye(3)) - # you can now access self.sigma anywhere in your module - ----- - -*************** -Remove samplers -*************** - -:class:`~torch.utils.data.distributed.DistributedSampler` is automatically handled by Lightning. - -See :ref:`replace-sampler-ddp` for more information. - ----- - -*************************************** -Synchronize validation and test logging -*************************************** - -When running in distributed mode, we have to ensure that the validation and test step logging calls are synchronized across processes. -This is done by adding ``sync_dist=True`` to all ``self.log`` calls in the validation and test step. -This ensures that each GPU worker has the same behaviour when tracking model checkpoints, which is important for later downstream tasks such as testing the best checkpoint across all workers. -The ``sync_dist`` option can also be used in logging calls during the step methods, but be aware that this can lead to significant communication overhead and slow down your training. - -Note if you use any built in metrics or custom metrics that use `TorchMetrics `_, these do not need to be updated and are automatically handled for you. - -.. testcode:: - - def validation_step(self, batch, batch_idx): - x, y = batch - logits = self(x) - loss = self.loss(logits, y) - # Add sync_dist=True to sync logging across all GPU workers (may have performance impact) - self.log("validation_loss", loss, on_step=True, on_epoch=True, sync_dist=True) - - - def test_step(self, batch, batch_idx): - x, y = batch - logits = self(x) - loss = self.loss(logits, y) - # Add sync_dist=True to sync logging across all GPU workers (may have performance impact) - self.log("test_loss", loss, on_step=True, on_epoch=True, sync_dist=True) - -It is possible to perform some computation manually and log the reduced result on rank 0 as follows: - -.. testcode:: - - def test_step(self, batch, batch_idx): - x, y = batch - tensors = self(x) - return tensors - - - def test_epoch_end(self, outputs): - mean = torch.mean(self.all_gather(outputs)) - - # When logging only on rank 0, don't forget to add - # ``rank_zero_only=True`` to avoid deadlocks on synchronization. - if self.trainer.is_global_zero: - self.log("my_reduced_metric", mean, rank_zero_only=True) - ----- - -********************** -Make models pickleable -********************** -It's very likely your code is already `pickleable `_, -in that case no change in necessary. -However, if you run a distributed model and get the following error: - -.. code-block:: - - self._launch(process_obj) - File "/net/software/local/python/3.6.5/lib/python3.6/multiprocessing/popen_spawn_posix.py", line 47, - in _launch reduction.dump(process_obj, fp) - File "/net/software/local/python/3.6.5/lib/python3.6/multiprocessing/reduction.py", line 60, in dump - ForkingPickler(file, protocol).dump(obj) - _pickle.PicklingError: Can't pickle at 0x2b599e088ae8>: - attribute lookup on __main__ failed - -This means something in your model definition, transforms, optimizer, dataloader or callbacks cannot be pickled, and the following code will fail: - -.. code-block:: python - - import pickle - - pickle.dump(some_object) - -This is a limitation of using multiple processes for distributed training within PyTorch. -To fix this issue, find your piece of code that cannot be pickled. The end of the stacktrace -is usually helpful. -ie: in the stacktrace example here, there seems to be a lambda function somewhere in the code -which cannot be pickled. - -.. code-block:: - - self._launch(process_obj) - File "/net/software/local/python/3.6.5/lib/python3.6/multiprocessing/popen_spawn_posix.py", line 47, - in _launch reduction.dump(process_obj, fp) - File "/net/software/local/python/3.6.5/lib/python3.6/multiprocessing/reduction.py", line 60, in dump - ForkingPickler(file, protocol).dump(obj) - _pickle.PicklingError: Can't pickle [THIS IS THE THING TO FIND AND DELETE]: - attribute lookup on __main__ failed diff --git a/docs/_sources/accelerators/gpu.rst.txt b/docs/_sources/accelerators/gpu.rst.txt deleted file mode 100644 index dff7646..0000000 --- a/docs/_sources/accelerators/gpu.rst.txt +++ /dev/null @@ -1,63 +0,0 @@ -.. _gpu: - -Accelerator: GPU training -========================= - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Prepare your code (Optional) - :description: Prepare your code to run on any hardware - :col_css: col-md-4 - :button_link: accelerator_prepare.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Basic - :description: Learn the basics of single and multi-GPU training. - :col_css: col-md-4 - :button_link: gpu_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Learn about different distributed strategies, torchelastic and how to optimize communication layers. - :col_css: col-md-4 - :button_link: gpu_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Advanced - :description: Train 1 trillion+ parameter models with these techniques. - :col_css: col-md-4 - :button_link: gpu_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Expert - :description: Develop new strategies for training and deploying larger and larger models. - :col_css: col-md-4 - :button_link: gpu_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: FAQ - :description: Frequently asked questions about GPU training. - :col_css: col-md-4 - :button_link: gpu_faq.html - :height: 150 - -.. raw:: html - -
-
diff --git a/docs/_sources/accelerators/gpu_advanced.rst.txt b/docs/_sources/accelerators/gpu_advanced.rst.txt deleted file mode 100644 index eadeb03..0000000 --- a/docs/_sources/accelerators/gpu_advanced.rst.txt +++ /dev/null @@ -1,16 +0,0 @@ -:orphan: - -.. _gpu_advanced: - -GPU training (Advanced) -======================= -**Audience:** Users looking to scale massive models (ie: 1 Trillion parameters). - ----- - -For experts pushing the state-of-the-art in model development, Lightning offers various techniques to enable Trillion+ parameter-scale models. - ----- - -.. - .. include:: ../advanced/model_parallel.rst diff --git a/docs/_sources/accelerators/gpu_basic.rst.txt b/docs/_sources/accelerators/gpu_basic.rst.txt deleted file mode 100644 index 43be718..0000000 --- a/docs/_sources/accelerators/gpu_basic.rst.txt +++ /dev/null @@ -1,97 +0,0 @@ -:orphan: - -.. _gpu_basic: - -GPU training (Basic) -==================== -**Audience:** Users looking to save money and run large models faster using single or multiple - ----- - -What is a GPU? --------------- -A Graphics Processing Unit (GPU), is a specialized hardware accelerator designed to speed up mathematical computations used in gaming and deep learning. - ----- - -Train on 1 GPU --------------- - -Make sure you're running on a machine with at least one GPU. There's no need to specify any NVIDIA flags -as Lightning will do it for you. - -.. testcode:: - :skipif: torch.cuda.device_count() < 1 - - trainer = Trainer(accelerator="gpu", devices=1) - ----------------- - - -.. _multi_gpu: - -Train on multiple GPUs ----------------------- - -To use multiple GPUs, set the number of devices in the Trainer or the index of the GPUs. - -.. code:: - - trainer = Trainer(accelerator="gpu", devices=4) - -Choosing GPU devices -^^^^^^^^^^^^^^^^^^^^ - -You can select the GPU devices using ranges, a list of indices or a string containing -a comma separated list of GPU ids: - -.. testsetup:: - - k = 1 - -.. testcode:: - :skipif: torch.cuda.device_count() < 2 - - # DEFAULT (int) specifies how many GPUs to use per node - Trainer(accelerator="gpu", devices=k) - - # Above is equivalent to - Trainer(accelerator="gpu", devices=list(range(k))) - - # Specify which GPUs to use (don't use when running on cluster) - Trainer(accelerator="gpu", devices=[0, 1]) - - # Equivalent using a string - Trainer(accelerator="gpu", devices="0, 1") - - # To use all available GPUs put -1 or '-1' - # equivalent to list(range(torch.cuda.device_count())) - Trainer(accelerator="gpu", devices=-1) - -The table below lists examples of possible input formats and how they are interpreted by Lightning. - -+------------------+-----------+---------------------+---------------------------------+ -| `devices` | Type | Parsed | Meaning | -+==================+===========+=====================+=================================+ -| 3 | int | [0, 1, 2] | first 3 GPUs | -+------------------+-----------+---------------------+---------------------------------+ -| -1 | int | [0, 1, 2, ...] | all available GPUs | -+------------------+-----------+---------------------+---------------------------------+ -| [0] | list | [0] | GPU 0 | -+------------------+-----------+---------------------+---------------------------------+ -| [1, 3] | list | [1, 3] | GPUs 1 and 3 | -+------------------+-----------+---------------------+---------------------------------+ -| "3" | str | [0, 1, 2] | first 3 GPUs | -+------------------+-----------+---------------------+---------------------------------+ -| "1, 3" | str | [1, 3] | GPUs 1 and 3 | -+------------------+-----------+---------------------+---------------------------------+ -| "-1" | str | [0, 1, 2, ...] | all available GPUs | -+------------------+-----------+---------------------+---------------------------------+ - -.. note:: - - When specifying number of ``devices`` as an integer ``devices=k``, setting the trainer flag - ``auto_select_gpus=True`` will automatically help you find ``k`` GPUs that are not - occupied by other processes. This is especially useful when GPUs are configured - to be in "exclusive mode", such that only one process at a time can access them. - For more details see the :doc:`trainer guide <../common/trainer>`. diff --git a/docs/_sources/accelerators/gpu_expert.rst.txt b/docs/_sources/accelerators/gpu_expert.rst.txt deleted file mode 100644 index a2178a3..0000000 --- a/docs/_sources/accelerators/gpu_expert.rst.txt +++ /dev/null @@ -1,21 +0,0 @@ -:orphan: - -.. _gpu_expert: - -GPU training (Expert) -===================== -**Audience:** Experts creating new scaling techniques such as Deepspeed or FSDP - ----- - -Lightning enables experts focused on researching new ways of optimizing distributed training/inference strategies to create new strategies and plug them into Lightning. - -For example, Lightning worked closely with the Microsoft team to develop a Deepspeed integration and with the Facebook(Meta) team to develop a FSDP integration. - ----- - -.. include:: ../advanced/strategy_registry.rst - ----- - -.. include:: ../extensions/strategy.rst diff --git a/docs/_sources/accelerators/gpu_faq.rst.txt b/docs/_sources/accelerators/gpu_faq.rst.txt deleted file mode 100644 index c697b2c..0000000 --- a/docs/_sources/accelerators/gpu_faq.rst.txt +++ /dev/null @@ -1,97 +0,0 @@ -:orphan: - -.. _gpu_faq: - -GPU training (FAQ) -================== - -****************************************************************** -How should I adjust the learning rate when using multiple devices? -****************************************************************** - -When using distributed training make sure to modify your learning rate according to your effective -batch size. - -Let's say you have a batch size of 7 in your dataloader. - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - return Dataset(..., batch_size=7) - -In DDP, DDP_SPAWN, Deepspeed, DDP_SHARDED, or Horovod your effective batch size will be 7 * devices * num_nodes. - -.. code-block:: python - - # effective batch size = 7 * 8 - Trainer(accelerator="gpu", devices=8, strategy="ddp") - Trainer(accelerator="gpu", devices=8, strategy="ddp_spawn") - Trainer(accelerator="gpu", devices=8, strategy="ddp_sharded") - Trainer(accelerator="gpu", devices=8, strategy="horovod") - - # effective batch size = 7 * 8 * 10 - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp") - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp_spawn") - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp_sharded") - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="horovod") - -In DDP2 or DP, your effective batch size will be 7 * num_nodes. -The reason is that the full batch is visible to all GPUs on the node when using DDP2. - -.. code-block:: python - - # effective batch size = 7 - Trainer(accelerator="gpu", devices=8, strategy="ddp2") - Trainer(accelerator="gpu", devices=8, strategy="dp") - - # effective batch size = 7 * 10 - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp2") - Trainer(accelerator="gpu", devices=8, strategy="dp") - - -.. note:: Huge batch sizes are actually really bad for convergence. Check out: - `Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour `_ - ----- - -********************************************************* -How do I use multiple GPUs on Jupyter or Colab notebooks? -********************************************************* - -To use multiple GPUs on notebooks, use the *DP* mode. - -.. code-block:: python - - Trainer(accelerator="gpu", devices=4, strategy="dp") - -If you want to use other models, please launch your training via the command-shell. - -.. note:: Learn how to :ref:`access a cloud machine with multiple GPUs ` in this guide. - ----- - -***************************************************** -I'm getting errors related to Pickling. What do I do? -***************************************************** - -Pickle is Python's mechanism for serializing and unserializing data. A majority of distributed modes require that your code is fully pickle compliant. If you run into an issue with pickling try the following to figure out the issue - -.. code-block:: python - - import pickle - - model = YourModel() - pickle.dumps(model) - -If you `ddp` your code doesn't need to be pickled. - -.. code-block:: python - - Trainer(accelerator="gpu", devices=4, strategy="ddp") - -If you use `ddp_spawn` the pickling requirement remains. This is a limitation of Python. - -.. code-block:: python - - Trainer(accelerator="gpu", devices=4, strategy="ddp_spawn") diff --git a/docs/_sources/accelerators/gpu_intermediate.rst.txt b/docs/_sources/accelerators/gpu_intermediate.rst.txt deleted file mode 100644 index c4d9ad8..0000000 --- a/docs/_sources/accelerators/gpu_intermediate.rst.txt +++ /dev/null @@ -1,533 +0,0 @@ -:orphan: - -.. _gpu_intermediate: - -GPU training (Intermediate) -=========================== -**Audience:** Users looking to train across machines or experiment with different scaling techniques. - ----- - -Distributed Training strategies -------------------------------- -Lightning supports multiple ways of doing distributed training. - -.. raw:: html - - - -| - -- Data Parallel (``strategy='dp'``) (multiple-gpus, 1 machine) -- DistributedDataParallel (``strategy='ddp'``) (multiple-gpus across many machines (python script based)). -- DistributedDataParallel (``strategy='ddp_spawn'``) (multiple-gpus across many machines (spawn based)). -- DistributedDataParallel 2 (``strategy='ddp2'``) (DP in a machine, DDP across machines). -- Horovod (``strategy='horovod'``) (multi-machine, multi-gpu, configured at runtime) -- Bagua (``strategy='bagua'``) (multiple-gpus across many machines with advanced training algorithms) - -.. note:: - If you request multiple GPUs or nodes without setting a mode, DDP Spawn will be automatically used. - -For a deeper understanding of what Lightning is doing, feel free to read this -`guide `_. - - -Data Parallel -^^^^^^^^^^^^^ -:class:`~torch.nn.DataParallel` (DP) splits a batch across k GPUs. -That is, if you have a batch of 32 and use DP with 2 GPUs, each GPU will process 16 samples, -after which the root node will aggregate the results. - -.. warning:: DP use is discouraged by PyTorch and Lightning. State is not maintained on the replicas created by the - :class:`~torch.nn.DataParallel` wrapper and you may see errors or misbehavior if you assign state to the module - in the ``forward()`` or ``*_step()`` methods. For the same reason we cannot fully support - :doc:`Manual Optimization <../model/manual_optimization>` with DP. Use DDP which is more stable and at least 3x faster. - -.. warning:: DP only supports scattering and gathering primitive collections of tensors like lists, dicts, etc. - Therefore the :meth:`~pytorch_lightning.core.hooks.ModelHooks.transfer_batch_to_device` hook does not apply in - this mode and if you have overridden it, it will not be called. - -.. testcode:: - :skipif: torch.cuda.device_count() < 2 - - # train on 2 GPUs (using DP mode) - trainer = Trainer(accelerator="gpu", devices=2, strategy="dp") - -Distributed Data Parallel -^^^^^^^^^^^^^^^^^^^^^^^^^ -:class:`~torch.nn.parallel.DistributedDataParallel` (DDP) works as follows: - -1. Each GPU across each node gets its own process. - -2. Each GPU gets visibility into a subset of the overall dataset. It will only ever see that subset. - -3. Each process inits the model. - -4. Each process performs a full forward and backward pass in parallel. - -5. The gradients are synced and averaged across all processes. - -6. Each process updates its optimizer. - -.. code-block:: python - - # train on 8 GPUs (same machine (ie: node)) - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp") - - # train on 32 GPUs (4 nodes) - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp", num_nodes=4) - -This Lightning implementation of DDP calls your script under the hood multiple times with the correct environment -variables: - -.. code-block:: bash - - # example for 3 GPUs DDP - MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=0 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc - MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=1 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc - MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=2 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc - -We use DDP this way because `ddp_spawn` has a few limitations (due to Python and PyTorch): - -1. Since `.spawn()` trains the model in subprocesses, the model on the main process does not get updated. -2. Dataloader(num_workers=N), where N is large, bottlenecks training with DDP... ie: it will be VERY slow or won't work at all. This is a PyTorch limitation. -3. Forces everything to be picklable. - -There are cases in which it is NOT possible to use DDP. Examples are: - -- Jupyter Notebook, Google COLAB, Kaggle, etc. -- You have a nested script without a root package - -In these situations you should use `dp` or `ddp_spawn` instead. - -Distributed Data Parallel 2 -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In certain cases, it's advantageous to use all batches on the same machine instead of a subset. -For instance, you might want to compute a NCE loss where it pays to have more negative samples. - -In this case, we can use DDP2 which behaves like DP in a machine and DDP across nodes. DDP2 does the following: - -1. Copies a subset of the data to each node. - -2. Inits a model on each node. - -3. Runs a forward and backward pass using DP. - -4. Syncs gradients across nodes. - -5. Applies the optimizer updates. - -.. code-block:: python - - # train on 32 GPUs (4 nodes) - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp2", num_nodes=4) - -Distributed Data Parallel Spawn -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -`ddp_spawn` is exactly like `ddp` except that it uses .spawn to start the training processes. - -.. warning:: It is STRONGLY recommended to use `DDP` for speed and performance. - -.. code-block:: python - - mp.spawn(self.ddp_train, nprocs=self.num_processes, args=(model,)) - -If your script does not support being called from the command line (ie: it is nested without a root -project module) you can use the following method: - -.. code-block:: python - - # train on 8 GPUs (same machine (ie: node)) - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp_spawn") - -We STRONGLY discourage this use because it has limitations (due to Python and PyTorch): - -1. The model you pass in will not update. Please save a checkpoint and restore from there. -2. Set Dataloader(num_workers=0) or it will bottleneck training. - -`ddp` is MUCH faster than `ddp_spawn`. We recommend you - -1. Install a top-level module for your project using setup.py - -.. code-block:: python - - # setup.py - #!/usr/bin/env python - - from setuptools import setup, find_packages - - setup( - name="src", - version="0.0.1", - description="Describe Your Cool Project", - author="", - author_email="", - url="https://github.com/YourSeed", # REPLACE WITH YOUR OWN GITHUB PROJECT LINK - install_requires=["pytorch-lightning"], - packages=find_packages(), - ) - -2. Setup your project like so: - -.. code-block:: bash - - /project - /src - some_file.py - /or_a_folder - setup.py - -3. Install as a root-level package - -.. code-block:: bash - - cd /project - pip install -e . - -You can then call your scripts anywhere - -.. code-block:: bash - - cd /project/src - python some_file.py --accelerator 'gpu' --devices 8 --strategy 'ddp' - - -Horovod -^^^^^^^ -`Horovod `_ allows the same training script to be used for single-GPU, -multi-GPU, and multi-node training. - -Like Distributed Data Parallel, every process in Horovod operates on a single GPU with a fixed -subset of the data. Gradients are averaged across all GPUs in parallel during the backward pass, -then synchronously applied before beginning the next step. - -The number of worker processes is configured by a driver application (`horovodrun` or `mpirun`). In -the training script, Horovod will detect the number of workers from the environment, and automatically -scale the learning rate to compensate for the increased total batch size. - -Horovod can be configured in the training script to run with any number of GPUs / processes as follows: - -.. code-block:: python - - # train Horovod on GPU (number of GPUs / machines provided on command-line) - trainer = Trainer(strategy="horovod", accelerator="gpu", devices=1) - - # train Horovod on CPU (number of processes / machines provided on command-line) - trainer = Trainer(strategy="horovod") - -When starting the training job, the driver application will then be used to specify the total -number of worker processes: - -.. code-block:: bash - - # run training with 4 GPUs on a single machine - horovodrun -np 4 python train.py - - # run training with 8 GPUs on two machines (4 GPUs each) - horovodrun -np 8 -H hostname1:4,hostname2:4 python train.py - -See the official `Horovod documentation `_ for details -on installation and performance tuning. - - -Bagua -^^^^^ -`Bagua `_ is a deep learning training acceleration framework which supports -multiple advanced distributed training algorithms including: - -- `Gradient AllReduce `_ for centralized synchronous communication, where gradients are averaged among all workers. -- `Decentralized SGD `_ for decentralized synchronous communication, where each worker exchanges data with one or a few specific workers. -- `ByteGrad `_ and `QAdam `_ for low precision communication, where data is compressed into low precision before communication. -- `Asynchronous Model Average `_ for asynchronous communication, where workers are not required to be synchronized in the same iteration in a lock-step style. - -By default, Bagua uses *Gradient AllReduce* algorithm, which is also the algorithm implemented in Distributed Data Parallel and Horovod, -but Bagua can usually produce a higher training throughput due to its backend written in Rust. - -.. code-block:: python - - # train on 4 GPUs (using Bagua mode) - trainer = Trainer(strategy="bagua", accelerator="gpu", devices=4) - - -By specifying the ``algorithm`` in the ``BaguaStrategy``, you can select more advanced training algorithms featured by Bagua: - - -.. code-block:: python - - # train on 4 GPUs, using Bagua Gradient AllReduce algorithm - trainer = Trainer( - strategy=BaguaStrategy(algorithm="gradient_allreduce"), - accelerator="gpu", - devices=4, - ) - - # train on 4 GPUs, using Bagua ByteGrad algorithm - trainer = Trainer( - strategy=BaguaStrategy(algorithm="bytegrad"), - accelerator="gpu", - devices=4, - ) - - # train on 4 GPUs, using Bagua Decentralized SGD - trainer = Trainer( - strategy=BaguaStrategy(algorithm="decentralized"), - accelerator="gpu", - devices=4, - ) - - # train on 4 GPUs, using Bagua Low Precision Decentralized SGD - trainer = Trainer( - strategy=BaguaStrategy(algorithm="low_precision_decentralized"), - accelerator="gpu", - devices=4, - ) - - # train on 4 GPUs, using Asynchronous Model Average algorithm, with a synchronization interval of 100ms - trainer = Trainer( - strategy=BaguaStrategy(algorithm="async", sync_interval_ms=100), - accelerator="gpu", - devices=4, - ) - -To use *QAdam*, we need to initialize -`QAdamOptimizer `_ first: - -.. code-block:: python - - from pytorch_lightning.strategies import BaguaStrategy - from bagua.torch_api.algorithms.q_adam import QAdamOptimizer - - - class MyModel(pl.LightningModule): - ... - - def configure_optimizers(self): - # initialize QAdam Optimizer - return QAdamOptimizer(self.parameters(), lr=0.05, warmup_steps=100) - - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=BaguaStrategy(algorithm="qadam"), - ) - trainer.fit(model) - -Bagua relies on its own `launcher `_ to schedule jobs. -Below, find examples using ``bagua.distributed.launch`` which follows ``torch.distributed.launch`` API: - -.. code-block:: bash - - # start training with 8 GPUs on a single node - python -m bagua.distributed.launch --nproc_per_node=8 train.py - -If the ssh service is available with passwordless login on each node, you can launch the distributed job on a -single node with ``baguarun`` which has a similar syntax as ``mpirun``. When staring the job, ``baguarun`` will -automatically spawn new processes on each of your training node provided by ``--host_list`` option and each node in it -is described as an ip address followed by a ssh port. - -.. code-block:: bash - - # Run on node1 (or node2) to start training on two nodes (node1 and node2), 8 GPUs per node - baguarun --host_list hostname1:ssh_port1,hostname2:ssh_port2 --nproc_per_node=8 --master_port=port1 train.py - - -.. note:: You can also start training in the same way as Distributed Data Parallel. However, system optimizations like - `Bagua-Net `_ and - `Performance autotuning `_ can only be enabled through bagua - launcher. It is worth noting that with ``Bagua-Net``, Distributed Data Parallel can also achieve - better performance without modifying the training script. - - -See `Bagua Tutorials `_ for more details on installation and advanced features. - - -DP/DDP2 caveats -^^^^^^^^^^^^^^^ -In DP and DDP2 each GPU within a machine sees a portion of a batch. -DP and ddp2 roughly do the following: - -.. testcode:: - - def distributed_forward(batch, model): - batch = torch.Tensor(32, 8) - gpu_0_batch = batch[:8] - gpu_1_batch = batch[8:16] - gpu_2_batch = batch[16:24] - gpu_3_batch = batch[24:] - - y_0 = model_copy_gpu_0(gpu_0_batch) - y_1 = model_copy_gpu_1(gpu_1_batch) - y_2 = model_copy_gpu_2(gpu_2_batch) - y_3 = model_copy_gpu_3(gpu_3_batch) - - return [y_0, y_1, y_2, y_3] - -So, when Lightning calls any of the `training_step`, `validation_step`, `test_step` -you will only be operating on one of those pieces. - -.. testcode:: - - # the batch here is a portion of the FULL batch - def training_step(self, batch, batch_idx): - y_0 = batch - -For most metrics, this doesn't really matter. However, if you want -to add something to your computational graph (like softmax) -using all batch parts you can use the `training_step_end` step. - -.. testcode:: - - def training_step_end(self, outputs): - # only use when on dp - outputs = torch.cat(outputs, dim=1) - softmax = softmax(outputs, dim=1) - out = softmax.mean() - return out - -In pseudocode, the full sequence is: - -.. code-block:: python - - # get data - batch = next(dataloader) - - # copy model and data to each gpu - batch_splits = split_batch(batch, num_gpus) - models = copy_model_to_gpus(model) - - # in parallel, operate on each batch chunk - all_results = [] - for gpu_num in gpus: - batch_split = batch_splits[gpu_num] - gpu_model = models[gpu_num] - out = gpu_model(batch_split) - all_results.append(out) - - # use the full batch for something like softmax - full_out = model.training_step_end(all_results) - -To illustrate why this is needed, let's look at DataParallel - -.. testcode:: - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self(batch) - - # on dp or ddp2 if we did softmax now it would be wrong - # because batch is actually a piece of the full batch - return y_hat - - - def training_step_end(self, step_output): - # step_output has outputs of each part of the batch - - # do softmax here - outputs = torch.cat(outputs, dim=1) - softmax = softmax(outputs, dim=1) - out = softmax.mean() - - return out - -If `training_step_end` is defined it will be called regardless of TPU, DP, DDP, etc... which means -it will behave the same regardless of the backend. - -Validation and test step have the same option when using DP. - -.. testcode:: - - def validation_step_end(self, step_output): - ... - - - def test_step_end(self, step_output): - ... - - -Distributed and 16-bit precision -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Due to an issue with Apex and DataParallel (PyTorch and NVIDIA issue), Lightning does -not allow 16-bit and DP training. We tried to get this to work, but it's an issue on their end. - -Below are the possible configurations we support. - -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| 1 GPU | 1+ GPUs | DP | DDP | 16-bit | command | -+=======+=========+=====+=====+========+=======================================================================+ -| Y | | | | | `Trainer(accelerator="gpu", devices=1)` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| Y | | | | Y | `Trainer(accelerator="gpu", devices=1, precision=16)` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| | Y | Y | | | `Trainer(accelerator="gpu", devices=k, strategy='dp')` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| | Y | | Y | | `Trainer(accelerator="gpu", devices=k, strategy='ddp')` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| | Y | | Y | Y | `Trainer(accelerator="gpu", devices=k, strategy='ddp', precision=16)` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ - - -Implement Your Own Distributed (DDP) training -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you need your own way to init PyTorch DDP you can override :meth:`pytorch_lightning.strategies.ddp.DDPStrategy.init_dist_connection`. - -If you also need to use your own DDP implementation, override :meth:`pytorch_lightning.strategies.ddp.DDPStrategy.configure_ddp`. - ----------- - -Torch Distributed Elastic -------------------------- -Lightning supports the use of Torch Distributed Elastic to enable fault-tolerant and elastic distributed job scheduling. To use it, specify the 'ddp' or 'ddp2' backend and the number of GPUs you want to use in the trainer. - -.. code-block:: python - - Trainer(accelerator="gpu", devices=8, strategy="ddp") - -To launch a fault-tolerant job, run the following on all nodes. - -.. code-block:: bash - - python -m torch.distributed.run - --nnodes=NUM_NODES - --nproc_per_node=TRAINERS_PER_NODE - --rdzv_id=JOB_ID - --rdzv_backend=c10d - --rdzv_endpoint=HOST_NODE_ADDR - YOUR_LIGHTNING_TRAINING_SCRIPT.py (--arg1 ... train script args...) - -To launch an elastic job, run the following on at least ``MIN_SIZE`` nodes and at most ``MAX_SIZE`` nodes. - -.. code-block:: bash - - python -m torch.distributed.run - --nnodes=MIN_SIZE:MAX_SIZE - --nproc_per_node=TRAINERS_PER_NODE - --rdzv_id=JOB_ID - --rdzv_backend=c10d - --rdzv_endpoint=HOST_NODE_ADDR - YOUR_LIGHTNING_TRAINING_SCRIPT.py (--arg1 ... train script args...) - -See the official `Torch Distributed Elastic documentation `_ for details -on installation and more use cases. - -Optimize multi-machine communication ------------------------------------- - -By default, Lightning will select the ``nccl`` backend over ``gloo`` when running on GPUs. -Find more information about PyTorch's supported backends `here `__. - -Lightning allows explicitly specifying the backend via the `process_group_backend` constructor argument on the relevant Strategy classes. By default, Lightning will select the appropriate process group backend based on the hardware used. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy - - # Explicitly specify the process group backend if you choose to - ddp = DDPStrategy(process_group_backend="nccl") - - # Configure the strategy on the Trainer - trainer = Trainer(strategy=ddp, accelerator="gpu", devices=8) diff --git a/docs/_sources/accelerators/hpu.rst.txt b/docs/_sources/accelerators/hpu.rst.txt deleted file mode 100644 index 13eeab8..0000000 --- a/docs/_sources/accelerators/hpu.rst.txt +++ /dev/null @@ -1,40 +0,0 @@ -.. _hpu: - -Accelerator: HPU training -========================= - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Prepare your code (Optional) - :description: Prepare your code to run on any hardware - :col_css: col-md-4 - :button_link: accelerator_prepare.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Basic - :description: Learn the basics of single and multi-HPU core training. - :col_css: col-md-4 - :button_link: hpu_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Enable state-of-the-art scaling with advanced mix-precision settings. - :col_css: col-md-4 - :button_link: hpu_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/accelerators/hpu_basic.rst.txt b/docs/_sources/accelerators/hpu_basic.rst.txt deleted file mode 100644 index e07e153..0000000 --- a/docs/_sources/accelerators/hpu_basic.rst.txt +++ /dev/null @@ -1,83 +0,0 @@ -:orphan: - -.. _hpu_basics: - -Accelerator: HPU training -========================= -**Audience:** Users looking to save money and run large models faster using single or multiple Gaudi devices. - ----- - -What is an HPU? ---------------- - -`Habana® Gaudi® AI Processor (HPU) `__ training processors are built on a heterogeneous architecture with a cluster of fully programmable Tensor Processing Cores (TPC) along with its associated development tools and libraries, and a configurable Matrix Math engine. - -The TPC core is a VLIW SIMD processor with an instruction set and hardware tailored to serve training workloads efficiently. -The Gaudi memory architecture includes on-die SRAM and local memories in each TPC and, -Gaudi is the first DL training processor that has integrated RDMA over Converged Ethernet (RoCE v2) engines on-chip. - -On the software side, the PyTorch Habana bridge interfaces between the framework and SynapseAI software stack to enable the execution of deep learning models on the Habana Gaudi device. - -Gaudi offers a substantial price/performance advantage -- so you get to do more deep learning training while spending less. - -For more information, check out `Gaudi Architecture `__ and `Gaudi Developer Docs `__. - ----- - -Run on 1 Gaudi --------------- - -To enable PyTorch Lightning to utilize the HPU accelerator, simply provide ``accelerator="hpu"`` parameter to the Trainer class. - -.. code-block:: python - - trainer = Trainer(accelerator="hpu", devices=1) - ----- - -Run on multiple Gaudis ----------------------- -The ``devices=8`` and ``accelerator="hpu"`` parameters to the Trainer class enables the Habana accelerator for distributed training with 8 Gaudis. -It uses :class:`~pytorch_lightning.strategies.hpu_parallel.HPUParallelStrategy` internally which is based on DDP strategy with the addition of Habana's collective communication library (HCCL) to support scale-up within a node and scale-out across multiple nodes. - -.. code-block:: python - - trainer = Trainer(devices=8, accelerator="hpu") - ----- - -Select Gaudis automatically ---------------------------- - -Lightning can automatically detect the number of Gaudi devices to run on. This setting is enabled by default if the devices argument is missing. - -.. code-block:: python - - # equivalent - trainer = Trainer(accelerator="hpu") - trainer = Trainer(accelerator="hpu", devices="auto") - ----- - -How to access HPUs ------------------- - -To use HPUs, you must have access to a system with HPU devices. - -AWS -^^^ -You can either use `Gaudi-based AWS EC2 DL1 instances `__ or `Supermicro X12 Gaudi server `__ to get access to HPUs. - -Check out the `Get Started Guide with AWS and Habana `__. - ----- - -.. _known-limitations_hpu: - -Known limitations ------------------ - -* Multiple optimizers are not supported. -* `Habana dataloader `__ is not supported. -* :class:`~pytorch_lightning.callbacks.device_stats_monitor.DeviceStatsMonitor` is not supported. diff --git a/docs/_sources/accelerators/hpu_intermediate.rst.txt b/docs/_sources/accelerators/hpu_intermediate.rst.txt deleted file mode 100644 index 65dca85..0000000 --- a/docs/_sources/accelerators/hpu_intermediate.rst.txt +++ /dev/null @@ -1,68 +0,0 @@ -:orphan: - -.. _hpu_intermediate: - -Accelerator: HPU training -========================= -**Audience:** Gaudi chip users looking to save memory and scale models with mixed-precision training. - ----- - -Enable Mixed Precision ----------------------- - -Lightning also allows mixed precision training with HPUs. -By default, HPU training will use 32-bit precision. To enable mixed precision, set the ``precision`` flag. - -.. code-block:: python - - trainer = Trainer(devices=1, accelerator="hpu", precision=16) - ----- - -Customize Mixed Precision -------------------------- - -Internally, :class:`~pytorch_lightning.plugins.precision.hpu.HPUPrecisionPlugin` uses the Habana Mixed Precision (HMP) package to enable mixed precision training. - -You can execute the ops in FP32 or BF16 precision. The HMP package modifies the Python operators to add the appropriate cast operations for the arguments before execution. -The default settings enable users to enable mixed precision training with minimal code easily. - -In addition to the default settings in HMP, users also have the option of overriding these defaults and providing their -BF16 and FP32 operator lists by passing them as parameter to :class:`~pytorch_lightning.plugins.precision.hpu.HPUPrecisionPlugin`. - -The below snippet shows an example model using MNIST with a single Habana Gaudi device and making use of HMP by overriding the default parameters. -This enables advanced users to provide their own BF16 and FP32 operator list instead of using the HMP defaults. - -.. code-block:: python - - import pytorch_lightning as pl - from pytorch_lightning.plugins import HPUPrecisionPlugin - - # Initialize a trainer with HPU accelerator for HPU strategy for single device, - # with mixed precision using overidden HMP settings - trainer = pl.Trainer( - accelerator="hpu", - devices=1, - # Optional Habana mixed precision params to be set - # Checkout `pl_examples/hpu_examples/simple_mnist/ops_bf16_mnist.txt` for the format - plugins=[ - HPUPrecisionPlugin( - precision=16, - opt_level="O1", - verbose=False, - bf16_file_path="ops_bf16_mnist.txt", - fp32_file_path="ops_fp32_mnist.txt", - ) - ], - ) - - # Init our model - model = LitClassifier() - # Init the data - dm = MNISTDataModule(batch_size=batch_size) - - # Train the model ⚡ - trainer.fit(model, datamodule=dm) - -For more details, please refer to `PyTorch Mixed Precision Training on Gaudi `__. diff --git a/docs/_sources/accelerators/ipu.rst.txt b/docs/_sources/accelerators/ipu.rst.txt deleted file mode 100644 index 138814f..0000000 --- a/docs/_sources/accelerators/ipu.rst.txt +++ /dev/null @@ -1,48 +0,0 @@ -.. _ipu: - -Accelerator: IPU training -========================= - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Prepare your code (Optional) - :description: Prepare your code to run on any hardware - :col_css: col-md-6 - :button_link: accelerator_prepare.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Basic - :description: Learn the basics of single and multi-IPU training. - :col_css: col-md-6 - :button_link: ipu_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Tune model performance with mix-precision settings and the performance analyser. - :col_css: col-md-6 - :button_link: ipu_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Advanced - :description: Learn advanced techniques to customize IPU training for massive models. - :col_css: col-md-6 - :button_link: ipu_advanced.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/accelerators/ipu_advanced.rst.txt b/docs/_sources/accelerators/ipu_advanced.rst.txt deleted file mode 100644 index 1dc4e71..0000000 --- a/docs/_sources/accelerators/ipu_advanced.rst.txt +++ /dev/null @@ -1,143 +0,0 @@ -:orphan: - -.. _ipu_advanced: - -Accelerator: IPU training -========================= -**Audience:** Users looking to customize IPU training for massive models. - ----- - -Advanced IPU options --------------------- - -IPUs provide further optimizations to speed up training. By using the ``IPUStrategy`` we can set the ``device_iterations``, which controls the number of iterations run directly on the IPU devices before returning to the host. Increasing the number of on-device iterations will improve throughput, as there is less device to host communication required. - -.. note:: - - When using model parallelism, it is a hard requirement to increase the number of device iterations to ensure we fully saturate the devices via micro-batching. see :ref:`ipu-model-parallelism` for more information. - -.. code-block:: python - - import pytorch_lightning as pl - from pytorch_lightning.strategies import IPUStrategy - - model = MyLightningModule() - trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(device_iterations=32)) - trainer.fit(model) - -Note that by default we return the last device iteration loss. You can override this by passing in your own ``poptorch.Options`` and setting the AnchorMode as described in the `PopTorch documentation `__. - -.. code-block:: python - - import poptorch - import pytorch_lightning as pl - from pytorch_lightning.strategies import IPUStrategy - - model = MyLightningModule() - inference_opts = poptorch.Options() - inference_opts.deviceIterations(32) - - training_opts = poptorch.Options() - training_opts.anchorMode(poptorch.AnchorMode.All) - training_opts.deviceIterations(32) - - trainer = Trainer( - accelerator="ipu", devices=8, strategy=IPUStrategy(inference_opts=inference_opts, training_opts=training_opts) - ) - trainer.fit(model) - -You can also override all options by passing the ``poptorch.Options`` to the plugin. See `PopTorch options documentation `__ for more information. - ----- - -.. _ipu-model-parallelism: - -Model parallelism ------------------ - -Due to the IPU architecture, larger models should be parallelized across IPUs by design. Currently PopTorch provides the capabilities via annotations as described in `parallel execution strategies `__. - -Below is an example using the block annotation in a LightningModule. - -.. note:: - - Currently, when using model parallelism we do not infer the number of IPUs required for you. This is done via the annotations themselves. If you specify 4 different IDs when defining Blocks, this means your model will be split onto 4 different IPUs. - - This is also mutually exclusive with the Trainer flag. In other words, if your model is split onto 2 IPUs and you set ``Trainer(accelerator="ipu", devices=4)`` this will require 8 IPUs in total: data parallelism will be used to replicate the two-IPU model 4 times. - - When pipelining the model you must also increase the `device_iterations` to ensure full data saturation of the devices data, i.e whilst one device in the model pipeline processes a batch of data, the other device can start on the next batch. For example if the model is split onto 4 IPUs, we require `device_iterations` to be at-least 4. - - -.. code-block:: python - - import pytorch_lightning as pl - import poptorch - - - class MyLightningModule(pl.LightningModule): - def __init__(self): - super().__init__() - # This will place layer1, layer2+layer3, layer4, softmax on different IPUs at runtime. - # BeginBlock will start a new id for all layers within this block - self.layer1 = poptorch.BeginBlock(torch.nn.Linear(5, 10), ipu_id=0) - - # This layer starts a new block, - # adding subsequent layers to this current block at runtime - # till the next block has been declared - self.layer2 = poptorch.BeginBlock(torch.nn.Linear(10, 5), ipu_id=1) - self.layer3 = torch.nn.Linear(5, 5) - - # Create new blocks - self.layer4 = poptorch.BeginBlock(torch.nn.Linear(5, 5), ipu_id=2) - self.softmax = poptorch.BeginBlock(torch.nn.Softmax(dim=1), ipu_id=3) - - ... - - - model = MyLightningModule() - trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(device_iterations=20)) - trainer.fit(model) - - -You can also use the block context manager within the forward function, or any of the step functions. - -.. code-block:: python - - import pytorch_lightning as pl - import poptorch - - - class MyLightningModule(pl.LightningModule): - def __init__(self): - super().__init__() - self.layer1 = torch.nn.Linear(5, 10) - self.layer2 = torch.nn.Linear(10, 5) - self.layer3 = torch.nn.Linear(5, 5) - self.layer4 = torch.nn.Linear(5, 5) - - self.act = torch.nn.ReLU() - self.softmax = torch.nn.Softmax(dim=1) - - def forward(self, x): - - with poptorch.Block(ipu_id=0): - x = self.act(self.layer1(x)) - - with poptorch.Block(ipu_id=1): - x = self.act(self.layer2(x)) - - with poptorch.Block(ipu_id=2): - x = self.act(self.layer3(x)) - x = self.act(self.layer4(x)) - - with poptorch.Block(ipu_id=3): - x = self.softmax(x) - return x - - ... - - - model = MyLightningModule() - trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(device_iterations=20)) - trainer.fit(model) diff --git a/docs/_sources/accelerators/ipu_basic.rst.txt b/docs/_sources/accelerators/ipu_basic.rst.txt deleted file mode 100644 index 492c7bf..0000000 --- a/docs/_sources/accelerators/ipu_basic.rst.txt +++ /dev/null @@ -1,68 +0,0 @@ -:orphan: - -.. _ipu_basic: - -Accelerator: IPU training -========================= -**Audience:** Users looking to save money and run large models faster using single or multiple IPU devices. - ----- - -What is an IPU? ---------------- - -The Graphcore `Intelligence Processing Unit (IPU) `__, built for Artificial Intelligence and Machine Learning, consists of many individual cores, called *tiles*, allowing highly parallel computation. Due to the high bandwidth between tiles, IPUs facilitate machine learning loads where parallelization is essential. Because computation is heavily parallelized, - -IPUs operate in a different way to conventional accelerators such as CPU/GPUs. IPUs do not require large batch sizes for maximum parallelization, can provide optimizations across the compiled graph and rely on model parallelism to fully utilize tiles for larger models. - -IPUs are used to build IPU-PODs, rack-based systems of IPU-Machines for larger workloads. See the `IPU Architecture `__ for more information. - -See the `Graphcore Glossary `__ for the definitions of other IPU-specific terminology. - -.. note:: - IPU support is experimental and a work in progress (see :ref:`known-limitations`). If you run into any problems, please leave an issue. - ----- - -Run on 1 IPU ------------- -To use a single IPU, set the accelerator and devices argument. - -.. code-block:: python - - trainer = pl.Trainer(accelerator="ipu", devices=1) - ----- - -Run on multiple IPUs --------------------- -To use multiple IPUs set the devices to a number that is a power of 2 (i.e: 2, 4, 8, 16, ...) - -.. code-block:: python - - trainer = pl.Trainer(accelerator="ipu", devices=8) - ----- - -How to access IPUs ------------------- - -To use IPUs you must have access to a system with IPU devices. To get access see `get started `__. - -You must ensure that the IPU system has enabled the PopART and Poplar packages from the SDK. Instructions are in the Get Started guide for your IPU system, on the Graphcore `documents portal `__. - ----- - -.. _known-limitations: - -Known limitations ------------------ - -Currently there are some known limitations that are being addressed in the near future to make the experience seamless when moving from different devices. - -Please see the `MNIST example `__ which displays most of the limitations and how to overcome them till they are resolved. - -* ``self.log`` is not supported in the ``training_step``, ``validation_step``, ``test_step`` or ``predict_step``. This is due to the step function being traced and sent to the IPU devices. We're actively working on fixing this -* Multiple optimizers are not supported. ``training_step`` only supports returning one loss from the ``training_step`` function as a result -* Since the step functions are traced, branching logic or any form of primitive values are traced into constants. Be mindful as this could lead to errors in your custom code -* Clipping gradients is not supported diff --git a/docs/_sources/accelerators/ipu_intermediate.rst.txt b/docs/_sources/accelerators/ipu_intermediate.rst.txt deleted file mode 100644 index 68c866e..0000000 --- a/docs/_sources/accelerators/ipu_intermediate.rst.txt +++ /dev/null @@ -1,63 +0,0 @@ -:orphan: - -.. _ipu_intermediate: - -Accelerator: IPU training -========================= -**Audience:** IPU users looking to increase performance via mixed precision and analysis tools. - ----- - -Mixed precision & 16 bit precision ----------------------------------- - -Lightning also supports training in mixed precision with IPUs. -By default, IPU training will use 32-bit precision. To enable mixed precision, -set the precision flag. - -.. note:: - Currently there is no dynamic scaling of the loss with mixed precision training. - -.. code-block:: python - - import pytorch_lightning as pl - - model = MyLightningModule() - trainer = pl.Trainer(accelerator="ipu", devices=8, precision=16) - trainer.fit(model) - -You can also use pure 16-bit training, where the weights are also in 16-bit precision. - -.. code-block:: python - - import pytorch_lightning as pl - from pytorch_lightning.strategies import IPUStrategy - - model = MyLightningModule() - model = model.half() - trainer = pl.Trainer(accelerator="ipu", devices=8, precision=16) - trainer.fit(model) - ----- - -PopVision Graph Analyser ------------------------- - -.. figure:: ../_static/images/accelerator/ipus/profiler.png - :alt: PopVision Graph Analyser - :width: 500 - -Lightning supports integration with the `PopVision Graph Analyser Tool `__. This helps to look at utilization of IPU devices and provides helpful metrics during the lifecycle of your trainer. Once you have gained access, The PopVision Graph Analyser Tool can be downloaded via the `GraphCore download website `__. - -Lightning supports dumping all reports to a directory to open using the tool. - -.. code-block:: python - - import pytorch_lightning as pl - from pytorch_lightning.strategies import IPUStrategy - - model = MyLightningModule() - trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(autoreport_dir="report_dir/")) - trainer.fit(model) - -This will dump all reports to ``report_dir/`` which can then be opened using the Graph Analyser Tool, see `Opening Reports `__. diff --git a/docs/_sources/accelerators/tpu.rst.txt b/docs/_sources/accelerators/tpu.rst.txt deleted file mode 100644 index 6809277..0000000 --- a/docs/_sources/accelerators/tpu.rst.txt +++ /dev/null @@ -1,55 +0,0 @@ -.. _tpu: - -Accelerator: TPU training -========================= - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Prepare your code (Optional) - :description: Prepare your code to run on any hardware - :col_css: col-md-4 - :button_link: accelerator_prepare.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Basic - :description: Learn the basics of single and multi-TPU core training. - :col_css: col-md-4 - :button_link: tpu_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Scale massive models using cloud TPUs. - :col_css: col-md-4 - :button_link: tpu_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Advanced - :description: Dive into XLA and advanced techniques to optimize TPU-powered models. - :col_css: col-md-4 - :button_link: tpu_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: FAQ - :description: Frequently asked questions about TPU training. - :col_css: col-md-4 - :button_link: gpu_faq.html - :height: 150 - -.. raw:: html - -
-
diff --git a/docs/_sources/accelerators/tpu_advanced.rst.txt b/docs/_sources/accelerators/tpu_advanced.rst.txt deleted file mode 100644 index 0aa490e..0000000 --- a/docs/_sources/accelerators/tpu_advanced.rst.txt +++ /dev/null @@ -1,68 +0,0 @@ -:orphan: - -TPU training (Advanced) -======================= -**Audience:** Users looking to apply advanced performance techniques to TPU training. - ----- - -Weight Sharing/Tying --------------------- -Weight Tying/Sharing is a technique where in the module weights are shared among two or more layers. -This is a common method to reduce memory consumption and is utilized in many State of the Art -architectures today. - -PyTorch XLA requires these weights to be tied/shared after moving the model -to the TPU device. To support this requirement Lightning provides a model hook which is -called after the model is moved to the device. Any weights that require to be tied should -be done in the `on_post_move_to_device` model hook. This will ensure that the weights -among the modules are shared and not copied. - -PyTorch Lightning has an inbuilt check which verifies that the model parameter lengths -match once the model is moved to the device. If the lengths do not match Lightning -throws a warning message. - -Example: - -.. code-block:: python - - from pytorch_lightning.core.lightning import LightningModule - from torch import nn - from pytorch_lightning.trainer.trainer import Trainer - - - class WeightSharingModule(LightningModule): - def __init__(self): - super().__init__() - self.layer_1 = nn.Linear(32, 10, bias=False) - self.layer_2 = nn.Linear(10, 32, bias=False) - self.layer_3 = nn.Linear(32, 10, bias=False) - # TPU shared weights are copied independently - # on the XLA device and this line won't have any effect. - # However, it works fine for CPU and GPU. - self.layer_3.weight = self.layer_1.weight - - def forward(self, x): - x = self.layer_1(x) - x = self.layer_2(x) - x = self.layer_3(x) - return x - - def on_post_move_to_device(self): - # Weights shared after the model has been moved to TPU Device - self.layer_3.weight = self.layer_1.weight - - - model = WeightSharingModule() - trainer = Trainer(max_epochs=1, accelerator="tpu", devices=8) - -See `XLA Documentation `_ - ----- - -XLA ---- -XLA is the library that interfaces PyTorch with the TPUs. -For more information check out `XLA `_. - -Guide for `troubleshooting XLA `_ diff --git a/docs/_sources/accelerators/tpu_basic.rst.txt b/docs/_sources/accelerators/tpu_basic.rst.txt deleted file mode 100644 index af7c45f..0000000 --- a/docs/_sources/accelerators/tpu_basic.rst.txt +++ /dev/null @@ -1,255 +0,0 @@ -:orphan: - -TPU training (Basic) -==================== -**Audience:** Users looking to train on single or multiple TPU cores. - ----- - -.. raw:: html - - - -| - -Lightning supports running on TPUs. At this moment, TPUs are available -on Google Cloud (GCP), Google Colab and Kaggle Environments. For more information on TPUs -`watch this video `_. - ----------------- - -What is a TPU? --------------- -Tensor Processing Unit (TPU) is an AI accelerator application-specific integrated circuit (ASIC) developed by Google specifically for neural networks. - -A TPU has 8 cores where each core is optimized for 128x128 matrix multiplies. In general, a single TPU is about as fast as 5 V100 GPUs! - -A TPU pod hosts many TPUs on it. Currently, TPU v3 Pod has up to 2048 TPU cores and 32 TiB of memory! -You can request a full pod from Google cloud or a "slice" which gives you -some subset of those 2048 cores. - ----- - -Run on 1 TPU core ------------------ -Enable the following Trainer arguments to run on 1 TPU. - -.. code:: - - trainer = Trainer(accelerator="tpu", devices=1) - ----- - -Run on multiple TPU cores -------------------------- -For multiple TPU cores, change the value of the devices flag. - -.. code:: - - trainer = Trainer(accelerator="tpu", devices=8) - ----- - -Run on a specific TPU core --------------------------- - -To run on a specific core, specify the index of the TPU core. - -.. code-block:: python - - trainer = pl.Trainer(accelerator="tpu", devices=[5]) - -This example runs on the 5th core, not on five cores. - ----- - -How to access TPUs ------------------- -To access TPUs, there are three main ways. - -Google Colab -^^^^^^^^^^^^ -Colab is like a jupyter notebook with a free GPU or TPU -hosted on GCP. - -To get a TPU on colab, follow these steps: - -1. Go to `https://colab.research.google.com/ `_. - -2. Click "new notebook" (bottom right of pop-up). - -3. Click runtime > change runtime settings. Select Python 3, and hardware accelerator "TPU". - This will give you a TPU with 8 cores. - -4. Next, insert this code into the first cell and execute. - This will install the xla library that interfaces between PyTorch and the TPU. - - .. code-block:: - - !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.9-cp37-cp37m-linux_x86_64.whl - -5. Once the above is done, install PyTorch Lightning. - - .. code-block:: - - !pip install pytorch-lightning - -6. Then set up your LightningModule as normal. - -Google Cloud (GCP) -^^^^^^^^^^^^^^^^^^ -? - -Kaggle -^^^^^^ -For starting Kaggle projects with TPUs, refer to this `kernel `_. - ----- - -Optimize Performance --------------------- - -The TPU was designed for specific workloads and operations to carry out large volumes of matrix multiplication, -convolution operations and other commonly used ops in applied deep learning. -The specialization makes it a strong choice for NLP tasks, sequential convolutional networks, and under low precision operation. -There are cases in which training on TPUs is slower when compared with GPUs, for possible reasons listed: - -- Too small batch size. -- Explicit evaluation of tensors during training, e.g. ``tensor.item()`` -- Tensor shapes (e.g. model inputs) change often during training. -- Limited resources when using TPU's with PyTorch `Link `_ -- XLA Graph compilation during the initial steps `Reference `_ -- Some tensor ops are not fully supported on TPU, or not supported at all. These operations will be performed on CPU (context switch). -- PyTorch integration is still experimental. Some performance bottlenecks may simply be the result of unfinished implementation. - -The official PyTorch XLA `performance guide `_ -has more detailed information on how PyTorch code can be optimized for TPU. In particular, the -`metrics report `_ allows -one to identify operations that lead to context switching. - ----- - -FAQ ---- - -**XLA configuration is missing** - -.. code-block:: - - File "/usr/local/lib/python3.8/dist-packages/torch_xla/core/xla_model.py", line 18, in - _DEVICES = xu.LazyProperty(lambda: torch_xla._XLAC._xla_get_devices()) - RuntimeError: tensorflow/compiler/xla/xla_client/computation_client.cc:273 : Missing XLA configuration - Traceback (most recent call last): - ... - File "/home/kaushikbokka/pytorch-lightning/pytorch_lightning/utilities/device_parser.py", line 125, in parse_tpu_cores - raise MisconfigurationException('No TPU devices were found.') - pytorch_lightning.utilities.exceptions.MisconfigurationException: No TPU devices were found. - -This means the system is missing XLA configuration. You would need to set up XRT TPU device configuration. - -For TPUVM architecture, you could set it in your terminal by: - -.. code-block:: bash - - export XRT_TPU_CONFIG="localservice;0;localhost:51011" - -And for the old TPU + 2VM architecture, you could set it by: - -.. code-block:: bash - - export TPU_IP_ADDRESS=10.39.209.42 # You could get the IP Address in the GCP TPUs section - export XRT_TPU_CONFIG="tpu_worker;0;$TPU_IP_ADDRESS:8470" - ----- - -**How to clear up the programs using TPUs in the background** - -.. code-block:: bash - - lsof -w /lib/libtpu.so | grep "python" | awk '{print $2}' | xargs -r kill -9 - -Sometimes, there can still be old programs running on the TPUs, which would make the TPUs unavailable to use. You could use the above command in the terminal to kill the running processes. - ----- - -**Replication issue** - -.. code-block:: - - File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 200, in set_replication - replication_devices = xla_replication_devices(devices) - File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 187, in xla_replication_devices - .format(len(local_devices), len(kind_devices))) - RuntimeError: Cannot replicate if number of devices (1) is different from 8 - -This error is raised when the XLA device is called outside the spawn process. Internally in `TPUSpawn` Strategy for training on multiple tpu cores, we use XLA's `xmp.spawn`. -Don't use ``xm.xla_device()`` while working on Lightning + TPUs! - ----- - -**Unsupported datatype transfer to TPU** - -.. code-block:: - - File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 205, in _for_each_instance_rewrite - v = _for_each_instance_rewrite(result.__dict__[k], select_fn, fn, rwmap) - File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 206, in _for_each_instance_rewrite - result.__dict__[k] = v - TypeError: 'mappingproxy' object does not support item assignment - -PyTorch XLA only supports Tensor objects for CPU to TPU data transfer. Might cause issues if the User is trying to send some non-tensor objects through the DataLoader or during saving states. - ----- - -**Using `tpu_spawn_debug` Strategy alias** - -.. code-block:: python - - import pytorch_lightning as pl - - my_model = MyLightningModule() - trainer = pl.Trainer(accelerator="tpu", devices=8, strategy="tpu_spawn_debug") - trainer.fit(my_model) - -Example Metrics report: - -.. code-block:: - - Metric: CompileTime - TotalSamples: 202 - Counter: 06m09s401ms746.001us - ValueRate: 778ms572.062us / second - Rate: 0.425201 / second - Percentiles: 1%=001ms32.778us; 5%=001ms61.283us; 10%=001ms79.236us; 20%=001ms110.973us; 50%=001ms228.773us; 80%=001ms339.183us; 90%=001ms434.305us; 95%=002ms921.063us; 99%=21s102ms853.173us - - -A lot of PyTorch operations aren't lowered to XLA, which could lead to significant slowdown of the training process. -These operations are moved to the CPU memory and evaluated, and then the results are transferred back to the XLA device(s). -By using the `tpu_spawn_debug` Strategy, users could create a metrics report to diagnose issues. - -The report includes things like (`XLA Reference `_): - -* how many times we issue XLA compilations and time spent on issuing. -* how many times we execute and time spent on execution -* how many device data handles we create/destroy etc. - ----- - -**TPU Pod Training Startup script** - -All TPU VMs in a Pod setup are required to access the model code and data. -One easy way to achieve this is to use the following startup script when creating the TPU VM pod. -It will perform the data downloading on all TPU VMs. Note that you need to export the corresponding environment variables following the instruction in Create TPU Node. - -.. code-block:: bash - - gcloud alpha compute tpus tpu-vm create ${TPU_NAME} --zone ${ZONE} --project ${PROJECT_ID} --accelerator-type v3-32 --version ${RUNTIME_VERSION} --metadata startup-script=setup.py - -Then users could ssh to any TPU worker, e.g. worker 0, check if data/model downloading is finished and -start the training after generating the ssh-keys to ssh between VM workers on a pod: - -.. code-block:: bash - - python3 -m torch_xla.distributed.xla_dist --tpu=$TPU_NAME -- python3 train.py --max_epochs=5 --batch_size=32 diff --git a/docs/_sources/accelerators/tpu_faq.rst.txt b/docs/_sources/accelerators/tpu_faq.rst.txt deleted file mode 100644 index af7c45f..0000000 --- a/docs/_sources/accelerators/tpu_faq.rst.txt +++ /dev/null @@ -1,255 +0,0 @@ -:orphan: - -TPU training (Basic) -==================== -**Audience:** Users looking to train on single or multiple TPU cores. - ----- - -.. raw:: html - - - -| - -Lightning supports running on TPUs. At this moment, TPUs are available -on Google Cloud (GCP), Google Colab and Kaggle Environments. For more information on TPUs -`watch this video `_. - ----------------- - -What is a TPU? --------------- -Tensor Processing Unit (TPU) is an AI accelerator application-specific integrated circuit (ASIC) developed by Google specifically for neural networks. - -A TPU has 8 cores where each core is optimized for 128x128 matrix multiplies. In general, a single TPU is about as fast as 5 V100 GPUs! - -A TPU pod hosts many TPUs on it. Currently, TPU v3 Pod has up to 2048 TPU cores and 32 TiB of memory! -You can request a full pod from Google cloud or a "slice" which gives you -some subset of those 2048 cores. - ----- - -Run on 1 TPU core ------------------ -Enable the following Trainer arguments to run on 1 TPU. - -.. code:: - - trainer = Trainer(accelerator="tpu", devices=1) - ----- - -Run on multiple TPU cores -------------------------- -For multiple TPU cores, change the value of the devices flag. - -.. code:: - - trainer = Trainer(accelerator="tpu", devices=8) - ----- - -Run on a specific TPU core --------------------------- - -To run on a specific core, specify the index of the TPU core. - -.. code-block:: python - - trainer = pl.Trainer(accelerator="tpu", devices=[5]) - -This example runs on the 5th core, not on five cores. - ----- - -How to access TPUs ------------------- -To access TPUs, there are three main ways. - -Google Colab -^^^^^^^^^^^^ -Colab is like a jupyter notebook with a free GPU or TPU -hosted on GCP. - -To get a TPU on colab, follow these steps: - -1. Go to `https://colab.research.google.com/ `_. - -2. Click "new notebook" (bottom right of pop-up). - -3. Click runtime > change runtime settings. Select Python 3, and hardware accelerator "TPU". - This will give you a TPU with 8 cores. - -4. Next, insert this code into the first cell and execute. - This will install the xla library that interfaces between PyTorch and the TPU. - - .. code-block:: - - !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.9-cp37-cp37m-linux_x86_64.whl - -5. Once the above is done, install PyTorch Lightning. - - .. code-block:: - - !pip install pytorch-lightning - -6. Then set up your LightningModule as normal. - -Google Cloud (GCP) -^^^^^^^^^^^^^^^^^^ -? - -Kaggle -^^^^^^ -For starting Kaggle projects with TPUs, refer to this `kernel `_. - ----- - -Optimize Performance --------------------- - -The TPU was designed for specific workloads and operations to carry out large volumes of matrix multiplication, -convolution operations and other commonly used ops in applied deep learning. -The specialization makes it a strong choice for NLP tasks, sequential convolutional networks, and under low precision operation. -There are cases in which training on TPUs is slower when compared with GPUs, for possible reasons listed: - -- Too small batch size. -- Explicit evaluation of tensors during training, e.g. ``tensor.item()`` -- Tensor shapes (e.g. model inputs) change often during training. -- Limited resources when using TPU's with PyTorch `Link `_ -- XLA Graph compilation during the initial steps `Reference `_ -- Some tensor ops are not fully supported on TPU, or not supported at all. These operations will be performed on CPU (context switch). -- PyTorch integration is still experimental. Some performance bottlenecks may simply be the result of unfinished implementation. - -The official PyTorch XLA `performance guide `_ -has more detailed information on how PyTorch code can be optimized for TPU. In particular, the -`metrics report `_ allows -one to identify operations that lead to context switching. - ----- - -FAQ ---- - -**XLA configuration is missing** - -.. code-block:: - - File "/usr/local/lib/python3.8/dist-packages/torch_xla/core/xla_model.py", line 18, in - _DEVICES = xu.LazyProperty(lambda: torch_xla._XLAC._xla_get_devices()) - RuntimeError: tensorflow/compiler/xla/xla_client/computation_client.cc:273 : Missing XLA configuration - Traceback (most recent call last): - ... - File "/home/kaushikbokka/pytorch-lightning/pytorch_lightning/utilities/device_parser.py", line 125, in parse_tpu_cores - raise MisconfigurationException('No TPU devices were found.') - pytorch_lightning.utilities.exceptions.MisconfigurationException: No TPU devices were found. - -This means the system is missing XLA configuration. You would need to set up XRT TPU device configuration. - -For TPUVM architecture, you could set it in your terminal by: - -.. code-block:: bash - - export XRT_TPU_CONFIG="localservice;0;localhost:51011" - -And for the old TPU + 2VM architecture, you could set it by: - -.. code-block:: bash - - export TPU_IP_ADDRESS=10.39.209.42 # You could get the IP Address in the GCP TPUs section - export XRT_TPU_CONFIG="tpu_worker;0;$TPU_IP_ADDRESS:8470" - ----- - -**How to clear up the programs using TPUs in the background** - -.. code-block:: bash - - lsof -w /lib/libtpu.so | grep "python" | awk '{print $2}' | xargs -r kill -9 - -Sometimes, there can still be old programs running on the TPUs, which would make the TPUs unavailable to use. You could use the above command in the terminal to kill the running processes. - ----- - -**Replication issue** - -.. code-block:: - - File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 200, in set_replication - replication_devices = xla_replication_devices(devices) - File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 187, in xla_replication_devices - .format(len(local_devices), len(kind_devices))) - RuntimeError: Cannot replicate if number of devices (1) is different from 8 - -This error is raised when the XLA device is called outside the spawn process. Internally in `TPUSpawn` Strategy for training on multiple tpu cores, we use XLA's `xmp.spawn`. -Don't use ``xm.xla_device()`` while working on Lightning + TPUs! - ----- - -**Unsupported datatype transfer to TPU** - -.. code-block:: - - File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 205, in _for_each_instance_rewrite - v = _for_each_instance_rewrite(result.__dict__[k], select_fn, fn, rwmap) - File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 206, in _for_each_instance_rewrite - result.__dict__[k] = v - TypeError: 'mappingproxy' object does not support item assignment - -PyTorch XLA only supports Tensor objects for CPU to TPU data transfer. Might cause issues if the User is trying to send some non-tensor objects through the DataLoader or during saving states. - ----- - -**Using `tpu_spawn_debug` Strategy alias** - -.. code-block:: python - - import pytorch_lightning as pl - - my_model = MyLightningModule() - trainer = pl.Trainer(accelerator="tpu", devices=8, strategy="tpu_spawn_debug") - trainer.fit(my_model) - -Example Metrics report: - -.. code-block:: - - Metric: CompileTime - TotalSamples: 202 - Counter: 06m09s401ms746.001us - ValueRate: 778ms572.062us / second - Rate: 0.425201 / second - Percentiles: 1%=001ms32.778us; 5%=001ms61.283us; 10%=001ms79.236us; 20%=001ms110.973us; 50%=001ms228.773us; 80%=001ms339.183us; 90%=001ms434.305us; 95%=002ms921.063us; 99%=21s102ms853.173us - - -A lot of PyTorch operations aren't lowered to XLA, which could lead to significant slowdown of the training process. -These operations are moved to the CPU memory and evaluated, and then the results are transferred back to the XLA device(s). -By using the `tpu_spawn_debug` Strategy, users could create a metrics report to diagnose issues. - -The report includes things like (`XLA Reference `_): - -* how many times we issue XLA compilations and time spent on issuing. -* how many times we execute and time spent on execution -* how many device data handles we create/destroy etc. - ----- - -**TPU Pod Training Startup script** - -All TPU VMs in a Pod setup are required to access the model code and data. -One easy way to achieve this is to use the following startup script when creating the TPU VM pod. -It will perform the data downloading on all TPU VMs. Note that you need to export the corresponding environment variables following the instruction in Create TPU Node. - -.. code-block:: bash - - gcloud alpha compute tpus tpu-vm create ${TPU_NAME} --zone ${ZONE} --project ${PROJECT_ID} --accelerator-type v3-32 --version ${RUNTIME_VERSION} --metadata startup-script=setup.py - -Then users could ssh to any TPU worker, e.g. worker 0, check if data/model downloading is finished and -start the training after generating the ssh-keys to ssh between VM workers on a pod: - -.. code-block:: bash - - python3 -m torch_xla.distributed.xla_dist --tpu=$TPU_NAME -- python3 train.py --max_epochs=5 --batch_size=32 diff --git a/docs/_sources/accelerators/tpu_intermediate.rst.txt b/docs/_sources/accelerators/tpu_intermediate.rst.txt deleted file mode 100644 index 826f568..0000000 --- a/docs/_sources/accelerators/tpu_intermediate.rst.txt +++ /dev/null @@ -1,113 +0,0 @@ -:orphan: - -TPU training (Intermediate) -=========================== -**Audience:** Users looking to use cloud TPUs. - ----- - -DistributedSamplers -------------------- -Lightning automatically inserts the correct samplers - no need to do this yourself! - -Usually, with TPUs (and DDP), you would need to define a DistributedSampler to move the right -chunk of data to the appropriate TPU. As mentioned, this is not needed in Lightning - -.. note:: Don't add distributedSamplers. Lightning does this automatically - -If for some reason you still need to, this is how to construct the sampler -for TPU use - -.. code-block:: python - - import torch_xla.core.xla_model as xm - - - def train_dataloader(self): - dataset = MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()) - - # required for TPU support - sampler = None - if use_tpu: - sampler = torch.utils.data.distributed.DistributedSampler( - dataset, num_replicas=xm.xrt_world_size(), rank=xm.get_ordinal(), shuffle=True - ) - - loader = DataLoader(dataset, sampler=sampler, batch_size=32) - - return loader - -Configure the number of TPU cores in the trainer. You can only choose 1 or 8. -To use a full TPU pod skip to the TPU pod section. - -.. code-block:: python - - import pytorch_lightning as pl - - my_model = MyLightningModule() - trainer = pl.Trainer(accelerator="tpu", devices=8) - trainer.fit(my_model) - -That's it! Your model will train on all 8 TPU cores. - ----------------- - -Distributed Backend with TPU ----------------------------- -The ``accelerator`` option used for GPUs does not apply to TPUs. -TPUs work in DDP mode by default (distributing over each core) - ----------------- - -TPU VM ------- -Lightning supports training on the new Cloud TPU VMs. -Previously, we needed separate VMs to connect to the TPU machines, but as -Cloud TPU VMs run on the TPU Host machines, it allows direct SSH access -for the users. Hence, this architecture upgrade leads to cheaper and significantly -better performance and usability while working with TPUs. - -The TPUVMs come pre-installed with latest versions of PyTorch and PyTorch XLA. -After connecting to the VM and before running your Lightning code, you would need -to set the XRT TPU device configuration. - -.. code-block:: bash - - $ export XRT_TPU_CONFIG="localservice;0;localhost:51011" - -You could learn more about the Cloud TPU VM architecture `here `_ - ----------------- - -TPU Pod -------- -To train on more than 8 cores, your code actually doesn't change! -All you need to do is submit the following command: - -.. code-block:: bash - - $ python -m torch_xla.distributed.xla_dist - --tpu=$TPU_POD_NAME - --conda-env=torch-xla-nightly - -- python /usr/share/torch-xla-1.8.1/pytorch/xla/test/test_train_imagenet.py --fake_data - -See `this guide `_ -on how to set up the instance groups and VMs needed to run TPU Pods. - ----------------- - -16 bit precision ----------------- -Lightning also supports training in 16-bit precision with TPUs. -By default, TPU training will use 32-bit precision. To enable 16-bit, -set the 16-bit flag. - -.. code-block:: python - - import pytorch_lightning as pl - - my_model = MyLightningModule() - trainer = pl.Trainer(accelerator="tpu", devices=8, precision=16) - trainer.fit(my_model) - -Under the hood the xla library will use the `bfloat16 type `_. diff --git a/docs/_sources/advanced/model_parallel.rst.txt b/docs/_sources/advanced/model_parallel.rst.txt deleted file mode 100644 index 811bc57..0000000 --- a/docs/_sources/advanced/model_parallel.rst.txt +++ /dev/null @@ -1,905 +0,0 @@ -.. _model-parallel: - -Train 1 trillion+ parameter models -================================== - -When training large models, fitting larger batch sizes, or trying to increase throughput using multi-GPU compute, Lightning provides advanced optimized distributed training strategies to support these cases and offer substantial improvements in memory usage. - -In many cases these strategies are some flavour of model parallelism however we only introduce concepts at a high level to get you started. Refer to the `FairScale documentation `_ for more information about model parallelism. - -Note that some of the extreme memory saving configurations will affect the speed of training. This Speed/Memory trade-off in most cases can be adjusted. - -Some of these memory-efficient strategies rely on offloading onto other forms of memory, such as CPU RAM or NVMe. This means you can even see memory benefits on a **single GPU**, using a strategy such as :ref:`deepspeed-zero-stage-3-offload`. - -Check out this amazing video explaining model parallelism and how it works behind the scenes: - -.. raw:: html - - - - -Choosing an Advanced Distributed GPU Strategy -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you would like to stick with PyTorch DDP, see :ref:`ddp-optimizations`. - -Unlike :class:`~torch.nn.parallel.DistributedDataParallel` (DDP) where the maximum trainable model size and batch size do not change with respect to the number of GPUs, memory-optimized strategies can accommodate bigger models and larger batches as more GPUs are used. This means as you scale up the number of GPUs, you can reach the number of model parameters you'd like to train. - -There are many considerations when choosing a strategy as described below. In addition, check out the visualization of various strategy benchmarks using `minGPT `__ `here `__. - -Pre-training vs Fine-tuning -""""""""""""""""""""""""""" - -When fine-tuning, we often use a magnitude less data compared to pre-training a model. This is important when choosing a distributed strategy as usually for pre-training, **we are compute-bound**. -This means we cannot sacrifice throughput as much as if we were fine-tuning, because in fine-tuning the data requirement is smaller. - -Overall: - -* When **fine-tuning** a model, use advanced memory efficient strategies such as :ref:`deepspeed-zero-stage-3` or :ref:`deepspeed-zero-stage-3-offload`, allowing you to fine-tune larger models if you are limited on compute -* When **pre-training** a model, use simpler optimizations such :ref:`sharded-training`, :ref:`deepspeed-zero-stage-2` or :ref:`fully-sharded-training`, scaling the number of GPUs to reach larger parameter sizes -* For both fine-tuning and pre-training, use :ref:`deepspeed-activation-checkpointing` or :ref:`fairscale-activation-checkpointing` as the throughput degradation is not significant - -For example when using 128 GPUs, you can **pre-train** large 10 to 20 Billion parameter models using :ref:`deepspeed-zero-stage-2` without having to take a performance hit with more advanced optimized multi-gpu strategy. - -But for **fine-tuning** a model, you can reach 10 to 20 Billion parameter models using :ref:`deepspeed-zero-stage-3-offload` on a **single GPU**. This does come with a significant throughput hit, which needs to be weighed accordingly. - -When Shouldn't I use an Optimized Distributed Strategy? -""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -Sharding techniques help when model sizes are fairly large; roughly 500M+ parameters is where we've seen benefits. However, in the following cases, we recommend sticking to ordinary distributed strategies -* When your model is small (ResNet50 of around 80M Parameters), unless you are using unusually large batch sizes or inputs. -* Due to high distributed communication between devices, if running on a slow network/interconnect, the training might be much slower than expected and then it's up to you to determince the tradeoff here. - ----------- - -.. _sharded-training: - -Sharded Training -^^^^^^^^^^^^^^^^ -Lightning integration of optimizer sharded training provided by `FairScale `_. -The technique can be found within `DeepSpeed ZeRO `_ and -`ZeRO-2 `_, -however the implementation is built from the ground up to be PyTorch compatible and standalone. -Sharded Training allows you to maintain GPU scaling efficiency, whilst reducing memory overhead drastically. In short, expect near-normal linear scaling (if your network allows), and significantly reduced memory usage when training large models. - -Sharded Training still utilizes Data Parallel Training under the hood, except optimizer states and gradients are sharded across GPUs. -This means the memory overhead per GPU is lower, as each GPU only has to maintain a partition of your optimizer state and gradients. - -The benefits vary by model and parameter sizes, but we've recorded up to a 63% memory reduction per GPU allowing us to double our model sizes. Because of efficient communication, -these benefits in multi-GPU setups are almost free and throughput scales well with multi-node setups. - -It is highly recommended to use Sharded Training in multi-GPU environments where memory is limited, or where training larger models are beneficial (500M+ parameter models). -A technical note: as batch size scales, storing activations for the backwards pass becomes the bottleneck in training. As a result, sharding optimizer state and gradients becomes less impactful. -Use :ref:`fairscale-activation-checkpointing` to see even more benefit at the cost of some throughput. - -To use Sharded Training, you need to first install FairScale using the command below. - -.. code-block:: bash - - pip install fairscale - - -.. code-block:: python - - # train using Sharded DDP - trainer = Trainer(strategy="ddp_sharded") - -Sharded Training can work across all DDP variants by adding the additional ``--strategy ddp_sharded`` flag via command line using a PyTorch Lightning script. - -Internally we re-initialize your optimizers and shard them across your machines and processes. We handle all communication using PyTorch distributed, so no code changes are required. - ----------- - -.. _fully-sharded-training: - -Fully Sharded Training -^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: - Fully Sharded Training is in beta and the API is subject to change. Please create an `issue `_ if you run into any issues. - -`Fully Sharded `__ shards optimizer state, gradients and parameters across data parallel workers. This allows you to fit much larger models onto multiple GPUs into memory. - -Fully Sharded Training alleviates the need to worry about balancing layers onto specific devices using some form of pipe parallelism, and optimizes for distributed communication with minimal effort. - -Shard Parameters to Reach 10+ Billion Parameters -"""""""""""""""""""""""""""""""""""""""""""""""" - -To reach larger parameter sizes and be memory efficient, we have to shard parameters. There are various ways to enable this. - -.. note:: - Currently Fully Sharded Training relies on the user to wrap the model with Fully Sharded within the ``LightningModule``. - This means you must create a single model that is treated as a ``torch.nn.Module`` within the ``LightningModule``. - This is a limitation of Fully Sharded Training that will be resolved in the future. - -Enabling Module Sharding for Maximum Memory Efficiency -"""""""""""""""""""""""""""""""""""""""""""""""""""""" - -To activate parameter sharding, you must wrap your model using provided ``wrap`` or ``auto_wrap`` functions as described below. Internally in Lightning, we enable a context manager around the ``configure_sharded_model`` function to make sure the ``wrap`` and ``auto_wrap`` parameters are passed correctly. - -When not using Fully Sharded these wrap functions are a no-op. This means once the changes have been made, there is no need to remove the changes for other strategies. - -``auto_wrap`` will recursively wrap :class:`~torch.nn.Module` within the ``LightningModule`` with nested Fully Sharded Wrappers, -signalling that we'd like to partition these modules across data parallel devices, discarding the full weights when not required (information :class:`here `). - -``auto_wrap`` can have varying level of success based on the complexity of your model. **Auto Wrap does not support models with shared parameters**. - -``wrap`` will simply wrap the module with a Fully Sharded Parallel class with the correct parameters from the Lightning context manager. - -Below is an example of using both ``wrap`` and ``auto_wrap`` to create your model. - -.. code-block:: python - - import torch - import torch.nn as nn - import pytorch_lightning as pl - from pytorch_lightning import Trainer - from fairscale.nn import checkpoint_wrapper, auto_wrap, wrap - - - class MyModel(pl.LightningModule): - def __init__(self): - super().__init__() - self.linear_layer = nn.Linear(32, 32) - self.block = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - self.final_block = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - - def configure_sharded_model(self): - # modules are sharded across processes - # as soon as they are wrapped with ``wrap`` or ``auto_wrap``. - # During the forward/backward passes, weights get synced across processes - # and de-allocated once computation is complete, saving memory. - - # Wraps the layer in a Fully Sharded Wrapper automatically - linear_layer = wrap(self.linear_layer) - - # Wraps the module recursively - # based on a minimum number of parameters (default 100M parameters) - block = auto_wrap(self.block) - - # For best memory efficiency, - # add FairScale activation checkpointing - final_block = auto_wrap(checkpoint_wrapper(self.final_block)) - self.model = nn.Sequential(linear_layer, nn.ReLU(), block, final_block) - - def configure_optimizers(self): - return torch.optim.AdamW(self.model.parameters()) - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="fsdp", precision=16) - trainer.fit(model) - - trainer.test() - trainer.predict() - - ----------- - -.. _fairscale-activation-checkpointing: - -FairScale Activation Checkpointing -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Activation checkpointing frees activations from memory as soon as they are not needed during the forward pass. They are then re-computed for the backwards pass as needed. Activation checkpointing is very useful when you have intermediate layers that produce large activations. - -FairScales' checkpointing wrapper also handles batch norm layers correctly unlike the PyTorch implementation, ensuring stats are tracked correctly due to the multiple forward passes. - -This saves memory when training larger models however requires wrapping modules you'd like to use activation checkpointing on. See :class:`here ` for more information. - -.. warning:: - - Ensure to not wrap the entire model with activation checkpointing. This is not the intended usage of activation checkpointing, and will lead to failures as seen in `this discussion `__. - -.. code-block:: python - - from pytorch_lightning import Trainer - from fairscale.nn import checkpoint_wrapper - - - class MyModel(pl.LightningModule): - def __init__(self): - super().__init__() - # Wrap layers using checkpoint_wrapper - self.block_1 = checkpoint_wrapper(nn.Sequential(nn.Linear(32, 32), nn.ReLU())) - self.block_2 = nn.Linear(32, 2) - - -.. _deepspeed_advanced: - -DeepSpeed -^^^^^^^^^ - -.. note:: - The DeepSpeed strategy is in beta and the API is subject to change. Please create an `issue `_ if you run into any issues. - -`DeepSpeed `__ is a deep learning training optimization library, providing the means to train massive billion parameter models at scale. -Using the DeepSpeed strategy, we were able to **train model sizes of 10 Billion parameters and above**, with a lot of useful information in this `benchmark `_ and the `DeepSpeed docs `__. -DeepSpeed also offers lower level training optimizations, and efficient optimizers such as `1-bit Adam `_. We recommend using DeepSpeed in environments where speed and memory optimizations are important (such as training large billion parameter models). - -Below is a summary of all the configurations of DeepSpeed. - -* :ref:`deepspeed-zero-stage-1` - **Shard optimizer states**, remains at speed parity with DDP whilst providing memory improvement - -* :ref:`deepspeed-zero-stage-2` - **Shard optimizer states and gradients**, remains at speed parity with DDP whilst providing even more memory improvement - -* :ref:`deepspeed-zero-stage-2-offload` - **Offload optimizer states and gradients to CPU**. Increases distributed communication volume and GPU-CPU device transfer, but provides significant memory improvement - -* :ref:`deepspeed-zero-stage-3` - **Shard optimizer states, gradients, parameters and optionally activations**. Increases distributed communication volume, but provides even more memory improvement - -* :ref:`deepspeed-zero-stage-3-offload` - **Offload optimizer states, gradients, parameters and optionally activations to CPU**. Increases distributed communication volume and GPU-CPU device transfer, but even more significant memory improvement. - -* :ref:`deepspeed-activation-checkpointing` - **Free activations after forward pass**. Increases computation, but provides memory improvement for all stages. - -To use DeepSpeed, you first need to install DeepSpeed using the commands below. - -.. code-block:: bash - - pip install deepspeed - -If you run into an issue with the install or later in training, ensure that the CUDA version of the PyTorch you've installed matches your locally installed CUDA (you can see which one has been recognized by running ``nvcc --version``). - -.. note:: - - DeepSpeed currently only supports single optimizer, single scheduler within the training loop. - - When saving a checkpoint we rely on DeepSpeed which saves a directory containing the model and various components. - - -.. _deepspeed-zero-stage-1: - -DeepSpeed ZeRO Stage 1 -"""""""""""""""""""""" - -`DeepSpeed ZeRO Stage 1 `_ partitions your optimizer states (Stage 1) across your GPUs to reduce memory. - -It is recommended to skip Stage 1 and use Stage 2, which comes with larger memory improvements and still remains efficient. Stage 1 is useful to pair with certain optimizations such as `Torch ORT `__. - -.. code-block:: python - - from pytorch_lightning import Trainer - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_1", precision=16) - trainer.fit(model) - - -.. _deepspeed-zero-stage-2: - -DeepSpeed ZeRO Stage 2 -"""""""""""""""""""""" - -`DeepSpeed ZeRO Stage 2 `_ partitions your optimizer states (Stage 1) and your gradients (Stage 2) across your GPUs to reduce memory. In most cases, this is more efficient or at parity with DDP, primarily due to the optimized custom communications written by the DeepSpeed team. -As a result, benefits can also be seen on a single GPU. Do note that the default bucket sizes allocate around ``3.6GB`` of VRAM to use during distributed communications, which can be tweaked when instantiating the strategy described in a few sections below. - -.. code-block:: python - - from pytorch_lightning import Trainer - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2", precision=16) - trainer.fit(model) - -.. code-block:: bash - - python train.py --strategy deepspeed_stage_2 --precision 16 --accelerator 'gpu' --devices 4 - - -.. _deepspeed-zero-stage-2-offload: - -DeepSpeed ZeRO Stage 2 Offload -"""""""""""""""""""""""""""""" - -Below we show an example of running `ZeRO-Offload `_. ZeRO-Offload leverages the host CPU to offload optimizer memory/computation, reducing the overall memory consumption. - -.. code-block:: python - - from pytorch_lightning import Trainer - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2_offload", precision=16) - trainer.fit(model) - - -This can also be done via the command line using a PyTorch Lightning script: - -.. code-block:: bash - - python train.py --strategy deepspeed_stage_2_offload --precision 16 --accelerator 'gpu' --devices 4 - - -You can also modify the ZeRO-Offload parameters via the strategy as below. - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DeepSpeedStrategy(offload_optimizer=True, allgather_bucket_size=5e8, reduce_bucket_size=5e8), - precision=16, - ) - trainer.fit(model) - - -.. note:: - We suggest tuning the ``allgather_bucket_size`` parameter and ``reduce_bucket_size`` parameter to find optimum parameters based on your model size. - These control how large a buffer we limit the model to using when reducing gradients/gathering updated parameters. Smaller values will result in less memory, but tradeoff with speed. - - DeepSpeed allocates a reduce buffer size `multiplied by 1.5x `_ so take that into consideration when tweaking the parameters. - - The strategy sets a reasonable default of ``2e8``, which should work for most low VRAM GPUs (less than ``7GB``), allocating roughly ``3.6GB`` of VRAM as buffer. Higher VRAM GPUs should aim for values around ``5e8``. - -For even more speed benefit, DeepSpeed offers an optimized CPU version of ADAM called `DeepSpeedCPUAdam `_ to run the offloaded computation, which is faster than the standard PyTorch implementation. - -.. code-block:: python - - import pytorch_lightning - from pytorch_lightning import Trainer - from deepspeed.ops.adam import DeepSpeedCPUAdam - - - class MyModel(pl.LightningModule): - ... - - def configure_optimizers(self): - # DeepSpeedCPUAdam provides 5x to 7x speedup over torch.optim.adam(w) - return DeepSpeedCPUAdam(self.parameters()) - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2_offload", precision=16) - trainer.fit(model) - - -.. _deepspeed-zero-stage-3: - -DeepSpeed ZeRO Stage 3 -"""""""""""""""""""""" - -DeepSpeed ZeRO Stage 3 shards the optimizer states, gradients and the model parameters (also optionally activations). Sharding model parameters and activations comes with an increase in distributed communication, however allows you to scale your models massively from one GPU to multiple GPUs. -**The DeepSpeed team report the ability to fine-tune models with over 40B parameters on a single GPU and over 2 Trillion parameters on 512 GPUs.** For more information we suggest checking the `DeepSpeed ZeRO-3 Offload documentation `__. - -We've ran benchmarks for all these features and given a simple example of how all these features work in Lightning, which you can see at `minGPT `_. - -To reach the highest memory efficiency or model size, you must: - -1. Use the DeepSpeed strategy with the stage 3 parameter -2. Use CPU Offloading to offload weights to CPU, plus have a reasonable amount of CPU RAM to offload onto -3. Use DeepSpeed Activation Checkpointing to shard activations - -Below we describe how to enable all of these to see benefit. **With all these improvements we reached 45 Billion parameters training a GPT model on 8 GPUs with ~1TB of CPU RAM available**. - -Also please have a look at our :ref:`deepspeed-zero-stage-3-tips` which contains a lot of helpful information when configuring your own models. - -.. note:: - - When saving a model using DeepSpeed and Stage 3, model states and optimizer states will be saved in separate sharded states (based on the world size). See :ref:`deepspeed-zero-stage-3-single-file` to obtain a single checkpoint file. - -.. code-block:: python - - from pytorch_lightning import Trainer - from deepspeed.ops.adam import FusedAdam - - - class MyModel(pl.LightningModule): - ... - - def configure_optimizers(self): - return FusedAdam(self.parameters()) - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) - trainer.fit(model) - - trainer.test() - trainer.predict() - - -You can also use the Lightning Trainer to run predict or evaluate with DeepSpeed once the model has been trained. - -.. code-block:: python - - from pytorch_lightning import Trainer - - - class MyModel(pl.LightningModule): - ... - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) - trainer.test(ckpt_path="my_saved_deepspeed_checkpoint.ckpt") - - -Shard Model Instantly to Reduce Initialization Time/Memory -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -When instantiating really large models, it is sometimes necessary to shard the model layers instantly. - -This is the case if layers may not fit on one single machines CPU or GPU memory, but would fit once sharded across multiple machines. -We expose a hook that layers initialized within the hook will be sharded instantly on a per layer basis, allowing you to instantly shard models. - -This reduces the time taken to initialize very large models, as well as ensure we do not run out of memory when instantiating larger models. For more information you can refer to the DeepSpeed docs for `Constructing Massive Models `_. - -.. code-block:: python - - import torch.nn as nn - from pytorch_lightning import Trainer - from deepspeed.ops.adam import FusedAdam - - - class MyModel(pl.LightningModule): - ... - - def configure_sharded_model(self): - # Created within sharded model context, modules are instantly sharded across processes - # as soon as they are made. - self.block = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - - def configure_optimizers(self): - return FusedAdam(self.parameters()) - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) - trainer.fit(model) - - trainer.test() - trainer.predict() - - -.. _deepspeed-zero-stage-3-offload: - -DeepSpeed ZeRO Stage 3 Offload -"""""""""""""""""""""""""""""" - -DeepSpeed ZeRO Stage 3 Offloads optimizer state, gradients to the host CPU to reduce memory usage as ZeRO Stage 2 does, however additionally allows you to offload the parameters as well for even more memory saving. - -.. note:: - - When saving a model using DeepSpeed and Stage 3, model states and optimizer states will be saved in separate sharded states (based on the world size). See :ref:`deepspeed-zero-stage-3-single-file` to obtain a single checkpoint file. - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - # Enable CPU Offloading - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) - trainer.fit(model) - - # Enable CPU Offloading, and offload parameters to CPU - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DeepSpeedStrategy( - stage=3, - offload_optimizer=True, - offload_parameters=True, - ), - precision=16, - ) - trainer.fit(model) - - -DeepSpeed Infinity (NVMe Offloading) -"""""""""""""""""""""""""""""""""""" - -Additionally, DeepSpeed supports offloading to NVMe drives for even larger models, utilizing the large memory space found in NVMes. DeepSpeed `reports `__ the ability to fine-tune 1 Trillion+ parameters using NVMe Offloading on one 8 GPU machine. Below shows how to enable this, assuming the NVMe drive is mounted in a directory called ``/local_nvme``. - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - # Enable CPU Offloading - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) - trainer.fit(model) - - # Enable CPU Offloading, and offload parameters to CPU - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DeepSpeedStrategy( - stage=3, - offload_optimizer=True, - offload_parameters=True, - remote_device="nvme", - offload_params_device="nvme", - offload_optimizer_device="nvme", - nvme_path="/local_nvme", - ), - precision=16, - ) - trainer.fit(model) - -When offloading to NVMe you may notice that the speed is slow. There are parameters that need to be tuned based on the drives that you are using. Running the `aio_bench_perf_sweep.py `__ script can help you to find optimum parameters. See the `issue `__ for more information on how to parse the information. - -.. _deepspeed-activation-checkpointing: - -DeepSpeed Activation Checkpointing -"""""""""""""""""""""""""""""""""" - -Activation checkpointing frees activations from memory as soon as they are not needed during the forward pass. -They are then re-computed for the backwards pass as needed. - -Activation checkpointing is very useful when you have intermediate layers that produce large activations. - -This saves memory when training larger models, however requires using a checkpoint function to run modules as shown below. - -.. warning:: - - Ensure to not wrap the entire model with activation checkpointing. This is not the intended usage of activation checkpointing, and will lead to failures as seen in `this discussion `__. - -.. code-block:: python - - from pytorch_lightning import Trainer - import deepspeed - - - class MyModel(LightningModule): - ... - - def __init__(self): - super().__init__() - self.block_1 = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - self.block_2 = torch.nn.Linear(32, 2) - - def forward(self, x): - # Use the DeepSpeed checkpointing function instead of calling the module directly - # checkpointing self.block_1 means the activations are deleted after use, - # and re-calculated during the backward passes - x = deepspeed.checkpointing.checkpoint(self.block_1, x) - return self.block_2(x) - - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - import deepspeed - - - class MyModel(pl.LightningModule): - ... - - def configure_sharded_model(self): - self.block_1 = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - self.block_2 = torch.nn.Linear(32, 2) - - def forward(self, x): - # Use the DeepSpeed checkpointing function instead of calling the module directly - x = deepspeed.checkpointing.checkpoint(self.block_1, x) - return self.block_2(x) - - - model = MyModel() - - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) - - # Enable CPU Activation Checkpointing - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DeepSpeedStrategy( - stage=3, - offload_optimizer=True, # Enable CPU Offloading - cpu_checkpointing=True, # (Optional) offload activations to CPU - ), - precision=16, - ) - trainer.fit(model) - - -.. _deepspeed-zero-stage-3-tips: - -DeepSpeed ZeRO Stage 3 Tips -""""""""""""""""""""""""""" - -Here is some helpful information when setting up DeepSpeed ZeRO Stage 3 with Lightning. - -* If you're using Adam or AdamW, ensure to use FusedAdam or DeepSpeedCPUAdam (for CPU Offloading) rather than the default torch optimizers as they come with large speed benefits -* Treat your GPU/CPU memory as one large pool. In some cases, you may not want to offload certain things (like activations) to provide even more space to offload model parameters -* When offloading to the CPU, make sure to bump up the batch size as GPU memory will be freed -* We also support sharded checkpointing. By passing ``save_full_weights=False`` to the ``DeepSpeedStrategy``, we'll save shards of the model which allows you to save extremely large models. However to load the model and run test/validation/predict you must use the Trainer object. - -.. _deepspeed-zero-stage-3-single-file: - -Collating Single File Checkpoint for DeepSpeed ZeRO Stage 3 -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -After training using ZeRO Stage 3, you'll notice that your checkpoints are a directory of sharded model and optimizer states. If you'd like to collate a single file from the checkpoint directory please use the below command, which handles all the Lightning states additionally when collating the file. - -.. code-block:: python - - from pytorch_lightning.utilities.deepspeed import convert_zero_checkpoint_to_fp32_state_dict - - # lightning deepspeed has saved a directory instead of a file - save_path = "lightning_logs/version_0/checkpoints/epoch=0-step=0.ckpt/" - output_path = "lightning_model.pt" - convert_zero_checkpoint_to_fp32_state_dict(save_path, output_path) - - -.. warning:: - - This single file checkpoint does not include the optimizer/lr-scheduler states. This means we cannot restore training via the ``trainer.fit(ckpt_path=)`` call. Ensure to keep the sharded checkpoint directory if this is required. - -Custom DeepSpeed Config -""""""""""""""""""""""" - -In some cases you may want to define your own DeepSpeed Config, to access all parameters defined. We've exposed most of the important parameters, however, there may be debugging parameters to enable. Also, DeepSpeed allows the use of custom DeepSpeed optimizers and schedulers defined within a config file that is supported. - -.. note:: - All strategy default parameters will be ignored when a config object is passed. - All compatible arguments can be seen in the `DeepSpeed docs `_. - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - deepspeed_config = { - "zero_allow_untested_optimizer": True, - "optimizer": { - "type": "OneBitAdam", - "params": { - "lr": 3e-5, - "betas": [0.998, 0.999], - "eps": 1e-5, - "weight_decay": 1e-9, - "cuda_aware": True, - }, - }, - "scheduler": { - "type": "WarmupLR", - "params": { - "last_batch_iteration": -1, - "warmup_min_lr": 0, - "warmup_max_lr": 3e-5, - "warmup_num_steps": 100, - }, - }, - "zero_optimization": { - "stage": 2, # Enable Stage 2 ZeRO (Optimizer/Gradient state partitioning) - "offload_optimizer": True, # Enable Offloading optimizer state/calculation to the host CPU - "contiguous_gradients": True, # Reduce gradient fragmentation. - "overlap_comm": True, # Overlap reduce/backward operation of gradients for speed. - "allgather_bucket_size": 2e8, # Number of elements to all gather at once. - "reduce_bucket_size": 2e8, # Number of elements we reduce/allreduce at once. - }, - } - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy=DeepSpeedStrategy(config=deepspeed_config), precision=16) - trainer.fit(model) - - -We support taking the config as a json formatted file: - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - model = MyModel() - trainer = Trainer( - accelerator="gpu", devices=4, strategy=DeepSpeedStrategy(config="/path/to/deepspeed_config.json"), precision=16 - ) - trainer.fit(model) - - -You can use also use an environment variable via your PyTorch Lightning script: - -.. code-block:: bash - - PL_DEEPSPEED_CONFIG_PATH=/path/to/deepspeed_config.json python train.py --strategy deepspeed - ----------- - -.. _ddp-optimizations: - -DDP Optimizations -^^^^^^^^^^^^^^^^^ - - -When Using DDP Strategies, Set find_unused_parameters=False -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -By default, we have set ``find_unused_parameters=True`` for compatibility reasons that have been observed in the past (refer to the `discussion `_ for more details). -When enabled, it can result in a performance hit and can be disabled in most cases. Read more about it `here `_. - -.. tip:: - It applies to all DDP strategies that support ``find_unused_parameters`` as input. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy - - trainer = pl.Trainer( - accelerator="gpu", - devices=2, - strategy=DDPStrategy(find_unused_parameters=False), - ) - -.. code-block:: python - - from pytorch_lightning.strategies import DDPSpawnStrategy - - trainer = pl.Trainer( - accelerator="gpu", - devices=2, - strategy=DDPSpawnStrategy(find_unused_parameters=False), - ) - - -DDP Static Graph -"""""""""""""""" - -`DDP static graph `__ assumes that your model -employs the same set of used/unused parameters in every iteration, so that it can deterministically know the flow of -training and apply special optimizations during runtime. - -.. note:: - DDP static graph support requires PyTorch>=1.11.0 - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - - trainer = Trainer(devices=4, strategy=DDPStrategy(static_graph=True)) - - -When Using DDP on a Multi-node Cluster, Set NCCL Parameters -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -`NCCL `__ is the NVIDIA Collective Communications Library that is used by PyTorch to handle communication across nodes and GPUs. There are reported benefits in terms of speedups when adjusting NCCL parameters as seen in this `issue `__. In the issue, we see a 30% speed improvement when training the Transformer XLM-RoBERTa and a 15% improvement in training with Detectron2. - -NCCL parameters can be adjusted via environment variables. - -.. note:: - - AWS and GCP already set default values for these on their clusters. This is typically useful for custom cluster setups. - -* `NCCL_NSOCKS_PERTHREAD `__ -* `NCCL_SOCKET_NTHREADS `__ -* `NCCL_MIN_NCHANNELS `__ - -.. code-block:: bash - - export NCCL_NSOCKS_PERTHREAD=4 - export NCCL_SOCKET_NTHREADS=2 - - -Gradients as Bucket View -"""""""""""""""""""""""" - -Enabling ``gradient_as_bucket_view=True`` in the ``DDPStrategy`` will make gradients views point to different offsets of the ``allreduce`` communication buckets. See :class:`~torch.nn.parallel.DistributedDataParallel` for more information. - -This can reduce peak memory usage and throughput as saved memory will be equal to the total gradient memory + removes the need to copy gradients to the ``allreduce`` communication buckets. - -.. note:: - - When ``gradient_as_bucket_view=True`` you cannot call ``detach_()`` on gradients. If hitting such errors, please fix it by referring to the :meth:`~torch.optim.Optimizer.zero_grad` function in ``torch/optim/optimizer.py`` as a solution (`source `__). - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy=DDPStrategy(gradient_as_bucket_view=True)) - trainer.fit(model) - -DDP Communication Hooks -""""""""""""""""""""""" - -DDP Communication hooks is an interface to control how gradients are communicated across workers, overriding the standard allreduce in DistributedDataParallel. This allows you to enable performance improving communication hooks when using multiple nodes. - -Enable `FP16 Compress Hook for multi-node throughput improvement `__: - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - from torch.distributed.algorithms.ddp_comm_hooks import default_hooks as default - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy=DDPStrategy(ddp_comm_hook=default.fp16_compress_hook)) - trainer.fit(model) - -Enable `PowerSGD for multi-node throughput improvement `__: - -.. note:: - - PowerSGD typically requires extra memory of the same size as the model’s gradients to enable error feedback, which can compensate for biased compressed communication and improve accuracy (`source `__). - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - from torch.distributed.algorithms.ddp_comm_hooks import powerSGD_hook as powerSGD - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DDPStrategy( - ddp_comm_state=powerSGD.PowerSGDState( - process_group=None, - matrix_approximation_rank=1, - start_powerSGD_iter=5000, - ), - ddp_comm_hook=powerSGD.powerSGD_hook, - ), - ) - trainer.fit(model) - - -Combine hooks for accumulated benefit: - -.. note:: - DDP communication wrappers support requires PyTorch>=1.9.0 - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - from torch.distributed.algorithms.ddp_comm_hooks import ( - default_hooks as default, - powerSGD_hook as powerSGD, - ) - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DDPStrategy( - ddp_comm_state=powerSGD.PowerSGDState( - process_group=None, - matrix_approximation_rank=1, - start_powerSGD_iter=5000, - ), - ddp_comm_hook=powerSGD.powerSGD_hook, - ddp_comm_wrapper=default.fp16_compress_wrapper, - ), - ) - trainer.fit(model) - - -When using Post-localSGD, you must also pass ``model_averaging_period`` to allow for model parameter averaging: - -.. note:: - Post-localSGD support requires PyTorch>=1.10.0 - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - from torch.distributed.algorithms.ddp_comm_hooks import post_localSGD_hook as post_localSGD - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DDPStrategy( - ddp_comm_state=post_localSGD.PostLocalSGDState( - process_group=None, - subgroup=None, - start_localSGD_iter=8, - ), - ddp_comm_hook=post_localSGD.post_localSGD_hook, - model_averaging_period=4, - ), - ) - trainer.fit(model) diff --git a/docs/_sources/advanced/pruning_quantization.rst.txt b/docs/_sources/advanced/pruning_quantization.rst.txt deleted file mode 100644 index 552a96d..0000000 --- a/docs/_sources/advanced/pruning_quantization.rst.txt +++ /dev/null @@ -1,115 +0,0 @@ -.. _pruning_quantization: - -######################## -Pruning and Quantization -######################## - -Pruning and Quantization are techniques to compress model size for deployment, allowing inference speed up and energy saving without significant accuracy losses. - -******* -Pruning -******* - -.. warning:: - - Pruning is in beta and subject to change. - -Pruning is a technique which focuses on eliminating some of the model weights to reduce the model size and decrease inference requirements. - -Pruning has been shown to achieve significant efficiency improvements while minimizing the drop in model performance (prediction quality). Model pruning is recommended for cloud endpoints, deploying models on edge devices, or mobile inference (among others). - -To enable pruning during training in Lightning, simply pass in the :class:`~pytorch_lightning.callbacks.ModelPruning` callback to the Lightning Trainer. PyTorch's native pruning implementation is used under the hood. - -This callback supports multiple pruning functions: pass any `torch.nn.utils.prune `_ function as a string to select which weights to prune (`random_unstructured `_, `RandomStructured `_, etc) or implement your own by subclassing `BasePruningMethod `_. - -.. code-block:: python - - from pytorch_lightning.callbacks import ModelPruning - - # set the amount to be the fraction of parameters to prune - trainer = Trainer(callbacks=[ModelPruning("l1_unstructured", amount=0.5)]) - -You can also perform iterative pruning, apply the `lottery ticket hypothesis `__, and more! - -.. code-block:: python - - def compute_amount(epoch): - # the sum of all returned values need to be smaller than 1 - if epoch == 10: - return 0.5 - - elif epoch == 50: - return 0.25 - - elif 75 < epoch < 99: - return 0.01 - - - # the amount can be also be a callable - trainer = Trainer(callbacks=[ModelPruning("l1_unstructured", amount=compute_amount)]) - - -************ -Quantization -************ - -.. warning :: - Quantization is in beta and subject to change. - -Model quantization is another performance optimization technique that allows speeding up inference and decreasing memory requirements by performing computations and storing tensors at lower bitwidths (such as INT8 or FLOAT16) than floating-point precision. This is particularly beneficial during model deployment. - -Quantization Aware Training (QAT) mimics the effects of quantization during training: The computations are carried-out in floating-point precision but the subsequent quantization effect is taken into account. The weights and activations are quantized into lower precision only for inference, when training is completed. - -Quantization is useful when it is required to serve large models on machines with limited memory, or when there's a need to switch between models and reducing the I/O time is important. For example, switching between monolingual speech recognition models across multiple languages. - -Lightning includes :class:`~pytorch_lightning.callbacks.QuantizationAwareTraining` callback (using PyTorch's native quantization, read more `here `__), which allows creating fully quantized models (compatible with torchscript). - -.. code-block:: python - - from pytorch_lightning.callbacks import QuantizationAwareTraining - - - class RegressionModel(LightningModule): - def __init__(self): - super().__init__() - self.layer_0 = nn.Linear(16, 64) - self.layer_0a = torch.nn.ReLU() - self.layer_1 = nn.Linear(64, 64) - self.layer_1a = torch.nn.ReLU() - self.layer_end = nn.Linear(64, 1) - - def forward(self, x): - x = self.layer_0(x) - x = self.layer_0a(x) - x = self.layer_1(x) - x = self.layer_1a(x) - x = self.layer_end(x) - return x - - - trainer = Trainer(callbacks=[QuantizationAwareTraining()]) - qmodel = RegressionModel() - trainer.fit(qmodel, ...) - - batch = iter(my_dataloader()).next() - qmodel(qmodel.quant(batch[0])) - - tsmodel = qmodel.to_torchscript() - tsmodel(tsmodel.quant(batch[0])) - -You can further customize the callback: - -.. code-block:: python - - - qcb = QuantizationAwareTraining( - # specification of quant estimation quality - observer_type="histogram", - # specify which layers shall be merged together to increase efficiency - modules_to_fuse=[(f"layer_{i}", f"layer_{i}a") for i in range(2)], - # make your model compatible with all original input/outputs, in such case the model is wrapped in a shell with entry/exit layers. - input_compatible=True, - ) - - batch = iter(my_dataloader()).next() - qmodel(batch[0]) diff --git a/docs/_sources/advanced/strategy_registry.rst.txt b/docs/_sources/advanced/strategy_registry.rst.txt deleted file mode 100644 index d92069a..0000000 --- a/docs/_sources/advanced/strategy_registry.rst.txt +++ /dev/null @@ -1,49 +0,0 @@ -Strategy Registry -================= - -.. warning:: The Strategy Registry is experimental and subject to change. - -Lightning includes a registry that holds information about Training strategies and allows for the registration of new custom strategies. - -The Strategies are assigned strings that identify them, such as "ddp", "deepspeed_stage_2_offload", and so on. -It also returns the optional description and parameters for initialising the Strategy that were defined during registration. - - -.. code-block:: python - - # Training with the DDP Strategy with `find_unused_parameters` as False - trainer = Trainer(strategy="ddp_find_unused_parameters_false", accelerator="gpu", devices=4) - - # Training with DeepSpeed ZeRO Stage 3 and CPU Offload - trainer = Trainer(strategy="deepspeed_stage_3_offload", accelerator="gpu", devices=3) - - # Training with the TPU Spawn Strategy with `debug` as True - trainer = Trainer(strategy="tpu_spawn_debug", accelerator="tpu", devices=8) - - -Additionally, you can pass your custom registered training strategies to the ``strategy`` argument. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy, StrategyRegistry, CheckpointIO - - - class CustomCheckpointIO(CheckpointIO): - def save_checkpoint(self, checkpoint: Dict[str, Any], path: Union[str, Path]) -> None: - ... - - def load_checkpoint(self, path: Union[str, Path]) -> Dict[str, Any]: - ... - - - custom_checkpoint_io = CustomCheckpointIO() - - # Register the DDP Strategy with your custom CheckpointIO plugin - StrategyRegistry.register( - "ddp_custom_checkpoint_io", - DDPStrategy, - description="DDP Strategy with custom checkpoint io plugin", - checkpoint_io=custom_checkpoint_io, - ) - - trainer = Trainer(strategy="ddp_custom_checkpoint_io", accelerator="gpu", devices=2) diff --git a/docs/_sources/advanced/training_tricks.rst.txt b/docs/_sources/advanced/training_tricks.rst.txt deleted file mode 100644 index a8d5c2d..0000000 --- a/docs/_sources/advanced/training_tricks.rst.txt +++ /dev/null @@ -1,356 +0,0 @@ -.. testsetup:: * - - from pytorch_lightning.callbacks import StochasticWeightAveraging - -.. _training_tricks: - -############################# -Effective Training Techniques -############################# - -Lightning implements various techniques to help during training that can help make the training smoother. - ----------- - -******************** -Accumulate Gradients -******************** - -.. include:: ../common/gradient_accumulation.rst - ----------- - -***************** -Gradient Clipping -***************** - -Gradient clipping can be enabled to avoid exploding gradients. By default, this will clip the gradient norm by calling -:func:`torch.nn.utils.clip_grad_norm_` computed over all model parameters together. -If the Trainer's ``gradient_clip_algorithm`` is set to ``'value'`` (``'norm'`` by default), this will use instead -:func:`torch.nn.utils.clip_grad_value_` for each parameter instead. - -.. note:: - If using mixed precision, the ``gradient_clip_val`` does not need to be changed as the gradients are unscaled - before applying the clipping function. - -.. seealso:: :class:`~pytorch_lightning.trainer.trainer.Trainer` - -.. testcode:: - - # DEFAULT (ie: don't clip) - trainer = Trainer(gradient_clip_val=0) - - # clip gradients' global norm to <=0.5 using gradient_clip_algorithm='norm' by default - trainer = Trainer(gradient_clip_val=0.5) - - # clip gradients' maximum magnitude to <=0.5 - trainer = Trainer(gradient_clip_val=0.5, gradient_clip_algorithm="value") - -Read more about :ref:`Configuring Gradient Clipping ` for advanced use-cases. - ----------- - -*************************** -Stochastic Weight Averaging -*************************** - -Stochastic Weight Averaging (SWA) can make your models generalize better at virtually no additional cost. -This can be used with both non-trained and trained models. The SWA procedure smooths the loss landscape thus making -it harder to end up in a local minimum during optimization. - -For a more detailed explanation of SWA and how it works, -read `this post `__ by the PyTorch team. - -.. seealso:: The :class:`~pytorch_lightning.callbacks.StochasticWeightAveraging` callback - -.. testcode:: - - # Enable Stochastic Weight Averaging using the callback - trainer = Trainer(callbacks=[StochasticWeightAveraging(swa_lrs=1e-2)]) - ----------- - -***************** -Batch Size Finder -***************** - -Auto-scaling of batch size can be enabled to find the largest batch size that fits into -memory. Large batch size often yields a better estimation of the gradients, but may also result in -longer training time. Inspired by https://github.com/BlackHC/toma. - -.. seealso:: :class:`~pytorch_lightning.trainer.trainer.Trainer` - -.. code-block:: python - - # DEFAULT (ie: don't scale batch size automatically) - trainer = Trainer(auto_scale_batch_size=None) - - # Autoscale batch size - trainer = Trainer(auto_scale_batch_size=None | "power" | "binsearch") - - # Find the batch size - trainer.tune(model) - -Currently, this feature supports two modes ``'power'`` scaling and ``'binsearch'`` -scaling. In ``'power'`` scaling, starting from a batch size of 1 keeps doubling -the batch size until an out-of-memory (OOM) error is encountered. Setting the -argument to ``'binsearch'`` will initially also try doubling the batch size until -it encounters an OOM, after which it will do a binary search that will finetune the -batch size. Additionally, it should be noted that the batch size scaler cannot -search for batch sizes larger than the size of the training dataset. - - -.. note:: - - This feature expects that a ``batch_size`` field is either located as a model attribute - i.e. ``model.batch_size`` or as a field in your ``hparams`` i.e. ``model.hparams.batch_size``. - Similarly it can work with datamodules too. The field should exist and will be updated by - the results of this algorithm. Additionally, your ``train_dataloader()`` method should depend - on this field for this feature to work i.e. - - .. code-block:: python - - # using LightningModule - class LitModel(LightningModule): - def __init__(self, batch_size): - super().__init__() - self.save_hyperparameters() - # or - self.batch_size = batch_size - - def train_dataloader(self): - return DataLoader(train_dataset, batch_size=self.batch_size | self.hparams.batch_size) - - - trainer = Trainer(...) - model = LitModel(batch_size=32) - trainer.tune(model) - - # using LightningDataModule - class LitDataModule(LightningDataModule): - def __init__(self, batch_size): - super().__init__() - self.save_hyperparameters() - # or - self.batch_size = batch_size - - def train_dataloader(self): - return DataLoader(train_dataset, batch_size=self.batch_size | self.hparams.batch_size) - - - trainer = Trainer(...) - model = MyModel() - datamodule = LitDataModule(batch_size=32) - trainer.tune(model, datamodule=datamodule) - -.. warning:: - - Due to the constraints listed above, this features does *NOT* work when passing dataloaders directly - to ``.fit()``. - -The scaling algorithm has a number of parameters that the user can control by -invoking the :meth:`~pytorch_lightning.tuner.tuning.Tuner.scale_batch_size` method: - -.. code-block:: python - - # Use default in trainer construction - trainer = Trainer() - tuner = Tuner(trainer) - - # Invoke method - new_batch_size = tuner.scale_batch_size(model, *extra_parameters_here) - - # Override old batch size (this is done automatically) - model.hparams.batch_size = new_batch_size - - # Fit as normal - trainer.fit(model) - -The algorithm in short works by: - 1. Dumping the current state of the model and trainer - 2. Iteratively until convergence or maximum number of tries ``max_trials`` (default 25) has been reached: - - Call ``fit()`` method of trainer. This evaluates ``steps_per_trial`` (default 3) number of - optimization steps. Each training step can trigger an OOM error if the tensors - (training batch, weights, gradients, etc.) allocated during the steps have a - too large memory footprint. - - If an OOM error is encountered, decrease batch size else increase it. - How much the batch size is increased/decreased is determined by the chosen - strategy. - 3. The found batch size is saved to either ``model.batch_size`` or ``model.hparams.batch_size`` - 4. Restore the initial state of model and trainer - -.. warning:: Batch size finder is not yet supported for DDP or any of its variations, it is coming soon. - ----------- - -.. _learning_rate_finder: - -******************** -Learning Rate Finder -******************** - -.. raw:: html - - - -| - -For training deep neural networks, selecting a good learning rate is essential -for both better performance and faster convergence. Even optimizers such as -:class:`~torch.optim.Adam` that are self-adjusting the learning rate can benefit from more optimal -choices. - -To reduce the amount of guesswork concerning choosing a good initial learning -rate, a `learning rate finder` can be used. As described in `this paper `_ -a learning rate finder does a small run where the learning rate is increased -after each processed batch and the corresponding loss is logged. The result of -this is a ``lr`` vs. ``loss`` plot that can be used as guidance for choosing an optimal -initial learning rate. - -.. warning:: - - For the moment, this feature only works with models having a single optimizer. - LR Finder support for DDP and any of its variations is not implemented yet. It is coming soon. - - -Using Lightning's built-in LR finder -==================================== - -To enable the learning rate finder, your :doc:`lightning module <../common/lightning_module>` needs to -have a ``learning_rate`` or ``lr`` attribute (or as a field in your ``hparams`` i.e. -``hparams.learning_rate`` or ``hparams.lr``). Then, set ``Trainer(auto_lr_find=True)`` -during trainer construction, and then call ``trainer.tune(model)`` to run the LR finder. -The suggested ``learning_rate`` will be written to the console and will be automatically -set to your :doc:`lightning module <../common/lightning_module>`, which can be accessed -via ``self.learning_rate`` or ``self.lr``. - -.. seealso:: :ref:`trainer.tune `. - -.. code-block:: python - - class LitModel(LightningModule): - def __init__(self, learning_rate): - super().__init__() - self.learning_rate = learning_rate - self.model = Model(...) - - def configure_optimizers(self): - return Adam(self.parameters(), lr=(self.lr or self.learning_rate)) - - - model = LitModel() - - # finds learning rate automatically - # sets hparams.lr or hparams.learning_rate to that learning rate - trainer = Trainer(auto_lr_find=True) - - trainer.tune(model) - -If your model is using an arbitrary value instead of ``self.lr`` or ``self.learning_rate``, set that value as ``auto_lr_find``: - -.. code-block:: python - - model = LitModel() - - # to set to your own hparams.my_value - trainer = Trainer(auto_lr_find="my_value") - - trainer.tune(model) - -You can also inspect the results of the learning rate finder or just play around -with the parameters of the algorithm. This can be done by invoking the -:meth:`~pytorch_lightning.tuner.tuning.Tuner.lr_find` method. A typical example of this would look like: - -.. code-block:: python - - model = MyModelClass(hparams) - trainer = Trainer() - - # Run learning rate finder - lr_finder = trainer.tuner.lr_find(model) - - # Results can be found in - print(lr_finder.results) - - # Plot with - fig = lr_finder.plot(suggest=True) - fig.show() - - # Pick point based on plot, or get suggestion - new_lr = lr_finder.suggestion() - - # update hparams of the model - model.hparams.lr = new_lr - - # Fit model - trainer.fit(model) - -The figure produced by ``lr_finder.plot()`` should look something like the figure -below. It is recommended to not pick the learning rate that achieves the lowest -loss, but instead something in the middle of the sharpest downward slope (red point). -This is the point returned py ``lr_finder.suggestion()``. - -.. figure:: ../_static/images/trainer/lr_finder.png - ----------- - -************************** -Advanced GPU Optimizations -************************** - -When training on single or multiple GPU machines, Lightning offers a host of advanced optimizations to improve throughput, memory efficiency, and model scaling. -Refer to :doc:`Advanced GPU Optimized Training <../advanced/model_parallel>` for more details. - ----------- - - -.. _ddp_spawn_shared_memory: - -****************************************** -Sharing Datasets Across Process Boundaries -****************************************** - -The :class:`~pytorch_lightning.core.datamodule.LightningDataModule` class provides an organized way to decouple data loading from training logic, with :meth:`~pytorch_lightning.core.hooks.DataHooks.prepare_data` being used for downloading and pre-processing the dataset on a single process, and :meth:`~pytorch_lightning.core.hooks.DataHooks.setup` loading the pre-processed data for each process individually: - -.. code-block:: python - - class MNISTDataModule(pl.LightningDataModule): - def prepare_data(self): - MNIST(self.data_dir, download=True) - - def setup(self, stage: Optional[str] = None): - self.mnist = MNIST(self.data_dir) - - def train_loader(self): - return DataLoader(self.mnist, batch_size=128) - -However, for in-memory datasets, that means that each process will hold a (redundant) replica of the dataset in memory, which may be impractical when using many processes while utilizing datasets that nearly fit into CPU memory, as the memory consumption will scale up linearly with the number of processes. -For example, when training Graph Neural Networks, a common strategy is to load the entire graph into CPU memory for fast access to the entire graph structure and its features, and to then perform neighbor sampling to obtain mini-batches that fit onto the GPU. - -A simple way to prevent redundant dataset replicas is to rely on :obj:`torch.multiprocessing` to share the `data automatically between spawned processes via shared memory `_. -For this, all data pre-loading should be done on the main process inside :meth:`DataModule.__init__`. As a result, all tensor-data will get automatically shared when using the :class:`~pytorch_lightning.plugins.strategies.ddp_spawn.DDPSpawnStrategy` strategy. - -.. warning:: - - :obj:`torch.multiprocessing` will send a handle of each individual tensor to other processes. - In order to prevent any errors due to too many open file handles, try to reduce the number of tensors to share, *e.g.*, by stacking your data into a single tensor. - -.. code-block:: python - - class MNISTDataModule(pl.LightningDataModule): - def __init__(self, data_dir: str): - self.mnist = MNIST(data_dir, download=True, transform=T.ToTensor()) - - def train_loader(self): - return DataLoader(self.mnist, batch_size=128) - - - model = Model(...) - datamodule = MNISTDataModule("data/MNIST") - - trainer = Trainer(accelerator="gpu", devices=2, strategy="ddp_spawn") - trainer.fit(model, datamodule) - -See the `graph-level `_ and `node-level `_ prediction examples in PyTorch Geometric for practical use-cases. diff --git a/docs/_sources/advanced/transfer_learning.rst.txt b/docs/_sources/advanced/transfer_learning.rst.txt deleted file mode 100644 index caa739b..0000000 --- a/docs/_sources/advanced/transfer_learning.rst.txt +++ /dev/null @@ -1,128 +0,0 @@ -################# -Transfer Learning -################# -**Audience**: Users looking to use pretrained models with Lightning. - ----- - -************************* -Use any PyTorch nn.Module -************************* -Any model that is a PyTorch nn.Module can be used with Lightning (because LightningModules are nn.Modules also). - ----- - -******************************** -Use a pretrained LightningModule -******************************** -Let's use the `AutoEncoder` as a feature extractor in a separate model. - -.. testcode:: - - class Encoder(torch.nn.Module): - ... - - - class AutoEncoder(LightningModule): - def __init__(self): - self.encoder = Encoder() - self.decoder = Decoder() - - - class CIFAR10Classifier(LightningModule): - def __init__(self): - # init the pretrained LightningModule - self.feature_extractor = AutoEncoder.load_from_checkpoint(PATH) - self.feature_extractor.freeze() - - # the autoencoder outputs a 100-dim representation and CIFAR-10 has 10 classes - self.classifier = nn.Linear(100, 10) - - def forward(self, x): - representations = self.feature_extractor(x) - x = self.classifier(representations) - ... - -We used our pretrained Autoencoder (a LightningModule) for transfer learning! - ----- - -*********************************** -Example: Imagenet (Computer Vision) -*********************************** - -.. testcode:: - :skipif: not _TORCHVISION_AVAILABLE - - import torchvision.models as models - - - class ImagenetTransferLearning(LightningModule): - def __init__(self): - super().__init__() - - # init a pretrained resnet - backbone = models.resnet50(pretrained=True) - num_filters = backbone.fc.in_features - layers = list(backbone.children())[:-1] - self.feature_extractor = nn.Sequential(*layers) - - # use the pretrained model to classify cifar-10 (10 image classes) - num_target_classes = 10 - self.classifier = nn.Linear(num_filters, num_target_classes) - - def forward(self, x): - self.feature_extractor.eval() - with torch.no_grad(): - representations = self.feature_extractor(x).flatten(1) - x = self.classifier(representations) - ... - -Finetune - -.. code-block:: python - - model = ImagenetTransferLearning() - trainer = Trainer() - trainer.fit(model) - -And use it to predict your data of interest - -.. code-block:: python - - model = ImagenetTransferLearning.load_from_checkpoint(PATH) - model.freeze() - - x = some_images_from_cifar10() - predictions = model(x) - -We used a pretrained model on imagenet, finetuned on CIFAR-10 to predict on CIFAR-10. -In the non-academic world we would finetune on a tiny dataset you have and predict on your dataset. - ----- - -******************* -Example: BERT (NLP) -******************* -Lightning is completely agnostic to what's used for transfer learning so long -as it is a `torch.nn.Module` subclass. - -Here's a model that uses `Huggingface transformers `_. - -.. testcode:: - - class BertMNLIFinetuner(LightningModule): - def __init__(self): - super().__init__() - - self.bert = BertModel.from_pretrained("bert-base-cased", output_attentions=True) - self.W = nn.Linear(bert.config.hidden_size, 3) - self.num_classes = 3 - - def forward(self, input_ids, attention_mask, token_type_ids): - - h, _, attn = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids) - - h_cls = h[:, 0] - logits = self.W(h_cls) - return logits, attn diff --git a/docs/_sources/benchmarking/benchmarks.rst.txt b/docs/_sources/benchmarking/benchmarks.rst.txt deleted file mode 100644 index af9715f..0000000 --- a/docs/_sources/benchmarking/benchmarks.rst.txt +++ /dev/null @@ -1,19 +0,0 @@ -:orphan: - -Benchmark with vanilla PyTorch -============================== - -In this section we set grounds for comparison between vanilla PyTorch and PT Lightning for most common scenarios. - -Time comparison ---------------- - -We have set regular benchmarking against PyTorch vanilla training loop on with RNN and simple MNIST classifier as per of out CI. -In average for simple MNIST CNN classifier we are only about 0.06s slower per epoch, see detail chart bellow. - -.. figure:: ../_static/images/benchmarks/figure-parity-times.png - :alt: Speed parity to vanilla PT, created on 2020-12-16 - :width: 500 - - -Learn more about reproducible benchmarking from the `PyTorch Reproducibility Guide `__. diff --git a/docs/_sources/cli/lightning_cli.rst.txt b/docs/_sources/cli/lightning_cli.rst.txt deleted file mode 100644 index 76f3f12..0000000 --- a/docs/_sources/cli/lightning_cli.rst.txt +++ /dev/null @@ -1,94 +0,0 @@ -:orphan: - -.. _lightning-cli: - -############################ -Eliminate config boilerplate -############################ - -********* -Basic use -********* - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Control it all from the CLI - :description: Learn to control a LightningModule and LightningDataModule from the CLI - :col_css: col-md-4 - :button_link: lightning_cli_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 2: Mix models and datasets - :description: Register models, datasets, optimizers and learning rate schedulers - :col_css: col-md-4 - :button_link: lightning_cli_intermediate_2.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 3: Control it all via YAML - :description: Enable composable YAMLs - :col_css: col-md-4 - :button_link: lightning_cli_advanced.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
- ----- - -************ -Advanced use -************ - -.. raw:: html - -
-
- -.. displayitem:: - :header: YAML for production - :description: Use the Lightning CLI with YAMLs for production environments - :col_css: col-md-6 - :button_link: lightning_cli_advanced_2.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Customize configs for complex projects - :description: Learn how to connect complex projects with each Registry. - :col_css: col-md-6 - :button_link: lightning_cli_advanced_3.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Extend the Lightning CLI - :description: Customize the Lightning CLI - :col_css: col-md-6 - :button_link: lightning_cli_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: FAQ - :description: Frequently asked questions about working with the Lightning CLI and YAML files - :col_css: col-md-6 - :button_link: lightning_cli_faq.html - :height: 150 - -.. raw:: html - -
-
diff --git a/docs/_sources/cli/lightning_cli_advanced.rst.txt b/docs/_sources/cli/lightning_cli_advanced.rst.txt deleted file mode 100644 index 2d4f330..0000000 --- a/docs/_sources/cli/lightning_cli_advanced.rst.txt +++ /dev/null @@ -1,113 +0,0 @@ -:orphan: - -####################################### -Eliminate config boilerplate (Advanced) -####################################### -**Audience:** Users looking to modularize their code for a professional project. - -**Pre-reqs:** You must have read :doc:`(Control it all from the CLI) `. - ----- - -*************************** -What is a yaml config file? -*************************** -A yaml is a standard configuration file that describes parameters for sections of a program. It is a common tool in engineering, and it has recently started to gain popularity in machine learning. - -.. code:: yaml - - # file.yaml - car: - max_speed:100 - max_passengers:2 - plane: - fuel_capacity: 50 - class_3: - option_1: 'x' - option_2: 'y' - ----- - - -********************* -Print the config used -********************* -Before or after you run a training routine, you can print the full training spec in yaml format using ``--print_config``: - -.. code:: bash - - python main.py fit --print_config - -which generates the following config: - -.. code:: bash - - seed_everything: null - trainer: - logger: true - ... - terminate_on_nan: null - model: - out_dim: 10 - learning_rate: 0.02 - data: - data_dir: ./ - ckpt_path: null - ----- - -******************************** -Write a config yaml from the CLI -******************************** -To have a copy of the configuration that produced this model, save a *yaml* file from the *--print_config* outputs: - -.. code:: bash - - python main.py fit --model.learning_rate 0.001 --print_config > config.yaml - ----- - -********************** -Run from a single yaml -********************** -To run from a yaml, pass a yaml produced with ``--print_config`` to the ``--config`` argument: - -.. code:: bash - - python main.py fit --config config.yaml - -when using a yaml to run, you can still pass in inline arguments - -.. code:: bash - - python main.py fit --config config.yaml --trainer.max_epochs 100 - ----- - -****************** -Compose yaml files -****************** -For production or complex research projects it's advisable to have each object in its own config file. To compose all the configs, pass them all inline: - -.. code-block:: bash - - $ python trainer.py fit --config trainer.yaml --config datamodules.yaml --config models.yaml ... - -The configs will be parsed sequentially. Let's say we have two configs with the same args: - -.. code:: yaml - - # trainer.yaml - trainer: - num_epochs: 10 - - - # trainer_2.yaml - trainer: - num_epochs: 20 - -the ones from the last config will be used (num_epochs = 20) in this case: - -.. code-block:: bash - - $ python trainer.py fit --config trainer.yaml --config trainer_2.yaml diff --git a/docs/_sources/cli/lightning_cli_advanced_2.rst.txt b/docs/_sources/cli/lightning_cli_advanced_2.rst.txt deleted file mode 100644 index 0474699..0000000 --- a/docs/_sources/cli/lightning_cli_advanced_2.rst.txt +++ /dev/null @@ -1,207 +0,0 @@ -:orphan: - -.. testsetup:: * - :skipif: not _JSONARGPARSE_AVAILABLE - - import torch - from unittest import mock - from typing import List - import pytorch_lightning as pl - from pytorch_lightning import LightningModule, LightningDataModule, Trainer, Callback - - - class NoFitTrainer(Trainer): - def fit(self, *_, **__): - pass - - - class LightningCLI(pl.utilities.cli.LightningCLI): - def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): - super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) - - - class MyModel(LightningModule): - def __init__( - self, - encoder_layers: int = 12, - decoder_layers: List[int] = [2, 4], - batch_size: int = 8, - ): - pass - - - class MyDataModule(LightningDataModule): - def __init__(self, batch_size: int = 8): - self.num_classes = 5 - - - mock_argv = mock.patch("sys.argv", ["any.py"]) - mock_argv.start() - -.. testcleanup:: * - - mock_argv.stop() - -####################################### -Eliminate config boilerplate (Advanced) -####################################### - -****************************** -Customize arguments by command -****************************** -To customize arguments by subcommand, pass the config *before* the subcommand: - -.. code-block:: bash - - $ python main.py [before] [subcommand] [after] - $ python main.py ... fit ... - -For example, here we set the Trainer argument [max_steps = 100] for the full training routine and [max_steps = 10] for testing: - -.. code-block:: bash - - # config1.yaml - fit: - trainer: - max_steps: 100 - test: - trainer: - max_epochs: 10 - -now you can toggle this behavior by subcommand: - -.. code-block:: bash - - # full routine with max_steps = 100 - $ python main.py --config config1.yaml fit - - # test only with max_epochs = 10 - $ python main.py --config config1.yaml test - ----- - -********************* -Use groups of options -********************* -Groups of options can also be given as independent config files: - -.. code-block:: bash - - $ python trainer.py fit --trainer trainer.yaml --model model.yaml --data data.yaml [...] - ----- - -*************************** -Run from cloud yaml configs -*************************** -For certain enterprise workloads, Lightning CLI supports running from hosted configs: - -.. code-block:: bash - - $ python trainer.py [subcommand] --config s3://bucket/config.yaml - -For more options, refer to :doc:`Remote filesystems <../common/remote_fs>`. - ----- - -************************************** -Use a config via environment variables -************************************** -For certain CI/CD systems, it's useful to pass in config files as environment variables: - -.. code-block:: bash - - $ python trainer.py fit --trainer "$TRAINER_CONFIG" --model "$MODEL_CONFIG" [...] - ----- - -*************************************** -Run from environment variables directly -*************************************** -The Lightning CLI can convert every possible CLI flag into an environment variable. To enable this, set the *env_parse* argument: - -.. code:: python - - LightningCLI(env_parse=True) - -now use the ``--help`` CLI flag with any subcommand: - -.. code:: bash - - $ python main.py fit --help - -which will show you ALL possible environment variables you can now set: - -.. code:: bash - - usage: main.py [options] fit [-h] [-c CONFIG] - [--trainer.max_epochs MAX_EPOCHS] [--trainer.min_epochs MIN_EPOCHS] - [--trainer.max_steps MAX_STEPS] [--trainer.min_steps MIN_STEPS] - ... - [--ckpt_path CKPT_PATH] - - optional arguments: - ... - --model CONFIG Path to a configuration file. - --model.out_dim OUT_DIM - (type: int, default: 10) - --model.learning_rate LEARNING_RATE - (type: float, default: 0.02) - -now you can customize the behavior via environment variables: - -.. code:: bash - - # set the options via env vars - $ export LEARNING_RATE=0.01 - $ export OUT_DIM=5 - - $ python main.py fit - ----- - -************************ -Set default config files -************************ -To set a path to a config file of defaults, use the ``default_config_files`` argument: - -.. testcode:: - - cli = LightningCLI(MyModel, MyDataModule, parser_kwargs={"default_config_files": ["my_cli_defaults.yaml"]}) - -or if you want defaults per subcommand: - -.. testcode:: - - cli = LightningCLI(MyModel, MyDataModule, parser_kwargs={"fit": {"default_config_files": ["my_fit_defaults.yaml"]}}) - -For more configuration options, refer to the `ArgumentParser API -`_ documentation. - ----- - -***************************** -Enable variable interpolation -***************************** -In certain cases where multiple configs need to share variables, consider using variable interpolation. Variable interpolation -allows you to add variables to your yaml configs like so: - -.. code-block:: yaml - - model: - encoder_layers: 12 - decoder_layers: - - ${model.encoder_layers} - - 4 - -To enable variable interpolation, first install omegaconf: - -.. code:: bash - - pip install omegaconf - -Once this is installed, the Lightning CLI will automatically handle variables in yaml files: - -.. code bash: - - python main.py --model.encoder_layers=12 diff --git a/docs/_sources/cli/lightning_cli_advanced_3.rst.txt b/docs/_sources/cli/lightning_cli_advanced_3.rst.txt deleted file mode 100644 index 2eeae17..0000000 --- a/docs/_sources/cli/lightning_cli_advanced_3.rst.txt +++ /dev/null @@ -1,415 +0,0 @@ -:orphan: - -.. testsetup:: * - :skipif: not _JSONARGPARSE_AVAILABLE - - import torch - from unittest import mock - from typing import List - import pytorch_lightning as pl - from pytorch_lightning import LightningModule, LightningDataModule, Trainer, Callback - - - class NoFitTrainer(Trainer): - def fit(self, *_, **__): - pass - - - class LightningCLI(pl.utilities.cli.LightningCLI): - def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): - super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) - - - class MyModel(LightningModule): - def __init__( - self, - encoder_layers: int = 12, - decoder_layers: List[int] = [2, 4], - batch_size: int = 8, - ): - pass - - - class MyDataModule(LightningDataModule): - def __init__(self, batch_size: int = 8): - self.num_classes = 5 - - - MyModelBaseClass = MyModel - MyDataModuleBaseClass = MyDataModule - - mock_argv = mock.patch("sys.argv", ["any.py"]) - mock_argv.start() - -.. testcleanup:: * - - mock_argv.stop() - -Instantiation only mode -^^^^^^^^^^^^^^^^^^^^^^^ - -The CLI is designed to start fitting with minimal code changes. On class instantiation, the CLI will automatically -call the trainer function associated to the subcommand provided so you don't have to do it. -To avoid this, you can set the following argument: - -.. testcode:: - - cli = LightningCLI(MyModel, run=False) # True by default - # you'll have to call fit yourself: - cli.trainer.fit(cli.model) - -In this mode, there are subcommands added to the parser. -This can be useful to implement custom logic without having to subclass the CLI, but still using the CLI's instantiation -and argument parsing capabilities. - - -Subclass registration -^^^^^^^^^^^^^^^^^^^^^ - -To use shorthand notation, the options need to be registered beforehand. This can be easily done with: - -.. code-block:: - - LightningCLI(auto_registry=True) # False by default - -which will register all subclasses of :class:`torch.optim.Optimizer`, :class:`torch.optim.lr_scheduler._LRScheduler`, -:class:`~pytorch_lightning.core.lightning.LightningModule`, -:class:`~pytorch_lightning.core.datamodule.LightningDataModule`, :class:`~pytorch_lightning.callbacks.Callback`, and -:class:`~pytorch_lightning.loggers.LightningLoggerBase` across all imported modules. This includes those in your own -code. - -Alternatively, if this is left unset, only the subclasses defined in PyTorch's :class:`torch.optim.Optimizer`, -:class:`torch.optim.lr_scheduler._LRScheduler` and Lightning's :class:`~pytorch_lightning.callbacks.Callback` and -:class:`~pytorch_lightning.loggers.LightningLoggerBase` subclassess will be registered. - -In subsequent sections, we will go over adding specific classes to specific registries as well as how to use -shorthand notation. - - -Trainer Callbacks and arguments with class type -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -A very important argument of the :class:`~pytorch_lightning.trainer.trainer.Trainer` class is the :code:`callbacks`. In -contrast to other more simple arguments which just require numbers or strings, :code:`callbacks` expects a list of -instances of subclasses of :class:`~pytorch_lightning.callbacks.Callback`. To specify this kind of argument in a config -file, each callback must be given as a dictionary including a :code:`class_path` entry with an import path of the class, -and optionally an :code:`init_args` entry with arguments required to instantiate it. Therefore, a simple configuration -file example that defines a couple of callbacks is the following: - -.. code-block:: yaml - - trainer: - callbacks: - - class_path: pytorch_lightning.callbacks.EarlyStopping - init_args: - patience: 5 - - class_path: pytorch_lightning.callbacks.LearningRateMonitor - init_args: - ... - -Similar to the callbacks, any arguments in :class:`~pytorch_lightning.trainer.trainer.Trainer` and user extended -:class:`~pytorch_lightning.core.lightning.LightningModule` and -:class:`~pytorch_lightning.core.datamodule.LightningDataModule` classes that have as type hint a class can be configured -the same way using :code:`class_path` and :code:`init_args`. - -For callbacks in particular, Lightning simplifies the command line so that only -the :class:`~pytorch_lightning.callbacks.Callback` name is required. -The argument's order matters and the user needs to pass the arguments in the following way. - -.. code-block:: bash - - $ python ... \ - --trainer.callbacks={CALLBACK_1_NAME} \ - --trainer.callbacks.{CALLBACK_1_ARGS_1}=... \ - --trainer.callbacks.{CALLBACK_1_ARGS_2}=... \ - ... - --trainer.callbacks={CALLBACK_N_NAME} \ - --trainer.callbacks.{CALLBACK_N_ARGS_1}=... \ - ... - -Here is an example: - -.. code-block:: bash - - $ python ... \ - --trainer.callbacks=EarlyStopping \ - --trainer.callbacks.patience=5 \ - --trainer.callbacks=LearningRateMonitor \ - --trainer.callbacks.logging_interval=epoch - -Lightning provides a mechanism for you to add your own callbacks and benefit from the command line simplification -as described above: - -.. code-block:: python - - from pytorch_lightning.utilities.cli import CALLBACK_REGISTRY - - - @CALLBACK_REGISTRY - class CustomCallback(Callback): - ... - - - cli = LightningCLI(...) - -.. code-block:: bash - - $ python ... --trainer.callbacks=CustomCallback ... - -.. note:: - - This shorthand notation is only supported in the shell and not inside a configuration file. The configuration file - generated by calling the previous command with ``--print_config`` will have the ``class_path`` notation. - - .. code-block:: yaml - - trainer: - callbacks: - - class_path: your_class_path.CustomCallback - init_args: - ... - - -.. tip:: - - ``--trainer.logger`` also supports shorthand notation and a ``LOGGER_REGISTRY`` is available to register custom - Loggers. - - -Multiple models and/or datasets -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Additionally, the tool can be configured such that a model and/or a datamodule is -specified by an import path and init arguments. For example, with a tool implemented as: - -.. code-block:: python - - cli = LightningCLI(MyModelBaseClass, MyDataModuleBaseClass, subclass_mode_model=True, subclass_mode_data=True) - -A possible config file could be as follows: - -.. code-block:: yaml - - model: - class_path: mycode.mymodels.MyModel - init_args: - decoder_layers: - - 2 - - 4 - encoder_layers: 12 - data: - class_path: mycode.mydatamodules.MyDataModule - init_args: - ... - trainer: - callbacks: - - class_path: pytorch_lightning.callbacks.EarlyStopping - init_args: - patience: 5 - ... - -Only model classes that are a subclass of :code:`MyModelBaseClass` would be allowed, and similarly only subclasses of -:code:`MyDataModuleBaseClass`. If as base classes :class:`~pytorch_lightning.core.lightning.LightningModule` and -:class:`~pytorch_lightning.core.datamodule.LightningDataModule` are given, then the tool would allow any lightning -module and data module. - -.. tip:: - - Note that with the subclass modes the :code:`--help` option does not show information for a specific subclass. To - get help for a subclass the options :code:`--model.help` and :code:`--data.help` can be used, followed by the - desired class path. Similarly :code:`--print_config` does not include the settings for a particular subclass. To - include them the class path should be given before the :code:`--print_config` option. Examples for both help and - print config are: - - .. code-block:: bash - - $ python trainer.py fit --model.help mycode.mymodels.MyModel - $ python trainer.py fit --model mycode.mymodels.MyModel --print_config - - -Models with multiple submodules -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Many use cases require to have several modules each with its own configurable options. One possible way to handle this -with LightningCLI is to implement a single module having as init parameters each of the submodules. Since the init -parameters have as type a class, then in the configuration these would be specified with :code:`class_path` and -:code:`init_args` entries. For instance a model could be implemented as: - -.. testcode:: - - class MyMainModel(LightningModule): - def __init__(self, encoder: nn.Module, decoder: nn.Module): - """Example encoder-decoder submodules model - - Args: - encoder: Instance of a module for encoding - decoder: Instance of a module for decoding - """ - super().__init__() - self.encoder = encoder - self.decoder = decoder - -If the CLI is implemented as :code:`LightningCLI(MyMainModel)` the configuration would be as follows: - -.. code-block:: yaml - - model: - encoder: - class_path: mycode.myencoders.MyEncoder - init_args: - ... - decoder: - class_path: mycode.mydecoders.MyDecoder - init_args: - ... - -It is also possible to combine :code:`subclass_mode_model=True` and submodules, thereby having two levels of -:code:`class_path`. - - -Class type defaults -^^^^^^^^^^^^^^^^^^^ - -The support for classes as type hints allows to try many possibilities with the same CLI. This is a useful feature, but -it can make it tempting to use an instance of a class as a default. For example: - -.. code-block:: - - class MyMainModel(LightningModule): - def __init__( - self, - backbone: torch.nn.Module = MyModel(encoder_layers=24), # BAD PRACTICE! - ): - super().__init__() - self.backbone = backbone - -Normally classes are mutable as it is in this case. The instance of :code:`MyModel` would be created the moment that the -module that defines :code:`MyMainModel` is first imported. This means that the default of :code:`backbone` will be -initialized before the CLI class runs :code:`seed_everything` making it non-reproducible. Furthermore, if -:code:`MyMainModel` is used more than once in the same Python process and the :code:`backbone` parameter is not -overridden, the same instance would be used in multiple places which very likely is not what the developer intended. -Having an instance as default also makes it impossible to generate the complete config file since for arbitrary classes -it is not known which arguments were used to instantiate it. - -A good solution to these problems is to not have a default or set the default to a special value (e.g. a -string) which would be checked in the init and instantiated accordingly. If a class parameter has no default and the CLI -is subclassed then a default can be set as follows: - -.. testcode:: - - default_backbone = { - "class_path": "import.path.of.MyModel", - "init_args": { - "encoder_layers": 24, - }, - } - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.set_defaults({"model.backbone": default_backbone}) - -A more compact version that avoids writing a dictionary would be: - -.. testcode:: - - from jsonargparse import lazy_instance - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.set_defaults({"model.backbone": lazy_instance(MyModel, encoder_layers=24)}) - -Optimizers -^^^^^^^^^^ - -If you will not be changing the class, you can manually add the arguments for specific optimizers and/or -learning rate schedulers by subclassing the CLI. This has the advantage of providing the proper help message for those -classes. The following code snippet shows how to implement it: - -.. testcode:: - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.add_optimizer_args(torch.optim.Adam) - parser.add_lr_scheduler_args(torch.optim.lr_scheduler.ExponentialLR) - -With this, in the config the :code:`optimizer` and :code:`lr_scheduler` groups would accept all of the options for the -given classes, in this example :code:`Adam` and :code:`ExponentialLR`. -Therefore, the config file would be structured like: - -.. code-block:: yaml - - optimizer: - lr: 0.01 - lr_scheduler: - gamma: 0.2 - model: - ... - trainer: - ... - -Where the arguments can be passed directly through command line without specifying the class. For example: - -.. code-block:: bash - - $ python trainer.py fit --optimizer.lr=0.01 --lr_scheduler.gamma=0.2 - -The automatic implementation of :code:`configure_optimizers` can be disabled by linking the configuration group. An -example can be when one wants to add support for multiple optimizers: - -.. code-block:: python - - from pytorch_lightning.utilities.cli import instantiate_class - - - class MyModel(LightningModule): - def __init__(self, optimizer1_init: dict, optimizer2_init: dict): - super().__init__() - self.optimizer1_init = optimizer1_init - self.optimizer2_init = optimizer2_init - - def configure_optimizers(self): - optimizer1 = instantiate_class(self.parameters(), self.optimizer1_init) - optimizer2 = instantiate_class(self.parameters(), self.optimizer2_init) - return [optimizer1, optimizer2] - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.add_optimizer_args( - OPTIMIZER_REGISTRY.classes, nested_key="gen_optimizer", link_to="model.optimizer1_init" - ) - parser.add_optimizer_args( - OPTIMIZER_REGISTRY.classes, nested_key="gen_discriminator", link_to="model.optimizer2_init" - ) - - - cli = MyLightningCLI(MyModel) - -The value given to :code:`optimizer*_init` will always be a dictionary including :code:`class_path` and -:code:`init_args` entries. The function :func:`~pytorch_lightning.utilities.cli.instantiate_class` -takes care of importing the class defined in :code:`class_path` and instantiating it using some positional arguments, -in this case :code:`self.parameters()`, and the :code:`init_args`. -Any number of optimizers and learning rate schedulers can be added when using :code:`link_to`. - -With shorthand notation: - -.. code-block:: bash - - $ python trainer.py fit \ - --gen_optimizer=Adam \ - --gen_optimizer.lr=0.01 \ - --gen_discriminator=AdamW \ - --gen_discriminator.lr=0.0001 - -You can also pass the class path directly, for example, if the optimizer hasn't been registered to the -``OPTIMIZER_REGISTRY``: - -.. code-block:: bash - - $ python trainer.py fit \ - --gen_optimizer.class_path=torch.optim.Adam \ - --gen_optimizer.init_args.lr=0.01 \ - --gen_discriminator.class_path=torch.optim.AdamW \ - --gen_discriminator.init_args.lr=0.0001 diff --git a/docs/_sources/cli/lightning_cli_expert.rst.txt b/docs/_sources/cli/lightning_cli_expert.rst.txt deleted file mode 100644 index dbd6061..0000000 --- a/docs/_sources/cli/lightning_cli_expert.rst.txt +++ /dev/null @@ -1,266 +0,0 @@ -:orphan: - -.. testsetup:: * - :skipif: not _JSONARGPARSE_AVAILABLE - - import torch - from unittest import mock - from typing import List - import pytorch_lightning as pl - from pytorch_lightning import LightningModule, LightningDataModule, Trainer, Callback - - - class NoFitTrainer(Trainer): - def fit(self, *_, **__): - pass - - - class LightningCLI(pl.utilities.cli.LightningCLI): - def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): - super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) - - - class MyModel(LightningModule): - def __init__( - self, - encoder_layers: int = 12, - decoder_layers: List[int] = [2, 4], - batch_size: int = 8, - ): - pass - - - class MyClassModel(LightningModule): - def __init__(self, num_classes: int): - pass - - - class MyDataModule(LightningDataModule): - def __init__(self, batch_size: int = 8): - self.num_classes = 5 - - - def send_email(address, message): - pass - - - mock_argv = mock.patch("sys.argv", ["any.py"]) - mock_argv.start() - -.. testcleanup:: * - - mock_argv.stop() - -####################################### -Eliminate config boilerplate (Advanced) -####################################### -**Audience:** Users who already understand the LightningCLI and want to customize it. - ----- - -************************** -Customize the LightningCLI -************************** - -The init parameters of the :class:`~pytorch_lightning.utilities.cli.LightningCLI` class can be used to customize some -things, namely: the description of the tool, enabling parsing of environment variables and additional arguments to -instantiate the trainer and configuration parser. - -Nevertheless the init arguments are not enough for many use cases. For this reason the class is designed so that can be -extended to customize different parts of the command line tool. The argument parser class used by -:class:`~pytorch_lightning.utilities.cli.LightningCLI` is -:class:`~pytorch_lightning.utilities.cli.LightningArgumentParser` which is an extension of python's argparse, thus -adding arguments can be done using the :func:`add_argument` method. In contrast to argparse it has additional methods to -add arguments, for example :func:`add_class_arguments` adds all arguments from the init of a class, though requiring -parameters to have type hints. For more details about this please refer to the `respective documentation -`_. - -The :class:`~pytorch_lightning.utilities.cli.LightningCLI` class has the -:meth:`~pytorch_lightning.utilities.cli.LightningCLI.add_arguments_to_parser` method which can be implemented to include -more arguments. After parsing, the configuration is stored in the :code:`config` attribute of the class instance. The -:class:`~pytorch_lightning.utilities.cli.LightningCLI` class also has two methods that can be used to run code before -and after the trainer runs: :code:`before_` and :code:`after_`. -A realistic example for these would be to send an email before and after the execution. -The code for the :code:`fit` subcommand would be something like: - -.. testcode:: - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.add_argument("--notification_email", default="will@email.com") - - def before_fit(self): - send_email(address=self.config["notification_email"], message="trainer.fit starting") - - def after_fit(self): - send_email(address=self.config["notification_email"], message="trainer.fit finished") - - - cli = MyLightningCLI(MyModel) - -Note that the config object :code:`self.config` is a dictionary whose keys are global options or groups of options. It -has the same structure as the yaml format described previously. This means for instance that the parameters used for -instantiating the trainer class can be found in :code:`self.config['fit']['trainer']`. - -.. tip:: - - Have a look at the :class:`~pytorch_lightning.utilities.cli.LightningCLI` class API reference to learn about other - methods that can be extended to customize a CLI. - ----- - -************************** -Configure forced callbacks -************************** -As explained previously, any Lightning callback can be added by passing it through command line or -including it in the config via :code:`class_path` and :code:`init_args` entries. - -However, certain callbacks MUST be coupled with a model so they are always present and configurable. -This can be implemented as follows: - -.. testcode:: - - from pytorch_lightning.callbacks import EarlyStopping - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.add_lightning_class_args(EarlyStopping, "my_early_stopping") - parser.set_defaults({"my_early_stopping.monitor": "val_loss", "my_early_stopping.patience": 5}) - - - cli = MyLightningCLI(MyModel) - -To change the configuration of the :code:`EarlyStopping` in the config it would be: - -.. code-block:: yaml - - model: - ... - trainer: - ... - my_early_stopping: - patience: 5 - -.. note:: - - The example above overrides a default in :code:`add_arguments_to_parser`. This is included to show that defaults can - be changed if needed. However, note that overriding of defaults in the source code is not intended to be used to - store the best hyperparameters for a task after experimentation. To ease reproducibility the source code should be - stable. It is better practice to store the best hyperparameters for a task in a configuration file independent from - the source code. - ----- - -******************* -Class type defaults -******************* - -The support for classes as type hints allows to try many possibilities with the same CLI. This is a useful feature, but -it can make it tempting to use an instance of a class as a default. For example: - -.. testcode:: - - class MyMainModel(LightningModule): - def __init__( - self, - backbone: torch.nn.Module = MyModel(encoder_layers=24), # BAD PRACTICE! - ): - super().__init__() - self.backbone = backbone - -Normally classes are mutable as it is in this case. The instance of :code:`MyModel` would be created the moment that the -module that defines :code:`MyMainModel` is first imported. This means that the default of :code:`backbone` will be -initialized before the CLI class runs :code:`seed_everything` making it non-reproducible. Furthermore, if -:code:`MyMainModel` is used more than once in the same Python process and the :code:`backbone` parameter is not -overridden, the same instance would be used in multiple places which very likely is not what the developer intended. -Having an instance as default also makes it impossible to generate the complete config file since for arbitrary classes -it is not known which arguments were used to instantiate it. - -A good solution to these problems is to not have a default or set the default to a special value (e.g. a -string) which would be checked in the init and instantiated accordingly. If a class parameter has no default and the CLI -is subclassed then a default can be set as follows: - -.. testcode:: - - default_backbone = { - "class_path": "import.path.of.MyModel", - "init_args": { - "encoder_layers": 24, - }, - } - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.set_defaults({"model.backbone": default_backbone}) - -A more compact version that avoids writing a dictionary would be: - -.. testcode:: - - from jsonargparse import lazy_instance - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.set_defaults({"model.backbone": lazy_instance(MyModel, encoder_layers=24)}) - ----- - -************************ -Connect two config files -************************ -Another case in which it might be desired to extend :class:`~pytorch_lightning.utilities.cli.LightningCLI` is that the -model and data module depend on a common parameter. For example in some cases both classes require to know the -:code:`batch_size`. It is a burden and error prone giving the same value twice in a config file. To avoid this the -parser can be configured so that a value is only given once and then propagated accordingly. With a tool implemented -like shown below, the :code:`batch_size` only has to be provided in the :code:`data` section of the config. - -.. testcode:: - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.link_arguments("data.batch_size", "model.batch_size") - - - cli = MyLightningCLI(MyModel, MyDataModule) - -The linking of arguments is observed in the help of the tool, which for this example would look like: - -.. code-block:: bash - - $ python trainer.py fit --help - ... - --data.batch_size BATCH_SIZE - Number of samples in a batch (type: int, default: 8) - - Linked arguments: - model.batch_size <-- data.batch_size - Number of samples in a batch (type: int) - -Sometimes a parameter value is only available after class instantiation. An example could be that your model requires -the number of classes to instantiate its fully connected layer (for a classification task) but the value is not -available until the data module has been instantiated. The code below illustrates how to address this. - -.. testcode:: - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.link_arguments("data.num_classes", "model.num_classes", apply_on="instantiate") - - - cli = MyLightningCLI(MyClassModel, MyDataModule) - -Instantiation links are used to automatically determine the order of instantiation, in this case data first. - -.. tip:: - - The linking of arguments can be used for more complex cases. For example to derive a value via a function that takes - multiple settings as input. For more details have a look at the API of `link_arguments - `_. - - -The linking of arguments is intended for things that are meant to be non-configurable. This improves the CLI user -experience since it avoids the need for providing more parameters. A related concept is -variable interpolation which in contrast keeps things being configurable. diff --git a/docs/_sources/cli/lightning_cli_faq.rst.txt b/docs/_sources/cli/lightning_cli_faq.rst.txt deleted file mode 100644 index ca1be71..0000000 --- a/docs/_sources/cli/lightning_cli_faq.rst.txt +++ /dev/null @@ -1,136 +0,0 @@ -:orphan: - -.. testsetup:: * - :skipif: not _JSONARGPARSE_AVAILABLE - - import torch - from unittest import mock - from typing import List - import pytorch_lightning as pl - from pytorch_lightning import LightningModule, LightningDataModule, Trainer, Callback - - - class NoFitTrainer(Trainer): - def fit(self, *_, **__): - pass - - - class LightningCLI(pl.utilities.cli.LightningCLI): - def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): - super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) - - - class MyModel(LightningModule): - def __init__( - self, - encoder_layers: int = 12, - decoder_layers: List[int] = [2, 4], - batch_size: int = 8, - ): - pass - - - mock_argv = mock.patch("sys.argv", ["any.py"]) - mock_argv.start() - -.. testcleanup:: * - - mock_argv.stop() - -##################################### -Eliminate config boilerplate (expert) -##################################### - -*************** -Troubleshooting -*************** -The standard behavior for CLIs, when they fail, is to terminate the process with a non-zero exit code and a short message -to hint the user about the cause. This is problematic while developing the CLI since there is no information to track -down the root of the problem. A simple change in the instantiation of the ``LightningCLI`` can be used such that when -there is a failure an exception is raised and the full stack trace printed. - -.. testcode:: - - cli = LightningCLI(MyModel, parser_kwargs={"error_handler": None}) - -.. note:: - - When asking about problems and reporting issues please set the ``error_handler`` to ``None`` and include the stack - trace in your description. With this, it is more likely for people to help out identifying the cause without needing - to create a reproducible script. - ----- - -************************************* -Reproducibility with the LightningCLI -************************************* -The topic of reproducibility is complex and it is impossible to guarantee reproducibility by just providing a class that -people can use in unexpected ways. Nevertheless, the :class:`~pytorch_lightning.utilities.cli.LightningCLI` tries to -give a framework and recommendations to make reproducibility simpler. - -When an experiment is run, it is good practice to use a stable version of the source code, either being a released -package or at least a commit of some version controlled repository. For each run of a CLI the config file is -automatically saved including all settings. This is useful to figure out what was done for a particular run without -requiring to look at the source code. If by mistake the exact version of the source code is lost or some defaults -changed, having the full config means that most of the information is preserved. - -The class is targeted at implementing CLIs because running a command from a shell provides a separation with the Python -source code. Ideally the CLI would be placed in your path as part of the installation of a stable package, instead of -running from a clone of a repository that could have uncommitted local modifications. Creating installable packages that -include CLIs is out of the scope of this document. This is mentioned only as a teaser for people who would strive for -the best practices possible. - - -For every CLI implemented, users are encouraged to learn how to run it by reading the documentation printed with the -:code:`--help` option and use the :code:`--print_config` option to guide the writing of config files. A few more details -that might not be clear by only reading the help are the following. - -:class:`~pytorch_lightning.utilities.cli.LightningCLI` is based on argparse and as such follows the same arguments style -as many POSIX command line tools. Long options are prefixed with two dashes and its corresponding values should be -provided with an empty space or an equal sign, as :code:`--option value` or :code:`--option=value`. Command line options -are parsed from left to right, therefore if a setting appears multiple times the value most to the right will override -the previous ones. If a class has an init parameter that is required (i.e. no default value), it is given as -:code:`--option` which makes it explicit and more readable instead of relying on positional arguments. - ----- - -********************* -What is a subcommand? -********************* -A subcommand is what is the action the LightningCLI applies to the script: - -.. code:: bash - - python main.py [subcommand] - -See the Potential subcommands with: - -.. code:: bash - - python main.py --help - -which prints: - -.. code:: bash - - ... - - fit Runs the full optimization routine. - validate Perform one evaluation epoch over the validation set. - test Perform one evaluation epoch over the test set. - predict Run inference on your data. - tune Runs routines to tune hyperparameters before training. - -use a subcommand as follows: - -.. code:: bash - - python main.py fit - python main.py test - ----- - -**************** -What is the CLI? -**************** -CLI is short for commandline interface. Use your terminal to enter these commands. diff --git a/docs/_sources/cli/lightning_cli_intermediate.rst.txt b/docs/_sources/cli/lightning_cli_intermediate.rst.txt deleted file mode 100644 index 36c6adb..0000000 --- a/docs/_sources/cli/lightning_cli_intermediate.rst.txt +++ /dev/null @@ -1,204 +0,0 @@ -:orphan: - -########################################### -Eliminate config boilerplate (Intermediate) -########################################### -**Audience:** Users who want advanced modularity via the commandline interface (CLI). - -**Pre-reqs:** You must already understand how to use a commandline and :doc:`LightningDataModule <../data/datamodule>`. - ----- - -*************************** -What is config boilerplate? -*************************** -As Lightning projects grow in complexity it becomes desirable to enable full customizability from the commandline (CLI) so you can -change any hyperparameters without changing your code: - -.. code:: bash - - # Mix and match anything - $ python main.py --command fit --model.learning_rate 0.02 - $ python main.py --command fit --model.learning_rate 0.01 --trainer.fast_dev_run True - -This is what the Lightning CLI enables. Without the Lightning CLI, you usually end up with a TON of boilerplate that looks like this: - -.. code:: python - - from argparse import ArgumentParser - - if __name__ == "__main__": - parser = ArgumentParser() - parser.add_argument("--learning_rate_1", default=0.02) - parser.add_argument("--learning_rate_2", default=0.03) - parser.add_argument("--model", default="cnn") - parser.add_argument("--command", default="fit") - parser.add_argument("--run_fast", default=True) - ... - # add 100 more of these - ... - - args = parser.parse_args() - - if args.model == "cnn": - model = ConvNet(learning_rate=args.learning_rate_1) - elif args.model == "transformer": - model = Transformer(learning_rate=args.learning_rate_2) - trainer = Trainer(fast_dev_run=args.run_fast) - ... - - if args.command == "fit": - trainer.fit() - elif args.command == "test": - ... - -This kind of boilerplate is unsustainable as projects grow in complexity. - ----- - -************************ -Enable the Lightning CLI -************************ -To enable the Lightning CLI install the extras: - -.. code:: bash - - pip install pytorch-lightning[extra] - -if the above fails, only install jsonargparse: - -.. code:: bash - - pip install -U jsonargparse[signatures] - ----- - -************************** -Connect a model to the CLI -************************** -The simplest way to control a model with the CLI is to wrap it in the LightningCLI object: - -.. code:: python - - # main.py - - import torch - from pytorch_lightning.utilities.cli import LightningCLI - from pytorch_lightning import LightningModule, demos - - - class DemoModel(LightningModule): - def __init__(self, out_dim: int = 10, learning_rate: float = 0.02): - super().__init__() - self.l1 = torch.nn.Linear(32, out_dim) - self.learning_rate = learning_rate - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - def training_step(self, batch, batch_nb): - x = batch - x = self(x) - loss = x.sum() - return loss - - def configure_optimizers(self): - return torch.optim.Adam(self.parameters(), lr=self.learning_rate) - - - cli = LightningCLI(DemoModel, demos.BoringDataModule) - # don't call fit!! - -Now your model can be managed via the CLI. To see the available commands type: - -.. code:: bash - - $ python main.py --help - -Which prints out: - -.. code:: bash - - usage: a.py [-h] [-c CONFIG] [--print_config [={comments,skip_null,skip_default}+]] - {fit,validate,test,predict,tune} ... - - pytorch-lightning trainer command line tool - - optional arguments: - -h, --help Show this help message and exit. - -c CONFIG, --config CONFIG - Path to a configuration file in json or yaml format. - --print_config [={comments,skip_null,skip_default}+] - Print configuration and exit. - - subcommands: - For more details of each subcommand add it as argument followed by --help. - - {fit,validate,test,predict,tune} - fit Runs the full optimization routine. - validate Perform one evaluation epoch over the validation set. - test Perform one evaluation epoch over the test set. - predict Run inference on your data. - tune Runs routines to tune hyperparameters before training. - - -the message tells us that we have a few available subcommands: - -.. code:: bash - - python main.py [subcommand] - -which you can use depending on your use case: - -.. code:: bash - - $ python main.py fit - $ python main.py validate - $ python main.py test - $ python main.py predict - $ python main.py tune - ----- - -************************** -Train a model with the CLI -************************** -To run the full training routine (train, val, test), use the subcommand ``fit``: - -.. code:: bash - - python main.py fit - -View all available options with the ``--help`` command: - -.. code:: bash - - usage: main.py [options] fit [-h] [-c CONFIG] - [--seed_everything SEED_EVERYTHING] [--trainer CONFIG] - ... - [--ckpt_path CKPT_PATH] - --trainer.logger LOGGER - - optional arguments: - : - --model.out_dim OUT_DIM - (type: int, default: 10) - --model.learning_rate LEARNING_RATE - (type: float, default: 0.02) - : - --data CONFIG Path to a configuration file. - --data.data_dir DATA_DIR - (type: str, default: ./) - -With the Lightning CLI enabled, you can now change the parameters without touching your code: - -.. code:: bash - - # change the learning_rate - python main.py fit --model.out_dim 30 - - # change the out dimensions also - python main.py fit --model.out_dim 10 --model.learning_rate 0.1 - - # change trainer and data arguments too - python main.py fit --model.out_dim 2 --model.learning_rate 0.1 --data.data_dir '~/' --trainer.logger False diff --git a/docs/_sources/cli/lightning_cli_intermediate_2.rst.txt b/docs/_sources/cli/lightning_cli_intermediate_2.rst.txt deleted file mode 100644 index 493d536..0000000 --- a/docs/_sources/cli/lightning_cli_intermediate_2.rst.txt +++ /dev/null @@ -1,251 +0,0 @@ -:orphan: - -########################################### -Eliminate config boilerplate (intermediate) -########################################### -**Audience:** Users who have multiple models and datasets per project. - -**Pre-reqs:** You must have read :doc:`(Control it all from the CLI) `. - ----- - -**************************************** -Why do I want to mix models and datasets -**************************************** -Lightning projects usually begin with one model and one dataset. As the project grows in complexity and you introduce more models and more datasets, it becomes desirable -to mix any model with any dataset directly from the commandline without changing your code. - - -.. code:: bash - - # Mix and match anything - $ python main.py fit --model=GAN --data=MNIST - $ python main.py fit --model=Transformer --data=MNIST - -This is what the Lightning CLI enables. Otherwise, this kind of configuration requires a significant amount of boilerplate that often looks like this: - -.. code:: python - - # choose model - if args.model == "gan": - model = GAN(args.feat_dim) - elif args.model == "transformer": - model = Transformer(args.feat_dim) - ... - - # choose datamodule - if args.data == "MNIST": - datamodule = MNIST() - elif args.data == "imagenet": - datamodule = Imagenet() - ... - - # mix them! - trainer.fit(model, datamodule) - ----- - -************************* -Register LightningModules -************************* -Connect models across different files with the ``MODEL_REGISTRY`` to make them available from the CLI: - -.. code:: python - - # main.py - - from pytorch_lightning import demos - from pytorch_lightning.utilities import cli as pl_cli - - - @pl_cli.MODEL_REGISTRY - class Model1(demos.DemoModel): - def configure_optimizers(self): - print("⚡", "using Model1", "⚡") - return super().configure_optimizers() - - - @pl_cli.MODEL_REGISTRY - class Model2(demos.DemoModel): - def configure_optimizers(self): - print("⚡", "using Model2", "⚡") - return super().configure_optimizers() - - - cli = pl_cli.LightningCLI(datamodule_class=demos.BoringDataModule) - -Now you can choose between any model from the CLI: - -.. code:: bash - - # use Model1 - python main.py fit --model Model1 - - # use Model2 - python main.py fit --model Model2 - ----- - -******************** -Register DataModules -******************** -Connect DataModules across different files with the ``DATAMODULE_REGISTRY`` to make them available from the CLI: - -.. code:: python - - # main.py - import torch - from pytorch_lightning.utilities import cli as pl_cli - from pytorch_lightning import demos - - - @pl_cli.DATAMODULE_REGISTRY - class FakeDataset1(demos.BoringDataModule): - def train_dataloader(self): - print("⚡", "using FakeDataset1", "⚡") - return torch.utils.data.DataLoader(self.random_train) - - - @pl_cli.DATAMODULE_REGISTRY - class FakeDataset2(demos.BoringDataModule): - def train_dataloader(self): - print("⚡", "using FakeDataset2", "⚡") - return torch.utils.data.DataLoader(self.random_train) - - - cli = pl_cli.LightningCLI(demos.DemoModel) - -Now you can choose between any dataset at runtime: - -.. code:: bash - - # use Model1 - python main.py fit --data FakeDataset1 - - # use Model2 - python main.py fit --data FakeDataset2 - ----- - -******************* -Register optimizers -******************* -Connect optimizers with the ``OPTIMIZER_REGISTRY`` to make them available from the CLI: - -.. code:: python - - # main.py - import torch - from pytorch_lightning.utilities import cli as pl_cli - from pytorch_lightning import demos - - - @pl_cli.OPTIMIZER_REGISTRY - class LitAdam(torch.optim.Adam): - def step(self, closure): - print("⚡", "using LitAdam", "⚡") - super().step(closure) - - - @pl_cli.OPTIMIZER_REGISTRY - class FancyAdam(torch.optim.Adam): - def step(self, closure): - print("⚡", "using FancyAdam", "⚡") - super().step(closure) - - - cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule) - -Now you can choose between any optimizer at runtime: - -.. code:: bash - - # use LitAdam - python main.py fit --optimizer LitAdam - - # use FancyAdam - python main.py fit --optimizer FancyAdam - -Bonus: If you need only 1 optimizer, the Lightning CLI already works out of the box with any Optimizer from ``torch.optim.optim``: - -.. code:: bash - - python main.py fit --optimizer AdamW - -If the optimizer you want needs other arguments, add them via the CLI (no need to change your code)! - -.. code:: bash - - python main.py fit --optimizer SGD --optimizer.lr=0.01 - ----- - -********************** -Register LR schedulers -********************** -Connect learning rate schedulers with the ``LR_SCHEDULER_REGISTRY`` to make them available from the CLI: - -.. code:: python - - # main.py - import torch - from pytorch_lightning.utilities import cli as pl_cli - from pytorch_lightning import demos - - - @pl_cli.LR_SCHEDULER_REGISTRY - class LitLRScheduler(torch.optim.lr_scheduler.CosineAnnealingLR): - def step(self): - print("⚡", "using LitLRScheduler", "⚡") - super().step() - - - cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule) - -Now you can choose between any learning rate scheduler at runtime: - -.. code:: bash - - # LitLRScheduler - python main.py fit --lr_scheduler LitLRScheduler - - -Bonus: If you need only 1 LRScheduler, the Lightning CLI already works out of the box with any LRScheduler from ``torch.optim``: - -.. code:: bash - - python main.py fit --lr_scheduler CosineAnnealingLR - python main.py fit --lr_scheduler LinearLR - ... - -If the scheduler you want needs other arguments, add them via the CLI (no need to change your code)! - -.. code:: bash - - python main.py fit --lr_scheduler=ReduceLROnPlateau --lr_scheduler.monitor=epoch - ----- - -************************* -Register from any package -************************* -A shortcut to register many classes from a package is to use the ``register_classes`` method. Here we register all optimizers from the ``torch.optim`` library: - -.. code:: python - - import torch - from pytorch_lightning.utilities import cli as pl_cli - from pytorch_lightning import demos - - # add all PyTorch optimizers! - pl_cli.OPTIMIZER_REGISTRY.register_classes(module=torch.optim, base_cls=torch.optim.Optimizer) - - cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule) - -Now use any of the optimizers in the ``torch.optim`` library: - -.. code:: bash - - python main.py fit --optimizer AdamW - -This method is supported by all the registry classes. diff --git a/docs/_sources/clouds/cloud_training.rst.txt b/docs/_sources/clouds/cloud_training.rst.txt deleted file mode 100644 index 1bd57b1..0000000 --- a/docs/_sources/clouds/cloud_training.rst.txt +++ /dev/null @@ -1,86 +0,0 @@ -.. _grid: - -################## -Train on the cloud -################## -**Audience:** Users who want to develop and train models on the cloud (public cloud, private cloud or onprem clusters). - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Access a GPU machine on the cloud - :description: Learn to train models using an interactive cloud machine. - :col_css: col-md-4 - :button_link: session_basic.html - :height: 200 - :tag: basic - -.. displayitem:: - :header: 2: Run a model in the background on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-4 - :button_link: run_basic.html - :height: 200 - :tag: intermediate - -.. displayitem:: - :header: 3: Save up to 80% on cloud costs with fault-tolerant training - :description: Run on the cloud for 1/10th the price with fault-tolerant training. - :col_css: col-md-4 - :button_link: fault_tolerant_training_basic.html - :height: 200 - :tag: intermediate - -.. displayitem:: - :header: 4: Run many models at once - :description: Run many models at once (sweep) to find the best performing model. - :col_css: col-md-4 - :button_link: run_intermediate.html - :height: 200 - :tag: intermediate - -.. displayitem:: - :header: 5: Run on your own cloud - :description: Learn how to run on your Company or University private clouds. - :col_css: col-md-4 - :button_link: run_expert.html - :height: 200 - :tag: expert - -.. raw:: html - -
-
- ----- - -.. raw:: html - -
-
- -.. raw:: html - - - - -.. raw:: html - -
-
- -`Grid.ai `_ is the official cloud training solution for PyTorch Lightning. Grid is designed to support researcher workloads at both academic labs and major companies. - -.. raw:: html - -
-
diff --git a/docs/_sources/clouds/cloud_training_intermediate.rst.txt b/docs/_sources/clouds/cloud_training_intermediate.rst.txt deleted file mode 100644 index c5a65d7..0000000 --- a/docs/_sources/clouds/cloud_training_intermediate.rst.txt +++ /dev/null @@ -1,7 +0,0 @@ -:orphan: - -.. _grid_cloud_intermediate: - -################################# -Train on the cloud (intermediate) -################################# diff --git a/docs/_sources/clouds/cluster.rst.txt b/docs/_sources/clouds/cluster.rst.txt deleted file mode 100644 index 59a252a..0000000 --- a/docs/_sources/clouds/cluster.rst.txt +++ /dev/null @@ -1,48 +0,0 @@ -######################### -Run on an on-prem cluster -######################### - - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run on an on-prem cluster - :description: Learn to train models on a general compute cluster. - :col_css: col-md-6 - :button_link: cluster_intermediate_1.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Run with Torch Distributed - :description: Run models on a cluster with torch distributed. - :col_css: col-md-6 - :button_link: cluster_intermediate_2.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Run on a SLURM cluster - :description: Run models on a SLURM-managed cluster - :col_css: col-md-6 - :button_link: cluster_advanced.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Integrate your own cluster - :description: Learn how to integrate your own cluster - :col_css: col-md-6 - :button_link: cluster_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/clouds/cluster_advanced.rst.txt b/docs/_sources/clouds/cluster_advanced.rst.txt deleted file mode 100644 index 918bf06..0000000 --- a/docs/_sources/clouds/cluster_advanced.rst.txt +++ /dev/null @@ -1,213 +0,0 @@ -#################################### -Run on an on-prem cluster (advanced) -#################################### - -.. _slurm: - ----- - -****************************** -Run on a SLRUM managed cluster -****************************** -Lightning automates the details behind training on a SLURM-powered cluster. In contrast to the general purpose -cluster above, the user does not start the jobs manually on each node and instead submits it to SLURM which -schedules the resources and time for which the job is allowed to run. - ----- - -*************************** -Design your training script -*************************** - -To train a model using multiple nodes, do the following: - -1. Design your :ref:`lightning_module` (no need to add anything specific here). - -2. Enable DDP in the trainer - - .. code-block:: python - - # train on 32 GPUs across 4 nodes - trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp") - -3. It's a good idea to structure your training script like this: - - .. testcode:: - - # train.py - def main(hparams): - model = LightningTemplateModel(hparams) - - trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp") - - trainer.fit(model) - - - if __name__ == "__main__": - root_dir = os.path.dirname(os.path.realpath(__file__)) - parent_parser = ArgumentParser(add_help=False) - hyperparams = parser.parse_args() - - # TRAIN - main(hyperparams) - -4. Create the appropriate SLURM job: - - .. code-block:: bash - - # (submit.sh) - #!/bin/bash -l - - # SLURM SUBMIT SCRIPT - #SBATCH --nodes=4 - #SBATCH --gres=gpu:8 - #SBATCH --ntasks-per-node=8 - #SBATCH --mem=0 - #SBATCH --time=0-02:00:00 - - # activate conda env - source activate $1 - - # debugging flags (optional) - export NCCL_DEBUG=INFO - export PYTHONFAULTHANDLER=1 - - # on your cluster you might need these: - # set the network interface - # export NCCL_SOCKET_IFNAME=^docker0,lo - - # might need the latest CUDA - # module load NCCL/2.4.7-1-cuda.10.0 - - # run script from above - srun python3 train.py - -5. If you want auto-resubmit (read below), add this line to the submit.sh script - - .. code-block:: bash - - #SBATCH --signal=SIGUSR1@90 - -6. Submit the SLURM job - - .. code-block:: bash - - sbatch submit.sh - ----- - -********************************** -Enable auto wall-time resubmitions -********************************** -When you use Lightning in a SLURM cluster, it automatically detects when it is about -to run into the wall time and does the following: - -1. Saves a temporary checkpoint. -2. Requeues the job. -3. When the job starts, it loads the temporary checkpoint. - -To get this behavior make sure to add the correct signal to your SLURM script - -.. code-block:: bash - - # 90 seconds before training ends - SBATCH --signal=SIGUSR1@90 - -If auto-resubmit is not desired, it can be turned off in the :class:`~pytorch_lightning.plugins.environments.slurm_environment.SLURMEnvironment` plugin: - -.. code-block:: python - - from pytorch_lightning.plugins.environments import SLURMEnvironment - - trainer = Trainer(plugins=[SLURMEnvironment(auto_requeue=False)]) - ----- - -*********************** -Build your SLURM script -*********************** -Instead of manually building SLURM scripts, you can use the -`SlurmCluster object `_ -to do this for you. The SlurmCluster can also run a grid search if you pass -in a `HyperOptArgumentParser -`_. - -Here is an example where you run a grid search of 9 combinations of hyperparameters. -See also the multi-node examples -`here `__. - -.. code-block:: python - - # grid search 3 values of learning rate and 3 values of number of layers for your net - # this generates 9 experiments (lr=1e-3, layers=16), (lr=1e-3, layers=32), - # (lr=1e-3, layers=64), ... (lr=1e-1, layers=64) - parser = HyperOptArgumentParser(strategy="grid_search", add_help=False) - parser.opt_list("--learning_rate", default=0.001, type=float, options=[1e-3, 1e-2, 1e-1], tunable=True) - parser.opt_list("--layers", default=1, type=float, options=[16, 32, 64], tunable=True) - hyperparams = parser.parse_args() - - # Slurm cluster submits 9 jobs, each with a set of hyperparams - cluster = SlurmCluster( - hyperparam_optimizer=hyperparams, - log_path="/some/path/to/save", - ) - - # OPTIONAL FLAGS WHICH MAY BE CLUSTER DEPENDENT - # which interface your nodes use for communication - cluster.add_command("export NCCL_SOCKET_IFNAME=^docker0,lo") - - # see the output of the NCCL connection process - # NCCL is how the nodes talk to each other - cluster.add_command("export NCCL_DEBUG=INFO") - - # setting a main port here is a good idea. - cluster.add_command("export MASTER_PORT=%r" % PORT) - - # ************** DON'T FORGET THIS *************** - # MUST load the latest NCCL version - cluster.load_modules(["NCCL/2.4.7-1-cuda.10.0"]) - - # configure cluster - cluster.per_experiment_nb_nodes = 12 - cluster.per_experiment_nb_gpus = 8 - - cluster.add_slurm_cmd(cmd="ntasks-per-node", value=8, comment="1 task per gpu") - - # submit a script with 9 combinations of hyper params - # (lr=1e-3, layers=16), (lr=1e-3, layers=32), (lr=1e-3, layers=64), ... (lr=1e-1, layers=64) - cluster.optimize_parallel_cluster_gpu( - main, nb_trials=9, job_name="name_for_squeue" # how many permutations of the grid search to run - ) - - -The other option is that you generate scripts on your own via a bash command or use our -:doc:`native solution <../clouds/cloud_training>`. - ----- - -******** -Get help -******** -Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI. - -Try it out for free today: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: cloud_training.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
bool: - """Return True if the cluster is managed (you don't launch processes yourself)""" - return True - - def world_size(self) -> int: - return int(os.environ["WORLD_SIZE"]) - - def global_rank(self) -> int: - return int(os.environ["RANK"]) - - def local_rank(self) -> int: - return int(os.environ["LOCAL_RANK"]) - - def node_rank(self) -> int: - return int(os.environ["NODE_RANK"]) - - def main_address(self) -> str: - return os.environ["MASTER_ADDRESS"] - - def main_port(self) -> int: - return int(os.environ["MASTER_PORT"]) - - - trainer = Trainer(plugins=[MyClusterEnvironment()]) - ----- - -******** -Get help -******** -Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI. - -Try it out for free today: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: cloud_training.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
`_. and requires the following environment variables to be defined on each node: - -- *MASTER_PORT* - required; has to be a free port on machine with NODE_RANK 0 -- *MASTER_ADDR* - required (except for NODE_RANK 0); address of NODE_RANK 0 node -- *WORLD_SIZE* - required; how many nodes are in the cluster -- *NODE_RANK* - required; id of the node in the cluster - -.. _training_script_setup: - ----- - -************************* -Setup the training script -************************* -To train a model using multiple nodes, do the following: - -1. Design your :ref:`lightning_module` (no need to add anything specific here). - -2. Enable DDP in the trainer - - .. code-block:: python - - # train on 32 GPUs across 4 nodes - trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp") - ----- - -*************************** -Submit a job to the cluster -*************************** -To submit a training job to the cluster you need to run the same training script on each node of the cluster. -This means that you need to: - -1. Copy all third-party libraries to each node (usually means - distribute requirements.txt file and install it). -2. Copy all your import dependencies and the script itself to each node. -3. Run the script on each node. - ----- - -****************** -Debug on a cluster -****************** -When running in DDP mode, some errors in your code can show up as an NCCL issue. -Set the ``NCCL_DEBUG=INFO`` environment variable to see the ACTUAL error. - -.. code-block:: bash - - NCCL_DEBUG=INFO python train.py ... - ----- - -******** -Get help -******** -Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI. - -Try it out for free today: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: cloud_training.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
`__ provides helper functions to setup distributed environment variables from the `PyTorch distributed communication package `__ that need to be defined on each node. - -Once the script is setup like described in :ref:` Training Script Setup`, you can run the below command across your nodes to start multi-node training. - -Like a custom cluster, you have to ensure that there is network connectivity between the nodes with firewall rules that allow traffic flow on a specified *MASTER_PORT*. - -Finally, you'll need to decide which node you'd like to be the main node (*MASTER_ADDR*), and the ranks of each node (*NODE_RANK*). - -For example: - -* *MASTER_ADDR* 10.10.10.16 -* *MASTER_PORT* 29500 -* *NODE_RANK* 0 for the first node, 1 for the second node - -Run the below command with the appropriate variables set on each node. - -.. code-block:: bash - - python -m torch.distributed.run - --nnodes=2 # number of nodes you'd like to run with - --master_addr - --master_port - --node_rank - train.py (--arg1 ... train script args...) - -.. note:: - - ``torch.distributed.run`` assumes that you'd like to spawn a process per GPU if GPU devices are found on the node. This can be adjusted with ``-nproc_per_node``. - ----- - -******** -Get help -******** -Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI. - -Try it out for free today: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: cloud_training.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
`. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Save money with fault-tolerant training on the cloud - :col_css: col-md-4 - :button_link: fault_tolerant_training_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Expert - :description: Learn how to enable fault tolerance on any cloud or cluster environment - :col_css: col-md-4 - :button_link: fault_tolerant_training_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: FAQ - :description: Frequently asked questions about fault-tolerant training. - :col_css: col-md-4 - :button_link: fault_tolerant_training_faq.html - :height: 150 - -.. raw:: html - -
-
diff --git a/docs/_sources/clouds/fault_tolerant_training_basic.rst.txt b/docs/_sources/clouds/fault_tolerant_training_basic.rst.txt deleted file mode 100644 index d5af9ed..0000000 --- a/docs/_sources/clouds/fault_tolerant_training_basic.rst.txt +++ /dev/null @@ -1,43 +0,0 @@ -:orphan: - -############################### -Fault-tolerant Training (basic) -############################### -**Audience:** User who want to run on the cloud or a cluster environment. - -**Pre-requisites**: Users must have first read :doc:`Run on the cloud (basic) ` - ----- - -******************************** -What is fault-tolerant training? -******************************** -When developing models on the cloud or cluster environments, you may be forced to restart from scratch in the event of a software or hardware failure (ie: a *fault*). Lightning models can run fault-proof. - -With Fault Tolerant Training, when ``Trainer.fit()`` fails in the middle of an epoch during training or validation, -Lightning will restart exactly where it failed, and everything will be restored (down to the batch it was on even if the dataset was shuffled). - -.. warning:: Fault-tolerant Training is currently an experimental feature within Lightning. - ----- - -*************************************************** -Use fault-tolerance to save money on cloud training -*************************************************** -Cloud providers offer pre-emptible machines which can be priced as low as 1/10th the cost but can be shut-down automatically at any time. -Because fault-tolerant training can automatically recover from an interruption, you can train models for many weeks/months at a time for the pre-emptible prices. - -To easily run on the cloud with fault-tolerance with lightning-grid, use the following arguments: - -.. code-block:: bash - - grid run --use_spot --auto_resume lightning_script.py - -The ``--use_spot`` argument enables cheap preemptible pricing (but the machines that can be interrupted). -If the machine is interrupted, the ``--auto_resume`` argument automatically restarts the machine. - -As long as you are running a script that runs a lightning model, the model will restore itself and handle all the details of fault tolerance. - ----- - -.. include:: grid_costs.rst diff --git a/docs/_sources/clouds/fault_tolerant_training_expert.rst.txt b/docs/_sources/clouds/fault_tolerant_training_expert.rst.txt deleted file mode 100644 index f0051f7..0000000 --- a/docs/_sources/clouds/fault_tolerant_training_expert.rst.txt +++ /dev/null @@ -1,34 +0,0 @@ -:orphan: - -################################ -Fault-tolerant Training (expert) -################################ -**Audience**: Experts looking to enable and handle their own fault-tolerance. - -**Pre-requisites**: Users must have first read :doc:`Fault-tolrance Training (basic) ` - ----- - -*************************************** -Enable fault-tolerant behavior anywhere -*************************************** -To enable fault tolerance on your own cloud or cluster environment enable the *PL_FAULT_TOLERANT_TRAINING* environment variable: - -.. code-block:: bash - - PL_FAULT_TOLERANT_TRAINING=1 python script.py - -Although Lighting will now be fault-tolerant, you'll have to handle all the nuances of making sure the models are automatically restarted. - -.. note:: This complexity is already handled for you if you use **lightning-grid**. - ----- - -************************************************** -Enable fault-tolerant behavior on your own cluster -************************************************** -The simplest way to enable fault-tolerant behavior is to enable lightning-grid to work on your on-prem cluster or cloud environment which will handle all the nuances of fault-tolerant training at scale. - -Email us to connect with your own cloud account: - -``_ diff --git a/docs/_sources/clouds/fault_tolerant_training_faq.rst.txt b/docs/_sources/clouds/fault_tolerant_training_faq.rst.txt deleted file mode 100644 index 4f2bdf4..0000000 --- a/docs/_sources/clouds/fault_tolerant_training_faq.rst.txt +++ /dev/null @@ -1,144 +0,0 @@ -:orphan: - -############################# -Fault-tolerant Training (FAQ) -############################# - -******************************* -How do I use iterable datasets? -******************************* -To support fault-tolerance, you will need to use and expose a sampler within your dataset. - -For example, the following implementation for an iterable dataset sub-classing :class:`~torch.utils.data.IterableDataset` won't be supported. - -.. code-block:: python - - from torch.utils.data import IterableDataset, DataLoader - - - # does not support fault tolerance training! - class RandomIterableDataset(IterableDataset): - def __init__(self, size: int, count: int): - self.count = count - self.size = size - - def __iter__(self): - for _ in range(self.count): - yield torch.randn(self.size) - - -There are two primary reasons why Lightning can't support the previous implementation. - -* Lightning cannot infer what you are iterating over, making it difficult to restart training. Lightning Fault Tolerant Training requires a :class:`~torch.utils.data.distributed.Sampler` to be used to encapsulate the fetching logic, requiring both the sampler and an iterator to be made available as attributes within the dataset, so Lightning can access them to track progress. -* Implementing the `__next__` method is required as it separates iterator creation from its consumption, which is essential for Lightning to wrap the iterator before their consumption. - -If your iterable dataset are implemented in the following way, everything should works as expected. - -.. code-block:: python - - import torch - from torch.utils.data import IterableDataset, DataLoader - - - class RandomIterableDataset(IterableDataset): - def __init__(self, size: int, length: int): - self.data = torch.randn(length, size) - - # expose the sampler as an attribute - self.sampler = RandomSampler(range(length)) - - def __iter__(self) -> "RandomIterableDataset": - # expose the generator from the sampler as an attribute - # the ``sampler_iter`` will be wrapped by Lightning to ensure - # we can capture random seeds and iteration count for fast-forward samplers - # while restarting. - self.sampler_iter = iter(self.sampler) - return self - - def __next__(self) -> torch.Tensor: - # call next on the iterator and get the associated data. - # the logic here can become more complex but the sampler - # should be the central piece for fetching the next sample - index = next(self.sampler_iter) - return self.data[index] - ----- - -********************************** -How do I use multiple dataloaders? -********************************** -If you are using multiple training dataloaders, Lightning won't be able to restore the random state properly. - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - loader_a = torch.utils.data.DataLoader(range(8), batch_size=4) - loader_b = torch.utils.data.DataLoader(range(16), batch_size=4) - return {"loader_a": loader_a, "loader_b": loader_b} - - def training_step(self, batch, batch_idx): - # access the data in the same format as the collection of dataloaders. - # dict, list are supported. - loader_a = batch["loader_a"] - loader_b = batch["loader_b"] - - -If you believe this to be useful, please open a `feature request `_. - - ----- - -********************************* -What are the performance impacts? -********************************* -Fault-tolerant Training was tested on common and worst-case scenarios in order to measure the impact of the internal state tracking on the total training time. -On tiny models like the `BoringModel and RandomDataset `_ -which has virtually no data loading and processing overhead, we noticed up to 50% longer training time with fault tolerance enabled. -In this worst-case scenario, fault-tolerant adds an overhead that is noticeable in comparison to the compute time for dataloading itself. -However, for more realistic training workloads where data loading and preprocessing is more expensive, the constant overhead that fault tolerance adds becomes less noticeable or not noticeable at all. -For example, when training with ResNet50 on CIFAR 10 we have observed a 0.5% to 1% increase in training time depending on ``batch size`` or ``number of workers``. - -More detailed benchmarks will be shared in the future. - -.. note:: - - The extra time is coming from several parts: - - - Capturing the iteration count + random states for each sample within each DataLoader workers and pass it through the data_queue - - Extra logic to handle / store the dataloader's states from each batch. - ----- - -************************************ -What happens to my shuffled dataset? -************************************ -If you are using a single map-based dataset by sub-classing :class:`~torch.utils.data.Dataset`, everything should work as expected. - -.. code-block:: python - - from torch.utils.data import Dataset, DataLoader - - - class RandomDataset(Dataset): - def __init__(self, size: int, length: int): - self.len = length - self.data = torch.randn(length, size) - - def __getitem__(self, index): - return self.data[index] - - def __len__(self): - return self.len - ----- - -****************************** -What parts are fault-tolerant? -****************************** -Lightning keeps track of the following state updates during training: - -* Samplers indices and random states across multiple processes and workers: This enables restoring random transforms and batch fetching to the exact state as it was right before the failure. -* Optimizers, learning rate schedulers, callbacks, etc.. -* Loop progression -* Logging internal states such that metric reductions on epoch end are not getting affected by the failure and model selection can continue as expected. diff --git a/docs/_sources/clouds/grid_costs.rst.txt b/docs/_sources/clouds/grid_costs.rst.txt deleted file mode 100644 index 04b1864..0000000 --- a/docs/_sources/clouds/grid_costs.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -**** -Cost -**** -Lightning (via `lightning-grid `_) provides access to cloud machines to the community for free. However, you must buy credits on `lightning-grid `_ which are used to pay the cloud providers on your behalf. - -If you want to run on your own AWS account and pay the cloud provider directly, please contact our onprem team: ``_ diff --git a/docs/_sources/clouds/run_advanced.rst.txt b/docs/_sources/clouds/run_advanced.rst.txt deleted file mode 100644 index 3418dee..0000000 --- a/docs/_sources/clouds/run_advanced.rst.txt +++ /dev/null @@ -1,130 +0,0 @@ -:orphan: - -.. _grid_cloud_advanced: - -############################# -Train on the cloud (advanced) -############################# -**Audience**: Anyone looking to train a model on the cloud in the background - ----- - -**************************** -What is background training? -**************************** -Background training lets you train models in the background without you needing to interact with the machine. As the model trains you can monitor its progress via Tensorboard or an experiment manager of your choice. - ----- - -************************* -0: Install lightning-grid -************************* -First Navigate to https://platform.grid.ai to create a free account. - -Next, install lightning-grid and login - -.. code:: bash - - pip install lightning-grid - grid login - ----- - -******************* -1: Create a dataset -******************* -Create a datastore which optimizes your datasets for training at scale on the cloud. - -First, let's download a dummy dataset we created. - -.. code:: bash - - # download - curl https://pl-flash-data.s3.amazonaws.com/cifar5.zip -o cifar5.zip - - # unzip - unzip cifar5.zip - -Now create the datastore - -.. code:: bash - - grid datastore create cifar5/ --name cifar5 - -Now your dataset is ready to be used for training on the cloud! - -.. note:: In some *research* workflows, your model script ALSO downloads the dataset. If the dataset is only a few GBs this is fine. Otherwise we recommend you create a Datastore. - ----- - -************************** -2: Choose the model to run -************************** -You can run any python script in the background. For this example, we'll use a simple classifier: - -Clone the code to your machine: - -.. code bash - - git clone https://github.com/williamFalcon/cifar5-simple.git - - -.. note:: Code repositories can be as complicated as needed. This is just a simple demo. - ----- - -******************* -3: Run on the cloud -******************* -To run this model on the cloud, use the **grid run** command which has two parts: - -.. code:: bash - - grid run [run args] file.py [file args] - -To attach the datastore **cifar5** to the **cifar5.py** file use the following command: - -.. code:: bash - - # command | the datastore to use | the model | argument to the model - grid run --datastore_name cifar5 cifar5.py.py --data_dir /datastores/cifar5 - ----- - -********************* -4: Monitor and manage -********************* -Now that your model is running in the background you can monitor and manage it `here `_. - -You can also monitor its progress on the commandline: - -.. code:: bash - - grid status - ----- - -********** -Next Steps -********** -Here are the recommended next steps depending on your workflow. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run many models at once - :description: Learn how to run many models at once using sweeps. - :col_css: col-md-12 - :button_link: session_intermediate.html - :height: 150 - :tag: basic - -.. raw:: html - -
-
`_. - -You can also monitor its progress on the commandline: - -.. code:: bash - - grid status - ----- - -.. include:: grid_costs.rst - ----- - -********** -Next Steps -********** -Here are the recommended next steps depending on your workflow. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run many models at once - :description: Learn how to find the best performaning model by running multiple models at once using a sweep. - :col_css: col-md-4 - :button_link: run_intermediate.html - :height: 150 - :tag: basic - -.. raw:: html - -
-
` for more information. - ----- - -*********************************** -Run on your own cloud (hassle free) -*********************************** -Cluster training can get complicated once you start doing multi-node training, fault-tolerant training or sweeps. -If you'd prefer to not deal with any of the hassles of running on your own cloud environments, lightning-grid enables University and Enterprise customers to run on the cloud with their own credentials or even onprem. - -These are some of the benefits of running via lightning-grid: - -- create datasets optimized for scale -- fully configurable on-prem deployment -- SOC-2 compliance (in-progress) (ETA Q3 2022) -- micro cost optimizations everywhere (which add up) -- built-in fault tolerance -- enabled collaboration for teams and enterprises - -Contact our sales support engineering team so we can help you set up Grid with your own cloud credentials. - -Email us to connect with your own cloud account: - -``_. diff --git a/docs/_sources/clouds/run_intermediate.rst.txt b/docs/_sources/clouds/run_intermediate.rst.txt deleted file mode 100644 index dad2edf..0000000 --- a/docs/_sources/clouds/run_intermediate.rst.txt +++ /dev/null @@ -1,229 +0,0 @@ -:orphan: - -.. _grid_cloud_run_intermediate: - -################################# -Train on the cloud (intermediate) -################################# -**Audience**: User looking to run many models at once - ----- - -**************** -What is a sweep? -**************** -A sweep is the term giving to running the same model multiple times with different hyperparameters to find the one that performs the best (according to your definition of performance). - -Let's say I have a python script that trains a Lighting model to classify images. We run this file like so: - -.. code:: bash - - grid run file.py --batch_size 8 - -with such a model, I would be interested in knowing how it performs with different batch size. In this case, I'm going to train many versions of this model. - -.. code:: bash - - # run 4 models in parallel - grid run file.py --batch_size 8 - grid run file.py --batch_size 16 - grid run file.py --batch_size 32 - grid run file.py --batch_size 64 - -Now I can see how my model performs according to the layers and based on time and cost I can pick my "best" model: - -.. list-table:: Training speed vs cost - :widths: 10 40 15 15 - :header-rows: 1 - - * - Batch size - - classification accuracy (%) - - training time - - cost - * - 8 - - 0.80 - - 5 minutes - - $0.15 - * - 16 - - 0.85 - - 10 minutes - - $0.30 - * - 32 - - 0.90 - - 30 minutes - - $0.50 - * - 64 - - 0.95 - - 60 minutes - - $1.01 - ----- - -************* -Start a Sweep -************* -First, recall that in the `previous tutorial `_ we ran a single model using this command: - -.. code:: bash - - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 - -Now we're going to run that same model 4 different times each with a different number of layers: - -.. code:: bash - - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 8 - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 16 - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 32 - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 64 - -Grid has a special syntax based on python that gives you shortcuts for sweeps. The shortcut for the above commands is: - -.. code:: bash - - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size "[8, 16, 32, 64]" - ----- - -**************** -Syntax Shortcuts -**************** - -List -==== - -.. code:: bash - - grid run file.py --batch_size "[8, 16, 32, 64]" - -equivalent to: - -.. code:: bash - - grid run file.py --batch_size 8 - grid run file.py --batch_size 16 - grid run file.py --batch_size 32 - grid run file.py --batch_size 64 - ----- - -Range -===== - -.. code:: bash - - grid run file.py --batch_size "range(1, 10, 2)" - -equivalent to: - -.. code:: bash - - grid run main.py --batch_size 1 - grid run main.py --batch_size 3 - grid run main.py --batch_size 5 - grid run main.py --batch_size 7 - grid run main.py --batch_size 9 - ---- - -String list -=========== - -.. code:: bash - - grid run file.py --model_backbone "['resnet18' 'transformer', 'resnet50']" - -equivalent to: - -.. code:: bash - - grid run file.py --model_backbone 'resnet18' - grid run file.py --model_backbone 'transformer' - grid run file.py --model_backbone 'resnet50' - ----- - -Sampling -======== - -.. code:: bash - - grid run file.py --learning_rate "uniform(1e-5, 1e-1, 3)" - -equivalent to: - -.. code:: bash - - grid run file.py --learning_rate 0.03977392 - grid run file.py --learning_rate 0.04835479 - grid run file.py --learning_rate 0.05200016 - ----- - -**************** -Sweep strategies -**************** -Models often have dozens of hyperparameters. We usually don't run all combinations because it would be too prohibitive. Grid supports two strategies: - ----- - -Grid search -=========== -Grid search is a common approach that tries all combinations of hyperparamaters. Grid will automatically compute combinations when it detects special syntax: - -.. code:: bash - - grid run file.py --batch_size "[1, 2]" --layers "[3, 5]" - -is equivalent to: - -.. code:: bash - - grid run file.py --batch_size 1 --layers 3 - grid run file.py --batch_size 2 --layers 3 - grid run file.py --batch_size 1 --layers 5 - grid run file.py --batch_size 2 --layers 5 - ----- - -Random search -============= -With random search, we choose only a subset of hyperparamaters. The larger the number of trials (*num_trials*) the more probable we'll find a great performing model without needing to try all possible combinations. - -.. code:: bash - - grid run --strategy random_search --num_trials 2 file.py --batch_size "[1, 2]" --layers "[3, 5]" - -the above command generates the 4 combinations and runs only 2 at random - -.. code:: bash - - grid run file.py --batch_size 2 --layers 3 - grid run file.py --batch_size 1 --layers 5 - ----- - -********** -Next Steps -********** -Here are the recommended next steps depending on your workflow. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run with your own cloud credentials - :description: Learn how to use Grid products with your Company or University cloud account. - :col_css: col-md-4 - :button_link: run_expert.html - :height: 180 - :tag: expert - -.. raw:: html - -
-
`_ to create a free account, then start a new Grid Session. - -A Grid Session is an interactive machine with 1-16 GPUs per machine. - -.. image:: https://docs.grid.ai/assets/images/new-session-3c58be3fd64ffabcdeb7b52516e0782e.gif - :alt: Start a Grid Session in a few seconds - ----- - -************************* -Open the Jupyter Notebook -************************* -Once the Session starts, open a Jupyter notebook. - -.. raw:: html - - - ----- - -************************ -Clone and run your model -************************ -On the Jupyter page you can use a Notebook, or to clone your code and run via the CLI. - -.. raw:: html - - - ----- - -.. include:: grid_costs.rst - ----- - -********** -Next Steps -********** -Here are the recommended next steps depending on your workflow. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run a model in the background - :description: Learn to run a model in the background - :col_css: col-md-6 - :button_link: run_basic.html - :height: 180 - :tag: basic - -.. displayitem:: - :header: Run with your own cloud credentials - :description: Learn how to use Grid products on your Company or University private cloud account. - :col_css: col-md-6 - :button_link: run_expert.html - :height: 180 - :tag: expert - -.. raw:: html - -
-
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Learn to save and load checkpoints - :col_css: col-md-3 - :button_link: checkpointing_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Customize checkpointing behavior - :col_css: col-md-3 - :button_link: checkpointing_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Advanced - :description: Enable cloud-based checkpointing and composable checkpoints. - :col_css: col-md-3 - :button_link: checkpointing_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Expert - :description: Customize checkpointing for custom distributed strategies and accelerators. - :col_css: col-md-3 - :button_link: checkpointing_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
- - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: ModelCheckpoint API - :description: Dig into the ModelCheckpoint API - :col_css: col-md-4 - :button_link: ../api/pytorch_lightning.callbacks.ModelCheckpoint.html - :height: 150 - -.. raw:: html - -
-
diff --git a/docs/_sources/common/checkpointing_advanced.rst.txt b/docs/_sources/common/checkpointing_advanced.rst.txt deleted file mode 100644 index 561ca95..0000000 --- a/docs/_sources/common/checkpointing_advanced.rst.txt +++ /dev/null @@ -1,75 +0,0 @@ -.. _checkpointing_advanced: - -######################## -Checkpointing (advanced) -######################## - - -***************** -Cloud checkpoints -***************** -Lightning is integrated with the major remote file systems including local filesystems and several cloud storage providers such as -`S3 `_ on `AWS `_, `GCS `_ on `Google Cloud `_, -or `ADL `_ on `Azure `_. - -PyTorch Lightning uses `fsspec `_ internally to handle all filesystem operations. - ----- - -Save a cloud checkpoint -======================= - -To save to a remote filesystem, prepend a protocol like "s3:/" to the root_dir used for writing and reading model data. - -.. code-block:: python - - # `default_root_dir` is the default path used for logs and checkpoints - trainer = Trainer(default_root_dir="s3://my_bucket/data/") - trainer.fit(model) - ----- - -Resume training from a cloud checkpoint -======================================= -To resume training from a cloud checkpoint use a cloud url. - -.. code-block:: python - - trainer = Trainer(default_root_dir=tmpdir, max_steps=3) - trainer.fit(model, ckpt_path="s3://my_bucket/ckpts/classifier.ckpt") - -PyTorch Lightning uses `fsspec `_ internally to handle all filesystem operations. - ----- - -*************************** -Modularize your checkpoints -*************************** -Checkpoints can also save the state of :doc:`datamodules <../extensions/datamodules_state>` and :doc:`callbacks <../extensions/callbacks_state>`. - ----- - -**************************** -Modify a checkpoint anywhere -**************************** -When you need to change the components of a checkpoint before saving or loading, use the :meth:`~pytorch_lightning.core.hooks.CheckpointHooks.on_save_checkpoint` and :meth:`~pytorch_lightning.core.hooks.CheckpointHooks.on_load_checkpoint` of your ``LightningModule``. - -.. code:: python - - class LitModel(pl.LightningModule): - def on_save_checkpoint(self, checkpoint): - checkpoint["something_cool_i_want_to_save"] = my_cool_pickable_object - - def on_load_checkpoint(self, checkpoint): - my_cool_pickable_object = checkpoint["something_cool_i_want_to_save"] - -Use the above approach when you need to couple this behavior to your LightningModule for reproducibility reasons. Otherwise, Callbacks also have the :meth:`~pytorch_lightning.callbacks.base.Callback.on_save_checkpoint` and :meth:`~pytorch_lightning.callbacks.base.Callback.on_load_checkpoint` which you should use instead: - -.. code:: python - - class LitCallback(pl.Callback): - def on_save_checkpoint(self, checkpoint): - checkpoint["something_cool_i_want_to_save"] = my_cool_pickable_object - - def on_load_checkpoint(self, checkpoint): - my_cool_pickable_object = checkpoint["something_cool_i_want_to_save"] diff --git a/docs/_sources/common/checkpointing_basic.rst.txt b/docs/_sources/common/checkpointing_basic.rst.txt deleted file mode 100644 index 899de91..0000000 --- a/docs/_sources/common/checkpointing_basic.rst.txt +++ /dev/null @@ -1,189 +0,0 @@ -:orphan: - -.. _checkpointing_basic: - -##################### -Checkpointing (basic) -##################### -**Audience:** All users - ----- - -********************* -What is a checkpoint? -********************* -When a model is training, the performance changes as it continues to see more data. It is a best practice to save the state of a model throughout the training process. This gives you a version of the model, *a checkpoint*, at each key point during the development of the model. Once training has completed, use the checkpoint that corresponds to the best performance you found during the training process. - -Checkpoints also enable your training to resume from where it was in case the training process is interrupted. - -PyTorch Lightning checkpoints are fully usable in plain PyTorch. - ----- - -************************ -Contents of a checkpoint -************************ -A Lightning checkpoint contains a dump of the model's entire internal state. Unlike plain PyTorch, Lightning saves *everything* you need to restore a model even in the most complex distributed training environments. - -Inside a Lightning checkpoint you'll find: - -- 16-bit scaling factor (if using 16-bit precision training) -- Current epoch -- Global step -- LightningModule's state_dict -- State of all optimizers -- State of all learning rate schedulers -- State of all callbacks (for stateful callbacks) -- State of datamodule (for stateful datamodules) -- The hyperparameters used for that model if passed in as hparams (Argparse.Namespace) -- State of Loops (if using Fault-Tolerant training) - ----- - -***************** -Save a checkpoint -***************** -Lightning automatically saves a checkpoint for you in your current working directory, with the state of your last training epoch. This makes sure you can resume training in case it was interrupted. - -.. code-block:: python - - # simply by using the Trainer you get automatic checkpointing - trainer = Trainer() - -To change the checkpoint path use the `default_root_dir` argument: - -.. code-block:: python - - # saves checkpoints to 'some/path/' at every epoch end - trainer = Trainer(default_root_dir="some/path/") - ----- - -******************************* -LightningModule from checkpoint -******************************* - -To load a LightningModule along with its weights and hyperparameters use the following method: - -.. code-block:: python - - model = MyLightningModule.load_from_checkpoint("/path/to/checkpoint.ckpt") - - # disable randomness, dropout, etc... - model.eval() - - # predict with the model - y_hat = model(x) - ----- - -Save hyperparameters -==================== -The LightningModule allows you to automatically save all the hyperparameters passed to *init* simply by calling *self.save_hyperparameters()*. - -.. code-block:: python - - class MyLightningModule(LightningModule): - def __init__(self, learning_rate, another_parameter, *args, **kwargs): - super().__init__() - self.save_hyperparameters() - -The hyperparameters are saved to the "hyper_parameters" key in the checkpoint - -.. code-block:: python - - checkpoint = torch.load(checkpoint, map_location=lambda storage, loc: storage) - print(checkpoint["hyper_parameters"]) - # {"learning_rate": the_value, "another_parameter": the_other_value} - -The LightningModule also has access to the Hyperparameters - -.. code-block:: python - - model = MyLightningModule.load_from_checkpoint("/path/to/checkpoint.ckpt") - print(model.learning_rate) - ----- - -Initalize with other parameters -=============================== -If you used the *self.save_hyperparameters()* method in the init of the LightningModule, you can initialize the model with different hyperparameters. - -.. code-block:: python - - # if you train and save the model like this it will use these values when loading - # the weights. But you can overwrite this - LitModel(in_dim=32, out_dim=10) - - # uses in_dim=32, out_dim=10 - model = LitModel.load_from_checkpoint(PATH) - - # uses in_dim=128, out_dim=10 - model = LitModel.load_from_checkpoint(PATH, in_dim=128, out_dim=10) - ----- - -************************* -nn.Module from checkpoint -************************* -Lightning checkpoints are fully compatible with plain torch nn.Modules. - -.. code-block:: python - - checkpoint = torch.load(CKPT_PATH) - print(checkpoint.keys()) - -For example, let's pretend we created a LightningModule like so: - -.. code-block:: python - - class Encoder(nn.Module): - ... - - - class Decoder(nn.Module): - ... - - - class Autoencoder(pl.LightningModule): - def __init__(self, encoder, decoder, *args, **kwargs): - ... - - - autoencoder = Autoencoder(Encoder(), Decoder()) - -Once the autoencoder has trained, pull out the relevant weights for your torch nn.Module: - -.. code-block:: python - - checkpoint = torch.load(CKPT_PATH) - encoder_weights = checkpoint["encoder"] - decoder_weights = checkpoint["decoder"] - ----- - -********************* -Disable checkpointing -********************* - -You can disable checkpointing by passing: - -.. testcode:: - - trainer = Trainer(enable_checkpointing=False) - ----- - -********************* -Resume training state -********************* - -If you don't just want to load weights, but instead restore the full training, do the following: - -.. code-block:: python - - model = LitModel() - trainer = Trainer() - - # automatically restores model, epoch, step, LR schedulers, apex, etc... - trainer.fit(model, ckpt_path="some/path/to/my_checkpoint.ckpt") diff --git a/docs/_sources/common/checkpointing_expert.rst.txt b/docs/_sources/common/checkpointing_expert.rst.txt deleted file mode 100644 index c1859d6..0000000 --- a/docs/_sources/common/checkpointing_expert.rst.txt +++ /dev/null @@ -1,89 +0,0 @@ -:orphan: - -.. _checkpointing_expert: - -###################### -Checkpointing (expert) -###################### - -TODO: I don't understand this... - -*********************** -Customize Checkpointing -*********************** - -.. warning:: - - The Checkpoint IO API is experimental and subject to change. - - -Lightning supports modifying the checkpointing save/load functionality through the ``CheckpointIO``. This encapsulates the save/load logic -that is managed by the ``Strategy``. ``CheckpointIO`` is different from :meth:`~pytorch_lightning.core.hooks.CheckpointHooks.on_save_checkpoint` -and :meth:`~pytorch_lightning.core.hooks.CheckpointHooks.on_load_checkpoint` methods as it determines how the checkpoint is saved/loaded to storage rather than -what's saved in the checkpoint. - - -****************************** -Built-in Checkpoint IO Plugins -****************************** - -.. list-table:: Built-in Checkpoint IO Plugins - :widths: 25 75 - :header-rows: 1 - - * - Plugin - - Description - * - :class:`~pytorch_lightning.plugins.io.TorchCheckpointIO` - - CheckpointIO that utilizes :func:`torch.save` and :func:`torch.load` to save and load checkpoints - respectively, common for most use cases. - * - :class:`~pytorch_lightning.plugins.io.XLACheckpointIO` - - CheckpointIO that utilizes :func:`xm.save` to save checkpoints for TPU training strategies. - - -*************************** -Custom Checkpoint IO Plugin -*************************** - -``CheckpointIO`` can be extended to include your custom save/load functionality to and from a path. The ``CheckpointIO`` object can be passed to either a ``Trainer`` directly or a ``Strategy`` as shown below: - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.callbacks import ModelCheckpoint - from pytorch_lightning.plugins import CheckpointIO - from pytorch_lightning.strategies import SingleDeviceStrategy - - - class CustomCheckpointIO(CheckpointIO): - def save_checkpoint(self, checkpoint, path, storage_options=None): - ... - - def load_checkpoint(self, path, storage_options=None): - ... - - def remove_checkpoint(self, path): - ... - - - custom_checkpoint_io = CustomCheckpointIO() - - # Either pass into the Trainer object - model = MyModel() - trainer = Trainer( - plugins=[custom_checkpoint_io], - callbacks=ModelCheckpoint(save_last=True), - ) - trainer.fit(model) - - # or pass into Strategy - model = MyModel() - device = torch.device("cpu") - trainer = Trainer( - strategy=SingleDeviceStrategy(device, checkpoint_io=custom_checkpoint_io), - callbacks=ModelCheckpoint(save_last=True), - ) - trainer.fit(model) - -.. note:: - - Some ``TrainingTypePlugins`` like ``DeepSpeedStrategy`` do not support custom ``CheckpointIO`` as checkpointing logic is not modifiable. diff --git a/docs/_sources/common/checkpointing_intermediate.rst.txt b/docs/_sources/common/checkpointing_intermediate.rst.txt deleted file mode 100644 index 7796575..0000000 --- a/docs/_sources/common/checkpointing_intermediate.rst.txt +++ /dev/null @@ -1,175 +0,0 @@ -:orphan: - -.. _checkpointing_intermediate: - -############################ -Checkpointing (intermediate) -############################ -**Audience:** Users looking to customize the checkpointing behavior - ----- - -***************************** -Modify checkpointing behavior -***************************** -For fine-grain control over checkpointing behavior, use the :class:`~pytorch_lightning.callbacks.ModelCheckpoint` object - -.. code-block:: python - - from pytorch_lightning.callbacks import ModelCheckpoint - - checkpoint_callback = ModelCheckpoint(dirpath="my/path/", save_top_k=2, monitor="val_loss") - trainer = Trainer(callbacks=[checkpoint_callback]) - trainer.fit(model) - checkpoint_callback.best_model_path - -Any value that has been logged via *self.log* in the LightningModule can be monitored. - -.. code-block:: python - - class LitModel(pl.LightningModule): - def training_step(self, batch, batch_idx): - self.log("my_metric", x) - - - # 'my_metric' is now able to be monitored - checkpoint_callback = ModelCheckpoint(monitor="my_metric") - ----- - -***************************** -Save checkpoints by condition -***************************** -To save checkpoints based on a (*when/which/what/where*) condition (for example *when* the validation_loss is lower) modify the :class:`~pytorch_lightning.callbacks.ModelCheckpoint` properties. - -When -==== - -- When using iterative training which doesn't have an epoch, you can checkpoint at every ``N`` training steps by specifying ``every_n_training_steps=N``. -- You can also control the interval of epochs between checkpoints using ``every_n_epochs`` between checkpoints, to avoid slowdowns. -- You can checkpoint at a regular time interval using ``train_time_interval`` argument independent of the steps or epochs. -- In case you are monitoring a training metrics, we'd suggest using ``save_on_train_epoch_end=True`` to ensure the required metric is being accumulated correctly for creating a checkpoint. - - -Which -===== - -- You can save the last checkpoint when training ends using ``save_last`` argument. -- You can save top-K and last-K checkpoints by configuring the ``monitor`` and ``save_top_k`` argument. - -| - - .. testcode:: - - from pytorch_lightning.callbacks import ModelCheckpoint - - - # saves top-K checkpoints based on "val_loss" metric - checkpoint_callback = ModelCheckpoint( - save_top_k=10, - monitor="val_loss", - mode="min", - dirpath="my/path/", - filename="sample-mnist-{epoch:02d}-{val_loss:.2f}", - ) - - # saves last-K checkpoints based on "global_step" metric - # make sure you log it inside your LightningModule - checkpoint_callback = ModelCheckpoint( - save_top_k=10, - monitor="global_step", - mode="max", - dirpath="my/path/", - filename="sample-mnist-{epoch:02d}-{global_step}", - ) - -- You can customize the checkpointing behavior to monitor any quantity of your training or validation steps. For example, if you want to update your checkpoints based on your validation loss: - -| - - .. testcode:: - - from pytorch_lightning.callbacks import ModelCheckpoint - - - class LitAutoEncoder(LightningModule): - def validation_step(self, batch, batch_idx): - x, y = batch - y_hat = self.backbone(x) - - # 1. calculate loss - loss = F.cross_entropy(y_hat, y) - - # 2. log val_loss - self.log("val_loss", loss) - - - # 3. Init ModelCheckpoint callback, monitoring "val_loss" - checkpoint_callback = ModelCheckpoint(monitor="val_loss") - - # 4. Add your callback to the callbacks list - trainer = Trainer(callbacks=[checkpoint_callback]) - - -What -==== - -- By default, the ``ModelCheckpoint`` callback saves model weights, optimizer states, etc., but in case you have limited disk space or just need the model weights to be saved you can specify ``save_weights_only=True``. - - -Where -===== - -- It gives you the ability to specify the ``dirpath`` and ``filename`` for your checkpoints. Filename can also be dynamic so you can inject the metrics that are being logged using :meth:`~pytorch_lightning.core.lightning.LightningModule.log`. - -| - - .. testcode:: - - from pytorch_lightning.callbacks import ModelCheckpoint - - - # saves a file like: my/path/sample-mnist-epoch=02-val_loss=0.32.ckpt - checkpoint_callback = ModelCheckpoint( - dirpath="my/path/", - filename="sample-mnist-{epoch:02d}-{val_loss:.2f}", - ) - -| - -The :class:`~pytorch_lightning.callbacks.ModelCheckpoint` callback is very robust and should cover 99% of the use-cases. If you find a use-case that is not configured yet, feel free to open an issue with a feature request on GitHub -and the Lightning Team will be happy to integrate/help integrate it. - ----- - -************************* -Save checkpoints manually -************************* - -You can manually save checkpoints and restore your model from the checkpointed state using :meth:`~pytorch_lightning.trainer.trainer.Trainer.save_checkpoint` -and :meth:`~pytorch_lightning.core.saving.ModelIO.load_from_checkpoint`. - -.. code-block:: python - - model = MyLightningModule(hparams) - trainer.fit(model) - trainer.save_checkpoint("example.ckpt") - - # load the checkpoint later as normal - new_model = MyLightningModule.load_from_checkpoint(checkpoint_path="example.ckpt") - -Manual saving with distributed training -======================================= -In distributed training cases where a model is running across many machines, Lightning ensures that only one checkpoint is saved instead of a model per machine. This requires no code changes as seen below: - -.. code-block:: python - - trainer = Trainer(strategy="ddp") - model = MyLightningModule(hparams) - trainer.fit(model) - # Saves only on the main process - trainer.save_checkpoint("example.ckpt") - -Not using :meth:`~pytorch_lightning.trainer.trainer.Trainer.save_checkpoint` can lead to unexpected behavior and potential deadlock. Using other saving functions will result in all devices attempting to save the checkpoint. As a result, we highly recommend using the Trainer's save functionality. -If using custom saving functions cannot be avoided, we recommend using the :func:`~pytorch_lightning.utilities.rank_zero.rank_zero_only` decorator to ensure saving occurs only on the main process. Note that this will only work if all ranks hold the exact same state and won't work when using -model parallel distributed strategies such as deepspeed or sharded training. diff --git a/docs/_sources/common/child_modules.rst.txt b/docs/_sources/common/child_modules.rst.txt deleted file mode 100644 index d3c1832..0000000 --- a/docs/_sources/common/child_modules.rst.txt +++ /dev/null @@ -1,70 +0,0 @@ -Research projects tend to test different approaches to the same dataset. -This is very easy to do in Lightning with inheritance. - -For example, imagine we now want to train an ``AutoEncoder`` to use as a feature extractor for images. -The only things that change in the ``LitAutoEncoder`` model are the init, forward, training, validation and test step. - -.. code-block:: python - - class Encoder(torch.nn.Module): - ... - - - class Decoder(torch.nn.Module): - ... - - - class AutoEncoder(torch.nn.Module): - def __init__(self): - super().__init__() - self.encoder = Encoder() - self.decoder = Decoder() - - def forward(self, x): - return self.decoder(self.encoder(x)) - - - class LitAutoEncoder(LightningModule): - def __init__(self, auto_encoder): - super().__init__() - self.auto_encoder = auto_encoder - self.metric = torch.nn.MSELoss() - - def forward(self, x): - return self.auto_encoder.encoder(x) - - def training_step(self, batch, batch_idx): - x, _ = batch - x_hat = self.auto_encoder(x) - loss = self.metric(x, x_hat) - return loss - - def validation_step(self, batch, batch_idx): - self._shared_eval(batch, batch_idx, "val") - - def test_step(self, batch, batch_idx): - self._shared_eval(batch, batch_idx, "test") - - def _shared_eval(self, batch, batch_idx, prefix): - x, _ = batch - x_hat = self.auto_encoder(x) - loss = self.metric(x, x_hat) - self.log(f"{prefix}_loss", loss) - - -and we can train this using the ``Trainer``: - -.. code-block:: python - - auto_encoder = AutoEncoder() - lightning_module = LitAutoEncoder(auto_encoder) - trainer = Trainer() - trainer.fit(lightning_module, train_dataloader, val_dataloader) - -And remember that the forward method should define the practical use of a :class:`~pytorch_lightning.core.lightning.LightningModule`. -In this case, we want to use the ``LitAutoEncoder`` to extract image representations: - -.. code-block:: python - - some_images = torch.Tensor(32, 1, 28, 28) - representations = lightning_module(some_images) diff --git a/docs/_sources/common/console_logs.rst.txt b/docs/_sources/common/console_logs.rst.txt deleted file mode 100644 index 6761432..0000000 --- a/docs/_sources/common/console_logs.rst.txt +++ /dev/null @@ -1,26 +0,0 @@ -############### -Console logging -############### -**Audience:** Engineers looking to capture more visible logs. - ----- - -******************* -Enable console logs -******************* -Lightning logs useful information about the training process and user warnings to the console. -You can retrieve the Lightning console logger and change it to your liking. For example, adjust the logging level -or redirect output for certain modules to log files: - -.. testcode:: - - import logging - - # configure logging at the root level of Lightning - logging.getLogger("pytorch_lightning").setLevel(logging.ERROR) - - # configure logging on module level, redirect to file - logger = logging.getLogger("pytorch_lightning.core") - logger.addHandler(logging.FileHandler("core.log")) - -Read more about custom Python logging `here `_. diff --git a/docs/_sources/common/early_stopping.rst.txt b/docs/_sources/common/early_stopping.rst.txt deleted file mode 100644 index 593106f..0000000 --- a/docs/_sources/common/early_stopping.rst.txt +++ /dev/null @@ -1,99 +0,0 @@ -.. testsetup:: * - - from pytorch_lightning.callbacks.early_stopping import EarlyStopping - -.. _early_stopping: - - -############## -Early Stopping -############## - -.. raw:: html - - - - -*********************** -Stopping an Epoch Early -*********************** - -You can stop and skip the rest of the current epoch early by overriding :meth:`~pytorch_lightning.core.hooks.ModelHooks.on_train_batch_start` to return ``-1`` when some condition is met. - -If you do this repeatedly, for every epoch you had originally requested, then this will stop your entire training. - - -********************** -EarlyStopping Callback -********************** - -The :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback can be used to monitor a metric and stop the training when no improvement is observed. - -To enable it: - -- Import :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback. -- Log the metric you want to monitor using :meth:`~pytorch_lightning.core.lightning.LightningModule.log` method. -- Init the callback, and set ``monitor`` to the logged metric of your choice. -- Set the ``mode`` based on the metric needs to be monitored. -- Pass the :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback to the :class:`~pytorch_lightning.trainer.trainer.Trainer` callbacks flag. - -.. code-block:: python - - from pytorch_lightning.callbacks.early_stopping import EarlyStopping - - - class LitModel(LightningModule): - def validation_step(self, batch, batch_idx): - loss = ... - self.log("val_loss", loss) - - - model = LitModel() - trainer = Trainer(callbacks=[EarlyStopping(monitor="val_loss", mode="min")]) - trainer.fit(model) - -You can customize the callbacks behaviour by changing its parameters. - -.. testcode:: - - early_stop_callback = EarlyStopping(monitor="val_accuracy", min_delta=0.00, patience=3, verbose=False, mode="max") - trainer = Trainer(callbacks=[early_stop_callback]) - - -Additional parameters that stop training at extreme points: - -- ``stopping_threshold``: Stops training immediately once the monitored quantity reaches this threshold. - It is useful when we know that going beyond a certain optimal value does not further benefit us. -- ``divergence_threshold``: Stops training as soon as the monitored quantity becomes worse than this threshold. - When reaching a value this bad, we believes the model cannot recover anymore and it is better to stop early and run with different initial conditions. -- ``check_finite``: When turned on, it stops training if the monitored metric becomes NaN or infinite. -- ``check_on_train_epoch_end``: When turned on, it checks the metric at the end of a training epoch. Use this only when you are monitoring any metric logged within - training-specific hooks on epoch-level. - - -In case you need early stopping in a different part of training, subclass :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` -and change where it is called: - -.. testcode:: - - class MyEarlyStopping(EarlyStopping): - def on_validation_end(self, trainer, pl_module): - # override this to disable early stopping at the end of val loop - pass - - def on_train_end(self, trainer, pl_module): - # instead, do it at the end of training loop - self._run_early_stopping_check(trainer) - -.. note:: - The :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback runs - at the end of every validation epoch by default. However, the frequency of validation - can be modified by setting various parameters in the :class:`~pytorch_lightning.trainer.trainer.Trainer`, - for example :paramref:`~pytorch_lightning.trainer.trainer.Trainer.check_val_every_n_epoch` - and :paramref:`~pytorch_lightning.trainer.trainer.Trainer.val_check_interval`. - It must be noted that the ``patience`` parameter counts the number of - validation checks with no improvement, and not the number of training epochs. - Therefore, with parameters ``check_val_every_n_epoch=10`` and ``patience=3``, the trainer - will perform at least 40 training epochs before being stopped. diff --git a/docs/_sources/common/evaluation.rst.txt b/docs/_sources/common/evaluation.rst.txt deleted file mode 100644 index e126a70..0000000 --- a/docs/_sources/common/evaluation.rst.txt +++ /dev/null @@ -1,33 +0,0 @@ -.. _val-test-dataset: - -******************************** -Add validation and test datasets -******************************** - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Add a validation and test loop to avoid overfitting. - :col_css: col-md-6 - :button_link: evaluation_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Learn about more complex validation and test workflows - :col_css: col-md-6 - :button_link: evaluation_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/common/evaluation_basic.rst.txt b/docs/_sources/common/evaluation_basic.rst.txt deleted file mode 100644 index 5f933ee..0000000 --- a/docs/_sources/common/evaluation_basic.rst.txt +++ /dev/null @@ -1,128 +0,0 @@ -:orphan: - -################################# -Validate and test a model (basic) -################################# -**Audience**: Users who want to add a validation loop to avoid overfitting - ----- - -*************** -Add a test loop -*************** -To make sure a model can generalize to an unseen dataset (ie: to publish a paper or in a production environment) a dataset is normally split into two parts, the *train* split and the *test* split. - -The test set is **NOT** used during training, it is **ONLY** used once the model has been trained to see how the model will do in the real-world. - ----- - -Find the train and test splits -============================== -Datasets come with two splits. Refer to the dataset documentation to find the *train* and *test* splits. - -.. code-block:: python - - import torch.utils.data as data - from torchvision import datasets - - # Load data sets - train_set = datasets.MNIST(root="MNIST", download=True, train=True) - test_set = datasets.MNIST(root="MNIST", download=True, train=False) - ----- - -Define the test loop -==================== -To add a test loop, implement the **test_step** method of the LightningModule - -.. code:: python - - class LitAutoEncoder(pl.LightningModule): - def training_step(self, batch, batch_idx): - ... - - def test_step(self, batch, batch_idx): - # this is the test loop - x, y = batch - x = x.view(x.size(0), -1) - z = self.encoder(x) - x_hat = self.decoder(z) - test_loss = F.mse_loss(x_hat, x) - self.log("test_loss", test_loss) - ----- - -Train with the test loop -======================== -Once the model has finished training, call **.test** - -.. code-block:: python - - from torch.utils.data import DataLoader - - # initialize the Trainer - trainer = Trainer() - - # test the model - trainer.test(model, dataloaders=DataLoader(test_set)) - ----- - -********************* -Add a validation loop -********************* -During training, it's common practice to use a small portion of the train split to determine when the model has finished training. - ----- - -Split the training data -======================= -As a rule of thumb, we use 20% of the training set as the **validation set**. This number varies from dataset to dataset. - -.. code-block:: python - - # use 20% of training data for validation - train_set_size = int(len(train_set) * 0.8) - valid_set_size = len(train_set) - train_set_size - - # split the train set into two - seed = torch.Generator().manual_seed(42) - train_set, valid_set = data.random_split(train_set, [train_set_size, valid_set_size], generator=seed) - ----- - -Define the validation loop -========================== -To add a validation loop, implement the **validation_step** method of the LightningModule - -.. code:: python - - class LitAutoEncoder(pl.LightningModule): - def training_step(self, batch, batch_idx): - ... - - def validation_step(self, batch, batch_idx): - # this is the validation loop - x, y = batch - x = x.view(x.size(0), -1) - z = self.encoder(x) - x_hat = self.decoder(z) - test_loss = F.mse_loss(x_hat, x) - self.log("val_loss", test_loss) - ----- - -Train with the validation loop -============================== -To run the validation loop, pass in the validation set to **.fit** - -.. code-block:: python - - from torch.utils.data import DataLoader - - train_set = DataLoader(train_set) - val_set = DataLoader(val_set) - - # train with both splits - trainer = Trainer() - trainer.fit(model, train_set, val_set) diff --git a/docs/_sources/common/evaluation_intermediate.rst.txt b/docs/_sources/common/evaluation_intermediate.rst.txt deleted file mode 100644 index 7c0ca00..0000000 --- a/docs/_sources/common/evaluation_intermediate.rst.txt +++ /dev/null @@ -1,157 +0,0 @@ -.. _test_set: - -:orphan: - -######################################## -Validate and test a model (intermediate) -######################################## - -During and after training we need a way to evaluate our models to make sure they are not overfitting while training and -generalize well on unseen or real-world data. There are generally 2 stages of evaluation: validation and testing. To some -degree they serve the same purpose, to make sure models works on real data but they have some practical differences. - -Validation is usually done during training, traditionally after each training epoch. It can be used for hyperparameter optimization or tracking model performance during training. -It's a part of the training process. - -Testing is usually done once we are satisfied with the training and only with the best model selected from the validation metrics. - -Let's see how these can be performed with Lightning. - -******* -Testing -******* - -Lightning allows the user to test their models with any compatible test dataloaders. This can be done before/after training -and is completely agnostic to :meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` call. The logic used here is defined under -:meth:`~pytorch_lightning.core.lightning.LightningModule.test_step`. - -Testing is performed using the ``Trainer`` object's ``.test()`` method. - -.. automethod:: pytorch_lightning.trainer.Trainer.test - :noindex: - - -Test after Fit -============== - -To run the test set after training completes, use this method. - -.. code-block:: python - - # run full training - trainer.fit(model) - - # (1) load the best checkpoint automatically (lightning tracks this for you) - trainer.test(ckpt_path="best") - - # (2) test using a specific checkpoint - trainer.test(ckpt_path="/path/to/my_checkpoint.ckpt") - - # (3) test with an explicit model (will use this model and not load a checkpoint) - trainer.test(model) - -.. warning:: - - It is recommended to test with ``Trainer(devices=1)`` since distributed strategies such as DDP - use :class:`~torch.utils.data.distributed.DistributedSampler` internally, which replicates some samples to - make sure all devices have same batch size in case of uneven inputs. This is helpful to make sure - benchmarking for research papers is done the right way. - - -Test Multiple Models -==================== - -You can run the test set on multiple models using the same trainer instance. - -.. code-block:: python - - model1 = LitModel() - model2 = GANModel() - - trainer = Trainer() - trainer.test(model1) - trainer.test(model2) - - -Test Pre-Trained Model -====================== - -To run the test set on a pre-trained model, use this method. - -.. code-block:: python - - model = MyLightningModule.load_from_checkpoint( - checkpoint_path="/path/to/pytorch_checkpoint.ckpt", - hparams_file="/path/to/test_tube/experiment/version/hparams.yaml", - map_location=None, - ) - - # init trainer with whatever options - trainer = Trainer(...) - - # test (pass in the model) - trainer.test(model) - -In this case, the options you pass to trainer will be used when -running the test set (ie: 16-bit, dp, ddp, etc...) - - -Test with Additional DataLoaders -================================ - -You can still run inference on a test dataset even if the :meth:`~pytorch_lightning.core.hooks.DataHooks.test_dataloader` method hasn't been -defined within your :doc:`lightning module <../common/lightning_module>` instance. This would be the case when your test data -is not available at the time your model was declared. - -.. code-block:: python - - # setup your data loader - test_dataloader = DataLoader(...) - - # test (pass in the loader) - trainer.test(dataloaders=test_dataloader) - -You can either pass in a single dataloader or a list of them. This optional named -parameter can be used in conjunction with any of the above use cases. Additionally, -you can also pass in an :doc:`datamodules <../data/datamodule>` that have overridden the -:ref:`datamodule_test_dataloader_label` method. - -.. code-block:: python - - class MyDataModule(pl.LightningDataModule): - ... - - def test_dataloader(self): - return DataLoader(...) - - - # setup your datamodule - dm = MyDataModule(...) - - # test (pass in datamodule) - trainer.test(datamodule=dm) - ----------- - -********** -Validation -********** - -Lightning allows the user to validate their models with any compatible ``val dataloaders``. This can be done before/after training. -The logic associated to the validation is defined within the :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step`. - -Apart from this ``.validate`` has same API as ``.test``, but would rely respectively on :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step` and :meth:`~pytorch_lightning.core.lightning.LightningModule.test_step`. - -.. note:: - ``.validate`` method uses the same validation logic being used under validation happening within - :meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` call. - -.. warning:: - - When using ``trainer.validate()``, it is recommended to use ``Trainer(devices=1)`` since distributed strategies such as DDP - uses :class:`~torch.utils.data.distributed.DistributedSampler` internally, which replicates some samples to - make sure all devices have same batch size in case of uneven inputs. This is helpful to make sure - benchmarking for research papers is done the right way. - -.. automethod:: pytorch_lightning.trainer.Trainer.validate - :noindex: diff --git a/docs/_sources/common/gradient_accumulation.rst.txt b/docs/_sources/common/gradient_accumulation.rst.txt deleted file mode 100644 index c65e75e..0000000 --- a/docs/_sources/common/gradient_accumulation.rst.txt +++ /dev/null @@ -1,43 +0,0 @@ -Accumulated gradients run K small batches of size ``N`` before doing a backward pass. The effect is a large effective batch size of size ``KxN``, where ``N`` is the batch size. -Internally it doesn't stack up the batches and do a forward pass rather it accumulates the gradients for K batches and then do an ``optimizer.step`` to make sure the -effective batch size is increased but there is no memory overhead. - -.. warning:: - - When using distributed training for eg. DDP, with let's say with ``P`` devices, each device accumulates independently i.e. it stores the gradients - after each ``loss.backward()`` and doesn't sync the gradients across the devices until we call ``optimizer.step()``. So for each accumulation - step, the effective batch size on each device will remain ``N*K`` but right before the ``optimizer.step()``, the gradient sync will make the effective - batch size as ``P*N*K``. For DP, since the batch is split across devices, the final effective batch size will be ``N*K``. - -.. seealso:: :class:`~pytorch_lightning.trainer.trainer.Trainer` - -.. testcode:: - - # DEFAULT (ie: no accumulated grads) - trainer = Trainer(accumulate_grad_batches=1) - - # Accumulate gradients for 7 batches - trainer = Trainer(accumulate_grad_batches=7) - -You can set different values for it at different epochs by passing a dictionary, where the key represents the epoch at which the value for gradient accumulation -should be updated. - -.. testcode:: - - # till 5th epoch, it will accumulate every 8 batches. From 5th epoch - # till 9th epoch it will accumulate every 4 batches and after that no accumulation - # will happen. Note that you need to use zero-indexed epoch keys here - trainer = Trainer(accumulate_grad_batches={0: 8, 4: 4, 8: 1}) - -Or, you can create custom :class:`~pytorch_lightning.callbacks.gradient_accumulation_scheduler.GradientAccumulationScheduler` - -.. testcode:: - - from pytorch_lightning.callbacks import GradientAccumulationScheduler - - - # till 5th epoch, it will accumulate every 8 batches. From 5th epoch - # till 9th epoch it will accumulate every 4 batches and after that no accumulation - # will happen. Note that you need to use zero-indexed epoch keys here - accumulator = GradientAccumulationScheduler(scheduling={0: 8, 4: 4, 8: 1}) - trainer = Trainer(callbacks=accumulator) diff --git a/docs/_sources/common/hyperparameters.rst.txt b/docs/_sources/common/hyperparameters.rst.txt deleted file mode 100644 index 9103100..0000000 --- a/docs/_sources/common/hyperparameters.rst.txt +++ /dev/null @@ -1,279 +0,0 @@ -.. testsetup:: * - - from argparse import ArgumentParser, Namespace - - sys.argv = ["foo"] - -Configure hyperparameters from the CLI --------------------------------------- - -Lightning has utilities to interact seamlessly with the command line ``ArgumentParser`` -and plays well with the hyperparameter optimization framework of your choice. - ----------- - -ArgumentParser -^^^^^^^^^^^^^^ -Lightning is designed to augment a lot of the functionality of the built-in Python ArgumentParser - -.. testcode:: - - from argparse import ArgumentParser - - parser = ArgumentParser() - parser.add_argument("--layer_1_dim", type=int, default=128) - args = parser.parse_args() - -This allows you to call your program like so: - -.. code-block:: bash - - python trainer.py --layer_1_dim 64 - ----------- - -Argparser Best Practices -^^^^^^^^^^^^^^^^^^^^^^^^ -It is best practice to layer your arguments in three sections. - -1. Trainer args (``accelerator``, ``devices``, ``num_nodes``, etc...) -2. Model specific arguments (``layer_dim``, ``num_layers``, ``learning_rate``, etc...) -3. Program arguments (``data_path``, ``cluster_email``, etc...) - -| - -We can do this as follows. First, in your ``LightningModule``, define the arguments -specific to that module. Remember that data splits or data paths may also be specific to -a module (i.e.: if your project has a model that trains on Imagenet and another on CIFAR-10). - -.. testcode:: - - class LitModel(LightningModule): - @staticmethod - def add_model_specific_args(parent_parser): - parser = parent_parser.add_argument_group("LitModel") - parser.add_argument("--encoder_layers", type=int, default=12) - parser.add_argument("--data_path", type=str, default="/some/path") - return parent_parser - -Now in your main trainer file, add the ``Trainer`` args, the program args, and add the model args - -.. testcode:: - - # ---------------- - # trainer_main.py - # ---------------- - from argparse import ArgumentParser - - parser = ArgumentParser() - - # add PROGRAM level args - parser.add_argument("--conda_env", type=str, default="some_name") - parser.add_argument("--notification_email", type=str, default="will@email.com") - - # add model specific args - parser = LitModel.add_model_specific_args(parser) - - # add all the available trainer options to argparse - # ie: now --accelerator --devices --num_nodes ... --fast_dev_run all work in the cli - parser = Trainer.add_argparse_args(parser) - - args = parser.parse_args() - -Now you can call run your program like so: - -.. code-block:: bash - - python trainer_main.py --accelerator 'gpu' --devices 2 --num_nodes 2 --conda_env 'my_env' --encoder_layers 12 - -Finally, make sure to start the training like so: - -.. code-block:: python - - # init the trainer like this - trainer = Trainer.from_argparse_args(args, early_stopping_callback=...) - - # NOT like this - trainer = Trainer(accelerator=hparams.accelerator, devices=hparams.devices, ...) - - # init the model with Namespace directly - model = LitModel(args) - - # or init the model with all the key-value pairs - dict_args = vars(args) - model = LitModel(**dict_args) - ----------- - -LightningModule hyperparameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Often times we train many versions of a model. You might share that model or come back to it a few months later -at which point it is very useful to know how that model was trained (i.e.: what learning rate, neural network, etc...). - -Lightning has a standardized way of saving the information for you in checkpoints and YAML files. The goal here is to -improve readability and reproducibility. - -save_hyperparameters -"""""""""""""""""""" - -Use :meth:`~pytorch_lightning.core.lightning.LightningModule.save_hyperparameters` within your -:class:`~pytorch_lightning.core.lightning.LightningModule`'s ``__init__`` method. -It will enable Lightning to store all the provided arguments under the ``self.hparams`` attribute. -These hyperparameters will also be stored within the model checkpoint, which simplifies model re-instantiation after training. - -.. code-block:: python - - class LitMNIST(LightningModule): - def __init__(self, layer_1_dim=128, learning_rate=1e-2): - super().__init__() - # call this to save (layer_1_dim=128, learning_rate=1e-4) to the checkpoint - self.save_hyperparameters() - - # equivalent - self.save_hyperparameters("layer_1_dim", "learning_rate") - - # Now possible to access layer_1_dim from hparams - self.hparams.layer_1_dim - - -In addition, loggers that support it will automatically log the contents of ``self.hparams``. - -Excluding hyperparameters -""""""""""""""""""""""""" - -By default, every parameter of the ``__init__`` method will be considered a hyperparameter to the LightningModule. -However, sometimes some parameters need to be excluded from saving, for example when they are not serializable. -Those parameters should be provided back when reloading the LightningModule. -In this case, exclude them explicitly: - -.. code-block:: python - - class LitMNIST(LightningModule): - def __init__(self, loss_fx, generator_network, layer_1_dim=128): - super().__init__() - self.layer_1_dim = layer_1_dim - self.loss_fx = loss_fx - - # call this to save only (layer_1_dim=128) to the checkpoint - self.save_hyperparameters("layer_1_dim") - - # equivalent - self.save_hyperparameters(ignore=["loss_fx", "generator_network"]) - - -load_from_checkpoint -"""""""""""""""""""" - -LightningModules that have hyperparameters automatically saved with :meth:`~pytorch_lightning.core.lightning.LightningModule.save_hyperparameters` -can conveniently be loaded and instantiated directly from a checkpoint with :meth:`~pytorch_lightning.core.lightning.LightningModule.load_from_checkpoint`: - -.. code-block:: python - - # to load specify the other args - model = LitMNIST.load_from_checkpoint(PATH, loss_fx=torch.nn.SomeOtherLoss, generator_network=MyGenerator()) - - -If parameters were excluded, they need to be provided at the time of loading: - -.. code-block:: python - - # the excluded parameters were `loss_fx` and `generator_network` - model = LitMNIST.load_from_checkpoint(PATH, loss_fx=torch.nn.SomeOtherLoss, generator_network=MyGenerator()) - - ----------- - -Trainer args -^^^^^^^^^^^^ -To recap, add ALL possible trainer flags to the argparser and init the ``Trainer`` this way - -.. code-block:: python - - parser = ArgumentParser() - parser = Trainer.add_argparse_args(parser) - hparams = parser.parse_args() - - trainer = Trainer.from_argparse_args(hparams) - - # or if you need to pass in callbacks - trainer = Trainer.from_argparse_args(hparams, enable_checkpointing=..., callbacks=[...]) - ----------- - -Multiple Lightning Modules -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -We often have multiple Lightning Modules where each one has different arguments. Instead of -polluting the ``main.py`` file, the ``LightningModule`` lets you define arguments for each one. - -.. testcode:: - - class LitMNIST(LightningModule): - def __init__(self, layer_1_dim, **kwargs): - super().__init__() - self.layer_1 = nn.Linear(28 * 28, layer_1_dim) - - @staticmethod - def add_model_specific_args(parent_parser): - parser = parent_parser.add_argument_group("LitMNIST") - parser.add_argument("--layer_1_dim", type=int, default=128) - return parent_parser - -.. testcode:: - - class GoodGAN(LightningModule): - def __init__(self, encoder_layers, **kwargs): - super().__init__() - self.encoder = Encoder(layers=encoder_layers) - - @staticmethod - def add_model_specific_args(parent_parser): - parser = parent_parser.add_argument_group("GoodGAN") - parser.add_argument("--encoder_layers", type=int, default=12) - return parent_parser - - -Now we can allow each model to inject the arguments it needs in the ``main.py`` - -.. code-block:: python - - def main(args): - dict_args = vars(args) - - # pick model - if args.model_name == "gan": - model = GoodGAN(**dict_args) - elif args.model_name == "mnist": - model = LitMNIST(**dict_args) - - trainer = Trainer.from_argparse_args(args) - trainer.fit(model) - - - if __name__ == "__main__": - parser = ArgumentParser() - parser = Trainer.add_argparse_args(parser) - - # figure out which model to use - parser.add_argument("--model_name", type=str, default="gan", help="gan or mnist") - - # THIS LINE IS KEY TO PULL THE MODEL NAME - temp_args, _ = parser.parse_known_args() - - # let the model add what it wants - if temp_args.model_name == "gan": - parser = GoodGAN.add_model_specific_args(parser) - elif temp_args.model_name == "mnist": - parser = LitMNIST.add_model_specific_args(parser) - - args = parser.parse_args() - - # train - main(args) - -and now we can train MNIST or the GAN using the command line interface! - -.. code-block:: bash - - $ python main.py --model_name gan --encoder_layers 24 - $ python main.py --model_name mnist --layer_1_dim 128 diff --git a/docs/_sources/common/lightning_module.rst.txt b/docs/_sources/common/lightning_module.rst.txt deleted file mode 100644 index 19bb9b0..0000000 --- a/docs/_sources/common/lightning_module.rst.txt +++ /dev/null @@ -1,1664 +0,0 @@ -.. role:: hidden - :class: hidden-section - -.. _lightning_module: - -############### -LightningModule -############### - -A :class:`~LightningModule` organizes your PyTorch code into 6 sections: - -- Computations (init). -- Train Loop (training_step) -- Validation Loop (validation_step) -- Test Loop (test_step) -- Prediction Loop (predict_step) -- Optimizers and LR Schedulers (configure_optimizers) - -| - -.. raw:: html - - - -| - -Notice a few things. - -1. It is the SAME code. -2. The PyTorch code IS NOT abstracted - just organized. -3. All the other code that's not in the :class:`~LightningModule` - has been automated for you by the Trainer. - -| - - .. code-block:: python - - net = Net() - trainer = Trainer() - trainer.fit(net) - -4. There are no ``.cuda()`` or ``.to(device)`` calls required. Lightning does these for you. - -| - - .. code-block:: python - - # don't do in Lightning - x = torch.Tensor(2, 3) - x = x.cuda() - x = x.to(device) - - # do this instead - x = x # leave it alone! - - # or to init a new tensor - new_x = torch.Tensor(2, 3) - new_x = new_x.type_as(x) - -5. When running under a distributed strategy, Lightning handles the distributed sampler for you by default. - -| - - .. code-block:: python - - # Don't do in Lightning... - data = MNIST(...) - sampler = DistributedSampler(data) - DataLoader(data, sampler=sampler) - - # do this instead - data = MNIST(...) - DataLoader(data) - -6. A :class:`~LightningModule` is a :class:`torch.nn.Module` but with added functionality. Use it as such! - -| - - .. code-block:: python - - net = Net.load_from_checkpoint(PATH) - net.freeze() - out = net(x) - -Thus, to use Lightning, you just need to organize your code which takes about 30 minutes, -(and let's be real, you probably should do anyway). - ------------- - -*************** -Starter Example -*************** - -Here are the only required methods. - -.. code-block:: python - - import pytorch_lightning as pl - import torch.nn as nn - import torch.nn.functional as F - - - class LitModel(pl.LightningModule): - def __init__(self): - super().__init__() - self.l1 = nn.Linear(28 * 28, 10) - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self(x) - loss = F.cross_entropy(y_hat, y) - return loss - - def configure_optimizers(self): - return torch.optim.Adam(self.parameters(), lr=0.02) - -Which you can train by doing: - -.. code-block:: python - - train_loader = DataLoader(MNIST(os.getcwd(), download=True, transform=transforms.ToTensor())) - trainer = pl.Trainer(max_epochs=1) - model = LitModel() - - trainer.fit(model, train_dataloaders=train_loader) - -The LightningModule has many convenience methods, but the core ones you need to know about are: - -.. list-table:: - :widths: 50 50 - :header-rows: 1 - - * - Name - - Description - * - init - - Define computations here - * - forward - - Use for inference only (separate from training_step) - * - training_step - - the complete training loop - * - validation_step - - the complete validation loop - * - test_step - - the complete test loop - * - predict_step - - the complete prediction loop - * - configure_optimizers - - define optimizers and LR schedulers - ----------- - -******** -Training -******** - -Training Loop -============= - -To activate the training loop, override the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` method. - -.. code-block:: python - - class LitClassifier(pl.LightningModule): - def __init__(self, model): - super().__init__() - self.model = model - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - return loss - -Under the hood, Lightning does the following (pseudocode): - -.. code-block:: python - - # put model in train mode and enable gradient calculation - model.train() - torch.set_grad_enabled(True) - - outs = [] - for batch_idx, batch in enumerate(train_dataloader): - loss = training_step(batch, batch_idx) - outs.append(loss.detach()) - - # clear gradients - optimizer.zero_grad() - - # backward - loss.backward() - - # update parameters - optimizer.step() - - -Train Epoch-level Metrics -========================= - -If you want to calculate epoch-level metrics and log them, use :meth:`~pytorch_lightning.core.lightning.LightningModule.log`. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - - # logs metrics for each training_step, - # and the average across the epoch, to the progress bar and logger - self.log("train_loss", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True) - return loss - -The :meth:`~pytorch_lightning.core.lightning.LightningModule.log` object automatically reduces the -requested metrics across a complete epoch and devices. Here's the pseudocode of what it does under the hood: - -.. code-block:: python - - outs = [] - for batch_idx, batch in enumerate(train_dataloader): - # forward - loss = training_step(batch, batch_idx) - outs.append(loss) - - # clear gradients - optimizer.zero_grad() - - # backward - loss.backward() - - # update parameters - optimizer.step() - - epoch_metric = torch.mean(torch.stack([x for x in outs])) - -Train Epoch-level Operations -============================ - -If you need to do something with all the outputs of each :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step`, -override the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_epoch_end` method. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - preds = ... - return {"loss": loss, "other_stuff": preds} - - - def training_epoch_end(self, training_step_outputs): - all_preds = torch.stack(training_step_outputs) - ... - -The matching pseudocode is: - -.. code-block:: python - - outs = [] - for batch_idx, batch in enumerate(train_dataloader): - # forward - loss = training_step(batch, batch_idx) - outs.append(loss) - - # clear gradients - optimizer.zero_grad() - - # backward - loss.backward() - - # update parameters - optimizer.step() - - training_epoch_end(outs) - -Training with DataParallel -========================== - -When training using a ``strategy`` that splits data from each batch across GPUs, sometimes you might -need to aggregate them on the main GPU for processing (DP, or DDP2). - -In this case, implement the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step_end` -method which will have outputs from all the devices and you can accumulate to get the effective results. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - pred = ... - return {"loss": loss, "pred": pred} - - - def training_step_end(self, batch_parts): - # predictions from each GPU - predictions = batch_parts["pred"] - # losses from each GPU - losses = batch_parts["loss"] - - gpu_0_prediction = predictions[0] - gpu_1_prediction = predictions[1] - - # do something with both outputs - return (losses[0] + losses[1]) / 2 - - - def training_epoch_end(self, training_step_outputs): - for out in training_step_outputs: - ... - -Here is the Lightning training pseudo-code for DP: - -.. code-block:: python - - outs = [] - for batch_idx, train_batch in enumerate(train_dataloader): - batches = split_batch(train_batch) - dp_outs = [] - for sub_batch in batches: - # 1 - dp_out = training_step(sub_batch, batch_idx) - dp_outs.append(dp_out) - - # 2 - out = training_step_end(dp_outs) - outs.append(out) - - # do something with the outputs for all batches - # 3 - training_epoch_end(outs) - ------------------- - -********** -Validation -********** - -Validation Loop -=============== - -To activate the validation loop while training, override the :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step` method. - -.. code-block:: python - - class LitModel(pl.LightningModule): - def validation_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - self.log("val_loss", loss) - -Under the hood, Lightning does the following (pseudocode): - -.. code-block:: python - - # ... - for batch_idx, batch in enumerate(train_dataloader): - loss = model.training_step(batch, batch_idx) - loss.backward() - # ... - - if validate_at_some_point: - # disable grads + batchnorm + dropout - torch.set_grad_enabled(False) - model.eval() - - # ----------------- VAL LOOP --------------- - for val_batch_idx, val_batch in enumerate(val_dataloader): - val_out = model.validation_step(val_batch, val_batch_idx) - # ----------------- VAL LOOP --------------- - - # enable grads + batchnorm + dropout - torch.set_grad_enabled(True) - model.train() - -You can also run just the validation loop on your validation dataloaders by overriding :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step` -and calling :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate`. - -.. code-block:: python - - model = Model() - trainer = Trainer() - trainer.validate(model) - -.. note:: - - It is recommended to validate on single device to ensure each sample/batch gets evaluated exactly once. - This is helpful to make sure benchmarking for research papers is done the right way. Otherwise, in a - multi-device setting, samples could occur duplicated when :class:`~torch.utils.data.distributed.DistributedSampler` - is used, for eg. with ``strategy="ddp"``. It replicates some samples on some devices to make sure all devices have - same batch size in case of uneven inputs. - - -Validation Epoch-level Metrics -============================== - -If you need to do something with all the outputs of each :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step`, -override the :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_epoch_end` method. Note that this method is called before :meth:`~pytorch_lightning.core.lightning.LightningModule.training_epoch_end`. - -.. code-block:: python - - def validation_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - pred = ... - return pred - - - def validation_epoch_end(self, validation_step_outputs): - all_preds = torch.stack(validation_step_outputs) - ... - -Validating with DataParallel -============================ - -When training using a ``strategy`` that splits data from each batch across GPUs, sometimes you might -need to aggregate them on the main GPU for processing (DP, or DDP2). - -In this case, implement the :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step_end` -method which will have outputs from all the devices and you can accumulate to get the effective results. - -.. code-block:: python - - def validation_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - pred = ... - return {"loss": loss, "pred": pred} - - - def validation_step_end(self, batch_parts): - # predictions from each GPU - predictions = batch_parts["pred"] - # losses from each GPU - losses = batch_parts["loss"] - - gpu_0_prediction = predictions[0] - gpu_1_prediction = predictions[1] - - # do something with both outputs - return (losses[0] + losses[1]) / 2 - - - def validation_epoch_end(self, validation_step_outputs): - for out in validation_step_outputs: - ... - -Here is the Lightning validation pseudo-code for DP: - -.. code-block:: python - - outs = [] - for batch in dataloader: - batches = split_batch(batch) - dp_outs = [] - for sub_batch in batches: - # 1 - dp_out = validation_step(sub_batch) - dp_outs.append(dp_out) - - # 2 - out = validation_step_end(dp_outs) - outs.append(out) - - # do something with the outputs for all batches - # 3 - validation_epoch_end(outs) - ----------------- - -******* -Testing -******* - -Test Loop -========= - -The process for enabling a test loop is the same as the process for enabling a validation loop. Please refer to -the section above for details. For this you need to override the :meth:`~pytorch_lightning.core.lightning.LightningModule.test_step` method. - -The only difference is that the test loop is only called when :meth:`~pytorch_lightning.trainer.trainer.Trainer.test` is used. - -.. code-block:: python - - model = Model() - trainer = Trainer() - trainer.fit(model) - - # automatically loads the best weights for you - trainer.test(model) - -There are two ways to call ``test()``: - -.. code-block:: python - - # call after training - trainer = Trainer() - trainer.fit(model) - - # automatically auto-loads the best weights from the previous run - trainer.test(dataloaders=test_dataloader) - - # or call with pretrained model - model = MyLightningModule.load_from_checkpoint(PATH) - trainer = Trainer() - trainer.test(model, dataloaders=test_dataloader) - -.. note:: - - It is recommended to validate on single device to ensure each sample/batch gets evaluated exactly once. - This is helpful to make sure benchmarking for research papers is done the right way. Otherwise, in a - multi-device setting, samples could occur duplicated when :class:`~torch.utils.data.distributed.DistributedSampler` - is used, for eg. with ``strategy="ddp"``. It replicates some samples on some devices to make sure all devices have - same batch size in case of uneven inputs. - - ----------- - -********* -Inference -********* - -Prediction Loop -=============== - -By default, the :meth:`~pytorch_lightning.core.lightning.LightningModule.predict_step` method runs the -:meth:`~pytorch_lightning.core.lightning.LightningModule.forward` method. In order to customize this behaviour, -simply override the :meth:`~pytorch_lightning.core.lightning.LightningModule.predict_step` method. - -For the example let's override ``predict_step`` and try out `Monte Carlo Dropout `_: - -.. code-block:: python - - class LitMCdropoutModel(pl.LightningModule): - def __init__(self, model, mc_iteration): - super().__init__() - self.model = model - self.dropout = nn.Dropout() - self.mc_iteration = mc_iteration - - def predict_step(self, batch, batch_idx): - # enable Monte Carlo Dropout - self.dropout.train() - - # take average of `self.mc_iteration` iterations - pred = torch.vstack([self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)]).mean(dim=0) - return pred - -Under the hood, Lightning does the following (pseudocode): - -.. code-block:: python - - # disable grads + batchnorm + dropout - torch.set_grad_enabled(False) - model.eval() - all_preds = [] - - for batch_idx, batch in enumerate(predict_dataloader): - pred = model.predict_step(batch, batch_idx) - all_preds.append(pred) - -There are two ways to call ``predict()``: - -.. code-block:: python - - # call after training - trainer = Trainer() - trainer.fit(model) - - # automatically auto-loads the best weights from the previous run - predictions = trainer.predict(dataloaders=predict_dataloader) - - # or call with pretrained model - model = MyLightningModule.load_from_checkpoint(PATH) - trainer = Trainer() - predictions = trainer.predict(model, dataloaders=test_dataloader) - -Inference in Research -===================== - -If you want to perform inference with the system, you can add a ``forward`` method to the LightningModule. - -.. note:: When using forward, you are responsible to call :func:`~torch.nn.Module.eval` and use the :func:`~torch.no_grad` context manager. - -.. code-block:: python - - class Autoencoder(pl.LightningModule): - def forward(self, x): - return self.decoder(x) - - - model = Autoencoder() - model.eval() - with torch.no_grad(): - reconstruction = model(embedding) - -The advantage of adding a forward is that in complex systems, you can do a much more involved inference procedure, -such as text generation: - -.. code-block:: python - - class Seq2Seq(pl.LightningModule): - def forward(self, x): - embeddings = self(x) - hidden_states = self.encoder(embeddings) - for h in hidden_states: - # decode - ... - return decoded - -In the case where you want to scale your inference, you should be using -:meth:`~pytorch_lightning.core.lightning.LightningModule.predict_step`. - -.. code-block:: python - - class Autoencoder(pl.LightningModule): - def forward(self, x): - return self.decoder(x) - - def predict_step(self, batch, batch_idx, dataloader_idx=0): - # this calls forward - return self(batch) - - - data_module = ... - model = Autoencoder() - trainer = Trainer(accelerator="gpu", devices=2) - trainer.predict(model, data_module) - -Inference in Production -======================= - -For cases like production, you might want to iterate different models inside a LightningModule. - -.. code-block:: python - - from torchmetrics.functional import accuracy - - - class ClassificationTask(pl.LightningModule): - def __init__(self, model): - super().__init__() - self.model = model - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - return loss - - def validation_step(self, batch, batch_idx): - loss, acc = self._shared_eval_step(batch, batch_idx) - metrics = {"val_acc": acc, "val_loss": loss} - self.log_dict(metrics) - return metrics - - def test_step(self, batch, batch_idx): - loss, acc = self._shared_eval_step(batch, batch_idx) - metrics = {"test_acc": acc, "test_loss": loss} - self.log_dict(metrics) - return metrics - - def _shared_eval_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - acc = accuracy(y_hat, y) - return loss, acc - - def predict_step(self, batch, batch_idx, dataloader_idx=0): - x, y = batch - y_hat = self.model(x) - return y_hat - - def configure_optimizers(self): - return torch.optim.Adam(self.model.parameters(), lr=0.02) - -Then pass in any arbitrary model to be fit with this task - -.. code-block:: python - - for model in [resnet50(), vgg16(), BidirectionalRNN()]: - task = ClassificationTask(model) - - trainer = Trainer(accelerator="gpu", devices=2) - trainer.fit(task, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader) - -Tasks can be arbitrarily complex such as implementing GAN training, self-supervised or even RL. - -.. code-block:: python - - class GANTask(pl.LightningModule): - def __init__(self, generator, discriminator): - super().__init__() - self.generator = generator - self.discriminator = discriminator - - ... - -When used like this, the model can be separated from the Task and thus used in production without needing to keep it in -a ``LightningModule``. - -The following example shows how you can run inference in the Python runtime: - -.. code-block:: python - - task = ClassificationTask(model) - trainer = Trainer(accelerator="gpu", devices=2) - trainer.fit(task, train_dataloader, val_dataloader) - trainer.save_checkpoint("best_model.ckpt") - - # use model after training or load weights and drop into the production system - model = ClassificationTask.load_from_checkpoint("best_model.ckpt") - x = ... - model.eval() - with torch.no_grad(): - y_hat = model(x) - -Check out :ref:`Inference in Production ` guide to learn about the possible ways to perform inference in production. - - ------------ - - -************* -Child Modules -************* - -.. include:: ../common/child_modules.rst - ------------ - -******************* -LightningModule API -******************* - - -Methods -======= - -all_gather -~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.all_gather - :noindex: - -configure_callbacks -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.configure_callbacks - :noindex: - -configure_optimizers -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.configure_optimizers - :noindex: - -forward -~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.forward - :noindex: - -freeze -~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.freeze - :noindex: - -.. _lm-log: - -log -~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.log - :noindex: - -log_dict -~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.log_dict - :noindex: - -lr_schedulers -~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.lr_schedulers - :noindex: - -manual_backward -~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.manual_backward - :noindex: - -optimizers -~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.optimizers - :noindex: - -print -~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.print - :noindex: - -predict_step -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.predict_step - :noindex: - -save_hyperparameters -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.save_hyperparameters - :noindex: - -toggle_optimizer -~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.toggle_optimizer - :noindex: - -test_step -~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.test_step - :noindex: - -test_step_end -~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.test_step_end - :noindex: - -test_epoch_end -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.test_epoch_end - :noindex: - -to_onnx -~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.to_onnx - :noindex: - -to_torchscript -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.to_torchscript - :noindex: - -training_step -~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.training_step - :noindex: - -training_step_end -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.training_step_end - :noindex: - -training_epoch_end -~~~~~~~~~~~~~~~~~~ -.. automethod:: pytorch_lightning.core.lightning.LightningModule.training_epoch_end - :noindex: - -unfreeze -~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.unfreeze - :noindex: - -untoggle_optimizer -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.untoggle_optimizer - :noindex: - -validation_step -~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.validation_step - :noindex: - -validation_step_end -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.validation_step_end - :noindex: - -validation_epoch_end -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.validation_epoch_end - :noindex: - ------------ - -Properties -========== - -These are properties available in a LightningModule. - -current_epoch -~~~~~~~~~~~~~ - -The number of epochs run. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.current_epoch == 0: - ... - -device -~~~~~~ - -The device the module is on. Use it to keep your code device agnostic. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - z = torch.rand(2, 3, device=self.device) - -global_rank -~~~~~~~~~~~ - -The ``global_rank`` is the index of the current process across all nodes and devices. -Lightning will perform some operations such as logging, weight checkpointing only when ``global_rank=0``. You -usually do not need to use this property, but it is useful to know how to access it if needed. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.global_rank == 0: - # do something only once across all the nodes - ... - -global_step -~~~~~~~~~~~ - -The number of optimizer steps taken (does not reset each epoch). -This includes multiple optimizers and TBPTT steps (if enabled). - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.logger.experiment.log_image(..., step=self.global_step) - -hparams -~~~~~~~ - -The arguments passed through ``LightningModule.__init__()`` and saved by calling -:meth:`~pytorch_lightning.core.mixins.hparams_mixin.HyperparametersMixin.save_hyperparameters` could be accessed by the ``hparams`` attribute. - -.. code-block:: python - - def __init__(self, learning_rate): - self.save_hyperparameters() - - - def configure_optimizers(self): - return Adam(self.parameters(), lr=self.hparams.learning_rate) - -logger -~~~~~~ - -The current logger being used (tensorboard or other supported logger) - -.. code-block:: python - - def training_step(self, batch, batch_idx): - # the generic logger (same no matter if tensorboard or other supported logger) - self.logger - - # the particular logger - tensorboard_logger = self.logger.experiment - -loggers -~~~~~~~ - -The list of loggers currently being used by the Trainer. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - # List of Logger objects - loggers = self.loggers - for logger in loggers: - logger.log_metrics({"foo": 1.0}) - -local_rank -~~~~~~~~~~~ - -The ``local_rank`` is the index of the current process across all the devices for the current node. -You usually do not need to use this property, but it is useful to know how to access it if needed. -For example, if using 10 machines (or nodes), the GPU at index 0 on each machine has local_rank = 0. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.local_rank == 0: - # do something only once across each node - ... - -precision -~~~~~~~~~ - -The type of precision used: - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.precision == 16: - ... - -trainer -~~~~~~~ - -Pointer to the trainer - -.. code-block:: python - - def training_step(self, batch, batch_idx): - max_steps = self.trainer.max_steps - any_flag = self.trainer.any_flag - -prepare_data_per_node -~~~~~~~~~~~~~~~~~~~~~ - -If set to ``True`` will call ``prepare_data()`` on LOCAL_RANK=0 for every node. -If set to ``False`` will only call from NODE_RANK=0, LOCAL_RANK=0. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self): - super().__init__() - self.prepare_data_per_node = True - -automatic_optimization -~~~~~~~~~~~~~~~~~~~~~~ - -When set to ``False``, Lightning does not automate the optimization process. This means you are responsible for handling -your optimizers. However, we do take care of precision and any accelerators used. - -See :ref:`manual optimization ` for details. - -.. code-block:: python - - def __init__(self): - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - opt = self.optimizers(use_pl_optimizer=True) - - loss = ... - opt.zero_grad() - self.manual_backward(loss) - opt.step() - -This is recommended only if using 2+ optimizers AND if you know how to perform the optimization procedure properly. Note -that automatic optimization can still be used with multiple optimizers by relying on the ``optimizer_idx`` parameter. -Manual optimization is most useful for research topics like reinforcement learning, sparse coding, and GAN research. - -.. code-block:: python - - def __init__(self): - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - # access your optimizers with use_pl_optimizer=False. Default is True - opt_a, opt_b = self.optimizers(use_pl_optimizer=True) - - gen_loss = ... - opt_a.zero_grad() - self.manual_backward(gen_loss) - opt_a.step() - - disc_loss = ... - opt_b.zero_grad() - self.manual_backward(disc_loss) - opt_b.step() - -example_input_array -~~~~~~~~~~~~~~~~~~~ - -Set and access example_input_array, which basically represents a single batch. - -.. code-block:: python - - def __init__(self): - self.example_input_array = ... - self.generator = ... - - - def on_train_epoch_end(self): - # generate some images using the example_input_array - gen_images = self.generator(self.example_input_array) - -truncated_bptt_steps -~~~~~~~~~~~~~~~~~~~~ - -Truncated Backpropagation Through Time (TBPTT) performs perform backpropogation every k steps of -a much longer sequence. This is made possible by passing training batches -split along the time-dimensions into splits of size k to the -``training_step``. In order to keep the same forward propagation behavior, all -hidden states should be kept in-between each time-dimension split. - - -If this is enabled, your batches will automatically get truncated -and the Trainer will apply Truncated Backprop to it. - -(`Williams et al. "An efficient gradient-based algorithm for on-line training of -recurrent network trajectories." -`_) - -`Tutorial `_ - -.. testcode:: python - - from pytorch_lightning import LightningModule - - - class MyModel(LightningModule): - def __init__(self, input_size, hidden_size, num_layers): - super().__init__() - # batch_first has to be set to True - self.lstm = nn.LSTM( - input_size=input_size, - hidden_size=hidden_size, - num_layers=num_layers, - batch_first=True, - ) - - ... - - # Important: This property activates truncated backpropagation through time - # Setting this value to 2 splits the batch into sequences of size 2 - self.truncated_bptt_steps = 2 - - # Truncated back-propagation through time - def training_step(self, batch, batch_idx, hiddens): - x, y = batch - - # the training step must be updated to accept a ``hiddens`` argument - # hiddens are the hiddens from the previous truncated backprop step - out, hiddens = self.lstm(x, hiddens) - - ... - - return {"loss": ..., "hiddens": hiddens} - -Lightning takes care of splitting your batch along the time-dimension. It is -assumed to be the second dimension of your batches. Therefore, in the -example above, we have set ``batch_first=True``. - -.. code-block:: python - - # we use the second as the time dimension - # (batch, time, ...) - sub_batch = batch[0, 0:t, ...] - -To modify how the batch is split, -override the :meth:`pytorch_lightning.core.lightning.LightningModule.tbptt_split_batch` method: - -.. testcode:: python - - class LitMNIST(LightningModule): - def tbptt_split_batch(self, batch, split_size): - # do your own splitting on the batch - return splits - --------------- - -.. _lightning_hooks: - -Hooks -===== - -This is the pseudocode to describe the structure of :meth:`~pytorch_lightning.trainer.Trainer.fit`. -The inputs and outputs of each function are not represented for simplicity. Please check each function's API reference -for more information. - -.. code-block:: python - - def fit(self): - if global_rank == 0: - # prepare data is called on GLOBAL_ZERO only - prepare_data() - - configure_callbacks() - - with parallel(devices): - # devices can be GPUs, TPUs, ... - train_on_device(model) - - - def train_on_device(model): - # called PER DEVICE - on_fit_start() - setup("fit") - configure_optimizers() - - # the sanity check runs here - - on_train_start() - for epoch in epochs: - fit_loop() - on_train_end() - - on_fit_end() - teardown("fit") - - - def fit_loop(): - on_train_epoch_start() - - for batch in train_dataloader(): - on_train_batch_start() - - on_before_batch_transfer() - transfer_batch_to_device() - on_after_batch_transfer() - - training_step() - - on_before_zero_grad() - optimizer_zero_grad() - - on_before_backward() - backward() - on_after_backward() - - on_before_optimizer_step() - configure_gradient_clipping() - optimizer_step() - - on_train_batch_end() - - if should_check_val: - val_loop() - # end training epoch - training_epoch_end() - - on_train_epoch_end() - - - def val_loop(): - on_validation_model_eval() # calls `model.eval()` - torch.set_grad_enabled(False) - - on_validation_start() - on_validation_epoch_start() - - val_outs = [] - for batch_idx, batch in enumerate(val_dataloader()): - on_validation_batch_start(batch, batch_idx) - - batch = on_before_batch_transfer(batch) - batch = transfer_batch_to_device(batch) - batch = on_after_batch_transfer(batch) - - out = validation_step(batch, batch_idx) - - on_validation_batch_end(batch, batch_idx) - val_outs.append(out) - - validation_epoch_end(val_outs) - - on_validation_epoch_end() - on_validation_end() - - # set up for train - on_validation_model_train() # calls `model.train()` - torch.set_grad_enabled(True) - -backward -~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.backward - :noindex: - -on_before_backward -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_before_backward - :noindex: - -on_after_backward -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_after_backward - :noindex: - -on_before_zero_grad -~~~~~~~~~~~~~~~~~~~ -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_before_zero_grad - :noindex: - -on_fit_start -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_fit_start - :noindex: - -on_fit_end -~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_fit_end - :noindex: - - -on_load_checkpoint -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_load_checkpoint - :noindex: - -on_save_checkpoint -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_save_checkpoint - :noindex: - -load_from_checkpoint -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.load_from_checkpoint - :noindex: - -on_hpc_save -~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_hpc_save - :noindex: - -on_hpc_load -~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_hpc_load - :noindex: - -on_train_start -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_start - :noindex: - -on_train_end -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_end - :noindex: - -on_validation_start -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_start - :noindex: - -on_validation_end -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_end - :noindex: - -on_test_batch_start -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_batch_start - :noindex: - -on_test_batch_end -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_batch_end - :noindex: - -on_test_epoch_start -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_epoch_start - :noindex: - -on_test_epoch_end -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_epoch_end - :noindex: - -on_test_start -~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_start - :noindex: - -on_test_end -~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_end - :noindex: - -on_predict_batch_start -~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_batch_start - :noindex: - -on_predict_batch_end -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_batch_end - :noindex: - -on_predict_epoch_start -~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_epoch_start - :noindex: - -on_predict_epoch_end -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_epoch_end - :noindex: - -on_predict_start -~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_start - :noindex: - -on_predict_end -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_end - :noindex: - -on_train_batch_start -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_batch_start - :noindex: - -on_train_batch_end -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_batch_end - :noindex: - -on_train_epoch_start -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_epoch_start - :noindex: - -on_train_epoch_end -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_epoch_end - :noindex: - -on_validation_batch_start -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_batch_start - :noindex: - -on_validation_batch_end -~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_batch_end - :noindex: - -on_validation_epoch_start -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_epoch_start - :noindex: - -on_validation_epoch_end -~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_epoch_end - :noindex: - -on_post_move_to_device -~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_post_move_to_device - :noindex: - -configure_sharded_model -~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.configure_sharded_model - :noindex: - -on_validation_model_eval -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_model_eval - :noindex: - -on_validation_model_train -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_model_train - :noindex: - -on_test_model_eval -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_model_eval - :noindex: - -on_test_model_train -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_model_train - :noindex: - -on_before_optimizer_step -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_before_optimizer_step - :noindex: - -configure_gradient_clipping -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.configure_gradient_clipping - :noindex: - -optimizer_step -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.optimizer_step - :noindex: - -optimizer_zero_grad -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.optimizer_zero_grad - :noindex: - -prepare_data -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.prepare_data - :noindex: - -setup -~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.setup - :noindex: - -tbptt_split_batch -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.tbptt_split_batch - :noindex: - -teardown -~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.teardown - :noindex: - -train_dataloader -~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.train_dataloader - :noindex: - -val_dataloader -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.val_dataloader - :noindex: - -test_dataloader -~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.test_dataloader - :noindex: - -predict_dataloader -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.predict_dataloader - :noindex: - -on_train_dataloader -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_dataloader - :noindex: - -on_val_dataloader -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_val_dataloader - :noindex: - -on_test_dataloader -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_dataloader - :noindex: - -on_predict_dataloader -~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_dataloader - :noindex: - -transfer_batch_to_device -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.transfer_batch_to_device - :noindex: - -on_before_batch_transfer -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_before_batch_transfer - :noindex: - -on_after_batch_transfer -~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_after_batch_transfer - :noindex: - -add_to_queue -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.add_to_queue - :noindex: - -get_from_queue -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.get_from_queue - :noindex: diff --git a/docs/_sources/common/optimization.rst.txt b/docs/_sources/common/optimization.rst.txt deleted file mode 100644 index e7e9e12..0000000 --- a/docs/_sources/common/optimization.rst.txt +++ /dev/null @@ -1,327 +0,0 @@ -:orphan: - -.. _optimization: - -############ -Optimization -############ - -Lightning offers two modes for managing the optimization process: - -- Manual Optimization -- Automatic Optimization - -For the majority of research cases, **automatic optimization** will do the right thing for you and it is what most -users should use. - -For advanced/expert users who want to do esoteric optimization schedules or techniques, use **manual optimization**. - -.. _manual_optimization: - ----- - -.. include:: ../model/manual_optimization.rst - ------ - -********************** -Automatic Optimization -********************** - -With Lightning, most users don't have to think about when to call ``.zero_grad()``, ``.backward()`` and ``.step()`` -since Lightning automates that for you. - -Under the hood, Lightning does the following: - -.. code-block:: python - - for epoch in epochs: - for batch in data: - - def closure(): - loss = model.training_step(batch, batch_idx, ...) - optimizer.zero_grad() - loss.backward() - return loss - - optimizer.step(closure) - - lr_scheduler.step() - -In the case of multiple optimizers, Lightning does the following: - -.. code-block:: python - - for epoch in epochs: - for batch in data: - for opt in optimizers: - - def closure(): - loss = model.training_step(batch, batch_idx, optimizer_idx) - opt.zero_grad() - loss.backward() - return loss - - opt.step(closure) - - for lr_scheduler in lr_schedulers: - lr_scheduler.step() - -As can be seen in the code snippet above, Lightning defines a closure with ``training_step()``, ``optimizer.zero_grad()`` -and ``loss.backward()`` for the optimization. This mechanism is in place to support optimizers which operate on the -output of the closure (e.g. the loss) or need to call the closure several times (e.g. :class:`~torch.optim.LBFGS`). - -.. warning:: - - Before v1.2.2, Lightning internally calls ``backward``, ``step`` and ``zero_grad`` in the order. - From v1.2.2, the order is changed to ``zero_grad``, ``backward`` and ``step``. - - -Gradient Accumulation -===================== - -.. include:: ../common/gradient_accumulation.rst - - -Use Multiple Optimizers (like GANs) -=================================== - -To use multiple optimizers (optionally with learning rate schedulers), return two or more optimizers from -:meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`. - -.. testcode:: python - - # two optimizers, no schedulers - def configure_optimizers(self): - return Adam(...), SGD(...) - - - # two optimizers, one scheduler for adam only - def configure_optimizers(self): - opt1 = Adam(...) - opt2 = SGD(...) - optimizers = [opt1, opt2] - lr_schedulers = {"scheduler": ReduceLROnPlateau(opt1, ...), "monitor": "metric_to_track"} - return optimizers, lr_schedulers - - - # two optimizers, two schedulers - def configure_optimizers(self): - opt1 = Adam(...) - opt2 = SGD(...) - return [opt1, opt2], [StepLR(opt1, ...), OneCycleLR(opt2, ...)] - -Under the hood, Lightning will call each optimizer sequentially: - -.. code-block:: python - - for epoch in epochs: - for batch in data: - for opt in optimizers: - loss = train_step(batch, batch_idx, optimizer_idx) - opt.zero_grad() - loss.backward() - opt.step() - - for lr_scheduler in lr_schedulers: - lr_scheduler.step() - - -Step Optimizeres at Arbitrary Intervals -======================================= - -To do more interesting things with your optimizers such as learning rate warm-up or odd scheduling, -override the :meth:`~pytorch_lightning.core.lightning.LightningModule.optimizer_step` function. - -.. warning:: - If you are overriding this method, make sure that you pass the ``optimizer_closure`` parameter to - ``optimizer.step()`` function as shown in the examples because ``training_step()``, ``optimizer.zero_grad()``, - ``loss.backward()`` are called in the closure function. - -For example, here step optimizer A every batch and optimizer B every 2 batches. - -.. testcode:: python - - # Alternating schedule for optimizer steps (e.g. GANs) - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu=False, - using_native_amp=False, - using_lbfgs=False, - ): - # update generator every step - if optimizer_idx == 0: - optimizer.step(closure=optimizer_closure) - - # update discriminator every 2 steps - if optimizer_idx == 1: - if (batch_idx + 1) % 2 == 0: - # the closure (which includes the `training_step`) will be executed by `optimizer.step` - optimizer.step(closure=optimizer_closure) - else: - # call the closure by itself to run `training_step` + `backward` without an optimizer step - optimizer_closure() - - # ... - # add as many optimizers as you want - -Here we add a manual learning rate warm-up without an lr scheduler. - -.. testcode:: python - - # learning rate warm-up - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu=False, - using_native_amp=False, - using_lbfgs=False, - ): - # update params - optimizer.step(closure=optimizer_closure) - - # skip the first 500 steps - if self.trainer.global_step < 500: - lr_scale = min(1.0, float(self.trainer.global_step + 1) / 500.0) - for pg in optimizer.param_groups: - pg["lr"] = lr_scale * self.hparams.learning_rate - - -Access your Own Optimizer -========================= - -The provided ``optimizer`` is a :class:`~pytorch_lightning.core.optimizer.LightningOptimizer` object wrapping your own optimizer -configured in your :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`. -You can access your own optimizer with ``optimizer.optimizer``. However, if you use your own optimizer -to perform a step, Lightning won't be able to support accelerators, precision and profiling for you. - -.. testcode:: python - - # function hook in LightningModule - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu=False, - using_native_amp=False, - using_lbfgs=False, - ): - optimizer.step(closure=optimizer_closure) - - - # `optimizer` is a `LightningOptimizer` wrapping the optimizer. - # To access it, do the following. - # However, it won't work on TPU, AMP, etc... - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu=False, - using_native_amp=False, - using_lbfgs=False, - ): - optimizer = optimizer.optimizer - optimizer.step(closure=optimizer_closure) - ------ - - -Bring your own Custom Learning Rate Schedulers -============================================== - -Lightning allows using custom learning rate schedulers that aren't available in `PyTorch natively `_. -One good example is `Timm Schedulers `_. When using custom learning rate schedulers -relying on a different API from Native PyTorch ones, you should override the :meth:`~pytorch_lightning.core.lightning.LightningModule.lr_scheduler_step` with your desired logic. -If you are using native PyTorch schedulers, there is no need to override this hook since Lightning will handle it automatically by default. - -.. code-block:: python - - from timm.scheduler import TanhLRScheduler - - - def configure_optimizers(self): - optimizer = ... - scheduler = TanhLRScheduler(optimizer, ...) - return [optimizer], [{"scheduler": scheduler, "interval": "epoch"}] - - - def lr_scheduler_step(self, scheduler, optimizer_idx, metric): - scheduler.step(epoch=self.current_epoch) # timm's scheduler need the epoch value - - -.. _configure_gradient_clipping: - -Configure Gradient Clipping -=========================== - -To configure custom gradient clipping, consider overriding -the :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_gradient_clipping` method. -Attributes ``gradient_clip_val`` and ``gradient_clip_algorithm`` from Trainer will be passed in the -respective arguments here and Lightning will handle gradient clipping for you. In case you want to set -different values for your arguments of your choice and let Lightning handle the gradient clipping, you can -use the inbuilt :meth:`~pytorch_lightning.core.lightning.LightningModule.clip_gradients` method and pass -the arguments along with your optimizer. - -.. warning:: - Make sure to not override :meth:`~pytorch_lightning.core.lightning.LightningModule.clip_gradients` - method. If you want to customize gradient clipping, consider using - :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_gradient_clipping` method. - -For example, here we will apply gradient clipping only to the gradients associated with optimizer A. - -.. testcode:: python - - def configure_gradient_clipping(self, optimizer, optimizer_idx, gradient_clip_val, gradient_clip_algorithm): - if optimizer_idx == 0: - # Lightning will handle the gradient clipping - self.clip_gradients( - optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm - ) - -Here we configure gradient clipping differently for optimizer B. - -.. testcode:: python - - def configure_gradient_clipping(self, optimizer, optimizer_idx, gradient_clip_val, gradient_clip_algorithm): - if optimizer_idx == 0: - # Lightning will handle the gradient clipping - self.clip_gradients( - optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm - ) - elif optimizer_idx == 1: - self.clip_gradients( - optimizer, gradient_clip_val=gradient_clip_val * 2, gradient_clip_algorithm=gradient_clip_algorithm - ) - - -Total Stepping Batches -====================== - -You can use built-in trainer property :paramref:`~pytorch_lightning.trainer.trainer.Trainer.estimated_stepping_batches` to compute -total number of stepping batches for the complete training. The property is computed considering gradient accumulation factor and -distributed setting into consideration so you don't have to derive it manually. One good example where this can be helpful is while using -:class:`~torch.optim.lr_scheduler.OneCycleLR` scheduler, which requires pre-computed ``total_steps`` during initialization. - -.. code-block:: python - - def configure_optimizers(self): - optimizer = ... - scheduler = torch.optim.lr_scheduler.OneCycleLR( - optimizer, max_lr=1e-3, total_steps=self.trainer.estimated_stepping_batches - ) - return [optimizer], [scheduler] diff --git a/docs/_sources/common/precision.rst.txt b/docs/_sources/common/precision.rst.txt deleted file mode 100644 index 15fcdf0..0000000 --- a/docs/_sources/common/precision.rst.txt +++ /dev/null @@ -1,43 +0,0 @@ -:orphan: - -.. _precision: - -############### -N-Bit Precision -############### - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Enable your models to train faster and save memory with different floating-point precision settings. - :col_css: col-md-4 - :button_link: precision_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Enable state-of-the-art scaling with advanced mix-precision settings. - :col_css: col-md-4 - :button_link: precision_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Expert - :description: Create new precision techniques and enable them through Lightning. - :col_css: col-md-4 - :button_link: precision_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/common/precision_basic.rst.txt b/docs/_sources/common/precision_basic.rst.txt deleted file mode 100644 index 3cc0b3a..0000000 --- a/docs/_sources/common/precision_basic.rst.txt +++ /dev/null @@ -1,92 +0,0 @@ -:orphan: - -.. _precision_basic: - -####################### -N-Bit Precision (Basic) -####################### -**Audience:** Users looking to train models faster and consume less memory. - ----- - -If you're looking to run models faster or consume less memory, consider tweaking the precision settings of your models. - -Lower precision, such as 16-bit floating-point, requires less memory and enables training and deploying larger models. -Higher precision, such as the 64-bit floating-point, can be used for highly sensitive use-cases. - ----- - -**************** -16-bit Precision -**************** - -Use 16-bit precision to cut your memory consumption in half so that you can train and deploy larger models. If your GPUs are [`Tensor Core `_] GPUs, you can also get a ~3x speed improvement. Half precision can sometimes lead to unstable training. - -.. code:: - - Trainer(precision=16) - ----- - -**************** -32-bit Precision -**************** - -32-bit precision is the default used across all models and research. This precision is known to be stable in contrast to lower precision settings. - -.. testcode:: - - Trainer(precision=32) - ----- - -**************** -64-bit Precision -**************** - -For certain scientific computations, 64-bit precision enables more accurate models. However, doubling the precision from 32 to 64 bit also doubles the memory requirements. - -.. testcode:: - - Trainer(precision=64) - -.. note:: - - Since in deep learning, memory is always a bottleneck, especially when dealing with a large volume of data and with limited resources. - It is recommended using single precision for better speed. Although you can still use it if you want for your particular use-case. - ----- - -******************************** -Precision support by accelerator -******************************** - -.. list-table:: Precision with Accelerators - :widths: 20 20 20 20 20 - :header-rows: 1 - - * - Precision - - CPU - - GPU - - TPU - - IPU - * - 16 - - No - - Yes - - No - - Yes - * - BFloat16 - - Yes - - Yes - - Yes - - No - * - 32 - - Yes - - Yes - - Yes - - Yes - * - 64 - - Yes - - Yes - - No - - No diff --git a/docs/_sources/common/precision_expert.rst.txt b/docs/_sources/common/precision_expert.rst.txt deleted file mode 100644 index 34bc955..0000000 --- a/docs/_sources/common/precision_expert.rst.txt +++ /dev/null @@ -1,28 +0,0 @@ -:orphan: - -.. _precision_expert: - -######################## -N-Bit Precision (Expert) -######################## -**Audience:** Researchers looking to integrate their new precision techniques into Lightning. - - -***************** -Precision Plugins -***************** - -You can also customize and pass your own Precision Plugin by subclassing the :class:`~pytorch_lightning.plugins.precision.precision_plugin.PrecisionPlugin` class. - -- Perform pre and post backward/optimizer step operations such as scaling gradients. -- Provide context managers for forward, training_step, etc. - -.. code-block:: python - - class CustomPrecisionPlugin(PrecisionPlugin): - precision = 16 - - ... - - - trainer = Trainer(plugins=[CustomPrecisionPlugin()]) diff --git a/docs/_sources/common/precision_intermediate.rst.txt b/docs/_sources/common/precision_intermediate.rst.txt deleted file mode 100644 index 9ed4c75..0000000 --- a/docs/_sources/common/precision_intermediate.rst.txt +++ /dev/null @@ -1,143 +0,0 @@ -:orphan: - -.. _precision_intermediate: - -############################## -N-Bit Precision (Intermediate) -############################## -**Audience:** Users looking to scale larger models or take advantage of optimized accelerators. - ----- - -************************ -What is Mixed Precision? -************************ - -PyTorch, like most deep learning frameworks, trains on 32-bit floating-point (FP32) arithmetic by default. However, many deep learning models do not require this to reach complete accuracy. By conducting -operations in half-precision format while keeping minimum information in single-precision to maintain as much information as possible in crucial areas of the network, mixed precision training delivers -significant computational speedup. Switching to mixed precision has resulted in considerable training speedups since the introduction of Tensor Cores in the Volta and Turing architectures. It combines -FP32 and lower-bit floating-points (such as FP16) to reduce memory footprint and increase performance during model training and evaluation. It accomplishes this by recognizing the steps that require -complete accuracy and employing a 32-bit floating-point for those steps only, while using a 16-bit floating-point for the rest. When compared to complete precision training, mixed precision training -delivers all of these benefits while ensuring that no task-specific accuracy is lost. [`2 `_]. - -.. note:: - - In some cases, it is essential to remain in FP32 for numerical stability, so keep this in mind when using mixed precision. - For example, when running scatter operations during the forward (such as torchpoint3d), computation must remain in FP32. - -.. warning:: - - Do not cast anything to other dtypes manually using ``torch.autocast`` or ``tensor.half()`` when using native precision because - this can bring instability. - - .. code-block:: python - - class LitModel(LightningModule): - def training_step(self, batch, batch_idx): - outs = self(batch) - - a_float32 = torch.rand((8, 8), device=self.device, dtype=self.dtype) - b_float32 = torch.rand((8, 4), device=self.device, dtype=self.dtype) - - # casting to float16 manually - with torch.autocast(device_type=self.device.type): - c_float16 = torch.mm(a_float32, b_float32) - target = self.layer(c_float16.flatten()[None]) - - # here outs is of type float32 and target is of type float16 - loss = torch.mm(target @ outs).float() - return loss - - - trainer = Trainer(accelerator="gpu", devices=1, precision=32) - ----- - -******************** -FP16 Mixed Precision -******************** - -In most cases, mixed precision uses FP16. Supported `PyTorch operations `__ automatically run in FP16, saving memory and improving throughput on the supported accelerators. - - -.. note:: - - When using TPUs, setting ``precision=16`` will enable bfloat16, the only supported half precision type on TPUs. - -.. testcode:: - :skipif: not torch.cuda.is_available() - - Trainer(accelerator="gpu", devices=1, precision=16) - - -PyTorch Native --------------- - -PyTorch 1.6 release introduced mixed precision functionality into their core as the AMP package, `torch.cuda.amp `__. It is more flexible and intuitive compared to `NVIDIA APEX `__. -Since computation happens in FP16, there is a chance of numerical instability during training. This is handled internally by a dynamic grad scaler which skips invalid steps and adjusts the scaler to ensure subsequent steps fall within a finite range. For more information `see the autocast docs `__. -Lightning uses native amp by default with ``precision=16|"bf16"``. You can also set it using: - -.. testcode:: - - Trainer(precision=16, amp_backend="native") - - -NVIDIA APEX ------------ - -.. warning:: - - We strongly recommend using the above native mixed precision rather than NVIDIA APEX unless you require more refined control. - -`NVIDIA APEX `__ offers additional flexibility in setting mixed precision. This can be useful when trying out different precision configurations, such as keeping most of your weights in FP16 and running computation in FP16. - -.. testcode:: - :skipif: not _APEX_AVAILABLE or not torch.cuda.is_available() - - Trainer(accelerator="gpu", devices=1, amp_backend="apex", precision=16) - -Set the `NVIDIA optimization level `__ via the trainer. - -.. testcode:: - :skipif: not _APEX_AVAILABLE or not torch.cuda.is_available() - - Trainer(accelerator="gpu", devices=1, amp_backend="apex", amp_level="O2", precision=16) - ----- - -************************ -BFloat16 Mixed Precision -************************ - -.. warning:: - - BFloat16 requires PyTorch 1.10 or later and is only supported with PyTorch Native AMP. - - BFloat16 is also experimental and may not provide significant speedups or memory improvements, offering better numerical stability. - - Do note for GPUs, the most significant benefits require `Ampere `__ based GPUs, such as A100s or 3090s. - -BFloat16 Mixed precision is similar to FP16 mixed precision, however, it maintains more of the "dynamic range" that FP32 offers. This means it is able to improve numerical stability than FP16 mixed precision. For more information, see `this TPU performance blogpost `__. - -Under the hood, we use `torch.autocast `__ with the dtype set to ``bfloat16``, with no gradient scaling. - -.. testcode:: - :skipif: not _TORCH_GREATER_EQUAL_1_10 or not torch.cuda.is_available() - - Trainer(accelerator="gpu", devices=1, precision="bf16") - -It is also possible to use BFloat16 mixed precision on the CPU, relying on MKLDNN under the hood. - -.. testcode:: - :skipif: not _TORCH_GREATER_EQUAL_1_10 - - Trainer(precision="bf16") - ----- - -*************** -8-bit Optimizer -*************** - -It is possible to further reduce the precision using third-party libraries like `bitsandbytes `_. Although, -Lightning doesn't support it out of the box yet but you can still use it by configuring it in your LightningModule and setting ``Trainer(precision=32)``. diff --git a/docs/_sources/common/progress_bar.rst.txt b/docs/_sources/common/progress_bar.rst.txt deleted file mode 100644 index d00c716..0000000 --- a/docs/_sources/common/progress_bar.rst.txt +++ /dev/null @@ -1,138 +0,0 @@ -.. testsetup:: * - - from pytorch_lightning.trainer.trainer import Trainer - -.. _progress_bar: - - -Customize the progress bar -========================== - -Lightning supports two different types of progress bars (`tqdm `_ and `rich `_). :class:`~pytorch_lightning.callbacks.TQDMProgressBar` is used by default, -but you can override it by passing a custom :class:`~pytorch_lightning.callbacks.TQDMProgressBar` or :class:`~pytorch_lightning.callbacks.RichProgressBar` to the ``callbacks`` argument of the :class:`~pytorch_lightning.trainer.trainer.Trainer`. - -You could also use the :class:`~pytorch_lightning.callbacks.ProgressBarBase` class to implement your own progress bar. - -------------- - -TQDMProgressBar ---------------- - -The :class:`~pytorch_lightning.callbacks.TQDMProgressBar` uses the `tqdm `_ library internally and is the default progress bar used by Lightning. -It prints to ``stdout`` and shows up to four different bars: - -- **sanity check progress:** the progress during the sanity check run -- **main progress:** shows training + validation progress combined. It also accounts for multiple validation runs during training when :paramref:`~pytorch_lightning.trainer.trainer.Trainer.val_check_interval` is used. -- **validation progress:** only visible during validation; shows total progress over all validation datasets. -- **test progress:** only active when testing; shows total progress over all test datasets. - -For infinite datasets, the progress bar never ends. - -You can update ``refresh_rate`` (rate (number of batches) at which the progress bar get updated) for :class:`~pytorch_lightning.callbacks.TQDMProgressBar` by: - -.. code-block:: python - - from pytorch_lightning.callbacks import TQDMProgressBar - - trainer = Trainer(callbacks=[TQDMProgressBar(refresh_rate=10)]) - -If you want to customize the default :class:`~pytorch_lightning.callbacks.TQDMProgressBar` used by Lightning, you can override -specific methods of the callback class and pass your custom implementation to the :class:`~pytorch_lightning.trainer.trainer.Trainer`. - -.. code-block:: python - - class LitProgressBar(TQDMProgressBar): - def init_validation_tqdm(self): - bar = super().init_validation_tqdm() - bar.set_description("running validation...") - return bar - - - trainer = Trainer(callbacks=[LitProgressBar()]) - -.. seealso:: - - :class:`~pytorch_lightning.callbacks.TQDMProgressBar` docs. - - `tqdm library `__ - ----------------- - -RichProgressBar ---------------- - -`Rich `_ is a Python library for rich text and beautiful formatting in the terminal. -To use the :class:`~pytorch_lightning.callbacks.RichProgressBar` as your progress bar, first install the package: - -.. code-block:: bash - - pip install rich - -Then configure the callback and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.callbacks import RichProgressBar - - trainer = Trainer(callbacks=[RichProgressBar()]) - -Customize the theme for your :class:`~pytorch_lightning.callbacks.RichProgressBar` like this: - -.. code-block:: python - - from pytorch_lightning.callbacks import RichProgressBar - from pytorch_lightning.callbacks.progress.rich_progress import RichProgressBarTheme - - # create your own theme! - progress_bar = RichProgressBar( - theme=RichProgressBarTheme( - description="green_yellow", - progress_bar="green1", - progress_bar_finished="green1", - progress_bar_pulse="#6206E0", - batch_progress="green_yellow", - time="grey82", - processing_speed="grey82", - metrics="grey82", - ) - ) - - trainer = Trainer(callbacks=progress_bar) - -You can customize the components used within :class:`~pytorch_lightning.callbacks.RichProgressBar` with ease by overriding the -:func:`~pytorch_lightning.callbacks.RichProgressBar.configure_columns` method. - -.. code-block:: python - - from rich.progress import TextColumn - - custom_column = TextColumn("[progress.description]Custom Rich Progress Bar!") - - - class CustomRichProgressBar(RichProgressBar): - def configure_columns(self, trainer): - return [custom_column] - - - progress_bar = CustomRichProgressBar() - -If you wish for a new progress bar to be displayed at the end of every epoch, you should enable -:paramref:`RichProgressBar.leave ` by passing ``True`` - -.. code-block:: python - - from pytorch_lightning.callbacks import RichProgressBar - - trainer = Trainer(callbacks=[RichProgressBar(leave=True)]) - -.. seealso:: - - :class:`~pytorch_lightning.callbacks.RichProgressBar` docs. - - :class:`~pytorch_lightning.callbacks.RichModelSummary` docs to customize the model summary table. - - `Rich library `__. - - -.. note:: - - Progress bar is automatically enabled with the Trainer, and to disable it, one should do this: - - .. code-block:: python - - trainer = Trainer(enable_progress_bar=False) diff --git a/docs/_sources/common/remote_fs.rst.txt b/docs/_sources/common/remote_fs.rst.txt deleted file mode 100644 index 29a4fe7..0000000 --- a/docs/_sources/common/remote_fs.rst.txt +++ /dev/null @@ -1,57 +0,0 @@ -.. _remote_fs: - -################## -Remote Filesystems -################## - -PyTorch Lightning enables working with data from a variety of filesystems, including local filesystems and several cloud storage providers such as -`S3 `_ on `AWS `_, `GCS `_ on `Google Cloud `_, -or `ADL `_ on `Azure `_. - -This applies to saving and writing checkpoints, as well as for logging. -Working with different filesystems can be accomplished by appending a protocol like "s3:/" to file paths for writing and reading data. - -.. code-block:: python - - # `default_root_dir` is the default path used for logs and checkpoints - trainer = Trainer(default_root_dir="s3://my_bucket/data/") - trainer.fit(model) - -You could pass custom paths to loggers for logging data. - -.. code-block:: python - - from pytorch_lightning.loggers import TensorBoardLogger - - logger = TensorBoardLogger(save_dir="s3://my_bucket/logs/") - - trainer = Trainer(logger=logger) - trainer.fit(model) - -Additionally, you could also resume training with a checkpoint stored at a remote filesystem. - -.. code-block:: python - - trainer = Trainer(default_root_dir=tmpdir, max_steps=3) - trainer.fit(model, ckpt_path="s3://my_bucket/ckpts/classifier.ckpt") - -PyTorch Lightning uses `fsspec `_ internally to handle all filesystem operations. - -The most common filesystems supported by Lightning are: - -* Local filesystem: ``file://`` - It's the default and doesn't need any protocol to be used. It's installed by default in Lightning. -* Amazon S3: ``s3://`` - Amazon S3 remote binary store, using the library `s3fs `__. Run ``pip install fsspec[s3]`` to install it. -* Google Cloud Storage: ``gcs://`` or ``gs://`` - Google Cloud Storage, using `gcsfs `__. Run ``pip install fsspec[gcs]`` to install it. -* Microsoft Azure Storage: ``adl://``, ``abfs://`` or ``az://`` - Microsoft Azure Storage, using `adlfs `__. Run ``pip install fsspec[adl]`` to install it. -* Hadoop File System: ``hdfs://`` - Hadoop Distributed File System. This uses `PyArrow `__ as the backend. Run ``pip install fsspec[hdfs]`` to install it. - -You could learn more about the available filesystems with: - -.. code-block:: python - - from fsspec.registry import known_implementations - - print(known_implementations) - - -You could also look into :ref:`CheckpointIO Plugin ` for more details on how to customize saving and loading checkpoints. diff --git a/docs/_sources/common/trainer.rst.txt b/docs/_sources/common/trainer.rst.txt deleted file mode 100644 index 848ac8a..0000000 --- a/docs/_sources/common/trainer.rst.txt +++ /dev/null @@ -1,1832 +0,0 @@ -.. role:: hidden - :class: hidden-section - -.. testsetup:: * - - import os - from pytorch_lightning.trainer.trainer import Trainer - from pytorch_lightning.core.lightning import LightningModule - from pytorch_lightning.utilities.seed import seed_everything - -.. _trainer: - -Trainer -======= - -Once you've organized your PyTorch code into a LightningModule, -the Trainer automates everything else. - -.. raw:: html - - - -| - -This abstraction achieves the following: - -1. You maintain control over all aspects via PyTorch code without an added abstraction. - -2. The trainer uses best practices embedded by contributors and users - from top AI labs such as Facebook AI Research, NYU, MIT, Stanford, etc... - -3. The trainer allows overriding any key part that you don't want automated. - -| - ------------ - -Basic use ---------- - -This is the basic use of the trainer: - -.. code-block:: python - - model = MyLightningModule() - - trainer = Trainer() - trainer.fit(model, train_dataloader, val_dataloader) - --------- - -Under the hood --------------- -Under the hood, the Lightning Trainer handles the training loop details for you, some examples include: - -- Automatically enabling/disabling grads -- Running the training, validation and test dataloaders -- Calling the Callbacks at the appropriate times -- Putting batches and computations on the correct devices - -Here's the pseudocode for what the trainer does under the hood (showing the train loop only) - -.. code-block:: python - - # put model in train mode - model.train() - torch.set_grad_enabled(True) - - losses = [] - for batch in train_dataloader: - # calls hooks like this one - on_train_batch_start() - - # train step - loss = training_step(batch) - - # clear gradients - optimizer.zero_grad() - - # backward - loss.backward() - - # update parameters - optimizer.step() - - losses.append(loss) - - --------- - -Trainer in Python scripts -------------------------- -In Python scripts, it's recommended you use a main function to call the Trainer. - -.. code-block:: python - - from argparse import ArgumentParser - - - def main(hparams): - model = LightningModule() - trainer = Trainer(accelerator=hparams.accelerator, devices=hparams.devices) - trainer.fit(model) - - - if __name__ == "__main__": - parser = ArgumentParser() - parser.add_argument("--accelerator", default=None) - parser.add_argument("--devices", default=None) - args = parser.parse_args() - - main(args) - -So you can run it like so: - -.. code-block:: bash - - python main.py --accelerator 'gpu' --devices 2 - -.. note:: - - Pro-tip: You don't need to define all flags manually. Lightning can add them automatically - -.. code-block:: python - - from argparse import ArgumentParser - - - def main(args): - model = LightningModule() - trainer = Trainer.from_argparse_args(args) - trainer.fit(model) - - - if __name__ == "__main__": - parser = ArgumentParser() - parser = Trainer.add_argparse_args(parser) - args = parser.parse_args() - - main(args) - -So you can run it like so: - -.. code-block:: bash - - python main.py --accelerator 'gpu' --devices 2 --max_steps 10 --limit_train_batches 10 --any_trainer_arg x - -.. note:: - If you want to stop a training run early, you can press "Ctrl + C" on your keyboard. - The trainer will catch the ``KeyboardInterrupt`` and attempt a graceful shutdown, including - running accelerator callback ``on_train_end`` to clean up memory. The trainer object will also set - an attribute ``interrupted`` to ``True`` in such cases. If you have a callback which shuts down compute - resources, for example, you can conditionally run the shutdown logic for only uninterrupted runs. - ------------- - -Validation ----------- -You can perform an evaluation epoch over the validation set, outside of the training loop, -using :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate`. This might be -useful if you want to collect new metrics from a model right at its initialization -or after it has already been trained. - -.. code-block:: python - - trainer.validate(dataloaders=val_dataloaders) - ------------- - -Testing -------- -Once you're done training, feel free to run the test set! -(Only right before publishing your paper or pushing to production) - -.. code-block:: python - - trainer.test(dataloaders=test_dataloaders) - ------------- - -Reproducibility ---------------- - -To ensure full reproducibility from run to run you need to set seeds for pseudo-random generators, -and set ``deterministic`` flag in ``Trainer``. - -Example:: - - from pytorch_lightning import Trainer, seed_everything - - seed_everything(42, workers=True) - # sets seeds for numpy, torch and python.random. - model = Model() - trainer = Trainer(deterministic=True) - - -By setting ``workers=True`` in :func:`~pytorch_lightning.utilities.seed.seed_everything`, Lightning derives -unique seeds across all dataloader workers and processes for :mod:`torch`, :mod:`numpy` and stdlib -:mod:`random` number generators. When turned on, it ensures that e.g. data augmentations are not repeated across workers. - -------- - -.. _trainer_flags: - -Trainer flags -------------- - -accelerator -^^^^^^^^^^^ - -Supports passing different accelerator types (``"cpu", "gpu", "tpu", "ipu", "auto"``) -as well as custom accelerator instances. - -.. code-block:: python - - # CPU accelerator - trainer = Trainer(accelerator="cpu") - - # Training with GPU Accelerator using 2 GPUs - trainer = Trainer(devices=2, accelerator="gpu") - - # Training with TPU Accelerator using 8 tpu cores - trainer = Trainer(devices=8, accelerator="tpu") - - # Training with GPU Accelerator using the DistributedDataParallel strategy - trainer = Trainer(devices=4, accelerator="gpu", strategy="ddp") - -.. note:: The ``"auto"`` option recognizes the machine you are on, and selects the respective ``Accelerator``. - -.. code-block:: python - - # If your machine has GPUs, it will use the GPU Accelerator for training - trainer = Trainer(devices=2, accelerator="auto") - -You can also modify hardware behavior by subclassing an existing accelerator to adjust for your needs. - -Example:: - - class MyOwnAcc(CPUAccelerator): - ... - - Trainer(accelerator=MyOwnAcc()) - -.. note:: - - If the ``devices`` flag is not defined, it will assume ``devices`` to be ``"auto"`` and fetch the ``auto_device_count`` - from the accelerator. - - .. code-block:: python - - # This is part of the built-in `GPUAccelerator` - class GPUAccelerator(Accelerator): - """Accelerator for GPU devices.""" - - @staticmethod - def auto_device_count() -> int: - """Get the devices when set to auto.""" - return torch.cuda.device_count() - - - # Training with GPU Accelerator using total number of gpus available on the system - Trainer(accelerator="gpu") - -.. warning:: Passing training strategies (e.g., ``"ddp"``) to ``accelerator`` has been deprecated in v1.5.0 - and will be removed in v1.7.0. Please use the ``strategy`` argument instead. - -accumulate_grad_batches -^^^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Accumulates grads every k batches or as set up in the dict. -Trainer also calls ``optimizer.step()`` for the last indivisible step number. - -.. testcode:: - - # default used by the Trainer (no accumulation) - trainer = Trainer(accumulate_grad_batches=1) - -Example:: - - # accumulate every 4 batches (effective batch size is batch*4) - trainer = Trainer(accumulate_grad_batches=4) - - # no accumulation for epochs 1-4. accumulate 3 for epochs 5-10. accumulate 20 after that - trainer = Trainer(accumulate_grad_batches={5: 3, 10: 20}) - -amp_backend -^^^^^^^^^^^ - -.. raw:: html - - - -| - -Use PyTorch AMP ('native'), or NVIDIA apex ('apex'). - -.. testcode:: - - # using PyTorch built-in AMP, default used by the Trainer - trainer = Trainer(amp_backend="native") - - # using NVIDIA Apex - trainer = Trainer(amp_backend="apex") - -amp_level -^^^^^^^^^ - -.. raw:: html - - - -| - -The optimization level to use (O1, O2, etc...) -for 16-bit GPU precision (using NVIDIA apex under the hood). - -Check `NVIDIA apex docs `_ for level - -Example:: - - # default used by the Trainer - trainer = Trainer(amp_level='O2') - -auto_scale_batch_size -^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Automatically tries to find the largest batch size that fits into memory, -before any training. - -.. code-block:: python - - # default used by the Trainer (no scaling of batch size) - trainer = Trainer(auto_scale_batch_size=None) - - # run batch size scaling, result overrides hparams.batch_size - trainer = Trainer(auto_scale_batch_size="binsearch") - - # call tune to find the batch size - trainer.tune(model) - -auto_select_gpus -^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -If enabled and ``devices`` is an integer, pick available GPUs automatically. -This is especially useful when GPUs are configured to be in "exclusive mode", -such that only one process at a time can access them. - -Example:: - - # no auto selection (picks first 2 GPUs on system, may fail if other process is occupying) - trainer = Trainer(accelerator="gpu", devices=2, auto_select_gpus=False) - - # enable auto selection (will find two available GPUs on system) - trainer = Trainer(accelerator="gpu", devices=2, auto_select_gpus=True) - - # specifies all GPUs regardless of its availability - Trainer(accelerator="gpu", devices=-1, auto_select_gpus=False) - - # specifies all available GPUs (if only one GPU is not occupied, uses one gpu) - Trainer(accelerator="gpu", devices=-1, auto_select_gpus=True) - -auto_lr_find -^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Runs a learning rate finder algorithm (see this `paper `_) -when calling trainer.tune(), to find optimal initial learning rate. - -.. code-block:: python - - # default used by the Trainer (no learning rate finder) - trainer = Trainer(auto_lr_find=False) - -Example:: - - # run learning rate finder, results override hparams.learning_rate - trainer = Trainer(auto_lr_find=True) - - # call tune to find the lr - trainer.tune(model) - -Example:: - - # run learning rate finder, results override hparams.my_lr_arg - trainer = Trainer(auto_lr_find='my_lr_arg') - - # call tune to find the lr - trainer.tune(model) - -.. note:: - See the :ref:`learning rate finder guide `. - -benchmark -^^^^^^^^^ - -.. raw:: html - - - -| - -Defaults to ``True`` if :paramref:`~pytorch_lightning.trainer.Trainer.deterministic` is not set. -This flag sets the ``torch.backends.cudnn.benchmark`` flag. You can read more about its impact -`here `__ - -This is likely to increase the speed of your system if your input sizes don't change. However, if they do, then it -might make your system slower. The CUDNN auto-tuner will try to find the best algorithm for the hardware when a new -input size is encountered. Read more about it `here `__. - -Example:: - - # defaults to True if not deterministic (which is False by default) - trainer = Trainer() - - # you can overwrite the value - trainer = Trainer(benchmark=False) - -deterministic -^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -This flag sets the ``torch.backends.cudnn.deterministic`` flag. -Might make your system slower, but ensures reproducibility. -Also sets ``$HOROVOD_FUSION_THRESHOLD=0``. - -For more info check `PyTorch docs `_. - -Example:: - - # default used by the Trainer - trainer = Trainer(deterministic=False) - -callbacks -^^^^^^^^^ - -.. raw:: html - - - -| - -Add a list of :class:`~pytorch_lightning.callbacks.Callback`. Callbacks run sequentially in the order defined here -with the exception of :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` callbacks which run -after all others to ensure all states are saved to the checkpoints. - -.. code-block:: python - - # a list of callbacks - callbacks = [PrintCallback()] - trainer = Trainer(callbacks=callbacks) - -Example:: - - from pytorch_lightning.callbacks import Callback - - class PrintCallback(Callback): - def on_train_start(self, trainer, pl_module): - print("Training is started!") - def on_train_end(self, trainer, pl_module): - print("Training is done.") - - -Model-specific callbacks can also be added inside the ``LightningModule`` through -:meth:`~pytorch_lightning.core.lightning.LightningModule.configure_callbacks`. -Callbacks returned in this hook will extend the list initially given to the ``Trainer`` argument, and replace -the trainer callbacks should there be two or more of the same type. -:class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` callbacks always run last. - - -check_val_every_n_epoch -^^^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Check val every n train epochs. - -Example:: - - # default used by the Trainer - trainer = Trainer(check_val_every_n_epoch=1) - - # run val loop every 10 training epochs - trainer = Trainer(check_val_every_n_epoch=10) - -checkpoint_callback -^^^^^^^^^^^^^^^^^^^ - -.. warning:: `checkpoint_callback` has been deprecated in v1.5 and will be removed in v1.7. - To disable checkpointing, pass ``enable_checkpointing = False`` to the Trainer instead. - - -default_root_dir -^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Default path for logs and weights when no logger or -:class:`pytorch_lightning.callbacks.ModelCheckpoint` callback passed. On -certain clusters you might want to separate where logs and checkpoints are -stored. If you don't then use this argument for convenience. Paths can be local -paths or remote paths such as `s3://bucket/path` or 'hdfs://path/'. Credentials -will need to be set up to use remote filepaths. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(default_root_dir=os.getcwd()) - -devices -^^^^^^^ - -Number of devices to train on (``int``), which devices to train on (``list`` or ``str``), or ``"auto"``. -It will be mapped to either ``gpus``, ``tpu_cores``, ``num_processes`` or ``ipus``, -based on the accelerator type (``"cpu", "gpu", "tpu", "ipu", "auto"``). - -.. code-block:: python - - # Training with CPU Accelerator using 2 processes - trainer = Trainer(devices=2, accelerator="cpu") - - # Training with GPU Accelerator using GPUs 1 and 3 - trainer = Trainer(devices=[1, 3], accelerator="gpu") - - # Training with TPU Accelerator using 8 tpu cores - trainer = Trainer(devices=8, accelerator="tpu") - -.. tip:: The ``"auto"`` option recognizes the devices to train on, depending on the ``Accelerator`` being used. - -.. code-block:: python - - # If your machine has GPUs, it will use all the available GPUs for training - trainer = Trainer(devices="auto", accelerator="auto") - - # Training with CPU Accelerator using 1 process - trainer = Trainer(devices="auto", accelerator="cpu") - - # Training with TPU Accelerator using 8 tpu cores - trainer = Trainer(devices="auto", accelerator="tpu") - - # Training with IPU Accelerator using 4 ipus - trainer = Trainer(devices="auto", accelerator="ipu") - -.. note:: - - If the ``devices`` flag is not defined, it will assume ``devices`` to be ``"auto"`` and fetch the ``auto_device_count`` - from the accelerator. - - .. code-block:: python - - # This is part of the built-in `GPUAccelerator` - class GPUAccelerator(Accelerator): - """Accelerator for GPU devices.""" - - @staticmethod - def auto_device_count() -> int: - """Get the devices when set to auto.""" - return torch.cuda.device_count() - - - # Training with GPU Accelerator using total number of gpus available on the system - Trainer(accelerator="gpu") - -enable_checkpointing -^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -By default Lightning saves a checkpoint for you in your current working directory, with the state of your last training epoch, -Checkpoints capture the exact value of all parameters used by a model. -To disable automatic checkpointing, set this to `False`. - -.. code-block:: python - - # default used by Trainer, saves the most recent model to a single checkpoint after each epoch - trainer = Trainer(enable_checkpointing=True) - - # turn off automatic checkpointing - trainer = Trainer(enable_checkpointing=False) - - -You can override the default behavior by initializing the :class:`~pytorch_lightning.callbacks.ModelCheckpoint` -callback, and adding it to the :paramref:`~pytorch_lightning.trainer.trainer.Trainer.callbacks` list. -See :doc:`Saving and Loading Checkpoints <../common/checkpointing>` for how to customize checkpointing. - -.. testcode:: - - from pytorch_lightning.callbacks import ModelCheckpoint - - # Init ModelCheckpoint callback, monitoring 'val_loss' - checkpoint_callback = ModelCheckpoint(monitor="val_loss") - - # Add your callback to the callbacks list - trainer = Trainer(callbacks=[checkpoint_callback]) - -fast_dev_run -^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Runs n if set to ``n`` (int) else 1 if set to ``True`` batch(es) of train, val and test -to find any bugs (ie: a sort of unit test). - -Under the hood the pseudocode looks like this when running *fast_dev_run* with a single batch: - -.. code-block:: python - - # loading - __init__() - prepare_data - - # test training step - training_batch = next(train_dataloader) - training_step(training_batch) - - # test val step - val_batch = next(val_dataloader) - out = validation_step(val_batch) - validation_epoch_end([out]) - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(fast_dev_run=False) - - # runs 1 train, val, test batch and program ends - trainer = Trainer(fast_dev_run=True) - - # runs 7 train, val, test batches and program ends - trainer = Trainer(fast_dev_run=7) - -.. note:: - - This argument is a bit different from ``limit_train/val/test_batches``. Setting this argument will - disable tuner, checkpoint callbacks, early stopping callbacks, loggers and logger callbacks like - ``LearningRateLogger`` and runs for only 1 epoch. This must be used only for debugging purposes. - ``limit_train/val/test_batches`` only limits the number of batches and won't disable anything. - -flush_logs_every_n_steps -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: ``flush_logs_every_n_steps`` has been deprecated in v1.5 and will be removed in v1.7. - Please configure flushing directly in the logger instead. - -.. raw:: html - - - -| - -Writes logs to disk this often. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(flush_logs_every_n_steps=100) - -See Also: - - :doc:`logging <../extensions/logging>` - -.. _gpus: - -gpus -^^^^ - -.. warning:: ``gpus=x`` has been deprecated in v1.7 and will be removed in v2.0. - Please use ``accelerator='gpu'`` and ``devices=x`` instead. - -.. raw:: html - - - -| - -- Number of GPUs to train on (int) -- or which GPUs to train on (list) -- can handle strings - -.. testcode:: - - # default used by the Trainer (ie: train on CPU) - trainer = Trainer(gpus=None) - - # equivalent - trainer = Trainer(gpus=0) - -Example:: - - # int: train on 2 gpus - trainer = Trainer(gpus=2) - - # list: train on GPUs 1, 4 (by bus ordering) - trainer = Trainer(gpus=[1, 4]) - trainer = Trainer(gpus='1, 4') # equivalent - - # -1: train on all gpus - trainer = Trainer(gpus=-1) - trainer = Trainer(gpus='-1') # equivalent - - # combine with num_nodes to train on multiple GPUs across nodes - # uses 8 gpus in total - trainer = Trainer(gpus=2, num_nodes=4) - - # train only on GPUs 1 and 4 across nodes - trainer = Trainer(gpus=[1, 4], num_nodes=4) - -See Also: - - :ref:`Multi GPU Training ` - -gradient_clip_val -^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Gradient clipping value - -- 0 means don't clip. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(gradient_clip_val=0.0) - -limit_train_batches -^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -How much of training dataset to check. -Useful when debugging or testing something that happens at the end of an epoch. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(limit_train_batches=1.0) - -Example:: - - # default used by the Trainer - trainer = Trainer(limit_train_batches=1.0) - - # run through only 25% of the training set each epoch - trainer = Trainer(limit_train_batches=0.25) - - # run through only 10 batches of the training set each epoch - trainer = Trainer(limit_train_batches=10) - -limit_test_batches -^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -How much of test dataset to check. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(limit_test_batches=1.0) - - # run through only 25% of the test set each epoch - trainer = Trainer(limit_test_batches=0.25) - - # run for only 10 batches - trainer = Trainer(limit_test_batches=10) - -In the case of multiple test dataloaders, the limit applies to each dataloader individually. - -limit_val_batches -^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -How much of validation dataset to check. -Useful when debugging or testing something that happens at the end of an epoch. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(limit_val_batches=1.0) - - # run through only 25% of the validation set each epoch - trainer = Trainer(limit_val_batches=0.25) - - # run for only 10 batches - trainer = Trainer(limit_val_batches=10) - -In the case of multiple validation dataloaders, the limit applies to each dataloader individually. - -log_every_n_steps -^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - - -How often to add logging rows (does not write to disk) - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(log_every_n_steps=50) - -See Also: - - :doc:`logging <../extensions/logging>` - -logger -^^^^^^ - -.. raw:: html - - - -| - -:doc:`Logger <../visualize/loggers>` (or iterable collection of loggers) for experiment tracking. A ``True`` value uses the default ``TensorBoardLogger`` shown below. ``False`` will disable logging. - -.. testcode:: - - from pytorch_lightning.loggers import TensorBoardLogger - - # default logger used by trainer - logger = TensorBoardLogger(save_dir=os.getcwd(), version=1, name="lightning_logs") - Trainer(logger=logger) - -max_epochs -^^^^^^^^^^ - -.. raw:: html - - - -| - -Stop training once this number of epochs is reached - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(max_epochs=1000) - -If both ``max_epochs`` and ``max_steps`` aren't specified, ``max_epochs`` will default to ``1000``. -To enable infinite training, set ``max_epochs = -1``. - -min_epochs -^^^^^^^^^^ - -.. raw:: html - - - -| - -Force training for at least these many epochs - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(min_epochs=1) - -max_steps -^^^^^^^^^ - -.. raw:: html - - - -| - -Stop training after this number of :ref:`global steps `. -Training will stop if max_steps or max_epochs have reached (earliest). - -.. testcode:: - - # Default (disabled) - trainer = Trainer(max_steps=None) - - # Stop after 100 steps - trainer = Trainer(max_steps=100) - -If ``max_steps`` is not specified, ``max_epochs`` will be used instead (and ``max_epochs`` defaults to -``1000`` if ``max_epochs`` is not specified). To disable this default, set ``max_steps = -1``. - -min_steps -^^^^^^^^^ - -.. raw:: html - - - -| - -Force training for at least this number of :ref:`global steps `. -Trainer will train model for at least min_steps or min_epochs (latest). - -.. testcode:: - - # Default (disabled) - trainer = Trainer(min_steps=None) - - # Run at least for 100 steps (disable min_epochs) - trainer = Trainer(min_steps=100, min_epochs=0) - -max_time -^^^^^^^^ - -Set the maximum amount of time for training. Training will get interrupted mid-epoch. -For customizable options use the :class:`~pytorch_lightning.callbacks.timer.Timer` callback. - -.. testcode:: - - # Default (disabled) - trainer = Trainer(max_time=None) - - # Stop after 12 hours of training or when reaching 10 epochs (string) - trainer = Trainer(max_time="00:12:00:00", max_epochs=10) - - # Stop after 1 day and 5 hours (dict) - trainer = Trainer(max_time={"days": 1, "hours": 5}) - -In case ``max_time`` is used together with ``min_steps`` or ``min_epochs``, the ``min_*`` requirement -always has precedence. - -num_nodes -^^^^^^^^^ - -.. raw:: html - - - -| - -Number of GPU nodes for distributed training. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(num_nodes=1) - - # to train on 8 nodes - trainer = Trainer(num_nodes=8) - -num_processes -^^^^^^^^^^^^^ - -.. warning:: ``num_processes=x`` has been deprecated in v1.7 and will be removed in v2.0. - Please use ``accelerator='cpu'`` and ``devices=x`` instead. - -.. raw:: html - - - -| - -Number of processes to train with. Automatically set to the number of GPUs -when using ``strategy="ddp"``. Set to a number greater than 1 when -using ``accelerator="cpu"`` and ``strategy="ddp"`` to mimic distributed training on a -machine without GPUs. This is useful for debugging, but **will not** provide -any speedup, since single-process Torch already makes efficient use of multiple -CPUs. While it would typically spawns subprocesses for training, setting -``num_nodes > 1`` and keeping ``num_processes = 1`` runs training in the main -process. - -.. testcode:: - - # Simulate DDP for debugging on your GPU-less laptop - trainer = Trainer(accelerator="cpu", strategy="ddp", num_processes=2) - -num_sanity_val_steps -^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Sanity check runs n batches of val before starting the training routine. -This catches any bugs in your validation without having to wait for the first validation check. -The Trainer uses 2 steps by default. Turn it off or modify it here. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(num_sanity_val_steps=2) - - # turn it off - trainer = Trainer(num_sanity_val_steps=0) - - # check all validation data - trainer = Trainer(num_sanity_val_steps=-1) - - -This option will reset the validation dataloader unless ``num_sanity_val_steps=0``. - -overfit_batches -^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Uses this much data of the training & validation set. -If the training & validation dataloaders have ``shuffle=True``, Lightning will automatically disable it. - -Useful for quickly debugging or trying to overfit on purpose. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(overfit_batches=0.0) - - # use only 1% of the train & val set - trainer = Trainer(overfit_batches=0.01) - - # overfit on 10 of the same batches - trainer = Trainer(overfit_batches=10) - -plugins -^^^^^^^ - -.. raw:: html - - - -| - -:ref:`Plugins` allow you to connect arbitrary backends, precision libraries, clusters etc. For example: - -- :ref:`Checkpoint IO ` -- `TorchElastic `_ -- :ref:`Precision Plugins ` - -To define your own behavior, subclass the relevant class and pass it in. Here's an example linking up your own -:class:`~pytorch_lightning.plugins.environments.ClusterEnvironment`. - -.. code-block:: python - - from pytorch_lightning.plugins.environments import ClusterEnvironment - - - class MyCluster(ClusterEnvironment): - def main_address(self): - return your_main_address - - def main_port(self): - return your_main_port - - def world_size(self): - return the_world_size - - - trainer = Trainer(plugins=[MyCluster()], ...) - -precision -^^^^^^^^^ - -.. raw:: html - - - -| - -Lightning supports either double (64), float (32), bfloat16 (bf16), or half (16) precision training. - -Half precision, or mixed precision, is the combined use of 32 and 16 bit floating points to reduce memory footprint during model training. This can result in improved performance, achieving +3X speedups on modern GPUs. - -.. testcode:: - :skipif: not torch.cuda.is_available() - - # default used by the Trainer - trainer = Trainer(precision=32) - - # 16-bit precision - trainer = Trainer(precision=16, accelerator="gpu", devices=1) # works only on CUDA - - # bfloat16 precision - trainer = Trainer(precision="bf16") - - # 64-bit precision - trainer = Trainer(precision=64) - - -.. note:: When running on TPUs, torch.bfloat16 will be used but tensor printing will still show torch.float32. - -.. admonition:: If you are interested in using Apex 16-bit training: - :class: dropdown - - NVIDIA Apex and DDP have instability problems. We recommend using the native AMP for 16-bit precision with multiple GPUs. - To use Apex 16-bit training: - - 1. `Install apex. `__ - - 2. Set the ``precision`` trainer flag to 16. You can customize the `Apex optimization level `_ by setting the `amp_level` flag. - - .. testcode:: - :skipif: not _APEX_AVAILABLE or not torch.cuda.is_available() - - # turn on 16-bit - trainer = Trainer(amp_backend="apex", amp_level="O2", precision=16, accelerator="gpu", devices=1) - - -process_position -^^^^^^^^^^^^^^^^ - -.. warning:: ``process_position`` has been deprecated in v1.5 and will be removed in v1.7. - Please pass :class:`~pytorch_lightning.callbacks.progress.TQDMProgressBar` with ``process_position`` - directly to the Trainer's ``callbacks`` argument instead. - -.. raw:: html - - - -| - -Orders the progress bar. Useful when running multiple trainers on the same node. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(process_position=0) - -.. note:: This argument is ignored if a custom callback is passed to :paramref:`~Trainer.callbacks`. - -profiler -^^^^^^^^ - -.. raw:: html - - - -| - -To profile individual steps during training and assist in identifying bottlenecks. - -See the :doc:`profiler documentation <../tuning/profiler>`. for more details. - -.. testcode:: - - from pytorch_lightning.profiler import SimpleProfiler, AdvancedProfiler - - # default used by the Trainer - trainer = Trainer(profiler=None) - - # to profile standard training events, equivalent to `profiler=SimpleProfiler()` - trainer = Trainer(profiler="simple") - - # advanced profiler for function-level stats, equivalent to `profiler=AdvancedProfiler()` - trainer = Trainer(profiler="advanced") - -enable_progress_bar -^^^^^^^^^^^^^^^^^^^ - -Whether to enable or disable the progress bar. Defaults to True. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(enable_progress_bar=True) - - # disable progress bar - trainer = Trainer(enable_progress_bar=False) - -reload_dataloaders_every_n_epochs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Set to a positive integer to reload dataloaders every n epochs. - -.. code-block:: python - - # if 0 (default) - train_loader = model.train_dataloader() - for epoch in epochs: - for batch in train_loader: - ... - - # if a positive integer - for epoch in epochs: - if not epoch % reload_dataloaders_every_n_epochs: - train_loader = model.train_dataloader() - for batch in train_loader: - ... - -.. _replace-sampler-ddp: - -replace_sampler_ddp -^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Enables auto adding of :class:`~torch.utils.data.distributed.DistributedSampler`. In PyTorch, you must use it in -distributed settings such as TPUs or multi-node. The sampler makes sure each GPU sees the appropriate part of your data. -By default it will add ``shuffle=True`` for train sampler and ``shuffle=False`` for val/test sampler. -If you want to customize it, you can set ``replace_sampler_ddp=False`` and add your own distributed sampler. -If ``replace_sampler_ddp=True`` and a distributed sampler was already added, -Lightning will not replace the existing one. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(replace_sampler_ddp=True) - -By setting to False, you have to add your own distributed sampler: - -.. code-block:: python - - # in your LightningModule or LightningDataModule - def train_dataloader(self): - # default used by the Trainer - sampler = torch.utils.data.distributed.DistributedSampler(dataset, shuffle=True) - dataloader = DataLoader(dataset, batch_size=32, sampler=sampler) - return dataloader - -.. note:: For iterable datasets, we don't do this automatically. - -resume_from_checkpoint -^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: ``resume_from_checkpoint`` is deprecated in v1.5 and will be removed in v2.0. - Please pass ``trainer.fit(ckpt_path="some/path/to/my_checkpoint.ckpt")`` instead. - - -.. raw:: html - - - -| - -To resume training from a specific checkpoint pass in the path here. If resuming from a mid-epoch -checkpoint, training will start from the beginning of the next epoch. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(resume_from_checkpoint=None) - - # resume from a specific checkpoint - trainer = Trainer(resume_from_checkpoint="some/path/to/my_checkpoint.ckpt") - -strategy -^^^^^^^^ - -Supports passing different training strategies with aliases (ddp, ddp_spawn, etc) as well as custom strategies. - -.. code-block:: python - - # Training with the DistributedDataParallel strategy on 4 GPUs - trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4) - - # Training with the DDP Spawn strategy using 4 cpu processes - trainer = Trainer(strategy="ddp_spawn", accelerator="cpu", devices=4) - -.. note:: Additionally, you can pass your custom strategy to the ``strategy`` argument. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy - - - class CustomDDPStrategy(DDPStrategy): - def configure_ddp(self): - self._model = MyCustomDistributedDataParallel( - self.model, - device_ids=..., - ) - - - trainer = Trainer(strategy=CustomDDPStrategy(), accelerator="gpu", devices=2) - -See Also: - - :ref:`Multi GPU Training `. - - :doc:`Model Parallel GPU training guide <../advanced/model_parallel>`. - - :doc:`TPU training guide <../accelerators/tpu>`. - -sync_batchnorm -^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Enable synchronization between batchnorm layers across all GPUs. - -.. testcode:: - - trainer = Trainer(sync_batchnorm=True) - -track_grad_norm -^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -- no tracking (-1) -- Otherwise tracks that norm (2 for 2-norm) - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(track_grad_norm=-1) - - # track the 2-norm - trainer = Trainer(track_grad_norm=2) - -.. _tpu_cores: - -tpu_cores -^^^^^^^^^ - -.. warning:: ``tpu_cores=x`` has been deprecated in v1.7 and will be removed in v2.0. - Please use ``accelerator='tpu'`` and ``devices=x`` instead. - -.. raw:: html - - - -| - -- How many TPU cores to train on (1 or 8). -- Which TPU core to train on [1-8] - -A single TPU v2 or v3 has 8 cores. A TPU pod has -up to 2048 cores. A slice of a POD means you get as many cores -as you request. - -Your effective batch size is batch_size * total tpu cores. - -This parameter can be either 1 or 8. - -Example:: - - # your_trainer_file.py - - # default used by the Trainer (ie: train on CPU) - trainer = Trainer(tpu_cores=None) - - # int: train on a single core - trainer = Trainer(tpu_cores=1) - - # list: train on a single selected core - trainer = Trainer(tpu_cores=[2]) - - # int: train on all cores few cores - trainer = Trainer(tpu_cores=8) - - # for 8+ cores must submit via xla script with - # a max of 8 cores specified. The XLA script - # will duplicate script onto each TPU in the POD - trainer = Trainer(tpu_cores=8) - -To train on more than 8 cores (ie: a POD), -submit this script using the xla_dist script. - -Example:: - - python -m torch_xla.distributed.xla_dist - --tpu=$TPU_POD_NAME - --conda-env=torch-xla-nightly - --env=XLA_USE_BF16=1 - -- python your_trainer_file.py - - -val_check_interval -^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -How often within one training epoch to check the validation set. -Can specify as float or int. - -- pass a ``float`` in the range [0.0, 1.0] to check after a fraction of the training epoch. -- pass an ``int`` to check after a fixed number of training batches. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(val_check_interval=1.0) - - # check validation set 4 times during a training epoch - trainer = Trainer(val_check_interval=0.25) - - # check validation set every 1000 training batches - # use this when using iterableDataset and your dataset has no length - # (ie: production cases with streaming data) - trainer = Trainer(val_check_interval=1000) - - -.. code-block:: python - - # Here is the computation to estimate the total number of batches seen within an epoch. - - # Find the total number of train batches - total_train_batches = total_train_samples // (train_batch_size * world_size) - - # Compute how many times we will call validation during the training loop - val_check_batch = max(1, int(total_train_batches * val_check_interval)) - val_checks_per_epoch = total_train_batches / val_check_batch - - # Find the total number of validation batches - total_val_batches = total_val_samples // (val_batch_size * world_size) - - # Total number of batches run - total_fit_batches = total_train_batches + total_val_batches - - -weights_save_path -^^^^^^^^^^^^^^^^^ - - -.. warning:: `weights_save_path` has been deprecated in v1.6 and will be removed in v1.8. Please pass - ``dirpath`` directly to the :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` - callback. - - -.. raw:: html - - - -| - -Directory of where to save weights if specified. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(weights_save_path=os.getcwd()) - - # save to your custom path - trainer = Trainer(weights_save_path="my/path") - -Example:: - - # if checkpoint callback used, then overrides the weights path - # **NOTE: this saves weights to some/path NOT my/path - checkpoint = ModelCheckpoint(dirpath='some/path') - trainer = Trainer( - callbacks=[checkpoint], - weights_save_path='my/path' - ) - -weights_summary -^^^^^^^^^^^^^^^ - -.. warning:: `weights_summary` is deprecated in v1.5 and will be removed in v1.7. Please pass :class:`~pytorch_lightning.callbacks.model_summary.ModelSummary` - directly to the Trainer's ``callbacks`` argument instead. To disable the model summary, - pass ``enable_model_summary = False`` to the Trainer. - - -.. raw:: html - - - -| - -Prints a summary of the weights when training begins. -Options: 'full', 'top', None. - -.. testcode:: - - # default used by the Trainer (ie: print summary of top level modules) - trainer = Trainer(weights_summary="top") - - # print full summary of all modules and submodules - trainer = Trainer(weights_summary="full") - - # don't print a summary - trainer = Trainer(weights_summary=None) - - -enable_model_summary -^^^^^^^^^^^^^^^^^^^^ - -Whether to enable or disable the model summarization. Defaults to True. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(enable_model_summary=True) - - # disable summarization - trainer = Trainer(enable_model_summary=False) - - # enable custom summarization - from pytorch_lightning.callbacks import ModelSummary - - trainer = Trainer(enable_model_summary=True, callbacks=[ModelSummary(max_depth=-1)]) - ------ - -Trainer class API ------------------ - -Methods -^^^^^^^ - -init -**** - -.. automethod:: pytorch_lightning.trainer.Trainer.__init__ - :noindex: - -fit -**** - -.. automethod:: pytorch_lightning.trainer.Trainer.fit - :noindex: - -validate -******** - -.. automethod:: pytorch_lightning.trainer.Trainer.validate - :noindex: - -test -**** - -.. automethod:: pytorch_lightning.trainer.Trainer.test - :noindex: - -predict -******* - -.. automethod:: pytorch_lightning.trainer.Trainer.predict - :noindex: - -tune -**** - -.. automethod:: pytorch_lightning.trainer.Trainer.tune - :noindex: - - -Properties -^^^^^^^^^^ - -callback_metrics -**************** - -The metrics available to callbacks. These are automatically set when you log via `self.log` - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("a_val", 2) - - - callback_metrics = trainer.callback_metrics - assert callback_metrics["a_val"] == 2 - -current_epoch -************* - -The number of epochs run. - -.. code-block:: python - - if trainer.current_epoch >= 10: - ... - -global_step -*********** - -The number of optimizer steps taken (does not reset each epoch). -This includes multiple optimizers and TBPTT steps (if enabled). - -.. code-block:: python - - if trainer.global_step >= 100: - ... - -logger -******* - -The current logger being used. Here's an example using tensorboard - -.. code-block:: python - - logger = trainer.logger - tensorboard = logger.experiment - - -loggers -******** - -The list of loggers currently being used by the Trainer. - -.. code-block:: python - - # List of Logger objects - loggers = trainer.loggers - for logger in loggers: - logger.log_metrics({"foo": 1.0}) - - -logged_metrics -************** - -The metrics sent to the logger (visualizer). - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("a_val", 2, logger=True) - - - logged_metrics = trainer.logged_metrics - assert logged_metrics["a_val"] == 2 - -log_dir -******* -The directory for the current experiment. Use this to save images to, etc... - -.. code-block:: python - - def training_step(self, batch, batch_idx): - img = ... - save_img(img, self.trainer.log_dir) - - - -is_global_zero -************** - -Whether this process is the global zero in multi-node training - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.trainer.is_global_zero: - print("in node 0, accelerator 0") - -progress_bar_metrics -******************** - -The metrics sent to the progress bar. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("a_val", 2, prog_bar=True) - - - progress_bar_metrics = trainer.progress_bar_metrics - assert progress_bar_metrics["a_val"] == 2 - - -estimated_stepping_batches -************************** - -Check out :meth:`~pytorch_lightning.trainer.trainer.Trainer.estimated_stepping_batches`. - -state -***** - -The current state of the Trainer, including the current function that is running, the stage of -execution within that function, and the status of the Trainer. - -.. code-block:: python - - # fn in ("fit", "validate", "test", "predict", "tune") - trainer.state.fn - # status in ("initializing", "running", "finished", "interrupted") - trainer.state.status - # stage in ("train", "sanity_check", "validate", "test", "predict", "tune") - trainer.state.stage diff --git a/docs/_sources/common_usecases.rst.txt b/docs/_sources/common_usecases.rst.txt deleted file mode 100644 index 606eea9..0000000 --- a/docs/_sources/common_usecases.rst.txt +++ /dev/null @@ -1,171 +0,0 @@ -:orphan: - -################ -Common Workflows -################ - -Customize and extend Lightning for things like custom hardware or distributed strategies. - -.. join_slack:: - :align: left - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Avoid overfitting - :description: Add a training and test loop. - :col_css: col-md-12 - :button_link: common/evaluation.html - :height: 100 - -.. displayitem:: - :header: Build a model - :description: Steps to build a model. - :col_css: col-md-12 - :button_link: model/build_model.html - :height: 100 - -.. displayitem:: - :header: Configure hyperparameters from the CLI - :description: Enable basic CLI with Lightning. - :col_css: col-md-12 - :button_link: common/hyperparameters.html - :height: 100 - -.. displayitem:: - :header: Customize the progress bar - :description: Change the progress bar behavior. - :col_css: col-md-12 - :button_link: common/progress_bar.html - :height: 100 - -.. displayitem:: - :header: Deploy models into production - :description: Deploy models with different levels of scale. - :col_css: col-md-12 - :button_link: deploy/production.html - :height: 100 - -.. displayitem:: - :header: Effective Training Techniques - :description: Explore advanced training techniques. - :col_css: col-md-12 - :button_link: advanced/training_tricks.html - :height: 100 - -.. displayitem:: - :header: Eliminate config boilerplate - :description: Control your training via CLI and YAML. - :col_css: col-md-12 - :button_link: cli/lightning_cli.html - :height: 100 - -.. displayitem:: - :header: Find bottlenecks in your code - :description: Learn to find bottlenecks in your code. - :col_css: col-md-12 - :button_link: tuning/profiler.html - :height: 100 - -.. displayitem:: - :header: Finetune a model - :description: Learn to use pretrained models - :col_css: col-md-12 - :button_link: advanced/transfer_learning.html - :height: 100 - -.. displayitem:: - :header: Manage Experiments - :description: Learn to track and visualize experiments - :col_css: col-md-12 - :button_link: visualize/logging_intermediate.html - :height: 100 - -.. displayitem:: - :header: Run on an on-prem cluster - :description: Learn to run on your own cluster - :col_css: col-md-12 - :button_link: clouds/cluster.html - :height: 100 - -.. displayitem:: - :header: Save and load model progress - :description: Save and load progress with checkpoints. - :col_css: col-md-12 - :button_link: common/checkpointing_basic.html - :height: 100 - -.. displayitem:: - :header: Save memory with half-precision - :description: Enable half-precision to train faster and save memory. - :col_css: col-md-12 - :button_link: common/precision.html - :height: 100 - -.. displayitem:: - :header: Train 1 trillion+ parameter models - :description: Scale GPU training to 1 trillion + parameter models - :col_css: col-md-12 - :button_link: advanced/model_parallel.html - :height: 100 - -.. displayitem:: - :header: Train on the cloud - :description: Run models on the cloud. - :col_css: col-md-12 - :button_link: clouds/cloud_training.html - :height: 100 - -.. displayitem:: - :header: Train on single or multiple GPUs - :description: Train models faster with GPUs. - :col_css: col-md-12 - :button_link: accelerators/gpu.html - :height: 100 - -.. displayitem:: - :header: Train on single or multiple HPUs - :description: Train models faster with HPUs. - :col_css: col-md-12 - :button_link: accelerators/hpu.html - :height: 100 - -.. displayitem:: - :header: Train on single or multiple IPUs - :description: Train models faster with IPUs. - :col_css: col-md-12 - :button_link: accelerators/ipu.html - :height: 100 - -.. displayitem:: - :header: Train on single or multiple TPUs - :description: Train models faster with TPUs. - :col_css: col-md-12 - :button_link: accelerators/tpu.html - :height: 100 - -.. displayitem:: - :header: Track and Visualize Experiments - :description: Learn to track and visualize experiments - :col_css: col-md-12 - :button_link: visualize/logging_intermediate.html - :height: 100 - -.. displayitem:: - :header: Use a pure PyTorch training loop - :description: Run your pure PyTorch loop with Lightning. - :col_css: col-md-12 - :button_link: model/own_your_loop.html - :height: 100 - -.. raw:: html - -
-
diff --git a/docs/_sources/data/datamodule.rst.txt b/docs/_sources/data/datamodule.rst.txt deleted file mode 100644 index bce1877..0000000 --- a/docs/_sources/data/datamodule.rst.txt +++ /dev/null @@ -1,501 +0,0 @@ -.. _datamodules: - -################### -LightningDataModule -################### -A datamodule is a shareable, reusable class that encapsulates all the steps needed to process data: - -.. raw:: html - - - -| - -A datamodule encapsulates the five steps involved in data processing in PyTorch: - -1. Download / tokenize / process. -2. Clean and (maybe) save to disk. -3. Load inside :class:`~torch.utils.data.Dataset`. -4. Apply transforms (rotate, tokenize, etc...). -5. Wrap inside a :class:`~torch.utils.data.DataLoader`. - -| - -This class can then be shared and used anywhere: - -.. code-block:: python - - from pl_bolts.datamodules import CIFAR10DataModule, ImagenetDataModule - - model = LitClassifier() - trainer = Trainer() - - imagenet = ImagenetDataModule() - trainer.fit(model, datamodule=imagenet) - - cifar10 = CIFAR10DataModule() - trainer.fit(model, datamodule=cifar10) - ---------------- - -*************************** -Why do I need a DataModule? -*************************** -In normal PyTorch code, the data cleaning/preparation is usually scattered across many files. This makes -sharing and reusing the exact splits and transforms across projects impossible. - -Datamodules are for you if you ever asked the questions: - -- what splits did you use? -- what transforms did you use? -- what normalization did you use? -- how did you prepare/tokenize the data? - --------------- - -********************* -What is a DataModule? -********************* -A DataModule is simply a collection of a train_dataloader(s), val_dataloader(s), test_dataloader(s) and -predict_dataloader(s) along with the matching transforms and data processing/downloads steps required. - -Here's a simple PyTorch example: - -.. code-block:: python - - # regular PyTorch - test_data = MNIST(my_path, train=False, download=True) - predict_data = MNIST(my_path, train=False, download=True) - train_data = MNIST(my_path, train=True, download=True) - train_data, val_data = random_split(train_data, [55000, 5000]) - - train_loader = DataLoader(train_data, batch_size=32) - val_loader = DataLoader(val_data, batch_size=32) - test_loader = DataLoader(test_data, batch_size=32) - predict_loader = DataLoader(predict_data, batch_size=32) - -The equivalent DataModule just organizes the same exact code, but makes it reusable across projects. - -.. code-block:: python - - class MNISTDataModule(pl.LightningDataModule): - def __init__(self, data_dir: str = "path/to/dir", batch_size: int = 32): - super().__init__() - self.data_dir = data_dir - self.batch_size = batch_size - - def setup(self, stage: Optional[str] = None): - self.mnist_test = MNIST(self.data_dir, train=False) - self.mnist_predict = MNIST(self.data_dir, train=False) - mnist_full = MNIST(self.data_dir, train=True) - self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) - - def train_dataloader(self): - return DataLoader(self.mnist_train, batch_size=self.batch_size) - - def val_dataloader(self): - return DataLoader(self.mnist_val, batch_size=self.batch_size) - - def test_dataloader(self): - return DataLoader(self.mnist_test, batch_size=self.batch_size) - - def predict_dataloader(self): - return DataLoader(self.mnist_predict, batch_size=self.batch_size) - - def teardown(self, stage: Optional[str] = None): - # Used to clean-up when the run is finished - ... - -But now, as the complexity of your processing grows (transforms, multiple-GPU training), you can -let Lightning handle those details for you while making this dataset reusable so you can share with -colleagues or use in different projects. - -.. code-block:: python - - mnist = MNISTDataModule(my_path) - model = LitClassifier() - - trainer = Trainer() - trainer.fit(model, mnist) - -Here's a more realistic, complex DataModule that shows how much more reusable the datamodule is. - -.. code-block:: python - - import pytorch_lightning as pl - from torch.utils.data import random_split, DataLoader - - # Note - you must have torchvision installed for this example - from torchvision.datasets import MNIST - from torchvision import transforms - - - class MNISTDataModule(pl.LightningDataModule): - def __init__(self, data_dir: str = "./"): - super().__init__() - self.data_dir = data_dir - self.transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) - - def prepare_data(self): - # download - MNIST(self.data_dir, train=True, download=True) - MNIST(self.data_dir, train=False, download=True) - - def setup(self, stage: Optional[str] = None): - - # Assign train/val datasets for use in dataloaders - if stage == "fit" or stage is None: - mnist_full = MNIST(self.data_dir, train=True, transform=self.transform) - self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) - - # Assign test dataset for use in dataloader(s) - if stage == "test" or stage is None: - self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform) - - if stage == "predict" or stage is None: - self.mnist_predict = MNIST(self.data_dir, train=False, transform=self.transform) - - def train_dataloader(self): - return DataLoader(self.mnist_train, batch_size=32) - - def val_dataloader(self): - return DataLoader(self.mnist_val, batch_size=32) - - def test_dataloader(self): - return DataLoader(self.mnist_test, batch_size=32) - - def predict_dataloader(self): - return DataLoader(self.mnist_predict, batch_size=32) - ---------------- - -*********************** -LightningDataModule API -*********************** -To define a DataModule the following methods are used to create train/val/test/predict dataloaders: - -- :ref:`prepare_data` (how to download, tokenize, etc...) -- :ref:`setup` (how to split, define dataset, etc...) -- :ref:`train_dataloader` -- :ref:`val_dataloader` -- :ref:`test_dataloader` -- :ref:`predict_dataloader` - - -prepare_data -============ -Downloading and saving data with multiple processes (distributed settings) will result in corrupted data. Lightning -ensures the :meth:`~pytorch_lightning.core.hooks.DataHooks.prepare_data` is called only within a single process on CPU, -so you can safely add your downloading logic within. In case of multi-node training, the execution of this hook -depends upon :ref:`prepare_data_per_node`. :meth:`~pytorch_lightning.core.hooks.DataHooks.setup` is called after -``prepare_data`` and there is a barrier in between which ensures that all the processes proceed to ``setup`` once the data is prepared and available for use. - -- download, i.e. download data only once on the disk from a single process -- tokenize. Since it's a one time process, it is not recommended to do it on all processes -- etc... - -.. code-block:: python - - class MNISTDataModule(pl.LightningDataModule): - def prepare_data(self): - # download - MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()) - MNIST(os.getcwd(), train=False, download=True, transform=transforms.ToTensor()) - - -.. warning:: - - ``prepare_data`` is called from the main process. It is not recommended to assign state here (e.g. ``self.x = y``) since it is called on a single process and if you assign - states here then they won't be available for other processes. - - -setup -===== -There are also data operations you might want to perform on every GPU. Use :meth:`~pytorch_lightning.core.hooks.DataHooks.setup` to do things like: - -- count number of classes -- build vocabulary -- perform train/val/test splits -- create datasets -- apply transforms (defined explicitly in your datamodule) -- etc... - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def setup(self, stage: Optional[str] = None): - - # Assign Train/val split(s) for use in Dataloaders - if stage in (None, "fit"): - mnist_full = MNIST(self.data_dir, train=True, download=True, transform=self.transform) - self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) - - # Assign Test split(s) for use in Dataloaders - if stage in (None, "test"): - self.mnist_test = MNIST(self.data_dir, train=False, download=True, transform=self.transform) - - -For eg., if you are working with NLP task where you need to tokenize the text and use it, then you can do something like as follows: - -.. code-block:: python - - class LitDataModule(LightningDataModule): - def prepare_data(self): - dataset = load_Dataset(...) - train_dataset = ... - val_dataset = ... - # tokenize - # save it to disk - - def setup(self, stage): - # load it back here - dataset = load_dataset_from_disk(...) - - -This method expects a ``stage`` argument. -It is used to separate setup logic for ``trainer.{fit,validate,test,predict}``. If ``setup`` is called with ``stage=None``, -we assume all stages have been set-up. - -.. note:: :ref:`setup` is called from every process across all the nodes. Setting state here is recommended. -.. note:: :ref:`teardown` can be used to clean up the state. It is also called from every process across all the nodes. - - -train_dataloader -================ -Use the :meth:`~pytorch_lightning.core.hooks.DataHooks.train_dataloader` method to generate the training dataloader(s). -Usually you just wrap the dataset you defined in :ref:`setup`. This is the dataloader that the Trainer -:meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` method uses. - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def train_dataloader(self): - return DataLoader(self.mnist_train, batch_size=64) - -.. _datamodule_val_dataloader_label: - -val_dataloader -============== -Use the :meth:`~pytorch_lightning.core.hooks.DataHooks.val_dataloader` method to generate the validation dataloader(s). -Usually you just wrap the dataset you defined in :ref:`setup`. This is the dataloader that the Trainer -:meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` and :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate` methods uses. - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def val_dataloader(self): - return DataLoader(self.mnist_val, batch_size=64) - - -.. _datamodule_test_dataloader_label: - -test_dataloader -=============== -Use the :meth:`~pytorch_lightning.core.hooks.DataHooks.test_dataloader` method to generate the test dataloader(s). -Usually you just wrap the dataset you defined in :ref:`setup`. This is the dataloader that the Trainer -:meth:`~pytorch_lightning.trainer.trainer.Trainer.test` method uses. - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def test_dataloader(self): - return DataLoader(self.mnist_test, batch_size=64) - - -predict_dataloader -================== -Use the :meth:`~pytorch_lightning.core.hooks.DataHooks.predict_dataloader` method to generate the prediction dataloader(s). -Usually you just wrap the dataset you defined in :ref:`setup`. This is the dataloader that the Trainer -:meth:`~pytorch_lightning.trainer.trainer.Trainer.predict` method uses. - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def predict_dataloader(self): - return DataLoader(self.mnist_predict, batch_size=64) - - -transfer_batch_to_device -======================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.transfer_batch_to_device - :noindex: - -on_before_batch_transfer -======================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_before_batch_transfer - :noindex: - -on_after_batch_transfer -======================= - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_after_batch_transfer - :noindex: - -load_state_dict -=============== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.load_state_dict - :noindex: - -state_dict -========== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.state_dict - :noindex: - -on_train_dataloader -=================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_train_dataloader - :noindex: - -on_val_dataloader -================= - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_val_dataloader - :noindex: - -on_test_dataloader -================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_test_dataloader - :noindex: - -on_predict_dataloader -===================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_predict_dataloader - :noindex: - -teardown -======== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.teardown - :noindex: - -prepare_data_per_node -===================== -If set to ``True`` will call ``prepare_data()`` on LOCAL_RANK=0 for every node. -If set to ``False`` will only call from NODE_RANK=0, LOCAL_RANK=0. - -.. testcode:: - - class LitDataModule(LightningDataModule): - def __init__(self): - super().__init__() - self.prepare_data_per_node = True - - ------------------- - -****************** -Using a DataModule -****************** - -The recommended way to use a DataModule is simply: - -.. code-block:: python - - dm = MNISTDataModule() - model = Model() - trainer.fit(model, datamodule=dm) - trainer.test(datamodule=dm) - trainer.validate(datamodule=dm) - trainer.predict(datamodule=dm) - -If you need information from the dataset to build your model, then run -:ref:`prepare_data` and -:ref:`setup` manually (Lightning ensures -the method runs on the correct devices). - -.. code-block:: python - - dm = MNISTDataModule() - dm.prepare_data() - dm.setup(stage="fit") - - model = Model(num_classes=dm.num_classes, width=dm.width, vocab=dm.vocab) - trainer.fit(model, dm) - - dm.setup(stage="test") - trainer.test(datamodule=dm) - ----------------- - -***************************** -DataModules without Lightning -***************************** -You can of course use DataModules in plain PyTorch code as well. - -.. code-block:: python - - # download, etc... - dm = MNISTDataModule() - dm.prepare_data() - - # splits/transforms - dm.setup(stage="fit") - - # use data - for batch in dm.train_dataloader(): - ... - - for batch in dm.val_dataloader(): - ... - - dm.teardown(stage="fit") - - # lazy load test data - dm.setup(stage="test") - for batch in dm.test_dataloader(): - ... - - dm.teardown(stage="test") - -But overall, DataModules encourage reproducibility by allowing all details of a dataset to be specified in a unified -structure. - ----------------- - -****************************** -Hyperparameters in DataModules -****************************** -Like LightningModules, DataModules support hyperparameters with the same API. - -.. code-block:: python - - import pytorch_lightning as pl - - - class CustomDataModule(pl.LightningDataModule): - def __init__(self, *args, **kwargs): - super().__init__() - self.save_hyperparameters() - - def configure_optimizers(self): - # access the saved hyperparameters - opt = optim.Adam(self.parameters(), lr=self.hparams.lr) - -Refer to ``save_hyperparameters`` in :doc:`lightning module <../common/lightning_module>` for more details. - - ----- - -.. include:: ../extensions/datamodules_state.rst diff --git a/docs/_sources/debug/debugging.rst.txt b/docs/_sources/debug/debugging.rst.txt deleted file mode 100644 index 8fd78d6..0000000 --- a/docs/_sources/debug/debugging.rst.txt +++ /dev/null @@ -1,41 +0,0 @@ -.. _debugging: - -################ -Debug your model -################ - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Learn the basics of model debugging. - :col_css: col-md-4 - :button_link: debugging_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Learn to debug machine learning operations - :col_css: col-md-4 - :button_link: debugging_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Advanced - :description: Learn to debug distributed models - :col_css: col-md-4 - :button_link: debugging_advanced.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/debug/debugging_advanced.rst.txt b/docs/_sources/debug/debugging_advanced.rst.txt deleted file mode 100644 index 0c1685a..0000000 --- a/docs/_sources/debug/debugging_advanced.rst.txt +++ /dev/null @@ -1,44 +0,0 @@ -:orphan: - -.. _debugging_advanced: - -########################### -Debug your model (advanced) -########################### -**Audience**: Users who want to debug distributed models. - ----- - -************************ -Debug distributed models -************************ -To debug a distributed model, we recommend you debug it locally by running the distributed version on CPUs: - -.. code-block:: python - - trainer = Trainer(accelerator="cpu", strategy="ddp", devices=2) - -On the CPU, you can use `pdb `_ or `breakpoint() `_ -or use regular print statements. - -.. testcode:: - - class LitModel(LightningModule): - def training_step(self, batch, batch_idx): - - debugging_message = ... - print(f"RANK - {self.trainer.global_rank}: {debugging_message}") - - if self.trainer.global_rank == 0: - import pdb - - pdb.set_trace() - - # to prevent other processes from moving forward until all processes are in sync - self.trainer.strategy.barrier() - -When everything works, switch back to GPU by changing only the accelerator. - -.. code-block:: python - - trainer = Trainer(accelerator="gpu", strategy="ddp", devices=2) diff --git a/docs/_sources/debug/debugging_basic.rst.txt b/docs/_sources/debug/debugging_basic.rst.txt deleted file mode 100644 index 147285f..0000000 --- a/docs/_sources/debug/debugging_basic.rst.txt +++ /dev/null @@ -1,161 +0,0 @@ -:orphan: - -.. _debugging_basic: - -######################## -Debug your model (basic) -######################## -**Audience**: Users who want to learn the basics of debugging models. - -.. raw:: html - - - ----- - -********************************** -How does Lightning help me debug ? -********************************** -The Lightning Trainer has *a lot* of arguments devoted to maximizing your debugging productivity. - ----- - -**************** -Set a breakpoint -**************** -A breakpoint stops your code execution so you can inspect variables, etc... and allow your code to execute one line at a time. - -.. code:: python - - def function_to_debug(): - x = 2 - - # set breakpoint - import pdb - - pdb.set_trace() - y = x ** 2 - -In this example, the code will stop before executing the ``y = x**2`` line. - ----- - -************************************ -Run all your model code once quickly -************************************ -If you've ever trained a model for days only to crash during validation or testing then this trainer argument is about to become your best friend. - -The :paramref:`~pytorch_lightning.trainer.trainer.Trainer.fast_dev_run` argument in the trainer runs 5 batch of training, validation, test and prediction data through your trainer to see if there are any bugs: - -.. code:: python - - Trainer(fast_dev_run=True) - -To change how many batches to use, change the argument to an integer. Here we run 7 batches of each: - -.. code:: python - - Trainer(fast_dev_run=7) - - -.. note:: - - This argument will disable tuner, checkpoint callbacks, early stopping callbacks, - loggers and logger callbacks like :class:`~pytorch_lightning.callbacks.lr_monitor.LearningRateMonitor` and - :class:`~pytorch_lightning.callbacks.device_stats_monitor.DeviceStatsMonitor`. - ----- - -************************ -Shorten the epoch length -************************ -Sometimes it's helpful to only use a fraction of your training, val, test, or predict data (or a set number of batches). -For example, you can use 20% of the training set and 1% of the validation set. - -On larger datasets like Imagenet, this can help you debug or test a few things faster than waiting for a full epoch. - -.. testcode:: - - # use only 10% of training data and 1% of val data - trainer = Trainer(limit_train_batches=0.1, limit_val_batches=0.01) - - # use 10 batches of train and 5 batches of val - trainer = Trainer(limit_train_batches=10, limit_val_batches=5) - ----- - -****************** -Run a Sanity Check -****************** -Lightning runs **2** steps of validation in the beginning of training. -This avoids crashing in the validation loop sometime deep into a lengthy training loop. - -(See: :paramref:`~pytorch_lightning.trainer.trainer.Trainer.num_sanity_val_steps` -argument of :class:`~pytorch_lightning.trainer.trainer.Trainer`) - -.. testcode:: - - trainer = Trainer(num_sanity_val_steps=2) - ----- - -************************************* -Print LightningModule weights summary -************************************* -Whenever the ``.fit()`` function gets called, the Trainer will print the weights summary for the LightningModule. - -.. code:: python - - trainer.fit(...) - -this generate a table like: - -.. code-block:: text - - | Name | Type | Params - ---------------------------------- - 0 | net | Sequential | 132 K - 1 | net.0 | Linear | 131 K - 2 | net.1 | BatchNorm1d | 1.0 K - -To add the child modules to the summary add a :class:`~pytorch_lightning.callbacks.model_summary.ModelSummary`: - -.. testcode:: - - from pytorch_lightning.callbacks import ModelSummary - - trainer = Trainer(callbacks=[ModelSummary(max_depth=-1)]) - -To turn off the autosummary use: - -.. code:: python - - Trainer(enable_model_summary=False) - ----- - -*********************************** -Print input output layer dimensions -*********************************** -Another debugging tool is to display the intermediate input- and output sizes of all your layers by setting the -``example_input_array`` attribute in your LightningModule. - -.. code-block:: python - - class LitModel(LightningModule): - def __init__(self, *args, **kwargs): - self.example_input_array = torch.Tensor(32, 1, 28, 28) - -With the input array, the summary table will include the input and output layer dimensions: - -.. code-block:: text - - | Name | Type | Params | In sizes | Out sizes - -------------------------------------------------------------- - 0 | net | Sequential | 132 K | [10, 256] | [10, 512] - 1 | net.0 | Linear | 131 K | [10, 256] | [10, 512] - 2 | net.1 | BatchNorm1d | 1.0 K | [10, 512] | [10, 512] - -when you call ``.fit()`` on the Trainer. This can help you find bugs in the composition of your layers. diff --git a/docs/_sources/debug/debugging_intermediate.rst.txt b/docs/_sources/debug/debugging_intermediate.rst.txt deleted file mode 100644 index da8eb59..0000000 --- a/docs/_sources/debug/debugging_intermediate.rst.txt +++ /dev/null @@ -1,79 +0,0 @@ -:orphan: - -.. _debugging_intermediate: - - -############################### -Debug your model (intermediate) -############################### -**Audience**: Users who want to debug their ML code - ----- - -*************************** -Why should I debug ML code? -*************************** -Machine learning code requires debugging mathematical correctness, which is not something non-ML code has to deal with. Lightning implements a few best-practice techniques to give all users, expert level ML debugging abilities. - ----- - -************************************** -Overfit your model on a Subset of Data -************************************** -A good debugging technique is to take a tiny portion of your data (say 2 samples per class), -and try to get your model to overfit. If it can't, it's a sign it won't work with large datasets. - -(See: :paramref:`~pytorch_lightning.trainer.trainer.Trainer.overfit_batches` -argument of :class:`~pytorch_lightning.trainer.trainer.Trainer`) - -.. testcode:: - - # use only 1% of training data (and turn off validation) - trainer = Trainer(overfit_batches=0.01) - - # similar, but with a fixed 10 batches - trainer = Trainer(overfit_batches=10) - -When using this argument, the validation loop will be disabled. We will also replace the sampler -in the training set to turn off shuffle for you. - ----- - -******************************** -Look-out for exploding gradients -******************************** -One major problem that plagues models is exploding gradients. Gradient norm is one technique that can help keep gradients from exploding. - -.. testcode:: - - # the 2-norm - trainer = Trainer(track_grad_norm=2) - -This will plot the 2-norm to your experiment manager. If you notice the norm is going up, there's a good chance your gradients are/will explode. - -One technique to stop exploding gradients is to clip the gradient - -.. testcode:: - - # DEFAULT (ie: don't clip) - trainer = Trainer(gradient_clip_val=0) - - # clip gradients' global norm to <=0.5 using gradient_clip_algorithm='norm' by default - trainer = Trainer(gradient_clip_val=0.5) - - # clip gradients' maximum magnitude to <=0.5 - trainer = Trainer(gradient_clip_val=0.5, gradient_clip_algorithm="value") - ----- - -************************* -Detect autograd anomalies -************************* -Lightning helps you detect anomalies in the PyTorh autograd engine via PyTorch's built-in -`Anomaly Detection Context-manager `_. - -Enable it via the **detect_anomaly** trainer argument: - -.. testcode:: - - trainer = Trainer(detect_anomaly=True) diff --git a/docs/_sources/deploy/production.rst.txt b/docs/_sources/deploy/production.rst.txt deleted file mode 100644 index 686fcc5..0000000 --- a/docs/_sources/deploy/production.rst.txt +++ /dev/null @@ -1,79 +0,0 @@ -.. _production_inference: - -############################# -Deploy models into production -############################# - -****** -Basics -****** - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Learn the basics of predicting with Lightning - :col_css: col-md-6 - :button_link: production_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Learn to remove the Lightning dependencies and use pure PyTorch for prediction. - :col_css: col-md-6 - :button_link: production_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
- ----- - -******** -Advanced -******** - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Deploy with ONNX - :description: Optimize models for enterprise-scale production environments with ONNX. - :col_css: col-md-4 - :button_link: production_advanced.html - :height: 180 - :tag: advanced - -.. displayitem:: - :header: Deploy with torchscript - :description: Optimize models for enterprise-scale production environments with torchscript. - :col_css: col-md-4 - :button_link: production_advanced_2.html - :height: 180 - :tag: advanced - -.. displayitem:: - :header: Compress models for fast inference - :description: Compress models for fast inference for deployment with Quantization and Pruning. - :col_css: col-md-4 - :button_link: ../advanced/pruning_quantization.html - :height: 180 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/deploy/production_advanced.rst.txt b/docs/_sources/deploy/production_advanced.rst.txt deleted file mode 100644 index 750355d..0000000 --- a/docs/_sources/deploy/production_advanced.rst.txt +++ /dev/null @@ -1,60 +0,0 @@ -######################################## -Deploy models into production (advanced) -######################################## -**Audience**: Machine learning engineers optimizing models for enterprise-scale production environments. - ----- - -************************** -Compile your model to ONNX -************************** -`ONNX `_ is a package developed by Microsoft to optimize inference. ONNX allows the model to be independent of PyTorch and run on any ONNX Runtime. - -To export your model to ONNX format call the :meth:`~pytorch_lightning.core.lightning.LightningModule.to_onnx` function on your :class:`~pytorch_lightning.core.lightning.LightningModule` with the ``filepath`` and ``input_sample``. - -.. code-block:: python - - class SimpleModel(LightningModule): - def __init__(self): - super().__init__() - self.l1 = torch.nn.Linear(in_features=64, out_features=4) - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - - # create the model - model = SimpleModel() - filepath = "model.onnx" - input_sample = torch.randn((1, 64)) - model.to_onnx(filepath, input_sample, export_params=True) - -You can also skip passing the input sample if the ``example_input_array`` property is specified in your :class:`~pytorch_lightning.core.lightning.LightningModule`. - -.. code-block:: python - - class SimpleModel(LightningModule): - def __init__(self): - super().__init__() - self.l1 = torch.nn.Linear(in_features=64, out_features=4) - self.example_input_array = torch.randn(7, 64) - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - - # create the model - model = SimpleModel() - filepath = "model.onnx" - model.to_onnx(filepath, export_params=True) - -Once you have the exported model, you can run it on your ONNX runtime in the following way: - -.. code-block:: python - - import onnxruntime - - ort_session = onnxruntime.InferenceSession(filepath) - input_name = ort_session.get_inputs()[0].name - ort_inputs = {input_name: np.random.randn(1, 64)} - ort_outs = ort_session.run(None, ort_inputs) diff --git a/docs/_sources/deploy/production_advanced_2.rst.txt b/docs/_sources/deploy/production_advanced_2.rst.txt deleted file mode 100644 index e86aee8..0000000 --- a/docs/_sources/deploy/production_advanced_2.rst.txt +++ /dev/null @@ -1,69 +0,0 @@ -:orphan: - -######################################## -Deploy models into production (advanced) -######################################## -**Audience**: Machine learning engineers optimizing models for enterprise-scale production environments. - ----- - -********************************* -Compile your model to TorchScript -********************************* -`TorchScript `_ allows you to serialize your models in a way that it can be loaded in non-Python environments. -The ``LightningModule`` has a handy method :meth:`~pytorch_lightning.core.lightning.LightningModule.to_torchscript` that returns a scripted module which you -can save or directly use. - -.. testcode:: python - - class SimpleModel(LightningModule): - def __init__(self): - super().__init__() - self.l1 = torch.nn.Linear(in_features=64, out_features=4) - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - - # create the model - model = SimpleModel() - script = model.to_torchscript() - - # save for use in production environment - torch.jit.save(script, "model.pt") - -It is recommended that you install the latest supported version of PyTorch to use this feature without limitations. - -Once you have the exported model, you can run it in Pytorch or C++ runtime: - -.. code-block:: python - - inp = torch.rand(1, 64) - scripted_module = torch.jit.load("model.pt") - output = scripted_module(inp) - - -If you want to script a different method, you can decorate the method with :func:`torch.jit.export`: - -.. code-block:: python - - class LitMCdropoutModel(pl.LightningModule): - def __init__(self, model, mc_iteration): - super().__init__() - self.model = model - self.dropout = nn.Dropout() - self.mc_iteration = mc_iteration - - @torch.jit.export - def predict_step(self, batch, batch_idx): - # enable Monte Carlo Dropout - self.dropout.train() - - # take average of `self.mc_iteration` iterations - pred = [self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)] - pred = torch.vstack(pred).mean(dim=0) - return pred - - - model = LitMCdropoutModel(...) - script = model.to_torchscript(file_path="model.pt", method="script") diff --git a/docs/_sources/deploy/production_basic.rst.txt b/docs/_sources/deploy/production_basic.rst.txt deleted file mode 100644 index 00e9caa..0000000 --- a/docs/_sources/deploy/production_basic.rst.txt +++ /dev/null @@ -1,80 +0,0 @@ -##################################### -Deploy models into production (basic) -##################################### -**Audience**: All users. - ----- - -***************************** -Load a checkpoint and predict -***************************** -The easiest way to use a model for predictions is to load the weights using **load_from_checkpoint** found in the LightningModule. - -.. code-block:: python - - model = LitModel.load_from_checkpoint("best_model.ckpt") - model.eval() - x = torch.randn(1, 64) - - with torch.no_grad(): - y_hat = model(x) - ----- - -************************************** -Predict step with your LightningModule -************************************** -Loading a checkpoint and predicting still leaves you with a lot of boilerplate around the predict epoch. The **predict step** in the LightningModule removes this boilerplate. - -.. code-block:: python - - class MyModel(LightningModule): - def predict_step(self, batch, batch_idx, dataloader_idx=0): - return self(batch) - -And pass in any dataloader to the Lightning Trainer: - -.. code-block:: python - - data_loader = DataLoader(...) - model = MyModel() - trainer = Trainer() - predictions = trainer.predict(model, data_loader) - ----- - -******************************** -Enable complicated predict logic -******************************** -When you need to add complicated pre-processing or post-processing logic to your data use the predict step. For example here we do `Monte Carlo Dropout `_ for predictions: - -.. code-block:: python - - class LitMCdropoutModel(pl.LightningModule): - def __init__(self, model, mc_iteration): - super().__init__() - self.model = model - self.dropout = nn.Dropout() - self.mc_iteration = mc_iteration - - def predict_step(self, batch, batch_idx): - # enable Monte Carlo Dropout - self.dropout.train() - - # take average of `self.mc_iteration` iterations - pred = [self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)] - pred = torch.vstack(pred).mean(dim=0) - return pred - ----- - -**************************** -Enable distributed inference -**************************** -By using the predict step in Lightning you get free distributed inference - - -.. code-block:: python - - trainer = Trainer(devices=8, accelerator="gpu") - predictions = trainer.predict(model, data_loader) diff --git a/docs/_sources/deploy/production_intermediate.rst.txt b/docs/_sources/deploy/production_intermediate.rst.txt deleted file mode 100644 index eacb03d..0000000 --- a/docs/_sources/deploy/production_intermediate.rst.txt +++ /dev/null @@ -1,99 +0,0 @@ -############################################ -Deploy models into production (intermediate) -############################################ -**Audience**: Researchers and MLEs looking to use their models for predictions without Lightning dependencies. - ----- - -********************* -Use PyTorch as normal -********************* -If you prefer to use PyTorch directly, feel free to use any Lightning checkpoint without Lightning. - -.. code-block:: python - - import torch - - model = torch.load("path/to/lightning/checkpoint.ckpt") - model.eval() - -You can also pull out the specific modules you want out of the checkpoint: - -.. code-block:: python - - model = torch.load("path/to/lightning/checkpoint.ckpt") - encoder = model["encoder"] - encoder.eval() - ----- - -******************************************** -Extract nn.Module from Lightning checkpoints -******************************************** -You can also load the saved checkpoint and use it as a regular :class:`torch.nn.Module`. You can extract all your :class:`torch.nn.Module` -and load the weights using the checkpoint saved using LightningModule after training. For this, we recommend copying the exact implementation -from your LightningModule ``init`` and ``forward`` method. - -.. code-block:: python - - class Encoder(nn.Module): - ... - - - class Decoder(nn.Module): - ... - - - class AutoEncoderProd(nn.Module): - def __init__(self): - super().__init__() - self.encoder = Encoder() - self.decoder = Decoder() - - def forward(self, x): - return self.encoder(x) - - - class AutoEncoderSystem(LightningModule): - def __init__(self): - super().__init__() - self.auto_encoder = AutoEncoderProd() - - def forward(self, x): - return self.auto_encoder.encoder(x) - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.auto_encoder.encoder(x) - y_hat = self.auto_encoder.decoder(y_hat) - loss = ... - return loss - - - # train it - trainer = Trainer(devices=2, accelerator="gpu", strategy="ddp") - model = AutoEncoderSystem() - trainer.fit(model, train_dataloader, val_dataloader) - trainer.save_checkpoint("best_model.ckpt") - - - # create the PyTorch model and load the checkpoint weights - model = AutoEncoderProd() - checkpoint = torch.load("best_model.ckpt") - hyper_parameters = checkpoint["hyper_parameters"] - - # if you want to restore any hyperparameters, you can pass them too - model = AutoEncoderProd(**hyper_parameters) - - state_dict = checkpoint["state_dict"] - - # update keys by dropping `auto_encoder.` - for key in list(model_weights): - model_weights[key.replace("auto_encoder.", "")] = model_weights.pop(key) - - model.load_state_dict(model_weights) - model.eval() - x = torch.randn(1, 64) - - with torch.no_grad(): - y_hat = model(x) diff --git a/docs/_sources/ecosystem/asr_nlp_tts.rst.txt b/docs/_sources/ecosystem/asr_nlp_tts.rst.txt deleted file mode 100644 index b624696..0000000 --- a/docs/_sources/ecosystem/asr_nlp_tts.rst.txt +++ /dev/null @@ -1,816 +0,0 @@ -:orphan: - -################# -Conversational AI -################# - -These are amazing ecosystems to help with Automatic Speech Recognition (ASR), Natural Language Processing (NLP), and Text to speech (TTS). - ----- - -**** -NeMo -**** - -`NVIDIA NeMo `_ is a toolkit for building new State-of-the-Art -Conversational AI models. NeMo has separate collections for Automatic Speech Recognition (ASR), -Natural Language Processing (NLP), and Text-to-Speech (TTS) models. Each collection consists of -prebuilt modules that include everything needed to train on your data. -Every module can easily be customized, extended, and composed to create new Conversational AI -model architectures. - -Conversational AI architectures are typically very large and require a lot of data and compute -for training. NeMo uses PyTorch Lightning for easy and performant multi-GPU/multi-node -mixed-precision training. - -.. note:: Every NeMo model is a LightningModule that comes equipped with all supporting infrastructure for training and reproducibility. - ----------- - -NeMo Models -=========== - -NeMo Models contain everything needed to train and reproduce state of the art Conversational AI -research and applications, including: - -- neural network architectures -- datasets/data loaders -- data preprocessing/postprocessing -- data augmentors -- optimizers and schedulers -- tokenizers -- language models - -NeMo uses `Hydra `_ for configuring both NeMo models and the PyTorch Lightning Trainer. -Depending on the domain and application, many different AI libraries will have to be configured -to build the application. Hydra makes it easy to bring all of these libraries together -so that each can be configured from .yaml or the Hydra CLI. - -.. note:: Every NeMo model has an example configuration file and a corresponding script that contains all configurations needed for training. - -The end result of using NeMo, Pytorch Lightning, and Hydra is that -NeMo models all have the same look and feel. This makes it easy to do Conversational AI research -across multiple domains. NeMo models are also fully compatible with the PyTorch ecosystem. - -Installing NeMo ---------------- - -Before installing NeMo, please install Cython first. - -.. code-block:: bash - - pip install Cython - -For ASR and TTS models, also install these linux utilities. - -.. code-block:: bash - - apt-get update && apt-get install -y libsndfile1 ffmpeg - -Then installing the latest NeMo release is a simple pip install. - -.. code-block:: bash - - pip install nemo_toolkit[all]==1.0.0b1 - -To install the main branch from GitHub: - -.. code-block:: bash - - python -m pip install git+https://github.com/NVIDIA/NeMo.git@main#egg=nemo_toolkit[all] - -To install from a local clone of NeMo: - -.. code-block:: bash - - ./reinstall.sh # from cloned NeMo's git root - -For Docker users, the NeMo container is available on -`NGC `_. - -.. code-block:: bash - - docker pull nvcr.io/nvidia/nemo:v1.0.0b1 - -.. code-block:: bash - - docker run --runtime=nvidia -it --rm -v --shm-size=8g -p 8888:8888 -p 6006:6006 --ulimit memlock=-1 --ulimit stack=67108864 nvcr.io/nvidia/nemo:v1.0.0b1 - -Experiment Manager ------------------- - -NeMo's Experiment Manager leverages PyTorch Lightning for model checkpointing, -TensorBoard Logging, and Weights and Biases logging. The Experiment Manager is included by default -in all NeMo example scripts. - -.. code-block:: python - - exp_manager(trainer, cfg.get("exp_manager", None)) - -And is configurable via .yaml with Hydra. - -.. code-block:: bash - - exp_manager: - exp_dir: null - name: *name - create_tensorboard_logger: True - create_checkpoint_callback: True - -Optionally launch Tensorboard to view training results in ./nemo_experiments (by default). - -.. code-block:: bash - - tensorboard --bind_all --logdir nemo_experiments - --------- - -Automatic Speech Recognition (ASR) -================================== - -Everything needed to train Convolutional ASR models is included with NeMo. -NeMo supports multiple Speech Recognition architectures, including Jasper and QuartzNet. -`NeMo Speech Models `_ -can be trained from scratch on custom datasets or -fine-tuned using pre-trained checkpoints trained on thousands of hours of audio -that can be restored for immediate use. - -Some typical ASR tasks are included with NeMo: - -- `Audio transcription `_ -- `Byte Pair/Word Piece Training `_ -- `Speech Commands `_ -- `Voice Activity Detection `_ -- `Speaker Recognition `_ - -See this `asr notebook `_ -for a full tutorial on doing ASR with NeMo, PyTorch Lightning, and Hydra. - -Specify ASR Model Configurations with YAML File ------------------------------------------------ - -NeMo Models and the PyTorch Lightning Trainer can be fully configured from .yaml files using Hydra. - -See this `asr config `_ -for the entire speech to text .yaml file. - -.. code-block:: yaml - - # configure the PyTorch Lightning Trainer - trainer: - gpus: 0 # number of gpus - max_epochs: 5 - max_steps: null # computed at runtime if not set - num_nodes: 1 - accelerator: ddp - ... - # configure the ASR model - model: - ... - encoder: - cls: nemo.collections.asr.modules.ConvASREncoder - params: - feat_in: *n_mels - activation: relu - conv_mask: true - - jasper: - - filters: 128 - repeat: 1 - kernel: [11] - stride: [1] - dilation: [1] - dropout: *dropout - ... - # all other configuration, data, optimizer, preprocessor, etc - ... - -Developing ASR Model From Scratch ---------------------------------- - -`speech_to_text.py `_ - -.. code-block:: python - - # hydra_runner calls hydra.main and is useful for multi-node experiments - @hydra_runner(config_path="conf", config_name="config") - def main(cfg): - trainer = Trainer(**cfg.trainer) - asr_model = EncDecCTCModel(cfg.model, trainer) - trainer.fit(asr_model) - - -Hydra makes every aspect of the NeMo model, -including the PyTorch Lightning Trainer, customizable from the command line. - -.. code-block:: bash - - python NeMo/examples/asr/speech_to_text.py --config-name=quartznet_15x5 \ - trainer.accelerator=gpu \ - trainer.devices=4 \ - trainer.max_epochs=128 \ - +trainer.precision=16 \ - model.train_ds.manifest_filepath=/librispeech-train-all.json \ - model.validation_ds.manifest_filepath=/librispeech-dev-other.json \ - model.train_ds.batch_size=64 \ - +model.validation_ds.num_workers=16 \ - +model.train_ds.num_workers=16 - -.. note:: Training NeMo ASR models can take days/weeks so it is highly recommended to use multiple GPUs and multiple nodes with the PyTorch Lightning Trainer. - - -Using State-Of-The-Art Pre-trained ASR Model --------------------------------------------- - -Transcribe audio with QuartzNet model pretrained on ~3300 hours of audio. - -.. code-block:: python - - quartznet = EncDecCTCModel.from_pretrained("QuartzNet15x5Base-En") - - files = ["path/to/my.wav"] # file duration should be less than 25 seconds - - for fname, transcription in zip(files, quartznet.transcribe(paths2audio_files=files)): - print(f"Audio in {fname} was recognized as: {transcription}") - -To see the available pretrained checkpoints: - -.. code-block:: python - - EncDecCTCModel.list_available_models() - -NeMo ASR Model Under the Hood ------------------------------ - -Any aspect of ASR training or model architecture design can easily be customized -with PyTorch Lightning since every NeMo model is a Lightning Module. - -.. code-block:: python - - class EncDecCTCModel(ASRModel): - """Base class for encoder decoder CTC-based models.""" - - ... - - def forward(self, input_signal, input_signal_length): - processed_signal, processed_signal_len = self.preprocessor( - input_signal=input_signal, - length=input_signal_length, - ) - # Spec augment is not applied during evaluation/testing - if self.spec_augmentation is not None and self.training: - processed_signal = self.spec_augmentation(input_spec=processed_signal) - encoded, encoded_len = self.encoder(audio_signal=processed_signal, length=processed_signal_len) - log_probs = self.decoder(encoder_output=encoded) - greedy_predictions = log_probs.argmax(dim=-1, keepdim=False) - return log_probs, encoded_len, greedy_predictions - - # PTL-specific methods - def training_step(self, batch, batch_nb): - audio_signal, audio_signal_len, transcript, transcript_len = batch - log_probs, encoded_len, predictions = self.forward( - input_signal=audio_signal, input_signal_length=audio_signal_len - ) - loss_value = self.loss( - log_probs=log_probs, targets=transcript, input_lengths=encoded_len, target_lengths=transcript_len - ) - wer_num, wer_denom = self._wer(predictions, transcript, transcript_len) - self.log_dict( - { - "train_loss": loss_value, - "training_batch_wer": wer_num / wer_denom, - "learning_rate": self._optimizer.param_groups[0]["lr"], - } - ) - return loss_value - -Neural Types in NeMo ASR ------------------------- - -NeMo Models and Neural Modules come with Neural Type checking. -Neural type checking is extremely useful when combining many different neural -network architectures for a production-grade application. - -.. code-block:: python - - @property - def input_types(self) -> Optional[Dict[str, NeuralType]]: - if hasattr(self.preprocessor, "_sample_rate"): - audio_eltype = AudioSignal(freq=self.preprocessor._sample_rate) - else: - audio_eltype = AudioSignal() - return { - "input_signal": NeuralType(("B", "T"), audio_eltype), - "input_signal_length": NeuralType(tuple("B"), LengthsType()), - } - - - @property - def output_types(self) -> Optional[Dict[str, NeuralType]]: - return { - "outputs": NeuralType(("B", "T", "D"), LogprobsType()), - "encoded_lengths": NeuralType(tuple("B"), LengthsType()), - "greedy_predictions": NeuralType(("B", "T"), LabelsType()), - } - --------- - -Natural Language Processing (NLP) -================================= - -Everything needed to finetune BERT-like language models for NLP tasks is included with NeMo. -`NeMo NLP Models `_ -include `HuggingFace Transformers `_ -and `NVIDIA Megatron-LM `_ BERT and Bio-Megatron models. -NeMo can also be used for pretraining BERT-based language models from HuggingFace. - -Any of the HuggingFace encoders or Megatron-LM encoders can easily be used for the NLP tasks -that are included with NeMo: - -- `Glue Benchmark (All tasks) `_ -- `Intent Slot Classification `_ -- `Language Modeling (BERT Pretraining) `_ -- `Question Answering `_ -- `Text Classification `_ (including Sentiment Analysis) -- `Token Classification `_ (including Named Entity Recognition) -- `Punctuation and Capitalization `_ - -Named Entity Recognition (NER) ------------------------------- - -NER (or more generally token classification) is the NLP task of detecting and classifying key information (entities) in text. -This task is very popular in Healthcare and Finance. In finance, for example, it can be important to identify -geographical, geopolitical, organizational, persons, events, and natural phenomenon entities. -See this `NER notebook `_ -for a full tutorial on doing NER with NeMo, PyTorch Lightning, and Hydra. - -Specify NER Model Configurations with YAML File ------------------------------------------------ - -.. note:: NeMo Models and the PyTorch Lightning Trainer can be fully configured from .yaml files using Hydra. - -See this `token classification config `_ -for the entire NER (token classification) .yaml file. - -.. code-block:: yaml - - # configure any argument of the PyTorch Lightning Trainer - trainer: - gpus: 1 # the number of gpus, 0 for CPU - num_nodes: 1 - max_epochs: 5 - ... - # configure any aspect of the token classification model here - model: - dataset: - data_dir: ??? # /path/to/data - class_balancing: null # choose from [null, weighted_loss]. Weighted_loss enables the weighted class balancing of the loss, may be used for handling unbalanced classes - max_seq_length: 128 - ... - tokenizer: - tokenizer_name: ${model.language_model.pretrained_model_name} # or sentencepiece - vocab_file: null # path to vocab file - ... - # the language model can be from HuggingFace or Megatron-LM - language_model: - pretrained_model_name: bert-base-uncased - lm_checkpoint: null - ... - # the classifier for the downstream task - head: - num_fc_layers: 2 - fc_dropout: 0.5 - activation: 'relu' - ... - # all other configuration: train/val/test/ data, optimizer, experiment manager, etc - ... - -Developing NER Model From Scratch ---------------------------------- - -`token_classification.py `_ - -.. code-block:: python - - # hydra_runner calls hydra.main and is useful for multi-node experiments - @hydra_runner(config_path="conf", config_name="token_classification_config") - def main(cfg: DictConfig) -> None: - trainer = pl.Trainer(**cfg.trainer) - model = TokenClassificationModel(cfg.model, trainer=trainer) - trainer.fit(model) - -After training, we can do inference with the saved NER model using PyTorch Lightning. - -Inference from file: - -.. code-block:: python - - gpu = 1 if cfg.trainer.gpus != 0 else 0 - trainer = pl.Trainer(accelerator="gpu", devices=gpu) - model.set_trainer(trainer) - model.evaluate_from_file( - text_file=os.path.join(cfg.model.dataset.data_dir, cfg.model.validation_ds.text_file), - labels_file=os.path.join(cfg.model.dataset.data_dir, cfg.model.validation_ds.labels_file), - output_dir=exp_dir, - add_confusion_matrix=True, - normalize_confusion_matrix=True, - ) - -Or we can run inference on a few examples: - -.. code-block:: python - - queries = ["we bought four shirts from the nvidia gear store in santa clara.", "Nvidia is a company in Santa Clara."] - results = model.add_predictions(queries) - - for query, result in zip(queries, results): - logging.info(f"Query : {query}") - logging.info(f"Result: {result.strip()}\n") - -Hydra makes every aspect of the NeMo model, including the PyTorch Lightning Trainer, customizable from the command line. - -.. code-block:: bash - - python token_classification.py \ - model.language_model.pretrained_model_name=bert-base-cased \ - model.head.num_fc_layers=2 \ - model.dataset.data_dir=/path/to/my/data \ - trainer.max_epochs=5 \ - trainer.accelerator=gpu \ - trainer.devices=[0,1] - ------------ - -Tokenizers ----------- - -Tokenization is the process of converting natural language text into integer arrays -which can be used for machine learning. -For NLP tasks, tokenization is an essential part of data preprocessing. -NeMo supports all BERT-like model tokenizers from -`HuggingFace's AutoTokenizer `_ -and also supports `Google's SentencePieceTokenizer `_ -which can be trained on custom data. - -To see the list of supported tokenizers: - -.. code-block:: python - - from nemo.collections import nlp as nemo_nlp - - nemo_nlp.modules.get_tokenizer_list() - -See this `tokenizer notebook `_ -for a full tutorial on using tokenizers in NeMo. - -Language Models ---------------- - -Language models are used to extract information from (tokenized) text. -Much of the state-of-the-art in natural language processing is achieved -by fine-tuning pretrained language models on the downstream task. - -With NeMo, you can either `pretrain `_ -a BERT model on your data or use a pretrained language model from `HuggingFace Transformers `_ -or `NVIDIA Megatron-LM `_. - -To see the list of language models available in NeMo: - -.. code-block:: python - - nemo_nlp.modules.get_pretrained_lm_models_list(include_external=True) - -Easily switch between any language model in the above list by using `.get_lm_model`. - -.. code-block:: python - - nemo_nlp.modules.get_lm_model(pretrained_model_name="distilbert-base-uncased") - -See this `language model notebook `_ -for a full tutorial on using pretrained language models in NeMo. - -Using a Pre-trained NER Model ------------------------------ - -NeMo has pre-trained NER models that can be used -to get started with Token Classification right away. -Models are automatically downloaded from NGC, -cached locally to disk, -and loaded into GPU memory using the `.from_pretrained` method. - -.. code-block:: python - - # load pre-trained NER model - pretrained_ner_model = TokenClassificationModel.from_pretrained(model_name="NERModel") - - # define the list of queries for inference - queries = [ - "we bought four shirts from the nvidia gear store in santa clara.", - "Nvidia is a company.", - "The Adventures of Tom Sawyer by Mark Twain is an 1876 novel about a young boy growing " - + "up along the Mississippi River.", - ] - results = pretrained_ner_model.add_predictions(queries) - - for query, result in zip(queries, results): - print() - print(f"Query : {query}") - print(f"Result: {result.strip()}\n") - -NeMo NER Model Under the Hood ------------------------------ - -Any aspect of NLP training or model architecture design can easily be customized with PyTorch Lightning -since every NeMo model is a Lightning Module. - -.. code-block:: python - - class TokenClassificationModel(ModelPT): - """ - Token Classification Model with BERT, applicable for tasks such as Named Entity Recognition - """ - - ... - - def forward(self, input_ids, token_type_ids, attention_mask): - hidden_states = self.bert_model( - input_ids=input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask - ) - logits = self.classifier(hidden_states=hidden_states) - return logits - - # PTL-specific methods - def training_step(self, batch, batch_idx): - """ - Lightning calls this inside the training loop with the data from the training dataloader - passed in as `batch`. - """ - input_ids, input_type_ids, input_mask, subtokens_mask, loss_mask, labels = batch - logits = self(input_ids=input_ids, token_type_ids=input_type_ids, attention_mask=input_mask) - - loss = self.loss(logits=logits, labels=labels, loss_mask=loss_mask) - self.log_dict({"train_loss": loss, "lr": self._optimizer.param_groups[0]["lr"]}) - return loss - - ... - -Neural Types in NeMo NLP ------------------------- - -NeMo Models and Neural Modules come with Neural Type checking. -Neural type checking is extremely useful when combining many different neural network architectures -for a production-grade application. - -.. code-block:: python - - @property - def input_types(self) -> Optional[Dict[str, NeuralType]]: - return self.bert_model.input_types - - - @property - def output_types(self) -> Optional[Dict[str, NeuralType]]: - return self.classifier.output_types - --------- - -Text-To-Speech (TTS) -==================== - -Everything needed to train TTS models and generate audio is included with NeMo. -`NeMo TTS Models `_ -can be trained from scratch on your own data or pretrained models can be downloaded -automatically. NeMo currently supports a two step inference procedure. -First, a model is used to generate a mel spectrogram from text. -Second, a model is used to generate audio from a mel spectrogram. - -Mel Spectrogram Generators: - -- `Tacotron 2 `_ -- `Glow-TTS `_ - -Audio Generators: - -- Griffin-Lim -- `WaveGlow `_ -- `SqueezeWave `_ - - -Specify TTS Model Configurations with YAML File ------------------------------------------------ - -.. note:: NeMo Models and PyTorch Lightning Trainer can be fully configured from .yaml files using Hydra. - -`tts/conf/glow_tts.yaml `_ - -.. code-block:: yaml - - # configure the PyTorch Lightning Trainer - trainer: - gpus: -1 # number of gpus - max_epochs: 350 - num_nodes: 1 - accelerator: ddp - ... - - # configure the TTS model - model: - ... - encoder: - cls: nemo.collections.tts.modules.glow_tts.TextEncoder - params: - n_vocab: 148 - out_channels: *n_mels - hidden_channels: 192 - filter_channels: 768 - filter_channels_dp: 256 - ... - # all other configuration, data, optimizer, parser, preprocessor, etc - ... - -Developing TTS Model From Scratch ---------------------------------- - -`tts/glow_tts.py `_ - -.. code-block:: python - - # hydra_runner calls hydra.main and is useful for multi-node experiments - @hydra_runner(config_path="conf", config_name="glow_tts") - def main(cfg): - trainer = pl.Trainer(**cfg.trainer) - model = GlowTTSModel(cfg=cfg.model, trainer=trainer) - trainer.fit(model) - -Hydra makes every aspect of the NeMo model, including the PyTorch Lightning Trainer, customizable from the command line. - -.. code-block:: bash - - python NeMo/examples/tts/glow_tts.py \ - trainer.accelerator=gpu \ - trainer.devices=4 \ - trainer.max_epochs=400 \ - ... - train_dataset=/path/to/train/data \ - validation_datasets=/path/to/val/data \ - model.train_ds.batch_size = 64 \ - -.. note:: Training NeMo TTS models from scratch can take days or weeks so it is highly recommended to use multiple GPUs and multiple nodes with the PyTorch Lightning Trainer. - -Using State-Of-The-Art Pre-trained TTS Model --------------------------------------------- - -Generate speech using models trained on `LJSpeech `, -around 24 hours of single speaker data. - -See this `TTS notebook `_ -for a full tutorial on generating speech with NeMo, PyTorch Lightning, and Hydra. - -.. code-block:: python - - # load pretrained spectrogram model - spec_gen = SpecModel.from_pretrained("GlowTTS-22050Hz").cuda() - - # load pretrained Generators - vocoder = WaveGlowModel.from_pretrained("WaveGlow-22050Hz").cuda() - - - def infer(spec_gen_model, vocder_model, str_input): - with torch.no_grad(): - parsed = spec_gen.parse(text_to_generate) - spectrogram = spec_gen.generate_spectrogram(tokens=parsed) - audio = vocoder.convert_spectrogram_to_audio(spec=spectrogram) - if isinstance(spectrogram, torch.Tensor): - spectrogram = spectrogram.to("cpu").numpy() - if len(spectrogram.shape) == 3: - spectrogram = spectrogram[0] - if isinstance(audio, torch.Tensor): - audio = audio.to("cpu").numpy() - return spectrogram, audio - - - text_to_generate = input("Input what you want the model to say: ") - spec, audio = infer(spec_gen, vocoder, text_to_generate) - -To see the available pretrained checkpoints: - -.. code-block:: python - - # spec generator - GlowTTSModel.list_available_models() - - # vocoder - WaveGlowModel.list_available_models() - -NeMo TTS Model Under the Hood ------------------------------ - -Any aspect of TTS training or model architecture design can easily -be customized with PyTorch Lightning since every NeMo model is a LightningModule. - -`glow_tts.py `_ - -.. code-block:: python - - class GlowTTSModel(SpectrogramGenerator): - """ - GlowTTS model used to generate spectrograms from text - Consists of a text encoder and an invertible spectrogram decoder - """ - - ... - # NeMo models come with neural type checking - @typecheck( - input_types={ - "x": NeuralType(("B", "T"), TokenIndex()), - "x_lengths": NeuralType(("B"), LengthsType()), - "y": NeuralType(("B", "D", "T"), MelSpectrogramType(), optional=True), - "y_lengths": NeuralType(("B"), LengthsType(), optional=True), - "gen": NeuralType(optional=True), - "noise_scale": NeuralType(optional=True), - "length_scale": NeuralType(optional=True), - } - ) - def forward(self, *, x, x_lengths, y=None, y_lengths=None, gen=False, noise_scale=0.3, length_scale=1.0): - if gen: - return self.glow_tts.generate_spect( - text=x, text_lengths=x_lengths, noise_scale=noise_scale, length_scale=length_scale - ) - else: - return self.glow_tts(text=x, text_lengths=x_lengths, spect=y, spect_lengths=y_lengths) - - ... - - def step(self, y, y_lengths, x, x_lengths): - z, y_m, y_logs, logdet, logw, logw_, y_lengths, attn = self( - x=x, x_lengths=x_lengths, y=y, y_lengths=y_lengths, gen=False - ) - - l_mle, l_length, logdet = self.loss( - z=z, - y_m=y_m, - y_logs=y_logs, - logdet=logdet, - logw=logw, - logw_=logw_, - x_lengths=x_lengths, - y_lengths=y_lengths, - ) - - loss = sum([l_mle, l_length]) - - return l_mle, l_length, logdet, loss, attn - - # PTL-specific methods - def training_step(self, batch, batch_idx): - y, y_lengths, x, x_lengths = batch - - y, y_lengths = self.preprocessor(input_signal=y, length=y_lengths) - - l_mle, l_length, logdet, loss, _ = self.step(y, y_lengths, x, x_lengths) - - self.log_dict({"l_mle": l_mle, "l_length": l_length, "logdet": logdet}, prog_bar=True) - return loss - - ... - -Neural Types in NeMo TTS ------------------------- - -NeMo Models and Neural Modules come with Neural Type checking. -Neural type checking is extremely useful when combining many different neural network architectures -for a production-grade application. - -.. code-block:: python - - @typecheck( - input_types={ - "x": NeuralType(("B", "T"), TokenIndex()), - "x_lengths": NeuralType(("B"), LengthsType()), - "y": NeuralType(("B", "D", "T"), MelSpectrogramType(), optional=True), - "y_lengths": NeuralType(("B"), LengthsType(), optional=True), - "gen": NeuralType(optional=True), - "noise_scale": NeuralType(optional=True), - "length_scale": NeuralType(optional=True), - } - ) - def forward(self, *, x, x_lengths, y=None, y_lengths=None, gen=False, noise_scale=0.3, length_scale=1.0): - ... - --------- - -Learn More -========== - -- Watch the `NVIDIA NeMo Intro Video `_ -- Watch the `PyTorch Lightning and NVIDIA NeMo Discussion Video `_ -- Visit the `NVIDIA NeMo Developer Website `_ -- Read the `NVIDIA NeMo PyTorch Blog `_ -- Download pre-trained `ASR `_, `NLP `_, and `TTS `_ models on `NVIDIA NGC `_ to quickly get started with NeMo. -- Become an expert on Building Conversational AI applications with our `tutorials `_, and `example scripts `_, -- See our `developer guide `_ for more information on core NeMo concepts, ASR/NLP/TTS collections, and the NeMo API. - -.. note:: NeMo tutorial notebooks can be run on `Google Colab `_. - -NVIDIA `NeMo `_ is actively being developed on GitHub. -`Contributions `_ are welcome! diff --git a/docs/_sources/ecosystem/bolts.rst.txt b/docs/_sources/ecosystem/bolts.rst.txt deleted file mode 100644 index 56c7768..0000000 --- a/docs/_sources/ecosystem/bolts.rst.txt +++ /dev/null @@ -1,92 +0,0 @@ -:orphan: - -Lightning Bolts -=============== - -`PyTorch Lightning Bolts `_, is our official collection -of prebuilt models across many research domains. - -.. code-block:: bash - - pip install lightning-bolts - -In bolts we have: - -- A collection of pretrained state-of-the-art models. -- A collection of models designed to bootstrap your research. -- A collection of callbacks, transforms, full datasets. -- All models work on CPUs, TPUs, GPUs and 16-bit precision. - ------------------ - -Quality control ---------------- -The Lightning community builds bolts and contributes them to Bolts. -The lightning team guarantees that contributions are: - -- Rigorously Tested (CPUs, GPUs, TPUs). -- Rigorously Documented. -- Standardized via PyTorch Lightning. -- Optimized for speed. -- Checked for correctness. - ---------- - -Example 1: Pretrained, prebuilt models --------------------------------------- - -.. code-block:: python - - from pl_bolts.models import VAE, GPT2, ImageGPT, PixelCNN - from pl_bolts.models.self_supervised import AMDIM, CPCV2, SimCLR, MocoV2 - from pl_bolts.models import LinearRegression, LogisticRegression - from pl_bolts.models.gans import GAN - from pl_bolts.callbacks import PrintTableMetricsCallback - from pl_bolts.datamodules import FashionMNISTDataModule, CIFAR10DataModule, ImagenetDataModule - ------------- - -Example 2: Extend for faster research -------------------------------------- -Bolts are contributed with benchmarks and continuous-integration tests. This means -you can trust the implementations and use them to bootstrap your research much faster. - -.. code-block:: python - - from pl_bolts.models import ImageGPT - from pl_bolts.self_supervised import SimCLR - - - class VideoGPT(ImageGPT): - def training_step(self, batch, batch_idx): - x, y = batch - x = _shape_input(x) - - logits = self.gpt(x) - simclr_features = self.simclr(x) - - # ----------------- - # do something new with GPT logits + simclr_features - # ----------------- - - loss = self.criterion(logits.view(-1, logits.size(-1)), x.view(-1).long()) - - self.log("loss", loss) - return loss - ----------- - -Example 3: Callbacks --------------------- -We also have a collection of callbacks. - -.. code-block:: python - - from pl_bolts.callbacks import PrintTableMetricsCallback - import pytorch_lightning as pl - - trainer = pl.Trainer(callbacks=[PrintTableMetricsCallback()]) - - # loss│train_loss│val_loss│epoch - # ────────────────────────────── - # 2.2541470527648926│2.2541470527648926│2.2158432006835938│0 diff --git a/docs/_sources/ecosystem/community_examples.rst.txt b/docs/_sources/ecosystem/community_examples.rst.txt deleted file mode 100644 index f535857..0000000 --- a/docs/_sources/ecosystem/community_examples.rst.txt +++ /dev/null @@ -1,36 +0,0 @@ -:orphan: - -Community Examples -================== - - -- `Lightning Bolts: Deep Learning components for extending PyTorch Lightning `_. -- `Lightning Flash: Your PyTorch AI Factory - Flash enables you to easily configure and run complex AI recipes `_. -- `Contextual Emotion Detection (DoubleDistilBert) `_ -- `Cotatron: Transcription-Guided Speech Encoder `_ -- `FasterRCNN object detection + Hydra `_ -- `Image Inpainting using Partial Convolutions `_ -- `MNIST on TPU `_ -- `NER (transformers, TPU) `_ -- `NeuralTexture (CVPR) `_ -- `Recurrent Attentive Neural Process `_ -- `Siamese Nets for One-shot Image Recognition `_ -- `Speech Transformers `_ -- `Transformers transfer learning (Huggingface) `_ -- `Transformers text classification `_ -- `VAE Library of over 18+ VAE flavors `_ -- `Transformers Question Answering (SQuAD) `_ -- `Atlas: End-to-End 3D Scene Reconstruction from Posed Images `_ -- `Self-Supervised Representation Learning (MoCo and BYOL) `_ -- `PyTorch-Forecasting: Time series forecasting package `_ -- `Transformers masked language modeling `_ -- `PyTorch Geometric examples with PyTorch Lightning and Hydra `_ -- `PyTorch Tabular: Deep learning with tabular data `_ -- `Asteroid: An audio source separation toolkit for researchers `_ - - -PyTorch Ecosystem Examples -========================== - -- `PyTorch Geometric: Deep learning on graphs and other irregular structures `_. -- `TorchIO, MONAI and Lightning for 3D medical image segmentation `_. diff --git a/docs/_sources/ecosystem/ecosystem-ci.rst.txt b/docs/_sources/ecosystem/ecosystem-ci.rst.txt deleted file mode 100644 index 04ff342..0000000 --- a/docs/_sources/ecosystem/ecosystem-ci.rst.txt +++ /dev/null @@ -1,30 +0,0 @@ -:orphan: - -Ecosystem CI -============ - -`Ecosystem CI `_ automates issue discovery for your projects against Lightning nightly and releases. -It is a lightweight repository that provides easy configuration of Continues Integration running on CPUs and GPUs. -Any user who wants to keep their project aligned with current and future Lightning releases can use the EcoSystem CI to configure their integrations. -Read more: `Stay Ahead of Breaking Changes with the New Lightning Ecosystem CI `_ - --------------- - -*********************** -Integrate a New Project -*********************** - -Follow the instructions below to add a new project to the PyTorch Lightning ecosystem. - -1. Fork the ecosystem CI repository to be able to create a `new Pull Request `_ and work within a specific branch. -2. Create a new config file in ``configs/`` folder and call it ``.yaml``. -3. Define runtime for CPU and link the config for GPU: - For CPU integrations, list OS and Python version combination to be running with GitHub actions. - For GPU integrations, you only add the path to the config (OS/Linux and Python version is fixed) to be running with Azure pipelines. -4. Add a Contact to the ``.github/CODEOWNERS`` list for your organization folder or just a single project. -5. Create a Draft PR with all mentioned requirements. -6. Join our `Slack `_ (Optional) channel ``#alerts-ecosystem-ci`` to be notified if your project is breaking. - - -To learn more about Ecosystem CI, please refer to the `Ecosystem CI repo `_. -Also, note that some particular implementation details described above may evolve over time. diff --git a/docs/_sources/ecosystem/flash.rst.txt b/docs/_sources/ecosystem/flash.rst.txt deleted file mode 100644 index 31c6e9d..0000000 --- a/docs/_sources/ecosystem/flash.rst.txt +++ /dev/null @@ -1,78 +0,0 @@ -:orphan: - -Lightning Flash -=============== - -`Lightning Flash `_ is a high-level deep learning framework for fast prototyping, baselining, fine-tuning, and solving deep learning problems. -Flash makes complex AI recipes for over 15 tasks across 7 data domains accessible to all. -It is built for beginners with a simple API that requires very little deep learning background, and for data scientists, Kagglers, applied ML practitioners, and deep learning researchers that -want a quick way to get a deep learning baseline with advanced features PyTorch Lightning offers. - -.. code-block:: bash - - pip install lightning-flash - ------------------ - -********************************* -Using Lightning Flash in 3 Steps! -********************************* - -1. Load your Data ------------------ - -All data loading in Flash is performed via a ``from_*`` classmethod of a ``DataModule``. -Which ``DataModule`` to use and which ``from_*`` methods are available depends on the task you want to perform. -For example, for image segmentation where your data is stored in folders, you would use the ``SemanticSegmentationData``'s `from_folders `_ method: - -.. code-block:: python - - from flash.image import SemanticSegmentationData - - dm = SemanticSegmentationData.from_folders( - train_folder="data/CameraRGB", - train_target_folder="data/CameraSeg", - val_split=0.1, - image_size=(256, 256), - num_classes=21, - ) - ------------- - -2. Configure your Model ------------------------ - -Our tasks come loaded with pre-trained backbones and (where applicable) heads. -You can view the available backbones to use with your task using `available_backbones `_. -Once you've chosen, create the model: - -.. code-block:: python - - from flash.image import SemanticSegmentation - - print(SemanticSegmentation.available_heads()) - # ['deeplabv3', 'deeplabv3plus', 'fpn', ..., 'unetplusplus'] - - print(SemanticSegmentation.available_backbones("fpn")) - # ['densenet121', ..., 'xception'] # + 113 models - - print(SemanticSegmentation.available_pretrained_weights("efficientnet-b0")) - # ['imagenet', 'advprop'] - - model = SemanticSegmentation(head="fpn", backbone="efficientnet-b0", pretrained="advprop", num_classes=dm.num_classes) - ------------- - -3. Finetune! ------------- - -.. code-block:: python - - from flash import Trainer - - trainer = Trainer(max_epochs=3) - trainer.finetune(model, datamodule=datamodule, strategy="freeze") - trainer.save_checkpoint("semantic_segmentation_model.pt") - - -To learn more about Lightning Flash, please refer to the `Lightning Flash documentation `_. diff --git a/docs/_sources/ecosystem/metrics.rst.txt b/docs/_sources/ecosystem/metrics.rst.txt deleted file mode 100644 index 8ec155f..0000000 --- a/docs/_sources/ecosystem/metrics.rst.txt +++ /dev/null @@ -1,93 +0,0 @@ -:orphan: - -TorchMetrics -============ - -`TorchMetrics `_ is a collection of machine learning metrics for distributed, -scalable PyTorch models and an easy-to-use API to create custom metrics. It has a collection of 60+ PyTorch metrics implementations and -is rigorously tested for all edge cases. - -.. code-block:: bash - - pip install torchmetrics - -In TorchMetrics, we offer the following benefits: - -- A standardized interface to increase reproducibility -- Reduced Boilerplate -- Distributed-training compatible -- Rigorously tested -- Automatic accumulation over batches -- Automatic synchronization across multiple devices - ------------------ - -Example 1: Functional Metrics ------------------------------ - -Below is a simple example for calculating the accuracy using the functional interface: - -.. code-block:: python - - import torch - import torchmetrics - - # simulate a classification problem - preds = torch.randn(10, 5).softmax(dim=-1) - target = torch.randint(5, (10,)) - - acc = torchmetrics.functional.accuracy(preds, target) - ------------- - -Example 2: Module Metrics -------------------------- - -The example below shows how to use the class-based interface: - -.. code-block:: python - - import torch - import torchmetrics - - # initialize metric - metric = torchmetrics.Accuracy() - - n_batches = 10 - for i in range(n_batches): - # simulate a classification problem - preds = torch.randn(10, 5).softmax(dim=-1) - target = torch.randint(5, (10,)) - # metric on current batch - acc = metric(preds, target) - print(f"Accuracy on batch {i}: {acc}") - - # metric on all batches using custom accumulation - acc = metric.compute() - print(f"Accuracy on all data: {acc}") - - # Reseting internal state such that metric ready for new data - metric.reset() - ------------- - -Example 3: TorchMetrics with Lightning --------------------------------------- - -The example below shows how to use a metric in your :doc:`LightningModule <../common/lightning_module>`: - -.. code-block:: python - - class MyModel(LightningModule): - def __init__(self): - ... - self.accuracy = torchmetrics.Accuracy() - - def training_step(self, batch, batch_idx): - x, y = batch - preds = self(x) - ... - # log step metric - self.accuracy(preds, y) - self.log("train_acc_step", self.accuracy, on_epoch=True) - ... diff --git a/docs/_sources/ecosystem/transformers.rst.txt b/docs/_sources/ecosystem/transformers.rst.txt deleted file mode 100644 index b20402a..0000000 --- a/docs/_sources/ecosystem/transformers.rst.txt +++ /dev/null @@ -1,47 +0,0 @@ -:orphan: - -Lightning Transformers -====================== - -`Lightning Transformers `_ offers a flexible interface for training and fine-tuning SOTA Transformer models -using the :doc:`PyTorch Lightning Trainer <../common/trainer>`. - -.. code-block:: bash - - pip install lightning-transformers - -In Lightning Transformers, we offer the following benefits: - -- Powered by `PyTorch Lightning `_ - Accelerators, custom Callbacks, Loggers, and high performance scaling with minimal changes. -- Backed by `HuggingFace Transformers `_ models and datasets, spanning multiple modalities and tasks within NLP/Audio and Vision. -- Task Abstraction for Rapid Research & Experimentation - Build your own custom transformer tasks across all modalities with little friction. -- Powerful config composition backed by `Hydra `_ - simply swap out models, optimizers, schedulers task, and many more configurations without touching the code. -- Seamless Memory and Speed Optimizations - Out-of-the-box training optimizations such as `DeepSpeed ZeRO `_ or `FairScale Sharded Training `_ with no code changes. - ------------------ - -Using Lightning-Transformers ----------------------------- - -Lightning Transformers has a collection of tasks for common NLP problems such as `language_modeling `_, -`translation `_ and more. To use, simply: - -1. Pick a task to train (passed to ``train.py`` as ``task=``) - -2. Pick a dataset (passed to ``train.py`` as ``dataset=``) - -3. Customize the backbone, optimizer, or any component within the config - -4. Add any :doc:`Lightning supported parameters and optimizations <../common/trainer>` - -.. code-block:: bash - - python train.py \ - task= \ - dataset= - backbone.pretrained_model_name_or_path= # Optionally change the HF backbone - optimizer= # Optionally specify optimizer (Default AdamW) - trainer. # Optionally specify Lightning trainer arguments - - -To learn more about Lightning Transformers, please refer to the `Lightning Transformers documentation `_. diff --git a/docs/_sources/expertise_levels.rst.txt b/docs/_sources/expertise_levels.rst.txt deleted file mode 100644 index 9b563f7..0000000 --- a/docs/_sources/expertise_levels.rst.txt +++ /dev/null @@ -1,298 +0,0 @@ -:orphan: - -Level up -======== -Learn enough Lightning to match the level of expertise required by your research or job. - -.. join_slack:: - :align: left - :margin: 30 - ----- - -Basic skills ------------- -Learn the basics of model development with Lightning. Researchers and machine learning engineers should start here. - - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 1: Train a model - :description: Learn the basics of training a model. - :button_link: model/train_model_basic.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :header: Level 2: Add a validation and test set - :description: Add validation and test sets to avoid over/underfitting. - :button_link: levels/basic_level_2.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :header: Level 3: Use pretrained models - :description: Learn how to use pretrained models with Lightning - :button_link: advanced/transfer_learning.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :header: Level 4: Enable script parameters - :description: Add parameters to your script so you can run from the commandline. - :button_link: common/hyperparameters.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :header: Level 5: Understand and visualize your model - :description: Remove bottlenecks and visualize your model - :button_link: levels/basic_level_5.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :description: Use your model for predictions. - :header: Level 6: Predict with your model - :button_link: levels/core_level_6.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. raw:: html - -
-
- ----- - -Intermediate skills -------------------- -Learn to scale up your models and enable collaborative model development at academic or industry research labs. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 7: Interactive cloud development - :description: Learn how to access GPUs and TPUs on the cloud. - :button_link: levels/intermediate_level_7.html - :col_css: col-md-6 - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 8: Train in the background on the cloud - :description: Learn how to run models on the cloud in the background. - :button_link: levels/intermediate_level_8.html - :col_css: col-md-6 - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 9: Modularize your projects - :description: Create DataModules to enable dataset reusability. - :col_css: col-md-6 - :button_link: levels/intermediate_level_9.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 10: Understand your model - :description: Use advanced visuals to find the best performing model. - :col_css: col-md-6 - :button_link: levels/intermediate_level_10.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 11: Explore SOTA scaling techniques - :description: Explore SOTA techniques to help convergence, stability and scalability. - :col_css: col-md-6 - :button_link: levels/intermediate_level_11.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 12: Deploy your models - :description: Learn how to deploy your models with optimizations like ONNX and torchscript. - :col_css: col-md-6 - :button_link: levels/intermediate_level_12.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 13: Optimize training speed - :description: Use advanced profilers to mixed precision to train bigger models, faster. - :col_css: col-md-6 - :button_link: levels/intermediate_level_13.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 14: Run on on-prem clusters - :description: Run on a custom on-prem cluster or SLURM cluster. - :col_css: col-md-6 - :button_link: levels/intermediate_level_14.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
- ----- - -Advanced skills ---------------- -Configure all aspects of Lightning for advanced usecases. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 15: Customize configs to run in production - :description: Enable composable YAMLs - :col_css: col-md-6 - :button_link: levels/advanced_level_15.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 16: Customize the trainer - :description: Inject custom code into the trainer and modify the progress bar. - :col_css: col-md-6 - :button_link: levels/advanced_level_16.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 17: Own the training loop - :description: Learn all the ways of owning your raw PyTorch loops with Lighting. - :col_css: col-md-6 - :button_link: levels/advanced_level_17.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 18: Enable advanced checkpointing - :description: Enable composable or cloud based checkpoints. - :col_css: col-md-6 - :button_link: levels/advanced_level_18.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 19: Explore IPUs - :description: Explore Intelligence Processing Unit (IPU) for model scaling. - :col_css: col-md-6 - :button_link: levels/advanced_level_19.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 20: Explore HPUs - :description: Explore Havana Gaudi Processing Unit (HPU) for model scaling. - :col_css: col-md-6 - :button_link: levels/advanced_level_20.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 21: Master TPUs - :description: Master TPUs and run on cloud TPUs. - :col_css: col-md-6 - :button_link: levels/advanced_level_21.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 22: Reach 1 trillion parameters on GPUs - :description: Scale to 1 trillion params on GPUs. - :col_css: col-md-6 - :button_link: levels/advanced_level_22.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
- ----- - -Expert skills -------------- -Customize and extend Lightning for things like custom hardware or distributed strategies. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 23: Extend the Lightning CLI - :description: Extend the functionality of the Lightning CLI. - :col_css: col-md-6 - :button_link: levels/expert_level_23.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 24: Integrate a custom cluster - :description: Integrate a custom cluster into Lightning. - :col_css: col-md-6 - :button_link: levels/expert_level_24.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 25: Explore fault-tolerance in-depth - :description: Understand the details of fault-tolerance. - :col_css: col-md-6 - :button_link: clouds/fault_tolerant_training_faq.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 26: Make your own profiler - :description: Make your own profiler. - :col_css: col-md-6 - :button_link: tuning/profiler_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 27: Add a new accelerator or Strategy - :description: Integrate a new accelerator or distributed strategy. - :col_css: col-md-6 - :button_link: levels/expert_level_27.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/extensions/accelerator.rst.txt b/docs/_sources/extensions/accelerator.rst.txt deleted file mode 100644 index dd5a067..0000000 --- a/docs/_sources/extensions/accelerator.rst.txt +++ /dev/null @@ -1,130 +0,0 @@ -.. _accelerator: - -########### -Accelerator -########### - -The Accelerator connects a Lightning Trainer to arbitrary hardware (CPUs, GPUs, TPUs, IPUs, ...). -Currently there are accelerators for: - -- CPU -- :doc:`GPU <../accelerators/gpu>` -- :doc:`TPU <../accelerators/tpu>` -- :doc:`IPU <../accelerators/ipu>` -- :doc:`HPU <../accelerators/hpu>` - -The Accelerator is part of the Strategy which manages communication across multiple devices (distributed communication). -Whenever the Trainer, the loops or any other component in Lightning needs to talk to hardware, it calls into the Strategy and the Strategy calls into the Accelerator. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/strategies/overview.jpeg - :alt: Illustration of the Strategy as a composition of the Accelerator and several plugins - -We expose Accelerators and Strategies mainly for expert users who want to extend Lightning to work with new -hardware and distributed training or clusters. - - ----------- - -Create a Custom Accelerator ---------------------------- - -Here is how you create a new Accelerator. -Let's pretend we want to integrate the fictional XPU accelerator and we have access to its hardware through a library -``xpulib``. - -.. code-block:: python - - import xpulib - - - class XPUAccelerator(Accelerator): - """Experimental support for XPU, optimized for large-scale machine learning.""" - - @staticmethod - def parse_devices(devices: Any) -> Any: - # Put parsing logic here how devices can be passed into the Trainer - # via the `devices` argument - return devices - - @staticmethod - def get_parallel_devices(devices: Any) -> Any: - # Here, convert the device indices to actual device objects - return [torch.device("xpu", idx) for idx in devices] - - @staticmethod - def auto_device_count() -> int: - # Return a value for auto-device selection when `Trainer(devices="auto")` - return xpulib.available_devices() - - @staticmethod - def is_available() -> bool: - return xpulib.is_available() - - def get_device_stats(self, device: Union[str, torch.device]) -> Dict[str, Any]: - # Return optional device statistics for loggers - return {} - - -Finally, add the XPUAccelerator to the Trainer: - -.. code-block:: python - - from pytorch_lightning import Trainer - - accelerator = XPUAccelerator() - trainer = Trainer(accelerator=accelerator, devices=2) - - -:doc:`Learn more about Strategies <../extensions/strategy>` and how they interact with the Accelerator. - - ----------- - -Registering Accelerators ------------------------- - -If you wish to switch to a custom accelerator from the CLI without code changes, you can implement the :meth:`~pytorch_lightning.accelerators.accelerator.Accelerator.register_accelerators` class method to register your new accelerator under a shorthand name like so: - -.. code-block:: python - - class XPUAccelerator(Accelerator): - ... - - @classmethod - def register_accelerators(cls, accelerator_registry): - accelerator_registry.register( - "xpu", - cls, - description=f"XPU Accelerator - optimized for large-scale machine learning.", - ) - -Now, this is possible: - -.. code-block:: python - - trainer = Trainer(accelerator="xpu") - -Or if you are using the Lightning CLI, for example: - -.. code-block:: bash - - python train.py fit --trainer.accelerator=xpu --trainer.devices=2 - - ----------- - -Accelerator API ---------------- - -.. currentmodule:: pytorch_lightning.accelerators - -.. autosummary:: - :nosignatures: - :template: classtemplate.rst - - Accelerator - CPUAccelerator - GPUAccelerator - HPUAccelerator - IPUAccelerator - TPUAccelerator diff --git a/docs/_sources/extensions/callbacks.rst.txt b/docs/_sources/extensions/callbacks.rst.txt deleted file mode 100644 index 6def5ee..0000000 --- a/docs/_sources/extensions/callbacks.rst.txt +++ /dev/null @@ -1,408 +0,0 @@ -.. role:: hidden - :class: hidden-section - -.. _callbacks: - -######## -Callback -######## - -.. raw:: html - - - -| - -A callback is a self-contained program that can be reused across projects. - -Lightning has a callback system to execute them when needed. Callbacks should capture NON-ESSENTIAL -logic that is NOT required for your :doc:`lightning module <../common/lightning_module>` to run. - -Here's the flow of how the callback hooks are executed: - -.. raw:: html - - - -An overall Lightning system should have: - -1. Trainer for all engineering -2. LightningModule for all research code. -3. Callbacks for non-essential code. - -| - -Example: - -.. testcode:: - - from pytorch_lightning.callbacks import Callback - - - class MyPrintingCallback(Callback): - def on_train_start(self, trainer, pl_module): - print("Training is starting") - - def on_train_end(self, trainer, pl_module): - print("Training is ending") - - - trainer = Trainer(callbacks=[MyPrintingCallback()]) - -We successfully extended functionality without polluting our super clean -:doc:`lightning module <../common/lightning_module>` research code. - ------------ - -******** -Examples -******** -You can do pretty much anything with callbacks. - -- `Add a MLP to fine-tune self-supervised networks `_. -- `Find how to modify an image input to trick the classification result `_. -- `Interpolate the latent space of any variational model `_. -- `Log images to Tensorboard for any model `_. - - --------------- - -****************** -Built-in Callbacks -****************** -Lightning has a few built-in callbacks. - -.. note:: - For a richer collection of callbacks, check out our - `bolts library `_. - -.. currentmodule:: pytorch_lightning.callbacks - -.. autosummary:: - :nosignatures: - :template: classtemplate.rst - - BackboneFinetuning - BaseFinetuning - BasePredictionWriter - Callback - DeviceStatsMonitor - EarlyStopping - GradientAccumulationScheduler - LambdaCallback - LearningRateMonitor - ModelCheckpoint - ModelPruning - ModelSummary - ProgressBarBase - QuantizationAwareTraining - RichModelSummary - RichProgressBar - StochasticWeightAveraging - Timer - TQDMProgressBar - ----------- - -.. include:: callbacks_state.rst - ----------- - -************** -Best Practices -************** -The following are best practices when using/designing callbacks. - -1. Callbacks should be isolated in their functionality. -2. Your callback should not rely on the behavior of other callbacks in order to work properly. -3. Do not manually call methods from the callback. -4. Directly calling methods (eg. `on_validation_end`) is strongly discouraged. -5. Whenever possible, your callbacks should not depend on the order in which they are executed. - ------------ - -.. _callback_hooks: - -************ -Callback API -************ -Here is the full API of methods available in the Callback base class. - -The :class:`~pytorch_lightning.callbacks.Callback` class is the base for all the callbacks in Lightning just like the :class:`~pytorch_lightning.core.lightning.LightningModule` is the base for all models. -It defines a public interface that each callback implementation must follow, the key ones are: - -Properties -========== - -state_key -^^^^^^^^^ - -.. autoattribute:: pytorch_lightning.callbacks.Callback.state_key - :noindex: - - -Hooks -===== - -on_configure_sharded_model -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_configure_sharded_model - :noindex: - -setup -^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.setup - :noindex: - -teardown -^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.teardown - :noindex: - -on_init_start -^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_init_start - :noindex: - -on_init_end -^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_init_end - :noindex: - -on_fit_start -^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_fit_start - :noindex: - -on_fit_end -^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_fit_end - :noindex: - -on_sanity_check_start -^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_sanity_check_start - :noindex: - -on_sanity_check_end -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_sanity_check_end - :noindex: - -on_train_batch_start -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_batch_start - :noindex: - -on_train_batch_end -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_batch_end - :noindex: - -on_train_epoch_start -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_epoch_start - :noindex: - -on_train_epoch_end -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_epoch_end - :noindex: - -on_validation_epoch_start -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_epoch_start - :noindex: - -on_validation_epoch_end -^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_epoch_end - :noindex: - -on_test_epoch_start -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_epoch_start - :noindex: - -on_test_epoch_end -^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_epoch_end - :noindex: - -on_predict_epoch_start -^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_epoch_start - :noindex: - -on_predict_epoch_end -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_epoch_end - :noindex: - -.. automethod:: pytorch_lightning.callbacks.Callback.on_epoch_end - :noindex: - -on_validation_batch_start -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_batch_start - :noindex: - -on_validation_batch_end -^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_batch_end - :noindex: - -on_test_batch_start -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_batch_start - :noindex: - -on_test_batch_end -^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_batch_end - :noindex: - -on_predict_batch_start -^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_batch_start - :noindex: - -on_predict_batch_end -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_batch_end - :noindex: - -on_train_start -^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_start - :noindex: - -on_train_end -^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_end - :noindex: - -on_validation_start -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_start - :noindex: - -on_validation_end -^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_end - :noindex: - -on_test_start -^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_start - :noindex: - -on_test_end -^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_end - :noindex: - -on_predict_start -^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_start - :noindex: - -on_predict_end -^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_end - :noindex: - -on_keyboard_interrupt -^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_keyboard_interrupt - :noindex: - -on_exception -^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_exception - :noindex: - -state_dict -^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.state_dict - :noindex: - -on_save_checkpoint -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_save_checkpoint - :noindex: - -load_state_dict -^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.load_state_dict - :noindex: - -on_load_checkpoint -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_load_checkpoint - :noindex: - -on_before_backward -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_before_backward - :noindex: - -on_after_backward -^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_after_backward - :noindex: - -on_before_optimizer_step -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_before_optimizer_step - :noindex: - -on_before_zero_grad -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_before_zero_grad - :noindex: diff --git a/docs/_sources/extensions/callbacks_state.rst.txt b/docs/_sources/extensions/callbacks_state.rst.txt deleted file mode 100644 index 0a104ca..0000000 --- a/docs/_sources/extensions/callbacks_state.rst.txt +++ /dev/null @@ -1,62 +0,0 @@ -******************* -Save Callback state -******************* - -Some callbacks require internal state in order to function properly. You can optionally -choose to persist your callback's state as part of model checkpoint files using -:meth:`~pytorch_lightning.callbacks.Callback.state_dict` and :meth:`~pytorch_lightning.callbacks.Callback.load_state_dict`. -Note that the returned state must be able to be pickled. - -When your callback is meant to be used only as a singleton callback then implementing the above two hooks is enough -to persist state effectively. However, if passing multiple instances of the callback to the Trainer is supported, then -the callback must define a :attr:`~pytorch_lightning.callbacks.Callback.state_key` property in order for Lightning -to be able to distinguish the different states when loading the callback state. This concept is best illustrated by -the following example. - -.. testcode:: - - class Counter(Callback): - def __init__(self, what="epochs", verbose=True): - self.what = what - self.verbose = verbose - self.state = {"epochs": 0, "batches": 0} - - @property - def state_key(self): - # note: we do not include `verbose` here on purpose - return self._generate_state_key(what=self.what) - - def on_train_epoch_end(self, *args, **kwargs): - if self.what == "epochs": - self.state["epochs"] += 1 - - def on_train_batch_end(self, *args, **kwargs): - if self.what == "batches": - self.state["batches"] += 1 - - def load_state_dict(self, state_dict): - self.state.update(state_dict) - - def state_dict(self): - return self.state.copy() - - - # two callbacks of the same type are being used - trainer = Trainer(callbacks=[Counter(what="epochs"), Counter(what="batches")]) - -A Lightning checkpoint from this Trainer with the two stateful callbacks will include the following information: - -.. code-block:: - - { - "state_dict": ..., - "callbacks": { - "Counter{'what': 'batches'}": {"batches": 32, "epochs": 0}, - "Counter{'what': 'epochs'}": {"batches": 0, "epochs": 2}, - ... - } - } - -The implementation of a :attr:`~pytorch_lightning.callbacks.Callback.state_key` is essential here. If it were missing, -Lightning would not be able to disambiguate the state for these two callbacks, and :attr:`~pytorch_lightning.callbacks.Callback.state_key` -by default only defines the class name as the key, e.g., here ``Counter``. diff --git a/docs/_sources/extensions/datamodules_state.rst.txt b/docs/_sources/extensions/datamodules_state.rst.txt deleted file mode 100644 index 61710d7..0000000 --- a/docs/_sources/extensions/datamodules_state.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -Save DataModule state -===================== -When a checkpoint is created, it asks every DataModule for their state. If your DataModule defines the *state_dict* and *load_state_dict* methods, the checkpoint will automatically track and restore your DataModules. - -.. code:: python - - class LitDataModule(pl.DataModuler): - def state_dict(self): - # track whatever you want here - state = {"current_train_batch_index": self.current_train_batch_index} - return state - - def load_state_dict(self, state_dict): - # restore the state based on what you tracked in (def state_dict) - self.current_train_batch_index = state_dict["current_train_batch_index"] diff --git a/docs/_sources/extensions/generated/pytorch_lightning.loggers.CSVLogger.rst.txt b/docs/_sources/extensions/generated/pytorch_lightning.loggers.CSVLogger.rst.txt deleted file mode 100644 index bd8ccbf..0000000 --- a/docs/_sources/extensions/generated/pytorch_lightning.loggers.CSVLogger.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -CSVLogger -========= - -.. autoclass:: CSVLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/docs/_sources/extensions/generated/pytorch_lightning.loggers.CometLogger.rst.txt b/docs/_sources/extensions/generated/pytorch_lightning.loggers.CometLogger.rst.txt deleted file mode 100644 index 324d77c..0000000 --- a/docs/_sources/extensions/generated/pytorch_lightning.loggers.CometLogger.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -CometLogger -=========== - -.. autoclass:: CometLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/docs/_sources/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.rst.txt b/docs/_sources/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.rst.txt deleted file mode 100644 index 2eaf478..0000000 --- a/docs/_sources/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -MLFlowLogger -============ - -.. autoclass:: MLFlowLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/docs/_sources/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.rst.txt b/docs/_sources/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.rst.txt deleted file mode 100644 index e9da513..0000000 --- a/docs/_sources/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -NeptuneLogger -============= - -.. autoclass:: NeptuneLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/docs/_sources/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.rst.txt b/docs/_sources/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.rst.txt deleted file mode 100644 index 6bcd4a2..0000000 --- a/docs/_sources/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -TensorBoardLogger -================= - -.. autoclass:: TensorBoardLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/docs/_sources/extensions/generated/pytorch_lightning.loggers.WandbLogger.rst.txt b/docs/_sources/extensions/generated/pytorch_lightning.loggers.WandbLogger.rst.txt deleted file mode 100644 index 3dcb424..0000000 --- a/docs/_sources/extensions/generated/pytorch_lightning.loggers.WandbLogger.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -WandbLogger -=========== - -.. autoclass:: WandbLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/docs/_sources/extensions/logging.rst.txt b/docs/_sources/extensions/logging.rst.txt deleted file mode 100644 index 8bad452..0000000 --- a/docs/_sources/extensions/logging.rst.txt +++ /dev/null @@ -1,410 +0,0 @@ -:orphan: - -.. testsetup:: * - - from pytorch_lightning import loggers as pl_loggers - -.. role:: hidden - :class: hidden-section - -.. _logging: - - -####### -Logging -####### - -***************** -Supported Loggers -***************** - -The following are loggers we support: - -.. currentmodule:: pytorch_lightning.loggers - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: classtemplate.rst - - CometLogger - CSVLogger - MLFlowLogger - NeptuneLogger - TensorBoardLogger - WandbLogger - - -The above loggers will normally plot an additional chart (**global_step VS epoch**). Depending on the loggers you use, there might be some additional charts too. - -By default, Lightning uses ``TensorBoard`` logger under the hood, and stores the logs to a directory (by default in ``lightning_logs/``). - -.. testcode:: - - from pytorch_lightning import Trainer - - # Automatically logs to a directory (by default ``lightning_logs/``) - trainer = Trainer() - -To see your logs: - -.. code-block:: bash - - tensorboard --logdir=lightning_logs/ - -To visualize tensorboard in a jupyter notebook environment, run the following command in a jupyter cell: - -.. code-block:: bash - - %reload_ext tensorboard - %tensorboard --logdir=lightning_logs/ - -You can also pass a custom Logger to the :class:`~pytorch_lightning.trainer.trainer.Trainer`. - -.. testcode:: - - from pytorch_lightning import loggers as pl_loggers - - tb_logger = pl_loggers.TensorBoardLogger(save_dir="logs/") - trainer = Trainer(logger=tb_logger) - -Choose from any of the others such as MLflow, Comet, Neptune, WandB, etc. - -.. testcode:: - - comet_logger = pl_loggers.CometLogger(save_dir="logs/") - trainer = Trainer(logger=comet_logger) - -To use multiple loggers, simply pass in a ``list`` or ``tuple`` of loggers. - -.. testcode:: - - tb_logger = pl_loggers.TensorBoardLogger(save_dir="logs/") - comet_logger = pl_loggers.CometLogger(save_dir="logs/") - trainer = Trainer(logger=[tb_logger, comet_logger]) - -.. note:: - - By default, Lightning logs every 50 steps. Use Trainer flags to :ref:`logging_frequency`. - -.. note:: - - By default, all loggers log to ``os.getcwd()``. You can change the logging path using - ``Trainer(default_root_dir="/your/path/to/save/checkpoints")`` without instantiating a logger. - ----------- - -****************************** -Logging from a LightningModule -****************************** - -Lightning offers automatic log functionalities for logging scalars, or manual logging for anything else. - -Automatic Logging -================= - -Use the :meth:`~pytorch_lightning.core.lightning.LightningModule.log` or :meth:`~pytorch_lightning.core.lightning.LightningModule.log_dict` -methods to log from anywhere in a :doc:`LightningModule <../common/lightning_module>` and :doc:`callbacks <../extensions/callbacks>`. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("my_metric", x) - - - # or a dict to get multiple metrics on the same plot if the logger supports it - def training_step(self, batch, batch_idx): - self.log("performance", {"acc": acc, "recall": recall}) - - - # or a dict to log all metrics at once with individual plots - def training_step(self, batch, batch_idx): - self.log_dict({"acc": acc, "recall": recall}) - -.. note:: - Everything explained below applies to both :meth:`~pytorch_lightning.core.lightning.LightningModule.log` or :meth:`~pytorch_lightning.core.lightning.LightningModule.log_dict` methods. - -Depending on where the :meth:`~pytorch_lightning.core.lightning.LightningModule.log` method is called, Lightning auto-determines -the correct logging mode for you. Of course you can override the default behavior by manually setting the -:meth:`~pytorch_lightning.core.lightning.LightningModule.log` parameters. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("my_loss", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True) - -The :meth:`~pytorch_lightning.core.lightning.LightningModule.log` method has a few options: - -* ``on_step``: Logs the metric at the current step. -* ``on_epoch``: Automatically accumulates and logs at the end of the epoch. -* ``prog_bar``: Logs to the progress bar (Default: ``False``). -* ``logger``: Logs to the logger like ``Tensorboard``, or any other custom logger passed to the :class:`~pytorch_lightning.trainer.trainer.Trainer` (Default: ``True``). -* ``reduce_fx``: Reduction function over step values for end of epoch. Uses :meth:`torch.mean` by default. -* ``enable_graph``: If True, will not auto detach the graph. -* ``sync_dist``: If True, reduces the metric across devices. Use with care as this may lead to a significant communication overhead. -* ``sync_dist_group``: The DDP group to sync across. -* ``add_dataloader_idx``: If True, appends the index of the current dataloader to the name (when using multiple dataloaders). If False, user needs to give unique names for each dataloader to not mix the values. -* ``batch_size``: Current batch size used for accumulating logs logged with ``on_epoch=True``. This will be directly inferred from the loaded batch, but for some data structures you might need to explicitly provide it. -* ``rank_zero_only``: Whether the value will be logged only on rank 0. This will prevent synchronization which would produce a deadlock as not all processes would perform this log call. - -.. list-table:: Default behavior of logging in Callback or LightningModule - :widths: 50 25 25 - :header-rows: 1 - - * - Hook - - on_step - - on_epoch - * - on_train_start, on_train_epoch_start, on_train_epoch_end, training_epoch_end - - False - - True - * - on_before_backward, on_after_backward, on_before_optimizer_step, on_before_zero_grad - - True - - False - * - on_train_batch_start, on_train_batch_end, training_step, training_step_end - - True - - False - * - on_validation_start, on_validation_epoch_start, on_validation_epoch_end, validation_epoch_end - - False - - True - * - on_validation_batch_start, on_validation_batch_end, validation_step, validation_step_end - - False - - True - - -.. note:: - - While logging tensor metrics with ``on_epoch=True`` inside step-level hooks and using mean-reduction (default) to accumulate the metrics across the current epoch, Lightning tries to extract the - batch size from the current batch. If multiple possible batch sizes are found, a warning is logged and if it fails to extract the batch size from the current batch, which is possible if - the batch is a custom structure/collection, then an error is raised. To avoid this, you can specify the ``batch_size`` inside the ``self.log(... batch_size=batch_size)`` call. - - .. code-block:: python - - def training_step(self, batch, batch_idx): - # extracts the batch size from `batch` - self.log("train_loss", loss, on_epoch=True) - - - def validation_step(self, batch, batch_idx): - # uses `batch_size=10` - self.log("val_loss", loss, batch_size=10) - -.. note:: - - - The above config for ``validation`` applies for ``test`` hooks as well. - - - Setting ``on_epoch=True`` will cache all your logged values during the full training epoch and perform a - reduction in ``on_train_epoch_end``. We recommend using `TorchMetrics `_, when working with custom reduction. - - - Setting both ``on_step=True`` and ``on_epoch=True`` will create two keys per metric you log with - suffix ``_step`` and ``_epoch`` respectively. You can refer to these keys e.g. in the `monitor` - argument of :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` or in the graphs plotted to the logger of your choice. - - -If your work requires to log in an unsupported method, please open an issue with a clear description of why it is blocking you. - - -Manual Logging Non-Scalar Artifacts -=================================== - -If you want to log anything that is not a scalar, like histograms, text, images, etc., you may need to use the logger object directly. - -.. code-block:: python - - def training_step(self): - ... - # the logger you used (in this case tensorboard) - tensorboard = self.logger.experiment - tensorboard.add_image() - tensorboard.add_histogram(...) - tensorboard.add_figure(...) - - ----------- - -******************** -Make a Custom Logger -******************** - -You can implement your own logger by writing a class that inherits from :class:`~pytorch_lightning.loggers.logger.Logger`. -Use the :func:`~pytorch_lightning.loggers.logger.rank_zero_experiment` and :func:`~pytorch_lightning.utilities.rank_zero.rank_zero_only` decorators to make sure that only the first process in DDP training creates the experiment and logs the data respectively. - -.. testcode:: - - from pytorch_lightning.loggers.logger import Logger, rank_zero_experiment - from pytorch_lightning.utilities.distributed import rank_zero_only - - - class MyLogger(Logger): - @property - def name(self): - return "MyLogger" - - @property - def version(self): - # Return the experiment version, int or str. - return "0.1" - - @rank_zero_only - def log_hyperparams(self, params): - # params is an argparse.Namespace - # your code to record hyperparameters goes here - pass - - @rank_zero_only - def log_metrics(self, metrics, step): - # metrics is a dictionary of metric names and values - # your code to record metrics goes here - pass - - @rank_zero_only - def save(self): - # Optional. Any code necessary to save logger data goes here - pass - - @rank_zero_only - def finalize(self, status): - # Optional. Any code that needs to be run after training - # finishes goes here - pass - -If you write a logger that may be useful to others, please send -a pull request to add it to Lightning! - ----------- - -.. _logging_frequency: - - -************************* -Control Logging Frequency -************************* - -Logging frequency -================= - -It may slow down training to log on every single batch. By default, Lightning logs every 50 rows, or 50 training steps. -To change this behaviour, set the ``log_every_n_steps`` :class:`~pytorch_lightning.trainer.trainer.Trainer` flag. - -.. testcode:: - - k = 10 - trainer = Trainer(log_every_n_steps=k) - - -Log Writing Frequency -===================== - -Individual logger implementations determine their flushing frequency. For example, on the -:class:`~pytorch_lightning.loggers.csv_logs.CSVLogger` you can set the flag ``flush_logs_every_n_steps``. - ----------- - -************ -Progress Bar -************ - -You can add any metric to the progress bar using :meth:`~pytorch_lightning.core.lightning.LightningModule.log` -method, setting ``prog_bar=True``. - - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("my_loss", loss, prog_bar=True) - - -You could learn more about progress bars supported by Lightning :doc:`here <../common/progress_bar>`. - -Modifying the Progress Bar -========================== - -The progress bar by default already includes the training loss and version number of the experiment -if you are using a logger. These defaults can be customized by overriding the -:meth:`~pytorch_lightning.callbacks.progress.base.ProgressBarBase.get_metrics` hook in your logger. - -.. code-block:: python - - from pytorch_lightning.callbacks.progress import Tqdm - - - class CustomProgressBar(Tqdm): - def get_metrics(self, *args, **kwargs): - # don't show the version number - items = super().get_metrics() - items.pop("v_num", None) - return items - - ----------- - - -************************* -Configure Console Logging -************************* - -Lightning logs useful information about the training process and user warnings to the console. -You can retrieve the Lightning console logger and change it to your liking. For example, adjust the logging level -or redirect output for certain modules to log files: - -.. testcode:: - - import logging - - # configure logging at the root level of Lightning - logging.getLogger("pytorch_lightning").setLevel(logging.ERROR) - - # configure logging on module level, redirect to file - logger = logging.getLogger("pytorch_lightning.core") - logger.addHandler(logging.FileHandler("core.log")) - -Read more about custom Python logging `here `_. - - ----------- - -*********************** -Logging Hyperparameters -*********************** - -When training a model, it is useful to know what hyperparams went into that model. -When Lightning creates a checkpoint, it stores a key ``"hyper_parameters"`` with the hyperparams. - -.. code-block:: python - - lightning_checkpoint = torch.load(filepath, map_location=lambda storage, loc: storage) - hyperparams = lightning_checkpoint["hyper_parameters"] - -Some loggers also allow logging the hyperparams used in the experiment. For instance, -when using the ``TensorBoardLogger``, all hyperparams will show -in the `hparams tab `_. - -.. note:: - If you want to track a metric in the tensorboard hparams tab, log scalars to the key ``hp_metric``. If tracking multiple metrics, initialize ``TensorBoardLogger`` with ``default_hp_metric=False`` and call ``log_hyperparams`` only once with your metric keys and initial values. Subsequent updates can simply be logged to the metric keys. Refer to the examples below for setting up proper hyperparams metrics tracking within the :doc:`LightningModule <../common/lightning_module>`. - - .. code-block:: python - - # Using default_hp_metric - def validation_step(self, batch, batch_idx): - self.log("hp_metric", some_scalar) - - - # Using custom or multiple metrics (default_hp_metric=False) - def on_train_start(self): - self.logger.log_hyperparams(self.hparams, {"hp/metric_1": 0, "hp/metric_2": 0}) - - - def validation_step(self, batch, batch_idx): - self.log("hp/metric_1", some_scalar_1) - self.log("hp/metric_2", some_scalar_2) - - In the example, using ``"hp/"`` as a prefix allows for the metrics to be grouped under "hp" in the tensorboard scalar tab where you can collapse them. - ------------ - -*************************** -Managing Remote Filesystems -*************************** - -Lightning supports saving logs to a variety of filesystems, including local filesystems and several cloud storage providers. - -Check out the :doc:`Remote Filesystems <../common/remote_fs>` doc for more info. diff --git a/docs/_sources/extensions/loops.rst.txt b/docs/_sources/extensions/loops.rst.txt deleted file mode 100644 index c24d4ce..0000000 --- a/docs/_sources/extensions/loops.rst.txt +++ /dev/null @@ -1,461 +0,0 @@ -.. _loop-customization-extensions: - - -Loops -===== - -Loops let advanced users swap out the default gradient descent optimization loop at the core of Lightning with a different optimization paradigm. - -The Lightning Trainer is built on top of the standard gradient descent optimization loop which works for 90%+ of machine learning use cases: - -.. code-block:: python - - for i, batch in enumerate(dataloader): - x, y = batch - y_hat = model(x) - loss = loss_function(y_hat, y) - optimizer.zero_grad() - loss.backward() - optimizer.step() - -However, some new research use cases such as meta-learning, active learning, recommendation systems, etc., require a different loop structure. -For example here is a simple loop that guides the weight updates with a loss from a special validation split: - -.. code-block:: python - - for i, batch in enumerate(train_dataloader): - x, y = batch - y_hat = model(x) - loss = loss_function(y_hat, y) - optimizer.zero_grad() - loss.backward() - - val_loss = 0 - for i, val_batch in enumerate(val_dataloader): - x, y = val_batch - y_hat = model(x) - val_loss += loss_function(y_hat, y) - - scale_gradients(model, 1 / val_loss) - optimizer.step() - - -With Lightning Loops, you can customize to non-standard gradient descent optimizations to get the same loop above: - -.. code-block:: python - - trainer = Trainer() - trainer.fit_loop.epoch_loop = MyGradientDescentLoop() - -Think of this as swapping out the engine in a car! - ----------- - -Understanding the Default Trainer Loop --------------------------------------- - -The Lightning :class:`~pytorch_lightning.trainer.trainer.Trainer` automates the standard optimization loop which every PyTorch user is familiar with: - -.. code-block:: python - - for i, batch in enumerate(dataloader): - x, y = batch - y_hat = model(x) - loss = loss_function(y_hat, y) - optimizer.zero_grad() - loss.backward() - optimizer.step() - -The core research logic is simply shifted to the :class:`~pytorch_lightning.core.lightning.LightningModule`: - -.. code-block:: python - - for i, batch in enumerate(dataloader): - # x, y = batch moved to training_step - # y_hat = model(x) moved to training_step - # loss = loss_function(y_hat, y) moved to training_step - loss = lightning_module.training_step(batch, i) - - # Lightning handles automatically: - optimizer.zero_grad() - loss.backward() - optimizer.step() - -Under the hood, the above loop is implemented using the :class:`~pytorch_lightning.loops.base.Loop` API like so: - -.. code-block:: python - - class DefaultLoop(Loop): - def advance(self, batch, i): - loss = lightning_module.training_step(batch, i) - optimizer.zero_grad() - loss.backward() - optimizer.step() - - def run(self, dataloader): - for i, batch in enumerate(dataloader): - self.advance(batch, i) - -Defining a loop within a class interface instead of hard-coding a raw Python for/while loop has several benefits: - -1. You can have full control over the data flow through loops. -2. You can add new loops and nest as many of them as you want. -3. If needed, the state of a loop can be :ref:`saved and resumed `. -4. New hooks can be injected at any point. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/loops/epoch-loop-steps.gif - :alt: Animation showing how to convert a standard training loop to a Lightning loop - ----------- - -.. _override-default-loops-extensions: - -Overriding the default Loops ----------------------------- - -The fastest way to get started with loops, is to override functionality of an existing loop. -Lightning has 4 main loops which relies on : :class:`~pytorch_lightning.loops.fit_loop.FitLoop` for fitting (training and validating), -:class:`~pytorch_lightning.loops.dataloader.evaluation_loop.EvaluationLoop` for validating or testing, -:class:`~pytorch_lightning.loops.dataloader.prediction_loop.PredictionLoop` for predicting. - -For simple changes that don't require a custom loop, you can modify each of these loops. - -Each loop has a series of methods that can be modified. -For example with the :class:`~pytorch_lightning.loops.fit_loop.FitLoop`: - -.. code-block:: python - - from pytorch_lightning.loops import FitLoop - - - class MyLoop(FitLoop): - def advance(self): - """Advance from one iteration to the next.""" - - def on_advance_end(self): - """Do something at the end of an iteration.""" - - def on_run_end(self): - """Do something when the loop ends.""" - -A full list with all built-in loops and subloops can be found :ref:`here `. - -To add your own modifications to a loop, simply subclass an existing loop class and override what you need. -Here is a simple example how to add a new hook: - -.. code-block:: python - - from pytorch_lightning.loops import FitLoop - - - class CustomFitLoop(FitLoop): - def advance(self): - # ... whatever code before - - # pass anything you want to the hook - self.trainer.call_hook("my_new_hook", *args, **kwargs) - - # ... whatever code after - -Now simply attach the correct loop in the trainer directly: - -.. code-block:: python - - trainer = Trainer(...) - trainer.fit_loop = CustomFitLoop() - - # fit() now uses the new FitLoop! - trainer.fit(...) - - # the equivalent for validate() - val_loop = CustomValLoop() - trainer = Trainer() - trainer.validate_loop = val_loop - trainer.validate(...) - -Now your code is FULLY flexible and you can still leverage ALL the best parts of Lightning! - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/loops/replace-fit-loop.gif - :alt: Animation showing how to replace a loop on the Trainer - ----------- - -Creating a New Loop From Scratch --------------------------------- - -You can also go wild and implement a full loop from scratch by sub-classing the :class:`~pytorch_lightning.loops.base.Loop` base class. -You will need to override a minimum of two things: - -.. code-block:: python - - from pytorch_lightning.loop import Loop - - - class MyFancyLoop(Loop): - @property - def done(self): - """Provide a condition to stop the loop.""" - - def advance(self): - """ - Access your dataloader/s in whatever way you want. - Do your fancy optimization things. - Call the LightningModule methods at your leisure. - """ - -Finally, attach it into the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - trainer = Trainer(...) - trainer.fit_loop = MyFancyLoop() - - # fit() now uses your fancy loop! - trainer.fit(...) - -But beware: Loop customization gives you more power and full control over the Trainer and with great power comes great responsibility. -We recommend that you familiarize yourself with :ref:`overriding the default loops ` first before you start building a new loop from the ground up. - ----------- - -Loop API --------- -Here is the full API of methods available in the Loop base class. - -The :class:`~pytorch_lightning.loops.base.Loop` class is the base of all loops in the same way as the :class:`~pytorch_lightning.core.lightning.LightningModule` is the base of all models. -It defines a public interface that each loop implementation must follow, the key ones are: - -Properties -^^^^^^^^^^ - -done -~~~~ - -.. autoattribute:: pytorch_lightning.loops.base.Loop.done - :noindex: - -skip (optional) -~~~~~~~~~~~~~~~ - -.. autoattribute:: pytorch_lightning.loops.base.Loop.skip - :noindex: - -Methods -^^^^^^^ - -reset (optional) -~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.loops.base.Loop.reset - :noindex: - -advance -~~~~~~~ - -.. automethod:: pytorch_lightning.loops.base.Loop.advance - :noindex: - -run (optional) -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.loops.base.Loop.run - :noindex: - - ----------- - -Subloops --------- - -When you want to customize nested loops within loops, use the :meth:`~pytorch_lightning.loops.base.Loop.replace` method: - -.. code-block:: python - - # This takes care of properly instantiating the new Loop and setting all references - trainer.fit_loop.replace(epoch_loop=MyEpochLoop) - # Trainer runs the fit loop with your new epoch loop! - trainer.fit(model) - -Alternatively, for more fine-grained control, use the :meth:`~pytorch_lightning.loops.base.Loop.connect` method: - -.. code-block:: python - - # Optional: stitch back the trainer arguments - epoch_loop = MyEpochLoop(trainer.fit_loop.epoch_loop.min_steps, trainer.fit_loop.epoch_loop.max_steps) - # Optional: connect children loops as they might have existing state - epoch_loop.connect(trainer.fit_loop.epoch_loop.batch_loop, trainer.fit_loop.epoch_loop.val_loop) - # Instantiate and connect the loop. - trainer.fit_loop.connect(epoch_loop=epoch_loop) - trainer.fit(model) - -More about the built-in loops and how they are composed is explained in the next section. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/loops/connect-epoch-loop.gif - :alt: Animation showing how to connect a custom subloop - ----------- - -Built-in Loops --------------- - -.. _loop-structure-extensions: - -The training loop in Lightning is called *fit loop* and is actually a combination of several loops. -Here is what the structure would look like in plain Python: - -.. code-block:: python - - # FitLoop - for epoch in range(max_epochs): - - # TrainingEpochLoop - for batch_idx, batch in enumerate(train_dataloader): - - # TrainingBatchLoop - for split_batch in tbptt_split(batch): - - # OptimizerLoop - for optimizer_idx, opt in enumerate(optimizers): - - loss = lightning_module.training_step(batch, batch_idx, optimizer_idx) - ... - - # ValidationEpochLoop - for batch_idx, batch in enumerate(val_dataloader): - lightning_module.validation_step(batch, batch_idx, optimizer_idx) - ... - - -Each of these :code:`for`-loops represents a class implementing the :class:`~pytorch_lightning.loops.base.Loop` interface. - - -.. list-table:: Trainer entry points and associated loops - :widths: 25 75 - :header-rows: 1 - - * - Built-in loop - - Description - * - :class:`~pytorch_lightning.loops.fit_loop.FitLoop` - - The :class:`~pytorch_lightning.loops.fit_loop.FitLoop` is the top-level loop where training starts. - It simply counts the epochs and iterates from one to the next by calling :code:`TrainingEpochLoop.run()` in its :code:`advance()` method. - * - :class:`~pytorch_lightning.loops.epoch.training_epoch_loop.TrainingEpochLoop` - - The :class:`~pytorch_lightning.loops.epoch.training_epoch_loop.TrainingEpochLoop` is the one that iterates over the dataloader that the user returns in their :meth:`~pytorch_lightning.core.lightning.LightningModule.train_dataloader` method. - Its main responsibilities are calling the :code:`*_epoch_start` and :code:`*_epoch_end` hooks, accumulating outputs if the user request them in one of these hooks, and running validation at the requested interval. - The validation is carried out by yet another loop, :class:`~pytorch_lightning.loops.epoch.validation_epoch_loop.ValidationEpochLoop`. - - In the :code:`run()` method, the training epoch loop could in theory simply call the :code:`LightningModule.training_step` already and perform the optimization. - However, Lightning has built-in support for automatic optimization with multiple optimizers and on top of that also supports :ref:`TBPTT `. - For this reason there are actually two more loops nested under :class:`~pytorch_lightning.loops.epoch.training_epoch_loop.TrainingEpochLoop`. - * - :class:`~pytorch_lightning.loops.batch.training_batch_loop.TrainingBatchLoop` - - The responsibility of the :class:`~pytorch_lightning.loops.batch.training_batch_loop.TrainingBatchLoop` is to split a batch given by the :class:`~pytorch_lightning.loops.epoch.training_epoch_loop.TrainingEpochLoop` along the time-dimension and iterate over the list of splits. - It also keeps track of the hidden state *hiddens* returned by the training step. - By default, when truncated back-propagation through time (TBPTT) is turned off, this loop does not do anything except redirect the call to the :class:`~pytorch_lightning.loops.optimization.optimizer_loop.OptimizerLoop`. - Read more about :ref:`TBPTT `. - * - :class:`~pytorch_lightning.loops.optimization.optimizer_loop.OptimizerLoop` - - The :class:`~pytorch_lightning.loops.optimization.optimizer_loop.OptimizerLoop` iterates over one or multiple optimizers and for each one it calls the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` method with the batch, the current batch index and the optimizer index if multiple optimizers are requested. - It is the leaf node in the tree of loops and performs the actual optimization (forward, zero grad, backward, optimizer step). - * - :class:`~pytorch_lightning.loops.optimization.manual_loop.ManualOptimization` - - Substitutes the :class:`~pytorch_lightning.loops.optimization.optimizer_loop.OptimizerLoop` in case of :doc:`manual optimization <../model/manual_optimization>` and implements the manual optimization step. - * - :class:`~pytorch_lightning.loops.dataloader.evaluation_loop.EvaluationLoop` - - The :class:`~pytorch_lightning.loops.dataloader.evaluation_loop.EvaluationLoop` is the top-level loop where validation/testing starts. - It simply iterates over each evaluation dataloader from one to the next by calling :code:`EvaluationEpochLoop.run()` in its :code:`advance()` method. - * - :class:`~pytorch_lightning.loops.dataloader.prediction_loop.PredictionLoop` - - The :class:`~pytorch_lightning.loops.dataloader.prediction_loop.PredictionLoop` is the top-level loop where prediction starts. - It simply iterates over each prediction dataloader from one to the next by calling :code:`PredictionEpochLoop.run()` in its :code:`advance()` method. - - ----------- - -Available Loops in Lightning Flash ----------------------------------- - -`Active Learning `__ is a machine learning practice in which the user interacts with the learner in order to provide new labels when required. - -You can find a real use case in `Lightning Flash `_. - -Flash implements the :code:`ActiveLearningLoop` that you can use together with the :code:`ActiveLearningDataModule` to label new data on the fly. -To run the following demo, install Flash and `BaaL `__ first: - -.. code-block:: bash - - pip install lightning-flash baal - -.. code-block:: python - - import torch - - import flash - from flash.core.classification import Probabilities - from flash.core.data.utils import download_data - from flash.image import ImageClassificationData, ImageClassifier - from flash.image.classification.integrations.baal import ActiveLearningDataModule, ActiveLearningLoop - - # 1. Create the DataModule - download_data("https://pl-flash-data.s3.amazonaws.com/hymenoptera_data.zip", "./data") - - # Implement the research use-case where we mask labels from labelled dataset. - datamodule = ActiveLearningDataModule( - ImageClassificationData.from_folders(train_folder="data/hymenoptera_data/train/", batch_size=2), - initial_num_labels=5, - val_split=0.1, - ) - - # 2. Build the task - head = torch.nn.Sequential( - torch.nn.Dropout(p=0.1), - torch.nn.Linear(512, datamodule.num_classes), - ) - model = ImageClassifier(backbone="resnet18", head=head, num_classes=datamodule.num_classes, output=Probabilities()) - - - # 3.1 Create the trainer - trainer = flash.Trainer(max_epochs=3) - - # 3.2 Create the active learning loop and connect it to the trainer - active_learning_loop = ActiveLearningLoop(label_epoch_frequency=1) - active_learning_loop.connect(trainer.fit_loop) - trainer.fit_loop = active_learning_loop - - # 3.3 Finetune - trainer.finetune(model, datamodule=datamodule, strategy="freeze") - - # 4. Predict what's on a few images! ants or bees? - predictions = model.predict("data/hymenoptera_data/val/bees/65038344_52a45d090d.jpg") - print(predictions) - - # 5. Save the model! - trainer.save_checkpoint("image_classification_model.pt") - -Here is the `Active Learning Loop example `_ and the `code for the active learning loop `_. - - ----------- - -Advanced Examples ------------------ - - -.. list-table:: Ready-to-run loop examples and tutorials - :widths: 25 75 - :header-rows: 1 - - * - Link to Example - - Description - * - `K-fold Cross Validation `_ - - `KFold / Cross Validation `__ is a machine learning practice in which the training dataset is being partitioned into ``num_folds`` complementary subsets. - One cross validation round will perform fitting where one fold is left out for validation and the other folds are used for training. - To reduce variability, once all rounds are performed using the different folds, the trained models are ensembled and their predictions are - averaged when estimating the model's predictive performance on the test dataset. - * - `Yielding Training Step `_ - - This loop enables you to write the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` hook - as a Python Generator for automatic optimization with multiple optimizers, i.e., you can :code:`yield` loss - values from it instead of returning them. This can enable more elegant and expressive implementations, as shown - shown with a GAN in this example. - - ----------- - -Advanced Features ------------------ - -Next: :doc:`Advanced loop features <../extensions/loops_advanced>` diff --git a/docs/_sources/extensions/loops_advanced.rst.txt b/docs/_sources/extensions/loops_advanced.rst.txt deleted file mode 100644 index e71c827..0000000 --- a/docs/_sources/extensions/loops_advanced.rst.txt +++ /dev/null @@ -1,41 +0,0 @@ -:orphan: - -Loops (Advanced) -================ - -.. _persisting loop state: - -Persisting the State of Loops ------------------------------ - -.. note:: - - This is an experimental feature and is not activated by default. - Set the environment variable `PL_FAULT_TOLERANT_TRAINING = 1` to enable saving the progress of loops. - Read more about :doc:`fault-tolerant training <../clouds/fault_tolerant_training>`. - -A powerful property of the class-based loop interface is that it can own an internal state. -Loop instances can save their state to the checkpoint through corresponding hooks and if implemented accordingly, resume the state of execution at the appropriate place. -This design is particularly interesting for fault-tolerant training which is an experimental feature released in Lightning v1.5. - -The two hooks :meth:`~pytorch_lightning.loops.base.Loop.on_save_checkpoint` and :meth:`~pytorch_lightning.loops.base.Loop.on_load_checkpoint` function very similarly to how LightningModules and Callbacks save and load state. - -.. code-block:: python - - def on_save_checkpoint(self): - state_dict["iteration"] = self.iteration - return state_dict - - - def on_load_checkpoint(self, state_dict): - self.iteration = state_dict["iteration"] - -When the Trainer is restarting from a checkpoint (e.g., through :code:`trainer.fit(ckpt_path=...)`), the loop exposes a boolean attribute :attr:`~pytorch_lightning.loops.base.Loop.restarting`. -Based around the value of this variable, the user can write the loop in such a way that it can restart from an arbitrary point given the state loaded from the checkpoint. -For example, the implementation of the :meth:`~pytorch_lightning.loops.base.Loop.reset` method could look like this given our previous example: - -.. code-block:: python - - def reset(self): - if not self.restarting: - self.iteration = 0 diff --git a/docs/_sources/extensions/plugins.rst.txt b/docs/_sources/extensions/plugins.rst.txt deleted file mode 100644 index 392a072..0000000 --- a/docs/_sources/extensions/plugins.rst.txt +++ /dev/null @@ -1,119 +0,0 @@ -.. _plugins: - -####### -Plugins -####### - -.. include:: ../links.rst - -Plugins allow custom integrations to the internals of the Trainer such as custom precision, checkpointing or -cluster environment implementation. - -Under the hood, the Lightning Trainer is using plugins in the training routine, added automatically -depending on the provided Trainer arguments. - -There are three types of Plugins in Lightning with different responsibilities: - -- Precision Plugins -- CheckpointIO Plugins -- Cluster Environments - -You can make the Trainer use one or multiple plugins by adding it to the ``plugins`` argument like so: - -.. code-block:: python - - trainer = Trainer(plugins=[plugin1, plugin2, ...]) - - -By default, the plugins get selected based on the rest of the Trainer settings such as the ``strategy``. - - ------------ - -.. _precision-plugins: - -***************** -Precision Plugins -***************** - -We provide precision plugins for you to benefit from numerical representations with lower precision than -32-bit floating-point or higher precision, such as 64-bit floating-point. - -.. code-block:: python - - # Training with 16-bit precision - trainer = Trainer(precision=16) - -The full list of built-in precision plugins is listed below. - -.. currentmodule:: pytorch_lightning.plugins.precision - -.. autosummary:: - :nosignatures: - :template: classtemplate.rst - - ApexMixedPrecisionPlugin - DeepSpeedPrecisionPlugin - DoublePrecisionPlugin - FullyShardedNativeMixedPrecisionPlugin - HPUPrecisionPlugin - IPUPrecisionPlugin - MixedPrecisionPlugin - NativeMixedPrecisionPlugin - PrecisionPlugin - ShardedNativeMixedPrecisionPlugin - TPUBf16PrecisionPlugin - TPUPrecisionPlugin - -More information regarding precision with Lightning can be found :ref:`here ` - ------------ - - -.. _checkpoint_io_plugins: - -******************** -CheckpointIO Plugins -******************** - -As part of our commitment to extensibility, we have abstracted Lightning's checkpointing logic into the :class:`~pytorch_lightning.plugins.io.CheckpointIO` plugin. -With this, you have the ability to customize the checkpointing logic to match the needs of your infrastructure. - -Below is a list of built-in plugins for checkpointing. - -.. currentmodule:: pytorch_lightning.plugins.io - -.. autosummary:: - :nosignatures: - :template: classtemplate.rst - - CheckpointIO - HPUCheckpointIO - TorchCheckpointIO - XLACheckpointIO - -Learn more about custom checkpointing with Lightning :ref:`here `. - ------------ - - -.. _cluster_environment_plugins: - -******************** -Cluster Environments -******************** - -You can define the interface of your own cluster environment based on the requirements of your infrastructure. - -.. currentmodule:: pytorch_lightning.plugins.environments - -.. autosummary:: - :nosignatures: - :template: classtemplate.rst - - ClusterEnvironment - KubeflowEnvironment - LightningEnvironment - LSFEnvironment - SLURMEnvironment - TorchElasticEnvironment diff --git a/docs/_sources/extensions/strategy.rst.txt b/docs/_sources/extensions/strategy.rst.txt deleted file mode 100644 index ad9d799..0000000 --- a/docs/_sources/extensions/strategy.rst.txt +++ /dev/null @@ -1,122 +0,0 @@ -:orphan: - -################### -What is a Strategy? -################### - -Strategy controls the model distribution across training, evaluation, and prediction to be used by the :doc:`Trainer <../common/trainer>`. It can be controlled by passing different -strategy with aliases (``"ddp"``, ``"ddp_spawn"``, ``"deepspeed"`` and so on) as well as a custom strategy to the ``strategy`` parameter for Trainer. - -The Strategy in PyTorch Lightning handles the following responsibilities: - -* Launch and teardown of training processes (if applicable). -* Setup communication between processes (NCCL, GLOO, MPI, and so on). -* Provide a unified communication interface for reduction, broadcast, and so on. -* Owns the :class:`~pytorch_lightning.core.lightning.LightningModule` -* Handles/owns optimizers and schedulers. - - -:class:`~pytorch_lightning.strategies.strategy.Strategy` also manages the accelerator, precision, and checkpointing plugins. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/strategies/overview.jpeg - :alt: Illustration of the Strategy as a composition of the Accelerator and several plugins - -We expose Strategies mainly for expert users that want to extend Lightning for new hardware support or new distributed backends (e.g. a backend not yet supported by `PyTorch `_ itself). - - ----- - -########################### -Enable Different Strategies -########################### - -.. code-block:: python - - # Training with the DistributedDataParallel strategy on 4 GPUs - trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4) - - # Training with the custom DistributedDataParallel strategy on 4 GPUs - trainer = Trainer(strategy=DDPStrategy(...), accelerator="gpu", devices=4) - - # Training with the DDP Spawn strategy using auto accelerator selection - trainer = Trainer(strategy="ddp_spawn", accelerator="auto", devices=4) - - # Training with the DeepSpeed strategy on available GPUs - trainer = Trainer(strategy="deepspeed", accelerator="gpu", devices="auto") - - # Training with the DDP strategy using 3 CPU processes - trainer = Trainer(strategy="ddp", accelerator="cpu", devices=3) - - # Training with the DDP Spawn strategy on 8 TPU cores - trainer = Trainer(strategy="ddp_spawn", accelerator="tpu", devices=8) - - # Training with the default IPU strategy on 8 IPUs - trainer = Trainer(accelerator="ipu", devices=8) - ----- - -######################## -Create a Custom Strategy -######################## - -Expert users may choose to extend an existing strategy by overriding its methods. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy - - - class CustomDDPStrategy(DDPStrategy): - def configure_ddp(self): - self.model = MyCustomDistributedDataParallel( - self.model, - device_ids=..., - ) - -or by subclassing the base class :class:`~pytorch_lightning.strategies.Strategy` to create new ones. These custom strategies -can then be passed into the ``Trainer`` directly via the ``strategy`` parameter. - -.. code-block:: python - - # custom plugins - trainer = Trainer(strategy=CustomDDPStrategy()) - - # fully custom accelerator and plugins - accelerator = MyAccelerator() - precision_plugin = MyPrecisionPlugin() - training_strategy = CustomDDPStrategy(accelerator=accelerator, precision_plugin=precision_plugin) - trainer = Trainer(strategy=training_strategy) - - -The complete list of built-in strategies is listed below. - ----- - -############################# -Available Training Strategies -############################# - -.. currentmodule:: pytorch_lightning.strategies - -.. autosummary:: - :nosignatures: - :template: classtemplate.rst - - BaguaStrategy - DDP2Strategy - DDPFullyShardedStrategy - DDPShardedStrategy - DDPSpawnShardedStrategy - DDPSpawnStrategy - DDPStrategy - DataParallelStrategy - DeepSpeedStrategy - HorovodStrategy - HPUParallelStrategy - IPUStrategy - ParallelStrategy - SingleDeviceStrategy - SingleHPUStrategy - SingleTPUStrategy - Strategy - TPUSpawnStrategy diff --git a/docs/_sources/guides/data.rst.txt b/docs/_sources/guides/data.rst.txt deleted file mode 100644 index 72dba27..0000000 --- a/docs/_sources/guides/data.rst.txt +++ /dev/null @@ -1,423 +0,0 @@ -:orphan: - -.. _data: - -############# -Managing Data -############# - -**************************** -Data Containers in Lightning -**************************** - -There are a few different data containers used in Lightning: - -.. list-table:: Data objects - :widths: 20 80 - :header-rows: 1 - - * - Object - - Definition - * - :class:`~torch.utils.data.Dataset` - - The PyTorch :class:`~torch.utils.data.Dataset` represents a map from keys to data samples. - * - :class:`~torch.utils.data.IterableDataset` - - The PyTorch :class:`~torch.utils.data.IterableDataset` represents a stream of data. - * - :class:`~torch.utils.data.DataLoader` - - The PyTorch :class:`~torch.utils.data.DataLoader` represents a Python iterable over a Dataset. - * - :class:`~pytorch_lightning.core.datamodule.LightningDataModule` - - A :class:`~pytorch_lightning.core.datamodule.LightningDataModule` is simply a collection of: training DataLoader(s), validation DataLoader(s), test DataLoader(s) and predict DataLoader(s), along with the matching transforms and data processing/downloads steps required. - - -Why Use LightningDataModule? -============================ - -The :class:`~pytorch_lightning.core.datamodule.LightningDataModule` was designed as a way of decoupling data-related hooks from the :class:`~pytorch_lightning.core.lightning.LightningModule` so you can develop dataset agnostic models. The :class:`~pytorch_lightning.core.datamodule.LightningDataModule` makes it easy to hot swap different Datasets with your model, so you can test it and benchmark it across domains. It also makes sharing and reusing the exact data splits and transforms across projects possible. - -Read :ref:`this ` for more details on LightningDataModule. - ---------- - -.. _multiple-dataloaders: - -***************** -Multiple Datasets -***************** - -There are a few ways to pass multiple Datasets to Lightning: - -1. Create a DataLoader that iterates over multiple Datasets under the hood. -2. In the training loop, you can pass multiple DataLoaders as a dict or list/tuple, and Lightning will - automatically combine the batches from different DataLoaders. -3. In the validation, test, or prediction, you have the option to return multiple DataLoaders as list/tuple, which Lightning will call sequentially - or combine the DataLoaders using :class:`~pytorch_lightning.trainer.supporters.CombinedLoader`, which Lightning will - automatically combine the batches from different DataLoaders. - - -Using LightningDataModule -========================= - -You can set more than one :class:`~torch.utils.data.DataLoader` in your :class:`~pytorch_lightning.core.datamodule.LightningDataModule` using its DataLoader hooks -and Lightning will use the correct one. - -.. testcode:: - - class DataModule(LightningDataModule): - - ... - - def train_dataloader(self): - return DataLoader(self.train_dataset) - - def val_dataloader(self): - return [DataLoader(self.val_dataset_1), DataLoader(self.val_dataset_2)] - - def test_dataloader(self): - return DataLoader(self.test_dataset) - - def predict_dataloader(self): - return DataLoader(self.predict_dataset) - - -Using LightningModule Hooks -=========================== - -Concatenated Dataset --------------------- - -For training with multiple Datasets, you can create a :class:`~torch.utils.data.DataLoader` class -which wraps your multiple Datasets using :class:`~torch.utils.data.ConcatDataset`. This, of course, -also works for testing, validation, and prediction Datasets. - -.. testcode:: - - from torch.utils.data import ConcatDataset - - - class LitModel(LightningModule): - def train_dataloader(self): - concat_dataset = ConcatDataset(datasets.ImageFolder(traindir_A), datasets.ImageFolder(traindir_B)) - - loader = DataLoader( - concat_dataset, batch_size=args.batch_size, shuffle=True, num_workers=args.workers, pin_memory=True - ) - return loader - - def val_dataloader(self): - # SAME - ... - - def test_dataloader(self): - # SAME - ... - - -Return Multiple DataLoaders ---------------------------- - -You can set multiple DataLoaders in your :class:`~pytorch_lightning.core.lightning.LightningModule`, and Lightning will take care of batch combination. - -For more details, refer to :paramref:`~pytorch_lightning.trainer.trainer.Trainer.multiple_trainloader_mode` - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - - loader_a = DataLoader(range(6), batch_size=4) - loader_b = DataLoader(range(15), batch_size=5) - - # pass loaders as a dict. This will create batches like this: - # {'a': batch from loader_a, 'b': batch from loader_b} - loaders = {"a": loader_a, "b": loader_b} - - # OR: - # pass loaders as sequence. This will create batches like this: - # [batch from loader_a, batch from loader_b] - loaders = [loader_a, loader_b] - - return loaders - -Furthermore, Lightning also supports nested lists and dicts (or a combination). - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - - loader_a = DataLoader(range(8), batch_size=4) - loader_b = DataLoader(range(16), batch_size=2) - - return {"a": loader_a, "b": loader_b} - - def training_step(self, batch, batch_idx): - # access a dictionary with a batch from each DataLoader - batch_a = batch["a"] - batch_b = batch["b"] - - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - - loader_a = DataLoader(range(8), batch_size=4) - loader_b = DataLoader(range(16), batch_size=4) - loader_c = DataLoader(range(32), batch_size=4) - loader_c = DataLoader(range(64), batch_size=4) - - # pass loaders as a nested dict. This will create batches like this: - loaders = {"loaders_a_b": [loader_a, loader_b], "loaders_c_d": {"c": loader_c, "d": loader_d}} - return loaders - - def training_step(self, batch, batch_idx): - # access the data - batch_a_b = batch["loaders_a_b"] - batch_c_d = batch["loaders_c_d"] - - batch_a = batch_a_b[0] - batch_b = batch_a_b[1] - - batch_c = batch_c_d["c"] - batch_d = batch_c_d["d"] - -Alternatively, you can also pass in a :class:`~pytorch_lightning.trainer.supporters.CombinedLoader` containing multiple DataLoaders. - -.. testcode:: - - from pytorch_lightning.trainer.supporters import CombinedLoader - - - def train_dataloader(self): - loader_a = DataLoader() - loader_b = DataLoader() - loaders = {"a": loader_a, "b": loader_b} - combined_loader = CombinedLoader(loaders, mode="max_size_cycle") - return combined_loader - - - def training_step(self, batch, batch_idx): - batch_a = batch["a"] - batch_b = batch["b"] - - -Multiple Validation/Test/Predict DataLoaders -============================================ - -For validation, test and predict DataLoaders, you can pass a single DataLoader or a list of them. This optional named -parameter can be used in conjunction with any of the above use cases. You can choose to pass -the batches sequentially or simultaneously, as is done for the training step. -The default mode for these DataLoaders is sequential. Note that when using a sequence of DataLoaders you need -to add an additional argument ``dataloader_idx`` in their corresponding step specific hook. The corresponding loop will process -the DataLoaders in sequential order; that is, the first DataLoader will be processed completely, then the second one, and so on. - -Refer to the following for more details for the default sequential option: - -- :meth:`~pytorch_lightning.core.hooks.DataHooks.val_dataloader` -- :meth:`~pytorch_lightning.core.hooks.DataHooks.test_dataloader` -- :meth:`~pytorch_lightning.core.hooks.DataHooks.predict_dataloader` - -.. testcode:: - - def val_dataloader(self): - loader_1 = DataLoader() - loader_2 = DataLoader() - return [loader_1, loader_2] - - - def validation_step(self, batch, batch_idx, dataloader_idx): - ... - - -Evaluation DataLoaders are iterated over sequentially. If you want to iterate over them in parallel, PyTorch Lightning provides a :class:`~pytorch_lightning.trainer.supporters.CombinedLoader` object which supports collections of DataLoaders such as list, tuple, or dictionary. The DataLoaders can be accessed using in the same way as the provided structure: - -.. testcode:: - - from pytorch_lightning.trainer.supporters import CombinedLoader - - - def val_dataloader(self): - loader_a = DataLoader() - loader_b = DataLoader() - loaders = {"a": loader_a, "b": loader_b} - combined_loaders = CombinedLoader(loaders, mode="max_size_cycle") - return combined_loaders - - - def validation_step(self, batch, batch_idx): - batch_a = batch["a"] - batch_b = batch["b"] - - -Evaluate with Additional DataLoaders -==================================== - -You can evaluate your models using additional DataLoaders even if the DataLoader specific hooks haven't been defined within your -:class:`~pytorch_lightning.core.lightning.LightningModule`. For example, this would be the case if your test data -set is not available at the time your model was declared. Simply pass the test set to the :meth:`~pytorch_lightning.trainer.trainer.Trainer.test` method: - -.. code-block:: python - - # setup your DataLoader - test = DataLoader(...) - - # test (pass in the loader) - trainer.test(dataloaders=test) - --------------- - -******************************************** -Accessing DataLoaders within LightningModule -******************************************** - -In the case that you require access to the DataLoader or Dataset objects, DataLoaders for each step can be accessed using the ``Trainer`` object: - -.. testcode:: - - from pytorch_lightning import LightningModule - - - class Model(LightningModule): - def test_step(self, batch, batch_idx, dataloader_idx): - test_dl = self.trainer.test_dataloaders[dataloader_idx] - test_dataset = test_dl.dataset - test_sampler = test_dl.sampler - ... - # extract metadata, etc. from the dataset: - ... - -If you are using a :class:`~pytorch_lightning.trainer.supporters.CombinedLoader` object which allows you to fetch batches from a collection of DataLoaders -simultaneously which supports collections of DataLoader such as list, tuple, or dictionary. The DataLoaders can be accessed using the same collection structure: - -.. code-block:: python - - from pytorch_lightning.trainer.supporters import CombinedLoader - - test_dl1 = ... - test_dl2 = ... - - # If you provided a list of DataLoaders: - - combined_loader = CombinedLoader([test_dl1, test_dl2]) - list_of_loaders = combined_loader.loaders - test_dl1 = list_of_loaders.loaders[0] - - - # If you provided dictionary of DataLoaders: - - combined_loader = CombinedLoader({"dl1": test_dl1, "dl2": test_dl2}) - dictionary_of_loaders = combined_loader.loaders - test_dl1 = dictionary_of_loaders["dl1"] - --------------- - -.. _sequential-data: - -*************** -Sequential Data -*************** - -Lightning has built in support for dealing with sequential data. - - -Packed Sequences as Inputs -========================== - -When using :class:`~torch.nn.utils.rnn.PackedSequence`, do two things: - -1. Return either a padded tensor in dataset or a list of variable length tensors in the DataLoader's `collate_fn `_ (example shows the list implementation). -2. Pack the sequence in forward or training and validation steps depending on use case. - -| - -.. testcode:: - - # For use in DataLoader - def collate_fn(batch): - x = [item[0] for item in batch] - y = [item[1] for item in batch] - return x, y - - - # In LightningModule - def training_step(self, batch, batch_idx): - x = rnn.pack_sequence(batch[0], enforce_sorted=False) - y = rnn.pack_sequence(batch[1], enforce_sorted=False) - - -Truncated Backpropagation Through Time (TBPTT) -============================================== - -There are times when multiple backwards passes are needed for each batch. -For example, it may save memory to use **Truncated Backpropagation Through Time** when training RNNs. - -Lightning can handle TBPTT automatically via this flag. - -.. testcode:: - - from pytorch_lightning import LightningModule - - - class MyModel(LightningModule): - def __init__(self): - super().__init__() - # Important: This property activates truncated backpropagation through time - # Setting this value to 2 splits the batch into sequences of size 2 - self.truncated_bptt_steps = 2 - - # Truncated back-propagation through time - def training_step(self, batch, batch_idx, hiddens): - # the training step must be updated to accept a ``hiddens`` argument - # hiddens are the hiddens from the previous truncated backprop step - out, hiddens = self.lstm(data, hiddens) - return {"loss": ..., "hiddens": hiddens} - -.. note:: If you need to modify how the batch is split, - override :func:`~pytorch_lightning.core.lightning.LightningModule.tbptt_split_batch`. - - -Iterable Datasets -================= -Lightning supports using :class:`~torch.utils.data.IterableDataset` as well as map-style Datasets. IterableDatasets provide a more natural -option when using sequential data. - -.. note:: When using an :class:`~torch.utils.data.IterableDataset` you must set the ``val_check_interval`` to 1.0 (the default) or an int - (specifying the number of training batches to run before each validation loop) when initializing the Trainer. This is - because the IterableDataset does not have a ``__len__`` and Lightning requires this to calculate the validation - interval when ``val_check_interval`` is less than one. Similarly, you can set ``limit_{mode}_batches`` to a float or - an int. If it is set to 0.0 or 0, it will set ``num_{mode}_batches`` to 0, if it is an int, it will set ``num_{mode}_batches`` - to ``limit_{mode}_batches``, if it is set to 1.0 it will run for the whole dataset, otherwise it will throw an exception. - Here ``mode`` can be train/val/test/predict. - -When iterable datasets are used, Lightning will pre-fetch 1 batch (in addition to the current batch) so it can detect -when the training will stop and run validation if necessary. - -.. testcode:: - - # IterableDataset - class CustomDataset(IterableDataset): - def __init__(self, data): - self.data_source = data - - def __iter__(self): - return iter(self.data_source) - - - # Setup DataLoader - def train_dataloader(self): - seq_data = ["A", "long", "time", "ago", "in", "a", "galaxy", "far", "far", "away"] - iterable_dataset = CustomDataset(seq_data) - - dataloader = DataLoader(dataset=iterable_dataset, batch_size=5) - return dataloader - - -.. testcode:: - - # Set val_check_interval - trainer = Trainer(val_check_interval=100) - - # Set limit_val_batches to 0.0 or 0 - trainer = Trainer(limit_val_batches=0.0) - - # Set limit_val_batches as an int - trainer = Trainer(limit_val_batches=100) diff --git a/docs/_sources/guides/speed.rst.txt b/docs/_sources/guides/speed.rst.txt deleted file mode 100644 index 1020755..0000000 --- a/docs/_sources/guides/speed.rst.txt +++ /dev/null @@ -1,477 +0,0 @@ -:orphan: - -.. testsetup:: * - - from pytorch_lightning.callbacks.early_stopping import EarlyStopping - -.. _training-speedup: - - -####################### -Speed Up Model Training -####################### - -When you are limited with the resources, it becomes hard to speed up model training and reduce the training time -without affecting the model's performance. There are multiple ways you can speed up your model's time to convergence. - -************************ -Training on Accelerators -************************ - -**Use when:** Whenever possible! - -With Lightning, running on GPUs, TPUs, IPUs on multiple nodes is a simple switch of a flag. - -GPU Training -============ - -Lightning supports a variety of plugins to speed up distributed GPU training. Most notably: - -* :class:`~pytorch_lightning.strategies.DDPStrategy` -* :class:`~pytorch_lightning.strategies.DDPShardedStrategy` -* :class:`~pytorch_lightning.strategies.DeepSpeedStrategy` - -.. code-block:: python - - # run on 1 gpu - trainer = Trainer(accelerator="gpu", devices=1) - - # train on 8 GPUs, using the DDP strategy - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp") - - # train on multiple GPUs across nodes (uses 8 GPUs in total) - trainer = Trainer(accelerator="gpu", devices=2, num_nodes=4) - - -GPU Training Speedup Tips -------------------------- - -When training on single or multiple GPU machines, Lightning offers a host of advanced optimizations to improve throughput, memory efficiency, and model scaling. -Refer to :doc:`Advanced GPU Optimized Training for more details <../advanced/model_parallel>`. - -Prefer DDP Over DP -^^^^^^^^^^^^^^^^^^ -:class:`~pytorch_lightning.strategies.dp.DataParallelStrategy` performs three GPU transfers for EVERY batch: - -1. Copy the model to the device. -2. Copy the data to the device. -3. Copy the outputs of each device back to the main device. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/distributed_training/dp.gif - :alt: Animation showing DP execution. - :width: 500 - :align: center - -| - -Whereas :class:`~pytorch_lightning.strategies.ddp.DDPStrategy` only performs two transfer operations, making DDP much faster than DP: - -1. Moving data to the device. -2. Transfer and sync gradients. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/distributed_training/ddp.gif - :alt: Animation showing DDP execution. - :width: 500 - :align: center - -| - -For more details on how to tune performance with DDP, please see the :ref:`DDP Optimizations ` section. - -DataLoaders -^^^^^^^^^^^ - -When building your DataLoader set ``num_workers>0`` and ``pin_memory=True`` (only for GPUs). - -.. code-block:: python - - Dataloader(dataset, num_workers=8, pin_memory=True) - -num_workers -^^^^^^^^^^^ - -The question of how many workers to specify in ``num_workers`` is tricky. Here's a summary of `some references `_, and our suggestions: - -1. ``num_workers=0`` means ONLY the main process will load batches (that can be a bottleneck). -2. ``num_workers=1`` means ONLY one worker (just not the main process) will load data, but it will still be slow. -3. The performance of high ``num_workers`` depends on the batch size and your machine. -4. A general place to start is to set ``num_workers`` equal to the number of CPU cores on that machine. You can get the number of CPU cores in python using ``os.cpu_count()``, but note that depending on your batch size, you may overflow RAM memory. - -.. warning:: Increasing ``num_workers`` will ALSO increase your CPU memory consumption. - -The best thing to do is to increase the ``num_workers`` slowly and stop once there is no more improvement in your training speed. - -For debugging purposes or for dataloaders that load very small datasets, it is desirable to set ``num_workers=0``. However, this will always log a warning for every dataloader with ``num_workers <= min(2, os.cpu_count())``. In such cases, you can specifically filter this warning by using: - -.. code-block:: python - - import warnings - - warnings.filterwarnings("ignore", ".*Consider increasing the value of the `num_workers` argument*") - - # or to ignore all warnings that could be false positives - from pytorch_lightning.utilities.warnings import PossibleUserWarning - - warnings.filterwarnings("ignore", category=PossibleUserWarning) - -Spawn -^^^^^ - -When using ``strategy="ddp_spawn"`` or training on TPUs, the way multiple GPUs/TPU cores are used is by calling :obj:`torch.multiprocessing` -``.spawn()`` under the hood. The problem is that PyTorch has issues with ``num_workers>0`` when using ``.spawn()``. For this reason, we recommend you -use ``strategy="ddp"`` so you can increase the ``num_workers``, however since DDP doesn't work in an interactive environment like IPython/Jupyter notebooks -your script has to be callable like so: - -.. code-block:: bash - - python my_program.py - -However, using ``strategy="ddp_spawn"`` enables to reduce memory usage with In-Memory Dataset and shared memory tensors. For more info, checkout -:ref:`Sharing Datasets Across Process Boundaries ` section. - -Persistent Workers -^^^^^^^^^^^^^^^^^^ - -When using ``strategy="ddp_spawn"`` and ``num_workers>0``, consider setting ``persistent_workers=True`` inside your DataLoader since it can result in data-loading bottlenecks and slowdowns. -This is a limitation of Python ``.spawn()`` and PyTorch. - - -TPU Training -============ - -You can set the ``devices`` trainer argument to 1, [7] (specific core) or eight cores. - -.. code-block:: python - - # train on 1 TPU core - trainer = Trainer(accelerator="tpu", devices=1) - - # train on 7th TPU core - trainer = Trainer(accelerator="tpu", devices=[7]) - - # train on 8 TPU cores - trainer = Trainer(accelerator="tpu", devices=8) - -To train on more than eight cores (a POD), -submit this script using the xla_dist script. - -Example:: - - python -m torch_xla.distributed.xla_dist - --tpu=$TPU_POD_NAME - --conda-env=torch-xla-nightly - --env=XLA_USE_BF16=1 - -- python your_trainer_file.py - - -Read more in our :ref:`training-speedup` and :ref:`plugins` guides. - - ------------ - -************** -Early Stopping -************** - -Usually, long training epochs can lead to either overfitting or no major improvements in your metrics due to no limited convergence. -Here :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback can help you stop the training entirely by monitoring a metric of your choice. - -You can read more about it :ref:`here `. - ----------- - -.. _speed-amp: - -********************************* -Mixed Precision (16-bit) Training -********************************* - -Lower precision, such as the 16-bit floating-point, enables the training and deployment of large neural networks since they require less memory, enhance data transfer operations since they required -less memory bandwidth and run match operations much faster on GPUs that support Tensor Core. - -**Use when:** - -* You want to optimize for memory usage on a GPU. -* You have a GPU that supports 16-bit precision (NVIDIA pascal architecture or newer). -* Your optimization algorithm (training_step) is numerically stable. -* You want to be the cool person in the lab :p - -.. raw:: html - - - -| - -Mixed precision combines the use of both 32 and 16-bit floating points to reduce memory footprint during model training, resulting in improved performance, achieving upto +3X speedups on modern GPUs. - -Lightning offers mixed precision training for GPUs and CPUs, as well as bfloat16 mixed precision training for TPUs. - - -.. testcode:: - :skipif: torch.cuda.device_count() < 4 - - # 16-bit precision - trainer = Trainer(precision=16, accelerator="gpu", devices=4) - - -Read more about :ref:`mixed-precision training `. - - ----------------- - - -*********************** -Control Training Epochs -*********************** - -**Use when:** You run a hyperparameter search to find good initial parameters and want to save time, cost (money), or power (environment). -It can allow you to be more cost efficient and also run more experiments at the same time. - -You can use Trainer flags to force training for a minimum number of epochs or limit it to a max number of epochs. Use the ``min_epochs`` and ``max_epochs`` Trainer flags to set the number of epochs to run. -Setting ``min_epochs=N`` makes sure that the training will run for at least ``N`` epochs. Setting ``max_epochs=N`` will ensure that training won't happen after -``N`` epochs. - -.. testcode:: - - # DEFAULT - trainer = Trainer(min_epochs=1, max_epochs=1000) - - -If running iteration based training, i.e., infinite / iterable DataLoader, you can also control the number of steps with the ``min_steps`` and ``max_steps`` flags: - -.. testcode:: - - trainer = Trainer(max_steps=1000) - - trainer = Trainer(min_steps=100) - -You can also interrupt training based on training time: - -.. testcode:: - - # Stop after 12 hours of training or when reaching 10 epochs (string) - trainer = Trainer(max_time="00:12:00:00", max_epochs=10) - - # Stop after 1 day and 5 hours (dict) - trainer = Trainer(max_time={"days": 1, "hours": 5}) - -Learn more in our :ref:`trainer_flags` guide. - - ----------------- - -**************************** -Control Validation Frequency -**************************** - -Check Validation Every n Epochs -=============================== - -**Use when:** You have a small dataset and want to run fewer validation checks. - -You can limit validation check to only run every n epochs using the ``check_val_every_n_epoch`` Trainer flag. - -.. testcode:: - - # default - trainer = Trainer(check_val_every_n_epoch=1) - - # runs validation after every 7th Epoch - trainer = Trainer(check_val_every_n_epoch=7) - - -Validation Within Training Epoch -================================ - -**Use when:** You have a large training dataset and want to run mid-epoch validation checks. - -For large datasets, it's often desirable to check validation multiple times within a training epoch. -Pass in a float to check that often within one training epoch. Pass in an int ``K`` to check every ``K`` training batch. -Must use an ``int`` if using an :class:`~torch.utils.data.IterableDataset`. - -.. testcode:: - - # default - trainer = Trainer(val_check_interval=1.0) - - # check every 1/4 th of an epoch - trainer = Trainer(val_check_interval=0.25) - - # check every 100 train batches (ie: for IterableDatasets or fixed frequency) - trainer = Trainer(val_check_interval=100) - -Learn more in our :ref:`trainer_flags` guide. - ----------------- - -********************* -Preload Data Into RAM -********************* - -**Use when:** You need access to all samples in a dataset at once. - -When your training or preprocessing requires many operations to be performed on entire dataset(s), it can -sometimes be beneficial to store all data in RAM given there is enough space. -However, loading all data at the beginning of the training script has the disadvantage that it can take a long -time, and hence, it slows down the development process. Another downside is that in multiprocessing (e.g., DDP) -the data would get copied in each process. -One can overcome these problems by copying the data into RAM in advance. -Most UNIX-based operating systems provide direct access to tmpfs through a mount point typically named ``/dev/shm``. - -Increase shared memory if necessary. Refer to the documentation of your OS on how to do this. - -1. Copy training data to shared memory: - - .. code-block:: bash - - cp -r /path/to/data/on/disk /dev/shm/ - -2. Refer to the new data root in your script or command-line arguments: - - .. code-block:: python - - datamodule = MyDataModule(data_root="/dev/shm/my_data") - ---------- - -************** -Model Toggling -************** - -**Use when:** Performing gradient accumulation with multiple optimizers in a -distributed setting. - -Here is an explanation of what it does: - -* Considering the current optimizer as A and all other optimizers as B. -* Toggling, which means all parameters from B exclusive to A will have their ``requires_grad`` attribute set to ``False``. -* Restoring their original state when exiting the context manager. - -When performing gradient accumulation, there is no need to perform grad synchronization during the accumulation phase. -Setting ``sync_grad`` to ``False`` will block this synchronization and improve your training speed. - -:class:`~pytorch_lightning.core.optimizer.LightningOptimizer` provides a -:meth:`~pytorch_lightning.core.optimizer.LightningOptimizer.toggle_model` function as a -:func:`contextlib.contextmanager` for advanced users. - -Here is an example of an advanced use case: - -.. testcode:: - - # Scenario for a GAN with gradient accumulation every two batches and optimized for multiple GPUs. - class SimpleGAN(LightningModule): - def __init__(self): - super().__init__() - self.automatic_optimization = False - - def training_step(self, batch, batch_idx): - # Implementation follows the PyTorch tutorial: - # https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html - g_opt, d_opt = self.optimizers() - - X, _ = batch - X.requires_grad = True - batch_size = X.shape[0] - - real_label = torch.ones((batch_size, 1), device=self.device) - fake_label = torch.zeros((batch_size, 1), device=self.device) - - # Sync and clear gradients - # at the end of accumulation or - # at the end of an epoch. - is_last_batch_to_accumulate = (batch_idx + 1) % 2 == 0 or self.trainer.is_last_batch - - g_X = self.sample_G(batch_size) - - ########################## - # Optimize Discriminator # - ########################## - with d_opt.toggle_model(sync_grad=is_last_batch_to_accumulate): - d_x = self.D(X) - errD_real = self.criterion(d_x, real_label) - - d_z = self.D(g_X.detach()) - errD_fake = self.criterion(d_z, fake_label) - - errD = errD_real + errD_fake - - self.manual_backward(errD) - if is_last_batch_to_accumulate: - d_opt.step() - d_opt.zero_grad() - - ###################### - # Optimize Generator # - ###################### - with g_opt.toggle_model(sync_grad=is_last_batch_to_accumulate): - d_z = self.D(g_X) - errG = self.criterion(d_z, real_label) - - self.manual_backward(errG) - if is_last_batch_to_accumulate: - g_opt.step() - g_opt.zero_grad() - - self.log_dict({"g_loss": errG, "d_loss": errD}, prog_bar=True) - ------ - -***************** -Set Grads to None -***************** - -In order to improve performance, you can override :meth:`~pytorch_lightning.core.lightning.LightningModule.optimizer_zero_grad`. - -For a more detailed explanation of the pros / cons of this technique, -read the documentation for :meth:`~torch.optim.Optimizer.zero_grad` by the PyTorch team. - -.. testcode:: - - class Model(LightningModule): - def optimizer_zero_grad(self, epoch, batch_idx, optimizer, optimizer_idx): - optimizer.zero_grad(set_to_none=True) - - ------ - -*************** -Things to Avoid -*************** - -.item(), .numpy(), .cpu() -========================= - -Don't call ``.item()`` anywhere in your code. Use ``.detach()`` instead to remove the connected graph calls. Lightning -takes a great deal of care to be optimized for this. - -Clear Cache -=========== - -Don't call :func:`torch.cuda.empty_cache` unnecessarily! Every time you call this, ALL your GPUs have to wait to sync. - -Transferring Tensors to Device -============================== - -LightningModules know what device they are on! Construct tensors on the device directly to avoid CPU->Device transfer. - -.. code-block:: python - - # bad - t = torch.rand(2, 2).cuda() - - # good (self is LightningModule) - t = torch.rand(2, 2, device=self.device) - - -For tensors that need to be model attributes, it is best practice to register them as buffers in the module's -``__init__`` method: - -.. code-block:: python - - # bad - self.t = torch.rand(2, 2, device=self.device) - - # good - self.register_buffer("t", torch.rand(2, 2)) diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt deleted file mode 100644 index d646beb..0000000 --- a/docs/_sources/index.rst.txt +++ /dev/null @@ -1,275 +0,0 @@ -.. PyTorch-Lightning documentation master file, created by - sphinx-quickstart on Fri Nov 15 07:48:22 2019. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -⚡ PyTorch Lightning에 오신 것을 환영합니다! -============================================== - -.. twocolumns:: - :left: - .. image:: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/mov.gif - :alt: Animation showing how to convert a standard training loop to a Lightning loop - :right: - PyTorch Lightning(파이토치 라이트닝))은 대규모에서 성능을 포기하지 않으면서 최대한의 유연성을 필요로 하는 전문적인 AI 연구자들과 머신러닝 엔지니어들을 위한 딥러닝 프레임워크입니다. - Lightning(라이트닝)은 프로젝트가 생각으로부터 문서 / 제품화에 이르는 동안 함께 발전합니다. - -.. raw:: html - -
-
-
-
- -.. join_slack:: - :align: center - :margin: 0 - -.. raw:: html - -
-
- - -.. raw:: html - -
- - -Lightning 설치하기 ----------------------- - - -.. raw:: html - -
-
- -Pip 사용자라면, - -.. code-block:: bash - - pip install pytorch-lightning - -.. raw:: html - -
-
- -Conda 사용자라면, - -.. code-block:: bash - - conda install pytorch-lightning -c conda-forge - -.. raw:: html - -
-
- -또는 `advanced install guide `_ 참조하세요. - -.. raw:: html - -
- -처음이신가요? ------------------ - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. customcalloutitem:: - :header: LIGHTNING 15분 만에 배워보기 - :description: 일반적인 Lightning 워크플로우의 주요한 7단계를 배웁니다. - :button_link: starter/introduction.html - -.. customcalloutitem:: - :header: Benchmarking - :description: Learn how to benchmark PyTorch Lightning. - :button_link: benchmarking/benchmarks.html - -.. raw:: html - -
-
- -.. End of callout item section - -.. raw:: html - -
- -이미 Lightning 사용자라면? ---------------------------- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. customcalloutitem:: - :description: Learn Lightning in small bites at 4 levels of expertise: Introductory, intermediate, advanced and expert. - :header: Level Up! - :button_link: expertise_levels.html - -.. customcalloutitem:: - :description: Detailed description of API each package. Assumes you already have basic Lightning knowledge. - :header: API Reference - :button_link: api_references.html - -.. customcalloutitem:: - :description: From NLP, Computer vision to RL and meta learning - see how to use Lightning in ALL research areas. - :header: Hands-on Examples - :button_link: tutorials.html - -.. customcalloutitem:: - :description: Learn how to do everything from hyperparameters sweeps to cloud training to Pruning and Quantization with Lightning. - :header: Common Workflows - :button_link: common_usecases.html - -.. customcalloutitem:: - :description: Convert your current code to Lightning - :header: Convert code to PyTorch Lightning - :button_link: starter/converting.html - - -.. raw:: html - -
-
- -.. End of callout item section - -.. raw:: html - -
- -.. toctree:: - :maxdepth: 1 - :name: start - :caption: Get Started - - starter/introduction - Organize existing PyTorch into Lightning - - -.. toctree:: - :maxdepth: 2 - :name: levels - :caption: Level Up - - levels/core_skills - levels/intermediate - levels/advanced - levels/expert - -.. toctree:: - :maxdepth: 2 - :name: pl_docs - :caption: Core API - - common/lightning_module - common/trainer - -.. toctree:: - :maxdepth: 1 - :name: Common Workflows - :caption: Common Workflows - - Avoid overfitting - model/build_model.rst - common/hyperparameters - common/progress_bar - deploy/production - advanced/training_tricks - cli/lightning_cli - tuning/profiler - Finetune a model - Manage experiments - clouds/cluster - advanced/model_parallel - clouds/cloud_training - Save and load model progress - Save memory with half-precision - Train on single or multiple GPUs - Train on single or multiple HPUs - Train on single or multiple IPUs - Train on single or multiple TPUs - model/own_your_loop - -.. toctree:: - :maxdepth: 1 - :name: Glossary - :caption: Glossary - - Accelerators - Callback - Checkpointing - Cluster - Cloud checkpoint - Console Logging - Debugging - Early stopping - Experiment manager (Logger) - Fault tolerant training - Flash - Grid AI - GPU - Half precision - HPU - Inference - IPU - Lightning CLI - Lightning Lite - LightningDataModule - LightningModule - Lightning Transformers - Log - Loops - TPU - Metrics - Model - Model Parallel - Plugins - Progress bar - Production - Predict - Profiler - Pruning and Quantization - Remote filesystem and FSSPEC - Strategy registry - Style guide - Sweep - SWA - SLURM - Transfer learning - Trainer - Torch distributed - -.. toctree:: - :maxdepth: 1 - :name: Hands-on Examples - :caption: Hands-on Examples - :glob: - - PyTorch Lightning 101 class - From PyTorch to PyTorch Lightning [Blog] - From PyTorch to PyTorch Lightning [Video] - - -.. raw:: html - -
- -색인 및 검색 ------------------- - -* :ref:`genindex` -* :ref:`search` diff --git a/docs/_sources/levels/advanced.rst.txt b/docs/_sources/levels/advanced.rst.txt deleted file mode 100644 index 4ffe090..0000000 --- a/docs/_sources/levels/advanced.rst.txt +++ /dev/null @@ -1,87 +0,0 @@ - -############### -Advanced skills -############### - -Configure all aspects of Lightning for advanced usecases. - -.. join_slack:: - :align: left - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 15: Customize configs to run in production - :description: Enable composable YAMLs - :col_css: col-md-6 - :button_link: advanced_level_15.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 16: Customize the trainer - :description: Inject custom code into the trainer and modify the progress bar. - :col_css: col-md-6 - :button_link: advanced_level_16.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 17: Own the training loop - :description: Learn all the ways of owning your raw PyTorch loops with Lighting. - :col_css: col-md-6 - :button_link: advanced_level_17.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 18: Enable advanced checkpointing - :description: Enable composable or cloud based checkpoints. - :col_css: col-md-6 - :button_link: advanced_level_18.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 19: Explore IPUs - :description: Explore Intelligence Processing Unit (IPU) for model scaling. - :col_css: col-md-6 - :button_link: advanced_level_19.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 20: Explore HPUs - :description: Explore Havana Gaudi Processing Unit (HPU) for model scaling. - :col_css: col-md-6 - :button_link: advanced_level_20.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 21: Master TPUs - :description: Master TPUs and run on cloud TPUs. - :col_css: col-md-6 - :button_link: advanced_level_21.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 22: Reach 1 trillion parameters on GPUs - :description: Scale to 1 trillion params on GPUs. - :col_css: col-md-6 - :button_link: advanced_level_22.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/advanced_level_15.rst.txt b/docs/_sources/levels/advanced_level_15.rst.txt deleted file mode 100644 index 761dbd3..0000000 --- a/docs/_sources/levels/advanced_level_15.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -################################################ -Level 15: Customize configs to run in production -################################################ - -This level goes over advanced YAML use for running models in production. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Control it all via YAML - :description: Enable composable YAMLs. - :col_css: col-md-6 - :button_link: ../cli/lightning_cli_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: 2: Use YAML for production - :description: Use the Lightning CLI with YAMLs for production environments. - :col_css: col-md-6 - :button_link: ../cli/lightning_cli_advanced_2.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/advanced_level_16.rst.txt b/docs/_sources/levels/advanced_level_16.rst.txt deleted file mode 100644 index fd41df1..0000000 --- a/docs/_sources/levels/advanced_level_16.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -############################### -Level 16: Customize the trainer -############################### - -In this level, you'll learn to modify the Trainer behavior. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Create and use Callbacks - :description: Modify Trainer behavior with reusable, self-contained code. - :col_css: col-md-6 - :button_link: ../extensions/callbacks.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Customize the progress bar - :description: Create beautiful custom progress bars. - :col_css: col-md-6 - :button_link: ../common/progress_bar.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/advanced_level_17.rst.txt b/docs/_sources/levels/advanced_level_17.rst.txt deleted file mode 100644 index c05c8c9..0000000 --- a/docs/_sources/levels/advanced_level_17.rst.txt +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -############################### -Level 17: Own the training loop -############################### - -Learn all the ways of owning your raw PyTorch loops with Lighting. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Enable manual optimization - :description: Gain control of the training loop with manual optimization and LightningModule methods. - :col_css: col-md-4 - :button_link: ../model/build_model_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Use a Raw PyTorch Loop - :description: Migrate complex PyTorch projects to Lightning and push bleeding-edge research with the raw PyTorch loop. - :col_css: col-md-4 - :button_link: ../model/build_model_expert.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Make a custom Lightning Loop - :description: Conduct bleeding-edge research like meta-learning and RL with a custom Loop. - :col_css: col-md-4 - :button_link: ../extensions/loops.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/advanced_level_18.rst.txt b/docs/_sources/levels/advanced_level_18.rst.txt deleted file mode 100644 index a7d7966..0000000 --- a/docs/_sources/levels/advanced_level_18.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -####################################### -Level 18: Enable advanced checkpointing -####################################### - -This level shows you how to enable composable and/or cloud based checkpoints. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Enable composable and cloud checkpoints - :description: Enable cloud-based checkpointing and composable checkpoints. - :col_css: col-md-6 - :button_link: ../common/checkpointing_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Explore remote filesystems - :description: Explore advanced cloud checkpointing features. - :col_css: col-md-6 - :button_link: ../common/remote_fs.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/advanced_level_19.rst.txt b/docs/_sources/levels/advanced_level_19.rst.txt deleted file mode 100644 index c7b6697..0000000 --- a/docs/_sources/levels/advanced_level_19.rst.txt +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -###################### -Level 19: Explore IPUs -###################### - -Explore Intelligence Processing Unit (IPU) for model scaling. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Prepare your code (Optional) - :description: Prepare your code to run on any hardware. - :col_css: col-md-4 - :button_link: ../accelerators/accelerator_prepare.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Train models on IPUs - :description: Learn the basics of single and multi-IPU training. - :col_css: col-md-4 - :button_link: ../accelerators/ipu_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Optimize models training on IPUs - :description: Tune model performance with mix-precision and the performance analyser. - :col_css: col-md-4 - :button_link: ../accelerators/ipu_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/advanced_level_20.rst.txt b/docs/_sources/levels/advanced_level_20.rst.txt deleted file mode 100644 index 7e9d562..0000000 --- a/docs/_sources/levels/advanced_level_20.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -###################### -Level 19: Explore HPUs -###################### - -Explore Intelligence Processing Unit (IPU) for model scaling. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on HPUs - :description: Learn the basics of single and multi-HPU core training. - :col_css: col-md-6 - :button_link: ../accelerators/hpu_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Optimize models training on HPUs - :description: Enable state-of-the-art scaling with advanced mix-precision settings. - :col_css: col-md-6 - :button_link: ../accelerators/hpu_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/advanced_level_21.rst.txt b/docs/_sources/levels/advanced_level_21.rst.txt deleted file mode 100644 index 5252a1c..0000000 --- a/docs/_sources/levels/advanced_level_21.rst.txt +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -##################### -Level 21: Master TPUs -##################### - -Master cloud TPU training with profiling and scaling techniques. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run on cloud TPUs - :description: Scale massive models using cloud TPUs. - :col_css: col-md-4 - :button_link: ../accelerators/tpu_intermediate.html - :height: 180 - :tag: intermediate - -.. displayitem:: - :header: Explore advanced TPU scaling techniques - :description: Dive into XLA and advanced techniques to optimize TPU-powered models. - :col_css: col-md-4 - :button_link: ../accelerators/tpu_advanced.html - :height: 180 - :tag: advanced - -.. displayitem:: - :header: Profile TPU code - :description: Learn to profile TPU code. - :col_css: col-md-4 - :button_link: ../tuning/profiler_advanced.html - :height: 180 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/advanced_level_22.rst.txt b/docs/_sources/levels/advanced_level_22.rst.txt deleted file mode 100644 index a90a482..0000000 --- a/docs/_sources/levels/advanced_level_22.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -############################################# -Level 22: Reach 1 trillion parameters on GPUs -############################################# - -Scale to 1 trillion+ parameters with multiple distributed strategies. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Scale with distributed strategies - :description: Learn about different distributed strategies to reach bigger model parameter sizes. - :col_css: col-md-6 - :button_link: ../accelerators/gpu_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Reach 1 trillion parameters on GPUs - :description: Scale to 1 trillion params on GPUs with FSDP and Deepspeed. - :col_css: col-md-6 - :button_link: ../advanced/model_parallel.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/basic_level_2.rst.txt b/docs/_sources/levels/basic_level_2.rst.txt deleted file mode 100644 index 348a389..0000000 --- a/docs/_sources/levels/basic_level_2.rst.txt +++ /dev/null @@ -1,41 +0,0 @@ -:orphan: - -###################################### -Level 2: Add a validation and test set -###################################### - -.. raw:: html - -
-
- -.. displayitem:: - :header: Validate and test a model - :description: Add a validation and test data split to avoid overfitting. - :col_css: col-md-4 - :button_link: ../common/evaluation_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Save your model progress - :description: Learn to save the state of a model as it trains. - :col_css: col-md-4 - :button_link: ../common/checkpointing_basic.html#save-a-checkpoint - :height: 150 - :tag: basic - -.. displayitem:: - :header: Enable early stopping - :description: Use early stopping to decide when to stop training your model. - :col_css: col-md-4 - :button_link: ../common/early_stopping.html - :height: 150 - :tag: basic - - - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/basic_level_5.rst.txt b/docs/_sources/levels/basic_level_5.rst.txt deleted file mode 100644 index 98de180..0000000 --- a/docs/_sources/levels/basic_level_5.rst.txt +++ /dev/null @@ -1,39 +0,0 @@ -:orphan: - -########################################################## -Level 5: Debug, visualize and find performance bottlenecks -########################################################## - -.. raw:: html - -
-
- -.. displayitem:: - :header: Debug your model - :description: Learn the basics of model debugging - :col_css: col-md-4 - :button_link: ../debug/debugging_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Find bottlenecks in training - :description: Learn to find bottlenecks in the training loop. - :col_css: col-md-4 - :button_link: ../tuning/profiler_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Visualize metrics, images, and text. - :description: Learn how to track and visualize metrics, images and text. - :col_css: col-md-4 - :button_link: ../visualize/logging_basic.html - :height: 150 - :tag: basic - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/core_level_3.rst.txt b/docs/_sources/levels/core_level_3.rst.txt deleted file mode 100644 index ea34853..0000000 --- a/docs/_sources/levels/core_level_3.rst.txt +++ /dev/null @@ -1,31 +0,0 @@ -:orphan: - -#################################### -Level 3: Visualize training progress -#################################### - -.. raw:: html - -
-
- -.. displayitem:: - :header: Visualize metrics, images, and text. - :description: Learn how to track and visualize metrics, images and text. - :col_css: col-md-6 - :button_link: ../visualize/logging_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Use third-party experiment managers - :description: Enable third-party experiment managers with advanced visualizations. - :col_css: col-md-6 - :button_link: ../visualize/logging_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/core_level_6.rst.txt b/docs/_sources/levels/core_level_6.rst.txt deleted file mode 100644 index 99686bc..0000000 --- a/docs/_sources/levels/core_level_6.rst.txt +++ /dev/null @@ -1,39 +0,0 @@ -:orphan: - -################################# - Level 6: Predict with your model -################################# - -.. raw:: html - -
-
- -.. displayitem:: - :header: Load model weights - :description: Learn to load the weights (checkpoint) of a model. - :col_css: col-md-4 - :button_link: ../common/checkpointing_basic.html#lightningmodule-from-checkpoint - :height: 150 - :tag: basic - -.. displayitem:: - :header: Predict with LightningModule - :description: Learn the basics of predicting with Lightning. - :col_css: col-md-4 - :button_link: ../deploy/production_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Predict with pure PyTorch - :description: Learn to use pure PyTorch without the Lightning dependencies for prediction. - :col_css: col-md-4 - :button_link: ../deploy/production_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/core_skills.rst.txt b/docs/_sources/levels/core_skills.rst.txt deleted file mode 100644 index 19cab69..0000000 --- a/docs/_sources/levels/core_skills.rst.txt +++ /dev/null @@ -1,70 +0,0 @@ - -############ -Basic skills -############ -Learn the basics of model development with Lightning. Researchers and machine learning engineers should start here. - -.. join_slack:: - :align: left - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 1: Train a model - :description: Learn the basics of training a model. - :button_link: ../model/train_model_basic.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :header: Level 2: Add a validation and test set - :description: Add validation and test sets to avoid over/underfitting. - :button_link: /levels/basic_level_2.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :header: Level 3: Use pretrained models - :description: Learn how to use pretrained models with Lightning - :button_link: ../advanced/transfer_learning.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :header: Level 4: Enable script parameters - :description: Add parameters to your script so you can run from the commandline. - :button_link: ../common/hyperparameters.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :header: Level 5: Understand and visualize your model - :description: Remove bottlenecks and visualize your model - :button_link: ../levels/basic_level_5.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. displayitem:: - :description: Use your model for predictions. - :header: Level 6: Predict with your model - :button_link: core_level_6.html - :col_css: col-md-6 - :height: 150 - :tag: basic - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/expert.rst.txt b/docs/_sources/levels/expert.rst.txt deleted file mode 100644 index d41680b..0000000 --- a/docs/_sources/levels/expert.rst.txt +++ /dev/null @@ -1,63 +0,0 @@ - -############# -Expert skills -############# - -Customize and extend Lightning for things like custom hardware or distributed strategies. - -.. join_slack:: - :align: left - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 23: Extend the Lightning CLI - :description: Extend the functionality of the Lightning CLI. - :col_css: col-md-6 - :button_link: expert_level_23.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 24: Integrate a custom cluster - :description: Integrate a custom cluster into Lightning. - :col_css: col-md-6 - :button_link: expert_level_24.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 25: Explore fault-tolerance in-depth - :description: Understand the details of fault-tolerance. - :col_css: col-md-6 - :button_link: ../clouds/fault_tolerant_training_faq.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 26: Make your own profiler - :description: Make your own profiler. - :col_css: col-md-6 - :button_link: ../tuning/profiler_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 27: Add a new accelerator or Strategy - :description: Integrate a new accelerator or distributed strategy. - :col_css: col-md-6 - :button_link: expert_level_27.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/expert_level_23.rst.txt b/docs/_sources/levels/expert_level_23.rst.txt deleted file mode 100644 index 9b143a0..0000000 --- a/docs/_sources/levels/expert_level_23.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -################################## -Level 23: Extend the Lightning CLI -################################## - -Extend the functionality of the Lightning CLI. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Customize configs for complex projects - :description: Learn how to connect complex projects with each Registry. - :col_css: col-md-6 - :button_link: ../cli/lightning_cli_advanced_3.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Extend the Lightning CLI - :description: Customize the Lightning CLI - :col_css: col-md-6 - :button_link: ../cli/lightning_cli_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/expert_level_24.rst.txt b/docs/_sources/levels/expert_level_24.rst.txt deleted file mode 100644 index b32a8ac..0000000 --- a/docs/_sources/levels/expert_level_24.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -#################################### -Level 24: Integrate a custom cluster -#################################### - -Extend the functionality of the Lightning CLI. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Integrate your own cluster - :description: Learn how to integrate your own cluster - :col_css: col-md-6 - :button_link: ../clouds/cluster_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Run on your own cloud - :description: Learn how to run on your Company or University private clouds. - :col_css: col-md-6 - :button_link: ../clouds/run_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/expert_level_27.rst.txt b/docs/_sources/levels/expert_level_27.rst.txt deleted file mode 100644 index c2d682b..0000000 --- a/docs/_sources/levels/expert_level_27.rst.txt +++ /dev/null @@ -1,53 +0,0 @@ -:orphan: - -########################################### -Level 27: Add a new accelerator or Strategy -########################################### - -Integrate a new accelerator or distributed strategy. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Develop a new strategy - :description: Develop new strategies for training and deploying larger and larger models. - :col_css: col-md-6 - :button_link: ../accelerators/gpu_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: 2: Customize checkpointing with new strategies. - :description: Customize checkpointing for custom distributed strategies and accelerators. - :col_css: col-md-6 - :button_link: ../common/checkpointing_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: 3: Register a new strategy - :description: Enable a new strategy to be used in Lightning. - :col_css: col-md-6 - :button_link: ../advanced/strategy_registry.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: 4: Create a new precision technique - :description: Create new precision techniques and enable them through Lightning. - :col_css: col-md-6 - :button_link: ../common/precision_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate.rst.txt b/docs/_sources/levels/intermediate.rst.txt deleted file mode 100644 index 331e477..0000000 --- a/docs/_sources/levels/intermediate.rst.txt +++ /dev/null @@ -1,89 +0,0 @@ - -################### -Intermediate skills -################### - -Learn to scale up your models and enable collaborative model development at academic or industry research labs. - -.. join_slack:: - :align: left - ----- - -.. include:: ../links.rst - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 7: Interactive cloud development - :description: Learn how to access GPUs and TPUs on the cloud. - :button_link: intermediate_level_7.html - :col_css: col-md-6 - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 8: Train in the background on the cloud - :description: Learn how to run models on the cloud in the background. - :button_link: intermediate_level_8.html - :col_css: col-md-6 - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 9: Modularize your projects - :description: Create DataModules to enable dataset reusability. - :col_css: col-md-6 - :button_link: intermediate_level_9.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 10: Understand your model - :description: Use advanced visuals to find the best performing model. - :col_css: col-md-6 - :button_link: intermediate_level_10.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 11: Explore SOTA scaling techniques - :description: Explore SOTA techniques to help convergence, stability and scalability. - :col_css: col-md-6 - :button_link: intermediate_level_11.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 12: Deploy your models - :description: Learn how to deploy your models with optimizations like ONNX and torchscript. - :col_css: col-md-6 - :button_link: intermediate_level_12.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 13: Optimize training speed - :description: Use advanced profilers to mixed precision to train bigger models, faster. - :col_css: col-md-6 - :button_link: intermediate_level_13.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 14: Run on on-prem clusters - :description: Run on a custom on-prem cluster or SLURM cluster. - :col_css: col-md-6 - :button_link: intermediate_level_14.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate_level_10.rst.txt b/docs/_sources/levels/intermediate_level_10.rst.txt deleted file mode 100644 index d7f5dc5..0000000 --- a/docs/_sources/levels/intermediate_level_10.rst.txt +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -############################### -Level 10: Understand your model -############################### - -Find the best model using advanced visualizations for deeper insights. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Alter checkpoint behavior - :description: Learn to monitor metrics and enable checkpointing by condition. - :col_css: col-md-4 - :button_link: ../common/checkpointing_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 2: Visualize more than metrics - :description: Use advanced visualization techniques provided by experiment managers. - :col_css: col-md-4 - :button_link: ../visualize/logging_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 3: Granular control of logging - :description: Gain granular control over logging to optimize for speed. - :col_css: col-md-4 - :button_link: ../visualize/logging_advanced.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate_level_11.rst.txt b/docs/_sources/levels/intermediate_level_11.rst.txt deleted file mode 100644 index 4c7ed06..0000000 --- a/docs/_sources/levels/intermediate_level_11.rst.txt +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -######################################### -Level 11: Explore SOTA scaling techniques -######################################### - -In this level you'll explore SOTA techniques to help convergence, stability and scalability. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Half precision training - :description: Enable your models to train faster and save memory with different floating-point precision settings. - :col_css: col-md-6 - :button_link: ../common/precision_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: 2: SOTA scaling techniques - :description: Enable techniques to help scaling and convergence. - :col_css: col-md-6 - :button_link: ../advanced/training_tricks.html - :height: 150 - :tag: basic - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate_level_12.rst.txt b/docs/_sources/levels/intermediate_level_12.rst.txt deleted file mode 100644 index fe1c076..0000000 --- a/docs/_sources/levels/intermediate_level_12.rst.txt +++ /dev/null @@ -1,46 +0,0 @@ -:orphan: - -############################ -Level 12: Deploy your models -############################ - -In this level you'll learn a few options for deploying models into production. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Deploy with ONNX - :description: Optimize models for enterprise-scale production environments with ONNX. - :col_css: col-md-4 - :button_link: ../deploy/production_advanced.html - :height: 180 - :tag: advanced - -.. displayitem:: - :header: Deploy with torchscript - :description: Optimize models for enterprise-scale production environments with torchscript. - :col_css: col-md-4 - :button_link: ../deploy/production_advanced_2.html - :height: 180 - :tag: advanced - -.. displayitem:: - :header: Compress models for fast inference - :description: Compress model size for deployment with Quantization and Pruning. - :col_css: col-md-4 - :button_link: ../advanced/pruning_quantization.html - :height: 180 - :tag: advanced - - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate_level_13.rst.txt b/docs/_sources/levels/intermediate_level_13.rst.txt deleted file mode 100644 index 38ac7aa..0000000 --- a/docs/_sources/levels/intermediate_level_13.rst.txt +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -################################# -Level 13: Optimize training speed -################################# - -In this level you'll use advanced profilers and mixed precision techniques to train bigger models faster. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Explore advanced mixed precision settings - :description: Enable state-of-the-art scaling with advanced mix-precision settings. - :col_css: col-md-4 - :button_link: ../common/precision_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Enable advanced profilers - :description: Tune model performance with profilers. - :col_css: col-md-4 - :button_link: ../tuning/profiler_basic.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Profile PyTorch operations - :description: Learn to find bottlenecks in PyTorch operations. - :col_css: col-md-4 - :button_link: ../tuning/profiler_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate_level_14.rst.txt b/docs/_sources/levels/intermediate_level_14.rst.txt deleted file mode 100644 index a779ede..0000000 --- a/docs/_sources/levels/intermediate_level_14.rst.txt +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -################################# -Level 14: Run on on-prem clusters -################################# - -In this level you'll learn to run on onprem clusters. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run on an on-prem cluster - :description: Learn to train models on a general compute cluster. - :col_css: col-md-4 - :button_link: ../clouds/cluster_intermediate_1.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Run on a SLURM cluster - :description: Run models on a SLURM-managed cluster - :col_css: col-md-4 - :button_link: ../clouds/cluster_advanced.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Run with Torch Distributed - :description: Run models on a cluster with torch distributed. - :col_css: col-md-4 - :button_link: ../clouds/cluster_intermediate_2.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate_level_7.rst.txt b/docs/_sources/levels/intermediate_level_7.rst.txt deleted file mode 100644 index cc55fd7..0000000 --- a/docs/_sources/levels/intermediate_level_7.rst.txt +++ /dev/null @@ -1,51 +0,0 @@ -:orphan: - -###################################### -Level 7: Interactive cloud development -###################################### - -Learn to develop models on cloud GPUs and TPUs. - ----- - -.. raw:: html - -
-
- -.. displayitem:: - :header: Prepare your code (Optional) - :description: Prepare your code to run on any hardware. - :col_css: col-md-3 - :button_link: ../accelerators/accelerator_prepare.html - :height: 180 - :tag: basic - -.. displayitem:: - :header: Access a cloud machine with GPUs - :description: Learn how to get a cloud machine with single or multiple GPUs. - :col_css: col-md-3 - :button_link: ../clouds/session_basic.html - :height: 180 - :tag: basic - -.. displayitem:: - :header: GPU Training - :description: Learn the basics on single and multi-GPU training. - :col_css: col-md-3 - :button_link: ../accelerators/gpu_basic.html - :height: 180 - :tag: basic - -.. displayitem:: - :header: TPU Training - :description: Learn the basics on single and multi-TPU core training. - :col_css: col-md-3 - :button_link: ../accelerators/tpu_basic.html - :height: 180 - :tag: basic - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate_level_8.rst.txt b/docs/_sources/levels/intermediate_level_8.rst.txt deleted file mode 100644 index 190364a..0000000 --- a/docs/_sources/levels/intermediate_level_8.rst.txt +++ /dev/null @@ -1,53 +0,0 @@ -:orphan: - -################################ -Level 8: Run models on the cloud -################################ - -Learn to run models on the cloud in the background asynchroneously. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Run a model in the background on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: ../clouds/run_basic.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 2: Save up to 80% on cloud costs with fault-tolerant training - :description: Run on the cloud for 1/10th the price with fault-tolerant training. - :col_css: col-md-6 - :button_link: ../clouds/fault_tolerant_training_basic.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 3: Run many models at once - :description: Run many models at once (sweep) to find the best performing model. - :col_css: col-md-6 - :button_link: ../clouds/run_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 4: Run on your own cloud - :description: Learn how to run on your Company or University private clouds. - :col_css: col-md-6 - :button_link: ../clouds/run_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/levels/intermediate_level_9.rst.txt b/docs/_sources/levels/intermediate_level_9.rst.txt deleted file mode 100644 index 8c537d7..0000000 --- a/docs/_sources/levels/intermediate_level_9.rst.txt +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -################################# -Level 9: Modularize your projects -################################# - -This module teaches you how to setup complex projects that can be controlled via the CLI. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Modularize your datasets - :description: Reuse datasets across models by using DataModules - :col_css: col-md-4 - :button_link: ../data/datamodule.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 2: Control it all from the CLI - :description: Learn to control a LightningModule and LightningDataModule from the CLI - :col_css: col-md-4 - :button_link: ../cli/lightning_cli_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 3: Mix models and datasets - :description: Register models, datasets, optimizers and learning rate schedulers - :col_css: col-md-4 - :button_link: ../cli/lightning_cli_intermediate_2.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/docs/_sources/links.rst.txt b/docs/_sources/links.rst.txt deleted file mode 100644 index 64ec918..0000000 --- a/docs/_sources/links.rst.txt +++ /dev/null @@ -1,2 +0,0 @@ -.. _PyTorchJob: https://www.kubeflow.org/docs/components/training/pytorch/ -.. _Kubeflow: https://www.kubeflow.org diff --git a/docs/_sources/model/build_model.rst.txt b/docs/_sources/model/build_model.rst.txt deleted file mode 100644 index 8d12110..0000000 --- a/docs/_sources/model/build_model.rst.txt +++ /dev/null @@ -1,55 +0,0 @@ -:orphan: - -############# -Build a Model -############# - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Train a model - :description: Build a model to learn the basic ideas of Lightning - :col_css: col-md-4 - :button_link: train_model_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: 2: Validate and test a model - :description: Add a validation and test data split to avoid overfitting. - :col_css: col-md-4 - :button_link: validate_model_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: 3: Supercharge training - :description: Enable state-of-the-art training techniques with the Trainer features. - :col_css: col-md-4 - :button_link: build_model_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: LightningModule API - :description: Dig into LightningModule API in depth - :col_css: col-md-4 - :button_link: ../common/lightning_module.html#lightningmodule-api - :height: 150 - -.. displayitem:: - :header: Trainer API - :description: Dig into Trainer API in depth - :col_css: col-md-4 - :button_link: ../common/trainer.html#trainer-class-api - :height: 150 - -.. raw:: html - -
-
diff --git a/docs/_sources/model/build_model_advanced.rst.txt b/docs/_sources/model/build_model_advanced.rst.txt deleted file mode 100644 index 33be842..0000000 --- a/docs/_sources/model/build_model_advanced.rst.txt +++ /dev/null @@ -1,25 +0,0 @@ -:orphan: - -######################## -Own your loop (advanced) -######################## - -*********************** -Customize training loop -*********************** - -.. image:: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/custom_loop.png - :width: 600 - :alt: Injecting custom code in a training loop - -Inject custom code anywhere in the Training loop using any of the 20+ methods (:ref:`lightning_hooks`) available in the LightningModule. - -.. testcode:: - - class LitModel(pl.LightningModule): - def backward(self, loss, optimizer, optimizer_idx): - loss.backward() - ----- - -.. include:: manual_optimization.rst diff --git a/docs/_sources/model/build_model_expert.rst.txt b/docs/_sources/model/build_model_expert.rst.txt deleted file mode 100644 index f321e90..0000000 --- a/docs/_sources/model/build_model_expert.rst.txt +++ /dev/null @@ -1,7 +0,0 @@ -:orphan: - -######################### -Raw PyTorch loop (expert) -######################### - -.. include:: ../starter/lightning_lite.rst diff --git a/docs/_sources/model/build_model_intermediate.rst.txt b/docs/_sources/model/build_model_intermediate.rst.txt deleted file mode 100644 index 55f1247..0000000 --- a/docs/_sources/model/build_model_intermediate.rst.txt +++ /dev/null @@ -1,47 +0,0 @@ -:orphan: - -################################### -Supercharge training (intermediate) -################################### - -************************ -Enable training features -************************ -Enable advanced training features using Trainer arguments. These are SOTA techniques that are automatically integrated into your training loop without changes to your code. - -.. code:: - - # train 1TB+ parameter models with Deepspeed/fsdp - trainer = Trainer( - devices=4, - accelerator="gpu", - strategy="deepspeed_stage_2", - precision=16 - ) - - # 20+ helpful arguments for rapid idea iteration - trainer = Trainer( - max_epochs=10, - min_epochs=5, - overfit_batches=1 - ) - - # access the latest state of the art techniques - trainer = Trainer(callbacks=[StochasticWeightAveraging(...)]) - ----- - -****************** -Extend the Trainer -****************** - -.. raw:: html - - - -If you have multiple lines of code with similar functionalities, you can use *callbacks* to easily group them together and toggle all of those lines on or off at the same time. - -.. code:: - - trainer = Trainer(callbacks=[AWSCheckpoints()]) diff --git a/docs/_sources/model/manual_optimization.rst.txt b/docs/_sources/model/manual_optimization.rst.txt deleted file mode 100644 index e4a31dd..0000000 --- a/docs/_sources/model/manual_optimization.rst.txt +++ /dev/null @@ -1,290 +0,0 @@ -******************* -Manual Optimization -******************* - -For advanced research topics like reinforcement learning, sparse coding, or GAN research, it may be desirable to -manually manage the optimization process. - -This is only recommended for experts who need ultimate flexibility. -Lightning will handle only accelerator, precision and strategy logic. -The users are left with ``optimizer.zero_grad()``, gradient accumulation, model toggling, etc.. - -To manually optimize, do the following: - -* Set ``self.automatic_optimization=False`` in your ``LightningModule``'s ``__init__``. -* Use the following functions and call them manually: - - * ``self.optimizers()`` to access your optimizers (one or multiple) - * ``optimizer.zero_grad()`` to clear the gradients from the previous training step - * ``self.manual_backward(loss)`` instead of ``loss.backward()`` - * ``optimizer.step()`` to update your model parameters - -Here is a minimal example of manual optimization. - -.. testcode:: python - - from pytorch_lightning import LightningModule - - - class MyModel(LightningModule): - def __init__(self): - super().__init__() - # Important: This property activates manual optimization. - self.automatic_optimization = False - - def training_step(self, batch, batch_idx): - opt = self.optimizers() - opt.zero_grad() - loss = self.compute_loss(batch) - self.manual_backward(loss) - opt.step() - -.. warning:: - Before 1.2, ``optimizer.step()`` was calling ``optimizer.zero_grad()`` internally. - From 1.2, it is left to the user's expertise. - -.. tip:: - Be careful where you call ``optimizer.zero_grad()``, or your model won't converge. - It is good practice to call ``optimizer.zero_grad()`` before ``self.manual_backward(loss)``. - - -Access your Own Optimizer -========================= - -The provided ``optimizer`` is a :class:`~pytorch_lightning.core.optimizer.LightningOptimizer` object wrapping your own optimizer -configured in your :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`. You can access your own optimizer -with ``optimizer.optimizer``. However, if you use your own optimizer to perform a step, Lightning won't be able to -support accelerators, precision and profiling for you. - -.. testcode:: python - - class Model(LightningModule): - def __init__(self): - super().__init__() - self.automatic_optimization = False - ... - - def training_step(self, batch, batch_idx): - optimizer = self.optimizers() - - # `optimizer` is a `LightningOptimizer` wrapping the optimizer. - # To access it, do the following. - # However, it won't work on TPU, AMP, etc... - optimizer = optimizer.optimizer - ... - -Gradient Accumulation -===================== - -You can accumulate gradients over batches similarly to ``accumulate_grad_batches`` argument in -:ref:`Trainer ` for automatic optimization. To perform gradient accumulation with one optimizer -after every ``N`` steps, you can do as such. - -.. testcode:: python - - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - opt = self.optimizers() - - loss = self.compute_loss(batch) - self.manual_backward(loss) - - # accumulate gradients of N batches - if (batch_idx + 1) % N == 0: - opt.step() - opt.zero_grad() - - -Use Multiple Optimizers (like GANs) -=================================== - -Here is an example training a simple GAN with multiple optimizers using manual optimization. - -.. testcode:: python - - import torch - from torch import Tensor - from pytorch_lightning import LightningModule - - - class SimpleGAN(LightningModule): - def __init__(self): - super().__init__() - self.G = Generator() - self.D = Discriminator() - - # Important: This property activates manual optimization. - self.automatic_optimization = False - - def sample_z(self, n) -> Tensor: - sample = self._Z.sample((n,)) - return sample - - def sample_G(self, n) -> Tensor: - z = self.sample_z(n) - return self.G(z) - - def training_step(self, batch, batch_idx): - # Implementation follows the PyTorch tutorial: - # https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html - g_opt, d_opt = self.optimizers() - - X, _ = batch - batch_size = X.shape[0] - - real_label = torch.ones((batch_size, 1), device=self.device) - fake_label = torch.zeros((batch_size, 1), device=self.device) - - g_X = self.sample_G(batch_size) - - ########################## - # Optimize Discriminator # - ########################## - d_x = self.D(X) - errD_real = self.criterion(d_x, real_label) - - d_z = self.D(g_X.detach()) - errD_fake = self.criterion(d_z, fake_label) - - errD = errD_real + errD_fake - - d_opt.zero_grad() - self.manual_backward(errD) - d_opt.step() - - ###################### - # Optimize Generator # - ###################### - d_z = self.D(g_X) - errG = self.criterion(d_z, real_label) - - g_opt.zero_grad() - self.manual_backward(errG) - g_opt.step() - - self.log_dict({"g_loss": errG, "d_loss": errD}, prog_bar=True) - - def configure_optimizers(self): - g_opt = torch.optim.Adam(self.G.parameters(), lr=1e-5) - d_opt = torch.optim.Adam(self.D.parameters(), lr=1e-5) - return g_opt, d_opt - - -Learning Rate Scheduling -======================== - -Every optimizer you use can be paired with any -`Learning Rate Scheduler `_. Please see the -documentation of :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers` for all the available options - -You can call ``lr_scheduler.step()`` at arbitrary intervals. -Use ``self.lr_schedulers()`` in your :class:`~pytorch_lightning.core.lightning.LightningModule` to access any learning rate schedulers -defined in your :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`. - -.. warning:: - * Before v1.3, Lightning automatically called ``lr_scheduler.step()`` in both automatic and manual optimization. From - 1.3, ``lr_scheduler.step()`` is now for the user to call at arbitrary intervals. - * Note that the ``lr_scheduler_config`` keys, such as ``"frequency"`` and ``"interval"``, will be ignored even if they are provided in - your :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers` during manual optimization. - -Here is an example calling ``lr_scheduler.step()`` every step. - -.. testcode:: python - - # step every batch - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - # do forward, backward, and optimization - ... - - # single scheduler - sch = self.lr_schedulers() - sch.step() - - # multiple schedulers - sch1, sch2 = self.lr_schedulers() - sch1.step() - sch2.step() - -If you want to call ``lr_scheduler.step()`` every ``N`` steps/epochs, do the following. - -.. testcode:: python - - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - # do forward, backward, and optimization - ... - - sch = self.lr_schedulers() - - # step every N batches - if (batch_idx + 1) % N == 0: - sch.step() - - # step every N epochs - if self.trainer.is_last_batch and (self.trainer.current_epoch + 1) % N == 0: - sch.step() - -If you want to call schedulers that require a metric value after each epoch, consider doing the following: - -.. testcode:: - - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def training_epoch_end(self, outputs): - sch = self.lr_schedulers() - - # If the selected scheduler is a ReduceLROnPlateau scheduler. - if isinstance(sch, torch.optim.lr_scheduler.ReduceLROnPlateau): - sch.step(self.trainer.callback_metrics["loss"]) - -Use Closure for LBFGS-like Optimizers -===================================== - -It is a good practice to provide the optimizer with a closure function that performs a ``forward``, ``zero_grad`` and -``backward`` of your model. It is optional for most optimizers, but makes your code compatible if you switch to an -optimizer which requires a closure, such as :class:`~torch.optim.LBFGS`. - -See `the PyTorch docs `_ for more about the closure. - -Here is an example using a closure function. - -.. testcode:: python - - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def configure_optimizers(self): - return torch.optim.LBFGS(...) - - - def training_step(self, batch, batch_idx): - opt = self.optimizers() - - def closure(): - loss = self.compute_loss(batch) - opt.zero_grad() - self.manual_backward(loss) - return loss - - opt.step(closure=closure) - -.. warning:: - The :class:`~torch.optim.LBFGS` optimizer is not supported for apex AMP, native AMP, IPUs, or DeepSpeed. diff --git a/docs/_sources/model/own_your_loop.rst.txt b/docs/_sources/model/own_your_loop.rst.txt deleted file mode 100644 index 5982b0a..0000000 --- a/docs/_sources/model/own_your_loop.rst.txt +++ /dev/null @@ -1,41 +0,0 @@ -:orphan: - -################################ -Use a pure PyTorch training loop -################################ - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Enable manual optimization - :description: Gain control of the training loop with manual optimization and LightningModule methods. - :col_css: col-md-4 - :button_link: build_model_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Use a Raw PyTorch Loop - :description: Migrate complex PyTorch projects to Lightning and push bleeding-edge research with the raw PyTorch loop. - :col_css: col-md-4 - :button_link: build_model_expert.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Make a custom Lightning Loop - :description: Conduct bleeding-edge research like meta-learning and RL with a custom Loop. - :col_css: col-md-4 - :button_link: loops.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/model/train_model_basic.rst.txt b/docs/_sources/model/train_model_basic.rst.txt deleted file mode 100644 index 24bdab8..0000000 --- a/docs/_sources/model/train_model_basic.rst.txt +++ /dev/null @@ -1,129 +0,0 @@ -:orphan: - -##################### -Train a model (basic) -##################### -**Audience**: Users who need to train a model without coding their own training loops. - ----- - -*********** -Add imports -*********** -Add the relevant imports at the top of the file - -.. code:: python - - import os - import torch - from torch import nn - import torch.nn.functional as F - from torchvision import transforms - from torchvision.datasets import MNIST - from torch.utils.data import DataLoader, random_split - import pytorch_lightning as pl - ----- - -***************************** -Define the PyTorch nn.Modules -***************************** - -.. code:: python - - class Encoder(nn.Module): - def __init__(self): - self.l1 = nn.Sequential(nn.Linear(28 * 28, 64), nn.ReLU(), nn.Linear(64, 3)) - - def forward(self, x): - return self.l1(x) - - - class Decoder(nn.Module): - def __init__(self): - self.l1 = nn.Sequential(nn.Linear(3, 64), nn.ReLU(), nn.Linear(64, 28 * 28)) - - def forward(self, x): - return self.l1(x) - ----- - -************************ -Define a LightningModule -************************ -The LightningModule is the full **recipe** that defines how your nn.Modules interact. - -- The **training_step** defines how the *nn.Modules* interact together. -- In the **configure_optimizers** define the optimizer(s) for your models. - -.. code:: python - - class LitAutoEncoder(pl.LightningModule): - def __init__(self, encoder, decoder): - super().__init__() - self.encoder = encoder - self.decoder = decoder - - def training_step(self, batch, batch_idx): - # training_step defines the train loop. - x, y = batch - x = x.view(x.size(0), -1) - z = self.encoder(x) - x_hat = self.decoder(z) - loss = F.mse_loss(x_hat, x) - return loss - - def configure_optimizers(self): - optimizer = torch.optim.Adam(self.parameters(), lr=1e-3) - return optimizer - ----- - -*************************** -Define the training dataset -*************************** -Define a PyTorch :class:`~torch.utils.data.DataLoader` which contains your training dataset. - -.. code-block:: python - - dataset = MNIST(os.getcwd(), download=True, transform=transforms.ToTensor()) - train_loader = DataLoader(dataset) - ----- - -*************** -Train the model -*************** -To train the model use the Lightning :doc:`Trainer <../common/trainer>` which handles all the engineering and abstracts away all the complexity needed for scale. - -.. code-block:: python - - # model - autoencoder = LitAutoEncoder(Encoder(), Decoder()) - - # train model - trainer = pl.Trainer() - trainer.fit(model=autoencoder, train_dataloaders=train_loader) - ----- - -*************************** -Eliminate the training loop -*************************** -Under the hood, the Lightning Trainer runs the following training loop on your behalf - -.. code:: python - - autoencoder = LitAutoEncoder(encoder, decoder) - optimizer = autoencoder.configure_optimizers() - - for batch, batch_idx in enumerate(train_loader): - loss = autoencoder(batch, batch_idx) - - loss.backward() - optimizer.step() - optimizer.zero_grad() - -The power of Lightning comes when the training loop gets complicated as you add validation/test splits, schedulers, distributed training and all the latest SOTA techniques. - -With Lightning, you can add mix all these techniques together without needing to rewrite a new loop every time. diff --git a/docs/_sources/starter/converting.rst.txt b/docs/_sources/starter/converting.rst.txt deleted file mode 100644 index 952a93a..0000000 --- a/docs/_sources/starter/converting.rst.txt +++ /dev/null @@ -1,197 +0,0 @@ -.. _converting: - -###################################### -How to Organize PyTorch Into Lightning -###################################### - -To enable your code to work with Lightning, perform the following to organize PyTorch into Lightning. - --------- - -****************************** -1. Keep you Computational Code -****************************** - -Keep your regular nn.Module architecture - -.. testcode:: - - import pytorch_lightning as pl - import torch - import torch.nn as nn - import torch.nn.functional as F - - - class LitModel(nn.Module): - def __init__(self): - super().__init__() - self.layer_1 = nn.Linear(28 * 28, 128) - self.layer_2 = nn.Linear(128, 10) - - def forward(self, x): - x = x.view(x.size(0), -1) - x = self.layer_1(x) - x = F.relu(x) - x = self.layer_2(x) - return x - --------- - -*************************** -2. Configure Training Logic -*************************** -In the training_step of the LightningModule configure how your training routine behaves with a batch of training data: - -.. testcode:: - - class LitModel(pl.LightningModule): - def __init__(self, encoder): - super().__init__() - self.encoder = encoder - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.encoder(x) - loss = F.cross_entropy(y_hat, y) - return loss - -.. note:: If you need to fully own the training loop for complicated legacy projects, check out :doc:`Own your loop <../model/own_your_loop>`. - ----- - -**************************************** -3. Move Optimizer(s) and LR Scheduler(s) -**************************************** -Move your optimizers to the :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers` hook. - -.. testcode:: - - class LitModel(pl.LightningModule): - def configure_optimizers(self): - optimizer = torch.optim.Adam(self.encoder.parameters(), lr=1e-3) - lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1) - return [optimizer], [lr_scheduler] - --------- - -*************************************** -4. Organize Validation Logic (optional) -*************************************** -If you need a validation loop, configure how your validation routine behaves with a batch of validation data: - -.. testcode:: - - class LitModel(pl.LightningModule): - def validation_step(self, batch, batch_idx): - x, y = batch - y_hat = self.encoder(x) - val_loss = F.cross_entropy(y_hat, y) - self.log("val_loss", val_loss) - -.. tip:: ``trainer.validate()`` loads the best checkpoint automatically by default if checkpointing was enabled during fitting. - --------- - -************************************ -5. Organize Testing Logic (optional) -************************************ -If you need a test loop, configure how your testing routine behaves with a batch of test data: - -.. testcode:: - - class LitModel(pl.LightningModule): - def test_step(self, batch, batch_idx): - x, y = batch - y_hat = self.encoder(x) - test_loss = F.cross_entropy(y_hat, y) - self.log("test_loss", test_loss) - --------- - -**************************************** -6. Configure Prediction Logic (optional) -**************************************** -If you need a prediction loop, configure how your prediction routine behaves with a batch of test data: - -.. testcode:: - - class LitModel(LightningModule): - def predict_step(self, batch, batch_idx): - x, y = batch - pred = self.encoder(x) - return pred - --------- - -****************************************** -7. Remove any .cuda() or .to(device) Calls -****************************************** - -Your :doc:`LightningModule <../common/lightning_module>` can automatically run on any hardware! - -If you have any explicit calls to ``.cuda()`` or ``.to(device)``, you can remove them since Lightning makes sure that the data coming from :class:`~torch.utils.data.DataLoader` -and all the :class:`~torch.nn.Module` instances initialized inside ``LightningModule.__init__`` are moved to the respective devices automatically. -If you still need to access the current device, you can use ``self.device`` anywhere in your ``LightningModule`` except in the ``__init__`` and ``setup`` methods. - -.. testcode:: - - class LitModel(LightningModule): - def training_step(self, batch, batch_idx): - z = torch.randn(4, 5, device=self.device) - ... - -Hint: If you are initializing a :class:`~torch.Tensor` within the ``LightningModule.__init__`` method and want it to be moved to the device automatically you should call -:meth:`~torch.nn.Module.register_buffer` to register it as a parameter. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self): - super().__init__() - self.register_buffer("running_mean", torch.zeros(num_features)) - --------- - -******************** -8. Use your own data -******************** -Regular PyTorch DataLoaders work with Lightning. For more modular and scalable datasets, check out :doc:`LightningDataModule <../data/datamodule>`. - ----- - -************ -Good to know -************ - -Additionally, you can run only the validation loop using :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate` method. - -.. code-block:: python - - model = LitModel() - trainer.validate(model) - -.. note:: ``model.eval()`` and ``torch.no_grad()`` are called automatically for validation. - - -The test loop isn't used within :meth:`~pytorch_lightning.trainer.trainer.Trainer.fit`, therefore, you would need to explicitly call :meth:`~pytorch_lightning.trainer.trainer.Trainer.test`. - -.. code-block:: python - - model = LitModel() - trainer.test(model) - -.. note:: ``model.eval()`` and ``torch.no_grad()`` are called automatically for testing. - -.. tip:: ``trainer.test()`` loads the best checkpoint automatically by default if checkpointing is enabled. - - -The predict loop will not be used until you call :meth:`~pytorch_lightning.trainer.trainer.Trainer.predict`. - -.. code-block:: python - - model = LitModel() - trainer.predict(model) - -.. note:: ``model.eval()`` and ``torch.no_grad()`` are called automatically for testing. - -.. tip:: ``trainer.predict()`` loads the best checkpoint automatically by default if checkpointing is enabled. diff --git a/docs/_sources/starter/installation.rst.txt b/docs/_sources/starter/installation.rst.txt deleted file mode 100644 index 6b72585..0000000 --- a/docs/_sources/starter/installation.rst.txt +++ /dev/null @@ -1,72 +0,0 @@ -:orphan: - -.. _installation: - -############ -Installation -############ - --------------- - -********************* -Installation with pip -********************* - -Install any supported version of PyTorch if you want from `PyTorch Installation Page `_. -Now you can install using `pip `_ using the following command: - -.. code-block:: bash - - pip install pytorch-lightning - --------------- - -*********************** -Installation with Conda -*********************** - -If you don't have conda installed, follow the `Conda Installation Guide `_. -Lightning can be installed with `conda `_ using the following command: - -.. code-block:: bash - - conda install pytorch-lightning -c conda-forge - -You can also use `Conda Environments `_: - -.. code-block:: bash - - conda activate my_env - conda install pytorch-lightning -c conda-forge - --------------- - -************************ -Installation from Source -************************ - -Install nightly from the source. Note that it contains all the bug fixes and newly released features that -are not published yet. This is the bleeding edge, so use it at your own discretion. - -.. code-block:: bash - - pip install https://github.com/PyTorchLightning/pytorch-lightning/archive/master.zip - -Install future patch releases from the source. Note that the patch release contains only the bug fixes for the recent major release. - -.. code-block:: bash - - pip install https://github.com/PyTorchLightning/pytorch-lightning/archive/refs/heads/release/1.5.x.zip - --------------- - -****************** -Lightning Coverage -****************** - -PyTorch Lightning is maintained and tested on different Python and PyTorch versions. - -Check out the `CI Coverage `_ for more info. - -It is rigorously tested across multiple GPUs, TPUs, CPUs and IPUs. GPU tests run on two NVIDIA P100. TPU tests run on Google GKE TPUv2/3. -TPU py3.7 means we support Colab and Kaggle env. IPU tests run on MK1 IPU boxes. diff --git a/docs/_sources/starter/introduction.rst.txt b/docs/_sources/starter/introduction.rst.txt deleted file mode 100644 index a5e92d2..0000000 --- a/docs/_sources/starter/introduction.rst.txt +++ /dev/null @@ -1,406 +0,0 @@ -:orphan: - -############################### -Lightning 15분 만에 배워보기 -############################### - -**필요한 배경지식:** 없음 - -**목표:** 이 문서에서는 일반적인 Lightning 워크플로우의 주요한 7단계를 안내합니다. - -PyTorch Lightning(파이토치 라이트닝)은 대규모로 엄청 빠른 성능을 요구하면서 최대한의 유연성을 필요로 하는 -전문적인 AI 연구자들과 머신러닝 엔지니어들을 위한 "배터리가 포함된(batteries included)" 딥러닝 프레임워크입니다. - -.. join_slack:: - :align: left - :margin: 20 - -Lightning(라이트닝)은 반복적으로 사용하는 코드(boilerplate)를 제거하고 확장성(scalability)을 확보하도록 PyTorch 코드를 재구성합니다. - -.. raw:: html - - - -| - -PyTorch 코드를 재구성함으로써, Lightning에서는 이런 것들이 가능해집니다: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 완전한 유연성 - :description: 반복되는 코드 없이 PyTorch를 그대로 사용하여 아이디어를 구현합니다. - :col_css: col-md-3 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/card_full_control.png - :height: 290 - -.. displayitem:: - :header: 재현성 + 가독성 - :description: 연구용 코드와 엔지니어링 코드를 분리하여 재현성을 갖추고 더 나은 가독성을 제공합니다. - :col_css: col-md-3 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/card_no_boilerplate.png - :height: 290 - -.. displayitem:: - :header: 간단한 다중 GPU 학습 - :description: 코드 변경 없이 여러개의 GPU/TPU/HPU 등을 사용합니다. - :col_css: col-md-3 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/card_hardware.png - :height: 290 - -.. displayitem:: - :header: 테스트 완료 - :description: 이미 모든 테스트를 완료하여 직접 테스트 할 필요없습니다. - :col_css: col-md-3 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/card_testing.png - :height: 290 - -.. raw:: html - -
-
- -.. End of callout item section - ----- - -****************************** -1: PyTorch Lightning 설치하기 -****************************** -.. raw:: html - -
-
- -`pip `_ 사용자라면, - -.. code-block:: bash - - pip install pytorch-lightning - -.. raw:: html - -
-
- -`conda `_ 사용자라면, - -.. code-block:: bash - - conda install pytorch-lightning -c conda-forge - -.. raw:: html - -
-
- -또는 `advanced install guide `_ 를 참조하세요. - ----- - -.. _new_project: - -***************************** -2: LightningModule 정의하기 -***************************** - -LightningModule을 사용하여 PyTorch nn.Module이 training_step (뿐만 아니라 validation_step이나 test_step) 내에서 복잡한 방식으로 함께 동작할 수 있도록 합니다. - -.. testcode:: - - import os - from torch import optim, nn, utils, Tensor - from tests.helpers.datasets import MNIST - import pytorch_lightning as pl - - # 원하는만큼의 nn.Module (또는 기존 모델)을 정의합니다. - encoder = nn.Sequential(nn.Linear(28 * 28, 64), nn.ReLU(), nn.Linear(64, 3)) - decoder = nn.Sequential(nn.Linear(3, 64), nn.ReLU(), nn.Linear(64, 28 * 28)) - - # LightningModule을 정의합니다. - class LitAutoEncoder(pl.LightningModule): - def __init__(self, encoder, decoder): - super().__init__() - self.encoder = encoder - self.decoder = decoder - - def training_step(self, batch, batch_idx): - # training_step defines the train loop. - # it is independent of forward - x, y = batch - x = x.view(x.size(0), -1) - z = self.encoder(x) - x_hat = self.decoder(z) - loss = nn.functional.mse_loss(x_hat, x) - # Logging to TensorBoard by default - self.log("train_loss", loss) - return loss - - def configure_optimizers(self): - optimizer = optim.Adam(self.parameters(), lr=1e-3) - return optimizer - - - # 오토인코더(autoencoder)를 초기화합니다. - autoencoder = LitAutoEncoder(encoder, decoder) - ----- - -********************** -3: 데이터셋 정의하기 -********************** - -Lightning은 *어떠한* 순회 가능한 객체(iterable; :class:`~torch.utils.data.DataLoader`, numpy 등...)도 학습/검증/테스트/예측용으로 나누어 사용할 수 있습니다. - -.. code-block:: python - - # 데이터를 설정합니다. - dataset = MNIST(os.getcwd(), download=True) - train_loader = utils.data.DataLoader(dataset) - ----- - -****************** -4: 모델 학습하기 -****************** - -Lightning :doc:`Trainer <../common/trainer>` 는 모든 :doc:`LightningModule <../common/lightning_module>` 과 데이터셋을 "함께(mix)" 학습할 수 있으며, -확장에 필요한 모든 엔지니어링적 복잡성들을 추상화(abstract)합니다. - -.. code-block:: python - - # 모델을 학습합니다 (힌트: 빠른 아이디어 반복에 도움이 되는 Trainer의 인자들을 참고하세요) - trainer = pl.Trainer(limit_train_batches=100, max_epochs=1) - trainer.fit(model=autoencoder, train_dataloaders=train_loader) - -Lightning :doc:`Trainer <../common/trainer>` 는 아래 예시들을 포함하여 `40종류 이상의 기법들 <../common/trainer.html#trainer-flags>`_ 을 자동화합니다: - -* 에폭(epoch) 및 배치(batch) 반복 -* ``optimizer.step()``, ``loss.backward()``, ``optimizer.zero_grad()`` 호출 -* 평가(evaluation) 도중 경사도(grads) 활성화/비활성화를 위한 ``model.eval()`` 호출 -* :doc:`체크포인트(checkpoint) 저장하기 및 불러오기 <../common/checkpointing>` -* 텐서보드(tensorboard) (:doc:`loggers <../visualize/loggers>` 옵션 참조) -* :doc:`Multi-GPU <../accelerators/gpu>` 지원 -* :doc:`TPU <../accelerators/tpu>` -* :ref:`16비트 정밀도(precision) AMP ` 지원 - ----- - - -****************** -5: 모델 사용하기 -****************** - -모델을 학습한 뒤에는 ONNX, TorchScript로 내보내기(export)하여 상용 환경에 포함하거나 단순히 가중치를 불러오고 예측을 실행할 수 있습니다. - -.. code:: python - - # 체크포인트(checkpoint)를 불러옵니다. - checkpoint = "./lightning_logs/version_0/checkpoints/epoch=0-step=100.ckpt" - autoencoder = LitAutoEncoder.load_from_checkpoint(checkpoint, encoder=encoder, decoder=decoder) - - # 학습한 nn.Module을 선택합니다. - encoder = autoencoder.encoder - encoder.eval() - - # 4개의 가짜 이미지로 예측(embed)합니다! - fake_image_batch = Tensor(4, 28 * 28) - embeddings = encoder(fake_image_batch) - print("⚡" * 20, "\nPredictions (4 image embeddings):\n", embeddings, "\n", "⚡" * 20) - ----- - -********************* -6: 학습 시각화하기 -********************* - -Lightning에는 *많은* 배터리가 포함되어 있습니다. 실험을 시각화하는데 사용하는 텐서보드(Tensorboard)도 유용한 도구 중 하나입니다. - -명령줄(commandline)에서 아래를 실행하고 브라우저에서 **http://localhost:6006/** 을 열어보세요. - -.. code:: bash - - tensorboard --logdir . - ----- - -************************* -7: 엄청 빠르게 학습하기 -************************* - -Trainer에 인자(argument)를 사용하여 고급 학습 기능을 사용할 수 있습니다. 이는 다른 코드를 변경하지 않으면서 학습 단계(train loop)에 자동으로 통합할 수 있도록 하는 최신(state-of-the-art)의 기술입니다. - -.. code:: - - # 4개의 GPU에서 학습 - trainer = Trainer( - devices=4, - accelerator="gpu", - ) - - # Deepspeed/FSDP를 사용하여 1TB 이상의 매개변수를 갖는 모델 학습 - trainer = Trainer( - devices=4, - accelerator="gpu", - strategy="deepspeed_stage_2", - precision=16 - ) - - # 빠른 아이디어 반복을 위한 20개 이상의 유용한 플래그(flag) - trainer = Trainer( - max_epochs=10, - min_epochs=5, - overfit_batches=1 - ) - - # 최신 기술을 사용 - trainer = Trainer(callbacks=[StochasticWeightAveraging(...)]) - ----- - -******************** -유연성 극대화하기 -******************** - -Lightning의 핵심 원칙은 **PyTorch의 어떠한 부분도 숨기지 않으면서** 언제나 최대한의 유연성을 제공하는 것입니다. - -Lightning은 프로젝트의 복잡도에 따라 *추가적인* 5단계의 유연성을 제공합니다. - ----- - -학습 단계(loop) 사용자 정의하기 -================================== - -.. image:: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/custom_loop.png - :width: 600 - :alt: Injecting custom code in a training loop - -LightningModule에서 사용할 수 있는 20개 이상의 메소드 (:ref:`lightning_hooks`) 중 일부를 사용하여 훈련 단계 어디에든 사용자 정의 코드를 삽입할 수 있습니다. - -.. testcode:: - - class LitAutoEncoder(pl.LightningModule): - def backward(self, loss, optimizer, optimizer_idx): - loss.backward() - ----- - -Trainer 확장하기 -================== - -.. raw:: html - - - -유사한 기능을 하는 여러줄의 코드가 있는 경우, 콜백(callback)을 사용하여 손쉽게 그룹으로 묶어서 해당하는 코드들을 동시에 켜거나 끌 수 있습니다. - -.. code:: - - trainer = Trainer(callbacks=[AWSCheckpoints()]) - ----- - -PyTorch 자체의 반복(loop) 사용하기 -=================================== - -최첨단 연구 시 특정 유형의 작업들을 위해, Lightning은 전문가들이 다양한 방식으로 학습 단계를 완전히 제어할 수 있는 기능을 제공합니다. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 직접 최적화(manual optimization) - :description: 자동화된 학습 단계에서 최적화 단계는 사용자가 직접 관여합니다. - :col_css: col-md-4 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/manual_opt.png - :button_link: ../model/build_model_advanced.html#manual-optimization - :image_height: 220px - :height: 320 - -.. displayitem:: - :header: Lightning Lite(라이트닝 라이트) - :description: 복잡한 PyTorch 프로젝트를 이관하기 위한 반복 단계를 완벽히 제어합니다. - :col_css: col-md-4 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/lite.png - :button_link: ../model/build_model_expert.html - :image_height: 220px - :height: 320 - -.. displayitem:: - :header: 반복(Loop) - :description: 메타학습(meta-learning), 강화학습(reinforcement learning), GAN을 완벽히 제어합니다. - :col_css: col-md-4 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/loops.png - :button_link: ../extensions/loops.html - :image_height: 220px - :height: 320 - -.. raw:: html - -
-
- -.. End of callout item section - ----- - -********** -다음 단계 -********** - -사용 사례에 따라, 아래 내용들 중 하나를 다음 단계로 살펴보세요. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 2: Add a validation and test set - :description: Add validation and test sets to avoid over/underfitting. - :button_link: ../levels/basic_level_2.html - :col_css: col-md-3 - :height: 180 - :tag: basic - -.. displayitem:: - :header: See more examples - :description: See examples across computer vision, NLP, RL, etc... - :col_css: col-md-3 - :button_link: ../tutorials.html - :height: 180 - :tag: basic - -.. displayitem:: - :header: I need my raw PyTorch Loop - :description: Expert-level control for researchers working on the bleeding-edge - :col_css: col-md-3 - :button_link: ../model/build_model_expert.html - :height: 180 - :tag: expert - -.. displayitem:: - :header: Deploy your model - :description: Learn how to predict or put your model into production - :col_css: col-md-3 - :button_link: ../deploy/production.html - :height: 180 - :tag: basic - -.. raw:: html - -
-
diff --git a/docs/_sources/starter/lightning_lite.rst.txt b/docs/_sources/starter/lightning_lite.rst.txt deleted file mode 100644 index 61b69cd..0000000 --- a/docs/_sources/starter/lightning_lite.rst.txt +++ /dev/null @@ -1,727 +0,0 @@ -########################################### -LightningLite (Stepping Stone to Lightning) -########################################### - - -:class:`~pytorch_lightning.lite.LightningLite` enables pure PyTorch users to scale their existing code -on any kind of device while retaining full control over their own loops and optimization logic. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/lite/lightning_lite.gif - :alt: Animation showing how to convert your PyTorch code to LightningLite. - :width: 500 - :align: center - -| - -:class:`~pytorch_lightning.lite.LightningLite` is the right tool for you if you match one of the two following descriptions: - -- I want to quickly scale my existing code to multiple devices with minimal code changes. -- I would like to convert my existing code to the Lightning API, but a full path to Lightning transition might be too complex. I am looking for a stepping stone to ensure reproducibility during the transition. - - -.. warning:: :class:`~pytorch_lightning.lite.LightningLite` is currently a beta feature. Its API is subject to change based on your feedback. - - ----------- - -**************** -Learn by example -**************** - - -My Existing PyTorch Code -======================== - -The ``run`` function contains custom training loop used to train ``MyModel`` on ``MyDataset`` for ``num_epochs`` epochs. - -.. code-block:: python - - import torch - from torch import nn - from torch.utils.data import DataLoader, Dataset - - - class MyModel(nn.Module): - ... - - - class MyDataset(Dataset): - ... - - - def run(args): - device = "cuda" if torch.cuda.is_available() else "cpu" - - model = MyModel(...).to(device) - optimizer = torch.optim.SGD(model.parameters(), ...) - - dataloader = DataLoader(MyDataset(...), ...) - - model.train() - for epoch in range(args.num_epochs): - for batch in dataloader: - batch = batch.to(device) - optimizer.zero_grad() - loss = model(batch) - loss.backward() - optimizer.step() - - - run(args) - ----------- - - -Convert to LightningLite -======================== - -Here are five required steps to convert to :class:`~pytorch_lightning.lite.LightningLite`. - -1. Subclass :class:`~pytorch_lightning.lite.LightningLite` and override its :meth:`~pytorch_lightning.lite.LightningLite.run` method. -2. Move the body of your existing ``run`` function into :class:`~pytorch_lightning.lite.LightningLite` ``run`` method. -3. Remove all ``.to(...)``, ``.cuda()`` etc calls since :class:`~pytorch_lightning.lite.LightningLite` will take care of it. -4. Apply :meth:`~pytorch_lightning.lite.LightningLite.setup` over each model and optimizers pair and :meth:`~pytorch_lightning.lite.LightningLite.setup_dataloaders` on all your dataloaders and replace ``loss.backward()`` by ``self.backward(loss)``. -5. Instantiate your :class:`~pytorch_lightning.lite.LightningLite` subclass and call its :meth:`~pytorch_lightning.lite.LightningLite.run` method. - -| - -.. code-block:: python - - import torch - from torch import nn - from torch.utils.data import DataLoader, Dataset - from pytorch_lightning.lite import LightningLite - - - class MyModel(nn.Module): - ... - - - class MyDataset(Dataset): - ... - - - class Lite(LightningLite): - def run(self, args): - - model = MyModel(...) - optimizer = torch.optim.SGD(model.parameters(), ...) - model, optimizer = self.setup(model, optimizer) # Scale your model / optimizers - - dataloader = DataLoader(MyDataset(...), ...) - dataloader = self.setup_dataloaders(dataloader) # Scale your dataloaders - - model.train() - for epoch in range(args.num_epochs): - for batch in dataloader: - optimizer.zero_grad() - loss = model(batch) - self.backward(loss) # instead of loss.backward() - optimizer.step() - - - Lite(...).run(args) - - -That's all. You can now train on any kind of device and scale your training. Check out `this `_ full MNIST training example with LightningLite. - -:class:`~pytorch_lightning.lite.LightningLite` takes care of device management, so you don't have to. -You should remove any device-specific logic within your code. - -Here is how to train on eight GPUs with `torch.bfloat16 `_ precision: - -.. code-block:: python - - Lite(strategy="ddp", devices=8, accelerator="gpu", precision="bf16").run(10) - -Here is how to use `DeepSpeed Zero3 `_ with eight GPUs and precision 16: - -.. code-block:: python - - Lite(strategy="deepspeed", devices=8, accelerator="gpu", precision=16).run(10) - -:class:`~pytorch_lightning.lite.LightningLite` can also figure it out automatically for you! - -.. code-block:: python - - Lite(devices="auto", accelerator="auto", precision=16).run(10) - -You can also easily use distributed collectives if required. -Here is an example while running on 256 GPUs (eight GPUs times 32 nodes). - -.. code-block:: python - - class Lite(LightningLite): - def run(self): - - # Transfer and concatenate tensors across processes - self.all_gather(...) - - # Transfer an object from one process to all the others - self.broadcast(..., src=...) - - # The total number of processes running across all devices and nodes. - self.world_size - - # The global index of the current process across all devices and nodes. - self.global_rank - - # The index of the current process among the processes running on the local node. - self.local_rank - - # The index of the current node. - self.node_rank - - # Wether this global rank is rank zero. - if self.is_global_zero: - # do something on rank 0 - ... - - # Wait for all processes to enter this call. - self.barrier() - - - Lite(strategy="ddp", devices=8, num_nodes=32, accelerator="gpu").run() - - -If you require custom data or model device placement, you can deactivate -:class:`~pytorch_lightning.lite.LightningLite` automatic placement by doing -``self.setup_dataloaders(..., move_to_device=False)`` for the data and -``self.setup(..., move_to_device=False)`` for the model. -Furthermore, you can access the current device from ``self.device`` or -rely on :meth:`~pytorch_lightning.lite.LightningLite.to_device` -utility to move an object to the current device. - - -.. note:: We recommend instantiating the models within the :meth:`~pytorch_lightning.lite.LightningLite.run` method as large models would cause an out-of-memory error otherwise. - -.. tip:: - - If you have hundreds or thousands of lines within your :meth:`~pytorch_lightning.lite.LightningLite.run` function - and you are feeling unsure about them, then that is the correct feeling. - In 2019, our :class:`~pytorch_lightning.core.lightning.LightningModule` was getting larger - and we got the same feeling, so we started to organize our code for simplicity, interoperability and standardization. - This is definitely a good sign that you should consider refactoring your code and / or switching to - :class:`~pytorch_lightning.core.lightning.LightningModule` ultimately. - - ----------- - - -Distributed Training Pitfalls -============================= - -The :class:`~pytorch_lightning.lite.LightningLite` provides you with the tools to scale your training, -but there are several major challenges ahead of you now: - - -.. list-table:: - :widths: 50 50 - :header-rows: 0 - - * - Processes divergence - - This happens when processes execute a different section of the code due to different if/else conditions, race conditions on existing files and so on, resulting in hanging. - * - Cross processes reduction - - Miscalculated metrics or gradients due to errors in their reduction. - * - Large sharded models - - Instantiation, materialization and state management of large models. - * - Rank 0 only actions - - Logging, profiling, and so on. - * - Checkpointing / Early stopping / Callbacks / Logging - - Ability to customize your training behavior easily and make it stateful. - * - Fault-tolerant training - - Ability to resume from a failure as if it never happened. - - -If you are facing one of those challenges, then you are already meeting the limit of :class:`~pytorch_lightning.lite.LightningLite`. -We recommend you to convert to :doc:`Lightning <../starter/introduction>`, so you never have to worry about those. - ----------- - -Convert to Lightning -==================== - -:class:`~pytorch_lightning.lite.LightningLite` is a stepping stone to transition fully to the Lightning API and benefit -from its hundreds of features. - -You can see our :class:`~pytorch_lightning.lite.LightningLite` class as a -future :class:`~pytorch_lightning.core.lightning.LightningModule`, and slowly refactor your code into its API. -Below, the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step`, :meth:`~pytorch_lightning.core.lightning.LightningModule.forward`, -:meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`, :meth:`~pytorch_lightning.core.lightning.LightningModule.train_dataloader` methods -are implemented. - - -.. code-block:: python - - class Lite(LightningLite): - - # 1. This would become the LightningModule `__init__` function. - def run(self, args): - self.args = args - - self.model = MyModel(...) - - self.fit() # This would be automated by the Lightning Trainer. - - # 2. This can be fully removed as Lightning creates its own fitting loop, - # and sets up the model, optimizer, dataloader, etc for you. - def fit(self): - # setup everything - optimizer = self.configure_optimizers() - self.model, optimizer = self.setup(self.model, optimizer) - dataloader = self.setup_dataloaders(self.train_dataloader()) - - # start fitting - self.model.train() - for epoch in range(num_epochs): - for batch in enumerate(dataloader): - optimizer.zero_grad() - loss = self.training_step(batch, batch_idx) - self.backward(loss) - optimizer.step() - - # 3. This stays here as it belongs to the LightningModule. - def forward(self, x): - return self.model(x) - - def training_step(self, batch, batch_idx): - return self.forward(batch) - - def configure_optimizers(self): - return torch.optim.SGD(self.model.parameters(), ...) - - # 4. [Optionally] This can stay here or be extracted to the LightningDataModule to enable higher composability. - def train_dataloader(self): - return DataLoader(MyDataset(...), ...) - - - Lite(...).run(args) - - -Finally, change the :meth:`~pytorch_lightning.lite.LightningLite.run` into a -:meth:`~pytorch_lightning.core.lightning.LightningModule.__init__` and drop the ``fit`` call from inside. - -.. code-block:: python - - from pytorch_lightning import LightningDataModule, LightningModule, Trainer - - - class LightningModel(LightningModule): - def __init__(self, args): - super().__init__() - self.model = MyModel(...) - - def forward(self, x): - return self.model(x) - - def training_step(self, batch, batch_idx): - loss = self(batch) - self.log("train_loss", loss) - return loss - - def configure_optimizers(self): - return torch.optim.SGD(self.model.parameters(), lr=0.001) - - - class BoringDataModule(LightningDataModule): - def train_dataloader(self): - return DataLoader(MyDataset(...), ...) - - - trainer = Trainer(max_epochs=10) - trainer.fit(LightningModel(), datamodule=BoringDataModule()) - - -You have successfully converted to PyTorch Lightning, and can now benefit from its hundred of features! - ----------- - -******************** -Lightning Lite Flags -******************** - -Lite is specialized in accelerated distributed training and inference. It offers you convenient ways to configure -your device and communication strategy and to switch seamlessly from one to the other. The terminology and usage are -identical to Lightning, which means minimum effort for you to convert when you decide to do so. - - -accelerator -=========== - -Choose one of ``"cpu"``, ``"gpu"``, ``"tpu"``, ``"auto"`` (IPU support is coming soon). - -.. code-block:: python - - # CPU accelerator - lite = Lite(accelerator="cpu") - - # Running with GPU Accelerator using 2 GPUs - lite = Lite(devices=2, accelerator="gpu") - - # Running with TPU Accelerator using 8 tpu cores - lite = Lite(devices=8, accelerator="tpu") - - # Running with GPU Accelerator using the DistributedDataParallel strategy - lite = Lite(devices=4, accelerator="gpu", strategy="ddp") - -The ``"auto"`` option recognizes the machine you are on and selects the available accelerator. - -.. code-block:: python - - # If your machine has GPUs, it will use the GPU Accelerator - lite = Lite(devices=2, accelerator="auto") - - -strategy -======== - -Choose a training strategy: ``"dp"``, ``"ddp"``, ``"ddp_spawn"``, ``"tpu_spawn"``, ``"deepspeed"``, ``"ddp_sharded"``, or ``"ddp_sharded_spawn"``. - -.. code-block:: python - - # Running with the DistributedDataParallel strategy on 4 GPUs - lite = Lite(strategy="ddp", accelerator="gpu", devices=4) - - # Running with the DDP Spawn strategy using 4 cpu processes - lite = Lite(strategy="ddp_spawn", accelerator="cpu", devices=4) - - -Additionally, you can pass in your custom strategy by configuring additional parameters. - -.. code-block:: python - - from pytorch_lightning.strategies import DeepSpeedStrategy - - lite = Lite(strategy=DeepSpeedStrategy(stage=2), accelerator="gpu", devices=2) - - -Support for Horovod and Fully Sharded training strategies are coming soon. - - -devices -======= - -Configure the devices to run on. Can be of type: - -- int: the number of devices (e.g., GPUs) to train on -- list of int: which device index (e.g., GPU ID) to train on (0-indexed) -- str: a string representation of one of the above - -.. code-block:: python - - # default used by Lite, i.e., use the CPU - lite = Lite(devices=None) - - # equivalent - lite = Lite(devices=0) - - # int: run on two GPUs - lite = Lite(devices=2, accelerator="gpu") - - # list: run on GPUs 1, 4 (by bus ordering) - lite = Lite(devices=[1, 4], accelerator="gpu") - lite = Lite(devices="1, 4", accelerator="gpu") # equivalent - - # -1: run on all GPUs - lite = Lite(devices=-1, accelerator="gpu") - lite = Lite(devices="-1", accelerator="gpu") # equivalent - - - -gpus -==== - -.. warning:: ``gpus=x`` has been deprecated in v1.7 and will be removed in v2.0. - Please use ``accelerator='gpu'`` and ``devices=x`` instead. - -Shorthand for setting ``devices=X`` and ``accelerator="gpu"``. - -.. code-block:: python - - # Run on two GPUs - lite = Lite(accelerator="gpu", devices=2) - - # Equivalent - lite = Lite(devices=2, accelerator="gpu") - - -tpu_cores -========= - -.. warning:: ``tpu_cores=x`` has been deprecated in v1.7 and will be removed in v2.0. - Please use ``accelerator='tpu'`` and ``devices=x`` instead. - -Shorthand for ``devices=X`` and ``accelerator="tpu"``. - -.. code-block:: python - - # Run on eight TPUs - lite = Lite(accelerator="tpu", devices=8) - - # Equivalent - lite = Lite(devices=8, accelerator="tpu") - - -num_nodes -========= - - -Number of cluster nodes for distributed operation. - -.. code-block:: python - - # Default used by Lite - lite = Lite(num_nodes=1) - - # Run on 8 nodes - lite = Lite(num_nodes=8) - - -Learn more about distributed multi-node training on clusters :doc:`here <../clouds/cluster>`. - - -precision -========= - -Lightning Lite supports double precision (64), full precision (32), or half precision (16) operation (including `bfloat16 `_). -Half precision, or mixed precision, is the combined use of 32 and 16-bit floating points to reduce the memory footprint during model training. -This can result in improved performance, achieving significant speedups on modern GPUs. - -.. code-block:: python - - # Default used by the Lite - lite = Lite(precision=32, devices=1) - - # 16-bit (mixed) precision - lite = Lite(precision=16, devices=1) - - # 16-bit bfloat precision - lite = Lite(precision="bf16", devices=1) - - # 64-bit (double) precision - lite = Lite(precision=64, devices=1) - - -plugins -======= - -:ref:`Plugins` allow you to connect arbitrary backends, precision libraries, clusters etc. For example: -To define your own behavior, subclass the relevant class and pass it in. Here's an example linking up your own -:class:`~pytorch_lightning.plugins.environments.ClusterEnvironment`. - -.. code-block:: python - - from pytorch_lightning.plugins.environments import ClusterEnvironment - - - class MyCluster(ClusterEnvironment): - @property - def main_address(self): - return your_main_address - - @property - def main_port(self): - return your_main_port - - def world_size(self): - return the_world_size - - - lite = Lite(plugins=[MyCluster()], ...) - - ----------- - - -********************** -Lightning Lite Methods -********************** - - -run -=== - -The run method serves two purposes: - -1. Override this method from the :class:`~pytorch_lightning.lite.lite.LightningLite` class and put your - training (or inference) code inside. -2. Launch the training procedure by calling the run method. Lite will take care of setting up the distributed backend. - -You can optionally pass arguments to the run method. For example, the hyperparameters or a backbone for the model. - -.. code-block:: python - - from pytorch_lightning.lite import LightningLite - - - class Lite(LightningLite): - - # Input arguments are optional; put whatever you need - def run(self, learning_rate, num_layers): - """Here goes your training loop""" - - - lite = Lite(accelerator="gpu", devices=2) - lite.run(learning_rate=0.01, num_layers=12) - - -setup -===== - -Set up a model and corresponding optimizer(s). If you need to set up multiple models, call ``setup()`` on each of them. -Moves the model and optimizer to the correct device automatically. - -.. code-block:: python - - model = nn.Linear(32, 64) - optimizer = torch.optim.SGD(model.parameters(), lr=0.001) - - # Set up model and optimizer for accelerated training - model, optimizer = self.setup(model, optimizer) - - # If you don't want Lite to set the device - model, optimizer = self.setup(model, optimizer, move_to_device=False) - - -The setup method also prepares the model for the selected precision choice so that operations during ``forward()`` get -cast automatically. - -setup_dataloaders -================= - -Set up one or multiple dataloaders for accelerated operation. If you are running a distributed strategy (e.g., DDP), Lite -replaces the sampler automatically for you. In addition, the dataloader will be configured to move the returned -data tensors to the correct device automatically. - -.. code-block:: python - - train_data = torch.utils.DataLoader(train_dataset, ...) - test_data = torch.utils.DataLoader(test_dataset, ...) - - train_data, test_data = self.setup_dataloaders(train_data, test_data) - - # If you don't want Lite to move the data to the device - train_data, test_data = self.setup_dataloaders(train_data, test_data, move_to_device=False) - - # If you don't want Lite to replace the sampler in the context of distributed training - train_data, test_data = self.setup_dataloaders(train_data, test_data, replace_sampler=False) - - -backward -======== - -This replaces any occurrences of ``loss.backward()`` and makes your code accelerator and precision agnostic. - -.. code-block:: python - - output = model(input) - loss = loss_fn(output, target) - - # loss.backward() - self.backward(loss) - - -to_device -========= - -Use :meth:`~pytorch_lightning.lite.lite.LightningLite.to_device` to move models, tensors or collections of tensors to -the current device. By default :meth:`~pytorch_lightning.lite.lite.LightningLite.setup` and -:meth:`~pytorch_lightning.lite.lite.LightningLite.setup_dataloaders` already move the model and data to the correct -device, so calling this method is only necessary for manual operation when needed. - -.. code-block:: python - - data = torch.load("dataset.pt") - data = self.to_device(data) - - -seed_everything -=============== - -Make your code reproducible by calling this method at the beginning of your run. - -.. code-block:: python - - # Instead of `torch.manual_seed(...)`, call: - self.seed_everything(1234) - - -This covers PyTorch, NumPy and Python random number generators. In addition, Lite takes care of properly initializing -the seed of dataloader worker processes (can be turned off by passing ``workers=False``). - - -autocast -======== - -Let the precision backend autocast the block of code under this context manager. This is optional and already done by -Lite for the model's forward method (once the model was :meth:`~pytorch_lightning.lite.lite.LightningLite.setup`). -You need this only if you wish to autocast more operations outside the ones in model forward: - -.. code-block:: python - - model, optimizer = self.setup(model, optimizer) - - # Lite handles precision automatically for the model - output = model(inputs) - - with self.autocast(): # optional - loss = loss_function(output, target) - - self.backward(loss) - ... - - -print -===== - -Print to the console via the built-in print function, but only on the main process. -This avoids excessive printing and logs when running on multiple devices/nodes. - - -.. code-block:: python - - # Print only on the main process - self.print(f"{epoch}/{num_epochs}| Train Epoch Loss: {loss}") - - -save -==== - -Save contents to a checkpoint. Replaces all occurrences of ``torch.save(...)`` in your code. Lite will take care of -handling the saving part correctly, no matter if you are running a single device, multi-devices or multi-nodes. - -.. code-block:: python - - # Instead of `torch.save(...)`, call: - self.save(model.state_dict(), "path/to/checkpoint.ckpt") - - -load -==== - -Load checkpoint contents from a file. Replaces all occurrences of ``torch.load(...)`` in your code. Lite will take care of -handling the loading part correctly, no matter if you are running a single device, multi-device, or multi-node. - -.. code-block:: python - - # Instead of `torch.load(...)`, call: - self.load("path/to/checkpoint.ckpt") - - -barrier -======= - -Call this if you want all processes to wait and synchronize. Once all processes have entered this call, -execution continues. Useful for example when you want to download data on one process and make all others wait until -the data is written to disk. - -.. code-block:: python - - # Download data only on one process - if self.global_rank == 0: - download_data("http://...") - - # Wait until all processes meet up here - self.barrier() - - # All processes are allowed to read the data now diff --git a/docs/_sources/starter/style_guide.rst.txt b/docs/_sources/starter/style_guide.rst.txt deleted file mode 100644 index ecdbae6..0000000 --- a/docs/_sources/starter/style_guide.rst.txt +++ /dev/null @@ -1,231 +0,0 @@ -################ -스타일 가이드 -################ - -파이토치 라이트닝(PyTorch Lightning)의 주요한 목표는 가독성과 재현성을 개선하는 것입니다. GitHub 저장소나 연구 프로젝트에서 -:class:`~pytorch_lightning.core.lightning.LightningModule` 을 발견하고, 관심있는 부분을 찾기 위해 정확히 어디를 봐야할지 정확히 알고 있다고 상상해보세요. - -이 스타일 가이드의 목표는 Lightning의 코드가 유사하게 구성되도록 권장하는데 있습니다. - --------------- - -*************** -LightningModule -*************** - -These are best practices for structuring your :class:`~pytorch_lightning.core.lightning.LightningModule` class: - -Systems vs Models -================= - -.. figure:: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/pl_docs/model_system.png - :width: 400 - -The main principle behind a LightningModule is that a full system should be self-contained. -In Lightning, we differentiate between a system and a model. - -A model is something like a resnet18, RNN, and so on. - -A system defines how a collection of models interact with each other with user-defined training/evaluation logic. Examples of this are: - -* GANs -* Seq2Seq -* BERT -* etc. - -A LightningModule can define both a system and a model: - -Here's a LightningModule that defines a system. This structure is what we recommend as a best practice. Keeping the model separate from the system improves -modularity, which eventually helps in better testing, reduces dependencies on the system and makes it easier to refactor. - -.. testcode:: - - class Encoder(nn.Module): - ... - - - class Decoder(nn.Module): - ... - - - class AutoEncoder(nn.Module): - def __init__(self): - super().__init__() - self.encoder = Encoder() - self.decoder = Decoder() - - def forward(self, x): - return self.encoder(x) - - - class AutoEncoderSystem(LightningModule): - def __init__(self): - super().__init__() - self.auto_encoder = AutoEncoder() - - -For fast prototyping, it's often useful to define all the computations in a LightningModule. For reusability -and scalability, it might be better to pass in the relevant backbones. - -Here's a LightningModule that defines a model. Although, we do not recommend to define a model like in the example. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self): - super().__init__() - self.layer_1 = nn.Linear() - self.layer_2 = nn.Linear() - self.layer_3 = nn.Linear() - - -Self-contained -============== - -A Lightning module should be self-contained. To see how self-contained your model is, a good test is to ask -yourself this question: - -"Can someone drop this file into a Trainer without knowing anything about the internals?" - -For example, we couple the optimizer with a model because the majority of models require a specific optimizer with -a specific learning rate scheduler to work well. - -Init -==== -The first place where LightningModules tend to stop being self-contained is in the init. Try to define all the relevant -sensible defaults in the init so that the user doesn't have to guess. - -Here's an example where a user will have to go hunt through files to figure out how to init this LightningModule. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self, params): - self.lr = params.lr - self.coef_x = params.coef_x - -Models defined as such leave you with many questions, such as what is ``coef_x``? Is it a string? A float? What is the range? -Instead, be explicit in your init - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self, encoder: nn.Module, coef_x: float = 0.2, lr: float = 1e-3): - ... - -Now the user doesn't have to guess. Instead, they know the value type, and the model has a sensible default where the -user can see the value immediately. - - -Method Order -============ -The only required methods in the LightningModule are: - -* init -* training_step -* configure_optimizers - -However, if you decide to implement the rest of the optional methods, the recommended order is: - -* model/system definition (init) -* if doing inference, define forward -* training hooks -* validation hooks -* test hooks -* predict hooks -* configure_optimizers -* any other hooks - -In practice, the code looks like this: - -.. code-block:: - - class LitModel(pl.LightningModule): - - def __init__(...): - - def forward(...): - - def training_step(...): - - def training_step_end(...): - - def training_epoch_end(...): - - def validation_step(...): - - def validation_step_end(...): - - def validation_epoch_end(...): - - def test_step(...): - - def test_step_end(...): - - def test_epoch_end(...): - - def configure_optimizers(...): - - def any_extra_hook(...): - - -Forward vs training_step -======================== - -We recommend using :meth:`~pytorch_lightning.core.lightning.LightningModule.forward` for inference/predictions and keeping -:meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` independent. - -.. code-block:: python - - def forward(self, x): - embeddings = self.encoder(x) - return embeddings - - - def training_step(self, batch, batch_idx): - x, _ = batch - z = self.encoder(x) - pred = self.decoder(z) - ... - - --------------- - -**** -Data -**** - -These are best practices for handling data. - -DataLoaders -=========== - -Lightning uses :class:`~torch.utils.data.DataLoader` to handle all the data flow through the system. Whenever you structure dataloaders, -make sure to tune the number of workers for maximum efficiency. - -.. warning:: Make sure not to use ``Trainer(strategy="ddp_spawn")`` with ``num_workers>0`` in the DataLoader or you will bottleneck you code. - -DataModules -=========== - -The :class:`~pytorch_lightning.core.datamodule.LightningDataModule` is designed as a way of decoupling data-related -hooks from the :class:`~pytorch_lightning.core.lightning.LightningModule` so you can develop dataset agnostic models. It makes it easy to hot swap different -datasets with your model, so you can test it and benchmark it across domains. It also makes sharing and reusing the exact data splits and transforms across projects possible. - -Check out :ref:`data` document to understand data management within Lightning and its best practices. - -* What dataset splits were used? -* How many samples does this dataset have overall and within each split? -* Which transforms were used? - -It's for this reason that we recommend you use datamodules. This is especially important when collaborating because -it will save your team a lot of time as well. - -All they need to do is drop a datamodule into the Trainer and not worry about what was done to the data. - -This is true for both academic and corporate settings where data cleaning and ad-hoc instructions slow down the progress -of iterating through ideas. - -- Checkout the live examples to get your hands dirty: -- `Introduction to PyTorch Lightning `_ -- `Introduction to DataModules `_ diff --git a/docs/_sources/tuning/profiler.rst.txt b/docs/_sources/tuning/profiler.rst.txt deleted file mode 100644 index 1ff7c24..0000000 --- a/docs/_sources/tuning/profiler.rst.txt +++ /dev/null @@ -1,49 +0,0 @@ -.. _profiler: - -############################# -Find bottlenecks in your code -############################# - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Learn to find bottlenecks in the training loop. - :col_css: col-md-3 - :button_link: profiler_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Learn to find bottlenecks in PyTorch operations. - :col_css: col-md-3 - :button_link: profiler_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Advanced - :description: Learn to profile TPU code. - :col_css: col-md-3 - :button_link: profiler_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Expert - :description: Learn to build your own profiler or profile custom pieces of code - :col_css: col-md-3 - :button_link: profiler_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/docs/_sources/tuning/profiler_advanced.rst.txt b/docs/_sources/tuning/profiler_advanced.rst.txt deleted file mode 100644 index ad2ab9e..0000000 --- a/docs/_sources/tuning/profiler_advanced.rst.txt +++ /dev/null @@ -1,74 +0,0 @@ -:orphan: - -.. _profiler_advanced: - -######################################## -Find bottlenecks in your code (advanced) -######################################## -**Audience**: Users who want to profile their TPU models to find bottlenecks and improve performance. - ----- - -************************ -Profile cloud TPU models -************************ -To profile TPU models use the :class:`~pytorch_lightning.profiler.xla.XLAProfiler` - -.. code-block:: python - - from pytorch_lightning.profiler import XLAProfiler - - profiler = XLAProfiler(port=9001) - trainer = Trainer(profiler=profiler) - ----- - -************************************* -Capture profiling logs in Tensorboard -************************************* -To capture profile logs in Tensorboard, follow these instructions: - ----- - -0: Setup the required installs -============================== -Use this `guide `_ to help you with the Cloud TPU required installations. - ----- - -1: Start Tensorboard -==================== -Start the `TensorBoard `_ server: - -.. code-block:: bash - - tensorboard --logdir ./tensorboard --port 9001 - -Now open the following url on your browser - -.. code-block:: bash - - http://localhost:9001/#profile - ----- - -2: Capture the profile -====================== -Once the code you want to profile is running: - -1. click on the ``CAPTURE PROFILE`` button. -2. Enter ``localhost:9001`` (default port for XLA Profiler) as the Profile Service URL. -3. Enter the number of milliseconds for the profiling duration -4. Click ``CAPTURE`` - ----- - -3: Don't stop your code -======================= -Make sure the code is running while you are trying to capture the traces. It will lead to better performance insights if the profiling duration is longer than the step time. - ----- - -4: View the profiling logs -========================== -Once the capture is finished, the page will refresh and you can browse through the insights using the **Tools** dropdown at the top left diff --git a/docs/_sources/tuning/profiler_basic.rst.txt b/docs/_sources/tuning/profiler_basic.rst.txt deleted file mode 100644 index 899e657..0000000 --- a/docs/_sources/tuning/profiler_basic.rst.txt +++ /dev/null @@ -1,121 +0,0 @@ -:orphan: - -.. _profiler_basic: - -##################################### -Find bottlenecks in your code (basic) -##################################### -**Audience**: Users who want to learn the basics of removing bottlenecks from their code - ----- - -************************ -Why do I need profiling? -************************ -Profiling helps you find bottlenecks in your code by capturing analytics such as how long a function takes or how much memory is used. - ------------- - -****************************** -Find training loop bottlenecks -****************************** -The most basic profile measures all the key methods across **Callbacks**, **DataModules** and the **LightningModule** in the training loop. - -.. code-block:: python - - trainer = Trainer(profiler="simple") - -Once the **.fit()** function has completed, you'll see an output like this: - -.. code-block:: - - FIT Profiler Report - - ----------------------------------------------------------------------------------------------- - | Action | Mean duration (s) | Total time (s) | - ----------------------------------------------------------------------------------------------- - | [LightningModule]BoringModel.prepare_data | 10.0001 | 20.00 | - | run_training_epoch | 6.1558 | 6.1558 | - | run_training_batch | 0.0022506 | 0.015754 | - | [LightningModule]BoringModel.optimizer_step | 0.0017477 | 0.012234 | - | [LightningModule]BoringModel.val_dataloader | 0.00024388 | 0.00024388 | - | on_train_batch_start | 0.00014637 | 0.0010246 | - | [LightningModule]BoringModel.teardown | 2.15e-06 | 2.15e-06 | - | [LightningModule]BoringModel.on_train_start | 1.644e-06 | 1.644e-06 | - | [LightningModule]BoringModel.on_train_end | 1.516e-06 | 1.516e-06 | - | [LightningModule]BoringModel.on_fit_end | 1.426e-06 | 1.426e-06 | - | [LightningModule]BoringModel.setup | 1.403e-06 | 1.403e-06 | - | [LightningModule]BoringModel.on_fit_start | 1.226e-06 | 1.226e-06 | - ----------------------------------------------------------------------------------------------- - -In this report we can see that the slowest function is **prepare_data**. Now you can figure out why data preparation is slowing down your training. - -The simple profiler measures all the standard methods used in the training loop automatically, including: - -- on_train_epoch_start -- on_train_epoch_end -- on_train_batch_start -- model_backward -- on_after_backward -- optimizer_step -- on_train_batch_end -- training_step_end -- on_training_end -- etc... - ----- - -************************************** -Profile the time within every function -************************************** -To profile the time within every function, use the :class:`~pytorch_lightning.profiler.advanced.AdvancedProfiler` built on top of Python's `cProfiler `_. - - -.. code-block:: python - - trainer = Trainer(profiler="advanced") - -Once the **.fit()** function has completed, you'll see an output like this: - -.. code-block:: - - Profiler Report - - Profile stats for: get_train_batch - 4869394 function calls (4863767 primitive calls) in 18.893 seconds - Ordered by: cumulative time - List reduced from 76 to 10 due to restriction <10> - ncalls tottime percall cumtime percall filename:lineno(function) - 3752/1876 0.011 0.000 18.887 0.010 {built-in method builtins.next} - 1876 0.008 0.000 18.877 0.010 dataloader.py:344(__next__) - 1876 0.074 0.000 18.869 0.010 dataloader.py:383(_next_data) - 1875 0.012 0.000 18.721 0.010 fetch.py:42(fetch) - 1875 0.084 0.000 18.290 0.010 fetch.py:44() - 60000 1.759 0.000 18.206 0.000 mnist.py:80(__getitem__) - 60000 0.267 0.000 13.022 0.000 transforms.py:68(__call__) - 60000 0.182 0.000 7.020 0.000 transforms.py:93(__call__) - 60000 1.651 0.000 6.839 0.000 functional.py:42(to_tensor) - 60000 0.260 0.000 5.734 0.000 transforms.py:167(__call__) - -If the profiler report becomes too long, you can stream the report to a file: - -.. code-block:: python - - from pytorch_lightning.profiler import AdvancedProfiler - - profiler = AdvancedProfiler(dirpath=".", filename="perf_logs") - trainer = Trainer(profiler=profiler) - ----- - -************************* -Measure accelerator usage -************************* -Another helpful technique to detect bottlenecks is to ensure that you're using the full capacity of your accelerator (GPU/TPU/IPU/HPU). -This can be measured with the :class:`~pytorch_lightning.callbacks.device_stats_monitor.DeviceStatsMonitor`: - -.. testcode:: - - from pytorch_lightning.callbacks import DeviceStatsMonitor - - trainer = Trainer(callbacks=[DeviceStatsMonitor()]) diff --git a/docs/_sources/tuning/profiler_expert.rst.txt b/docs/_sources/tuning/profiler_expert.rst.txt deleted file mode 100644 index 64ff784..0000000 --- a/docs/_sources/tuning/profiler_expert.rst.txt +++ /dev/null @@ -1,108 +0,0 @@ -:orphan: - -.. _profiler_expert: - -###################################### -Find bottlenecks in your code (expert) -###################################### -**Audience**: Users who want to build their own profilers. - ----- - -*********************** -Build your own profiler -*********************** -To build your own profiler, subclass :class:`~pytorch_lightning.profiler.base.Profiler` -and override some of its methods. Here is a simple example that profiles the first occurrence and total calls of each action: - -.. code-block:: python - - from pytorch_lightning.profiler import Profiler - from collections import defaultdict - import time - - - class ActionCountProfiler(Profiler): - def __init__(self, dirpath=None, filename=None): - super().__init__(dirpath=dirpath, filename=filename) - self._action_count = defaultdict(int) - self._action_first_occurrence = {} - - def start(self, action_name): - if action_name not in self._action_first_occurrence: - self._action_first_occurrence[action_name] = time.strftime("%m/%d/%Y, %H:%M:%S") - - def stop(self, action_name): - self._action_count[action_name] += 1 - - def summary(self): - res = f"\nProfile Summary: \n" - max_len = max(len(x) for x in self._action_count) - - for action_name in self._action_count: - # generate summary for actions called more than once - if self._action_count[action_name] > 1: - res += ( - f"{action_name:<{max_len}s} \t " - + "self._action_first_occurrence[action_name]} \t " - + "{self._action_count[action_name]} \n" - ) - - return res - - def teardown(self, stage): - self._action_count = {} - self._action_first_occurrence = {} - super().teardown(stage=stage) - -.. code-block:: python - - trainer = Trainer(profiler=ActionCountProfiler()) - trainer.fit(...) - ----- - -********************************** -Profile custom actions of interest -********************************** -To profile a specific action of interest, reference a profiler in the LightningModule. - -.. code-block:: python - - from pytorch_lightning.profiler import SimpleProfiler, PassThroughProfiler - - - class MyModel(LightningModule): - def __init__(self, profiler=None): - self.profiler = profiler or PassThroughProfiler() - -To profile in any part of your code, use the **self.profiler.profile()** function - -.. code-block:: python - - class MyModel(LightningModule): - def custom_processing_step(self, data): - with self.profiler.profile("my_custom_action"): - ... - return data - -Here's the full code: - -.. code-block:: python - - from pytorch_lightning.profiler import SimpleProfiler, PassThroughProfiler - - - class MyModel(LightningModule): - def __init__(self, profiler=None): - self.profiler = profiler or PassThroughProfiler() - - def custom_processing_step(self, data): - with self.profiler.profile("my_custom_action"): - ... - return data - - - profiler = SimpleProfiler() - model = MyModel(profiler) - trainer = Trainer(profiler=profiler, max_epochs=1) diff --git a/docs/_sources/tuning/profiler_intermediate.rst.txt b/docs/_sources/tuning/profiler_intermediate.rst.txt deleted file mode 100644 index d2b64b5..0000000 --- a/docs/_sources/tuning/profiler_intermediate.rst.txt +++ /dev/null @@ -1,181 +0,0 @@ -:orphan: - -.. _profiler_intermediate: - -############################################ -Find bottlenecks in your code (intermediate) -############################################ -**Audience**: Users who want to see more granular profiling information - ----- - -************************** -Profile pytorch operations -************************** -To understand the cost of each PyTorch operation, use the :class:`~pytorch_lightning.profiler.pytorch.PyTorchProfiler` built on top of the `PyTorch profiler `__. - -.. code-block:: python - - from pytorch_lightning.profiler import PyTorchProfiler - - profiler = PyTorchProfiler() - trainer = Trainer(profiler=profiler) - -The profiler will generate an output like this: - -.. code-block:: - - Profiler Report - - Profile stats for: training_step - --------------------- --------------- --------------- --------------- --------------- --------------- - Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg - --------------------- --------------- --------------- --------------- --------------- --------------- - t 62.10% 1.044ms 62.77% 1.055ms 1.055ms - addmm 32.32% 543.135us 32.69% 549.362us 549.362us - mse_loss 1.35% 22.657us 3.58% 60.105us 60.105us - mean 0.22% 3.694us 2.05% 34.523us 34.523us - div_ 0.64% 10.756us 1.90% 32.001us 16.000us - ones_like 0.21% 3.461us 0.81% 13.669us 13.669us - sum_out 0.45% 7.638us 0.74% 12.432us 12.432us - transpose 0.23% 3.786us 0.68% 11.393us 11.393us - as_strided 0.60% 10.060us 0.60% 10.060us 3.353us - to 0.18% 3.059us 0.44% 7.464us 7.464us - empty_like 0.14% 2.387us 0.41% 6.859us 6.859us - empty_strided 0.38% 6.351us 0.38% 6.351us 3.175us - fill_ 0.28% 4.782us 0.33% 5.566us 2.783us - expand 0.20% 3.336us 0.28% 4.743us 4.743us - empty 0.27% 4.456us 0.27% 4.456us 2.228us - copy_ 0.15% 2.526us 0.15% 2.526us 2.526us - broadcast_tensors 0.15% 2.492us 0.15% 2.492us 2.492us - size 0.06% 0.967us 0.06% 0.967us 0.484us - is_complex 0.06% 0.961us 0.06% 0.961us 0.481us - stride 0.03% 0.517us 0.03% 0.517us 0.517us - --------------------- --------------- --------------- --------------- --------------- --------------- - Self CPU time total: 1.681ms - -.. note:: - When using the PyTorch Profiler, wall clock time will not not be representative of the true wall clock time. - This is due to forcing profiled operations to be measured synchronously, when many CUDA ops happen asynchronously. - It is recommended to use this Profiler to find bottlenecks/breakdowns, however for end to end wall clock time use - the ``SimpleProfiler``. - ----- - -*************************** -Profile a distributed model -*************************** -To profile a distributed model, use the :class:`~pytorch_lightning.profiler.pytorch.PyTorchProfiler` with the *filename* argument which will save a report per rank. - -.. code-block:: python - - from pytorch_lightning.profiler import PyTorchProfiler - - profiler = PyTorchProfiler(filename="perf-logs") - trainer = Trainer(profiler=profiler) - -With two ranks, it will generate a report like so: - -.. code-block:: - - Profiler Report: rank 0 - - Profile stats for: training_step - --------------------- --------------- --------------- --------------- --------------- --------------- - Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg - --------------------- --------------- --------------- --------------- --------------- --------------- - t 62.10% 1.044ms 62.77% 1.055ms 1.055ms - addmm 32.32% 543.135us 32.69% 549.362us 549.362us - mse_loss 1.35% 22.657us 3.58% 60.105us 60.105us - mean 0.22% 3.694us 2.05% 34.523us 34.523us - div_ 0.64% 10.756us 1.90% 32.001us 16.000us - ones_like 0.21% 3.461us 0.81% 13.669us 13.669us - sum_out 0.45% 7.638us 0.74% 12.432us 12.432us - transpose 0.23% 3.786us 0.68% 11.393us 11.393us - as_strided 0.60% 10.060us 0.60% 10.060us 3.353us - to 0.18% 3.059us 0.44% 7.464us 7.464us - empty_like 0.14% 2.387us 0.41% 6.859us 6.859us - empty_strided 0.38% 6.351us 0.38% 6.351us 3.175us - fill_ 0.28% 4.782us 0.33% 5.566us 2.783us - expand 0.20% 3.336us 0.28% 4.743us 4.743us - empty 0.27% 4.456us 0.27% 4.456us 2.228us - copy_ 0.15% 2.526us 0.15% 2.526us 2.526us - broadcast_tensors 0.15% 2.492us 0.15% 2.492us 2.492us - size 0.06% 0.967us 0.06% 0.967us 0.484us - is_complex 0.06% 0.961us 0.06% 0.961us 0.481us - stride 0.03% 0.517us 0.03% 0.517us 0.517us - --------------------- --------------- --------------- --------------- --------------- --------------- - Self CPU time total: 1.681ms - -.. code-block:: - - Profiler Report: rank 1 - - Profile stats for: training_step - --------------------- --------------- --------------- --------------- --------------- --------------- - Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg - --------------------- --------------- --------------- --------------- --------------- --------------- - t 42.10% 1.044ms 62.77% 1.055ms 1.055ms - addmm 32.32% 543.135us 32.69% 549.362us 549.362us - mse_loss 1.35% 22.657us 3.58% 60.105us 60.105us - mean 0.22% 3.694us 2.05% 34.523us 34.523us - div_ 0.64% 10.756us 1.90% 32.001us 16.000us - ones_like 0.21% 3.461us 0.81% 13.669us 13.669us - sum_out 0.45% 7.638us 0.74% 12.432us 12.432us - transpose 0.23% 3.786us 0.68% 11.393us 11.393us - as_strided 0.60% 10.060us 0.60% 10.060us 3.353us - to 0.18% 3.059us 0.44% 7.464us 7.464us - empty_like 0.14% 2.387us 0.41% 6.859us 6.859us - empty_strided 0.38% 6.351us 0.38% 6.351us 3.175us - fill_ 0.28% 4.782us 0.33% 5.566us 2.783us - expand 0.20% 3.336us 0.28% 4.743us 4.743us - empty 0.27% 4.456us 0.27% 4.456us 2.228us - copy_ 0.15% 2.526us 0.15% 2.526us 2.526us - broadcast_tensors 0.15% 2.492us 0.15% 2.492us 2.492us - size 0.06% 0.967us 0.06% 0.967us 0.484us - is_complex 0.06% 0.961us 0.06% 0.961us 0.481us - stride 0.03% 0.517us 0.03% 0.517us 0.517us - --------------------- --------------- --------------- --------------- --------------- --------------- - Self CPU time total: 1.681ms - -This profiler will record ``training_step``, ``backward``, ``validation_step``, ``test_step``, and ``predict_step`` by default. -The output below shows the profiling for the action ``training_step``. The user can provide ``PyTorchProfiler(record_functions={...})`` -to extend the scope of profiled functions. - -.. note:: - When using the PyTorch Profiler, wall clock time will not not be representative of the true wall clock time. - This is due to forcing profiled operations to be measured synchronously, when many CUDA ops happen asynchronously. - It is recommended to use this Profiler to find bottlenecks/breakdowns, however for end to end wall clock time use - the ``SimpleProfiler``. - ----- - -***************************** -Visualize profiled operations -***************************** -To visualize the profiled operations, enable **emit_nvtx** in the :class:`~pytorch_lightning.profiler.pytorch.PyTorchProfiler`. - -.. code-block:: python - - from pytorch_lightning.profiler import PyTorchProfiler - - profiler = PyTorchProfiler(emit_nvtx=True) - trainer = Trainer(profiler=profiler) - -Then run as following: - -.. code-block:: - - nvprof --profile-from-start off -o trace_name.prof -- - -To visualize the profiled operation, you can either use **nvvp**: - -.. code-block:: - - nvvp trace_name.prof - -or python: - -.. code-block:: - - python -c 'import torch; print(torch.autograd.profiler.load_nvprof("trace_name.prof"))' diff --git a/docs/_sources/visualize/experiment_managers.rst.txt b/docs/_sources/visualize/experiment_managers.rst.txt deleted file mode 100644 index 30fada9..0000000 --- a/docs/_sources/visualize/experiment_managers.rst.txt +++ /dev/null @@ -1,25 +0,0 @@ -****************** -Manage Experiments -****************** -To track other artifacts, such as histograms or model topology graphs first select one of the many experiment managers (*loggers*) supported by Lightning - -.. code-block:: python - - from pytorch_lightning import loggers as pl_loggers - - tensorboard = pl_loggers.TensorBoardLogger() - trainer = Trainer(logger=tensorboard) - -then access the logger's API directly - -.. code-block:: python - - def training_step(self): - tensorboard = self.logger.experiment - tensorboard.add_image() - tensorboard.add_histogram(...) - tensorboard.add_figure(...) - ----- - -.. include:: supported_exp_managers.rst diff --git a/docs/_sources/visualize/loggers.rst.txt b/docs/_sources/visualize/loggers.rst.txt deleted file mode 100644 index bdf95ec..0000000 --- a/docs/_sources/visualize/loggers.rst.txt +++ /dev/null @@ -1,56 +0,0 @@ -.. _loggers: - -############################### -Track and Visualize Experiments -############################### - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Learn how to track and visualize metrics, images and text. - :col_css: col-md-4 - :button_link: logging_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Enable third-party experiment managers with advanced visualizations. - :col_css: col-md-4 - :button_link: logging_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Advanced - :description: Optimize model speed with advanced self.log arguments and cloud logging. - :col_css: col-md-4 - :button_link: logging_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Expert - :description: Make your own progress-bar or integrate a new experiment manager. - :col_css: col-md-4 - :button_link: logging_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: LightningModule.log API - :description: Dig into the LightningModule.log API in depth - :col_css: col-md-4 - :button_link: ../common/lightning_module.html#log - :height: 150 - -.. raw:: html - -
-
diff --git a/docs/_sources/visualize/logging_advanced.rst.txt b/docs/_sources/visualize/logging_advanced.rst.txt deleted file mode 100644 index ca11e39..0000000 --- a/docs/_sources/visualize/logging_advanced.rst.txt +++ /dev/null @@ -1,385 +0,0 @@ -:orphan: - -.. _logging_advanced: - -########################################## -Track and Visualize Experiments (advanced) -########################################## -**Audience:** Users who want to do advanced speed optimizations by customizing the logging behavior. - ----- - -**************************** -Change progress bar defaults -**************************** -To change the default values (ie: version number) shown in the progress bar, override the :meth:`~pytorch_lightning.callbacks.progress.base.ProgressBarBase.get_metrics` method in your logger. - -.. code-block:: python - - from pytorch_lightning.callbacks.progress import Tqdm - - - class CustomProgressBar(Tqdm): - def get_metrics(self, *args, **kwargs): - # don't show the version number - items = super().get_metrics() - items.pop("v_num", None) - return items - ----- - -************************************ -Customize tracking to speed up model -************************************ - - -Modify logging frequency -======================== - -Logging a metric on every single batch can slow down training. By default, Lightning logs every 50 rows, or 50 training steps. -To change this behaviour, set the *log_every_n_steps* :class:`~pytorch_lightning.trainer.trainer.Trainer` flag. - -.. testcode:: - - k = 10 - trainer = Trainer(log_every_n_steps=k) - ----- - -Modify flushing frequency -========================= - -Metrics are kept in memory for N steps to improve training efficiency. Every N steps, metrics flush to disk. To change the frequency of this flushing, use the *flush_logs_every_n_steps* Trainer argument. - -.. code-block:: python - - # faster training, high memory - Trainer(flush_logs_every_n_steps=500) - - # slower training, low memory - Trainer(flush_logs_every_n_steps=500) - -The higher *flush_logs_every_n_steps* is, the faster the model will train but the memory will build up until the next flush. -The smaller *flush_logs_every_n_steps* is, the slower the model will train but memory will be kept to a minimum. - -TODO: chart - ----- - -****************** -Customize self.log -****************** - -The LightningModule *self.log* method offers many configurations to customize its behavior. - ----- - -add_dataloader_idx -================== -**Default:** True - -If True, appends the index of the current dataloader to the name (when using multiple dataloaders). If False, user needs to give unique names for each dataloader to not mix the values. - -.. code-block:: python - - self.log(add_dataloader_idx=True) - ----- - -batch_size -========== -**Default:** None - -Current batch size used for accumulating logs logged with ``on_epoch=True``. This will be directly inferred from the loaded batch, but for some data structures you might need to explicitly provide it. - -.. code-block:: python - - self.log(batch_size=32) - ----- - -enable_graph -============ -**Default:** True - -If True, will not auto detach the graph. - -.. code-block:: python - - self.log(enable_graph=True) - ----- - -logger -====== -**Default:** True - -Send logs to the logger like ``Tensorboard``, or any other custom logger passed to the :class:`~pytorch_lightning.trainer.trainer.Trainer` (Default: ``True``). - -.. code-block:: python - - self.log(logger=True) - ----- - -on_epoch -======== -**Default:** It varies - -If this is True, that specific *self.log* call accumulates and reduces all metrics to the end of the epoch. - -.. code-block:: python - - self.log(on_epoch=True) - -The default value depends in which function this is called - -.. code-block:: python - - def training_step(self, batch, batch_idx): - # Default: False - self.log(on_epoch=False) - - - def validation_step(self, batch, batch_idx): - # Default: True - self.log(on_epoch=True) - - - def test_step(self, batch, batch_idx): - # Default: True - self.log(on_epoch=True) - ----- - -on_step -======= -**Default:** It varies - -If this is True, that specific *self.log* call will NOT accumulate metrics. Instead it will generate a timeseries across steps. - -.. code-block:: python - - self.log(on_step=True) - -The default value depends in which function this is called - -.. code-block:: python - - def training_step(self, batch, batch_idx): - # Default: True - self.log(on_step=True) - - - def validation_step(self, batch, batch_idx): - # Default: False - self.log(on_step=False) - - - def test_step(self, batch, batch_idx): - # Default: False - self.log(on_step=False) - - ----- - -prog_bar -======== -**Default:** False - -If set to True, logs will be sent to the progress bar. - -.. code-block:: python - - self.log(prog_bar=True) - ----- - -rank_zero_only -============== -**Default:** True - -Whether the value will be logged only on rank 0. This will prevent synchronization which would produce a deadlock as not all processes would perform this log call. - -.. code-block:: python - - self.log(rank_zero_only=True) - ----- - -reduce_fx -========= -**Default:** :meth:`torch.mean` - -Reduction function over step values for end of epoch. Uses :meth:`torch.mean` by default. - -.. code-block:: python - - self.log(reduce_fx=torch.mean) - ----- - -sync_dist -========= -**Default:** False - -If True, reduces the metric across devices. Use with care as this may lead to a significant communication overhead. - -.. code-block:: python - - self.log(sync_dist=False) - ----- - -sync_dist_group -=============== -**Default:** None - -The DDP group to sync across. - -.. code-block:: python - - import torch.distributed as dist - - group = dist.init_process_group("nccl", rank=self.global_rank, world_size=self.world_size) - self.log(sync_dist_group=group) - ----- - -*************************************** -Enable metrics for distributed training -*************************************** -For certain types of metrics that need complex aggregation, we recommended to build your metric using torchmetric which ensures all the complexities of metric aggregation in distributed environments is handled. - -First, implement your metric: - -.. code-block:: python - - import torch - import torchmetrics - - - class MyAccuracy(Metric): - def __init__(self, dist_sync_on_step=False): - # call `self.add_state`for every internal state that is needed for the metrics computations - # dist_reduce_fx indicates the function that should be used to reduce - # state from multiple processes - super().__init__(dist_sync_on_step=dist_sync_on_step) - - self.add_state("correct", default=torch.tensor(0), dist_reduce_fx="sum") - self.add_state("total", default=torch.tensor(0), dist_reduce_fx="sum") - - def update(self, preds: torch.Tensor, target: torch.Tensor): - # update metric states - preds, target = self._input_format(preds, target) - assert preds.shape == target.shape - - self.correct += torch.sum(preds == target) - self.total += target.numel() - - def compute(self): - # compute final result - return self.correct.float() / self.total - -To use the metric inside Lightning, 1) initialize it in the init, 2) compute the metric, 3) pass it into *self.log* - -.. code-block:: python - - class LitModel(LightningModule): - def __init__(self): - # 1. initialize the metric - self.accuracy = MyAccuracy() - - def training_step(self, batch, batch_idx): - x, y = batch - preds = self(x) - - # 2. compute the metric - self.accuracy(preds, y) - - # 3. log it - self.log("train_acc_step", self.accuracy) - ----- - -******************************** -Log to a custom cloud filesystem -******************************** -Lightning is integrated with the major remote file systems including local filesystems and several cloud storage providers such as -`S3 `_ on `AWS `_, `GCS `_ on `Google Cloud `_, -or `ADL `_ on `Azure `_. - -PyTorch Lightning uses `fsspec `_ internally to handle all filesystem operations. - -To save logs to a remote filesystem, prepend a protocol like "s3:/" to the root_dir used for writing and reading model data. - -.. code-block:: python - - from pytorch_lightning.loggers import TensorBoardLogger - - logger = TensorBoardLogger(save_dir="s3://my_bucket/logs/") - - trainer = Trainer(logger=logger) - trainer.fit(model) - ----- - -********************************* -Track both step and epoch metrics -********************************* -To track the timeseries over steps (*on_step*) as well as the accumulated epoch metric (*on_epoch*), set both to True - -.. code-block:: python - - self.log(on_step=True, on_epoch=True) - -Setting both to True will generate two graphs with *_step* for the timeseries over steps and *_epoch* for the epoch metric. - -# TODO: show images of both - ----- - -************************************** -Understand self.log automatic behavior -************************************** -This table shows the default values of *on_step* and *on_epoch* depending on the *LightningModule* or *Callback* method. - ----- - -In LightningModule -================== - -.. list-table:: Default behavior of logging in ightningModule - :widths: 50 25 25 - :header-rows: 1 - - * - Method - - on_step - - on_epoch - * - on_after_backward, on_before_backward, on_before_optimizer_step, on_before_zero_grad, training_step, training_step_end - - True - - False - * - training_epoch_end, test_epoch_end, test_step, test_step_end, validation_epoch_end, validation_step, validation_step_end - - False - - True - ----- - -In Callback -=========== - -.. list-table:: Default behavior of logging in Callback - :widths: 50 25 25 - :header-rows: 1 - - * - Method - - on_step - - on_epoch - * - on_after_backward, on_before_backward, on_before_optimizer_step, on_before_zero_grad, on_train_batch_start, on_train_batch_end - - True - - False - * - on_train_epoch_start, on_train_epoch_end, on_train_start, on_validation_batch_start, on_validation_batch_end, on_validation_start, on_validation_epoch_start, on_validation_epoch_end - - False - - True - -.. note:: To add logging to an unsupported method, please open an issue with a clear description of why it is blocking you. diff --git a/docs/_sources/visualize/logging_basic.rst.txt b/docs/_sources/visualize/logging_basic.rst.txt deleted file mode 100644 index 8732305..0000000 --- a/docs/_sources/visualize/logging_basic.rst.txt +++ /dev/null @@ -1,146 +0,0 @@ -:orphan: - -.. _logging_basic: - -####################################### -Track and Visualize Experiments (basic) -####################################### -**Audience:** Users who want to visualize and monitor their model development - ----- - -******************************* -Why do I need to track metrics? -******************************* -In model development, we track values of interest such as the *validation_loss* to visualize the learning process for our models. Model development is like driving a car without windows, charts and logs provide the *windows* to know where to drive the car. - -With Lightning, you can visualize virtually anything you can think of: numbers, text, images, audio. Your creativity and imagination are the only limiting factor. - ----- - -************* -Track metrics -************* -Metric visualization is the most basic but powerful way of understanding how your model is doing throughout the model development process. - -To track a metric, simply use the *self.log* method available inside the *LightningModule* - -.. code-block:: python - - class LitModel(pl.LightningModule): - def training_step(self, batch, batch_idx): - value = self.global_step - self.log("some_value", self.global_step) - -To log multiple metrics at once, use *self.log_dict* - -.. code-block:: python - - values = {"loss": loss, "acc": acc, "metric_n": metric_n} # add more items if needed - self.log_dict(values) - -TODO: show plot of metric changing over time - ----- - -View in the commandline -======================= - -To view metrics in the commandline progress bar, set the *prog_bar* argument to True. - -.. code-block:: python - - self.log(prog_bar=True) - -TODO: need progress bar here - ----- - -View in the browser -=================== -To view metrics in the browser you need to use an *experiment manager* with these capabilities. By Default, Lightning uses Tensorboard which is free and opensource. - -Tensorboard is already enabled by default - -.. code-block:: python - - # every trainer already has tensorboard enabled by default - trainer = Trainer() - -To launch the tensorboard dashboard run the following command on the commandline. - -.. code-block:: bash - - tensorboard --logdir=lightning_logs/ - -If you're using a notebook environment such as *colab* or *kaggle* or *jupyter*, launch Tensorboard with this command - -.. code-block:: bash - - %reload_ext tensorboard - %tensorboard --logdir=lightning_logs/ - ----- - -Accumulate a metric -=================== -When *self.log* is called inside the *training_step*, it generates a timeseries showing how the metric behaves over time. - -TODO: show chart - -However, For the validation and test sets we are not generally interested in plotting the metric values per batch of data. Instead, we want to compute a summary statistic (such as average, min or max) across the full split of data. - -When you call self.log inside the *validation_step* and *test_step*, Lightning automatically accumulates the metric and averages it once it's gone through the whole split (*epoch*). - -.. code-block:: python - - def validation_step(self, batch, batch_idx): - value = batch_idx + 1 - self.log("average_value", value) - -TODO: show single point plotted - -If you don't want to average, add your own function in the *reduce_fx* argument. - -.. code-block:: python - - # default function - self.log(reduce_fx=torch.mean) - ----- - -************ -Track images -************ -If your *experiment manager* supports image visualization, simply *log* the image with *self.log* - -.. code-block:: python - - # (32 batch samples, 3 channels, 32 width, 32 height) - image = torch.Tensor(32, 3, 28, 28) - self.log("an_image", image) - ----- - -********** -Track text -********** -If your *experiment manager* supports text visualization, simply *log* the text with *self.log* - -.. code-block:: python - - text = "hello world" - self.log("some_text", text) - -# TODO: show screenshot - ----- - -****************************** -Configure the saving directory -****************************** -By default, anything that is logged is saved to the current working directory. To use a different directory, set the *default_root_dir* argument in the Trainer. - -.. code-block:: python - - Trainer(default_root_dir="/your/custom/path") diff --git a/docs/_sources/visualize/logging_expert.rst.txt b/docs/_sources/visualize/logging_expert.rst.txt deleted file mode 100644 index 3b44ee9..0000000 --- a/docs/_sources/visualize/logging_expert.rst.txt +++ /dev/null @@ -1,135 +0,0 @@ -:orphan: - -.. _logging_expert: - -######################################## -Track and Visualize Experiments (expert) -######################################## -**Audience:** Users who want to make their own progress bars or integrate new experiment managers. - ----- - -*********************** -Change the progress bar -*********************** - -If you'd like to change the way the progress bar displays information you can use some of our built-in progress bard or build your own. - ----- - -Use the TQDMProgressBar -======================= -To use the TQDMProgressBar pass it into the *callbacks* :class:`~pytorch_lightning.trainer.trainer.Trainer` argument. - -.. code-block:: python - - from pytorch_lightning.callbacks import TQDMProgressBar - - trainer = Trainer(callbacks=[TQDMProgressBar()]) - ----- - -Use the RichProgressBar -======================= -The RichProgressBar can add custom colors and beautiful formatting for your progress bars. First, install the *`rich `_* library - -.. code-block:: bash - - pip install rich - -Then pass the callback into the callbacks :class:`~pytorch_lightning.trainer.trainer.Trainer` argument: - -.. code-block:: python - - from pytorch_lightning.callbacks import RichProgressBar - - trainer = Trainer(callbacks=[RichProgressBar()]) - -The rich progress bar can also have custom themes - -.. code-block:: python - - from pytorch_lightning.callbacks import RichProgressBar - from pytorch_lightning.callbacks.progress.rich_progress import RichProgressBarTheme - - # create your own theme! - theme = RichProgressBarTheme(description="green_yellow", progress_bar="green1") - - # init as normal - progress_bar = RichProgressBar(theme=theme) - trainer = Trainer(callbacks=progress_bar) - ----- - -************************ -Customize a progress bar -************************ -To customize either the :class:`~pytorch_lightning.callbacks.TQDMProgressBar` or the :class:`~pytorch_lightning.callbacks.RichProgressBar`, subclass it and override any of its methods. - -.. code-block:: python - - from pytorch_lightning.callbacks import TQDMProgressBar - - - class LitProgressBar(TQDMProgressBar): - def init_validation_tqdm(self): - bar = super().init_validation_tqdm() - bar.set_description("running validation...") - return bar - ----- - -*************************** -Build your own progress bar -*************************** -To build your own progress bar, subclass :class:`~pytorch_lightning.callbacks.ProgressBarBase` - -.. code-block:: python - - from pytorch_lightning.callbacks import ProgressBarBase - - - class LitProgressBar(ProgressBarBase): - def __init__(self): - super().__init__() # don't forget this :) - self.enable = True - - def disable(self): - self.enable = False - - def on_train_batch_end(self, trainer, pl_module, outputs, batch_idx): - super().on_train_batch_end(trainer, pl_module, outputs, batch_idx) # don't forget this :) - percent = (self.train_batch_idx / self.total_train_batches) * 100 - sys.stdout.flush() - sys.stdout.write(f"{percent:.01f} percent complete \r") - - - bar = LitProgressBar() - trainer = Trainer(callbacks=[bar]) - ----- - -******************************* -Integrate an experiment manager -******************************* -To create an integration between a custom logger and Lightning, subclass :class:`~pytorch_lightning.loggers.base.LightningLoggerBase` - -.. code-block:: python - - from pytorch_lightning.loggers import Logger - - - class LitLogger(Logger): - @property - def name(self) -> str: - return "my-experiment" - - @property - def version(self): - return "version_0" - - def log_metrics(self, metrics, step=None): - print("my logged metrics", metrics) - - def log_hyperparams(self, params, *args, **kwargs): - print("my logged hyperparameters", params) diff --git a/docs/_sources/visualize/logging_intermediate.rst.txt b/docs/_sources/visualize/logging_intermediate.rst.txt deleted file mode 100644 index 1b0dd6b..0000000 --- a/docs/_sources/visualize/logging_intermediate.rst.txt +++ /dev/null @@ -1,80 +0,0 @@ -.. _logging_intermediate: - -############################################## -Track and Visualize Experiments (intermediate) -############################################## -**Audience:** Users who want to track more complex outputs and use third-party experiment managers. - ----- - -******************************* -Track audio and other artifacts -******************************* -To track other artifacts, such as histograms or model topology graphs first select one of the many loggers supported by Lightning - -.. code-block:: python - - from pytorch_lightning import loggers as pl_loggers - - tensorboard = pl_loggers.TensorBoardLogger() - trainer = Trainer(logger=tensorboard) - -then access the logger's API directly - -.. code-block:: python - - def training_step(self): - tensorboard = self.logger.experiment - tensorboard.add_image() - tensorboard.add_histogram(...) - tensorboard.add_figure(...) - ----- - -.. include:: supported_exp_managers.rst - ----- - -**************************************** -Track multiple metrics in the same chart -**************************************** -If your logger supports plotting multiple metrics on the same chart, pass in a dictionary to *self.log*. - -.. code-block:: python - - self.log("performance", {"acc": acc, "recall": recall}) - ----- - -********************* -Track hyperparameters -********************* -To track hyperparameters, first call *save_hyperparameters* from the LightningModule init: - -.. code-block:: python - - class MyLightningModule(LightningModule): - def __init__(self, learning_rate, another_parameter, *args, **kwargs): - super().__init__() - self.save_hyperparameters() - -If your logger supports tracked hyperparameters, the hyperparameters will automatically show up on the logger dashboard. - -TODO: show tracked hyperparameters. - ----- - -******************** -Track model topology -******************** -Multiple loggers support visualizing the model topology. Here's an example that tracks the model topology using Tensorboard. - -.. code-block:: python - - def any_lightning_module_function_or_hook(self): - tensorboard_logger = self.logger.experiment - - prototype_array = torch.Tensor(32, 1, 28, 27) - tensorboard_logger.log_graph(model=self, input_array=prototype_array) - -TODO: show tensorboard topology. diff --git a/docs/_sources/visualize/supported_exp_managers.rst.txt b/docs/_sources/visualize/supported_exp_managers.rst.txt deleted file mode 100644 index 1a15ee2..0000000 --- a/docs/_sources/visualize/supported_exp_managers.rst.txt +++ /dev/null @@ -1,198 +0,0 @@ -Comet.ml -======== -To use `Comet.ml `_ first install the comet package: - -.. code-block:: bash - - pip install comet-ml - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import CometLogger - - comet_logger = CometLogger(api_key="YOUR_COMET_API_KEY") - trainer = Trainer(logger=comet_logger) - -Access the comet logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class LitModel(LightningModule): - def any_lightning_module_function_or_hook(self): - comet = self.logger.experiment - fake_images = torch.Tensor(32, 3, 28, 28) - comet.add_image("generated_images", fake_images, 0) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.CometLogger`. - ----- - -MLflow -====== -To use `MLflow `_ first install the MLflow package: - -.. code-block:: bash - - pip install mlflow - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import MLFlowLogger - - mlf_logger = MLFlowLogger(experiment_name="lightning_logs", tracking_uri="file:./ml-runs") - trainer = Trainer(logger=mlf_logger) - -Access the comet logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class LitModel(LightningModule): - def any_lightning_module_function_or_hook(self): - mlf_logger = self.logger.experiment - fake_images = torch.Tensor(32, 3, 28, 28) - mlf_logger.add_image("generated_images", fake_images, 0) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.MLFlowLogger`. - ----- - -Neptune.ai -========== -To use `Neptune.ai `_ first install the neptune package: - -.. code-block:: bash - - pip install neptune-client - -or with conda: - -.. code-block:: bash - - conda install -c conda-forge neptune-client - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import NeptuneLogger - - neptune_logger = NeptuneLogger( - api_key="ANONYMOUS", # replace with your own - project="common/pytorch-lightning-integration", # format "" - ) - trainer = Trainer(logger=neptune_logger) - -Access the neptune logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class LitModel(LightningModule): - def any_lightning_module_function_or_hook(self): - neptune_logger = self.logger.experiment["your/metadata/structure"] - neptune_logger.log(metadata) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.NeptuneLogger`. - ----- - -Tensorboard -=========== -`TensorBoard `_ already comes installed with Lightning. If you removed the install install the following package. - -.. code-block:: bash - - pip install tensorboard - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import TensorBoardLogger - - logger = TensorBoardLogger() - trainer = Trainer(logger=logger) - -Access the tensorboard logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class LitModel(LightningModule): - def any_lightning_module_function_or_hook(self): - tensorboard_logger = self.logger.experiment - fake_images = torch.Tensor(32, 3, 28, 28) - tensorboard_logger.add_image("generated_images", fake_images, 0) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.TensorBoardLogger`. - ----- - -Weights and Biases -================== -To use `Weights and Biases `_ (wandb) first install the wandb package: - -.. code-block:: bash - - pip install wandb - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import WandbLogger - - wandb_logger = WandbLogger(project="MNIST", log_model="all") - trainer = Trainer(logger=wandb_logger) - - # log gradients and model topology - wandb_logger.watch(model) - -Access the wandb logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class MyModule(LightningModule): - def any_lightning_module_function_or_hook(self): - wandb_logger = self.logger.experiment - fake_images = torch.Tensor(32, 3, 28, 28) - - # Option 1 - wandb_logger.log({"generated_images": [wandb.Image(fake_images, caption="...")]}) - - # Option 2 for specifically logging images - wandb_logger.log_image(key="generated_images", images=[fake_images]) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.WandbLogger`. -`Demo in Google Colab `__ with hyperparameter search and model logging. - ----- - -Use multiple exp managers -========================= -To use multiple experiment managers at the same time, pass a list to the *logger* :class:`~pytorch_lightning.trainer.trainer.Trainer` argument. - -.. code-block:: python - - from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger - - logger1 = TensorBoardLogger() - logger2 = WandbLogger() - trainer = Trainer(logger=[logger1, logger2]) - - -Access all loggers from any function (except the LightningModule *init*) to use their APIs for tracking advanced artifacts - -.. code-block:: python - - class MyModule(LightningModule): - def any_lightning_module_function_or_hook(self): - tensorboard_logger = self.logger.experiment[0] - wandb_logger = self.logger.experiment[1] - - fake_images = torch.Tensor(32, 3, 28, 28) - - tensorboard_logger.add_image("generated_images", fake_images, 0) - wandb_logger.add_image("generated_images", fake_images, 0) diff --git a/docs/_static/basic.css b/docs/_static/basic.css deleted file mode 100644 index bf18350..0000000 --- a/docs/_static/basic.css +++ /dev/null @@ -1,906 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 450px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a.brackets:before, -span.brackets > a:before{ - content: "["; -} - -a.brackets:after, -span.brackets > a:after { - content: "]"; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -table.footnote td, table.footnote th { - border: 0 !important; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -dl.footnote > dt, -dl.citation > dt { - float: left; - margin-right: 0.5em; -} - -dl.footnote > dd, -dl.citation > dd { - margin-bottom: 0em; -} - -dl.footnote > dd:after, -dl.citation > dd:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dt:after { - content: ":"; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs/_static/check-solid.svg b/docs/_static/check-solid.svg deleted file mode 100644 index 92fad4b..0000000 --- a/docs/_static/check-solid.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/docs/_static/classtemplate.rst b/docs/_static/classtemplate.rst deleted file mode 100644 index 398a0ec..0000000 --- a/docs/_static/classtemplate.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: {{ module }} - - -{{ name | underline }} - -.. autoclass:: {{ name }} - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: diff --git a/docs/_static/clipboard.min.js b/docs/_static/clipboard.min.js deleted file mode 100644 index 54b3c46..0000000 --- a/docs/_static/clipboard.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * clipboard.js v2.0.8 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 - - - - diff --git a/docs/_static/copybutton.css b/docs/_static/copybutton.css deleted file mode 100644 index 40eafe5..0000000 --- a/docs/_static/copybutton.css +++ /dev/null @@ -1,93 +0,0 @@ -/* Copy buttons */ -button.copybtn { - position: absolute; - display: flex; - top: .3em; - right: .3em; - width: 1.7em; - height: 1.7em; - opacity: 0; - transition: opacity 0.3s, border .3s, background-color .3s; - user-select: none; - padding: 0; - border: none; - outline: none; - border-radius: 0.4em; - /* The colors that GitHub uses */ - border: #1b1f2426 1px solid; - background-color: #f6f8fa; - color: #57606a; -} - -button.copybtn.success { - border-color: #22863a; - color: #22863a; -} - -button.copybtn svg { - stroke: currentColor; - width: 1.5em; - height: 1.5em; - padding: 0.1em; -} - -div.highlight { - position: relative; -} - -.highlight:hover button.copybtn { - opacity: 1; -} - -.highlight button.copybtn:hover { - background-color: rgb(235, 235, 235); -} - -.highlight button.copybtn:active { - background-color: rgb(187, 187, 187); -} - -/** - * A minimal CSS-only tooltip copied from: - * https://codepen.io/mildrenben/pen/rVBrpK - * - * To use, write HTML like the following: - * - *

Short

- */ - .o-tooltip--left { - position: relative; - } - - .o-tooltip--left:after { - opacity: 0; - visibility: hidden; - position: absolute; - content: attr(data-tooltip); - padding: .2em; - font-size: .8em; - left: -.2em; - background: grey; - color: white; - white-space: nowrap; - z-index: 2; - border-radius: 2px; - transform: translateX(-102%) translateY(0); - transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); -} - -.o-tooltip--left:hover:after { - display: block; - opacity: 1; - visibility: visible; - transform: translateX(-100%) translateY(0); - transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); - transition-delay: .5s; -} - -/* By default the copy button shouldn't show up when printing a page */ -@media print { - button.copybtn { - display: none; - } -} diff --git a/docs/_static/copybutton.js b/docs/_static/copybutton.js deleted file mode 100644 index 40ac331..0000000 --- a/docs/_static/copybutton.js +++ /dev/null @@ -1,220 +0,0 @@ -// Localization support -const messages = { - 'en': { - 'copy': 'Copy', - 'copy_to_clipboard': 'Copy to clipboard', - 'copy_success': 'Copied!', - 'copy_failure': 'Failed to copy', - }, - 'es' : { - 'copy': 'Copiar', - 'copy_to_clipboard': 'Copiar al portapapeles', - 'copy_success': '¡Copiado!', - 'copy_failure': 'Error al copiar', - }, - 'de' : { - 'copy': 'Kopieren', - 'copy_to_clipboard': 'In die Zwischenablage kopieren', - 'copy_success': 'Kopiert!', - 'copy_failure': 'Fehler beim Kopieren', - }, - 'fr' : { - 'copy': 'Copier', - 'copy_to_clipboard': 'Copié dans le presse-papier', - 'copy_success': 'Copié !', - 'copy_failure': 'Échec de la copie', - }, - 'ru': { - 'copy': 'Скопировать', - 'copy_to_clipboard': 'Скопировать в буфер', - 'copy_success': 'Скопировано!', - 'copy_failure': 'Не удалось скопировать', - }, - 'zh-CN': { - 'copy': '复制', - 'copy_to_clipboard': '复制到剪贴板', - 'copy_success': '复制成功!', - 'copy_failure': '复制失败', - }, - 'it' : { - 'copy': 'Copiare', - 'copy_to_clipboard': 'Copiato negli appunti', - 'copy_success': 'Copiato!', - 'copy_failure': 'Errore durante la copia', - } -} - -let locale = 'en' -if( document.documentElement.lang !== undefined - && messages[document.documentElement.lang] !== undefined ) { - locale = document.documentElement.lang -} - -let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; -if (doc_url_root == '#') { - doc_url_root = ''; -} - -/** - * SVG files for our copy buttons - */ -let iconCheck = ` - ${messages[locale]['copy_success']} - - -` - -// If the user specified their own SVG use that, otherwise use the default -let iconCopy = ``; -if (!iconCopy) { - iconCopy = ` - ${messages[locale]['copy_to_clipboard']} - - - -` -} - -/** - * Set up copy/paste for code blocks - */ - -const runWhenDOMLoaded = cb => { - if (document.readyState != 'loading') { - cb() - } else if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', cb) - } else { - document.attachEvent('onreadystatechange', function() { - if (document.readyState == 'complete') cb() - }) - } -} - -const codeCellId = index => `codecell${index}` - -// Clears selected text since ClipboardJS will select the text when copying -const clearSelection = () => { - if (window.getSelection) { - window.getSelection().removeAllRanges() - } else if (document.selection) { - document.selection.empty() - } -} - -// Changes tooltip text for two seconds, then changes it back -const temporarilyChangeTooltip = (el, oldText, newText) => { - el.setAttribute('data-tooltip', newText) - el.classList.add('success') - setTimeout(() => el.setAttribute('data-tooltip', oldText), 2000) - setTimeout(() => el.classList.remove('success'), 2000) -} - -// Changes the copy button icon for two seconds, then changes it back -const temporarilyChangeIcon = (el) => { - el.innerHTML = iconCheck; - setTimeout(() => {el.innerHTML = iconCopy}, 2000) -} - -const addCopyButtonToCodeCells = () => { - // If ClipboardJS hasn't loaded, wait a bit and try again. This - // happens because we load ClipboardJS asynchronously. - if (window.ClipboardJS === undefined) { - setTimeout(addCopyButtonToCodeCells, 250) - return - } - - // Add copybuttons to all of our code cells - const codeCells = document.querySelectorAll('div.highlight pre') - codeCells.forEach((codeCell, index) => { - const id = codeCellId(index) - codeCell.setAttribute('id', id) - - const clipboardButton = id => - `` - codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) - }) - -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -// Callback when a copy button is clicked. Will be passed the node that was clicked -// should then grab the text and replace pieces of text that shouldn't be used in output -function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { - - var regexp; - var match; - - // Do we check for line continuation characters and "HERE-documents"? - var useLineCont = !!lineContinuationChar - var useHereDoc = !!hereDocDelim - - // create regexp to capture prompt and remaining line - if (isRegexp) { - regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') - } else { - regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') - } - - const outputLines = []; - var promptFound = false; - var gotLineCont = false; - var gotHereDoc = false; - const lineGotPrompt = []; - for (const line of textContent.split('\n')) { - match = line.match(regexp) - if (match || gotLineCont || gotHereDoc) { - promptFound = regexp.test(line) - lineGotPrompt.push(promptFound) - if (removePrompts && promptFound) { - outputLines.push(match[2]) - } else { - outputLines.push(line) - } - gotLineCont = line.endsWith(lineContinuationChar) & useLineCont - if (line.includes(hereDocDelim) & useHereDoc) - gotHereDoc = !gotHereDoc - } else if (!onlyCopyPromptLines) { - outputLines.push(line) - } else if (copyEmptyLines && line.trim() === '') { - outputLines.push(line) - } - } - - // If no lines with the prompt were found then just use original lines - if (lineGotPrompt.some(v => v === true)) { - textContent = outputLines.join('\n'); - } - - // Remove a trailing newline to avoid auto-running when pasting - if (textContent.endsWith("\n")) { - textContent = textContent.slice(0, -1) - } - return textContent -} - - -var copyTargetText = (trigger) => { - var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); - return formatCopyText(target.innerText, '', false, true, true, true, '', '') -} - - // Initialize with a callback so we can modify the text before copy - const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) - - // Update UI with error/success messages - clipboard.on('success', event => { - clearSelection() - temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) - temporarilyChangeIcon(event.trigger) - }) - - clipboard.on('error', event => { - temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) - }) -} - -runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/docs/_static/copybutton_funcs.js b/docs/_static/copybutton_funcs.js deleted file mode 100644 index b9168c5..0000000 --- a/docs/_static/copybutton_funcs.js +++ /dev/null @@ -1,58 +0,0 @@ -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -// Callback when a copy button is clicked. Will be passed the node that was clicked -// should then grab the text and replace pieces of text that shouldn't be used in output -export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { - - var regexp; - var match; - - // Do we check for line continuation characters and "HERE-documents"? - var useLineCont = !!lineContinuationChar - var useHereDoc = !!hereDocDelim - - // create regexp to capture prompt and remaining line - if (isRegexp) { - regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') - } else { - regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') - } - - const outputLines = []; - var promptFound = false; - var gotLineCont = false; - var gotHereDoc = false; - const lineGotPrompt = []; - for (const line of textContent.split('\n')) { - match = line.match(regexp) - if (match || gotLineCont || gotHereDoc) { - promptFound = regexp.test(line) - lineGotPrompt.push(promptFound) - if (removePrompts && promptFound) { - outputLines.push(match[2]) - } else { - outputLines.push(line) - } - gotLineCont = line.endsWith(lineContinuationChar) & useLineCont - if (line.includes(hereDocDelim) & useHereDoc) - gotHereDoc = !gotHereDoc - } else if (!onlyCopyPromptLines) { - outputLines.push(line) - } else if (copyEmptyLines && line.trim() === '') { - outputLines.push(line) - } - } - - // If no lines with the prompt were found then just use original lines - if (lineGotPrompt.some(v => v === true)) { - textContent = outputLines.join('\n'); - } - - // Remove a trailing newline to avoid auto-running when pasting - if (textContent.endsWith("\n")) { - textContent = textContent.slice(0, -1) - } - return textContent -} diff --git a/docs/_static/css/theme.css b/docs/_static/css/theme.css deleted file mode 100644 index 9597990..0000000 --- a/docs/_static/css/theme.css +++ /dev/null @@ -1,14638 +0,0 @@ -@charset "UTF-8"; -/*! - * Bootstrap v4.6.1 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors - * Copyright 2011-2021 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -:root { - --blue: #007bff; - --indigo: #6610f2; - --purple: #6f42c1; - --pink: #e83e8c; - --red: #dc3545; - --orange: #fd7e14; - --yellow: #ffc107; - --green: #28a745; - --teal: #20c997; - --cyan: #17a2b8; - --white: #fff; - --gray: #6c757d; - --gray-dark: #343a40; - --primary: #007bff; - --secondary: #6c757d; - --success: #28a745; - --info: #17a2b8; - --warning: #ffc107; - --danger: #dc3545; - --light: #f8f9fa; - --dark: #343a40; - --breakpoint-xs: 0; - --breakpoint-sm: 576px; - --breakpoint-md: 768px; - --breakpoint-lg: 992px; - --breakpoint-xl: 1200px; - --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; -} - -*, -*::before, -*::after { - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -html { - font-family: sans-serif; - line-height: 1.15; - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { - display: block; -} - -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #212529; - text-align: left; - background-color: #fff; -} - -[tabindex="-1"]:focus:not(:focus-visible) { - outline: 0 !important; -} - -hr { - -webkit-box-sizing: content-box; - box-sizing: content-box; - height: 0; - overflow: visible; -} - -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: 0.5rem; -} - -p { - margin-top: 0; - margin-bottom: 1rem; -} - -abbr[title], -abbr[data-original-title] { - text-decoration: underline; - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - border-bottom: 0; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; -} - -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; -} - -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; -} - -dt { - font-weight: 700; -} - -dd { - margin-bottom: .5rem; - margin-left: 0; -} - -blockquote { - margin: 0 0 1rem; -} - -b, -strong { - font-weight: bolder; -} - -small { - font-size: 80%; -} - -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} - -sub { - bottom: -.25em; -} - -sup { - top: -.5em; -} - -a { - color: #007bff; - text-decoration: none; - background-color: transparent; -} -a:hover { - color: #0056b3; - text-decoration: underline; -} - -a:not([href]):not([class]) { - color: inherit; - text-decoration: none; -} -a:not([href]):not([class]):hover { - color: inherit; - text-decoration: none; -} - -pre, -code, -kbd, -samp { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-size: 1em; -} - -pre { - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - -ms-overflow-style: scrollbar; -} - -figure { - margin: 0 0 1rem; -} - -img { - vertical-align: middle; - border-style: none; -} - -svg { - overflow: hidden; - vertical-align: middle; -} - -table { - border-collapse: collapse; -} - -caption { - padding-top: 0.75rem; - padding-bottom: 0.75rem; - color: #6c757d; - text-align: left; - caption-side: bottom; -} - -th { - text-align: inherit; - text-align: -webkit-match-parent; -} - -label { - display: inline-block; - margin-bottom: 0.5rem; -} - -button { - border-radius: 0; -} - -button:focus:not(:focus-visible) { - outline: 0; -} - -input, -button, -select, -optgroup, -textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -button, -input { - overflow: visible; -} - -button, -select { - text-transform: none; -} - -[role="button"] { - cursor: pointer; -} - -select { - word-wrap: normal; -} - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -button:not(:disabled), -[type="button"]:not(:disabled), -[type="reset"]:not(:disabled), -[type="submit"]:not(:disabled) { - cursor: pointer; -} - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - padding: 0; - border-style: none; -} - -input[type="radio"], -input[type="checkbox"] { - -webkit-box-sizing: border-box; - box-sizing: border-box; - padding: 0; -} - -textarea { - overflow: auto; - resize: vertical; -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} - -legend { - display: block; - width: 100%; - max-width: 100%; - padding: 0; - margin-bottom: .5rem; - font-size: 1.5rem; - line-height: inherit; - color: inherit; - white-space: normal; -} - -progress { - vertical-align: baseline; -} - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -[type="search"] { - outline-offset: -2px; - -webkit-appearance: none; -} - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; -} - -output { - display: inline-block; -} - -summary { - display: list-item; - cursor: pointer; -} - -template { - display: none; -} - -[hidden] { - display: none !important; -} - -h1, h2, h3, h4, h5, h6, -.h1, .h2, .h3, .h4, .h5, .h6 { - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; -} - -h1, .h1 { - font-size: 2.5rem; -} - -h2, .h2 { - font-size: 2rem; -} - -h3, .h3 { - font-size: 1.75rem; -} - -h4, .h4 { - font-size: 1.5rem; -} - -h5, .h5 { - font-size: 1.25rem; -} - -h6, .h6 { - font-size: 1rem; -} - -.lead { - font-size: 1.25rem; - font-weight: 300; -} - -.display-1 { - font-size: 6rem; - font-weight: 300; - line-height: 1.2; -} - -.display-2 { - font-size: 5.5rem; - font-weight: 300; - line-height: 1.2; -} - -.display-3 { - font-size: 4.5rem; - font-weight: 300; - line-height: 1.2; -} - -.display-4 { - font-size: 3.5rem; - font-weight: 300; - line-height: 1.2; -} - -hr { - margin-top: 1rem; - margin-bottom: 1rem; - border: 0; - border-top: 1px solid rgba(0, 0, 0, 0.1); -} - -small, -.small { - font-size: 80%; - font-weight: 400; -} - -mark, -.mark { - padding: 0.2em; - background-color: #fcf8e3; -} - -.list-unstyled { - padding-left: 0; - list-style: none; -} - -.list-inline { - padding-left: 0; - list-style: none; -} - -.list-inline-item { - display: inline-block; -} -.list-inline-item:not(:last-child) { - margin-right: 0.5rem; -} - -.initialism { - font-size: 90%; - text-transform: uppercase; -} - -.blockquote { - margin-bottom: 1rem; - font-size: 1.25rem; -} - -.blockquote-footer { - display: block; - font-size: 80%; - color: #6c757d; -} -.blockquote-footer::before { - content: "\2014\00A0"; -} - -.img-fluid { - max-width: 100%; - height: auto; -} - -.img-thumbnail { - padding: 0.25rem; - background-color: #fff; - border: 1px solid #dee2e6; - border-radius: 0.25rem; - max-width: 100%; - height: auto; -} - -.figure { - display: inline-block; -} - -.figure-img { - margin-bottom: 0.5rem; - line-height: 1; -} - -.figure-caption { - font-size: 90%; - color: #6c757d; -} - -code { - font-size: 87.5%; - color: #e83e8c; - word-wrap: break-word; -} -a > code { - color: inherit; -} - -kbd { - padding: 0.2rem 0.4rem; - font-size: 87.5%; - color: #fff; - background-color: #212529; - border-radius: 0.2rem; -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: 700; -} - -pre { - display: block; - font-size: 87.5%; - color: #212529; -} -pre code { - font-size: inherit; - color: inherit; - word-break: normal; -} - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} - -.container, -.container-fluid, -.container-sm, -.container-md, -.container-lg, -.container-xl { - width: 100%; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 576px) { - .container, .container-sm { - max-width: 540px; - } -} -@media (min-width: 768px) { - .container, .container-sm, .container-md { - max-width: 720px; - } -} -@media (min-width: 992px) { - .container, .container-sm, .container-md, .container-lg { - max-width: 960px; - } -} -@media (min-width: 1200px) { - .container, .container-sm, .container-md, .container-lg, .container-xl { - max-width: 1140px; - } -} -.row { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -15px; - margin-left: -15px; -} - -.no-gutters { - margin-right: 0; - margin-left: 0; -} -.no-gutters > .col, -.no-gutters > [class*="col-"] { - padding-right: 0; - padding-left: 0; -} - -.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, -.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, -.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, -.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, -.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, -.col-xl-auto { - position: relative; - width: 100%; - padding-right: 15px; - padding-left: 15px; -} - -.col { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; -} - -.row-cols-1 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.row-cols-2 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.row-cols-3 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; -} - -.row-cols-4 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.row-cols-5 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; -} - -.row-cols-6 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; -} - -.col-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; -} - -.col-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.33333333%; - flex: 0 0 8.33333333%; - max-width: 8.33333333%; -} - -.col-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.66666667%; - flex: 0 0 16.66666667%; - max-width: 16.66666667%; -} - -.col-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; -} - -.col-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.33333333%; - flex: 0 0 33.33333333%; - max-width: 33.33333333%; -} - -.col-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.66666667%; - flex: 0 0 41.66666667%; - max-width: 41.66666667%; -} - -.col-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; -} - -.col-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.33333333%; - flex: 0 0 58.33333333%; - max-width: 58.33333333%; -} - -.col-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.66666667%; - flex: 0 0 66.66666667%; - max-width: 66.66666667%; -} - -.col-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; -} - -.col-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.33333333%; - flex: 0 0 83.33333333%; - max-width: 83.33333333%; -} - -.col-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.66666667%; - flex: 0 0 91.66666667%; - max-width: 91.66666667%; -} - -.col-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; -} - -.order-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; -} - -.order-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; -} - -.order-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; -} - -.order-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; -} - -.order-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; -} - -.order-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; -} - -.order-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; -} - -.order-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; -} - -.order-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; -} - -.order-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; -} - -.order-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; -} - -.order-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; -} - -.order-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; -} - -.order-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; -} - -.order-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; -} - -.offset-1 { - margin-left: 8.33333333%; -} - -.offset-2 { - margin-left: 16.66666667%; -} - -.offset-3 { - margin-left: 25%; -} - -.offset-4 { - margin-left: 33.33333333%; -} - -.offset-5 { - margin-left: 41.66666667%; -} - -.offset-6 { - margin-left: 50%; -} - -.offset-7 { - margin-left: 58.33333333%; -} - -.offset-8 { - margin-left: 66.66666667%; -} - -.offset-9 { - margin-left: 75%; -} - -.offset-10 { - margin-left: 83.33333333%; -} - -.offset-11 { - margin-left: 91.66666667%; -} - -@media (min-width: 576px) { - .col-sm { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - - .row-cols-sm-1 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - - .row-cols-sm-2 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - - .row-cols-sm-3 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; - } - - .row-cols-sm-4 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - - .row-cols-sm-5 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - - .row-cols-sm-6 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; - } - - .col-sm-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - - .col-sm-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.33333333%; - flex: 0 0 8.33333333%; - max-width: 8.33333333%; - } - - .col-sm-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.66666667%; - flex: 0 0 16.66666667%; - max-width: 16.66666667%; - } - - .col-sm-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - - .col-sm-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.33333333%; - flex: 0 0 33.33333333%; - max-width: 33.33333333%; - } - - .col-sm-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.66666667%; - flex: 0 0 41.66666667%; - max-width: 41.66666667%; - } - - .col-sm-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - - .col-sm-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.33333333%; - flex: 0 0 58.33333333%; - max-width: 58.33333333%; - } - - .col-sm-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.66666667%; - flex: 0 0 66.66666667%; - max-width: 66.66666667%; - } - - .col-sm-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - - .col-sm-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.33333333%; - flex: 0 0 83.33333333%; - max-width: 83.33333333%; - } - - .col-sm-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.66666667%; - flex: 0 0 91.66666667%; - max-width: 91.66666667%; - } - - .col-sm-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - - .order-sm-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; - } - - .order-sm-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; - } - - .order-sm-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; - } - - .order-sm-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; - } - - .order-sm-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; - } - - .order-sm-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; - } - - .order-sm-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; - } - - .order-sm-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; - } - - .order-sm-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; - } - - .order-sm-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; - } - - .order-sm-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; - } - - .order-sm-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; - } - - .order-sm-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; - } - - .order-sm-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; - } - - .order-sm-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; - } - - .offset-sm-0 { - margin-left: 0; - } - - .offset-sm-1 { - margin-left: 8.33333333%; - } - - .offset-sm-2 { - margin-left: 16.66666667%; - } - - .offset-sm-3 { - margin-left: 25%; - } - - .offset-sm-4 { - margin-left: 33.33333333%; - } - - .offset-sm-5 { - margin-left: 41.66666667%; - } - - .offset-sm-6 { - margin-left: 50%; - } - - .offset-sm-7 { - margin-left: 58.33333333%; - } - - .offset-sm-8 { - margin-left: 66.66666667%; - } - - .offset-sm-9 { - margin-left: 75%; - } - - .offset-sm-10 { - margin-left: 83.33333333%; - } - - .offset-sm-11 { - margin-left: 91.66666667%; - } -} -@media (min-width: 768px) { - .col-md { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - - .row-cols-md-1 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - - .row-cols-md-2 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - - .row-cols-md-3 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; - } - - .row-cols-md-4 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - - .row-cols-md-5 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - - .row-cols-md-6 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; - } - - .col-md-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - - .col-md-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.33333333%; - flex: 0 0 8.33333333%; - max-width: 8.33333333%; - } - - .col-md-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.66666667%; - flex: 0 0 16.66666667%; - max-width: 16.66666667%; - } - - .col-md-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - - .col-md-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.33333333%; - flex: 0 0 33.33333333%; - max-width: 33.33333333%; - } - - .col-md-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.66666667%; - flex: 0 0 41.66666667%; - max-width: 41.66666667%; - } - - .col-md-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - - .col-md-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.33333333%; - flex: 0 0 58.33333333%; - max-width: 58.33333333%; - } - - .col-md-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.66666667%; - flex: 0 0 66.66666667%; - max-width: 66.66666667%; - } - - .col-md-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - - .col-md-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.33333333%; - flex: 0 0 83.33333333%; - max-width: 83.33333333%; - } - - .col-md-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.66666667%; - flex: 0 0 91.66666667%; - max-width: 91.66666667%; - } - - .col-md-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - - .order-md-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; - } - - .order-md-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; - } - - .order-md-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; - } - - .order-md-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; - } - - .order-md-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; - } - - .order-md-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; - } - - .order-md-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; - } - - .order-md-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; - } - - .order-md-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; - } - - .order-md-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; - } - - .order-md-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; - } - - .order-md-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; - } - - .order-md-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; - } - - .order-md-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; - } - - .order-md-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; - } - - .offset-md-0 { - margin-left: 0; - } - - .offset-md-1 { - margin-left: 8.33333333%; - } - - .offset-md-2 { - margin-left: 16.66666667%; - } - - .offset-md-3 { - margin-left: 25%; - } - - .offset-md-4 { - margin-left: 33.33333333%; - } - - .offset-md-5 { - margin-left: 41.66666667%; - } - - .offset-md-6 { - margin-left: 50%; - } - - .offset-md-7 { - margin-left: 58.33333333%; - } - - .offset-md-8 { - margin-left: 66.66666667%; - } - - .offset-md-9 { - margin-left: 75%; - } - - .offset-md-10 { - margin-left: 83.33333333%; - } - - .offset-md-11 { - margin-left: 91.66666667%; - } -} -@media (min-width: 992px) { - .col-lg { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - - .row-cols-lg-1 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - - .row-cols-lg-2 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - - .row-cols-lg-3 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; - } - - .row-cols-lg-4 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - - .row-cols-lg-5 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - - .row-cols-lg-6 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; - } - - .col-lg-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - - .col-lg-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.33333333%; - flex: 0 0 8.33333333%; - max-width: 8.33333333%; - } - - .col-lg-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.66666667%; - flex: 0 0 16.66666667%; - max-width: 16.66666667%; - } - - .col-lg-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - - .col-lg-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.33333333%; - flex: 0 0 33.33333333%; - max-width: 33.33333333%; - } - - .col-lg-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.66666667%; - flex: 0 0 41.66666667%; - max-width: 41.66666667%; - } - - .col-lg-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - - .col-lg-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.33333333%; - flex: 0 0 58.33333333%; - max-width: 58.33333333%; - } - - .col-lg-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.66666667%; - flex: 0 0 66.66666667%; - max-width: 66.66666667%; - } - - .col-lg-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - - .col-lg-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.33333333%; - flex: 0 0 83.33333333%; - max-width: 83.33333333%; - } - - .col-lg-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.66666667%; - flex: 0 0 91.66666667%; - max-width: 91.66666667%; - } - - .col-lg-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - - .order-lg-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; - } - - .order-lg-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; - } - - .order-lg-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; - } - - .order-lg-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; - } - - .order-lg-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; - } - - .order-lg-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; - } - - .order-lg-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; - } - - .order-lg-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; - } - - .order-lg-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; - } - - .order-lg-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; - } - - .order-lg-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; - } - - .order-lg-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; - } - - .order-lg-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; - } - - .order-lg-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; - } - - .order-lg-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; - } - - .offset-lg-0 { - margin-left: 0; - } - - .offset-lg-1 { - margin-left: 8.33333333%; - } - - .offset-lg-2 { - margin-left: 16.66666667%; - } - - .offset-lg-3 { - margin-left: 25%; - } - - .offset-lg-4 { - margin-left: 33.33333333%; - } - - .offset-lg-5 { - margin-left: 41.66666667%; - } - - .offset-lg-6 { - margin-left: 50%; - } - - .offset-lg-7 { - margin-left: 58.33333333%; - } - - .offset-lg-8 { - margin-left: 66.66666667%; - } - - .offset-lg-9 { - margin-left: 75%; - } - - .offset-lg-10 { - margin-left: 83.33333333%; - } - - .offset-lg-11 { - margin-left: 91.66666667%; - } -} -@media (min-width: 1200px) { - .col-xl { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - max-width: 100%; - } - - .row-cols-xl-1 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - - .row-cols-xl-2 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - - .row-cols-xl-3 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.3333333333%; - flex: 0 0 33.3333333333%; - max-width: 33.3333333333%; - } - - .row-cols-xl-4 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - - .row-cols-xl-5 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } - - .row-cols-xl-6 > * { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.6666666667%; - flex: 0 0 16.6666666667%; - max-width: 16.6666666667%; - } - - .col-xl-auto { - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - width: auto; - max-width: 100%; - } - - .col-xl-1 { - -webkit-box-flex: 0; - -ms-flex: 0 0 8.33333333%; - flex: 0 0 8.33333333%; - max-width: 8.33333333%; - } - - .col-xl-2 { - -webkit-box-flex: 0; - -ms-flex: 0 0 16.66666667%; - flex: 0 0 16.66666667%; - max-width: 16.66666667%; - } - - .col-xl-3 { - -webkit-box-flex: 0; - -ms-flex: 0 0 25%; - flex: 0 0 25%; - max-width: 25%; - } - - .col-xl-4 { - -webkit-box-flex: 0; - -ms-flex: 0 0 33.33333333%; - flex: 0 0 33.33333333%; - max-width: 33.33333333%; - } - - .col-xl-5 { - -webkit-box-flex: 0; - -ms-flex: 0 0 41.66666667%; - flex: 0 0 41.66666667%; - max-width: 41.66666667%; - } - - .col-xl-6 { - -webkit-box-flex: 0; - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - } - - .col-xl-7 { - -webkit-box-flex: 0; - -ms-flex: 0 0 58.33333333%; - flex: 0 0 58.33333333%; - max-width: 58.33333333%; - } - - .col-xl-8 { - -webkit-box-flex: 0; - -ms-flex: 0 0 66.66666667%; - flex: 0 0 66.66666667%; - max-width: 66.66666667%; - } - - .col-xl-9 { - -webkit-box-flex: 0; - -ms-flex: 0 0 75%; - flex: 0 0 75%; - max-width: 75%; - } - - .col-xl-10 { - -webkit-box-flex: 0; - -ms-flex: 0 0 83.33333333%; - flex: 0 0 83.33333333%; - max-width: 83.33333333%; - } - - .col-xl-11 { - -webkit-box-flex: 0; - -ms-flex: 0 0 91.66666667%; - flex: 0 0 91.66666667%; - max-width: 91.66666667%; - } - - .col-xl-12 { - -webkit-box-flex: 0; - -ms-flex: 0 0 100%; - flex: 0 0 100%; - max-width: 100%; - } - - .order-xl-first { - -webkit-box-ordinal-group: 0; - -ms-flex-order: -1; - order: -1; - } - - .order-xl-last { - -webkit-box-ordinal-group: 14; - -ms-flex-order: 13; - order: 13; - } - - .order-xl-0 { - -webkit-box-ordinal-group: 1; - -ms-flex-order: 0; - order: 0; - } - - .order-xl-1 { - -webkit-box-ordinal-group: 2; - -ms-flex-order: 1; - order: 1; - } - - .order-xl-2 { - -webkit-box-ordinal-group: 3; - -ms-flex-order: 2; - order: 2; - } - - .order-xl-3 { - -webkit-box-ordinal-group: 4; - -ms-flex-order: 3; - order: 3; - } - - .order-xl-4 { - -webkit-box-ordinal-group: 5; - -ms-flex-order: 4; - order: 4; - } - - .order-xl-5 { - -webkit-box-ordinal-group: 6; - -ms-flex-order: 5; - order: 5; - } - - .order-xl-6 { - -webkit-box-ordinal-group: 7; - -ms-flex-order: 6; - order: 6; - } - - .order-xl-7 { - -webkit-box-ordinal-group: 8; - -ms-flex-order: 7; - order: 7; - } - - .order-xl-8 { - -webkit-box-ordinal-group: 9; - -ms-flex-order: 8; - order: 8; - } - - .order-xl-9 { - -webkit-box-ordinal-group: 10; - -ms-flex-order: 9; - order: 9; - } - - .order-xl-10 { - -webkit-box-ordinal-group: 11; - -ms-flex-order: 10; - order: 10; - } - - .order-xl-11 { - -webkit-box-ordinal-group: 12; - -ms-flex-order: 11; - order: 11; - } - - .order-xl-12 { - -webkit-box-ordinal-group: 13; - -ms-flex-order: 12; - order: 12; - } - - .offset-xl-0 { - margin-left: 0; - } - - .offset-xl-1 { - margin-left: 8.33333333%; - } - - .offset-xl-2 { - margin-left: 16.66666667%; - } - - .offset-xl-3 { - margin-left: 25%; - } - - .offset-xl-4 { - margin-left: 33.33333333%; - } - - .offset-xl-5 { - margin-left: 41.66666667%; - } - - .offset-xl-6 { - margin-left: 50%; - } - - .offset-xl-7 { - margin-left: 58.33333333%; - } - - .offset-xl-8 { - margin-left: 66.66666667%; - } - - .offset-xl-9 { - margin-left: 75%; - } - - .offset-xl-10 { - margin-left: 83.33333333%; - } - - .offset-xl-11 { - margin-left: 91.66666667%; - } -} -.table { - width: 100%; - margin-bottom: 1rem; - color: #212529; -} -.table th, -.table td { - padding: 0.75rem; - vertical-align: top; - border-top: 1px solid #dee2e6; -} -.table thead th { - vertical-align: bottom; - border-bottom: 2px solid #dee2e6; -} -.table tbody + tbody { - border-top: 2px solid #dee2e6; -} - -.table-sm th, -.table-sm td { - padding: 0.3rem; -} - -.table-bordered { - border: 1px solid #dee2e6; -} -.table-bordered th, -.table-bordered td { - border: 1px solid #dee2e6; -} -.table-bordered thead th, -.table-bordered thead td { - border-bottom-width: 2px; -} - -.table-borderless th, -.table-borderless td, -.table-borderless thead th, -.table-borderless tbody + tbody { - border: 0; -} - -.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(0, 0, 0, 0.05); -} - -.table-hover tbody tr:hover { - color: #212529; - background-color: rgba(0, 0, 0, 0.075); -} - -.table-primary, -.table-primary > th, -.table-primary > td { - background-color: #b8daff; -} -.table-primary th, -.table-primary td, -.table-primary thead th, -.table-primary tbody + tbody { - border-color: #7abaff; -} - -.table-hover .table-primary:hover { - background-color: #9fcdff; -} -.table-hover .table-primary:hover > td, -.table-hover .table-primary:hover > th { - background-color: #9fcdff; -} - -.table-secondary, -.table-secondary > th, -.table-secondary > td { - background-color: #d6d8db; -} -.table-secondary th, -.table-secondary td, -.table-secondary thead th, -.table-secondary tbody + tbody { - border-color: #b3b7bb; -} - -.table-hover .table-secondary:hover { - background-color: #c8cbcf; -} -.table-hover .table-secondary:hover > td, -.table-hover .table-secondary:hover > th { - background-color: #c8cbcf; -} - -.table-success, -.table-success > th, -.table-success > td { - background-color: #c3e6cb; -} -.table-success th, -.table-success td, -.table-success thead th, -.table-success tbody + tbody { - border-color: #8fd19e; -} - -.table-hover .table-success:hover { - background-color: #b1dfbb; -} -.table-hover .table-success:hover > td, -.table-hover .table-success:hover > th { - background-color: #b1dfbb; -} - -.table-info, -.table-info > th, -.table-info > td { - background-color: #bee5eb; -} -.table-info th, -.table-info td, -.table-info thead th, -.table-info tbody + tbody { - border-color: #86cfda; -} - -.table-hover .table-info:hover { - background-color: #abdde5; -} -.table-hover .table-info:hover > td, -.table-hover .table-info:hover > th { - background-color: #abdde5; -} - -.table-warning, -.table-warning > th, -.table-warning > td { - background-color: #ffeeba; -} -.table-warning th, -.table-warning td, -.table-warning thead th, -.table-warning tbody + tbody { - border-color: #ffdf7e; -} - -.table-hover .table-warning:hover { - background-color: #ffe8a1; -} -.table-hover .table-warning:hover > td, -.table-hover .table-warning:hover > th { - background-color: #ffe8a1; -} - -.table-danger, -.table-danger > th, -.table-danger > td { - background-color: #f5c6cb; -} -.table-danger th, -.table-danger td, -.table-danger thead th, -.table-danger tbody + tbody { - border-color: #ed969e; -} - -.table-hover .table-danger:hover { - background-color: #f1b0b7; -} -.table-hover .table-danger:hover > td, -.table-hover .table-danger:hover > th { - background-color: #f1b0b7; -} - -.table-light, -.table-light > th, -.table-light > td { - background-color: #fdfdfe; -} -.table-light th, -.table-light td, -.table-light thead th, -.table-light tbody + tbody { - border-color: #fbfcfc; -} - -.table-hover .table-light:hover { - background-color: #ececf6; -} -.table-hover .table-light:hover > td, -.table-hover .table-light:hover > th { - background-color: #ececf6; -} - -.table-dark, -.table-dark > th, -.table-dark > td { - background-color: #c6c8ca; -} -.table-dark th, -.table-dark td, -.table-dark thead th, -.table-dark tbody + tbody { - border-color: #95999c; -} - -.table-hover .table-dark:hover { - background-color: #b9bbbe; -} -.table-hover .table-dark:hover > td, -.table-hover .table-dark:hover > th { - background-color: #b9bbbe; -} - -.table-active, -.table-active > th, -.table-active > td { - background-color: rgba(0, 0, 0, 0.075); -} - -.table-hover .table-active:hover { - background-color: rgba(0, 0, 0, 0.075); -} -.table-hover .table-active:hover > td, -.table-hover .table-active:hover > th { - background-color: rgba(0, 0, 0, 0.075); -} - -.table .thead-dark th { - color: #fff; - background-color: #343a40; - border-color: #454d55; -} -.table .thead-light th { - color: #495057; - background-color: #e9ecef; - border-color: #dee2e6; -} - -.table-dark { - color: #fff; - background-color: #343a40; -} -.table-dark th, -.table-dark td, -.table-dark thead th { - border-color: #454d55; -} -.table-dark.table-bordered { - border: 0; -} -.table-dark.table-striped tbody tr:nth-of-type(odd) { - background-color: rgba(255, 255, 255, 0.05); -} -.table-dark.table-hover tbody tr:hover { - color: #fff; - background-color: rgba(255, 255, 255, 0.075); -} - -@media (max-width: 575.98px) { - .table-responsive-sm { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-sm > .table-bordered { - border: 0; - } -} -@media (max-width: 767.98px) { - .table-responsive-md { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-md > .table-bordered { - border: 0; - } -} -@media (max-width: 991.98px) { - .table-responsive-lg { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-lg > .table-bordered { - border: 0; - } -} -@media (max-width: 1199.98px) { - .table-responsive-xl { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .table-responsive-xl > .table-bordered { - border: 0; - } -} -.table-responsive { - display: block; - width: 100%; - overflow-x: auto; - -webkit-overflow-scrolling: touch; -} -.table-responsive > .table-bordered { - border: 0; -} - -.form-control { - display: block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #ced4da; - border-radius: 0.25rem; - -webkit-transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} -@media (prefers-reduced-motion: reduce) { - .form-control { - -webkit-transition: none; - transition: none; - } -} -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} -.form-control:focus { - color: #495057; - background-color: #fff; - border-color: #80bdff; - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} -.form-control::-webkit-input-placeholder { - color: #6c757d; - opacity: 1; -} -.form-control::-moz-placeholder { - color: #6c757d; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #6c757d; - opacity: 1; -} -.form-control::-ms-input-placeholder { - color: #6c757d; - opacity: 1; -} -.form-control::placeholder { - color: #6c757d; - opacity: 1; -} -.form-control:disabled, .form-control[readonly] { - background-color: #e9ecef; - opacity: 1; -} - -input[type="date"].form-control, -input[type="time"].form-control, -input[type="datetime-local"].form-control, -input[type="month"].form-control { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} - -select.form-control:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 #495057; -} -select.form-control:focus::-ms-value { - color: #495057; - background-color: #fff; -} - -.form-control-file, -.form-control-range { - display: block; - width: 100%; -} - -.col-form-label { - padding-top: calc(0.375rem + 1px); - padding-bottom: calc(0.375rem + 1px); - margin-bottom: 0; - font-size: inherit; - line-height: 1.5; -} - -.col-form-label-lg { - padding-top: calc(0.5rem + 1px); - padding-bottom: calc(0.5rem + 1px); - font-size: 1.25rem; - line-height: 1.5; -} - -.col-form-label-sm { - padding-top: calc(0.25rem + 1px); - padding-bottom: calc(0.25rem + 1px); - font-size: 0.875rem; - line-height: 1.5; -} - -.form-control-plaintext { - display: block; - width: 100%; - padding: 0.375rem 0; - margin-bottom: 0; - font-size: 1rem; - line-height: 1.5; - color: #212529; - background-color: transparent; - border: solid transparent; - border-width: 1px 0; -} -.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { - padding-right: 0; - padding-left: 0; -} - -.form-control-sm { - height: calc(1.5em + 0.5rem + 2px); - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.form-control-lg { - height: calc(1.5em + 1rem + 2px); - padding: 0.5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -select.form-control[size], select.form-control[multiple] { - height: auto; -} - -textarea.form-control { - height: auto; -} - -.form-group { - margin-bottom: 1rem; -} - -.form-text { - display: block; - margin-top: 0.25rem; -} - -.form-row { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -5px; - margin-left: -5px; -} -.form-row > .col, -.form-row > [class*="col-"] { - padding-right: 5px; - padding-left: 5px; -} - -.form-check { - position: relative; - display: block; - padding-left: 1.25rem; -} - -.form-check-input { - position: absolute; - margin-top: 0.3rem; - margin-left: -1.25rem; -} -.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { - color: #6c757d; -} - -.form-check-label { - margin-bottom: 0; -} - -.form-check-inline { - display: -webkit-inline-box; - display: -ms-inline-flexbox; - display: inline-flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding-left: 0; - margin-right: 0.75rem; -} -.form-check-inline .form-check-input { - position: static; - margin-top: 0; - margin-right: 0.3125rem; - margin-left: 0; -} - -.valid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #28a745; -} - -.valid-tooltip { - position: absolute; - top: 100%; - left: 0; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: .1rem; - font-size: 0.875rem; - line-height: 1.5; - color: #fff; - background-color: rgba(40, 167, 69, 0.9); - border-radius: 0.25rem; -} -.form-row > .col > .valid-tooltip, .form-row > [class*="col-"] > .valid-tooltip { - left: 5px; -} - -.was-validated :valid ~ .valid-feedback, -.was-validated :valid ~ .valid-tooltip, -.is-valid ~ .valid-feedback, -.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .form-control:valid, .form-control.is-valid { - border-color: #28a745; - padding-right: calc(1.5em + 0.75rem) !important; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} -.was-validated .form-control:valid:focus, .form-control.is-valid:focus { - border-color: #28a745; - -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.was-validated select.form-control:valid, select.form-control.is-valid { - padding-right: 3rem !important; - background-position: right 1.5rem center; -} - -.was-validated textarea.form-control:valid, textarea.form-control.is-valid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); -} - -.was-validated .custom-select:valid, .custom-select.is-valid { - border-color: #28a745; - padding-right: calc(0.75em + 2.3125rem) !important; - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 0.75rem center/8px 10px no-repeat, #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat; -} -.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { - border-color: #28a745; - -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: #28a745; -} -.was-validated .form-check-input:valid ~ .valid-feedback, -.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, -.form-check-input.is-valid ~ .valid-tooltip { - display: block; -} - -.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { - color: #28a745; -} -.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { - border-color: #28a745; -} -.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { - border-color: #34ce57; - background-color: #34ce57; -} -.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { - -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} -.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { - border-color: #28a745; -} - -.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { - border-color: #28a745; -} -.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { - border-color: #28a745; - -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); -} - -.invalid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #dc3545; -} - -.invalid-tooltip { - position: absolute; - top: 100%; - left: 0; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: .1rem; - font-size: 0.875rem; - line-height: 1.5; - color: #fff; - background-color: rgba(220, 53, 69, 0.9); - border-radius: 0.25rem; -} -.form-row > .col > .invalid-tooltip, .form-row > [class*="col-"] > .invalid-tooltip { - left: 5px; -} - -.was-validated :invalid ~ .invalid-feedback, -.was-validated :invalid ~ .invalid-tooltip, -.is-invalid ~ .invalid-feedback, -.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .form-control:invalid, .form-control.is-invalid { - border-color: #dc3545; - padding-right: calc(1.5em + 0.75rem) !important; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); -} -.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { - border-color: #dc3545; - -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.was-validated select.form-control:invalid, select.form-control.is-invalid { - padding-right: 3rem !important; - background-position: right 1.5rem center; -} - -.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); -} - -.was-validated .custom-select:invalid, .custom-select.is-invalid { - border-color: #dc3545; - padding-right: calc(0.75em + 2.3125rem) !important; - background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 0.75rem center/8px 10px no-repeat, #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) no-repeat; -} -.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { - border-color: #dc3545; - -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: #dc3545; -} -.was-validated .form-check-input:invalid ~ .invalid-feedback, -.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, -.form-check-input.is-invalid ~ .invalid-tooltip { - display: block; -} - -.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { - color: #dc3545; -} -.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { - border-color: #dc3545; -} -.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { - border-color: #e4606d; - background-color: #e4606d; -} -.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { - -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} -.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { - border-color: #dc3545; -} - -.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { - border-color: #dc3545; -} -.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { - border-color: #dc3545; - -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); -} - -.form-inline { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} -.form-inline .form-check { - width: 100%; -} -@media (min-width: 576px) { - .form-inline label { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - margin-bottom: 0; - } - .form-inline .form-group { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-flex: 0; - -ms-flex: 0 0 auto; - flex: 0 0 auto; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - margin-bottom: 0; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-plaintext { - display: inline-block; - } - .form-inline .input-group, - .form-inline .custom-select { - width: auto; - } - .form-inline .form-check { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - width: auto; - padding-left: 0; - } - .form-inline .form-check-input { - position: relative; - -ms-flex-negative: 0; - flex-shrink: 0; - margin-top: 0; - margin-right: 0.25rem; - margin-left: 0; - } - .form-inline .custom-control { - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - } - .form-inline .custom-control-label { - margin-bottom: 0; - } -} - -.btn { - display: inline-block; - font-weight: 400; - color: #212529; - text-align: center; - vertical-align: middle; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-color: transparent; - border: 1px solid transparent; - padding: 0.375rem 0.75rem; - font-size: 1rem; - line-height: 1.5; - border-radius: 0.25rem; - -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} -@media (prefers-reduced-motion: reduce) { - .btn { - -webkit-transition: none; - transition: none; - } -} -.btn:hover { - color: #212529; - text-decoration: none; -} -.btn:focus, .btn.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} -.btn.disabled, .btn:disabled { - opacity: 0.65; -} -.btn:not(:disabled):not(.disabled) { - cursor: pointer; -} - -a.btn.disabled, -fieldset:disabled a.btn { - pointer-events: none; -} - -.btn-primary { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} -.btn-primary:hover { - color: #fff; - background-color: #0069d9; - border-color: #0062cc; -} -.btn-primary:focus, .btn-primary.focus { - color: #fff; - background-color: #0069d9; - border-color: #0062cc; - -webkit-box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); - box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); -} -.btn-primary.disabled, .btn-primary:disabled { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} -.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, .show > .btn-primary.dropdown-toggle { - color: #fff; - background-color: #0062cc; - border-color: #005cbf; -} -.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-primary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); - box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); -} - -.btn-secondary { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} -.btn-secondary:hover { - color: #fff; - background-color: #5a6268; - border-color: #545b62; -} -.btn-secondary:focus, .btn-secondary.focus { - color: #fff; - background-color: #5a6268; - border-color: #545b62; - -webkit-box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); - box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); -} -.btn-secondary.disabled, .btn-secondary:disabled { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} -.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, .show > .btn-secondary.dropdown-toggle { - color: #fff; - background-color: #545b62; - border-color: #4e555b; -} -.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-secondary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); - box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); -} - -.btn-success { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} -.btn-success:hover { - color: #fff; - background-color: #218838; - border-color: #1e7e34; -} -.btn-success:focus, .btn-success.focus { - color: #fff; - background-color: #218838; - border-color: #1e7e34; - -webkit-box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); - box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); -} -.btn-success.disabled, .btn-success:disabled { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} -.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, .show > .btn-success.dropdown-toggle { - color: #fff; - background-color: #1e7e34; - border-color: #1c7430; -} -.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, .show > .btn-success.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); - box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); -} - -.btn-info { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} -.btn-info:hover { - color: #fff; - background-color: #138496; - border-color: #117a8b; -} -.btn-info:focus, .btn-info.focus { - color: #fff; - background-color: #138496; - border-color: #117a8b; - -webkit-box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); - box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); -} -.btn-info.disabled, .btn-info:disabled { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} -.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, .show > .btn-info.dropdown-toggle { - color: #fff; - background-color: #117a8b; - border-color: #10707f; -} -.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, .show > .btn-info.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); - box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); -} - -.btn-warning { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} -.btn-warning:hover { - color: #212529; - background-color: #e0a800; - border-color: #d39e00; -} -.btn-warning:focus, .btn-warning.focus { - color: #212529; - background-color: #e0a800; - border-color: #d39e00; - -webkit-box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); - box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); -} -.btn-warning.disabled, .btn-warning:disabled { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} -.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, .show > .btn-warning.dropdown-toggle { - color: #212529; - background-color: #d39e00; - border-color: #c69500; -} -.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-warning.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); - box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); -} - -.btn-danger { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} -.btn-danger:hover { - color: #fff; - background-color: #c82333; - border-color: #bd2130; -} -.btn-danger:focus, .btn-danger.focus { - color: #fff; - background-color: #c82333; - border-color: #bd2130; - -webkit-box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); - box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); -} -.btn-danger.disabled, .btn-danger:disabled { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} -.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, .show > .btn-danger.dropdown-toggle { - color: #fff; - background-color: #bd2130; - border-color: #b21f2d; -} -.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-danger.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); - box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); -} - -.btn-light { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} -.btn-light:hover { - color: #212529; - background-color: #e2e6ea; - border-color: #dae0e5; -} -.btn-light:focus, .btn-light.focus { - color: #212529; - background-color: #e2e6ea; - border-color: #dae0e5; - -webkit-box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); - box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); -} -.btn-light.disabled, .btn-light:disabled { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} -.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, .show > .btn-light.dropdown-toggle { - color: #212529; - background-color: #dae0e5; - border-color: #d3d9df; -} -.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, .show > .btn-light.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); - box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); -} - -.btn-dark { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} -.btn-dark:hover { - color: #fff; - background-color: #23272b; - border-color: #1d2124; -} -.btn-dark:focus, .btn-dark.focus { - color: #fff; - background-color: #23272b; - border-color: #1d2124; - -webkit-box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); - box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); -} -.btn-dark.disabled, .btn-dark:disabled { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} -.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, .show > .btn-dark.dropdown-toggle { - color: #fff; - background-color: #1d2124; - border-color: #171a1d; -} -.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-dark.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); - box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); -} - -.btn-outline-primary { - color: #007bff; - border-color: #007bff; -} -.btn-outline-primary:hover { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} -.btn-outline-primary:focus, .btn-outline-primary.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} -.btn-outline-primary.disabled, .btn-outline-primary:disabled { - color: #007bff; - background-color: transparent; -} -.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, .show > .btn-outline-primary.dropdown-toggle { - color: #fff; - background-color: #007bff; - border-color: #007bff; -} -.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.btn-outline-secondary { - color: #6c757d; - border-color: #6c757d; -} -.btn-outline-secondary:hover { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} -.btn-outline-secondary:focus, .btn-outline-secondary.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} -.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { - color: #6c757d; - background-color: transparent; -} -.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, .show > .btn-outline-secondary.dropdown-toggle { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; -} -.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.btn-outline-success { - color: #28a745; - border-color: #28a745; -} -.btn-outline-success:hover { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} -.btn-outline-success:focus, .btn-outline-success.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} -.btn-outline-success.disabled, .btn-outline-success:disabled { - color: #28a745; - background-color: transparent; -} -.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, .show > .btn-outline-success.dropdown-toggle { - color: #fff; - background-color: #28a745; - border-color: #28a745; -} -.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-success.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.btn-outline-info { - color: #17a2b8; - border-color: #17a2b8; -} -.btn-outline-info:hover { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} -.btn-outline-info:focus, .btn-outline-info.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} -.btn-outline-info.disabled, .btn-outline-info:disabled { - color: #17a2b8; - background-color: transparent; -} -.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, .show > .btn-outline-info.dropdown-toggle { - color: #fff; - background-color: #17a2b8; - border-color: #17a2b8; -} -.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-info.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.btn-outline-warning { - color: #ffc107; - border-color: #ffc107; -} -.btn-outline-warning:hover { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} -.btn-outline-warning:focus, .btn-outline-warning.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} -.btn-outline-warning.disabled, .btn-outline-warning:disabled { - color: #ffc107; - background-color: transparent; -} -.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, .show > .btn-outline-warning.dropdown-toggle { - color: #212529; - background-color: #ffc107; - border-color: #ffc107; -} -.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.btn-outline-danger { - color: #dc3545; - border-color: #dc3545; -} -.btn-outline-danger:hover { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} -.btn-outline-danger:focus, .btn-outline-danger.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} -.btn-outline-danger.disabled, .btn-outline-danger:disabled { - color: #dc3545; - background-color: transparent; -} -.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, .show > .btn-outline-danger.dropdown-toggle { - color: #fff; - background-color: #dc3545; - border-color: #dc3545; -} -.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.btn-outline-light { - color: #f8f9fa; - border-color: #f8f9fa; -} -.btn-outline-light:hover { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} -.btn-outline-light:focus, .btn-outline-light.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} -.btn-outline-light.disabled, .btn-outline-light:disabled { - color: #f8f9fa; - background-color: transparent; -} -.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, .show > .btn-outline-light.dropdown-toggle { - color: #212529; - background-color: #f8f9fa; - border-color: #f8f9fa; -} -.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-light.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.btn-outline-dark { - color: #343a40; - border-color: #343a40; -} -.btn-outline-dark:hover { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} -.btn-outline-dark:focus, .btn-outline-dark.focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} -.btn-outline-dark.disabled, .btn-outline-dark:disabled { - color: #343a40; - background-color: transparent; -} -.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, .show > .btn-outline-dark.dropdown-toggle { - color: #fff; - background-color: #343a40; - border-color: #343a40; -} -.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { - -webkit-box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.btn-link { - font-weight: 400; - color: #007bff; - text-decoration: none; -} -.btn-link:hover { - color: #0056b3; - text-decoration: underline; -} -.btn-link:focus, .btn-link.focus { - text-decoration: underline; -} -.btn-link:disabled, .btn-link.disabled { - color: #6c757d; - pointer-events: none; -} - -.btn-lg, .btn-group-lg > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -.btn-sm, .btn-group-sm > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 0.5rem; -} - -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} - -.fade { - -webkit-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; -} -@media (prefers-reduced-motion: reduce) { - .fade { - -webkit-transition: none; - transition: none; - } -} -.fade:not(.show) { - opacity: 0; -} - -.collapse:not(.show) { - display: none; -} - -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height 0.35s ease; - transition: height 0.35s ease; -} -@media (prefers-reduced-motion: reduce) { - .collapsing { - -webkit-transition: none; - transition: none; - } -} - -.dropup, -.dropright, -.dropdown, -.dropleft { - position: relative; -} - -.dropdown-toggle { - white-space: nowrap; -} -.dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid; - border-right: 0.3em solid transparent; - border-bottom: 0; - border-left: 0.3em solid transparent; -} -.dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 10rem; - padding: 0.5rem 0; - margin: 0.125rem 0 0; - font-size: 1rem; - color: #212529; - text-align: left; - list-style: none; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 0.25rem; -} - -.dropdown-menu-left { - right: auto; - left: 0; -} - -.dropdown-menu-right { - right: 0; - left: auto; -} - -@media (min-width: 576px) { - .dropdown-menu-sm-left { - right: auto; - left: 0; - } - - .dropdown-menu-sm-right { - right: 0; - left: auto; - } -} -@media (min-width: 768px) { - .dropdown-menu-md-left { - right: auto; - left: 0; - } - - .dropdown-menu-md-right { - right: 0; - left: auto; - } -} -@media (min-width: 992px) { - .dropdown-menu-lg-left { - right: auto; - left: 0; - } - - .dropdown-menu-lg-right { - right: 0; - left: auto; - } -} -@media (min-width: 1200px) { - .dropdown-menu-xl-left { - right: auto; - left: 0; - } - - .dropdown-menu-xl-right { - right: 0; - left: auto; - } -} -.dropup .dropdown-menu { - top: auto; - bottom: 100%; - margin-top: 0; - margin-bottom: 0.125rem; -} -.dropup .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0; - border-right: 0.3em solid transparent; - border-bottom: 0.3em solid; - border-left: 0.3em solid transparent; -} -.dropup .dropdown-toggle:empty::after { - margin-left: 0; -} - -.dropright .dropdown-menu { - top: 0; - right: auto; - left: 100%; - margin-top: 0; - margin-left: 0.125rem; -} -.dropright .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0; - border-bottom: 0.3em solid transparent; - border-left: 0.3em solid; -} -.dropright .dropdown-toggle:empty::after { - margin-left: 0; -} -.dropright .dropdown-toggle::after { - vertical-align: 0; -} - -.dropleft .dropdown-menu { - top: 0; - right: 100%; - left: auto; - margin-top: 0; - margin-right: 0.125rem; -} -.dropleft .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; -} -.dropleft .dropdown-toggle::after { - display: none; -} -.dropleft .dropdown-toggle::before { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0.3em solid; - border-bottom: 0.3em solid transparent; -} -.dropleft .dropdown-toggle:empty::after { - margin-left: 0; -} -.dropleft .dropdown-toggle::before { - vertical-align: 0; -} - -.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { - right: auto; - bottom: auto; -} - -.dropdown-divider { - height: 0; - margin: 0.5rem 0; - overflow: hidden; - border-top: 1px solid #e9ecef; -} - -.dropdown-item { - display: block; - width: 100%; - padding: 0.25rem 1.5rem; - clear: both; - font-weight: 400; - color: #212529; - text-align: inherit; - white-space: nowrap; - background-color: transparent; - border: 0; -} -.dropdown-item:hover, .dropdown-item:focus { - color: #16181b; - text-decoration: none; - background-color: #e9ecef; -} -.dropdown-item.active, .dropdown-item:active { - color: #fff; - text-decoration: none; - background-color: #007bff; -} -.dropdown-item.disabled, .dropdown-item:disabled { - color: #adb5bd; - pointer-events: none; - background-color: transparent; -} - -.dropdown-menu.show { - display: block; -} - -.dropdown-header { - display: block; - padding: 0.5rem 1.5rem; - margin-bottom: 0; - font-size: 0.875rem; - color: #6c757d; - white-space: nowrap; -} - -.dropdown-item-text { - display: block; - padding: 0.25rem 1.5rem; - color: #212529; -} - -.btn-group, -.btn-group-vertical { - position: relative; - display: -webkit-inline-box; - display: -ms-inline-flexbox; - display: inline-flex; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover { - z-index: 1; -} -.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, -.btn-group-vertical > .btn:focus, -.btn-group-vertical > .btn:active, -.btn-group-vertical > .btn.active { - z-index: 1; -} - -.btn-toolbar { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; -} -.btn-toolbar .input-group { - width: auto; -} - -.btn-group > .btn:not(:first-child), -.btn-group > .btn-group:not(:first-child) { - margin-left: -1px; -} -.btn-group > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:not(:first-child), -.btn-group > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.dropdown-toggle-split { - padding-right: 0.5625rem; - padding-left: 0.5625rem; -} -.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropright .dropdown-toggle-split::after { - margin-left: 0; -} -.dropleft .dropdown-toggle-split::before { - margin-right: 0; -} - -.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { - padding-right: 0.375rem; - padding-left: 0.375rem; -} - -.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { - padding-right: 0.75rem; - padding-left: 0.75rem; -} - -.btn-group-vertical { - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: flex-start; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group { - width: 100%; -} -.btn-group-vertical > .btn:not(:first-child), -.btn-group-vertical > .btn-group:not(:first-child) { - margin-top: -1px; -} -.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group-vertical > .btn-group:not(:last-child) > .btn { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:not(:first-child), -.btn-group-vertical > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.btn-group-toggle > .btn, -.btn-group-toggle > .btn-group > .btn { - margin-bottom: 0; -} -.btn-group-toggle > .btn input[type="radio"], -.btn-group-toggle > .btn input[type="checkbox"], -.btn-group-toggle > .btn-group > .btn input[type="radio"], -.btn-group-toggle > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} - -.input-group { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-align: stretch; - -ms-flex-align: stretch; - align-items: stretch; - width: 100%; -} -.input-group > .form-control, -.input-group > .form-control-plaintext, -.input-group > .custom-select, -.input-group > .custom-file { - position: relative; - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - width: 1%; - min-width: 0; - margin-bottom: 0; -} -.input-group > .form-control + .form-control, -.input-group > .form-control + .custom-select, -.input-group > .form-control + .custom-file, -.input-group > .form-control-plaintext + .form-control, -.input-group > .form-control-plaintext + .custom-select, -.input-group > .form-control-plaintext + .custom-file, -.input-group > .custom-select + .form-control, -.input-group > .custom-select + .custom-select, -.input-group > .custom-select + .custom-file, -.input-group > .custom-file + .form-control, -.input-group > .custom-file + .custom-select, -.input-group > .custom-file + .custom-file { - margin-left: -1px; -} -.input-group > .form-control:focus, -.input-group > .custom-select:focus, -.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { - z-index: 3; -} -.input-group > .custom-file .custom-file-input:focus { - z-index: 4; -} -.input-group > .form-control:not(:first-child), -.input-group > .custom-select:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group > .custom-file { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} -.input-group > .custom-file:not(:last-child) .custom-file-label, .input-group > .custom-file:not(:last-child) .custom-file-label::after { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group > .custom-file:not(:first-child) .custom-file-label { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group:not(.has-validation) > .form-control:not(:last-child), -.input-group:not(.has-validation) > .custom-select:not(:last-child), -.input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label, -.input-group:not(.has-validation) > .custom-file:not(:last-child) .custom-file-label::after { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group.has-validation > .form-control:nth-last-child(n + 3), -.input-group.has-validation > .custom-select:nth-last-child(n + 3), -.input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label, -.input-group.has-validation > .custom-file:nth-last-child(n + 3) .custom-file-label::after { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group-prepend, -.input-group-append { - display: -webkit-box; - display: -ms-flexbox; - display: flex; -} -.input-group-prepend .btn, -.input-group-append .btn { - position: relative; - z-index: 2; -} -.input-group-prepend .btn:focus, -.input-group-append .btn:focus { - z-index: 3; -} -.input-group-prepend .btn + .btn, -.input-group-prepend .btn + .input-group-text, -.input-group-prepend .input-group-text + .input-group-text, -.input-group-prepend .input-group-text + .btn, -.input-group-append .btn + .btn, -.input-group-append .btn + .input-group-text, -.input-group-append .input-group-text + .input-group-text, -.input-group-append .input-group-text + .btn { - margin-left: -1px; -} - -.input-group-prepend { - margin-right: -1px; -} - -.input-group-append { - margin-left: -1px; -} - -.input-group-text { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding: 0.375rem 0.75rem; - margin-bottom: 0; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - text-align: center; - white-space: nowrap; - background-color: #e9ecef; - border: 1px solid #ced4da; - border-radius: 0.25rem; -} -.input-group-text input[type="radio"], -.input-group-text input[type="checkbox"] { - margin-top: 0; -} - -.input-group-lg > .form-control:not(textarea), -.input-group-lg > .custom-select { - height: calc(1.5em + 1rem + 2px); -} - -.input-group-lg > .form-control, -.input-group-lg > .custom-select, -.input-group-lg > .input-group-prepend > .input-group-text, -.input-group-lg > .input-group-append > .input-group-text, -.input-group-lg > .input-group-prepend > .btn, -.input-group-lg > .input-group-append > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - line-height: 1.5; - border-radius: 0.3rem; -} - -.input-group-sm > .form-control:not(textarea), -.input-group-sm > .custom-select { - height: calc(1.5em + 0.5rem + 2px); -} - -.input-group-sm > .form-control, -.input-group-sm > .custom-select, -.input-group-sm > .input-group-prepend > .input-group-text, -.input-group-sm > .input-group-append > .input-group-text, -.input-group-sm > .input-group-prepend > .btn, -.input-group-sm > .input-group-append > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; - border-radius: 0.2rem; -} - -.input-group-lg > .custom-select, -.input-group-sm > .custom-select { - padding-right: 1.75rem; -} - -.input-group > .input-group-prepend > .btn, -.input-group > .input-group-prepend > .input-group-text, -.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .btn, -.input-group:not(.has-validation) > .input-group-append:not(:last-child) > .input-group-text, -.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .btn, -.input-group.has-validation > .input-group-append:nth-last-child(n + 3) > .input-group-text, -.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} - -.input-group > .input-group-append > .btn, -.input-group > .input-group-append > .input-group-text, -.input-group > .input-group-prepend:not(:first-child) > .btn, -.input-group > .input-group-prepend:not(:first-child) > .input-group-text, -.input-group > .input-group-prepend:first-child > .btn:not(:first-child), -.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -.custom-control { - position: relative; - z-index: 1; - display: block; - min-height: 1.5rem; - padding-left: 1.5rem; - -webkit-print-color-adjust: exact; - color-adjust: exact; -} - -.custom-control-inline { - display: -webkit-inline-box; - display: -ms-inline-flexbox; - display: inline-flex; - margin-right: 1rem; -} - -.custom-control-input { - position: absolute; - left: 0; - z-index: -1; - width: 1rem; - height: 1.25rem; - opacity: 0; -} -.custom-control-input:checked ~ .custom-control-label::before { - color: #fff; - border-color: #007bff; - background-color: #007bff; -} -.custom-control-input:focus ~ .custom-control-label::before { - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} -.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { - border-color: #80bdff; -} -.custom-control-input:not(:disabled):active ~ .custom-control-label::before { - color: #fff; - background-color: #b3d7ff; - border-color: #b3d7ff; -} -.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { - color: #6c757d; -} -.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { - background-color: #e9ecef; -} - -.custom-control-label { - position: relative; - margin-bottom: 0; - vertical-align: top; -} -.custom-control-label::before { - position: absolute; - top: 0.25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - pointer-events: none; - content: ""; - background-color: #fff; - border: #adb5bd solid 1px; -} -.custom-control-label::after { - position: absolute; - top: 0.25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - content: ""; - background: 50% / 50% 50% no-repeat; -} - -.custom-checkbox .custom-control-label::before { - border-radius: 0.25rem; -} -.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); -} -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { - border-color: #007bff; - background-color: #007bff; -} -.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); -} -.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} -.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-radio .custom-control-label::before { - border-radius: 50%; -} -.custom-radio .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); -} -.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-switch { - padding-left: 2.25rem; -} -.custom-switch .custom-control-label::before { - left: -2.25rem; - width: 1.75rem; - pointer-events: all; - border-radius: 0.5rem; -} -.custom-switch .custom-control-label::after { - top: calc(0.25rem + 2px); - left: calc(-2.25rem + 2px); - width: calc(1rem - 4px); - height: calc(1rem - 4px); - background-color: #adb5bd; - border-radius: 0.5rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} -@media (prefers-reduced-motion: reduce) { - .custom-switch .custom-control-label::after { - -webkit-transition: none; - transition: none; - } -} -.custom-switch .custom-control-input:checked ~ .custom-control-label::after { - background-color: #fff; - -webkit-transform: translateX(0.75rem); - transform: translateX(0.75rem); -} -.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { - background-color: rgba(0, 123, 255, 0.5); -} - -.custom-select { - display: inline-block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 1.75rem 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - vertical-align: middle; - background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") right 0.75rem center/8px 10px no-repeat; - border: 1px solid #ced4da; - border-radius: 0.25rem; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} -.custom-select:focus { - border-color: #80bdff; - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} -.custom-select:focus::-ms-value { - color: #495057; - background-color: #fff; -} -.custom-select[multiple], .custom-select[size]:not([size="1"]) { - height: auto; - padding-right: 0.75rem; - background-image: none; -} -.custom-select:disabled { - color: #6c757d; - background-color: #e9ecef; -} -.custom-select::-ms-expand { - display: none; -} -.custom-select:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 #495057; -} - -.custom-select-sm { - height: calc(1.5em + 0.5rem + 2px); - padding-top: 0.25rem; - padding-bottom: 0.25rem; - padding-left: 0.5rem; - font-size: 0.875rem; -} - -.custom-select-lg { - height: calc(1.5em + 1rem + 2px); - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - font-size: 1.25rem; -} - -.custom-file { - position: relative; - display: inline-block; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - margin-bottom: 0; -} - -.custom-file-input { - position: relative; - z-index: 2; - width: 100%; - height: calc(1.5em + 0.75rem + 2px); - margin: 0; - overflow: hidden; - opacity: 0; -} -.custom-file-input:focus ~ .custom-file-label { - border-color: #80bdff; - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} -.custom-file-input[disabled] ~ .custom-file-label, .custom-file-input:disabled ~ .custom-file-label { - background-color: #e9ecef; -} -.custom-file-input:lang(en) ~ .custom-file-label::after { - content: "Browse"; -} -.custom-file-input ~ .custom-file-label[data-browse]::after { - content: attr(data-browse); -} - -.custom-file-label { - position: absolute; - top: 0; - right: 0; - left: 0; - z-index: 1; - height: calc(1.5em + 0.75rem + 2px); - padding: 0.375rem 0.75rem; - overflow: hidden; - font-weight: 400; - line-height: 1.5; - color: #495057; - background-color: #fff; - border: 1px solid #ced4da; - border-radius: 0.25rem; -} -.custom-file-label::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - z-index: 3; - display: block; - height: calc(1.5em + 0.75rem); - padding: 0.375rem 0.75rem; - line-height: 1.5; - color: #495057; - content: "Browse"; - background-color: #e9ecef; - border-left: inherit; - border-radius: 0 0.25rem 0.25rem 0; -} - -.custom-range { - width: 100%; - height: 1.4rem; - padding: 0; - background-color: transparent; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -} -.custom-range:focus { - outline: 0; -} -.custom-range:focus::-webkit-slider-thumb { - -webkit-box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} -.custom-range:focus::-moz-range-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} -.custom-range:focus::-ms-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} -.custom-range::-moz-focus-outer { - border: 0; -} -.custom-range::-webkit-slider-thumb { - width: 1rem; - height: 1rem; - margin-top: -0.25rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - -webkit-appearance: none; - appearance: none; -} -@media (prefers-reduced-motion: reduce) { - .custom-range::-webkit-slider-thumb { - -webkit-transition: none; - transition: none; - } -} -.custom-range::-webkit-slider-thumb:active { - background-color: #b3d7ff; -} -.custom-range::-webkit-slider-runnable-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; -} -.custom-range::-moz-range-thumb { - width: 1rem; - height: 1rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - -moz-appearance: none; - appearance: none; -} -@media (prefers-reduced-motion: reduce) { - .custom-range::-moz-range-thumb { - -moz-transition: none; - transition: none; - } -} -.custom-range::-moz-range-thumb:active { - background-color: #b3d7ff; -} -.custom-range::-moz-range-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; -} -.custom-range::-ms-thumb { - width: 1rem; - height: 1rem; - margin-top: 0; - margin-right: 0.2rem; - margin-left: 0.2rem; - background-color: #007bff; - border: 0; - border-radius: 1rem; - -ms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - appearance: none; -} -@media (prefers-reduced-motion: reduce) { - .custom-range::-ms-thumb { - -ms-transition: none; - transition: none; - } -} -.custom-range::-ms-thumb:active { - background-color: #b3d7ff; -} -.custom-range::-ms-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: transparent; - border-color: transparent; - border-width: 0.5rem; -} -.custom-range::-ms-fill-lower { - background-color: #dee2e6; - border-radius: 1rem; -} -.custom-range::-ms-fill-upper { - margin-right: 15px; - background-color: #dee2e6; - border-radius: 1rem; -} -.custom-range:disabled::-webkit-slider-thumb { - background-color: #adb5bd; -} -.custom-range:disabled::-webkit-slider-runnable-track { - cursor: default; -} -.custom-range:disabled::-moz-range-thumb { - background-color: #adb5bd; -} -.custom-range:disabled::-moz-range-track { - cursor: default; -} -.custom-range:disabled::-ms-thumb { - background-color: #adb5bd; -} - -.custom-control-label::before, -.custom-file-label, -.custom-select { - -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} -@media (prefers-reduced-motion: reduce) { - .custom-control-label::before, - .custom-file-label, - .custom-select { - -webkit-transition: none; - transition: none; - } -} - -.nav { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} - -.nav-link { - display: block; - padding: 0.5rem 1rem; -} -.nav-link:hover, .nav-link:focus { - text-decoration: none; -} -.nav-link.disabled { - color: #6c757d; - pointer-events: none; - cursor: default; -} - -.nav-tabs { - border-bottom: 1px solid #dee2e6; -} -.nav-tabs .nav-link { - margin-bottom: -1px; - border: 1px solid transparent; - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} -.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { - border-color: #e9ecef #e9ecef #dee2e6; -} -.nav-tabs .nav-link.disabled { - color: #6c757d; - background-color: transparent; - border-color: transparent; -} -.nav-tabs .nav-link.active, -.nav-tabs .nav-item.show .nav-link { - color: #495057; - background-color: #fff; - border-color: #dee2e6 #dee2e6 #fff; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -.nav-pills .nav-link { - border-radius: 0.25rem; -} -.nav-pills .nav-link.active, -.nav-pills .show > .nav-link { - color: #fff; - background-color: #007bff; -} - -.nav-fill > .nav-link, -.nav-fill .nav-item { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - text-align: center; -} - -.nav-justified > .nav-link, -.nav-justified .nav-item { - -ms-flex-preferred-size: 0; - flex-basis: 0; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - text-align: center; -} - -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} - -.navbar { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: justify; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 0.5rem 1rem; -} -.navbar .container, -.navbar .container-fluid, -.navbar .container-sm, -.navbar .container-md, -.navbar .container-lg, -.navbar .container-xl { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: justify; - -ms-flex-pack: justify; - justify-content: space-between; -} - -.navbar-brand { - display: inline-block; - padding-top: 0.3125rem; - padding-bottom: 0.3125rem; - margin-right: 1rem; - font-size: 1.25rem; - line-height: inherit; - white-space: nowrap; -} -.navbar-brand:hover, .navbar-brand:focus { - text-decoration: none; -} - -.navbar-nav { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.navbar-nav .nav-link { - padding-right: 0; - padding-left: 0; -} -.navbar-nav .dropdown-menu { - position: static; - float: none; -} - -.navbar-text { - display: inline-block; - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.navbar-collapse { - -ms-flex-preferred-size: 100%; - flex-basis: 100%; - -webkit-box-flex: 1; - -ms-flex-positive: 1; - flex-grow: 1; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} - -.navbar-toggler { - padding: 0.25rem 0.75rem; - font-size: 1.25rem; - line-height: 1; - background-color: transparent; - border: 1px solid transparent; - border-radius: 0.25rem; -} -.navbar-toggler:hover, .navbar-toggler:focus { - text-decoration: none; -} - -.navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - content: ""; - background: 50% / 100% 100% no-repeat; -} - -.navbar-nav-scroll { - max-height: 75vh; - overflow-y: auto; -} - -@media (max-width: 575.98px) { - .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid, - .navbar-expand-sm > .container-sm, - .navbar-expand-sm > .container-md, - .navbar-expand-sm > .container-lg, - .navbar-expand-sm > .container-xl { - padding-right: 0; - padding-left: 0; - } -} -@media (min-width: 576px) { - .navbar-expand-sm { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-sm .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-sm .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-sm .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-sm > .container, - .navbar-expand-sm > .container-fluid, - .navbar-expand-sm > .container-sm, - .navbar-expand-sm > .container-md, - .navbar-expand-sm > .container-lg, - .navbar-expand-sm > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-sm .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-sm .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-sm .navbar-toggler { - display: none; - } -} -@media (max-width: 767.98px) { - .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid, - .navbar-expand-md > .container-sm, - .navbar-expand-md > .container-md, - .navbar-expand-md > .container-lg, - .navbar-expand-md > .container-xl { - padding-right: 0; - padding-left: 0; - } -} -@media (min-width: 768px) { - .navbar-expand-md { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-md .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-md .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-md .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-md > .container, - .navbar-expand-md > .container-fluid, - .navbar-expand-md > .container-sm, - .navbar-expand-md > .container-md, - .navbar-expand-md > .container-lg, - .navbar-expand-md > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-md .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-md .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-md .navbar-toggler { - display: none; - } -} -@media (max-width: 991.98px) { - .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid, - .navbar-expand-lg > .container-sm, - .navbar-expand-lg > .container-md, - .navbar-expand-lg > .container-lg, - .navbar-expand-lg > .container-xl { - padding-right: 0; - padding-left: 0; - } -} -@media (min-width: 992px) { - .navbar-expand-lg { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-lg .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-lg .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-lg .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-lg > .container, - .navbar-expand-lg > .container-fluid, - .navbar-expand-lg > .container-sm, - .navbar-expand-lg > .container-md, - .navbar-expand-lg > .container-lg, - .navbar-expand-lg > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-lg .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-lg .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-lg .navbar-toggler { - display: none; - } -} -@media (max-width: 1199.98px) { - .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid, - .navbar-expand-xl > .container-sm, - .navbar-expand-xl > .container-md, - .navbar-expand-xl > .container-lg, - .navbar-expand-xl > .container-xl { - padding-right: 0; - padding-left: 0; - } -} -@media (min-width: 1200px) { - .navbar-expand-xl { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; - } - .navbar-expand-xl .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .navbar-expand-xl .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-xl .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-xl > .container, - .navbar-expand-xl > .container-fluid, - .navbar-expand-xl > .container-sm, - .navbar-expand-xl > .container-md, - .navbar-expand-xl > .container-lg, - .navbar-expand-xl > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; - } - .navbar-expand-xl .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-xl .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; - } - .navbar-expand-xl .navbar-toggler { - display: none; - } -} -.navbar-expand { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; -} -.navbar-expand > .container, -.navbar-expand > .container-fluid, -.navbar-expand > .container-sm, -.navbar-expand > .container-md, -.navbar-expand > .container-lg, -.navbar-expand > .container-xl { - padding-right: 0; - padding-left: 0; -} -.navbar-expand .navbar-nav { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; -} -.navbar-expand .navbar-nav .dropdown-menu { - position: absolute; -} -.navbar-expand .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; -} -.navbar-expand > .container, -.navbar-expand > .container-fluid, -.navbar-expand > .container-sm, -.navbar-expand > .container-md, -.navbar-expand > .container-lg, -.navbar-expand > .container-xl { - -ms-flex-wrap: nowrap; - flex-wrap: nowrap; -} -.navbar-expand .navbar-nav-scroll { - overflow: visible; -} -.navbar-expand .navbar-collapse { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - -ms-flex-preferred-size: auto; - flex-basis: auto; -} -.navbar-expand .navbar-toggler { - display: none; -} - -.navbar-light .navbar-brand { - color: rgba(0, 0, 0, 0.9); -} -.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { - color: rgba(0, 0, 0, 0.9); -} -.navbar-light .navbar-nav .nav-link { - color: rgba(0, 0, 0, 0.5); -} -.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { - color: rgba(0, 0, 0, 0.7); -} -.navbar-light .navbar-nav .nav-link.disabled { - color: rgba(0, 0, 0, 0.3); -} -.navbar-light .navbar-nav .show > .nav-link, -.navbar-light .navbar-nav .active > .nav-link, -.navbar-light .navbar-nav .nav-link.show, -.navbar-light .navbar-nav .nav-link.active { - color: rgba(0, 0, 0, 0.9); -} -.navbar-light .navbar-toggler { - color: rgba(0, 0, 0, 0.5); - border-color: rgba(0, 0, 0, 0.1); -} -.navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); -} -.navbar-light .navbar-text { - color: rgba(0, 0, 0, 0.5); -} -.navbar-light .navbar-text a { - color: rgba(0, 0, 0, 0.9); -} -.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { - color: rgba(0, 0, 0, 0.9); -} - -.navbar-dark .navbar-brand { - color: #fff; -} -.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { - color: #fff; -} -.navbar-dark .navbar-nav .nav-link { - color: rgba(255, 255, 255, 0.5); -} -.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { - color: rgba(255, 255, 255, 0.75); -} -.navbar-dark .navbar-nav .nav-link.disabled { - color: rgba(255, 255, 255, 0.25); -} -.navbar-dark .navbar-nav .show > .nav-link, -.navbar-dark .navbar-nav .active > .nav-link, -.navbar-dark .navbar-nav .nav-link.show, -.navbar-dark .navbar-nav .nav-link.active { - color: #fff; -} -.navbar-dark .navbar-toggler { - color: rgba(255, 255, 255, 0.5); - border-color: rgba(255, 255, 255, 0.1); -} -.navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); -} -.navbar-dark .navbar-text { - color: rgba(255, 255, 255, 0.5); -} -.navbar-dark .navbar-text a { - color: #fff; -} -.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { - color: #fff; -} - -.card { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - min-width: 0; - word-wrap: break-word; - background-color: #fff; - background-clip: border-box; - border: 1px solid rgba(0, 0, 0, 0.125); - border-radius: 0.25rem; -} -.card > hr { - margin-right: 0; - margin-left: 0; -} -.card > .list-group { - border-top: inherit; - border-bottom: inherit; -} -.card > .list-group:first-child { - border-top-width: 0; - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); -} -.card > .list-group:last-child { - border-bottom-width: 0; - border-bottom-right-radius: calc(0.25rem - 1px); - border-bottom-left-radius: calc(0.25rem - 1px); -} -.card > .card-header + .list-group, -.card > .list-group + .card-footer { - border-top: 0; -} - -.card-body { - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-height: 1px; - padding: 1.25rem; -} - -.card-title { - margin-bottom: 0.75rem; -} - -.card-subtitle { - margin-top: -0.375rem; - margin-bottom: 0; -} - -.card-text:last-child { - margin-bottom: 0; -} - -.card-link:hover { - text-decoration: none; -} -.card-link + .card-link { - margin-left: 1.25rem; -} - -.card-header { - padding: 0.75rem 1.25rem; - margin-bottom: 0; - background-color: rgba(0, 0, 0, 0.03); - border-bottom: 1px solid rgba(0, 0, 0, 0.125); -} -.card-header:first-child { - border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; -} - -.card-footer { - padding: 0.75rem 1.25rem; - background-color: rgba(0, 0, 0, 0.03); - border-top: 1px solid rgba(0, 0, 0, 0.125); -} -.card-footer:last-child { - border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); -} - -.card-header-tabs { - margin-right: -0.625rem; - margin-bottom: -0.75rem; - margin-left: -0.625rem; - border-bottom: 0; -} - -.card-header-pills { - margin-right: -0.625rem; - margin-left: -0.625rem; -} - -.card-img-overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: 1.25rem; - border-radius: calc(0.25rem - 1px); -} - -.card-img, -.card-img-top, -.card-img-bottom { - -ms-flex-negative: 0; - flex-shrink: 0; - width: 100%; -} - -.card-img, -.card-img-top { - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); -} - -.card-img, -.card-img-bottom { - border-bottom-right-radius: calc(0.25rem - 1px); - border-bottom-left-radius: calc(0.25rem - 1px); -} - -.card-deck .card { - margin-bottom: 15px; -} -@media (min-width: 576px) { - .card-deck { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - margin-right: -15px; - margin-left: -15px; - } - .card-deck .card { - -webkit-box-flex: 1; - -ms-flex: 1 0 0%; - flex: 1 0 0%; - margin-right: 15px; - margin-bottom: 0; - margin-left: 15px; - } -} - -.card-group > .card { - margin-bottom: 15px; -} -@media (min-width: 576px) { - .card-group { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-flow: row wrap; - flex-flow: row wrap; - } - .card-group > .card { - -webkit-box-flex: 1; - -ms-flex: 1 0 0%; - flex: 1 0 0%; - margin-bottom: 0; - } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; - } - .card-group > .card:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-right-radius: 0; - } - .card-group > .card:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-left-radius: 0; - } -} - -.card-columns .card { - margin-bottom: 0.75rem; -} -@media (min-width: 576px) { - .card-columns { - -webkit-column-count: 3; - -moz-column-count: 3; - column-count: 3; - -webkit-column-gap: 1.25rem; - -moz-column-gap: 1.25rem; - column-gap: 1.25rem; - orphans: 1; - widows: 1; - } - .card-columns .card { - display: inline-block; - width: 100%; - } -} - -.accordion { - overflow-anchor: none; -} -.accordion > .card { - overflow: hidden; -} -.accordion > .card:not(:last-of-type) { - border-bottom: 0; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.accordion > .card:not(:first-of-type) { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.accordion > .card > .card-header { - border-radius: 0; - margin-bottom: -1px; -} - -.breadcrumb { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - padding: 0.75rem 1rem; - margin-bottom: 1rem; - list-style: none; - background-color: #e9ecef; - border-radius: 0.25rem; -} - -.breadcrumb-item + .breadcrumb-item { - padding-left: 0.5rem; -} -.breadcrumb-item + .breadcrumb-item::before { - float: left; - padding-right: 0.5rem; - color: #6c757d; - content: "/"; -} -.breadcrumb-item + .breadcrumb-item:hover::before { - text-decoration: underline; -} -.breadcrumb-item + .breadcrumb-item:hover::before { - text-decoration: none; -} -.breadcrumb-item.active { - color: #6c757d; -} - -.pagination { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - padding-left: 0; - list-style: none; - border-radius: 0.25rem; -} - -.page-link { - position: relative; - display: block; - padding: 0.5rem 0.75rem; - margin-left: -1px; - line-height: 1.25; - color: #007bff; - background-color: #fff; - border: 1px solid #dee2e6; -} -.page-link:hover { - z-index: 2; - color: #0056b3; - text-decoration: none; - background-color: #e9ecef; - border-color: #dee2e6; -} -.page-link:focus { - z-index: 3; - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); -} - -.page-item:first-child .page-link { - margin-left: 0; - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} -.page-item:last-child .page-link { - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; -} -.page-item.active .page-link { - z-index: 3; - color: #fff; - background-color: #007bff; - border-color: #007bff; -} -.page-item.disabled .page-link { - color: #6c757d; - pointer-events: none; - cursor: auto; - background-color: #fff; - border-color: #dee2e6; -} - -.pagination-lg .page-link { - padding: 0.75rem 1.5rem; - font-size: 1.25rem; - line-height: 1.5; -} -.pagination-lg .page-item:first-child .page-link { - border-top-left-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; -} -.pagination-lg .page-item:last-child .page-link { - border-top-right-radius: 0.3rem; - border-bottom-right-radius: 0.3rem; -} - -.pagination-sm .page-link { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - line-height: 1.5; -} -.pagination-sm .page-item:first-child .page-link { - border-top-left-radius: 0.2rem; - border-bottom-left-radius: 0.2rem; -} -.pagination-sm .page-item:last-child .page-link { - border-top-right-radius: 0.2rem; - border-bottom-right-radius: 0.2rem; -} - -.badge { - display: inline-block; - padding: 0.25em 0.4em; - font-size: 75%; - font-weight: 700; - line-height: 1; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: 0.25rem; - -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; -} -@media (prefers-reduced-motion: reduce) { - .badge { - -webkit-transition: none; - transition: none; - } -} -a.badge:hover, a.badge:focus { - text-decoration: none; -} -.badge:empty { - display: none; -} - -.btn .badge { - position: relative; - top: -1px; -} - -.badge-pill { - padding-right: 0.6em; - padding-left: 0.6em; - border-radius: 10rem; -} - -.badge-primary { - color: #fff; - background-color: #007bff; -} -a.badge-primary:hover, a.badge-primary:focus { - color: #fff; - background-color: #0062cc; -} -a.badge-primary:focus, a.badge-primary.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); -} - -.badge-secondary { - color: #fff; - background-color: #6c757d; -} -a.badge-secondary:hover, a.badge-secondary:focus { - color: #fff; - background-color: #545b62; -} -a.badge-secondary:focus, a.badge-secondary.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); - box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); -} - -.badge-success { - color: #fff; - background-color: #28a745; -} -a.badge-success:hover, a.badge-success:focus { - color: #fff; - background-color: #1e7e34; -} -a.badge-success:focus, a.badge-success.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); -} - -.badge-info { - color: #fff; - background-color: #17a2b8; -} -a.badge-info:hover, a.badge-info:focus { - color: #fff; - background-color: #117a8b; -} -a.badge-info:focus, a.badge-info.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); - box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); -} - -.badge-warning { - color: #212529; - background-color: #ffc107; -} -a.badge-warning:hover, a.badge-warning:focus { - color: #212529; - background-color: #d39e00; -} -a.badge-warning:focus, a.badge-warning.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); - box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); -} - -.badge-danger { - color: #fff; - background-color: #dc3545; -} -a.badge-danger:hover, a.badge-danger:focus { - color: #fff; - background-color: #bd2130; -} -a.badge-danger:focus, a.badge-danger.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); -} - -.badge-light { - color: #212529; - background-color: #f8f9fa; -} -a.badge-light:hover, a.badge-light:focus { - color: #212529; - background-color: #dae0e5; -} -a.badge-light:focus, a.badge-light.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); - box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); -} - -.badge-dark { - color: #fff; - background-color: #343a40; -} -a.badge-dark:hover, a.badge-dark:focus { - color: #fff; - background-color: #1d2124; -} -a.badge-dark:focus, a.badge-dark.focus { - outline: 0; - -webkit-box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); - box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); -} - -.jumbotron { - padding: 2rem 1rem; - margin-bottom: 2rem; - background-color: #e9ecef; - border-radius: 0.3rem; -} -@media (min-width: 576px) { - .jumbotron { - padding: 4rem 2rem; - } -} - -.jumbotron-fluid { - padding-right: 0; - padding-left: 0; - border-radius: 0; -} - -.alert { - position: relative; - padding: 0.75rem 1.25rem; - margin-bottom: 1rem; - border: 1px solid transparent; - border-radius: 0.25rem; -} - -.alert-heading { - color: inherit; -} - -.alert-link { - font-weight: 700; -} - -.alert-dismissible { - padding-right: 4rem; -} -.alert-dismissible .close { - position: absolute; - top: 0; - right: 0; - z-index: 2; - padding: 0.75rem 1.25rem; - color: inherit; -} - -.alert-primary { - color: #004085; - background-color: #cce5ff; - border-color: #b8daff; -} -.alert-primary hr { - border-top-color: #9fcdff; -} -.alert-primary .alert-link { - color: #002752; -} - -.alert-secondary { - color: #383d41; - background-color: #e2e3e5; - border-color: #d6d8db; -} -.alert-secondary hr { - border-top-color: #c8cbcf; -} -.alert-secondary .alert-link { - color: #202326; -} - -.alert-success { - color: #155724; - background-color: #d4edda; - border-color: #c3e6cb; -} -.alert-success hr { - border-top-color: #b1dfbb; -} -.alert-success .alert-link { - color: #0b2e13; -} - -.alert-info { - color: #0c5460; - background-color: #d1ecf1; - border-color: #bee5eb; -} -.alert-info hr { - border-top-color: #abdde5; -} -.alert-info .alert-link { - color: #062c33; -} - -.alert-warning { - color: #856404; - background-color: #fff3cd; - border-color: #ffeeba; -} -.alert-warning hr { - border-top-color: #ffe8a1; -} -.alert-warning .alert-link { - color: #533f03; -} - -.alert-danger { - color: #721c24; - background-color: #f8d7da; - border-color: #f5c6cb; -} -.alert-danger hr { - border-top-color: #f1b0b7; -} -.alert-danger .alert-link { - color: #491217; -} - -.alert-light { - color: #818182; - background-color: #fefefe; - border-color: #fdfdfe; -} -.alert-light hr { - border-top-color: #ececf6; -} -.alert-light .alert-link { - color: #686868; -} - -.alert-dark { - color: #1b1e21; - background-color: #d6d8d9; - border-color: #c6c8ca; -} -.alert-dark hr { - border-top-color: #b9bbbe; -} -.alert-dark .alert-link { - color: #040505; -} - -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 1rem 0; - } - to { - background-position: 0 0; - } -} - -@keyframes progress-bar-stripes { - from { - background-position: 1rem 0; - } - to { - background-position: 0 0; - } -} -.progress { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - height: 1rem; - overflow: hidden; - line-height: 0; - font-size: 0.75rem; - background-color: #e9ecef; - border-radius: 0.25rem; -} - -.progress-bar { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - overflow: hidden; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #007bff; - -webkit-transition: width 0.6s ease; - transition: width 0.6s ease; -} -@media (prefers-reduced-motion: reduce) { - .progress-bar { - -webkit-transition: none; - transition: none; - } -} - -.progress-bar-striped { - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-size: 1rem 1rem; -} - -.progress-bar-animated { - -webkit-animation: 1s linear infinite progress-bar-stripes; - animation: 1s linear infinite progress-bar-stripes; -} -@media (prefers-reduced-motion: reduce) { - .progress-bar-animated { - -webkit-animation: none; - animation: none; - } -} - -.media { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: flex-start; -} - -.media-body { - -webkit-box-flex: 1; - -ms-flex: 1; - flex: 1; -} - -.list-group { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - border-radius: 0.25rem; -} - -.list-group-item-action { - width: 100%; - color: #495057; - text-align: inherit; -} -.list-group-item-action:hover, .list-group-item-action:focus { - z-index: 1; - color: #495057; - text-decoration: none; - background-color: #f8f9fa; -} -.list-group-item-action:active { - color: #212529; - background-color: #e9ecef; -} - -.list-group-item { - position: relative; - display: block; - padding: 0.75rem 1.25rem; - background-color: #fff; - border: 1px solid rgba(0, 0, 0, 0.125); -} -.list-group-item:first-child { - border-top-left-radius: inherit; - border-top-right-radius: inherit; -} -.list-group-item:last-child { - border-bottom-right-radius: inherit; - border-bottom-left-radius: inherit; -} -.list-group-item.disabled, .list-group-item:disabled { - color: #6c757d; - pointer-events: none; - background-color: #fff; -} -.list-group-item.active { - z-index: 2; - color: #fff; - background-color: #007bff; - border-color: #007bff; -} -.list-group-item + .list-group-item { - border-top-width: 0; -} -.list-group-item + .list-group-item.active { - margin-top: -1px; - border-top-width: 1px; -} - -.list-group-horizontal { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; -} -.list-group-horizontal > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; -} -.list-group-horizontal > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; -} -.list-group-horizontal > .list-group-item.active { - margin-top: 0; -} -.list-group-horizontal > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; -} -.list-group-horizontal > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; -} - -@media (min-width: 576px) { - .list-group-horizontal-sm { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-sm > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-sm > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } - .list-group-horizontal-sm > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-sm > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; - } - .list-group-horizontal-sm > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; - } -} -@media (min-width: 768px) { - .list-group-horizontal-md { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-md > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-md > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } - .list-group-horizontal-md > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-md > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; - } - .list-group-horizontal-md > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; - } -} -@media (min-width: 992px) { - .list-group-horizontal-lg { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-lg > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-lg > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } - .list-group-horizontal-lg > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-lg > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; - } - .list-group-horizontal-lg > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; - } -} -@media (min-width: 1200px) { - .list-group-horizontal-xl { - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - -ms-flex-direction: row; - flex-direction: row; - } - .list-group-horizontal-xl > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; - } - .list-group-horizontal-xl > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; - } - .list-group-horizontal-xl > .list-group-item.active { - margin-top: 0; - } - .list-group-horizontal-xl > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; - } - .list-group-horizontal-xl > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; - } -} -.list-group-flush { - border-radius: 0; -} -.list-group-flush > .list-group-item { - border-width: 0 0 1px; -} -.list-group-flush > .list-group-item:last-child { - border-bottom-width: 0; -} - -.list-group-item-primary { - color: #004085; - background-color: #b8daff; -} -.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { - color: #004085; - background-color: #9fcdff; -} -.list-group-item-primary.list-group-item-action.active { - color: #fff; - background-color: #004085; - border-color: #004085; -} - -.list-group-item-secondary { - color: #383d41; - background-color: #d6d8db; -} -.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { - color: #383d41; - background-color: #c8cbcf; -} -.list-group-item-secondary.list-group-item-action.active { - color: #fff; - background-color: #383d41; - border-color: #383d41; -} - -.list-group-item-success { - color: #155724; - background-color: #c3e6cb; -} -.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { - color: #155724; - background-color: #b1dfbb; -} -.list-group-item-success.list-group-item-action.active { - color: #fff; - background-color: #155724; - border-color: #155724; -} - -.list-group-item-info { - color: #0c5460; - background-color: #bee5eb; -} -.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { - color: #0c5460; - background-color: #abdde5; -} -.list-group-item-info.list-group-item-action.active { - color: #fff; - background-color: #0c5460; - border-color: #0c5460; -} - -.list-group-item-warning { - color: #856404; - background-color: #ffeeba; -} -.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { - color: #856404; - background-color: #ffe8a1; -} -.list-group-item-warning.list-group-item-action.active { - color: #fff; - background-color: #856404; - border-color: #856404; -} - -.list-group-item-danger { - color: #721c24; - background-color: #f5c6cb; -} -.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { - color: #721c24; - background-color: #f1b0b7; -} -.list-group-item-danger.list-group-item-action.active { - color: #fff; - background-color: #721c24; - border-color: #721c24; -} - -.list-group-item-light { - color: #818182; - background-color: #fdfdfe; -} -.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { - color: #818182; - background-color: #ececf6; -} -.list-group-item-light.list-group-item-action.active { - color: #fff; - background-color: #818182; - border-color: #818182; -} - -.list-group-item-dark { - color: #1b1e21; - background-color: #c6c8ca; -} -.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { - color: #1b1e21; - background-color: #b9bbbe; -} -.list-group-item-dark.list-group-item-action.active { - color: #fff; - background-color: #1b1e21; - border-color: #1b1e21; -} - -.close { - float: right; - font-size: 1.5rem; - font-weight: 700; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - opacity: .5; -} -.close:hover { - color: #000; - text-decoration: none; -} -.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { - opacity: .75; -} - -button.close { - padding: 0; - background-color: transparent; - border: 0; -} - -a.close.disabled { - pointer-events: none; -} - -.toast { - -ms-flex-preferred-size: 350px; - flex-basis: 350px; - max-width: 350px; - font-size: 0.875rem; - background-color: rgba(255, 255, 255, 0.85); - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.1); - -webkit-box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); - box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); - opacity: 0; - border-radius: 0.25rem; -} -.toast:not(:last-child) { - margin-bottom: 0.75rem; -} -.toast.showing { - opacity: 1; -} -.toast.show { - display: block; - opacity: 1; -} -.toast.hide { - display: none; -} - -.toast-header { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding: 0.25rem 0.75rem; - color: #6c757d; - background-color: rgba(255, 255, 255, 0.85); - background-clip: padding-box; - border-bottom: 1px solid rgba(0, 0, 0, 0.05); - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); -} - -.toast-body { - padding: 0.75rem; -} - -.modal-open { - overflow: hidden; -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} - -.modal { - position: fixed; - top: 0; - left: 0; - z-index: 1050; - display: none; - width: 100%; - height: 100%; - overflow: hidden; - outline: 0; -} - -.modal-dialog { - position: relative; - width: auto; - margin: 0.5rem; - pointer-events: none; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform 0.3s ease-out; - transition: -webkit-transform 0.3s ease-out; - transition: transform 0.3s ease-out; - transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out; - -webkit-transform: translate(0, -50px); - transform: translate(0, -50px); -} -@media (prefers-reduced-motion: reduce) { - .modal.fade .modal-dialog { - -webkit-transition: none; - transition: none; - } -} -.modal.show .modal-dialog { - -webkit-transform: none; - transform: none; -} -.modal.modal-static .modal-dialog { - -webkit-transform: scale(1.02); - transform: scale(1.02); -} - -.modal-dialog-scrollable { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - max-height: calc(100% - 1rem); -} -.modal-dialog-scrollable .modal-content { - max-height: calc(100vh - 1rem); - overflow: hidden; -} -.modal-dialog-scrollable .modal-header, -.modal-dialog-scrollable .modal-footer { - -ms-flex-negative: 0; - flex-shrink: 0; -} -.modal-dialog-scrollable .modal-body { - overflow-y: auto; -} - -.modal-dialog-centered { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - min-height: calc(100% - 1rem); -} -.modal-dialog-centered::before { - display: block; - height: calc(100vh - 1rem); - height: -webkit-min-content; - height: -moz-min-content; - height: min-content; - content: ""; -} -.modal-dialog-centered.modal-dialog-scrollable { - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - height: 100%; -} -.modal-dialog-centered.modal-dialog-scrollable .modal-content { - max-height: none; -} -.modal-dialog-centered.modal-dialog-scrollable::before { - content: none; -} - -.modal-content { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - -ms-flex-direction: column; - flex-direction: column; - width: 100%; - pointer-events: auto; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; - outline: 0; -} - -.modal-backdrop { - position: fixed; - top: 0; - left: 0; - z-index: 1040; - width: 100vw; - height: 100vh; - background-color: #000; -} -.modal-backdrop.fade { - opacity: 0; -} -.modal-backdrop.show { - opacity: 0.5; -} - -.modal-header { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: start; - -ms-flex-align: start; - align-items: flex-start; - -webkit-box-pack: justify; - -ms-flex-pack: justify; - justify-content: space-between; - padding: 1rem 1rem; - border-bottom: 1px solid #dee2e6; - border-top-left-radius: calc(0.3rem - 1px); - border-top-right-radius: calc(0.3rem - 1px); -} -.modal-header .close { - padding: 1rem 1rem; - margin: -1rem -1rem -1rem auto; -} - -.modal-title { - margin-bottom: 0; - line-height: 1.5; -} - -.modal-body { - position: relative; - -webkit-box-flex: 1; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - padding: 1rem; -} - -.modal-footer { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: end; - -ms-flex-pack: end; - justify-content: flex-end; - padding: 0.75rem; - border-top: 1px solid #dee2e6; - border-bottom-right-radius: calc(0.3rem - 1px); - border-bottom-left-radius: calc(0.3rem - 1px); -} -.modal-footer > * { - margin: 0.25rem; -} - -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} - -@media (min-width: 576px) { - .modal-dialog { - max-width: 500px; - margin: 1.75rem auto; - } - - .modal-dialog-scrollable { - max-height: calc(100% - 3.5rem); - } - .modal-dialog-scrollable .modal-content { - max-height: calc(100vh - 3.5rem); - } - - .modal-dialog-centered { - min-height: calc(100% - 3.5rem); - } - .modal-dialog-centered::before { - height: calc(100vh - 3.5rem); - height: -webkit-min-content; - height: -moz-min-content; - height: min-content; - } - - .modal-sm { - max-width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg, - .modal-xl { - max-width: 800px; - } -} -@media (min-width: 1200px) { - .modal-xl { - max-width: 1140px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.875rem; - word-wrap: break-word; - opacity: 0; -} -.tooltip.show { - opacity: 0.9; -} -.tooltip .arrow { - position: absolute; - display: block; - width: 0.8rem; - height: 0.4rem; -} -.tooltip .arrow::before { - position: absolute; - content: ""; - border-color: transparent; - border-style: solid; -} - -.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { - padding: 0.4rem 0; -} -.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { - bottom: 0; -} -.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { - top: 0; - border-width: 0.4rem 0.4rem 0; - border-top-color: #000; -} - -.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { - padding: 0 0.4rem; -} -.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { - left: 0; - width: 0.4rem; - height: 0.8rem; -} -.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { - right: 0; - border-width: 0.4rem 0.4rem 0.4rem 0; - border-right-color: #000; -} - -.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { - padding: 0.4rem 0; -} -.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { - top: 0; -} -.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { - bottom: 0; - border-width: 0 0.4rem 0.4rem; - border-bottom-color: #000; -} - -.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { - padding: 0 0.4rem; -} -.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { - right: 0; - width: 0.4rem; - height: 0.8rem; -} -.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { - left: 0; - border-width: 0.4rem 0 0.4rem 0.4rem; - border-left-color: #000; -} - -.tooltip-inner { - max-width: 200px; - padding: 0.25rem 0.5rem; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 0.25rem; -} - -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: block; - max-width: 276px; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.875rem; - word-wrap: break-word; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; -} -.popover .arrow { - position: absolute; - display: block; - width: 1rem; - height: 0.5rem; - margin: 0 0.3rem; -} -.popover .arrow::before, .popover .arrow::after { - position: absolute; - display: block; - content: ""; - border-color: transparent; - border-style: solid; -} - -.bs-popover-top, .bs-popover-auto[x-placement^="top"] { - margin-bottom: 0.5rem; -} -.bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { - bottom: calc(-0.5rem - 1px); -} -.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { - bottom: 0; - border-width: 0.5rem 0.5rem 0; - border-top-color: rgba(0, 0, 0, 0.25); -} -.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^="top"] > .arrow::after { - bottom: 1px; - border-width: 0.5rem 0.5rem 0; - border-top-color: #fff; -} - -.bs-popover-right, .bs-popover-auto[x-placement^="right"] { - margin-left: 0.5rem; -} -.bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { - left: calc(-0.5rem - 1px); - width: 0.5rem; - height: 1rem; - margin: 0.3rem 0; -} -.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^="right"] > .arrow::before { - left: 0; - border-width: 0.5rem 0.5rem 0.5rem 0; - border-right-color: rgba(0, 0, 0, 0.25); -} -.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^="right"] > .arrow::after { - left: 1px; - border-width: 0.5rem 0.5rem 0.5rem 0; - border-right-color: #fff; -} - -.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { - margin-top: 0.5rem; -} -.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { - top: calc(-0.5rem - 1px); -} -.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { - top: 0; - border-width: 0 0.5rem 0.5rem 0.5rem; - border-bottom-color: rgba(0, 0, 0, 0.25); -} -.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after { - top: 1px; - border-width: 0 0.5rem 0.5rem 0.5rem; - border-bottom-color: #fff; -} -.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { - position: absolute; - top: 0; - left: 50%; - display: block; - width: 1rem; - margin-left: -0.5rem; - content: ""; - border-bottom: 1px solid #f7f7f7; -} - -.bs-popover-left, .bs-popover-auto[x-placement^="left"] { - margin-right: 0.5rem; -} -.bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { - right: calc(-0.5rem - 1px); - width: 0.5rem; - height: 1rem; - margin: 0.3rem 0; -} -.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^="left"] > .arrow::before { - right: 0; - border-width: 0.5rem 0 0.5rem 0.5rem; - border-left-color: rgba(0, 0, 0, 0.25); -} -.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^="left"] > .arrow::after { - right: 1px; - border-width: 0.5rem 0 0.5rem 0.5rem; - border-left-color: #fff; -} - -.popover-header { - padding: 0.5rem 0.75rem; - margin-bottom: 0; - font-size: 1rem; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-top-left-radius: calc(0.3rem - 1px); - border-top-right-radius: calc(0.3rem - 1px); -} -.popover-header:empty { - display: none; -} - -.popover-body { - padding: 0.5rem 0.75rem; - color: #212529; -} - -.carousel { - position: relative; -} - -.carousel.pointer-event { - -ms-touch-action: pan-y; - touch-action: pan-y; -} - -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner::after { - display: block; - clear: both; - content: ""; -} - -.carousel-item { - position: relative; - display: none; - float: left; - width: 100%; - margin-right: -100%; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-transition: -webkit-transform 0.6s ease-in-out; - transition: -webkit-transform 0.6s ease-in-out; - transition: transform 0.6s ease-in-out; - transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; -} -@media (prefers-reduced-motion: reduce) { - .carousel-item { - -webkit-transition: none; - transition: none; - } -} - -.carousel-item.active, -.carousel-item-next, -.carousel-item-prev { - display: block; -} - -.carousel-item-next:not(.carousel-item-left), -.active.carousel-item-right { - -webkit-transform: translateX(100%); - transform: translateX(100%); -} - -.carousel-item-prev:not(.carousel-item-right), -.active.carousel-item-left { - -webkit-transform: translateX(-100%); - transform: translateX(-100%); -} - -.carousel-fade .carousel-item { - opacity: 0; - -webkit-transition-property: opacity; - transition-property: opacity; - -webkit-transform: none; - transform: none; -} -.carousel-fade .carousel-item.active, -.carousel-fade .carousel-item-next.carousel-item-left, -.carousel-fade .carousel-item-prev.carousel-item-right { - z-index: 1; - opacity: 1; -} -.carousel-fade .active.carousel-item-left, -.carousel-fade .active.carousel-item-right { - z-index: 0; - opacity: 0; - -webkit-transition: opacity 0s 0.6s; - transition: opacity 0s 0.6s; -} -@media (prefers-reduced-motion: reduce) { - .carousel-fade .active.carousel-item-left, - .carousel-fade .active.carousel-item-right { - -webkit-transition: none; - transition: none; - } -} - -.carousel-control-prev, -.carousel-control-next { - position: absolute; - top: 0; - bottom: 0; - z-index: 1; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - width: 15%; - padding: 0; - color: #fff; - text-align: center; - background: none; - border: 0; - opacity: 0.5; - -webkit-transition: opacity 0.15s ease; - transition: opacity 0.15s ease; -} -@media (prefers-reduced-motion: reduce) { - .carousel-control-prev, - .carousel-control-next { - -webkit-transition: none; - transition: none; - } -} -.carousel-control-prev:hover, .carousel-control-prev:focus, -.carousel-control-next:hover, -.carousel-control-next:focus { - color: #fff; - text-decoration: none; - outline: 0; - opacity: 0.9; -} - -.carousel-control-prev { - left: 0; -} - -.carousel-control-next { - right: 0; -} - -.carousel-control-prev-icon, -.carousel-control-next-icon { - display: inline-block; - width: 20px; - height: 20px; - background: 50% / 100% 100% no-repeat; -} - -.carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e"); -} - -.carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e"); -} - -.carousel-indicators { - position: absolute; - right: 0; - bottom: 0; - left: 0; - z-index: 15; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - padding-left: 0; - margin-right: 15%; - margin-left: 15%; - list-style: none; -} -.carousel-indicators li { - -webkit-box-sizing: content-box; - box-sizing: content-box; - -webkit-box-flex: 0; - -ms-flex: 0 1 auto; - flex: 0 1 auto; - width: 30px; - height: 3px; - margin-right: 3px; - margin-left: 3px; - text-indent: -999px; - cursor: pointer; - background-color: #fff; - background-clip: padding-box; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; - opacity: .5; - -webkit-transition: opacity 0.6s ease; - transition: opacity 0.6s ease; -} -@media (prefers-reduced-motion: reduce) { - .carousel-indicators li { - -webkit-transition: none; - transition: none; - } -} -.carousel-indicators .active { - opacity: 1; -} - -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; -} - -@-webkit-keyframes spinner-border { - to { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} - -@keyframes spinner-border { - to { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -.spinner-border { - display: inline-block; - width: 2rem; - height: 2rem; - vertical-align: -0.125em; - border: 0.25em solid currentColor; - border-right-color: transparent; - border-radius: 50%; - -webkit-animation: .75s linear infinite spinner-border; - animation: .75s linear infinite spinner-border; -} - -.spinner-border-sm { - width: 1rem; - height: 1rem; - border-width: 0.2em; -} - -@-webkit-keyframes spinner-grow { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes spinner-grow { - 0% { - -webkit-transform: scale(0); - transform: scale(0); - } - 50% { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} -.spinner-grow { - display: inline-block; - width: 2rem; - height: 2rem; - vertical-align: -0.125em; - background-color: currentColor; - border-radius: 50%; - opacity: 0; - -webkit-animation: .75s linear infinite spinner-grow; - animation: .75s linear infinite spinner-grow; -} - -.spinner-grow-sm { - width: 1rem; - height: 1rem; -} - -@media (prefers-reduced-motion: reduce) { - .spinner-border, - .spinner-grow { - -webkit-animation-duration: 1.5s; - animation-duration: 1.5s; - } -} -.align-baseline { - vertical-align: baseline !important; -} - -.align-top { - vertical-align: top !important; -} - -.align-middle { - vertical-align: middle !important; -} - -.align-bottom { - vertical-align: bottom !important; -} - -.align-text-bottom { - vertical-align: text-bottom !important; -} - -.align-text-top { - vertical-align: text-top !important; -} - -.bg-primary { - background-color: #007bff !important; -} - -a.bg-primary:hover, a.bg-primary:focus, -button.bg-primary:hover, -button.bg-primary:focus { - background-color: #0062cc !important; -} - -.bg-secondary { - background-color: #6c757d !important; -} - -a.bg-secondary:hover, a.bg-secondary:focus, -button.bg-secondary:hover, -button.bg-secondary:focus { - background-color: #545b62 !important; -} - -.bg-success { - background-color: #28a745 !important; -} - -a.bg-success:hover, a.bg-success:focus, -button.bg-success:hover, -button.bg-success:focus { - background-color: #1e7e34 !important; -} - -.bg-info { - background-color: #17a2b8 !important; -} - -a.bg-info:hover, a.bg-info:focus, -button.bg-info:hover, -button.bg-info:focus { - background-color: #117a8b !important; -} - -.bg-warning { - background-color: #ffc107 !important; -} - -a.bg-warning:hover, a.bg-warning:focus, -button.bg-warning:hover, -button.bg-warning:focus { - background-color: #d39e00 !important; -} - -.bg-danger { - background-color: #dc3545 !important; -} - -a.bg-danger:hover, a.bg-danger:focus, -button.bg-danger:hover, -button.bg-danger:focus { - background-color: #bd2130 !important; -} - -.bg-light { - background-color: #f8f9fa !important; -} - -a.bg-light:hover, a.bg-light:focus, -button.bg-light:hover, -button.bg-light:focus { - background-color: #dae0e5 !important; -} - -.bg-dark { - background-color: #343a40 !important; -} - -a.bg-dark:hover, a.bg-dark:focus, -button.bg-dark:hover, -button.bg-dark:focus { - background-color: #1d2124 !important; -} - -.bg-white { - background-color: #fff !important; -} - -.bg-transparent { - background-color: transparent !important; -} - -.border { - border: 1px solid #dee2e6 !important; -} - -.border-top { - border-top: 1px solid #dee2e6 !important; -} - -.border-right { - border-right: 1px solid #dee2e6 !important; -} - -.border-bottom { - border-bottom: 1px solid #dee2e6 !important; -} - -.border-left { - border-left: 1px solid #dee2e6 !important; -} - -.border-0 { - border: 0 !important; -} - -.border-top-0 { - border-top: 0 !important; -} - -.border-right-0 { - border-right: 0 !important; -} - -.border-bottom-0 { - border-bottom: 0 !important; -} - -.border-left-0 { - border-left: 0 !important; -} - -.border-primary { - border-color: #007bff !important; -} - -.border-secondary { - border-color: #6c757d !important; -} - -.border-success { - border-color: #28a745 !important; -} - -.border-info { - border-color: #17a2b8 !important; -} - -.border-warning { - border-color: #ffc107 !important; -} - -.border-danger { - border-color: #dc3545 !important; -} - -.border-light { - border-color: #f8f9fa !important; -} - -.border-dark { - border-color: #343a40 !important; -} - -.border-white { - border-color: #fff !important; -} - -.rounded-sm { - border-radius: 0.2rem !important; -} - -.rounded { - border-radius: 0.25rem !important; -} - -.rounded-top { - border-top-left-radius: 0.25rem !important; - border-top-right-radius: 0.25rem !important; -} - -.rounded-right { - border-top-right-radius: 0.25rem !important; - border-bottom-right-radius: 0.25rem !important; -} - -.rounded-bottom { - border-bottom-right-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; -} - -.rounded-left { - border-top-left-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; -} - -.rounded-lg { - border-radius: 0.3rem !important; -} - -.rounded-circle { - border-radius: 50% !important; -} - -.rounded-pill { - border-radius: 50rem !important; -} - -.rounded-0 { - border-radius: 0 !important; -} - -.clearfix::after { - display: block; - clear: both; - content: ""; -} - -.d-none { - display: none !important; -} - -.d-inline { - display: inline !important; -} - -.d-inline-block { - display: inline-block !important; -} - -.d-block { - display: block !important; -} - -.d-table { - display: table !important; -} - -.d-table-row { - display: table-row !important; -} - -.d-table-cell { - display: table-cell !important; -} - -.d-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; -} - -.d-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; -} - -@media (min-width: 576px) { - .d-sm-none { - display: none !important; - } - - .d-sm-inline { - display: inline !important; - } - - .d-sm-inline-block { - display: inline-block !important; - } - - .d-sm-block { - display: block !important; - } - - .d-sm-table { - display: table !important; - } - - .d-sm-table-row { - display: table-row !important; - } - - .d-sm-table-cell { - display: table-cell !important; - } - - .d-sm-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - - .d-sm-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} -@media (min-width: 768px) { - .d-md-none { - display: none !important; - } - - .d-md-inline { - display: inline !important; - } - - .d-md-inline-block { - display: inline-block !important; - } - - .d-md-block { - display: block !important; - } - - .d-md-table { - display: table !important; - } - - .d-md-table-row { - display: table-row !important; - } - - .d-md-table-cell { - display: table-cell !important; - } - - .d-md-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - - .d-md-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} -@media (min-width: 992px) { - .d-lg-none { - display: none !important; - } - - .d-lg-inline { - display: inline !important; - } - - .d-lg-inline-block { - display: inline-block !important; - } - - .d-lg-block { - display: block !important; - } - - .d-lg-table { - display: table !important; - } - - .d-lg-table-row { - display: table-row !important; - } - - .d-lg-table-cell { - display: table-cell !important; - } - - .d-lg-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - - .d-lg-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} -@media (min-width: 1200px) { - .d-xl-none { - display: none !important; - } - - .d-xl-inline { - display: inline !important; - } - - .d-xl-inline-block { - display: inline-block !important; - } - - .d-xl-block { - display: block !important; - } - - .d-xl-table { - display: table !important; - } - - .d-xl-table-row { - display: table-row !important; - } - - .d-xl-table-cell { - display: table-cell !important; - } - - .d-xl-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - - .d-xl-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} -@media print { - .d-print-none { - display: none !important; - } - - .d-print-inline { - display: inline !important; - } - - .d-print-inline-block { - display: inline-block !important; - } - - .d-print-block { - display: block !important; - } - - .d-print-table { - display: table !important; - } - - .d-print-table-row { - display: table-row !important; - } - - .d-print-table-cell { - display: table-cell !important; - } - - .d-print-flex { - display: -webkit-box !important; - display: -ms-flexbox !important; - display: flex !important; - } - - .d-print-inline-flex { - display: -webkit-inline-box !important; - display: -ms-inline-flexbox !important; - display: inline-flex !important; - } -} -.embed-responsive { - position: relative; - display: block; - width: 100%; - padding: 0; - overflow: hidden; -} -.embed-responsive::before { - display: block; - content: ""; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} - -.embed-responsive-21by9::before { - padding-top: 42.85714286%; -} - -.embed-responsive-16by9::before { - padding-top: 56.25%; -} - -.embed-responsive-4by3::before { - padding-top: 75%; -} - -.embed-responsive-1by1::before { - padding-top: 100%; -} - -.flex-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; -} - -.flex-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; -} - -.flex-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; -} - -.flex-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; -} - -.flex-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; -} - -.flex-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; -} - -.flex-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; -} - -.flex-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; -} - -.flex-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; -} - -.flex-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; -} - -.flex-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; -} - -.flex-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; -} - -.justify-content-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; -} - -.justify-content-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; -} - -.justify-content-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; -} - -.justify-content-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; -} - -.justify-content-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; -} - -.align-items-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; -} - -.align-items-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; -} - -.align-items-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; -} - -.align-items-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; -} - -.align-items-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; -} - -.align-content-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; -} - -.align-content-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; -} - -.align-content-center { - -ms-flex-line-pack: center !important; - align-content: center !important; -} - -.align-content-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; -} - -.align-content-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; -} - -.align-content-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; -} - -.align-self-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; -} - -.align-self-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; -} - -.align-self-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; -} - -.align-self-center { - -ms-flex-item-align: center !important; - align-self: center !important; -} - -.align-self-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; -} - -.align-self-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; -} - -@media (min-width: 576px) { - .flex-sm-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; - } - - .flex-sm-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; - } - - .flex-sm-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - - .flex-sm-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - - .flex-sm-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - - .flex-sm-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - - .flex-sm-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - - .flex-sm-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - - .flex-sm-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - - .flex-sm-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - - .flex-sm-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - - .flex-sm-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - - .justify-content-sm-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - - .justify-content-sm-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - - .justify-content-sm-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; - } - - .justify-content-sm-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - - .justify-content-sm-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - - .align-items-sm-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; - } - - .align-items-sm-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; - } - - .align-items-sm-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; - } - - .align-items-sm-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - - .align-items-sm-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - - .align-content-sm-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - - .align-content-sm-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - - .align-content-sm-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - - .align-content-sm-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - - .align-content-sm-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - - .align-content-sm-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - - .align-self-sm-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - - .align-self-sm-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - - .align-self-sm-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - - .align-self-sm-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - - .align-self-sm-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - - .align-self-sm-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} -@media (min-width: 768px) { - .flex-md-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; - } - - .flex-md-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; - } - - .flex-md-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - - .flex-md-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - - .flex-md-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - - .flex-md-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - - .flex-md-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - - .flex-md-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - - .flex-md-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - - .flex-md-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - - .flex-md-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - - .flex-md-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - - .justify-content-md-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - - .justify-content-md-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - - .justify-content-md-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; - } - - .justify-content-md-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - - .justify-content-md-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - - .align-items-md-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; - } - - .align-items-md-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; - } - - .align-items-md-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; - } - - .align-items-md-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - - .align-items-md-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - - .align-content-md-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - - .align-content-md-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - - .align-content-md-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - - .align-content-md-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - - .align-content-md-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - - .align-content-md-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - - .align-self-md-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - - .align-self-md-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - - .align-self-md-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - - .align-self-md-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - - .align-self-md-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - - .align-self-md-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} -@media (min-width: 992px) { - .flex-lg-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; - } - - .flex-lg-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; - } - - .flex-lg-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - - .flex-lg-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - - .flex-lg-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - - .flex-lg-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - - .flex-lg-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - - .flex-lg-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - - .flex-lg-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - - .flex-lg-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - - .flex-lg-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - - .flex-lg-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - - .justify-content-lg-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - - .justify-content-lg-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - - .justify-content-lg-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; - } - - .justify-content-lg-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - - .justify-content-lg-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - - .align-items-lg-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; - } - - .align-items-lg-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; - } - - .align-items-lg-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; - } - - .align-items-lg-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - - .align-items-lg-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - - .align-content-lg-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - - .align-content-lg-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - - .align-content-lg-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - - .align-content-lg-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - - .align-content-lg-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - - .align-content-lg-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - - .align-self-lg-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - - .align-self-lg-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - - .align-self-lg-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - - .align-self-lg-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - - .align-self-lg-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - - .align-self-lg-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} -@media (min-width: 1200px) { - .flex-xl-row { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: row !important; - flex-direction: row !important; - } - - .flex-xl-column { - -webkit-box-orient: vertical !important; - -webkit-box-direction: normal !important; - -ms-flex-direction: column !important; - flex-direction: column !important; - } - - .flex-xl-row-reverse { - -webkit-box-orient: horizontal !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: row-reverse !important; - flex-direction: row-reverse !important; - } - - .flex-xl-column-reverse { - -webkit-box-orient: vertical !important; - -webkit-box-direction: reverse !important; - -ms-flex-direction: column-reverse !important; - flex-direction: column-reverse !important; - } - - .flex-xl-wrap { - -ms-flex-wrap: wrap !important; - flex-wrap: wrap !important; - } - - .flex-xl-nowrap { - -ms-flex-wrap: nowrap !important; - flex-wrap: nowrap !important; - } - - .flex-xl-wrap-reverse { - -ms-flex-wrap: wrap-reverse !important; - flex-wrap: wrap-reverse !important; - } - - .flex-xl-fill { - -webkit-box-flex: 1 !important; - -ms-flex: 1 1 auto !important; - flex: 1 1 auto !important; - } - - .flex-xl-grow-0 { - -webkit-box-flex: 0 !important; - -ms-flex-positive: 0 !important; - flex-grow: 0 !important; - } - - .flex-xl-grow-1 { - -webkit-box-flex: 1 !important; - -ms-flex-positive: 1 !important; - flex-grow: 1 !important; - } - - .flex-xl-shrink-0 { - -ms-flex-negative: 0 !important; - flex-shrink: 0 !important; - } - - .flex-xl-shrink-1 { - -ms-flex-negative: 1 !important; - flex-shrink: 1 !important; - } - - .justify-content-xl-start { - -webkit-box-pack: start !important; - -ms-flex-pack: start !important; - justify-content: flex-start !important; - } - - .justify-content-xl-end { - -webkit-box-pack: end !important; - -ms-flex-pack: end !important; - justify-content: flex-end !important; - } - - .justify-content-xl-center { - -webkit-box-pack: center !important; - -ms-flex-pack: center !important; - justify-content: center !important; - } - - .justify-content-xl-between { - -webkit-box-pack: justify !important; - -ms-flex-pack: justify !important; - justify-content: space-between !important; - } - - .justify-content-xl-around { - -ms-flex-pack: distribute !important; - justify-content: space-around !important; - } - - .align-items-xl-start { - -webkit-box-align: start !important; - -ms-flex-align: start !important; - align-items: flex-start !important; - } - - .align-items-xl-end { - -webkit-box-align: end !important; - -ms-flex-align: end !important; - align-items: flex-end !important; - } - - .align-items-xl-center { - -webkit-box-align: center !important; - -ms-flex-align: center !important; - align-items: center !important; - } - - .align-items-xl-baseline { - -webkit-box-align: baseline !important; - -ms-flex-align: baseline !important; - align-items: baseline !important; - } - - .align-items-xl-stretch { - -webkit-box-align: stretch !important; - -ms-flex-align: stretch !important; - align-items: stretch !important; - } - - .align-content-xl-start { - -ms-flex-line-pack: start !important; - align-content: flex-start !important; - } - - .align-content-xl-end { - -ms-flex-line-pack: end !important; - align-content: flex-end !important; - } - - .align-content-xl-center { - -ms-flex-line-pack: center !important; - align-content: center !important; - } - - .align-content-xl-between { - -ms-flex-line-pack: justify !important; - align-content: space-between !important; - } - - .align-content-xl-around { - -ms-flex-line-pack: distribute !important; - align-content: space-around !important; - } - - .align-content-xl-stretch { - -ms-flex-line-pack: stretch !important; - align-content: stretch !important; - } - - .align-self-xl-auto { - -ms-flex-item-align: auto !important; - align-self: auto !important; - } - - .align-self-xl-start { - -ms-flex-item-align: start !important; - align-self: flex-start !important; - } - - .align-self-xl-end { - -ms-flex-item-align: end !important; - align-self: flex-end !important; - } - - .align-self-xl-center { - -ms-flex-item-align: center !important; - align-self: center !important; - } - - .align-self-xl-baseline { - -ms-flex-item-align: baseline !important; - align-self: baseline !important; - } - - .align-self-xl-stretch { - -ms-flex-item-align: stretch !important; - align-self: stretch !important; - } -} -.float-left { - float: left !important; -} - -.float-right { - float: right !important; -} - -.float-none { - float: none !important; -} - -@media (min-width: 576px) { - .float-sm-left { - float: left !important; - } - - .float-sm-right { - float: right !important; - } - - .float-sm-none { - float: none !important; - } -} -@media (min-width: 768px) { - .float-md-left { - float: left !important; - } - - .float-md-right { - float: right !important; - } - - .float-md-none { - float: none !important; - } -} -@media (min-width: 992px) { - .float-lg-left { - float: left !important; - } - - .float-lg-right { - float: right !important; - } - - .float-lg-none { - float: none !important; - } -} -@media (min-width: 1200px) { - .float-xl-left { - float: left !important; - } - - .float-xl-right { - float: right !important; - } - - .float-xl-none { - float: none !important; - } -} -.user-select-all { - -webkit-user-select: all !important; - -moz-user-select: all !important; - -ms-user-select: all !important; - user-select: all !important; -} - -.user-select-auto { - -webkit-user-select: auto !important; - -moz-user-select: auto !important; - -ms-user-select: auto !important; - user-select: auto !important; -} - -.user-select-none { - -webkit-user-select: none !important; - -moz-user-select: none !important; - -ms-user-select: none !important; - user-select: none !important; -} - -.overflow-auto { - overflow: auto !important; -} - -.overflow-hidden { - overflow: hidden !important; -} - -.position-static { - position: static !important; -} - -.position-relative { - position: relative !important; -} - -.position-absolute { - position: absolute !important; -} - -.position-fixed { - position: fixed !important; -} - -.position-sticky { - position: sticky !important; -} - -.fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1030; -} - -.fixed-bottom { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 1030; -} - -@supports (position: sticky) { - .sticky-top { - position: sticky; - top: 0; - z-index: 1020; - } -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border: 0; -} - -.sr-only-focusable:active, .sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - overflow: visible; - clip: auto; - white-space: normal; -} - -.shadow-sm { - -webkit-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; -} - -.shadow { - -webkit-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; -} - -.shadow-lg { - -webkit-box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; - box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; -} - -.shadow-none { - -webkit-box-shadow: none !important; - box-shadow: none !important; -} - -.w-25 { - width: 25% !important; -} - -.w-50 { - width: 50% !important; -} - -.w-75 { - width: 75% !important; -} - -.w-100 { - width: 100% !important; -} - -.w-auto { - width: auto !important; -} - -.h-25 { - height: 25% !important; -} - -.h-50 { - height: 50% !important; -} - -.h-75 { - height: 75% !important; -} - -.h-100 { - height: 100% !important; -} - -.h-auto { - height: auto !important; -} - -.mw-100 { - max-width: 100% !important; -} - -.mh-100 { - max-height: 100% !important; -} - -.min-vw-100 { - min-width: 100vw !important; -} - -.min-vh-100 { - min-height: 100vh !important; -} - -.vw-100 { - width: 100vw !important; -} - -.vh-100 { - height: 100vh !important; -} - -.m-0 { - margin: 0 !important; -} - -.mt-0, -.my-0 { - margin-top: 0 !important; -} - -.mr-0, -.mx-0 { - margin-right: 0 !important; -} - -.mb-0, -.my-0 { - margin-bottom: 0 !important; -} - -.ml-0, -.mx-0 { - margin-left: 0 !important; -} - -.m-1 { - margin: 0.25rem !important; -} - -.mt-1, -.my-1 { - margin-top: 0.25rem !important; -} - -.mr-1, -.mx-1 { - margin-right: 0.25rem !important; -} - -.mb-1, -.my-1 { - margin-bottom: 0.25rem !important; -} - -.ml-1, -.mx-1 { - margin-left: 0.25rem !important; -} - -.m-2 { - margin: 0.5rem !important; -} - -.mt-2, -.my-2 { - margin-top: 0.5rem !important; -} - -.mr-2, -.mx-2 { - margin-right: 0.5rem !important; -} - -.mb-2, -.my-2 { - margin-bottom: 0.5rem !important; -} - -.ml-2, -.mx-2 { - margin-left: 0.5rem !important; -} - -.m-3 { - margin: 1rem !important; -} - -.mt-3, -.my-3 { - margin-top: 1rem !important; -} - -.mr-3, -.mx-3 { - margin-right: 1rem !important; -} - -.mb-3, -.my-3 { - margin-bottom: 1rem !important; -} - -.ml-3, -.mx-3 { - margin-left: 1rem !important; -} - -.m-4 { - margin: 1.5rem !important; -} - -.mt-4, -.my-4 { - margin-top: 1.5rem !important; -} - -.mr-4, -.mx-4 { - margin-right: 1.5rem !important; -} - -.mb-4, -.my-4 { - margin-bottom: 1.5rem !important; -} - -.ml-4, -.mx-4 { - margin-left: 1.5rem !important; -} - -.m-5 { - margin: 3rem !important; -} - -.mt-5, -.my-5 { - margin-top: 3rem !important; -} - -.mr-5, -.mx-5 { - margin-right: 3rem !important; -} - -.mb-5, -.my-5 { - margin-bottom: 3rem !important; -} - -.ml-5, -.mx-5 { - margin-left: 3rem !important; -} - -.p-0 { - padding: 0 !important; -} - -.pt-0, -.py-0 { - padding-top: 0 !important; -} - -.pr-0, -.px-0 { - padding-right: 0 !important; -} - -.pb-0, -.py-0 { - padding-bottom: 0 !important; -} - -.pl-0, -.px-0 { - padding-left: 0 !important; -} - -.p-1 { - padding: 0.25rem !important; -} - -.pt-1, -.py-1 { - padding-top: 0.25rem !important; -} - -.pr-1, -.px-1 { - padding-right: 0.25rem !important; -} - -.pb-1, -.py-1 { - padding-bottom: 0.25rem !important; -} - -.pl-1, -.px-1 { - padding-left: 0.25rem !important; -} - -.p-2 { - padding: 0.5rem !important; -} - -.pt-2, -.py-2 { - padding-top: 0.5rem !important; -} - -.pr-2, -.px-2 { - padding-right: 0.5rem !important; -} - -.pb-2, -.py-2 { - padding-bottom: 0.5rem !important; -} - -.pl-2, -.px-2 { - padding-left: 0.5rem !important; -} - -.p-3 { - padding: 1rem !important; -} - -.pt-3, -.py-3 { - padding-top: 1rem !important; -} - -.pr-3, -.px-3 { - padding-right: 1rem !important; -} - -.pb-3, -.py-3 { - padding-bottom: 1rem !important; -} - -.pl-3, -.px-3 { - padding-left: 1rem !important; -} - -.p-4 { - padding: 1.5rem !important; -} - -.pt-4, -.py-4 { - padding-top: 1.5rem !important; -} - -.pr-4, -.px-4 { - padding-right: 1.5rem !important; -} - -.pb-4, -.py-4 { - padding-bottom: 1.5rem !important; -} - -.pl-4, -.px-4 { - padding-left: 1.5rem !important; -} - -.p-5 { - padding: 3rem !important; -} - -.pt-5, -.py-5 { - padding-top: 3rem !important; -} - -.pr-5, -.px-5 { - padding-right: 3rem !important; -} - -.pb-5, -.py-5 { - padding-bottom: 3rem !important; -} - -.pl-5, -.px-5 { - padding-left: 3rem !important; -} - -.m-n1 { - margin: -0.25rem !important; -} - -.mt-n1, -.my-n1 { - margin-top: -0.25rem !important; -} - -.mr-n1, -.mx-n1 { - margin-right: -0.25rem !important; -} - -.mb-n1, -.my-n1 { - margin-bottom: -0.25rem !important; -} - -.ml-n1, -.mx-n1 { - margin-left: -0.25rem !important; -} - -.m-n2 { - margin: -0.5rem !important; -} - -.mt-n2, -.my-n2 { - margin-top: -0.5rem !important; -} - -.mr-n2, -.mx-n2 { - margin-right: -0.5rem !important; -} - -.mb-n2, -.my-n2 { - margin-bottom: -0.5rem !important; -} - -.ml-n2, -.mx-n2 { - margin-left: -0.5rem !important; -} - -.m-n3 { - margin: -1rem !important; -} - -.mt-n3, -.my-n3 { - margin-top: -1rem !important; -} - -.mr-n3, -.mx-n3 { - margin-right: -1rem !important; -} - -.mb-n3, -.my-n3 { - margin-bottom: -1rem !important; -} - -.ml-n3, -.mx-n3 { - margin-left: -1rem !important; -} - -.m-n4 { - margin: -1.5rem !important; -} - -.mt-n4, -.my-n4 { - margin-top: -1.5rem !important; -} - -.mr-n4, -.mx-n4 { - margin-right: -1.5rem !important; -} - -.mb-n4, -.my-n4 { - margin-bottom: -1.5rem !important; -} - -.ml-n4, -.mx-n4 { - margin-left: -1.5rem !important; -} - -.m-n5 { - margin: -3rem !important; -} - -.mt-n5, -.my-n5 { - margin-top: -3rem !important; -} - -.mr-n5, -.mx-n5 { - margin-right: -3rem !important; -} - -.mb-n5, -.my-n5 { - margin-bottom: -3rem !important; -} - -.ml-n5, -.mx-n5 { - margin-left: -3rem !important; -} - -.m-auto { - margin: auto !important; -} - -.mt-auto, -.my-auto { - margin-top: auto !important; -} - -.mr-auto, -.mx-auto { - margin-right: auto !important; -} - -.mb-auto, -.my-auto { - margin-bottom: auto !important; -} - -.ml-auto, -.mx-auto { - margin-left: auto !important; -} - -@media (min-width: 576px) { - .m-sm-0 { - margin: 0 !important; - } - - .mt-sm-0, - .my-sm-0 { - margin-top: 0 !important; - } - - .mr-sm-0, - .mx-sm-0 { - margin-right: 0 !important; - } - - .mb-sm-0, - .my-sm-0 { - margin-bottom: 0 !important; - } - - .ml-sm-0, - .mx-sm-0 { - margin-left: 0 !important; - } - - .m-sm-1 { - margin: 0.25rem !important; - } - - .mt-sm-1, - .my-sm-1 { - margin-top: 0.25rem !important; - } - - .mr-sm-1, - .mx-sm-1 { - margin-right: 0.25rem !important; - } - - .mb-sm-1, - .my-sm-1 { - margin-bottom: 0.25rem !important; - } - - .ml-sm-1, - .mx-sm-1 { - margin-left: 0.25rem !important; - } - - .m-sm-2 { - margin: 0.5rem !important; - } - - .mt-sm-2, - .my-sm-2 { - margin-top: 0.5rem !important; - } - - .mr-sm-2, - .mx-sm-2 { - margin-right: 0.5rem !important; - } - - .mb-sm-2, - .my-sm-2 { - margin-bottom: 0.5rem !important; - } - - .ml-sm-2, - .mx-sm-2 { - margin-left: 0.5rem !important; - } - - .m-sm-3 { - margin: 1rem !important; - } - - .mt-sm-3, - .my-sm-3 { - margin-top: 1rem !important; - } - - .mr-sm-3, - .mx-sm-3 { - margin-right: 1rem !important; - } - - .mb-sm-3, - .my-sm-3 { - margin-bottom: 1rem !important; - } - - .ml-sm-3, - .mx-sm-3 { - margin-left: 1rem !important; - } - - .m-sm-4 { - margin: 1.5rem !important; - } - - .mt-sm-4, - .my-sm-4 { - margin-top: 1.5rem !important; - } - - .mr-sm-4, - .mx-sm-4 { - margin-right: 1.5rem !important; - } - - .mb-sm-4, - .my-sm-4 { - margin-bottom: 1.5rem !important; - } - - .ml-sm-4, - .mx-sm-4 { - margin-left: 1.5rem !important; - } - - .m-sm-5 { - margin: 3rem !important; - } - - .mt-sm-5, - .my-sm-5 { - margin-top: 3rem !important; - } - - .mr-sm-5, - .mx-sm-5 { - margin-right: 3rem !important; - } - - .mb-sm-5, - .my-sm-5 { - margin-bottom: 3rem !important; - } - - .ml-sm-5, - .mx-sm-5 { - margin-left: 3rem !important; - } - - .p-sm-0 { - padding: 0 !important; - } - - .pt-sm-0, - .py-sm-0 { - padding-top: 0 !important; - } - - .pr-sm-0, - .px-sm-0 { - padding-right: 0 !important; - } - - .pb-sm-0, - .py-sm-0 { - padding-bottom: 0 !important; - } - - .pl-sm-0, - .px-sm-0 { - padding-left: 0 !important; - } - - .p-sm-1 { - padding: 0.25rem !important; - } - - .pt-sm-1, - .py-sm-1 { - padding-top: 0.25rem !important; - } - - .pr-sm-1, - .px-sm-1 { - padding-right: 0.25rem !important; - } - - .pb-sm-1, - .py-sm-1 { - padding-bottom: 0.25rem !important; - } - - .pl-sm-1, - .px-sm-1 { - padding-left: 0.25rem !important; - } - - .p-sm-2 { - padding: 0.5rem !important; - } - - .pt-sm-2, - .py-sm-2 { - padding-top: 0.5rem !important; - } - - .pr-sm-2, - .px-sm-2 { - padding-right: 0.5rem !important; - } - - .pb-sm-2, - .py-sm-2 { - padding-bottom: 0.5rem !important; - } - - .pl-sm-2, - .px-sm-2 { - padding-left: 0.5rem !important; - } - - .p-sm-3 { - padding: 1rem !important; - } - - .pt-sm-3, - .py-sm-3 { - padding-top: 1rem !important; - } - - .pr-sm-3, - .px-sm-3 { - padding-right: 1rem !important; - } - - .pb-sm-3, - .py-sm-3 { - padding-bottom: 1rem !important; - } - - .pl-sm-3, - .px-sm-3 { - padding-left: 1rem !important; - } - - .p-sm-4 { - padding: 1.5rem !important; - } - - .pt-sm-4, - .py-sm-4 { - padding-top: 1.5rem !important; - } - - .pr-sm-4, - .px-sm-4 { - padding-right: 1.5rem !important; - } - - .pb-sm-4, - .py-sm-4 { - padding-bottom: 1.5rem !important; - } - - .pl-sm-4, - .px-sm-4 { - padding-left: 1.5rem !important; - } - - .p-sm-5 { - padding: 3rem !important; - } - - .pt-sm-5, - .py-sm-5 { - padding-top: 3rem !important; - } - - .pr-sm-5, - .px-sm-5 { - padding-right: 3rem !important; - } - - .pb-sm-5, - .py-sm-5 { - padding-bottom: 3rem !important; - } - - .pl-sm-5, - .px-sm-5 { - padding-left: 3rem !important; - } - - .m-sm-n1 { - margin: -0.25rem !important; - } - - .mt-sm-n1, - .my-sm-n1 { - margin-top: -0.25rem !important; - } - - .mr-sm-n1, - .mx-sm-n1 { - margin-right: -0.25rem !important; - } - - .mb-sm-n1, - .my-sm-n1 { - margin-bottom: -0.25rem !important; - } - - .ml-sm-n1, - .mx-sm-n1 { - margin-left: -0.25rem !important; - } - - .m-sm-n2 { - margin: -0.5rem !important; - } - - .mt-sm-n2, - .my-sm-n2 { - margin-top: -0.5rem !important; - } - - .mr-sm-n2, - .mx-sm-n2 { - margin-right: -0.5rem !important; - } - - .mb-sm-n2, - .my-sm-n2 { - margin-bottom: -0.5rem !important; - } - - .ml-sm-n2, - .mx-sm-n2 { - margin-left: -0.5rem !important; - } - - .m-sm-n3 { - margin: -1rem !important; - } - - .mt-sm-n3, - .my-sm-n3 { - margin-top: -1rem !important; - } - - .mr-sm-n3, - .mx-sm-n3 { - margin-right: -1rem !important; - } - - .mb-sm-n3, - .my-sm-n3 { - margin-bottom: -1rem !important; - } - - .ml-sm-n3, - .mx-sm-n3 { - margin-left: -1rem !important; - } - - .m-sm-n4 { - margin: -1.5rem !important; - } - - .mt-sm-n4, - .my-sm-n4 { - margin-top: -1.5rem !important; - } - - .mr-sm-n4, - .mx-sm-n4 { - margin-right: -1.5rem !important; - } - - .mb-sm-n4, - .my-sm-n4 { - margin-bottom: -1.5rem !important; - } - - .ml-sm-n4, - .mx-sm-n4 { - margin-left: -1.5rem !important; - } - - .m-sm-n5 { - margin: -3rem !important; - } - - .mt-sm-n5, - .my-sm-n5 { - margin-top: -3rem !important; - } - - .mr-sm-n5, - .mx-sm-n5 { - margin-right: -3rem !important; - } - - .mb-sm-n5, - .my-sm-n5 { - margin-bottom: -3rem !important; - } - - .ml-sm-n5, - .mx-sm-n5 { - margin-left: -3rem !important; - } - - .m-sm-auto { - margin: auto !important; - } - - .mt-sm-auto, - .my-sm-auto { - margin-top: auto !important; - } - - .mr-sm-auto, - .mx-sm-auto { - margin-right: auto !important; - } - - .mb-sm-auto, - .my-sm-auto { - margin-bottom: auto !important; - } - - .ml-sm-auto, - .mx-sm-auto { - margin-left: auto !important; - } -} -@media (min-width: 768px) { - .m-md-0 { - margin: 0 !important; - } - - .mt-md-0, - .my-md-0 { - margin-top: 0 !important; - } - - .mr-md-0, - .mx-md-0 { - margin-right: 0 !important; - } - - .mb-md-0, - .my-md-0 { - margin-bottom: 0 !important; - } - - .ml-md-0, - .mx-md-0 { - margin-left: 0 !important; - } - - .m-md-1 { - margin: 0.25rem !important; - } - - .mt-md-1, - .my-md-1 { - margin-top: 0.25rem !important; - } - - .mr-md-1, - .mx-md-1 { - margin-right: 0.25rem !important; - } - - .mb-md-1, - .my-md-1 { - margin-bottom: 0.25rem !important; - } - - .ml-md-1, - .mx-md-1 { - margin-left: 0.25rem !important; - } - - .m-md-2 { - margin: 0.5rem !important; - } - - .mt-md-2, - .my-md-2 { - margin-top: 0.5rem !important; - } - - .mr-md-2, - .mx-md-2 { - margin-right: 0.5rem !important; - } - - .mb-md-2, - .my-md-2 { - margin-bottom: 0.5rem !important; - } - - .ml-md-2, - .mx-md-2 { - margin-left: 0.5rem !important; - } - - .m-md-3 { - margin: 1rem !important; - } - - .mt-md-3, - .my-md-3 { - margin-top: 1rem !important; - } - - .mr-md-3, - .mx-md-3 { - margin-right: 1rem !important; - } - - .mb-md-3, - .my-md-3 { - margin-bottom: 1rem !important; - } - - .ml-md-3, - .mx-md-3 { - margin-left: 1rem !important; - } - - .m-md-4 { - margin: 1.5rem !important; - } - - .mt-md-4, - .my-md-4 { - margin-top: 1.5rem !important; - } - - .mr-md-4, - .mx-md-4 { - margin-right: 1.5rem !important; - } - - .mb-md-4, - .my-md-4 { - margin-bottom: 1.5rem !important; - } - - .ml-md-4, - .mx-md-4 { - margin-left: 1.5rem !important; - } - - .m-md-5 { - margin: 3rem !important; - } - - .mt-md-5, - .my-md-5 { - margin-top: 3rem !important; - } - - .mr-md-5, - .mx-md-5 { - margin-right: 3rem !important; - } - - .mb-md-5, - .my-md-5 { - margin-bottom: 3rem !important; - } - - .ml-md-5, - .mx-md-5 { - margin-left: 3rem !important; - } - - .p-md-0 { - padding: 0 !important; - } - - .pt-md-0, - .py-md-0 { - padding-top: 0 !important; - } - - .pr-md-0, - .px-md-0 { - padding-right: 0 !important; - } - - .pb-md-0, - .py-md-0 { - padding-bottom: 0 !important; - } - - .pl-md-0, - .px-md-0 { - padding-left: 0 !important; - } - - .p-md-1 { - padding: 0.25rem !important; - } - - .pt-md-1, - .py-md-1 { - padding-top: 0.25rem !important; - } - - .pr-md-1, - .px-md-1 { - padding-right: 0.25rem !important; - } - - .pb-md-1, - .py-md-1 { - padding-bottom: 0.25rem !important; - } - - .pl-md-1, - .px-md-1 { - padding-left: 0.25rem !important; - } - - .p-md-2 { - padding: 0.5rem !important; - } - - .pt-md-2, - .py-md-2 { - padding-top: 0.5rem !important; - } - - .pr-md-2, - .px-md-2 { - padding-right: 0.5rem !important; - } - - .pb-md-2, - .py-md-2 { - padding-bottom: 0.5rem !important; - } - - .pl-md-2, - .px-md-2 { - padding-left: 0.5rem !important; - } - - .p-md-3 { - padding: 1rem !important; - } - - .pt-md-3, - .py-md-3 { - padding-top: 1rem !important; - } - - .pr-md-3, - .px-md-3 { - padding-right: 1rem !important; - } - - .pb-md-3, - .py-md-3 { - padding-bottom: 1rem !important; - } - - .pl-md-3, - .px-md-3 { - padding-left: 1rem !important; - } - - .p-md-4 { - padding: 1.5rem !important; - } - - .pt-md-4, - .py-md-4 { - padding-top: 1.5rem !important; - } - - .pr-md-4, - .px-md-4 { - padding-right: 1.5rem !important; - } - - .pb-md-4, - .py-md-4 { - padding-bottom: 1.5rem !important; - } - - .pl-md-4, - .px-md-4 { - padding-left: 1.5rem !important; - } - - .p-md-5 { - padding: 3rem !important; - } - - .pt-md-5, - .py-md-5 { - padding-top: 3rem !important; - } - - .pr-md-5, - .px-md-5 { - padding-right: 3rem !important; - } - - .pb-md-5, - .py-md-5 { - padding-bottom: 3rem !important; - } - - .pl-md-5, - .px-md-5 { - padding-left: 3rem !important; - } - - .m-md-n1 { - margin: -0.25rem !important; - } - - .mt-md-n1, - .my-md-n1 { - margin-top: -0.25rem !important; - } - - .mr-md-n1, - .mx-md-n1 { - margin-right: -0.25rem !important; - } - - .mb-md-n1, - .my-md-n1 { - margin-bottom: -0.25rem !important; - } - - .ml-md-n1, - .mx-md-n1 { - margin-left: -0.25rem !important; - } - - .m-md-n2 { - margin: -0.5rem !important; - } - - .mt-md-n2, - .my-md-n2 { - margin-top: -0.5rem !important; - } - - .mr-md-n2, - .mx-md-n2 { - margin-right: -0.5rem !important; - } - - .mb-md-n2, - .my-md-n2 { - margin-bottom: -0.5rem !important; - } - - .ml-md-n2, - .mx-md-n2 { - margin-left: -0.5rem !important; - } - - .m-md-n3 { - margin: -1rem !important; - } - - .mt-md-n3, - .my-md-n3 { - margin-top: -1rem !important; - } - - .mr-md-n3, - .mx-md-n3 { - margin-right: -1rem !important; - } - - .mb-md-n3, - .my-md-n3 { - margin-bottom: -1rem !important; - } - - .ml-md-n3, - .mx-md-n3 { - margin-left: -1rem !important; - } - - .m-md-n4 { - margin: -1.5rem !important; - } - - .mt-md-n4, - .my-md-n4 { - margin-top: -1.5rem !important; - } - - .mr-md-n4, - .mx-md-n4 { - margin-right: -1.5rem !important; - } - - .mb-md-n4, - .my-md-n4 { - margin-bottom: -1.5rem !important; - } - - .ml-md-n4, - .mx-md-n4 { - margin-left: -1.5rem !important; - } - - .m-md-n5 { - margin: -3rem !important; - } - - .mt-md-n5, - .my-md-n5 { - margin-top: -3rem !important; - } - - .mr-md-n5, - .mx-md-n5 { - margin-right: -3rem !important; - } - - .mb-md-n5, - .my-md-n5 { - margin-bottom: -3rem !important; - } - - .ml-md-n5, - .mx-md-n5 { - margin-left: -3rem !important; - } - - .m-md-auto { - margin: auto !important; - } - - .mt-md-auto, - .my-md-auto { - margin-top: auto !important; - } - - .mr-md-auto, - .mx-md-auto { - margin-right: auto !important; - } - - .mb-md-auto, - .my-md-auto { - margin-bottom: auto !important; - } - - .ml-md-auto, - .mx-md-auto { - margin-left: auto !important; - } -} -@media (min-width: 992px) { - .m-lg-0 { - margin: 0 !important; - } - - .mt-lg-0, - .my-lg-0 { - margin-top: 0 !important; - } - - .mr-lg-0, - .mx-lg-0 { - margin-right: 0 !important; - } - - .mb-lg-0, - .my-lg-0 { - margin-bottom: 0 !important; - } - - .ml-lg-0, - .mx-lg-0 { - margin-left: 0 !important; - } - - .m-lg-1 { - margin: 0.25rem !important; - } - - .mt-lg-1, - .my-lg-1 { - margin-top: 0.25rem !important; - } - - .mr-lg-1, - .mx-lg-1 { - margin-right: 0.25rem !important; - } - - .mb-lg-1, - .my-lg-1 { - margin-bottom: 0.25rem !important; - } - - .ml-lg-1, - .mx-lg-1 { - margin-left: 0.25rem !important; - } - - .m-lg-2 { - margin: 0.5rem !important; - } - - .mt-lg-2, - .my-lg-2 { - margin-top: 0.5rem !important; - } - - .mr-lg-2, - .mx-lg-2 { - margin-right: 0.5rem !important; - } - - .mb-lg-2, - .my-lg-2 { - margin-bottom: 0.5rem !important; - } - - .ml-lg-2, - .mx-lg-2 { - margin-left: 0.5rem !important; - } - - .m-lg-3 { - margin: 1rem !important; - } - - .mt-lg-3, - .my-lg-3 { - margin-top: 1rem !important; - } - - .mr-lg-3, - .mx-lg-3 { - margin-right: 1rem !important; - } - - .mb-lg-3, - .my-lg-3 { - margin-bottom: 1rem !important; - } - - .ml-lg-3, - .mx-lg-3 { - margin-left: 1rem !important; - } - - .m-lg-4 { - margin: 1.5rem !important; - } - - .mt-lg-4, - .my-lg-4 { - margin-top: 1.5rem !important; - } - - .mr-lg-4, - .mx-lg-4 { - margin-right: 1.5rem !important; - } - - .mb-lg-4, - .my-lg-4 { - margin-bottom: 1.5rem !important; - } - - .ml-lg-4, - .mx-lg-4 { - margin-left: 1.5rem !important; - } - - .m-lg-5 { - margin: 3rem !important; - } - - .mt-lg-5, - .my-lg-5 { - margin-top: 3rem !important; - } - - .mr-lg-5, - .mx-lg-5 { - margin-right: 3rem !important; - } - - .mb-lg-5, - .my-lg-5 { - margin-bottom: 3rem !important; - } - - .ml-lg-5, - .mx-lg-5 { - margin-left: 3rem !important; - } - - .p-lg-0 { - padding: 0 !important; - } - - .pt-lg-0, - .py-lg-0 { - padding-top: 0 !important; - } - - .pr-lg-0, - .px-lg-0 { - padding-right: 0 !important; - } - - .pb-lg-0, - .py-lg-0 { - padding-bottom: 0 !important; - } - - .pl-lg-0, - .px-lg-0 { - padding-left: 0 !important; - } - - .p-lg-1 { - padding: 0.25rem !important; - } - - .pt-lg-1, - .py-lg-1 { - padding-top: 0.25rem !important; - } - - .pr-lg-1, - .px-lg-1 { - padding-right: 0.25rem !important; - } - - .pb-lg-1, - .py-lg-1 { - padding-bottom: 0.25rem !important; - } - - .pl-lg-1, - .px-lg-1 { - padding-left: 0.25rem !important; - } - - .p-lg-2 { - padding: 0.5rem !important; - } - - .pt-lg-2, - .py-lg-2 { - padding-top: 0.5rem !important; - } - - .pr-lg-2, - .px-lg-2 { - padding-right: 0.5rem !important; - } - - .pb-lg-2, - .py-lg-2 { - padding-bottom: 0.5rem !important; - } - - .pl-lg-2, - .px-lg-2 { - padding-left: 0.5rem !important; - } - - .p-lg-3 { - padding: 1rem !important; - } - - .pt-lg-3, - .py-lg-3 { - padding-top: 1rem !important; - } - - .pr-lg-3, - .px-lg-3 { - padding-right: 1rem !important; - } - - .pb-lg-3, - .py-lg-3 { - padding-bottom: 1rem !important; - } - - .pl-lg-3, - .px-lg-3 { - padding-left: 1rem !important; - } - - .p-lg-4 { - padding: 1.5rem !important; - } - - .pt-lg-4, - .py-lg-4 { - padding-top: 1.5rem !important; - } - - .pr-lg-4, - .px-lg-4 { - padding-right: 1.5rem !important; - } - - .pb-lg-4, - .py-lg-4 { - padding-bottom: 1.5rem !important; - } - - .pl-lg-4, - .px-lg-4 { - padding-left: 1.5rem !important; - } - - .p-lg-5 { - padding: 3rem !important; - } - - .pt-lg-5, - .py-lg-5 { - padding-top: 3rem !important; - } - - .pr-lg-5, - .px-lg-5 { - padding-right: 3rem !important; - } - - .pb-lg-5, - .py-lg-5 { - padding-bottom: 3rem !important; - } - - .pl-lg-5, - .px-lg-5 { - padding-left: 3rem !important; - } - - .m-lg-n1 { - margin: -0.25rem !important; - } - - .mt-lg-n1, - .my-lg-n1 { - margin-top: -0.25rem !important; - } - - .mr-lg-n1, - .mx-lg-n1 { - margin-right: -0.25rem !important; - } - - .mb-lg-n1, - .my-lg-n1 { - margin-bottom: -0.25rem !important; - } - - .ml-lg-n1, - .mx-lg-n1 { - margin-left: -0.25rem !important; - } - - .m-lg-n2 { - margin: -0.5rem !important; - } - - .mt-lg-n2, - .my-lg-n2 { - margin-top: -0.5rem !important; - } - - .mr-lg-n2, - .mx-lg-n2 { - margin-right: -0.5rem !important; - } - - .mb-lg-n2, - .my-lg-n2 { - margin-bottom: -0.5rem !important; - } - - .ml-lg-n2, - .mx-lg-n2 { - margin-left: -0.5rem !important; - } - - .m-lg-n3 { - margin: -1rem !important; - } - - .mt-lg-n3, - .my-lg-n3 { - margin-top: -1rem !important; - } - - .mr-lg-n3, - .mx-lg-n3 { - margin-right: -1rem !important; - } - - .mb-lg-n3, - .my-lg-n3 { - margin-bottom: -1rem !important; - } - - .ml-lg-n3, - .mx-lg-n3 { - margin-left: -1rem !important; - } - - .m-lg-n4 { - margin: -1.5rem !important; - } - - .mt-lg-n4, - .my-lg-n4 { - margin-top: -1.5rem !important; - } - - .mr-lg-n4, - .mx-lg-n4 { - margin-right: -1.5rem !important; - } - - .mb-lg-n4, - .my-lg-n4 { - margin-bottom: -1.5rem !important; - } - - .ml-lg-n4, - .mx-lg-n4 { - margin-left: -1.5rem !important; - } - - .m-lg-n5 { - margin: -3rem !important; - } - - .mt-lg-n5, - .my-lg-n5 { - margin-top: -3rem !important; - } - - .mr-lg-n5, - .mx-lg-n5 { - margin-right: -3rem !important; - } - - .mb-lg-n5, - .my-lg-n5 { - margin-bottom: -3rem !important; - } - - .ml-lg-n5, - .mx-lg-n5 { - margin-left: -3rem !important; - } - - .m-lg-auto { - margin: auto !important; - } - - .mt-lg-auto, - .my-lg-auto { - margin-top: auto !important; - } - - .mr-lg-auto, - .mx-lg-auto { - margin-right: auto !important; - } - - .mb-lg-auto, - .my-lg-auto { - margin-bottom: auto !important; - } - - .ml-lg-auto, - .mx-lg-auto { - margin-left: auto !important; - } -} -@media (min-width: 1200px) { - .m-xl-0 { - margin: 0 !important; - } - - .mt-xl-0, - .my-xl-0 { - margin-top: 0 !important; - } - - .mr-xl-0, - .mx-xl-0 { - margin-right: 0 !important; - } - - .mb-xl-0, - .my-xl-0 { - margin-bottom: 0 !important; - } - - .ml-xl-0, - .mx-xl-0 { - margin-left: 0 !important; - } - - .m-xl-1 { - margin: 0.25rem !important; - } - - .mt-xl-1, - .my-xl-1 { - margin-top: 0.25rem !important; - } - - .mr-xl-1, - .mx-xl-1 { - margin-right: 0.25rem !important; - } - - .mb-xl-1, - .my-xl-1 { - margin-bottom: 0.25rem !important; - } - - .ml-xl-1, - .mx-xl-1 { - margin-left: 0.25rem !important; - } - - .m-xl-2 { - margin: 0.5rem !important; - } - - .mt-xl-2, - .my-xl-2 { - margin-top: 0.5rem !important; - } - - .mr-xl-2, - .mx-xl-2 { - margin-right: 0.5rem !important; - } - - .mb-xl-2, - .my-xl-2 { - margin-bottom: 0.5rem !important; - } - - .ml-xl-2, - .mx-xl-2 { - margin-left: 0.5rem !important; - } - - .m-xl-3 { - margin: 1rem !important; - } - - .mt-xl-3, - .my-xl-3 { - margin-top: 1rem !important; - } - - .mr-xl-3, - .mx-xl-3 { - margin-right: 1rem !important; - } - - .mb-xl-3, - .my-xl-3 { - margin-bottom: 1rem !important; - } - - .ml-xl-3, - .mx-xl-3 { - margin-left: 1rem !important; - } - - .m-xl-4 { - margin: 1.5rem !important; - } - - .mt-xl-4, - .my-xl-4 { - margin-top: 1.5rem !important; - } - - .mr-xl-4, - .mx-xl-4 { - margin-right: 1.5rem !important; - } - - .mb-xl-4, - .my-xl-4 { - margin-bottom: 1.5rem !important; - } - - .ml-xl-4, - .mx-xl-4 { - margin-left: 1.5rem !important; - } - - .m-xl-5 { - margin: 3rem !important; - } - - .mt-xl-5, - .my-xl-5 { - margin-top: 3rem !important; - } - - .mr-xl-5, - .mx-xl-5 { - margin-right: 3rem !important; - } - - .mb-xl-5, - .my-xl-5 { - margin-bottom: 3rem !important; - } - - .ml-xl-5, - .mx-xl-5 { - margin-left: 3rem !important; - } - - .p-xl-0 { - padding: 0 !important; - } - - .pt-xl-0, - .py-xl-0 { - padding-top: 0 !important; - } - - .pr-xl-0, - .px-xl-0 { - padding-right: 0 !important; - } - - .pb-xl-0, - .py-xl-0 { - padding-bottom: 0 !important; - } - - .pl-xl-0, - .px-xl-0 { - padding-left: 0 !important; - } - - .p-xl-1 { - padding: 0.25rem !important; - } - - .pt-xl-1, - .py-xl-1 { - padding-top: 0.25rem !important; - } - - .pr-xl-1, - .px-xl-1 { - padding-right: 0.25rem !important; - } - - .pb-xl-1, - .py-xl-1 { - padding-bottom: 0.25rem !important; - } - - .pl-xl-1, - .px-xl-1 { - padding-left: 0.25rem !important; - } - - .p-xl-2 { - padding: 0.5rem !important; - } - - .pt-xl-2, - .py-xl-2 { - padding-top: 0.5rem !important; - } - - .pr-xl-2, - .px-xl-2 { - padding-right: 0.5rem !important; - } - - .pb-xl-2, - .py-xl-2 { - padding-bottom: 0.5rem !important; - } - - .pl-xl-2, - .px-xl-2 { - padding-left: 0.5rem !important; - } - - .p-xl-3 { - padding: 1rem !important; - } - - .pt-xl-3, - .py-xl-3 { - padding-top: 1rem !important; - } - - .pr-xl-3, - .px-xl-3 { - padding-right: 1rem !important; - } - - .pb-xl-3, - .py-xl-3 { - padding-bottom: 1rem !important; - } - - .pl-xl-3, - .px-xl-3 { - padding-left: 1rem !important; - } - - .p-xl-4 { - padding: 1.5rem !important; - } - - .pt-xl-4, - .py-xl-4 { - padding-top: 1.5rem !important; - } - - .pr-xl-4, - .px-xl-4 { - padding-right: 1.5rem !important; - } - - .pb-xl-4, - .py-xl-4 { - padding-bottom: 1.5rem !important; - } - - .pl-xl-4, - .px-xl-4 { - padding-left: 1.5rem !important; - } - - .p-xl-5 { - padding: 3rem !important; - } - - .pt-xl-5, - .py-xl-5 { - padding-top: 3rem !important; - } - - .pr-xl-5, - .px-xl-5 { - padding-right: 3rem !important; - } - - .pb-xl-5, - .py-xl-5 { - padding-bottom: 3rem !important; - } - - .pl-xl-5, - .px-xl-5 { - padding-left: 3rem !important; - } - - .m-xl-n1 { - margin: -0.25rem !important; - } - - .mt-xl-n1, - .my-xl-n1 { - margin-top: -0.25rem !important; - } - - .mr-xl-n1, - .mx-xl-n1 { - margin-right: -0.25rem !important; - } - - .mb-xl-n1, - .my-xl-n1 { - margin-bottom: -0.25rem !important; - } - - .ml-xl-n1, - .mx-xl-n1 { - margin-left: -0.25rem !important; - } - - .m-xl-n2 { - margin: -0.5rem !important; - } - - .mt-xl-n2, - .my-xl-n2 { - margin-top: -0.5rem !important; - } - - .mr-xl-n2, - .mx-xl-n2 { - margin-right: -0.5rem !important; - } - - .mb-xl-n2, - .my-xl-n2 { - margin-bottom: -0.5rem !important; - } - - .ml-xl-n2, - .mx-xl-n2 { - margin-left: -0.5rem !important; - } - - .m-xl-n3 { - margin: -1rem !important; - } - - .mt-xl-n3, - .my-xl-n3 { - margin-top: -1rem !important; - } - - .mr-xl-n3, - .mx-xl-n3 { - margin-right: -1rem !important; - } - - .mb-xl-n3, - .my-xl-n3 { - margin-bottom: -1rem !important; - } - - .ml-xl-n3, - .mx-xl-n3 { - margin-left: -1rem !important; - } - - .m-xl-n4 { - margin: -1.5rem !important; - } - - .mt-xl-n4, - .my-xl-n4 { - margin-top: -1.5rem !important; - } - - .mr-xl-n4, - .mx-xl-n4 { - margin-right: -1.5rem !important; - } - - .mb-xl-n4, - .my-xl-n4 { - margin-bottom: -1.5rem !important; - } - - .ml-xl-n4, - .mx-xl-n4 { - margin-left: -1.5rem !important; - } - - .m-xl-n5 { - margin: -3rem !important; - } - - .mt-xl-n5, - .my-xl-n5 { - margin-top: -3rem !important; - } - - .mr-xl-n5, - .mx-xl-n5 { - margin-right: -3rem !important; - } - - .mb-xl-n5, - .my-xl-n5 { - margin-bottom: -3rem !important; - } - - .ml-xl-n5, - .mx-xl-n5 { - margin-left: -3rem !important; - } - - .m-xl-auto { - margin: auto !important; - } - - .mt-xl-auto, - .my-xl-auto { - margin-top: auto !important; - } - - .mr-xl-auto, - .mx-xl-auto { - margin-right: auto !important; - } - - .mb-xl-auto, - .my-xl-auto { - margin-bottom: auto !important; - } - - .ml-xl-auto, - .mx-xl-auto { - margin-left: auto !important; - } -} -.stretched-link::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1; - pointer-events: auto; - content: ""; - background-color: rgba(0, 0, 0, 0); -} - -.text-monospace { - font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; -} - -.text-justify { - text-align: justify !important; -} - -.text-wrap { - white-space: normal !important; -} - -.text-nowrap { - white-space: nowrap !important; -} - -.text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.text-left { - text-align: left !important; -} - -.text-right { - text-align: right !important; -} - -.text-center { - text-align: center !important; -} - -@media (min-width: 576px) { - .text-sm-left { - text-align: left !important; - } - - .text-sm-right { - text-align: right !important; - } - - .text-sm-center { - text-align: center !important; - } -} -@media (min-width: 768px) { - .text-md-left { - text-align: left !important; - } - - .text-md-right { - text-align: right !important; - } - - .text-md-center { - text-align: center !important; - } -} -@media (min-width: 992px) { - .text-lg-left { - text-align: left !important; - } - - .text-lg-right { - text-align: right !important; - } - - .text-lg-center { - text-align: center !important; - } -} -@media (min-width: 1200px) { - .text-xl-left { - text-align: left !important; - } - - .text-xl-right { - text-align: right !important; - } - - .text-xl-center { - text-align: center !important; - } -} -.text-lowercase { - text-transform: lowercase !important; -} - -.text-uppercase { - text-transform: uppercase !important; -} - -.text-capitalize { - text-transform: capitalize !important; -} - -.font-weight-light { - font-weight: 300 !important; -} - -.font-weight-lighter { - font-weight: lighter !important; -} - -.font-weight-normal { - font-weight: 400 !important; -} - -.font-weight-bold { - font-weight: 700 !important; -} - -.font-weight-bolder { - font-weight: bolder !important; -} - -.font-italic { - font-style: italic !important; -} - -.text-white { - color: #fff !important; -} - -.text-primary { - color: #007bff !important; -} - -a.text-primary:hover, a.text-primary:focus { - color: #0056b3 !important; -} - -.text-secondary { - color: #6c757d !important; -} - -a.text-secondary:hover, a.text-secondary:focus { - color: #494f54 !important; -} - -.text-success { - color: #28a745 !important; -} - -a.text-success:hover, a.text-success:focus { - color: #19692c !important; -} - -.text-info { - color: #17a2b8 !important; -} - -a.text-info:hover, a.text-info:focus { - color: #0f6674 !important; -} - -.text-warning { - color: #ffc107 !important; -} - -a.text-warning:hover, a.text-warning:focus { - color: #ba8b00 !important; -} - -.text-danger { - color: #dc3545 !important; -} - -a.text-danger:hover, a.text-danger:focus { - color: #a71d2a !important; -} - -.text-light { - color: #f8f9fa !important; -} - -a.text-light:hover, a.text-light:focus { - color: #cbd3da !important; -} - -.text-dark { - color: #343a40 !important; -} - -a.text-dark:hover, a.text-dark:focus { - color: #121416 !important; -} - -.text-body { - color: #212529 !important; -} - -.text-muted { - color: #6c757d !important; -} - -.text-black-50 { - color: rgba(0, 0, 0, 0.5) !important; -} - -.text-white-50 { - color: rgba(255, 255, 255, 0.5) !important; -} - -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} - -.text-decoration-none { - text-decoration: none !important; -} - -.text-break { - word-break: break-word !important; - word-wrap: break-word !important; -} - -.text-reset { - color: inherit !important; -} - -.visible { - visibility: visible !important; -} - -.invisible { - visibility: hidden !important; -} - -@media print { - *, - *::before, - *::after { - text-shadow: none !important; - -webkit-box-shadow: none !important; - box-shadow: none !important; - } - - a:not(.btn) { - text-decoration: underline; - } - - abbr[title]::after { - content: " (" attr(title) ")"; - } - - pre { - white-space: pre-wrap !important; - } - - pre, - blockquote { - border: 1px solid #adb5bd; - page-break-inside: avoid; - } - - tr, - img { - page-break-inside: avoid; - } - - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - - h2, - h3 { - page-break-after: avoid; - } - - @page { - size: a3; - } - body { - min-width: 992px !important; - } - - .container { - min-width: 992px !important; - } - - .navbar { - display: none; - } - - .badge { - border: 1px solid #000; - } - - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - - .table-bordered th, - .table-bordered td { - border: 1px solid #dee2e6 !important; - } - - .table-dark { - color: inherit; - } - .table-dark th, - .table-dark td, - .table-dark thead th, - .table-dark tbody + tbody { - border-color: #dee2e6; - } - - .table .thead-dark th { - color: inherit; - border-color: #dee2e6; - } -} -/*Github syntax highlighting theme via Rouge*/ -.highlight table td { - padding: 5px; -} - -.highlight table pre { - margin: 0; -} - -.highlight .cm { - color: #999988; - font-style: italic; -} - -.highlight .cp { - color: #999999; - font-weight: bold; -} - -.highlight .c1 { - color: #999988; - font-style: italic; -} - -.highlight .cs { - color: #999999; - font-weight: bold; - font-style: italic; -} - -.highlight .c, .highlight .cd { - color: #999988; - font-style: italic; -} - -.highlight .err { - color: #a61717; - background-color: #e3d2d2; -} - -.highlight .gd { - color: #000000; - background-color: #ffdddd; -} - -.highlight .ge { - color: #000000; - font-style: italic; -} - -.highlight .gr { - color: #aa0000; -} - -.highlight .gh { - color: #999999; -} - -.highlight .gi { - color: #000000; - background-color: #ddffdd; -} - -.highlight .go { - color: #888888; -} - -.highlight .gp { - color: #555555; -} - -.highlight .gs { - font-weight: bold; -} - -.highlight .gu { - color: #aaaaaa; -} - -.highlight .gt { - color: #aa0000; -} - -.highlight .kc { - color: #000000; - font-weight: bold; -} - -.highlight .kd { - color: #000000; - font-weight: bold; -} - -.highlight .kn { - color: #000000; - font-weight: bold; -} - -.highlight .kp { - color: #000000; - font-weight: bold; -} - -.highlight .kr { - color: #000000; - font-weight: bold; -} - -.highlight .kt { - color: #445588; - font-weight: bold; -} - -.highlight .k, .highlight .kv { - color: #000000; - font-weight: bold; -} - -.highlight .mf { - color: #009999; -} - -.highlight .mh { - color: #009999; -} - -.highlight .il { - color: #009999; -} - -.highlight .mi { - color: #009999; -} - -.highlight .mo { - color: #009999; -} - -.highlight .m, .highlight .mb, .highlight .mx { - color: #009999; -} - -.highlight .sb { - color: #d14; -} - -.highlight .sc { - color: #d14; -} - -.highlight .sd { - color: #d14; -} - -.highlight .s2 { - color: #d14; -} - -.highlight .se { - color: #d14; -} - -.highlight .sh { - color: #d14; -} - -.highlight .si { - color: #d14; -} - -.highlight .sx { - color: #d14; -} - -.highlight .sr { - color: #009926; -} - -.highlight .s1 { - color: #d14; -} - -.highlight .ss { - color: #990073; -} - -.highlight .s { - color: #d14; -} - -.highlight .na { - color: #008080; -} - -.highlight .bp { - color: #525252; -} - -.highlight .nb { - color: #0086B3; -} - -.highlight .nc { - color: #445588; - font-weight: bold; -} - -.highlight .no { - color: #008080; -} - -.highlight .nd { - color: #3c5d5d; - font-weight: bold; -} - -.highlight .ni { - color: #800080; -} - -.highlight .ne { - color: #990000; - font-weight: bold; -} - -.highlight .nf { - color: #990000; - font-weight: bold; -} - -.highlight .nl { - color: #990000; - font-weight: bold; -} - -.highlight .nn { - color: #555555; -} - -.highlight .nt { - color: #000080; -} - -.highlight .vc { - color: #008080; -} - -.highlight .vg { - color: #008080; -} - -.highlight .vi { - color: #008080; -} - -.highlight .nv { - color: #008080; -} - -.highlight .ow { - color: #000000; - font-weight: bold; -} - -.highlight .o { - color: #000000; - font-weight: bold; -} - -.highlight .n { - color: #000000; - font-weight: bold; -} - -.highlight .p { - color: #000000; - font-weight: bold; -} - -.highlight .w { - color: #bbbbbb; -} - -.highlight { - background-color: #f8f8f8; -} - -@font-face { - font-family: FreightSans; - font-weight: 700; - font-style: normal; - src: url("../fonts/FreightSans/freight-sans-bold.woff2") format("woff2"), url("../fonts/FreightSans/freight-sans-bold.woff") format("woff"); -} -@font-face { - font-family: FreightSans; - font-weight: 700; - font-style: italic; - src: url("../fonts/FreightSans/freight-sans-bold-italic.woff2") format("woff2"), url("../fonts/FreightSans/freight-sans-bold-italic.woff") format("woff"); -} -@font-face { - font-family: FreightSans; - font-weight: 500; - font-style: normal; - src: url("../fonts/FreightSans/freight-sans-medium.woff2") format("woff2"), url("../fonts/FreightSans/freight-sans-medium.woff") format("woff"); -} -@font-face { - font-family: FreightSans; - font-weight: 500; - font-style: italic; - src: url("../fonts/FreightSans/freight-sans-medium-italic.woff2") format("woff2"), url("../fonts/FreightSans/freight-sans-medium-italic.woff") format("woff"); -} -@font-face { - font-family: FreightSans; - font-weight: 100; - font-style: normal; - src: url("../fonts/FreightSans/freight-sans-light.woff2") format("woff2"), url("../fonts/FreightSans/freight-sans-light.woff") format("woff"); -} -@font-face { - font-family: FreightSans; - font-weight: 100; - font-style: italic; - src: url("../fonts/FreightSans/freight-sans-light-italic.woff2") format("woff2"), url("../fonts/FreightSans/freight-sans-light-italic.woff") format("woff"); -} -@font-face { - font-family: FreightSans; - font-weight: 400; - font-style: italic; - src: url("../fonts/FreightSans/freight-sans-book-italic.woff2") format("woff2"), url("../fonts/FreightSans/freight-sans-book-italic.woff") format("woff"); -} -@font-face { - font-family: FreightSans; - font-weight: 400; - font-style: normal; - src: url("../fonts/FreightSans/freight-sans-book.woff2") format("woff2"), url("../fonts/FreightSans/freight-sans-book.woff") format("woff"); -} -@font-face { - font-family: IBMPlexMono; - font-weight: 600; - font-style: normal; - unicode-range: u+0020-007f; - src: local("IBMPlexMono-SemiBold"), url("../fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff2") format("woff2"), url("../fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff") format("woff"); -} -@font-face { - font-family: IBMPlexMono; - font-weight: 500; - font-style: normal; - unicode-range: u+0020-007f; - src: local("IBMPlexMono-Medium"), url("../fonts/IBMPlexMono/IBMPlexMono-Medium.woff2") format("woff2"), url("../fonts/IBMPlexMono/IBMPlexMono-Medium.woff") format("woff"); -} -@font-face { - font-family: IBMPlexMono; - font-weight: 400; - font-style: normal; - unicode-range: u+0020-007f; - src: local("IBMPlexMono-Regular"), url("../fonts/IBMPlexMono/IBMPlexMono-Regular.woff2") format("woff2"), url("../fonts/IBMPlexMono/IBMPlexMono-Regular.woff") format("woff"); -} -@font-face { - font-family: IBMPlexMono; - font-weight: 300; - font-style: normal; - unicode-range: u+0020-007f; - src: local("IBMPlexMono-Light"), url("../fonts/IBMPlexMono/IBMPlexMono-Light.woff2") format("woff2"), url("../fonts/IBMPlexMono/IBMPlexMono-Light.woff") format("woff"); -} -@font-face { - font-family: UCity; - font-weight: 600; - font-style: normal; - src: local("UCity-Semibold"), url("../fonts/UCity/UCity-Semibold.woff2") format("woff2"); -} -@font-face { - font-family: UCity; - font-weight: 400; - font-style: normal; - src: local("UCity-Regular"), url("../fonts/UCity/UCity-Regular.woff2") format("woff2"); -} -@font-face { - font-family: UCity; - font-weight: 300; - font-style: normal; - src: local("UCity-Light"), url("../fonts/UCity/UCity-Light.woff2") format("woff2"); -} -html { - position: relative; - min-height: 100%; - font-size: 12px; -} -@media screen and (min-width: 768px) { - html { - font-size: 16px; - } -} - -* { - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -body { - font-family: FreightSans, Helvetica Neue, Helvetica, Arial, sans-serif; -} - -a:link, -a:visited, -a:hover { - text-decoration: none; - color: #792ee5; -} - -a.with-right-arrow, .btn.with-right-arrow { - padding-right: 1.375rem; - position: relative; - background-image: url("../images/chevron-right-orange.svg"); - background-size: 6px 13px; - background-position: center right 5px; - background-repeat: no-repeat; -} -@media screen and (min-width: 768px) { - a.with-right-arrow, .btn.with-right-arrow { - background-size: 8px 14px; - background-position: center right 12px; - padding-right: 2rem; - } -} - -::-webkit-input-placeholder { - color: #792ee5; -} - -::-moz-placeholder { - color: #792ee5; -} - -:-ms-input-placeholder { - color: #792ee5; -} - -:-moz-placeholder { - color: #792ee5; -} - -.email-subscribe-form input.email { - color: #792ee5; - border: none; - border-bottom: 1px solid #939393; - width: 100%; - background-color: transparent; - outline: none; - font-size: 1.125rem; - letter-spacing: 0.25px; - line-height: 2.25rem; -} -.email-subscribe-form input[type="submit"] { - position: absolute; - right: 0; - top: 10px; - height: 15px; - width: 15px; - background-image: url("../images/arrow-right-with-tail.svg"); - background-color: transparent; - background-repeat: no-repeat; - background-size: 15px 15px; - background-position: center center; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - border: 0; -} - -.email-subscribe-form-fields-wrapper { - position: relative; -} - -.anchorjs-link { - color: #6c6c6d !important; -} -@media screen and (min-width: 768px) { - .anchorjs-link:hover { - color: inherit; - text-decoration: none !important; - } -} - -.pytorch-article #table-of-contents { - display: none; -} - -.badge { - font-weight: inherit; - border-radius: 3px; -} - -.badge-primary { - background-color: #792ee5; -} - -.badge-secondary { - background-color: #979797; -} - -code, kbd, pre, samp { - font-family: "Inconsolata", "IBMPlexMono", "SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", "monospace"; -} - -code span, kbd span, pre span, samp span { - font-family: "Inconsolata", "IBMPlexMono", "SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", "monospace"; -} - -pre { - padding: 1.125rem; - background-color: #f3f4f7; -} -pre code { - font-size: 0.875rem; -} -pre.highlight { - background-color: #f3f4f7; - line-height: 1.3125rem; -} - -code.highlighter-rouge { - color: #6c6c6d; - background-color: #f3f4f7; - padding: 2px 6px; -} - -a:link code.highlighter-rouge, -a:visited code.highlighter-rouge, -a:hover code.highlighter-rouge { - color: #fe6162; -} -a:link.has-code, -a:visited.has-code, -a:hover.has-code { - color: #fe6162; -} - -p code, -h1 code, -h2 code, -h3 code, -h4 code, -h5 code, -h6 code { - font-size: 78.5%; -} - -pre { - white-space: pre-wrap; - white-space: -moz-pre-wrap; - white-space: -pre-wrap; - white-space: -o-pre-wrap; - word-wrap: break-word; -} - -.header-holder { - height: 68px; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - left: 0; - margin-left: auto; - margin-right: auto; - position: fixed; - right: 0; - top: 0; - width: 100%; - z-index: 9999; - background-color: #ffffff; - border-bottom: 1px solid #e2e2e2; -} -@media screen and (min-width: 1100px) { - .header-holder { - height: 90px; - } -} - -.header-container { - position: relative; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} -.header-container:before, .header-container:after { - content: ""; - display: table; -} -.header-container:after { - clear: both; -} -.header-container { - *zoom: 1; -} -@media screen and (min-width: 1100px) { - .header-container { - display: block; - } -} - -.header-logo { - height: 32px; - width: calc(100% - 25px); - background-image: url("../images/logo_light.svg"), url("../images/logo.svg"); - background-repeat: no-repeat; - background-size: contain; - display: block; - float: left; - z-index: 10; -} -@media screen and (min-width: 1100px) { - .header-logo { - background-size: contain; - position: absolute; - height: 43px; - width: 25%; - top: 4px; - float: none; - } -} -@media screen and (min-width: 1600px) { - .header-logo { - width: 350px; - } -} - -.main-menu-open-button { - background-image: url("../images/icon-menu-dots.svg"); - background-position: center center; - background-size: 25px 7px; - background-repeat: no-repeat; - width: 25px; - height: 17px; - position: absolute; - right: 0; - top: 8px; -} -@media screen and (min-width: 1100px) { - .main-menu-open-button { - display: none; - } -} - -.header-holder .main-menu { - display: none; -} -@media screen and (min-width: 1100px) { - .header-holder .main-menu { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - -webkit-box-pack: end; - -ms-flex-pack: end; - justify-content: flex-end; - } -} -.header-holder .main-menu ul { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - margin: 0; -} -.header-holder .main-menu ul li { - display: inline-block; - margin-right: 40px; - position: relative; -} -.header-holder .main-menu ul li.active:after { - content: "•"; - bottom: -24px; - color: #792ee5; - font-size: 1.375rem; - left: 0; - position: absolute; - right: 0; - text-align: center; -} -.header-holder .main-menu ul li.active a { - color: #792ee5; -} -.header-holder .main-menu ul li.docs-active:after { - content: "•"; - bottom: -24px; - color: #792ee5; - font-size: 1.375rem; - left: -24px; - position: absolute; - right: 0; - text-align: center; -} -.header-holder .main-menu ul li:last-of-type { - margin-right: 0; -} -.header-holder .main-menu ul li a { - color: #1C1C1C; - font-family: UCity; - font-size: 14px; - font-weight: 600; - line-height: 1.25rem; - letter-spacing: 0px; - text-align: left; - text-decoration: none; -} -@media screen and (min-width: 1100px) { - .header-holder .main-menu ul li a:hover { - color: #792ee5; - } -} - -.mobile-main-menu { - display: none; -} -.mobile-main-menu.open { - background-color: #262626; - display: block; - height: 100%; - left: 0; - margin-left: auto; - margin-right: auto; - min-height: 100%; - position: fixed; - right: 0; - top: 0; - width: 100%; - z-index: 99999; -} - -.mobile-main-menu .container-fluid, .mobile-main-menu .container-sm, .mobile-main-menu .container-md, .mobile-main-menu .container-lg, .mobile-main-menu .container-xl { - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - height: 68px; - position: relative; -} -.mobile-main-menu .container-fluid:before, .mobile-main-menu .container-sm:before, .mobile-main-menu .container-md:before, .mobile-main-menu .container-lg:before, .mobile-main-menu .container-xl:before, .mobile-main-menu .container-fluid:after, .mobile-main-menu .container-sm:after, .mobile-main-menu .container-md:after, .mobile-main-menu .container-lg:after, .mobile-main-menu .container-xl:after { - content: ""; - display: table; -} -.mobile-main-menu .container-fluid:after, .mobile-main-menu .container-sm:after, .mobile-main-menu .container-md:after, .mobile-main-menu .container-lg:after, .mobile-main-menu .container-xl:after { - clear: both; -} -.mobile-main-menu .container-fluid, .mobile-main-menu .container-sm, .mobile-main-menu .container-md, .mobile-main-menu .container-lg, .mobile-main-menu .container-xl { - *zoom: 1; -} - -.mobile-main-menu.open ul { - list-style-type: none; - padding: 0; -} -.mobile-main-menu.open ul li a, .mobile-main-menu.open .resources-mobile-menu-title { - font-size: 2rem; - color: #ffffff; - letter-spacing: 0; - line-height: 4rem; - text-decoration: none; -} -.mobile-main-menu.open ul li.active a { - color: #792ee5; -} - -.main-menu-close-button { - background-image: url("../images/icon-close.svg"); - background-position: center center; - background-repeat: no-repeat; - background-size: 24px 24px; - height: 24px; - position: absolute; - right: 0; - width: 24px; - top: 1px; -} - -.mobile-main-menu-header-container { - position: relative; -} - -.mobile-main-menu-links-container { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - padding-left: 2.8125rem; - height: calc(100% - 68px); - overflow-y: scroll; -} -.mobile-main-menu-links-container .main-menu { - height: 100%; -} - -.mobile-main-menu-links-container ul.resources-mobile-menu-items li { - padding-left: 15px; -} - -.site-footer { - padding: 2.5rem 0; - width: 100%; - background: #000000; - background-size: 100%; - margin-left: 0; - margin-right: 0; - position: relative; - z-index: 201; -} -@media screen and (min-width: 768px) { - .site-footer { - padding: 5rem 0; - } -} -.site-footer p { - color: #ffffff; -} -.site-footer ul { - list-style-type: none; - padding-left: 0; - margin-bottom: 0; -} -.site-footer ul li { - font-size: 1.125rem; - line-height: 2rem; - color: #A0A0A1; - padding-bottom: 0.375rem; -} -.site-footer ul li.list-title { - padding-bottom: 0.75rem; - color: #ffffff; -} -.site-footer a:link, -.site-footer a:visited { - color: inherit; -} -@media screen and (min-width: 768px) { - .site-footer a:hover { - color: #792ee5; - } -} - -.docs-tutorials-resources { - background-color: #262626; - color: #ffffff; - padding-top: 2.5rem; - padding-bottom: 2.5rem; - position: relative; - z-index: 201; -} -@media screen and (min-width: 768px) { - .docs-tutorials-resources { - padding-top: 5rem; - padding-bottom: 5rem; - } -} -.docs-tutorials-resources p { - color: #929292; - font-size: 1.125rem; -} -.docs-tutorials-resources h2 { - font-size: 1.5rem; - letter-spacing: -0.25px; - text-transform: none; - margin-bottom: 0.25rem; -} -@media screen and (min-width: 768px) { - .docs-tutorials-resources h2 { - margin-bottom: 1.25rem; - } -} -.docs-tutorials-resources .col-md-4 { - margin-bottom: 2rem; - text-align: center; -} -@media screen and (min-width: 768px) { - .docs-tutorials-resources .col-md-4 { - margin-bottom: 0; - } -} -.docs-tutorials-resources .with-right-arrow { - margin-left: 12px; -} -.docs-tutorials-resources .with-right-arrow:hover { - background-image: url("../images/chevron-right-white.svg"); -} -.docs-tutorials-resources p { - font-size: 1rem; - line-height: 1.5rem; - letter-spacing: 0.22px; - color: #939393; - margin-bottom: 0; -} -@media screen and (min-width: 768px) { - .docs-tutorials-resources p { - margin-bottom: 1.25rem; - } -} -.docs-tutorials-resources a { - font-size: 1.125rem; - color: #792ee5; -} -.docs-tutorials-resources a:hover { - color: #ffffff; -} - -.footer-container { - position: relative; -} - -@media screen and (min-width: 768px) { - .footer-logo-wrapper { - position: absolute; - top: 0; - left: 30px; - } -} - -.footer-logo { - background-image: url("../images/logo-icon.svg"); - background-position: center; - background-repeat: no-repeat; - background-size: 20px 24px; - display: block; - height: 24px; - margin-bottom: 2.8125rem; - width: 20px; -} -@media screen and (min-width: 768px) { - .footer-logo { - background-size: 29px 36px; - height: 36px; - margin-bottom: 0; - margin-bottom: 0; - width: 29px; - } -} - -.footer-links-wrapper { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; -} -@media screen and (min-width: 768px) { - .footer-links-wrapper { - -ms-flex-wrap: initial; - flex-wrap: initial; - -webkit-box-pack: end; - -ms-flex-pack: end; - justify-content: flex-end; - } -} - -.footer-links-col { - margin-bottom: 3.75rem; - width: 50%; -} -@media screen and (min-width: 768px) { - .footer-links-col { - margin-bottom: 0; - width: 14%; - margin-right: 23px; - } - .footer-links-col.follow-us-col { - width: 18%; - margin-right: 0; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - .footer-links-col { - width: 18%; - margin-right: 30px; - } -} - -.footer-social-icons { - margin: 8.5625rem 0 2.5rem 0; -} -.footer-social-icons a { - height: 32px; - width: 32px; - display: inline-block; - background-color: #CCCDD1; - border-radius: 50%; - margin-right: 5px; -} -.footer-social-icons a.facebook { - background-image: url("../images/logo-facebook-dark.svg"); - background-position: center center; - background-size: 9px 18px; - background-repeat: no-repeat; -} -.footer-social-icons a.twitter { - background-image: url("../images/logo-twitter-dark.svg"); - background-position: center center; - background-size: 17px 17px; - background-repeat: no-repeat; -} -.footer-social-icons a.youtube { - background-image: url("../images/logo-youtube-dark.svg"); - background-position: center center; - background-repeat: no-repeat; -} - -.site-footer .mc-field-group { - margin-top: -2px; -} - -article.pytorch-article { - max-width: 920px; - margin: 0 auto; -} -article.pytorch-article h2, -article.pytorch-article h3, -article.pytorch-article h4, -article.pytorch-article h5, -article.pytorch-article h6 { - margin: 1.375rem 0; - color: #262626; -} -article.pytorch-article h2 { - font-size: 1.625rem; - letter-spacing: 1.33px; - line-height: 2rem; - text-transform: none; -} -article.pytorch-article h3 { - font-weight: 600; - font-size: 1.25rem; - line-height: 1.5rem; - text-transform: none; -} -article.pytorch-article h4, -article.pytorch-article h5, -article.pytorch-article h6 { - font-size: 1.125rem; - letter-spacing: -0.19px; - line-height: 1.875rem; -} -article.pytorch-article p { - margin-bottom: 1.125rem; -} -article.pytorch-article p, -article.pytorch-article ul li, -article.pytorch-article ol li, -article.pytorch-article dl dt, -article.pytorch-article dl dd, -article.pytorch-article blockquote { - font-size: 1rem; - line-height: 1.375rem; - color: #262626; - letter-spacing: 0.01px; - font-weight: 500; -} -article.pytorch-article table { - margin-bottom: 2.5rem; - width: 100%; -} -article.pytorch-article table thead { - border-bottom: 1px solid #cacaca; -} -article.pytorch-article table th { - padding: 0.625rem; - color: #262626; -} -article.pytorch-article table td { - padding: 0.3125rem; -} -article.pytorch-article table tr th:first-of-type, -article.pytorch-article table tr td:first-of-type { - padding-left: 0; -} -article.pytorch-article table.docutils.field-list th.field-name { - padding: 0.3125rem; - padding-left: 0; -} -article.pytorch-article table.docutils.field-list td.field-body { - padding: 0.3125rem; -} -article.pytorch-article table.docutils.field-list td.field-body p:last-of-type { - margin-bottom: 0; -} -article.pytorch-article ul, -article.pytorch-article ol { - margin: 1.5rem 0 3.125rem 0; -} -@media screen and (min-width: 768px) { - article.pytorch-article ul, - article.pytorch-article ol { - padding-left: 6.25rem; - } -} -article.pytorch-article ul li, -article.pytorch-article ol li { - margin-bottom: 0.625rem; -} -article.pytorch-article dl { - margin-bottom: 1.5rem; -} -article.pytorch-article dl dt { - margin-bottom: 0.75rem; -} -article.pytorch-article pre { - margin-bottom: 2.5rem; -} -article.pytorch-article hr { - margin-top: 4.6875rem; - margin-bottom: 4.6875rem; -} -article.pytorch-article blockquote { - margin: 0 auto; - margin-bottom: 2.5rem; - width: 65%; -} -article.pytorch-article .hidden { - display: none; -} -article.pytorch-article img { - width: 100%; -} - -html { - height: 100%; -} -@media screen and (min-width: 768px) { - html { - font-size: 16px; - } -} - -body { - background: #ffffff; - height: 100%; - margin: 0; -} -body.no-scroll { - height: 100%; - overflow: hidden; -} - -p { - margin-top: 0; - margin-bottom: 1.125rem; -} -p a:link, -p a:visited, -p a:hover { - color: #792ee5; - text-decoration: none; -} -@media screen and (min-width: 768px) { - p a:hover { - text-decoration: underline; - } -} -p a:link, -p a:visited, -p a:hover { - color: #792ee5; -} - -.wy-breadcrumbs li a { - color: #792ee5; -} - -ul.pytorch-breadcrumbs { - padding-left: 0; - list-style-type: none; -} -ul.pytorch-breadcrumbs li { - display: inline-block; - font-size: 0.875rem; -} -ul.pytorch-breadcrumbs a { - color: #792ee5; - text-decoration: none; -} - -.table-of-contents-link-wrapper { - display: block; - margin-top: 0; - padding: 1.25rem 1.875rem; - background-color: #f3f4f7; - position: relative; - color: #262626; - font-size: 1.25rem; -} -.table-of-contents-link-wrapper.is-open .toggle-table-of-contents { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); -} -@media screen and (min-width: 1100px) { - .table-of-contents-link-wrapper { - display: none; - } -} - -.toggle-table-of-contents { - background-image: url("../images/chevron-down-grey.svg"); - background-position: center center; - background-repeat: no-repeat; - background-size: 18px 18px; - height: 100%; - position: absolute; - right: 21px; - width: 30px; - top: 0; -} - -.tutorials-header .header-logo { - background-image: url("../images/logo.svg"); - height: 38px; - top: -10px; -} -.tutorials-header .main-menu ul li a { - color: #262626; -} -.tutorials-header .main-menu-open-button { - background-image: url("../images/icon-menu-dots-dark.svg"); -} - -.rst-content footer .rating-hr.hr-top { - margin-bottom: -0.0625rem; -} -.rst-content footer .rating-hr.hr-bottom { - margin-top: -0.0625rem; -} -.rst-content footer .rating-container { - display: -webkit-inline-box; - display: -ms-inline-flexbox; - display: inline-flex; - font-size: 1.125rem; -} -.rst-content footer .rating-container .rating-prompt, .rst-content footer .rating-container .was-helpful-thank-you { - padding: 0.625rem 1.25rem 0.625rem 1.25rem; -} -.rst-content footer .rating-container .was-helpful-thank-you { - display: none; -} -.rst-content footer .rating-container .rating-prompt.yes-link, .rst-content footer .rating-container .rating-prompt.no-link { - color: #792ee5; - cursor: pointer; -} -.rst-content footer .rating-container .rating-prompt.yes-link:hover, .rst-content footer .rating-container .rating-prompt.no-link:hover { - background-color: #792ee5; - color: #ffffff; -} -.rst-content footer .rating-container .stars-outer { - display: inline-block; - position: relative; - font-family: FontAwesome; - padding: 0.625rem 1.25rem 0.625rem 1.25rem; -} -.rst-content footer .rating-container .stars-outer i { - cursor: pointer; -} -.rst-content footer .rating-container .stars-outer .star-fill { - color: #792ee5; -} -.rst-content footer div[role="contentinfo"] { - padding-top: 2.5rem; -} -.rst-content footer div[role="contentinfo"] p { - margin-bottom: 0; -} - -h1 { - font-size: 2rem; - letter-spacing: 1.78px; - line-height: 2.5rem; - text-transform: uppercase; - margin: 1.375rem 0; -} - -span.pre { - color: #6c6c6d; - background-color: #f3f4f7; - padding: 2px 6px; -} - -pre { - background-color: #f3f4f7; - padding: 1.375rem; -} - -.highlight .c1 { - color: #6c6c6d; -} - -.headerlink { - display: none !important; -} - -a:link.has-code, -a:hover.has-code, -a:visited.has-code { - color: #fe6162; -} -a:link.has-code span, -a:hover.has-code span, -a:visited.has-code span { - color: #fe6162; -} - -article.pytorch-article ul, -article.pytorch-article ol { - padding-left: 1.875rem; - margin: 0; -} -article.pytorch-article ul li, -article.pytorch-article ol li { - margin: 0; - line-height: 1.75rem; -} -article.pytorch-article ul p, -article.pytorch-article ol p { - line-height: 1.75rem; - margin-bottom: 0; -} -article.pytorch-article ul ul, -article.pytorch-article ul ol, -article.pytorch-article ol ul, -article.pytorch-article ol ol { - margin: 0; -} -article.pytorch-article h1, -article.pytorch-article h2, -article.pytorch-article h3, -article.pytorch-article h4, -article.pytorch-article h5, -article.pytorch-article h6 { - font-weight: normal; -} -article.pytorch-article h1 a, -article.pytorch-article h2 a, -article.pytorch-article h3 a, -article.pytorch-article h4 a, -article.pytorch-article h5 a, -article.pytorch-article h6 a { - color: #262626; -} -article.pytorch-article p.caption { - margin-top: 1.25rem; -} - -article.pytorch-article .section:first-of-type h1:first-of-type { - margin-top: 0; -} - -.left-menu-link { - background-color: green; -} -.left-menu-link:hover { - background-color: #792ee5; -} - -article.pytorch-article .sphx-glr-thumbcontainer { - margin: 0; - border: 1px solid #d6d7d8; - border-radius: 0; - width: 45%; - text-align: center; - margin-bottom: 5%; -} -@media screen and (max-width: 1100px) { - article.pytorch-article .sphx-glr-thumbcontainer:nth-child(odd) { - margin-left: 0; - margin-right: 2.5%; - } - article.pytorch-article .sphx-glr-thumbcontainer:nth-child(even) { - margin-right: 0; - margin-left: 2.5%; - } - article.pytorch-article .sphx-glr-thumbcontainer .figure { - width: 40%; - } -} -@media screen and (min-width: 1101px) { - article.pytorch-article .sphx-glr-thumbcontainer { - margin-right: 3%; - margin-bottom: 3%; - width: 30%; - } -} -article.pytorch-article .sphx-glr-thumbcontainer .caption-text a { - color: #1C1C1C; - text-decoration: none; - font-family: UCity; - font-size: 0.875rem; - font-style: normal; - font-weight: 600; - line-height: 1.25rem; - letter-spacing: 0px; - text-align: left; -} -article.pytorch-article .sphx-glr-thumbcontainer:hover { - -webkit-box-shadow: none; - box-shadow: none; - border-bottom-color: #ffffff; -} -article.pytorch-article .sphx-glr-thumbcontainer:hover .figure:before { - bottom: 100%; -} -article.pytorch-article .sphx-glr-thumbcontainer .figure { - width: 80%; -} -article.pytorch-article .sphx-glr-thumbcontainer .figure:before { - content: ""; - display: block; - position: absolute; - top: 0; - bottom: 35%; - left: 0; - right: 0; - background: #8A94B3; - opacity: 0.10; -} -article.pytorch-article .sphx-glr-thumbcontainer .figure a.reference.internal { - text-align: left; -} -@media screen and (min-width: 768px) { - article.pytorch-article .sphx-glr-thumbcontainer:after { - content: ""; - display: block; - width: 0; - height: 1px; - position: absolute; - bottom: -1px; - left: 5px; - background-color: #792ee5; - -webkit-transition: width .250s ease-in-out; - transition: width .250s ease-in-out; - } - article.pytorch-article .sphx-glr-thumbcontainer:hover:after { - width: calc(100% - 5px); - } -} -@media screen and (min-width: 768px) { - article.pytorch-article .sphx-glr-thumbcontainer:after { - background-color: #792ee5; - } -} - -article.pytorch-article .section :not(dt) > code { - color: #262626; - border-top: solid 2px #ffffff; - background-color: #ffffff; - border-bottom: solid 2px #ffffff; - padding: 0px 3px; - -webkit-box-decoration-break: clone; - box-decoration-break: clone; -} -article.pytorch-article .section :not(dt) > code .pre { - outline: 0px; - padding: 0px; -} -article.pytorch-article .function dt, article.pytorch-article .method dt, article.pytorch-article .attribute dt, article.pytorch-article .class .attribute dt, article.pytorch-article .class dt { - position: relative; - background: #f3f4f7; - padding: 0.5rem; - border-left: 3px solid #792ee5; - word-wrap: break-word; - padding-right: 100px; -} -article.pytorch-article .function dt em.property, article.pytorch-article .method dt em.property, article.pytorch-article .attribute dt em.property, article.pytorch-article .class dt em.property { - font-family: inherit; -} -article.pytorch-article .function dt em, article.pytorch-article .class dt em, article.pytorch-article .attribute dt em, article.pytorch-article .class .attribute dt em, article.pytorch-article .method dt em, article.pytorch-article .function dt .sig-paren, article.pytorch-article .class dt .sig-paren, article.pytorch-article .attribute dt .sig-paren, article.pytorch-article .method dt .sig-paren { - font-family: "Inconsolata", "IBMPlexMono", "SFMono-Regular", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", "monospace"; - font-size: 87.5%; -} -article.pytorch-article .function dt a, article.pytorch-article .method dt a, article.pytorch-article .attribute dt a, article.pytorch-article .class .attribute dt a, article.pytorch-article .class dt a { - position: absolute; - right: 30px; - padding-right: 0; - top: 50%; - -webkit-transform: perspective(1px) translateY(-50%); - transform: perspective(1px) translateY(-50%); -} -article.pytorch-article .function dt:hover .viewcode-link, article.pytorch-article .method dt:hover .viewcode-link, article.pytorch-article .attribute dt:hover .viewcode-link, article.pytorch-article .class dt:hover .viewcode-link { - color: #792ee5; -} -article.pytorch-article .function .anchorjs-link, article.pytorch-article .method .anchorjs-link, article.pytorch-article .attribute .anchorjs-link, article.pytorch-article .class .anchorjs-link { - display: inline; - position: absolute; - right: 8px; - font-size: 1.5625rem !important; - padding-left: 0; -} -article.pytorch-article .function dt > code, article.pytorch-article .method dt > code, article.pytorch-article .attribute dt > code, article.pytorch-article .class .attribute dt > code, article.pytorch-article .class dt > code { - color: #262626; - border-top: solid 2px #f3f4f7; - background-color: #f3f4f7; - border-bottom: solid 2px #f3f4f7; - -webkit-box-decoration-break: clone; - box-decoration-break: clone; -} -article.pytorch-article .function .viewcode-link, article.pytorch-article .method .viewcode-link, article.pytorch-article .attribute .viewcode-link, article.pytorch-article .class .viewcode-link { - font-size: 0.875rem; - color: #979797; - letter-spacing: 0; - line-height: 1.5rem; - text-transform: uppercase; -} -article.pytorch-article .function dd, article.pytorch-article .method dd, article.pytorch-article .attribute dd, article.pytorch-article .class .attribute dd, article.pytorch-article .class dd { - padding-left: 3.75rem; -} -article.pytorch-article .function dd p, article.pytorch-article .method dd p, article.pytorch-article .attribute dd p, article.pytorch-article .class .attribute dd p, article.pytorch-article .class dd p { - color: #262626; -} -article.pytorch-article .function table tbody tr th.field-name, article.pytorch-article .method table tbody tr th.field-name, article.pytorch-article .attribute table tbody tr th.field-name, article.pytorch-article .class table tbody tr th.field-name { - white-space: nowrap; - color: #262626; - width: 20%; -} -@media screen and (min-width: 768px) { - article.pytorch-article .function table tbody tr th.field-name, article.pytorch-article .method table tbody tr th.field-name, article.pytorch-article .attribute table tbody tr th.field-name, article.pytorch-article .class table tbody tr th.field-name { - width: 15%; - } -} -article.pytorch-article .function table tbody tr td.field-body, article.pytorch-article .method table tbody tr td.field-body, article.pytorch-article .attribute table tbody tr td.field-body, article.pytorch-article .class table tbody tr td.field-body { - padding: 0.625rem; - width: 80%; - color: #262626; -} -@media screen and (min-width: 768px) { - article.pytorch-article .function table tbody tr td.field-body, article.pytorch-article .method table tbody tr td.field-body, article.pytorch-article .attribute table tbody tr td.field-body, article.pytorch-article .class table tbody tr td.field-body { - width: 85%; - } -} -@media screen and (min-width: 1600px) { - article.pytorch-article .function table tbody tr td.field-body, article.pytorch-article .method table tbody tr td.field-body, article.pytorch-article .attribute table tbody tr td.field-body, article.pytorch-article .class table tbody tr td.field-body { - padding-left: 1.25rem; - } -} -article.pytorch-article .function table tbody tr td.field-body p, article.pytorch-article .method table tbody tr td.field-body p, article.pytorch-article .attribute table tbody tr td.field-body p, article.pytorch-article .class table tbody tr td.field-body p { - padding-left: 0px; -} -article.pytorch-article .function table tbody tr td.field-body p:last-of-type, article.pytorch-article .method table tbody tr td.field-body p:last-of-type, article.pytorch-article .attribute table tbody tr td.field-body p:last-of-type, article.pytorch-article .class table tbody tr td.field-body p:last-of-type { - margin-bottom: 0; -} -article.pytorch-article .function table tbody tr td.field-body ol, article.pytorch-article .method table tbody tr td.field-body ol, article.pytorch-article .attribute table tbody tr td.field-body ol, article.pytorch-article .class table tbody tr td.field-body ol, article.pytorch-article .function table tbody tr td.field-body ul, article.pytorch-article .method table tbody tr td.field-body ul, article.pytorch-article .attribute table tbody tr td.field-body ul, article.pytorch-article .class table tbody tr td.field-body ul { - padding-left: 1rem; - padding-bottom: 0; -} -article.pytorch-article .function table.docutils.field-list, article.pytorch-article .method table.docutils.field-list, article.pytorch-article .attribute table.docutils.field-list, article.pytorch-article .class table.docutils.field-list { - margin-bottom: 0.75rem; -} -article.pytorch-article .attribute .has-code { - float: none; -} -article.pytorch-article .class dt { - border-left: none; - border-top: 3px solid #792ee5; - padding-left: 4em; -} -article.pytorch-article .class dt em.property { - position: absolute; - left: 0.5rem; -} -article.pytorch-article .class dd .docutils dt { - padding-left: 0.5rem; -} -article.pytorch-article .class em.property { - text-transform: uppercase; - font-style: normal; - color: #792ee5; - font-size: 1rem; - letter-spacing: 0; - padding-right: 0.75rem; -} -article.pytorch-article .class dl dt em.property { - position: static; - left: 0; - padding-right: 0; -} -article.pytorch-article .class .method dt, -article.pytorch-article .class .staticmethod dt { - border-left: 3px solid #792ee5; - border-top: none; -} -article.pytorch-article .class .method dt, -article.pytorch-article .class .staticmethod dt { - padding-left: 0.5rem; -} -article.pytorch-article .class .attribute dt { - border-top: none; -} -article.pytorch-article .class .attribute dt em.property { - position: relative; - left: 0; -} -article.pytorch-article table { - table-layout: fixed; -} - -article.pytorch-article .note, -article.pytorch-article .warning, -article.pytorch-article .tip, -article.pytorch-article .seealso, -article.pytorch-article .hint, -article.pytorch-article .important, -article.pytorch-article .caution, -article.pytorch-article .danger, -article.pytorch-article .attention, -article.pytorch-article .error { - background: #f3f4f7; - margin-top: 1.875rem; - margin-bottom: 1.125rem; -} -article.pytorch-article .note .admonition-title, -article.pytorch-article .warning .admonition-title, -article.pytorch-article .tip .admonition-title, -article.pytorch-article .seealso .admonition-title, -article.pytorch-article .hint .admonition-title, -article.pytorch-article .important .admonition-title, -article.pytorch-article .caution .admonition-title, -article.pytorch-article .danger .admonition-title, -article.pytorch-article .attention .admonition-title, -article.pytorch-article .error .admonition-title { - color: #ffffff; - letter-spacing: 1px; - text-transform: uppercase; - margin-bottom: 1.125rem; - padding: 3px 0 3px 1.375rem; - position: relative; - font-size: 0.875rem; -} -article.pytorch-article .note .admonition-title:before, -article.pytorch-article .warning .admonition-title:before, -article.pytorch-article .tip .admonition-title:before, -article.pytorch-article .seealso .admonition-title:before, -article.pytorch-article .hint .admonition-title:before, -article.pytorch-article .important .admonition-title:before, -article.pytorch-article .caution .admonition-title:before, -article.pytorch-article .danger .admonition-title:before, -article.pytorch-article .attention .admonition-title:before, -article.pytorch-article .error .admonition-title:before { - content: "\2022"; - position: absolute; - left: 9px; - color: #ffffff; - top: 2px; -} -article.pytorch-article .note p:nth-child(n + 2), -article.pytorch-article .warning p:nth-child(n + 2), -article.pytorch-article .tip p:nth-child(n + 2), -article.pytorch-article .seealso p:nth-child(n + 2), -article.pytorch-article .hint p:nth-child(n + 2), -article.pytorch-article .important p:nth-child(n + 2), -article.pytorch-article .caution p:nth-child(n + 2), -article.pytorch-article .danger p:nth-child(n + 2), -article.pytorch-article .attention p:nth-child(n + 2), -article.pytorch-article .error p:nth-child(n + 2) { - padding: 0 1.375rem; -} -article.pytorch-article .note table, -article.pytorch-article .warning table, -article.pytorch-article .tip table, -article.pytorch-article .seealso table, -article.pytorch-article .hint table, -article.pytorch-article .important table, -article.pytorch-article .caution table, -article.pytorch-article .danger table, -article.pytorch-article .attention table, -article.pytorch-article .error table { - margin: 0 2rem; - width: auto; -} -article.pytorch-article .note .pre, -article.pytorch-article .note pre, -article.pytorch-article .warning .pre, -article.pytorch-article .warning pre, -article.pytorch-article .tip .pre, -article.pytorch-article .tip pre, -article.pytorch-article .seealso .pre, -article.pytorch-article .seealso pre, -article.pytorch-article .hint .pre, -article.pytorch-article .hint pre, -article.pytorch-article .important .pre, -article.pytorch-article .important pre, -article.pytorch-article .caution .pre, -article.pytorch-article .caution pre, -article.pytorch-article .danger .pre, -article.pytorch-article .danger pre, -article.pytorch-article .attention .pre, -article.pytorch-article .attention pre, -article.pytorch-article .error .pre, -article.pytorch-article .error pre { - background: #ffffff; - outline: 1px solid #e9e9e9; -} -article.pytorch-article .note :not(dt) > code, -article.pytorch-article .warning :not(dt) > code, -article.pytorch-article .tip :not(dt) > code, -article.pytorch-article .seealso :not(dt) > code, -article.pytorch-article .hint :not(dt) > code, -article.pytorch-article .important :not(dt) > code, -article.pytorch-article .caution :not(dt) > code, -article.pytorch-article .danger :not(dt) > code, -article.pytorch-article .attention :not(dt) > code, -article.pytorch-article .error :not(dt) > code { - border-top: solid 2px #ffffff; - background-color: #ffffff; - border-bottom: solid 2px #ffffff; - padding: 0px 3px; - -webkit-box-decoration-break: clone; - box-decoration-break: clone; - outline: 1px solid #e9e9e9; -} -article.pytorch-article .note :not(dt) > code .pre, -article.pytorch-article .warning :not(dt) > code .pre, -article.pytorch-article .tip :not(dt) > code .pre, -article.pytorch-article .seealso :not(dt) > code .pre, -article.pytorch-article .hint :not(dt) > code .pre, -article.pytorch-article .important :not(dt) > code .pre, -article.pytorch-article .caution :not(dt) > code .pre, -article.pytorch-article .danger :not(dt) > code .pre, -article.pytorch-article .attention :not(dt) > code .pre, -article.pytorch-article .error :not(dt) > code .pre { - outline: 0px; - padding: 0px; -} -article.pytorch-article .note pre, -article.pytorch-article .warning pre, -article.pytorch-article .tip pre, -article.pytorch-article .seealso pre, -article.pytorch-article .hint pre, -article.pytorch-article .important pre, -article.pytorch-article .caution pre, -article.pytorch-article .danger pre, -article.pytorch-article .attention pre, -article.pytorch-article .error pre { - margin-bottom: 0; -} -article.pytorch-article .note .highlight, -article.pytorch-article .warning .highlight, -article.pytorch-article .tip .highlight, -article.pytorch-article .seealso .highlight, -article.pytorch-article .hint .highlight, -article.pytorch-article .important .highlight, -article.pytorch-article .caution .highlight, -article.pytorch-article .danger .highlight, -article.pytorch-article .attention .highlight, -article.pytorch-article .error .highlight { - margin: 0 2rem 1.125rem 2rem; -} -article.pytorch-article .note ul, -article.pytorch-article .note ol, -article.pytorch-article .warning ul, -article.pytorch-article .warning ol, -article.pytorch-article .tip ul, -article.pytorch-article .tip ol, -article.pytorch-article .seealso ul, -article.pytorch-article .seealso ol, -article.pytorch-article .hint ul, -article.pytorch-article .hint ol, -article.pytorch-article .important ul, -article.pytorch-article .important ol, -article.pytorch-article .caution ul, -article.pytorch-article .caution ol, -article.pytorch-article .danger ul, -article.pytorch-article .danger ol, -article.pytorch-article .attention ul, -article.pytorch-article .attention ol, -article.pytorch-article .error ul, -article.pytorch-article .error ol { - padding-left: 3.25rem; -} -article.pytorch-article .note ul li, -article.pytorch-article .note ol li, -article.pytorch-article .warning ul li, -article.pytorch-article .warning ol li, -article.pytorch-article .tip ul li, -article.pytorch-article .tip ol li, -article.pytorch-article .seealso ul li, -article.pytorch-article .seealso ol li, -article.pytorch-article .hint ul li, -article.pytorch-article .hint ol li, -article.pytorch-article .important ul li, -article.pytorch-article .important ol li, -article.pytorch-article .caution ul li, -article.pytorch-article .caution ol li, -article.pytorch-article .danger ul li, -article.pytorch-article .danger ol li, -article.pytorch-article .attention ul li, -article.pytorch-article .attention ol li, -article.pytorch-article .error ul li, -article.pytorch-article .error ol li { - color: #262626; -} -article.pytorch-article .note p, -article.pytorch-article .warning p, -article.pytorch-article .tip p, -article.pytorch-article .seealso p, -article.pytorch-article .hint p, -article.pytorch-article .important p, -article.pytorch-article .caution p, -article.pytorch-article .danger p, -article.pytorch-article .attention p, -article.pytorch-article .error p { - margin-top: 1.125rem; -} -article.pytorch-article .note .admonition-title { - background: #54c7ec; -} -article.pytorch-article .warning .admonition-title { - background: #e94f3b; -} -article.pytorch-article .tip .admonition-title { - background: #6bcebb; -} -article.pytorch-article .seealso .admonition-title { - background: #6bcebb; -} -article.pytorch-article .hint .admonition-title { - background: #a2cdde; -} -article.pytorch-article .important .admonition-title { - background: #5890ff; -} -article.pytorch-article .caution .admonition-title { - background: #f7923a; -} -article.pytorch-article .danger .admonition-title { - background: #db2c49; -} -article.pytorch-article .attention .admonition-title { - background: #f5a623; -} -article.pytorch-article .error .admonition-title { - background: #cc2f90; -} -article.pytorch-article .sphx-glr-download-link-note.admonition.note, -article.pytorch-article .reference.download.internal, article.pytorch-article .sphx-glr-signature { - display: none; -} -article.pytorch-article .admonition > p:last-of-type { - margin-bottom: 0; - padding-bottom: 1.125rem !important; -} - -.pytorch-article div.sphx-glr-download a { - background-color: #f3f4f7; - background-image: url("../images/arrow-down-orange.svg"); - background-repeat: no-repeat; - background-position: left 10px center; - background-size: 15px 15px; - border-radius: 0; - border: none; - display: block; - text-align: left; - padding: 0.9375rem 3.125rem; - position: relative; - margin: 1.25rem auto; -} -@media screen and (min-width: 768px) { - .pytorch-article div.sphx-glr-download a:after { - content: ""; - display: block; - width: 0; - height: 1px; - position: absolute; - bottom: -1px; - left: 5px; - background-color: #792ee5; - -webkit-transition: width .250s ease-in-out; - transition: width .250s ease-in-out; - } - .pytorch-article div.sphx-glr-download a:hover:after { - width: calc(100% - 5px); - } -} -@media screen and (min-width: 768px) { - .pytorch-article div.sphx-glr-download a:after { - background-color: #792ee5; - } -} -@media screen and (min-width: 768px) { - .pytorch-article div.sphx-glr-download a { - background-position: left 20px center; - } -} -.pytorch-article div.sphx-glr-download a:hover { - -webkit-box-shadow: none; - box-shadow: none; - text-decoration: none; - background-image: url("../images/arrow-down-orange.svg"); - background-color: #f3f4f7; -} -.pytorch-article div.sphx-glr-download a span.pre { - background-color: transparent; - font-size: 1.125rem; - padding: 0; - color: #262626; -} -.pytorch-article div.sphx-glr-download a code, .pytorch-article div.sphx-glr-download a kbd, .pytorch-article div.sphx-glr-download a pre, .pytorch-article div.sphx-glr-download a samp, .pytorch-article div.sphx-glr-download a span.pre { - font-family: FreightSans, Helvetica Neue, Helvetica, Arial, sans-serif; -} - -.pytorch-article p.sphx-glr-script-out { - margin-bottom: 1.125rem; -} - -.pytorch-article div.sphx-glr-script-out { - margin-bottom: 2.5rem; -} -.pytorch-article div.sphx-glr-script-out .highlight { - margin-left: 0; - margin-top: 0; -} -.pytorch-article div.sphx-glr-script-out .highlight pre { - background-color: #fdede9; - padding: 1.5625rem; - color: #837b79; -} -.pytorch-article div.sphx-glr-script-out + p { - margin-top: unset; -} - -article.pytorch-article .wy-table-responsive table { - border: none; - border-color: #ffffff !important; - table-layout: fixed; -} -article.pytorch-article .wy-table-responsive table thead tr { - border-bottom: 2px solid #6c6c6d; -} -article.pytorch-article .wy-table-responsive table thead th { - line-height: 1.75rem; - padding-left: 0.9375rem; - padding-right: 0.9375rem; -} -article.pytorch-article .wy-table-responsive table tbody .row-odd { - background-color: #f3f4f7; -} -article.pytorch-article .wy-table-responsive table tbody td { - color: #6c6c6d; - white-space: normal; - padding: 0.9375rem; - font-size: 1rem; - line-height: 1.375rem; -} -article.pytorch-article .wy-table-responsive table tbody td .pre { - background: #ffffff; - color: #792ee5; - font-size: 87.5%; -} -article.pytorch-article .wy-table-responsive table tbody td code { - font-size: 87.5%; -} - -a[rel~="prev"], a[rel~="next"] { - padding: 0.375rem 0 0 0; -} - -img.next-page, -img.previous-page { - width: 8px; - height: 10px; - position: relative; - top: -1px; -} - -img.previous-page { - -webkit-transform: scaleX(-1); - transform: scaleX(-1); -} - -.rst-footer-buttons { - margin-top: 1.875rem; - margin-bottom: 1.875rem; -} -.rst-footer-buttons .btn:focus, -.rst-footer-buttons .btn.focus { - -webkit-box-shadow: none; - box-shadow: none; -} - -article.pytorch-article blockquote { - margin-left: 3.75rem; - color: #6c6c6d; -} - -article.pytorch-article .caption { - color: #6c6c6d; - letter-spacing: 0.25px; - line-height: 2.125rem; -} - -article.pytorch-article .math { - color: #262626; - width: auto; - text-align: center; -} -article.pytorch-article .math img { - width: auto; -} - -.pytorch-breadcrumbs-wrapper { - width: 100%; -} -@media screen and (min-width: 1101px) { - .pytorch-breadcrumbs-wrapper { - float: left; - margin-left: 3%; - width: 73%; - } -} -@media screen and (min-width: 1600px) { - .pytorch-breadcrumbs-wrapper { - width: 850px; - margin-left: 1.875rem; - } -} -.pytorch-breadcrumbs-wrapper .pytorch-breadcrumbs-aside { - float: right; -} -.pytorch-breadcrumbs-wrapper .pytorch-breadcrumbs-aside .fa.fa-github { - margin-top: 5px; - display: block; -} - -.pytorch-article .container { - padding-left: 0; - padding-right: 0; - max-width: none; -} - -a:link, -a:visited, -a:hover { - color: #792ee5; -} - -::-webkit-input-placeholder { - color: #792ee5; -} - -::-moz-placeholder { - color: #792ee5; -} - -:-ms-input-placeholder { - color: #792ee5; -} - -:-moz-placeholder { - color: #792ee5; -} - -@media screen and (min-width: 768px) { - .site-footer a:hover { - color: #792ee5; - } -} - -.docs-tutorials-resources a { - color: #792ee5; -} - -.header-holder { - position: relative; - z-index: 201; -} - -.header-holder .main-menu ul li.active:after { - color: #792ee5; -} -.header-holder .main-menu ul li.active a { - color: #792ee5; -} -@media screen and (min-width: 1100px) { - .header-holder .main-menu ul li a:hover { - color: #792ee5; - } -} - -.mobile-main-menu.open ul li.active a { - color: #792ee5; -} - -.version { - padding-bottom: 1rem; -} - -.pytorch-call-to-action-links { - padding-top: 0; - display: -webkit-box; - display: -ms-flexbox; - display: flex; -} -@media screen and (min-width: 768px) { - .pytorch-call-to-action-links { - padding-top: 2.5rem; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - .pytorch-call-to-action-links { - padding-top: 0; - } -} -@media (min-width: 1100px) and (max-width: 1239px) { - .pytorch-call-to-action-links { - padding-top: 2.5rem; - } -} -.pytorch-call-to-action-links #tutorial-type { - display: none; -} -.pytorch-call-to-action-links .call-to-action-img, .pytorch-call-to-action-links .call-to-action-notebook-img { - height: 1.375rem; - width: 1.375rem; - margin-right: 10px; -} -.pytorch-call-to-action-links .call-to-action-notebook-img { - height: 1rem; -} -.pytorch-call-to-action-links a { - padding-right: 1.25rem; - color: #000000; - cursor: pointer; -} -.pytorch-call-to-action-links a:hover { - color: #792ee5; -} -.pytorch-call-to-action-links a .call-to-action-desktop-view { - display: none; -} -@media screen and (min-width: 768px) { - .pytorch-call-to-action-links a .call-to-action-desktop-view { - display: block; - } -} -.pytorch-call-to-action-links a .call-to-action-mobile-view { - display: block; -} -@media screen and (min-width: 768px) { - .pytorch-call-to-action-links a .call-to-action-mobile-view { - display: none; - } -} -.pytorch-call-to-action-links a #google-colab-link, .pytorch-call-to-action-links a #download-notebook-link, -.pytorch-call-to-action-links a #github-view-link { - padding-bottom: 0.625rem; - border-bottom: 1px solid #f3f4f7; - padding-right: 2.5rem; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; -} -.pytorch-call-to-action-links a #google-colab-link:hover, .pytorch-call-to-action-links a #download-notebook-link:hover, -.pytorch-call-to-action-links a #github-view-link:hover { - border-bottom-color: #792ee5; - color: #792ee5; -} - -.slack-container { - display: -webkit-box; - display: -ms-flexbox; - display: flex; -} -.slack-container .slack-button { - border: 1px solid transparent; - background: linear-gradient(206.91deg, #792EE5 16.83%, #3EABB3 144.59%); - border-radius: 6px; - height: 30px; - width: 210px; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - font-size: 14px; -} -.slack-container a { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - height: 100%; - text-decoration: none; -} -.slack-container .icon { - height: 100%; - color: white; - margin-left: 10px; - padding-top: 2px; -} -.slack-container .button-title { - margin: auto; - margin-left: 5px; - margin-right: 5px; - color: white; -} - -.slack-align-left { - -webkit-box-pack: start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.slack-align-center { - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; -} - -.slack-align-right { - -webkit-box-pack: end; - -ms-flex-pack: end; - justify-content: flex-end; -} - -#tutorial-cards-container #tutorial-cards { - width: 100%; -} -#tutorial-cards-container .tutorials-nav { - padding-left: 0; - padding-right: 0; - padding-bottom: 0; -} -#tutorial-cards-container .tutorials-hr { - margin-top: 1rem; - margin-bottom: 1rem; -} -#tutorial-cards-container .card.tutorials-card { - border: 1px solid #cfcfcf; - border-radius: 5px; - height: 98px; - margin-bottom: 1.25rem; - margin-bottom: 1.875rem; - cursor: pointer; -} -@media screen and (min-width: 1240px) { - #tutorial-cards-container .card.tutorials-card { - height: 200px; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - #tutorial-cards-container .card.tutorials-card { - height: 200px; - } -} -#tutorial-cards-container .card.tutorials-card .tutorials-image { - position: absolute; - top: 0px; - right: 0px; - height: 96px; - width: 96px; - opacity: 0.7; -} -#tutorial-cards-container .card.tutorials-card .tutorials-image img { - height: 100%; - width: 100%; -} -@media screen and (min-width: 768px) { - #tutorial-cards-container .card.tutorials-card .tutorials-image { - height: 198px; - width: 198px; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - #tutorial-cards-container .card.tutorials-card .tutorials-image { - height: 198px; - width: 198px; - } -} -#tutorial-cards-container .card.tutorials-card .tutorials-image:before { - content: ''; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - z-index: 1; - opacity: .075; -} -#tutorial-cards-container .card.tutorials-card .card-title-container { - display: -webkit-inline-box; - display: -ms-inline-flexbox; - display: inline-flex; -} -#tutorial-cards-container .card.tutorials-card .card-title-container h4 { - margin-bottom: 1.125rem; - margin-top: 0; - font-size: 1.5rem; -} -#tutorial-cards-container .card.tutorials-card p.card-summary, #tutorial-cards-container .card.tutorials-card .card-title-container, #tutorial-cards-container .card.tutorials-card p.tags { - white-space: nowrap; - overflow-y: hidden; - overflow-x: scroll; -} -@media screen and (min-width: 768px) { - #tutorial-cards-container .card.tutorials-card p.card-summary, #tutorial-cards-container .card.tutorials-card .card-title-container, #tutorial-cards-container .card.tutorials-card p.tags { - overflow: hidden; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - #tutorial-cards-container .card.tutorials-card p.card-summary, #tutorial-cards-container .card.tutorials-card .card-title-container, #tutorial-cards-container .card.tutorials-card p.tags { - overflow: hidden; - } -} -#tutorial-cards-container .card.tutorials-card p.card-summary, #tutorial-cards-container .card.tutorials-card p.tags { - color: #6c6c6d; - font-weight: 400; - margin-bottom: 0; - line-height: 1.5rem; - font-size: 0.9375rem; -} -#tutorial-cards-container .card.tutorials-card p.card-summary { - height: 1.5rem; -} -@media screen and (min-width: 768px) { - #tutorial-cards-container .card.tutorials-card p.card-summary { - white-space: normal; - height: 4.5rem; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - #tutorial-cards-container .card.tutorials-card p.card-summary { - white-space: normal; - height: 4.5rem; - } -} -#tutorial-cards-container .card.tutorials-card p.card-summary, #tutorial-cards-container .card.tutorials-card .card-title-container { - width: calc(100% - 96px); -} -@media screen and (min-width: 768px) { - #tutorial-cards-container .card.tutorials-card p.card-summary, #tutorial-cards-container .card.tutorials-card .card-title-container { - width: calc(100% - 200px); - } -} -@media (min-width: 768px) and (max-width: 1239px) { - #tutorial-cards-container .card.tutorials-card p.card-summary, #tutorial-cards-container .card.tutorials-card .card-title-container { - width: calc(100% - 200px); - } -} -#tutorial-cards-container .card.tutorials-card p.tags { - position: absolute; - bottom: 0.75rem; - width: calc(100% - 96px - 1.25rem - 1.25rem); -} -@media screen and (min-width: 768px) { - #tutorial-cards-container .card.tutorials-card p.tags { - width: calc(100% - 200px - 1.25rem - 1.25rem); - } -} -@media (min-width: 768px) and (max-width: 1239px) { - #tutorial-cards-container .card.tutorials-card p.tags { - width: calc(100% - 200px - 1.25rem - 1.25rem); - } -} -#tutorial-cards-container .card.tutorials-card h4 { - color: #262626; - margin-bottom: 1.125rem; -} -#tutorial-cards-container .card.tutorials-card a { - height: 100%; -} -@media screen and (min-width: 768px) { - #tutorial-cards-container .card.tutorials-card a { - min-height: 190px; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - #tutorial-cards-container .card.tutorials-card a { - min-height: 234px; - } -} -@media screen and (min-width: 768px) { - #tutorial-cards-container .card.tutorials-card:after { - content: ""; - display: block; - width: 0; - height: 1px; - position: absolute; - bottom: -1px; - left: 5px; - background-color: #792ee5; - -webkit-transition: width .250s ease-in-out; - transition: width .250s ease-in-out; - } - #tutorial-cards-container .card.tutorials-card:hover:after { - width: calc(100% - 5px); - } -} -#tutorial-cards-container .card.tutorials-card:hover { - background-color: #ffffff; - border: 1px solid #e2e2e2; -} -#tutorial-cards-container .card.tutorials-card:hover p.card-summary { - color: #262626; -} -#tutorial-cards-container .card.tutorials-card:hover .tutorials-image { - opacity: unset; -} -#tutorial-cards-container .tutorial-tags-container { - width: 100%; -} -#tutorial-cards-container .tutorial-tags-container.active { - width: 0; -} -#tutorial-cards-container .tutorial-filter-menu ul { - list-style-type: none; - padding-left: 1.25rem; -} -#tutorial-cards-container .tutorial-filter-menu ul li { - padding-right: 1.25rem; - word-break: break-all; -} -#tutorial-cards-container .tutorial-filter-menu ul li a { - color: #979797; -} -#tutorial-cards-container .tutorial-filter-menu ul li a:hover { - color: #792ee5; -} -#tutorial-cards-container .tutorial-filter { - cursor: pointer; -} -#tutorial-cards-container .filter-btn { - color: #979797; - border: 1px solid #979797; - border-radius: 3px; - display: inline-block; - text-align: center; - white-space: nowrap; - vertical-align: middle; - padding: 0.375rem 0.75rem; - font-size: 1rem; - line-height: 1.5; - margin-bottom: 5px; -} -#tutorial-cards-container .filter-btn:hover { - border: 1px solid #792ee5; - color: #792ee5; -} -#tutorial-cards-container .filter-btn.selected { - background-color: #792ee5; - border: 1px solid #792ee5; - color: #ffffff; -} -#tutorial-cards-container .all-tag-selected { - background-color: #979797; - color: #ffffff; -} -#tutorial-cards-container .all-tag-selected:hover { - border-color: #979797; - color: #ffffff; -} -#tutorial-cards-container .pagination .page { - border: 1px solid #dee2e6; - padding: 0.5rem 0.75rem; -} -#tutorial-cards-container .pagination .active .page { - background-color: #dee2e6; -} - -article.pytorch-article .display-card-container .col-md-2 { - padding: 5px; -} -article.pytorch-article .display-card-container .col-md-3 { - padding: 5px; -} -article.pytorch-article .display-card-container .col-md-4 { - padding: 5px; -} -article.pytorch-article .display-card-container .col-md-6 { - padding: 5px; -} -article.pytorch-article .display-card-container .col-md-12 { - padding: 5px; -} -article.pytorch-article .display-card-container h3 { - margin: 5px 0 10px 0; -} -@media screen and (min-width: 768px) { - article.pytorch-article .display-card-container .display-card-hover:after { - content: ""; - display: block; - width: 0; - height: 2px; - position: absolute; - top: calc(100% - 12px); - left: 15px; - background-color: #792ee5; - -webkit-transition: width 0.25s ease-in-out; - transition: width 0.25s ease-in-out; - } - article.pytorch-article .display-card-container .display-card-hover:hover:after { - width: calc(100% - 30px); - } -} -article.pytorch-article .display-card-container .card-tag { - position: absolute; - bottom: 17px; - left: 15px; - padding: 2px 3px 2px 3px; - border: 1px solid #792ee5; - color: #792ee5; - border-radius: 4px; - font-size: 10px; - font-weight: 500; - text-transform: uppercase; -} -article.pytorch-article .display-card-container .display-card { - padding-bottom: 10px; - border: 1px solid #cfcfcf; - border-radius: 5px; - padding: 10px; -} -article.pytorch-article .display-card-container .display-card .body-paragraph { - color: #666666; - font-weight: 400; - font-size: 0.875rem; - line-height: 1.25rem; -} -article.pytorch-article .display-card-container .display-card .image-center { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - margin: auto; -} -article.pytorch-article .display-card-container .display-card .image-center img { - height: 125px; - margin: auto; - width: 100%; - -o-object-fit: contain; - object-fit: contain; -} -article.pytorch-article .display-card-container .display-card .image-right { - float: right; - height: 100%; -} -article.pytorch-article .display-card-container .display-card .image-right img { - height: 100%; - width: auto; - -o-object-fit: contain; - object-fit: contain; -} -article.pytorch-article .tutorials-callout-container { - padding-bottom: 50px; -} -article.pytorch-article .tutorials-callout-container .col-md-6 { - padding: 5px; -} -article.pytorch-article .tutorials-callout-container .text-container { - padding-bottom: 10px; - border: 1px solid #cfcfcf; - border-radius: 5px; - padding: 10px; -} -@media screen and (min-width: 768px) { - article.pytorch-article .tutorials-callout-container .text-container:after { - content: ""; - display: block; - width: 0; - height: 2px; - position: absolute; - top: calc(100% - 12px); - left: 15px; - background-color: #792ee5; - -webkit-transition: width .25s ease-in-out; - transition: width .25s ease-in-out; - } - article.pytorch-article .tutorials-callout-container .text-container:hover:after { - width: calc(100% - 30px); - } -} -@media screen and (min-width: 768px) { - article.pytorch-article .tutorials-callout-container .text-container { - height: 160px; - } -} -article.pytorch-article .tutorials-callout-container .text-container .body-paragraph { - color: #666666; - font-weight: 400; - font-size: 0.875rem; - line-height: 1.25rem; -} -article.pytorch-article .tutorials-callout-container .text-container-small { - padding-bottom: 10px; - border: 1px solid #cfcfcf; - border-radius: 5px; - padding: 10px; - margin-bottom: 10px; -} -@media screen and (min-width: 768px) { - article.pytorch-article .tutorials-callout-container .text-container-small:after { - content: ""; - display: block; - width: 0; - height: 2px; - position: absolute; - top: calc(100% - 22px); - left: 15px; - background-color: #792ee5; - -webkit-transition: width .25s ease-in-out; - transition: width .25s ease-in-out; - } - article.pytorch-article .tutorials-callout-container .text-container-small:hover:after { - width: calc(100% - 30px); - } -} -@media screen and (min-width: 768px) { - article.pytorch-article .tutorials-callout-container .text-container-small { - height: 130px; - } -} -article.pytorch-article .tutorials-callout-container .text-container-small .body-paragraph { - color: #666666; - font-weight: 400; - font-size: 0.875rem; - line-height: 1.25rem; -} -article.pytorch-article .tutorials-callout-container .btn.callout-button { - font-size: 1.125rem; - border-radius: 0; - border: none; - background-color: #f3f4f7; - color: #6c6c6d; - font-weight: 400; - position: relative; - letter-spacing: 0.25px; -} -@media screen and (min-width: 768px) { - article.pytorch-article .tutorials-callout-container .btn.callout-button:after { - content: ""; - display: block; - width: 0; - height: 1px; - position: absolute; - bottom: -1px; - left: 5px; - background-color: #792ee5; - -webkit-transition: width .250s ease-in-out; - transition: width .250s ease-in-out; - } - article.pytorch-article .tutorials-callout-container .btn.callout-button:hover:after { - width: calc(100% - 5px); - } -} -article.pytorch-article .tutorials-callout-container .btn.callout-button a { - color: inherit; -} - -.center-wrapper { - max-width: 560px; - height: auto; - margin: 1.5rem auto; -} - -.video-wrapper { - position: relative; - padding-bottom: 56.25%; - /* 16:9 */ -} - -.video-wrapper iframe { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -.pytorch-container { - margin: 0 auto; - padding: 0 1.875rem; - width: auto; - position: relative; -} -@media screen and (min-width: 1100px) { - .pytorch-container { - padding: 0; - } -} -@media screen and (min-width: 1101px) { - .pytorch-container { - margin-left: 25%; - } -} -@media screen and (min-width: 1600px) { - .pytorch-container { - margin-left: 350px; - } -} -.pytorch-container:before, .pytorch-container:after { - content: ""; - display: table; -} -.pytorch-container:after { - clear: both; -} -.pytorch-container { - *zoom: 1; -} - -.pytorch-content-wrap { - background-color: #ffffff; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - position: relative; - padding-top: 0; -} -.pytorch-content-wrap:before, .pytorch-content-wrap:after { - content: ""; - display: table; -} -.pytorch-content-wrap:after { - clear: both; -} -.pytorch-content-wrap { - *zoom: 1; -} -@media screen and (min-width: 1101px) { - .pytorch-content-wrap { - padding-top: 45px; - float: left; - width: 100%; - display: block; - } -} -@media screen and (min-width: 1600px) { - .pytorch-content-wrap { - width: 100%; - } -} - -.pytorch-content { - background: #ffffff; - width: 100%; - max-width: 700px; - position: relative; -} - -.pytorch-content-left { - min-height: 100vh; - margin-top: 2.5rem; - width: 100%; -} -@media screen and (min-width: 1101px) { - .pytorch-content-left { - margin-top: 0; - margin-left: 20px; - width: 73%; - float: left; - } -} -@media screen and (min-width: 1600px) { - .pytorch-content-left { - width: 73%; - margin-left: 30px; - } -} -.pytorch-content-left .main-content { - padding-top: 0.9375rem; -} -.pytorch-content-left .main-content ul.simple { - padding-bottom: 1.25rem; -} -.pytorch-content-left .main-content .note:nth-child(1), .pytorch-content-left .main-content .warning:nth-child(1) { - margin-top: 0; -} - -.pytorch-content-right { - display: none; - position: relative; - overflow-x: hidden; - overflow-y: hidden; -} -@media screen and (min-width: 1101px) { - .pytorch-content-right { - display: block; - margin-left: 0; - width: 25%; - float: left; - height: 100%; - padding-right: 5px; - } -} -@media screen and (min-width: 1600px) { - .pytorch-content-right { - width: 24%; - padding-right: 5px; - } -} - -@media screen and (min-width: 1101px) { - .pytorch-side-scroll { - position: relative; - overflow-x: hidden; - overflow-y: scroll; - height: 100%; - } -} - -.pytorch-menu-vertical { - padding: 1.25rem 1.875rem 2.5rem 1.875rem; -} -@media screen and (min-width: 1101px) { - .pytorch-menu-vertical { - display: block; - padding-top: 0; - padding-right: 13.5%; - padding-bottom: 5.625rem; - } -} -@media screen and (min-width: 1600px) { - .pytorch-menu-vertical { - padding-left: 0; - padding-right: 1.5625rem; - } -} - -.pytorch-left-menu { - display: none; - background-color: white; - color: #262626; - overflow: scroll; - border-right: 1px solid #e2e2e2; -} -@media screen and (min-width: 1101px) { - .pytorch-left-menu { - display: block; - overflow-x: hidden; - overflow-y: hidden; - padding-bottom: 110px; - padding: 0 1.875rem 0 0; - width: 22%; - z-index: 200; - float: left; - } - .pytorch-left-menu.make-fixed { - position: fixed; - top: 0; - bottom: 0; - left: 0; - float: none; - } -} -@media screen and (min-width: 1600px) { - .pytorch-left-menu { - padding: 0 0 0 1.875rem; - width: 350px; - } -} - -.expand-menu, .hide-menu { - color: #6c6c6d; - padding-left: 10px; - cursor: none; - float: right; - pointer-events: none; -} - -.menu-item-decorator { - color: #848484; -} - -.collapse { - display: none; -} - -.left-nav-top-caption { - padding-top: 1rem; -} - -.pytorch-left-menu p.caption { - display: block; - margin-bottom: 0px; - text-transform: none; - white-space: normal; - border-radius: 6px; - padding: 0.5rem; - color: #1C1C1C; - font-family: UCity; - font-size: 0.875rem; - font-style: normal; - font-weight: 600; - line-height: 1.25rem; - letter-spacing: 0px; - text-align: left; -} -.pytorch-left-menu p.caption:hover { - background-color: #EFEEFF; - color: #4F00BA; -} - -.pytorch-left-menu-search { - margin-bottom: 2.5rem; -} -@media screen and (min-width: 1101px) { - .pytorch-left-menu-search { - margin: 1.25rem 0.625rem 1.875rem 0; - } -} - -.pytorch-left-menu-search ::-webkit-input-placeholder { - color: #262626; -} -.pytorch-left-menu-search ::-moz-placeholder { - color: #262626; -} -.pytorch-left-menu-search :-ms-input-placeholder { - color: #262626; -} -.pytorch-left-menu-search ::-ms-input-placeholder { - color: #262626; -} -.pytorch-left-menu-search ::placeholder { - color: #262626; -} - -.pytorch-left-menu-search input[type=text] { - border-radius: 0; - padding: 0.5rem 0.75rem; - border-color: #ffffff; - color: #262626; - border-style: solid; - font-size: 1rem; - width: 100%; - background-color: #f3f4f7; - background-image: url("../images/search-icon.svg"); - background-repeat: no-repeat; - background-size: 18px 18px; - background-position: 12px 10px; - padding-left: 40px; - background-color: #F6F8FB; -} -.pytorch-left-menu-search input[type=text]:focus { - outline: 0; -} - -@media screen and (min-width: 1101px) { - .pytorch-left-menu .pytorch-side-scroll { - width: 120%; - } -} -@media screen and (min-width: 1600px) { - .pytorch-left-menu .pytorch-side-scroll { - width: 340px; - } -} - -.pytorch-right-menu { - min-height: 100px; - overflow-x: hidden; - overflow-y: hidden; - left: 0; - z-index: 200; - padding-top: 0; - position: relative; -} -@media screen and (min-width: 1101px) { - .pytorch-right-menu { - width: 100%; - } - .pytorch-right-menu.scrolling-fixed { - position: fixed; - top: 45px; - left: 81.1%; - width: 19%; - padding-right: 10px; - } - .pytorch-right-menu.scrolling-absolute { - position: absolute; - left: 0; - } -} -@media screen and (min-width: 1600px) { - .pytorch-right-menu { - left: 0; - width: 100%; - } - .pytorch-right-menu.scrolling-fixed { - position: fixed; - top: 45px; - left: 80%; - } - .pytorch-right-menu.scrolling-absolute { - position: absolute; - left: 0; - } -} - -.pytorch-left-menu ul, -.pytorch-right-menu ul { - list-style-type: none; - padding-left: 0; - margin-bottom: 2.5rem; -} -.pytorch-left-menu > ul, -.pytorch-right-menu > ul { - margin-bottom: 2.5rem; -} -.pytorch-left-menu a:link, -.pytorch-left-menu a:visited, -.pytorch-left-menu a:hover, -.pytorch-right-menu a:link, -.pytorch-right-menu a:visited, -.pytorch-right-menu a:hover { - color: #6c6c6d; - font-size: 0.875rem; - line-height: 1rem; - padding: 0; - text-decoration: none; -} -.pytorch-left-menu a:link.reference.internal, -.pytorch-left-menu a:visited.reference.internal, -.pytorch-left-menu a:hover.reference.internal, -.pytorch-right-menu a:link.reference.internal, -.pytorch-right-menu a:visited.reference.internal, -.pytorch-right-menu a:hover.reference.internal { - min-height: 25px; - height: auto; - padding: 5px; - border-radius: 6px; - position: relative; - width: 100%; -} -.pytorch-left-menu a:link.reference.internal:hover, -.pytorch-left-menu a:visited.reference.internal:hover, -.pytorch-left-menu a:hover.reference.internal:hover, -.pytorch-right-menu a:link.reference.internal:hover, -.pytorch-right-menu a:visited.reference.internal:hover, -.pytorch-right-menu a:hover.reference.internal:hover { - background-color: #EFEEFF; - color: #4F00BA; -} -.pytorch-left-menu a:link.reference.external, -.pytorch-left-menu a:visited.reference.external, -.pytorch-left-menu a:hover.reference.external, -.pytorch-right-menu a:link.reference.external, -.pytorch-right-menu a:visited.reference.external, -.pytorch-right-menu a:hover.reference.external { - min-height: 25px; - height: auto; - padding: 5px; - border-radius: 6px; - position: relative; - width: 100%; -} -.pytorch-left-menu a:link.reference.external:hover, -.pytorch-left-menu a:visited.reference.external:hover, -.pytorch-left-menu a:hover.reference.external:hover, -.pytorch-right-menu a:link.reference.external:hover, -.pytorch-right-menu a:visited.reference.external:hover, -.pytorch-right-menu a:hover.reference.external:hover { - background-color: #EFEEFF; - color: #4F00BA; -} -.pytorch-left-menu li code, -.pytorch-right-menu li code { - border: none; - background: inherit; - color: inherit; - padding-left: 0; - padding-right: 0; -} -.pytorch-left-menu li span.toctree-expand, -.pytorch-right-menu li span.toctree-expand { - display: block; - float: left; - margin-left: -1.2em; - font-size: 0.8em; - line-height: 1.6em; -} -.pytorch-left-menu li.on a, .pytorch-left-menu li.current > a, -.pytorch-right-menu li.on a, -.pytorch-right-menu li.current > a { - position: relative; - border: none; -} -.pytorch-left-menu li.on a span.toctree-expand, .pytorch-left-menu li.current > a span.toctree-expand, -.pytorch-right-menu li.on a span.toctree-expand, -.pytorch-right-menu li.current > a span.toctree-expand { - display: block; - font-size: 0.8em; - line-height: 1.6em; -} -.pytorch-left-menu li.toctree-l1.current > a, -.pytorch-right-menu li.toctree-l1.current > a { - color: #792ee5; -} -.pytorch-left-menu li.toctree-l1.current > a:before, -.pytorch-right-menu li.toctree-l1.current > a:before { - content: ""; - display: block; - width: 2px; - height: 100%; - background: #792ee5; - left: -5px; - top: 0; - position: absolute; -} -.pytorch-left-menu li.toctree-l1.current li.toctree-l2 > ul, .pytorch-left-menu li.toctree-l2.current li.toctree-l3 > ul, -.pytorch-right-menu li.toctree-l1.current li.toctree-l2 > ul, -.pytorch-right-menu li.toctree-l2.current li.toctree-l3 > ul { - display: none; -} -.pytorch-left-menu li.toctree-l1.current li.toctree-l2.current > ul, .pytorch-left-menu li.toctree-l2.current li.toctree-l3.current > ul, -.pytorch-right-menu li.toctree-l1.current li.toctree-l2.current > ul, -.pytorch-right-menu li.toctree-l2.current li.toctree-l3.current > ul { - display: block; -} -.pytorch-left-menu li.toctree-l2.current li.toctree-l3 > a, -.pytorch-right-menu li.toctree-l2.current li.toctree-l3 > a { - display: block; -} -.pytorch-left-menu li.toctree-l3, -.pytorch-right-menu li.toctree-l3 { - font-size: 0.9em; -} -.pytorch-left-menu li.toctree-l3.current li.toctree-l4 > a, -.pytorch-right-menu li.toctree-l3.current li.toctree-l4 > a { - display: block; -} -.pytorch-left-menu li.toctree-l4, -.pytorch-right-menu li.toctree-l4 { - font-size: 0.9em; -} -.pytorch-left-menu li.current ul, -.pytorch-right-menu li.current ul { - display: block; -} -.pytorch-left-menu li ul, -.pytorch-right-menu li ul { - margin-bottom: 0; - display: none; -} -.pytorch-left-menu li ul li a, -.pytorch-right-menu li ul li a { - margin-bottom: 0; -} -.pytorch-left-menu a, -.pytorch-right-menu a { - display: inline-block; - position: relative; -} -.pytorch-left-menu a:hover, -.pytorch-right-menu a:hover { - cursor: pointer; -} -.pytorch-left-menu a:active, -.pytorch-right-menu a:active { - cursor: pointer; -} - -.pytorch-left-menu ul { - padding-left: 0; - margin-left: 5px; -} -.pytorch-right-menu a:link, -.pytorch-right-menu a:visited, -.pytorch-right-menu a:hover { - color: #6c6c6d; -} -.pytorch-right-menu a:link span.pre, -.pytorch-right-menu a:visited span.pre, -.pytorch-right-menu a:hover span.pre { - color: #6c6c6d; -} -.pytorch-right-menu a.reference.internal.expanded:before { - content: "-"; - font-family: monospace; - position: absolute; - left: -12px; -} -.pytorch-right-menu a.reference.internal.not-expanded:before { - content: "+"; - font-family: monospace; - position: absolute; - left: -12px; -} -.pytorch-right-menu li.active > a { - color: #792ee5; -} -.pytorch-right-menu li.active > a span.pre, .pytorch-right-menu li.active > a:before { - color: #792ee5; -} -.pytorch-right-menu li.active > a:after { - content: "\2022"; - color: #792ee5; - display: inline-block; - font-size: 1.375rem; - left: -17px; - position: absolute; - top: 1px; -} -.pytorch-right-menu .pytorch-side-scroll > ul > li > ul > li { - margin-bottom: 0; -} -.pytorch-right-menu ul ul { - padding-left: 0; -} -.pytorch-right-menu ul ul li { - padding-left: 0px; -} -.pytorch-right-menu ul ul li a.reference.internal { - padding-left: 0; -} -.pytorch-right-menu ul ul li ul { - display: none; - padding-left: 10px; -} -.pytorch-right-menu ul ul li li a.reference.internal { - padding-left: 0; -} -.pytorch-right-menu li ul { - display: block; -} - -.pytorch-right-menu .pytorch-side-scroll { - padding-top: 20px; -} -@media screen and (min-width: 1101px) { - .pytorch-right-menu .pytorch-side-scroll { - width: 100%; - } -} -@media screen and (min-width: 1600px) { - .pytorch-right-menu .pytorch-side-scroll { - width: 100%; - } -} -.pytorch-right-menu .pytorch-side-scroll > ul { - padding-left: 20px; - padding-right: 0; - margin-bottom: 0; -} -@media screen and (min-width: 1600px) { - .pytorch-right-menu .pytorch-side-scroll > ul { - padding-left: 25px; - } -} -.pytorch-right-menu .pytorch-side-scroll > ul > li > a.reference.internal { - color: #262626; - font-weight: 500; -} -.pytorch-right-menu .pytorch-side-scroll ul li { - position: relative; -} - -#pytorch-right-menu .side-scroll-highlight { - color: #792ee5; -} - -.header-container { - max-width: none; - margin-top: 4px; -} -@media screen and (min-width: 1101px) { - .header-container { - margin-top: 0; - } -} -@media screen and (min-width: 1600px) { - .header-container { - margin-top: 0; - } -} - -.container-fluid.header-holder, .header-holder.container-sm, .header-holder.container-md, .header-holder.container-lg, .header-holder.container-xl { - padding-right: 0; - padding-left: 0; -} - -.header-holder .container { - max-width: none; - padding-right: 1.875rem; - padding-left: 1.875rem; -} -@media screen and (min-width: 1101px) { - .header-holder .container { - padding-right: 1.875rem; - padding-left: 1.875rem; - } -} - -.header-holder .main-menu { - -webkit-box-pack: unset; - -ms-flex-pack: unset; - justify-content: unset; - position: relative; -} -@media screen and (min-width: 1101px) { - .header-holder .main-menu ul { - padding-left: 0; - margin-left: 26%; - } -} -@media screen and (min-width: 1600px) { - .header-holder .main-menu ul { - padding-left: 38px; - margin-left: 310px; - } -} - -.pytorch-page-level-bar { - display: none; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - background-color: #ffffff; - border-bottom: 1px solid #e2e2e2; - width: 100%; - z-index: 201; -} -@media screen and (min-width: 1101px) { - .pytorch-page-level-bar { - left: 0; - display: -webkit-box; - display: -ms-flexbox; - display: flex; - height: 45px; - padding-left: 0; - width: 100%; - position: absolute; - z-index: 1; - } - .pytorch-page-level-bar.left-menu-is-fixed { - position: fixed; - top: 0; - left: 25%; - padding-left: 0; - right: 0; - width: 75%; - } -} -@media screen and (min-width: 1600px) { - .pytorch-page-level-bar { - left: 0; - right: 0; - width: auto; - z-index: 1; - } - .pytorch-page-level-bar.left-menu-is-fixed { - left: 350px; - right: 0; - width: auto; - } -} -.pytorch-page-level-bar ul, .pytorch-page-level-bar li { - margin: 0; -} - -.pytorch-shortcuts-wrapper { - display: none; -} -@media screen and (min-width: 1101px) { - .pytorch-shortcuts-wrapper { - font-size: 0.875rem; - float: left; - margin-left: 2%; - } -} -@media screen and (min-width: 1600px) { - .pytorch-shortcuts-wrapper { - margin-left: 1.875rem; - } -} - -.cookie-banner-wrapper { - display: none; -} -.cookie-banner-wrapper .container { - padding-left: 1.875rem; - padding-right: 1.875rem; - max-width: 1240px; -} -.cookie-banner-wrapper.is-visible { - display: block; - position: fixed; - bottom: 0; - background-color: #f3f4f7; - min-height: 100px; - width: 100%; - z-index: 401; - border-top: 3px solid #ededee; -} -.cookie-banner-wrapper .gdpr-notice { - color: #6c6c6d; - margin-top: 1.5625rem; - text-align: left; - max-width: 1440px; -} -@media screen and (min-width: 768px) { - .cookie-banner-wrapper .gdpr-notice { - width: 77%; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - .cookie-banner-wrapper .gdpr-notice { - width: inherit; - } -} -.cookie-banner-wrapper .gdpr-notice .cookie-policy-link { - color: #343434; -} -.cookie-banner-wrapper .close-button { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: transparent; - border: 1px solid #f3f4f7; - height: 1.3125rem; - position: absolute; - bottom: 42px; - right: 0; - top: 0; - cursor: pointer; - outline: none; -} -@media screen and (min-width: 768px) { - .cookie-banner-wrapper .close-button { - right: 20%; - top: inherit; - } -} -@media (min-width: 768px) and (max-width: 1239px) { - .cookie-banner-wrapper .close-button { - right: 0; - top: 0; - } -} - -.main-menu ul li .resources-dropdown a { - cursor: pointer; -} -.main-menu ul li .dropdown-menu { - border-radius: 0; - padding: 0; -} -.main-menu ul li .dropdown-menu .dropdown-item { - color: #6c6c6d; - border-bottom: 1px solid #e2e2e2; -} -.main-menu ul li .dropdown-menu .dropdown-item:last-of-type { - border-bottom-color: transparent; -} -.main-menu ul li .dropdown-menu .dropdown-item:hover { - background-color: #792ee5; -} -.main-menu ul li .dropdown-menu .dropdown-item p { - font-size: 1rem; - color: #979797; -} -.main-menu ul li .dropdown-menu a.dropdown-item:hover { - color: #ffffff; -} -.main-menu ul li .dropdown-menu a.dropdown-item:hover p { - color: #ffffff; -} - -.resources-dropdown-menu { - display: none; - position: absolute; - z-index: 1000; - display: none; - float: left; - min-width: 10rem; - padding: 0.5rem 0; - font-size: 1rem; - color: #212529; - text-align: left; - list-style: none; - background-color: #ffffff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 0.25rem; -} - -.resources-dropdown:hover .resources-dropdown-menu { - display: block; -} - -.main-menu ul li .resources-dropdown-menu { - border-radius: 0; - padding: 0; -} -.main-menu ul li.active:hover .resources-dropdown-menu { - display: block; -} - -.main-menu ul li .resources-dropdown-menu .dropdown-item { - color: #6c6c6d; - border-bottom: 1px solid #e2e2e2; -} - -.resources-dropdown .with-down-orange-arrow { - padding-right: 2rem; - position: relative; - background: url("../images/chevron-down-orange.svg"); - background-size: 10px 15px; - background-position: top 0px right 12px; - background-repeat: no-repeat; -} - -.with-down-arrow { - padding-right: 2rem; - position: relative; - background-image: url("../images/chevron-down-black.svg"); - background-size: 14px 18px; - background-position: top 7px right 10px; - background-repeat: no-repeat; -} -.with-down-arrow:hover { - background-image: url("../images/chevron-down-orange.svg"); - background-repeat: no-repeat; -} - -.header-holder .main-menu ul li a.nav-dropdown-item { - display: block; - width: 100%; - clear: both; - font-weight: 400; - color: #979797; - text-align: left; - padding: 5px; - background-color: transparent; - border-bottom: 1px solid #e2e2e2; -} -.header-holder .main-menu ul li a.nav-dropdown-item:last-of-type { - border-bottom-color: transparent; -} -.header-holder .main-menu ul li a.nav-dropdown-item:hover { - background-color: #EFEEFF; - color: #792ee5; -} -.header-holder .main-menu ul li a.nav-dropdown-item .dropdown-title { - font-family: UCity; - font-size: 0.75rem; - font-weight: 400; - line-height: 1.375rem; - color: #6c6c6d; -} - -.header-holder .main-menu ul li a.nav-dropdown-item:hover .dropdown-title { - background-color: hover_background; - color: #792ee5; -} - -/*# sourceMappingURL=theme.css.map */ \ No newline at end of file diff --git a/docs/_static/doctools.js b/docs/_static/doctools.js deleted file mode 100644 index e1bfd70..0000000 --- a/docs/_static/doctools.js +++ /dev/null @@ -1,358 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for all documentation. - * - * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/** - * select a different prefix for underscore - */ -$u = _.noConflict(); - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", - "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", - "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {}; -} - */ - -/** - * small helper function to urldecode strings - * - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL - */ -jQuery.urldecode = function(x) { - if (!x) { - return x - } - return decodeURIComponent(x.replace(/\+/g, ' ')); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initIndexTable(); - this.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated === 'undefined') - return string; - return (typeof translated === 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated === 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - if (!body.length) { - body = $('body'); - } - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlighted'); - }); - }, 10); - $('') - .appendTo($('#searchbox')); - } - }, - - /** - * init the domain index toggle buttons - */ - initIndexTable : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - $('tr.cg-' + idnum).toggle(); - if (src.substr(-9) === 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('#searchbox .highlight-link').fadeOut(300); - $('span.highlighted').removeClass('highlighted'); - var url = new URL(window.location); - url.searchParams.delete('highlight'); - window.history.replaceState({}, '', url); - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar : function() { - $('input[name=q]').first().focus(); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this === '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - }, - - initOnKeyListeners: function() { - // only install a listener if it is really needed - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && - !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) - return; - - $(document).keydown(function(event) { - var activeElementType = document.activeElement.tagName; - // don't navigate when in search box, textarea, dropdown or button - if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' - && activeElementType !== 'BUTTON') { - if (event.altKey || event.ctrlKey || event.metaKey) - return; - - if (!event.shiftKey) { - switch (event.key) { - case 'ArrowLeft': - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) - break; - var prevHref = $('link[rel="prev"]').prop('href'); - if (prevHref) { - window.location.href = prevHref; - return false; - } - break; - case 'ArrowRight': - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) - break; - var nextHref = $('link[rel="next"]').prop('href'); - if (nextHref) { - window.location.href = nextHref; - return false; - } - break; - case 'Escape': - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) - break; - Documentation.hideSearchWords(); - return false; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case '/': - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) - break; - Documentation.focusSearchBar(); - return false; - } - } - }); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js deleted file mode 100644 index 6f4c5ec..0000000 --- a/docs/_static/documentation_options.js +++ /dev/null @@ -1,14 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '1.7.0dev', - LANGUAGE: 'None', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/docs/_static/file.png b/docs/_static/file.png deleted file mode 100644 index a858a41..0000000 Binary files a/docs/_static/file.png and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-bold-italic.woff b/docs/_static/fonts/FreightSans/freight-sans-bold-italic.woff deleted file mode 100644 index e317248..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-bold-italic.woff and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-bold-italic.woff2 b/docs/_static/fonts/FreightSans/freight-sans-bold-italic.woff2 deleted file mode 100644 index cec2dc9..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-bold-italic.woff2 and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-bold.woff b/docs/_static/fonts/FreightSans/freight-sans-bold.woff deleted file mode 100644 index de46625..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-bold.woff and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-bold.woff2 b/docs/_static/fonts/FreightSans/freight-sans-bold.woff2 deleted file mode 100644 index dc05cd8..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-bold.woff2 and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-book-italic.woff b/docs/_static/fonts/FreightSans/freight-sans-book-italic.woff deleted file mode 100644 index a50e503..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-book-italic.woff and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-book-italic.woff2 b/docs/_static/fonts/FreightSans/freight-sans-book-italic.woff2 deleted file mode 100644 index fe284db..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-book-italic.woff2 and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-book.woff b/docs/_static/fonts/FreightSans/freight-sans-book.woff deleted file mode 100644 index 6ab8775..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-book.woff and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-book.woff2 b/docs/_static/fonts/FreightSans/freight-sans-book.woff2 deleted file mode 100644 index 2688739..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-book.woff2 and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-light-italic.woff b/docs/_static/fonts/FreightSans/freight-sans-light-italic.woff deleted file mode 100644 index beda58d..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-light-italic.woff and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-light-italic.woff2 b/docs/_static/fonts/FreightSans/freight-sans-light-italic.woff2 deleted file mode 100644 index e2fa013..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-light-italic.woff2 and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-light.woff b/docs/_static/fonts/FreightSans/freight-sans-light.woff deleted file mode 100644 index 226a0bf..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-light.woff and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-light.woff2 b/docs/_static/fonts/FreightSans/freight-sans-light.woff2 deleted file mode 100644 index 6d8ff2c..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-light.woff2 and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-medium-italic.woff b/docs/_static/fonts/FreightSans/freight-sans-medium-italic.woff deleted file mode 100644 index a42115d..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-medium-italic.woff and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-medium-italic.woff2 b/docs/_static/fonts/FreightSans/freight-sans-medium-italic.woff2 deleted file mode 100644 index 16a7713..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-medium-italic.woff2 and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-medium.woff b/docs/_static/fonts/FreightSans/freight-sans-medium.woff deleted file mode 100644 index 5ea3453..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-medium.woff and /dev/null differ diff --git a/docs/_static/fonts/FreightSans/freight-sans-medium.woff2 b/docs/_static/fonts/FreightSans/freight-sans-medium.woff2 deleted file mode 100644 index c58b6a5..0000000 Binary files a/docs/_static/fonts/FreightSans/freight-sans-medium.woff2 and /dev/null differ diff --git a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Light.woff b/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Light.woff deleted file mode 100644 index cf37a5c..0000000 Binary files a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Light.woff and /dev/null differ diff --git a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Light.woff2 b/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Light.woff2 deleted file mode 100644 index 955a6ea..0000000 Binary files a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Light.woff2 and /dev/null differ diff --git a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Medium.woff b/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Medium.woff deleted file mode 100644 index fc65a67..0000000 Binary files a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Medium.woff and /dev/null differ diff --git a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Medium.woff2 b/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Medium.woff2 deleted file mode 100644 index c352e40..0000000 Binary files a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Medium.woff2 and /dev/null differ diff --git a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Regular.woff b/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Regular.woff deleted file mode 100644 index 7d63d89..0000000 Binary files a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Regular.woff and /dev/null differ diff --git a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Regular.woff2 b/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Regular.woff2 deleted file mode 100644 index d0d7ded..0000000 Binary files a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-Regular.woff2 and /dev/null differ diff --git a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff b/docs/_static/fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff deleted file mode 100644 index 1da7753..0000000 Binary files a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff and /dev/null differ diff --git a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff2 b/docs/_static/fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff2 deleted file mode 100644 index 79dffdb..0000000 Binary files a/docs/_static/fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff2 and /dev/null differ diff --git a/docs/_static/fonts/Inconsolata/Inconsolata.woff2 b/docs/_static/fonts/Inconsolata/Inconsolata.woff2 deleted file mode 100644 index 5d7a78e..0000000 Binary files a/docs/_static/fonts/Inconsolata/Inconsolata.woff2 and /dev/null differ diff --git a/docs/_static/fonts/UCity/UCity-Light.woff2 b/docs/_static/fonts/UCity/UCity-Light.woff2 deleted file mode 100644 index 27ba105..0000000 Binary files a/docs/_static/fonts/UCity/UCity-Light.woff2 and /dev/null differ diff --git a/docs/_static/fonts/UCity/UCity-Regular.woff2 b/docs/_static/fonts/UCity/UCity-Regular.woff2 deleted file mode 100644 index 86aafa8..0000000 Binary files a/docs/_static/fonts/UCity/UCity-Regular.woff2 and /dev/null differ diff --git a/docs/_static/fonts/UCity/UCity-Semibold.woff2 b/docs/_static/fonts/UCity/UCity-Semibold.woff2 deleted file mode 100644 index 50be9a4..0000000 Binary files a/docs/_static/fonts/UCity/UCity-Semibold.woff2 and /dev/null differ diff --git a/docs/_static/images/accelerator/ipus/profiler.png b/docs/_static/images/accelerator/ipus/profiler.png deleted file mode 100644 index cbed276..0000000 Binary files a/docs/_static/images/accelerator/ipus/profiler.png and /dev/null differ diff --git a/docs/_static/images/arrow-down-orange.svg b/docs/_static/images/arrow-down-orange.svg deleted file mode 100644 index 99b0e57..0000000 --- a/docs/_static/images/arrow-down-orange.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - Group 5 - Created with Sketch. - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_static/images/arrow-right-with-tail.svg b/docs/_static/images/arrow-right-with-tail.svg deleted file mode 100644 index f6e85bb..0000000 --- a/docs/_static/images/arrow-right-with-tail.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - Page 1 - Created with Sketch. - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_static/images/benchmarks/figure-parity-times.png b/docs/_static/images/benchmarks/figure-parity-times.png deleted file mode 100644 index 2e8c589..0000000 Binary files a/docs/_static/images/benchmarks/figure-parity-times.png and /dev/null differ diff --git a/docs/_static/images/chevron-down-black.svg b/docs/_static/images/chevron-down-black.svg deleted file mode 100644 index 097bc07..0000000 --- a/docs/_static/images/chevron-down-black.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - Created with Sketch. - - - - - - - - - - - - - diff --git a/docs/_static/images/chevron-down-grey.svg b/docs/_static/images/chevron-down-grey.svg deleted file mode 100644 index 82d6514..0000000 --- a/docs/_static/images/chevron-down-grey.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - -Created with Sketch. - - - - - - - - - - - - diff --git a/docs/_static/images/chevron-down-orange.svg b/docs/_static/images/chevron-down-orange.svg deleted file mode 100644 index d993c72..0000000 --- a/docs/_static/images/chevron-down-orange.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - Created with Sketch. - - - - - - - - - - - - - diff --git a/docs/_static/images/chevron-down-white.svg b/docs/_static/images/chevron-down-white.svg deleted file mode 100644 index e6c94e2..0000000 --- a/docs/_static/images/chevron-down-white.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - Created with Sketch. - - - - - - - - - - - - - diff --git a/docs/_static/images/chevron-right-orange.svg b/docs/_static/images/chevron-right-orange.svg deleted file mode 100644 index b43c4f7..0000000 --- a/docs/_static/images/chevron-right-orange.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - -Page 1 -Created with Sketch. - - - - - - - - - - diff --git a/docs/_static/images/chevron-right-white.svg b/docs/_static/images/chevron-right-white.svg deleted file mode 100644 index dd9e77f..0000000 --- a/docs/_static/images/chevron-right-white.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - -Page 1 -Created with Sketch. - - - - - - - - - - \ No newline at end of file diff --git a/docs/_static/images/home-footer-background.jpg b/docs/_static/images/home-footer-background.jpg deleted file mode 100644 index b307bb5..0000000 Binary files a/docs/_static/images/home-footer-background.jpg and /dev/null differ diff --git a/docs/_static/images/icon-close.svg b/docs/_static/images/icon-close.svg deleted file mode 100644 index 348964e..0000000 --- a/docs/_static/images/icon-close.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - Page 1 - Created with Sketch. - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_static/images/icon-menu-dots-dark.svg b/docs/_static/images/icon-menu-dots-dark.svg deleted file mode 100644 index fa2ad04..0000000 --- a/docs/_static/images/icon-menu-dots-dark.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - Page 1 - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/_static/images/lightning_examples/augmentation_kornia.svg b/docs/_static/images/lightning_examples/augmentation_kornia.svg deleted file mode 100644 index 481762a..0000000 --- a/docs/_static/images/lightning_examples/augmentation_kornia.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/docs/_static/images/logo-facebook-dark.svg b/docs/_static/images/logo-facebook-dark.svg deleted file mode 100644 index cff1791..0000000 --- a/docs/_static/images/logo-facebook-dark.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/docs/_static/images/logo-icon.svg b/docs/_static/images/logo-icon.svg deleted file mode 100644 index a3ff2ee..0000000 --- a/docs/_static/images/logo-icon.svg +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - diff --git a/docs/_static/images/logo-large.svg b/docs/_static/images/logo-large.svg deleted file mode 100644 index 4a6cd73..0000000 --- a/docs/_static/images/logo-large.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/docs/_static/images/logo-lightning-large.svg b/docs/_static/images/logo-lightning-large.svg deleted file mode 100644 index 4a6cd73..0000000 --- a/docs/_static/images/logo-lightning-large.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - diff --git a/docs/_static/images/logo-pytorch-dark.svg b/docs/_static/images/logo-pytorch-dark.svg deleted file mode 100644 index 9b4c1a5..0000000 --- a/docs/_static/images/logo-pytorch-dark.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_static/images/logo-pytorch-icon.svg b/docs/_static/images/logo-pytorch-icon.svg deleted file mode 100644 index 575f682..0000000 --- a/docs/_static/images/logo-pytorch-icon.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/docs/_static/images/logo-pytorch.svg b/docs/_static/images/logo-pytorch.svg deleted file mode 100644 index f8d44b9..0000000 --- a/docs/_static/images/logo-pytorch.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_static/images/logo-twitter-dark.svg b/docs/_static/images/logo-twitter-dark.svg deleted file mode 100644 index 1572570..0000000 --- a/docs/_static/images/logo-twitter-dark.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - diff --git a/docs/_static/images/logo-youtube-dark.svg b/docs/_static/images/logo-youtube-dark.svg deleted file mode 100644 index e3cfedd..0000000 --- a/docs/_static/images/logo-youtube-dark.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_static/images/pytorch-colab.svg b/docs/_static/images/pytorch-colab.svg deleted file mode 100644 index 2ab15e2..0000000 --- a/docs/_static/images/pytorch-colab.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - diff --git a/docs/_static/images/pytorch-download.svg b/docs/_static/images/pytorch-download.svg deleted file mode 100644 index cc37d63..0000000 --- a/docs/_static/images/pytorch-download.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/docs/_static/images/pytorch-github.svg b/docs/_static/images/pytorch-github.svg deleted file mode 100644 index 2c2570d..0000000 --- a/docs/_static/images/pytorch-github.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/docs/_static/images/pytorch-x.svg b/docs/_static/images/pytorch-x.svg deleted file mode 100644 index 8207af4..0000000 --- a/docs/_static/images/pytorch-x.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/docs/_static/images/search-icon.svg b/docs/_static/images/search-icon.svg deleted file mode 100644 index 32b597e..0000000 --- a/docs/_static/images/search-icon.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - Created with Sketch. - - - - - - - - - - - - - - - diff --git a/docs/_static/images/trainer/lr_finder.png b/docs/_static/images/trainer/lr_finder.png deleted file mode 100644 index bd1667b..0000000 Binary files a/docs/_static/images/trainer/lr_finder.png and /dev/null differ diff --git a/docs/_static/images/view-page-source-icon.svg b/docs/_static/images/view-page-source-icon.svg deleted file mode 100644 index 8477adf..0000000 --- a/docs/_static/images/view-page-source-icon.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/docs/_static/jquery-3.5.1.js b/docs/_static/jquery-3.5.1.js deleted file mode 100644 index 5093733..0000000 --- a/docs/_static/jquery-3.5.1.js +++ /dev/null @@ -1,10872 +0,0 @@ -/*! - * jQuery JavaScript Library v3.5.1 - * https://jquery.com/ - * - * Includes Sizzle.js - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://jquery.org/license - * - * Date: 2020-05-04T22:49Z - */ -( function( global, factory ) { - - "use strict"; - - if ( typeof module === "object" && typeof module.exports === "object" ) { - - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get jQuery. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info. - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 -// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode -// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common -// enough that all such attempts are guarded in a try block. -"use strict"; - -var arr = []; - -var getProto = Object.getPrototypeOf; - -var slice = arr.slice; - -var flat = arr.flat ? function( array ) { - return arr.flat.call( array ); -} : function( array ) { - return arr.concat.apply( [], array ); -}; - - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var fnToString = hasOwn.toString; - -var ObjectFunctionString = fnToString.call( Object ); - -var support = {}; - -var isFunction = function isFunction( obj ) { - - // Support: Chrome <=57, Firefox <=52 - // In some browsers, typeof returns "function" for HTML elements - // (i.e., `typeof document.createElement( "object" ) === "function"`). - // We don't want to classify *any* DOM node as a function. - return typeof obj === "function" && typeof obj.nodeType !== "number"; - }; - - -var isWindow = function isWindow( obj ) { - return obj != null && obj === obj.window; - }; - - -var document = window.document; - - - - var preservedScriptAttributes = { - type: true, - src: true, - nonce: true, - noModule: true - }; - - function DOMEval( code, node, doc ) { - doc = doc || document; - - var i, val, - script = doc.createElement( "script" ); - - script.text = code; - if ( node ) { - for ( i in preservedScriptAttributes ) { - - // Support: Firefox 64+, Edge 18+ - // Some browsers don't support the "nonce" property on scripts. - // On the other hand, just using `getAttribute` is not enough as - // the `nonce` attribute is reset to an empty string whenever it - // becomes browsing-context connected. - // See https://github.com/whatwg/html/issues/2369 - // See https://html.spec.whatwg.org/#nonce-attributes - // The `node.getAttribute` check was added for the sake of - // `jQuery.globalEval` so that it can fake a nonce-containing node - // via an object. - val = node[ i ] || node.getAttribute && node.getAttribute( i ); - if ( val ) { - script.setAttribute( i, val ); - } - } - } - doc.head.appendChild( script ).parentNode.removeChild( script ); - } - - -function toType( obj ) { - if ( obj == null ) { - return obj + ""; - } - - // Support: Android <=2.3 only (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call( obj ) ] || "object" : - typeof obj; -} -/* global Symbol */ -// Defining this global in .eslintrc.json would create a danger of using the global -// unguarded in another place, it seems safer to define global only for this module - - - -var - version = "3.5.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }; - -jQuery.fn = jQuery.prototype = { - - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - - // Return all the elements in a clean array - if ( num == null ) { - return slice.call( this ); - } - - // Return just the one element from the set - return num < 0 ? this[ num + this.length ] : this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - each: function( callback ) { - return jQuery.each( this, callback ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map( this, function( elem, i ) { - return callback.call( elem, i, elem ); - } ) ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - even: function() { - return this.pushStack( jQuery.grep( this, function( _elem, i ) { - return ( i + 1 ) % 2; - } ) ); - }, - - odd: function() { - return this.pushStack( jQuery.grep( this, function( _elem, i ) { - return i % 2; - } ) ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[ 0 ] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !isFunction( target ) ) { - target = {}; - } - - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - - // Only deal with non-null/undefined values - if ( ( options = arguments[ i ] ) != null ) { - - // Extend the base object - for ( name in options ) { - copy = options[ name ]; - - // Prevent Object.prototype pollution - // Prevent never-ending loop - if ( name === "__proto__" || target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject( copy ) || - ( copyIsArray = Array.isArray( copy ) ) ) ) { - src = target[ name ]; - - // Ensure proper type for the source value - if ( copyIsArray && !Array.isArray( src ) ) { - clone = []; - } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { - clone = {}; - } else { - clone = src; - } - copyIsArray = false; - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend( { - - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - isPlainObject: function( obj ) { - var proto, Ctor; - - // Detect obvious negatives - // Use toString instead of jQuery.type to catch host objects - if ( !obj || toString.call( obj ) !== "[object Object]" ) { - return false; - } - - proto = getProto( obj ); - - // Objects with no prototype (e.g., `Object.create( null )`) are plain - if ( !proto ) { - return true; - } - - // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; - return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; - }, - - isEmptyObject: function( obj ) { - var name; - - for ( name in obj ) { - return false; - } - return true; - }, - - // Evaluates a script in a provided context; falls back to the global one - // if not specified. - globalEval: function( code, options, doc ) { - DOMEval( code, { nonce: options && options.nonce }, doc ); - }, - - each: function( obj, callback ) { - var length, i = 0; - - if ( isArrayLike( obj ) ) { - length = obj.length; - for ( ; i < length; i++ ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } else { - for ( i in obj ) { - if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { - break; - } - } - } - - return obj; - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArrayLike( Object( arr ) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var length, value, - i = 0, - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArrayLike( elems ) ) { - length = elems.length; - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return flat( ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -} ); - -if ( typeof Symbol === "function" ) { - jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; -} - -// Populate the class2type map -jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), -function( _i, name ) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -} ); - -function isArrayLike( obj ) { - - // Support: real iOS 8.2 only (not reproducible in simulator) - // `in` check used to prevent JIT error (gh-2145) - // hasOwn isn't used here due to false negatives - // regarding Nodelist length in IE - var length = !!obj && "length" in obj && obj.length, - type = toType( obj ); - - if ( isFunction( obj ) || isWindow( obj ) ) { - return false; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.3.5 - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://js.foundation/ - * - * Date: 2020-03-14 - */ -( function( window ) { -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - nonnativeSelectorCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // Instance methods - hasOwn = ( {} ).hasOwnProperty, - arr = [], - pop = arr.pop, - pushNative = arr.push, - push = arr.push, - slice = arr.slice, - - // Use a stripped-down indexOf as it's faster than native - // https://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[ i ] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + - "ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - - // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram - identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + - "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + - - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - - // "Attribute values must be CSS identifiers [capture 5] - // or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + - whitespace + "*\\]", - - pseudos = ":(" + identifier + ")(?:\\((" + - - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rwhitespace = new RegExp( whitespace + "+", "g" ), - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + - whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + - "*" ), - rdescend = new RegExp( whitespace + "|>" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + identifier + ")" ), - "CLASS": new RegExp( "^\\.(" + identifier + ")" ), - "TAG": new RegExp( "^(" + identifier + "|[*])" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + - whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + - whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + - "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + - "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rhtml = /HTML$/i, - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - - // CSS escapes - // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), - funescape = function( escape, nonHex ) { - var high = "0x" + escape.slice( 1 ) - 0x10000; - - return nonHex ? - - // Strip the backslash prefix from a non-hex escape sequence - nonHex : - - // Replace a hexadecimal escape sequence with the encoded Unicode code point - // Support: IE <=11+ - // For values outside the Basic Multilingual Plane (BMP), manually construct a - // surrogate pair - high < 0 ? - String.fromCharCode( high + 0x10000 ) : - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // CSS string/identifier serialization - // https://drafts.csswg.org/cssom/#common-serializing-idioms - rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, - fcssescape = function( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + - ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; - } - - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; - }, - - // Used for iframes - // See setDocument() - // Removing the function wrapper causes a "Permission Denied" - // error in IE - unloadHandler = function() { - setDocument(); - }, - - inDisabledFieldset = addCombinator( - function( elem ) { - return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; - }, - { dir: "parentNode", next: "legend" } - ); - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - ( arr = slice.call( preferredDoc.childNodes ) ), - preferredDoc.childNodes - ); - - // Support: Android<4.0 - // Detect silently failing push.apply - // eslint-disable-next-line no-unused-expressions - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - pushNative.apply( target, slice.call( els ) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - - // Can't trust NodeList.length - while ( ( target[ j++ ] = els[ i++ ] ) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var m, i, elem, nid, match, groups, newSelector, - newContext = context && context.ownerDocument, - - // nodeType defaults to 9, since context defaults to document - nodeType = context ? context.nodeType : 9; - - results = results || []; - - // Return early from calls with invalid selector or context - if ( typeof selector !== "string" || !selector || - nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { - - return results; - } - - // Try to shortcut find operations (as opposed to filters) in HTML documents - if ( !seed ) { - setDocument( context ); - context = context || document; - - if ( documentIsHTML ) { - - // If the selector is sufficiently simple, try using a "get*By*" DOM method - // (excepting DocumentFragment context, where the methods don't exist) - if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { - - // ID selector - if ( ( m = match[ 1 ] ) ) { - - // Document context - if ( nodeType === 9 ) { - if ( ( elem = context.getElementById( m ) ) ) { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - - // Element context - } else { - - // Support: IE, Opera, Webkit - // TODO: identify versions - // getElementById can match elements by name instead of ID - if ( newContext && ( elem = newContext.getElementById( m ) ) && - contains( context, elem ) && - elem.id === m ) { - - results.push( elem ); - return results; - } - } - - // Type selector - } else if ( match[ 2 ] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Class selector - } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && - context.getElementsByClassName ) { - - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // Take advantage of querySelectorAll - if ( support.qsa && - !nonnativeSelectorCache[ selector + " " ] && - ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && - - // Support: IE 8 only - // Exclude object elements - ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { - - newSelector = selector; - newContext = context; - - // qSA considers elements outside a scoping root when evaluating child or - // descendant combinators, which is not what we want. - // In such cases, we work around the behavior by prefixing every selector in the - // list with an ID selector referencing the scope context. - // The technique has to be used as well when a leading combinator is used - // as such selectors are not recognized by querySelectorAll. - // Thanks to Andrew Dupont for this technique. - if ( nodeType === 1 && - ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; - - // We can use :scope instead of the ID hack if the browser - // supports it & if we're not changing the context. - if ( newContext !== context || !support.scope ) { - - // Capture the context ID, setting it first if necessary - if ( ( nid = context.getAttribute( "id" ) ) ) { - nid = nid.replace( rcssescape, fcssescape ); - } else { - context.setAttribute( "id", ( nid = expando ) ); - } - } - - // Prefix every selector in the list - groups = tokenize( selector ); - i = groups.length; - while ( i-- ) { - groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + - toSelector( groups[ i ] ); - } - newSelector = groups.join( "," ); - } - - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch ( qsaError ) { - nonnativeSelectorCache( selector, true ); - } finally { - if ( nid === expando ) { - context.removeAttribute( "id" ); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {function(string, object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return ( cache[ key + " " ] = value ); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created element and returns a boolean result - */ -function assert( fn ) { - var el = document.createElement( "fieldset" ); - - try { - return !!fn( el ); - } catch ( e ) { - return false; - } finally { - - // Remove from its parent by default - if ( el.parentNode ) { - el.parentNode.removeChild( el ); - } - - // release memory in IE - el = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split( "|" ), - i = arr.length; - - while ( i-- ) { - Expr.attrHandle[ arr[ i ] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - a.sourceIndex - b.sourceIndex; - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( ( cur = cur.nextSibling ) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return ( name === "input" || name === "button" ) && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for :enabled/:disabled - * @param {Boolean} disabled true for :disabled; false for :enabled - */ -function createDisabledPseudo( disabled ) { - - // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable - return function( elem ) { - - // Only certain elements can match :enabled or :disabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled - // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled - if ( "form" in elem ) { - - // Check for inherited disabledness on relevant non-disabled elements: - // * listed form-associated elements in a disabled fieldset - // https://html.spec.whatwg.org/multipage/forms.html#category-listed - // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled - // * option elements in a disabled optgroup - // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled - // All such elements have a "form" property. - if ( elem.parentNode && elem.disabled === false ) { - - // Option elements defer to a parent optgroup if present - if ( "label" in elem ) { - if ( "label" in elem.parentNode ) { - return elem.parentNode.disabled === disabled; - } else { - return elem.disabled === disabled; - } - } - - // Support: IE 6 - 11 - // Use the isDisabled shortcut property to check for disabled fieldset ancestors - return elem.isDisabled === disabled || - - // Where there is no isDisabled, check manually - /* jshint -W018 */ - elem.isDisabled !== !disabled && - inDisabledFieldset( elem ) === disabled; - } - - return elem.disabled === disabled; - - // Try to winnow out elements that can't be disabled before trusting the disabled property. - // Some victims get caught in our net (label, legend, menu, track), but it shouldn't - // even exist on them, let alone have a boolean value. - } else if ( "label" in elem ) { - return elem.disabled === disabled; - } - - // Remaining elements are neither :enabled nor :disabled - return false; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction( function( argument ) { - argument = +argument; - return markFunction( function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ ( j = matchIndexes[ i ] ) ] ) { - seed[ j ] = !( matches[ j ] = seed[ j ] ); - } - } - } ); - } ); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== "undefined" && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - var namespace = elem.namespaceURI, - docElem = ( elem.ownerDocument || elem ).documentElement; - - // Support: IE <=8 - // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes - // https://bugs.jquery.com/ticket/4833 - return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, subWindow, - doc = node ? node.ownerDocument || node : preferredDoc; - - // Return early if doc is invalid or already selected - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Update global variables - document = doc; - docElem = document.documentElement; - documentIsHTML = !isXML( document ); - - // Support: IE 9 - 11+, Edge 12 - 18+ - // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( preferredDoc != document && - ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { - - // Support: IE 11, Edge - if ( subWindow.addEventListener ) { - subWindow.addEventListener( "unload", unloadHandler, false ); - - // Support: IE 9 - 10 only - } else if ( subWindow.attachEvent ) { - subWindow.attachEvent( "onunload", unloadHandler ); - } - } - - // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, - // Safari 4 - 5 only, Opera <=11.6 - 12.x only - // IE/Edge & older browsers don't support the :scope pseudo-class. - // Support: Safari 6.0 only - // Safari 6.0 supports :scope but it's an alias of :root there. - support.scope = assert( function( el ) { - docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); - return typeof el.querySelectorAll !== "undefined" && - !el.querySelectorAll( ":scope fieldset div" ).length; - } ); - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties - // (excepting IE8 booleans) - support.attributes = assert( function( el ) { - el.className = "i"; - return !el.getAttribute( "className" ); - } ); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert( function( el ) { - el.appendChild( document.createComment( "" ) ); - return !el.getElementsByTagName( "*" ).length; - } ); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( document.getElementsByClassName ); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert( function( el ) { - docElem.appendChild( el ).id = expando; - return !document.getElementsByName || !document.getElementsByName( expando ).length; - } ); - - // ID filter and find - if ( support.getById ) { - Expr.filter[ "ID" ] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute( "id" ) === attrId; - }; - }; - Expr.find[ "ID" ] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var elem = context.getElementById( id ); - return elem ? [ elem ] : []; - } - }; - } else { - Expr.filter[ "ID" ] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== "undefined" && - elem.getAttributeNode( "id" ); - return node && node.value === attrId; - }; - }; - - // Support: IE 6 - 7 only - // getElementById is not reliable as a find shortcut - Expr.find[ "ID" ] = function( id, context ) { - if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { - var node, i, elems, - elem = context.getElementById( id ); - - if ( elem ) { - - // Verify the id attribute - node = elem.getAttributeNode( "id" ); - if ( node && node.value === id ) { - return [ elem ]; - } - - // Fall back on getElementsByName - elems = context.getElementsByName( id ); - i = 0; - while ( ( elem = elems[ i++ ] ) ) { - node = elem.getAttributeNode( "id" ); - if ( node && node.value === id ) { - return [ elem ]; - } - } - } - - return []; - } - }; - } - - // Tag - Expr.find[ "TAG" ] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); - } - } : - - function( tag, context ) { - var elem, - tmp = [], - i = 0, - - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( ( elem = results[ i++ ] ) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See https://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert( function( el ) { - - var input; - - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // https://bugs.jquery.com/ticket/12359 - docElem.appendChild( el ).innerHTML = "" + - ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !el.querySelectorAll( "[selected]" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push( "~=" ); - } - - // Support: IE 11+, Edge 15 - 18+ - // IE 11/Edge don't find elements on a `[name='']` query in some cases. - // Adding a temporary attribute to the document before the selection works - // around the issue. - // Interestingly, IE 10 & older don't seem to have the issue. - input = document.createElement( "input" ); - input.setAttribute( "name", "" ); - el.appendChild( input ); - if ( !el.querySelectorAll( "[name='']" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + - whitespace + "*(?:''|\"\")" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !el.querySelectorAll( ":checked" ).length ) { - rbuggyQSA.push( ":checked" ); - } - - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push( ".#.+[+~]" ); - } - - // Support: Firefox <=3.6 - 5 only - // Old Firefox doesn't throw on a badly-escaped identifier. - el.querySelectorAll( "\\\f" ); - rbuggyQSA.push( "[\\r\\n\\f]" ); - } ); - - assert( function( el ) { - el.innerHTML = "" + - ""; - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = document.createElement( "input" ); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( el.querySelectorAll( "[name=d]" ).length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE9-11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - docElem.appendChild( el ).disabled = true; - if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: Opera 10 - 11 only - // Opera 10-11 does not throw on post-comma invalid pseudos - el.querySelectorAll( "*,:x" ); - rbuggyQSA.push( ",.*:" ); - } ); - } - - if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector ) ) ) ) { - - assert( function( el ) { - - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( el, "*" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( el, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - } ); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully self-exclusive - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - ) ); - } : - function( a, b ) { - if ( b ) { - while ( ( b = b.parentNode ) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { - - // Choose the first element that is related to our preferred document - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( a == document || a.ownerDocument == preferredDoc && - contains( preferredDoc, a ) ) { - return -1; - } - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( b == document || b.ownerDocument == preferredDoc && - contains( preferredDoc, b ) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - /* eslint-disable eqeqeq */ - return a == document ? -1 : - b == document ? 1 : - /* eslint-enable eqeqeq */ - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( ( cur = cur.parentNode ) ) { - ap.unshift( cur ); - } - cur = b; - while ( ( cur = cur.parentNode ) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[ i ] === bp[ i ] ) { - i++; - } - - return i ? - - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[ i ], bp[ i ] ) : - - // Otherwise nodes in our document sort first - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - /* eslint-disable eqeqeq */ - ap[ i ] == preferredDoc ? -1 : - bp[ i ] == preferredDoc ? 1 : - /* eslint-enable eqeqeq */ - 0; - }; - - return document; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - setDocument( elem ); - - if ( support.matchesSelector && documentIsHTML && - !nonnativeSelectorCache[ expr + " " ] && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch ( e ) { - nonnativeSelectorCache( expr, true ); - } - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - - // Set document vars if needed - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( ( context.ownerDocument || context ) != document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - - // Set document vars if needed - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( ( elem.ownerDocument || elem ) != document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - ( val = elem.getAttributeNode( name ) ) && val.specified ? - val.value : - null; -}; - -Sizzle.escape = function( sel ) { - return ( sel + "" ).replace( rcssescape, fcssescape ); -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( ( elem = results[ i++ ] ) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - - // If no nodeType, this is expected to be an array - while ( ( node = elem[ i++ ] ) ) { - - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[ 1 ] = match[ 1 ].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[ 3 ] = ( match[ 3 ] || match[ 4 ] || - match[ 5 ] || "" ).replace( runescape, funescape ); - - if ( match[ 2 ] === "~=" ) { - match[ 3 ] = " " + match[ 3 ] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[ 1 ] = match[ 1 ].toLowerCase(); - - if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { - - // nth-* requires argument - if ( !match[ 3 ] ) { - Sizzle.error( match[ 0 ] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[ 4 ] = +( match[ 4 ] ? - match[ 5 ] + ( match[ 6 ] || 1 ) : - 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); - match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); - - // other types prohibit arguments - } else if ( match[ 3 ] ) { - Sizzle.error( match[ 0 ] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[ 6 ] && match[ 2 ]; - - if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[ 3 ] ) { - match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - - // Get excess from tokenize (recursively) - ( excess = tokenize( unquoted, true ) ) && - - // advance to the next closing parenthesis - ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { - - // excess is a negative index - match[ 0 ] = match[ 0 ].slice( 0, excess ); - match[ 2 ] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { - return true; - } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - ( pattern = new RegExp( "(^|" + whitespace + - ")" + className + "(" + whitespace + "|$)" ) ) && classCache( - className, function( elem ) { - return pattern.test( - typeof elem.className === "string" && elem.className || - typeof elem.getAttribute !== "undefined" && - elem.getAttribute( "class" ) || - "" - ); - } ); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - /* eslint-disable max-len */ - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - /* eslint-enable max-len */ - - }; - }, - - "CHILD": function( type, what, _argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, _context, xml ) { - var cache, uniqueCache, outerCache, node, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType, - diff = false; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( ( node = node[ dir ] ) ) { - if ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) { - - return false; - } - } - - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - - // Seek `elem` from a previously-cached index - - // ...in a gzip-friendly way - node = parent; - outerCache = node[ expando ] || ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex && cache[ 2 ]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( ( node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - ( diff = nodeIndex = 0 ) || start.pop() ) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else { - - // Use previously-cached element index if available - if ( useCache ) { - - // ...in a gzip-friendly way - node = elem; - outerCache = node[ expando ] || ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - cache = uniqueCache[ type ] || []; - nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; - diff = nodeIndex; - } - - // xml :nth-child(...) - // or :nth-last-child(...) or :nth(-last)?-of-type(...) - if ( diff === false ) { - - // Use the same loop as above to seek `elem` from the start - while ( ( node = ++nodeIndex && node && node[ dir ] || - ( diff = nodeIndex = 0 ) || start.pop() ) ) { - - if ( ( ofType ? - node.nodeName.toLowerCase() === name : - node.nodeType === 1 ) && - ++diff ) { - - // Cache the index of each encountered element - if ( useCache ) { - outerCache = node[ expando ] || - ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - uniqueCache[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction( function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf( seed, matched[ i ] ); - seed[ idx ] = !( matches[ idx ] = matched[ i ] ); - } - } ) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - - // Potentially complex pseudos - "not": markFunction( function( selector ) { - - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction( function( seed, matches, _context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( ( elem = unmatched[ i ] ) ) { - seed[ i ] = !( matches[ i ] = elem ); - } - } - } ) : - function( elem, _context, xml ) { - input[ 0 ] = elem; - matcher( input, null, xml, results ); - - // Don't keep the element (issue #299) - input[ 0 ] = null; - return !results.pop(); - }; - } ), - - "has": markFunction( function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - } ), - - "contains": markFunction( function( text ) { - text = text.replace( runescape, funescape ); - return function( elem ) { - return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; - }; - } ), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - - // lang value must be a valid identifier - if ( !ridentifier.test( lang || "" ) ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( ( elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); - return false; - }; - } ), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && - ( !document.hasFocus || document.hasFocus() ) && - !!( elem.type || elem.href || ~elem.tabIndex ); - }, - - // Boolean properties - "enabled": createDisabledPseudo( false ), - "disabled": createDisabledPseudo( true ), - - "checked": function( elem ) { - - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return ( nodeName === "input" && !!elem.checked ) || - ( nodeName === "option" && !!elem.selected ); - }, - - "selected": function( elem ) { - - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - // eslint-disable-next-line no-unused-expressions - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos[ "empty" ]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( ( attr = elem.getAttribute( "type" ) ) == null || - attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo( function() { - return [ 0 ]; - } ), - - "last": createPositionalPseudo( function( _matchIndexes, length ) { - return [ length - 1 ]; - } ), - - "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - } ), - - "even": createPositionalPseudo( function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - "odd": createPositionalPseudo( function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { - var i = argument < 0 ? - argument + length : - argument > length ? - length : - argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ), - - "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - } ) - } -}; - -Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || ( match = rcomma.exec( soFar ) ) ) { - if ( match ) { - - // Don't consume trailing commas as valid - soFar = soFar.slice( match[ 0 ].length ) || soFar; - } - groups.push( ( tokens = [] ) ); - } - - matched = false; - - // Combinators - if ( ( match = rcombinators.exec( soFar ) ) ) { - matched = match.shift(); - tokens.push( { - value: matched, - - // Cast descendant combinators to space - type: match[ 0 ].replace( rtrim, " " ) - } ); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || - ( match = preFilters[ type ]( match ) ) ) ) { - matched = match.shift(); - tokens.push( { - value: matched, - type: type, - matches: match - } ); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[ i ].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - skip = combinator.next, - key = skip || dir, - checkNonElements = base && key === "parentNode", - doneName = done++; - - return combinator.first ? - - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - return false; - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, uniqueCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching - if ( xml ) { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( ( elem = elem[ dir ] ) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || ( elem[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ elem.uniqueID ] || - ( outerCache[ elem.uniqueID ] = {} ); - - if ( skip && skip === elem.nodeName.toLowerCase() ) { - elem = elem[ dir ] || elem; - } else if ( ( oldCache = uniqueCache[ key ] ) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return ( newCache[ 2 ] = oldCache[ 2 ] ); - } else { - - // Reuse newcache so results back-propagate to previous elements - uniqueCache[ key ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { - return true; - } - } - } - } - } - return false; - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[ i ]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[ 0 ]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[ i ], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( ( elem = unmatched[ i ] ) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction( function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( - selector || "*", - context.nodeType ? [ context ] : context, - [] - ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( ( elem = temp[ i ] ) ) { - matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( ( elem = matcherOut[ i ] ) ) { - - // Restore matcherIn since elem is not yet a final match - temp.push( ( matcherIn[ i ] = elem ) ); - } - } - postFinder( null, ( matcherOut = [] ), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( ( elem = matcherOut[ i ] ) && - ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { - - seed[ temp ] = !( results[ temp ] = elem ); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - } ); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[ 0 ].type ], - implicitRelative = leadingRelative || Expr.relative[ " " ], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - ( checkContext = context ).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - - // Avoid hanging onto element (issue #299) - checkContext = null; - return ret; - } ]; - - for ( ; i < len; i++ ) { - if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { - matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; - } else { - matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[ j ].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens - .slice( 0, i - 1 ) - .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), - - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), - len = elems.length; - - if ( outermost ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - outermostContext = context == document || context || outermost; - } - - // Add elements passing elementMatchers directly to results - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - // eslint-disable-next-line eqeqeq - if ( !context && elem.ownerDocument != document ) { - setDocument( elem ); - xml = !documentIsHTML; - } - while ( ( matcher = elementMatchers[ j++ ] ) ) { - if ( matcher( elem, context || document, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - - // They will have gone through all possible matchers - if ( ( elem = !matcher && elem ) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // `i` is now the count of elements visited above, and adding it to `matchedCount` - // makes the latter nonnegative. - matchedCount += i; - - // Apply set filters to unmatched elements - // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` - // equals `i`), unless we didn't visit _any_ elements in the above loop because we have - // no element matchers and no seed. - // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that - // case, which will result in a "00" `matchedCount` that differs from `i` but is also - // numerically zero. - if ( bySet && i !== matchedCount ) { - j = 0; - while ( ( matcher = setMatchers[ j++ ] ) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !( unmatched[ i ] || setMatched[ i ] ) ) { - setMatched[ i ] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[ i ] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( - selector, - matcherFromGroupMatchers( elementMatchers, setMatchers ) - ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( ( selector = compiled.selector || selector ) ); - - results = results || []; - - // Try to minimize operations if there is only one selector in the list and no seed - // (the latter of which guarantees us context) - if ( match.length === 1 ) { - - // Reduce context if the leading compound selector is an ID - tokens = match[ 0 ] = match[ 0 ].slice( 0 ); - if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && - context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { - - context = ( Expr.find[ "ID" ]( token.matches[ 0 ] - .replace( runescape, funescape ), context ) || [] )[ 0 ]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[ i ]; - - // Abort if we hit a combinator - if ( Expr.relative[ ( type = token.type ) ] ) { - break; - } - if ( ( find = Expr.find[ type ] ) ) { - - // Search, expanding context for leading sibling combinators - if ( ( seed = find( - token.matches[ 0 ].replace( runescape, funescape ), - rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || - context - ) ) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - !context || rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; - -// Support: Chrome 14-35+ -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert( function( el ) { - - // Should return 1, but returns 4 (following) - return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; -} ); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert( function( el ) { - el.innerHTML = ""; - return el.firstChild.getAttribute( "href" ) === "#"; -} ) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - } ); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert( function( el ) { - el.innerHTML = ""; - el.firstChild.setAttribute( "value", "" ); - return el.firstChild.getAttribute( "value" ) === ""; -} ) ) { - addHandle( "value", function( elem, _name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - } ); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert( function( el ) { - return el.getAttribute( "disabled" ) == null; -} ) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - ( val = elem.getAttributeNode( name ) ) && val.specified ? - val.value : - null; - } - } ); -} - -return Sizzle; - -} )( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; - -// Deprecated -jQuery.expr[ ":" ] = jQuery.expr.pseudos; -jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; -jQuery.escapeSelector = Sizzle.escape; - - - - -var dir = function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; -}; - - -var siblings = function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; -}; - - -var rneedsContext = jQuery.expr.match.needsContext; - - - -function nodeName( elem, name ) { - - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - -}; -var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); - - - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) !== not; - } ); - } - - // Single element - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - } ); - } - - // Arraylike of elements (jQuery, arguments, Array) - if ( typeof qualifier !== "string" ) { - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) > -1 ) !== not; - } ); - } - - // Filtered directly for both simple and complex selectors - return jQuery.filter( qualifier, elements, not ); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - if ( elems.length === 1 && elem.nodeType === 1 ) { - return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; - } - - return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - } ) ); -}; - -jQuery.fn.extend( { - find: function( selector ) { - var i, ret, - len = this.length, - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter( function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - } ) ); - } - - ret = this.pushStack( [] ); - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - return len > 1 ? jQuery.uniqueSort( ret ) : ret; - }, - filter: function( selector ) { - return this.pushStack( winnow( this, selector || [], false ) ); - }, - not: function( selector ) { - return this.pushStack( winnow( this, selector || [], true ) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -} ); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - // Shortcut simple #id case for speed - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, - - init = jQuery.fn.init = function( selector, context, root ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Method init() accepts an alternate rootjQuery - // so migrate can support jQuery.sub (gh-2101) - root = root || rootjQuery; - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[ 0 ] === "<" && - selector[ selector.length - 1 ] === ">" && - selector.length >= 3 ) { - - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && ( match[ 1 ] || !context ) ) { - - // HANDLE: $(html) -> $(array) - if ( match[ 1 ] ) { - context = context instanceof jQuery ? context[ 0 ] : context; - - // Option to run scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[ 1 ], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - - // Properties of context are called as methods if possible - if ( isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[ 2 ] ); - - if ( elem ) { - - // Inject the element directly into the jQuery object - this[ 0 ] = elem; - this.length = 1; - } - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || root ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this[ 0 ] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( isFunction( selector ) ) { - return root.ready !== undefined ? - root.ready( selector ) : - - // Execute immediately if ready is not present - selector( jQuery ); - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - - // Methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend( { - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter( function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[ i ] ) ) { - return true; - } - } - } ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - targets = typeof selectors !== "string" && jQuery( selectors ); - - // Positional selectors never match, since there's no _selection_ context - if ( !rneedsContext.test( selectors ) ) { - for ( ; i < l; i++ ) { - for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { - - // Always skip document fragments - if ( cur.nodeType < 11 && ( targets ? - targets.index( cur ) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector( cur, selectors ) ) ) { - - matched.push( cur ); - break; - } - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); - }, - - // Determine the position of an element within the set - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // Index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.uniqueSort( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter( selector ) - ); - } -} ); - -function sibling( cur, dir ) { - while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each( { - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, _i, until ) { - return dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, _i, until ) { - return dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, _i, until ) { - return dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return siblings( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return siblings( elem.firstChild ); - }, - contents: function( elem ) { - if ( elem.contentDocument != null && - - // Support: IE 11+ - // elements with no `data` attribute has an object - // `contentDocument` with a `null` prototype. - getProto( elem.contentDocument ) ) { - - return elem.contentDocument; - } - - // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only - // Treat the template element as a regular one in browsers that - // don't support it. - if ( nodeName( elem, "template" ) ) { - elem = elem.content || elem; - } - - return jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.uniqueSort( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -} ); -var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); - - - -// Convert String-formatted options into Object-formatted ones -function createOptions( options ) { - var object = {}; - jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { - object[ flag ] = true; - } ); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - createOptions( options ) : - jQuery.extend( {}, options ); - - var // Flag to know if list is currently firing - firing, - - // Last fire value for non-forgettable lists - memory, - - // Flag to know if list was already fired - fired, - - // Flag to prevent firing - locked, - - // Actual callback list - list = [], - - // Queue of execution data for repeatable lists - queue = [], - - // Index of currently firing callback (modified by add/remove as needed) - firingIndex = -1, - - // Fire callbacks - fire = function() { - - // Enforce single-firing - locked = locked || options.once; - - // Execute callbacks for all pending executions, - // respecting firingIndex overrides and runtime changes - fired = firing = true; - for ( ; queue.length; firingIndex = -1 ) { - memory = queue.shift(); - while ( ++firingIndex < list.length ) { - - // Run callback and check for early termination - if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && - options.stopOnFalse ) { - - // Jump to end and forget the data so .add doesn't re-fire - firingIndex = list.length; - memory = false; - } - } - } - - // Forget the data if we're done with it - if ( !options.memory ) { - memory = false; - } - - firing = false; - - // Clean up if we're done firing for good - if ( locked ) { - - // Keep an empty list if we have data for future add calls - if ( memory ) { - list = []; - - // Otherwise, this object is spent - } else { - list = ""; - } - } - }, - - // Actual Callbacks object - self = { - - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - - // If we have memory from a past run, we should fire after adding - if ( memory && !firing ) { - firingIndex = list.length - 1; - queue.push( memory ); - } - - ( function add( args ) { - jQuery.each( args, function( _, arg ) { - if ( isFunction( arg ) ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && toType( arg ) !== "string" ) { - - // Inspect recursively - add( arg ); - } - } ); - } )( arguments ); - - if ( memory && !firing ) { - fire(); - } - } - return this; - }, - - // Remove a callback from the list - remove: function() { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - - // Handle firing indexes - if ( index <= firingIndex ) { - firingIndex--; - } - } - } ); - return this; - }, - - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? - jQuery.inArray( fn, list ) > -1 : - list.length > 0; - }, - - // Remove all callbacks from the list - empty: function() { - if ( list ) { - list = []; - } - return this; - }, - - // Disable .fire and .add - // Abort any current/pending executions - // Clear all callbacks and values - disable: function() { - locked = queue = []; - list = memory = ""; - return this; - }, - disabled: function() { - return !list; - }, - - // Disable .fire - // Also disable .add unless we have memory (since it would have no effect) - // Abort any pending executions - lock: function() { - locked = queue = []; - if ( !memory && !firing ) { - list = memory = ""; - } - return this; - }, - locked: function() { - return !!locked; - }, - - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( !locked ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - queue.push( args ); - if ( !firing ) { - fire(); - } - } - return this; - }, - - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -function Identity( v ) { - return v; -} -function Thrower( ex ) { - throw ex; -} - -function adoptValue( value, resolve, reject, noValue ) { - var method; - - try { - - // Check for promise aspect first to privilege synchronous behavior - if ( value && isFunction( ( method = value.promise ) ) ) { - method.call( value ).done( resolve ).fail( reject ); - - // Other thenables - } else if ( value && isFunction( ( method = value.then ) ) ) { - method.call( value, resolve, reject ); - - // Other non-thenables - } else { - - // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: - // * false: [ value ].slice( 0 ) => resolve( value ) - // * true: [ value ].slice( 1 ) => resolve() - resolve.apply( undefined, [ value ].slice( noValue ) ); - } - - // For Promises/A+, convert exceptions into rejections - // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in - // Deferred#then to conditionally suppress rejection. - } catch ( value ) { - - // Support: Android 4.0 only - // Strict mode functions invoked without .call/.apply get global-object context - reject.apply( undefined, [ value ] ); - } -} - -jQuery.extend( { - - Deferred: function( func ) { - var tuples = [ - - // action, add listener, callbacks, - // ... .then handlers, argument index, [final state] - [ "notify", "progress", jQuery.Callbacks( "memory" ), - jQuery.Callbacks( "memory" ), 2 ], - [ "resolve", "done", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 0, "resolved" ], - [ "reject", "fail", jQuery.Callbacks( "once memory" ), - jQuery.Callbacks( "once memory" ), 1, "rejected" ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - "catch": function( fn ) { - return promise.then( null, fn ); - }, - - // Keep pipe for back-compat - pipe: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - - return jQuery.Deferred( function( newDefer ) { - jQuery.each( tuples, function( _i, tuple ) { - - // Map tuples (progress, done, fail) to arguments (done, fail, progress) - var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; - - // deferred.progress(function() { bind to newDefer or newDefer.notify }) - // deferred.done(function() { bind to newDefer or newDefer.resolve }) - // deferred.fail(function() { bind to newDefer or newDefer.reject }) - deferred[ tuple[ 1 ] ]( function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && isFunction( returned.promise ) ) { - returned.promise() - .progress( newDefer.notify ) - .done( newDefer.resolve ) - .fail( newDefer.reject ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( - this, - fn ? [ returned ] : arguments - ); - } - } ); - } ); - fns = null; - } ).promise(); - }, - then: function( onFulfilled, onRejected, onProgress ) { - var maxDepth = 0; - function resolve( depth, deferred, handler, special ) { - return function() { - var that = this, - args = arguments, - mightThrow = function() { - var returned, then; - - // Support: Promises/A+ section 2.3.3.3.3 - // https://promisesaplus.com/#point-59 - // Ignore double-resolution attempts - if ( depth < maxDepth ) { - return; - } - - returned = handler.apply( that, args ); - - // Support: Promises/A+ section 2.3.1 - // https://promisesaplus.com/#point-48 - if ( returned === deferred.promise() ) { - throw new TypeError( "Thenable self-resolution" ); - } - - // Support: Promises/A+ sections 2.3.3.1, 3.5 - // https://promisesaplus.com/#point-54 - // https://promisesaplus.com/#point-75 - // Retrieve `then` only once - then = returned && - - // Support: Promises/A+ section 2.3.4 - // https://promisesaplus.com/#point-64 - // Only check objects and functions for thenability - ( typeof returned === "object" || - typeof returned === "function" ) && - returned.then; - - // Handle a returned thenable - if ( isFunction( then ) ) { - - // Special processors (notify) just wait for resolution - if ( special ) { - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ) - ); - - // Normal processors (resolve) also hook into progress - } else { - - // ...and disregard older resolution values - maxDepth++; - - then.call( - returned, - resolve( maxDepth, deferred, Identity, special ), - resolve( maxDepth, deferred, Thrower, special ), - resolve( maxDepth, deferred, Identity, - deferred.notifyWith ) - ); - } - - // Handle all other returned values - } else { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Identity ) { - that = undefined; - args = [ returned ]; - } - - // Process the value(s) - // Default process is resolve - ( special || deferred.resolveWith )( that, args ); - } - }, - - // Only normal processors (resolve) catch and reject exceptions - process = special ? - mightThrow : - function() { - try { - mightThrow(); - } catch ( e ) { - - if ( jQuery.Deferred.exceptionHook ) { - jQuery.Deferred.exceptionHook( e, - process.stackTrace ); - } - - // Support: Promises/A+ section 2.3.3.3.4.1 - // https://promisesaplus.com/#point-61 - // Ignore post-resolution exceptions - if ( depth + 1 >= maxDepth ) { - - // Only substitute handlers pass on context - // and multiple values (non-spec behavior) - if ( handler !== Thrower ) { - that = undefined; - args = [ e ]; - } - - deferred.rejectWith( that, args ); - } - } - }; - - // Support: Promises/A+ section 2.3.3.3.1 - // https://promisesaplus.com/#point-57 - // Re-resolve promises immediately to dodge false rejection from - // subsequent errors - if ( depth ) { - process(); - } else { - - // Call an optional hook to record the stack, in case of exception - // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getStackHook ) { - process.stackTrace = jQuery.Deferred.getStackHook(); - } - window.setTimeout( process ); - } - }; - } - - return jQuery.Deferred( function( newDefer ) { - - // progress_handlers.add( ... ) - tuples[ 0 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onProgress ) ? - onProgress : - Identity, - newDefer.notifyWith - ) - ); - - // fulfilled_handlers.add( ... ) - tuples[ 1 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onFulfilled ) ? - onFulfilled : - Identity - ) - ); - - // rejected_handlers.add( ... ) - tuples[ 2 ][ 3 ].add( - resolve( - 0, - newDefer, - isFunction( onRejected ) ? - onRejected : - Thrower - ) - ); - } ).promise(); - }, - - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 5 ]; - - // promise.progress = list.add - // promise.done = list.add - // promise.fail = list.add - promise[ tuple[ 1 ] ] = list.add; - - // Handle state - if ( stateString ) { - list.add( - function() { - - // state = "resolved" (i.e., fulfilled) - // state = "rejected" - state = stateString; - }, - - // rejected_callbacks.disable - // fulfilled_callbacks.disable - tuples[ 3 - i ][ 2 ].disable, - - // rejected_handlers.disable - // fulfilled_handlers.disable - tuples[ 3 - i ][ 3 ].disable, - - // progress_callbacks.lock - tuples[ 0 ][ 2 ].lock, - - // progress_handlers.lock - tuples[ 0 ][ 3 ].lock - ); - } - - // progress_handlers.fire - // fulfilled_handlers.fire - // rejected_handlers.fire - list.add( tuple[ 3 ].fire ); - - // deferred.notify = function() { deferred.notifyWith(...) } - // deferred.resolve = function() { deferred.resolveWith(...) } - // deferred.reject = function() { deferred.rejectWith(...) } - deferred[ tuple[ 0 ] ] = function() { - deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); - return this; - }; - - // deferred.notifyWith = list.fireWith - // deferred.resolveWith = list.fireWith - // deferred.rejectWith = list.fireWith - deferred[ tuple[ 0 ] + "With" ] = list.fireWith; - } ); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( singleValue ) { - var - - // count of uncompleted subordinates - remaining = arguments.length, - - // count of unprocessed arguments - i = remaining, - - // subordinate fulfillment data - resolveContexts = Array( i ), - resolveValues = slice.call( arguments ), - - // the master Deferred - master = jQuery.Deferred(), - - // subordinate callback factory - updateFunc = function( i ) { - return function( value ) { - resolveContexts[ i ] = this; - resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( !( --remaining ) ) { - master.resolveWith( resolveContexts, resolveValues ); - } - }; - }; - - // Single- and empty arguments are adopted like Promise.resolve - if ( remaining <= 1 ) { - adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, - !remaining ); - - // Use .then() to unwrap secondary thenables (cf. gh-3000) - if ( master.state() === "pending" || - isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { - - return master.then(); - } - } - - // Multiple arguments are aggregated like Promise.all array elements - while ( i-- ) { - adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); - } - - return master.promise(); - } -} ); - - -// These usually indicate a programmer mistake during development, -// warn about them ASAP rather than swallowing them by default. -var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; - -jQuery.Deferred.exceptionHook = function( error, stack ) { - - // Support: IE 8 - 9 only - // Console exists when dev tools are open, which can happen at any time - if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { - window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); - } -}; - - - - -jQuery.readyException = function( error ) { - window.setTimeout( function() { - throw error; - } ); -}; - - - - -// The deferred used on DOM ready -var readyList = jQuery.Deferred(); - -jQuery.fn.ready = function( fn ) { - - readyList - .then( fn ) - - // Wrap jQuery.readyException in a function so that the lookup - // happens at the time of error handling instead of callback - // registration. - .catch( function( error ) { - jQuery.readyException( error ); - } ); - - return this; -}; - -jQuery.extend( { - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - } -} ); - -jQuery.ready.then = readyList.then; - -// The ready event handler and self cleanup method -function completed() { - document.removeEventListener( "DOMContentLoaded", completed ); - window.removeEventListener( "load", completed ); - jQuery.ready(); -} - -// Catch cases where $(document).ready() is called -// after the browser event has already occurred. -// Support: IE <=9 - 10 only -// Older IE sometimes signals "interactive" too soon -if ( document.readyState === "complete" || - ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { - - // Handle it asynchronously to allow scripts the opportunity to delay ready - window.setTimeout( jQuery.ready ); - -} else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed ); -} - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( toType( key ) === "object" ) { - chainable = true; - for ( i in key ) { - access( elems, fn, i, key[ i ], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, _key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( - elems[ i ], key, raw ? - value : - value.call( elems[ i ], i, fn( elems[ i ], key ) ) - ); - } - } - } - - if ( chainable ) { - return elems; - } - - // Gets - if ( bulk ) { - return fn.call( elems ); - } - - return len ? fn( elems[ 0 ], key ) : emptyGet; -}; - - -// Matches dashed string for camelizing -var rmsPrefix = /^-ms-/, - rdashAlpha = /-([a-z])/g; - -// Used by camelCase as callback to replace() -function fcamelCase( _all, letter ) { - return letter.toUpperCase(); -} - -// Convert dashed to camelCase; used by the css and data modules -// Support: IE <=9 - 11, Edge 12 - 15 -// Microsoft forgot to hump their vendor prefix (#9572) -function camelCase( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); -} -var acceptData = function( owner ) { - - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - - - -function Data() { - this.expando = jQuery.expando + Data.uid++; -} - -Data.uid = 1; - -Data.prototype = { - - cache: function( owner ) { - - // Check if the owner object already has a cache - var value = owner[ this.expando ]; - - // If not, create one - if ( !value ) { - value = {}; - - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return an empty object. - if ( acceptData( owner ) ) { - - // If it is a node unlikely to be stringify-ed or looped over - // use plain assignment - if ( owner.nodeType ) { - owner[ this.expando ] = value; - - // Otherwise secure it in a non-enumerable property - // configurable must be true to allow the property to be - // deleted when data is removed - } else { - Object.defineProperty( owner, this.expando, { - value: value, - configurable: true - } ); - } - } - } - - return value; - }, - set: function( owner, data, value ) { - var prop, - cache = this.cache( owner ); - - // Handle: [ owner, key, value ] args - // Always use camelCase key (gh-2257) - if ( typeof data === "string" ) { - cache[ camelCase( data ) ] = value; - - // Handle: [ owner, { properties } ] args - } else { - - // Copy the properties one-by-one to the cache object - for ( prop in data ) { - cache[ camelCase( prop ) ] = data[ prop ]; - } - } - return cache; - }, - get: function( owner, key ) { - return key === undefined ? - this.cache( owner ) : - - // Always use camelCase key (gh-2257) - owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; - }, - access: function( owner, key, value ) { - - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ( ( key && typeof key === "string" ) && value === undefined ) ) { - - return this.get( owner, key ); - } - - // When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, - cache = owner[ this.expando ]; - - if ( cache === undefined ) { - return; - } - - if ( key !== undefined ) { - - // Support array or space separated string of keys - if ( Array.isArray( key ) ) { - - // If key is an array of keys... - // We always set camelCase keys, so remove that. - key = key.map( camelCase ); - } else { - key = camelCase( key ); - - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - key = key in cache ? - [ key ] : - ( key.match( rnothtmlwhite ) || [] ); - } - - i = key.length; - - while ( i-- ) { - delete cache[ key[ i ] ]; - } - } - - // Remove the expando if there's no more data - if ( key === undefined || jQuery.isEmptyObject( cache ) ) { - - // Support: Chrome <=35 - 45 - // Webkit & Blink performance suffers when deleting properties - // from DOM nodes, so set to undefined instead - // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) - if ( owner.nodeType ) { - owner[ this.expando ] = undefined; - } else { - delete owner[ this.expando ]; - } - } - }, - hasData: function( owner ) { - var cache = owner[ this.expando ]; - return cache !== undefined && !jQuery.isEmptyObject( cache ); - } -}; -var dataPriv = new Data(); - -var dataUser = new Data(); - - - -// Implementation Summary -// -// 1. Enforce API surface and semantic compatibility with 1.9.x branch -// 2. Improve the module's maintainability by reducing the storage -// paths to a single mechanism. -// 3. Use the same single mechanism to support "private" and "user" data. -// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) -// 5. Avoid exposing implementation details on user objects (eg. expando properties) -// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 - -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /[A-Z]/g; - -function getData( data ) { - if ( data === "true" ) { - return true; - } - - if ( data === "false" ) { - return false; - } - - if ( data === "null" ) { - return null; - } - - // Only convert to a number if it doesn't change the string - if ( data === +data + "" ) { - return +data; - } - - if ( rbrace.test( data ) ) { - return JSON.parse( data ); - } - - return data; -} - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = getData( data ); - } catch ( e ) {} - - // Make sure we set the data so it isn't changed later - dataUser.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend( { - hasData: function( elem ) { - return dataUser.hasData( elem ) || dataPriv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return dataUser.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - dataUser.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to dataPriv methods, these can be deprecated. - _data: function( elem, name, data ) { - return dataPriv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - dataPriv.remove( elem, name ); - } -} ); - -jQuery.fn.extend( { - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = dataUser.get( elem ); - - if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE 11 only - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = camelCase( name.slice( 5 ) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - dataPriv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each( function() { - dataUser.set( this, key ); - } ); - } - - return access( this, function( value ) { - var data; - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - - // Attempt to get data from the cache - // The key will always be camelCased in Data - data = dataUser.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, key ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each( function() { - - // We always store the camelCased key - dataUser.set( this, key, value ); - } ); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each( function() { - dataUser.remove( this, key ); - } ); - } -} ); - - -jQuery.extend( { - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = dataPriv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || Array.isArray( data ) ) { - queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // Clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // Not public - generate a queueHooks object, or return the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { - empty: jQuery.Callbacks( "once memory" ).add( function() { - dataPriv.remove( elem, [ type + "queue", key ] ); - } ) - } ); - } -} ); - -jQuery.fn.extend( { - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[ 0 ], type ); - } - - return data === undefined ? - this : - this.each( function() { - var queue = jQuery.queue( this, type, data ); - - // Ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - } ); - }, - dequeue: function( type ) { - return this.each( function() { - jQuery.dequeue( this, type ); - } ); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -} ); -var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; - -var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); - - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var documentElement = document.documentElement; - - - - var isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ); - }, - composed = { composed: true }; - - // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only - // Check attachment across shadow DOM boundaries when possible (gh-3504) - // Support: iOS 10.0-10.2 only - // Early iOS 10 versions support `attachShadow` but not `getRootNode`, - // leading to errors. We need to check for `getRootNode`. - if ( documentElement.getRootNode ) { - isAttached = function( elem ) { - return jQuery.contains( elem.ownerDocument, elem ) || - elem.getRootNode( composed ) === elem.ownerDocument; - }; - } -var isHiddenWithinTree = function( elem, el ) { - - // isHiddenWithinTree might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - - // Inline style trumps all - return elem.style.display === "none" || - elem.style.display === "" && - - // Otherwise, check computed style - // Support: Firefox <=43 - 45 - // Disconnected elements can have computed display: none, so first confirm that elem is - // in the document. - isAttached( elem ) && - - jQuery.css( elem, "display" ) === "none"; - }; - - - -function adjustCSS( elem, prop, valueParts, tween ) { - var adjusted, scale, - maxIterations = 20, - currentValue = tween ? - function() { - return tween.cur(); - } : - function() { - return jQuery.css( elem, prop, "" ); - }, - initial = currentValue(), - unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), - - // Starting value computation is required for potential unit mismatches - initialInUnit = elem.nodeType && - ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && - rcssNum.exec( jQuery.css( elem, prop ) ); - - if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { - - // Support: Firefox <=54 - // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) - initial = initial / 2; - - // Trust units reported by jQuery.css - unit = unit || initialInUnit[ 3 ]; - - // Iteratively approximate from a nonzero starting point - initialInUnit = +initial || 1; - - while ( maxIterations-- ) { - - // Evaluate and update our best guess (doubling guesses that zero out). - // Finish if the scale equals or crosses 1 (making the old*new product non-positive). - jQuery.style( elem, prop, initialInUnit + unit ); - if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { - maxIterations = 0; - } - initialInUnit = initialInUnit / scale; - - } - - initialInUnit = initialInUnit * 2; - jQuery.style( elem, prop, initialInUnit + unit ); - - // Make sure we update the tween properties later on - valueParts = valueParts || []; - } - - if ( valueParts ) { - initialInUnit = +initialInUnit || +initial || 0; - - // Apply relative offset (+=/-=) if specified - adjusted = valueParts[ 1 ] ? - initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : - +valueParts[ 2 ]; - if ( tween ) { - tween.unit = unit; - tween.start = initialInUnit; - tween.end = adjusted; - } - } - return adjusted; -} - - -var defaultDisplayMap = {}; - -function getDefaultDisplay( elem ) { - var temp, - doc = elem.ownerDocument, - nodeName = elem.nodeName, - display = defaultDisplayMap[ nodeName ]; - - if ( display ) { - return display; - } - - temp = doc.body.appendChild( doc.createElement( nodeName ) ); - display = jQuery.css( temp, "display" ); - - temp.parentNode.removeChild( temp ); - - if ( display === "none" ) { - display = "block"; - } - defaultDisplayMap[ nodeName ] = display; - - return display; -} - -function showHide( elements, show ) { - var display, elem, - values = [], - index = 0, - length = elements.length; - - // Determine new display value for elements that need to change - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - display = elem.style.display; - if ( show ) { - - // Since we force visibility upon cascade-hidden elements, an immediate (and slow) - // check is required in this first loop unless we have a nonempty display value (either - // inline or about-to-be-restored) - if ( display === "none" ) { - values[ index ] = dataPriv.get( elem, "display" ) || null; - if ( !values[ index ] ) { - elem.style.display = ""; - } - } - if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { - values[ index ] = getDefaultDisplay( elem ); - } - } else { - if ( display !== "none" ) { - values[ index ] = "none"; - - // Remember what we're overwriting - dataPriv.set( elem, "display", display ); - } - } - } - - // Set the display of the elements in a second loop to avoid constant reflow - for ( index = 0; index < length; index++ ) { - if ( values[ index ] != null ) { - elements[ index ].style.display = values[ index ]; - } - } - - return elements; -} - -jQuery.fn.extend( { - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - if ( typeof state === "boolean" ) { - return state ? this.show() : this.hide(); - } - - return this.each( function() { - if ( isHiddenWithinTree( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - } ); - } -} ); -var rcheckableType = ( /^(?:checkbox|radio)$/i ); - -var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); - -var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); - - - -( function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // Support: Android 4.0 - 4.3 only - // Check state lost if the name is set (#11217) - // Support: Windows Web Apps (WWA) - // `name` and `type` must use .setAttribute for WWA (#14901) - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Android <=4.1 only - // Older WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: IE <=11 only - // Make sure textarea (and checkbox) defaultValue is properly cloned - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; - - // Support: IE <=9 only - // IE <=9 replaces "; - support.option = !!div.lastChild; -} )(); - - -// We have to close these tags to support XHTML (#13200) -var wrapMap = { - - // XHTML parsers do not magically insert elements in the - // same way that tag soup parsers do. So we cannot shorten - // this by omitting or other required elements. - thead: [ 1, "", "
" ], - col: [ 2, "", "
" ], - tr: [ 2, "", "
" ], - td: [ 3, "", "
" ], - - _default: [ 0, "", "" ] -}; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// Support: IE <=9 only -if ( !support.option ) { - wrapMap.optgroup = wrapMap.option = [ 1, "" ]; -} - - -function getAll( context, tag ) { - - // Support: IE <=9 - 11 only - // Use typeof to avoid zero-argument method invocation on host objects (#15151) - var ret; - - if ( typeof context.getElementsByTagName !== "undefined" ) { - ret = context.getElementsByTagName( tag || "*" ); - - } else if ( typeof context.querySelectorAll !== "undefined" ) { - ret = context.querySelectorAll( tag || "*" ); - - } else { - ret = []; - } - - if ( tag === undefined || tag && nodeName( context, tag ) ) { - return jQuery.merge( [ context ], ret ); - } - - return ret; -} - - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - dataPriv.set( - elems[ i ], - "globalEval", - !refElements || dataPriv.get( refElements[ i ], "globalEval" ) - ); - } -} - - -var rhtml = /<|&#?\w+;/; - -function buildFragment( elems, context, scripts, selection, ignored ) { - var elem, tmp, tag, wrap, attached, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( toType( elem ) === "object" ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Ensure the created nodes are orphaned (#12392) - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( ( elem = nodes[ i++ ] ) ) { - - // Skip elements already in the context collection (trac-4087) - if ( selection && jQuery.inArray( elem, selection ) > -1 ) { - if ( ignored ) { - ignored.push( elem ); - } - continue; - } - - attached = isAttached( elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( attached ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( ( elem = tmp[ j++ ] ) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; -} - - -var - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -// Support: IE <=9 - 11+ -// focus() and blur() are asynchronous, except when they are no-op. -// So expect focus to be synchronous when the element is already active, -// and blur to be synchronous when the element is not already active. -// (focus and blur are always synchronous in other supported browsers, -// this just defines when we can count on it). -function expectSync( elem, type ) { - return ( elem === safeActiveElement() ) === ( type === "focus" ); -} - -// Support: IE <=9 only -// Accessing document.activeElement can throw unexpectedly -// https://bugs.jquery.com/ticket/13393 -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -function on( elem, types, selector, data, fn, one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - on( elem, type, selector, data, types[ type ], one ); - } - return elem; - } - - if ( data == null && fn == null ) { - - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return elem; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return elem.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - } ); -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.get( elem ); - - // Only attach events to objects that accept data - if ( !acceptData( elem ) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Ensure that invalid selectors throw exceptions at attach time - // Evaluate against documentElement in case elem is a non-element node (e.g., document) - if ( selector ) { - jQuery.find.matchesSelector( documentElement, selector ); - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !( events = elemData.events ) ) { - events = elemData.events = Object.create( null ); - } - if ( !( eventHandle = elemData.handle ) ) { - eventHandle = elemData.handle = function( e ) { - - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend( { - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join( "." ) - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !( handlers = events[ type ] ) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || - special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); - - if ( !elemData || !( events = elemData.events ) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[ t ] ) || []; - type = origType = tmp[ 1 ]; - namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[ 2 ] && - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || - selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || - special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove data and the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - dataPriv.remove( elem, "handle events" ); - } - }, - - dispatch: function( nativeEvent ) { - - var i, j, ret, matched, handleObj, handlerQueue, - args = new Array( arguments.length ), - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( nativeEvent ), - - handlers = ( - dataPriv.get( this, "events" ) || Object.create( null ) - )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[ 0 ] = event; - - for ( i = 1; i < arguments.length; i++ ) { - args[ i ] = arguments[ i ]; - } - - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( ( handleObj = matched.handlers[ j++ ] ) && - !event.isImmediatePropagationStopped() ) { - - // If the event is namespaced, then each handler is only invoked if it is - // specially universal or its namespaces are a superset of the event's. - if ( !event.rnamespace || handleObj.namespace === false || - event.rnamespace.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || - handleObj.handler ).apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( ( event.result = ret ) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, handleObj, sel, matchedHandlers, matchedSelectors, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - if ( delegateCount && - - // Support: IE <=9 - // Black-hole SVG instance trees (trac-13180) - cur.nodeType && - - // Support: Firefox <=42 - // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) - // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click - // Support: IE 11 only - // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) - !( event.type === "click" && event.button >= 1 ) ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't check non-elements (#13208) - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { - matchedHandlers = []; - matchedSelectors = {}; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matchedSelectors[ sel ] === undefined ) { - matchedSelectors[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) > -1 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matchedSelectors[ sel ] ) { - matchedHandlers.push( handleObj ); - } - } - if ( matchedHandlers.length ) { - handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); - } - } - } - } - - // Add the remaining (directly-bound) handlers - cur = this; - if ( delegateCount < handlers.length ) { - handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); - } - - return handlerQueue; - }, - - addProp: function( name, hook ) { - Object.defineProperty( jQuery.Event.prototype, name, { - enumerable: true, - configurable: true, - - get: isFunction( hook ) ? - function() { - if ( this.originalEvent ) { - return hook( this.originalEvent ); - } - } : - function() { - if ( this.originalEvent ) { - return this.originalEvent[ name ]; - } - }, - - set: function( value ) { - Object.defineProperty( this, name, { - enumerable: true, - configurable: true, - writable: true, - value: value - } ); - } - } ); - }, - - fix: function( originalEvent ) { - return originalEvent[ jQuery.expando ] ? - originalEvent : - new jQuery.Event( originalEvent ); - }, - - special: { - load: { - - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - click: { - - // Utilize native event to ensure correct state for checkable inputs - setup: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Claim the first handler - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", returnTrue ); - } - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function( data ) { - - // For mutual compressibility with _default, replace `this` access with a local var. - // `|| data` is dead code meant only to preserve the variable through minification. - var el = this || data; - - // Force setup before triggering a click - if ( rcheckableType.test( el.type ) && - el.click && nodeName( el, "input" ) ) { - - leverageNative( el, "click" ); - } - - // Return non-false to allow normal event-path propagation - return true; - }, - - // For cross-browser consistency, suppress native .click() on links - // Also prevent it if we're currently inside a leveraged native-event stack - _default: function( event ) { - var target = event.target; - return rcheckableType.test( target.type ) && - target.click && nodeName( target, "input" ) && - dataPriv.get( target, "click" ) || - nodeName( target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - } -}; - -// Ensure the presence of an event listener that handles manually-triggered -// synthetic events by interrupting progress until reinvoked in response to -// *native* events that it fires directly, ensuring that state changes have -// already occurred before other listeners are invoked. -function leverageNative( el, type, expectSync ) { - - // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add - if ( !expectSync ) { - if ( dataPriv.get( el, type ) === undefined ) { - jQuery.event.add( el, type, returnTrue ); - } - return; - } - - // Register the controller as a special universal handler for all event namespaces - dataPriv.set( el, type, false ); - jQuery.event.add( el, type, { - namespace: false, - handler: function( event ) { - var notAsync, result, - saved = dataPriv.get( this, type ); - - if ( ( event.isTrigger & 1 ) && this[ type ] ) { - - // Interrupt processing of the outer synthetic .trigger()ed event - // Saved data should be false in such cases, but might be a leftover capture object - // from an async native handler (gh-4350) - if ( !saved.length ) { - - // Store arguments for use when handling the inner native event - // There will always be at least one argument (an event object), so this array - // will not be confused with a leftover capture object. - saved = slice.call( arguments ); - dataPriv.set( this, type, saved ); - - // Trigger the native event and capture its result - // Support: IE <=9 - 11+ - // focus() and blur() are asynchronous - notAsync = expectSync( this, type ); - this[ type ](); - result = dataPriv.get( this, type ); - if ( saved !== result || notAsync ) { - dataPriv.set( this, type, false ); - } else { - result = {}; - } - if ( saved !== result ) { - - // Cancel the outer synthetic event - event.stopImmediatePropagation(); - event.preventDefault(); - return result.value; - } - - // If this is an inner synthetic event for an event with a bubbling surrogate - // (focus or blur), assume that the surrogate already propagated from triggering the - // native event and prevent that from happening again here. - // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the - // bubbling surrogate propagates *after* the non-bubbling base), but that seems - // less bad than duplication. - } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { - event.stopPropagation(); - } - - // If this is a native event triggered above, everything is now in order - // Fire an inner synthetic event with the original arguments - } else if ( saved.length ) { - - // ...and capture the result - dataPriv.set( this, type, { - value: jQuery.event.trigger( - - // Support: IE <=9 - 11+ - // Extend with the prototype to reset the above stopImmediatePropagation() - jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), - saved.slice( 1 ), - this - ) - } ); - - // Abort handling of the native event - event.stopImmediatePropagation(); - } - } - } ); -} - -jQuery.removeEvent = function( elem, type, handle ) { - - // This "if" is needed for plain objects - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle ); - } -}; - -jQuery.Event = function( src, props ) { - - // Allow instantiation without the 'new' keyword - if ( !( this instanceof jQuery.Event ) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - - // Support: Android <=2.3 only - src.returnValue === false ? - returnTrue : - returnFalse; - - // Create target properties - // Support: Safari <=6 - 7 only - // Target should not be a text node (#504, #13143) - this.target = ( src.target && src.target.nodeType === 3 ) ? - src.target.parentNode : - src.target; - - this.currentTarget = src.currentTarget; - this.relatedTarget = src.relatedTarget; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || Date.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - constructor: jQuery.Event, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - isSimulated: false, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && !this.isSimulated ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && !this.isSimulated ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Includes all common event props including KeyEvent and MouseEvent specific props -jQuery.each( { - altKey: true, - bubbles: true, - cancelable: true, - changedTouches: true, - ctrlKey: true, - detail: true, - eventPhase: true, - metaKey: true, - pageX: true, - pageY: true, - shiftKey: true, - view: true, - "char": true, - code: true, - charCode: true, - key: true, - keyCode: true, - button: true, - buttons: true, - clientX: true, - clientY: true, - offsetX: true, - offsetY: true, - pointerId: true, - pointerType: true, - screenX: true, - screenY: true, - targetTouches: true, - toElement: true, - touches: true, - - which: function( event ) { - var button = event.button; - - // Add which for key events - if ( event.which == null && rkeyEvent.test( event.type ) ) { - return event.charCode != null ? event.charCode : event.keyCode; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { - if ( button & 1 ) { - return 1; - } - - if ( button & 2 ) { - return 3; - } - - if ( button & 4 ) { - return 2; - } - - return 0; - } - - return event.which; - } -}, jQuery.event.addProp ); - -jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { - jQuery.event.special[ type ] = { - - // Utilize native event if possible so blur/focus sequence is correct - setup: function() { - - // Claim the first handler - // dataPriv.set( this, "focus", ... ) - // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, expectSync ); - - // Return false to allow normal processing in the caller - return false; - }, - trigger: function() { - - // Force setup before trigger - leverageNative( this, type ); - - // Return non-false to allow normal event-path propagation - return true; - }, - - delegateType: delegateType - }; -} ); - -// Create mouseenter/leave events using mouseover/out and event-time checks -// so that event delegation works in jQuery. -// Do the same for pointerenter/pointerleave and pointerover/pointerout -// -// Support: Safari 7 only -// Safari sends mouseenter too often; see: -// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 -// for the description of the bug (it existed in older Chrome versions as well). -jQuery.each( { - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mouseenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -} ); - -jQuery.fn.extend( { - - on: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn ); - }, - one: function( types, selector, data, fn ) { - return on( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? - handleObj.origType + "." + handleObj.namespace : - handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each( function() { - jQuery.event.remove( this, types, fn, selector ); - } ); - } -} ); - - -var - - // Support: IE <=10 - 11, Edge 12 - 13 only - // In IE/Edge using regex groups here causes severe slowdowns. - // See https://connect.microsoft.com/IE/feedback/details/1736512/ - rnoInnerhtml = /\s*$/g; - -// Prefer a tbody over its parent table for containing new rows -function manipulationTarget( elem, content ) { - if ( nodeName( elem, "table" ) && - nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { - - return jQuery( elem ).children( "tbody" )[ 0 ] || elem; - } - - return elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { - elem.type = elem.type.slice( 5 ); - } else { - elem.removeAttribute( "type" ); - } - - return elem; -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( dataPriv.hasData( src ) ) { - pdataOld = dataPriv.get( src ); - events = pdataOld.events; - - if ( events ) { - dataPriv.remove( dest, "handle events" ); - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( dataUser.hasData( src ) ) { - udataOld = dataUser.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - dataUser.set( dest, udataCur ); - } -} - -// Fix IE bugs, see support tests -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -function domManip( collection, args, callback, ignored ) { - - // Flatten any nested arrays - args = flat( args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = collection.length, - iNoClone = l - 1, - value = args[ 0 ], - valueIsFunction = isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( valueIsFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return collection.each( function( index ) { - var self = collection.eq( index ); - if ( valueIsFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - domManip( self, args, callback, ignored ); - } ); - } - - if ( l ) { - fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - // Require either new content or an interest in ignored elements to invoke the callback - if ( first || ignored ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item - // instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - - // Support: Android <=4.0 only, PhantomJS 1 only - // push.apply(_, arraylike) throws on ancient WebKit - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( collection[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !dataPriv.access( node, "globalEval" ) && - jQuery.contains( doc, node ) ) { - - if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { - - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl && !node.noModule ) { - jQuery._evalUrl( node.src, { - nonce: node.nonce || node.getAttribute( "nonce" ) - }, doc ); - } - } else { - DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); - } - } - } - } - } - } - - return collection; -} - -function remove( elem, selector, keepData ) { - var node, - nodes = selector ? jQuery.filter( selector, elem ) : elem, - i = 0; - - for ( ; ( node = nodes[ i ] ) != null; i++ ) { - if ( !keepData && node.nodeType === 1 ) { - jQuery.cleanData( getAll( node ) ); - } - - if ( node.parentNode ) { - if ( keepData && isAttached( node ) ) { - setGlobalEval( getAll( node, "script" ) ); - } - node.parentNode.removeChild( node ); - } - } - - return elem; -} - -jQuery.extend( { - htmlPrefilter: function( html ) { - return html; - }, - - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = isAttached( elem ); - - // Fix IE cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - cleanData: function( elems ) { - var data, elem, type, - special = jQuery.event.special, - i = 0; - - for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { - if ( acceptData( elem ) ) { - if ( ( data = elem[ dataPriv.expando ] ) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataPriv.expando ] = undefined; - } - if ( elem[ dataUser.expando ] ) { - - // Support: Chrome <=35 - 45+ - // Assign undefined instead of using delete, see Data#remove - elem[ dataUser.expando ] = undefined; - } - } - } - } -} ); - -jQuery.fn.extend( { - detach: function( selector ) { - return remove( this, selector, true ); - }, - - remove: function( selector ) { - return remove( this, selector ); - }, - - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each( function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - } ); - }, null, value, arguments.length ); - }, - - append: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - } ); - }, - - prepend: function() { - return domManip( this, arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - } ); - }, - - before: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - } ); - }, - - after: function() { - return domManip( this, arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - } ); - }, - - empty: function() { - var elem, - i = 0; - - for ( ; ( elem = this[ i ] ) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - } ); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = jQuery.htmlPrefilter( value ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch ( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var ignored = []; - - // Make the changes, replacing each non-ignored context element with the new content - return domManip( this, arguments, function( elem ) { - var parent = this.parentNode; - - if ( jQuery.inArray( this, ignored ) < 0 ) { - jQuery.cleanData( getAll( this ) ); - if ( parent ) { - parent.replaceChild( elem, this ); - } - } - - // Force callback invocation - }, ignored ); - } -} ); - -jQuery.each( { - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: Android <=4.0 only, PhantomJS 1 only - // .get() because push.apply(_, arraylike) throws on ancient WebKit - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -} ); -var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); - -var getStyles = function( elem ) { - - // Support: IE <=11 only, Firefox <=30 (#15098, #14150) - // IE throws on elements created in popups - // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" - var view = elem.ownerDocument.defaultView; - - if ( !view || !view.opener ) { - view = window; - } - - return view.getComputedStyle( elem ); - }; - -var swap = function( elem, options, callback ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.call( elem ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; -}; - - -var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); - - - -( function() { - - // Executing both pixelPosition & boxSizingReliable tests require only one layout - // so they're executed at the same time to save the second computation. - function computeStyleTests() { - - // This is a singleton, we need to execute it only once - if ( !div ) { - return; - } - - container.style.cssText = "position:absolute;left:-11111px;width:60px;" + - "margin-top:1px;padding:0;border:0"; - div.style.cssText = - "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + - "margin:auto;border:1px;padding:1px;" + - "width:60%;top:1%"; - documentElement.appendChild( container ).appendChild( div ); - - var divStyle = window.getComputedStyle( div ); - pixelPositionVal = divStyle.top !== "1%"; - - // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 - reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; - - // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 - // Some styles come back with percentage values, even though they shouldn't - div.style.right = "60%"; - pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; - - // Support: IE 9 - 11 only - // Detect misreporting of content dimensions for box-sizing:border-box elements - boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; - - // Support: IE 9 only - // Detect overflow:scroll screwiness (gh-3699) - // Support: Chrome <=64 - // Don't get tricked when zoom affects offsetWidth (gh-4029) - div.style.position = "absolute"; - scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; - - documentElement.removeChild( container ); - - // Nullify the div so it wouldn't be stored in the memory and - // it will also be a sign that checks already performed - div = null; - } - - function roundPixelMeasures( measure ) { - return Math.round( parseFloat( measure ) ); - } - - var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, - reliableTrDimensionsVal, reliableMarginLeftVal, - container = document.createElement( "div" ), - div = document.createElement( "div" ); - - // Finish early in limited (non-browser) environments - if ( !div.style ) { - return; - } - - // Support: IE <=9 - 11 only - // Style of cloned element affects source element cloned (#8908) - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - jQuery.extend( support, { - boxSizingReliable: function() { - computeStyleTests(); - return boxSizingReliableVal; - }, - pixelBoxStyles: function() { - computeStyleTests(); - return pixelBoxStylesVal; - }, - pixelPosition: function() { - computeStyleTests(); - return pixelPositionVal; - }, - reliableMarginLeft: function() { - computeStyleTests(); - return reliableMarginLeftVal; - }, - scrollboxSize: function() { - computeStyleTests(); - return scrollboxSizeVal; - }, - - // Support: IE 9 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Behavior in IE 9 is more subtle than in newer versions & it passes - // some versions of this test; make sure not to make it pass there! - reliableTrDimensions: function() { - var table, tr, trChild, trStyle; - if ( reliableTrDimensionsVal == null ) { - table = document.createElement( "table" ); - tr = document.createElement( "tr" ); - trChild = document.createElement( "div" ); - - table.style.cssText = "position:absolute;left:-11111px"; - tr.style.height = "1px"; - trChild.style.height = "9px"; - - documentElement - .appendChild( table ) - .appendChild( tr ) - .appendChild( trChild ); - - trStyle = window.getComputedStyle( tr ); - reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; - - documentElement.removeChild( table ); - } - return reliableTrDimensionsVal; - } - } ); -} )(); - - -function curCSS( elem, name, computed ) { - var width, minWidth, maxWidth, ret, - - // Support: Firefox 51+ - // Retrieving style before computed somehow - // fixes an issue with getting wrong values - // on detached elements - style = elem.style; - - computed = computed || getStyles( elem ); - - // getPropertyValue is needed for: - // .css('filter') (IE 9 only, #12537) - // .css('--customProperty) (#3144) - if ( computed ) { - ret = computed.getPropertyValue( name ) || computed[ name ]; - - if ( ret === "" && !isAttached( elem ) ) { - ret = jQuery.style( elem, name ); - } - - // A tribute to the "awesome hack by Dean Edwards" - // Android Browser returns percentage for some values, - // but width seems to be reliably pixels. - // This is against the CSSOM draft spec: - // https://drafts.csswg.org/cssom/#resolved-values - if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret !== undefined ? - - // Support: IE <=9 - 11 only - // IE returns zIndex value as an integer. - ret + "" : - ret; -} - - -function addGetHookIf( conditionFn, hookFn ) { - - // Define the hook, we'll check on the first run if it's really needed. - return { - get: function() { - if ( conditionFn() ) { - - // Hook not needed (or it's not possible to use it due - // to missing dependency), remove it. - delete this.get; - return; - } - - // Hook needed; redefine it so that the support test is not executed again. - return ( this.get = hookFn ).apply( this, arguments ); - } - }; -} - - -var cssPrefixes = [ "Webkit", "Moz", "ms" ], - emptyStyle = document.createElement( "div" ).style, - vendorProps = {}; - -// Return a vendor-prefixed property or undefined -function vendorPropName( name ) { - - // Check for vendor prefixed names - var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in emptyStyle ) { - return name; - } - } -} - -// Return a potentially-mapped jQuery.cssProps or vendor prefixed property -function finalPropName( name ) { - var final = jQuery.cssProps[ name ] || vendorProps[ name ]; - - if ( final ) { - return final; - } - if ( name in emptyStyle ) { - return name; - } - return vendorProps[ name ] = vendorPropName( name ) || name; -} - - -var - - // Swappable if display is none or starts with table - // except "table", "table-cell", or "table-caption" - // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rcustomProp = /^--/, - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: "0", - fontWeight: "400" - }; - -function setPositiveNumber( _elem, value, subtract ) { - - // Any relative (+/-) values have already been - // normalized at this point - var matches = rcssNum.exec( value ); - return matches ? - - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : - value; -} - -function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { - var i = dimension === "width" ? 1 : 0, - extra = 0, - delta = 0; - - // Adjustment may not be necessary - if ( box === ( isBorderBox ? "border" : "content" ) ) { - return 0; - } - - for ( ; i < 4; i += 2 ) { - - // Both box models exclude margin - if ( box === "margin" ) { - delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); - } - - // If we get here with a content-box, we're seeking "padding" or "border" or "margin" - if ( !isBorderBox ) { - - // Add padding - delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // For "border" or "margin", add border - if ( box !== "padding" ) { - delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - - // But still keep track of it otherwise - } else { - extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - - // If we get here with a border-box (content + padding + border), we're seeking "content" or - // "padding" or "margin" - } else { - - // For "content", subtract padding - if ( box === "content" ) { - delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // For "content" or "padding", subtract border - if ( box !== "margin" ) { - delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - // Account for positive content-box scroll gutter when requested by providing computedVal - if ( !isBorderBox && computedVal >= 0 ) { - - // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border - // Assuming integer scroll gutter, subtract the rest and round down - delta += Math.max( 0, Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - computedVal - - delta - - extra - - 0.5 - - // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter - // Use an explicit zero to avoid NaN (gh-3964) - ) ) || 0; - } - - return delta; -} - -function getWidthOrHeight( elem, dimension, extra ) { - - // Start with computed style - var styles = getStyles( elem ), - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). - // Fake content-box until we know it's needed to know the true value. - boxSizingNeeded = !support.boxSizingReliable() || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - valueIsBorderBox = isBorderBox, - - val = curCSS( elem, dimension, styles ), - offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); - - // Support: Firefox <=54 - // Return a confounding non-pixel value or feign ignorance, as appropriate. - if ( rnumnonpx.test( val ) ) { - if ( !extra ) { - return val; - } - val = "auto"; - } - - - // Support: IE 9 - 11 only - // Use offsetWidth/offsetHeight for when box sizing is unreliable. - // In those cases, the computed value can be trusted to be border-box. - if ( ( !support.boxSizingReliable() && isBorderBox || - - // Support: IE 10 - 11+, Edge 15 - 18+ - // IE/Edge misreport `getComputedStyle` of table rows with width/height - // set in CSS while `offset*` properties report correct values. - // Interestingly, in some cases IE 9 doesn't suffer from this issue. - !support.reliableTrDimensions() && nodeName( elem, "tr" ) || - - // Fall back to offsetWidth/offsetHeight when value is "auto" - // This happens for inline elements with no explicit setting (gh-3571) - val === "auto" || - - // Support: Android <=4.1 - 4.3 only - // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) - !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && - - // Make sure the element is visible & connected - elem.getClientRects().length ) { - - isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // Where available, offsetWidth/offsetHeight approximate border box dimensions. - // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the - // retrieved value as a content box dimension. - valueIsBorderBox = offsetProp in elem; - if ( valueIsBorderBox ) { - val = elem[ offsetProp ]; - } - } - - // Normalize "" and auto - val = parseFloat( val ) || 0; - - // Adjust for the element's box model - return ( val + - boxModelAdjustment( - elem, - dimension, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles, - - // Provide the current computed size to request scroll gutter calculation (gh-3589) - val - ) - ) + "px"; -} - -jQuery.extend( { - - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Don't automatically add "px" to these possibly-unitless properties - cssNumber: { - "animationIterationCount": true, - "columnCount": true, - "fillOpacity": true, - "flexGrow": true, - "flexShrink": true, - "fontWeight": true, - "gridArea": true, - "gridColumn": true, - "gridColumnEnd": true, - "gridColumnStart": true, - "gridRow": true, - "gridRowEnd": true, - "gridRowStart": true, - "lineHeight": true, - "opacity": true, - "order": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: {}, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ), - style = elem.style; - - // Make sure that we're working with the right name. We don't - // want to query the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Gets hook for the prefixed version, then unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // Convert "+=" or "-=" to relative numbers (#7345) - if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { - value = adjustCSS( elem, name, ret ); - - // Fixes bug #9237 - type = "number"; - } - - // Make sure that null and NaN values aren't set (#7116) - if ( value == null || value !== value ) { - return; - } - - // If a number was passed in, add the unit (except for certain CSS properties) - // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append - // "px" to a few hardcoded values. - if ( type === "number" && !isCustomProp ) { - value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); - } - - // background-* props affect original clone's values - if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !( "set" in hooks ) || - ( value = hooks.set( elem, value, extra ) ) !== undefined ) { - - if ( isCustomProp ) { - style.setProperty( name, value ); - } else { - style[ name ] = value; - } - } - - } else { - - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && - ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { - - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = camelCase( name ), - isCustomProp = rcustomProp.test( name ); - - // Make sure that we're working with the right name. We don't - // want to modify the value if it is a CSS custom property - // since they are user-defined. - if ( !isCustomProp ) { - name = finalPropName( origName ); - } - - // Try prefixed name followed by the unprefixed name - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - // Convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Make numeric if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || isFinite( num ) ? num || 0 : val; - } - - return val; - } -} ); - -jQuery.each( [ "height", "width" ], function( _i, dimension ) { - jQuery.cssHooks[ dimension ] = { - get: function( elem, computed, extra ) { - if ( computed ) { - - // Certain elements can have dimension info if we invisibly show them - // but it must have a current display style that would benefit - return rdisplayswap.test( jQuery.css( elem, "display" ) ) && - - // Support: Safari 8+ - // Table columns in Safari have non-zero offsetWidth & zero - // getBoundingClientRect().width unless display is changed. - // Support: IE <=11 only - // Running getBoundingClientRect on a disconnected node - // in IE throws an error. - ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? - swap( elem, cssShow, function() { - return getWidthOrHeight( elem, dimension, extra ); - } ) : - getWidthOrHeight( elem, dimension, extra ); - } - }, - - set: function( elem, value, extra ) { - var matches, - styles = getStyles( elem ), - - // Only read styles.position if the test has a chance to fail - // to avoid forcing a reflow. - scrollboxSizeBuggy = !support.scrollboxSize() && - styles.position === "absolute", - - // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) - boxSizingNeeded = scrollboxSizeBuggy || extra, - isBorderBox = boxSizingNeeded && - jQuery.css( elem, "boxSizing", false, styles ) === "border-box", - subtract = extra ? - boxModelAdjustment( - elem, - dimension, - extra, - isBorderBox, - styles - ) : - 0; - - // Account for unreliable border-box dimensions by comparing offset* to computed and - // faking a content-box to get border and padding (gh-3699) - if ( isBorderBox && scrollboxSizeBuggy ) { - subtract -= Math.ceil( - elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - - parseFloat( styles[ dimension ] ) - - boxModelAdjustment( elem, dimension, "border", false, styles ) - - 0.5 - ); - } - - // Convert to pixels if value adjustment is needed - if ( subtract && ( matches = rcssNum.exec( value ) ) && - ( matches[ 3 ] || "px" ) !== "px" ) { - - elem.style[ dimension ] = value; - value = jQuery.css( elem, dimension ); - } - - return setPositiveNumber( elem, value, subtract ); - } - }; -} ); - -jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, - function( elem, computed ) { - if ( computed ) { - return ( parseFloat( curCSS( elem, "marginLeft" ) ) || - elem.getBoundingClientRect().left - - swap( elem, { marginLeft: 0 }, function() { - return elem.getBoundingClientRect().left; - } ) - ) + "px"; - } - } -); - -// These hooks are used by animate to expand properties -jQuery.each( { - margin: "", - padding: "", - border: "Width" -}, function( prefix, suffix ) { - jQuery.cssHooks[ prefix + suffix ] = { - expand: function( value ) { - var i = 0, - expanded = {}, - - // Assumes a single number if not a string - parts = typeof value === "string" ? value.split( " " ) : [ value ]; - - for ( ; i < 4; i++ ) { - expanded[ prefix + cssExpand[ i ] + suffix ] = - parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; - } - - return expanded; - } - }; - - if ( prefix !== "margin" ) { - jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; - } -} ); - -jQuery.fn.extend( { - css: function( name, value ) { - return access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( Array.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - } -} ); - - -function Tween( elem, options, prop, end, easing ) { - return new Tween.prototype.init( elem, options, prop, end, easing ); -} -jQuery.Tween = Tween; - -Tween.prototype = { - constructor: Tween, - init: function( elem, options, prop, end, easing, unit ) { - this.elem = elem; - this.prop = prop; - this.easing = easing || jQuery.easing._default; - this.options = options; - this.start = this.now = this.cur(); - this.end = end; - this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); - }, - cur: function() { - var hooks = Tween.propHooks[ this.prop ]; - - return hooks && hooks.get ? - hooks.get( this ) : - Tween.propHooks._default.get( this ); - }, - run: function( percent ) { - var eased, - hooks = Tween.propHooks[ this.prop ]; - - if ( this.options.duration ) { - this.pos = eased = jQuery.easing[ this.easing ]( - percent, this.options.duration * percent, 0, 1, this.options.duration - ); - } else { - this.pos = eased = percent; - } - this.now = ( this.end - this.start ) * eased + this.start; - - if ( this.options.step ) { - this.options.step.call( this.elem, this.now, this ); - } - - if ( hooks && hooks.set ) { - hooks.set( this ); - } else { - Tween.propHooks._default.set( this ); - } - return this; - } -}; - -Tween.prototype.init.prototype = Tween.prototype; - -Tween.propHooks = { - _default: { - get: function( tween ) { - var result; - - // Use a property on the element directly when it is not a DOM element, - // or when there is no matching style property that exists. - if ( tween.elem.nodeType !== 1 || - tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { - return tween.elem[ tween.prop ]; - } - - // Passing an empty string as a 3rd parameter to .css will automatically - // attempt a parseFloat and fallback to a string if the parse fails. - // Simple values such as "10px" are parsed to Float; - // complex values such as "rotate(1rad)" are returned as-is. - result = jQuery.css( tween.elem, tween.prop, "" ); - - // Empty strings, null, undefined and "auto" are converted to 0. - return !result || result === "auto" ? 0 : result; - }, - set: function( tween ) { - - // Use step hook for back compat. - // Use cssHook if its there. - // Use .style if available and use plain properties where available. - if ( jQuery.fx.step[ tween.prop ] ) { - jQuery.fx.step[ tween.prop ]( tween ); - } else if ( tween.elem.nodeType === 1 && ( - jQuery.cssHooks[ tween.prop ] || - tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { - jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); - } else { - tween.elem[ tween.prop ] = tween.now; - } - } - } -}; - -// Support: IE <=9 only -// Panic based approach to setting things on disconnected nodes -Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { - set: function( tween ) { - if ( tween.elem.nodeType && tween.elem.parentNode ) { - tween.elem[ tween.prop ] = tween.now; - } - } -}; - -jQuery.easing = { - linear: function( p ) { - return p; - }, - swing: function( p ) { - return 0.5 - Math.cos( p * Math.PI ) / 2; - }, - _default: "swing" -}; - -jQuery.fx = Tween.prototype.init; - -// Back compat <1.8 extension point -jQuery.fx.step = {}; - - - - -var - fxNow, inProgress, - rfxtypes = /^(?:toggle|show|hide)$/, - rrun = /queueHooks$/; - -function schedule() { - if ( inProgress ) { - if ( document.hidden === false && window.requestAnimationFrame ) { - window.requestAnimationFrame( schedule ); - } else { - window.setTimeout( schedule, jQuery.fx.interval ); - } - - jQuery.fx.tick(); - } -} - -// Animations created synchronously will run synchronously -function createFxNow() { - window.setTimeout( function() { - fxNow = undefined; - } ); - return ( fxNow = Date.now() ); -} - -// Generate parameters to create a standard animation -function genFx( type, includeWidth ) { - var which, - i = 0, - attrs = { height: type }; - - // If we include width, step value is 1 to do all cssExpand values, - // otherwise step value is 2 to skip over Left and Right - includeWidth = includeWidth ? 1 : 0; - for ( ; i < 4; i += 2 - includeWidth ) { - which = cssExpand[ i ]; - attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; - } - - if ( includeWidth ) { - attrs.opacity = attrs.width = type; - } - - return attrs; -} - -function createTween( value, prop, animation ) { - var tween, - collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), - index = 0, - length = collection.length; - for ( ; index < length; index++ ) { - if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { - - // We're done with this property - return tween; - } - } -} - -function defaultPrefilter( elem, props, opts ) { - var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, - isBox = "width" in props || "height" in props, - anim = this, - orig = {}, - style = elem.style, - hidden = elem.nodeType && isHiddenWithinTree( elem ), - dataShow = dataPriv.get( elem, "fxshow" ); - - // Queue-skipping animations hijack the fx hooks - if ( !opts.queue ) { - hooks = jQuery._queueHooks( elem, "fx" ); - if ( hooks.unqueued == null ) { - hooks.unqueued = 0; - oldfire = hooks.empty.fire; - hooks.empty.fire = function() { - if ( !hooks.unqueued ) { - oldfire(); - } - }; - } - hooks.unqueued++; - - anim.always( function() { - - // Ensure the complete handler is called before this completes - anim.always( function() { - hooks.unqueued--; - if ( !jQuery.queue( elem, "fx" ).length ) { - hooks.empty.fire(); - } - } ); - } ); - } - - // Detect show/hide animations - for ( prop in props ) { - value = props[ prop ]; - if ( rfxtypes.test( value ) ) { - delete props[ prop ]; - toggle = toggle || value === "toggle"; - if ( value === ( hidden ? "hide" : "show" ) ) { - - // Pretend to be hidden if this is a "show" and - // there is still data from a stopped show/hide - if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { - hidden = true; - - // Ignore all other no-op show/hide data - } else { - continue; - } - } - orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); - } - } - - // Bail out if this is a no-op like .hide().hide() - propTween = !jQuery.isEmptyObject( props ); - if ( !propTween && jQuery.isEmptyObject( orig ) ) { - return; - } - - // Restrict "overflow" and "display" styles during box animations - if ( isBox && elem.nodeType === 1 ) { - - // Support: IE <=9 - 11, Edge 12 - 15 - // Record all 3 overflow attributes because IE does not infer the shorthand - // from identically-valued overflowX and overflowY and Edge just mirrors - // the overflowX value there. - opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; - - // Identify a display type, preferring old show/hide data over the CSS cascade - restoreDisplay = dataShow && dataShow.display; - if ( restoreDisplay == null ) { - restoreDisplay = dataPriv.get( elem, "display" ); - } - display = jQuery.css( elem, "display" ); - if ( display === "none" ) { - if ( restoreDisplay ) { - display = restoreDisplay; - } else { - - // Get nonempty value(s) by temporarily forcing visibility - showHide( [ elem ], true ); - restoreDisplay = elem.style.display || restoreDisplay; - display = jQuery.css( elem, "display" ); - showHide( [ elem ] ); - } - } - - // Animate inline elements as inline-block - if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { - if ( jQuery.css( elem, "float" ) === "none" ) { - - // Restore the original display value at the end of pure show/hide animations - if ( !propTween ) { - anim.done( function() { - style.display = restoreDisplay; - } ); - if ( restoreDisplay == null ) { - display = style.display; - restoreDisplay = display === "none" ? "" : display; - } - } - style.display = "inline-block"; - } - } - } - - if ( opts.overflow ) { - style.overflow = "hidden"; - anim.always( function() { - style.overflow = opts.overflow[ 0 ]; - style.overflowX = opts.overflow[ 1 ]; - style.overflowY = opts.overflow[ 2 ]; - } ); - } - - // Implement show/hide animations - propTween = false; - for ( prop in orig ) { - - // General show/hide setup for this element animation - if ( !propTween ) { - if ( dataShow ) { - if ( "hidden" in dataShow ) { - hidden = dataShow.hidden; - } - } else { - dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); - } - - // Store hidden/visible for toggle so `.stop().toggle()` "reverses" - if ( toggle ) { - dataShow.hidden = !hidden; - } - - // Show elements before animating them - if ( hidden ) { - showHide( [ elem ], true ); - } - - /* eslint-disable no-loop-func */ - - anim.done( function() { - - /* eslint-enable no-loop-func */ - - // The final step of a "hide" animation is actually hiding the element - if ( !hidden ) { - showHide( [ elem ] ); - } - dataPriv.remove( elem, "fxshow" ); - for ( prop in orig ) { - jQuery.style( elem, prop, orig[ prop ] ); - } - } ); - } - - // Per-property setup - propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); - if ( !( prop in dataShow ) ) { - dataShow[ prop ] = propTween.start; - if ( hidden ) { - propTween.end = propTween.start; - propTween.start = 0; - } - } - } -} - -function propFilter( props, specialEasing ) { - var index, name, easing, value, hooks; - - // camelCase, specialEasing and expand cssHook pass - for ( index in props ) { - name = camelCase( index ); - easing = specialEasing[ name ]; - value = props[ index ]; - if ( Array.isArray( value ) ) { - easing = value[ 1 ]; - value = props[ index ] = value[ 0 ]; - } - - if ( index !== name ) { - props[ name ] = value; - delete props[ index ]; - } - - hooks = jQuery.cssHooks[ name ]; - if ( hooks && "expand" in hooks ) { - value = hooks.expand( value ); - delete props[ name ]; - - // Not quite $.extend, this won't overwrite existing keys. - // Reusing 'index' because we have the correct "name" - for ( index in value ) { - if ( !( index in props ) ) { - props[ index ] = value[ index ]; - specialEasing[ index ] = easing; - } - } - } else { - specialEasing[ name ] = easing; - } - } -} - -function Animation( elem, properties, options ) { - var result, - stopped, - index = 0, - length = Animation.prefilters.length, - deferred = jQuery.Deferred().always( function() { - - // Don't match elem in the :animated selector - delete tick.elem; - } ), - tick = function() { - if ( stopped ) { - return false; - } - var currentTime = fxNow || createFxNow(), - remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), - - // Support: Android 2.3 only - // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) - temp = remaining / animation.duration || 0, - percent = 1 - temp, - index = 0, - length = animation.tweens.length; - - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( percent ); - } - - deferred.notifyWith( elem, [ animation, percent, remaining ] ); - - // If there's more to do, yield - if ( percent < 1 && length ) { - return remaining; - } - - // If this was an empty animation, synthesize a final progress notification - if ( !length ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - } - - // Resolve the animation and report its conclusion - deferred.resolveWith( elem, [ animation ] ); - return false; - }, - animation = deferred.promise( { - elem: elem, - props: jQuery.extend( {}, properties ), - opts: jQuery.extend( true, { - specialEasing: {}, - easing: jQuery.easing._default - }, options ), - originalProperties: properties, - originalOptions: options, - startTime: fxNow || createFxNow(), - duration: options.duration, - tweens: [], - createTween: function( prop, end ) { - var tween = jQuery.Tween( elem, animation.opts, prop, end, - animation.opts.specialEasing[ prop ] || animation.opts.easing ); - animation.tweens.push( tween ); - return tween; - }, - stop: function( gotoEnd ) { - var index = 0, - - // If we are going to the end, we want to run all the tweens - // otherwise we skip this part - length = gotoEnd ? animation.tweens.length : 0; - if ( stopped ) { - return this; - } - stopped = true; - for ( ; index < length; index++ ) { - animation.tweens[ index ].run( 1 ); - } - - // Resolve when we played the last frame; otherwise, reject - if ( gotoEnd ) { - deferred.notifyWith( elem, [ animation, 1, 0 ] ); - deferred.resolveWith( elem, [ animation, gotoEnd ] ); - } else { - deferred.rejectWith( elem, [ animation, gotoEnd ] ); - } - return this; - } - } ), - props = animation.props; - - propFilter( props, animation.opts.specialEasing ); - - for ( ; index < length; index++ ) { - result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); - if ( result ) { - if ( isFunction( result.stop ) ) { - jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - result.stop.bind( result ); - } - return result; - } - } - - jQuery.map( props, createTween, animation ); - - if ( isFunction( animation.opts.start ) ) { - animation.opts.start.call( elem, animation ); - } - - // Attach callbacks from options - animation - .progress( animation.opts.progress ) - .done( animation.opts.done, animation.opts.complete ) - .fail( animation.opts.fail ) - .always( animation.opts.always ); - - jQuery.fx.timer( - jQuery.extend( tick, { - elem: elem, - anim: animation, - queue: animation.opts.queue - } ) - ); - - return animation; -} - -jQuery.Animation = jQuery.extend( Animation, { - - tweeners: { - "*": [ function( prop, value ) { - var tween = this.createTween( prop, value ); - adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); - return tween; - } ] - }, - - tweener: function( props, callback ) { - if ( isFunction( props ) ) { - callback = props; - props = [ "*" ]; - } else { - props = props.match( rnothtmlwhite ); - } - - var prop, - index = 0, - length = props.length; - - for ( ; index < length; index++ ) { - prop = props[ index ]; - Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; - Animation.tweeners[ prop ].unshift( callback ); - } - }, - - prefilters: [ defaultPrefilter ], - - prefilter: function( callback, prepend ) { - if ( prepend ) { - Animation.prefilters.unshift( callback ); - } else { - Animation.prefilters.push( callback ); - } - } -} ); - -jQuery.speed = function( speed, easing, fn ) { - var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { - complete: fn || !fn && easing || - isFunction( speed ) && speed, - duration: speed, - easing: fn && easing || easing && !isFunction( easing ) && easing - }; - - // Go to the end state if fx are off - if ( jQuery.fx.off ) { - opt.duration = 0; - - } else { - if ( typeof opt.duration !== "number" ) { - if ( opt.duration in jQuery.fx.speeds ) { - opt.duration = jQuery.fx.speeds[ opt.duration ]; - - } else { - opt.duration = jQuery.fx.speeds._default; - } - } - } - - // Normalize opt.queue - true/undefined/null -> "fx" - if ( opt.queue == null || opt.queue === true ) { - opt.queue = "fx"; - } - - // Queueing - opt.old = opt.complete; - - opt.complete = function() { - if ( isFunction( opt.old ) ) { - opt.old.call( this ); - } - - if ( opt.queue ) { - jQuery.dequeue( this, opt.queue ); - } - }; - - return opt; -}; - -jQuery.fn.extend( { - fadeTo: function( speed, to, easing, callback ) { - - // Show any hidden elements after setting opacity to 0 - return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() - - // Animate to the value specified - .end().animate( { opacity: to }, speed, easing, callback ); - }, - animate: function( prop, speed, easing, callback ) { - var empty = jQuery.isEmptyObject( prop ), - optall = jQuery.speed( speed, easing, callback ), - doAnimation = function() { - - // Operate on a copy of prop so per-property easing won't be lost - var anim = Animation( this, jQuery.extend( {}, prop ), optall ); - - // Empty animations, or finishing resolves immediately - if ( empty || dataPriv.get( this, "finish" ) ) { - anim.stop( true ); - } - }; - doAnimation.finish = doAnimation; - - return empty || optall.queue === false ? - this.each( doAnimation ) : - this.queue( optall.queue, doAnimation ); - }, - stop: function( type, clearQueue, gotoEnd ) { - var stopQueue = function( hooks ) { - var stop = hooks.stop; - delete hooks.stop; - stop( gotoEnd ); - }; - - if ( typeof type !== "string" ) { - gotoEnd = clearQueue; - clearQueue = type; - type = undefined; - } - if ( clearQueue ) { - this.queue( type || "fx", [] ); - } - - return this.each( function() { - var dequeue = true, - index = type != null && type + "queueHooks", - timers = jQuery.timers, - data = dataPriv.get( this ); - - if ( index ) { - if ( data[ index ] && data[ index ].stop ) { - stopQueue( data[ index ] ); - } - } else { - for ( index in data ) { - if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { - stopQueue( data[ index ] ); - } - } - } - - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && - ( type == null || timers[ index ].queue === type ) ) { - - timers[ index ].anim.stop( gotoEnd ); - dequeue = false; - timers.splice( index, 1 ); - } - } - - // Start the next in the queue if the last step wasn't forced. - // Timers currently will call their complete callbacks, which - // will dequeue but only if they were gotoEnd. - if ( dequeue || !gotoEnd ) { - jQuery.dequeue( this, type ); - } - } ); - }, - finish: function( type ) { - if ( type !== false ) { - type = type || "fx"; - } - return this.each( function() { - var index, - data = dataPriv.get( this ), - queue = data[ type + "queue" ], - hooks = data[ type + "queueHooks" ], - timers = jQuery.timers, - length = queue ? queue.length : 0; - - // Enable finishing flag on private data - data.finish = true; - - // Empty the queue first - jQuery.queue( this, type, [] ); - - if ( hooks && hooks.stop ) { - hooks.stop.call( this, true ); - } - - // Look for any active animations, and finish them - for ( index = timers.length; index--; ) { - if ( timers[ index ].elem === this && timers[ index ].queue === type ) { - timers[ index ].anim.stop( true ); - timers.splice( index, 1 ); - } - } - - // Look for any animations in the old queue and finish them - for ( index = 0; index < length; index++ ) { - if ( queue[ index ] && queue[ index ].finish ) { - queue[ index ].finish.call( this ); - } - } - - // Turn off finishing flag - delete data.finish; - } ); - } -} ); - -jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { - var cssFn = jQuery.fn[ name ]; - jQuery.fn[ name ] = function( speed, easing, callback ) { - return speed == null || typeof speed === "boolean" ? - cssFn.apply( this, arguments ) : - this.animate( genFx( name, true ), speed, easing, callback ); - }; -} ); - -// Generate shortcuts for custom animations -jQuery.each( { - slideDown: genFx( "show" ), - slideUp: genFx( "hide" ), - slideToggle: genFx( "toggle" ), - fadeIn: { opacity: "show" }, - fadeOut: { opacity: "hide" }, - fadeToggle: { opacity: "toggle" } -}, function( name, props ) { - jQuery.fn[ name ] = function( speed, easing, callback ) { - return this.animate( props, speed, easing, callback ); - }; -} ); - -jQuery.timers = []; -jQuery.fx.tick = function() { - var timer, - i = 0, - timers = jQuery.timers; - - fxNow = Date.now(); - - for ( ; i < timers.length; i++ ) { - timer = timers[ i ]; - - // Run the timer and safely remove it when done (allowing for external removal) - if ( !timer() && timers[ i ] === timer ) { - timers.splice( i--, 1 ); - } - } - - if ( !timers.length ) { - jQuery.fx.stop(); - } - fxNow = undefined; -}; - -jQuery.fx.timer = function( timer ) { - jQuery.timers.push( timer ); - jQuery.fx.start(); -}; - -jQuery.fx.interval = 13; -jQuery.fx.start = function() { - if ( inProgress ) { - return; - } - - inProgress = true; - schedule(); -}; - -jQuery.fx.stop = function() { - inProgress = null; -}; - -jQuery.fx.speeds = { - slow: 600, - fast: 200, - - // Default speed - _default: 400 -}; - - -// Based off of the plugin by Clint Helfers, with permission. -// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ -jQuery.fn.delay = function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = window.setTimeout( next, time ); - hooks.stop = function() { - window.clearTimeout( timeout ); - }; - } ); -}; - - -( function() { - var input = document.createElement( "input" ), - select = document.createElement( "select" ), - opt = select.appendChild( document.createElement( "option" ) ); - - input.type = "checkbox"; - - // Support: Android <=4.3 only - // Default value for a checkbox should be "on" - support.checkOn = input.value !== ""; - - // Support: IE <=11 only - // Must access selectedIndex to make default options select - support.optSelected = opt.selected; - - // Support: IE <=11 only - // An input loses its value after becoming a radio - input = document.createElement( "input" ); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; -} )(); - - -var boolHook, - attrHandle = jQuery.expr.attrHandle; - -jQuery.fn.extend( { - attr: function( name, value ) { - return access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each( function() { - jQuery.removeAttr( this, name ); - } ); - } -} ); - -jQuery.extend( { - attr: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set attributes on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - // Attribute hooks are determined by the lowercase version - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - hooks = jQuery.attrHooks[ name.toLowerCase() ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); - } - - if ( value !== undefined ) { - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - } - - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - elem.setAttribute( name, value + "" ); - return value; - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? undefined : ret; - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !support.radioValue && value === "radio" && - nodeName( elem, "input" ) ) { - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - removeAttr: function( elem, value ) { - var name, - i = 0, - - // Attribute names can contain non-HTML whitespace characters - // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 - attrNames = value && value.match( rnothtmlwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( ( name = attrNames[ i++ ] ) ) { - elem.removeAttribute( name ); - } - } - } -} ); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { - var getter = attrHandle[ name ] || jQuery.find.attr; - - attrHandle[ name ] = function( elem, name, isXML ) { - var ret, handle, - lowercaseName = name.toLowerCase(); - - if ( !isXML ) { - - // Avoid an infinite loop by temporarily removing this function from the getter - handle = attrHandle[ lowercaseName ]; - attrHandle[ lowercaseName ] = ret; - ret = getter( elem, name, isXML ) != null ? - lowercaseName : - null; - attrHandle[ lowercaseName ] = handle; - } - return ret; - }; -} ); - - - - -var rfocusable = /^(?:input|select|textarea|button)$/i, - rclickable = /^(?:a|area)$/i; - -jQuery.fn.extend( { - prop: function( name, value ) { - return access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each( function() { - delete this[ jQuery.propFix[ name ] || name ]; - } ); - } -} ); - -jQuery.extend( { - prop: function( elem, name, value ) { - var ret, hooks, - nType = elem.nodeType; - - // Don't get/set properties on text, comment and attribute nodes - if ( nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && - ( ret = hooks.set( elem, value, name ) ) !== undefined ) { - return ret; - } - - return ( elem[ name ] = value ); - } - - if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { - return ret; - } - - return elem[ name ]; - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - - // Support: IE <=9 - 11 only - // elem.tabIndex doesn't always return the - // correct value when it hasn't been explicitly set - // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - // Use proper attribute retrieval(#12072) - var tabindex = jQuery.find.attr( elem, "tabindex" ); - - if ( tabindex ) { - return parseInt( tabindex, 10 ); - } - - if ( - rfocusable.test( elem.nodeName ) || - rclickable.test( elem.nodeName ) && - elem.href - ) { - return 0; - } - - return -1; - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - } -} ); - -// Support: IE <=11 only -// Accessing the selectedIndex property -// forces the browser to respect setting selected -// on the option -// The getter ensures a default option is selected -// when in an optgroup -// eslint rule "no-unused-expressions" is disabled for this code -// since it considers such accessions noop -if ( !support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - }, - set: function( elem ) { - - /* eslint no-unused-expressions: "off" */ - - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - }; -} - -jQuery.each( [ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -} ); - - - - - // Strip and collapse whitespace according to HTML spec - // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace - function stripAndCollapse( value ) { - var tokens = value.match( rnothtmlwhite ) || []; - return tokens.join( " " ); - } - - -function getClass( elem ) { - return elem.getAttribute && elem.getAttribute( "class" ) || ""; -} - -function classesToArray( value ) { - if ( Array.isArray( value ) ) { - return value; - } - if ( typeof value === "string" ) { - return value.match( rnothtmlwhite ) || []; - } - return []; -} - -jQuery.fn.extend( { - addClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - classes = classesToArray( value ); - - if ( classes.length ) { - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, curValue, clazz, j, finalValue, - i = 0; - - if ( isFunction( value ) ) { - return this.each( function( j ) { - jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); - } ); - } - - if ( !arguments.length ) { - return this.attr( "class", "" ); - } - - classes = classesToArray( value ); - - if ( classes.length ) { - while ( ( elem = this[ i++ ] ) ) { - curValue = getClass( elem ); - - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); - - if ( cur ) { - j = 0; - while ( ( clazz = classes[ j++ ] ) ) { - - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) > -1 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - - // Only assign if different to avoid unneeded rendering. - finalValue = stripAndCollapse( cur ); - if ( curValue !== finalValue ) { - elem.setAttribute( "class", finalValue ); - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isValidValue = type === "string" || Array.isArray( value ); - - if ( typeof stateVal === "boolean" && isValidValue ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - if ( isFunction( value ) ) { - return this.each( function( i ) { - jQuery( this ).toggleClass( - value.call( this, i, getClass( this ), stateVal ), - stateVal - ); - } ); - } - - return this.each( function() { - var className, i, self, classNames; - - if ( isValidValue ) { - - // Toggle individual class names - i = 0; - self = jQuery( this ); - classNames = classesToArray( value ); - - while ( ( className = classNames[ i++ ] ) ) { - - // Check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( value === undefined || type === "boolean" ) { - className = getClass( this ); - if ( className ) { - - // Store className if set - dataPriv.set( this, "__className__", className ); - } - - // If the element has a class name or if we're passed `false`, - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - if ( this.setAttribute ) { - this.setAttribute( "class", - className || value === false ? - "" : - dataPriv.get( this, "__className__" ) || "" - ); - } - } - } ); - }, - - hasClass: function( selector ) { - var className, elem, - i = 0; - - className = " " + selector + " "; - while ( ( elem = this[ i++ ] ) ) { - if ( elem.nodeType === 1 && - ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { - return true; - } - } - - return false; - } -} ); - - - - -var rreturn = /\r/g; - -jQuery.fn.extend( { - val: function( value ) { - var hooks, ret, valueIsFunction, - elem = this[ 0 ]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || - jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && - "get" in hooks && - ( ret = hooks.get( elem, "value" ) ) !== undefined - ) { - return ret; - } - - ret = elem.value; - - // Handle most common string cases - if ( typeof ret === "string" ) { - return ret.replace( rreturn, "" ); - } - - // Handle cases where value is null/undef or number - return ret == null ? "" : ret; - } - - return; - } - - valueIsFunction = isFunction( value ); - - return this.each( function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( valueIsFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - - } else if ( typeof val === "number" ) { - val += ""; - - } else if ( Array.isArray( val ) ) { - val = jQuery.map( val, function( value ) { - return value == null ? "" : value + ""; - } ); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - } ); - } -} ); - -jQuery.extend( { - valHooks: { - option: { - get: function( elem ) { - - var val = jQuery.find.attr( elem, "value" ); - return val != null ? - val : - - // Support: IE <=10 - 11 only - // option.text throws exceptions (#14686, #14858) - // Strip and collapse whitespace - // https://html.spec.whatwg.org/#strip-and-collapse-whitespace - stripAndCollapse( jQuery.text( elem ) ); - } - }, - select: { - get: function( elem ) { - var value, option, i, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one", - values = one ? null : [], - max = one ? index + 1 : options.length; - - if ( index < 0 ) { - i = max; - - } else { - i = one ? index : 0; - } - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Support: IE <=9 only - // IE8-9 doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - - // Don't return options that are disabled or in a disabled optgroup - !option.disabled && - ( !option.parentNode.disabled || - !nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - - /* eslint-disable no-cond-assign */ - - if ( option.selected = - jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 - ) { - optionSet = true; - } - - /* eslint-enable no-cond-assign */ - } - - // Force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - } -} ); - -// Radios and checkboxes getter/setter -jQuery.each( [ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( Array.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); - } - } - }; - if ( !support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - return elem.getAttribute( "value" ) === null ? "on" : elem.value; - }; - } -} ); - - - - -// Return jQuery for attributes-only inclusion - - -support.focusin = "onfocusin" in window; - - -var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - stopPropagationCallback = function( e ) { - e.stopPropagation(); - }; - -jQuery.extend( jQuery.event, { - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; - - cur = lastElement = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "." ) > -1 ) { - - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split( "." ); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf( ":" ) < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join( "." ); - event.rnamespace = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === ( elem.ownerDocument || document ) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { - lastElement = cur; - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( - dataPriv.get( cur, "events" ) || Object.create( null ) - )[ event.type ] && - dataPriv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( ( !special._default || - special._default.apply( eventPath.pop(), data ) === false ) && - acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - - if ( event.isPropagationStopped() ) { - lastElement.addEventListener( type, stopPropagationCallback ); - } - - elem[ type ](); - - if ( event.isPropagationStopped() ) { - lastElement.removeEventListener( type, stopPropagationCallback ); - } - - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - // Piggyback on a donor event to simulate a different one - // Used only for `focus(in | out)` events - simulate: function( type, elem, event ) { - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true - } - ); - - jQuery.event.trigger( e, null, elem ); - } - -} ); - -jQuery.fn.extend( { - - trigger: function( type, data ) { - return this.each( function() { - jQuery.event.trigger( type, data, this ); - } ); - }, - triggerHandler: function( type, data ) { - var elem = this[ 0 ]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -} ); - - -// Support: Firefox <=44 -// Firefox doesn't have focus(in | out) events -// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 -// -// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 -// focus(in | out) events fire after focus & blur events, -// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order -// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 -if ( !support.focusin ) { - jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - - // Handle: regular nodes (via `this.ownerDocument`), window - // (via `this.document`) & document (via `this`). - var doc = this.ownerDocument || this.document || this, - attaches = dataPriv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this.document || this, - attaches = dataPriv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - dataPriv.remove( doc, fix ); - - } else { - dataPriv.access( doc, fix, attaches ); - } - } - }; - } ); -} -var location = window.location; - -var nonce = { guid: Date.now() }; - -var rquery = ( /\?/ ); - - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; -}; - - -var - rbracket = /\[\]$/, - rCRLF = /\r?\n/g, - rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, - rsubmittable = /^(?:input|select|textarea|keygen)/i; - -function buildParams( prefix, obj, traditional, add ) { - var name; - - if ( Array.isArray( obj ) ) { - - // Serialize array item. - jQuery.each( obj, function( i, v ) { - if ( traditional || rbracket.test( prefix ) ) { - - // Treat each array item as a scalar. - add( prefix, v ); - - } else { - - // Item is non-scalar (array or object), encode its numeric index. - buildParams( - prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", - v, - traditional, - add - ); - } - } ); - - } else if ( !traditional && toType( obj ) === "object" ) { - - // Serialize object item. - for ( name in obj ) { - buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); - } - - } else { - - // Serialize scalar item. - add( prefix, obj ); - } -} - -// Serialize an array of form elements or a set of -// key/values into a query string -jQuery.param = function( a, traditional ) { - var prefix, - s = [], - add = function( key, valueOrFunction ) { - - // If value is a function, invoke it and use its return value - var value = isFunction( valueOrFunction ) ? - valueOrFunction() : - valueOrFunction; - - s[ s.length ] = encodeURIComponent( key ) + "=" + - encodeURIComponent( value == null ? "" : value ); - }; - - if ( a == null ) { - return ""; - } - - // If an array was passed in, assume that it is an array of form elements. - if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { - - // Serialize the form elements - jQuery.each( a, function() { - add( this.name, this.value ); - } ); - - } else { - - // If traditional, encode the "old" way (the way 1.3.2 or older - // did it), otherwise encode params recursively. - for ( prefix in a ) { - buildParams( prefix, a[ prefix ], traditional, add ); - } - } - - // Return the resulting serialization - return s.join( "&" ); -}; - -jQuery.fn.extend( { - serialize: function() { - return jQuery.param( this.serializeArray() ); - }, - serializeArray: function() { - return this.map( function() { - - // Can add propHook for "elements" to filter or add form elements - var elements = jQuery.prop( this, "elements" ); - return elements ? jQuery.makeArray( elements ) : this; - } ) - .filter( function() { - var type = this.type; - - // Use .is( ":disabled" ) so that fieldset[disabled] works - return this.name && !jQuery( this ).is( ":disabled" ) && - rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && - ( this.checked || !rcheckableType.test( type ) ); - } ) - .map( function( _i, elem ) { - var val = jQuery( this ).val(); - - if ( val == null ) { - return null; - } - - if ( Array.isArray( val ) ) { - return jQuery.map( val, function( val ) { - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ); - } - - return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; - } ).get(); - } -} ); - - -var - r20 = /%20/g, - rhash = /#.*$/, - rantiCache = /([?&])_=[^&]*/, - rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, - - // #7653, #8125, #8152: local protocol detection - rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, - rnoContent = /^(?:GET|HEAD)$/, - rprotocol = /^\/\//, - - /* Prefilters - * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) - * 2) These are called: - * - BEFORE asking for a transport - * - AFTER param serialization (s.data is a string if s.processData is true) - * 3) key is the dataType - * 4) the catchall symbol "*" can be used - * 5) execution will start with transport dataType and THEN continue down to "*" if needed - */ - prefilters = {}, - - /* Transports bindings - * 1) key is the dataType - * 2) the catchall symbol "*" can be used - * 3) selection will start with transport dataType and THEN go to "*" if needed - */ - transports = {}, - - // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression - allTypes = "*/".concat( "*" ), - - // Anchor tag for parsing the document origin - originAnchor = document.createElement( "a" ); - originAnchor.href = location.href; - -// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport -function addToPrefiltersOrTransports( structure ) { - - // dataTypeExpression is optional and defaults to "*" - return function( dataTypeExpression, func ) { - - if ( typeof dataTypeExpression !== "string" ) { - func = dataTypeExpression; - dataTypeExpression = "*"; - } - - var dataType, - i = 0, - dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; - - if ( isFunction( func ) ) { - - // For each dataType in the dataTypeExpression - while ( ( dataType = dataTypes[ i++ ] ) ) { - - // Prepend if requested - if ( dataType[ 0 ] === "+" ) { - dataType = dataType.slice( 1 ) || "*"; - ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); - - // Otherwise append - } else { - ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); - } - } - } - }; -} - -// Base inspection function for prefilters and transports -function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { - - var inspected = {}, - seekingTransport = ( structure === transports ); - - function inspect( dataType ) { - var selected; - inspected[ dataType ] = true; - jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { - var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); - if ( typeof dataTypeOrTransport === "string" && - !seekingTransport && !inspected[ dataTypeOrTransport ] ) { - - options.dataTypes.unshift( dataTypeOrTransport ); - inspect( dataTypeOrTransport ); - return false; - } else if ( seekingTransport ) { - return !( selected = dataTypeOrTransport ); - } - } ); - return selected; - } - - return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); -} - -// A special extend for ajax options -// that takes "flat" options (not to be deep extended) -// Fixes #9887 -function ajaxExtend( target, src ) { - var key, deep, - flatOptions = jQuery.ajaxSettings.flatOptions || {}; - - for ( key in src ) { - if ( src[ key ] !== undefined ) { - ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; - } - } - if ( deep ) { - jQuery.extend( true, target, deep ); - } - - return target; -} - -/* Handles responses to an ajax request: - * - finds the right dataType (mediates between content-type and expected dataType) - * - returns the corresponding response - */ -function ajaxHandleResponses( s, jqXHR, responses ) { - - var ct, type, finalDataType, firstDataType, - contents = s.contents, - dataTypes = s.dataTypes; - - // Remove auto dataType and get content-type in the process - while ( dataTypes[ 0 ] === "*" ) { - dataTypes.shift(); - if ( ct === undefined ) { - ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); - } - } - - // Check if we're dealing with a known content-type - if ( ct ) { - for ( type in contents ) { - if ( contents[ type ] && contents[ type ].test( ct ) ) { - dataTypes.unshift( type ); - break; - } - } - } - - // Check to see if we have a response for the expected dataType - if ( dataTypes[ 0 ] in responses ) { - finalDataType = dataTypes[ 0 ]; - } else { - - // Try convertible dataTypes - for ( type in responses ) { - if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { - finalDataType = type; - break; - } - if ( !firstDataType ) { - firstDataType = type; - } - } - - // Or just use first one - finalDataType = finalDataType || firstDataType; - } - - // If we found a dataType - // We add the dataType to the list if needed - // and return the corresponding response - if ( finalDataType ) { - if ( finalDataType !== dataTypes[ 0 ] ) { - dataTypes.unshift( finalDataType ); - } - return responses[ finalDataType ]; - } -} - -/* Chain conversions given the request and the original response - * Also sets the responseXXX fields on the jqXHR instance - */ -function ajaxConvert( s, response, jqXHR, isSuccess ) { - var conv2, current, conv, tmp, prev, - converters = {}, - - // Work with a copy of dataTypes in case we need to modify it for conversion - dataTypes = s.dataTypes.slice(); - - // Create converters map with lowercased keys - if ( dataTypes[ 1 ] ) { - for ( conv in s.converters ) { - converters[ conv.toLowerCase() ] = s.converters[ conv ]; - } - } - - current = dataTypes.shift(); - - // Convert to each sequential dataType - while ( current ) { - - if ( s.responseFields[ current ] ) { - jqXHR[ s.responseFields[ current ] ] = response; - } - - // Apply the dataFilter if provided - if ( !prev && isSuccess && s.dataFilter ) { - response = s.dataFilter( response, s.dataType ); - } - - prev = current; - current = dataTypes.shift(); - - if ( current ) { - - // There's only work to do if current dataType is non-auto - if ( current === "*" ) { - - current = prev; - - // Convert response if prev dataType is non-auto and differs from current - } else if ( prev !== "*" && prev !== current ) { - - // Seek a direct converter - conv = converters[ prev + " " + current ] || converters[ "* " + current ]; - - // If none found, seek a pair - if ( !conv ) { - for ( conv2 in converters ) { - - // If conv2 outputs current - tmp = conv2.split( " " ); - if ( tmp[ 1 ] === current ) { - - // If prev can be converted to accepted input - conv = converters[ prev + " " + tmp[ 0 ] ] || - converters[ "* " + tmp[ 0 ] ]; - if ( conv ) { - - // Condense equivalence converters - if ( conv === true ) { - conv = converters[ conv2 ]; - - // Otherwise, insert the intermediate dataType - } else if ( converters[ conv2 ] !== true ) { - current = tmp[ 0 ]; - dataTypes.unshift( tmp[ 1 ] ); - } - break; - } - } - } - } - - // Apply converter (if not an equivalence) - if ( conv !== true ) { - - // Unless errors are allowed to bubble, catch and return them - if ( conv && s.throws ) { - response = conv( response ); - } else { - try { - response = conv( response ); - } catch ( e ) { - return { - state: "parsererror", - error: conv ? e : "No conversion from " + prev + " to " + current - }; - } - } - } - } - } - } - - return { state: "success", data: response }; -} - -jQuery.extend( { - - // Counter for holding the number of active queries - active: 0, - - // Last-Modified header cache for next request - lastModified: {}, - etag: {}, - - ajaxSettings: { - url: location.href, - type: "GET", - isLocal: rlocalProtocol.test( location.protocol ), - global: true, - processData: true, - async: true, - contentType: "application/x-www-form-urlencoded; charset=UTF-8", - - /* - timeout: 0, - data: null, - dataType: null, - username: null, - password: null, - cache: null, - throws: false, - traditional: false, - headers: {}, - */ - - accepts: { - "*": allTypes, - text: "text/plain", - html: "text/html", - xml: "application/xml, text/xml", - json: "application/json, text/javascript" - }, - - contents: { - xml: /\bxml\b/, - html: /\bhtml/, - json: /\bjson\b/ - }, - - responseFields: { - xml: "responseXML", - text: "responseText", - json: "responseJSON" - }, - - // Data converters - // Keys separate source (or catchall "*") and destination types with a single space - converters: { - - // Convert anything to text - "* text": String, - - // Text to html (true = no transformation) - "text html": true, - - // Evaluate text as a json expression - "text json": JSON.parse, - - // Parse text as xml - "text xml": jQuery.parseXML - }, - - // For options that shouldn't be deep extended: - // you can add your own custom options here if - // and when you create one that shouldn't be - // deep extended (see ajaxExtend) - flatOptions: { - url: true, - context: true - } - }, - - // Creates a full fledged settings object into target - // with both ajaxSettings and settings fields. - // If target is omitted, writes into ajaxSettings. - ajaxSetup: function( target, settings ) { - return settings ? - - // Building a settings object - ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : - - // Extending ajaxSettings - ajaxExtend( jQuery.ajaxSettings, target ); - }, - - ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), - ajaxTransport: addToPrefiltersOrTransports( transports ), - - // Main method - ajax: function( url, options ) { - - // If url is an object, simulate pre-1.5 signature - if ( typeof url === "object" ) { - options = url; - url = undefined; - } - - // Force options to be an object - options = options || {}; - - var transport, - - // URL without anti-cache param - cacheURL, - - // Response headers - responseHeadersString, - responseHeaders, - - // timeout handle - timeoutTimer, - - // Url cleanup var - urlAnchor, - - // Request state (becomes false upon send and true upon completion) - completed, - - // To know if global events are to be dispatched - fireGlobals, - - // Loop variable - i, - - // uncached part of the url - uncached, - - // Create the final options object - s = jQuery.ajaxSetup( {}, options ), - - // Callbacks context - callbackContext = s.context || s, - - // Context for global events is callbackContext if it is a DOM node or jQuery collection - globalEventContext = s.context && - ( callbackContext.nodeType || callbackContext.jquery ) ? - jQuery( callbackContext ) : - jQuery.event, - - // Deferreds - deferred = jQuery.Deferred(), - completeDeferred = jQuery.Callbacks( "once memory" ), - - // Status-dependent callbacks - statusCode = s.statusCode || {}, - - // Headers (they are sent all at once) - requestHeaders = {}, - requestHeadersNames = {}, - - // Default abort message - strAbort = "canceled", - - // Fake xhr - jqXHR = { - readyState: 0, - - // Builds headers hashtable if needed - getResponseHeader: function( key ) { - var match; - if ( completed ) { - if ( !responseHeaders ) { - responseHeaders = {}; - while ( ( match = rheaders.exec( responseHeadersString ) ) ) { - responseHeaders[ match[ 1 ].toLowerCase() + " " ] = - ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) - .concat( match[ 2 ] ); - } - } - match = responseHeaders[ key.toLowerCase() + " " ]; - } - return match == null ? null : match.join( ", " ); - }, - - // Raw string - getAllResponseHeaders: function() { - return completed ? responseHeadersString : null; - }, - - // Caches the header - setRequestHeader: function( name, value ) { - if ( completed == null ) { - name = requestHeadersNames[ name.toLowerCase() ] = - requestHeadersNames[ name.toLowerCase() ] || name; - requestHeaders[ name ] = value; - } - return this; - }, - - // Overrides response content-type header - overrideMimeType: function( type ) { - if ( completed == null ) { - s.mimeType = type; - } - return this; - }, - - // Status-dependent callbacks - statusCode: function( map ) { - var code; - if ( map ) { - if ( completed ) { - - // Execute the appropriate callbacks - jqXHR.always( map[ jqXHR.status ] ); - } else { - - // Lazy-add the new callbacks in a way that preserves old ones - for ( code in map ) { - statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; - } - } - } - return this; - }, - - // Cancel the request - abort: function( statusText ) { - var finalText = statusText || strAbort; - if ( transport ) { - transport.abort( finalText ); - } - done( 0, finalText ); - return this; - } - }; - - // Attach deferreds - deferred.promise( jqXHR ); - - // Add protocol if not provided (prefilters might expect it) - // Handle falsy url in the settings object (#10093: consistency with old signature) - // We also use the url parameter if available - s.url = ( ( url || s.url || location.href ) + "" ) - .replace( rprotocol, location.protocol + "//" ); - - // Alias method option to type as per ticket #12004 - s.type = options.method || options.type || s.method || s.type; - - // Extract dataTypes list - s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; - - // A cross-domain request is in order when the origin doesn't match the current origin. - if ( s.crossDomain == null ) { - urlAnchor = document.createElement( "a" ); - - // Support: IE <=8 - 11, Edge 12 - 15 - // IE throws exception on accessing the href property if url is malformed, - // e.g. http://example.com:80x/ - try { - urlAnchor.href = s.url; - - // Support: IE <=8 - 11 only - // Anchor's host property isn't correctly set when s.url is relative - urlAnchor.href = urlAnchor.href; - s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== - urlAnchor.protocol + "//" + urlAnchor.host; - } catch ( e ) { - - // If there is an error parsing the URL, assume it is crossDomain, - // it can be rejected by the transport if it is invalid - s.crossDomain = true; - } - } - - // Convert data if not already a string - if ( s.data && s.processData && typeof s.data !== "string" ) { - s.data = jQuery.param( s.data, s.traditional ); - } - - // Apply prefilters - inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); - - // If request was aborted inside a prefilter, stop there - if ( completed ) { - return jqXHR; - } - - // We can fire global events as of now if asked to - // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) - fireGlobals = jQuery.event && s.global; - - // Watch for a new set of requests - if ( fireGlobals && jQuery.active++ === 0 ) { - jQuery.event.trigger( "ajaxStart" ); - } - - // Uppercase the type - s.type = s.type.toUpperCase(); - - // Determine if request has content - s.hasContent = !rnoContent.test( s.type ); - - // Save the URL in case we're toying with the If-Modified-Since - // and/or If-None-Match header later on - // Remove hash to simplify url manipulation - cacheURL = s.url.replace( rhash, "" ); - - // More options handling for requests with no content - if ( !s.hasContent ) { - - // Remember the hash so we can put it back - uncached = s.url.slice( cacheURL.length ); - - // If data is available and should be processed, append data to url - if ( s.data && ( s.processData || typeof s.data === "string" ) ) { - cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; - - // #9682: remove data so that it's not used in an eventual retry - delete s.data; - } - - // Add or update anti-cache param if needed - if ( s.cache === false ) { - cacheURL = cacheURL.replace( rantiCache, "$1" ); - uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + - uncached; - } - - // Put hash and anti-cache on the URL that will be requested (gh-1732) - s.url = cacheURL + uncached; - - // Change '%20' to '+' if this is encoded form body content (gh-2658) - } else if ( s.data && s.processData && - ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { - s.data = s.data.replace( r20, "+" ); - } - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - if ( jQuery.lastModified[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); - } - if ( jQuery.etag[ cacheURL ] ) { - jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); - } - } - - // Set the correct header, if data is being sent - if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { - jqXHR.setRequestHeader( "Content-Type", s.contentType ); - } - - // Set the Accepts header for the server, depending on the dataType - jqXHR.setRequestHeader( - "Accept", - s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? - s.accepts[ s.dataTypes[ 0 ] ] + - ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : - s.accepts[ "*" ] - ); - - // Check for headers option - for ( i in s.headers ) { - jqXHR.setRequestHeader( i, s.headers[ i ] ); - } - - // Allow custom headers/mimetypes and early abort - if ( s.beforeSend && - ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { - - // Abort if not done already and return - return jqXHR.abort(); - } - - // Aborting is no longer a cancellation - strAbort = "abort"; - - // Install callbacks on deferreds - completeDeferred.add( s.complete ); - jqXHR.done( s.success ); - jqXHR.fail( s.error ); - - // Get transport - transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); - - // If no transport, we auto-abort - if ( !transport ) { - done( -1, "No Transport" ); - } else { - jqXHR.readyState = 1; - - // Send global event - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); - } - - // If request was aborted inside ajaxSend, stop there - if ( completed ) { - return jqXHR; - } - - // Timeout - if ( s.async && s.timeout > 0 ) { - timeoutTimer = window.setTimeout( function() { - jqXHR.abort( "timeout" ); - }, s.timeout ); - } - - try { - completed = false; - transport.send( requestHeaders, done ); - } catch ( e ) { - - // Rethrow post-completion exceptions - if ( completed ) { - throw e; - } - - // Propagate others as results - done( -1, e ); - } - } - - // Callback for when everything is done - function done( status, nativeStatusText, responses, headers ) { - var isSuccess, success, error, response, modified, - statusText = nativeStatusText; - - // Ignore repeat invocations - if ( completed ) { - return; - } - - completed = true; - - // Clear timeout if it exists - if ( timeoutTimer ) { - window.clearTimeout( timeoutTimer ); - } - - // Dereference transport for early garbage collection - // (no matter how long the jqXHR object will be used) - transport = undefined; - - // Cache response headers - responseHeadersString = headers || ""; - - // Set readyState - jqXHR.readyState = status > 0 ? 4 : 0; - - // Determine if successful - isSuccess = status >= 200 && status < 300 || status === 304; - - // Get response data - if ( responses ) { - response = ajaxHandleResponses( s, jqXHR, responses ); - } - - // Use a noop converter for missing script - if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { - s.converters[ "text script" ] = function() {}; - } - - // Convert no matter what (that way responseXXX fields are always set) - response = ajaxConvert( s, response, jqXHR, isSuccess ); - - // If successful, handle type chaining - if ( isSuccess ) { - - // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. - if ( s.ifModified ) { - modified = jqXHR.getResponseHeader( "Last-Modified" ); - if ( modified ) { - jQuery.lastModified[ cacheURL ] = modified; - } - modified = jqXHR.getResponseHeader( "etag" ); - if ( modified ) { - jQuery.etag[ cacheURL ] = modified; - } - } - - // if no content - if ( status === 204 || s.type === "HEAD" ) { - statusText = "nocontent"; - - // if not modified - } else if ( status === 304 ) { - statusText = "notmodified"; - - // If we have data, let's convert it - } else { - statusText = response.state; - success = response.data; - error = response.error; - isSuccess = !error; - } - } else { - - // Extract error from statusText and normalize for non-aborts - error = statusText; - if ( status || !statusText ) { - statusText = "error"; - if ( status < 0 ) { - status = 0; - } - } - } - - // Set data for the fake xhr object - jqXHR.status = status; - jqXHR.statusText = ( nativeStatusText || statusText ) + ""; - - // Success/Error - if ( isSuccess ) { - deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); - } else { - deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); - } - - // Status-dependent callbacks - jqXHR.statusCode( statusCode ); - statusCode = undefined; - - if ( fireGlobals ) { - globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", - [ jqXHR, s, isSuccess ? success : error ] ); - } - - // Complete - completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); - - if ( fireGlobals ) { - globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); - - // Handle the global AJAX counter - if ( !( --jQuery.active ) ) { - jQuery.event.trigger( "ajaxStop" ); - } - } - } - - return jqXHR; - }, - - getJSON: function( url, data, callback ) { - return jQuery.get( url, data, callback, "json" ); - }, - - getScript: function( url, callback ) { - return jQuery.get( url, undefined, callback, "script" ); - } -} ); - -jQuery.each( [ "get", "post" ], function( _i, method ) { - jQuery[ method ] = function( url, data, callback, type ) { - - // Shift arguments if data argument was omitted - if ( isFunction( data ) ) { - type = type || callback; - callback = data; - data = undefined; - } - - // The url can be an options object (which then must have .url) - return jQuery.ajax( jQuery.extend( { - url: url, - type: method, - dataType: type, - data: data, - success: callback - }, jQuery.isPlainObject( url ) && url ) ); - }; -} ); - -jQuery.ajaxPrefilter( function( s ) { - var i; - for ( i in s.headers ) { - if ( i.toLowerCase() === "content-type" ) { - s.contentType = s.headers[ i ] || ""; - } - } -} ); - - -jQuery._evalUrl = function( url, options, doc ) { - return jQuery.ajax( { - url: url, - - // Make this explicit, since user can override this through ajaxSetup (#11264) - type: "GET", - dataType: "script", - cache: true, - async: false, - global: false, - - // Only evaluate the response if it is successful (gh-4126) - // dataFilter is not invoked for failure responses, so using it instead - // of the default converter is kludgy but it works. - converters: { - "text script": function() {} - }, - dataFilter: function( response ) { - jQuery.globalEval( response, options, doc ); - } - } ); -}; - - -jQuery.fn.extend( { - wrapAll: function( html ) { - var wrap; - - if ( this[ 0 ] ) { - if ( isFunction( html ) ) { - html = html.call( this[ 0 ] ); - } - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map( function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - } ).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( isFunction( html ) ) { - return this.each( function( i ) { - jQuery( this ).wrapInner( html.call( this, i ) ); - } ); - } - - return this.each( function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - } ); - }, - - wrap: function( html ) { - var htmlIsFunction = isFunction( html ); - - return this.each( function( i ) { - jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); - } ); - }, - - unwrap: function( selector ) { - this.parent( selector ).not( "body" ).each( function() { - jQuery( this ).replaceWith( this.childNodes ); - } ); - return this; - } -} ); - - -jQuery.expr.pseudos.hidden = function( elem ) { - return !jQuery.expr.pseudos.visible( elem ); -}; -jQuery.expr.pseudos.visible = function( elem ) { - return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); -}; - - - - -jQuery.ajaxSettings.xhr = function() { - try { - return new window.XMLHttpRequest(); - } catch ( e ) {} -}; - -var xhrSuccessStatus = { - - // File protocol always yields status code 0, assume 200 - 0: 200, - - // Support: IE <=9 only - // #1450: sometimes IE returns 1223 when it should be 204 - 1223: 204 - }, - xhrSupported = jQuery.ajaxSettings.xhr(); - -support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); -support.ajax = xhrSupported = !!xhrSupported; - -jQuery.ajaxTransport( function( options ) { - var callback, errorCallback; - - // Cross domain only allowed if supported through XMLHttpRequest - if ( support.cors || xhrSupported && !options.crossDomain ) { - return { - send: function( headers, complete ) { - var i, - xhr = options.xhr(); - - xhr.open( - options.type, - options.url, - options.async, - options.username, - options.password - ); - - // Apply custom fields if provided - if ( options.xhrFields ) { - for ( i in options.xhrFields ) { - xhr[ i ] = options.xhrFields[ i ]; - } - } - - // Override mime type if needed - if ( options.mimeType && xhr.overrideMimeType ) { - xhr.overrideMimeType( options.mimeType ); - } - - // X-Requested-With header - // For cross-domain requests, seeing as conditions for a preflight are - // akin to a jigsaw puzzle, we simply never set it to be sure. - // (it can always be set on a per-request basis or even using ajaxSetup) - // For same-domain requests, won't change header if already provided. - if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { - headers[ "X-Requested-With" ] = "XMLHttpRequest"; - } - - // Set headers - for ( i in headers ) { - xhr.setRequestHeader( i, headers[ i ] ); - } - - // Callback - callback = function( type ) { - return function() { - if ( callback ) { - callback = errorCallback = xhr.onload = - xhr.onerror = xhr.onabort = xhr.ontimeout = - xhr.onreadystatechange = null; - - if ( type === "abort" ) { - xhr.abort(); - } else if ( type === "error" ) { - - // Support: IE <=9 only - // On a manual native abort, IE9 throws - // errors on any property access that is not readyState - if ( typeof xhr.status !== "number" ) { - complete( 0, "error" ); - } else { - complete( - - // File: protocol always yields status 0; see #8605, #14207 - xhr.status, - xhr.statusText - ); - } - } else { - complete( - xhrSuccessStatus[ xhr.status ] || xhr.status, - xhr.statusText, - - // Support: IE <=9 only - // IE9 has no XHR2 but throws on binary (trac-11426) - // For XHR2 non-text, let the caller handle it (gh-2498) - ( xhr.responseType || "text" ) !== "text" || - typeof xhr.responseText !== "string" ? - { binary: xhr.response } : - { text: xhr.responseText }, - xhr.getAllResponseHeaders() - ); - } - } - }; - }; - - // Listen to events - xhr.onload = callback(); - errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); - - // Support: IE 9 only - // Use onreadystatechange to replace onabort - // to handle uncaught aborts - if ( xhr.onabort !== undefined ) { - xhr.onabort = errorCallback; - } else { - xhr.onreadystatechange = function() { - - // Check readyState before timeout as it changes - if ( xhr.readyState === 4 ) { - - // Allow onerror to be called first, - // but that will not handle a native abort - // Also, save errorCallback to a variable - // as xhr.onerror cannot be accessed - window.setTimeout( function() { - if ( callback ) { - errorCallback(); - } - } ); - } - }; - } - - // Create the abort callback - callback = callback( "abort" ); - - try { - - // Do send the request (this may raise an exception) - xhr.send( options.hasContent && options.data || null ); - } catch ( e ) { - - // #14683: Only rethrow if this hasn't been notified as an error yet - if ( callback ) { - throw e; - } - } - }, - - abort: function() { - if ( callback ) { - callback(); - } - } - }; - } -} ); - - - - -// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) -jQuery.ajaxPrefilter( function( s ) { - if ( s.crossDomain ) { - s.contents.script = false; - } -} ); - -// Install script dataType -jQuery.ajaxSetup( { - accepts: { - script: "text/javascript, application/javascript, " + - "application/ecmascript, application/x-ecmascript" - }, - contents: { - script: /\b(?:java|ecma)script\b/ - }, - converters: { - "text script": function( text ) { - jQuery.globalEval( text ); - return text; - } - } -} ); - -// Handle cache's special case and crossDomain -jQuery.ajaxPrefilter( "script", function( s ) { - if ( s.cache === undefined ) { - s.cache = false; - } - if ( s.crossDomain ) { - s.type = "GET"; - } -} ); - -// Bind script tag hack transport -jQuery.ajaxTransport( "script", function( s ) { - - // This transport only deals with cross domain or forced-by-attrs requests - if ( s.crossDomain || s.scriptAttrs ) { - var script, callback; - return { - send: function( _, complete ) { - script = jQuery( " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Hardware agnostic training (preparation)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Hardware agnostic training (preparation)

-

To train on CPU/GPU/TPU without changing your code, we need to build a few good habits :)

-
-
-

Delete .cuda() or .to() calls

-

Delete any calls to .cuda() or .to(device).

-
# before lightning
-def forward(self, x):
-    x = x.cuda(0)
-    layer_1.cuda(0)
-    x_hat = layer_1(x)
-
-
-# after lightning
-def forward(self, x):
-    x_hat = layer_1(x)
-
-
-
-
-
-

Init tensors using type_as and register_buffer

-

When you need to create a new tensor, use type_as. -This will make your code scale to any arbitrary number of GPUs or TPUs with Lightning.

-
# before lightning
-def forward(self, x):
-    z = torch.Tensor(2, 3)
-    z = z.cuda(0)
-
-
-# with lightning
-def forward(self, x):
-    z = torch.Tensor(2, 3)
-    z = z.type_as(x)
-
-
-

The LightningModule knows what device it is on. You can access the reference via self.device. -Sometimes it is necessary to store tensors as module attributes. However, if they are not parameters they will -remain on the CPU even if the module gets moved to a new device. To prevent that and remain device agnostic, -register the tensor as a buffer in your modules’ __init__ method with register_buffer().

-
class LitModel(LightningModule):
-    def __init__(self):
-        ...
-        self.register_buffer("sigma", torch.eye(3))
-        # you can now access self.sigma anywhere in your module
-
-
-
-
-
-

Remove samplers

-

DistributedSampler is automatically handled by Lightning.

-

See replace_sampler_ddp for more information.

-
-
-
-

Synchronize validation and test logging

-

When running in distributed mode, we have to ensure that the validation and test step logging calls are synchronized across processes. -This is done by adding sync_dist=True to all self.log calls in the validation and test step. -This ensures that each GPU worker has the same behaviour when tracking model checkpoints, which is important for later downstream tasks such as testing the best checkpoint across all workers. -The sync_dist option can also be used in logging calls during the step methods, but be aware that this can lead to significant communication overhead and slow down your training.

-

Note if you use any built in metrics or custom metrics that use TorchMetrics, these do not need to be updated and are automatically handled for you.

-
def validation_step(self, batch, batch_idx):
-    x, y = batch
-    logits = self(x)
-    loss = self.loss(logits, y)
-    # Add sync_dist=True to sync logging across all GPU workers (may have performance impact)
-    self.log("validation_loss", loss, on_step=True, on_epoch=True, sync_dist=True)
-
-
-def test_step(self, batch, batch_idx):
-    x, y = batch
-    logits = self(x)
-    loss = self.loss(logits, y)
-    # Add sync_dist=True to sync logging across all GPU workers (may have performance impact)
-    self.log("test_loss", loss, on_step=True, on_epoch=True, sync_dist=True)
-
-
-

It is possible to perform some computation manually and log the reduced result on rank 0 as follows:

-
def test_step(self, batch, batch_idx):
-    x, y = batch
-    tensors = self(x)
-    return tensors
-
-
-def test_epoch_end(self, outputs):
-    mean = torch.mean(self.all_gather(outputs))
-
-    # When logging only on rank 0, don't forget to add
-    # ``rank_zero_only=True`` to avoid deadlocks on synchronization.
-    if self.trainer.is_global_zero:
-        self.log("my_reduced_metric", mean, rank_zero_only=True)
-
-
-
-
-
-

Make models pickleable

-

It’s very likely your code is already pickleable, -in that case no change in necessary. -However, if you run a distributed model and get the following error:

-
self._launch(process_obj)
-File "/net/software/local/python/3.6.5/lib/python3.6/multiprocessing/popen_spawn_posix.py", line 47,
-in _launch reduction.dump(process_obj, fp)
-File "/net/software/local/python/3.6.5/lib/python3.6/multiprocessing/reduction.py", line 60, in dump
-ForkingPickler(file, protocol).dump(obj)
-_pickle.PicklingError: Can't pickle <function <lambda> at 0x2b599e088ae8>:
-attribute lookup <lambda> on __main__ failed
-
-
-

This means something in your model definition, transforms, optimizer, dataloader or callbacks cannot be pickled, and the following code will fail:

-
import pickle
-
-pickle.dump(some_object)
-
-
-

This is a limitation of using multiple processes for distributed training within PyTorch. -To fix this issue, find your piece of code that cannot be pickled. The end of the stacktrace -is usually helpful. -ie: in the stacktrace example here, there seems to be a lambda function somewhere in the code -which cannot be pickled.

-
self._launch(process_obj)
-File "/net/software/local/python/3.6.5/lib/python3.6/multiprocessing/popen_spawn_posix.py", line 47,
-in _launch reduction.dump(process_obj, fp)
-File "/net/software/local/python/3.6.5/lib/python3.6/multiprocessing/reduction.py", line 60, in dump
-ForkingPickler(file, protocol).dump(obj)
-_pickle.PicklingError: Can't pickle [THIS IS THE THING TO FIND AND DELETE]:
-attribute lookup <lambda> on __main__ failed
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/gpu.html b/docs/accelerators/gpu.html deleted file mode 100644 index 64b6384..0000000 --- a/docs/accelerators/gpu.html +++ /dev/null @@ -1,761 +0,0 @@ - - - - - - - - - - - - - - Accelerator: GPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: GPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
- - -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/gpu_advanced.html b/docs/accelerators/gpu_advanced.html deleted file mode 100644 index dcccce2..0000000 --- a/docs/accelerators/gpu_advanced.html +++ /dev/null @@ -1,692 +0,0 @@ - - - - - - - - - - - - - - GPU training (Advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • GPU training (Advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

GPU training (Advanced)

-

Audience: Users looking to scale massive models (ie: 1 Trillion parameters).

-
-

For experts pushing the state-of-the-art in model development, Lightning offers various techniques to enable Trillion+ parameter-scale models.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/gpu_basic.html b/docs/accelerators/gpu_basic.html deleted file mode 100644 index bf93504..0000000 --- a/docs/accelerators/gpu_basic.html +++ /dev/null @@ -1,802 +0,0 @@ - - - - - - - - - - - - - - GPU training (Basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • GPU training (Basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

GPU training (Basic)

-

Audience: Users looking to save money and run large models faster using single or multiple

-
-
-

What is a GPU?

-

A Graphics Processing Unit (GPU), is a specialized hardware accelerator designed to speed up mathematical computations used in gaming and deep learning.

-
-
-
-

Train on 1 GPU

-

Make sure you’re running on a machine with at least one GPU. There’s no need to specify any NVIDIA flags -as Lightning will do it for you.

-
trainer = Trainer(accelerator="gpu", devices=1)
-
-
-
-
-
-

Train on multiple GPUs

-

To use multiple GPUs, set the number of devices in the Trainer or the index of the GPUs.

-
trainer = Trainer(accelerator="gpu", devices=4)
-
-
-
-

Choosing GPU devices

-

You can select the GPU devices using ranges, a list of indices or a string containing -a comma separated list of GPU ids:

-
# DEFAULT (int) specifies how many GPUs to use per node
-Trainer(accelerator="gpu", devices=k)
-
-# Above is equivalent to
-Trainer(accelerator="gpu", devices=list(range(k)))
-
-# Specify which GPUs to use (don't use when running on cluster)
-Trainer(accelerator="gpu", devices=[0, 1])
-
-# Equivalent using a string
-Trainer(accelerator="gpu", devices="0, 1")
-
-# To use all available GPUs put -1 or '-1'
-# equivalent to list(range(torch.cuda.device_count()))
-Trainer(accelerator="gpu", devices=-1)
-
-
-

The table below lists examples of possible input formats and how they are interpreted by Lightning.

- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

devices

Type

Parsed

Meaning

3

int

[0, 1, 2]

first 3 GPUs

-1

int

[0, 1, 2, …]

all available GPUs

[0]

list

[0]

GPU 0

[1, 3]

list

[1, 3]

GPUs 1 and 3

“3”

str

[0, 1, 2]

first 3 GPUs

“1, 3”

str

[1, 3]

GPUs 1 and 3

“-1”

str

[0, 1, 2, …]

all available GPUs

-
-

Note

-

When specifying number of devices as an integer devices=k, setting the trainer flag -auto_select_gpus=True will automatically help you find k GPUs that are not -occupied by other processes. This is especially useful when GPUs are configured -to be in “exclusive mode”, such that only one process at a time can access them. -For more details see the trainer guide.

-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/gpu_expert.html b/docs/accelerators/gpu_expert.html deleted file mode 100644 index 6fb1e99..0000000 --- a/docs/accelerators/gpu_expert.html +++ /dev/null @@ -1,887 +0,0 @@ - - - - - - - - - - - - - - GPU training (Expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • GPU training (Expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

GPU training (Expert)

-

Audience: Experts creating new scaling techniques such as Deepspeed or FSDP

-
-

Lightning enables experts focused on researching new ways of optimizing distributed training/inference strategies to create new strategies and plug them into Lightning.

-

For example, Lightning worked closely with the Microsoft team to develop a Deepspeed integration and with the Facebook(Meta) team to develop a FSDP integration.

-
-
-
-

Strategy Registry

-
-

Warning

-

The Strategy Registry is experimental and subject to change.

-
-

Lightning includes a registry that holds information about Training strategies and allows for the registration of new custom strategies.

-

The Strategies are assigned strings that identify them, such as “ddp”, “deepspeed_stage_2_offload”, and so on. -It also returns the optional description and parameters for initialising the Strategy that were defined during registration.

-
# Training with the DDP Strategy with `find_unused_parameters` as False
-trainer = Trainer(strategy="ddp_find_unused_parameters_false", accelerator="gpu", devices=4)
-
-# Training with DeepSpeed ZeRO Stage 3 and CPU Offload
-trainer = Trainer(strategy="deepspeed_stage_3_offload", accelerator="gpu", devices=3)
-
-# Training with the TPU Spawn Strategy with `debug` as True
-trainer = Trainer(strategy="tpu_spawn_debug", accelerator="tpu", devices=8)
-
-
-

Additionally, you can pass your custom registered training strategies to the strategy argument.

-
from pytorch_lightning.strategies import DDPStrategy, StrategyRegistry, CheckpointIO
-
-
-class CustomCheckpointIO(CheckpointIO):
-    def save_checkpoint(self, checkpoint: Dict[str, Any], path: Union[str, Path]) -> None:
-        ...
-
-    def load_checkpoint(self, path: Union[str, Path]) -> Dict[str, Any]:
-        ...
-
-
-custom_checkpoint_io = CustomCheckpointIO()
-
-# Register the DDP Strategy with your custom CheckpointIO plugin
-StrategyRegistry.register(
-    "ddp_custom_checkpoint_io",
-    DDPStrategy,
-    description="DDP Strategy with custom checkpoint io plugin",
-    checkpoint_io=custom_checkpoint_io,
-)
-
-trainer = Trainer(strategy="ddp_custom_checkpoint_io", accelerator="gpu", devices=2)
-
-
-
-
-
orphan
-

-
-
-

What is a Strategy?

-

Strategy controls the model distribution across training, evaluation, and prediction to be used by the Trainer. It can be controlled by passing different -strategy with aliases ("ddp", "ddp_spawn", "deepspeed" and so on) as well as a custom strategy to the strategy parameter for Trainer.

-

The Strategy in PyTorch Lightning handles the following responsibilities:

-
    -
  • Launch and teardown of training processes (if applicable).

  • -
  • Setup communication between processes (NCCL, GLOO, MPI, and so on).

  • -
  • Provide a unified communication interface for reduction, broadcast, and so on.

  • -
  • Owns the LightningModule

  • -
  • Handles/owns optimizers and schedulers.

  • -
-

Strategy also manages the accelerator, precision, and checkpointing plugins.

-Illustration of the Strategy as a composition of the Accelerator and several plugins -

We expose Strategies mainly for expert users that want to extend Lightning for new hardware support or new distributed backends (e.g. a backend not yet supported by PyTorch itself).

-
-
-
-

Enable Different Strategies

-
# Training with the DistributedDataParallel strategy on 4 GPUs
-trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4)
-
-# Training with the custom DistributedDataParallel strategy on 4 GPUs
-trainer = Trainer(strategy=DDPStrategy(...), accelerator="gpu", devices=4)
-
-# Training with the DDP Spawn strategy using auto accelerator selection
-trainer = Trainer(strategy="ddp_spawn", accelerator="auto", devices=4)
-
-# Training with the DeepSpeed strategy on available GPUs
-trainer = Trainer(strategy="deepspeed", accelerator="gpu", devices="auto")
-
-# Training with the DDP strategy using 3 CPU processes
-trainer = Trainer(strategy="ddp", accelerator="cpu", devices=3)
-
-# Training with the DDP Spawn strategy on 8 TPU cores
-trainer = Trainer(strategy="ddp_spawn", accelerator="tpu", devices=8)
-
-# Training with the default IPU strategy on 8 IPUs
-trainer = Trainer(accelerator="ipu", devices=8)
-
-
-
-
-
-

Create a Custom Strategy

-

Expert users may choose to extend an existing strategy by overriding its methods.

-
from pytorch_lightning.strategies import DDPStrategy
-
-
-class CustomDDPStrategy(DDPStrategy):
-    def configure_ddp(self):
-        self.model = MyCustomDistributedDataParallel(
-            self.model,
-            device_ids=...,
-        )
-
-
-

or by subclassing the base class Strategy to create new ones. These custom strategies -can then be passed into the Trainer directly via the strategy parameter.

-
# custom plugins
-trainer = Trainer(strategy=CustomDDPStrategy())
-
-# fully custom accelerator and plugins
-accelerator = MyAccelerator()
-precision_plugin = MyPrecisionPlugin()
-training_strategy = CustomDDPStrategy(accelerator=accelerator, precision_plugin=precision_plugin)
-trainer = Trainer(strategy=training_strategy)
-
-
-

The complete list of built-in strategies is listed below.

-
-
-
-

Available Training Strategies

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

BaguaStrategy

Strategy for training using the Bagua library, with advanced distributed training algorithms and system optimizations.

DDP2Strategy

DDP2 behaves like DP in one node, but synchronization across nodes behaves like in DDP.

DDPFullyShardedStrategy

Plugin for Fully Sharded Data Parallel provided by FairScale.

DDPShardedStrategy

Optimizer and gradient sharded training provided by FairScale.

DDPSpawnShardedStrategy

Optimizer sharded training provided by FairScale.

DDPSpawnStrategy

Spawns processes using the torch.multiprocessing.spawn() method and joins processes after training finishes.

DDPStrategy

Strategy for multi-process single-device training on one or multiple nodes.

DataParallelStrategy

Implements data-parallel training in a single process, i.e., the model gets replicated to each device and each gets a split of the data.

DeepSpeedStrategy

Provides capabilities to run training using the DeepSpeed library, with training optimizations for large billion parameter models.

HorovodStrategy

Plugin for Horovod distributed training integration.

HPUParallelStrategy

Strategy for distributed training on multiple HPU devices.

IPUStrategy

Plugin for training on IPU devices.

ParallelStrategy

Plugin for training with multiple processes in parallel.

SingleDeviceStrategy

Strategy that handles communication on a single device.

SingleHPUStrategy

Strategy for training on single HPU device.

SingleTPUStrategy

Strategy for training on a single TPU device.

Strategy

Base class for all strategies that change the behaviour of the training, validation and test- loop.

TPUSpawnStrategy

Strategy for training multiple TPU devices using the torch_xla.distributed.xla_multiprocessing.spawn() method.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/gpu_faq.html b/docs/accelerators/gpu_faq.html deleted file mode 100644 index acbcd6f..0000000 --- a/docs/accelerators/gpu_faq.html +++ /dev/null @@ -1,766 +0,0 @@ - - - - - - - - - - - - - - GPU training (FAQ) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • GPU training (FAQ)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

GPU training (FAQ)

-
-

How should I adjust the learning rate when using multiple devices?

-

When using distributed training make sure to modify your learning rate according to your effective -batch size.

-

Let’s say you have a batch size of 7 in your dataloader.

-
class LitModel(LightningModule):
-    def train_dataloader(self):
-        return Dataset(..., batch_size=7)
-
-
-

In DDP, DDP_SPAWN, Deepspeed, DDP_SHARDED, or Horovod your effective batch size will be 7 * devices * num_nodes.

-
# effective batch size = 7 * 8
-Trainer(accelerator="gpu", devices=8, strategy="ddp")
-Trainer(accelerator="gpu", devices=8, strategy="ddp_spawn")
-Trainer(accelerator="gpu", devices=8, strategy="ddp_sharded")
-Trainer(accelerator="gpu", devices=8, strategy="horovod")
-
-# effective batch size = 7 * 8 * 10
-Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp")
-Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp_spawn")
-Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp_sharded")
-Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="horovod")
-
-
-

In DDP2 or DP, your effective batch size will be 7 * num_nodes. -The reason is that the full batch is visible to all GPUs on the node when using DDP2.

-
# effective batch size = 7
-Trainer(accelerator="gpu", devices=8, strategy="ddp2")
-Trainer(accelerator="gpu", devices=8, strategy="dp")
-
-# effective batch size = 7 * 10
-Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp2")
-Trainer(accelerator="gpu", devices=8, strategy="dp")
-
-
-
-

Note

-

Huge batch sizes are actually really bad for convergence. Check out: -Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour

-
-
-
-
-

How do I use multiple GPUs on Jupyter or Colab notebooks?

-

To use multiple GPUs on notebooks, use the DP mode.

-
Trainer(accelerator="gpu", devices=4, strategy="dp")
-
-
-

If you want to use other models, please launch your training via the command-shell.

-
-

Note

-

Learn how to access a cloud machine with multiple GPUs in this guide.

-
-
-
- -
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/gpu_intermediate.html b/docs/accelerators/gpu_intermediate.html deleted file mode 100644 index 4792c6d..0000000 --- a/docs/accelerators/gpu_intermediate.html +++ /dev/null @@ -1,1205 +0,0 @@ - - - - - - - - - - - - - - GPU training (Intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • GPU training (Intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

GPU training (Intermediate)

-

Audience: Users looking to train across machines or experiment with different scaling techniques.

-
-
-

Distributed Training strategies

-

Lightning supports multiple ways of doing distributed training.

-
-

-
-
    -
  • Data Parallel (strategy='dp') (multiple-gpus, 1 machine)

  • -
  • DistributedDataParallel (strategy='ddp') (multiple-gpus across many machines (python script based)).

  • -
  • DistributedDataParallel (strategy='ddp_spawn') (multiple-gpus across many machines (spawn based)).

  • -
  • DistributedDataParallel 2 (strategy='ddp2') (DP in a machine, DDP across machines).

  • -
  • Horovod (strategy='horovod') (multi-machine, multi-gpu, configured at runtime)

  • -
  • Bagua (strategy='bagua') (multiple-gpus across many machines with advanced training algorithms)

  • -
-
-

Note

-

If you request multiple GPUs or nodes without setting a mode, DDP Spawn will be automatically used.

-
-

For a deeper understanding of what Lightning is doing, feel free to read this -guide.

-
-

Data Parallel

-

DataParallel (DP) splits a batch across k GPUs. -That is, if you have a batch of 32 and use DP with 2 GPUs, each GPU will process 16 samples, -after which the root node will aggregate the results.

-
-

Warning

-

DP use is discouraged by PyTorch and Lightning. State is not maintained on the replicas created by the -DataParallel wrapper and you may see errors or misbehavior if you assign state to the module -in the forward() or *_step() methods. For the same reason we cannot fully support -Manual Optimization with DP. Use DDP which is more stable and at least 3x faster.

-
-
-

Warning

-

DP only supports scattering and gathering primitive collections of tensors like lists, dicts, etc. -Therefore the transfer_batch_to_device() hook does not apply in -this mode and if you have overridden it, it will not be called.

-
-
# train on 2 GPUs (using DP mode)
-trainer = Trainer(accelerator="gpu", devices=2, strategy="dp")
-
-
-
-
-

Distributed Data Parallel

-

DistributedDataParallel (DDP) works as follows:

-
    -
  1. Each GPU across each node gets its own process.

  2. -
  3. Each GPU gets visibility into a subset of the overall dataset. It will only ever see that subset.

  4. -
  5. Each process inits the model.

  6. -
  7. Each process performs a full forward and backward pass in parallel.

  8. -
  9. The gradients are synced and averaged across all processes.

  10. -
  11. Each process updates its optimizer.

  12. -
-
# train on 8 GPUs (same machine (ie: node))
-trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp")
-
-# train on 32 GPUs (4 nodes)
-trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp", num_nodes=4)
-
-
-

This Lightning implementation of DDP calls your script under the hood multiple times with the correct environment -variables:

-
# example for 3 GPUs DDP
-MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=0 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc
-MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=1 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc
-MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=2 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc
-
-
-

We use DDP this way because ddp_spawn has a few limitations (due to Python and PyTorch):

-
    -
  1. Since .spawn() trains the model in subprocesses, the model on the main process does not get updated.

  2. -
  3. Dataloader(num_workers=N), where N is large, bottlenecks training with DDP… ie: it will be VERY slow or won’t work at all. This is a PyTorch limitation.

  4. -
  5. Forces everything to be picklable.

  6. -
-

There are cases in which it is NOT possible to use DDP. Examples are:

-
    -
  • Jupyter Notebook, Google COLAB, Kaggle, etc.

  • -
  • You have a nested script without a root package

  • -
-

In these situations you should use dp or ddp_spawn instead.

-
-
-

Distributed Data Parallel 2

-

In certain cases, it’s advantageous to use all batches on the same machine instead of a subset. -For instance, you might want to compute a NCE loss where it pays to have more negative samples.

-

In this case, we can use DDP2 which behaves like DP in a machine and DDP across nodes. DDP2 does the following:

-
    -
  1. Copies a subset of the data to each node.

  2. -
  3. Inits a model on each node.

  4. -
  5. Runs a forward and backward pass using DP.

  6. -
  7. Syncs gradients across nodes.

  8. -
  9. Applies the optimizer updates.

  10. -
-
# train on 32 GPUs (4 nodes)
-trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp2", num_nodes=4)
-
-
-
-
-

Distributed Data Parallel Spawn

-

ddp_spawn is exactly like ddp except that it uses .spawn to start the training processes.

-
-

Warning

-

It is STRONGLY recommended to use DDP for speed and performance.

-
-
mp.spawn(self.ddp_train, nprocs=self.num_processes, args=(model,))
-
-
-

If your script does not support being called from the command line (ie: it is nested without a root -project module) you can use the following method:

-
# train on 8 GPUs (same machine (ie: node))
-trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp_spawn")
-
-
-

We STRONGLY discourage this use because it has limitations (due to Python and PyTorch):

-
    -
  1. The model you pass in will not update. Please save a checkpoint and restore from there.

  2. -
  3. Set Dataloader(num_workers=0) or it will bottleneck training.

  4. -
-

ddp is MUCH faster than ddp_spawn. We recommend you

-
    -
  1. Install a top-level module for your project using setup.py

  2. -
-
# setup.py
-#!/usr/bin/env python
-
-from setuptools import setup, find_packages
-
-setup(
-    name="src",
-    version="0.0.1",
-    description="Describe Your Cool Project",
-    author="",
-    author_email="",
-    url="https://github.com/YourSeed",  # REPLACE WITH YOUR OWN GITHUB PROJECT LINK
-    install_requires=["pytorch-lightning"],
-    packages=find_packages(),
-)
-
-
-
    -
  1. Setup your project like so:

  2. -
-
/project
-    /src
-        some_file.py
-        /or_a_folder
-    setup.py
-
-
-
    -
  1. Install as a root-level package

  2. -
-
cd /project
-pip install -e .
-
-
-

You can then call your scripts anywhere

-
cd /project/src
-python some_file.py --accelerator 'gpu' --devices 8 --strategy 'ddp'
-
-
-
-
-

Horovod

-

Horovod allows the same training script to be used for single-GPU, -multi-GPU, and multi-node training.

-

Like Distributed Data Parallel, every process in Horovod operates on a single GPU with a fixed -subset of the data. Gradients are averaged across all GPUs in parallel during the backward pass, -then synchronously applied before beginning the next step.

-

The number of worker processes is configured by a driver application (horovodrun or mpirun). In -the training script, Horovod will detect the number of workers from the environment, and automatically -scale the learning rate to compensate for the increased total batch size.

-

Horovod can be configured in the training script to run with any number of GPUs / processes as follows:

-
# train Horovod on GPU (number of GPUs / machines provided on command-line)
-trainer = Trainer(strategy="horovod", accelerator="gpu", devices=1)
-
-# train Horovod on CPU (number of processes / machines provided on command-line)
-trainer = Trainer(strategy="horovod")
-
-
-

When starting the training job, the driver application will then be used to specify the total -number of worker processes:

-
# run training with 4 GPUs on a single machine
-horovodrun -np 4 python train.py
-
-# run training with 8 GPUs on two machines (4 GPUs each)
-horovodrun -np 8 -H hostname1:4,hostname2:4 python train.py
-
-
-

See the official Horovod documentation for details -on installation and performance tuning.

-
-
-

Bagua

-

Bagua is a deep learning training acceleration framework which supports -multiple advanced distributed training algorithms including:

-
    -
  • Gradient AllReduce for centralized synchronous communication, where gradients are averaged among all workers.

  • -
  • Decentralized SGD for decentralized synchronous communication, where each worker exchanges data with one or a few specific workers.

  • -
  • ByteGrad and QAdam for low precision communication, where data is compressed into low precision before communication.

  • -
  • Asynchronous Model Average for asynchronous communication, where workers are not required to be synchronized in the same iteration in a lock-step style.

  • -
-

By default, Bagua uses Gradient AllReduce algorithm, which is also the algorithm implemented in Distributed Data Parallel and Horovod, -but Bagua can usually produce a higher training throughput due to its backend written in Rust.

-
# train on 4 GPUs (using Bagua mode)
-trainer = Trainer(strategy="bagua", accelerator="gpu", devices=4)
-
-
-

By specifying the algorithm in the BaguaStrategy, you can select more advanced training algorithms featured by Bagua:

-
# train on 4 GPUs, using Bagua Gradient AllReduce algorithm
-trainer = Trainer(
-    strategy=BaguaStrategy(algorithm="gradient_allreduce"),
-    accelerator="gpu",
-    devices=4,
-)
-
-# train on 4 GPUs, using Bagua ByteGrad algorithm
-trainer = Trainer(
-    strategy=BaguaStrategy(algorithm="bytegrad"),
-    accelerator="gpu",
-    devices=4,
-)
-
-# train on 4 GPUs, using Bagua Decentralized SGD
-trainer = Trainer(
-    strategy=BaguaStrategy(algorithm="decentralized"),
-    accelerator="gpu",
-    devices=4,
-)
-
-# train on 4 GPUs, using Bagua Low Precision Decentralized SGD
-trainer = Trainer(
-    strategy=BaguaStrategy(algorithm="low_precision_decentralized"),
-    accelerator="gpu",
-    devices=4,
-)
-
-# train on 4 GPUs, using Asynchronous Model Average algorithm, with a synchronization interval of 100ms
-trainer = Trainer(
-    strategy=BaguaStrategy(algorithm="async", sync_interval_ms=100),
-    accelerator="gpu",
-    devices=4,
-)
-
-
-

To use QAdam, we need to initialize -QAdamOptimizer first:

-
from pytorch_lightning.strategies import BaguaStrategy
-from bagua.torch_api.algorithms.q_adam import QAdamOptimizer
-
-
-class MyModel(pl.LightningModule):
-    ...
-
-    def configure_optimizers(self):
-        # initialize QAdam Optimizer
-        return QAdamOptimizer(self.parameters(), lr=0.05, warmup_steps=100)
-
-
-model = MyModel()
-trainer = Trainer(
-    accelerator="gpu",
-    devices=4,
-    strategy=BaguaStrategy(algorithm="qadam"),
-)
-trainer.fit(model)
-
-
-

Bagua relies on its own launcher to schedule jobs. -Below, find examples using bagua.distributed.launch which follows torch.distributed.launch API:

-
# start training with 8 GPUs on a single node
-python -m bagua.distributed.launch --nproc_per_node=8 train.py
-
-
-

If the ssh service is available with passwordless login on each node, you can launch the distributed job on a -single node with baguarun which has a similar syntax as mpirun. When staring the job, baguarun will -automatically spawn new processes on each of your training node provided by --host_list option and each node in it -is described as an ip address followed by a ssh port.

-
# Run on node1 (or node2) to start training on two nodes (node1 and node2), 8 GPUs per node
-baguarun --host_list hostname1:ssh_port1,hostname2:ssh_port2 --nproc_per_node=8 --master_port=port1 train.py
-
-
-
-

Note

-

You can also start training in the same way as Distributed Data Parallel. However, system optimizations like -Bagua-Net and -Performance autotuning can only be enabled through bagua -launcher. It is worth noting that with Bagua-Net, Distributed Data Parallel can also achieve -better performance without modifying the training script.

-
-

See Bagua Tutorials for more details on installation and advanced features.

-
-
-

DP/DDP2 caveats

-

In DP and DDP2 each GPU within a machine sees a portion of a batch. -DP and ddp2 roughly do the following:

-
def distributed_forward(batch, model):
-    batch = torch.Tensor(32, 8)
-    gpu_0_batch = batch[:8]
-    gpu_1_batch = batch[8:16]
-    gpu_2_batch = batch[16:24]
-    gpu_3_batch = batch[24:]
-
-    y_0 = model_copy_gpu_0(gpu_0_batch)
-    y_1 = model_copy_gpu_1(gpu_1_batch)
-    y_2 = model_copy_gpu_2(gpu_2_batch)
-    y_3 = model_copy_gpu_3(gpu_3_batch)
-
-    return [y_0, y_1, y_2, y_3]
-
-
-

So, when Lightning calls any of the training_step, validation_step, test_step -you will only be operating on one of those pieces.

-
# the batch here is a portion of the FULL batch
-def training_step(self, batch, batch_idx):
-    y_0 = batch
-
-
-

For most metrics, this doesn’t really matter. However, if you want -to add something to your computational graph (like softmax) -using all batch parts you can use the training_step_end step.

-
def training_step_end(self, outputs):
-    # only use when  on dp
-    outputs = torch.cat(outputs, dim=1)
-    softmax = softmax(outputs, dim=1)
-    out = softmax.mean()
-    return out
-
-
-

In pseudocode, the full sequence is:

-
# get data
-batch = next(dataloader)
-
-# copy model and data to each gpu
-batch_splits = split_batch(batch, num_gpus)
-models = copy_model_to_gpus(model)
-
-# in parallel, operate on each batch chunk
-all_results = []
-for gpu_num in gpus:
-    batch_split = batch_splits[gpu_num]
-    gpu_model = models[gpu_num]
-    out = gpu_model(batch_split)
-    all_results.append(out)
-
-# use the full batch for something like softmax
-full_out = model.training_step_end(all_results)
-
-
-

To illustrate why this is needed, let’s look at DataParallel

-
def training_step(self, batch, batch_idx):
-    x, y = batch
-    y_hat = self(batch)
-
-    # on dp or ddp2 if we did softmax now it would be wrong
-    # because batch is actually a piece of the full batch
-    return y_hat
-
-
-def training_step_end(self, step_output):
-    # step_output has outputs of each part of the batch
-
-    # do softmax here
-    outputs = torch.cat(outputs, dim=1)
-    softmax = softmax(outputs, dim=1)
-    out = softmax.mean()
-
-    return out
-
-
-

If training_step_end is defined it will be called regardless of TPU, DP, DDP, etc… which means -it will behave the same regardless of the backend.

-

Validation and test step have the same option when using DP.

-
def validation_step_end(self, step_output):
-    ...
-
-
-def test_step_end(self, step_output):
-    ...
-
-
-
-
-

Distributed and 16-bit precision

-

Due to an issue with Apex and DataParallel (PyTorch and NVIDIA issue), Lightning does -not allow 16-bit and DP training. We tried to get this to work, but it’s an issue on their end.

-

Below are the possible configurations we support.

- -------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

1 GPU

1+ GPUs

DP

DDP

16-bit

command

Y

Trainer(accelerator=”gpu”, devices=1)

Y

Y

Trainer(accelerator=”gpu”, devices=1, precision=16)

Y

Y

Trainer(accelerator=”gpu”, devices=k, strategy=’dp’)

Y

Y

Trainer(accelerator=”gpu”, devices=k, strategy=’ddp’)

Y

Y

Y

Trainer(accelerator=”gpu”, devices=k, strategy=’ddp’, precision=16)

-
-
-

Implement Your Own Distributed (DDP) training

-

If you need your own way to init PyTorch DDP you can override pytorch_lightning.strategies.ddp.DDPStrategy.init_dist_connection().

-

If you also need to use your own DDP implementation, override pytorch_lightning.strategies.ddp.DDPStrategy.configure_ddp().

-
-
-
-
-

Torch Distributed Elastic

-

Lightning supports the use of Torch Distributed Elastic to enable fault-tolerant and elastic distributed job scheduling. To use it, specify the ‘ddp’ or ‘ddp2’ backend and the number of GPUs you want to use in the trainer.

-
Trainer(accelerator="gpu", devices=8, strategy="ddp")
-
-
-

To launch a fault-tolerant job, run the following on all nodes.

-
python -m torch.distributed.run
-        --nnodes=NUM_NODES
-        --nproc_per_node=TRAINERS_PER_NODE
-        --rdzv_id=JOB_ID
-        --rdzv_backend=c10d
-        --rdzv_endpoint=HOST_NODE_ADDR
-        YOUR_LIGHTNING_TRAINING_SCRIPT.py (--arg1 ... train script args...)
-
-
-

To launch an elastic job, run the following on at least MIN_SIZE nodes and at most MAX_SIZE nodes.

-
python -m torch.distributed.run
-        --nnodes=MIN_SIZE:MAX_SIZE
-        --nproc_per_node=TRAINERS_PER_NODE
-        --rdzv_id=JOB_ID
-        --rdzv_backend=c10d
-        --rdzv_endpoint=HOST_NODE_ADDR
-        YOUR_LIGHTNING_TRAINING_SCRIPT.py (--arg1 ... train script args...)
-
-
-

See the official Torch Distributed Elastic documentation for details -on installation and more use cases.

-
-
-

Optimize multi-machine communication

-

By default, Lightning will select the nccl backend over gloo when running on GPUs. -Find more information about PyTorch’s supported backends here.

-

Lightning allows explicitly specifying the backend via the process_group_backend constructor argument on the relevant Strategy classes. By default, Lightning will select the appropriate process group backend based on the hardware used.

-
from pytorch_lightning.strategies import DDPStrategy
-
-# Explicitly specify the process group backend if you choose to
-ddp = DDPStrategy(process_group_backend="nccl")
-
-# Configure the strategy on the Trainer
-trainer = Trainer(strategy=ddp, accelerator="gpu", devices=8)
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/hpu.html b/docs/accelerators/hpu.html deleted file mode 100644 index 767c74b..0000000 --- a/docs/accelerators/hpu.html +++ /dev/null @@ -1,731 +0,0 @@ - - - - - - - - - - - - - - Accelerator: HPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: HPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/hpu_basic.html b/docs/accelerators/hpu_basic.html deleted file mode 100644 index ee508e5..0000000 --- a/docs/accelerators/hpu_basic.html +++ /dev/null @@ -1,757 +0,0 @@ - - - - - - - - - - - - - - Accelerator: HPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: HPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Accelerator: HPU training

-

Audience: Users looking to save money and run large models faster using single or multiple Gaudi devices.

-
-
-

What is an HPU?

-

Habana® Gaudi® AI Processor (HPU) training processors are built on a heterogeneous architecture with a cluster of fully programmable Tensor Processing Cores (TPC) along with its associated development tools and libraries, and a configurable Matrix Math engine.

-

The TPC core is a VLIW SIMD processor with an instruction set and hardware tailored to serve training workloads efficiently. -The Gaudi memory architecture includes on-die SRAM and local memories in each TPC and, -Gaudi is the first DL training processor that has integrated RDMA over Converged Ethernet (RoCE v2) engines on-chip.

-

On the software side, the PyTorch Habana bridge interfaces between the framework and SynapseAI software stack to enable the execution of deep learning models on the Habana Gaudi device.

-

Gaudi offers a substantial price/performance advantage – so you get to do more deep learning training while spending less.

-

For more information, check out Gaudi Architecture and Gaudi Developer Docs.

-
-
-
-

Run on 1 Gaudi

-

To enable PyTorch Lightning to utilize the HPU accelerator, simply provide accelerator="hpu" parameter to the Trainer class.

-
trainer = Trainer(accelerator="hpu", devices=1)
-
-
-
-
-
-

Run on multiple Gaudis

-

The devices=8 and accelerator="hpu" parameters to the Trainer class enables the Habana accelerator for distributed training with 8 Gaudis. -It uses HPUParallelStrategy internally which is based on DDP strategy with the addition of Habana’s collective communication library (HCCL) to support scale-up within a node and scale-out across multiple nodes.

-
trainer = Trainer(devices=8, accelerator="hpu")
-
-
-
-
-
-

Select Gaudis automatically

-

Lightning can automatically detect the number of Gaudi devices to run on. This setting is enabled by default if the devices argument is missing.

-
# equivalent
-trainer = Trainer(accelerator="hpu")
-trainer = Trainer(accelerator="hpu", devices="auto")
-
-
-
-
-
-

How to access HPUs

-

To use HPUs, you must have access to a system with HPU devices.

-
-

AWS

-

You can either use Gaudi-based AWS EC2 DL1 instances or Supermicro X12 Gaudi server to get access to HPUs.

-

Check out the Get Started Guide with AWS and Habana.

-
-
-
-
-

Known limitations

-
    -
  • Multiple optimizers are not supported.

  • -
  • Habana dataloader is not supported.

  • -
  • DeviceStatsMonitor is not supported.

  • -
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/hpu_intermediate.html b/docs/accelerators/hpu_intermediate.html deleted file mode 100644 index 1ab7684..0000000 --- a/docs/accelerators/hpu_intermediate.html +++ /dev/null @@ -1,744 +0,0 @@ - - - - - - - - - - - - - - Accelerator: HPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: HPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Accelerator: HPU training

-

Audience: Gaudi chip users looking to save memory and scale models with mixed-precision training.

-
-
-

Enable Mixed Precision

-

Lightning also allows mixed precision training with HPUs. -By default, HPU training will use 32-bit precision. To enable mixed precision, set the precision flag.

-
trainer = Trainer(devices=1, accelerator="hpu", precision=16)
-
-
-
-
-
-

Customize Mixed Precision

-

Internally, HPUPrecisionPlugin uses the Habana Mixed Precision (HMP) package to enable mixed precision training.

-

You can execute the ops in FP32 or BF16 precision. The HMP package modifies the Python operators to add the appropriate cast operations for the arguments before execution. -The default settings enable users to enable mixed precision training with minimal code easily.

-

In addition to the default settings in HMP, users also have the option of overriding these defaults and providing their -BF16 and FP32 operator lists by passing them as parameter to HPUPrecisionPlugin.

-

The below snippet shows an example model using MNIST with a single Habana Gaudi device and making use of HMP by overriding the default parameters. -This enables advanced users to provide their own BF16 and FP32 operator list instead of using the HMP defaults.

-
import pytorch_lightning as pl
-from pytorch_lightning.plugins import HPUPrecisionPlugin
-
-# Initialize a trainer with HPU accelerator for HPU strategy for single device,
-# with mixed precision using overidden HMP settings
-trainer = pl.Trainer(
-    accelerator="hpu",
-    devices=1,
-    # Optional Habana mixed precision params to be set
-    # Checkout `pl_examples/hpu_examples/simple_mnist/ops_bf16_mnist.txt` for the format
-    plugins=[
-        HPUPrecisionPlugin(
-            precision=16,
-            opt_level="O1",
-            verbose=False,
-            bf16_file_path="ops_bf16_mnist.txt",
-            fp32_file_path="ops_fp32_mnist.txt",
-        )
-    ],
-)
-
-# Init our model
-model = LitClassifier()
-# Init the data
-dm = MNISTDataModule(batch_size=batch_size)
-
-# Train the model ⚡
-trainer.fit(model, datamodule=dm)
-
-
-

For more details, please refer to PyTorch Mixed Precision Training on Gaudi.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/ipu.html b/docs/accelerators/ipu.html deleted file mode 100644 index e7964d4..0000000 --- a/docs/accelerators/ipu.html +++ /dev/null @@ -1,741 +0,0 @@ - - - - - - - - - - - - - - Accelerator: IPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: IPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/ipu_advanced.html b/docs/accelerators/ipu_advanced.html deleted file mode 100644 index 6a91759..0000000 --- a/docs/accelerators/ipu_advanced.html +++ /dev/null @@ -1,811 +0,0 @@ - - - - - - - - - - - - - - Accelerator: IPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: IPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Accelerator: IPU training

-

Audience: Users looking to customize IPU training for massive models.

-
-
-

Advanced IPU options

-

IPUs provide further optimizations to speed up training. By using the IPUStrategy we can set the device_iterations, which controls the number of iterations run directly on the IPU devices before returning to the host. Increasing the number of on-device iterations will improve throughput, as there is less device to host communication required.

-
-

Note

-

When using model parallelism, it is a hard requirement to increase the number of device iterations to ensure we fully saturate the devices via micro-batching. see Model parallelism for more information.

-
-
import pytorch_lightning as pl
-from pytorch_lightning.strategies import IPUStrategy
-
-model = MyLightningModule()
-trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(device_iterations=32))
-trainer.fit(model)
-
-
-

Note that by default we return the last device iteration loss. You can override this by passing in your own poptorch.Options and setting the AnchorMode as described in the PopTorch documentation.

-
import poptorch
-import pytorch_lightning as pl
-from pytorch_lightning.strategies import IPUStrategy
-
-model = MyLightningModule()
-inference_opts = poptorch.Options()
-inference_opts.deviceIterations(32)
-
-training_opts = poptorch.Options()
-training_opts.anchorMode(poptorch.AnchorMode.All)
-training_opts.deviceIterations(32)
-
-trainer = Trainer(
-    accelerator="ipu", devices=8, strategy=IPUStrategy(inference_opts=inference_opts, training_opts=training_opts)
-)
-trainer.fit(model)
-
-
-

You can also override all options by passing the poptorch.Options to the plugin. See PopTorch options documentation for more information.

-
-
-
-

Model parallelism

-

Due to the IPU architecture, larger models should be parallelized across IPUs by design. Currently PopTorch provides the capabilities via annotations as described in parallel execution strategies.

-

Below is an example using the block annotation in a LightningModule.

-
-

Note

-

Currently, when using model parallelism we do not infer the number of IPUs required for you. This is done via the annotations themselves. If you specify 4 different IDs when defining Blocks, this means your model will be split onto 4 different IPUs.

-

This is also mutually exclusive with the Trainer flag. In other words, if your model is split onto 2 IPUs and you set Trainer(accelerator="ipu", devices=4) this will require 8 IPUs in total: data parallelism will be used to replicate the two-IPU model 4 times.

-

When pipelining the model you must also increase the device_iterations to ensure full data saturation of the devices data, i.e whilst one device in the model pipeline processes a batch of data, the other device can start on the next batch. For example if the model is split onto 4 IPUs, we require device_iterations to be at-least 4.

-
-
import pytorch_lightning as pl
-import poptorch
-
-
-class MyLightningModule(pl.LightningModule):
-    def __init__(self):
-        super().__init__()
-        # This will place layer1, layer2+layer3, layer4, softmax on different IPUs at runtime.
-        # BeginBlock will start a new id for all layers within this block
-        self.layer1 = poptorch.BeginBlock(torch.nn.Linear(5, 10), ipu_id=0)
-
-        # This layer starts a new block,
-        # adding subsequent layers to this current block at runtime
-        # till the next block has been declared
-        self.layer2 = poptorch.BeginBlock(torch.nn.Linear(10, 5), ipu_id=1)
-        self.layer3 = torch.nn.Linear(5, 5)
-
-        # Create new blocks
-        self.layer4 = poptorch.BeginBlock(torch.nn.Linear(5, 5), ipu_id=2)
-        self.softmax = poptorch.BeginBlock(torch.nn.Softmax(dim=1), ipu_id=3)
-
-    ...
-
-
-model = MyLightningModule()
-trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(device_iterations=20))
-trainer.fit(model)
-
-
-

You can also use the block context manager within the forward function, or any of the step functions.

-
import pytorch_lightning as pl
-import poptorch
-
-
-class MyLightningModule(pl.LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.layer1 = torch.nn.Linear(5, 10)
-        self.layer2 = torch.nn.Linear(10, 5)
-        self.layer3 = torch.nn.Linear(5, 5)
-        self.layer4 = torch.nn.Linear(5, 5)
-
-        self.act = torch.nn.ReLU()
-        self.softmax = torch.nn.Softmax(dim=1)
-
-    def forward(self, x):
-
-        with poptorch.Block(ipu_id=0):
-            x = self.act(self.layer1(x))
-
-        with poptorch.Block(ipu_id=1):
-            x = self.act(self.layer2(x))
-
-        with poptorch.Block(ipu_id=2):
-            x = self.act(self.layer3(x))
-            x = self.act(self.layer4(x))
-
-        with poptorch.Block(ipu_id=3):
-            x = self.softmax(x)
-        return x
-
-    ...
-
-
-model = MyLightningModule()
-trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(device_iterations=20))
-trainer.fit(model)
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/ipu_basic.html b/docs/accelerators/ipu_basic.html deleted file mode 100644 index bf22ee9..0000000 --- a/docs/accelerators/ipu_basic.html +++ /dev/null @@ -1,742 +0,0 @@ - - - - - - - - - - - - - - Accelerator: IPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: IPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Accelerator: IPU training

-

Audience: Users looking to save money and run large models faster using single or multiple IPU devices.

-
-
-

What is an IPU?

-

The Graphcore Intelligence Processing Unit (IPU), built for Artificial Intelligence and Machine Learning, consists of many individual cores, called tiles, allowing highly parallel computation. Due to the high bandwidth between tiles, IPUs facilitate machine learning loads where parallelization is essential. Because computation is heavily parallelized,

-

IPUs operate in a different way to conventional accelerators such as CPU/GPUs. IPUs do not require large batch sizes for maximum parallelization, can provide optimizations across the compiled graph and rely on model parallelism to fully utilize tiles for larger models.

-

IPUs are used to build IPU-PODs, rack-based systems of IPU-Machines for larger workloads. See the IPU Architecture for more information.

-

See the Graphcore Glossary for the definitions of other IPU-specific terminology.

-
-

Note

-

IPU support is experimental and a work in progress (see Known limitations). If you run into any problems, please leave an issue.

-
-
-
-
-

Run on 1 IPU

-

To use a single IPU, set the accelerator and devices argument.

-
trainer = pl.Trainer(accelerator="ipu", devices=1)
-
-
-
-
-
-

Run on multiple IPUs

-

To use multiple IPUs set the devices to a number that is a power of 2 (i.e: 2, 4, 8, 16, …)

-
trainer = pl.Trainer(accelerator="ipu", devices=8)
-
-
-
-
-
-

How to access IPUs

-

To use IPUs you must have access to a system with IPU devices. To get access see get started.

-

You must ensure that the IPU system has enabled the PopART and Poplar packages from the SDK. Instructions are in the Get Started guide for your IPU system, on the Graphcore documents portal.

-
-
-
-

Known limitations

-

Currently there are some known limitations that are being addressed in the near future to make the experience seamless when moving from different devices.

-

Please see the MNIST example which displays most of the limitations and how to overcome them till they are resolved.

-
    -
  • self.log is not supported in the training_step, validation_step, test_step or predict_step. This is due to the step function being traced and sent to the IPU devices. We’re actively working on fixing this

  • -
  • Multiple optimizers are not supported. training_step only supports returning one loss from the training_step function as a result

  • -
  • Since the step functions are traced, branching logic or any form of primitive values are traced into constants. Be mindful as this could lead to errors in your custom code

  • -
  • Clipping gradients is not supported

  • -
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/ipu_intermediate.html b/docs/accelerators/ipu_intermediate.html deleted file mode 100644 index 246fd36..0000000 --- a/docs/accelerators/ipu_intermediate.html +++ /dev/null @@ -1,739 +0,0 @@ - - - - - - - - - - - - - - Accelerator: IPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: IPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Accelerator: IPU training

-

Audience: IPU users looking to increase performance via mixed precision and analysis tools.

-
-
-

Mixed precision & 16 bit precision

-

Lightning also supports training in mixed precision with IPUs. -By default, IPU training will use 32-bit precision. To enable mixed precision, -set the precision flag.

-
-

Note

-

Currently there is no dynamic scaling of the loss with mixed precision training.

-
-
import pytorch_lightning as pl
-
-model = MyLightningModule()
-trainer = pl.Trainer(accelerator="ipu", devices=8, precision=16)
-trainer.fit(model)
-
-
-

You can also use pure 16-bit training, where the weights are also in 16-bit precision.

-
import pytorch_lightning as pl
-from pytorch_lightning.strategies import IPUStrategy
-
-model = MyLightningModule()
-model = model.half()
-trainer = pl.Trainer(accelerator="ipu", devices=8, precision=16)
-trainer.fit(model)
-
-
-
-
-
-

PopVision Graph Analyser

-
-PopVision Graph Analyser -
-

Lightning supports integration with the PopVision Graph Analyser Tool. This helps to look at utilization of IPU devices and provides helpful metrics during the lifecycle of your trainer. Once you have gained access, The PopVision Graph Analyser Tool can be downloaded via the GraphCore download website.

-

Lightning supports dumping all reports to a directory to open using the tool.

-
import pytorch_lightning as pl
-from pytorch_lightning.strategies import IPUStrategy
-
-model = MyLightningModule()
-trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(autoreport_dir="report_dir/"))
-trainer.fit(model)
-
-
-

This will dump all reports to report_dir/ which can then be opened using the Graph Analyser Tool, see Opening Reports.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/tpu.html b/docs/accelerators/tpu.html deleted file mode 100644 index 7bed7d0..0000000 --- a/docs/accelerators/tpu.html +++ /dev/null @@ -1,751 +0,0 @@ - - - - - - - - - - - - - - Accelerator: TPU training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Accelerator: TPU training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/tpu_advanced.html b/docs/accelerators/tpu_advanced.html deleted file mode 100644 index c24e042..0000000 --- a/docs/accelerators/tpu_advanced.html +++ /dev/null @@ -1,748 +0,0 @@ - - - - - - - - - - - - - - TPU training (Advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • TPU training (Advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

TPU training (Advanced)

-

Audience: Users looking to apply advanced performance techniques to TPU training.

-
-
-

Weight Sharing/Tying

-

Weight Tying/Sharing is a technique where in the module weights are shared among two or more layers. -This is a common method to reduce memory consumption and is utilized in many State of the Art -architectures today.

-

PyTorch XLA requires these weights to be tied/shared after moving the model -to the TPU device. To support this requirement Lightning provides a model hook which is -called after the model is moved to the device. Any weights that require to be tied should -be done in the on_post_move_to_device model hook. This will ensure that the weights -among the modules are shared and not copied.

-

PyTorch Lightning has an inbuilt check which verifies that the model parameter lengths -match once the model is moved to the device. If the lengths do not match Lightning -throws a warning message.

-

Example:

-
from pytorch_lightning.core.lightning import LightningModule
-from torch import nn
-from pytorch_lightning.trainer.trainer import Trainer
-
-
-class WeightSharingModule(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.layer_1 = nn.Linear(32, 10, bias=False)
-        self.layer_2 = nn.Linear(10, 32, bias=False)
-        self.layer_3 = nn.Linear(32, 10, bias=False)
-        # TPU shared weights are copied independently
-        # on the XLA device and this line won't have any effect.
-        # However, it works fine for CPU and GPU.
-        self.layer_3.weight = self.layer_1.weight
-
-    def forward(self, x):
-        x = self.layer_1(x)
-        x = self.layer_2(x)
-        x = self.layer_3(x)
-        return x
-
-    def on_post_move_to_device(self):
-        # Weights shared after the model has been moved to TPU Device
-        self.layer_3.weight = self.layer_1.weight
-
-
-model = WeightSharingModule()
-trainer = Trainer(max_epochs=1, accelerator="tpu", devices=8)
-
-
-

See XLA Documentation

-
-
-
-

XLA

-

XLA is the library that interfaces PyTorch with the TPUs. -For more information check out XLA.

-

Guide for troubleshooting XLA

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/tpu_basic.html b/docs/accelerators/tpu_basic.html deleted file mode 100644 index 1acb332..0000000 --- a/docs/accelerators/tpu_basic.html +++ /dev/null @@ -1,896 +0,0 @@ - - - - - - - - - - - - - - TPU training (Basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • TPU training (Basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

TPU training (Basic)

-

Audience: Users looking to train on single or multiple TPU cores.

-
-
-

-
-

Lightning supports running on TPUs. At this moment, TPUs are available -on Google Cloud (GCP), Google Colab and Kaggle Environments. For more information on TPUs -watch this video.

-
-
-

What is a TPU?

-

Tensor Processing Unit (TPU) is an AI accelerator application-specific integrated circuit (ASIC) developed by Google specifically for neural networks.

-

A TPU has 8 cores where each core is optimized for 128x128 matrix multiplies. In general, a single TPU is about as fast as 5 V100 GPUs!

-

A TPU pod hosts many TPUs on it. Currently, TPU v3 Pod has up to 2048 TPU cores and 32 TiB of memory! -You can request a full pod from Google cloud or a “slice” which gives you -some subset of those 2048 cores.

-
-
-
-

Run on 1 TPU core

-

Enable the following Trainer arguments to run on 1 TPU.

-
trainer = Trainer(accelerator="tpu", devices=1)
-
-
-
-
-
-

Run on multiple TPU cores

-

For multiple TPU cores, change the value of the devices flag.

-
trainer = Trainer(accelerator="tpu", devices=8)
-
-
-
-
-
-

Run on a specific TPU core

-

To run on a specific core, specify the index of the TPU core.

-
trainer = pl.Trainer(accelerator="tpu", devices=[5])
-
-
-

This example runs on the 5th core, not on five cores.

-
-
-
-

How to access TPUs

-

To access TPUs, there are three main ways.

-
-

Google Colab

-

Colab is like a jupyter notebook with a free GPU or TPU -hosted on GCP.

-

To get a TPU on colab, follow these steps:

-
    -
  1. Go to https://colab.research.google.com/.

  2. -
  3. Click “new notebook” (bottom right of pop-up).

  4. -
  5. Click runtime > change runtime settings. Select Python 3, and hardware accelerator “TPU”. -This will give you a TPU with 8 cores.

  6. -
  7. Next, insert this code into the first cell and execute. -This will install the xla library that interfaces between PyTorch and the TPU.

    -
    !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.9-cp37-cp37m-linux_x86_64.whl
    -
    -
    -
  8. -
  9. Once the above is done, install PyTorch Lightning.

    -
    !pip install pytorch-lightning
    -
    -
    -
  10. -
  11. Then set up your LightningModule as normal.

  12. -
-
-
-

Google Cloud (GCP)

-

?

-
-
-

Kaggle

-

For starting Kaggle projects with TPUs, refer to this kernel.

-
-
-
-
-

Optimize Performance

-

The TPU was designed for specific workloads and operations to carry out large volumes of matrix multiplication, -convolution operations and other commonly used ops in applied deep learning. -The specialization makes it a strong choice for NLP tasks, sequential convolutional networks, and under low precision operation. -There are cases in which training on TPUs is slower when compared with GPUs, for possible reasons listed:

-
    -
  • Too small batch size.

  • -
  • Explicit evaluation of tensors during training, e.g. tensor.item()

  • -
  • Tensor shapes (e.g. model inputs) change often during training.

  • -
  • Limited resources when using TPU’s with PyTorch Link

  • -
  • XLA Graph compilation during the initial steps Reference

  • -
  • Some tensor ops are not fully supported on TPU, or not supported at all. These operations will be performed on CPU (context switch).

  • -
  • PyTorch integration is still experimental. Some performance bottlenecks may simply be the result of unfinished implementation.

  • -
-

The official PyTorch XLA performance guide -has more detailed information on how PyTorch code can be optimized for TPU. In particular, the -metrics report allows -one to identify operations that lead to context switching.

-
-
-
-

FAQ

-

XLA configuration is missing

-
File "/usr/local/lib/python3.8/dist-packages/torch_xla/core/xla_model.py", line 18, in <lambda>
-    _DEVICES = xu.LazyProperty(lambda: torch_xla._XLAC._xla_get_devices())
-RuntimeError: tensorflow/compiler/xla/xla_client/computation_client.cc:273 : Missing XLA configuration
-Traceback (most recent call last):
-...
-File "/home/kaushikbokka/pytorch-lightning/pytorch_lightning/utilities/device_parser.py", line 125, in parse_tpu_cores
-    raise MisconfigurationException('No TPU devices were found.')
-pytorch_lightning.utilities.exceptions.MisconfigurationException: No TPU devices were found.
-
-
-

This means the system is missing XLA configuration. You would need to set up XRT TPU device configuration.

-

For TPUVM architecture, you could set it in your terminal by:

-
export XRT_TPU_CONFIG="localservice;0;localhost:51011"
-
-
-

And for the old TPU + 2VM architecture, you could set it by:

-
export TPU_IP_ADDRESS=10.39.209.42  # You could get the IP Address in the GCP TPUs section
-export XRT_TPU_CONFIG="tpu_worker;0;$TPU_IP_ADDRESS:8470"
-
-
-
-

How to clear up the programs using TPUs in the background

-
lsof -w /lib/libtpu.so | grep "python" |  awk '{print $2}' | xargs -r kill -9
-
-
-

Sometimes, there can still be old programs running on the TPUs, which would make the TPUs unavailable to use. You could use the above command in the terminal to kill the running processes.

-
-

Replication issue

-
File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 200, in set_replication
-    replication_devices = xla_replication_devices(devices)
-File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 187, in xla_replication_devices
-    .format(len(local_devices), len(kind_devices)))
-RuntimeError: Cannot replicate if number of devices (1) is different from 8
-
-
-

This error is raised when the XLA device is called outside the spawn process. Internally in TPUSpawn Strategy for training on multiple tpu cores, we use XLA’s xmp.spawn. -Don’t use xm.xla_device() while working on Lightning + TPUs!

-
-

Unsupported datatype transfer to TPU

-
File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 205, in _for_each_instance_rewrite
-    v = _for_each_instance_rewrite(result.__dict__[k], select_fn, fn, rwmap)
-File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 206, in _for_each_instance_rewrite
-    result.__dict__[k] = v
-TypeError: 'mappingproxy' object does not support item assignment
-
-
-

PyTorch XLA only supports Tensor objects for CPU to TPU data transfer. Might cause issues if the User is trying to send some non-tensor objects through the DataLoader or during saving states.

-
-

Using `tpu_spawn_debug` Strategy alias

-
import pytorch_lightning as pl
-
-my_model = MyLightningModule()
-trainer = pl.Trainer(accelerator="tpu", devices=8, strategy="tpu_spawn_debug")
-trainer.fit(my_model)
-
-
-

Example Metrics report:

-
Metric: CompileTime
-    TotalSamples: 202
-    Counter: 06m09s401ms746.001us
-    ValueRate: 778ms572.062us / second
-    Rate: 0.425201 / second
-    Percentiles: 1%=001ms32.778us; 5%=001ms61.283us; 10%=001ms79.236us; 20%=001ms110.973us; 50%=001ms228.773us; 80%=001ms339.183us; 90%=001ms434.305us; 95%=002ms921.063us; 99%=21s102ms853.173us
-
-
-

A lot of PyTorch operations aren’t lowered to XLA, which could lead to significant slowdown of the training process. -These operations are moved to the CPU memory and evaluated, and then the results are transferred back to the XLA device(s). -By using the tpu_spawn_debug Strategy, users could create a metrics report to diagnose issues.

-

The report includes things like (XLA Reference):

-
    -
  • how many times we issue XLA compilations and time spent on issuing.

  • -
  • how many times we execute and time spent on execution

  • -
  • how many device data handles we create/destroy etc.

  • -
-
-

TPU Pod Training Startup script

-

All TPU VMs in a Pod setup are required to access the model code and data. -One easy way to achieve this is to use the following startup script when creating the TPU VM pod. -It will perform the data downloading on all TPU VMs. Note that you need to export the corresponding environment variables following the instruction in Create TPU Node.

-
gcloud alpha compute tpus tpu-vm create ${TPU_NAME} --zone ${ZONE} --project ${PROJECT_ID} --accelerator-type v3-32 --version ${RUNTIME_VERSION} --metadata startup-script=setup.py
-
-
-

Then users could ssh to any TPU worker, e.g. worker 0, check if data/model downloading is finished and -start the training after generating the ssh-keys to ssh between VM workers on a pod:

-
python3 -m torch_xla.distributed.xla_dist --tpu=$TPU_NAME -- python3 train.py --max_epochs=5 --batch_size=32
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/tpu_faq.html b/docs/accelerators/tpu_faq.html deleted file mode 100644 index b65e83b..0000000 --- a/docs/accelerators/tpu_faq.html +++ /dev/null @@ -1,896 +0,0 @@ - - - - - - - - - - - - - - TPU training (Basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • TPU training (Basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

TPU training (Basic)

-

Audience: Users looking to train on single or multiple TPU cores.

-
-
-

-
-

Lightning supports running on TPUs. At this moment, TPUs are available -on Google Cloud (GCP), Google Colab and Kaggle Environments. For more information on TPUs -watch this video.

-
-
-

What is a TPU?

-

Tensor Processing Unit (TPU) is an AI accelerator application-specific integrated circuit (ASIC) developed by Google specifically for neural networks.

-

A TPU has 8 cores where each core is optimized for 128x128 matrix multiplies. In general, a single TPU is about as fast as 5 V100 GPUs!

-

A TPU pod hosts many TPUs on it. Currently, TPU v3 Pod has up to 2048 TPU cores and 32 TiB of memory! -You can request a full pod from Google cloud or a “slice” which gives you -some subset of those 2048 cores.

-
-
-
-

Run on 1 TPU core

-

Enable the following Trainer arguments to run on 1 TPU.

-
trainer = Trainer(accelerator="tpu", devices=1)
-
-
-
-
-
-

Run on multiple TPU cores

-

For multiple TPU cores, change the value of the devices flag.

-
trainer = Trainer(accelerator="tpu", devices=8)
-
-
-
-
-
-

Run on a specific TPU core

-

To run on a specific core, specify the index of the TPU core.

-
trainer = pl.Trainer(accelerator="tpu", devices=[5])
-
-
-

This example runs on the 5th core, not on five cores.

-
-
-
-

How to access TPUs

-

To access TPUs, there are three main ways.

-
-

Google Colab

-

Colab is like a jupyter notebook with a free GPU or TPU -hosted on GCP.

-

To get a TPU on colab, follow these steps:

-
    -
  1. Go to https://colab.research.google.com/.

  2. -
  3. Click “new notebook” (bottom right of pop-up).

  4. -
  5. Click runtime > change runtime settings. Select Python 3, and hardware accelerator “TPU”. -This will give you a TPU with 8 cores.

  6. -
  7. Next, insert this code into the first cell and execute. -This will install the xla library that interfaces between PyTorch and the TPU.

    -
    !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.9-cp37-cp37m-linux_x86_64.whl
    -
    -
    -
  8. -
  9. Once the above is done, install PyTorch Lightning.

    -
    !pip install pytorch-lightning
    -
    -
    -
  10. -
  11. Then set up your LightningModule as normal.

  12. -
-
-
-

Google Cloud (GCP)

-

?

-
-
-

Kaggle

-

For starting Kaggle projects with TPUs, refer to this kernel.

-
-
-
-
-

Optimize Performance

-

The TPU was designed for specific workloads and operations to carry out large volumes of matrix multiplication, -convolution operations and other commonly used ops in applied deep learning. -The specialization makes it a strong choice for NLP tasks, sequential convolutional networks, and under low precision operation. -There are cases in which training on TPUs is slower when compared with GPUs, for possible reasons listed:

-
    -
  • Too small batch size.

  • -
  • Explicit evaluation of tensors during training, e.g. tensor.item()

  • -
  • Tensor shapes (e.g. model inputs) change often during training.

  • -
  • Limited resources when using TPU’s with PyTorch Link

  • -
  • XLA Graph compilation during the initial steps Reference

  • -
  • Some tensor ops are not fully supported on TPU, or not supported at all. These operations will be performed on CPU (context switch).

  • -
  • PyTorch integration is still experimental. Some performance bottlenecks may simply be the result of unfinished implementation.

  • -
-

The official PyTorch XLA performance guide -has more detailed information on how PyTorch code can be optimized for TPU. In particular, the -metrics report allows -one to identify operations that lead to context switching.

-
-
-
-

FAQ

-

XLA configuration is missing

-
File "/usr/local/lib/python3.8/dist-packages/torch_xla/core/xla_model.py", line 18, in <lambda>
-    _DEVICES = xu.LazyProperty(lambda: torch_xla._XLAC._xla_get_devices())
-RuntimeError: tensorflow/compiler/xla/xla_client/computation_client.cc:273 : Missing XLA configuration
-Traceback (most recent call last):
-...
-File "/home/kaushikbokka/pytorch-lightning/pytorch_lightning/utilities/device_parser.py", line 125, in parse_tpu_cores
-    raise MisconfigurationException('No TPU devices were found.')
-pytorch_lightning.utilities.exceptions.MisconfigurationException: No TPU devices were found.
-
-
-

This means the system is missing XLA configuration. You would need to set up XRT TPU device configuration.

-

For TPUVM architecture, you could set it in your terminal by:

-
export XRT_TPU_CONFIG="localservice;0;localhost:51011"
-
-
-

And for the old TPU + 2VM architecture, you could set it by:

-
export TPU_IP_ADDRESS=10.39.209.42  # You could get the IP Address in the GCP TPUs section
-export XRT_TPU_CONFIG="tpu_worker;0;$TPU_IP_ADDRESS:8470"
-
-
-
-

How to clear up the programs using TPUs in the background

-
lsof -w /lib/libtpu.so | grep "python" |  awk '{print $2}' | xargs -r kill -9
-
-
-

Sometimes, there can still be old programs running on the TPUs, which would make the TPUs unavailable to use. You could use the above command in the terminal to kill the running processes.

-
-

Replication issue

-
File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 200, in set_replication
-    replication_devices = xla_replication_devices(devices)
-File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 187, in xla_replication_devices
-    .format(len(local_devices), len(kind_devices)))
-RuntimeError: Cannot replicate if number of devices (1) is different from 8
-
-
-

This error is raised when the XLA device is called outside the spawn process. Internally in TPUSpawn Strategy for training on multiple tpu cores, we use XLA’s xmp.spawn. -Don’t use xm.xla_device() while working on Lightning + TPUs!

-
-

Unsupported datatype transfer to TPU

-
File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 205, in _for_each_instance_rewrite
-    v = _for_each_instance_rewrite(result.__dict__[k], select_fn, fn, rwmap)
-File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 206, in _for_each_instance_rewrite
-    result.__dict__[k] = v
-TypeError: 'mappingproxy' object does not support item assignment
-
-
-

PyTorch XLA only supports Tensor objects for CPU to TPU data transfer. Might cause issues if the User is trying to send some non-tensor objects through the DataLoader or during saving states.

-
-

Using `tpu_spawn_debug` Strategy alias

-
import pytorch_lightning as pl
-
-my_model = MyLightningModule()
-trainer = pl.Trainer(accelerator="tpu", devices=8, strategy="tpu_spawn_debug")
-trainer.fit(my_model)
-
-
-

Example Metrics report:

-
Metric: CompileTime
-    TotalSamples: 202
-    Counter: 06m09s401ms746.001us
-    ValueRate: 778ms572.062us / second
-    Rate: 0.425201 / second
-    Percentiles: 1%=001ms32.778us; 5%=001ms61.283us; 10%=001ms79.236us; 20%=001ms110.973us; 50%=001ms228.773us; 80%=001ms339.183us; 90%=001ms434.305us; 95%=002ms921.063us; 99%=21s102ms853.173us
-
-
-

A lot of PyTorch operations aren’t lowered to XLA, which could lead to significant slowdown of the training process. -These operations are moved to the CPU memory and evaluated, and then the results are transferred back to the XLA device(s). -By using the tpu_spawn_debug Strategy, users could create a metrics report to diagnose issues.

-

The report includes things like (XLA Reference):

-
    -
  • how many times we issue XLA compilations and time spent on issuing.

  • -
  • how many times we execute and time spent on execution

  • -
  • how many device data handles we create/destroy etc.

  • -
-
-

TPU Pod Training Startup script

-

All TPU VMs in a Pod setup are required to access the model code and data. -One easy way to achieve this is to use the following startup script when creating the TPU VM pod. -It will perform the data downloading on all TPU VMs. Note that you need to export the corresponding environment variables following the instruction in Create TPU Node.

-
gcloud alpha compute tpus tpu-vm create ${TPU_NAME} --zone ${ZONE} --project ${PROJECT_ID} --accelerator-type v3-32 --version ${RUNTIME_VERSION} --metadata startup-script=setup.py
-
-
-

Then users could ssh to any TPU worker, e.g. worker 0, check if data/model downloading is finished and -start the training after generating the ssh-keys to ssh between VM workers on a pod:

-
python3 -m torch_xla.distributed.xla_dist --tpu=$TPU_NAME -- python3 train.py --max_epochs=5 --batch_size=32
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/accelerators/tpu_intermediate.html b/docs/accelerators/tpu_intermediate.html deleted file mode 100644 index 3e76dda..0000000 --- a/docs/accelerators/tpu_intermediate.html +++ /dev/null @@ -1,788 +0,0 @@ - - - - - - - - - - - - - - TPU training (Intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • TPU training (Intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

TPU training (Intermediate)

-

Audience: Users looking to use cloud TPUs.

-
-
-

DistributedSamplers

-

Lightning automatically inserts the correct samplers - no need to do this yourself!

-

Usually, with TPUs (and DDP), you would need to define a DistributedSampler to move the right -chunk of data to the appropriate TPU. As mentioned, this is not needed in Lightning

-
-

Note

-

Don’t add distributedSamplers. Lightning does this automatically

-
-

If for some reason you still need to, this is how to construct the sampler -for TPU use

-
import torch_xla.core.xla_model as xm
-
-
-def train_dataloader(self):
-    dataset = MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor())
-
-    # required for TPU support
-    sampler = None
-    if use_tpu:
-        sampler = torch.utils.data.distributed.DistributedSampler(
-            dataset, num_replicas=xm.xrt_world_size(), rank=xm.get_ordinal(), shuffle=True
-        )
-
-    loader = DataLoader(dataset, sampler=sampler, batch_size=32)
-
-    return loader
-
-
-

Configure the number of TPU cores in the trainer. You can only choose 1 or 8. -To use a full TPU pod skip to the TPU pod section.

-
import pytorch_lightning as pl
-
-my_model = MyLightningModule()
-trainer = pl.Trainer(accelerator="tpu", devices=8)
-trainer.fit(my_model)
-
-
-

That’s it! Your model will train on all 8 TPU cores.

-
-
-
-

Distributed Backend with TPU

-

The accelerator option used for GPUs does not apply to TPUs. -TPUs work in DDP mode by default (distributing over each core)

-
-
-
-

TPU VM

-

Lightning supports training on the new Cloud TPU VMs. -Previously, we needed separate VMs to connect to the TPU machines, but as -Cloud TPU VMs run on the TPU Host machines, it allows direct SSH access -for the users. Hence, this architecture upgrade leads to cheaper and significantly -better performance and usability while working with TPUs.

-

The TPUVMs come pre-installed with latest versions of PyTorch and PyTorch XLA. -After connecting to the VM and before running your Lightning code, you would need -to set the XRT TPU device configuration.

-
$ export XRT_TPU_CONFIG="localservice;0;localhost:51011"
-
-
-

You could learn more about the Cloud TPU VM architecture here

-
-
-
-

TPU Pod

-

To train on more than 8 cores, your code actually doesn’t change! -All you need to do is submit the following command:

-
$ python -m torch_xla.distributed.xla_dist
---tpu=$TPU_POD_NAME
---conda-env=torch-xla-nightly
--- python /usr/share/torch-xla-1.8.1/pytorch/xla/test/test_train_imagenet.py --fake_data
-
-
-

See this guide -on how to set up the instance groups and VMs needed to run TPU Pods.

-
-
-
-

16 bit precision

-

Lightning also supports training in 16-bit precision with TPUs. -By default, TPU training will use 32-bit precision. To enable 16-bit, -set the 16-bit flag.

-
import pytorch_lightning as pl
-
-my_model = MyLightningModule()
-trainer = pl.Trainer(accelerator="tpu", devices=8, precision=16)
-trainer.fit(my_model)
-
-
-

Under the hood the xla library will use the bfloat16 type.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/advanced/model_parallel.html b/docs/advanced/model_parallel.html deleted file mode 100644 index 4d9d962..0000000 --- a/docs/advanced/model_parallel.html +++ /dev/null @@ -1,1477 +0,0 @@ - - - - - - - - - - - - - - Train 1 trillion+ parameter models — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train 1 trillion+ parameter models
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train 1 trillion+ parameter models

-

When training large models, fitting larger batch sizes, or trying to increase throughput using multi-GPU compute, Lightning provides advanced optimized distributed training strategies to support these cases and offer substantial improvements in memory usage.

-

In many cases these strategies are some flavour of model parallelism however we only introduce concepts at a high level to get you started. Refer to the FairScale documentation for more information about model parallelism.

-

Note that some of the extreme memory saving configurations will affect the speed of training. This Speed/Memory trade-off in most cases can be adjusted.

-

Some of these memory-efficient strategies rely on offloading onto other forms of memory, such as CPU RAM or NVMe. This means you can even see memory benefits on a single GPU, using a strategy such as DeepSpeed ZeRO Stage 3 Offload.

-

Check out this amazing video explaining model parallelism and how it works behind the scenes:

-
-

Choosing an Advanced Distributed GPU Strategy

-

If you would like to stick with PyTorch DDP, see DDP Optimizations.

-

Unlike DistributedDataParallel (DDP) where the maximum trainable model size and batch size do not change with respect to the number of GPUs, memory-optimized strategies can accommodate bigger models and larger batches as more GPUs are used. This means as you scale up the number of GPUs, you can reach the number of model parameters you’d like to train.

-

There are many considerations when choosing a strategy as described below. In addition, check out the visualization of various strategy benchmarks using minGPT here.

-
-

Pre-training vs Fine-tuning

-

When fine-tuning, we often use a magnitude less data compared to pre-training a model. This is important when choosing a distributed strategy as usually for pre-training, we are compute-bound. -This means we cannot sacrifice throughput as much as if we were fine-tuning, because in fine-tuning the data requirement is smaller.

-

Overall:

- -

For example when using 128 GPUs, you can pre-train large 10 to 20 Billion parameter models using DeepSpeed ZeRO Stage 2 without having to take a performance hit with more advanced optimized multi-gpu strategy.

-

But for fine-tuning a model, you can reach 10 to 20 Billion parameter models using DeepSpeed ZeRO Stage 3 Offload on a single GPU. This does come with a significant throughput hit, which needs to be weighed accordingly.

-
-
-

When Shouldn’t I use an Optimized Distributed Strategy?

-

Sharding techniques help when model sizes are fairly large; roughly 500M+ parameters is where we’ve seen benefits. However, in the following cases, we recommend sticking to ordinary distributed strategies -* When your model is small (ResNet50 of around 80M Parameters), unless you are using unusually large batch sizes or inputs. -* Due to high distributed communication between devices, if running on a slow network/interconnect, the training might be much slower than expected and then it’s up to you to determince the tradeoff here.

-
-
-
-
-

Sharded Training

-

Lightning integration of optimizer sharded training provided by FairScale. -The technique can be found within DeepSpeed ZeRO and -ZeRO-2, -however the implementation is built from the ground up to be PyTorch compatible and standalone. -Sharded Training allows you to maintain GPU scaling efficiency, whilst reducing memory overhead drastically. In short, expect near-normal linear scaling (if your network allows), and significantly reduced memory usage when training large models.

-

Sharded Training still utilizes Data Parallel Training under the hood, except optimizer states and gradients are sharded across GPUs. -This means the memory overhead per GPU is lower, as each GPU only has to maintain a partition of your optimizer state and gradients.

-

The benefits vary by model and parameter sizes, but we’ve recorded up to a 63% memory reduction per GPU allowing us to double our model sizes. Because of efficient communication, -these benefits in multi-GPU setups are almost free and throughput scales well with multi-node setups.

-

It is highly recommended to use Sharded Training in multi-GPU environments where memory is limited, or where training larger models are beneficial (500M+ parameter models). -A technical note: as batch size scales, storing activations for the backwards pass becomes the bottleneck in training. As a result, sharding optimizer state and gradients becomes less impactful. -Use FairScale Activation Checkpointing to see even more benefit at the cost of some throughput.

-

To use Sharded Training, you need to first install FairScale using the command below.

-
pip install fairscale
-
-
-
# train using Sharded DDP
-trainer = Trainer(strategy="ddp_sharded")
-
-
-

Sharded Training can work across all DDP variants by adding the additional --strategy ddp_sharded flag via command line using a PyTorch Lightning script.

-

Internally we re-initialize your optimizers and shard them across your machines and processes. We handle all communication using PyTorch distributed, so no code changes are required.

-
-
-
-

Fully Sharded Training

-
-

Warning

-

Fully Sharded Training is in beta and the API is subject to change. Please create an issue if you run into any issues.

-
-

Fully Sharded shards optimizer state, gradients and parameters across data parallel workers. This allows you to fit much larger models onto multiple GPUs into memory.

-

Fully Sharded Training alleviates the need to worry about balancing layers onto specific devices using some form of pipe parallelism, and optimizes for distributed communication with minimal effort.

-
-

Shard Parameters to Reach 10+ Billion Parameters

-

To reach larger parameter sizes and be memory efficient, we have to shard parameters. There are various ways to enable this.

-
-

Note

-

Currently Fully Sharded Training relies on the user to wrap the model with Fully Sharded within the LightningModule. -This means you must create a single model that is treated as a torch.nn.Module within the LightningModule. -This is a limitation of Fully Sharded Training that will be resolved in the future.

-
-
-
-

Enabling Module Sharding for Maximum Memory Efficiency

-

To activate parameter sharding, you must wrap your model using provided wrap or auto_wrap functions as described below. Internally in Lightning, we enable a context manager around the configure_sharded_model function to make sure the wrap and auto_wrap parameters are passed correctly.

-

When not using Fully Sharded these wrap functions are a no-op. This means once the changes have been made, there is no need to remove the changes for other strategies.

-

auto_wrap will recursively wrap Module within the LightningModule with nested Fully Sharded Wrappers, -signalling that we’d like to partition these modules across data parallel devices, discarding the full weights when not required (information here).

-

auto_wrap can have varying level of success based on the complexity of your model. Auto Wrap does not support models with shared parameters.

-

wrap will simply wrap the module with a Fully Sharded Parallel class with the correct parameters from the Lightning context manager.

-

Below is an example of using both wrap and auto_wrap to create your model.

-
import torch
-import torch.nn as nn
-import pytorch_lightning as pl
-from pytorch_lightning import Trainer
-from fairscale.nn import checkpoint_wrapper, auto_wrap, wrap
-
-
-class MyModel(pl.LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.linear_layer = nn.Linear(32, 32)
-        self.block = nn.Sequential(nn.Linear(32, 32), nn.ReLU())
-        self.final_block = nn.Sequential(nn.Linear(32, 32), nn.ReLU())
-
-    def configure_sharded_model(self):
-        # modules are sharded across processes
-        # as soon as they are wrapped with ``wrap`` or ``auto_wrap``.
-        # During the forward/backward passes, weights get synced across processes
-        # and de-allocated once computation is complete, saving memory.
-
-        # Wraps the layer in a Fully Sharded Wrapper automatically
-        linear_layer = wrap(self.linear_layer)
-
-        # Wraps the module recursively
-        # based on a minimum number of parameters (default 100M parameters)
-        block = auto_wrap(self.block)
-
-        # For best memory efficiency,
-        # add FairScale activation checkpointing
-        final_block = auto_wrap(checkpoint_wrapper(self.final_block))
-        self.model = nn.Sequential(linear_layer, nn.ReLU(), block, final_block)
-
-    def configure_optimizers(self):
-        return torch.optim.AdamW(self.model.parameters())
-
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="fsdp", precision=16)
-trainer.fit(model)
-
-trainer.test()
-trainer.predict()
-
-
-
-
-
-
-

FairScale Activation Checkpointing

-

Activation checkpointing frees activations from memory as soon as they are not needed during the forward pass. They are then re-computed for the backwards pass as needed. Activation checkpointing is very useful when you have intermediate layers that produce large activations.

-

FairScales’ checkpointing wrapper also handles batch norm layers correctly unlike the PyTorch implementation, ensuring stats are tracked correctly due to the multiple forward passes.

-

This saves memory when training larger models however requires wrapping modules you’d like to use activation checkpointing on. See here for more information.

-
-

Warning

-

Ensure to not wrap the entire model with activation checkpointing. This is not the intended usage of activation checkpointing, and will lead to failures as seen in this discussion.

-
-
from pytorch_lightning import Trainer
-from fairscale.nn import checkpoint_wrapper
-
-
-class MyModel(pl.LightningModule):
-    def __init__(self):
-        super().__init__()
-        # Wrap layers using checkpoint_wrapper
-        self.block_1 = checkpoint_wrapper(nn.Sequential(nn.Linear(32, 32), nn.ReLU()))
-        self.block_2 = nn.Linear(32, 2)
-
-
-
-
-

DeepSpeed

-
-

Note

-

The DeepSpeed strategy is in beta and the API is subject to change. Please create an issue if you run into any issues.

-
-

DeepSpeed is a deep learning training optimization library, providing the means to train massive billion parameter models at scale. -Using the DeepSpeed strategy, we were able to train model sizes of 10 Billion parameters and above, with a lot of useful information in this benchmark and the DeepSpeed docs. -DeepSpeed also offers lower level training optimizations, and efficient optimizers such as 1-bit Adam. We recommend using DeepSpeed in environments where speed and memory optimizations are important (such as training large billion parameter models).

-

Below is a summary of all the configurations of DeepSpeed.

-
    -
  • DeepSpeed ZeRO Stage 1 - Shard optimizer states, remains at speed parity with DDP whilst providing memory improvement

  • -
  • DeepSpeed ZeRO Stage 2 - Shard optimizer states and gradients, remains at speed parity with DDP whilst providing even more memory improvement

  • -
  • DeepSpeed ZeRO Stage 2 Offload - Offload optimizer states and gradients to CPU. Increases distributed communication volume and GPU-CPU device transfer, but provides significant memory improvement

  • -
  • DeepSpeed ZeRO Stage 3 - Shard optimizer states, gradients, parameters and optionally activations. Increases distributed communication volume, but provides even more memory improvement

  • -
  • DeepSpeed ZeRO Stage 3 Offload - Offload optimizer states, gradients, parameters and optionally activations to CPU. Increases distributed communication volume and GPU-CPU device transfer, but even more significant memory improvement.

  • -
  • DeepSpeed Activation Checkpointing - Free activations after forward pass. Increases computation, but provides memory improvement for all stages.

  • -
-

To use DeepSpeed, you first need to install DeepSpeed using the commands below.

-
pip install deepspeed
-
-
-

If you run into an issue with the install or later in training, ensure that the CUDA version of the PyTorch you’ve installed matches your locally installed CUDA (you can see which one has been recognized by running nvcc --version).

-
-

Note

-

DeepSpeed currently only supports single optimizer, single scheduler within the training loop.

-

When saving a checkpoint we rely on DeepSpeed which saves a directory containing the model and various components.

-
-
-

DeepSpeed ZeRO Stage 1

-

DeepSpeed ZeRO Stage 1 partitions your optimizer states (Stage 1) across your GPUs to reduce memory.

-

It is recommended to skip Stage 1 and use Stage 2, which comes with larger memory improvements and still remains efficient. Stage 1 is useful to pair with certain optimizations such as Torch ORT.

-
from pytorch_lightning import Trainer
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_1", precision=16)
-trainer.fit(model)
-
-
-
-
-

DeepSpeed ZeRO Stage 2

-

DeepSpeed ZeRO Stage 2 partitions your optimizer states (Stage 1) and your gradients (Stage 2) across your GPUs to reduce memory. In most cases, this is more efficient or at parity with DDP, primarily due to the optimized custom communications written by the DeepSpeed team. -As a result, benefits can also be seen on a single GPU. Do note that the default bucket sizes allocate around 3.6GB of VRAM to use during distributed communications, which can be tweaked when instantiating the strategy described in a few sections below.

-
from pytorch_lightning import Trainer
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2", precision=16)
-trainer.fit(model)
-
-
-
python train.py --strategy deepspeed_stage_2 --precision 16 --accelerator 'gpu' --devices 4
-
-
-
-
-

DeepSpeed ZeRO Stage 2 Offload

-

Below we show an example of running ZeRO-Offload. ZeRO-Offload leverages the host CPU to offload optimizer memory/computation, reducing the overall memory consumption.

-
from pytorch_lightning import Trainer
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2_offload", precision=16)
-trainer.fit(model)
-
-
-

This can also be done via the command line using a PyTorch Lightning script:

-
python train.py --strategy deepspeed_stage_2_offload --precision 16 --accelerator 'gpu' --devices 4
-
-
-

You can also modify the ZeRO-Offload parameters via the strategy as below.

-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DeepSpeedStrategy
-
-model = MyModel()
-trainer = Trainer(
-    accelerator="gpu",
-    devices=4,
-    strategy=DeepSpeedStrategy(offload_optimizer=True, allgather_bucket_size=5e8, reduce_bucket_size=5e8),
-    precision=16,
-)
-trainer.fit(model)
-
-
-
-

Note

-

We suggest tuning the allgather_bucket_size parameter and reduce_bucket_size parameter to find optimum parameters based on your model size. -These control how large a buffer we limit the model to using when reducing gradients/gathering updated parameters. Smaller values will result in less memory, but tradeoff with speed.

-

DeepSpeed allocates a reduce buffer size multiplied by 1.5x so take that into consideration when tweaking the parameters.

-

The strategy sets a reasonable default of 2e8, which should work for most low VRAM GPUs (less than 7GB), allocating roughly 3.6GB of VRAM as buffer. Higher VRAM GPUs should aim for values around 5e8.

-
-

For even more speed benefit, DeepSpeed offers an optimized CPU version of ADAM called DeepSpeedCPUAdam to run the offloaded computation, which is faster than the standard PyTorch implementation.

-
import pytorch_lightning
-from pytorch_lightning import Trainer
-from deepspeed.ops.adam import DeepSpeedCPUAdam
-
-
-class MyModel(pl.LightningModule):
-    ...
-
-    def configure_optimizers(self):
-        # DeepSpeedCPUAdam provides 5x to 7x speedup over torch.optim.adam(w)
-        return DeepSpeedCPUAdam(self.parameters())
-
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2_offload", precision=16)
-trainer.fit(model)
-
-
-
-
-

DeepSpeed ZeRO Stage 3

-

DeepSpeed ZeRO Stage 3 shards the optimizer states, gradients and the model parameters (also optionally activations). Sharding model parameters and activations comes with an increase in distributed communication, however allows you to scale your models massively from one GPU to multiple GPUs. -The DeepSpeed team report the ability to fine-tune models with over 40B parameters on a single GPU and over 2 Trillion parameters on 512 GPUs. For more information we suggest checking the DeepSpeed ZeRO-3 Offload documentation.

-

We’ve ran benchmarks for all these features and given a simple example of how all these features work in Lightning, which you can see at minGPT.

-

To reach the highest memory efficiency or model size, you must:

-
    -
  1. Use the DeepSpeed strategy with the stage 3 parameter

  2. -
  3. Use CPU Offloading to offload weights to CPU, plus have a reasonable amount of CPU RAM to offload onto

  4. -
  5. Use DeepSpeed Activation Checkpointing to shard activations

  6. -
-

Below we describe how to enable all of these to see benefit. With all these improvements we reached 45 Billion parameters training a GPT model on 8 GPUs with ~1TB of CPU RAM available.

-

Also please have a look at our DeepSpeed ZeRO Stage 3 Tips which contains a lot of helpful information when configuring your own models.

-
-

Note

-

When saving a model using DeepSpeed and Stage 3, model states and optimizer states will be saved in separate sharded states (based on the world size). See Collating Single File Checkpoint for DeepSpeed ZeRO Stage 3 to obtain a single checkpoint file.

-
-
from pytorch_lightning import Trainer
-from deepspeed.ops.adam import FusedAdam
-
-
-class MyModel(pl.LightningModule):
-    ...
-
-    def configure_optimizers(self):
-        return FusedAdam(self.parameters())
-
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16)
-trainer.fit(model)
-
-trainer.test()
-trainer.predict()
-
-
-

You can also use the Lightning Trainer to run predict or evaluate with DeepSpeed once the model has been trained.

-
from pytorch_lightning import Trainer
-
-
-class MyModel(pl.LightningModule):
-    ...
-
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16)
-trainer.test(ckpt_path="my_saved_deepspeed_checkpoint.ckpt")
-
-
-
-
-

Shard Model Instantly to Reduce Initialization Time/Memory

-

When instantiating really large models, it is sometimes necessary to shard the model layers instantly.

-

This is the case if layers may not fit on one single machines CPU or GPU memory, but would fit once sharded across multiple machines. -We expose a hook that layers initialized within the hook will be sharded instantly on a per layer basis, allowing you to instantly shard models.

-

This reduces the time taken to initialize very large models, as well as ensure we do not run out of memory when instantiating larger models. For more information you can refer to the DeepSpeed docs for Constructing Massive Models.

-
import torch.nn as nn
-from pytorch_lightning import Trainer
-from deepspeed.ops.adam import FusedAdam
-
-
-class MyModel(pl.LightningModule):
-    ...
-
-    def configure_sharded_model(self):
-        # Created within sharded model context, modules are instantly sharded across processes
-        # as soon as they are made.
-        self.block = nn.Sequential(nn.Linear(32, 32), nn.ReLU())
-
-    def configure_optimizers(self):
-        return FusedAdam(self.parameters())
-
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16)
-trainer.fit(model)
-
-trainer.test()
-trainer.predict()
-
-
-
-
-

DeepSpeed ZeRO Stage 3 Offload

-

DeepSpeed ZeRO Stage 3 Offloads optimizer state, gradients to the host CPU to reduce memory usage as ZeRO Stage 2 does, however additionally allows you to offload the parameters as well for even more memory saving.

-
-

Note

-

When saving a model using DeepSpeed and Stage 3, model states and optimizer states will be saved in separate sharded states (based on the world size). See Collating Single File Checkpoint for DeepSpeed ZeRO Stage 3 to obtain a single checkpoint file.

-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DeepSpeedStrategy
-
-# Enable CPU Offloading
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16)
-trainer.fit(model)
-
-# Enable CPU Offloading, and offload parameters to CPU
-model = MyModel()
-trainer = Trainer(
-    accelerator="gpu",
-    devices=4,
-    strategy=DeepSpeedStrategy(
-        stage=3,
-        offload_optimizer=True,
-        offload_parameters=True,
-    ),
-    precision=16,
-)
-trainer.fit(model)
-
-
-
-
-

DeepSpeed Infinity (NVMe Offloading)

-

Additionally, DeepSpeed supports offloading to NVMe drives for even larger models, utilizing the large memory space found in NVMes. DeepSpeed reports the ability to fine-tune 1 Trillion+ parameters using NVMe Offloading on one 8 GPU machine. Below shows how to enable this, assuming the NVMe drive is mounted in a directory called /local_nvme.

-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DeepSpeedStrategy
-
-# Enable CPU Offloading
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16)
-trainer.fit(model)
-
-# Enable CPU Offloading, and offload parameters to CPU
-model = MyModel()
-trainer = Trainer(
-    accelerator="gpu",
-    devices=4,
-    strategy=DeepSpeedStrategy(
-        stage=3,
-        offload_optimizer=True,
-        offload_parameters=True,
-        remote_device="nvme",
-        offload_params_device="nvme",
-        offload_optimizer_device="nvme",
-        nvme_path="/local_nvme",
-    ),
-    precision=16,
-)
-trainer.fit(model)
-
-
-

When offloading to NVMe you may notice that the speed is slow. There are parameters that need to be tuned based on the drives that you are using. Running the aio_bench_perf_sweep.py script can help you to find optimum parameters. See the issue for more information on how to parse the information.

-
-
-

DeepSpeed Activation Checkpointing

-

Activation checkpointing frees activations from memory as soon as they are not needed during the forward pass. -They are then re-computed for the backwards pass as needed.

-

Activation checkpointing is very useful when you have intermediate layers that produce large activations.

-

This saves memory when training larger models, however requires using a checkpoint function to run modules as shown below.

-
-

Warning

-

Ensure to not wrap the entire model with activation checkpointing. This is not the intended usage of activation checkpointing, and will lead to failures as seen in this discussion.

-
-
from pytorch_lightning import Trainer
-import deepspeed
-
-
-class MyModel(LightningModule):
-    ...
-
-    def __init__(self):
-        super().__init__()
-        self.block_1 = nn.Sequential(nn.Linear(32, 32), nn.ReLU())
-        self.block_2 = torch.nn.Linear(32, 2)
-
-    def forward(self, x):
-        # Use the DeepSpeed checkpointing function instead of calling the module directly
-        # checkpointing self.block_1 means the activations are deleted after use,
-        # and re-calculated during the backward passes
-        x = deepspeed.checkpointing.checkpoint(self.block_1, x)
-        return self.block_2(x)
-
-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DeepSpeedStrategy
-import deepspeed
-
-
-class MyModel(pl.LightningModule):
-    ...
-
-    def configure_sharded_model(self):
-        self.block_1 = nn.Sequential(nn.Linear(32, 32), nn.ReLU())
-        self.block_2 = torch.nn.Linear(32, 2)
-
-    def forward(self, x):
-        # Use the DeepSpeed checkpointing function instead of calling the module directly
-        x = deepspeed.checkpointing.checkpoint(self.block_1, x)
-        return self.block_2(x)
-
-
-model = MyModel()
-
-trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16)
-
-# Enable CPU Activation Checkpointing
-trainer = Trainer(
-    accelerator="gpu",
-    devices=4,
-    strategy=DeepSpeedStrategy(
-        stage=3,
-        offload_optimizer=True,  # Enable CPU Offloading
-        cpu_checkpointing=True,  # (Optional) offload activations to CPU
-    ),
-    precision=16,
-)
-trainer.fit(model)
-
-
-
-
-

DeepSpeed ZeRO Stage 3 Tips

-

Here is some helpful information when setting up DeepSpeed ZeRO Stage 3 with Lightning.

-
    -
  • If you’re using Adam or AdamW, ensure to use FusedAdam or DeepSpeedCPUAdam (for CPU Offloading) rather than the default torch optimizers as they come with large speed benefits

  • -
  • Treat your GPU/CPU memory as one large pool. In some cases, you may not want to offload certain things (like activations) to provide even more space to offload model parameters

  • -
  • When offloading to the CPU, make sure to bump up the batch size as GPU memory will be freed

  • -
  • We also support sharded checkpointing. By passing save_full_weights=False to the DeepSpeedStrategy, we’ll save shards of the model which allows you to save extremely large models. However to load the model and run test/validation/predict you must use the Trainer object.

  • -
-
-
-

Collating Single File Checkpoint for DeepSpeed ZeRO Stage 3

-

After training using ZeRO Stage 3, you’ll notice that your checkpoints are a directory of sharded model and optimizer states. If you’d like to collate a single file from the checkpoint directory please use the below command, which handles all the Lightning states additionally when collating the file.

-
from pytorch_lightning.utilities.deepspeed import convert_zero_checkpoint_to_fp32_state_dict
-
-# lightning deepspeed has saved a directory instead of a file
-save_path = "lightning_logs/version_0/checkpoints/epoch=0-step=0.ckpt/"
-output_path = "lightning_model.pt"
-convert_zero_checkpoint_to_fp32_state_dict(save_path, output_path)
-
-
-
-

Warning

-

This single file checkpoint does not include the optimizer/lr-scheduler states. This means we cannot restore training via the trainer.fit(ckpt_path=) call. Ensure to keep the sharded checkpoint directory if this is required.

-
-
-
-

Custom DeepSpeed Config

-

In some cases you may want to define your own DeepSpeed Config, to access all parameters defined. We’ve exposed most of the important parameters, however, there may be debugging parameters to enable. Also, DeepSpeed allows the use of custom DeepSpeed optimizers and schedulers defined within a config file that is supported.

-
-

Note

-

All strategy default parameters will be ignored when a config object is passed. -All compatible arguments can be seen in the DeepSpeed docs.

-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DeepSpeedStrategy
-
-deepspeed_config = {
-    "zero_allow_untested_optimizer": True,
-    "optimizer": {
-        "type": "OneBitAdam",
-        "params": {
-            "lr": 3e-5,
-            "betas": [0.998, 0.999],
-            "eps": 1e-5,
-            "weight_decay": 1e-9,
-            "cuda_aware": True,
-        },
-    },
-    "scheduler": {
-        "type": "WarmupLR",
-        "params": {
-            "last_batch_iteration": -1,
-            "warmup_min_lr": 0,
-            "warmup_max_lr": 3e-5,
-            "warmup_num_steps": 100,
-        },
-    },
-    "zero_optimization": {
-        "stage": 2,  # Enable Stage 2 ZeRO (Optimizer/Gradient state partitioning)
-        "offload_optimizer": True,  # Enable Offloading optimizer state/calculation to the host CPU
-        "contiguous_gradients": True,  # Reduce gradient fragmentation.
-        "overlap_comm": True,  # Overlap reduce/backward operation of gradients for speed.
-        "allgather_bucket_size": 2e8,  # Number of elements to all gather at once.
-        "reduce_bucket_size": 2e8,  # Number of elements we reduce/allreduce at once.
-    },
-}
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy=DeepSpeedStrategy(config=deepspeed_config), precision=16)
-trainer.fit(model)
-
-
-

We support taking the config as a json formatted file:

-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DeepSpeedStrategy
-
-model = MyModel()
-trainer = Trainer(
-    accelerator="gpu", devices=4, strategy=DeepSpeedStrategy(config="/path/to/deepspeed_config.json"), precision=16
-)
-trainer.fit(model)
-
-
-

You can use also use an environment variable via your PyTorch Lightning script:

-
PL_DEEPSPEED_CONFIG_PATH=/path/to/deepspeed_config.json python train.py --strategy deepspeed
-
-
-
-
-
-
-

DDP Optimizations

-
-

When Using DDP Strategies, Set find_unused_parameters=False

-

By default, we have set find_unused_parameters=True for compatibility reasons that have been observed in the past (refer to the discussion for more details). -When enabled, it can result in a performance hit and can be disabled in most cases. Read more about it here.

-
-

Tip

-

It applies to all DDP strategies that support find_unused_parameters as input.

-
-
from pytorch_lightning.strategies import DDPStrategy
-
-trainer = pl.Trainer(
-    accelerator="gpu",
-    devices=2,
-    strategy=DDPStrategy(find_unused_parameters=False),
-)
-
-
-
from pytorch_lightning.strategies import DDPSpawnStrategy
-
-trainer = pl.Trainer(
-    accelerator="gpu",
-    devices=2,
-    strategy=DDPSpawnStrategy(find_unused_parameters=False),
-)
-
-
-
-
-

DDP Static Graph

-

DDP static graph assumes that your model -employs the same set of used/unused parameters in every iteration, so that it can deterministically know the flow of -training and apply special optimizations during runtime.

-
-

Note

-

DDP static graph support requires PyTorch>=1.11.0

-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DDPStrategy
-
-trainer = Trainer(devices=4, strategy=DDPStrategy(static_graph=True))
-
-
-
-
-

When Using DDP on a Multi-node Cluster, Set NCCL Parameters

-

NCCL is the NVIDIA Collective Communications Library that is used by PyTorch to handle communication across nodes and GPUs. There are reported benefits in terms of speedups when adjusting NCCL parameters as seen in this issue. In the issue, we see a 30% speed improvement when training the Transformer XLM-RoBERTa and a 15% improvement in training with Detectron2.

-

NCCL parameters can be adjusted via environment variables.

-
-

Note

-

AWS and GCP already set default values for these on their clusters. This is typically useful for custom cluster setups.

-
- -
export NCCL_NSOCKS_PERTHREAD=4
-export NCCL_SOCKET_NTHREADS=2
-
-
-
-
-

Gradients as Bucket View

-

Enabling gradient_as_bucket_view=True in the DDPStrategy will make gradients views point to different offsets of the allreduce communication buckets. See DistributedDataParallel for more information.

-

This can reduce peak memory usage and throughput as saved memory will be equal to the total gradient memory + removes the need to copy gradients to the allreduce communication buckets.

-
-

Note

-

When gradient_as_bucket_view=True you cannot call detach_() on gradients. If hitting such errors, please fix it by referring to the zero_grad() function in torch/optim/optimizer.py as a solution (source).

-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DDPStrategy
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy=DDPStrategy(gradient_as_bucket_view=True))
-trainer.fit(model)
-
-
-
-
-

DDP Communication Hooks

-

DDP Communication hooks is an interface to control how gradients are communicated across workers, overriding the standard allreduce in DistributedDataParallel. This allows you to enable performance improving communication hooks when using multiple nodes.

-

Enable FP16 Compress Hook for multi-node throughput improvement:

-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DDPStrategy
-from torch.distributed.algorithms.ddp_comm_hooks import default_hooks as default
-
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=4, strategy=DDPStrategy(ddp_comm_hook=default.fp16_compress_hook))
-trainer.fit(model)
-
-
-

Enable PowerSGD for multi-node throughput improvement:

-
-

Note

-

PowerSGD typically requires extra memory of the same size as the model’s gradients to enable error feedback, which can compensate for biased compressed communication and improve accuracy (source).

-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DDPStrategy
-from torch.distributed.algorithms.ddp_comm_hooks import powerSGD_hook as powerSGD
-
-model = MyModel()
-trainer = Trainer(
-    accelerator="gpu",
-    devices=4,
-    strategy=DDPStrategy(
-        ddp_comm_state=powerSGD.PowerSGDState(
-            process_group=None,
-            matrix_approximation_rank=1,
-            start_powerSGD_iter=5000,
-        ),
-        ddp_comm_hook=powerSGD.powerSGD_hook,
-    ),
-)
-trainer.fit(model)
-
-
-

Combine hooks for accumulated benefit:

-
-

Note

-

DDP communication wrappers support requires PyTorch>=1.9.0

-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DDPStrategy
-from torch.distributed.algorithms.ddp_comm_hooks import (
-    default_hooks as default,
-    powerSGD_hook as powerSGD,
-)
-
-model = MyModel()
-trainer = Trainer(
-    accelerator="gpu",
-    devices=4,
-    strategy=DDPStrategy(
-        ddp_comm_state=powerSGD.PowerSGDState(
-            process_group=None,
-            matrix_approximation_rank=1,
-            start_powerSGD_iter=5000,
-        ),
-        ddp_comm_hook=powerSGD.powerSGD_hook,
-        ddp_comm_wrapper=default.fp16_compress_wrapper,
-    ),
-)
-trainer.fit(model)
-
-
-

When using Post-localSGD, you must also pass model_averaging_period to allow for model parameter averaging:

-
-

Note

-

Post-localSGD support requires PyTorch>=1.10.0

-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.strategies import DDPStrategy
-from torch.distributed.algorithms.ddp_comm_hooks import post_localSGD_hook as post_localSGD
-
-model = MyModel()
-trainer = Trainer(
-    accelerator="gpu",
-    devices=4,
-    strategy=DDPStrategy(
-        ddp_comm_state=post_localSGD.PostLocalSGDState(
-            process_group=None,
-            subgroup=None,
-            start_localSGD_iter=8,
-        ),
-        ddp_comm_hook=post_localSGD.post_localSGD_hook,
-        model_averaging_period=4,
-    ),
-)
-trainer.fit(model)
-
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/advanced/pruning_quantization.html b/docs/advanced/pruning_quantization.html deleted file mode 100644 index 98e8cf4..0000000 --- a/docs/advanced/pruning_quantization.html +++ /dev/null @@ -1,795 +0,0 @@ - - - - - - - - - - - - - - Pruning and Quantization — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Pruning and Quantization
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Pruning and Quantization

-

Pruning and Quantization are techniques to compress model size for deployment, allowing inference speed up and energy saving without significant accuracy losses.

-
-

Pruning

-
-

Warning

-

Pruning is in beta and subject to change.

-
-

Pruning is a technique which focuses on eliminating some of the model weights to reduce the model size and decrease inference requirements.

-

Pruning has been shown to achieve significant efficiency improvements while minimizing the drop in model performance (prediction quality). Model pruning is recommended for cloud endpoints, deploying models on edge devices, or mobile inference (among others).

-

To enable pruning during training in Lightning, simply pass in the ModelPruning callback to the Lightning Trainer. PyTorch’s native pruning implementation is used under the hood.

-

This callback supports multiple pruning functions: pass any torch.nn.utils.prune function as a string to select which weights to prune (random_unstructured, RandomStructured, etc) or implement your own by subclassing BasePruningMethod.

-
from pytorch_lightning.callbacks import ModelPruning
-
-# set the amount to be the fraction of parameters to prune
-trainer = Trainer(callbacks=[ModelPruning("l1_unstructured", amount=0.5)])
-
-
-

You can also perform iterative pruning, apply the lottery ticket hypothesis, and more!

-
def compute_amount(epoch):
-    # the sum of all returned values need to be smaller than 1
-    if epoch == 10:
-        return 0.5
-
-    elif epoch == 50:
-        return 0.25
-
-    elif 75 < epoch < 99:
-        return 0.01
-
-
-# the amount can be also be a callable
-trainer = Trainer(callbacks=[ModelPruning("l1_unstructured", amount=compute_amount)])
-
-
-
-
-

Quantization

-
-

Warning

-

Quantization is in beta and subject to change.

-
-

Model quantization is another performance optimization technique that allows speeding up inference and decreasing memory requirements by performing computations and storing tensors at lower bitwidths (such as INT8 or FLOAT16) than floating-point precision. This is particularly beneficial during model deployment.

-

Quantization Aware Training (QAT) mimics the effects of quantization during training: The computations are carried-out in floating-point precision but the subsequent quantization effect is taken into account. The weights and activations are quantized into lower precision only for inference, when training is completed.

-

Quantization is useful when it is required to serve large models on machines with limited memory, or when there’s a need to switch between models and reducing the I/O time is important. For example, switching between monolingual speech recognition models across multiple languages.

-

Lightning includes QuantizationAwareTraining callback (using PyTorch’s native quantization, read more here), which allows creating fully quantized models (compatible with torchscript).

-
from pytorch_lightning.callbacks import QuantizationAwareTraining
-
-
-class RegressionModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.layer_0 = nn.Linear(16, 64)
-        self.layer_0a = torch.nn.ReLU()
-        self.layer_1 = nn.Linear(64, 64)
-        self.layer_1a = torch.nn.ReLU()
-        self.layer_end = nn.Linear(64, 1)
-
-    def forward(self, x):
-        x = self.layer_0(x)
-        x = self.layer_0a(x)
-        x = self.layer_1(x)
-        x = self.layer_1a(x)
-        x = self.layer_end(x)
-        return x
-
-
-trainer = Trainer(callbacks=[QuantizationAwareTraining()])
-qmodel = RegressionModel()
-trainer.fit(qmodel, ...)
-
-batch = iter(my_dataloader()).next()
-qmodel(qmodel.quant(batch[0]))
-
-tsmodel = qmodel.to_torchscript()
-tsmodel(tsmodel.quant(batch[0]))
-
-
-

You can further customize the callback:

-
qcb = QuantizationAwareTraining(
-    # specification of quant estimation quality
-    observer_type="histogram",
-    # specify which layers shall be merged together to increase efficiency
-    modules_to_fuse=[(f"layer_{i}", f"layer_{i}a") for i in range(2)],
-    # make your model compatible with all original input/outputs, in such case the model is wrapped in a shell with entry/exit layers.
-    input_compatible=True,
-)
-
-batch = iter(my_dataloader()).next()
-qmodel(batch[0])
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/advanced/strategy_registry.html b/docs/advanced/strategy_registry.html deleted file mode 100644 index 408b974..0000000 --- a/docs/advanced/strategy_registry.html +++ /dev/null @@ -1,741 +0,0 @@ - - - - - - - - - - - - - - Strategy Registry — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Strategy Registry
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Strategy Registry

-
-

Warning

-

The Strategy Registry is experimental and subject to change.

-
-

Lightning includes a registry that holds information about Training strategies and allows for the registration of new custom strategies.

-

The Strategies are assigned strings that identify them, such as “ddp”, “deepspeed_stage_2_offload”, and so on. -It also returns the optional description and parameters for initialising the Strategy that were defined during registration.

-
# Training with the DDP Strategy with `find_unused_parameters` as False
-trainer = Trainer(strategy="ddp_find_unused_parameters_false", accelerator="gpu", devices=4)
-
-# Training with DeepSpeed ZeRO Stage 3 and CPU Offload
-trainer = Trainer(strategy="deepspeed_stage_3_offload", accelerator="gpu", devices=3)
-
-# Training with the TPU Spawn Strategy with `debug` as True
-trainer = Trainer(strategy="tpu_spawn_debug", accelerator="tpu", devices=8)
-
-
-

Additionally, you can pass your custom registered training strategies to the strategy argument.

-
from pytorch_lightning.strategies import DDPStrategy, StrategyRegistry, CheckpointIO
-
-
-class CustomCheckpointIO(CheckpointIO):
-    def save_checkpoint(self, checkpoint: Dict[str, Any], path: Union[str, Path]) -> None:
-        ...
-
-    def load_checkpoint(self, path: Union[str, Path]) -> Dict[str, Any]:
-        ...
-
-
-custom_checkpoint_io = CustomCheckpointIO()
-
-# Register the DDP Strategy with your custom CheckpointIO plugin
-StrategyRegistry.register(
-    "ddp_custom_checkpoint_io",
-    DDPStrategy,
-    description="DDP Strategy with custom checkpoint io plugin",
-    checkpoint_io=custom_checkpoint_io,
-)
-
-trainer = Trainer(strategy="ddp_custom_checkpoint_io", accelerator="gpu", devices=2)
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/advanced/training_tricks.html b/docs/advanced/training_tricks.html deleted file mode 100644 index cfd9cdf..0000000 --- a/docs/advanced/training_tricks.html +++ /dev/null @@ -1,1063 +0,0 @@ - - - - - - - - - - - - - - Effective Training Techniques — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Effective Training Techniques
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Effective Training Techniques

-

Lightning implements various techniques to help during training that can help make the training smoother.

-
-
-

Accumulate Gradients

-

Accumulated gradients run K small batches of size N before doing a backward pass. The effect is a large effective batch size of size KxN, where N is the batch size. -Internally it doesn’t stack up the batches and do a forward pass rather it accumulates the gradients for K batches and then do an optimizer.step to make sure the -effective batch size is increased but there is no memory overhead.

-
-

Warning

-

When using distributed training for eg. DDP, with let’s say with P devices, each device accumulates independently i.e. it stores the gradients -after each loss.backward() and doesn’t sync the gradients across the devices until we call optimizer.step(). So for each accumulation -step, the effective batch size on each device will remain N*K but right before the optimizer.step(), the gradient sync will make the effective -batch size as P*N*K. For DP, since the batch is split across devices, the final effective batch size will be N*K.

-
-
-

See also

-

Trainer

-
-
# DEFAULT (ie: no accumulated grads)
-trainer = Trainer(accumulate_grad_batches=1)
-
-# Accumulate gradients for 7 batches
-trainer = Trainer(accumulate_grad_batches=7)
-
-
-

You can set different values for it at different epochs by passing a dictionary, where the key represents the epoch at which the value for gradient accumulation -should be updated.

-
# till 5th epoch, it will accumulate every 8 batches. From 5th epoch
-# till 9th epoch it will accumulate every 4 batches and after that no accumulation
-# will happen. Note that you need to use zero-indexed epoch keys here
-trainer = Trainer(accumulate_grad_batches={0: 8, 4: 4, 8: 1})
-
-
-

Or, you can create custom GradientAccumulationScheduler

-
from pytorch_lightning.callbacks import GradientAccumulationScheduler
-
-
-# till 5th epoch, it will accumulate every 8 batches. From 5th epoch
-# till 9th epoch it will accumulate every 4 batches and after that no accumulation
-# will happen. Note that you need to use zero-indexed epoch keys here
-accumulator = GradientAccumulationScheduler(scheduling={0: 8, 4: 4, 8: 1})
-trainer = Trainer(callbacks=accumulator)
-
-
-
-
-
-

Gradient Clipping

-

Gradient clipping can be enabled to avoid exploding gradients. By default, this will clip the gradient norm by calling -torch.nn.utils.clip_grad_norm_() computed over all model parameters together. -If the Trainer’s gradient_clip_algorithm is set to 'value' ('norm' by default), this will use instead -torch.nn.utils.clip_grad_value_() for each parameter instead.

-
-

Note

-

If using mixed precision, the gradient_clip_val does not need to be changed as the gradients are unscaled -before applying the clipping function.

-
-
-

See also

-

Trainer

-
-
# DEFAULT (ie: don't clip)
-trainer = Trainer(gradient_clip_val=0)
-
-# clip gradients' global norm to <=0.5 using gradient_clip_algorithm='norm' by default
-trainer = Trainer(gradient_clip_val=0.5)
-
-# clip gradients' maximum magnitude to <=0.5
-trainer = Trainer(gradient_clip_val=0.5, gradient_clip_algorithm="value")
-
-
-

Read more about Configuring Gradient Clipping for advanced use-cases.

-
-
-
-

Stochastic Weight Averaging

-

Stochastic Weight Averaging (SWA) can make your models generalize better at virtually no additional cost. -This can be used with both non-trained and trained models. The SWA procedure smooths the loss landscape thus making -it harder to end up in a local minimum during optimization.

-

For a more detailed explanation of SWA and how it works, -read this post by the PyTorch team.

-
-

See also

-

The StochasticWeightAveraging callback

-
-
# Enable Stochastic Weight Averaging using the callback
-trainer = Trainer(callbacks=[StochasticWeightAveraging(swa_lrs=1e-2)])
-
-
-
-
-
-

Batch Size Finder

-

Auto-scaling of batch size can be enabled to find the largest batch size that fits into -memory. Large batch size often yields a better estimation of the gradients, but may also result in -longer training time. Inspired by https://github.com/BlackHC/toma.

-
-

See also

-

Trainer

-
-
# DEFAULT (ie: don't scale batch size automatically)
-trainer = Trainer(auto_scale_batch_size=None)
-
-# Autoscale batch size
-trainer = Trainer(auto_scale_batch_size=None | "power" | "binsearch")
-
-# Find the batch size
-trainer.tune(model)
-
-
-

Currently, this feature supports two modes 'power' scaling and 'binsearch' -scaling. In 'power' scaling, starting from a batch size of 1 keeps doubling -the batch size until an out-of-memory (OOM) error is encountered. Setting the -argument to 'binsearch' will initially also try doubling the batch size until -it encounters an OOM, after which it will do a binary search that will finetune the -batch size. Additionally, it should be noted that the batch size scaler cannot -search for batch sizes larger than the size of the training dataset.

-
-

Note

-

This feature expects that a batch_size field is either located as a model attribute -i.e. model.batch_size or as a field in your hparams i.e. model.hparams.batch_size. -Similarly it can work with datamodules too. The field should exist and will be updated by -the results of this algorithm. Additionally, your train_dataloader() method should depend -on this field for this feature to work i.e.

-
# using LightningModule
-class LitModel(LightningModule):
-    def __init__(self, batch_size):
-        super().__init__()
-        self.save_hyperparameters()
-        # or
-        self.batch_size = batch_size
-
-    def train_dataloader(self):
-        return DataLoader(train_dataset, batch_size=self.batch_size | self.hparams.batch_size)
-
-
-trainer = Trainer(...)
-model = LitModel(batch_size=32)
-trainer.tune(model)
-
-# using LightningDataModule
-class LitDataModule(LightningDataModule):
-    def __init__(self, batch_size):
-        super().__init__()
-        self.save_hyperparameters()
-        # or
-        self.batch_size = batch_size
-
-    def train_dataloader(self):
-        return DataLoader(train_dataset, batch_size=self.batch_size | self.hparams.batch_size)
-
-
-trainer = Trainer(...)
-model = MyModel()
-datamodule = LitDataModule(batch_size=32)
-trainer.tune(model, datamodule=datamodule)
-
-
-
-
-

Warning

-

Due to the constraints listed above, this features does NOT work when passing dataloaders directly -to .fit().

-
-

The scaling algorithm has a number of parameters that the user can control by -invoking the scale_batch_size() method:

-
# Use default in trainer construction
-trainer = Trainer()
-tuner = Tuner(trainer)
-
-# Invoke method
-new_batch_size = tuner.scale_batch_size(model, *extra_parameters_here)
-
-# Override old batch size (this is done automatically)
-model.hparams.batch_size = new_batch_size
-
-# Fit as normal
-trainer.fit(model)
-
-
-
-
The algorithm in short works by:
    -
  1. Dumping the current state of the model and trainer

  2. -
  3. -
    Iteratively until convergence or maximum number of tries max_trials (default 25) has been reached:
      -
    • Call fit() method of trainer. This evaluates steps_per_trial (default 3) number of -optimization steps. Each training step can trigger an OOM error if the tensors -(training batch, weights, gradients, etc.) allocated during the steps have a -too large memory footprint.

    • -
    • If an OOM error is encountered, decrease batch size else increase it. -How much the batch size is increased/decreased is determined by the chosen -strategy.

    • -
    -
    -
    -
  4. -
  5. The found batch size is saved to either model.batch_size or model.hparams.batch_size

  6. -
  7. Restore the initial state of model and trainer

  8. -
-
-
-
-

Warning

-

Batch size finder is not yet supported for DDP or any of its variations, it is coming soon.

-
-
-
-
-

Learning Rate Finder

-
-

-
-

For training deep neural networks, selecting a good learning rate is essential -for both better performance and faster convergence. Even optimizers such as -Adam that are self-adjusting the learning rate can benefit from more optimal -choices.

-

To reduce the amount of guesswork concerning choosing a good initial learning -rate, a learning rate finder can be used. As described in this paper -a learning rate finder does a small run where the learning rate is increased -after each processed batch and the corresponding loss is logged. The result of -this is a lr vs. loss plot that can be used as guidance for choosing an optimal -initial learning rate.

-
-

Warning

-

For the moment, this feature only works with models having a single optimizer. -LR Finder support for DDP and any of its variations is not implemented yet. It is coming soon.

-
-
-

Using Lightning’s built-in LR finder

-

To enable the learning rate finder, your lightning module needs to -have a learning_rate or lr attribute (or as a field in your hparams i.e. -hparams.learning_rate or hparams.lr). Then, set Trainer(auto_lr_find=True) -during trainer construction, and then call trainer.tune(model) to run the LR finder. -The suggested learning_rate will be written to the console and will be automatically -set to your lightning module, which can be accessed -via self.learning_rate or self.lr.

-
-

See also

-

trainer.tune.

-
-
class LitModel(LightningModule):
-    def __init__(self, learning_rate):
-        super().__init__()
-        self.learning_rate = learning_rate
-        self.model = Model(...)
-
-    def configure_optimizers(self):
-        return Adam(self.parameters(), lr=(self.lr or self.learning_rate))
-
-
-model = LitModel()
-
-# finds learning rate automatically
-# sets hparams.lr or hparams.learning_rate to that learning rate
-trainer = Trainer(auto_lr_find=True)
-
-trainer.tune(model)
-
-
-

If your model is using an arbitrary value instead of self.lr or self.learning_rate, set that value as auto_lr_find:

-
model = LitModel()
-
-# to set to your own hparams.my_value
-trainer = Trainer(auto_lr_find="my_value")
-
-trainer.tune(model)
-
-
-

You can also inspect the results of the learning rate finder or just play around -with the parameters of the algorithm. This can be done by invoking the -lr_find() method. A typical example of this would look like:

-
model = MyModelClass(hparams)
-trainer = Trainer()
-
-# Run learning rate finder
-lr_finder = trainer.tuner.lr_find(model)
-
-# Results can be found in
-print(lr_finder.results)
-
-# Plot with
-fig = lr_finder.plot(suggest=True)
-fig.show()
-
-# Pick point based on plot, or get suggestion
-new_lr = lr_finder.suggestion()
-
-# update hparams of the model
-model.hparams.lr = new_lr
-
-# Fit model
-trainer.fit(model)
-
-
-

The figure produced by lr_finder.plot() should look something like the figure -below. It is recommended to not pick the learning rate that achieves the lowest -loss, but instead something in the middle of the sharpest downward slope (red point). -This is the point returned py lr_finder.suggestion().

-
-../_images/lr_finder.png -
-
-
-
-
-

Advanced GPU Optimizations

-

When training on single or multiple GPU machines, Lightning offers a host of advanced optimizations to improve throughput, memory efficiency, and model scaling. -Refer to Advanced GPU Optimized Training for more details.

-
-
-
-

Sharing Datasets Across Process Boundaries

-

The LightningDataModule class provides an organized way to decouple data loading from training logic, with prepare_data() being used for downloading and pre-processing the dataset on a single process, and setup() loading the pre-processed data for each process individually:

-
class MNISTDataModule(pl.LightningDataModule):
-    def prepare_data(self):
-        MNIST(self.data_dir, download=True)
-
-    def setup(self, stage: Optional[str] = None):
-        self.mnist = MNIST(self.data_dir)
-
-    def train_loader(self):
-        return DataLoader(self.mnist, batch_size=128)
-
-
-

However, for in-memory datasets, that means that each process will hold a (redundant) replica of the dataset in memory, which may be impractical when using many processes while utilizing datasets that nearly fit into CPU memory, as the memory consumption will scale up linearly with the number of processes. -For example, when training Graph Neural Networks, a common strategy is to load the entire graph into CPU memory for fast access to the entire graph structure and its features, and to then perform neighbor sampling to obtain mini-batches that fit onto the GPU.

-

A simple way to prevent redundant dataset replicas is to rely on torch.multiprocessing to share the data automatically between spawned processes via shared memory. -For this, all data pre-loading should be done on the main process inside DataModule.__init__(). As a result, all tensor-data will get automatically shared when using the DDPSpawnStrategy strategy.

-
-

Warning

-

torch.multiprocessing will send a handle of each individual tensor to other processes. -In order to prevent any errors due to too many open file handles, try to reduce the number of tensors to share, e.g., by stacking your data into a single tensor.

-
-
class MNISTDataModule(pl.LightningDataModule):
-    def __init__(self, data_dir: str):
-        self.mnist = MNIST(data_dir, download=True, transform=T.ToTensor())
-
-    def train_loader(self):
-        return DataLoader(self.mnist, batch_size=128)
-
-
-model = Model(...)
-datamodule = MNISTDataModule("data/MNIST")
-
-trainer = Trainer(accelerator="gpu", devices=2, strategy="ddp_spawn")
-trainer.fit(model, datamodule)
-
-
-

See the graph-level and node-level prediction examples in PyTorch Geometric for practical use-cases.

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/advanced/transfer_learning.html b/docs/advanced/transfer_learning.html deleted file mode 100644 index 1ae0896..0000000 --- a/docs/advanced/transfer_learning.html +++ /dev/null @@ -1,811 +0,0 @@ - - - - - - - - - - - - - - Transfer Learning — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Transfer Learning
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Transfer Learning

-

Audience: Users looking to use pretrained models with Lightning.

-
-
-

Use any PyTorch nn.Module

-

Any model that is a PyTorch nn.Module can be used with Lightning (because LightningModules are nn.Modules also).

-
-
-
-

Use a pretrained LightningModule

-

Let’s use the AutoEncoder as a feature extractor in a separate model.

-
class Encoder(torch.nn.Module):
-    ...
-
-
-class AutoEncoder(LightningModule):
-    def __init__(self):
-        self.encoder = Encoder()
-        self.decoder = Decoder()
-
-
-class CIFAR10Classifier(LightningModule):
-    def __init__(self):
-        # init the pretrained LightningModule
-        self.feature_extractor = AutoEncoder.load_from_checkpoint(PATH)
-        self.feature_extractor.freeze()
-
-        # the autoencoder outputs a 100-dim representation and CIFAR-10 has 10 classes
-        self.classifier = nn.Linear(100, 10)
-
-    def forward(self, x):
-        representations = self.feature_extractor(x)
-        x = self.classifier(representations)
-        ...
-
-
-

We used our pretrained Autoencoder (a LightningModule) for transfer learning!

-
-
-
-

Example: Imagenet (Computer Vision)

-
import torchvision.models as models
-
-
-class ImagenetTransferLearning(LightningModule):
-    def __init__(self):
-        super().__init__()
-
-        # init a pretrained resnet
-        backbone = models.resnet50(pretrained=True)
-        num_filters = backbone.fc.in_features
-        layers = list(backbone.children())[:-1]
-        self.feature_extractor = nn.Sequential(*layers)
-
-        # use the pretrained model to classify cifar-10 (10 image classes)
-        num_target_classes = 10
-        self.classifier = nn.Linear(num_filters, num_target_classes)
-
-    def forward(self, x):
-        self.feature_extractor.eval()
-        with torch.no_grad():
-            representations = self.feature_extractor(x).flatten(1)
-        x = self.classifier(representations)
-        ...
-
-
-

Finetune

-
model = ImagenetTransferLearning()
-trainer = Trainer()
-trainer.fit(model)
-
-
-

And use it to predict your data of interest

-
model = ImagenetTransferLearning.load_from_checkpoint(PATH)
-model.freeze()
-
-x = some_images_from_cifar10()
-predictions = model(x)
-
-
-

We used a pretrained model on imagenet, finetuned on CIFAR-10 to predict on CIFAR-10. -In the non-academic world we would finetune on a tiny dataset you have and predict on your dataset.

-
-
-
-

Example: BERT (NLP)

-

Lightning is completely agnostic to what’s used for transfer learning so long -as it is a torch.nn.Module subclass.

-

Here’s a model that uses Huggingface transformers.

-
class BertMNLIFinetuner(LightningModule):
-    def __init__(self):
-        super().__init__()
-
-        self.bert = BertModel.from_pretrained("bert-base-cased", output_attentions=True)
-        self.W = nn.Linear(bert.config.hidden_size, 3)
-        self.num_classes = 3
-
-    def forward(self, input_ids, attention_mask, token_type_ids):
-
-        h, _, attn = self.bert(input_ids=input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
-
-        h_cls = h[:, 0]
-        logits = self.W(h_cls)
-        return logits, attn
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/benchmarking/benchmarks.html b/docs/benchmarking/benchmarks.html deleted file mode 100644 index 9e91cbd..0000000 --- a/docs/benchmarking/benchmarks.html +++ /dev/null @@ -1,701 +0,0 @@ - - - - - - - - - - - - - - Benchmark with vanilla PyTorch — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Benchmark with vanilla PyTorch
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Benchmark with vanilla PyTorch

-

In this section we set grounds for comparison between vanilla PyTorch and PT Lightning for most common scenarios.

-
-

Time comparison

-

We have set regular benchmarking against PyTorch vanilla training loop on with RNN and simple MNIST classifier as per of out CI. -In average for simple MNIST CNN classifier we are only about 0.06s slower per epoch, see detail chart bellow.

-
-Speed parity to vanilla PT, created on 2020-12-16 -
-

Learn more about reproducible benchmarking from the PyTorch Reproducibility Guide.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/cli/lightning_cli.html b/docs/cli/lightning_cli.html deleted file mode 100644 index 9a471e8..0000000 --- a/docs/cli/lightning_cli.html +++ /dev/null @@ -1,784 +0,0 @@ - - - - - - - - - - - - - - Eliminate config boilerplate — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Eliminate config boilerplate
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/cli/lightning_cli_advanced.html b/docs/cli/lightning_cli_advanced.html deleted file mode 100644 index 04d3fb4..0000000 --- a/docs/cli/lightning_cli_advanced.html +++ /dev/null @@ -1,778 +0,0 @@ - - - - - - - - - - - - - - Eliminate config boilerplate (Advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Eliminate config boilerplate (Advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Eliminate config boilerplate (Advanced)

-

Audience: Users looking to modularize their code for a professional project.

-

Pre-reqs: You must have read (Control it all from the CLI).

-
-
-

What is a yaml config file?

-

A yaml is a standard configuration file that describes parameters for sections of a program. It is a common tool in engineering, and it has recently started to gain popularity in machine learning.

-
# file.yaml
-car:
-    max_speed:100
-    max_passengers:2
-plane:
-    fuel_capacity: 50
-class_3:
-    option_1: 'x'
-    option_2: 'y'
-
-
-
-
- -
-
-

Write a config yaml from the CLI

-

To have a copy of the configuration that produced this model, save a yaml file from the –print_config outputs:

-
python main.py fit --model.learning_rate 0.001 --print_config > config.yaml
-
-
-
-
-
-

Run from a single yaml

-

To run from a yaml, pass a yaml produced with --print_config to the --config argument:

-
python main.py fit --config config.yaml
-
-
-

when using a yaml to run, you can still pass in inline arguments

-
python main.py fit --config config.yaml --trainer.max_epochs 100
-
-
-
-
-
-

Compose yaml files

-

For production or complex research projects it’s advisable to have each object in its own config file. To compose all the configs, pass them all inline:

-
$ python trainer.py fit --config trainer.yaml --config datamodules.yaml --config models.yaml ...
-
-
-

The configs will be parsed sequentially. Let’s say we have two configs with the same args:

-
# trainer.yaml
-trainer:
-    num_epochs: 10
-
-
-# trainer_2.yaml
-trainer:
-    num_epochs: 20
-
-
-

the ones from the last config will be used (num_epochs = 20) in this case:

-
$ python trainer.py fit --config trainer.yaml --config trainer_2.yaml
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/cli/lightning_cli_advanced_2.html b/docs/cli/lightning_cli_advanced_2.html deleted file mode 100644 index fb799af..0000000 --- a/docs/cli/lightning_cli_advanced_2.html +++ /dev/null @@ -1,815 +0,0 @@ - - - - - - - - - - - - - - Eliminate config boilerplate (Advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Eliminate config boilerplate (Advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Eliminate config boilerplate (Advanced)

-
-

Customize arguments by command

-

To customize arguments by subcommand, pass the config before the subcommand:

-
$ python main.py [before] [subcommand] [after]
-$ python main.py  ...         fit       ...
-
-
-

For example, here we set the Trainer argument [max_steps = 100] for the full training routine and [max_steps = 10] for testing:

-
# config1.yaml
-fit:
-    trainer:
-        max_steps: 100
-test:
-    trainer:
-        max_epochs: 10
-
-
-

now you can toggle this behavior by subcommand:

-
# full routine with max_steps = 100
-$ python main.py --config config1.yaml fit
-
-# test only with max_epochs = 10
-$ python main.py --config config1.yaml test
-
-
-
-
-
-

Use groups of options

-

Groups of options can also be given as independent config files:

-
$ python trainer.py fit --trainer trainer.yaml --model model.yaml --data data.yaml [...]
-
-
-
-
-
-

Run from cloud yaml configs

-

For certain enterprise workloads, Lightning CLI supports running from hosted configs:

-
$ python trainer.py [subcommand] --config s3://bucket/config.yaml
-
-
-

For more options, refer to Remote filesystems.

-
-
-
-

Use a config via environment variables

-

For certain CI/CD systems, it’s useful to pass in config files as environment variables:

-
$ python trainer.py fit --trainer "$TRAINER_CONFIG" --model "$MODEL_CONFIG" [...]
-
-
-
-
-
-

Run from environment variables directly

-

The Lightning CLI can convert every possible CLI flag into an environment variable. To enable this, set the env_parse argument:

-
LightningCLI(env_parse=True)
-
-
-

now use the --help CLI flag with any subcommand:

-
$ python main.py fit --help
-
-
-

which will show you ALL possible environment variables you can now set:

-
usage: main.py [options] fit [-h] [-c CONFIG]
-                            [--trainer.max_epochs MAX_EPOCHS] [--trainer.min_epochs MIN_EPOCHS]
-                            [--trainer.max_steps MAX_STEPS] [--trainer.min_steps MIN_STEPS]
-                            ...
-                            [--ckpt_path CKPT_PATH]
-
-optional arguments:
-...
---model CONFIG        Path to a configuration file.
---model.out_dim OUT_DIM
-                        (type: int, default: 10)
---model.learning_rate LEARNING_RATE
-                        (type: float, default: 0.02)
-
-
-

now you can customize the behavior via environment variables:

-
# set the options via env vars
-$ export LEARNING_RATE=0.01
-$ export OUT_DIM=5
-
-$ python main.py fit
-
-
-
-
-
-

Set default config files

-

To set a path to a config file of defaults, use the default_config_files argument:

-
cli = LightningCLI(MyModel, MyDataModule, parser_kwargs={"default_config_files": ["my_cli_defaults.yaml"]})
-
-
-

or if you want defaults per subcommand:

-
cli = LightningCLI(MyModel, MyDataModule, parser_kwargs={"fit": {"default_config_files": ["my_fit_defaults.yaml"]}})
-
-
-

For more configuration options, refer to the ArgumentParser API documentation.

-
-
-
-

Enable variable interpolation

-

In certain cases where multiple configs need to share variables, consider using variable interpolation. Variable interpolation -allows you to add variables to your yaml configs like so:

-
model:
-  encoder_layers: 12
-  decoder_layers:
-  - ${model.encoder_layers}
-  - 4
-
-
-

To enable variable interpolation, first install omegaconf:

-
pip install omegaconf
-
-
-

Once this is installed, the Lightning CLI will automatically handle variables in yaml files:

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/cli/lightning_cli_advanced_3.html b/docs/cli/lightning_cli_advanced_3.html deleted file mode 100644 index cd317c9..0000000 --- a/docs/cli/lightning_cli_advanced_3.html +++ /dev/null @@ -1,1004 +0,0 @@ - - - - - - - - - - - - - - Instantiation only mode — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Instantiation only mode
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Instantiation only mode

-

The CLI is designed to start fitting with minimal code changes. On class instantiation, the CLI will automatically -call the trainer function associated to the subcommand provided so you don’t have to do it. -To avoid this, you can set the following argument:

-
cli = LightningCLI(MyModel, run=False)  # True by default
-# you'll have to call fit yourself:
-cli.trainer.fit(cli.model)
-
-
-

In this mode, there are subcommands added to the parser. -This can be useful to implement custom logic without having to subclass the CLI, but still using the CLI’s instantiation -and argument parsing capabilities.

-
-
-

Subclass registration

-

To use shorthand notation, the options need to be registered beforehand. This can be easily done with:

-
LightningCLI(auto_registry=True)  # False by default
-
-
-

which will register all subclasses of torch.optim.Optimizer, torch.optim.lr_scheduler._LRScheduler, -LightningModule, -LightningDataModule, Callback, and -LightningLoggerBase across all imported modules. This includes those in your own -code.

-

Alternatively, if this is left unset, only the subclasses defined in PyTorch’s torch.optim.Optimizer, -torch.optim.lr_scheduler._LRScheduler and Lightning’s Callback and -LightningLoggerBase subclassess will be registered.

-

In subsequent sections, we will go over adding specific classes to specific registries as well as how to use -shorthand notation.

-
-
-

Trainer Callbacks and arguments with class type

-

A very important argument of the Trainer class is the callbacks. In -contrast to other more simple arguments which just require numbers or strings, callbacks expects a list of -instances of subclasses of Callback. To specify this kind of argument in a config -file, each callback must be given as a dictionary including a class_path entry with an import path of the class, -and optionally an init_args entry with arguments required to instantiate it. Therefore, a simple configuration -file example that defines a couple of callbacks is the following:

-
trainer:
-  callbacks:
-    - class_path: pytorch_lightning.callbacks.EarlyStopping
-      init_args:
-        patience: 5
-    - class_path: pytorch_lightning.callbacks.LearningRateMonitor
-      init_args:
-        ...
-
-
-

Similar to the callbacks, any arguments in Trainer and user extended -LightningModule and -LightningDataModule classes that have as type hint a class can be configured -the same way using class_path and init_args.

-

For callbacks in particular, Lightning simplifies the command line so that only -the Callback name is required. -The argument’s order matters and the user needs to pass the arguments in the following way.

-
$ python ... \
-    --trainer.callbacks={CALLBACK_1_NAME} \
-    --trainer.callbacks.{CALLBACK_1_ARGS_1}=... \
-    --trainer.callbacks.{CALLBACK_1_ARGS_2}=... \
-    ...
-    --trainer.callbacks={CALLBACK_N_NAME} \
-    --trainer.callbacks.{CALLBACK_N_ARGS_1}=... \
-    ...
-
-
-

Here is an example:

-
$ python ... \
-    --trainer.callbacks=EarlyStopping \
-    --trainer.callbacks.patience=5 \
-    --trainer.callbacks=LearningRateMonitor \
-    --trainer.callbacks.logging_interval=epoch
-
-
-

Lightning provides a mechanism for you to add your own callbacks and benefit from the command line simplification -as described above:

-
from pytorch_lightning.utilities.cli import CALLBACK_REGISTRY
-
-
-@CALLBACK_REGISTRY
-class CustomCallback(Callback):
-    ...
-
-
-cli = LightningCLI(...)
-
-
-
$  python ... --trainer.callbacks=CustomCallback ...
-
-
-
-

Note

-

This shorthand notation is only supported in the shell and not inside a configuration file. The configuration file -generated by calling the previous command with --print_config will have the class_path notation.

-
trainer:
-  callbacks:
-    - class_path: your_class_path.CustomCallback
-      init_args:
-        ...
-
-
-
-
-

Tip

-

--trainer.logger also supports shorthand notation and a LOGGER_REGISTRY is available to register custom -Loggers.

-
-
-
-

Multiple models and/or datasets

-

Additionally, the tool can be configured such that a model and/or a datamodule is -specified by an import path and init arguments. For example, with a tool implemented as:

-
cli = LightningCLI(MyModelBaseClass, MyDataModuleBaseClass, subclass_mode_model=True, subclass_mode_data=True)
-
-
-

A possible config file could be as follows:

-
model:
-  class_path: mycode.mymodels.MyModel
-  init_args:
-    decoder_layers:
-    - 2
-    - 4
-    encoder_layers: 12
-data:
-  class_path: mycode.mydatamodules.MyDataModule
-  init_args:
-    ...
-trainer:
-  callbacks:
-    - class_path: pytorch_lightning.callbacks.EarlyStopping
-      init_args:
-        patience: 5
-    ...
-
-
-

Only model classes that are a subclass of MyModelBaseClass would be allowed, and similarly only subclasses of -MyDataModuleBaseClass. If as base classes LightningModule and -LightningDataModule are given, then the tool would allow any lightning -module and data module.

-
-

Tip

-

Note that with the subclass modes the --help option does not show information for a specific subclass. To -get help for a subclass the options --model.help and --data.help can be used, followed by the -desired class path. Similarly --print_config does not include the settings for a particular subclass. To -include them the class path should be given before the --print_config option. Examples for both help and -print config are:

-
$ python trainer.py fit --model.help mycode.mymodels.MyModel
-$ python trainer.py fit --model mycode.mymodels.MyModel --print_config
-
-
-
-
-
-

Models with multiple submodules

-

Many use cases require to have several modules each with its own configurable options. One possible way to handle this -with LightningCLI is to implement a single module having as init parameters each of the submodules. Since the init -parameters have as type a class, then in the configuration these would be specified with class_path and -init_args entries. For instance a model could be implemented as:

-
class MyMainModel(LightningModule):
-    def __init__(self, encoder: nn.Module, decoder: nn.Module):
-        """Example encoder-decoder submodules model
-
-        Args:
-            encoder: Instance of a module for encoding
-            decoder: Instance of a module for decoding
-        """
-        super().__init__()
-        self.encoder = encoder
-        self.decoder = decoder
-
-
-

If the CLI is implemented as LightningCLI(MyMainModel) the configuration would be as follows:

-
model:
-  encoder:
-    class_path: mycode.myencoders.MyEncoder
-    init_args:
-      ...
-  decoder:
-    class_path: mycode.mydecoders.MyDecoder
-    init_args:
-      ...
-
-
-

It is also possible to combine subclass_mode_model=True and submodules, thereby having two levels of -class_path.

-
-
-

Class type defaults

-

The support for classes as type hints allows to try many possibilities with the same CLI. This is a useful feature, but -it can make it tempting to use an instance of a class as a default. For example:

-
class MyMainModel(LightningModule):
-    def __init__(
-        self,
-        backbone: torch.nn.Module = MyModel(encoder_layers=24),  # BAD PRACTICE!
-    ):
-        super().__init__()
-        self.backbone = backbone
-
-
-

Normally classes are mutable as it is in this case. The instance of MyModel would be created the moment that the -module that defines MyMainModel is first imported. This means that the default of backbone will be -initialized before the CLI class runs seed_everything making it non-reproducible. Furthermore, if -MyMainModel is used more than once in the same Python process and the backbone parameter is not -overridden, the same instance would be used in multiple places which very likely is not what the developer intended. -Having an instance as default also makes it impossible to generate the complete config file since for arbitrary classes -it is not known which arguments were used to instantiate it.

-

A good solution to these problems is to not have a default or set the default to a special value (e.g. a -string) which would be checked in the init and instantiated accordingly. If a class parameter has no default and the CLI -is subclassed then a default can be set as follows:

-
default_backbone = {
-    "class_path": "import.path.of.MyModel",
-    "init_args": {
-        "encoder_layers": 24,
-    },
-}
-
-
-class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.set_defaults({"model.backbone": default_backbone})
-
-
-

A more compact version that avoids writing a dictionary would be:

-
from jsonargparse import lazy_instance
-
-
-class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.set_defaults({"model.backbone": lazy_instance(MyModel, encoder_layers=24)})
-
-
-
-
-

Optimizers

-

If you will not be changing the class, you can manually add the arguments for specific optimizers and/or -learning rate schedulers by subclassing the CLI. This has the advantage of providing the proper help message for those -classes. The following code snippet shows how to implement it:

-
class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.add_optimizer_args(torch.optim.Adam)
-        parser.add_lr_scheduler_args(torch.optim.lr_scheduler.ExponentialLR)
-
-
-

With this, in the config the optimizer and lr_scheduler groups would accept all of the options for the -given classes, in this example Adam and ExponentialLR. -Therefore, the config file would be structured like:

-
optimizer:
-  lr: 0.01
-lr_scheduler:
-  gamma: 0.2
-model:
-  ...
-trainer:
-  ...
-
-
-

Where the arguments can be passed directly through command line without specifying the class. For example:

-
$ python trainer.py fit --optimizer.lr=0.01 --lr_scheduler.gamma=0.2
-
-
-

The automatic implementation of configure_optimizers can be disabled by linking the configuration group. An -example can be when one wants to add support for multiple optimizers:

-
from pytorch_lightning.utilities.cli import instantiate_class
-
-
-class MyModel(LightningModule):
-    def __init__(self, optimizer1_init: dict, optimizer2_init: dict):
-        super().__init__()
-        self.optimizer1_init = optimizer1_init
-        self.optimizer2_init = optimizer2_init
-
-    def configure_optimizers(self):
-        optimizer1 = instantiate_class(self.parameters(), self.optimizer1_init)
-        optimizer2 = instantiate_class(self.parameters(), self.optimizer2_init)
-        return [optimizer1, optimizer2]
-
-
-class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.add_optimizer_args(
-            OPTIMIZER_REGISTRY.classes, nested_key="gen_optimizer", link_to="model.optimizer1_init"
-        )
-        parser.add_optimizer_args(
-            OPTIMIZER_REGISTRY.classes, nested_key="gen_discriminator", link_to="model.optimizer2_init"
-        )
-
-
-cli = MyLightningCLI(MyModel)
-
-
-

The value given to optimizer*_init will always be a dictionary including class_path and -init_args entries. The function instantiate_class() -takes care of importing the class defined in class_path and instantiating it using some positional arguments, -in this case self.parameters(), and the init_args. -Any number of optimizers and learning rate schedulers can be added when using link_to.

-

With shorthand notation:

-
$ python trainer.py fit \
-    --gen_optimizer=Adam \
-    --gen_optimizer.lr=0.01 \
-    --gen_discriminator=AdamW \
-    --gen_discriminator.lr=0.0001
-
-
-

You can also pass the class path directly, for example, if the optimizer hasn’t been registered to the -OPTIMIZER_REGISTRY:

-
$ python trainer.py fit \
-    --gen_optimizer.class_path=torch.optim.Adam \
-    --gen_optimizer.init_args.lr=0.01 \
-    --gen_discriminator.class_path=torch.optim.AdamW \
-    --gen_discriminator.init_args.lr=0.0001
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/cli/lightning_cli_expert.html b/docs/cli/lightning_cli_expert.html deleted file mode 100644 index 1d4599e..0000000 --- a/docs/cli/lightning_cli_expert.html +++ /dev/null @@ -1,870 +0,0 @@ - - - - - - - - - - - - - - Eliminate config boilerplate (Advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Eliminate config boilerplate (Advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Eliminate config boilerplate (Advanced)

-

Audience: Users who already understand the LightningCLI and want to customize it.

-
-
-

Customize the LightningCLI

-

The init parameters of the LightningCLI class can be used to customize some -things, namely: the description of the tool, enabling parsing of environment variables and additional arguments to -instantiate the trainer and configuration parser.

-

Nevertheless the init arguments are not enough for many use cases. For this reason the class is designed so that can be -extended to customize different parts of the command line tool. The argument parser class used by -LightningCLI is -LightningArgumentParser which is an extension of python’s argparse, thus -adding arguments can be done using the add_argument() method. In contrast to argparse it has additional methods to -add arguments, for example add_class_arguments() adds all arguments from the init of a class, though requiring -parameters to have type hints. For more details about this please refer to the respective documentation.

-

The LightningCLI class has the -add_arguments_to_parser() method which can be implemented to include -more arguments. After parsing, the configuration is stored in the config attribute of the class instance. The -LightningCLI class also has two methods that can be used to run code before -and after the trainer runs: before_<subcommand> and after_<subcommand>. -A realistic example for these would be to send an email before and after the execution. -The code for the fit subcommand would be something like:

-
class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.add_argument("--notification_email", default="will@email.com")
-
-    def before_fit(self):
-        send_email(address=self.config["notification_email"], message="trainer.fit starting")
-
-    def after_fit(self):
-        send_email(address=self.config["notification_email"], message="trainer.fit finished")
-
-
-cli = MyLightningCLI(MyModel)
-
-
-

Note that the config object self.config is a dictionary whose keys are global options or groups of options. It -has the same structure as the yaml format described previously. This means for instance that the parameters used for -instantiating the trainer class can be found in self.config['fit']['trainer'].

-
-

Tip

-

Have a look at the LightningCLI class API reference to learn about other -methods that can be extended to customize a CLI.

-
-
-
-
-

Configure forced callbacks

-

As explained previously, any Lightning callback can be added by passing it through command line or -including it in the config via class_path and init_args entries.

-

However, certain callbacks MUST be coupled with a model so they are always present and configurable. -This can be implemented as follows:

-
from pytorch_lightning.callbacks import EarlyStopping
-
-
-class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.add_lightning_class_args(EarlyStopping, "my_early_stopping")
-        parser.set_defaults({"my_early_stopping.monitor": "val_loss", "my_early_stopping.patience": 5})
-
-
-cli = MyLightningCLI(MyModel)
-
-
-

To change the configuration of the EarlyStopping in the config it would be:

-
model:
-  ...
-trainer:
-  ...
-my_early_stopping:
-  patience: 5
-
-
-
-

Note

-

The example above overrides a default in add_arguments_to_parser. This is included to show that defaults can -be changed if needed. However, note that overriding of defaults in the source code is not intended to be used to -store the best hyperparameters for a task after experimentation. To ease reproducibility the source code should be -stable. It is better practice to store the best hyperparameters for a task in a configuration file independent from -the source code.

-
-
-
-
-

Class type defaults

-

The support for classes as type hints allows to try many possibilities with the same CLI. This is a useful feature, but -it can make it tempting to use an instance of a class as a default. For example:

-
class MyMainModel(LightningModule):
-    def __init__(
-        self,
-        backbone: torch.nn.Module = MyModel(encoder_layers=24),  # BAD PRACTICE!
-    ):
-        super().__init__()
-        self.backbone = backbone
-
-
-

Normally classes are mutable as it is in this case. The instance of MyModel would be created the moment that the -module that defines MyMainModel is first imported. This means that the default of backbone will be -initialized before the CLI class runs seed_everything making it non-reproducible. Furthermore, if -MyMainModel is used more than once in the same Python process and the backbone parameter is not -overridden, the same instance would be used in multiple places which very likely is not what the developer intended. -Having an instance as default also makes it impossible to generate the complete config file since for arbitrary classes -it is not known which arguments were used to instantiate it.

-

A good solution to these problems is to not have a default or set the default to a special value (e.g. a -string) which would be checked in the init and instantiated accordingly. If a class parameter has no default and the CLI -is subclassed then a default can be set as follows:

-
default_backbone = {
-    "class_path": "import.path.of.MyModel",
-    "init_args": {
-        "encoder_layers": 24,
-    },
-}
-
-
-class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.set_defaults({"model.backbone": default_backbone})
-
-
-

A more compact version that avoids writing a dictionary would be:

-
from jsonargparse import lazy_instance
-
-
-class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.set_defaults({"model.backbone": lazy_instance(MyModel, encoder_layers=24)})
-
-
-
-
-
-

Connect two config files

-

Another case in which it might be desired to extend LightningCLI is that the -model and data module depend on a common parameter. For example in some cases both classes require to know the -batch_size. It is a burden and error prone giving the same value twice in a config file. To avoid this the -parser can be configured so that a value is only given once and then propagated accordingly. With a tool implemented -like shown below, the batch_size only has to be provided in the data section of the config.

-
class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.link_arguments("data.batch_size", "model.batch_size")
-
-
-cli = MyLightningCLI(MyModel, MyDataModule)
-
-
-

The linking of arguments is observed in the help of the tool, which for this example would look like:

-
$ python trainer.py fit --help
-  ...
-    --data.batch_size BATCH_SIZE
-                          Number of samples in a batch (type: int, default: 8)
-
-  Linked arguments:
-    model.batch_size <-- data.batch_size
-                          Number of samples in a batch (type: int)
-
-
-

Sometimes a parameter value is only available after class instantiation. An example could be that your model requires -the number of classes to instantiate its fully connected layer (for a classification task) but the value is not -available until the data module has been instantiated. The code below illustrates how to address this.

-
class MyLightningCLI(LightningCLI):
-    def add_arguments_to_parser(self, parser):
-        parser.link_arguments("data.num_classes", "model.num_classes", apply_on="instantiate")
-
-
-cli = MyLightningCLI(MyClassModel, MyDataModule)
-
-
-

Instantiation links are used to automatically determine the order of instantiation, in this case data first.

-
-

Tip

-

The linking of arguments can be used for more complex cases. For example to derive a value via a function that takes -multiple settings as input. For more details have a look at the API of link_arguments.

-
-

The linking of arguments is intended for things that are meant to be non-configurable. This improves the CLI user -experience since it avoids the need for providing more parameters. A related concept is -variable interpolation which in contrast keeps things being configurable.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/cli/lightning_cli_faq.html b/docs/cli/lightning_cli_faq.html deleted file mode 100644 index 2f48f3a..0000000 --- a/docs/cli/lightning_cli_faq.html +++ /dev/null @@ -1,768 +0,0 @@ - - - - - - - - - - - - - - Eliminate config boilerplate (expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Eliminate config boilerplate (expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Eliminate config boilerplate (expert)

-
-

Troubleshooting

-

The standard behavior for CLIs, when they fail, is to terminate the process with a non-zero exit code and a short message -to hint the user about the cause. This is problematic while developing the CLI since there is no information to track -down the root of the problem. A simple change in the instantiation of the LightningCLI can be used such that when -there is a failure an exception is raised and the full stack trace printed.

-
cli = LightningCLI(MyModel, parser_kwargs={"error_handler": None})
-
-
-
-

Note

-

When asking about problems and reporting issues please set the error_handler to None and include the stack -trace in your description. With this, it is more likely for people to help out identifying the cause without needing -to create a reproducible script.

-
-
-
-
-

Reproducibility with the LightningCLI

-

The topic of reproducibility is complex and it is impossible to guarantee reproducibility by just providing a class that -people can use in unexpected ways. Nevertheless, the LightningCLI tries to -give a framework and recommendations to make reproducibility simpler.

-

When an experiment is run, it is good practice to use a stable version of the source code, either being a released -package or at least a commit of some version controlled repository. For each run of a CLI the config file is -automatically saved including all settings. This is useful to figure out what was done for a particular run without -requiring to look at the source code. If by mistake the exact version of the source code is lost or some defaults -changed, having the full config means that most of the information is preserved.

-

The class is targeted at implementing CLIs because running a command from a shell provides a separation with the Python -source code. Ideally the CLI would be placed in your path as part of the installation of a stable package, instead of -running from a clone of a repository that could have uncommitted local modifications. Creating installable packages that -include CLIs is out of the scope of this document. This is mentioned only as a teaser for people who would strive for -the best practices possible.

-

For every CLI implemented, users are encouraged to learn how to run it by reading the documentation printed with the ---help option and use the --print_config option to guide the writing of config files. A few more details -that might not be clear by only reading the help are the following.

-

LightningCLI is based on argparse and as such follows the same arguments style -as many POSIX command line tools. Long options are prefixed with two dashes and its corresponding values should be -provided with an empty space or an equal sign, as --option value or --option=value. Command line options -are parsed from left to right, therefore if a setting appears multiple times the value most to the right will override -the previous ones. If a class has an init parameter that is required (i.e. no default value), it is given as ---option which makes it explicit and more readable instead of relying on positional arguments.

-
-
-
-

What is a subcommand?

-

A subcommand is what is the action the LightningCLI applies to the script:

-
python main.py [subcommand]
-
-
-

See the Potential subcommands with:

-
python main.py --help
-
-
-

which prints:

-
...
-
-fit                 Runs the full optimization routine.
-validate            Perform one evaluation epoch over the validation set.
-test                Perform one evaluation epoch over the test set.
-predict             Run inference on your data.
-tune                Runs routines to tune hyperparameters before training.
-
-
-

use a subcommand as follows:

-
python main.py fit
-python main.py test
-
-
-
-
-
-

What is the CLI?

-

CLI is short for commandline interface. Use your terminal to enter these commands.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/cli/lightning_cli_intermediate.html b/docs/cli/lightning_cli_intermediate.html deleted file mode 100644 index b81f240..0000000 --- a/docs/cli/lightning_cli_intermediate.html +++ /dev/null @@ -1,862 +0,0 @@ - - - - - - - - - - - - - - Eliminate config boilerplate (Intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Eliminate config boilerplate (Intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Eliminate config boilerplate (Intermediate)

-

Audience: Users who want advanced modularity via the commandline interface (CLI).

-

Pre-reqs: You must already understand how to use a commandline and LightningDataModule.

-
-
-

What is config boilerplate?

-

As Lightning projects grow in complexity it becomes desirable to enable full customizability from the commandline (CLI) so you can -change any hyperparameters without changing your code:

-
# Mix and match anything
-$ python main.py --command fit --model.learning_rate 0.02
-$ python main.py --command fit --model.learning_rate 0.01 --trainer.fast_dev_run True
-
-
-

This is what the Lightning CLI enables. Without the Lightning CLI, you usually end up with a TON of boilerplate that looks like this:

-
from argparse import ArgumentParser
-
-if __name__ == "__main__":
-    parser = ArgumentParser()
-    parser.add_argument("--learning_rate_1", default=0.02)
-    parser.add_argument("--learning_rate_2", default=0.03)
-    parser.add_argument("--model", default="cnn")
-    parser.add_argument("--command", default="fit")
-    parser.add_argument("--run_fast", default=True)
-    ...
-    # add 100 more of these
-    ...
-
-    args = parser.parse_args()
-
-    if args.model == "cnn":
-        model = ConvNet(learning_rate=args.learning_rate_1)
-    elif args.model == "transformer":
-        model = Transformer(learning_rate=args.learning_rate_2)
-    trainer = Trainer(fast_dev_run=args.run_fast)
-    ...
-
-    if args.command == "fit":
-        trainer.fit()
-    elif args.command == "test":
-        ...
-
-
-

This kind of boilerplate is unsustainable as projects grow in complexity.

-
-
-
-

Enable the Lightning CLI

-

To enable the Lightning CLI install the extras:

-
pip install pytorch-lightning[extra]
-
-
-

if the above fails, only install jsonargparse:

-
pip install -U jsonargparse[signatures]
-
-
-
-
-
-

Connect a model to the CLI

-

The simplest way to control a model with the CLI is to wrap it in the LightningCLI object:

-
# main.py
-
-import torch
-from pytorch_lightning.utilities.cli import LightningCLI
-from pytorch_lightning import LightningModule, demos
-
-
-class DemoModel(LightningModule):
-    def __init__(self, out_dim: int = 10, learning_rate: float = 0.02):
-        super().__init__()
-        self.l1 = torch.nn.Linear(32, out_dim)
-        self.learning_rate = learning_rate
-
-    def forward(self, x):
-        return torch.relu(self.l1(x.view(x.size(0), -1)))
-
-    def training_step(self, batch, batch_nb):
-        x = batch
-        x = self(x)
-        loss = x.sum()
-        return loss
-
-    def configure_optimizers(self):
-        return torch.optim.Adam(self.parameters(), lr=self.learning_rate)
-
-
-cli = LightningCLI(DemoModel, demos.BoringDataModule)
-# don't call fit!!
-
-
-

Now your model can be managed via the CLI. To see the available commands type:

-
$ python main.py --help
-
-
-

Which prints out:

-
usage: a.py [-h] [-c CONFIG] [--print_config [={comments,skip_null,skip_default}+]]
-        {fit,validate,test,predict,tune} ...
-
-pytorch-lightning trainer command line tool
-
-optional arguments:
--h, --help            Show this help message and exit.
--c CONFIG, --config CONFIG
-                        Path to a configuration file in json or yaml format.
---print_config [={comments,skip_null,skip_default}+]
-                        Print configuration and exit.
-
-subcommands:
-For more details of each subcommand add it as argument followed by --help.
-
-{fit,validate,test,predict,tune}
-    fit                 Runs the full optimization routine.
-    validate            Perform one evaluation epoch over the validation set.
-    test                Perform one evaluation epoch over the test set.
-    predict             Run inference on your data.
-    tune                Runs routines to tune hyperparameters before training.
-
-
-

the message tells us that we have a few available subcommands:

-
python main.py [subcommand]
-
-
-

which you can use depending on your use case:

-
$ python main.py fit
-$ python main.py validate
-$ python main.py test
-$ python main.py predict
-$ python main.py tune
-
-
-
-
-
-

Train a model with the CLI

-

To run the full training routine (train, val, test), use the subcommand fit:

-
python main.py fit
-
-
-

View all available options with the --help command:

-
usage: main.py [options] fit [-h] [-c CONFIG]
-                            [--seed_everything SEED_EVERYTHING] [--trainer CONFIG]
-                            ...
-                            [--ckpt_path CKPT_PATH]
-    --trainer.logger LOGGER
-
-optional arguments:
-<class '__main__.DemoModel'>:
-    --model.out_dim OUT_DIM
-                            (type: int, default: 10)
-    --model.learning_rate LEARNING_RATE
-                            (type: float, default: 0.02)
-<class 'pytorch_lightning.demos.boring_classes.BoringDataModule'>:
---data CONFIG         Path to a configuration file.
---data.data_dir DATA_DIR
-                        (type: str, default: ./)
-
-
-

With the Lightning CLI enabled, you can now change the parameters without touching your code:

-
# change the learning_rate
-python main.py fit --model.out_dim 30
-
-# change the out dimensions also
-python main.py fit --model.out_dim 10 --model.learning_rate 0.1
-
-# change trainer and data arguments too
-python main.py fit --model.out_dim 2 --model.learning_rate 0.1 --data.data_dir '~/' --trainer.logger False
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/cli/lightning_cli_intermediate_2.html b/docs/cli/lightning_cli_intermediate_2.html deleted file mode 100644 index 3c176d9..0000000 --- a/docs/cli/lightning_cli_intermediate_2.html +++ /dev/null @@ -1,900 +0,0 @@ - - - - - - - - - - - - - - Eliminate config boilerplate (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Eliminate config boilerplate (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Eliminate config boilerplate (intermediate)

-

Audience: Users who have multiple models and datasets per project.

-

Pre-reqs: You must have read (Control it all from the CLI).

-
-
-

Why do I want to mix models and datasets

-

Lightning projects usually begin with one model and one dataset. As the project grows in complexity and you introduce more models and more datasets, it becomes desirable -to mix any model with any dataset directly from the commandline without changing your code.

-
# Mix and match anything
-$ python main.py fit --model=GAN --data=MNIST
-$ python main.py fit --model=Transformer --data=MNIST
-
-
-

This is what the Lightning CLI enables. Otherwise, this kind of configuration requires a significant amount of boilerplate that often looks like this:

-
# choose model
-if args.model == "gan":
-    model = GAN(args.feat_dim)
-elif args.model == "transformer":
-    model = Transformer(args.feat_dim)
-...
-
-# choose datamodule
-if args.data == "MNIST":
-    datamodule = MNIST()
-elif args.data == "imagenet":
-    datamodule = Imagenet()
-...
-
-# mix them!
-trainer.fit(model, datamodule)
-
-
-
-
-
-

Register LightningModules

-

Connect models across different files with the MODEL_REGISTRY to make them available from the CLI:

-
# main.py
-
-from pytorch_lightning import demos
-from pytorch_lightning.utilities import cli as pl_cli
-
-
-@pl_cli.MODEL_REGISTRY
-class Model1(demos.DemoModel):
-    def configure_optimizers(self):
-        print("⚡", "using Model1", "⚡")
-        return super().configure_optimizers()
-
-
-@pl_cli.MODEL_REGISTRY
-class Model2(demos.DemoModel):
-    def configure_optimizers(self):
-        print("⚡", "using Model2", "⚡")
-        return super().configure_optimizers()
-
-
-cli = pl_cli.LightningCLI(datamodule_class=demos.BoringDataModule)
-
-
-

Now you can choose between any model from the CLI:

-
# use Model1
-python main.py fit --model Model1
-
-# use Model2
-python main.py fit --model Model2
-
-
-
-
-
-

Register DataModules

-

Connect DataModules across different files with the DATAMODULE_REGISTRY to make them available from the CLI:

-
# main.py
-import torch
-from pytorch_lightning.utilities import cli as pl_cli
-from pytorch_lightning import demos
-
-
-@pl_cli.DATAMODULE_REGISTRY
-class FakeDataset1(demos.BoringDataModule):
-    def train_dataloader(self):
-        print("⚡", "using FakeDataset1", "⚡")
-        return torch.utils.data.DataLoader(self.random_train)
-
-
-@pl_cli.DATAMODULE_REGISTRY
-class FakeDataset2(demos.BoringDataModule):
-    def train_dataloader(self):
-        print("⚡", "using FakeDataset2", "⚡")
-        return torch.utils.data.DataLoader(self.random_train)
-
-
-cli = pl_cli.LightningCLI(demos.DemoModel)
-
-
-

Now you can choose between any dataset at runtime:

-
# use Model1
-python main.py fit --data FakeDataset1
-
-# use Model2
-python main.py fit --data FakeDataset2
-
-
-
-
-
-

Register optimizers

-

Connect optimizers with the OPTIMIZER_REGISTRY to make them available from the CLI:

-
# main.py
-import torch
-from pytorch_lightning.utilities import cli as pl_cli
-from pytorch_lightning import demos
-
-
-@pl_cli.OPTIMIZER_REGISTRY
-class LitAdam(torch.optim.Adam):
-    def step(self, closure):
-        print("⚡", "using LitAdam", "⚡")
-        super().step(closure)
-
-
-@pl_cli.OPTIMIZER_REGISTRY
-class FancyAdam(torch.optim.Adam):
-    def step(self, closure):
-        print("⚡", "using FancyAdam", "⚡")
-        super().step(closure)
-
-
-cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule)
-
-
-

Now you can choose between any optimizer at runtime:

-
# use LitAdam
-python main.py fit --optimizer LitAdam
-
-# use FancyAdam
-python main.py fit --optimizer FancyAdam
-
-
-

Bonus: If you need only 1 optimizer, the Lightning CLI already works out of the box with any Optimizer from torch.optim.optim:

-
python main.py fit --optimizer AdamW
-
-
-

If the optimizer you want needs other arguments, add them via the CLI (no need to change your code)!

-
python main.py fit --optimizer SGD --optimizer.lr=0.01
-
-
-
-
-
-

Register LR schedulers

-

Connect learning rate schedulers with the LR_SCHEDULER_REGISTRY to make them available from the CLI:

-
# main.py
-import torch
-from pytorch_lightning.utilities import cli as pl_cli
-from pytorch_lightning import demos
-
-
-@pl_cli.LR_SCHEDULER_REGISTRY
-class LitLRScheduler(torch.optim.lr_scheduler.CosineAnnealingLR):
-    def step(self):
-        print("⚡", "using LitLRScheduler", "⚡")
-        super().step()
-
-
-cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule)
-
-
-

Now you can choose between any learning rate scheduler at runtime:

-
# LitLRScheduler
-python main.py fit --lr_scheduler LitLRScheduler
-
-
-

Bonus: If you need only 1 LRScheduler, the Lightning CLI already works out of the box with any LRScheduler from torch.optim:

-
python main.py fit --lr_scheduler CosineAnnealingLR
-python main.py fit --lr_scheduler LinearLR
-...
-
-
-

If the scheduler you want needs other arguments, add them via the CLI (no need to change your code)!

-
python main.py fit --lr_scheduler=ReduceLROnPlateau --lr_scheduler.monitor=epoch
-
-
-
-
-
-

Register from any package

-

A shortcut to register many classes from a package is to use the register_classes method. Here we register all optimizers from the torch.optim library:

-
import torch
-from pytorch_lightning.utilities import cli as pl_cli
-from pytorch_lightning import demos
-
-# add all PyTorch optimizers!
-pl_cli.OPTIMIZER_REGISTRY.register_classes(module=torch.optim, base_cls=torch.optim.Optimizer)
-
-cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule)
-
-
-

Now use any of the optimizers in the torch.optim library:

-
python main.py fit --optimizer AdamW
-
-
-

This method is supported by all the registry classes.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/cloud_training.html b/docs/clouds/cloud_training.html deleted file mode 100644 index 9084678..0000000 --- a/docs/clouds/cloud_training.html +++ /dev/null @@ -1,760 +0,0 @@ - - - - - - - - - - - - - - Train on the cloud — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train on the cloud
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train on the cloud

-

Audience: Users who want to develop and train models on the cloud (public cloud, private cloud or onprem clusters).

-
-
-
-
-

Grid.ai is the official cloud training solution for PyTorch Lightning. Grid is designed to support researcher workloads at both academic labs and major companies.

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/cloud_training_intermediate.html b/docs/clouds/cloud_training_intermediate.html deleted file mode 100644 index f968b8b..0000000 --- a/docs/clouds/cloud_training_intermediate.html +++ /dev/null @@ -1,688 +0,0 @@ - - - - - - - - - - - - - - Train on the cloud (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train on the cloud (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train on the cloud (intermediate)

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/cluster.html b/docs/clouds/cluster.html deleted file mode 100644 index 5c6673e..0000000 --- a/docs/clouds/cluster.html +++ /dev/null @@ -1,741 +0,0 @@ - - - - - - - - - - - - - - Run on an on-prem cluster — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Run on an on-prem cluster
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/cluster_advanced.html b/docs/clouds/cluster_advanced.html deleted file mode 100644 index b6d898f..0000000 --- a/docs/clouds/cluster_advanced.html +++ /dev/null @@ -1,882 +0,0 @@ - - - - - - - - - - - - - - Run on an on-prem cluster (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Run on an on-prem cluster (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Run on an on-prem cluster (advanced)

-
-
-

Run on a SLRUM managed cluster

-

Lightning automates the details behind training on a SLURM-powered cluster. In contrast to the general purpose -cluster above, the user does not start the jobs manually on each node and instead submits it to SLURM which -schedules the resources and time for which the job is allowed to run.

-
-
-
-

Design your training script

-

To train a model using multiple nodes, do the following:

-
    -
  1. Design your LightningModule (no need to add anything specific here).

  2. -
  3. Enable DDP in the trainer

    -
    # train on 32 GPUs across 4 nodes
    -trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp")
    -
    -
    -
  4. -
  5. It’s a good idea to structure your training script like this:

    -
    # train.py
    -def main(hparams):
    -    model = LightningTemplateModel(hparams)
    -
    -    trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp")
    -
    -    trainer.fit(model)
    -
    -
    -if __name__ == "__main__":
    -    root_dir = os.path.dirname(os.path.realpath(__file__))
    -    parent_parser = ArgumentParser(add_help=False)
    -    hyperparams = parser.parse_args()
    -
    -    # TRAIN
    -    main(hyperparams)
    -
    -
    -
  6. -
  7. Create the appropriate SLURM job:

    -
    # (submit.sh)
    -#!/bin/bash -l
    -
    -# SLURM SUBMIT SCRIPT
    -#SBATCH --nodes=4
    -#SBATCH --gres=gpu:8
    -#SBATCH --ntasks-per-node=8
    -#SBATCH --mem=0
    -#SBATCH --time=0-02:00:00
    -
    -# activate conda env
    -source activate $1
    -
    -# debugging flags (optional)
    -export NCCL_DEBUG=INFO
    -export PYTHONFAULTHANDLER=1
    -
    -# on your cluster you might need these:
    -# set the network interface
    -# export NCCL_SOCKET_IFNAME=^docker0,lo
    -
    -# might need the latest CUDA
    -# module load NCCL/2.4.7-1-cuda.10.0
    -
    -# run script from above
    -srun python3 train.py
    -
    -
    -
  8. -
  9. If you want auto-resubmit (read below), add this line to the submit.sh script

    -
    #SBATCH --signal=SIGUSR1@90
    -
    -
    -
  10. -
  11. Submit the SLURM job

    -
    sbatch submit.sh
    -
    -
    -
  12. -
-
-
-
-

Enable auto wall-time resubmitions

-

When you use Lightning in a SLURM cluster, it automatically detects when it is about -to run into the wall time and does the following:

-
    -
  1. Saves a temporary checkpoint.

  2. -
  3. Requeues the job.

  4. -
  5. When the job starts, it loads the temporary checkpoint.

  6. -
-

To get this behavior make sure to add the correct signal to your SLURM script

-
# 90 seconds before training ends
-SBATCH --signal=SIGUSR1@90
-
-
-

If auto-resubmit is not desired, it can be turned off in the SLURMEnvironment plugin:

-
from pytorch_lightning.plugins.environments import SLURMEnvironment
-
-trainer = Trainer(plugins=[SLURMEnvironment(auto_requeue=False)])
-
-
-
-
-
-

Build your SLURM script

-

Instead of manually building SLURM scripts, you can use the -SlurmCluster object -to do this for you. The SlurmCluster can also run a grid search if you pass -in a HyperOptArgumentParser.

-

Here is an example where you run a grid search of 9 combinations of hyperparameters. -See also the multi-node examples -here.

-
# grid search 3 values of learning rate and 3 values of number of layers for your net
-# this generates 9 experiments (lr=1e-3, layers=16), (lr=1e-3, layers=32),
-# (lr=1e-3, layers=64), ... (lr=1e-1, layers=64)
-parser = HyperOptArgumentParser(strategy="grid_search", add_help=False)
-parser.opt_list("--learning_rate", default=0.001, type=float, options=[1e-3, 1e-2, 1e-1], tunable=True)
-parser.opt_list("--layers", default=1, type=float, options=[16, 32, 64], tunable=True)
-hyperparams = parser.parse_args()
-
-# Slurm cluster submits 9 jobs, each with a set of hyperparams
-cluster = SlurmCluster(
-    hyperparam_optimizer=hyperparams,
-    log_path="/some/path/to/save",
-)
-
-# OPTIONAL FLAGS WHICH MAY BE CLUSTER DEPENDENT
-# which interface your nodes use for communication
-cluster.add_command("export NCCL_SOCKET_IFNAME=^docker0,lo")
-
-# see the output of the NCCL connection process
-# NCCL is how the nodes talk to each other
-cluster.add_command("export NCCL_DEBUG=INFO")
-
-# setting a main port here is a good idea.
-cluster.add_command("export MASTER_PORT=%r" % PORT)
-
-# ************** DON'T FORGET THIS ***************
-# MUST load the latest NCCL version
-cluster.load_modules(["NCCL/2.4.7-1-cuda.10.0"])
-
-# configure cluster
-cluster.per_experiment_nb_nodes = 12
-cluster.per_experiment_nb_gpus = 8
-
-cluster.add_slurm_cmd(cmd="ntasks-per-node", value=8, comment="1 task per gpu")
-
-# submit a script with 9 combinations of hyper params
-# (lr=1e-3, layers=16), (lr=1e-3, layers=32), (lr=1e-3, layers=64), ... (lr=1e-1, layers=64)
-cluster.optimize_parallel_cluster_gpu(
-    main, nb_trials=9, job_name="name_for_squeue"  # how many permutations of the grid search to run
-)
-
-
-

The other option is that you generate scripts on your own via a bash command or use our -native solution.

-
-
-
-

Get help

-

Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI.

-

Try it out for free today:

-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/cluster_expert.html b/docs/clouds/cluster_expert.html deleted file mode 100644 index c0f6f39..0000000 --- a/docs/clouds/cluster_expert.html +++ /dev/null @@ -1,750 +0,0 @@ - - - - - - - - - - - - - - Run on an on-prem cluster (expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Run on an on-prem cluster (expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Run on an on-prem cluster (expert)

-
-
-

Integrate your own cluster

-

Lightning provides an interface for providing your own definition of a cluster environment. It mainly consists of -parsing the right environment variables to access information such as world size, global and local rank (process id), -and node rank (node id). Here is an example of a custom -ClusterEnvironment:

-
import os
-from pytorch_lightning.plugins.environments import ClusterEnvironment
-
-
-class MyClusterEnvironment(ClusterEnvironment):
-    @property
-    def creates_processes_externally(self) -> bool:
-        """Return True if the cluster is managed (you don't launch processes yourself)"""
-        return True
-
-    def world_size(self) -> int:
-        return int(os.environ["WORLD_SIZE"])
-
-    def global_rank(self) -> int:
-        return int(os.environ["RANK"])
-
-    def local_rank(self) -> int:
-        return int(os.environ["LOCAL_RANK"])
-
-    def node_rank(self) -> int:
-        return int(os.environ["NODE_RANK"])
-
-    def main_address(self) -> str:
-        return os.environ["MASTER_ADDRESS"]
-
-    def main_port(self) -> int:
-        return int(os.environ["MASTER_PORT"])
-
-
-trainer = Trainer(plugins=[MyClusterEnvironment()])
-
-
-
-
-
-

Get help

-

Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI.

-

Try it out for free today:

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/cluster_intermediate_1.html b/docs/clouds/cluster_intermediate_1.html deleted file mode 100644 index 4de1950..0000000 --- a/docs/clouds/cluster_intermediate_1.html +++ /dev/null @@ -1,767 +0,0 @@ - - - - - - - - - - - - - - Run on an on-prem cluster (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Run on an on-prem cluster (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Run on an on-prem cluster (intermediate)

-

Audience: Users who need to run on an academic or enterprise private cluster.

-
-
-

Setup the cluster

-

This guide shows how to run a training job on a general purpose cluster. We recommend beginners to try this method -first because it requires the least amount of configuration and changes to the code. -To setup a multi-node computing cluster you need:

-
    -
  1. Multiple computers with PyTorch Lightning installed

  2. -
  3. A network connectivity between them with firewall rules that allow traffic flow on a specified MASTER_PORT.

  4. -
  5. Defined environment variables on each node required for the PyTorch Lightning multi-node distributed training

  6. -
-

PyTorch Lightning follows the design of PyTorch distributed communication package. and requires the following environment variables to be defined on each node:

-
    -
  • MASTER_PORT - required; has to be a free port on machine with NODE_RANK 0

  • -
  • MASTER_ADDR - required (except for NODE_RANK 0); address of NODE_RANK 0 node

  • -
  • WORLD_SIZE - required; how many nodes are in the cluster

  • -
  • NODE_RANK - required; id of the node in the cluster

  • -
-
-
-
-

Setup the training script

-

To train a model using multiple nodes, do the following:

-
    -
  1. Design your LightningModule (no need to add anything specific here).

  2. -
  3. Enable DDP in the trainer

    -
    # train on 32 GPUs across 4 nodes
    -trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp")
    -
    -
    -
  4. -
-
-
-
-

Submit a job to the cluster

-

To submit a training job to the cluster you need to run the same training script on each node of the cluster. -This means that you need to:

-
    -
  1. Copy all third-party libraries to each node (usually means - distribute requirements.txt file and install it).

  2. -
  3. Copy all your import dependencies and the script itself to each node.

  4. -
  5. Run the script on each node.

  6. -
-
-
-
-

Debug on a cluster

-

When running in DDP mode, some errors in your code can show up as an NCCL issue. -Set the NCCL_DEBUG=INFO environment variable to see the ACTUAL error.

-
NCCL_DEBUG=INFO python train.py ...
-
-
-
-
-
-

Get help

-

Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI.

-

Try it out for free today:

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/cluster_intermediate_2.html b/docs/clouds/cluster_intermediate_2.html deleted file mode 100644 index 0e99257..0000000 --- a/docs/clouds/cluster_intermediate_2.html +++ /dev/null @@ -1,744 +0,0 @@ - - - - - - - - - - - - - - Run on an on-prem cluster (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Run on an on-prem cluster (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Run on an on-prem cluster (intermediate)

-
-

Run with TorchDistributed

-

Torch Distributed Run provides helper functions to setup distributed environment variables from the PyTorch distributed communication package that need to be defined on each node.

-

Once the script is setup like described in :ref:` Training Script Setup<training_script_setup>`, you can run the below command across your nodes to start multi-node training.

-

Like a custom cluster, you have to ensure that there is network connectivity between the nodes with firewall rules that allow traffic flow on a specified MASTER_PORT.

-

Finally, you’ll need to decide which node you’d like to be the main node (MASTER_ADDR), and the ranks of each node (NODE_RANK).

-

For example:

-
    -
  • MASTER_ADDR 10.10.10.16

  • -
  • MASTER_PORT 29500

  • -
  • NODE_RANK 0 for the first node, 1 for the second node

  • -
-

Run the below command with the appropriate variables set on each node.

-
python -m torch.distributed.run
-    --nnodes=2 # number of nodes you'd like to run with
-    --master_addr <MASTER_ADDR>
-    --master_port <MASTER_PORT>
-    --node_rank <NODE_RANK>
-    train.py (--arg1 ... train script args...)
-
-
-
-

Note

-

torch.distributed.run assumes that you’d like to spawn a process per GPU if GPU devices are found on the node. This can be adjusted with -nproc_per_node.

-
-
-
-
-

Get help

-

Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI.

-

Try it out for free today:

-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/fault_tolerant_training.html b/docs/clouds/fault_tolerant_training.html deleted file mode 100644 index 93e7376..0000000 --- a/docs/clouds/fault_tolerant_training.html +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - - - - - - - - - Fault-tolerant Training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Fault-tolerant Training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/fault_tolerant_training_basic.html b/docs/clouds/fault_tolerant_training_basic.html deleted file mode 100644 index 2c0dc20..0000000 --- a/docs/clouds/fault_tolerant_training_basic.html +++ /dev/null @@ -1,725 +0,0 @@ - - - - - - - - - - - - - - Fault-tolerant Training (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Fault-tolerant Training (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Fault-tolerant Training (basic)

-

Audience: User who want to run on the cloud or a cluster environment.

-

Pre-requisites: Users must have first read Run on the cloud (basic)

-
-
-

What is fault-tolerant training?

-

When developing models on the cloud or cluster environments, you may be forced to restart from scratch in the event of a software or hardware failure (ie: a fault). Lightning models can run fault-proof.

-

With Fault Tolerant Training, when Trainer.fit() fails in the middle of an epoch during training or validation, -Lightning will restart exactly where it failed, and everything will be restored (down to the batch it was on even if the dataset was shuffled).

-
-

Warning

-

Fault-tolerant Training is currently an experimental feature within Lightning.

-
-
-
-
-

Use fault-tolerance to save money on cloud training

-

Cloud providers offer pre-emptible machines which can be priced as low as 1/10th the cost but can be shut-down automatically at any time. -Because fault-tolerant training can automatically recover from an interruption, you can train models for many weeks/months at a time for the pre-emptible prices.

-

To easily run on the cloud with fault-tolerance with lightning-grid, use the following arguments:

-
grid run --use_spot --auto_resume lightning_script.py
-
-
-

The --use_spot argument enables cheap preemptible pricing (but the machines that can be interrupted). -If the machine is interrupted, the --auto_resume argument automatically restarts the machine.

-

As long as you are running a script that runs a lightning model, the model will restore itself and handle all the details of fault tolerance.

-
-
-
-

Cost

-

Lightning (via lightning-grid) provides access to cloud machines to the community for free. However, you must buy credits on lightning-grid which are used to pay the cloud providers on your behalf.

-

If you want to run on your own AWS account and pay the cloud provider directly, please contact our onprem team: mailto:onprem@pytorchlightning.ai

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/fault_tolerant_training_expert.html b/docs/clouds/fault_tolerant_training_expert.html deleted file mode 100644 index 92149d6..0000000 --- a/docs/clouds/fault_tolerant_training_expert.html +++ /dev/null @@ -1,714 +0,0 @@ - - - - - - - - - - - - - - Fault-tolerant Training (expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Fault-tolerant Training (expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Fault-tolerant Training (expert)

-

Audience: Experts looking to enable and handle their own fault-tolerance.

-

Pre-requisites: Users must have first read Fault-tolrance Training (basic)

-
-
-

Enable fault-tolerant behavior anywhere

-

To enable fault tolerance on your own cloud or cluster environment enable the PL_FAULT_TOLERANT_TRAINING environment variable:

-
PL_FAULT_TOLERANT_TRAINING=1 python script.py
-
-
-

Although Lighting will now be fault-tolerant, you’ll have to handle all the nuances of making sure the models are automatically restarted.

-
-

Note

-

This complexity is already handled for you if you use lightning-grid.

-
-
-
-
-

Enable fault-tolerant behavior on your own cluster

-

The simplest way to enable fault-tolerant behavior is to enable lightning-grid to work on your on-prem cluster or cloud environment which will handle all the nuances of fault-tolerant training at scale.

-

Email us to connect with your own cloud account:

-

mailto:onprem@pytorchlightning.ai

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/fault_tolerant_training_faq.html b/docs/clouds/fault_tolerant_training_faq.html deleted file mode 100644 index d4d8190..0000000 --- a/docs/clouds/fault_tolerant_training_faq.html +++ /dev/null @@ -1,816 +0,0 @@ - - - - - - - - - - - - - - Fault-tolerant Training (FAQ) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Fault-tolerant Training (FAQ)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Fault-tolerant Training (FAQ)

-
-

How do I use iterable datasets?

-

To support fault-tolerance, you will need to use and expose a sampler within your dataset.

-

For example, the following implementation for an iterable dataset sub-classing IterableDataset won’t be supported.

-
from torch.utils.data import IterableDataset, DataLoader
-
-
-# does not support fault tolerance training!
-class RandomIterableDataset(IterableDataset):
-    def __init__(self, size: int, count: int):
-        self.count = count
-        self.size = size
-
-    def __iter__(self):
-        for _ in range(self.count):
-            yield torch.randn(self.size)
-
-
-

There are two primary reasons why Lightning can’t support the previous implementation.

-
    -
  • Lightning cannot infer what you are iterating over, making it difficult to restart training. Lightning Fault Tolerant Training requires a Sampler to be used to encapsulate the fetching logic, requiring both the sampler and an iterator to be made available as attributes within the dataset, so Lightning can access them to track progress.

  • -
  • Implementing the __next__ method is required as it separates iterator creation from its consumption, which is essential for Lightning to wrap the iterator before their consumption.

  • -
-

If your iterable dataset are implemented in the following way, everything should works as expected.

-
import torch
-from torch.utils.data import IterableDataset, DataLoader
-
-
-class RandomIterableDataset(IterableDataset):
-    def __init__(self, size: int, length: int):
-        self.data = torch.randn(length, size)
-
-        # expose the sampler as an attribute
-        self.sampler = RandomSampler(range(length))
-
-    def __iter__(self) -> "RandomIterableDataset":
-        # expose the generator from the sampler as an attribute
-        # the ``sampler_iter`` will be wrapped by Lightning to ensure
-        # we can capture random seeds and iteration count for fast-forward samplers
-        # while restarting.
-        self.sampler_iter = iter(self.sampler)
-        return self
-
-    def __next__(self) -> torch.Tensor:
-        # call next on the iterator and get the associated data.
-        # the logic here can become more complex but the sampler
-        # should be the central piece for fetching the next sample
-        index = next(self.sampler_iter)
-        return self.data[index]
-
-
-
-
-
-

How do I use multiple dataloaders?

-

If you are using multiple training dataloaders, Lightning won’t be able to restore the random state properly.

-
class LitModel(LightningModule):
-    def train_dataloader(self):
-        loader_a = torch.utils.data.DataLoader(range(8), batch_size=4)
-        loader_b = torch.utils.data.DataLoader(range(16), batch_size=4)
-        return {"loader_a": loader_a, "loader_b": loader_b}
-
-    def training_step(self, batch, batch_idx):
-        # access the data in the same format as the collection of dataloaders.
-        # dict, list are supported.
-        loader_a = batch["loader_a"]
-        loader_b = batch["loader_b"]
-
-
-

If you believe this to be useful, please open a feature request.

-
-
-
-

What are the performance impacts?

-

Fault-tolerant Training was tested on common and worst-case scenarios in order to measure the impact of the internal state tracking on the total training time. -On tiny models like the BoringModel and RandomDataset -which has virtually no data loading and processing overhead, we noticed up to 50% longer training time with fault tolerance enabled. -In this worst-case scenario, fault-tolerant adds an overhead that is noticeable in comparison to the compute time for dataloading itself. -However, for more realistic training workloads where data loading and preprocessing is more expensive, the constant overhead that fault tolerance adds becomes less noticeable or not noticeable at all. -For example, when training with ResNet50 on CIFAR 10 we have observed a 0.5% to 1% increase in training time depending on batch size or number of workers.

-

More detailed benchmarks will be shared in the future.

-
-

Note

-

The extra time is coming from several parts:

-
    -
  • Capturing the iteration count + random states for each sample within each DataLoader workers and pass it through the data_queue

  • -
  • Extra logic to handle / store the dataloader’s states from each batch.

  • -
-
-
-
-
-

What happens to my shuffled dataset?

-

If you are using a single map-based dataset by sub-classing Dataset, everything should work as expected.

-
from torch.utils.data import Dataset, DataLoader
-
-
-class RandomDataset(Dataset):
-    def __init__(self, size: int, length: int):
-        self.len = length
-        self.data = torch.randn(length, size)
-
-    def __getitem__(self, index):
-        return self.data[index]
-
-    def __len__(self):
-        return self.len
-
-
-
-
-
-

What parts are fault-tolerant?

-

Lightning keeps track of the following state updates during training:

-
    -
  • Samplers indices and random states across multiple processes and workers: This enables restoring random transforms and batch fetching to the exact state as it was right before the failure.

  • -
  • Optimizers, learning rate schedulers, callbacks, etc..

  • -
  • Loop progression

  • -
  • Logging internal states such that metric reductions on epoch end are not getting affected by the failure and model selection can continue as expected.

  • -
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/grid_costs.html b/docs/clouds/grid_costs.html deleted file mode 100644 index cdacaae..0000000 --- a/docs/clouds/grid_costs.html +++ /dev/null @@ -1,690 +0,0 @@ - - - - - - - - - - - - - - Cost — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Cost

-

Lightning (via lightning-grid) provides access to cloud machines to the community for free. However, you must buy credits on lightning-grid which are used to pay the cloud providers on your behalf.

-

If you want to run on your own AWS account and pay the cloud provider directly, please contact our onprem team: mailto:onprem@pytorchlightning.ai

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/run_advanced.html b/docs/clouds/run_advanced.html deleted file mode 100644 index da84b2f..0000000 --- a/docs/clouds/run_advanced.html +++ /dev/null @@ -1,784 +0,0 @@ - - - - - - - - - - - - - - Train on the cloud (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train on the cloud (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train on the cloud (advanced)

-

Audience: Anyone looking to train a model on the cloud in the background

-
-
-

What is background training?

-

Background training lets you train models in the background without you needing to interact with the machine. As the model trains you can monitor its progress via Tensorboard or an experiment manager of your choice.

-
-
-
-

0: Install lightning-grid

-

First Navigate to https://platform.grid.ai to create a free account.

-

Next, install lightning-grid and login

-
pip install lightning-grid
-grid login
-
-
-
-
-
-

1: Create a dataset

-

Create a datastore which optimizes your datasets for training at scale on the cloud.

-

First, let’s download a dummy dataset we created.

-
# download
-curl https://pl-flash-data.s3.amazonaws.com/cifar5.zip -o cifar5.zip
-
-# unzip
-unzip cifar5.zip
-
-
-

Now create the datastore

-
grid datastore create cifar5/ --name cifar5
-
-
-

Now your dataset is ready to be used for training on the cloud!

-
-

Note

-

In some research workflows, your model script ALSO downloads the dataset. If the dataset is only a few GBs this is fine. Otherwise we recommend you create a Datastore.

-
-
-
-
-

2: Choose the model to run

-

You can run any python script in the background. For this example, we’ll use a simple classifier:

-

Clone the code to your machine:

-
-

Note

-

Code repositories can be as complicated as needed. This is just a simple demo.

-
-
-
-
-

3: Run on the cloud

-

To run this model on the cloud, use the grid run command which has two parts:

-
grid run [run args] file.py [file args]
-
-
-

To attach the datastore cifar5 to the cifar5.py file use the following command:

-
# command | the datastore to use   |  the model  | argument to the model
-grid run --datastore_name cifar5 cifar5.py.py --data_dir /datastores/cifar5
-
-
-
-
-
-

4: Monitor and manage

-

Now that your model is running in the background you can monitor and manage it here.

-

You can also monitor its progress on the commandline:

-
grid status
-
-
-
-
-
-

Next Steps

-

Here are the recommended next steps depending on your workflow.

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/run_basic.html b/docs/clouds/run_basic.html deleted file mode 100644 index cf898e9..0000000 --- a/docs/clouds/run_basic.html +++ /dev/null @@ -1,788 +0,0 @@ - - - - - - - - - - - - - - Train on the cloud (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train on the cloud (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train on the cloud (intermediate)

-

Audience: Anyone looking to train a model on the cloud in the background

-
-
-

What is background training?

-

Background training lets you train models in the background without you needing to interact with the machine. As the model trains you can monitor its progress via Tensorboard or an experiment manager of your choice.

-
-
-
-

0: Install lightning-grid

-

First Navigate to https://platform.grid.ai to create a free account.

-

Next, install lightning-grid and login

-
pip install lightning-grid
-grid login
-
-# Login successful. Welcome to Grid.
-
-
-
-
-
-

1: Create a dataset

-

Create a datastore which optimizes your datasets for training at scale on the cloud. Datastores can be created from all sorts of sources such as .zip and .tar links, local files/folders and even s3 buckets.

-

Let’s create a datastore from this .zip file

-
grid datastore create https://pl-flash-data.s3.amazonaws.com/tinycifar5.zip --name cifar5
-
-
-

Now your dataset is ready to be used for training on the cloud!

-
-

Note

-

In some research workflows, your model script ALSO downloads the dataset. If the dataset is only a few GBs this is fine. Otherwise we recommend you create a Datastore.

-
-
-
-
-

2: Choose the model to run

-

You can run any python script in the background. For this example, we’ll use a simple classifier:

-

Clone the code to your machine:

-
git clone https://github.com/williamFalcon/cifar5-simple.git
-cd cifar5-simple
-
-
-
-

Note

-

Code repositories can be as complicated as needed. This is just a simple demo.

-
-
-
-
-

3: Run on the cloud

-

To run this model on the cloud with the attached datastore, use the grid run command:

-
grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5
-
-
-

The grid command has two parts the [run args] and the [file args]

-
grid run [run args] file.py [file args]
-
-
-
-
-
-

4: Monitor and manage

-

Now that your model is running in the background, monitor and manage it here.

-

You can also monitor its progress on the commandline:

-
grid status
-
-
-
-
-
-

Cost

-

Lightning (via lightning-grid) provides access to cloud machines to the community for free. However, you must buy credits on lightning-grid which are used to pay the cloud providers on your behalf.

-

If you want to run on your own AWS account and pay the cloud provider directly, please contact our onprem team: mailto:onprem@pytorchlightning.ai

-
-
-
-

Next Steps

-

Here are the recommended next steps depending on your workflow.

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/run_expert.html b/docs/clouds/run_expert.html deleted file mode 100644 index c8b4a71..0000000 --- a/docs/clouds/run_expert.html +++ /dev/null @@ -1,717 +0,0 @@ - - - - - - - - - - - - - - Train on the cloud (expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train on the cloud (expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train on the cloud (expert)

-

Audience: Corporate or academic users who want to run on their Company or University private cloud account.

-
-
-

Run on your own cloud or cluster

-

If you have access to a corporate or academic cluster, you can simply submit jobs that run Lightning models and lightning will automatically work. -Please refer to Clusters guide for more information.

-
-
-
-

Run on your own cloud (hassle free)

-

Cluster training can get complicated once you start doing multi-node training, fault-tolerant training or sweeps. -If you’d prefer to not deal with any of the hassles of running on your own cloud environments, lightning-grid enables University and Enterprise customers to run on the cloud with their own credentials or even onprem.

-

These are some of the benefits of running via lightning-grid:

-
    -
  • create datasets optimized for scale

  • -
  • fully configurable on-prem deployment

  • -
  • SOC-2 compliance (in-progress) (ETA Q3 2022)

  • -
  • micro cost optimizations everywhere (which add up)

  • -
  • built-in fault tolerance

  • -
  • enabled collaboration for teams and enterprises

  • -
-

Contact our sales support engineering team so we can help you set up Grid with your own cloud credentials.

-

Email us to connect with your own cloud account:

-

mailto:onprem@pytorchlightning.ai.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/run_intermediate.html b/docs/clouds/run_intermediate.html deleted file mode 100644 index 92c88bd..0000000 --- a/docs/clouds/run_intermediate.html +++ /dev/null @@ -1,900 +0,0 @@ - - - - - - - - - - - - - - Train on the cloud (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train on the cloud (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train on the cloud (intermediate)

-

Audience: User looking to run many models at once

-
-
-

What is a sweep?

-

A sweep is the term giving to running the same model multiple times with different hyperparameters to find the one that performs the best (according to your definition of performance).

-

Let’s say I have a python script that trains a Lighting model to classify images. We run this file like so:

-
grid run file.py --batch_size 8
-
-
-

with such a model, I would be interested in knowing how it performs with different batch size. In this case, I’m going to train many versions of this model.

-
# run 4 models in parallel
-grid run file.py --batch_size 8
-grid run file.py --batch_size 16
-grid run file.py --batch_size 32
-grid run file.py --batch_size 64
-
-
-

Now I can see how my model performs according to the layers and based on time and cost I can pick my “best” model:

- - ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Training speed vs cost

Batch size

classification accuracy (%)

training time

cost

8

0.80

5 minutes

$0.15

16

0.85

10 minutes

$0.30

32

0.90

30 minutes

$0.50

64

0.95

60 minutes

$1.01

-
-
-
-

Start a Sweep

-

First, recall that in the previous tutorial we ran a single model using this command:

-
grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5
-
-
-

Now we’re going to run that same model 4 different times each with a different number of layers:

-
grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 8
-grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 16
-grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 32
-grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 64
-
-
-

Grid has a special syntax based on python that gives you shortcuts for sweeps. The shortcut for the above commands is:

-
grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size "[8, 16, 32, 64]"
-
-
-
-
-
-

Syntax Shortcuts

-
-

List

-
grid run file.py --batch_size "[8, 16, 32, 64]"
-
-
-

equivalent to:

-
grid run file.py --batch_size 8
-grid run file.py --batch_size 16
-grid run file.py --batch_size 32
-grid run file.py --batch_size 64
-
-
-
-
-
-

Range

-
grid run file.py --batch_size "range(1, 10, 2)"
-
-
-

equivalent to:

-
grid run main.py --batch_size 1
-grid run main.py --batch_size 3
-grid run main.py --batch_size 5
-grid run main.py --batch_size 7
-grid run main.py --batch_size 9
-
-
-

-
-
-

String list

-
grid run file.py --model_backbone "['resnet18' 'transformer', 'resnet50']"
-
-
-

equivalent to:

-
grid run file.py --model_backbone 'resnet18'
-grid run file.py --model_backbone 'transformer'
-grid run file.py --model_backbone 'resnet50'
-
-
-
-
-
-

Sampling

-
grid run file.py --learning_rate "uniform(1e-5, 1e-1, 3)"
-
-
-

equivalent to:

-
grid run file.py --learning_rate 0.03977392
-grid run file.py --learning_rate 0.04835479
-grid run file.py --learning_rate 0.05200016
-
-
-
-
-
-
-

Sweep strategies

-

Models often have dozens of hyperparameters. We usually don’t run all combinations because it would be too prohibitive. Grid supports two strategies:

-
- -
- -
-
-
-

Next Steps

-

Here are the recommended next steps depending on your workflow.

-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/session_basic.html b/docs/clouds/session_basic.html deleted file mode 100644 index 5ce689c..0000000 --- a/docs/clouds/session_basic.html +++ /dev/null @@ -1,788 +0,0 @@ - - - - - - - - - - - - - - Train on the cloud (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train on the cloud (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train on the cloud (basic)

-

Audience: Anyone looking to train across many machines at once on the cloud.

-
-
-

Why do I need cloud training?

-

Training on the cloud is a cost effective way to train your models faster by allowing you to access powerful GPU machines.

-

For example, if your model takes 10 days to train on a CPU machine, here’s how cloud training can speed up your training time:

- - ----- - - - - - - - - - - - - - - - - - - - - - - - - -
Training speed vs cost

Machine type

Training time

Cost (AWS 1 M60 GPU)

CPU

10 days

$12.00

1 GPU

2 days

$11.52

2 GPU

1 day

$20.64

4 GPU

12 hours

$19.08

-
-
-
-

Start a cloud machine in < 1 minute

-

Lightning has a native cloud solution with various products (lightning-grid) designed for researchers and ML practicioners in industry. -To start an interactive machine simply go to Lightning Grid to create a free account, then start a new Grid Session.

-

A Grid Session is an interactive machine with 1-16 GPUs per machine.

-Start a Grid Session in a few seconds -
-
-
-

Open the Jupyter Notebook

-

Once the Session starts, open a Jupyter notebook.

-
-
-
-

Clone and run your model

-

On the Jupyter page you can use a Notebook, or to clone your code and run via the CLI.

-
-
-
-

Cost

-

Lightning (via lightning-grid) provides access to cloud machines to the community for free. However, you must buy credits on lightning-grid which are used to pay the cloud providers on your behalf.

-

If you want to run on your own AWS account and pay the cloud provider directly, please contact our onprem team: mailto:onprem@pytorchlightning.ai

-
-
-
-

Next Steps

-

Here are the recommended next steps depending on your workflow.

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/clouds/session_intermediate.html b/docs/clouds/session_intermediate.html deleted file mode 100644 index b9eb1dd..0000000 --- a/docs/clouds/session_intermediate.html +++ /dev/null @@ -1,689 +0,0 @@ - - - - - - - - - - - - - - Train on the cloud (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train on the cloud (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train on the cloud (basic)

-

Audience: Anyone looking to train across many machines at once on the cloud.

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/checkpointing.html b/docs/common/checkpointing.html deleted file mode 100644 index 1a0cdb8..0000000 --- a/docs/common/checkpointing.html +++ /dev/null @@ -1,754 +0,0 @@ - - - - - - - - - - - - - - Checkpointing — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/checkpointing_advanced.html b/docs/common/checkpointing_advanced.html deleted file mode 100644 index aee1421..0000000 --- a/docs/common/checkpointing_advanced.html +++ /dev/null @@ -1,762 +0,0 @@ - - - - - - - - - - - - - - Checkpointing (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Checkpointing (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Checkpointing (advanced)

-
-

Cloud checkpoints

-

Lightning is integrated with the major remote file systems including local filesystems and several cloud storage providers such as -S3 on AWS, GCS on Google Cloud, -or ADL on Azure.

-

PyTorch Lightning uses fsspec internally to handle all filesystem operations.

-
-
-

Save a cloud checkpoint

-

To save to a remote filesystem, prepend a protocol like “s3:/” to the root_dir used for writing and reading model data.

-
# `default_root_dir` is the default path used for logs and checkpoints
-trainer = Trainer(default_root_dir="s3://my_bucket/data/")
-trainer.fit(model)
-
-
-
-
-
-

Resume training from a cloud checkpoint

-

To resume training from a cloud checkpoint use a cloud url.

-
trainer = Trainer(default_root_dir=tmpdir, max_steps=3)
-trainer.fit(model, ckpt_path="s3://my_bucket/ckpts/classifier.ckpt")
-
-
-

PyTorch Lightning uses fsspec internally to handle all filesystem operations.

-
-
-
-
-

Modularize your checkpoints

-

Checkpoints can also save the state of datamodules and callbacks.

-
-
-
-

Modify a checkpoint anywhere

-

When you need to change the components of a checkpoint before saving or loading, use the on_save_checkpoint() and on_load_checkpoint() of your LightningModule.

-
class LitModel(pl.LightningModule):
-    def on_save_checkpoint(self, checkpoint):
-        checkpoint["something_cool_i_want_to_save"] = my_cool_pickable_object
-
-    def on_load_checkpoint(self, checkpoint):
-        my_cool_pickable_object = checkpoint["something_cool_i_want_to_save"]
-
-
-

Use the above approach when you need to couple this behavior to your LightningModule for reproducibility reasons. Otherwise, Callbacks also have the on_save_checkpoint() and on_load_checkpoint() which you should use instead:

-
class LitCallback(pl.Callback):
-    def on_save_checkpoint(self, checkpoint):
-        checkpoint["something_cool_i_want_to_save"] = my_cool_pickable_object
-
-    def on_load_checkpoint(self, checkpoint):
-        my_cool_pickable_object = checkpoint["something_cool_i_want_to_save"]
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/checkpointing_basic.html b/docs/common/checkpointing_basic.html deleted file mode 100644 index 85e04b0..0000000 --- a/docs/common/checkpointing_basic.html +++ /dev/null @@ -1,845 +0,0 @@ - - - - - - - - - - - - - - Checkpointing (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Checkpointing (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Checkpointing (basic)

-

Audience: All users

-
-
-

What is a checkpoint?

-

When a model is training, the performance changes as it continues to see more data. It is a best practice to save the state of a model throughout the training process. This gives you a version of the model, a checkpoint, at each key point during the development of the model. Once training has completed, use the checkpoint that corresponds to the best performance you found during the training process.

-

Checkpoints also enable your training to resume from where it was in case the training process is interrupted.

-

PyTorch Lightning checkpoints are fully usable in plain PyTorch.

-
-
-
-

Contents of a checkpoint

-

A Lightning checkpoint contains a dump of the model’s entire internal state. Unlike plain PyTorch, Lightning saves everything you need to restore a model even in the most complex distributed training environments.

-

Inside a Lightning checkpoint you’ll find:

-
    -
  • 16-bit scaling factor (if using 16-bit precision training)

  • -
  • Current epoch

  • -
  • Global step

  • -
  • LightningModule’s state_dict

  • -
  • State of all optimizers

  • -
  • State of all learning rate schedulers

  • -
  • State of all callbacks (for stateful callbacks)

  • -
  • State of datamodule (for stateful datamodules)

  • -
  • The hyperparameters used for that model if passed in as hparams (Argparse.Namespace)

  • -
  • State of Loops (if using Fault-Tolerant training)

  • -
-
-
-
-

Save a checkpoint

-

Lightning automatically saves a checkpoint for you in your current working directory, with the state of your last training epoch. This makes sure you can resume training in case it was interrupted.

-
# simply by using the Trainer you get automatic checkpointing
-trainer = Trainer()
-
-
-

To change the checkpoint path use the default_root_dir argument:

-
# saves checkpoints to 'some/path/' at every epoch end
-trainer = Trainer(default_root_dir="some/path/")
-
-
-
-
-
-

LightningModule from checkpoint

-

To load a LightningModule along with its weights and hyperparameters use the following method:

-
model = MyLightningModule.load_from_checkpoint("/path/to/checkpoint.ckpt")
-
-# disable randomness, dropout, etc...
-model.eval()
-
-# predict with the model
-y_hat = model(x)
-
-
-
-
-

Save hyperparameters

-

The LightningModule allows you to automatically save all the hyperparameters passed to init simply by calling self.save_hyperparameters().

-
class MyLightningModule(LightningModule):
-    def __init__(self, learning_rate, another_parameter, *args, **kwargs):
-        super().__init__()
-        self.save_hyperparameters()
-
-
-

The hyperparameters are saved to the “hyper_parameters” key in the checkpoint

-
checkpoint = torch.load(checkpoint, map_location=lambda storage, loc: storage)
-print(checkpoint["hyper_parameters"])
-# {"learning_rate": the_value, "another_parameter": the_other_value}
-
-
-

The LightningModule also has access to the Hyperparameters

-
model = MyLightningModule.load_from_checkpoint("/path/to/checkpoint.ckpt")
-print(model.learning_rate)
-
-
-
-
-
-

Initalize with other parameters

-

If you used the self.save_hyperparameters() method in the init of the LightningModule, you can initialize the model with different hyperparameters.

-
# if you train and save the model like this it will use these values when loading
-# the weights. But you can overwrite this
-LitModel(in_dim=32, out_dim=10)
-
-# uses in_dim=32, out_dim=10
-model = LitModel.load_from_checkpoint(PATH)
-
-# uses in_dim=128, out_dim=10
-model = LitModel.load_from_checkpoint(PATH, in_dim=128, out_dim=10)
-
-
-
-
-
-
-

nn.Module from checkpoint

-

Lightning checkpoints are fully compatible with plain torch nn.Modules.

-
checkpoint = torch.load(CKPT_PATH)
-print(checkpoint.keys())
-
-
-

For example, let’s pretend we created a LightningModule like so:

-
class Encoder(nn.Module):
-    ...
-
-
-class Decoder(nn.Module):
-    ...
-
-
-class Autoencoder(pl.LightningModule):
-    def __init__(self, encoder, decoder, *args, **kwargs):
-        ...
-
-
-autoencoder = Autoencoder(Encoder(), Decoder())
-
-
-

Once the autoencoder has trained, pull out the relevant weights for your torch nn.Module:

-
checkpoint = torch.load(CKPT_PATH)
-encoder_weights = checkpoint["encoder"]
-decoder_weights = checkpoint["decoder"]
-
-
-
-
-
-

Disable checkpointing

-

You can disable checkpointing by passing:

-
trainer = Trainer(enable_checkpointing=False)
-
-
-
-
-
-

Resume training state

-

If you don’t just want to load weights, but instead restore the full training, do the following:

-
model = LitModel()
-trainer = Trainer()
-
-# automatically restores model, epoch, step, LR schedulers, apex, etc...
-trainer.fit(model, ckpt_path="some/path/to/my_checkpoint.ckpt")
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/checkpointing_expert.html b/docs/common/checkpointing_expert.html deleted file mode 100644 index e046d52..0000000 --- a/docs/common/checkpointing_expert.html +++ /dev/null @@ -1,774 +0,0 @@ - - - - - - - - - - - - - - Checkpointing (expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Checkpointing (expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Checkpointing (expert)

-

TODO: I don’t understand this…

-
-

Customize Checkpointing

-
-

Warning

-

The Checkpoint IO API is experimental and subject to change.

-
-

Lightning supports modifying the checkpointing save/load functionality through the CheckpointIO. This encapsulates the save/load logic -that is managed by the Strategy. CheckpointIO is different from on_save_checkpoint() -and on_load_checkpoint() methods as it determines how the checkpoint is saved/loaded to storage rather than -what’s saved in the checkpoint.

-
-
-

Built-in Checkpoint IO Plugins

- - ---- - - - - - - - - - - - - - -
Built-in Checkpoint IO Plugins

Plugin

Description

TorchCheckpointIO

CheckpointIO that utilizes torch.save() and torch.load() to save and load checkpoints -respectively, common for most use cases.

XLACheckpointIO

CheckpointIO that utilizes xm.save() to save checkpoints for TPU training strategies.

-
-
-

Custom Checkpoint IO Plugin

-

CheckpointIO can be extended to include your custom save/load functionality to and from a path. The CheckpointIO object can be passed to either a Trainer directly or a Strategy as shown below:

-
from pytorch_lightning import Trainer
-from pytorch_lightning.callbacks import ModelCheckpoint
-from pytorch_lightning.plugins import CheckpointIO
-from pytorch_lightning.strategies import SingleDeviceStrategy
-
-
-class CustomCheckpointIO(CheckpointIO):
-    def save_checkpoint(self, checkpoint, path, storage_options=None):
-        ...
-
-    def load_checkpoint(self, path, storage_options=None):
-        ...
-
-    def remove_checkpoint(self, path):
-        ...
-
-
-custom_checkpoint_io = CustomCheckpointIO()
-
-# Either pass into the Trainer object
-model = MyModel()
-trainer = Trainer(
-    plugins=[custom_checkpoint_io],
-    callbacks=ModelCheckpoint(save_last=True),
-)
-trainer.fit(model)
-
-# or pass into Strategy
-model = MyModel()
-device = torch.device("cpu")
-trainer = Trainer(
-    strategy=SingleDeviceStrategy(device, checkpoint_io=custom_checkpoint_io),
-    callbacks=ModelCheckpoint(save_last=True),
-)
-trainer.fit(model)
-
-
-
-

Note

-

Some TrainingTypePlugins like DeepSpeedStrategy do not support custom CheckpointIO as checkpointing logic is not modifiable.

-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/checkpointing_intermediate.html b/docs/common/checkpointing_intermediate.html deleted file mode 100644 index 16b8ce7..0000000 --- a/docs/common/checkpointing_intermediate.html +++ /dev/null @@ -1,865 +0,0 @@ - - - - - - - - - - - - - - Checkpointing (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Checkpointing (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Checkpointing (intermediate)

-

Audience: Users looking to customize the checkpointing behavior

-
-
-

Modify checkpointing behavior

-

For fine-grain control over checkpointing behavior, use the ModelCheckpoint object

-
from pytorch_lightning.callbacks import ModelCheckpoint
-
-checkpoint_callback = ModelCheckpoint(dirpath="my/path/", save_top_k=2, monitor="val_loss")
-trainer = Trainer(callbacks=[checkpoint_callback])
-trainer.fit(model)
-checkpoint_callback.best_model_path
-
-
-

Any value that has been logged via self.log in the LightningModule can be monitored.

-
class LitModel(pl.LightningModule):
-    def training_step(self, batch, batch_idx):
-        self.log("my_metric", x)
-
-
-# 'my_metric' is now able to be monitored
-checkpoint_callback = ModelCheckpoint(monitor="my_metric")
-
-
-
-
-
-

Save checkpoints by condition

-

To save checkpoints based on a (when/which/what/where) condition (for example when the validation_loss is lower) modify the ModelCheckpoint properties.

-
-

When

-
    -
  • When using iterative training which doesn’t have an epoch, you can checkpoint at every N training steps by specifying every_n_training_steps=N.

  • -
  • You can also control the interval of epochs between checkpoints using every_n_epochs between checkpoints, to avoid slowdowns.

  • -
  • You can checkpoint at a regular time interval using train_time_interval argument independent of the steps or epochs.

  • -
  • In case you are monitoring a training metrics, we’d suggest using save_on_train_epoch_end=True to ensure the required metric is being accumulated correctly for creating a checkpoint.

  • -
-
-
-

Which

-
    -
  • You can save the last checkpoint when training ends using save_last argument.

  • -
  • You can save top-K and last-K checkpoints by configuring the monitor and save_top_k argument.

  • -
-
-

-
-
-
from pytorch_lightning.callbacks import ModelCheckpoint
-
-
-# saves top-K checkpoints based on "val_loss" metric
-checkpoint_callback = ModelCheckpoint(
-    save_top_k=10,
-    monitor="val_loss",
-    mode="min",
-    dirpath="my/path/",
-    filename="sample-mnist-{epoch:02d}-{val_loss:.2f}",
-)
-
-# saves last-K checkpoints based on "global_step" metric
-# make sure you log it inside your LightningModule
-checkpoint_callback = ModelCheckpoint(
-    save_top_k=10,
-    monitor="global_step",
-    mode="max",
-    dirpath="my/path/",
-    filename="sample-mnist-{epoch:02d}-{global_step}",
-)
-
-
-
-
    -
  • You can customize the checkpointing behavior to monitor any quantity of your training or validation steps. For example, if you want to update your checkpoints based on your validation loss:

  • -
-
-

-
-
-
from pytorch_lightning.callbacks import ModelCheckpoint
-
-
-class LitAutoEncoder(LightningModule):
-    def validation_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.backbone(x)
-
-        # 1. calculate loss
-        loss = F.cross_entropy(y_hat, y)
-
-        # 2. log val_loss
-        self.log("val_loss", loss)
-
-
-# 3. Init ModelCheckpoint callback, monitoring "val_loss"
-checkpoint_callback = ModelCheckpoint(monitor="val_loss")
-
-# 4. Add your callback to the callbacks list
-trainer = Trainer(callbacks=[checkpoint_callback])
-
-
-
-
-
-

What

-
    -
  • By default, the ModelCheckpoint callback saves model weights, optimizer states, etc., but in case you have limited disk space or just need the model weights to be saved you can specify save_weights_only=True.

  • -
-
-
-

Where

-
    -
  • It gives you the ability to specify the dirpath and filename for your checkpoints. Filename can also be dynamic so you can inject the metrics that are being logged using log().

  • -
-
-

-
-
-
from pytorch_lightning.callbacks import ModelCheckpoint
-
-
-# saves a file like: my/path/sample-mnist-epoch=02-val_loss=0.32.ckpt
-checkpoint_callback = ModelCheckpoint(
-    dirpath="my/path/",
-    filename="sample-mnist-{epoch:02d}-{val_loss:.2f}",
-)
-
-
-
-
-

-
-

The ModelCheckpoint callback is very robust and should cover 99% of the use-cases. If you find a use-case that is not configured yet, feel free to open an issue with a feature request on GitHub -and the Lightning Team will be happy to integrate/help integrate it.

-
-
-
-
-

Save checkpoints manually

-

You can manually save checkpoints and restore your model from the checkpointed state using save_checkpoint() -and load_from_checkpoint().

-
model = MyLightningModule(hparams)
-trainer.fit(model)
-trainer.save_checkpoint("example.ckpt")
-
-# load the checkpoint later as normal
-new_model = MyLightningModule.load_from_checkpoint(checkpoint_path="example.ckpt")
-
-
-
-

Manual saving with distributed training

-

In distributed training cases where a model is running across many machines, Lightning ensures that only one checkpoint is saved instead of a model per machine. This requires no code changes as seen below:

-
trainer = Trainer(strategy="ddp")
-model = MyLightningModule(hparams)
-trainer.fit(model)
-# Saves only on the main process
-trainer.save_checkpoint("example.ckpt")
-
-
-

Not using save_checkpoint() can lead to unexpected behavior and potential deadlock. Using other saving functions will result in all devices attempting to save the checkpoint. As a result, we highly recommend using the Trainer’s save functionality. -If using custom saving functions cannot be avoided, we recommend using the rank_zero_only() decorator to ensure saving occurs only on the main process. Note that this will only work if all ranks hold the exact same state and won’t work when using -model parallel distributed strategies such as deepspeed or sharded training.

-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/child_modules.html b/docs/common/child_modules.html deleted file mode 100644 index 130f1e0..0000000 --- a/docs/common/child_modules.html +++ /dev/null @@ -1,747 +0,0 @@ - - - - - - - - - - - - - - <no title> — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Research projects tend to test different approaches to the same dataset. -This is very easy to do in Lightning with inheritance.

-

For example, imagine we now want to train an AutoEncoder to use as a feature extractor for images. -The only things that change in the LitAutoEncoder model are the init, forward, training, validation and test step.

-
class Encoder(torch.nn.Module):
-    ...
-
-
-class Decoder(torch.nn.Module):
-    ...
-
-
-class AutoEncoder(torch.nn.Module):
-    def __init__(self):
-        super().__init__()
-        self.encoder = Encoder()
-        self.decoder = Decoder()
-
-    def forward(self, x):
-        return self.decoder(self.encoder(x))
-
-
-class LitAutoEncoder(LightningModule):
-    def __init__(self, auto_encoder):
-        super().__init__()
-        self.auto_encoder = auto_encoder
-        self.metric = torch.nn.MSELoss()
-
-    def forward(self, x):
-        return self.auto_encoder.encoder(x)
-
-    def training_step(self, batch, batch_idx):
-        x, _ = batch
-        x_hat = self.auto_encoder(x)
-        loss = self.metric(x, x_hat)
-        return loss
-
-    def validation_step(self, batch, batch_idx):
-        self._shared_eval(batch, batch_idx, "val")
-
-    def test_step(self, batch, batch_idx):
-        self._shared_eval(batch, batch_idx, "test")
-
-    def _shared_eval(self, batch, batch_idx, prefix):
-        x, _ = batch
-        x_hat = self.auto_encoder(x)
-        loss = self.metric(x, x_hat)
-        self.log(f"{prefix}_loss", loss)
-
-
-

and we can train this using the Trainer:

-
auto_encoder = AutoEncoder()
-lightning_module = LitAutoEncoder(auto_encoder)
-trainer = Trainer()
-trainer.fit(lightning_module, train_dataloader, val_dataloader)
-
-
-

And remember that the forward method should define the practical use of a LightningModule. -In this case, we want to use the LitAutoEncoder to extract image representations:

-
some_images = torch.Tensor(32, 1, 28, 28)
-representations = lightning_module(some_images)
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
-
    -
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/console_logs.html b/docs/common/console_logs.html deleted file mode 100644 index ba90330..0000000 --- a/docs/common/console_logs.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - - - - - - - - - Console logging — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Console logging

-

Audience: Engineers looking to capture more visible logs.

-
-
-

Enable console logs

-

Lightning logs useful information about the training process and user warnings to the console. -You can retrieve the Lightning console logger and change it to your liking. For example, adjust the logging level -or redirect output for certain modules to log files:

-
import logging
-
-# configure logging at the root level of Lightning
-logging.getLogger("pytorch_lightning").setLevel(logging.ERROR)
-
-# configure logging on module level, redirect to file
-logger = logging.getLogger("pytorch_lightning.core")
-logger.addHandler(logging.FileHandler("core.log"))
-
-
-

Read more about custom Python logging here.

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/early_stopping.html b/docs/common/early_stopping.html deleted file mode 100644 index bf3584a..0000000 --- a/docs/common/early_stopping.html +++ /dev/null @@ -1,775 +0,0 @@ - - - - - - - - - - - - - - Early Stopping — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Early Stopping

-
-

Stopping an Epoch Early

-

You can stop and skip the rest of the current epoch early by overriding on_train_batch_start() to return -1 when some condition is met.

-

If you do this repeatedly, for every epoch you had originally requested, then this will stop your entire training.

-
-
-

EarlyStopping Callback

-

The EarlyStopping callback can be used to monitor a metric and stop the training when no improvement is observed.

-

To enable it:

-
    -
  • Import EarlyStopping callback.

  • -
  • Log the metric you want to monitor using log() method.

  • -
  • Init the callback, and set monitor to the logged metric of your choice.

  • -
  • Set the mode based on the metric needs to be monitored.

  • -
  • Pass the EarlyStopping callback to the Trainer callbacks flag.

  • -
-
from pytorch_lightning.callbacks.early_stopping import EarlyStopping
-
-
-class LitModel(LightningModule):
-    def validation_step(self, batch, batch_idx):
-        loss = ...
-        self.log("val_loss", loss)
-
-
-model = LitModel()
-trainer = Trainer(callbacks=[EarlyStopping(monitor="val_loss", mode="min")])
-trainer.fit(model)
-
-
-

You can customize the callbacks behaviour by changing its parameters.

-
early_stop_callback = EarlyStopping(monitor="val_accuracy", min_delta=0.00, patience=3, verbose=False, mode="max")
-trainer = Trainer(callbacks=[early_stop_callback])
-
-
-

Additional parameters that stop training at extreme points:

-
    -
  • stopping_threshold: Stops training immediately once the monitored quantity reaches this threshold. -It is useful when we know that going beyond a certain optimal value does not further benefit us.

  • -
  • divergence_threshold: Stops training as soon as the monitored quantity becomes worse than this threshold. -When reaching a value this bad, we believes the model cannot recover anymore and it is better to stop early and run with different initial conditions.

  • -
  • check_finite: When turned on, it stops training if the monitored metric becomes NaN or infinite.

  • -
  • check_on_train_epoch_end: When turned on, it checks the metric at the end of a training epoch. Use this only when you are monitoring any metric logged within -training-specific hooks on epoch-level.

  • -
-

In case you need early stopping in a different part of training, subclass EarlyStopping -and change where it is called:

-
class MyEarlyStopping(EarlyStopping):
-    def on_validation_end(self, trainer, pl_module):
-        # override this to disable early stopping at the end of val loop
-        pass
-
-    def on_train_end(self, trainer, pl_module):
-        # instead, do it at the end of training loop
-        self._run_early_stopping_check(trainer)
-
-
-
-

Note

-

The EarlyStopping callback runs -at the end of every validation epoch by default. However, the frequency of validation -can be modified by setting various parameters in the Trainer, -for example check_val_every_n_epoch -and val_check_interval. -It must be noted that the patience parameter counts the number of -validation checks with no improvement, and not the number of training epochs. -Therefore, with parameters check_val_every_n_epoch=10 and patience=3, the trainer -will perform at least 40 training epochs before being stopped.

-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/evaluation.html b/docs/common/evaluation.html deleted file mode 100644 index c4ee553..0000000 --- a/docs/common/evaluation.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - - - - - - - - - Add validation and test datasets — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Add validation and test datasets
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/evaluation_basic.html b/docs/common/evaluation_basic.html deleted file mode 100644 index f9e097e..0000000 --- a/docs/common/evaluation_basic.html +++ /dev/null @@ -1,808 +0,0 @@ - - - - - - - - - - - - - - Validate and test a model (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Validate and test a model (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Validate and test a model (basic)

-

Audience: Users who want to add a validation loop to avoid overfitting

-
-
-

Add a test loop

-

To make sure a model can generalize to an unseen dataset (ie: to publish a paper or in a production environment) a dataset is normally split into two parts, the train split and the test split.

-

The test set is NOT used during training, it is ONLY used once the model has been trained to see how the model will do in the real-world.

-
-
-

Find the train and test splits

-

Datasets come with two splits. Refer to the dataset documentation to find the train and test splits.

-
import torch.utils.data as data
-from torchvision import datasets
-
-# Load data sets
-train_set = datasets.MNIST(root="MNIST", download=True, train=True)
-test_set = datasets.MNIST(root="MNIST", download=True, train=False)
-
-
-
-
-
-

Define the test loop

-

To add a test loop, implement the test_step method of the LightningModule

-
class LitAutoEncoder(pl.LightningModule):
-    def training_step(self, batch, batch_idx):
-        ...
-
-    def test_step(self, batch, batch_idx):
-        # this is the test loop
-        x, y = batch
-        x = x.view(x.size(0), -1)
-        z = self.encoder(x)
-        x_hat = self.decoder(z)
-        test_loss = F.mse_loss(x_hat, x)
-        self.log("test_loss", test_loss)
-
-
-
-
-
-

Train with the test loop

-

Once the model has finished training, call .test

-
from torch.utils.data import DataLoader
-
-# initialize the Trainer
-trainer = Trainer()
-
-# test the model
-trainer.test(model, dataloaders=DataLoader(test_set))
-
-
-
-
-
-
-

Add a validation loop

-

During training, it’s common practice to use a small portion of the train split to determine when the model has finished training.

-
-
-

Split the training data

-

As a rule of thumb, we use 20% of the training set as the validation set. This number varies from dataset to dataset.

-
# use 20% of training data for validation
-train_set_size = int(len(train_set) * 0.8)
-valid_set_size = len(train_set) - train_set_size
-
-# split the train set into two
-seed = torch.Generator().manual_seed(42)
-train_set, valid_set = data.random_split(train_set, [train_set_size, valid_set_size], generator=seed)
-
-
-
-
-
-

Define the validation loop

-

To add a validation loop, implement the validation_step method of the LightningModule

-
class LitAutoEncoder(pl.LightningModule):
-    def training_step(self, batch, batch_idx):
-        ...
-
-    def validation_step(self, batch, batch_idx):
-        # this is the validation loop
-        x, y = batch
-        x = x.view(x.size(0), -1)
-        z = self.encoder(x)
-        x_hat = self.decoder(z)
-        test_loss = F.mse_loss(x_hat, x)
-        self.log("val_loss", test_loss)
-
-
-
-
-
-

Train with the validation loop

-

To run the validation loop, pass in the validation set to .fit

-
from torch.utils.data import DataLoader
-
-train_set = DataLoader(train_set)
-val_set = DataLoader(val_set)
-
-# train with both splits
-trainer = Trainer()
-trainer.fit(model, train_set, val_set)
-
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/evaluation_intermediate.html b/docs/common/evaluation_intermediate.html deleted file mode 100644 index eb3640d..0000000 --- a/docs/common/evaluation_intermediate.html +++ /dev/null @@ -1,878 +0,0 @@ - - - - - - - - - - - - - - Validate and test a model (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Validate and test a model (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Validate and test a model (intermediate)

-

During and after training we need a way to evaluate our models to make sure they are not overfitting while training and -generalize well on unseen or real-world data. There are generally 2 stages of evaluation: validation and testing. To some -degree they serve the same purpose, to make sure models works on real data but they have some practical differences.

-

Validation is usually done during training, traditionally after each training epoch. It can be used for hyperparameter optimization or tracking model performance during training. -It’s a part of the training process.

-

Testing is usually done once we are satisfied with the training and only with the best model selected from the validation metrics.

-

Let’s see how these can be performed with Lightning.

-
-

Testing

-

Lightning allows the user to test their models with any compatible test dataloaders. This can be done before/after training -and is completely agnostic to fit() call. The logic used here is defined under -test_step().

-

Testing is performed using the Trainer object’s .test() method.

-
-
-Trainer.test(model=None, dataloaders=None, ckpt_path=None, verbose=True, datamodule=None)[source]
-

Perform one evaluation epoch over the test set. -It’s separated from fit to make sure you never run on your test set until you want to.

-
-
Parameters
-
    -
  • model (Optional[LightningModule]) – The model to test.

  • -
  • dataloaders (Union[DataLoader, Sequence[DataLoader], LightningDataModule, None]) – A torch.utils.data.DataLoader or a sequence of them, -or a LightningDataModule specifying test samples.

  • -
  • ckpt_path (Optional[str]) – Either best or path to the checkpoint you wish to test. -If None and the model instance was passed, use the current weights. -Otherwise, the best model checkpoint from the previous trainer.fit call will be loaded -if a checkpoint callback is configured.

  • -
  • verbose (bool) – If True, prints the test results.

  • -
  • datamodule (Optional[LightningDataModule]) – An instance of LightningDataModule.

  • -
-
-
Return type
-

List[Dict[str, float]]

-
-
Returns
-

List of dictionaries with metrics logged during the test phase, e.g., in model- or callback hooks -like test_step(), -test_epoch_end(), etc. -The length of the list corresponds to the number of test dataloaders used.

-
-
-
- -
-

Test after Fit

-

To run the test set after training completes, use this method.

-
# run full training
-trainer.fit(model)
-
-# (1) load the best checkpoint automatically (lightning tracks this for you)
-trainer.test(ckpt_path="best")
-
-# (2) test using a specific checkpoint
-trainer.test(ckpt_path="/path/to/my_checkpoint.ckpt")
-
-# (3) test with an explicit model (will use this model and not load a checkpoint)
-trainer.test(model)
-
-
-
-

Warning

-

It is recommended to test with Trainer(devices=1) since distributed strategies such as DDP -use DistributedSampler internally, which replicates some samples to -make sure all devices have same batch size in case of uneven inputs. This is helpful to make sure -benchmarking for research papers is done the right way.

-
-
-
-

Test Multiple Models

-

You can run the test set on multiple models using the same trainer instance.

-
model1 = LitModel()
-model2 = GANModel()
-
-trainer = Trainer()
-trainer.test(model1)
-trainer.test(model2)
-
-
-
-
-

Test Pre-Trained Model

-

To run the test set on a pre-trained model, use this method.

-
model = MyLightningModule.load_from_checkpoint(
-    checkpoint_path="/path/to/pytorch_checkpoint.ckpt",
-    hparams_file="/path/to/test_tube/experiment/version/hparams.yaml",
-    map_location=None,
-)
-
-# init trainer with whatever options
-trainer = Trainer(...)
-
-# test (pass in the model)
-trainer.test(model)
-
-
-

In this case, the options you pass to trainer will be used when -running the test set (ie: 16-bit, dp, ddp, etc…)

-
-
-

Test with Additional DataLoaders

-

You can still run inference on a test dataset even if the test_dataloader() method hasn’t been -defined within your lightning module instance. This would be the case when your test data -is not available at the time your model was declared.

-
# setup your data loader
-test_dataloader = DataLoader(...)
-
-# test (pass in the loader)
-trainer.test(dataloaders=test_dataloader)
-
-
-

You can either pass in a single dataloader or a list of them. This optional named -parameter can be used in conjunction with any of the above use cases. Additionally, -you can also pass in an datamodules that have overridden the -test_dataloader method.

-
class MyDataModule(pl.LightningDataModule):
-    ...
-
-    def test_dataloader(self):
-        return DataLoader(...)
-
-
-# setup your datamodule
-dm = MyDataModule(...)
-
-# test (pass in datamodule)
-trainer.test(datamodule=dm)
-
-
-
-
-
-
-

Validation

-

Lightning allows the user to validate their models with any compatible val dataloaders. This can be done before/after training. -The logic associated to the validation is defined within the validation_step().

-

Apart from this .validate has same API as .test, but would rely respectively on validation_step() and test_step().

-
-

Note

-

.validate method uses the same validation logic being used under validation happening within -fit() call.

-
-
-

Warning

-

When using trainer.validate(), it is recommended to use Trainer(devices=1) since distributed strategies such as DDP -uses DistributedSampler internally, which replicates some samples to -make sure all devices have same batch size in case of uneven inputs. This is helpful to make sure -benchmarking for research papers is done the right way.

-
-
-
-Trainer.validate(model=None, dataloaders=None, ckpt_path=None, verbose=True, datamodule=None)[source]
-

Perform one evaluation epoch over the validation set.

-
-
Parameters
-
    -
  • model (Optional[LightningModule]) – The model to validate.

  • -
  • dataloaders (Union[DataLoader, Sequence[DataLoader], LightningDataModule, None]) – A torch.utils.data.DataLoader or a sequence of them, -or a LightningDataModule specifying validation samples.

  • -
  • ckpt_path (Optional[str]) – Either best or path to the checkpoint you wish to validate. -If None and the model instance was passed, use the current weights. -Otherwise, the best model checkpoint from the previous trainer.fit call will be loaded -if a checkpoint callback is configured.

  • -
  • verbose (bool) – If True, prints the validation results.

  • -
  • datamodule (Optional[LightningDataModule]) – An instance of LightningDataModule.

  • -
-
-
Return type
-

List[Dict[str, float]]

-
-
Returns
-

List of dictionaries with metrics logged during the validation phase, e.g., in model- or callback hooks -like validation_step(), -validation_epoch_end(), etc. -The length of the list corresponds to the number of validation dataloaders used.

-
-
-
- -
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/gradient_accumulation.html b/docs/common/gradient_accumulation.html deleted file mode 100644 index f8d6505..0000000 --- a/docs/common/gradient_accumulation.html +++ /dev/null @@ -1,724 +0,0 @@ - - - - - - - - - - - - - - <no title> — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -

Accumulated gradients run K small batches of size N before doing a backward pass. The effect is a large effective batch size of size KxN, where N is the batch size. -Internally it doesn’t stack up the batches and do a forward pass rather it accumulates the gradients for K batches and then do an optimizer.step to make sure the -effective batch size is increased but there is no memory overhead.

-
-

Warning

-

When using distributed training for eg. DDP, with let’s say with P devices, each device accumulates independently i.e. it stores the gradients -after each loss.backward() and doesn’t sync the gradients across the devices until we call optimizer.step(). So for each accumulation -step, the effective batch size on each device will remain N*K but right before the optimizer.step(), the gradient sync will make the effective -batch size as P*N*K. For DP, since the batch is split across devices, the final effective batch size will be N*K.

-
-
-

See also

-

Trainer

-
-
# DEFAULT (ie: no accumulated grads)
-trainer = Trainer(accumulate_grad_batches=1)
-
-# Accumulate gradients for 7 batches
-trainer = Trainer(accumulate_grad_batches=7)
-
-
-

You can set different values for it at different epochs by passing a dictionary, where the key represents the epoch at which the value for gradient accumulation -should be updated.

-
# till 5th epoch, it will accumulate every 8 batches. From 5th epoch
-# till 9th epoch it will accumulate every 4 batches and after that no accumulation
-# will happen. Note that you need to use zero-indexed epoch keys here
-trainer = Trainer(accumulate_grad_batches={0: 8, 4: 4, 8: 1})
-
-
-

Or, you can create custom GradientAccumulationScheduler

-
from pytorch_lightning.callbacks import GradientAccumulationScheduler
-
-
-# till 5th epoch, it will accumulate every 8 batches. From 5th epoch
-# till 9th epoch it will accumulate every 4 batches and after that no accumulation
-# will happen. Note that you need to use zero-indexed epoch keys here
-accumulator = GradientAccumulationScheduler(scheduling={0: 8, 4: 4, 8: 1})
-trainer = Trainer(callbacks=accumulator)
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
-
    -
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/hyperparameters.html b/docs/common/hyperparameters.html deleted file mode 100644 index 19b0173..0000000 --- a/docs/common/hyperparameters.html +++ /dev/null @@ -1,944 +0,0 @@ - - - - - - - - - - - - - - Configure hyperparameters from the CLI — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Configure hyperparameters from the CLI
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Configure hyperparameters from the CLI

-

Lightning has utilities to interact seamlessly with the command line ArgumentParser -and plays well with the hyperparameter optimization framework of your choice.

-
-
-

ArgumentParser

-

Lightning is designed to augment a lot of the functionality of the built-in Python ArgumentParser

-
from argparse import ArgumentParser
-
-parser = ArgumentParser()
-parser.add_argument("--layer_1_dim", type=int, default=128)
-args = parser.parse_args()
-
-
-

This allows you to call your program like so:

-
python trainer.py --layer_1_dim 64
-
-
-
-
-
-

Argparser Best Practices

-

It is best practice to layer your arguments in three sections.

-
    -
  1. Trainer args (accelerator, devices, num_nodes, etc…)

  2. -
  3. Model specific arguments (layer_dim, num_layers, learning_rate, etc…)

  4. -
  5. Program arguments (data_path, cluster_email, etc…)

  6. -
-
-

-
-

We can do this as follows. First, in your LightningModule, define the arguments -specific to that module. Remember that data splits or data paths may also be specific to -a module (i.e.: if your project has a model that trains on Imagenet and another on CIFAR-10).

-
class LitModel(LightningModule):
-    @staticmethod
-    def add_model_specific_args(parent_parser):
-        parser = parent_parser.add_argument_group("LitModel")
-        parser.add_argument("--encoder_layers", type=int, default=12)
-        parser.add_argument("--data_path", type=str, default="/some/path")
-        return parent_parser
-
-
-

Now in your main trainer file, add the Trainer args, the program args, and add the model args

-
# ----------------
-# trainer_main.py
-# ----------------
-from argparse import ArgumentParser
-
-parser = ArgumentParser()
-
-# add PROGRAM level args
-parser.add_argument("--conda_env", type=str, default="some_name")
-parser.add_argument("--notification_email", type=str, default="will@email.com")
-
-# add model specific args
-parser = LitModel.add_model_specific_args(parser)
-
-# add all the available trainer options to argparse
-# ie: now --accelerator --devices --num_nodes ... --fast_dev_run all work in the cli
-parser = Trainer.add_argparse_args(parser)
-
-args = parser.parse_args()
-
-
-

Now you can call run your program like so:

-
python trainer_main.py --accelerator 'gpu' --devices 2 --num_nodes 2 --conda_env 'my_env' --encoder_layers 12
-
-
-

Finally, make sure to start the training like so:

-
# init the trainer like this
-trainer = Trainer.from_argparse_args(args, early_stopping_callback=...)
-
-# NOT like this
-trainer = Trainer(accelerator=hparams.accelerator, devices=hparams.devices, ...)
-
-# init the model with Namespace directly
-model = LitModel(args)
-
-# or init the model with all the key-value pairs
-dict_args = vars(args)
-model = LitModel(**dict_args)
-
-
-
-
-
-

LightningModule hyperparameters

-

Often times we train many versions of a model. You might share that model or come back to it a few months later -at which point it is very useful to know how that model was trained (i.e.: what learning rate, neural network, etc…).

-

Lightning has a standardized way of saving the information for you in checkpoints and YAML files. The goal here is to -improve readability and reproducibility.

-
-

save_hyperparameters

-

Use save_hyperparameters() within your -LightningModule’s __init__ method. -It will enable Lightning to store all the provided arguments under the self.hparams attribute. -These hyperparameters will also be stored within the model checkpoint, which simplifies model re-instantiation after training.

-
class LitMNIST(LightningModule):
-    def __init__(self, layer_1_dim=128, learning_rate=1e-2):
-        super().__init__()
-        # call this to save (layer_1_dim=128, learning_rate=1e-4) to the checkpoint
-        self.save_hyperparameters()
-
-        # equivalent
-        self.save_hyperparameters("layer_1_dim", "learning_rate")
-
-        # Now possible to access layer_1_dim from hparams
-        self.hparams.layer_1_dim
-
-
-

In addition, loggers that support it will automatically log the contents of self.hparams.

-
-
-

Excluding hyperparameters

-

By default, every parameter of the __init__ method will be considered a hyperparameter to the LightningModule. -However, sometimes some parameters need to be excluded from saving, for example when they are not serializable. -Those parameters should be provided back when reloading the LightningModule. -In this case, exclude them explicitly:

-
class LitMNIST(LightningModule):
-    def __init__(self, loss_fx, generator_network, layer_1_dim=128):
-        super().__init__()
-        self.layer_1_dim = layer_1_dim
-        self.loss_fx = loss_fx
-
-        # call this to save only (layer_1_dim=128) to the checkpoint
-        self.save_hyperparameters("layer_1_dim")
-
-        # equivalent
-        self.save_hyperparameters(ignore=["loss_fx", "generator_network"])
-
-
-
-
-

load_from_checkpoint

-

LightningModules that have hyperparameters automatically saved with save_hyperparameters() -can conveniently be loaded and instantiated directly from a checkpoint with load_from_checkpoint():

-
# to load specify the other args
-model = LitMNIST.load_from_checkpoint(PATH, loss_fx=torch.nn.SomeOtherLoss, generator_network=MyGenerator())
-
-
-

If parameters were excluded, they need to be provided at the time of loading:

-
# the excluded parameters were `loss_fx` and `generator_network`
-model = LitMNIST.load_from_checkpoint(PATH, loss_fx=torch.nn.SomeOtherLoss, generator_network=MyGenerator())
-
-
-
-
-
-
-

Trainer args

-

To recap, add ALL possible trainer flags to the argparser and init the Trainer this way

-
parser = ArgumentParser()
-parser = Trainer.add_argparse_args(parser)
-hparams = parser.parse_args()
-
-trainer = Trainer.from_argparse_args(hparams)
-
-# or if you need to pass in callbacks
-trainer = Trainer.from_argparse_args(hparams, enable_checkpointing=..., callbacks=[...])
-
-
-
-
-
-

Multiple Lightning Modules

-

We often have multiple Lightning Modules where each one has different arguments. Instead of -polluting the main.py file, the LightningModule lets you define arguments for each one.

-
class LitMNIST(LightningModule):
-    def __init__(self, layer_1_dim, **kwargs):
-        super().__init__()
-        self.layer_1 = nn.Linear(28 * 28, layer_1_dim)
-
-    @staticmethod
-    def add_model_specific_args(parent_parser):
-        parser = parent_parser.add_argument_group("LitMNIST")
-        parser.add_argument("--layer_1_dim", type=int, default=128)
-        return parent_parser
-
-
-
class GoodGAN(LightningModule):
-    def __init__(self, encoder_layers, **kwargs):
-        super().__init__()
-        self.encoder = Encoder(layers=encoder_layers)
-
-    @staticmethod
-    def add_model_specific_args(parent_parser):
-        parser = parent_parser.add_argument_group("GoodGAN")
-        parser.add_argument("--encoder_layers", type=int, default=12)
-        return parent_parser
-
-
-

Now we can allow each model to inject the arguments it needs in the main.py

-
def main(args):
-    dict_args = vars(args)
-
-    # pick model
-    if args.model_name == "gan":
-        model = GoodGAN(**dict_args)
-    elif args.model_name == "mnist":
-        model = LitMNIST(**dict_args)
-
-    trainer = Trainer.from_argparse_args(args)
-    trainer.fit(model)
-
-
-if __name__ == "__main__":
-    parser = ArgumentParser()
-    parser = Trainer.add_argparse_args(parser)
-
-    # figure out which model to use
-    parser.add_argument("--model_name", type=str, default="gan", help="gan or mnist")
-
-    # THIS LINE IS KEY TO PULL THE MODEL NAME
-    temp_args, _ = parser.parse_known_args()
-
-    # let the model add what it wants
-    if temp_args.model_name == "gan":
-        parser = GoodGAN.add_model_specific_args(parser)
-    elif temp_args.model_name == "mnist":
-        parser = LitMNIST.add_model_specific_args(parser)
-
-    args = parser.parse_args()
-
-    # train
-    main(args)
-
-
-

and now we can train MNIST or the GAN using the command line interface!

-
$ python main.py --model_name gan --encoder_layers 24
-$ python main.py --model_name mnist --layer_1_dim 128
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/lightning_module.html b/docs/common/lightning_module.html deleted file mode 100644 index 1041cd4..0000000 --- a/docs/common/lightning_module.html +++ /dev/null @@ -1,4988 +0,0 @@ - - - - - - - - - - - - - - LightningModule — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

LightningModule

-

A LightningModule organizes your PyTorch code into 6 sections:

-
    -
  • Computations (init).

  • -
  • Train Loop (training_step)

  • -
  • Validation Loop (validation_step)

  • -
  • Test Loop (test_step)

  • -
  • Prediction Loop (predict_step)

  • -
  • Optimizers and LR Schedulers (configure_optimizers)

  • -
-
-

-
-
-

-
-

Notice a few things.

-
    -
  1. It is the SAME code.

  2. -
  3. The PyTorch code IS NOT abstracted - just organized.

  4. -
  5. All the other code that’s not in the LightningModule -has been automated for you by the Trainer.

  6. -
-
-

-
-
-
net = Net()
-trainer = Trainer()
-trainer.fit(net)
-
-
-
-
    -
  1. There are no .cuda() or .to(device) calls required. Lightning does these for you.

  2. -
-
-

-
-
-
# don't do in Lightning
-x = torch.Tensor(2, 3)
-x = x.cuda()
-x = x.to(device)
-
-# do this instead
-x = x  # leave it alone!
-
-# or to init a new tensor
-new_x = torch.Tensor(2, 3)
-new_x = new_x.type_as(x)
-
-
-
-
    -
  1. When running under a distributed strategy, Lightning handles the distributed sampler for you by default.

  2. -
-
-

-
-
-
# Don't do in Lightning...
-data = MNIST(...)
-sampler = DistributedSampler(data)
-DataLoader(data, sampler=sampler)
-
-# do this instead
-data = MNIST(...)
-DataLoader(data)
-
-
-
-
    -
  1. A LightningModule is a torch.nn.Module but with added functionality. Use it as such!

  2. -
-
-

-
-
-
net = Net.load_from_checkpoint(PATH)
-net.freeze()
-out = net(x)
-
-
-
-

Thus, to use Lightning, you just need to organize your code which takes about 30 minutes, -(and let’s be real, you probably should do anyway).

-
-
-

Starter Example

-

Here are the only required methods.

-
import pytorch_lightning as pl
-import torch.nn as nn
-import torch.nn.functional as F
-
-
-class LitModel(pl.LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.l1 = nn.Linear(28 * 28, 10)
-
-    def forward(self, x):
-        return torch.relu(self.l1(x.view(x.size(0), -1)))
-
-    def training_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self(x)
-        loss = F.cross_entropy(y_hat, y)
-        return loss
-
-    def configure_optimizers(self):
-        return torch.optim.Adam(self.parameters(), lr=0.02)
-
-
-

Which you can train by doing:

-
train_loader = DataLoader(MNIST(os.getcwd(), download=True, transform=transforms.ToTensor()))
-trainer = pl.Trainer(max_epochs=1)
-model = LitModel()
-
-trainer.fit(model, train_dataloaders=train_loader)
-
-
-

The LightningModule has many convenience methods, but the core ones you need to know about are:

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Name

Description

init

Define computations here

forward

Use for inference only (separate from training_step)

training_step

the complete training loop

validation_step

the complete validation loop

test_step

the complete test loop

predict_step

the complete prediction loop

configure_optimizers

define optimizers and LR schedulers

-
-
-
-

Training

-
-

Training Loop

-

To activate the training loop, override the training_step() method.

-
class LitClassifier(pl.LightningModule):
-    def __init__(self, model):
-        super().__init__()
-        self.model = model
-
-    def training_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.model(x)
-        loss = F.cross_entropy(y_hat, y)
-        return loss
-
-
-

Under the hood, Lightning does the following (pseudocode):

-
# put model in train mode and enable gradient calculation
-model.train()
-torch.set_grad_enabled(True)
-
-outs = []
-for batch_idx, batch in enumerate(train_dataloader):
-    loss = training_step(batch, batch_idx)
-    outs.append(loss.detach())
-
-    # clear gradients
-    optimizer.zero_grad()
-
-    # backward
-    loss.backward()
-
-    # update parameters
-    optimizer.step()
-
-
-
-
-

Train Epoch-level Metrics

-

If you want to calculate epoch-level metrics and log them, use log().

-
def training_step(self, batch, batch_idx):
-    x, y = batch
-    y_hat = self.model(x)
-    loss = F.cross_entropy(y_hat, y)
-
-    # logs metrics for each training_step,
-    # and the average across the epoch, to the progress bar and logger
-    self.log("train_loss", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True)
-    return loss
-
-
-

The log() object automatically reduces the -requested metrics across a complete epoch and devices. Here’s the pseudocode of what it does under the hood:

-
outs = []
-for batch_idx, batch in enumerate(train_dataloader):
-    # forward
-    loss = training_step(batch, batch_idx)
-    outs.append(loss)
-
-    # clear gradients
-    optimizer.zero_grad()
-
-    # backward
-    loss.backward()
-
-    # update parameters
-    optimizer.step()
-
-epoch_metric = torch.mean(torch.stack([x for x in outs]))
-
-
-
-
-

Train Epoch-level Operations

-

If you need to do something with all the outputs of each training_step(), -override the training_epoch_end() method.

-
def training_step(self, batch, batch_idx):
-    x, y = batch
-    y_hat = self.model(x)
-    loss = F.cross_entropy(y_hat, y)
-    preds = ...
-    return {"loss": loss, "other_stuff": preds}
-
-
-def training_epoch_end(self, training_step_outputs):
-    all_preds = torch.stack(training_step_outputs)
-    ...
-
-
-

The matching pseudocode is:

-
outs = []
-for batch_idx, batch in enumerate(train_dataloader):
-    # forward
-    loss = training_step(batch, batch_idx)
-    outs.append(loss)
-
-    # clear gradients
-    optimizer.zero_grad()
-
-    # backward
-    loss.backward()
-
-    # update parameters
-    optimizer.step()
-
-training_epoch_end(outs)
-
-
-
-
-

Training with DataParallel

-

When training using a strategy that splits data from each batch across GPUs, sometimes you might -need to aggregate them on the main GPU for processing (DP, or DDP2).

-

In this case, implement the training_step_end() -method which will have outputs from all the devices and you can accumulate to get the effective results.

-
def training_step(self, batch, batch_idx):
-    x, y = batch
-    y_hat = self.model(x)
-    loss = F.cross_entropy(y_hat, y)
-    pred = ...
-    return {"loss": loss, "pred": pred}
-
-
-def training_step_end(self, batch_parts):
-    # predictions from each GPU
-    predictions = batch_parts["pred"]
-    # losses from each GPU
-    losses = batch_parts["loss"]
-
-    gpu_0_prediction = predictions[0]
-    gpu_1_prediction = predictions[1]
-
-    # do something with both outputs
-    return (losses[0] + losses[1]) / 2
-
-
-def training_epoch_end(self, training_step_outputs):
-    for out in training_step_outputs:
-        ...
-
-
-

Here is the Lightning training pseudo-code for DP:

-
outs = []
-for batch_idx, train_batch in enumerate(train_dataloader):
-    batches = split_batch(train_batch)
-    dp_outs = []
-    for sub_batch in batches:
-        # 1
-        dp_out = training_step(sub_batch, batch_idx)
-        dp_outs.append(dp_out)
-
-    # 2
-    out = training_step_end(dp_outs)
-    outs.append(out)
-
-# do something with the outputs for all batches
-# 3
-training_epoch_end(outs)
-
-
-
-
-
-
-

Validation

-
-

Validation Loop

-

To activate the validation loop while training, override the validation_step() method.

-
class LitModel(pl.LightningModule):
-    def validation_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.model(x)
-        loss = F.cross_entropy(y_hat, y)
-        self.log("val_loss", loss)
-
-
-

Under the hood, Lightning does the following (pseudocode):

-
# ...
-for batch_idx, batch in enumerate(train_dataloader):
-    loss = model.training_step(batch, batch_idx)
-    loss.backward()
-    # ...
-
-    if validate_at_some_point:
-        # disable grads + batchnorm + dropout
-        torch.set_grad_enabled(False)
-        model.eval()
-
-        # ----------------- VAL LOOP ---------------
-        for val_batch_idx, val_batch in enumerate(val_dataloader):
-            val_out = model.validation_step(val_batch, val_batch_idx)
-        # ----------------- VAL LOOP ---------------
-
-        # enable grads + batchnorm + dropout
-        torch.set_grad_enabled(True)
-        model.train()
-
-
-

You can also run just the validation loop on your validation dataloaders by overriding validation_step() -and calling validate().

-
model = Model()
-trainer = Trainer()
-trainer.validate(model)
-
-
-
-

Note

-

It is recommended to validate on single device to ensure each sample/batch gets evaluated exactly once. -This is helpful to make sure benchmarking for research papers is done the right way. Otherwise, in a -multi-device setting, samples could occur duplicated when DistributedSampler -is used, for eg. with strategy="ddp". It replicates some samples on some devices to make sure all devices have -same batch size in case of uneven inputs.

-
-
-
-

Validation Epoch-level Metrics

-

If you need to do something with all the outputs of each validation_step(), -override the validation_epoch_end() method. Note that this method is called before training_epoch_end().

-
def validation_step(self, batch, batch_idx):
-    x, y = batch
-    y_hat = self.model(x)
-    loss = F.cross_entropy(y_hat, y)
-    pred = ...
-    return pred
-
-
-def validation_epoch_end(self, validation_step_outputs):
-    all_preds = torch.stack(validation_step_outputs)
-    ...
-
-
-
-
-

Validating with DataParallel

-

When training using a strategy that splits data from each batch across GPUs, sometimes you might -need to aggregate them on the main GPU for processing (DP, or DDP2).

-

In this case, implement the validation_step_end() -method which will have outputs from all the devices and you can accumulate to get the effective results.

-
def validation_step(self, batch, batch_idx):
-    x, y = batch
-    y_hat = self.model(x)
-    loss = F.cross_entropy(y_hat, y)
-    pred = ...
-    return {"loss": loss, "pred": pred}
-
-
-def validation_step_end(self, batch_parts):
-    # predictions from each GPU
-    predictions = batch_parts["pred"]
-    # losses from each GPU
-    losses = batch_parts["loss"]
-
-    gpu_0_prediction = predictions[0]
-    gpu_1_prediction = predictions[1]
-
-    # do something with both outputs
-    return (losses[0] + losses[1]) / 2
-
-
-def validation_epoch_end(self, validation_step_outputs):
-    for out in validation_step_outputs:
-        ...
-
-
-

Here is the Lightning validation pseudo-code for DP:

-
outs = []
-for batch in dataloader:
-    batches = split_batch(batch)
-    dp_outs = []
-    for sub_batch in batches:
-        # 1
-        dp_out = validation_step(sub_batch)
-        dp_outs.append(dp_out)
-
-    # 2
-    out = validation_step_end(dp_outs)
-    outs.append(out)
-
-# do something with the outputs for all batches
-# 3
-validation_epoch_end(outs)
-
-
-
-
-
-
-

Testing

-
-

Test Loop

-

The process for enabling a test loop is the same as the process for enabling a validation loop. Please refer to -the section above for details. For this you need to override the test_step() method.

-

The only difference is that the test loop is only called when test() is used.

-
model = Model()
-trainer = Trainer()
-trainer.fit(model)
-
-# automatically loads the best weights for you
-trainer.test(model)
-
-
-

There are two ways to call test():

-
# call after training
-trainer = Trainer()
-trainer.fit(model)
-
-# automatically auto-loads the best weights from the previous run
-trainer.test(dataloaders=test_dataloader)
-
-# or call with pretrained model
-model = MyLightningModule.load_from_checkpoint(PATH)
-trainer = Trainer()
-trainer.test(model, dataloaders=test_dataloader)
-
-
-
-

Note

-

It is recommended to validate on single device to ensure each sample/batch gets evaluated exactly once. -This is helpful to make sure benchmarking for research papers is done the right way. Otherwise, in a -multi-device setting, samples could occur duplicated when DistributedSampler -is used, for eg. with strategy="ddp". It replicates some samples on some devices to make sure all devices have -same batch size in case of uneven inputs.

-
-
-
-
-
-

Inference

-
-

Prediction Loop

-

By default, the predict_step() method runs the -forward() method. In order to customize this behaviour, -simply override the predict_step() method.

-

For the example let’s override predict_step and try out Monte Carlo Dropout:

-
class LitMCdropoutModel(pl.LightningModule):
-    def __init__(self, model, mc_iteration):
-        super().__init__()
-        self.model = model
-        self.dropout = nn.Dropout()
-        self.mc_iteration = mc_iteration
-
-    def predict_step(self, batch, batch_idx):
-        # enable Monte Carlo Dropout
-        self.dropout.train()
-
-        # take average of `self.mc_iteration` iterations
-        pred = torch.vstack([self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)]).mean(dim=0)
-        return pred
-
-
-

Under the hood, Lightning does the following (pseudocode):

-
# disable grads + batchnorm + dropout
-torch.set_grad_enabled(False)
-model.eval()
-all_preds = []
-
-for batch_idx, batch in enumerate(predict_dataloader):
-    pred = model.predict_step(batch, batch_idx)
-    all_preds.append(pred)
-
-
-

There are two ways to call predict():

-
# call after training
-trainer = Trainer()
-trainer.fit(model)
-
-# automatically auto-loads the best weights from the previous run
-predictions = trainer.predict(dataloaders=predict_dataloader)
-
-# or call with pretrained model
-model = MyLightningModule.load_from_checkpoint(PATH)
-trainer = Trainer()
-predictions = trainer.predict(model, dataloaders=test_dataloader)
-
-
-
-
-

Inference in Research

-

If you want to perform inference with the system, you can add a forward method to the LightningModule.

-
-

Note

-

When using forward, you are responsible to call eval() and use the no_grad() context manager.

-
-
class Autoencoder(pl.LightningModule):
-    def forward(self, x):
-        return self.decoder(x)
-
-
-model = Autoencoder()
-model.eval()
-with torch.no_grad():
-    reconstruction = model(embedding)
-
-
-

The advantage of adding a forward is that in complex systems, you can do a much more involved inference procedure, -such as text generation:

-
class Seq2Seq(pl.LightningModule):
-    def forward(self, x):
-        embeddings = self(x)
-        hidden_states = self.encoder(embeddings)
-        for h in hidden_states:
-            # decode
-            ...
-        return decoded
-
-
-

In the case where you want to scale your inference, you should be using -predict_step().

-
class Autoencoder(pl.LightningModule):
-    def forward(self, x):
-        return self.decoder(x)
-
-    def predict_step(self, batch, batch_idx, dataloader_idx=0):
-        # this calls forward
-        return self(batch)
-
-
-data_module = ...
-model = Autoencoder()
-trainer = Trainer(accelerator="gpu", devices=2)
-trainer.predict(model, data_module)
-
-
-
-
-

Inference in Production

-

For cases like production, you might want to iterate different models inside a LightningModule.

-
from torchmetrics.functional import accuracy
-
-
-class ClassificationTask(pl.LightningModule):
-    def __init__(self, model):
-        super().__init__()
-        self.model = model
-
-    def training_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.model(x)
-        loss = F.cross_entropy(y_hat, y)
-        return loss
-
-    def validation_step(self, batch, batch_idx):
-        loss, acc = self._shared_eval_step(batch, batch_idx)
-        metrics = {"val_acc": acc, "val_loss": loss}
-        self.log_dict(metrics)
-        return metrics
-
-    def test_step(self, batch, batch_idx):
-        loss, acc = self._shared_eval_step(batch, batch_idx)
-        metrics = {"test_acc": acc, "test_loss": loss}
-        self.log_dict(metrics)
-        return metrics
-
-    def _shared_eval_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.model(x)
-        loss = F.cross_entropy(y_hat, y)
-        acc = accuracy(y_hat, y)
-        return loss, acc
-
-    def predict_step(self, batch, batch_idx, dataloader_idx=0):
-        x, y = batch
-        y_hat = self.model(x)
-        return y_hat
-
-    def configure_optimizers(self):
-        return torch.optim.Adam(self.model.parameters(), lr=0.02)
-
-
-

Then pass in any arbitrary model to be fit with this task

-
for model in [resnet50(), vgg16(), BidirectionalRNN()]:
-    task = ClassificationTask(model)
-
-    trainer = Trainer(accelerator="gpu", devices=2)
-    trainer.fit(task, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader)
-
-
-

Tasks can be arbitrarily complex such as implementing GAN training, self-supervised or even RL.

-
class GANTask(pl.LightningModule):
-    def __init__(self, generator, discriminator):
-        super().__init__()
-        self.generator = generator
-        self.discriminator = discriminator
-
-    ...
-
-
-

When used like this, the model can be separated from the Task and thus used in production without needing to keep it in -a LightningModule.

-

The following example shows how you can run inference in the Python runtime:

-
task = ClassificationTask(model)
-trainer = Trainer(accelerator="gpu", devices=2)
-trainer.fit(task, train_dataloader, val_dataloader)
-trainer.save_checkpoint("best_model.ckpt")
-
-# use model after training or load weights and drop into the production system
-model = ClassificationTask.load_from_checkpoint("best_model.ckpt")
-x = ...
-model.eval()
-with torch.no_grad():
-    y_hat = model(x)
-
-
-

Check out Inference in Production guide to learn about the possible ways to perform inference in production.

-
-
-
-
-

Child Modules

-

Research projects tend to test different approaches to the same dataset. -This is very easy to do in Lightning with inheritance.

-

For example, imagine we now want to train an AutoEncoder to use as a feature extractor for images. -The only things that change in the LitAutoEncoder model are the init, forward, training, validation and test step.

-
class Encoder(torch.nn.Module):
-    ...
-
-
-class Decoder(torch.nn.Module):
-    ...
-
-
-class AutoEncoder(torch.nn.Module):
-    def __init__(self):
-        super().__init__()
-        self.encoder = Encoder()
-        self.decoder = Decoder()
-
-    def forward(self, x):
-        return self.decoder(self.encoder(x))
-
-
-class LitAutoEncoder(LightningModule):
-    def __init__(self, auto_encoder):
-        super().__init__()
-        self.auto_encoder = auto_encoder
-        self.metric = torch.nn.MSELoss()
-
-    def forward(self, x):
-        return self.auto_encoder.encoder(x)
-
-    def training_step(self, batch, batch_idx):
-        x, _ = batch
-        x_hat = self.auto_encoder(x)
-        loss = self.metric(x, x_hat)
-        return loss
-
-    def validation_step(self, batch, batch_idx):
-        self._shared_eval(batch, batch_idx, "val")
-
-    def test_step(self, batch, batch_idx):
-        self._shared_eval(batch, batch_idx, "test")
-
-    def _shared_eval(self, batch, batch_idx, prefix):
-        x, _ = batch
-        x_hat = self.auto_encoder(x)
-        loss = self.metric(x, x_hat)
-        self.log(f"{prefix}_loss", loss)
-
-
-

and we can train this using the Trainer:

-
auto_encoder = AutoEncoder()
-lightning_module = LitAutoEncoder(auto_encoder)
-trainer = Trainer()
-trainer.fit(lightning_module, train_dataloader, val_dataloader)
-
-
-

And remember that the forward method should define the practical use of a LightningModule. -In this case, we want to use the LitAutoEncoder to extract image representations:

-
some_images = torch.Tensor(32, 1, 28, 28)
-representations = lightning_module(some_images)
-
-
-
-
-
-

LightningModule API

-
-

Methods

-
-

all_gather

-
-
-LightningModule.all_gather(data, group=None, sync_grads=False)[source]
-

Allows users to call self.all_gather() from the LightningModule, thus making the all_gather operation -accelerator agnostic. all_gather is a function provided by accelerators to gather a tensor from several -distributed processes.

-
-
Parameters
-
    -
  • data (Union[Tensor, Dict, List, Tuple]) – int, float, tensor of shape (batch, …), or a (possibly nested) collection thereof.

  • -
  • group (Optional[Any]) – the process group to gather results from. Defaults to all processes (world)

  • -
  • sync_grads (bool) – flag that allows users to synchronize gradients for the all_gather operation

  • -
-
-
Returns
-

A tensor of shape (world_size, batch, …), or if the input was a collection -the output will also be a collection with tensors of this shape.

-
-
-
- -
-
-

configure_callbacks

-
-
-LightningModule.configure_callbacks()[source]
-

Configure model-specific callbacks. When the model gets attached, e.g., when .fit() or .test() -gets called, the list or a callback returned here will be merged with the list of callbacks passed to the -Trainer’s callbacks argument. If a callback returned here has the same type as one or several callbacks -already present in the Trainer’s callbacks list, it will take priority and replace them. In addition, -Lightning will make sure ModelCheckpoint callbacks -run last.

-
-
Return type
-

Union[Sequence[Callback], Callback]

-
-
Returns
-

A callback or a list of callbacks which will extend the list of callbacks in the Trainer.

-
-
-

Example:

-
def configure_callbacks(self):
-    early_stop = EarlyStopping(monitor="val_acc", mode="max")
-    checkpoint = ModelCheckpoint(monitor="val_loss")
-    return [early_stop, checkpoint]
-
-
-
-

Note

-

Certain callback methods like on_init_start() -will never be invoked on the new callbacks returned here.

-
-
- -
-
-

configure_optimizers

-
-
-LightningModule.configure_optimizers()[source]
-

Choose what optimizers and learning-rate schedulers to use in your optimization. -Normally you’d need one. But in the case of GANs or similar you might have multiple.

-
-
Returns
-

Any of these 6 options.

-
    -
  • Single optimizer.

  • -
  • List or Tuple of optimizers.

  • -
  • Two lists - The first list has multiple optimizers, and the second has multiple LR schedulers -(or multiple lr_scheduler_config).

  • -
  • Dictionary, with an "optimizer" key, and (optionally) a "lr_scheduler" -key whose value is a single LR scheduler or lr_scheduler_config.

  • -
  • Tuple of dictionaries as described above, with an optional "frequency" key.

  • -
  • None - Fit will run without any optimizer.

  • -
-

-
-
-

The lr_scheduler_config is a dictionary which contains the scheduler and its associated configuration. -The default configuration is shown below.

-
lr_scheduler_config = {
-    # REQUIRED: The scheduler instance
-    "scheduler": lr_scheduler,
-    # The unit of the scheduler's step size, could also be 'step'.
-    # 'epoch' updates the scheduler on epoch end whereas 'step'
-    # updates it after a optimizer update.
-    "interval": "epoch",
-    # How many epochs/steps should pass between calls to
-    # `scheduler.step()`. 1 corresponds to updating the learning
-    # rate after every epoch/step.
-    "frequency": 1,
-    # Metric to to monitor for schedulers like `ReduceLROnPlateau`
-    "monitor": "val_loss",
-    # If set to `True`, will enforce that the value specified 'monitor'
-    # is available when the scheduler is updated, thus stopping
-    # training if not found. If set to `False`, it will only produce a warning
-    "strict": True,
-    # If using the `LearningRateMonitor` callback to monitor the
-    # learning rate progress, this keyword can be used to specify
-    # a custom logged name
-    "name": None,
-}
-
-
-

When there are schedulers in which the .step() method is conditioned on a value, such as the -torch.optim.lr_scheduler.ReduceLROnPlateau scheduler, Lightning requires that the -lr_scheduler_config contains the keyword "monitor" set to the metric name that the scheduler -should be conditioned on.

-
# The ReduceLROnPlateau scheduler requires a monitor
-def configure_optimizers(self):
-    optimizer = Adam(...)
-    return {
-        "optimizer": optimizer,
-        "lr_scheduler": {
-            "scheduler": ReduceLROnPlateau(optimizer, ...),
-            "monitor": "metric_to_track",
-            "frequency": "indicates how often the metric is updated"
-            # If "monitor" references validation metrics, then "frequency" should be set to a
-            # multiple of "trainer.check_val_every_n_epoch".
-        },
-    }
-
-
-# In the case of two optimizers, only one using the ReduceLROnPlateau scheduler
-def configure_optimizers(self):
-    optimizer1 = Adam(...)
-    optimizer2 = SGD(...)
-    scheduler1 = ReduceLROnPlateau(optimizer1, ...)
-    scheduler2 = LambdaLR(optimizer2, ...)
-    return (
-        {
-            "optimizer": optimizer1,
-            "lr_scheduler": {
-                "scheduler": scheduler1,
-                "monitor": "metric_to_track",
-            },
-        },
-        {"optimizer": optimizer2, "lr_scheduler": scheduler2},
-    )
-
-
-

Metrics can be made available to monitor by simply logging it using -self.log('metric_to_track', metric_val) in your LightningModule.

-
-

Note

-

The frequency value specified in a dict along with the optimizer key is an int corresponding -to the number of sequential batches optimized with the specific optimizer. -It should be given to none or to all of the optimizers. -There is a difference between passing multiple optimizers in a list, -and passing multiple optimizers in dictionaries with a frequency of 1:

-
-
    -
  • In the former case, all optimizers will operate on the given batch in each optimization step.

  • -
  • In the latter, only one optimizer will operate on the given batch at every step.

  • -
-
-

This is different from the frequency value specified in the lr_scheduler_config mentioned above.

-
def configure_optimizers(self):
-    optimizer_one = torch.optim.SGD(self.model.parameters(), lr=0.01)
-    optimizer_two = torch.optim.SGD(self.model.parameters(), lr=0.01)
-    return [
-        {"optimizer": optimizer_one, "frequency": 5},
-        {"optimizer": optimizer_two, "frequency": 10},
-    ]
-
-
-

In this example, the first optimizer will be used for the first 5 steps, -the second optimizer for the next 10 steps and that cycle will continue. -If an LR scheduler is specified for an optimizer using the lr_scheduler key in the above dict, -the scheduler will only be updated when its optimizer is being used.

-
-

Examples:

-
# most cases. no learning rate scheduler
-def configure_optimizers(self):
-    return Adam(self.parameters(), lr=1e-3)
-
-# multiple optimizer case (e.g.: GAN)
-def configure_optimizers(self):
-    gen_opt = Adam(self.model_gen.parameters(), lr=0.01)
-    dis_opt = Adam(self.model_dis.parameters(), lr=0.02)
-    return gen_opt, dis_opt
-
-# example with learning rate schedulers
-def configure_optimizers(self):
-    gen_opt = Adam(self.model_gen.parameters(), lr=0.01)
-    dis_opt = Adam(self.model_dis.parameters(), lr=0.02)
-    dis_sch = CosineAnnealing(dis_opt, T_max=10)
-    return [gen_opt, dis_opt], [dis_sch]
-
-# example with step-based learning rate schedulers
-# each optimizer has its own scheduler
-def configure_optimizers(self):
-    gen_opt = Adam(self.model_gen.parameters(), lr=0.01)
-    dis_opt = Adam(self.model_dis.parameters(), lr=0.02)
-    gen_sch = {
-        'scheduler': ExponentialLR(gen_opt, 0.99),
-        'interval': 'step'  # called after each training step
-    }
-    dis_sch = CosineAnnealing(dis_opt, T_max=10) # called every epoch
-    return [gen_opt, dis_opt], [gen_sch, dis_sch]
-
-# example with optimizer frequencies
-# see training procedure in `Improved Training of Wasserstein GANs`, Algorithm 1
-# https://arxiv.org/abs/1704.00028
-def configure_optimizers(self):
-    gen_opt = Adam(self.model_gen.parameters(), lr=0.01)
-    dis_opt = Adam(self.model_dis.parameters(), lr=0.02)
-    n_critic = 5
-    return (
-        {'optimizer': dis_opt, 'frequency': n_critic},
-        {'optimizer': gen_opt, 'frequency': 1}
-    )
-
-
-
-

Note

-

Some things to know:

-
    -
  • Lightning calls .backward() and .step() on each optimizer and learning rate scheduler as needed.

  • -
  • If you use 16-bit precision (precision=16), Lightning will automatically handle the optimizers.

  • -
  • If you use multiple optimizers, training_step() will have an additional optimizer_idx parameter.

  • -
  • If you use torch.optim.LBFGS, Lightning handles the closure function automatically for you.

  • -
  • If you use multiple optimizers, gradients will be calculated only for the parameters of current optimizer -at each training step.

  • -
  • If you need to control how often those optimizers step or override the default .step() schedule, -override the optimizer_step() hook.

  • -
-
-
- -
-
-

forward

-
-
-LightningModule.forward(*args, **kwargs)[source]
-

Same as torch.nn.Module.forward().

-
-
Parameters
-
    -
  • *args – Whatever you decide to pass into the forward method.

  • -
  • **kwargs – Keyword arguments are also possible.

  • -
-
-
Return type
-

Any

-
-
Returns
-

Your model’s output

-
-
-
- -
-
-

freeze

-
-
-LightningModule.freeze()[source]
-

Freeze all params for inference.

-

Example:

-
model = MyLightningModule(...)
-model.freeze()
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

log

-
-
-LightningModule.log(name, value, prog_bar=False, logger=True, on_step=None, on_epoch=None, reduce_fx='mean', enable_graph=False, sync_dist=False, sync_dist_group=None, add_dataloader_idx=True, batch_size=None, metric_attribute=None, rank_zero_only=False)[source]
-

Log a key, value pair.

-

Example:

-
self.log('train_loss', loss)
-
-
-

The default behavior per hook is documented here: Automatic Logging.

-
-
Parameters
-
    -
  • name (str) – key to log.

  • -
  • value (Union[Metric, Tensor, int, float, Mapping[str, Union[Metric, Tensor, int, float]]]) – value to log. Can be a float, Tensor, Metric, or a dictionary of the former.

  • -
  • prog_bar (bool) – if True logs to the progress bar.

  • -
  • logger (bool) – if True logs to the logger.

  • -
  • on_step (Optional[bool]) – if True logs at this step. The default value is determined by the hook. -See Automatic Logging for details.

  • -
  • on_epoch (Optional[bool]) – if True logs epoch accumulated metrics. The default value is determined by the hook. -See Automatic Logging for details.

  • -
  • reduce_fx (Union[str, Callable]) – reduction function over step values for end of epoch. torch.mean() by default.

  • -
  • enable_graph (bool) – if True, will not auto detach the graph.

  • -
  • sync_dist (bool) – if True, reduces the metric across devices. Use with care as this may lead to a significant -communication overhead.

  • -
  • sync_dist_group (Optional[Any]) – the DDP group to sync across.

  • -
  • add_dataloader_idx (bool) – if True, appends the index of the current dataloader to -the name (when using multiple dataloaders). If False, user needs to give unique names for -each dataloader to not mix the values.

  • -
  • batch_size (Optional[int]) – Current batch_size. This will be directly inferred from the loaded batch, -but for some data structures you might need to explicitly provide it.

  • -
  • metric_attribute (Optional[str]) – To restore the metric state, Lightning requires the reference of the -torchmetrics.Metric in your model. This is found automatically if it is a model attribute.

  • -
  • rank_zero_only (bool) – Whether the value will be logged only on rank 0. This will prevent synchronization which -would produce a deadlock as not all processes would perform this log call.

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

log_dict

-
-
-LightningModule.log_dict(dictionary, prog_bar=False, logger=True, on_step=None, on_epoch=None, reduce_fx='mean', enable_graph=False, sync_dist=False, sync_dist_group=None, add_dataloader_idx=True, batch_size=None, rank_zero_only=False)[source]
-

Log a dictionary of values at once.

-

Example:

-
values = {'loss': loss, 'acc': acc, ..., 'metric_n': metric_n}
-self.log_dict(values)
-
-
-
-
Parameters
-
    -
  • dictionary (Mapping[str, Union[Metric, Tensor, int, float, Mapping[str, Union[Metric, Tensor, int, float]]]]) – key value pairs. -The values can be a float, Tensor, Metric, or a dictionary of the former.

  • -
  • prog_bar (bool) – if True logs to the progress base.

  • -
  • logger (bool) – if True logs to the logger.

  • -
  • on_step (Optional[bool]) – if True logs at this step. -None auto-logs for training_step but not validation/test_step. -The default value is determined by the hook. -See Automatic Logging for details.

  • -
  • on_epoch (Optional[bool]) – if True logs epoch accumulated metrics. -None auto-logs for val/test step but not training_step. -The default value is determined by the hook. -See Automatic Logging for details.

  • -
  • reduce_fx (Union[str, Callable]) – reduction function over step values for end of epoch. torch.mean() by default.

  • -
  • enable_graph (bool) – if True, will not auto-detach the graph

  • -
  • sync_dist (bool) – if True, reduces the metric across GPUs/TPUs. Use with care as this may lead to a significant -communication overhead.

  • -
  • sync_dist_group (Optional[Any]) – the ddp group to sync across.

  • -
  • add_dataloader_idx (bool) – if True, appends the index of the current dataloader to -the name (when using multiple). If False, user needs to give unique names for -each dataloader to not mix values.

  • -
  • batch_size (Optional[int]) – Current batch size. This will be directly inferred from the loaded batch, -but some data structures might need to explicitly provide it.

  • -
  • rank_zero_only (bool) – Whether the value will be logged only on rank 0. This will prevent synchronization which -would produce a deadlock as not all processes would perform this log call.

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

lr_schedulers

-
-
-LightningModule.lr_schedulers()[source]
-

Returns the learning rate scheduler(s) that are being used during training. Useful for manual -optimization.

-
-
Return type
-

Union[_LRScheduler, ReduceLROnPlateau, List[Union[_LRScheduler, ReduceLROnPlateau]], None]

-
-
Returns
-

A single scheduler, or a list of schedulers in case multiple ones are present, or None if no -schedulers were returned in configure_optimizers().

-
-
-
- -
-
-

manual_backward

-
-
-LightningModule.manual_backward(loss, *args, **kwargs)[source]
-

Call this directly from your training_step() when doing optimizations manually. By using this, -Lightning can ensure that all the proper scaling gets applied when using mixed precision.

-

See manual optimization for more examples.

-

Example:

-
def training_step(...):
-    opt = self.optimizers()
-    loss = ...
-    opt.zero_grad()
-    # automatically applies scaling, etc...
-    self.manual_backward(loss)
-    opt.step()
-
-
-
-
Parameters
-
    -
  • loss (Tensor) – The tensor on which to compute gradients. Must have a graph attached.

  • -
  • *args – Additional positional arguments to be forwarded to backward()

  • -
  • **kwargs – Additional keyword arguments to be forwarded to backward()

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

optimizers

-
-
-LightningModule.optimizers(use_pl_optimizer: Literal[True] = True) Union[pytorch_lightning.core.optimizer.LightningOptimizer, List[pytorch_lightning.core.optimizer.LightningOptimizer]][source]
-
-LightningModule.optimizers(use_pl_optimizer: Literal[False]) Union[torch.optim.optimizer.Optimizer, List[torch.optim.optimizer.Optimizer]]
-
-LightningModule.optimizers(use_pl_optimizer: bool) Union[torch.optim.optimizer.Optimizer, pytorch_lightning.core.optimizer.LightningOptimizer, List[torch.optim.optimizer.Optimizer], List[pytorch_lightning.core.optimizer.LightningOptimizer]]
-

Returns the optimizer(s) that are being used during training. Useful for manual optimization.

-
-
Parameters
-

use_pl_optimizer (bool) – If True, will wrap the optimizer(s) in a -LightningOptimizer for automatic handling of precision and -profiling.

-
-
Return type
-

Union[Optimizer, LightningOptimizer, List[Optimizer], List[LightningOptimizer]]

-
-
Returns
-

A single optimizer, or a list of optimizers in case multiple ones are present.

-
-
-
- -
-
-

print

-
-
-LightningModule.print(*args, **kwargs)[source]
-

Prints only from process 0. Use this in any distributed mode to log only once.

-
-
Parameters
-
    -
  • *args – The thing to print. The same as for Python’s built-in print function.

  • -
  • **kwargs – The same as for Python’s built-in print function.

  • -
-
-
-

Example:

-
def forward(self, x):
-    self.print(x, 'in forward')
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

predict_step

-
-
-LightningModule.predict_step(batch, batch_idx, dataloader_idx=0)[source]
-

Step function called during predict(). By default, it -calls forward(). Override to add any processing -logic.

-

The predict_step() is used -to scale inference on multi-devices.

-

To prevent an OOM error, it is possible to use BasePredictionWriter -callback to write the predictions to disk or database after each batch or on epoch end.

-

The BasePredictionWriter should be used while using a spawn -based accelerator. This happens for Trainer(strategy="ddp_spawn") -or training on 8 TPU cores with Trainer(accelerator="tpu", devices=8) as predictions won’t be returned.

-

Example

-
class MyModel(LightningModule):
-
-    def predicts_step(self, batch, batch_idx, dataloader_idx=0):
-        return self(batch)
-
-dm = ...
-model = MyModel()
-trainer = Trainer(accelerator="gpu", devices=2)
-predictions = trainer.predict(model, dm)
-
-
-
-
Parameters
-
    -
  • batch (Any) – Current batch.

  • -
  • batch_idx (int) – Index of current batch.

  • -
  • dataloader_idx (int) – Index of the current dataloader.

  • -
-
-
Return type
-

Any

-
-
Returns
-

Predicted output

-
-
-
- -
-
-

save_hyperparameters

-
-
-LightningModule.save_hyperparameters(*args, ignore=None, frame=None, logger=True)
-

Save arguments to hparams attribute.

-
-
Parameters
-
    -
  • args (Any) – single object of dict, NameSpace or OmegaConf -or string names or arguments from class __init__

  • -
  • ignore (Union[Sequence[str], str, None]) – an argument name or a list of argument names from -class __init__ to be ignored

  • -
  • frame (Optional[frame]) – a frame object. Default is None

  • -
  • logger (bool) – Whether to send the hyperparameters to the logger. Default: True

  • -
-
-
-
-
Example::
>>> class ManuallyArgsModel(HyperparametersMixin):
-...     def __init__(self, arg1, arg2, arg3):
-...         super().__init__()
-...         # manually assign arguments
-...         self.save_hyperparameters('arg1', 'arg3')
-...     def forward(self, *args, **kwargs):
-...         ...
->>> model = ManuallyArgsModel(1, 'abc', 3.14)
->>> model.hparams
-"arg1": 1
-"arg3": 3.14
-
-
-
>>> class AutomaticArgsModel(HyperparametersMixin):
-...     def __init__(self, arg1, arg2, arg3):
-...         super().__init__()
-...         # equivalent automatic
-...         self.save_hyperparameters()
-...     def forward(self, *args, **kwargs):
-...         ...
->>> model = AutomaticArgsModel(1, 'abc', 3.14)
->>> model.hparams
-"arg1": 1
-"arg2": abc
-"arg3": 3.14
-
-
-
>>> class SingleArgModel(HyperparametersMixin):
-...     def __init__(self, params):
-...         super().__init__()
-...         # manually assign single argument
-...         self.save_hyperparameters(params)
-...     def forward(self, *args, **kwargs):
-...         ...
->>> model = SingleArgModel(Namespace(p1=1, p2='abc', p3=3.14))
->>> model.hparams
-"p1": 1
-"p2": abc
-"p3": 3.14
-
-
-
>>> class ManuallyArgsModel(HyperparametersMixin):
-...     def __init__(self, arg1, arg2, arg3):
-...         super().__init__()
-...         # pass argument(s) to ignore as a string or in a list
-...         self.save_hyperparameters(ignore='arg2')
-...     def forward(self, *args, **kwargs):
-...         ...
->>> model = ManuallyArgsModel(1, 'abc', 3.14)
->>> model.hparams
-"arg1": 1
-"arg3": 3.14
-
-
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

toggle_optimizer

-
-
-LightningModule.toggle_optimizer(optimizer, optimizer_idx)[source]
-

Makes sure only the gradients of the current optimizer’s parameters are calculated in the training step -to prevent dangling gradients in multiple-optimizer setup.

-

This is only called automatically when automatic optimization is enabled and multiple optimizers are used. -It works with untoggle_optimizer() to make sure param_requires_grad_state is properly reset.

-
-
Parameters
-
    -
  • optimizer (Union[Optimizer, LightningOptimizer]) – The optimizer to toggle.

  • -
  • optimizer_idx (int) – The index of the optimizer to toggle.

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

test_step

-
-
-LightningModule.test_step(*args, **kwargs)[source]
-

Operates on a single batch of data from the test set. -In this step you’d normally generate examples or calculate anything of interest -such as accuracy.

-
# the pseudocode for these calls
-test_outs = []
-for test_batch in test_data:
-    out = test_step(test_batch)
-    test_outs.append(out)
-test_epoch_end(test_outs)
-
-
-
-
Parameters
-
    -
  • batch – The output of your DataLoader.

  • -
  • batch_idx – The index of this batch.

  • -
  • dataloader_id – The index of the dataloader that produced this batch. -(only if multiple test dataloaders used).

  • -
-
-
Return type
-

Union[Tensor, Dict[str, Any], None]

-
-
Returns
-

Any of.

-
-
    -
  • Any object or value

  • -
  • None - Testing will skip to the next batch

  • -
-
-

-
-
-
# if you have one test dataloader:
-def test_step(self, batch, batch_idx):
-    ...
-
-
-# if you have multiple test dataloaders:
-def test_step(self, batch, batch_idx, dataloader_idx=0):
-    ...
-
-
-

Examples:

-
# CASE 1: A single test dataset
-def test_step(self, batch, batch_idx):
-    x, y = batch
-
-    # implement your own
-    out = self(x)
-    loss = self.loss(out, y)
-
-    # log 6 example images
-    # or generated text... or whatever
-    sample_imgs = x[:6]
-    grid = torchvision.utils.make_grid(sample_imgs)
-    self.logger.experiment.add_image('example_images', grid, 0)
-
-    # calculate acc
-    labels_hat = torch.argmax(out, dim=1)
-    test_acc = torch.sum(y == labels_hat).item() / (len(y) * 1.0)
-
-    # log the outputs!
-    self.log_dict({'test_loss': loss, 'test_acc': test_acc})
-
-
-

If you pass in multiple test dataloaders, test_step() will have an additional argument. We recommend -setting the default value of 0 so that you can quickly switch between single and multiple dataloaders.

-
# CASE 2: multiple test dataloaders
-def test_step(self, batch, batch_idx, dataloader_idx=0):
-    # dataloader_idx tells you which dataset this is.
-    ...
-
-
-
-

Note

-

If you don’t need to test you don’t need to implement this method.

-
-
-

Note

-

When the test_step() is called, the model has been put in eval mode and -PyTorch gradients have been disabled. At the end of the test epoch, the model goes back -to training mode and gradients are enabled.

-
-
- -
-
-

test_step_end

-
-
-LightningModule.test_step_end(*args, **kwargs)[source]
-

Use this when testing with dp or ddp2 because test_step() will operate on only part of the batch. -However, this is still optional and only needed for things like softmax or NCE loss.

-
-

Note

-

If you later switch to ddp or some other mode, this will still be called -so that you don’t have to change your code.

-
-
# pseudocode
-sub_batches = split_batches_for_dp(batch)
-step_output = [test_step(sub_batch) for sub_batch in sub_batches]
-test_step_end(step_output)
-
-
-
-
Parameters
-

step_output – What you return in test_step() for each batch part.

-
-
Return type
-

Union[Tensor, Dict[str, Any], None]

-
-
Returns
-

None or anything

-
-
-
# WITHOUT test_step_end
-# if used in DP or DDP2, this batch is 1/num_gpus large
-def test_step(self, batch, batch_idx):
-    # batch is 1/num_gpus big
-    x, y = batch
-
-    out = self(x)
-    loss = self.softmax(out)
-    self.log("test_loss", loss)
-
-
-# --------------
-# with test_step_end to do softmax over the full batch
-def test_step(self, batch, batch_idx):
-    # batch is 1/num_gpus big
-    x, y = batch
-
-    out = self.encoder(x)
-    return out
-
-
-def test_step_end(self, output_results):
-    # this out is now the full size of the batch
-    all_test_step_outs = output_results.out
-    loss = nce_loss(all_test_step_outs)
-    self.log("test_loss", loss)
-
-
-
-

See also

-

See the accelerators/gpu:Multi GPU Training guide for more details.

-
-
- -
-
-

test_epoch_end

-
-
-LightningModule.test_epoch_end(outputs)[source]
-

Called at the end of a test epoch with the output of all test steps.

-
# the pseudocode for these calls
-test_outs = []
-for test_batch in test_data:
-    out = test_step(test_batch)
-    test_outs.append(out)
-test_epoch_end(test_outs)
-
-
-
-
Parameters
-

outputs (Union[List[Union[Tensor, Dict[str, Any]]], List[List[Union[Tensor, Dict[str, Any]]]]]) – List of outputs you defined in test_step_end(), or if there -are multiple dataloaders, a list containing a list of outputs for each dataloader

-
-
Return type
-

None

-
-
Returns
-

None

-
-
-
-

Note

-

If you didn’t define a test_step(), this won’t be called.

-
-

Examples

-

With a single dataloader:

-
def test_epoch_end(self, outputs):
-    # do something with the outputs of all test batches
-    all_test_preds = test_step_outputs.predictions
-
-    some_result = calc_all_results(all_test_preds)
-    self.log(some_result)
-
-
-

With multiple dataloaders, outputs will be a list of lists. The outer list contains -one entry per dataloader, while the inner list contains the individual outputs of -each test step for that dataloader.

-
def test_epoch_end(self, outputs):
-    final_value = 0
-    for dataloader_outputs in outputs:
-        for test_step_out in dataloader_outputs:
-            # do something
-            final_value += test_step_out
-
-    self.log("final_metric", final_value)
-
-
-
- -
-
-

to_onnx

-
-
-LightningModule.to_onnx(file_path, input_sample=None, **kwargs)[source]
-

Saves the model in ONNX format.

-
-
Parameters
-
    -
  • file_path (Union[str, Path]) – The path of the file the onnx model should be saved to.

  • -
  • input_sample (Optional[Any]) – An input for tracing. Default: None (Use self.example_input_array)

  • -
  • **kwargs – Will be passed to torch.onnx.export function.

  • -
-
-
-

Example

-
>>> class SimpleModel(LightningModule):
-...     def __init__(self):
-...         super().__init__()
-...         self.l1 = torch.nn.Linear(in_features=64, out_features=4)
-...
-...     def forward(self, x):
-...         return torch.relu(self.l1(x.view(x.size(0), -1)))
-
-
-
>>> with tempfile.NamedTemporaryFile(suffix='.onnx', delete=False) as tmpfile:
-...     model = SimpleModel()
-...     input_sample = torch.randn((1, 64))
-...     model.to_onnx(tmpfile.name, input_sample, export_params=True)
-...     os.path.isfile(tmpfile.name)
-True
-
-
-
- -
-
-

to_torchscript

-
-
-LightningModule.to_torchscript(file_path=None, method='script', example_inputs=None, **kwargs)[source]
-

By default compiles the whole model to a ScriptModule. If you want to use tracing, -please provided the argument method='trace' and make sure that either the example_inputs argument is -provided, or the model has example_input_array set. If you would like to customize the modules that -are scripted you should override this method. In case you want to return multiple modules, we recommend -using a dictionary.

-
-
Parameters
-
    -
  • file_path (Union[str, Path, None]) – Path where to save the torchscript. Default: None (no file saved).

  • -
  • method (Optional[str]) – Whether to use TorchScript’s script or trace method. Default: ‘script’

  • -
  • example_inputs (Optional[Any]) – An input to be used to do tracing when method is set to ‘trace’. -Default: None (uses example_input_array)

  • -
  • **kwargs – Additional arguments that will be passed to the torch.jit.script() or -torch.jit.trace() function.

  • -
-
-
-
-

Note

-
    -
  • Requires the implementation of the -forward() method.

  • -
  • The exported script will be set to evaluation mode.

  • -
  • It is recommended that you install the latest supported version of PyTorch -to use this feature without limitations. See also the torch.jit -documentation for supported features.

  • -
-
-

Example

-
>>> class SimpleModel(LightningModule):
-...     def __init__(self):
-...         super().__init__()
-...         self.l1 = torch.nn.Linear(in_features=64, out_features=4)
-...
-...     def forward(self, x):
-...         return torch.relu(self.l1(x.view(x.size(0), -1)))
-...
->>> model = SimpleModel()
->>> model.to_torchscript(file_path="model.pt")  
->>> os.path.isfile("model.pt")  
->>> torch.jit.save(model.to_torchscript(file_path="model_trace.pt", method='trace', 
-...                                     example_inputs=torch.randn(1, 64)))  
->>> os.path.isfile("model_trace.pt")  
-True
-
-
-
-
Return type
-

Union[ScriptModule, Dict[str, ScriptModule]]

-
-
Returns
-

This LightningModule as a torchscript, regardless of whether file_path is -defined or not.

-
-
-
- -
-
-

training_step

-
-
-LightningModule.training_step(*args, **kwargs)[source]
-

Here you compute and return the training loss and some additional metrics for e.g. -the progress bar or logger.

-
-
Parameters
-
    -
  • batch (Tensor | (Tensor, …) | [Tensor, …]) – The output of your DataLoader. A tensor, tuple or list.

  • -
  • batch_idx (int) – Integer displaying index of this batch

  • -
  • optimizer_idx (int) – When using multiple optimizers, this argument will also be present.

  • -
  • hiddens (Any) – Passed in if -truncated_bptt_steps > 0.

  • -
-
-
Return type
-

Union[Tensor, Dict[str, Any]]

-
-
Returns
-

Any of.

-
    -
  • Tensor - The loss tensor

  • -
  • dict - A dictionary. Can include any keys, but must include the key 'loss'

  • -
  • -
    None - Training will skip to the next batch. This is only for automatic optimization.

    This is not supported for multi-GPU, TPU, IPU, or DeepSpeed.

    -
    -
    -
  • -
-

-
-
-

In this step you’d normally do the forward pass and calculate the loss for a batch. -You can also do fancier things like multiple forward passes or something model specific.

-

Example:

-
def training_step(self, batch, batch_idx):
-    x, y, z = batch
-    out = self.encoder(x)
-    loss = self.loss(out, x)
-    return loss
-
-
-

If you define multiple optimizers, this step will be called with an additional -optimizer_idx parameter.

-
# Multiple optimizers (e.g.: GANs)
-def training_step(self, batch, batch_idx, optimizer_idx):
-    if optimizer_idx == 0:
-        # do training_step with encoder
-        ...
-    if optimizer_idx == 1:
-        # do training_step with decoder
-        ...
-
-
-

If you add truncated back propagation through time you will also get an additional -argument with the hidden states of the previous step.

-
# Truncated back-propagation through time
-def training_step(self, batch, batch_idx, hiddens):
-    # hiddens are the hidden states from the previous truncated backprop step
-    out, hiddens = self.lstm(data, hiddens)
-    loss = ...
-    return {"loss": loss, "hiddens": hiddens}
-
-
-
-

Note

-

The loss value shown in the progress bar is smoothed (averaged) over the last values, -so it differs from the actual loss returned in train/validation step.

-
-
- -
-
-

training_step_end

-
-
-LightningModule.training_step_end(step_output)[source]
-

Use this when training with dp or ddp2 because training_step() will operate on only part of the -batch. However, this is still optional and only needed for things like softmax or NCE loss.

-
-

Note

-

If you later switch to ddp or some other mode, this will still be called -so that you don’t have to change your code

-
-
# pseudocode
-sub_batches = split_batches_for_dp(batch)
-step_output = [training_step(sub_batch) for sub_batch in sub_batches]
-training_step_end(step_output)
-
-
-
-
Parameters
-

step_output (Union[Tensor, Dict[str, Any]]) – What you return in training_step for each batch part.

-
-
Return type
-

Union[Tensor, Dict[str, Any]]

-
-
Returns
-

Anything

-
-
-

When using dp/ddp2 distributed backends, only a portion of the batch is inside the training_step:

-
def training_step(self, batch, batch_idx):
-    # batch is 1/num_gpus big
-    x, y = batch
-
-    out = self(x)
-
-    # softmax uses only a portion of the batch in the denominator
-    loss = self.softmax(out)
-    loss = nce_loss(loss)
-    return loss
-
-
-

If you wish to do something with all the parts of the batch, then use this method to do it:

-
def training_step(self, batch, batch_idx):
-    # batch is 1/num_gpus big
-    x, y = batch
-
-    out = self.encoder(x)
-    return {"pred": out}
-
-
-def training_step_end(self, training_step_outputs):
-    gpu_0_pred = training_step_outputs[0]["pred"]
-    gpu_1_pred = training_step_outputs[1]["pred"]
-    gpu_n_pred = training_step_outputs[n]["pred"]
-
-    # this softmax now uses the full batch
-    loss = nce_loss([gpu_0_pred, gpu_1_pred, gpu_n_pred])
-    return loss
-
-
-
-

See also

-

See the accelerators/gpu:Multi GPU Training guide for more details.

-
-
- -
-
-

training_epoch_end

-
-
-LightningModule.training_epoch_end(outputs)[source]
-

Called at the end of the training epoch with the outputs of all training steps. Use this in case you -need to do something with all the outputs returned by training_step().

-
# the pseudocode for these calls
-train_outs = []
-for train_batch in train_data:
-    out = training_step(train_batch)
-    train_outs.append(out)
-training_epoch_end(train_outs)
-
-
-
-
Parameters
-

outputs (List[Union[Tensor, Dict[str, Any]]]) – List of outputs you defined in training_step(). If there are multiple optimizers or when -using truncated_bptt_steps > 0, the lists have the dimensions -(n_batches, tbptt_steps, n_optimizers). Dimensions of length 1 are squeezed.

-
-
Return type
-

None

-
-
Returns
-

None

-
-
-
-

Note

-

If this method is not overridden, this won’t be called.

-
-
def training_epoch_end(self, training_step_outputs):
-    # do something with all training_step outputs
-    for out in training_step_outputs:
-        ...
-
-
-
- -
-
-

unfreeze

-
-
-LightningModule.unfreeze()[source]
-

Unfreeze all parameters for training.

-
model = MyLightningModule(...)
-model.unfreeze()
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

untoggle_optimizer

-
-
-LightningModule.untoggle_optimizer(optimizer_idx)[source]
-

Resets the state of required gradients that were toggled with toggle_optimizer().

-

This is only called automatically when automatic optimization is enabled and multiple optimizers are used.

-
-
Parameters
-

optimizer_idx (int) – The index of the optimizer to untoggle.

-
-
Return type
-

None

-
-
-
- -
-
-

validation_step

-
-
-LightningModule.validation_step(*args, **kwargs)[source]
-

Operates on a single batch of data from the validation set. -In this step you’d might generate examples or calculate anything of interest like accuracy.

-
# the pseudocode for these calls
-val_outs = []
-for val_batch in val_data:
-    out = validation_step(val_batch)
-    val_outs.append(out)
-validation_epoch_end(val_outs)
-
-
-
-
Parameters
-
    -
  • batch – The output of your DataLoader.

  • -
  • batch_idx – The index of this batch.

  • -
  • dataloader_idx – The index of the dataloader that produced this batch. -(only if multiple val dataloaders used)

  • -
-
-
Return type
-

Union[Tensor, Dict[str, Any], None]

-
-
Returns
-

    -
  • Any object or value

  • -
  • None - Validation will skip to the next batch

  • -
-

-
-
-
# pseudocode of order
-val_outs = []
-for val_batch in val_data:
-    out = validation_step(val_batch)
-    if defined("validation_step_end"):
-        out = validation_step_end(out)
-    val_outs.append(out)
-val_outs = validation_epoch_end(val_outs)
-
-
-
# if you have one val dataloader:
-def validation_step(self, batch, batch_idx):
-    ...
-
-
-# if you have multiple val dataloaders:
-def validation_step(self, batch, batch_idx, dataloader_idx=0):
-    ...
-
-
-

Examples:

-
# CASE 1: A single validation dataset
-def validation_step(self, batch, batch_idx):
-    x, y = batch
-
-    # implement your own
-    out = self(x)
-    loss = self.loss(out, y)
-
-    # log 6 example images
-    # or generated text... or whatever
-    sample_imgs = x[:6]
-    grid = torchvision.utils.make_grid(sample_imgs)
-    self.logger.experiment.add_image('example_images', grid, 0)
-
-    # calculate acc
-    labels_hat = torch.argmax(out, dim=1)
-    val_acc = torch.sum(y == labels_hat).item() / (len(y) * 1.0)
-
-    # log the outputs!
-    self.log_dict({'val_loss': loss, 'val_acc': val_acc})
-
-
-

If you pass in multiple val dataloaders, validation_step() will have an additional argument. We recommend -setting the default value of 0 so that you can quickly switch between single and multiple dataloaders.

-
# CASE 2: multiple validation dataloaders
-def validation_step(self, batch, batch_idx, dataloader_idx=0):
-    # dataloader_idx tells you which dataset this is.
-    ...
-
-
-
-

Note

-

If you don’t need to validate you don’t need to implement this method.

-
-
-

Note

-

When the validation_step() is called, the model has been put in eval mode -and PyTorch gradients have been disabled. At the end of validation, -the model goes back to training mode and gradients are enabled.

-
-
- -
-
-

validation_step_end

-
-
-LightningModule.validation_step_end(*args, **kwargs)[source]
-

Use this when validating with dp or ddp2 because validation_step() will operate on only part of -the batch. However, this is still optional and only needed for things like softmax or NCE loss.

-
-

Note

-

If you later switch to ddp or some other mode, this will still be called -so that you don’t have to change your code.

-
-
# pseudocode
-sub_batches = split_batches_for_dp(batch)
-step_output = [validation_step(sub_batch) for sub_batch in sub_batches]
-validation_step_end(step_output)
-
-
-
-
Parameters
-

step_output – What you return in validation_step() for each batch part.

-
-
Return type
-

Union[Tensor, Dict[str, Any], None]

-
-
Returns
-

None or anything

-
-
-
# WITHOUT validation_step_end
-# if used in DP or DDP2, this batch is 1/num_gpus large
-def validation_step(self, batch, batch_idx):
-    # batch is 1/num_gpus big
-    x, y = batch
-
-    out = self.encoder(x)
-    loss = self.softmax(out)
-    loss = nce_loss(loss)
-    self.log("val_loss", loss)
-
-
-# --------------
-# with validation_step_end to do softmax over the full batch
-def validation_step(self, batch, batch_idx):
-    # batch is 1/num_gpus big
-    x, y = batch
-
-    out = self(x)
-    return out
-
-
-def validation_step_end(self, val_step_outputs):
-    for out in val_step_outputs:
-        ...
-
-
-
-

See also

-

See the accelerators/gpu:Multi GPU Training guide for more details.

-
-
- -
-
-

validation_epoch_end

-
-
-LightningModule.validation_epoch_end(outputs)[source]
-

Called at the end of the validation epoch with the outputs of all validation steps.

-
# the pseudocode for these calls
-val_outs = []
-for val_batch in val_data:
-    out = validation_step(val_batch)
-    val_outs.append(out)
-validation_epoch_end(val_outs)
-
-
-
-
Parameters
-

outputs (Union[List[Union[Tensor, Dict[str, Any]]], List[List[Union[Tensor, Dict[str, Any]]]]]) – List of outputs you defined in validation_step(), or if there -are multiple dataloaders, a list containing a list of outputs for each dataloader.

-
-
Return type
-

None

-
-
Returns
-

None

-
-
-
-

Note

-

If you didn’t define a validation_step(), this won’t be called.

-
-

Examples

-

With a single dataloader:

-
def validation_epoch_end(self, val_step_outputs):
-    for out in val_step_outputs:
-        ...
-
-
-

With multiple dataloaders, outputs will be a list of lists. The outer list contains -one entry per dataloader, while the inner list contains the individual outputs of -each validation step for that dataloader.

-
def validation_epoch_end(self, outputs):
-    for dataloader_output_result in outputs:
-        dataloader_outs = dataloader_output_result.dataloader_i_outputs
-
-    self.log("final_metric", final_value)
-
-
-
- -
-
-
-
-

Properties

-

These are properties available in a LightningModule.

-
-

current_epoch

-

The number of epochs run.

-
def training_step(self, batch, batch_idx):
-    if self.current_epoch == 0:
-        ...
-
-
-
-
-

device

-

The device the module is on. Use it to keep your code device agnostic.

-
def training_step(self, batch, batch_idx):
-    z = torch.rand(2, 3, device=self.device)
-
-
-
-
-

global_rank

-

The global_rank is the index of the current process across all nodes and devices. -Lightning will perform some operations such as logging, weight checkpointing only when global_rank=0. You -usually do not need to use this property, but it is useful to know how to access it if needed.

-
def training_step(self, batch, batch_idx):
-    if self.global_rank == 0:
-        # do something only once across all the nodes
-        ...
-
-
-
-
-

global_step

-

The number of optimizer steps taken (does not reset each epoch). -This includes multiple optimizers and TBPTT steps (if enabled).

-
def training_step(self, batch, batch_idx):
-    self.logger.experiment.log_image(..., step=self.global_step)
-
-
-
-
-

hparams

-

The arguments passed through LightningModule.__init__() and saved by calling -save_hyperparameters() could be accessed by the hparams attribute.

-
def __init__(self, learning_rate):
-    self.save_hyperparameters()
-
-
-def configure_optimizers(self):
-    return Adam(self.parameters(), lr=self.hparams.learning_rate)
-
-
-
-
-

logger

-

The current logger being used (tensorboard or other supported logger)

-
def training_step(self, batch, batch_idx):
-    # the generic logger (same no matter if tensorboard or other supported logger)
-    self.logger
-
-    # the particular logger
-    tensorboard_logger = self.logger.experiment
-
-
-
-
-

loggers

-

The list of loggers currently being used by the Trainer.

-
def training_step(self, batch, batch_idx):
-    # List of Logger objects
-    loggers = self.loggers
-    for logger in loggers:
-        logger.log_metrics({"foo": 1.0})
-
-
-
-
-

local_rank

-

The local_rank is the index of the current process across all the devices for the current node. -You usually do not need to use this property, but it is useful to know how to access it if needed. -For example, if using 10 machines (or nodes), the GPU at index 0 on each machine has local_rank = 0.

-
def training_step(self, batch, batch_idx):
-    if self.local_rank == 0:
-        # do something only once across each node
-        ...
-
-
-
-
-

precision

-

The type of precision used:

-
def training_step(self, batch, batch_idx):
-    if self.precision == 16:
-        ...
-
-
-
-
-

trainer

-

Pointer to the trainer

-
def training_step(self, batch, batch_idx):
-    max_steps = self.trainer.max_steps
-    any_flag = self.trainer.any_flag
-
-
-
-
-

prepare_data_per_node

-

If set to True will call prepare_data() on LOCAL_RANK=0 for every node. -If set to False will only call from NODE_RANK=0, LOCAL_RANK=0.

-
class LitModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.prepare_data_per_node = True
-
-
-
-
-

automatic_optimization

-

When set to False, Lightning does not automate the optimization process. This means you are responsible for handling -your optimizers. However, we do take care of precision and any accelerators used.

-

See manual optimization for details.

-
def __init__(self):
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    opt = self.optimizers(use_pl_optimizer=True)
-
-    loss = ...
-    opt.zero_grad()
-    self.manual_backward(loss)
-    opt.step()
-
-
-

This is recommended only if using 2+ optimizers AND if you know how to perform the optimization procedure properly. Note -that automatic optimization can still be used with multiple optimizers by relying on the optimizer_idx parameter. -Manual optimization is most useful for research topics like reinforcement learning, sparse coding, and GAN research.

-
def __init__(self):
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    # access your optimizers with use_pl_optimizer=False. Default is True
-    opt_a, opt_b = self.optimizers(use_pl_optimizer=True)
-
-    gen_loss = ...
-    opt_a.zero_grad()
-    self.manual_backward(gen_loss)
-    opt_a.step()
-
-    disc_loss = ...
-    opt_b.zero_grad()
-    self.manual_backward(disc_loss)
-    opt_b.step()
-
-
-
-
-

example_input_array

-

Set and access example_input_array, which basically represents a single batch.

-
def __init__(self):
-    self.example_input_array = ...
-    self.generator = ...
-
-
-def on_train_epoch_end(self):
-    # generate some images using the example_input_array
-    gen_images = self.generator(self.example_input_array)
-
-
-
-
-

truncated_bptt_steps

-

Truncated Backpropagation Through Time (TBPTT) performs perform backpropogation every k steps of -a much longer sequence. This is made possible by passing training batches -split along the time-dimensions into splits of size k to the -training_step. In order to keep the same forward propagation behavior, all -hidden states should be kept in-between each time-dimension split.

-

If this is enabled, your batches will automatically get truncated -and the Trainer will apply Truncated Backprop to it.

-

(Williams et al. “An efficient gradient-based algorithm for on-line training of -recurrent network trajectories.”)

-

Tutorial

-
from pytorch_lightning import LightningModule
-
-
-class MyModel(LightningModule):
-    def __init__(self, input_size, hidden_size, num_layers):
-        super().__init__()
-        # batch_first has to be set to True
-        self.lstm = nn.LSTM(
-            input_size=input_size,
-            hidden_size=hidden_size,
-            num_layers=num_layers,
-            batch_first=True,
-        )
-
-        ...
-
-        # Important: This property activates truncated backpropagation through time
-        # Setting this value to 2 splits the batch into sequences of size 2
-        self.truncated_bptt_steps = 2
-
-    # Truncated back-propagation through time
-    def training_step(self, batch, batch_idx, hiddens):
-        x, y = batch
-
-        # the training step must be updated to accept a ``hiddens`` argument
-        # hiddens are the hiddens from the previous truncated backprop step
-        out, hiddens = self.lstm(x, hiddens)
-
-        ...
-
-        return {"loss": ..., "hiddens": hiddens}
-
-
-

Lightning takes care of splitting your batch along the time-dimension. It is -assumed to be the second dimension of your batches. Therefore, in the -example above, we have set batch_first=True.

-
# we use the second as the time dimension
-# (batch, time, ...)
-sub_batch = batch[0, 0:t, ...]
-
-
-

To modify how the batch is split, -override the pytorch_lightning.core.lightning.LightningModule.tbptt_split_batch() method:

-
class LitMNIST(LightningModule):
-    def tbptt_split_batch(self, batch, split_size):
-        # do your own splitting on the batch
-        return splits
-
-
-
-
-
-
-

Hooks

-

This is the pseudocode to describe the structure of fit(). -The inputs and outputs of each function are not represented for simplicity. Please check each function’s API reference -for more information.

-
def fit(self):
-    if global_rank == 0:
-        # prepare data is called on GLOBAL_ZERO only
-        prepare_data()
-
-    configure_callbacks()
-
-    with parallel(devices):
-        # devices can be GPUs, TPUs, ...
-        train_on_device(model)
-
-
-def train_on_device(model):
-    # called PER DEVICE
-    on_fit_start()
-    setup("fit")
-    configure_optimizers()
-
-    # the sanity check runs here
-
-    on_train_start()
-    for epoch in epochs:
-        fit_loop()
-    on_train_end()
-
-    on_fit_end()
-    teardown("fit")
-
-
-def fit_loop():
-    on_train_epoch_start()
-
-    for batch in train_dataloader():
-        on_train_batch_start()
-
-        on_before_batch_transfer()
-        transfer_batch_to_device()
-        on_after_batch_transfer()
-
-        training_step()
-
-        on_before_zero_grad()
-        optimizer_zero_grad()
-
-        on_before_backward()
-        backward()
-        on_after_backward()
-
-        on_before_optimizer_step()
-        configure_gradient_clipping()
-        optimizer_step()
-
-        on_train_batch_end()
-
-        if should_check_val:
-            val_loop()
-    # end training epoch
-    training_epoch_end()
-
-    on_train_epoch_end()
-
-
-def val_loop():
-    on_validation_model_eval()  # calls `model.eval()`
-    torch.set_grad_enabled(False)
-
-    on_validation_start()
-    on_validation_epoch_start()
-
-    val_outs = []
-    for batch_idx, batch in enumerate(val_dataloader()):
-        on_validation_batch_start(batch, batch_idx)
-
-        batch = on_before_batch_transfer(batch)
-        batch = transfer_batch_to_device(batch)
-        batch = on_after_batch_transfer(batch)
-
-        out = validation_step(batch, batch_idx)
-
-        on_validation_batch_end(batch, batch_idx)
-        val_outs.append(out)
-
-    validation_epoch_end(val_outs)
-
-    on_validation_epoch_end()
-    on_validation_end()
-
-    # set up for train
-    on_validation_model_train()  # calls `model.train()`
-    torch.set_grad_enabled(True)
-
-
-
-

backward

-
-
-LightningModule.backward(loss, optimizer, optimizer_idx, *args, **kwargs)[source]
-

Called to perform backward on the loss returned in training_step(). Override this hook with your -own implementation if you need to.

-
-
Parameters
-
    -
  • loss (Tensor) – The loss tensor returned by training_step(). If gradient accumulation is used, the loss here -holds the normalized value (scaled by 1 / accumulation steps).

  • -
  • optimizer (Optional[Optimizer]) – Current optimizer being used. None if using manual optimization.

  • -
  • optimizer_idx (Optional[int]) – Index of the current optimizer being used. None if using manual optimization.

  • -
-
-
-

Example:

-
def backward(self, loss, optimizer, optimizer_idx):
-    loss.backward()
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

on_before_backward

-
-
-LightningModule.on_before_backward(loss)
-

Called before loss.backward().

-
-
Parameters
-

loss (Tensor) – Loss divided by number of batches for gradient accumulation and scaled if using native AMP.

-
-
Return type
-

None

-
-
-
- -
-
-

on_after_backward

-
-
-LightningModule.on_after_backward()
-

Called after loss.backward() and before optimizers are stepped.

-
-

Note

-

If using native AMP, the gradients will not be unscaled at this point. -Use the on_before_optimizer_step if you need the unscaled gradients.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_before_zero_grad

-
-
-LightningModule.on_before_zero_grad(optimizer)
-

Called after training_step() and before optimizer.zero_grad().

-

Called in the training loop after taking an optimizer step and before zeroing grads. -Good place to inspect weight information with weights updated.

-

This is where it is called:

-
for optimizer in optimizers:
-    out = training_step(...)
-
-    model.on_before_zero_grad(optimizer) # < ---- called here
-    optimizer.zero_grad()
-
-    backward()
-
-
-
-
Parameters
-

optimizer (Optimizer) – The optimizer for which grads should be zeroed.

-
-
Return type
-

None

-
-
-
- -
-
-

on_fit_start

-
-
-LightningModule.on_fit_start()
-

Called at the very beginning of fit.

-

If on DDP it is called on every process

-
-
Return type
-

None

-
-
-
- -
-
-

on_fit_end

-
-
-LightningModule.on_fit_end()
-

Called at the very end of fit.

-

If on DDP it is called on every process

-
-
Return type
-

None

-
-
-
- -
-
-

on_load_checkpoint

-
-
-LightningModule.on_load_checkpoint(checkpoint)
-

Called by Lightning to restore your model. -If you saved something with on_save_checkpoint() this is your chance to restore this.

-
-
Parameters
-

checkpoint (Dict[str, Any]) – Loaded checkpoint

-
-
-

Example:

-
def on_load_checkpoint(self, checkpoint):
-    # 99% of the time you don't need to implement this method
-    self.something_cool_i_want_to_save = checkpoint['something_cool_i_want_to_save']
-
-
-
-

Note

-

Lightning auto-restores global step, epoch, and train state including amp scaling. -There is no need for you to restore anything regarding training.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_save_checkpoint

-
-
-LightningModule.on_save_checkpoint(checkpoint)
-

Called by Lightning when saving a checkpoint to give you a chance to store anything -else you might want to save.

-
-
Parameters
-

checkpoint (Dict[str, Any]) – The full checkpoint dictionary before it gets dumped to a file. -Implementations of this hook can insert additional data into this dictionary.

-
-
-

Example:

-
def on_save_checkpoint(self, checkpoint):
-    # 99% of use cases you don't need to implement this method
-    checkpoint['something_cool_i_want_to_save'] = my_cool_pickable_object
-
-
-
-

Note

-

Lightning saves all aspects of training (epoch, global step, etc…) -including amp scaling. -There is no need for you to store anything about training.

-
-
-
Return type
-

None

-
-
-
- -
-
-

load_from_checkpoint

-
-
-classmethod LightningModule.load_from_checkpoint(checkpoint_path, map_location=None, hparams_file=None, strict=True, **kwargs)
-

Primary way of loading a model from a checkpoint. When Lightning saves a checkpoint -it stores the arguments passed to __init__ in the checkpoint under "hyper_parameters".

-

Any arguments specified through **kwargs will override args stored in "hyper_parameters".

-
-
Parameters
-
    -
  • checkpoint_path (Union[str, IO]) – Path to checkpoint. This can also be a URL, or file-like object

  • -
  • map_location (Union[Dict[str, str], str, device, int, Callable, None]) – If your checkpoint saved a GPU model and you now load on CPUs -or a different number of GPUs, use this to map to the new setup. -The behaviour is the same as in torch.load().

  • -
  • hparams_file (Optional[str]) –

    Optional path to a .yaml file with hierarchical structure -as in this example:

    -
    drop_prob: 0.2
    -dataloader:
    -    batch_size: 32
    -
    -
    -

    You most likely won’t need this since Lightning will always save the hyperparameters -to the checkpoint. -However, if your checkpoint weights don’t have the hyperparameters saved, -use this method to pass in a .yaml file with the hparams you’d like to use. -These will be converted into a dict and passed into your -LightningModule for use.

    -

    If your model’s hparams argument is Namespace -and .yaml file has hierarchical structure, you need to refactor your model to treat -hparams as dict.

    -

  • -
  • strict (bool) – Whether to strictly enforce that the keys in checkpoint_path match the keys -returned by this module’s state dict.

  • -
  • kwargs – Any extra keyword args needed to init the model. Can also be used to override saved -hyperparameter values.

  • -
-
-
Returns
-

LightningModule instance with loaded weights and hyperparameters (if available).

-
-
-
-

Note

-

load_from_checkpoint is a class method. You should use your LightningModule -class to call it instead of the LightningModule instance.

-
-

Example:

-
# load weights without mapping ...
-model = MyLightningModule.load_from_checkpoint('path/to/checkpoint.ckpt')
-
-# or load weights mapping all weights from GPU 1 to GPU 0 ...
-map_location = {'cuda:1':'cuda:0'}
-model = MyLightningModule.load_from_checkpoint(
-    'path/to/checkpoint.ckpt',
-    map_location=map_location
-)
-
-# or load weights and hyperparameters from separate files.
-model = MyLightningModule.load_from_checkpoint(
-    'path/to/checkpoint.ckpt',
-    hparams_file='/path/to/hparams_file.yaml'
-)
-
-# override some of the params with new values
-model = MyLightningModule.load_from_checkpoint(
-    PATH,
-    num_layers=128,
-    pretrained_ckpt_path=NEW_PATH,
-)
-
-# predict
-pretrained_model.eval()
-pretrained_model.freeze()
-y_hat = pretrained_model(x)
-
-
-
- -
-
-

on_hpc_save

-
-
-LightningModule.on_hpc_save(checkpoint)
-

Hook to do whatever you need right before Slurm manager saves the model.

-
-
Parameters
-

checkpoint (Dict[str, Any]) – A dictionary in which you can save variables to save in a checkpoint. -Contents need to be pickleable.

-
-
-
-

Deprecated since version v1.6: This method is deprecated in v1.6 and will be removed in v1.8. -Please use LightningModule.on_save_checkpoint instead.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_hpc_load

-
-
-LightningModule.on_hpc_load(checkpoint)
-

Hook to do whatever you need right before Slurm manager loads the model.

-
-
Parameters
-

checkpoint (Dict[str, Any]) – A dictionary with variables from the checkpoint.

-
-
-
-

Deprecated since version v1.6: This method is deprecated in v1.6 and will be removed in v1.8. -Please use LightningModule.on_load_checkpoint instead.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_train_start

-
-
-LightningModule.on_train_start()
-

Called at the beginning of training after sanity check.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_end

-
-
-LightningModule.on_train_end()
-

Called at the end of training before logger experiment is closed.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_start

-
-
-LightningModule.on_validation_start()
-

Called at the beginning of validation.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_end

-
-
-LightningModule.on_validation_end()
-

Called at the end of validation.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_batch_start

-
-
-LightningModule.on_test_batch_start(batch, batch_idx, dataloader_idx)
-

Called in the test loop before anything happens for that batch.

-
-
Parameters
-
    -
  • batch (Any) – The batched data as it is returned by the test DataLoader.

  • -
  • batch_idx (int) – the index of the batch

  • -
  • dataloader_idx (int) – the index of the dataloader

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

on_test_batch_end

-
-
-LightningModule.on_test_batch_end(outputs, batch, batch_idx, dataloader_idx)
-

Called in the test loop after the batch.

-
-
Parameters
-
    -
  • outputs (Union[Tensor, Dict[str, Any], None]) – The outputs of test_step_end(test_step(x))

  • -
  • batch (Any) – The batched data as it is returned by the test DataLoader.

  • -
  • batch_idx (int) – the index of the batch

  • -
  • dataloader_idx (int) – the index of the dataloader

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

on_test_epoch_start

-
-
-LightningModule.on_test_epoch_start()
-

Called in the test loop at the very beginning of the epoch.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_epoch_end

-
-
-LightningModule.on_test_epoch_end()
-

Called in the test loop at the very end of the epoch.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_start

-
-
-LightningModule.on_test_start()
-

Called at the beginning of testing.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_end

-
-
-LightningModule.on_test_end()
-

Called at the end of testing.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_batch_start

-
-
-LightningModule.on_predict_batch_start(batch, batch_idx, dataloader_idx)
-

Called in the predict loop before anything happens for that batch.

-
-
Parameters
-
    -
  • batch (Any) – The batched data as it is returned by the test DataLoader.

  • -
  • batch_idx (int) – the index of the batch

  • -
  • dataloader_idx (int) – the index of the dataloader

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_batch_end

-
-
-LightningModule.on_predict_batch_end(outputs, batch, batch_idx, dataloader_idx)
-

Called in the predict loop after the batch.

-
-
Parameters
-
    -
  • outputs (Optional[Any]) – The outputs of predict_step_end(test_step(x))

  • -
  • batch (Any) – The batched data as it is returned by the test DataLoader.

  • -
  • batch_idx (int) – the index of the batch

  • -
  • dataloader_idx (int) – the index of the dataloader

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_epoch_start

-
-
-LightningModule.on_predict_epoch_start()
-

Called at the beginning of predicting.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_epoch_end

-
-
-LightningModule.on_predict_epoch_end(results)
-

Called at the end of predicting.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_start

-
-
-LightningModule.on_predict_start()
-

Called at the beginning of predicting.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_end

-
-
-LightningModule.on_predict_end()
-

Called at the end of predicting.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_batch_start

-
-
-LightningModule.on_train_batch_start(batch, batch_idx, unused=0)
-

Called in the training loop before anything happens for that batch.

-

If you return -1 here, you will skip training for the rest of the current epoch.

-
-
Parameters
-
    -
  • batch (Any) – The batched data as it is returned by the training DataLoader.

  • -
  • batch_idx (int) – the index of the batch

  • -
  • unused (int) – Deprecated argument. Will be removed in v1.7.

  • -
-
-
Return type
-

Optional[int]

-
-
-
- -
-
-

on_train_batch_end

-
-
-LightningModule.on_train_batch_end(outputs, batch, batch_idx, unused=0)
-

Called in the training loop after the batch.

-
-
Parameters
-
    -
  • outputs (Union[Tensor, Dict[str, Any]]) – The outputs of training_step_end(training_step(x))

  • -
  • batch (Any) – The batched data as it is returned by the training DataLoader.

  • -
  • batch_idx (int) – the index of the batch

  • -
  • unused (int) – Deprecated argument. Will be removed in v1.7.

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

on_train_epoch_start

-
-
-LightningModule.on_train_epoch_start()
-

Called in the training loop at the very beginning of the epoch.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_epoch_end

-
-
-LightningModule.on_train_epoch_end()
-

Called in the training loop at the very end of the epoch.

-

To access all batch outputs at the end of the epoch, either:

-
    -
  1. Implement training_epoch_end in the LightningModule OR

  2. -
  3. Cache data across steps on the attribute(s) of the LightningModule and access them in this hook

  4. -
-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_batch_start

-
-
-LightningModule.on_validation_batch_start(batch, batch_idx, dataloader_idx)
-

Called in the validation loop before anything happens for that batch.

-
-
Parameters
-
    -
  • batch (Any) – The batched data as it is returned by the validation DataLoader.

  • -
  • batch_idx (int) – the index of the batch

  • -
  • dataloader_idx (int) – the index of the dataloader

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_batch_end

-
-
-LightningModule.on_validation_batch_end(outputs, batch, batch_idx, dataloader_idx)
-

Called in the validation loop after the batch.

-
-
Parameters
-
    -
  • outputs (Union[Tensor, Dict[str, Any], None]) – The outputs of validation_step_end(validation_step(x))

  • -
  • batch (Any) – The batched data as it is returned by the validation DataLoader.

  • -
  • batch_idx (int) – the index of the batch

  • -
  • dataloader_idx (int) – the index of the dataloader

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_epoch_start

-
-
-LightningModule.on_validation_epoch_start()
-

Called in the validation loop at the very beginning of the epoch.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_epoch_end

-
-
-LightningModule.on_validation_epoch_end()
-

Called in the validation loop at the very end of the epoch.

-
-
Return type
-

None

-
-
-
- -
-
-

on_post_move_to_device

-
-
-LightningModule.on_post_move_to_device()
-

Called in the parameter_validation decorator after -to() is called. This is a good place to tie weights between -modules after moving them to a device. Can be used when training models with weight sharing properties on -TPU.

-

Addresses the handling of shared weights on TPU: -https://github.com/pytorch/xla/blob/master/TROUBLESHOOTING.md#xla-tensor-quirks

-

Example:

-
def on_post_move_to_device(self):
-    self.decoder.weight = self.encoder.weight
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

configure_sharded_model

-
-
-LightningModule.configure_sharded_model()
-

Hook to create modules in a distributed aware context. This is useful for when using sharded plugins, -where we’d like to shard the model instantly, which is useful for extremely large models which can save -memory and initialization time.

-

This hook is called during each of fit/val/test/predict stages in the same process, so ensure that -implementation of this hook is idempotent.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_model_eval

-
-
-LightningModule.on_validation_model_eval()
-

Sets the model to eval during the val loop.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_model_train

-
-
-LightningModule.on_validation_model_train()
-

Sets the model to train during the val loop.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_model_eval

-
-
-LightningModule.on_test_model_eval()
-

Sets the model to eval during the test loop.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_model_train

-
-
-LightningModule.on_test_model_train()
-

Sets the model to train during the test loop.

-
-
Return type
-

None

-
-
-
- -
-
-

on_before_optimizer_step

-
-
-LightningModule.on_before_optimizer_step(optimizer, optimizer_idx)
-

Called before optimizer.step().

-

If using gradient accumulation, the hook is called once the gradients have been accumulated. -See: accumulate_grad_batches.

-

If using native AMP, the loss will be unscaled before calling this hook. -See these docs -for more information on the scaling of gradients.

-

If clipping gradients, the gradients will not have been clipped yet.

-
-
Parameters
-
    -
  • optimizer (Optimizer) – Current optimizer being used.

  • -
  • optimizer_idx (int) – Index of the current optimizer being used.

  • -
-
-
-

Example:

-
def on_before_optimizer_step(self, optimizer, optimizer_idx):
-    # example to inspect gradient information in tensorboard
-    if self.trainer.global_step % 25 == 0:  # don't make the tf file huge
-        for k, v in self.named_parameters():
-            self.logger.experiment.add_histogram(
-                tag=k, values=v.grad, global_step=self.trainer.global_step
-            )
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

configure_gradient_clipping

-
-
-LightningModule.configure_gradient_clipping(optimizer, optimizer_idx, gradient_clip_val=None, gradient_clip_algorithm=None)[source]
-

Perform gradient clipping for the optimizer parameters. Called before optimizer_step().

-
-
Parameters
-
    -
  • optimizer (Optimizer) – Current optimizer being used.

  • -
  • optimizer_idx (int) – Index of the current optimizer being used.

  • -
  • gradient_clip_val (Union[int, float, None]) – The value at which to clip gradients. By default value passed in Trainer -will be available here.

  • -
  • gradient_clip_algorithm (Optional[str]) – The gradient clipping algorithm to use. By default value -passed in Trainer will be available here.

  • -
-
-
-

Example:

-
# Perform gradient clipping on gradients associated with discriminator (optimizer_idx=1) in GAN
-def configure_gradient_clipping(self, optimizer, optimizer_idx, gradient_clip_val, gradient_clip_algorithm):
-    if optimizer_idx == 1:
-        # Lightning will handle the gradient clipping
-        self.clip_gradients(
-            optimizer,
-            gradient_clip_val=gradient_clip_val,
-            gradient_clip_algorithm=gradient_clip_algorithm
-        )
-    else:
-        # implement your own custom logic to clip gradients for generator (optimizer_idx=0)
-
-
-
- -
-
-

optimizer_step

-
-
-LightningModule.optimizer_step(epoch, batch_idx, optimizer, optimizer_idx=0, optimizer_closure=None, on_tpu=False, using_native_amp=False, using_lbfgs=False)[source]
-

Override this method to adjust the default way the Trainer calls -each optimizer.

-

By default, Lightning calls step() and zero_grad() as shown in the example once per optimizer. -This method (and zero_grad()) won’t be called during the accumulation phase when -Trainer(accumulate_grad_batches != 1). Overriding this hook has no benefit with manual optimization.

-
-
Parameters
-
    -
  • epoch (int) – Current epoch

  • -
  • batch_idx (int) – Index of current batch

  • -
  • optimizer (Union[Optimizer, LightningOptimizer]) – A PyTorch optimizer

  • -
  • optimizer_idx (int) – If you used multiple optimizers, this indexes into that list.

  • -
  • optimizer_closure (Optional[Callable[[], Any]]) – The optimizer closure. This closure must be executed as it includes the -calls to training_step(), optimizer.zero_grad(), and backward().

  • -
  • on_tpu (bool) – True if TPU backward is required

  • -
  • using_native_amp (bool) – True if using native amp

  • -
  • using_lbfgs (bool) – True if the matching optimizer is torch.optim.LBFGS

  • -
-
-
-

Examples:

-
# DEFAULT
-def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_idx,
-                   optimizer_closure, on_tpu, using_native_amp, using_lbfgs):
-    optimizer.step(closure=optimizer_closure)
-
-# Alternating schedule for optimizer steps (i.e.: GANs)
-def optimizer_step(self, epoch, batch_idx, optimizer, optimizer_idx,
-                   optimizer_closure, on_tpu, using_native_amp, using_lbfgs):
-    # update generator opt every step
-    if optimizer_idx == 0:
-        optimizer.step(closure=optimizer_closure)
-
-    # update discriminator opt every 2 steps
-    if optimizer_idx == 1:
-        if (batch_idx + 1) % 2 == 0 :
-            optimizer.step(closure=optimizer_closure)
-        else:
-            # call the closure by itself to run `training_step` + `backward` without an optimizer step
-            optimizer_closure()
-
-    # ...
-    # add as many optimizers as you want
-
-
-

Here’s another example showing how to use this for more advanced things such as -learning rate warm-up:

-
# learning rate warm-up
-def optimizer_step(
-    self,
-    epoch,
-    batch_idx,
-    optimizer,
-    optimizer_idx,
-    optimizer_closure,
-    on_tpu,
-    using_native_amp,
-    using_lbfgs,
-):
-    # update params
-    optimizer.step(closure=optimizer_closure)
-
-    # manually warm up lr without a scheduler
-    if self.trainer.global_step < 500:
-        lr_scale = min(1.0, float(self.trainer.global_step + 1) / 500.0)
-        for pg in optimizer.param_groups:
-            pg["lr"] = lr_scale * self.learning_rate
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

optimizer_zero_grad

-
-
-LightningModule.optimizer_zero_grad(epoch, batch_idx, optimizer, optimizer_idx)[source]
-

Override this method to change the default behaviour of optimizer.zero_grad().

-
-
Parameters
-
    -
  • epoch (int) – Current epoch

  • -
  • batch_idx (int) – Index of current batch

  • -
  • optimizer (Optimizer) – A PyTorch optimizer

  • -
  • optimizer_idx (int) – If you used multiple optimizers this indexes into that list.

  • -
-
-
-

Examples:

-
# DEFAULT
-def optimizer_zero_grad(self, epoch, batch_idx, optimizer, optimizer_idx):
-    optimizer.zero_grad()
-
-# Set gradients to `None` instead of zero to improve performance.
-def optimizer_zero_grad(self, epoch, batch_idx, optimizer, optimizer_idx):
-    optimizer.zero_grad(set_to_none=True)
-
-
-

See torch.optim.Optimizer.zero_grad() for the explanation of the above example.

-
- -
-
-

prepare_data

-
-
-LightningModule.prepare_data()
-

Use this to download and prepare data. Downloading and saving data with multiple processes (distributed -settings) will result in corrupted data. Lightning ensures this method is called only within a single -process, so you can safely add your downloading logic within.

-
-

Warning

-

DO NOT set state to the model (use setup instead) -since this is NOT called on every device

-
-

Example:

-
def prepare_data(self):
-    # good
-    download_data()
-    tokenize()
-    etc()
-
-    # bad
-    self.split = data_split
-    self.some_state = some_other_state()
-
-
-

In DDP prepare_data can be called in two ways (using Trainer(prepare_data_per_node)):

-
    -
  1. Once per node. This is the default and is only called on LOCAL_RANK=0.

  2. -
  3. Once in total. Only called on GLOBAL_RANK=0.

  4. -
-

See prepare_data_per_node.

-

Example:

-
# DEFAULT
-# called once per node on LOCAL_RANK=0 of that node
-Trainer(prepare_data_per_node=True)
-
-# call on GLOBAL_RANK=0 (great for shared file systems)
-Trainer(prepare_data_per_node=False)
-
-
-

This is called before requesting the dataloaders:

-
model.prepare_data()
-initialize_distributed()
-model.setup(stage)
-model.train_dataloader()
-model.val_dataloader()
-model.test_dataloader()
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

setup

-
-
-LightningModule.setup(stage=None)
-

Called at the beginning of fit (train + validate), validate, test, or predict. This is a good hook when -you need to build models dynamically or adjust something about them. This hook is called on every process -when using DDP.

-
-
Parameters
-

stage (Optional[str]) – either 'fit', 'validate', 'test', or 'predict'

-
-
-

Example:

-
class LitModel(...):
-    def __init__(self):
-        self.l1 = None
-
-    def prepare_data(self):
-        download_data()
-        tokenize()
-
-        # don't do this
-        self.something = else
-
-    def setup(self, stage):
-        data = load_data(...)
-        self.l1 = nn.Linear(28, data.num_classes)
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

tbptt_split_batch

-
-
-LightningModule.tbptt_split_batch(batch, split_size)[source]
-

When using truncated backpropagation through time, each batch must be split along the -time dimension. Lightning handles this by default, but for custom behavior override -this function.

-
-
Parameters
-
    -
  • batch (Any) – Current batch

  • -
  • split_size (int) – The size of the split

  • -
-
-
Return type
-

List[Any]

-
-
Returns
-

List of batch splits. Each split will be passed to training_step() to enable truncated -back propagation through time. The default implementation splits root level Tensors and -Sequences at dim=1 (i.e. time dim). It assumes that each time dim is the same length.

-
-
-

Examples:

-
def tbptt_split_batch(self, batch, split_size):
-    splits = []
-    for t in range(0, time_dims[0], split_size):
-        batch_split = []
-        for i, x in enumerate(batch):
-            if isinstance(x, torch.Tensor):
-                split_x = x[:, t:t + split_size]
-            elif isinstance(x, collections.Sequence):
-                split_x = [None] * len(x)
-                for batch_idx in range(len(x)):
-                  split_x[batch_idx] = x[batch_idx][t:t + split_size]
-            batch_split.append(split_x)
-        splits.append(batch_split)
-    return splits
-
-
-
-

Note

-

Called in the training loop after -on_train_batch_start() -if truncated_bptt_steps > 0. -Each returned batch split is passed separately to training_step().

-
-
- -
-
-

teardown

-
-
-LightningModule.teardown(stage=None)
-

Called at the end of fit (train + validate), validate, test, or predict.

-
-
Parameters
-

stage (Optional[str]) – either 'fit', 'validate', 'test', or 'predict'

-
-
Return type
-

None

-
-
-
- -
-
-

train_dataloader

-
-
-LightningModule.train_dataloader()
-

Implement one or more PyTorch DataLoaders for training.

-
-
Return type
-

Union[DataLoader, Sequence[DataLoader], Sequence[Sequence[DataLoader]], Sequence[Dict[str, DataLoader]], Dict[str, DataLoader], Dict[str, Dict[str, DataLoader]], Dict[str, Sequence[DataLoader]]]

-
-
Returns
-

A collection of torch.utils.data.DataLoader specifying training samples. -In the case of multiple dataloaders, please see this section.

-
-
-

The dataloader you return will not be reloaded unless you set -reload_dataloaders_every_n_epochs to -a positive integer.

-

For data processing use the following pattern:

-
-
    -
  • download in prepare_data()

  • -
  • process and split in setup()

  • -
-
-

However, the above are only necessary for distributed processing.

-
-

Warning

-

do not assign state in prepare_data

-
-
    -
  • fit()

  • -
  • prepare_data()

  • -
  • setup()

  • -
-
-

Note

-

Lightning adds the correct sampler for distributed and arbitrary hardware. -There is no need to set it yourself.

-
-

Example:

-
# single dataloader
-def train_dataloader(self):
-    transform = transforms.Compose([transforms.ToTensor(),
-                                    transforms.Normalize((0.5,), (1.0,))])
-    dataset = MNIST(root='/path/to/mnist/', train=True, transform=transform,
-                    download=True)
-    loader = torch.utils.data.DataLoader(
-        dataset=dataset,
-        batch_size=self.batch_size,
-        shuffle=True
-    )
-    return loader
-
-# multiple dataloaders, return as list
-def train_dataloader(self):
-    mnist = MNIST(...)
-    cifar = CIFAR(...)
-    mnist_loader = torch.utils.data.DataLoader(
-        dataset=mnist, batch_size=self.batch_size, shuffle=True
-    )
-    cifar_loader = torch.utils.data.DataLoader(
-        dataset=cifar, batch_size=self.batch_size, shuffle=True
-    )
-    # each batch will be a list of tensors: [batch_mnist, batch_cifar]
-    return [mnist_loader, cifar_loader]
-
-# multiple dataloader, return as dict
-def train_dataloader(self):
-    mnist = MNIST(...)
-    cifar = CIFAR(...)
-    mnist_loader = torch.utils.data.DataLoader(
-        dataset=mnist, batch_size=self.batch_size, shuffle=True
-    )
-    cifar_loader = torch.utils.data.DataLoader(
-        dataset=cifar, batch_size=self.batch_size, shuffle=True
-    )
-    # each batch will be a dict of tensors: {'mnist': batch_mnist, 'cifar': batch_cifar}
-    return {'mnist': mnist_loader, 'cifar': cifar_loader}
-
-
-
- -
-
-

val_dataloader

-
-
-LightningModule.val_dataloader()
-

Implement one or multiple PyTorch DataLoaders for validation.

-

The dataloader you return will not be reloaded unless you set -reload_dataloaders_every_n_epochs to -a positive integer.

-

It’s recommended that all data downloads and preparation happen in prepare_data().

-
    -
  • fit()

  • -
  • validate()

  • -
  • prepare_data()

  • -
  • setup()

  • -
-
-

Note

-

Lightning adds the correct sampler for distributed and arbitrary hardware -There is no need to set it yourself.

-
-
-
Return type
-

Union[DataLoader, Sequence[DataLoader]]

-
-
Returns
-

A torch.utils.data.DataLoader or a sequence of them specifying validation samples.

-
-
-

Examples:

-
def val_dataloader(self):
-    transform = transforms.Compose([transforms.ToTensor(),
-                                    transforms.Normalize((0.5,), (1.0,))])
-    dataset = MNIST(root='/path/to/mnist/', train=False,
-                    transform=transform, download=True)
-    loader = torch.utils.data.DataLoader(
-        dataset=dataset,
-        batch_size=self.batch_size,
-        shuffle=False
-    )
-
-    return loader
-
-# can also return multiple dataloaders
-def val_dataloader(self):
-    return [loader_a, loader_b, ..., loader_n]
-
-
-
-

Note

-

If you don’t need a validation dataset and a validation_step(), you don’t need to -implement this method.

-
-
-

Note

-

In the case where you return multiple validation dataloaders, the validation_step() -will have an argument dataloader_idx which matches the order here.

-
-
- -
-
-

test_dataloader

-
-
-LightningModule.test_dataloader()
-

Implement one or multiple PyTorch DataLoaders for testing.

-

For data processing use the following pattern:

-
-
    -
  • download in prepare_data()

  • -
  • process and split in setup()

  • -
-
-

However, the above are only necessary for distributed processing.

-
-

Warning

-

do not assign state in prepare_data

-
-
    -
  • test()

  • -
  • prepare_data()

  • -
  • setup()

  • -
-
-

Note

-

Lightning adds the correct sampler for distributed and arbitrary hardware. -There is no need to set it yourself.

-
-
-
Return type
-

Union[DataLoader, Sequence[DataLoader]]

-
-
Returns
-

A torch.utils.data.DataLoader or a sequence of them specifying testing samples.

-
-
-

Example:

-
def test_dataloader(self):
-    transform = transforms.Compose([transforms.ToTensor(),
-                                    transforms.Normalize((0.5,), (1.0,))])
-    dataset = MNIST(root='/path/to/mnist/', train=False, transform=transform,
-                    download=True)
-    loader = torch.utils.data.DataLoader(
-        dataset=dataset,
-        batch_size=self.batch_size,
-        shuffle=False
-    )
-
-    return loader
-
-# can also return multiple dataloaders
-def test_dataloader(self):
-    return [loader_a, loader_b, ..., loader_n]
-
-
-
-

Note

-

If you don’t need a test dataset and a test_step(), you don’t need to implement -this method.

-
-
-

Note

-

In the case where you return multiple test dataloaders, the test_step() -will have an argument dataloader_idx which matches the order here.

-
-
- -
-
-

predict_dataloader

-
-
-LightningModule.predict_dataloader()
-

Implement one or multiple PyTorch DataLoaders for prediction.

-

It’s recommended that all data downloads and preparation happen in prepare_data().

-
    -
  • predict()

  • -
  • prepare_data()

  • -
  • setup()

  • -
-
-

Note

-

Lightning adds the correct sampler for distributed and arbitrary hardware -There is no need to set it yourself.

-
-
-
Return type
-

Union[DataLoader, Sequence[DataLoader]]

-
-
Returns
-

A torch.utils.data.DataLoader or a sequence of them specifying prediction samples.

-
-
-
-

Note

-

In the case where you return multiple prediction dataloaders, the predict_step() -will have an argument dataloader_idx which matches the order here.

-
-
- -
-
-

on_train_dataloader

-
-
-LightningModule.on_train_dataloader()
-

Called before requesting the train dataloader.

-
-

Deprecated since version v1.5: on_train_dataloader() is deprecated and will be removed in v1.7.0. -Please use train_dataloader() directly.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_val_dataloader

-
-
-LightningModule.on_val_dataloader()
-

Called before requesting the val dataloader.

-
-

Deprecated since version v1.5: on_val_dataloader() is deprecated and will be removed in v1.7.0. -Please use val_dataloader() directly.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_test_dataloader

-
-
-LightningModule.on_test_dataloader()
-

Called before requesting the test dataloader.

-
-

Deprecated since version v1.5: on_test_dataloader() is deprecated and will be removed in v1.7.0. -Please use test_dataloader() directly.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_dataloader

-
-
-LightningModule.on_predict_dataloader()
-

Called before requesting the predict dataloader.

-
-

Deprecated since version v1.5: on_predict_dataloader() is deprecated and will be removed in v1.7.0. -Please use predict_dataloader() directly.

-
-
-
Return type
-

None

-
-
-
- -
-
-

transfer_batch_to_device

-
-
-LightningModule.transfer_batch_to_device(batch, device, dataloader_idx)
-

Override this hook if your DataLoader returns tensors wrapped in a custom -data structure.

-

The data types listed below (and any arbitrary nesting of them) are supported out of the box:

-
    -
  • torch.Tensor or anything that implements .to(…)

  • -
  • list

  • -
  • dict

  • -
  • tuple

  • -
  • torchtext.data.batch.Batch

  • -
-

For anything else, you need to define how the data is moved to the target device (CPU, GPU, TPU, …).

-
-

Note

-

This hook should only transfer the data and not modify it, nor should it move the data to -any other device than the one passed in as argument (unless you know what you are doing). -To check the current state of execution of this hook you can use -self.trainer.training/testing/validating/predicting so that you can -add different logic as per your requirement.

-
-
-

Note

-

This hook only runs on single GPU training and DDP (no data-parallel). -Data-Parallel support will come in near future.

-
-
-
Parameters
-
    -
  • batch (Any) – A batch of data that needs to be transferred to a new device.

  • -
  • device (device) – The target device as defined in PyTorch.

  • -
  • dataloader_idx (int) – The index of the dataloader to which the batch belongs.

  • -
-
-
Return type
-

Any

-
-
Returns
-

A reference to the data on the new device.

-
-
-

Example:

-
def transfer_batch_to_device(self, batch, device, dataloader_idx):
-    if isinstance(batch, CustomBatch):
-        # move all tensors in your custom data structure to the device
-        batch.samples = batch.samples.to(device)
-        batch.targets = batch.targets.to(device)
-    elif dataloader_idx == 0:
-        # skip device transfer for the first dataloader or anything you wish
-        pass
-    else:
-        batch = super().transfer_batch_to_device(data, device, dataloader_idx)
-    return batch
-
-
-
-
Raises
-

MisconfigurationException – If using data-parallel, Trainer(strategy='dp').

-
-
-
-

See also

-
    -
  • move_data_to_device()

  • -
  • apply_to_collection()

  • -
-
-
- -
-
-

on_before_batch_transfer

-
-
-LightningModule.on_before_batch_transfer(batch, dataloader_idx)
-

Override to alter or apply batch augmentations to your batch before it is transferred to the device.

-
-

Note

-

To check the current state of execution of this hook you can use -self.trainer.training/testing/validating/predicting so that you can -add different logic as per your requirement.

-
-
-

Note

-

This hook only runs on single GPU training and DDP (no data-parallel). -Data-Parallel support will come in near future.

-
-
-
Parameters
-
    -
  • batch (Any) – A batch of data that needs to be altered or augmented.

  • -
  • dataloader_idx (int) – The index of the dataloader to which the batch belongs.

  • -
-
-
Return type
-

Any

-
-
Returns
-

A batch of data

-
-
-

Example:

-
def on_before_batch_transfer(self, batch, dataloader_idx):
-    batch['x'] = transforms(batch['x'])
-    return batch
-
-
-
-
Raises
-

MisconfigurationException – If using data-parallel, Trainer(strategy='dp').

-
-
-
-

See also

-
    -
  • on_after_batch_transfer()

  • -
  • transfer_batch_to_device()

  • -
-
-
- -
-
-

on_after_batch_transfer

-
-
-LightningModule.on_after_batch_transfer(batch, dataloader_idx)
-

Override to alter or apply batch augmentations to your batch after it is transferred to the device.

-
-

Note

-

To check the current state of execution of this hook you can use -self.trainer.training/testing/validating/predicting so that you can -add different logic as per your requirement.

-
-
-

Note

-

This hook only runs on single GPU training and DDP (no data-parallel). -Data-Parallel support will come in near future.

-
-
-
Parameters
-
    -
  • batch (Any) – A batch of data that needs to be altered or augmented.

  • -
  • dataloader_idx (int) – The index of the dataloader to which the batch belongs.

  • -
-
-
Return type
-

Any

-
-
Returns
-

A batch of data

-
-
-

Example:

-
def on_after_batch_transfer(self, batch, dataloader_idx):
-    batch['x'] = gpu_transforms(batch['x'])
-    return batch
-
-
-
-
Raises
-

MisconfigurationException – If using data-parallel, Trainer(strategy='dp').

-
-
-
-

See also

-
    -
  • on_before_batch_transfer()

  • -
  • transfer_batch_to_device()

  • -
-
-
- -
-
-

add_to_queue

-
-
-LightningModule.add_to_queue(queue)[source]
-

Appends the trainer.callback_metrics dictionary to the given queue. To avoid issues with memory -sharing, we cast the data to numpy.

-
-
Parameters
-

queue (_FakeQueue) – the instance of the queue to append the data.

-
-
-
-

Deprecated since version v1.5: This method was deprecated in v1.5 and will be removed in v1.7.

-
-
-
Return type
-

None

-
-
-
- -
-
-

get_from_queue

-
-
-LightningModule.get_from_queue(queue)[source]
-

Retrieve the trainer.callback_metrics dictionary from the given queue. To preserve consistency, -we cast back the data to torch.Tensor.

-
-
Parameters
-

queue (_FakeQueue) – the instance of the queue from where to get the data.

-
-
-
-

Deprecated since version v1.5: This method was deprecated in v1.5 and will be removed in v1.7.

-
-
-
Return type
-

None

-
-
-
- -
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/optimization.html b/docs/common/optimization.html deleted file mode 100644 index 428d7b8..0000000 --- a/docs/common/optimization.html +++ /dev/null @@ -1,1296 +0,0 @@ - - - - - - - - - - - - - - Optimization — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Optimization

-

Lightning offers two modes for managing the optimization process:

-
    -
  • Manual Optimization

  • -
  • Automatic Optimization

  • -
-

For the majority of research cases, automatic optimization will do the right thing for you and it is what most -users should use.

-

For advanced/expert users who want to do esoteric optimization schedules or techniques, use manual optimization.

-
-
-

Manual Optimization

-

For advanced research topics like reinforcement learning, sparse coding, or GAN research, it may be desirable to -manually manage the optimization process.

-

This is only recommended for experts who need ultimate flexibility. -Lightning will handle only accelerator, precision and strategy logic. -The users are left with optimizer.zero_grad(), gradient accumulation, model toggling, etc..

-

To manually optimize, do the following:

-
    -
  • Set self.automatic_optimization=False in your LightningModule’s __init__.

  • -
  • Use the following functions and call them manually:

    -
      -
    • self.optimizers() to access your optimizers (one or multiple)

    • -
    • optimizer.zero_grad() to clear the gradients from the previous training step

    • -
    • self.manual_backward(loss) instead of loss.backward()

    • -
    • optimizer.step() to update your model parameters

    • -
    -
  • -
-

Here is a minimal example of manual optimization.

-
from pytorch_lightning import LightningModule
-
-
-class MyModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        # Important: This property activates manual optimization.
-        self.automatic_optimization = False
-
-    def training_step(self, batch, batch_idx):
-        opt = self.optimizers()
-        opt.zero_grad()
-        loss = self.compute_loss(batch)
-        self.manual_backward(loss)
-        opt.step()
-
-
-
-

Warning

-

Before 1.2, optimizer.step() was calling optimizer.zero_grad() internally. -From 1.2, it is left to the user’s expertise.

-
-
-

Tip

-

Be careful where you call optimizer.zero_grad(), or your model won’t converge. -It is good practice to call optimizer.zero_grad() before self.manual_backward(loss).

-
-
-

Access your Own Optimizer

-

The provided optimizer is a LightningOptimizer object wrapping your own optimizer -configured in your configure_optimizers(). You can access your own optimizer -with optimizer.optimizer. However, if you use your own optimizer to perform a step, Lightning won’t be able to -support accelerators, precision and profiling for you.

-
class Model(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.automatic_optimization = False
-        ...
-
-    def training_step(self, batch, batch_idx):
-        optimizer = self.optimizers()
-
-        # `optimizer` is a `LightningOptimizer` wrapping the optimizer.
-        # To access it, do the following.
-        # However, it won't work on TPU, AMP, etc...
-        optimizer = optimizer.optimizer
-        ...
-
-
-
-
-

Gradient Accumulation

-

You can accumulate gradients over batches similarly to accumulate_grad_batches argument in -Trainer for automatic optimization. To perform gradient accumulation with one optimizer -after every N steps, you can do as such.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    opt = self.optimizers()
-
-    loss = self.compute_loss(batch)
-    self.manual_backward(loss)
-
-    # accumulate gradients of N batches
-    if (batch_idx + 1) % N == 0:
-        opt.step()
-        opt.zero_grad()
-
-
-
-
-

Use Multiple Optimizers (like GANs)

-

Here is an example training a simple GAN with multiple optimizers using manual optimization.

-
import torch
-from torch import Tensor
-from pytorch_lightning import LightningModule
-
-
-class SimpleGAN(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.G = Generator()
-        self.D = Discriminator()
-
-        # Important: This property activates manual optimization.
-        self.automatic_optimization = False
-
-    def sample_z(self, n) -> Tensor:
-        sample = self._Z.sample((n,))
-        return sample
-
-    def sample_G(self, n) -> Tensor:
-        z = self.sample_z(n)
-        return self.G(z)
-
-    def training_step(self, batch, batch_idx):
-        # Implementation follows the PyTorch tutorial:
-        # https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html
-        g_opt, d_opt = self.optimizers()
-
-        X, _ = batch
-        batch_size = X.shape[0]
-
-        real_label = torch.ones((batch_size, 1), device=self.device)
-        fake_label = torch.zeros((batch_size, 1), device=self.device)
-
-        g_X = self.sample_G(batch_size)
-
-        ##########################
-        # Optimize Discriminator #
-        ##########################
-        d_x = self.D(X)
-        errD_real = self.criterion(d_x, real_label)
-
-        d_z = self.D(g_X.detach())
-        errD_fake = self.criterion(d_z, fake_label)
-
-        errD = errD_real + errD_fake
-
-        d_opt.zero_grad()
-        self.manual_backward(errD)
-        d_opt.step()
-
-        ######################
-        # Optimize Generator #
-        ######################
-        d_z = self.D(g_X)
-        errG = self.criterion(d_z, real_label)
-
-        g_opt.zero_grad()
-        self.manual_backward(errG)
-        g_opt.step()
-
-        self.log_dict({"g_loss": errG, "d_loss": errD}, prog_bar=True)
-
-    def configure_optimizers(self):
-        g_opt = torch.optim.Adam(self.G.parameters(), lr=1e-5)
-        d_opt = torch.optim.Adam(self.D.parameters(), lr=1e-5)
-        return g_opt, d_opt
-
-
-
-
-

Learning Rate Scheduling

-

Every optimizer you use can be paired with any -Learning Rate Scheduler. Please see the -documentation of configure_optimizers() for all the available options

-

You can call lr_scheduler.step() at arbitrary intervals. -Use self.lr_schedulers() in your LightningModule to access any learning rate schedulers -defined in your configure_optimizers().

-
-

Warning

-
    -
  • Before v1.3, Lightning automatically called lr_scheduler.step() in both automatic and manual optimization. From -1.3, lr_scheduler.step() is now for the user to call at arbitrary intervals.

  • -
  • Note that the lr_scheduler_config keys, such as "frequency" and "interval", will be ignored even if they are provided in -your configure_optimizers() during manual optimization.

  • -
-
-

Here is an example calling lr_scheduler.step() every step.

-
# step every batch
-def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    # do forward, backward, and optimization
-    ...
-
-    # single scheduler
-    sch = self.lr_schedulers()
-    sch.step()
-
-    # multiple schedulers
-    sch1, sch2 = self.lr_schedulers()
-    sch1.step()
-    sch2.step()
-
-
-

If you want to call lr_scheduler.step() every N steps/epochs, do the following.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    # do forward, backward, and optimization
-    ...
-
-    sch = self.lr_schedulers()
-
-    # step every N batches
-    if (batch_idx + 1) % N == 0:
-        sch.step()
-
-    # step every N epochs
-    if self.trainer.is_last_batch and (self.trainer.current_epoch + 1) % N == 0:
-        sch.step()
-
-
-

If you want to call schedulers that require a metric value after each epoch, consider doing the following:

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_epoch_end(self, outputs):
-    sch = self.lr_schedulers()
-
-    # If the selected scheduler is a ReduceLROnPlateau scheduler.
-    if isinstance(sch, torch.optim.lr_scheduler.ReduceLROnPlateau):
-        sch.step(self.trainer.callback_metrics["loss"])
-
-
-
-
-

Use Closure for LBFGS-like Optimizers

-

It is a good practice to provide the optimizer with a closure function that performs a forward, zero_grad and -backward of your model. It is optional for most optimizers, but makes your code compatible if you switch to an -optimizer which requires a closure, such as LBFGS.

-

See the PyTorch docs for more about the closure.

-

Here is an example using a closure function.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def configure_optimizers(self):
-    return torch.optim.LBFGS(...)
-
-
-def training_step(self, batch, batch_idx):
-    opt = self.optimizers()
-
-    def closure():
-        loss = self.compute_loss(batch)
-        opt.zero_grad()
-        self.manual_backward(loss)
-        return loss
-
-    opt.step(closure=closure)
-
-
-
-

Warning

-

The LBFGS optimizer is not supported for apex AMP, native AMP, IPUs, or DeepSpeed.

-
-
-
-
-
-

Automatic Optimization

-

With Lightning, most users don’t have to think about when to call .zero_grad(), .backward() and .step() -since Lightning automates that for you.

-

Under the hood, Lightning does the following:

-
for epoch in epochs:
-    for batch in data:
-
-        def closure():
-            loss = model.training_step(batch, batch_idx, ...)
-            optimizer.zero_grad()
-            loss.backward()
-            return loss
-
-        optimizer.step(closure)
-
-    lr_scheduler.step()
-
-
-

In the case of multiple optimizers, Lightning does the following:

-
for epoch in epochs:
-    for batch in data:
-        for opt in optimizers:
-
-            def closure():
-                loss = model.training_step(batch, batch_idx, optimizer_idx)
-                opt.zero_grad()
-                loss.backward()
-                return loss
-
-            opt.step(closure)
-
-    for lr_scheduler in lr_schedulers:
-        lr_scheduler.step()
-
-
-

As can be seen in the code snippet above, Lightning defines a closure with training_step(), optimizer.zero_grad() -and loss.backward() for the optimization. This mechanism is in place to support optimizers which operate on the -output of the closure (e.g. the loss) or need to call the closure several times (e.g. LBFGS).

-
-

Warning

-

Before v1.2.2, Lightning internally calls backward, step and zero_grad in the order. -From v1.2.2, the order is changed to zero_grad, backward and step.

-
-
-

Gradient Accumulation

-

Accumulated gradients run K small batches of size N before doing a backward pass. The effect is a large effective batch size of size KxN, where N is the batch size. -Internally it doesn’t stack up the batches and do a forward pass rather it accumulates the gradients for K batches and then do an optimizer.step to make sure the -effective batch size is increased but there is no memory overhead.

-
-

Warning

-

When using distributed training for eg. DDP, with let’s say with P devices, each device accumulates independently i.e. it stores the gradients -after each loss.backward() and doesn’t sync the gradients across the devices until we call optimizer.step(). So for each accumulation -step, the effective batch size on each device will remain N*K but right before the optimizer.step(), the gradient sync will make the effective -batch size as P*N*K. For DP, since the batch is split across devices, the final effective batch size will be N*K.

-
-
-

See also

-

Trainer

-
-
# DEFAULT (ie: no accumulated grads)
-trainer = Trainer(accumulate_grad_batches=1)
-
-# Accumulate gradients for 7 batches
-trainer = Trainer(accumulate_grad_batches=7)
-
-
-

You can set different values for it at different epochs by passing a dictionary, where the key represents the epoch at which the value for gradient accumulation -should be updated.

-
# till 5th epoch, it will accumulate every 8 batches. From 5th epoch
-# till 9th epoch it will accumulate every 4 batches and after that no accumulation
-# will happen. Note that you need to use zero-indexed epoch keys here
-trainer = Trainer(accumulate_grad_batches={0: 8, 4: 4, 8: 1})
-
-
-

Or, you can create custom GradientAccumulationScheduler

-
from pytorch_lightning.callbacks import GradientAccumulationScheduler
-
-
-# till 5th epoch, it will accumulate every 8 batches. From 5th epoch
-# till 9th epoch it will accumulate every 4 batches and after that no accumulation
-# will happen. Note that you need to use zero-indexed epoch keys here
-accumulator = GradientAccumulationScheduler(scheduling={0: 8, 4: 4, 8: 1})
-trainer = Trainer(callbacks=accumulator)
-
-
-
-
-

Use Multiple Optimizers (like GANs)

-

To use multiple optimizers (optionally with learning rate schedulers), return two or more optimizers from -configure_optimizers().

-
# two optimizers, no schedulers
-def configure_optimizers(self):
-    return Adam(...), SGD(...)
-
-
-# two optimizers, one scheduler for adam only
-def configure_optimizers(self):
-    opt1 = Adam(...)
-    opt2 = SGD(...)
-    optimizers = [opt1, opt2]
-    lr_schedulers = {"scheduler": ReduceLROnPlateau(opt1, ...), "monitor": "metric_to_track"}
-    return optimizers, lr_schedulers
-
-
-# two optimizers, two schedulers
-def configure_optimizers(self):
-    opt1 = Adam(...)
-    opt2 = SGD(...)
-    return [opt1, opt2], [StepLR(opt1, ...), OneCycleLR(opt2, ...)]
-
-
-

Under the hood, Lightning will call each optimizer sequentially:

-
for epoch in epochs:
-    for batch in data:
-        for opt in optimizers:
-            loss = train_step(batch, batch_idx, optimizer_idx)
-            opt.zero_grad()
-            loss.backward()
-            opt.step()
-
-    for lr_scheduler in lr_schedulers:
-        lr_scheduler.step()
-
-
-
-
-

Step Optimizeres at Arbitrary Intervals

-

To do more interesting things with your optimizers such as learning rate warm-up or odd scheduling, -override the optimizer_step() function.

-
-

Warning

-

If you are overriding this method, make sure that you pass the optimizer_closure parameter to -optimizer.step() function as shown in the examples because training_step(), optimizer.zero_grad(), -loss.backward() are called in the closure function.

-
-

For example, here step optimizer A every batch and optimizer B every 2 batches.

-
# Alternating schedule for optimizer steps (e.g. GANs)
-def optimizer_step(
-    self,
-    epoch,
-    batch_idx,
-    optimizer,
-    optimizer_idx,
-    optimizer_closure,
-    on_tpu=False,
-    using_native_amp=False,
-    using_lbfgs=False,
-):
-    # update generator every step
-    if optimizer_idx == 0:
-        optimizer.step(closure=optimizer_closure)
-
-    # update discriminator every 2 steps
-    if optimizer_idx == 1:
-        if (batch_idx + 1) % 2 == 0:
-            # the closure (which includes the `training_step`) will be executed by `optimizer.step`
-            optimizer.step(closure=optimizer_closure)
-        else:
-            # call the closure by itself to run `training_step` + `backward` without an optimizer step
-            optimizer_closure()
-
-    # ...
-    # add as many optimizers as you want
-
-
-

Here we add a manual learning rate warm-up without an lr scheduler.

-
# learning rate warm-up
-def optimizer_step(
-    self,
-    epoch,
-    batch_idx,
-    optimizer,
-    optimizer_idx,
-    optimizer_closure,
-    on_tpu=False,
-    using_native_amp=False,
-    using_lbfgs=False,
-):
-    # update params
-    optimizer.step(closure=optimizer_closure)
-
-    # skip the first 500 steps
-    if self.trainer.global_step < 500:
-        lr_scale = min(1.0, float(self.trainer.global_step + 1) / 500.0)
-        for pg in optimizer.param_groups:
-            pg["lr"] = lr_scale * self.hparams.learning_rate
-
-
-
-
-

Access your Own Optimizer

-

The provided optimizer is a LightningOptimizer object wrapping your own optimizer -configured in your configure_optimizers(). -You can access your own optimizer with optimizer.optimizer. However, if you use your own optimizer -to perform a step, Lightning won’t be able to support accelerators, precision and profiling for you.

-
# function hook in LightningModule
-def optimizer_step(
-    self,
-    epoch,
-    batch_idx,
-    optimizer,
-    optimizer_idx,
-    optimizer_closure,
-    on_tpu=False,
-    using_native_amp=False,
-    using_lbfgs=False,
-):
-    optimizer.step(closure=optimizer_closure)
-
-
-# `optimizer` is a `LightningOptimizer` wrapping the optimizer.
-# To access it, do the following.
-# However, it won't work on TPU, AMP, etc...
-def optimizer_step(
-    self,
-    epoch,
-    batch_idx,
-    optimizer,
-    optimizer_idx,
-    optimizer_closure,
-    on_tpu=False,
-    using_native_amp=False,
-    using_lbfgs=False,
-):
-    optimizer = optimizer.optimizer
-    optimizer.step(closure=optimizer_closure)
-
-
-
-
-
-

Bring your own Custom Learning Rate Schedulers

-

Lightning allows using custom learning rate schedulers that aren’t available in PyTorch natively. -One good example is Timm Schedulers. When using custom learning rate schedulers -relying on a different API from Native PyTorch ones, you should override the lr_scheduler_step() with your desired logic. -If you are using native PyTorch schedulers, there is no need to override this hook since Lightning will handle it automatically by default.

-
from timm.scheduler import TanhLRScheduler
-
-
-def configure_optimizers(self):
-    optimizer = ...
-    scheduler = TanhLRScheduler(optimizer, ...)
-    return [optimizer], [{"scheduler": scheduler, "interval": "epoch"}]
-
-
-def lr_scheduler_step(self, scheduler, optimizer_idx, metric):
-    scheduler.step(epoch=self.current_epoch)  # timm's scheduler need the epoch value
-
-
-
-
-

Configure Gradient Clipping

-

To configure custom gradient clipping, consider overriding -the configure_gradient_clipping() method. -Attributes gradient_clip_val and gradient_clip_algorithm from Trainer will be passed in the -respective arguments here and Lightning will handle gradient clipping for you. In case you want to set -different values for your arguments of your choice and let Lightning handle the gradient clipping, you can -use the inbuilt clip_gradients() method and pass -the arguments along with your optimizer.

-
-

Warning

-

Make sure to not override clip_gradients() -method. If you want to customize gradient clipping, consider using -configure_gradient_clipping() method.

-
-

For example, here we will apply gradient clipping only to the gradients associated with optimizer A.

-
def configure_gradient_clipping(self, optimizer, optimizer_idx, gradient_clip_val, gradient_clip_algorithm):
-    if optimizer_idx == 0:
-        # Lightning will handle the gradient clipping
-        self.clip_gradients(
-            optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm
-        )
-
-
-

Here we configure gradient clipping differently for optimizer B.

-
def configure_gradient_clipping(self, optimizer, optimizer_idx, gradient_clip_val, gradient_clip_algorithm):
-    if optimizer_idx == 0:
-        # Lightning will handle the gradient clipping
-        self.clip_gradients(
-            optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm
-        )
-    elif optimizer_idx == 1:
-        self.clip_gradients(
-            optimizer, gradient_clip_val=gradient_clip_val * 2, gradient_clip_algorithm=gradient_clip_algorithm
-        )
-
-
-
-
-

Total Stepping Batches

-

You can use built-in trainer property estimated_stepping_batches to compute -total number of stepping batches for the complete training. The property is computed considering gradient accumulation factor and -distributed setting into consideration so you don’t have to derive it manually. One good example where this can be helpful is while using -OneCycleLR scheduler, which requires pre-computed total_steps during initialization.

-
def configure_optimizers(self):
-    optimizer = ...
-    scheduler = torch.optim.lr_scheduler.OneCycleLR(
-        optimizer, max_lr=1e-3, total_steps=self.trainer.estimated_stepping_batches
-    )
-    return [optimizer], [scheduler]
-
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/precision.html b/docs/common/precision.html deleted file mode 100644 index 0b8d6b3..0000000 --- a/docs/common/precision.html +++ /dev/null @@ -1,731 +0,0 @@ - - - - - - - - - - - - - - N-Bit Precision — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/precision_basic.html b/docs/common/precision_basic.html deleted file mode 100644 index 209922e..0000000 --- a/docs/common/precision_basic.html +++ /dev/null @@ -1,776 +0,0 @@ - - - - - - - - - - - - - - N-Bit Precision (Basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • N-Bit Precision (Basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

N-Bit Precision (Basic)

-

Audience: Users looking to train models faster and consume less memory.

-
-

If you’re looking to run models faster or consume less memory, consider tweaking the precision settings of your models.

-

Lower precision, such as 16-bit floating-point, requires less memory and enables training and deploying larger models. -Higher precision, such as the 64-bit floating-point, can be used for highly sensitive use-cases.

-
-
-

16-bit Precision

-

Use 16-bit precision to cut your memory consumption in half so that you can train and deploy larger models. If your GPUs are [Tensor Core] GPUs, you can also get a ~3x speed improvement. Half precision can sometimes lead to unstable training.

-
Trainer(precision=16)
-
-
-
-
-
-

32-bit Precision

-

32-bit precision is the default used across all models and research. This precision is known to be stable in contrast to lower precision settings.

-
Trainer(precision=32)
-
-
-
-
-
-

64-bit Precision

-

For certain scientific computations, 64-bit precision enables more accurate models. However, doubling the precision from 32 to 64 bit also doubles the memory requirements.

-
Trainer(precision=64)
-
-
-
-

Note

-

Since in deep learning, memory is always a bottleneck, especially when dealing with a large volume of data and with limited resources. -It is recommended using single precision for better speed. Although you can still use it if you want for your particular use-case.

-
-
-
-
-

Precision support by accelerator

- - ------- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Precision with Accelerators

Precision

CPU

GPU

TPU

IPU

16

No

Yes

No

Yes

BFloat16

Yes

Yes

Yes

No

32

Yes

Yes

Yes

Yes

64

Yes

Yes

No

No

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/precision_expert.html b/docs/common/precision_expert.html deleted file mode 100644 index fd1322b..0000000 --- a/docs/common/precision_expert.html +++ /dev/null @@ -1,709 +0,0 @@ - - - - - - - - - - - - - - N-Bit Precision (Expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • N-Bit Precision (Expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

N-Bit Precision (Expert)

-

Audience: Researchers looking to integrate their new precision techniques into Lightning.

-
-

Precision Plugins

-

You can also customize and pass your own Precision Plugin by subclassing the PrecisionPlugin class.

-
    -
  • Perform pre and post backward/optimizer step operations such as scaling gradients.

  • -
  • Provide context managers for forward, training_step, etc.

  • -
-
class CustomPrecisionPlugin(PrecisionPlugin):
-    precision = 16
-
-    ...
-
-
-trainer = Trainer(plugins=[CustomPrecisionPlugin()])
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/precision_intermediate.html b/docs/common/precision_intermediate.html deleted file mode 100644 index 0fc0e78..0000000 --- a/docs/common/precision_intermediate.html +++ /dev/null @@ -1,800 +0,0 @@ - - - - - - - - - - - - - - N-Bit Precision (Intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • N-Bit Precision (Intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

N-Bit Precision (Intermediate)

-

Audience: Users looking to scale larger models or take advantage of optimized accelerators.

-
-
-

What is Mixed Precision?

-

PyTorch, like most deep learning frameworks, trains on 32-bit floating-point (FP32) arithmetic by default. However, many deep learning models do not require this to reach complete accuracy. By conducting -operations in half-precision format while keeping minimum information in single-precision to maintain as much information as possible in crucial areas of the network, mixed precision training delivers -significant computational speedup. Switching to mixed precision has resulted in considerable training speedups since the introduction of Tensor Cores in the Volta and Turing architectures. It combines -FP32 and lower-bit floating-points (such as FP16) to reduce memory footprint and increase performance during model training and evaluation. It accomplishes this by recognizing the steps that require -complete accuracy and employing a 32-bit floating-point for those steps only, while using a 16-bit floating-point for the rest. When compared to complete precision training, mixed precision training -delivers all of these benefits while ensuring that no task-specific accuracy is lost. [2].

-
-

Note

-

In some cases, it is essential to remain in FP32 for numerical stability, so keep this in mind when using mixed precision. -For example, when running scatter operations during the forward (such as torchpoint3d), computation must remain in FP32.

-
-
-

Warning

-

Do not cast anything to other dtypes manually using torch.autocast or tensor.half() when using native precision because -this can bring instability.

-
class LitModel(LightningModule):
-    def training_step(self, batch, batch_idx):
-        outs = self(batch)
-
-        a_float32 = torch.rand((8, 8), device=self.device, dtype=self.dtype)
-        b_float32 = torch.rand((8, 4), device=self.device, dtype=self.dtype)
-
-        # casting to float16 manually
-        with torch.autocast(device_type=self.device.type):
-            c_float16 = torch.mm(a_float32, b_float32)
-            target = self.layer(c_float16.flatten()[None])
-
-        # here outs is of type float32 and target is of type float16
-        loss = torch.mm(target @ outs).float()
-        return loss
-
-
-trainer = Trainer(accelerator="gpu", devices=1, precision=32)
-
-
-
-
-
-
-

FP16 Mixed Precision

-

In most cases, mixed precision uses FP16. Supported PyTorch operations automatically run in FP16, saving memory and improving throughput on the supported accelerators.

-
-

Note

-

When using TPUs, setting precision=16 will enable bfloat16, the only supported half precision type on TPUs.

-
-
Trainer(accelerator="gpu", devices=1, precision=16)
-
-
-
-

PyTorch Native

-

PyTorch 1.6 release introduced mixed precision functionality into their core as the AMP package, torch.cuda.amp. It is more flexible and intuitive compared to NVIDIA APEX. -Since computation happens in FP16, there is a chance of numerical instability during training. This is handled internally by a dynamic grad scaler which skips invalid steps and adjusts the scaler to ensure subsequent steps fall within a finite range. For more information see the autocast docs. -Lightning uses native amp by default with precision=16|"bf16". You can also set it using:

-
Trainer(precision=16, amp_backend="native")
-
-
-
-
-

NVIDIA APEX

-
-

Warning

-

We strongly recommend using the above native mixed precision rather than NVIDIA APEX unless you require more refined control.

-
-

NVIDIA APEX offers additional flexibility in setting mixed precision. This can be useful when trying out different precision configurations, such as keeping most of your weights in FP16 and running computation in FP16.

-
Trainer(accelerator="gpu", devices=1, amp_backend="apex", precision=16)
-
-
-

Set the NVIDIA optimization level via the trainer.

-
Trainer(accelerator="gpu", devices=1, amp_backend="apex", amp_level="O2", precision=16)
-
-
-
-
-
-
-

BFloat16 Mixed Precision

-
-

Warning

-

BFloat16 requires PyTorch 1.10 or later and is only supported with PyTorch Native AMP.

-

BFloat16 is also experimental and may not provide significant speedups or memory improvements, offering better numerical stability.

-

Do note for GPUs, the most significant benefits require Ampere based GPUs, such as A100s or 3090s.

-
-

BFloat16 Mixed precision is similar to FP16 mixed precision, however, it maintains more of the “dynamic range” that FP32 offers. This means it is able to improve numerical stability than FP16 mixed precision. For more information, see this TPU performance blogpost.

-

Under the hood, we use torch.autocast with the dtype set to bfloat16, with no gradient scaling.

-
Trainer(accelerator="gpu", devices=1, precision="bf16")
-
-
-

It is also possible to use BFloat16 mixed precision on the CPU, relying on MKLDNN under the hood.

-
Trainer(precision="bf16")
-
-
-
-
-
-

8-bit Optimizer

-

It is possible to further reduce the precision using third-party libraries like bitsandbytes. Although, -Lightning doesn’t support it out of the box yet but you can still use it by configuring it in your LightningModule and setting Trainer(precision=32).

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/progress_bar.html b/docs/common/progress_bar.html deleted file mode 100644 index 07b626d..0000000 --- a/docs/common/progress_bar.html +++ /dev/null @@ -1,817 +0,0 @@ - - - - - - - - - - - - - - Customize the progress bar — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Customize the progress bar
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Customize the progress bar

-

Lightning supports two different types of progress bars (tqdm and rich). TQDMProgressBar is used by default, -but you can override it by passing a custom TQDMProgressBar or RichProgressBar to the callbacks argument of the Trainer.

-

You could also use the ProgressBarBase class to implement your own progress bar.

-
-
-

TQDMProgressBar

-

The TQDMProgressBar uses the tqdm library internally and is the default progress bar used by Lightning. -It prints to stdout and shows up to four different bars:

-
    -
  • sanity check progress: the progress during the sanity check run

  • -
  • main progress: shows training + validation progress combined. It also accounts for multiple validation runs during training when val_check_interval is used.

  • -
  • validation progress: only visible during validation; shows total progress over all validation datasets.

  • -
  • test progress: only active when testing; shows total progress over all test datasets.

  • -
-

For infinite datasets, the progress bar never ends.

-

You can update refresh_rate (rate (number of batches) at which the progress bar get updated) for TQDMProgressBar by:

-
from pytorch_lightning.callbacks import TQDMProgressBar
-
-trainer = Trainer(callbacks=[TQDMProgressBar(refresh_rate=10)])
-
-
-

If you want to customize the default TQDMProgressBar used by Lightning, you can override -specific methods of the callback class and pass your custom implementation to the Trainer.

-
class LitProgressBar(TQDMProgressBar):
-    def init_validation_tqdm(self):
-        bar = super().init_validation_tqdm()
-        bar.set_description("running validation...")
-        return bar
-
-
-trainer = Trainer(callbacks=[LitProgressBar()])
-
-
-
-

See also

- -
-
-
-
-

RichProgressBar

-

Rich is a Python library for rich text and beautiful formatting in the terminal. -To use the RichProgressBar as your progress bar, first install the package:

-
pip install rich
-
-
-

Then configure the callback and pass it to the Trainer:

-
from pytorch_lightning.callbacks import RichProgressBar
-
-trainer = Trainer(callbacks=[RichProgressBar()])
-
-
-

Customize the theme for your RichProgressBar like this:

-
from pytorch_lightning.callbacks import RichProgressBar
-from pytorch_lightning.callbacks.progress.rich_progress import RichProgressBarTheme
-
-# create your own theme!
-progress_bar = RichProgressBar(
-    theme=RichProgressBarTheme(
-        description="green_yellow",
-        progress_bar="green1",
-        progress_bar_finished="green1",
-        progress_bar_pulse="#6206E0",
-        batch_progress="green_yellow",
-        time="grey82",
-        processing_speed="grey82",
-        metrics="grey82",
-    )
-)
-
-trainer = Trainer(callbacks=progress_bar)
-
-
-

You can customize the components used within RichProgressBar with ease by overriding the -configure_columns() method.

-
from rich.progress import TextColumn
-
-custom_column = TextColumn("[progress.description]Custom Rich Progress Bar!")
-
-
-class CustomRichProgressBar(RichProgressBar):
-    def configure_columns(self, trainer):
-        return [custom_column]
-
-
-progress_bar = CustomRichProgressBar()
-
-
-

If you wish for a new progress bar to be displayed at the end of every epoch, you should enable -RichProgressBar.leave by passing True

-
from pytorch_lightning.callbacks import RichProgressBar
-
-trainer = Trainer(callbacks=[RichProgressBar(leave=True)])
-
-
-
-

See also

-
    -
  • RichProgressBar docs.

  • -
  • RichModelSummary docs to customize the model summary table.

  • -
  • Rich library.

  • -
-
-
-

Note

-

Progress bar is automatically enabled with the Trainer, and to disable it, one should do this:

-
trainer = Trainer(enable_progress_bar=False)
-
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/remote_fs.html b/docs/common/remote_fs.html deleted file mode 100644 index a4ab5c6..0000000 --- a/docs/common/remote_fs.html +++ /dev/null @@ -1,739 +0,0 @@ - - - - - - - - - - - - - - Remote Filesystems — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Remote Filesystems
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Remote Filesystems

-

PyTorch Lightning enables working with data from a variety of filesystems, including local filesystems and several cloud storage providers such as -S3 on AWS, GCS on Google Cloud, -or ADL on Azure.

-

This applies to saving and writing checkpoints, as well as for logging. -Working with different filesystems can be accomplished by appending a protocol like “s3:/” to file paths for writing and reading data.

-
# `default_root_dir` is the default path used for logs and checkpoints
-trainer = Trainer(default_root_dir="s3://my_bucket/data/")
-trainer.fit(model)
-
-
-

You could pass custom paths to loggers for logging data.

-
from pytorch_lightning.loggers import TensorBoardLogger
-
-logger = TensorBoardLogger(save_dir="s3://my_bucket/logs/")
-
-trainer = Trainer(logger=logger)
-trainer.fit(model)
-
-
-

Additionally, you could also resume training with a checkpoint stored at a remote filesystem.

-
trainer = Trainer(default_root_dir=tmpdir, max_steps=3)
-trainer.fit(model, ckpt_path="s3://my_bucket/ckpts/classifier.ckpt")
-
-
-

PyTorch Lightning uses fsspec internally to handle all filesystem operations.

-

The most common filesystems supported by Lightning are:

-
    -
  • Local filesystem: file:// - It’s the default and doesn’t need any protocol to be used. It’s installed by default in Lightning.

  • -
  • Amazon S3: s3:// - Amazon S3 remote binary store, using the library s3fs. Run pip install fsspec[s3] to install it.

  • -
  • Google Cloud Storage: gcs:// or gs:// - Google Cloud Storage, using gcsfs. Run pip install fsspec[gcs] to install it.

  • -
  • Microsoft Azure Storage: adl://, abfs:// or az:// - Microsoft Azure Storage, using adlfs. Run pip install fsspec[adl] to install it.

  • -
  • Hadoop File System: hdfs:// - Hadoop Distributed File System. This uses PyArrow as the backend. Run pip install fsspec[hdfs] to install it.

  • -
-

You could learn more about the available filesystems with:

-
from fsspec.registry import known_implementations
-
-print(known_implementations)
-
-
-

You could also look into CheckpointIO Plugin for more details on how to customize saving and loading checkpoints.

-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common/trainer.html b/docs/common/trainer.html deleted file mode 100644 index 105919f..0000000 --- a/docs/common/trainer.html +++ /dev/null @@ -1,2689 +0,0 @@ - - - - - - - - - - - - - - Trainer — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Trainer

-

Once you’ve organized your PyTorch code into a LightningModule, -the Trainer automates everything else.

-
-

-
-

This abstraction achieves the following:

-
    -
  1. You maintain control over all aspects via PyTorch code without an added abstraction.

  2. -
  3. The trainer uses best practices embedded by contributors and users -from top AI labs such as Facebook AI Research, NYU, MIT, Stanford, etc…

  4. -
  5. The trainer allows overriding any key part that you don’t want automated.

  6. -
-
-

-
-
-
-

Basic use

-

This is the basic use of the trainer:

-
model = MyLightningModule()
-
-trainer = Trainer()
-trainer.fit(model, train_dataloader, val_dataloader)
-
-
-
-
-
-

Under the hood

-

Under the hood, the Lightning Trainer handles the training loop details for you, some examples include:

-
    -
  • Automatically enabling/disabling grads

  • -
  • Running the training, validation and test dataloaders

  • -
  • Calling the Callbacks at the appropriate times

  • -
  • Putting batches and computations on the correct devices

  • -
-

Here’s the pseudocode for what the trainer does under the hood (showing the train loop only)

-
# put model in train mode
-model.train()
-torch.set_grad_enabled(True)
-
-losses = []
-for batch in train_dataloader:
-    # calls hooks like this one
-    on_train_batch_start()
-
-    # train step
-    loss = training_step(batch)
-
-    # clear gradients
-    optimizer.zero_grad()
-
-    # backward
-    loss.backward()
-
-    # update parameters
-    optimizer.step()
-
-    losses.append(loss)
-
-
-
-
-
-

Trainer in Python scripts

-

In Python scripts, it’s recommended you use a main function to call the Trainer.

-
from argparse import ArgumentParser
-
-
-def main(hparams):
-    model = LightningModule()
-    trainer = Trainer(accelerator=hparams.accelerator, devices=hparams.devices)
-    trainer.fit(model)
-
-
-if __name__ == "__main__":
-    parser = ArgumentParser()
-    parser.add_argument("--accelerator", default=None)
-    parser.add_argument("--devices", default=None)
-    args = parser.parse_args()
-
-    main(args)
-
-
-

So you can run it like so:

-
python main.py --accelerator 'gpu' --devices 2
-
-
-
-

Note

-

Pro-tip: You don’t need to define all flags manually. Lightning can add them automatically

-
-
from argparse import ArgumentParser
-
-
-def main(args):
-    model = LightningModule()
-    trainer = Trainer.from_argparse_args(args)
-    trainer.fit(model)
-
-
-if __name__ == "__main__":
-    parser = ArgumentParser()
-    parser = Trainer.add_argparse_args(parser)
-    args = parser.parse_args()
-
-    main(args)
-
-
-

So you can run it like so:

-
python main.py --accelerator 'gpu' --devices 2 --max_steps 10 --limit_train_batches 10 --any_trainer_arg x
-
-
-
-

Note

-

If you want to stop a training run early, you can press “Ctrl + C” on your keyboard. -The trainer will catch the KeyboardInterrupt and attempt a graceful shutdown, including -running accelerator callback on_train_end to clean up memory. The trainer object will also set -an attribute interrupted to True in such cases. If you have a callback which shuts down compute -resources, for example, you can conditionally run the shutdown logic for only uninterrupted runs.

-
-
-
-
-

Validation

-

You can perform an evaluation epoch over the validation set, outside of the training loop, -using validate(). This might be -useful if you want to collect new metrics from a model right at its initialization -or after it has already been trained.

-
trainer.validate(dataloaders=val_dataloaders)
-
-
-
-
-
-

Testing

-

Once you’re done training, feel free to run the test set! -(Only right before publishing your paper or pushing to production)

-
trainer.test(dataloaders=test_dataloaders)
-
-
-
-
-
-

Reproducibility

-

To ensure full reproducibility from run to run you need to set seeds for pseudo-random generators, -and set deterministic flag in Trainer.

-

Example:

-
from pytorch_lightning import Trainer, seed_everything
-
-seed_everything(42, workers=True)
-# sets seeds for numpy, torch and python.random.
-model = Model()
-trainer = Trainer(deterministic=True)
-
-
-

By setting workers=True in seed_everything(), Lightning derives -unique seeds across all dataloader workers and processes for torch, numpy and stdlib -random number generators. When turned on, it ensures that e.g. data augmentations are not repeated across workers.

-
-
-
-

Trainer flags

-
-

accelerator

-

Supports passing different accelerator types ("cpu", "gpu", "tpu", "ipu", "auto") -as well as custom accelerator instances.

-
# CPU accelerator
-trainer = Trainer(accelerator="cpu")
-
-# Training with GPU Accelerator using 2 GPUs
-trainer = Trainer(devices=2, accelerator="gpu")
-
-# Training with TPU Accelerator using 8 tpu cores
-trainer = Trainer(devices=8, accelerator="tpu")
-
-# Training with GPU Accelerator using the DistributedDataParallel strategy
-trainer = Trainer(devices=4, accelerator="gpu", strategy="ddp")
-
-
-
-

Note

-

The "auto" option recognizes the machine you are on, and selects the respective Accelerator.

-
-
# If your machine has GPUs, it will use the GPU Accelerator for training
-trainer = Trainer(devices=2, accelerator="auto")
-
-
-

You can also modify hardware behavior by subclassing an existing accelerator to adjust for your needs.

-

Example:

-
class MyOwnAcc(CPUAccelerator):
-    ...
-
-Trainer(accelerator=MyOwnAcc())
-
-
-
-

Note

-

If the devices flag is not defined, it will assume devices to be "auto" and fetch the auto_device_count -from the accelerator.

-
# This is part of the built-in `GPUAccelerator`
-class GPUAccelerator(Accelerator):
-    """Accelerator for GPU devices."""
-
-    @staticmethod
-    def auto_device_count() -> int:
-        """Get the devices when set to auto."""
-        return torch.cuda.device_count()
-
-
-# Training with GPU Accelerator using total number of gpus available on the system
-Trainer(accelerator="gpu")
-
-
-
-
-

Warning

-

Passing training strategies (e.g., "ddp") to accelerator has been deprecated in v1.5.0 -and will be removed in v1.7.0. Please use the strategy argument instead.

-
-
-
-

accumulate_grad_batches

-
-

-
-

Accumulates grads every k batches or as set up in the dict. -Trainer also calls optimizer.step() for the last indivisible step number.

-
# default used by the Trainer (no accumulation)
-trainer = Trainer(accumulate_grad_batches=1)
-
-
-

Example:

-
# accumulate every 4 batches (effective batch size is batch*4)
-trainer = Trainer(accumulate_grad_batches=4)
-
-# no accumulation for epochs 1-4. accumulate 3 for epochs 5-10. accumulate 20 after that
-trainer = Trainer(accumulate_grad_batches={5: 3, 10: 20})
-
-
-
-
-

amp_backend

-
-

-
-

Use PyTorch AMP (‘native’), or NVIDIA apex (‘apex’).

-
# using PyTorch built-in AMP, default used by the Trainer
-trainer = Trainer(amp_backend="native")
-
-# using NVIDIA Apex
-trainer = Trainer(amp_backend="apex")
-
-
-
-
-

amp_level

-
-

-
-

The optimization level to use (O1, O2, etc…) -for 16-bit GPU precision (using NVIDIA apex under the hood).

-

Check NVIDIA apex docs for level

-

Example:

-
# default used by the Trainer
-trainer = Trainer(amp_level='O2')
-
-
-
-
-

auto_scale_batch_size

-
-

-
-

Automatically tries to find the largest batch size that fits into memory, -before any training.

-
# default used by the Trainer (no scaling of batch size)
-trainer = Trainer(auto_scale_batch_size=None)
-
-# run batch size scaling, result overrides hparams.batch_size
-trainer = Trainer(auto_scale_batch_size="binsearch")
-
-# call tune to find the batch size
-trainer.tune(model)
-
-
-
-
-

auto_select_gpus

-
-

-
-

If enabled and devices is an integer, pick available GPUs automatically. -This is especially useful when GPUs are configured to be in “exclusive mode”, -such that only one process at a time can access them.

-

Example:

-
# no auto selection (picks first 2 GPUs on system, may fail if other process is occupying)
-trainer = Trainer(accelerator="gpu", devices=2, auto_select_gpus=False)
-
-# enable auto selection (will find two available GPUs on system)
-trainer = Trainer(accelerator="gpu", devices=2, auto_select_gpus=True)
-
-# specifies all GPUs regardless of its availability
-Trainer(accelerator="gpu", devices=-1, auto_select_gpus=False)
-
-# specifies all available GPUs (if only one GPU is not occupied, uses one gpu)
-Trainer(accelerator="gpu", devices=-1, auto_select_gpus=True)
-
-
-
-
-

auto_lr_find

-
-

-
-

Runs a learning rate finder algorithm (see this paper) -when calling trainer.tune(), to find optimal initial learning rate.

-
# default used by the Trainer (no learning rate finder)
-trainer = Trainer(auto_lr_find=False)
-
-
-

Example:

-
# run learning rate finder, results override hparams.learning_rate
-trainer = Trainer(auto_lr_find=True)
-
-# call tune to find the lr
-trainer.tune(model)
-
-
-

Example:

-
# run learning rate finder, results override hparams.my_lr_arg
-trainer = Trainer(auto_lr_find='my_lr_arg')
-
-# call tune to find the lr
-trainer.tune(model)
-
-
-
-

Note

-

See the learning rate finder guide.

-
-
-
-

benchmark

-
-

-
-

Defaults to True if deterministic is not set. -This flag sets the torch.backends.cudnn.benchmark flag. You can read more about its impact -here

-

This is likely to increase the speed of your system if your input sizes don’t change. However, if they do, then it -might make your system slower. The CUDNN auto-tuner will try to find the best algorithm for the hardware when a new -input size is encountered. Read more about it here.

-

Example:

-
# defaults to True if not deterministic (which is False by default)
-trainer = Trainer()
-
-# you can overwrite the value
-trainer = Trainer(benchmark=False)
-
-
-
-
-

deterministic

-
-

-
-

This flag sets the torch.backends.cudnn.deterministic flag. -Might make your system slower, but ensures reproducibility. -Also sets $HOROVOD_FUSION_THRESHOLD=0.

-

For more info check PyTorch docs.

-

Example:

-
# default used by the Trainer
-trainer = Trainer(deterministic=False)
-
-
-
-
-

callbacks

-
-

-
-

Add a list of Callback. Callbacks run sequentially in the order defined here -with the exception of ModelCheckpoint callbacks which run -after all others to ensure all states are saved to the checkpoints.

-
# a list of callbacks
-callbacks = [PrintCallback()]
-trainer = Trainer(callbacks=callbacks)
-
-
-

Example:

-
from pytorch_lightning.callbacks import Callback
-
-class PrintCallback(Callback):
-    def on_train_start(self, trainer, pl_module):
-        print("Training is started!")
-    def on_train_end(self, trainer, pl_module):
-        print("Training is done.")
-
-
-

Model-specific callbacks can also be added inside the LightningModule through -configure_callbacks(). -Callbacks returned in this hook will extend the list initially given to the Trainer argument, and replace -the trainer callbacks should there be two or more of the same type. -ModelCheckpoint callbacks always run last.

-
-
-

check_val_every_n_epoch

-
-

-
-

Check val every n train epochs.

-

Example:

-
# default used by the Trainer
-trainer = Trainer(check_val_every_n_epoch=1)
-
-# run val loop every 10 training epochs
-trainer = Trainer(check_val_every_n_epoch=10)
-
-
-
-
-

checkpoint_callback

-
-

Warning

-

checkpoint_callback has been deprecated in v1.5 and will be removed in v1.7. -To disable checkpointing, pass enable_checkpointing = False to the Trainer instead.

-
-
-
-

default_root_dir

-
-

-
-

Default path for logs and weights when no logger or -pytorch_lightning.callbacks.ModelCheckpoint callback passed. On -certain clusters you might want to separate where logs and checkpoints are -stored. If you don’t then use this argument for convenience. Paths can be local -paths or remote paths such as s3://bucket/path or ‘hdfs://path/’. Credentials -will need to be set up to use remote filepaths.

-
# default used by the Trainer
-trainer = Trainer(default_root_dir=os.getcwd())
-
-
-
-
-

devices

-

Number of devices to train on (int), which devices to train on (list or str), or "auto". -It will be mapped to either gpus, tpu_cores, num_processes or ipus, -based on the accelerator type ("cpu", "gpu", "tpu", "ipu", "auto").

-
# Training with CPU Accelerator using 2 processes
-trainer = Trainer(devices=2, accelerator="cpu")
-
-# Training with GPU Accelerator using GPUs 1 and 3
-trainer = Trainer(devices=[1, 3], accelerator="gpu")
-
-# Training with TPU Accelerator using 8 tpu cores
-trainer = Trainer(devices=8, accelerator="tpu")
-
-
-
-

Tip

-

The "auto" option recognizes the devices to train on, depending on the Accelerator being used.

-
-
# If your machine has GPUs, it will use all the available GPUs for training
-trainer = Trainer(devices="auto", accelerator="auto")
-
-# Training with CPU Accelerator using 1 process
-trainer = Trainer(devices="auto", accelerator="cpu")
-
-# Training with TPU Accelerator using 8 tpu cores
-trainer = Trainer(devices="auto", accelerator="tpu")
-
-# Training with IPU Accelerator using 4 ipus
-trainer = Trainer(devices="auto", accelerator="ipu")
-
-
-
-

Note

-

If the devices flag is not defined, it will assume devices to be "auto" and fetch the auto_device_count -from the accelerator.

-
# This is part of the built-in `GPUAccelerator`
-class GPUAccelerator(Accelerator):
-    """Accelerator for GPU devices."""
-
-    @staticmethod
-    def auto_device_count() -> int:
-        """Get the devices when set to auto."""
-        return torch.cuda.device_count()
-
-
-# Training with GPU Accelerator using total number of gpus available on the system
-Trainer(accelerator="gpu")
-
-
-
-
-
-

enable_checkpointing

-
-

-
-

By default Lightning saves a checkpoint for you in your current working directory, with the state of your last training epoch, -Checkpoints capture the exact value of all parameters used by a model. -To disable automatic checkpointing, set this to False.

-
# default used by Trainer, saves the most recent model to a single checkpoint after each epoch
-trainer = Trainer(enable_checkpointing=True)
-
-# turn off automatic checkpointing
-trainer = Trainer(enable_checkpointing=False)
-
-
-

You can override the default behavior by initializing the ModelCheckpoint -callback, and adding it to the callbacks list. -See Saving and Loading Checkpoints for how to customize checkpointing.

-
from pytorch_lightning.callbacks import ModelCheckpoint
-
-# Init ModelCheckpoint callback, monitoring 'val_loss'
-checkpoint_callback = ModelCheckpoint(monitor="val_loss")
-
-# Add your callback to the callbacks list
-trainer = Trainer(callbacks=[checkpoint_callback])
-
-
-
-
-

fast_dev_run

-
-

-
-

Runs n if set to n (int) else 1 if set to True batch(es) of train, val and test -to find any bugs (ie: a sort of unit test).

-

Under the hood the pseudocode looks like this when running fast_dev_run with a single batch:

-
# loading
-__init__()
-prepare_data
-
-# test training step
-training_batch = next(train_dataloader)
-training_step(training_batch)
-
-# test val step
-val_batch = next(val_dataloader)
-out = validation_step(val_batch)
-validation_epoch_end([out])
-
-
-
# default used by the Trainer
-trainer = Trainer(fast_dev_run=False)
-
-# runs 1 train, val, test batch and program ends
-trainer = Trainer(fast_dev_run=True)
-
-# runs 7 train, val, test batches and program ends
-trainer = Trainer(fast_dev_run=7)
-
-
-
-

Note

-

This argument is a bit different from limit_train/val/test_batches. Setting this argument will -disable tuner, checkpoint callbacks, early stopping callbacks, loggers and logger callbacks like -LearningRateLogger and runs for only 1 epoch. This must be used only for debugging purposes. -limit_train/val/test_batches only limits the number of batches and won’t disable anything.

-
-
-
-

flush_logs_every_n_steps

-
-

Warning

-

flush_logs_every_n_steps has been deprecated in v1.5 and will be removed in v1.7. -Please configure flushing directly in the logger instead.

-
-
-

-
-

Writes logs to disk this often.

-
# default used by the Trainer
-trainer = Trainer(flush_logs_every_n_steps=100)
-
-
-
-
See Also:
-
-
-
-
-

gpus

-
-

Warning

-

gpus=x has been deprecated in v1.7 and will be removed in v2.0. -Please use accelerator='gpu' and devices=x instead.

-
-
-

-
-
    -
  • Number of GPUs to train on (int)

  • -
  • or which GPUs to train on (list)

  • -
  • can handle strings

  • -
-
# default used by the Trainer (ie: train on CPU)
-trainer = Trainer(gpus=None)
-
-# equivalent
-trainer = Trainer(gpus=0)
-
-
-

Example:

-
# int: train on 2 gpus
-trainer = Trainer(gpus=2)
-
-# list: train on GPUs 1, 4 (by bus ordering)
-trainer = Trainer(gpus=[1, 4])
-trainer = Trainer(gpus='1, 4') # equivalent
-
-# -1: train on all gpus
-trainer = Trainer(gpus=-1)
-trainer = Trainer(gpus='-1') # equivalent
-
-# combine with num_nodes to train on multiple GPUs across nodes
-# uses 8 gpus in total
-trainer = Trainer(gpus=2, num_nodes=4)
-
-# train only on GPUs 1 and 4 across nodes
-trainer = Trainer(gpus=[1, 4], num_nodes=4)
-
-
-
-
See Also:
-
-
-
-
-

gradient_clip_val

-
-

-
-

Gradient clipping value

-
    -
  • 0 means don’t clip.

  • -
-
# default used by the Trainer
-trainer = Trainer(gradient_clip_val=0.0)
-
-
-
-
-

limit_train_batches

-
-

-
-

How much of training dataset to check. -Useful when debugging or testing something that happens at the end of an epoch.

-
# default used by the Trainer
-trainer = Trainer(limit_train_batches=1.0)
-
-
-

Example:

-
# default used by the Trainer
-trainer = Trainer(limit_train_batches=1.0)
-
-# run through only 25% of the training set each epoch
-trainer = Trainer(limit_train_batches=0.25)
-
-# run through only 10 batches of the training set each epoch
-trainer = Trainer(limit_train_batches=10)
-
-
-
-
-

limit_test_batches

-
-

-
-

How much of test dataset to check.

-
# default used by the Trainer
-trainer = Trainer(limit_test_batches=1.0)
-
-# run through only 25% of the test set each epoch
-trainer = Trainer(limit_test_batches=0.25)
-
-# run for only 10 batches
-trainer = Trainer(limit_test_batches=10)
-
-
-

In the case of multiple test dataloaders, the limit applies to each dataloader individually.

-
-
-

limit_val_batches

-
-

-
-

How much of validation dataset to check. -Useful when debugging or testing something that happens at the end of an epoch.

-
# default used by the Trainer
-trainer = Trainer(limit_val_batches=1.0)
-
-# run through only 25% of the validation set each epoch
-trainer = Trainer(limit_val_batches=0.25)
-
-# run for only 10 batches
-trainer = Trainer(limit_val_batches=10)
-
-
-

In the case of multiple validation dataloaders, the limit applies to each dataloader individually.

-
-
-

log_every_n_steps

-
-

-
-

How often to add logging rows (does not write to disk)

-
# default used by the Trainer
-trainer = Trainer(log_every_n_steps=50)
-
-
-
-
See Also:
-
-
-
-
-

logger

-
-

-
-

Logger (or iterable collection of loggers) for experiment tracking. A True value uses the default TensorBoardLogger shown below. False will disable logging.

-
from pytorch_lightning.loggers import TensorBoardLogger
-
-# default logger used by trainer
-logger = TensorBoardLogger(save_dir=os.getcwd(), version=1, name="lightning_logs")
-Trainer(logger=logger)
-
-
-
-
-

max_epochs

-
-

-
-

Stop training once this number of epochs is reached

-
# default used by the Trainer
-trainer = Trainer(max_epochs=1000)
-
-
-

If both max_epochs and max_steps aren’t specified, max_epochs will default to 1000. -To enable infinite training, set max_epochs = -1.

-
-
-

min_epochs

-
-

-
-

Force training for at least these many epochs

-
# default used by the Trainer
-trainer = Trainer(min_epochs=1)
-
-
-
-
-

max_steps

-
-

-
-

Stop training after this number of global steps. -Training will stop if max_steps or max_epochs have reached (earliest).

-
# Default (disabled)
-trainer = Trainer(max_steps=None)
-
-# Stop after 100 steps
-trainer = Trainer(max_steps=100)
-
-
-

If max_steps is not specified, max_epochs will be used instead (and max_epochs defaults to -1000 if max_epochs is not specified). To disable this default, set max_steps = -1.

-
-
-

min_steps

-
-

-
-

Force training for at least this number of global steps. -Trainer will train model for at least min_steps or min_epochs (latest).

-
# Default (disabled)
-trainer = Trainer(min_steps=None)
-
-# Run at least for 100 steps (disable min_epochs)
-trainer = Trainer(min_steps=100, min_epochs=0)
-
-
-
-
-

max_time

-

Set the maximum amount of time for training. Training will get interrupted mid-epoch. -For customizable options use the Timer callback.

-
# Default (disabled)
-trainer = Trainer(max_time=None)
-
-# Stop after 12 hours of training or when reaching 10 epochs (string)
-trainer = Trainer(max_time="00:12:00:00", max_epochs=10)
-
-# Stop after 1 day and 5 hours (dict)
-trainer = Trainer(max_time={"days": 1, "hours": 5})
-
-
-

In case max_time is used together with min_steps or min_epochs, the min_* requirement -always has precedence.

-
-
-

num_nodes

-
-

-
-

Number of GPU nodes for distributed training.

-
# default used by the Trainer
-trainer = Trainer(num_nodes=1)
-
-# to train on 8 nodes
-trainer = Trainer(num_nodes=8)
-
-
-
-
-

num_processes

-
-

Warning

-

num_processes=x has been deprecated in v1.7 and will be removed in v2.0. -Please use accelerator='cpu' and devices=x instead.

-
-
-

-
-

Number of processes to train with. Automatically set to the number of GPUs -when using strategy="ddp". Set to a number greater than 1 when -using accelerator="cpu" and strategy="ddp" to mimic distributed training on a -machine without GPUs. This is useful for debugging, but will not provide -any speedup, since single-process Torch already makes efficient use of multiple -CPUs. While it would typically spawns subprocesses for training, setting -num_nodes > 1 and keeping num_processes = 1 runs training in the main -process.

-
# Simulate DDP for debugging on your GPU-less laptop
-trainer = Trainer(accelerator="cpu", strategy="ddp", num_processes=2)
-
-
-
-
-

num_sanity_val_steps

-
-

-
-

Sanity check runs n batches of val before starting the training routine. -This catches any bugs in your validation without having to wait for the first validation check. -The Trainer uses 2 steps by default. Turn it off or modify it here.

-
# default used by the Trainer
-trainer = Trainer(num_sanity_val_steps=2)
-
-# turn it off
-trainer = Trainer(num_sanity_val_steps=0)
-
-# check all validation data
-trainer = Trainer(num_sanity_val_steps=-1)
-
-
-

This option will reset the validation dataloader unless num_sanity_val_steps=0.

-
-
-

overfit_batches

-
-

-
-

Uses this much data of the training & validation set. -If the training & validation dataloaders have shuffle=True, Lightning will automatically disable it.

-

Useful for quickly debugging or trying to overfit on purpose.

-
# default used by the Trainer
-trainer = Trainer(overfit_batches=0.0)
-
-# use only 1% of the train & val set
-trainer = Trainer(overfit_batches=0.01)
-
-# overfit on 10 of the same batches
-trainer = Trainer(overfit_batches=10)
-
-
-
-
-

plugins

-
-

-
-

Plugins allow you to connect arbitrary backends, precision libraries, clusters etc. For example:

- -

To define your own behavior, subclass the relevant class and pass it in. Here’s an example linking up your own -ClusterEnvironment.

-
from pytorch_lightning.plugins.environments import ClusterEnvironment
-
-
-class MyCluster(ClusterEnvironment):
-    def main_address(self):
-        return your_main_address
-
-    def main_port(self):
-        return your_main_port
-
-    def world_size(self):
-        return the_world_size
-
-
-trainer = Trainer(plugins=[MyCluster()], ...)
-
-
-
-
-

precision

-
-

-
-

Lightning supports either double (64), float (32), bfloat16 (bf16), or half (16) precision training.

-

Half precision, or mixed precision, is the combined use of 32 and 16 bit floating points to reduce memory footprint during model training. This can result in improved performance, achieving +3X speedups on modern GPUs.

-
# default used by the Trainer
-trainer = Trainer(precision=32)
-
-# 16-bit precision
-trainer = Trainer(precision=16, accelerator="gpu", devices=1)  # works only on CUDA
-
-# bfloat16 precision
-trainer = Trainer(precision="bf16")
-
-# 64-bit precision
-trainer = Trainer(precision=64)
-
-
-
-

Note

-

When running on TPUs, torch.bfloat16 will be used but tensor printing will still show torch.float32.

-
- -
-
-

process_position

-
-

Warning

-

process_position has been deprecated in v1.5 and will be removed in v1.7. -Please pass TQDMProgressBar with process_position -directly to the Trainer’s callbacks argument instead.

-
-
-

-
-

Orders the progress bar. Useful when running multiple trainers on the same node.

-
# default used by the Trainer
-trainer = Trainer(process_position=0)
-
-
-
-

Note

-

This argument is ignored if a custom callback is passed to callbacks.

-
-
-
-

profiler

-
-

-
-

To profile individual steps during training and assist in identifying bottlenecks.

-

See the profiler documentation. for more details.

-
from pytorch_lightning.profiler import SimpleProfiler, AdvancedProfiler
-
-# default used by the Trainer
-trainer = Trainer(profiler=None)
-
-# to profile standard training events, equivalent to `profiler=SimpleProfiler()`
-trainer = Trainer(profiler="simple")
-
-# advanced profiler for function-level stats, equivalent to `profiler=AdvancedProfiler()`
-trainer = Trainer(profiler="advanced")
-
-
-
-
-

enable_progress_bar

-

Whether to enable or disable the progress bar. Defaults to True.

-
# default used by the Trainer
-trainer = Trainer(enable_progress_bar=True)
-
-# disable progress bar
-trainer = Trainer(enable_progress_bar=False)
-
-
-
-
-

reload_dataloaders_every_n_epochs

-
-

-
-

Set to a positive integer to reload dataloaders every n epochs.

-
# if 0 (default)
-train_loader = model.train_dataloader()
-for epoch in epochs:
-    for batch in train_loader:
-        ...
-
-# if a positive integer
-for epoch in epochs:
-    if not epoch % reload_dataloaders_every_n_epochs:
-        train_loader = model.train_dataloader()
-    for batch in train_loader:
-        ...
-
-
-
-
-

replace_sampler_ddp

-
-

-
-

Enables auto adding of DistributedSampler. In PyTorch, you must use it in -distributed settings such as TPUs or multi-node. The sampler makes sure each GPU sees the appropriate part of your data. -By default it will add shuffle=True for train sampler and shuffle=False for val/test sampler. -If you want to customize it, you can set replace_sampler_ddp=False and add your own distributed sampler. -If replace_sampler_ddp=True and a distributed sampler was already added, -Lightning will not replace the existing one.

-
# default used by the Trainer
-trainer = Trainer(replace_sampler_ddp=True)
-
-
-

By setting to False, you have to add your own distributed sampler:

-
# in your LightningModule or LightningDataModule
-def train_dataloader(self):
-    # default used by the Trainer
-    sampler = torch.utils.data.distributed.DistributedSampler(dataset, shuffle=True)
-    dataloader = DataLoader(dataset, batch_size=32, sampler=sampler)
-    return dataloader
-
-
-
-

Note

-

For iterable datasets, we don’t do this automatically.

-
-
-
-

resume_from_checkpoint

-
-

Warning

-

resume_from_checkpoint is deprecated in v1.5 and will be removed in v2.0. -Please pass trainer.fit(ckpt_path="some/path/to/my_checkpoint.ckpt") instead.

-
-
-

-
-

To resume training from a specific checkpoint pass in the path here. If resuming from a mid-epoch -checkpoint, training will start from the beginning of the next epoch.

-
# default used by the Trainer
-trainer = Trainer(resume_from_checkpoint=None)
-
-# resume from a specific checkpoint
-trainer = Trainer(resume_from_checkpoint="some/path/to/my_checkpoint.ckpt")
-
-
-
-
-

strategy

-

Supports passing different training strategies with aliases (ddp, ddp_spawn, etc) as well as custom strategies.

-
# Training with the DistributedDataParallel strategy on 4 GPUs
-trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4)
-
-# Training with the DDP Spawn strategy using 4 cpu processes
-trainer = Trainer(strategy="ddp_spawn", accelerator="cpu", devices=4)
-
-
-
-

Note

-

Additionally, you can pass your custom strategy to the strategy argument.

-
-
from pytorch_lightning.strategies import DDPStrategy
-
-
-class CustomDDPStrategy(DDPStrategy):
-    def configure_ddp(self):
-        self._model = MyCustomDistributedDataParallel(
-            self.model,
-            device_ids=...,
-        )
-
-
-trainer = Trainer(strategy=CustomDDPStrategy(), accelerator="gpu", devices=2)
-
-
-
-
See Also:
-
-
-
-
-

sync_batchnorm

-
-

-
-

Enable synchronization between batchnorm layers across all GPUs.

-
trainer = Trainer(sync_batchnorm=True)
-
-
-
-
-

track_grad_norm

-
-

-
-
    -
  • no tracking (-1)

  • -
  • Otherwise tracks that norm (2 for 2-norm)

  • -
-
# default used by the Trainer
-trainer = Trainer(track_grad_norm=-1)
-
-# track the 2-norm
-trainer = Trainer(track_grad_norm=2)
-
-
-
-
-

tpu_cores

-
-

Warning

-

tpu_cores=x has been deprecated in v1.7 and will be removed in v2.0. -Please use accelerator='tpu' and devices=x instead.

-
-
-

-
-
    -
  • How many TPU cores to train on (1 or 8).

  • -
  • Which TPU core to train on [1-8]

  • -
-

A single TPU v2 or v3 has 8 cores. A TPU pod has -up to 2048 cores. A slice of a POD means you get as many cores -as you request.

-

Your effective batch size is batch_size * total tpu cores.

-

This parameter can be either 1 or 8.

-

Example:

-
# your_trainer_file.py
-
-# default used by the Trainer (ie: train on CPU)
-trainer = Trainer(tpu_cores=None)
-
-# int: train on a single core
-trainer = Trainer(tpu_cores=1)
-
-# list: train on a single selected core
-trainer = Trainer(tpu_cores=[2])
-
-# int: train on all cores few cores
-trainer = Trainer(tpu_cores=8)
-
-# for 8+ cores must submit via xla script with
-# a max of 8 cores specified. The XLA script
-# will duplicate script onto each TPU in the POD
-trainer = Trainer(tpu_cores=8)
-
-
-

To train on more than 8 cores (ie: a POD), -submit this script using the xla_dist script.

-

Example:

-
python -m torch_xla.distributed.xla_dist
---tpu=$TPU_POD_NAME
---conda-env=torch-xla-nightly
---env=XLA_USE_BF16=1
--- python your_trainer_file.py
-
-
-
-
-

val_check_interval

-
-

-
-

How often within one training epoch to check the validation set. -Can specify as float or int.

-
    -
  • pass a float in the range [0.0, 1.0] to check after a fraction of the training epoch.

  • -
  • pass an int to check after a fixed number of training batches.

  • -
-
# default used by the Trainer
-trainer = Trainer(val_check_interval=1.0)
-
-# check validation set 4 times during a training epoch
-trainer = Trainer(val_check_interval=0.25)
-
-# check validation set every 1000 training batches
-# use this when using iterableDataset and your dataset has no length
-# (ie: production cases with streaming data)
-trainer = Trainer(val_check_interval=1000)
-
-
-
# Here is the computation to estimate the total number of batches seen within an epoch.
-
-# Find the total number of train batches
-total_train_batches = total_train_samples // (train_batch_size * world_size)
-
-# Compute how many times we will call validation during the training loop
-val_check_batch = max(1, int(total_train_batches * val_check_interval))
-val_checks_per_epoch = total_train_batches / val_check_batch
-
-# Find the total number of validation batches
-total_val_batches = total_val_samples // (val_batch_size * world_size)
-
-# Total number of batches run
-total_fit_batches = total_train_batches + total_val_batches
-
-
-
-
-

weights_save_path

-
-

Warning

-

weights_save_path has been deprecated in v1.6 and will be removed in v1.8. Please pass -dirpath directly to the ModelCheckpoint -callback.

-
-
-

-
-

Directory of where to save weights if specified.

-
# default used by the Trainer
-trainer = Trainer(weights_save_path=os.getcwd())
-
-# save to your custom path
-trainer = Trainer(weights_save_path="my/path")
-
-
-

Example:

-
# if checkpoint callback used, then overrides the weights path
-# **NOTE: this saves weights to some/path NOT my/path
-checkpoint = ModelCheckpoint(dirpath='some/path')
-trainer = Trainer(
-    callbacks=[checkpoint],
-    weights_save_path='my/path'
-)
-
-
-
-
-

weights_summary

-
-

Warning

-

weights_summary is deprecated in v1.5 and will be removed in v1.7. Please pass ModelSummary -directly to the Trainer’s callbacks argument instead. To disable the model summary, -pass enable_model_summary = False to the Trainer.

-
-
-

-
-

Prints a summary of the weights when training begins. -Options: ‘full’, ‘top’, None.

-
# default used by the Trainer (ie: print summary of top level modules)
-trainer = Trainer(weights_summary="top")
-
-# print full summary of all modules and submodules
-trainer = Trainer(weights_summary="full")
-
-# don't print a summary
-trainer = Trainer(weights_summary=None)
-
-
-
-
-

enable_model_summary

-

Whether to enable or disable the model summarization. Defaults to True.

-
# default used by the Trainer
-trainer = Trainer(enable_model_summary=True)
-
-# disable summarization
-trainer = Trainer(enable_model_summary=False)
-
-# enable custom summarization
-from pytorch_lightning.callbacks import ModelSummary
-
-trainer = Trainer(enable_model_summary=True, callbacks=[ModelSummary(max_depth=-1)])
-
-
-
-
-
-
-

Trainer class API

-
-

Methods

-
-

init

-
-
-Trainer.__init__(logger=True, checkpoint_callback=None, enable_checkpointing=True, callbacks=None, default_root_dir=None, gradient_clip_val=None, gradient_clip_algorithm=None, process_position=0, num_nodes=1, num_processes=None, devices=None, gpus=None, auto_select_gpus=False, tpu_cores=None, ipus=None, log_gpu_memory=None, progress_bar_refresh_rate=None, enable_progress_bar=True, overfit_batches=0.0, track_grad_norm=- 1, check_val_every_n_epoch=1, fast_dev_run=False, accumulate_grad_batches=None, max_epochs=None, min_epochs=None, max_steps=- 1, min_steps=None, max_time=None, limit_train_batches=None, limit_val_batches=None, limit_test_batches=None, limit_predict_batches=None, val_check_interval=None, flush_logs_every_n_steps=None, log_every_n_steps=50, accelerator=None, strategy=None, sync_batchnorm=False, precision=32, enable_model_summary=True, weights_summary='top', weights_save_path=None, num_sanity_val_steps=2, resume_from_checkpoint=None, profiler=None, benchmark=None, deterministic=False, reload_dataloaders_every_n_epochs=0, auto_lr_find=False, replace_sampler_ddp=True, detect_anomaly=False, auto_scale_batch_size=False, prepare_data_per_node=None, plugins=None, amp_backend='native', amp_level=None, move_metrics_to_cpu=False, multiple_trainloader_mode='max_size_cycle', stochastic_weight_avg=False, terminate_on_nan=None)[source]
-

Customize every aspect of training via flags.

-
-
Parameters
-
    -
  • accelerator (Union[str, Accelerator, None]) –

    Supports passing different accelerator types (“cpu”, “gpu”, “tpu”, “ipu”, “hpu”, “auto”) -as well as custom accelerator instances.

    -
    -

    Deprecated since version v1.5: Passing training strategies (e.g., ‘ddp’) to accelerator has been deprecated in v1.5.0 -and will be removed in v1.7.0. Please use the strategy argument instead.

    -
    -

  • -
  • accumulate_grad_batches (Union[int, Dict[int, int], None]) – Accumulates grads every k batches or as set up in the dict. -Default: None.

  • -
  • amp_backend (str) – The mixed precision backend to use (“native” or “apex”). -Default: 'native''.

  • -
  • amp_level (Optional[str]) – The optimization level to use (O1, O2, etc…). By default it will be set to “O2” -if amp_backend is set to “apex”.

  • -
  • auto_lr_find (Union[bool, str]) – If set to True, will make trainer.tune() run a learning rate finder, -trying to optimize initial learning for faster convergence. trainer.tune() method will -set the suggested learning rate in self.lr or self.learning_rate in the LightningModule. -To use a different key set a string instead of True with the key name. -Default: False.

  • -
  • auto_scale_batch_size (Union[str, bool]) – If set to True, will initially run a batch size -finder trying to find the largest batch size that fits into memory. -The result will be stored in self.batch_size in the LightningModule. -Additionally, can be set to either power that estimates the batch size through -a power search or binsearch that estimates the batch size through a binary search. -Default: False.

  • -
  • auto_select_gpus (bool) – If enabled and gpus or devices is an integer, pick available -gpus automatically. This is especially useful when -GPUs are configured to be in “exclusive mode”, such -that only one process at a time can access them. -Default: False.

  • -
  • benchmark (Optional[bool]) – Sets torch.backends.cudnn.benchmark. -Defaults to True if deterministic -is False. Overwrite to manually set a different value. Default: None.

  • -
  • callbacks (Union[List[Callback], Callback, None]) – Add a callback or list of callbacks. -Default: None.

  • -
  • checkpoint_callback (Optional[bool]) –

    If True, enable checkpointing. -Default: None.

    -
    -

    Deprecated since version v1.5: checkpoint_callback has been deprecated in v1.5 and will be removed in v1.7. -Please consider using enable_checkpointing instead.

    -
    -

  • -
  • enable_checkpointing (bool) – If True, enable checkpointing. -It will configure a default ModelCheckpoint callback if there is no user-defined ModelCheckpoint in -callbacks. -Default: True.

  • -
  • check_val_every_n_epoch (int) – Check val every n train epochs. -Default: 1.

  • -
  • default_root_dir (Optional[str]) – Default path for logs and weights when no logger/ckpt_callback passed. -Default: os.getcwd(). -Can be remote file paths such as s3://mybucket/path or ‘hdfs://path/’

  • -
  • detect_anomaly (bool) – Enable anomaly detection for the autograd engine. -Default: False.

  • -
  • deterministic (bool) – If True, sets whether PyTorch operations must use deterministic algorithms. -Default: False.

  • -
  • devices (Union[List[int], str, int, None]) – Will be mapped to either gpus, tpu_cores, num_processes or ipus, -based on the accelerator type.

  • -
  • fast_dev_run (Union[int, bool]) – Runs n if set to n (int) else 1 if set to True batch(es) -of train, val and test to find any bugs (ie: a sort of unit test). -Default: False.

  • -
  • flush_logs_every_n_steps (Optional[int]) –

    How often to flush logs to disk (defaults to every 100 steps).

    -
    -

    Deprecated since version v1.5: flush_logs_every_n_steps has been deprecated in v1.5 and will be removed in v1.7. -Please configure flushing directly in the logger instead.

    -
    -

  • -
  • gpus (Union[List[int], str, int, None]) – Number of GPUs to train on (int) or which GPUs to train on (list or str) applied per node -Default: None.

  • -
  • gradient_clip_val (Union[int, float, None]) – The value at which to clip gradients. Passing gradient_clip_val=None disables -gradient clipping. If using Automatic Mixed Precision (AMP), the gradients will be unscaled before. -Default: None.

  • -
  • gradient_clip_algorithm (Optional[str]) – The gradient clipping algorithm to use. Pass gradient_clip_algorithm="value" -to clip by value, and gradient_clip_algorithm="norm" to clip by norm. By default it will -be set to "norm".

  • -
  • limit_train_batches (Union[int, float, None]) – How much of training dataset to check (float = fraction, int = num_batches). -Default: 1.0.

  • -
  • limit_val_batches (Union[int, float, None]) – How much of validation dataset to check (float = fraction, int = num_batches). -Default: 1.0.

  • -
  • limit_test_batches (Union[int, float, None]) – How much of test dataset to check (float = fraction, int = num_batches). -Default: 1.0.

  • -
  • limit_predict_batches (Union[int, float, None]) – How much of prediction dataset to check (float = fraction, int = num_batches). -Default: 1.0.

  • -
  • logger (Union[LightningLoggerBase, Iterable[LightningLoggerBase], bool]) – Logger (or iterable collection of loggers) for experiment tracking. A True value uses -the default TensorBoardLogger. False will disable logging. If multiple loggers are -provided and the save_dir property of that logger is not set, local files (checkpoints, -profiler traces, etc.) are saved in default_root_dir rather than in the log_dir of any -of the individual loggers. -Default: True.

  • -
  • log_gpu_memory (Optional[str]) –

    None, ‘min_max’, ‘all’. Might slow performance.

    -
    -

    Deprecated since version v1.5: Deprecated in v1.5.0 and will be removed in v1.7.0 -Please use the DeviceStatsMonitor callback directly instead.

    -
    -

  • -
  • log_every_n_steps (int) – How often to log within steps. -Default: 50.

  • -
  • prepare_data_per_node (Optional[bool]) –

    If True, each LOCAL_RANK=0 will call prepare data. -Otherwise only NODE_RANK=0, LOCAL_RANK=0 will prepare data

    -
    -

    Deprecated since version v1.5: Deprecated in v1.5.0 and will be removed in v1.7.0 -Please set prepare_data_per_node in LightningDataModule and/or -LightningModule directly instead.

    -
    -

  • -
  • process_position (int) –

    Orders the progress bar when running multiple models on same machine.

    -
    -

    Deprecated since version v1.5: process_position has been deprecated in v1.5 and will be removed in v1.7. -Please pass TQDMProgressBar with process_position -directly to the Trainer’s callbacks argument instead.

    -
    -

  • -
  • progress_bar_refresh_rate (Optional[int]) –

    How often to refresh progress bar (in steps). Value 0 disables progress bar. -Ignored when a custom progress bar is passed to callbacks. Default: None, means -a suitable value will be chosen based on the environment (terminal, Google COLAB, etc.).

    -
    -

    Deprecated since version v1.5: progress_bar_refresh_rate has been deprecated in v1.5 and will be removed in v1.7. -Please pass TQDMProgressBar with refresh_rate -directly to the Trainer’s callbacks argument instead. To disable the progress bar, -pass enable_progress_bar = False to the Trainer.

    -
    -

  • -
  • enable_progress_bar (bool) – Whether to enable to progress bar by default. -Default: False.

  • -
  • profiler (Union[BaseProfiler, str, None]) – To profile individual steps during training and assist in identifying bottlenecks. -Default: None.

  • -
  • overfit_batches (Union[int, float]) – Overfit a fraction of training data (float) or a set number of batches (int). -Default: 0.0.

  • -
  • plugins (Union[Strategy, PrecisionPlugin, ClusterEnvironment, CheckpointIO, LayerSync, str, List[Union[Strategy, PrecisionPlugin, ClusterEnvironment, CheckpointIO, LayerSync, str]], None]) – Plugins allow modification of core behavior like ddp and amp, and enable custom lightning plugins. -Default: None.

  • -
  • precision (Union[int, str]) – Double precision (64), full precision (32), half precision (16) or bfloat16 precision (bf16). -Can be used on CPU, GPU, TPUs, HPUs or IPUs. -Default: 32.

  • -
  • max_epochs (Optional[int]) – Stop training once this number of epochs is reached. Disabled by default (None). -If both max_epochs and max_steps are not specified, defaults to max_epochs = 1000. -To enable infinite training, set max_epochs = -1.

  • -
  • min_epochs (Optional[int]) – Force training for at least these many epochs. Disabled by default (None).

  • -
  • max_steps (int) – Stop training after this number of steps. Disabled by default (-1). If max_steps = -1 -and max_epochs = None, will default to max_epochs = 1000. To enable infinite training, set -max_epochs to -1.

  • -
  • min_steps (Optional[int]) – Force training for at least these number of steps. Disabled by default (None).

  • -
  • max_time (Union[str, timedelta, Dict[str, int], None]) – Stop training after this amount of time has passed. Disabled by default (None). -The time duration can be specified in the format DD:HH:MM:SS (days, hours, minutes seconds), as a -datetime.timedelta, or a dictionary with keys that will be passed to -datetime.timedelta.

  • -
  • num_nodes (int) – Number of GPU nodes for distributed training. -Default: 1.

  • -
  • num_processes (Optional[int]) – Number of processes for distributed training with accelerator="cpu". -Default: 1.

  • -
  • num_sanity_val_steps (int) – Sanity check runs n validation batches before starting the training routine. -Set it to -1 to run all batches in all validation dataloaders. -Default: 2.

  • -
  • reload_dataloaders_every_n_epochs (int) – Set to a non-negative integer to reload dataloaders every n epochs. -Default: 0.

  • -
  • replace_sampler_ddp (bool) – Explicitly enables or disables sampler replacement. If not specified this -will toggled automatically when DDP is used. By default it will add shuffle=True for -train sampler and shuffle=False for val/test sampler. If you want to customize it, -you can set replace_sampler_ddp=False and add your own distributed sampler.

  • -
  • resume_from_checkpoint (Union[str, Path, None]) –

    Path/URL of the checkpoint from which training is resumed. If there is -no checkpoint file at the path, an exception is raised. If resuming from mid-epoch checkpoint, -training will start from the beginning of the next epoch.

    -
    -

    Deprecated since version v1.5: resume_from_checkpoint is deprecated in v1.5 and will be removed in v2.0. -Please pass the path to Trainer.fit(..., ckpt_path=...) instead.

    -
    -

  • -
  • strategy (Union[str, Strategy, None]) – Supports different training strategies with aliases -as well custom strategies. -Default: None.

  • -
  • sync_batchnorm (bool) – Synchronize batch norm layers between process groups/whole world. -Default: False.

  • -
  • terminate_on_nan (Optional[bool]) –

    If set to True, will terminate training (by raising a ValueError) at the -end of each training batch, if any of the parameters or the loss are NaN or +/-inf.

    -
    -

    Deprecated since version v1.5: Trainer argument terminate_on_nan was deprecated in v1.5 and will be removed in 1.7. -Please use detect_anomaly instead.

    -
    -

  • -
  • detect_anomaly – Enable anomaly detection for the autograd engine. -Default: False.

  • -
  • tpu_cores (Union[List[int], str, int, None]) – How many TPU cores to train on (1 or 8) / Single TPU to train on (1) -Default: None.

  • -
  • ipus (Optional[int]) – How many IPUs to train on. -Default: None.

  • -
  • track_grad_norm (Union[int, float, str]) – -1 no tracking. Otherwise tracks that p-norm. May be set to ‘inf’ infinity-norm. If using -Automatic Mixed Precision (AMP), the gradients will be unscaled before logging them. -Default: -1.

  • -
  • val_check_interval (Union[int, float, None]) – How often to check the validation set. Pass a float in the range [0.0, 1.0] to check -after a fraction of the training epoch. Pass an int to check after a fixed number of training -batches. -Default: 1.0.

  • -
  • enable_model_summary (bool) – Whether to enable model summarization by default. -Default: True.

  • -
  • weights_summary (Optional[str]) –

    Prints a summary of the weights when training begins.

    -
    -

    Deprecated since version v1.5: weights_summary has been deprecated in v1.5 and will be removed in v1.7. -To disable the summary, pass enable_model_summary = False to the Trainer. -To customize the summary, pass ModelSummary -directly to the Trainer’s callbacks argument.

    -
    -

  • -
  • weights_save_path (Optional[str]) –

    Where to save weights if specified. Will override default_root_dir -for checkpoints only. Use this if for whatever reason you need the checkpoints -stored in a different place than the logs written in default_root_dir. -Can be remote file paths such as s3://mybucket/path or ‘hdfs://path/’ -Defaults to default_root_dir.

    -
    -

    Deprecated since version v1.6: weights_save_path has been deprecated in v1.6 and will be removed in v1.8. Please pass -dirpath directly to the ModelCheckpoint -callback.

    -
    -

  • -
  • move_metrics_to_cpu (bool) – Whether to force internal logged metrics to be moved to cpu. -This can save some gpu memory, but can make training slower. Use with attention. -Default: False.

  • -
  • multiple_trainloader_mode (str) – How to loop over the datasets when there are multiple train loaders. -In ‘max_size_cycle’ mode, the trainer ends one epoch when the largest dataset is traversed, -and smaller datasets reload when running out of their data. In ‘min_size’ mode, all the datasets -reload when reaching the minimum length of datasets. -Default: "max_size_cycle".

  • -
  • stochastic_weight_avg (bool) –

    Whether to use Stochastic Weight Averaging (SWA). -Default: False.

    -
    -

    Deprecated since version v1.5: stochastic_weight_avg has been deprecated in v1.5 and will be removed in v1.7. -Please pass StochasticWeightAveraging -directly to the Trainer’s callbacks argument instead.

    -
    -

  • -
-
-
-
- -
-
-

fit

-
-
-Trainer.fit(model, train_dataloaders=None, val_dataloaders=None, datamodule=None, ckpt_path=None)[source]
-

Runs the full optimization routine.

-
-
Parameters
-
    -
  • model (LightningModule) – Model to fit.

  • -
  • train_dataloaders (Union[DataLoader, Sequence[DataLoader], Sequence[Sequence[DataLoader]], Sequence[Dict[str, DataLoader]], Dict[str, DataLoader], Dict[str, Dict[str, DataLoader]], Dict[str, Sequence[DataLoader]], LightningDataModule, None]) – A collection of torch.utils.data.DataLoader or a -LightningDataModule specifying training samples. -In the case of multiple dataloaders, please see this section.

  • -
  • val_dataloaders (Union[DataLoader, Sequence[DataLoader], None]) – A torch.utils.data.DataLoader or a sequence of them specifying validation samples.

  • -
  • ckpt_path (Optional[str]) – Path/URL of the checkpoint from which training is resumed. If there is -no checkpoint file at the path, an exception is raised. If resuming from mid-epoch checkpoint, -training will start from the beginning of the next epoch.

  • -
  • datamodule (Optional[LightningDataModule]) – An instance of LightningDataModule.

  • -
-
-
Return type
-

None

-
-
-
- -
-
-

validate

-
-
-Trainer.validate(model=None, dataloaders=None, ckpt_path=None, verbose=True, datamodule=None)[source]
-

Perform one evaluation epoch over the validation set.

-
-
Parameters
-
    -
  • model (Optional[LightningModule]) – The model to validate.

  • -
  • dataloaders (Union[DataLoader, Sequence[DataLoader], LightningDataModule, None]) – A torch.utils.data.DataLoader or a sequence of them, -or a LightningDataModule specifying validation samples.

  • -
  • ckpt_path (Optional[str]) – Either best or path to the checkpoint you wish to validate. -If None and the model instance was passed, use the current weights. -Otherwise, the best model checkpoint from the previous trainer.fit call will be loaded -if a checkpoint callback is configured.

  • -
  • verbose (bool) – If True, prints the validation results.

  • -
  • datamodule (Optional[LightningDataModule]) – An instance of LightningDataModule.

  • -
-
-
Return type
-

List[Dict[str, float]]

-
-
Returns
-

List of dictionaries with metrics logged during the validation phase, e.g., in model- or callback hooks -like validation_step(), -validation_epoch_end(), etc. -The length of the list corresponds to the number of validation dataloaders used.

-
-
-
- -
-
-

test

-
-
-Trainer.test(model=None, dataloaders=None, ckpt_path=None, verbose=True, datamodule=None)[source]
-

Perform one evaluation epoch over the test set. -It’s separated from fit to make sure you never run on your test set until you want to.

-
-
Parameters
-
    -
  • model (Optional[LightningModule]) – The model to test.

  • -
  • dataloaders (Union[DataLoader, Sequence[DataLoader], LightningDataModule, None]) – A torch.utils.data.DataLoader or a sequence of them, -or a LightningDataModule specifying test samples.

  • -
  • ckpt_path (Optional[str]) – Either best or path to the checkpoint you wish to test. -If None and the model instance was passed, use the current weights. -Otherwise, the best model checkpoint from the previous trainer.fit call will be loaded -if a checkpoint callback is configured.

  • -
  • verbose (bool) – If True, prints the test results.

  • -
  • datamodule (Optional[LightningDataModule]) – An instance of LightningDataModule.

  • -
-
-
Return type
-

List[Dict[str, float]]

-
-
Returns
-

List of dictionaries with metrics logged during the test phase, e.g., in model- or callback hooks -like test_step(), -test_epoch_end(), etc. -The length of the list corresponds to the number of test dataloaders used.

-
-
-
- -
-
-

predict

-
-
-Trainer.predict(model=None, dataloaders=None, datamodule=None, return_predictions=None, ckpt_path=None)[source]
-

Run inference on your data. -This will call the model forward function to compute predictions. Useful to perform distributed -and batched predictions. Logging is disabled in the predict hooks.

-
-
Parameters
-
    -
  • model (Optional[LightningModule]) – The model to predict with.

  • -
  • dataloaders (Union[DataLoader, Sequence[DataLoader], LightningDataModule, None]) – A torch.utils.data.DataLoader or a sequence of them, -or a LightningDataModule specifying prediction samples.

  • -
  • datamodule (Optional[LightningDataModule]) – The datamodule with a predict_dataloader method that returns one or more dataloaders.

  • -
  • return_predictions (Optional[bool]) – Whether to return predictions. -True by default except when an accelerator that spawns processes is used (not supported).

  • -
  • ckpt_path (Optional[str]) – Either best or path to the checkpoint you wish to predict. -If None and the model instance was passed, use the current weights. -Otherwise, the best model checkpoint from the previous trainer.fit call will be loaded -if a checkpoint callback is configured.

  • -
-
-
Return type
-

Union[List[Any], List[List[Any]], None]

-
-
Returns
-

Returns a list of dictionaries, one for each provided dataloader containing their respective predictions.

-
-
-
- -
-
-

tune

-
-
-Trainer.tune(model, train_dataloaders=None, val_dataloaders=None, datamodule=None, scale_batch_size_kwargs=None, lr_find_kwargs=None)[source]
-

Runs routines to tune hyperparameters before training.

-
-
Parameters
-
    -
  • model (LightningModule) – Model to tune.

  • -
  • train_dataloaders (Union[DataLoader, Sequence[DataLoader], Sequence[Sequence[DataLoader]], Sequence[Dict[str, DataLoader]], Dict[str, DataLoader], Dict[str, Dict[str, DataLoader]], Dict[str, Sequence[DataLoader]], LightningDataModule, None]) – A collection of torch.utils.data.DataLoader or a -LightningDataModule specifying training samples. -In the case of multiple dataloaders, please see this section.

  • -
  • val_dataloaders (Union[DataLoader, Sequence[DataLoader], None]) – A torch.utils.data.DataLoader or a sequence of them specifying validation samples.

  • -
  • datamodule (Optional[LightningDataModule]) – An instance of LightningDataModule.

  • -
  • scale_batch_size_kwargs (Optional[Dict[str, Any]]) – Arguments for scale_batch_size()

  • -
  • lr_find_kwargs (Optional[Dict[str, Any]]) – Arguments for lr_find()

  • -
-
-
Return type
-

Dict[str, Union[int, _LRFinder, None]]

-
-
-
- -
-
-
-

Properties

-
-

callback_metrics

-

The metrics available to callbacks. These are automatically set when you log via self.log

-
def training_step(self, batch, batch_idx):
-    self.log("a_val", 2)
-
-
-callback_metrics = trainer.callback_metrics
-assert callback_metrics["a_val"] == 2
-
-
-
-
-

current_epoch

-

The number of epochs run.

-
if trainer.current_epoch >= 10:
-    ...
-
-
-
-
-

global_step

-

The number of optimizer steps taken (does not reset each epoch). -This includes multiple optimizers and TBPTT steps (if enabled).

-
if trainer.global_step >= 100:
-    ...
-
-
-
-
-

logger

-

The current logger being used. Here’s an example using tensorboard

-
logger = trainer.logger
-tensorboard = logger.experiment
-
-
-
-
-

loggers

-

The list of loggers currently being used by the Trainer.

-
# List of Logger objects
-loggers = trainer.loggers
-for logger in loggers:
-    logger.log_metrics({"foo": 1.0})
-
-
-
-
-

logged_metrics

-

The metrics sent to the logger (visualizer).

-
def training_step(self, batch, batch_idx):
-    self.log("a_val", 2, logger=True)
-
-
-logged_metrics = trainer.logged_metrics
-assert logged_metrics["a_val"] == 2
-
-
-
-
-

log_dir

-

The directory for the current experiment. Use this to save images to, etc…

-
def training_step(self, batch, batch_idx):
-    img = ...
-    save_img(img, self.trainer.log_dir)
-
-
-
-
-

is_global_zero

-

Whether this process is the global zero in multi-node training

-
def training_step(self, batch, batch_idx):
-    if self.trainer.is_global_zero:
-        print("in node 0, accelerator 0")
-
-
-
-
-

progress_bar_metrics

-

The metrics sent to the progress bar.

-
def training_step(self, batch, batch_idx):
-    self.log("a_val", 2, prog_bar=True)
-
-
-progress_bar_metrics = trainer.progress_bar_metrics
-assert progress_bar_metrics["a_val"] == 2
-
-
-
-
-

estimated_stepping_batches

-

Check out estimated_stepping_batches().

-
-
-

state

-

The current state of the Trainer, including the current function that is running, the stage of -execution within that function, and the status of the Trainer.

-
# fn in ("fit", "validate", "test", "predict", "tune")
-trainer.state.fn
-# status in ("initializing", "running", "finished", "interrupted")
-trainer.state.status
-# stage in ("train", "sanity_check", "validate", "test", "predict", "tune")
-trainer.state.stage
-
-
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/common_usecases.html b/docs/common_usecases.html deleted file mode 100644 index f7a91f3..0000000 --- a/docs/common_usecases.html +++ /dev/null @@ -1,915 +0,0 @@ - - - - - - - - - - - - - - Common Workflows — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Common Workflows

-

Customize and extend Lightning for things like custom hardware or distributed strategies.

-

-
-
-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/data/datamodule.html b/docs/data/datamodule.html deleted file mode 100644 index 618ce5f..0000000 --- a/docs/data/datamodule.html +++ /dev/null @@ -1,1428 +0,0 @@ - - - - - - - - - - - - - - LightningDataModule — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • LightningDataModule
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

LightningDataModule

-

A datamodule is a shareable, reusable class that encapsulates all the steps needed to process data:

-
-

-
-

A datamodule encapsulates the five steps involved in data processing in PyTorch:

-
    -
  1. Download / tokenize / process.

  2. -
  3. Clean and (maybe) save to disk.

  4. -
  5. Load inside Dataset.

  6. -
  7. Apply transforms (rotate, tokenize, etc…).

  8. -
  9. Wrap inside a DataLoader.

  10. -
-
-

-
-

This class can then be shared and used anywhere:

-
from pl_bolts.datamodules import CIFAR10DataModule, ImagenetDataModule
-
-model = LitClassifier()
-trainer = Trainer()
-
-imagenet = ImagenetDataModule()
-trainer.fit(model, datamodule=imagenet)
-
-cifar10 = CIFAR10DataModule()
-trainer.fit(model, datamodule=cifar10)
-
-
-
-
-

Why do I need a DataModule?

-

In normal PyTorch code, the data cleaning/preparation is usually scattered across many files. This makes -sharing and reusing the exact splits and transforms across projects impossible.

-

Datamodules are for you if you ever asked the questions:

-
    -
  • what splits did you use?

  • -
  • what transforms did you use?

  • -
  • what normalization did you use?

  • -
  • how did you prepare/tokenize the data?

  • -
-
-
-
-

What is a DataModule?

-

A DataModule is simply a collection of a train_dataloader(s), val_dataloader(s), test_dataloader(s) and -predict_dataloader(s) along with the matching transforms and data processing/downloads steps required.

-

Here’s a simple PyTorch example:

-
# regular PyTorch
-test_data = MNIST(my_path, train=False, download=True)
-predict_data = MNIST(my_path, train=False, download=True)
-train_data = MNIST(my_path, train=True, download=True)
-train_data, val_data = random_split(train_data, [55000, 5000])
-
-train_loader = DataLoader(train_data, batch_size=32)
-val_loader = DataLoader(val_data, batch_size=32)
-test_loader = DataLoader(test_data, batch_size=32)
-predict_loader = DataLoader(predict_data, batch_size=32)
-
-
-

The equivalent DataModule just organizes the same exact code, but makes it reusable across projects.

-
class MNISTDataModule(pl.LightningDataModule):
-    def __init__(self, data_dir: str = "path/to/dir", batch_size: int = 32):
-        super().__init__()
-        self.data_dir = data_dir
-        self.batch_size = batch_size
-
-    def setup(self, stage: Optional[str] = None):
-        self.mnist_test = MNIST(self.data_dir, train=False)
-        self.mnist_predict = MNIST(self.data_dir, train=False)
-        mnist_full = MNIST(self.data_dir, train=True)
-        self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])
-
-    def train_dataloader(self):
-        return DataLoader(self.mnist_train, batch_size=self.batch_size)
-
-    def val_dataloader(self):
-        return DataLoader(self.mnist_val, batch_size=self.batch_size)
-
-    def test_dataloader(self):
-        return DataLoader(self.mnist_test, batch_size=self.batch_size)
-
-    def predict_dataloader(self):
-        return DataLoader(self.mnist_predict, batch_size=self.batch_size)
-
-    def teardown(self, stage: Optional[str] = None):
-        # Used to clean-up when the run is finished
-        ...
-
-
-

But now, as the complexity of your processing grows (transforms, multiple-GPU training), you can -let Lightning handle those details for you while making this dataset reusable so you can share with -colleagues or use in different projects.

-
mnist = MNISTDataModule(my_path)
-model = LitClassifier()
-
-trainer = Trainer()
-trainer.fit(model, mnist)
-
-
-

Here’s a more realistic, complex DataModule that shows how much more reusable the datamodule is.

-
import pytorch_lightning as pl
-from torch.utils.data import random_split, DataLoader
-
-# Note - you must have torchvision installed for this example
-from torchvision.datasets import MNIST
-from torchvision import transforms
-
-
-class MNISTDataModule(pl.LightningDataModule):
-    def __init__(self, data_dir: str = "./"):
-        super().__init__()
-        self.data_dir = data_dir
-        self.transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])
-
-    def prepare_data(self):
-        # download
-        MNIST(self.data_dir, train=True, download=True)
-        MNIST(self.data_dir, train=False, download=True)
-
-    def setup(self, stage: Optional[str] = None):
-
-        # Assign train/val datasets for use in dataloaders
-        if stage == "fit" or stage is None:
-            mnist_full = MNIST(self.data_dir, train=True, transform=self.transform)
-            self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])
-
-        # Assign test dataset for use in dataloader(s)
-        if stage == "test" or stage is None:
-            self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform)
-
-        if stage == "predict" or stage is None:
-            self.mnist_predict = MNIST(self.data_dir, train=False, transform=self.transform)
-
-    def train_dataloader(self):
-        return DataLoader(self.mnist_train, batch_size=32)
-
-    def val_dataloader(self):
-        return DataLoader(self.mnist_val, batch_size=32)
-
-    def test_dataloader(self):
-        return DataLoader(self.mnist_test, batch_size=32)
-
-    def predict_dataloader(self):
-        return DataLoader(self.mnist_predict, batch_size=32)
-
-
-
-
-
-

LightningDataModule API

-

To define a DataModule the following methods are used to create train/val/test/predict dataloaders:

- -
-

prepare_data

-

Downloading and saving data with multiple processes (distributed settings) will result in corrupted data. Lightning -ensures the prepare_data() is called only within a single process on CPU, -so you can safely add your downloading logic within. In case of multi-node training, the execution of this hook -depends upon prepare_data_per_node. setup() is called after -prepare_data and there is a barrier in between which ensures that all the processes proceed to setup once the data is prepared and available for use.

-
    -
  • download, i.e. download data only once on the disk from a single process

  • -
  • tokenize. Since it’s a one time process, it is not recommended to do it on all processes

  • -
  • etc…

  • -
-
class MNISTDataModule(pl.LightningDataModule):
-    def prepare_data(self):
-        # download
-        MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor())
-        MNIST(os.getcwd(), train=False, download=True, transform=transforms.ToTensor())
-
-
-
-

Warning

-

prepare_data is called from the main process. It is not recommended to assign state here (e.g. self.x = y) since it is called on a single process and if you assign -states here then they won’t be available for other processes.

-
-
-
-

setup

-

There are also data operations you might want to perform on every GPU. Use setup() to do things like:

-
    -
  • count number of classes

  • -
  • build vocabulary

  • -
  • perform train/val/test splits

  • -
  • create datasets

  • -
  • apply transforms (defined explicitly in your datamodule)

  • -
  • etc…

  • -
-
import pytorch_lightning as pl
-
-
-class MNISTDataModule(pl.LightningDataModule):
-    def setup(self, stage: Optional[str] = None):
-
-        # Assign Train/val split(s) for use in Dataloaders
-        if stage in (None, "fit"):
-            mnist_full = MNIST(self.data_dir, train=True, download=True, transform=self.transform)
-            self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])
-
-        # Assign Test split(s) for use in Dataloaders
-        if stage in (None, "test"):
-            self.mnist_test = MNIST(self.data_dir, train=False, download=True, transform=self.transform)
-
-
-

For eg., if you are working with NLP task where you need to tokenize the text and use it, then you can do something like as follows:

-
class LitDataModule(LightningDataModule):
-    def prepare_data(self):
-        dataset = load_Dataset(...)
-        train_dataset = ...
-        val_dataset = ...
-        # tokenize
-        # save it to disk
-
-    def setup(self, stage):
-        # load it back here
-        dataset = load_dataset_from_disk(...)
-
-
-

This method expects a stage argument. -It is used to separate setup logic for trainer.{fit,validate,test,predict}. If setup is called with stage=None, -we assume all stages have been set-up.

-
-

Note

-

setup is called from every process across all the nodes. Setting state here is recommended.

-
-
-

Note

-

teardown can be used to clean up the state. It is also called from every process across all the nodes.

-
-
-
-

train_dataloader

-

Use the train_dataloader() method to generate the training dataloader(s). -Usually you just wrap the dataset you defined in setup. This is the dataloader that the Trainer -fit() method uses.

-
import pytorch_lightning as pl
-
-
-class MNISTDataModule(pl.LightningDataModule):
-    def train_dataloader(self):
-        return DataLoader(self.mnist_train, batch_size=64)
-
-
-
-
-

val_dataloader

-

Use the val_dataloader() method to generate the validation dataloader(s). -Usually you just wrap the dataset you defined in setup. This is the dataloader that the Trainer -fit() and validate() methods uses.

-
import pytorch_lightning as pl
-
-
-class MNISTDataModule(pl.LightningDataModule):
-    def val_dataloader(self):
-        return DataLoader(self.mnist_val, batch_size=64)
-
-
-
-
-

test_dataloader

-

Use the test_dataloader() method to generate the test dataloader(s). -Usually you just wrap the dataset you defined in setup. This is the dataloader that the Trainer -test() method uses.

-
import pytorch_lightning as pl
-
-
-class MNISTDataModule(pl.LightningDataModule):
-    def test_dataloader(self):
-        return DataLoader(self.mnist_test, batch_size=64)
-
-
-
-
-

predict_dataloader

-

Use the predict_dataloader() method to generate the prediction dataloader(s). -Usually you just wrap the dataset you defined in setup. This is the dataloader that the Trainer -predict() method uses.

-
import pytorch_lightning as pl
-
-
-class MNISTDataModule(pl.LightningDataModule):
-    def predict_dataloader(self):
-        return DataLoader(self.mnist_predict, batch_size=64)
-
-
-
-
-

transfer_batch_to_device

-
-
-LightningDataModule.transfer_batch_to_device(batch, device, dataloader_idx)
-

Override this hook if your DataLoader returns tensors wrapped in a custom -data structure.

-

The data types listed below (and any arbitrary nesting of them) are supported out of the box:

-
    -
  • torch.Tensor or anything that implements .to(…)

  • -
  • list

  • -
  • dict

  • -
  • tuple

  • -
  • torchtext.data.batch.Batch

  • -
-

For anything else, you need to define how the data is moved to the target device (CPU, GPU, TPU, …).

-
-

Note

-

This hook should only transfer the data and not modify it, nor should it move the data to -any other device than the one passed in as argument (unless you know what you are doing). -To check the current state of execution of this hook you can use -self.trainer.training/testing/validating/predicting so that you can -add different logic as per your requirement.

-
-
-

Note

-

This hook only runs on single GPU training and DDP (no data-parallel). -Data-Parallel support will come in near future.

-
-
-
Parameters
-
    -
  • batch (Any) – A batch of data that needs to be transferred to a new device.

  • -
  • device (device) – The target device as defined in PyTorch.

  • -
  • dataloader_idx (int) – The index of the dataloader to which the batch belongs.

  • -
-
-
Return type
-

Any

-
-
Returns
-

A reference to the data on the new device.

-
-
-

Example:

-
def transfer_batch_to_device(self, batch, device, dataloader_idx):
-    if isinstance(batch, CustomBatch):
-        # move all tensors in your custom data structure to the device
-        batch.samples = batch.samples.to(device)
-        batch.targets = batch.targets.to(device)
-    elif dataloader_idx == 0:
-        # skip device transfer for the first dataloader or anything you wish
-        pass
-    else:
-        batch = super().transfer_batch_to_device(data, device, dataloader_idx)
-    return batch
-
-
-
-
Raises
-

MisconfigurationException – If using data-parallel, Trainer(strategy='dp').

-
-
-
-

See also

-
    -
  • move_data_to_device()

  • -
  • apply_to_collection()

  • -
-
-
- -
-
-

on_before_batch_transfer

-
-
-LightningDataModule.on_before_batch_transfer(batch, dataloader_idx)
-

Override to alter or apply batch augmentations to your batch before it is transferred to the device.

-
-

Note

-

To check the current state of execution of this hook you can use -self.trainer.training/testing/validating/predicting so that you can -add different logic as per your requirement.

-
-
-

Note

-

This hook only runs on single GPU training and DDP (no data-parallel). -Data-Parallel support will come in near future.

-
-
-
Parameters
-
    -
  • batch (Any) – A batch of data that needs to be altered or augmented.

  • -
  • dataloader_idx (int) – The index of the dataloader to which the batch belongs.

  • -
-
-
Return type
-

Any

-
-
Returns
-

A batch of data

-
-
-

Example:

-
def on_before_batch_transfer(self, batch, dataloader_idx):
-    batch['x'] = transforms(batch['x'])
-    return batch
-
-
-
-
Raises
-

MisconfigurationException – If using data-parallel, Trainer(strategy='dp').

-
-
-
-

See also

-
    -
  • on_after_batch_transfer()

  • -
  • transfer_batch_to_device()

  • -
-
-
- -
-
-

on_after_batch_transfer

-
-
-LightningDataModule.on_after_batch_transfer(batch, dataloader_idx)
-

Override to alter or apply batch augmentations to your batch after it is transferred to the device.

-
-

Note

-

To check the current state of execution of this hook you can use -self.trainer.training/testing/validating/predicting so that you can -add different logic as per your requirement.

-
-
-

Note

-

This hook only runs on single GPU training and DDP (no data-parallel). -Data-Parallel support will come in near future.

-
-
-
Parameters
-
    -
  • batch (Any) – A batch of data that needs to be altered or augmented.

  • -
  • dataloader_idx (int) – The index of the dataloader to which the batch belongs.

  • -
-
-
Return type
-

Any

-
-
Returns
-

A batch of data

-
-
-

Example:

-
def on_after_batch_transfer(self, batch, dataloader_idx):
-    batch['x'] = gpu_transforms(batch['x'])
-    return batch
-
-
-
-
Raises
-

MisconfigurationException – If using data-parallel, Trainer(strategy='dp').

-
-
-
-

See also

-
    -
  • on_before_batch_transfer()

  • -
  • transfer_batch_to_device()

  • -
-
-
- -
-
-

load_state_dict

-
-
-LightningDataModule.load_state_dict(state_dict)[source]
-

Called when loading a checkpoint, implement to reload datamodule state given datamodule state_dict.

-
-
Parameters
-

state_dict (Dict[str, Any]) – the datamodule state returned by state_dict.

-
-
Return type
-

None

-
-
-
- -
-
-

state_dict

-
-
-LightningDataModule.state_dict()[source]
-

Called when saving a checkpoint, implement to generate and save datamodule state.

-
-
Return type
-

Dict[str, Any]

-
-
Returns
-

A dictionary containing datamodule state.

-
-
-
- -
-
-

on_train_dataloader

-
-
-LightningDataModule.on_train_dataloader()
-

Called before requesting the train dataloader.

-
-

Deprecated since version v1.5: on_train_dataloader() is deprecated and will be removed in v1.7.0. -Please use train_dataloader() directly.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_val_dataloader

-
-
-LightningDataModule.on_val_dataloader()
-

Called before requesting the val dataloader.

-
-

Deprecated since version v1.5: on_val_dataloader() is deprecated and will be removed in v1.7.0. -Please use val_dataloader() directly.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_test_dataloader

-
-
-LightningDataModule.on_test_dataloader()
-

Called before requesting the test dataloader.

-
-

Deprecated since version v1.5: on_test_dataloader() is deprecated and will be removed in v1.7.0. -Please use test_dataloader() directly.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_dataloader

-
-
-LightningDataModule.on_predict_dataloader()
-

Called before requesting the predict dataloader.

-
-

Deprecated since version v1.5: on_predict_dataloader() is deprecated and will be removed in v1.7.0. -Please use predict_dataloader() directly.

-
-
-
Return type
-

None

-
-
-
- -
-
-

teardown

-
-
-LightningDataModule.teardown(stage=None)
-

Called at the end of fit (train + validate), validate, test, or predict.

-
-
Parameters
-

stage (Optional[str]) – either 'fit', 'validate', 'test', or 'predict'

-
-
Return type
-

None

-
-
-
- -
-
-

prepare_data_per_node

-

If set to True will call prepare_data() on LOCAL_RANK=0 for every node. -If set to False will only call from NODE_RANK=0, LOCAL_RANK=0.

-
class LitDataModule(LightningDataModule):
-    def __init__(self):
-        super().__init__()
-        self.prepare_data_per_node = True
-
-
-
-
-
-
-

Using a DataModule

-

The recommended way to use a DataModule is simply:

-
dm = MNISTDataModule()
-model = Model()
-trainer.fit(model, datamodule=dm)
-trainer.test(datamodule=dm)
-trainer.validate(datamodule=dm)
-trainer.predict(datamodule=dm)
-
-
-

If you need information from the dataset to build your model, then run -prepare_data and -setup manually (Lightning ensures -the method runs on the correct devices).

-
dm = MNISTDataModule()
-dm.prepare_data()
-dm.setup(stage="fit")
-
-model = Model(num_classes=dm.num_classes, width=dm.width, vocab=dm.vocab)
-trainer.fit(model, dm)
-
-dm.setup(stage="test")
-trainer.test(datamodule=dm)
-
-
-
-
-
-

DataModules without Lightning

-

You can of course use DataModules in plain PyTorch code as well.

-
# download, etc...
-dm = MNISTDataModule()
-dm.prepare_data()
-
-# splits/transforms
-dm.setup(stage="fit")
-
-# use data
-for batch in dm.train_dataloader():
-    ...
-
-for batch in dm.val_dataloader():
-    ...
-
-dm.teardown(stage="fit")
-
-# lazy load test data
-dm.setup(stage="test")
-for batch in dm.test_dataloader():
-    ...
-
-dm.teardown(stage="test")
-
-
-

But overall, DataModules encourage reproducibility by allowing all details of a dataset to be specified in a unified -structure.

-
-
-
-

Hyperparameters in DataModules

-

Like LightningModules, DataModules support hyperparameters with the same API.

-
import pytorch_lightning as pl
-
-
-class CustomDataModule(pl.LightningDataModule):
-    def __init__(self, *args, **kwargs):
-        super().__init__()
-        self.save_hyperparameters()
-
-    def configure_optimizers(self):
-        # access the saved hyperparameters
-        opt = optim.Adam(self.parameters(), lr=self.hparams.lr)
-
-
-

Refer to save_hyperparameters in lightning module for more details.

-
-
-

Save DataModule state

-

When a checkpoint is created, it asks every DataModule for their state. If your DataModule defines the state_dict and load_state_dict methods, the checkpoint will automatically track and restore your DataModules.

-
class LitDataModule(pl.DataModuler):
-    def state_dict(self):
-        # track whatever you want here
-        state = {"current_train_batch_index": self.current_train_batch_index}
-        return state
-
-    def load_state_dict(self, state_dict):
-        # restore the state based on what you tracked in (def state_dict)
-        self.current_train_batch_index = state_dict["current_train_batch_index"]
-
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/debug/debugging.html b/docs/debug/debugging.html deleted file mode 100644 index 9d4a891..0000000 --- a/docs/debug/debugging.html +++ /dev/null @@ -1,731 +0,0 @@ - - - - - - - - - - - - - - Debug your model — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/debug/debugging_advanced.html b/docs/debug/debugging_advanced.html deleted file mode 100644 index 962a227..0000000 --- a/docs/debug/debugging_advanced.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - - - - - - - - - Debug your model (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Debug your model (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Debug your model (advanced)

-

Audience: Users who want to debug distributed models.

-
-
-

Debug distributed models

-

To debug a distributed model, we recommend you debug it locally by running the distributed version on CPUs:

-
trainer = Trainer(accelerator="cpu", strategy="ddp", devices=2)
-
-
-

On the CPU, you can use pdb or breakpoint() -or use regular print statements.

-
class LitModel(LightningModule):
-    def training_step(self, batch, batch_idx):
-
-        debugging_message = ...
-        print(f"RANK - {self.trainer.global_rank}: {debugging_message}")
-
-        if self.trainer.global_rank == 0:
-            import pdb
-
-            pdb.set_trace()
-
-        # to prevent other processes from moving forward until all processes are in sync
-        self.trainer.strategy.barrier()
-
-
-

When everything works, switch back to GPU by changing only the accelerator.

-
trainer = Trainer(accelerator="gpu", strategy="ddp", devices=2)
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/debug/debugging_basic.html b/docs/debug/debugging_basic.html deleted file mode 100644 index 3fad6ed..0000000 --- a/docs/debug/debugging_basic.html +++ /dev/null @@ -1,811 +0,0 @@ - - - - - - - - - - - - - - Debug your model (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Debug your model (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Debug your model (basic)

-

Audience: Users who want to learn the basics of debugging models.

-
-
-

How does Lightning help me debug ?

-

The Lightning Trainer has a lot of arguments devoted to maximizing your debugging productivity.

-
-
-
-

Set a breakpoint

-

A breakpoint stops your code execution so you can inspect variables, etc… and allow your code to execute one line at a time.

-
def function_to_debug():
-    x = 2
-
-    # set breakpoint
-    import pdb
-
-    pdb.set_trace()
-    y = x ** 2
-
-
-

In this example, the code will stop before executing the y = x**2 line.

-
-
-
-

Run all your model code once quickly

-

If you’ve ever trained a model for days only to crash during validation or testing then this trainer argument is about to become your best friend.

-

The fast_dev_run argument in the trainer runs 5 batch of training, validation, test and prediction data through your trainer to see if there are any bugs:

-
Trainer(fast_dev_run=True)
-
-
-

To change how many batches to use, change the argument to an integer. Here we run 7 batches of each:

-
Trainer(fast_dev_run=7)
-
-
-
-

Note

-

This argument will disable tuner, checkpoint callbacks, early stopping callbacks, -loggers and logger callbacks like LearningRateMonitor and -DeviceStatsMonitor.

-
-
-
-
-

Shorten the epoch length

-

Sometimes it’s helpful to only use a fraction of your training, val, test, or predict data (or a set number of batches). -For example, you can use 20% of the training set and 1% of the validation set.

-

On larger datasets like Imagenet, this can help you debug or test a few things faster than waiting for a full epoch.

-
# use only 10% of training data and 1% of val data
-trainer = Trainer(limit_train_batches=0.1, limit_val_batches=0.01)
-
-# use 10 batches of train and 5 batches of val
-trainer = Trainer(limit_train_batches=10, limit_val_batches=5)
-
-
-
-
-
-

Run a Sanity Check

-

Lightning runs 2 steps of validation in the beginning of training. -This avoids crashing in the validation loop sometime deep into a lengthy training loop.

-

(See: num_sanity_val_steps -argument of Trainer)

-
trainer = Trainer(num_sanity_val_steps=2)
-
-
-
-
- -
- -
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/debug/debugging_intermediate.html b/docs/debug/debugging_intermediate.html deleted file mode 100644 index c3fcf7d..0000000 --- a/docs/debug/debugging_intermediate.html +++ /dev/null @@ -1,748 +0,0 @@ - - - - - - - - - - - - - - Debug your model (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Debug your model (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Debug your model (intermediate)

-

Audience: Users who want to debug their ML code

-
-
-

Why should I debug ML code?

-

Machine learning code requires debugging mathematical correctness, which is not something non-ML code has to deal with. Lightning implements a few best-practice techniques to give all users, expert level ML debugging abilities.

-
-
-
-

Overfit your model on a Subset of Data

-

A good debugging technique is to take a tiny portion of your data (say 2 samples per class), -and try to get your model to overfit. If it can’t, it’s a sign it won’t work with large datasets.

-

(See: overfit_batches -argument of Trainer)

-
# use only 1% of training data (and turn off validation)
-trainer = Trainer(overfit_batches=0.01)
-
-# similar, but with a fixed 10 batches
-trainer = Trainer(overfit_batches=10)
-
-
-

When using this argument, the validation loop will be disabled. We will also replace the sampler -in the training set to turn off shuffle for you.

-
-
-
-

Look-out for exploding gradients

-

One major problem that plagues models is exploding gradients. Gradient norm is one technique that can help keep gradients from exploding.

-
# the 2-norm
-trainer = Trainer(track_grad_norm=2)
-
-
-

This will plot the 2-norm to your experiment manager. If you notice the norm is going up, there’s a good chance your gradients are/will explode.

-

One technique to stop exploding gradients is to clip the gradient

-
# DEFAULT (ie: don't clip)
-trainer = Trainer(gradient_clip_val=0)
-
-# clip gradients' global norm to <=0.5 using gradient_clip_algorithm='norm' by default
-trainer = Trainer(gradient_clip_val=0.5)
-
-# clip gradients' maximum magnitude to <=0.5
-trainer = Trainer(gradient_clip_val=0.5, gradient_clip_algorithm="value")
-
-
-
-
-
-

Detect autograd anomalies

-

Lightning helps you detect anomalies in the PyTorh autograd engine via PyTorch’s built-in -Anomaly Detection Context-manager.

-

Enable it via the detect_anomaly trainer argument:

-
trainer = Trainer(detect_anomaly=True)
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/deploy/production.html b/docs/deploy/production.html deleted file mode 100644 index f489dbc..0000000 --- a/docs/deploy/production.html +++ /dev/null @@ -1,764 +0,0 @@ - - - - - - - - - - - - - - Deploy models into production — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Deploy models into production
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/deploy/production_advanced.html b/docs/deploy/production_advanced.html deleted file mode 100644 index 281673a..0000000 --- a/docs/deploy/production_advanced.html +++ /dev/null @@ -1,751 +0,0 @@ - - - - - - - - - - - - - - Deploy models into production (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Deploy models into production (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Deploy models into production (advanced)

-

Audience: Machine learning engineers optimizing models for enterprise-scale production environments.

-
-
-

Compile your model to ONNX

-

ONNX is a package developed by Microsoft to optimize inference. ONNX allows the model to be independent of PyTorch and run on any ONNX Runtime.

-

To export your model to ONNX format call the to_onnx() function on your LightningModule with the filepath and input_sample.

-
class SimpleModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.l1 = torch.nn.Linear(in_features=64, out_features=4)
-
-    def forward(self, x):
-        return torch.relu(self.l1(x.view(x.size(0), -1)))
-
-
-# create the model
-model = SimpleModel()
-filepath = "model.onnx"
-input_sample = torch.randn((1, 64))
-model.to_onnx(filepath, input_sample, export_params=True)
-
-
-

You can also skip passing the input sample if the example_input_array property is specified in your LightningModule.

-
class SimpleModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.l1 = torch.nn.Linear(in_features=64, out_features=4)
-        self.example_input_array = torch.randn(7, 64)
-
-    def forward(self, x):
-        return torch.relu(self.l1(x.view(x.size(0), -1)))
-
-
-# create the model
-model = SimpleModel()
-filepath = "model.onnx"
-model.to_onnx(filepath, export_params=True)
-
-
-

Once you have the exported model, you can run it on your ONNX runtime in the following way:

-
import onnxruntime
-
-ort_session = onnxruntime.InferenceSession(filepath)
-input_name = ort_session.get_inputs()[0].name
-ort_inputs = {input_name: np.random.randn(1, 64)}
-ort_outs = ort_session.run(None, ort_inputs)
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/deploy/production_advanced_2.html b/docs/deploy/production_advanced_2.html deleted file mode 100644 index 6f05d73..0000000 --- a/docs/deploy/production_advanced_2.html +++ /dev/null @@ -1,746 +0,0 @@ - - - - - - - - - - - - - - Deploy models into production (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Deploy models into production (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Deploy models into production (advanced)

-

Audience: Machine learning engineers optimizing models for enterprise-scale production environments.

-
-
-

Compile your model to TorchScript

-

TorchScript allows you to serialize your models in a way that it can be loaded in non-Python environments. -The LightningModule has a handy method to_torchscript() that returns a scripted module which you -can save or directly use.

-
class SimpleModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.l1 = torch.nn.Linear(in_features=64, out_features=4)
-
-    def forward(self, x):
-        return torch.relu(self.l1(x.view(x.size(0), -1)))
-
-
-# create the model
-model = SimpleModel()
-script = model.to_torchscript()
-
-# save for use in production environment
-torch.jit.save(script, "model.pt")
-
-
-

It is recommended that you install the latest supported version of PyTorch to use this feature without limitations.

-

Once you have the exported model, you can run it in Pytorch or C++ runtime:

-
inp = torch.rand(1, 64)
-scripted_module = torch.jit.load("model.pt")
-output = scripted_module(inp)
-
-
-

If you want to script a different method, you can decorate the method with torch.jit.export():

-
class LitMCdropoutModel(pl.LightningModule):
-    def __init__(self, model, mc_iteration):
-        super().__init__()
-        self.model = model
-        self.dropout = nn.Dropout()
-        self.mc_iteration = mc_iteration
-
-    @torch.jit.export
-    def predict_step(self, batch, batch_idx):
-        # enable Monte Carlo Dropout
-        self.dropout.train()
-
-        # take average of `self.mc_iteration` iterations
-        pred = [self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)]
-        pred = torch.vstack(pred).mean(dim=0)
-        return pred
-
-
-model = LitMCdropoutModel(...)
-script = model.to_torchscript(file_path="model.pt", method="script")
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/deploy/production_basic.html b/docs/deploy/production_basic.html deleted file mode 100644 index 6129928..0000000 --- a/docs/deploy/production_basic.html +++ /dev/null @@ -1,767 +0,0 @@ - - - - - - - - - - - - - - Deploy models into production (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Deploy models into production (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Deploy models into production (basic)

-

Audience: All users.

-
-
-

Load a checkpoint and predict

-

The easiest way to use a model for predictions is to load the weights using load_from_checkpoint found in the LightningModule.

-
model = LitModel.load_from_checkpoint("best_model.ckpt")
-model.eval()
-x = torch.randn(1, 64)
-
-with torch.no_grad():
-    y_hat = model(x)
-
-
-
-
-
-

Predict step with your LightningModule

-

Loading a checkpoint and predicting still leaves you with a lot of boilerplate around the predict epoch. The predict step in the LightningModule removes this boilerplate.

-
class MyModel(LightningModule):
-    def predict_step(self, batch, batch_idx, dataloader_idx=0):
-        return self(batch)
-
-
-

And pass in any dataloader to the Lightning Trainer:

-
data_loader = DataLoader(...)
-model = MyModel()
-trainer = Trainer()
-predictions = trainer.predict(model, data_loader)
-
-
-
-
-
-

Enable complicated predict logic

-

When you need to add complicated pre-processing or post-processing logic to your data use the predict step. For example here we do Monte Carlo Dropout for predictions:

-
class LitMCdropoutModel(pl.LightningModule):
-    def __init__(self, model, mc_iteration):
-        super().__init__()
-        self.model = model
-        self.dropout = nn.Dropout()
-        self.mc_iteration = mc_iteration
-
-    def predict_step(self, batch, batch_idx):
-        # enable Monte Carlo Dropout
-        self.dropout.train()
-
-        # take average of `self.mc_iteration` iterations
-        pred = [self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)]
-        pred = torch.vstack(pred).mean(dim=0)
-        return pred
-
-
-
-
-
-

Enable distributed inference

-

By using the predict step in Lightning you get free distributed inference

-
trainer = Trainer(devices=8, accelerator="gpu")
-predictions = trainer.predict(model, data_loader)
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/deploy/production_intermediate.html b/docs/deploy/production_intermediate.html deleted file mode 100644 index 057969f..0000000 --- a/docs/deploy/production_intermediate.html +++ /dev/null @@ -1,791 +0,0 @@ - - - - - - - - - - - - - - Deploy models into production (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Deploy models into production (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Deploy models into production (intermediate)

-

Audience: Researchers and MLEs looking to use their models for predictions without Lightning dependencies.

-
-
-

Use PyTorch as normal

-

If you prefer to use PyTorch directly, feel free to use any Lightning checkpoint without Lightning.

-
import torch
-
-model = torch.load("path/to/lightning/checkpoint.ckpt")
-model.eval()
-
-
-

You can also pull out the specific modules you want out of the checkpoint:

-
model = torch.load("path/to/lightning/checkpoint.ckpt")
-encoder = model["encoder"]
-encoder.eval()
-
-
-
-
-
-

Extract nn.Module from Lightning checkpoints

-

You can also load the saved checkpoint and use it as a regular torch.nn.Module. You can extract all your torch.nn.Module -and load the weights using the checkpoint saved using LightningModule after training. For this, we recommend copying the exact implementation -from your LightningModule init and forward method.

-
class Encoder(nn.Module):
-    ...
-
-
-class Decoder(nn.Module):
-    ...
-
-
-class AutoEncoderProd(nn.Module):
-    def __init__(self):
-        super().__init__()
-        self.encoder = Encoder()
-        self.decoder = Decoder()
-
-    def forward(self, x):
-        return self.encoder(x)
-
-
-class AutoEncoderSystem(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.auto_encoder = AutoEncoderProd()
-
-    def forward(self, x):
-        return self.auto_encoder.encoder(x)
-
-    def training_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.auto_encoder.encoder(x)
-        y_hat = self.auto_encoder.decoder(y_hat)
-        loss = ...
-        return loss
-
-
-# train it
-trainer = Trainer(devices=2, accelerator="gpu", strategy="ddp")
-model = AutoEncoderSystem()
-trainer.fit(model, train_dataloader, val_dataloader)
-trainer.save_checkpoint("best_model.ckpt")
-
-
-# create the PyTorch model and load the checkpoint weights
-model = AutoEncoderProd()
-checkpoint = torch.load("best_model.ckpt")
-hyper_parameters = checkpoint["hyper_parameters"]
-
-# if you want to restore any hyperparameters, you can pass them too
-model = AutoEncoderProd(**hyper_parameters)
-
-state_dict = checkpoint["state_dict"]
-
-# update keys by dropping `auto_encoder.`
-for key in list(model_weights):
-    model_weights[key.replace("auto_encoder.", "")] = model_weights.pop(key)
-
-model.load_state_dict(model_weights)
-model.eval()
-x = torch.randn(1, 64)
-
-with torch.no_grad():
-    y_hat = model(x)
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/ecosystem/asr_nlp_tts.html b/docs/ecosystem/asr_nlp_tts.html deleted file mode 100644 index b9566d8..0000000 --- a/docs/ecosystem/asr_nlp_tts.html +++ /dev/null @@ -1,1452 +0,0 @@ - - - - - - - - - - - - - - Conversational AI — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Conversational AI
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Conversational AI

-

These are amazing ecosystems to help with Automatic Speech Recognition (ASR), Natural Language Processing (NLP), and Text to speech (TTS).

-
-
-

NeMo

-

NVIDIA NeMo is a toolkit for building new State-of-the-Art -Conversational AI models. NeMo has separate collections for Automatic Speech Recognition (ASR), -Natural Language Processing (NLP), and Text-to-Speech (TTS) models. Each collection consists of -prebuilt modules that include everything needed to train on your data. -Every module can easily be customized, extended, and composed to create new Conversational AI -model architectures.

-

Conversational AI architectures are typically very large and require a lot of data and compute -for training. NeMo uses PyTorch Lightning for easy and performant multi-GPU/multi-node -mixed-precision training.

-
-

Note

-

Every NeMo model is a LightningModule that comes equipped with all supporting infrastructure for training and reproducibility.

-
-
-
-

NeMo Models

-

NeMo Models contain everything needed to train and reproduce state of the art Conversational AI -research and applications, including:

-
    -
  • neural network architectures

  • -
  • datasets/data loaders

  • -
  • data preprocessing/postprocessing

  • -
  • data augmentors

  • -
  • optimizers and schedulers

  • -
  • tokenizers

  • -
  • language models

  • -
-

NeMo uses Hydra for configuring both NeMo models and the PyTorch Lightning Trainer. -Depending on the domain and application, many different AI libraries will have to be configured -to build the application. Hydra makes it easy to bring all of these libraries together -so that each can be configured from .yaml or the Hydra CLI.

-
-

Note

-

Every NeMo model has an example configuration file and a corresponding script that contains all configurations needed for training.

-
-

The end result of using NeMo, Pytorch Lightning, and Hydra is that -NeMo models all have the same look and feel. This makes it easy to do Conversational AI research -across multiple domains. NeMo models are also fully compatible with the PyTorch ecosystem.

-
-

Installing NeMo

-

Before installing NeMo, please install Cython first.

-
pip install Cython
-
-
-

For ASR and TTS models, also install these linux utilities.

-
apt-get update && apt-get install -y libsndfile1 ffmpeg
-
-
-

Then installing the latest NeMo release is a simple pip install.

-
pip install nemo_toolkit[all]==1.0.0b1
-
-
-

To install the main branch from GitHub:

-
python -m pip install git+https://github.com/NVIDIA/NeMo.git@main#egg=nemo_toolkit[all]
-
-
-

To install from a local clone of NeMo:

-
./reinstall.sh # from cloned NeMo's git root
-
-
-

For Docker users, the NeMo container is available on -NGC.

-
docker pull nvcr.io/nvidia/nemo:v1.0.0b1
-
-
-
docker run --runtime=nvidia -it --rm -v --shm-size=8g -p 8888:8888 -p 6006:6006 --ulimit memlock=-1 --ulimit stack=67108864 nvcr.io/nvidia/nemo:v1.0.0b1
-
-
-
-
-

Experiment Manager

-

NeMo’s Experiment Manager leverages PyTorch Lightning for model checkpointing, -TensorBoard Logging, and Weights and Biases logging. The Experiment Manager is included by default -in all NeMo example scripts.

-
exp_manager(trainer, cfg.get("exp_manager", None))
-
-
-

And is configurable via .yaml with Hydra.

-
exp_manager:
-    exp_dir: null
-    name: *name
-    create_tensorboard_logger: True
-    create_checkpoint_callback: True
-
-
-

Optionally launch Tensorboard to view training results in ./nemo_experiments (by default).

-
tensorboard --bind_all --logdir nemo_experiments
-
-
-
-
-
-
-

Automatic Speech Recognition (ASR)

-

Everything needed to train Convolutional ASR models is included with NeMo. -NeMo supports multiple Speech Recognition architectures, including Jasper and QuartzNet. -NeMo Speech Models -can be trained from scratch on custom datasets or -fine-tuned using pre-trained checkpoints trained on thousands of hours of audio -that can be restored for immediate use.

-

Some typical ASR tasks are included with NeMo:

- -

See this asr notebook -for a full tutorial on doing ASR with NeMo, PyTorch Lightning, and Hydra.

-
-

Specify ASR Model Configurations with YAML File

-

NeMo Models and the PyTorch Lightning Trainer can be fully configured from .yaml files using Hydra.

-

See this asr config -for the entire speech to text .yaml file.

-
# configure the PyTorch Lightning Trainer
-trainer:
-    gpus: 0 # number of gpus
-    max_epochs: 5
-    max_steps: null # computed at runtime if not set
-    num_nodes: 1
-    accelerator: ddp
-    ...
-# configure the ASR model
-model:
-    ...
-    encoder:
-        cls: nemo.collections.asr.modules.ConvASREncoder
-        params:
-            feat_in: *n_mels
-            activation: relu
-            conv_mask: true
-
-        jasper:
-            - filters: 128
-            repeat: 1
-            kernel: [11]
-            stride: [1]
-            dilation: [1]
-            dropout: *dropout
-            ...
-    # all other configuration, data, optimizer, preprocessor, etc
-    ...
-
-
-
-
-

Developing ASR Model From Scratch

-

speech_to_text.py

-
# hydra_runner calls hydra.main and is useful for multi-node experiments
-@hydra_runner(config_path="conf", config_name="config")
-def main(cfg):
-    trainer = Trainer(**cfg.trainer)
-    asr_model = EncDecCTCModel(cfg.model, trainer)
-    trainer.fit(asr_model)
-
-
-

Hydra makes every aspect of the NeMo model, -including the PyTorch Lightning Trainer, customizable from the command line.

-
python NeMo/examples/asr/speech_to_text.py --config-name=quartznet_15x5 \
-    trainer.accelerator=gpu \
-    trainer.devices=4 \
-    trainer.max_epochs=128 \
-    +trainer.precision=16 \
-    model.train_ds.manifest_filepath=<PATH_TO_DATA>/librispeech-train-all.json \
-    model.validation_ds.manifest_filepath=<PATH_TO_DATA>/librispeech-dev-other.json \
-    model.train_ds.batch_size=64 \
-    +model.validation_ds.num_workers=16 \
-    +model.train_ds.num_workers=16
-
-
-
-

Note

-

Training NeMo ASR models can take days/weeks so it is highly recommended to use multiple GPUs and multiple nodes with the PyTorch Lightning Trainer.

-
-
-
-

Using State-Of-The-Art Pre-trained ASR Model

-

Transcribe audio with QuartzNet model pretrained on ~3300 hours of audio.

-
quartznet = EncDecCTCModel.from_pretrained("QuartzNet15x5Base-En")
-
-files = ["path/to/my.wav"]  # file duration should be less than 25 seconds
-
-for fname, transcription in zip(files, quartznet.transcribe(paths2audio_files=files)):
-    print(f"Audio in {fname} was recognized as: {transcription}")
-
-
-

To see the available pretrained checkpoints:

-
EncDecCTCModel.list_available_models()
-
-
-
-
-

NeMo ASR Model Under the Hood

-

Any aspect of ASR training or model architecture design can easily be customized -with PyTorch Lightning since every NeMo model is a Lightning Module.

-
class EncDecCTCModel(ASRModel):
-    """Base class for encoder decoder CTC-based models."""
-
-    ...
-
-    def forward(self, input_signal, input_signal_length):
-        processed_signal, processed_signal_len = self.preprocessor(
-            input_signal=input_signal,
-            length=input_signal_length,
-        )
-        # Spec augment is not applied during evaluation/testing
-        if self.spec_augmentation is not None and self.training:
-            processed_signal = self.spec_augmentation(input_spec=processed_signal)
-        encoded, encoded_len = self.encoder(audio_signal=processed_signal, length=processed_signal_len)
-        log_probs = self.decoder(encoder_output=encoded)
-        greedy_predictions = log_probs.argmax(dim=-1, keepdim=False)
-        return log_probs, encoded_len, greedy_predictions
-
-    # PTL-specific methods
-    def training_step(self, batch, batch_nb):
-        audio_signal, audio_signal_len, transcript, transcript_len = batch
-        log_probs, encoded_len, predictions = self.forward(
-            input_signal=audio_signal, input_signal_length=audio_signal_len
-        )
-        loss_value = self.loss(
-            log_probs=log_probs, targets=transcript, input_lengths=encoded_len, target_lengths=transcript_len
-        )
-        wer_num, wer_denom = self._wer(predictions, transcript, transcript_len)
-        self.log_dict(
-            {
-                "train_loss": loss_value,
-                "training_batch_wer": wer_num / wer_denom,
-                "learning_rate": self._optimizer.param_groups[0]["lr"],
-            }
-        )
-        return loss_value
-
-
-
-
-

Neural Types in NeMo ASR

-

NeMo Models and Neural Modules come with Neural Type checking. -Neural type checking is extremely useful when combining many different neural -network architectures for a production-grade application.

-
@property
-def input_types(self) -> Optional[Dict[str, NeuralType]]:
-    if hasattr(self.preprocessor, "_sample_rate"):
-        audio_eltype = AudioSignal(freq=self.preprocessor._sample_rate)
-    else:
-        audio_eltype = AudioSignal()
-    return {
-        "input_signal": NeuralType(("B", "T"), audio_eltype),
-        "input_signal_length": NeuralType(tuple("B"), LengthsType()),
-    }
-
-
-@property
-def output_types(self) -> Optional[Dict[str, NeuralType]]:
-    return {
-        "outputs": NeuralType(("B", "T", "D"), LogprobsType()),
-        "encoded_lengths": NeuralType(tuple("B"), LengthsType()),
-        "greedy_predictions": NeuralType(("B", "T"), LabelsType()),
-    }
-
-
-
-
-
-
-

Natural Language Processing (NLP)

-

Everything needed to finetune BERT-like language models for NLP tasks is included with NeMo. -NeMo NLP Models -include HuggingFace Transformers -and NVIDIA Megatron-LM BERT and Bio-Megatron models. -NeMo can also be used for pretraining BERT-based language models from HuggingFace.

-

Any of the HuggingFace encoders or Megatron-LM encoders can easily be used for the NLP tasks -that are included with NeMo:

- -
-

Named Entity Recognition (NER)

-

NER (or more generally token classification) is the NLP task of detecting and classifying key information (entities) in text. -This task is very popular in Healthcare and Finance. In finance, for example, it can be important to identify -geographical, geopolitical, organizational, persons, events, and natural phenomenon entities. -See this NER notebook -for a full tutorial on doing NER with NeMo, PyTorch Lightning, and Hydra.

-
-
-

Specify NER Model Configurations with YAML File

-
-

Note

-

NeMo Models and the PyTorch Lightning Trainer can be fully configured from .yaml files using Hydra.

-
-

See this token classification config -for the entire NER (token classification) .yaml file.

-
# configure any argument of the PyTorch Lightning Trainer
-trainer:
-    gpus: 1 # the number of gpus, 0 for CPU
-    num_nodes: 1
-    max_epochs: 5
-    ...
-# configure any aspect of the token classification model here
-model:
-    dataset:
-        data_dir: ??? # /path/to/data
-        class_balancing: null # choose from [null, weighted_loss]. Weighted_loss enables the weighted class balancing of the loss, may be used for handling unbalanced classes
-        max_seq_length: 128
-        ...
-  tokenizer:
-    tokenizer_name: ${model.language_model.pretrained_model_name} # or sentencepiece
-    vocab_file: null # path to vocab file
-    ...
-# the language model can be from HuggingFace or Megatron-LM
-language_model:
-    pretrained_model_name: bert-base-uncased
-    lm_checkpoint: null
-    ...
-# the classifier for the downstream task
-  head:
-    num_fc_layers: 2
-    fc_dropout: 0.5
-    activation: 'relu'
-    ...
-# all other configuration: train/val/test/ data, optimizer, experiment manager, etc
-...
-
-
-
-
-

Developing NER Model From Scratch

-

token_classification.py

-
# hydra_runner calls hydra.main and is useful for multi-node experiments
-@hydra_runner(config_path="conf", config_name="token_classification_config")
-def main(cfg: DictConfig) -> None:
-    trainer = pl.Trainer(**cfg.trainer)
-    model = TokenClassificationModel(cfg.model, trainer=trainer)
-    trainer.fit(model)
-
-
-

After training, we can do inference with the saved NER model using PyTorch Lightning.

-

Inference from file:

-
gpu = 1 if cfg.trainer.gpus != 0 else 0
-trainer = pl.Trainer(accelerator="gpu", devices=gpu)
-model.set_trainer(trainer)
-model.evaluate_from_file(
-    text_file=os.path.join(cfg.model.dataset.data_dir, cfg.model.validation_ds.text_file),
-    labels_file=os.path.join(cfg.model.dataset.data_dir, cfg.model.validation_ds.labels_file),
-    output_dir=exp_dir,
-    add_confusion_matrix=True,
-    normalize_confusion_matrix=True,
-)
-
-
-

Or we can run inference on a few examples:

-
queries = ["we bought four shirts from the nvidia gear store in santa clara.", "Nvidia is a company in Santa Clara."]
-results = model.add_predictions(queries)
-
-for query, result in zip(queries, results):
-    logging.info(f"Query : {query}")
-    logging.info(f"Result: {result.strip()}\n")
-
-
-

Hydra makes every aspect of the NeMo model, including the PyTorch Lightning Trainer, customizable from the command line.

-
python token_classification.py \
-    model.language_model.pretrained_model_name=bert-base-cased \
-    model.head.num_fc_layers=2 \
-    model.dataset.data_dir=/path/to/my/data  \
-    trainer.max_epochs=5 \
-    trainer.accelerator=gpu \
-    trainer.devices=[0,1]
-
-
-
-
-
-

Tokenizers

-

Tokenization is the process of converting natural language text into integer arrays -which can be used for machine learning. -For NLP tasks, tokenization is an essential part of data preprocessing. -NeMo supports all BERT-like model tokenizers from -HuggingFace’s AutoTokenizer -and also supports Google’s SentencePieceTokenizer -which can be trained on custom data.

-

To see the list of supported tokenizers:

-
from nemo.collections import nlp as nemo_nlp
-
-nemo_nlp.modules.get_tokenizer_list()
-
-
-

See this tokenizer notebook -for a full tutorial on using tokenizers in NeMo.

-
-
-

Language Models

-

Language models are used to extract information from (tokenized) text. -Much of the state-of-the-art in natural language processing is achieved -by fine-tuning pretrained language models on the downstream task.

-

With NeMo, you can either pretrain -a BERT model on your data or use a pretrained language model from HuggingFace Transformers -or NVIDIA Megatron-LM.

-

To see the list of language models available in NeMo:

-
nemo_nlp.modules.get_pretrained_lm_models_list(include_external=True)
-
-
-

Easily switch between any language model in the above list by using .get_lm_model.

-
nemo_nlp.modules.get_lm_model(pretrained_model_name="distilbert-base-uncased")
-
-
-

See this language model notebook -for a full tutorial on using pretrained language models in NeMo.

-
-
-

Using a Pre-trained NER Model

-

NeMo has pre-trained NER models that can be used -to get started with Token Classification right away. -Models are automatically downloaded from NGC, -cached locally to disk, -and loaded into GPU memory using the .from_pretrained method.

-
# load pre-trained NER model
-pretrained_ner_model = TokenClassificationModel.from_pretrained(model_name="NERModel")
-
-# define the list of queries for inference
-queries = [
-    "we bought four shirts from the nvidia gear store in santa clara.",
-    "Nvidia is a company.",
-    "The Adventures of Tom Sawyer by Mark Twain is an 1876 novel about a young boy growing "
-    + "up along the Mississippi River.",
-]
-results = pretrained_ner_model.add_predictions(queries)
-
-for query, result in zip(queries, results):
-    print()
-    print(f"Query : {query}")
-    print(f"Result: {result.strip()}\n")
-
-
-
-
-

NeMo NER Model Under the Hood

-

Any aspect of NLP training or model architecture design can easily be customized with PyTorch Lightning -since every NeMo model is a Lightning Module.

-
class TokenClassificationModel(ModelPT):
-    """
-    Token Classification Model with BERT, applicable for tasks such as Named Entity Recognition
-    """
-
-    ...
-
-    def forward(self, input_ids, token_type_ids, attention_mask):
-        hidden_states = self.bert_model(
-            input_ids=input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask
-        )
-        logits = self.classifier(hidden_states=hidden_states)
-        return logits
-
-    # PTL-specific methods
-    def training_step(self, batch, batch_idx):
-        """
-        Lightning calls this inside the training loop with the data from the training dataloader
-        passed in as `batch`.
-        """
-        input_ids, input_type_ids, input_mask, subtokens_mask, loss_mask, labels = batch
-        logits = self(input_ids=input_ids, token_type_ids=input_type_ids, attention_mask=input_mask)
-
-        loss = self.loss(logits=logits, labels=labels, loss_mask=loss_mask)
-        self.log_dict({"train_loss": loss, "lr": self._optimizer.param_groups[0]["lr"]})
-        return loss
-
-    ...
-
-
-
-
-

Neural Types in NeMo NLP

-

NeMo Models and Neural Modules come with Neural Type checking. -Neural type checking is extremely useful when combining many different neural network architectures -for a production-grade application.

-
@property
-def input_types(self) -> Optional[Dict[str, NeuralType]]:
-    return self.bert_model.input_types
-
-
-@property
-def output_types(self) -> Optional[Dict[str, NeuralType]]:
-    return self.classifier.output_types
-
-
-
-
-
-
-

Text-To-Speech (TTS)

-

Everything needed to train TTS models and generate audio is included with NeMo. -NeMo TTS Models -can be trained from scratch on your own data or pretrained models can be downloaded -automatically. NeMo currently supports a two step inference procedure. -First, a model is used to generate a mel spectrogram from text. -Second, a model is used to generate audio from a mel spectrogram.

-

Mel Spectrogram Generators:

- -

Audio Generators:

- -
-

Specify TTS Model Configurations with YAML File

-
-

Note

-

NeMo Models and PyTorch Lightning Trainer can be fully configured from .yaml files using Hydra.

-
-

tts/conf/glow_tts.yaml

-
# configure the PyTorch Lightning Trainer
-trainer:
-    gpus: -1 # number of gpus
-    max_epochs: 350
-    num_nodes: 1
-    accelerator: ddp
-    ...
-
-# configure the TTS model
-model:
-    ...
-    encoder:
-        cls: nemo.collections.tts.modules.glow_tts.TextEncoder
-            params:
-            n_vocab: 148
-            out_channels: *n_mels
-            hidden_channels: 192
-            filter_channels: 768
-            filter_channels_dp: 256
-            ...
-# all other configuration, data, optimizer, parser, preprocessor, etc
-...
-
-
-
-
-

Developing TTS Model From Scratch

-

tts/glow_tts.py

-
# hydra_runner calls hydra.main and is useful for multi-node experiments
-@hydra_runner(config_path="conf", config_name="glow_tts")
-def main(cfg):
-    trainer = pl.Trainer(**cfg.trainer)
-    model = GlowTTSModel(cfg=cfg.model, trainer=trainer)
-    trainer.fit(model)
-
-
-

Hydra makes every aspect of the NeMo model, including the PyTorch Lightning Trainer, customizable from the command line.

-
python NeMo/examples/tts/glow_tts.py \
-    trainer.accelerator=gpu \
-    trainer.devices=4 \
-    trainer.max_epochs=400 \
-    ...
-    train_dataset=/path/to/train/data \
-    validation_datasets=/path/to/val/data \
-    model.train_ds.batch_size = 64 \
-
-
-
-

Note

-

Training NeMo TTS models from scratch can take days or weeks so it is highly recommended to use multiple GPUs and multiple nodes with the PyTorch Lightning Trainer.

-
-
-
-

Using State-Of-The-Art Pre-trained TTS Model

-

Generate speech using models trained on LJSpeech <https://keithito.com/LJ-Speech-Dataset/>, -around 24 hours of single speaker data.

-

See this TTS notebook -for a full tutorial on generating speech with NeMo, PyTorch Lightning, and Hydra.

-
# load pretrained spectrogram model
-spec_gen = SpecModel.from_pretrained("GlowTTS-22050Hz").cuda()
-
-# load pretrained Generators
-vocoder = WaveGlowModel.from_pretrained("WaveGlow-22050Hz").cuda()
-
-
-def infer(spec_gen_model, vocder_model, str_input):
-    with torch.no_grad():
-        parsed = spec_gen.parse(text_to_generate)
-        spectrogram = spec_gen.generate_spectrogram(tokens=parsed)
-        audio = vocoder.convert_spectrogram_to_audio(spec=spectrogram)
-    if isinstance(spectrogram, torch.Tensor):
-        spectrogram = spectrogram.to("cpu").numpy()
-    if len(spectrogram.shape) == 3:
-        spectrogram = spectrogram[0]
-    if isinstance(audio, torch.Tensor):
-        audio = audio.to("cpu").numpy()
-    return spectrogram, audio
-
-
-text_to_generate = input("Input what you want the model to say: ")
-spec, audio = infer(spec_gen, vocoder, text_to_generate)
-
-
-

To see the available pretrained checkpoints:

-
# spec generator
-GlowTTSModel.list_available_models()
-
-# vocoder
-WaveGlowModel.list_available_models()
-
-
-
-
-

NeMo TTS Model Under the Hood

-

Any aspect of TTS training or model architecture design can easily -be customized with PyTorch Lightning since every NeMo model is a LightningModule.

-

glow_tts.py

-
class GlowTTSModel(SpectrogramGenerator):
-    """
-    GlowTTS model used to generate spectrograms from text
-    Consists of a text encoder and an invertible spectrogram decoder
-    """
-
-    ...
-    # NeMo models come with neural type checking
-    @typecheck(
-        input_types={
-            "x": NeuralType(("B", "T"), TokenIndex()),
-            "x_lengths": NeuralType(("B"), LengthsType()),
-            "y": NeuralType(("B", "D", "T"), MelSpectrogramType(), optional=True),
-            "y_lengths": NeuralType(("B"), LengthsType(), optional=True),
-            "gen": NeuralType(optional=True),
-            "noise_scale": NeuralType(optional=True),
-            "length_scale": NeuralType(optional=True),
-        }
-    )
-    def forward(self, *, x, x_lengths, y=None, y_lengths=None, gen=False, noise_scale=0.3, length_scale=1.0):
-        if gen:
-            return self.glow_tts.generate_spect(
-                text=x, text_lengths=x_lengths, noise_scale=noise_scale, length_scale=length_scale
-            )
-        else:
-            return self.glow_tts(text=x, text_lengths=x_lengths, spect=y, spect_lengths=y_lengths)
-
-    ...
-
-    def step(self, y, y_lengths, x, x_lengths):
-        z, y_m, y_logs, logdet, logw, logw_, y_lengths, attn = self(
-            x=x, x_lengths=x_lengths, y=y, y_lengths=y_lengths, gen=False
-        )
-
-        l_mle, l_length, logdet = self.loss(
-            z=z,
-            y_m=y_m,
-            y_logs=y_logs,
-            logdet=logdet,
-            logw=logw,
-            logw_=logw_,
-            x_lengths=x_lengths,
-            y_lengths=y_lengths,
-        )
-
-        loss = sum([l_mle, l_length])
-
-        return l_mle, l_length, logdet, loss, attn
-
-    # PTL-specific methods
-    def training_step(self, batch, batch_idx):
-        y, y_lengths, x, x_lengths = batch
-
-        y, y_lengths = self.preprocessor(input_signal=y, length=y_lengths)
-
-        l_mle, l_length, logdet, loss, _ = self.step(y, y_lengths, x, x_lengths)
-
-        self.log_dict({"l_mle": l_mle, "l_length": l_length, "logdet": logdet}, prog_bar=True)
-        return loss
-
-    ...
-
-
-
-
-

Neural Types in NeMo TTS

-

NeMo Models and Neural Modules come with Neural Type checking. -Neural type checking is extremely useful when combining many different neural network architectures -for a production-grade application.

-
@typecheck(
-    input_types={
-        "x": NeuralType(("B", "T"), TokenIndex()),
-        "x_lengths": NeuralType(("B"), LengthsType()),
-        "y": NeuralType(("B", "D", "T"), MelSpectrogramType(), optional=True),
-        "y_lengths": NeuralType(("B"), LengthsType(), optional=True),
-        "gen": NeuralType(optional=True),
-        "noise_scale": NeuralType(optional=True),
-        "length_scale": NeuralType(optional=True),
-    }
-)
-def forward(self, *, x, x_lengths, y=None, y_lengths=None, gen=False, noise_scale=0.3, length_scale=1.0):
-    ...
-
-
-
-
-
-
-

Learn More

- -
-

Note

-

NeMo tutorial notebooks can be run on Google Colab.

-
-

NVIDIA NeMo is actively being developed on GitHub. -Contributions are welcome!

-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/ecosystem/bolts.html b/docs/ecosystem/bolts.html deleted file mode 100644 index d57cea8..0000000 --- a/docs/ecosystem/bolts.html +++ /dev/null @@ -1,774 +0,0 @@ - - - - - - - - - - - - - - Lightning Bolts — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Lightning Bolts

-

PyTorch Lightning Bolts, is our official collection -of prebuilt models across many research domains.

-
pip install lightning-bolts
-
-
-

In bolts we have:

-
    -
  • A collection of pretrained state-of-the-art models.

  • -
  • A collection of models designed to bootstrap your research.

  • -
  • A collection of callbacks, transforms, full datasets.

  • -
  • All models work on CPUs, TPUs, GPUs and 16-bit precision.

  • -
-
-
-

Quality control

-

The Lightning community builds bolts and contributes them to Bolts. -The lightning team guarantees that contributions are:

-
    -
  • Rigorously Tested (CPUs, GPUs, TPUs).

  • -
  • Rigorously Documented.

  • -
  • Standardized via PyTorch Lightning.

  • -
  • Optimized for speed.

  • -
  • Checked for correctness.

  • -
-
-
-
-

Example 1: Pretrained, prebuilt models

-
from pl_bolts.models import VAE, GPT2, ImageGPT, PixelCNN
-from pl_bolts.models.self_supervised import AMDIM, CPCV2, SimCLR, MocoV2
-from pl_bolts.models import LinearRegression, LogisticRegression
-from pl_bolts.models.gans import GAN
-from pl_bolts.callbacks import PrintTableMetricsCallback
-from pl_bolts.datamodules import FashionMNISTDataModule, CIFAR10DataModule, ImagenetDataModule
-
-
-
-
-
-

Example 2: Extend for faster research

-

Bolts are contributed with benchmarks and continuous-integration tests. This means -you can trust the implementations and use them to bootstrap your research much faster.

-
from pl_bolts.models import ImageGPT
-from pl_bolts.self_supervised import SimCLR
-
-
-class VideoGPT(ImageGPT):
-    def training_step(self, batch, batch_idx):
-        x, y = batch
-        x = _shape_input(x)
-
-        logits = self.gpt(x)
-        simclr_features = self.simclr(x)
-
-        # -----------------
-        # do something new with GPT logits + simclr_features
-        # -----------------
-
-        loss = self.criterion(logits.view(-1, logits.size(-1)), x.view(-1).long())
-
-        self.log("loss", loss)
-        return loss
-
-
-
-
-
-

Example 3: Callbacks

-

We also have a collection of callbacks.

-
from pl_bolts.callbacks import PrintTableMetricsCallback
-import pytorch_lightning as pl
-
-trainer = pl.Trainer(callbacks=[PrintTableMetricsCallback()])
-
-# loss│train_loss│val_loss│epoch
-# ──────────────────────────────
-# 2.2541470527648926│2.2541470527648926│2.2158432006835938│0
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/ecosystem/community_examples.html b/docs/ecosystem/community_examples.html deleted file mode 100644 index a118818..0000000 --- a/docs/ecosystem/community_examples.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - - - - - - - - - Community Examples — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Community Examples
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/ecosystem/ecosystem-ci.html b/docs/ecosystem/ecosystem-ci.html deleted file mode 100644 index c87cf75..0000000 --- a/docs/ecosystem/ecosystem-ci.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Ecosystem CI — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Ecosystem CI

-

Ecosystem CI automates issue discovery for your projects against Lightning nightly and releases. -It is a lightweight repository that provides easy configuration of Continues Integration running on CPUs and GPUs. -Any user who wants to keep their project aligned with current and future Lightning releases can use the EcoSystem CI to configure their integrations. -Read more: Stay Ahead of Breaking Changes with the New Lightning Ecosystem CI

-
-
-

Integrate a New Project

-

Follow the instructions below to add a new project to the PyTorch Lightning ecosystem.

-
    -
  1. Fork the ecosystem CI repository to be able to create a new Pull Request and work within a specific branch.

  2. -
  3. Create a new config file in configs/<Organization-name> folder and call it <project-name>.yaml.

  4. -
  5. Define runtime for CPU and link the config for GPU: -For CPU integrations, list OS and Python version combination to be running with GitHub actions. -For GPU integrations, you only add the path to the config (OS/Linux and Python version is fixed) to be running with Azure pipelines.

  6. -
  7. Add a Contact to the .github/CODEOWNERS list for your organization folder or just a single project.

  8. -
  9. Create a Draft PR with all mentioned requirements.

  10. -
  11. Join our Slack (Optional) channel #alerts-ecosystem-ci to be notified if your project is breaking.

  12. -
-

To learn more about Ecosystem CI, please refer to the Ecosystem CI repo. -Also, note that some particular implementation details described above may evolve over time.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/ecosystem/flash.html b/docs/ecosystem/flash.html deleted file mode 100644 index 91e2e95..0000000 --- a/docs/ecosystem/flash.html +++ /dev/null @@ -1,757 +0,0 @@ - - - - - - - - - - - - - - Lightning Flash — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Lightning Flash

-

Lightning Flash is a high-level deep learning framework for fast prototyping, baselining, fine-tuning, and solving deep learning problems. -Flash makes complex AI recipes for over 15 tasks across 7 data domains accessible to all. -It is built for beginners with a simple API that requires very little deep learning background, and for data scientists, Kagglers, applied ML practitioners, and deep learning researchers that -want a quick way to get a deep learning baseline with advanced features PyTorch Lightning offers.

-
pip install lightning-flash
-
-
-
-
-

Using Lightning Flash in 3 Steps!

-
-

1. Load your Data

-

All data loading in Flash is performed via a from_* classmethod of a DataModule. -Which DataModule to use and which from_* methods are available depends on the task you want to perform. -For example, for image segmentation where your data is stored in folders, you would use the SemanticSegmentationData’s from_folders method:

-
from flash.image import SemanticSegmentationData
-
-dm = SemanticSegmentationData.from_folders(
-    train_folder="data/CameraRGB",
-    train_target_folder="data/CameraSeg",
-    val_split=0.1,
-    image_size=(256, 256),
-    num_classes=21,
-)
-
-
-
-
-
-

2. Configure your Model

-

Our tasks come loaded with pre-trained backbones and (where applicable) heads. -You can view the available backbones to use with your task using available_backbones. -Once you’ve chosen, create the model:

-
from flash.image import SemanticSegmentation
-
-print(SemanticSegmentation.available_heads())
-# ['deeplabv3', 'deeplabv3plus', 'fpn', ..., 'unetplusplus']
-
-print(SemanticSegmentation.available_backbones("fpn"))
-# ['densenet121', ..., 'xception'] # + 113 models
-
-print(SemanticSegmentation.available_pretrained_weights("efficientnet-b0"))
-# ['imagenet', 'advprop']
-
-model = SemanticSegmentation(head="fpn", backbone="efficientnet-b0", pretrained="advprop", num_classes=dm.num_classes)
-
-
-
-
-
-

3. Finetune!

-
from flash import Trainer
-
-trainer = Trainer(max_epochs=3)
-trainer.finetune(model, datamodule=datamodule, strategy="freeze")
-trainer.save_checkpoint("semantic_segmentation_model.pt")
-
-
-

To learn more about Lightning Flash, please refer to the Lightning Flash documentation.

-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/ecosystem/metrics.html b/docs/ecosystem/metrics.html deleted file mode 100644 index 40f9b18..0000000 --- a/docs/ecosystem/metrics.html +++ /dev/null @@ -1,771 +0,0 @@ - - - - - - - - - - - - - - TorchMetrics — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

TorchMetrics

-

TorchMetrics is a collection of machine learning metrics for distributed, -scalable PyTorch models and an easy-to-use API to create custom metrics. It has a collection of 60+ PyTorch metrics implementations and -is rigorously tested for all edge cases.

-
pip install torchmetrics
-
-
-

In TorchMetrics, we offer the following benefits:

-
    -
  • A standardized interface to increase reproducibility

  • -
  • Reduced Boilerplate

  • -
  • Distributed-training compatible

  • -
  • Rigorously tested

  • -
  • Automatic accumulation over batches

  • -
  • Automatic synchronization across multiple devices

  • -
-
-
-

Example 1: Functional Metrics

-

Below is a simple example for calculating the accuracy using the functional interface:

-
import torch
-import torchmetrics
-
-# simulate a classification problem
-preds = torch.randn(10, 5).softmax(dim=-1)
-target = torch.randint(5, (10,))
-
-acc = torchmetrics.functional.accuracy(preds, target)
-
-
-
-
-
-

Example 2: Module Metrics

-

The example below shows how to use the class-based interface:

-
import torch
-import torchmetrics
-
-# initialize metric
-metric = torchmetrics.Accuracy()
-
-n_batches = 10
-for i in range(n_batches):
-    # simulate a classification problem
-    preds = torch.randn(10, 5).softmax(dim=-1)
-    target = torch.randint(5, (10,))
-    # metric on current batch
-    acc = metric(preds, target)
-    print(f"Accuracy on batch {i}: {acc}")
-
-# metric on all batches using custom accumulation
-acc = metric.compute()
-print(f"Accuracy on all data: {acc}")
-
-# Reseting internal state such that metric ready for new data
-metric.reset()
-
-
-
-
-
-

Example 3: TorchMetrics with Lightning

-

The example below shows how to use a metric in your LightningModule:

-
class MyModel(LightningModule):
-    def __init__(self):
-        ...
-        self.accuracy = torchmetrics.Accuracy()
-
-    def training_step(self, batch, batch_idx):
-        x, y = batch
-        preds = self(x)
-        ...
-        # log step metric
-        self.accuracy(preds, y)
-        self.log("train_acc_step", self.accuracy, on_epoch=True)
-        ...
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/ecosystem/transformers.html b/docs/ecosystem/transformers.html deleted file mode 100644 index 674eea4..0000000 --- a/docs/ecosystem/transformers.html +++ /dev/null @@ -1,725 +0,0 @@ - - - - - - - - - - - - - - Lightning Transformers — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Lightning Transformers
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Lightning Transformers

-

Lightning Transformers offers a flexible interface for training and fine-tuning SOTA Transformer models -using the PyTorch Lightning Trainer.

-
pip install lightning-transformers
-
-
-

In Lightning Transformers, we offer the following benefits:

-
    -
  • Powered by PyTorch Lightning - Accelerators, custom Callbacks, Loggers, and high performance scaling with minimal changes.

  • -
  • Backed by HuggingFace Transformers models and datasets, spanning multiple modalities and tasks within NLP/Audio and Vision.

  • -
  • Task Abstraction for Rapid Research & Experimentation - Build your own custom transformer tasks across all modalities with little friction.

  • -
  • Powerful config composition backed by Hydra - simply swap out models, optimizers, schedulers task, and many more configurations without touching the code.

  • -
  • Seamless Memory and Speed Optimizations - Out-of-the-box training optimizations such as DeepSpeed ZeRO or FairScale Sharded Training with no code changes.

  • -
-
-
-

Using Lightning-Transformers

-

Lightning Transformers has a collection of tasks for common NLP problems such as language_modeling, -translation and more. To use, simply:

-
    -
  1. Pick a task to train (passed to train.py as task=)

  2. -
  3. Pick a dataset (passed to train.py as dataset=)

  4. -
  5. Customize the backbone, optimizer, or any component within the config

  6. -
  7. Add any Lightning supported parameters and optimizations

  8. -
-
python train.py \
-    task=<TASK> \
-    dataset=<DATASET>
-    backbone.pretrained_model_name_or_path=<BACKBONE> # Optionally change the HF backbone
-    optimizer=<OPTIMIZER> # Optionally specify optimizer (Default AdamW)
-    trainer.<ANY_TRAINER_FLAGS> # Optionally specify Lightning trainer arguments
-
-
-

To learn more about Lightning Transformers, please refer to the Lightning Transformers documentation.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/expertise_levels.html b/docs/expertise_levels.html deleted file mode 100644 index 14258c2..0000000 --- a/docs/expertise_levels.html +++ /dev/null @@ -1,1006 +0,0 @@ - - - - - - - - - - - - - - Level up — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level up

-

Learn enough Lightning to match the level of expertise required by your research or job.

-

-
-
-

Basic skills

-

Learn the basics of model development with Lightning. Researchers and machine learning engineers should start here.

-
-
-
-

Intermediate skills

-

Learn to scale up your models and enable collaborative model development at academic or industry research labs.

-
-
-
-

Advanced skills

-

Configure all aspects of Lightning for advanced usecases.

-
-
-
-

Expert skills

-

Customize and extend Lightning for things like custom hardware or distributed strategies.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/accelerator.html b/docs/extensions/accelerator.html deleted file mode 100644 index 8592b6d..0000000 --- a/docs/extensions/accelerator.html +++ /dev/null @@ -1,819 +0,0 @@ - - - - - - - - - - - - - - Accelerator — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Accelerator

-

The Accelerator connects a Lightning Trainer to arbitrary hardware (CPUs, GPUs, TPUs, IPUs, …). -Currently there are accelerators for:

- -

The Accelerator is part of the Strategy which manages communication across multiple devices (distributed communication). -Whenever the Trainer, the loops or any other component in Lightning needs to talk to hardware, it calls into the Strategy and the Strategy calls into the Accelerator.

-Illustration of the Strategy as a composition of the Accelerator and several plugins -

We expose Accelerators and Strategies mainly for expert users who want to extend Lightning to work with new -hardware and distributed training or clusters.

-
-
-

Create a Custom Accelerator

-

Here is how you create a new Accelerator. -Let’s pretend we want to integrate the fictional XPU accelerator and we have access to its hardware through a library -xpulib.

-
import xpulib
-
-
-class XPUAccelerator(Accelerator):
-    """Experimental support for XPU, optimized for large-scale machine learning."""
-
-    @staticmethod
-    def parse_devices(devices: Any) -> Any:
-        # Put parsing logic here how devices can be passed into the Trainer
-        # via the `devices` argument
-        return devices
-
-    @staticmethod
-    def get_parallel_devices(devices: Any) -> Any:
-        # Here, convert the device indices to actual device objects
-        return [torch.device("xpu", idx) for idx in devices]
-
-    @staticmethod
-    def auto_device_count() -> int:
-        # Return a value for auto-device selection when `Trainer(devices="auto")`
-        return xpulib.available_devices()
-
-    @staticmethod
-    def is_available() -> bool:
-        return xpulib.is_available()
-
-    def get_device_stats(self, device: Union[str, torch.device]) -> Dict[str, Any]:
-        # Return optional device statistics for loggers
-        return {}
-
-
-

Finally, add the XPUAccelerator to the Trainer:

-
from pytorch_lightning import Trainer
-
-accelerator = XPUAccelerator()
-trainer = Trainer(accelerator=accelerator, devices=2)
-
-
-

Learn more about Strategies and how they interact with the Accelerator.

-
-
-
-

Registering Accelerators

-

If you wish to switch to a custom accelerator from the CLI without code changes, you can implement the register_accelerators() class method to register your new accelerator under a shorthand name like so:

-
class XPUAccelerator(Accelerator):
-    ...
-
-    @classmethod
-    def register_accelerators(cls, accelerator_registry):
-        accelerator_registry.register(
-            "xpu",
-            cls,
-            description=f"XPU Accelerator - optimized for large-scale machine learning.",
-        )
-
-
-

Now, this is possible:

-
trainer = Trainer(accelerator="xpu")
-
-
-

Or if you are using the Lightning CLI, for example:

-
python train.py fit --trainer.accelerator=xpu --trainer.devices=2
-
-
-
-
-
-

Accelerator API

- ---- - - - - - - - - - - - - - - - - - - - - -

Accelerator

The Accelerator Base Class.

CPUAccelerator

Accelerator for CPU devices.

GPUAccelerator

Accelerator for GPU devices.

HPUAccelerator

Accelerator for HPU devices.

IPUAccelerator

Accelerator for IPUs.

TPUAccelerator

Accelerator for TPU devices.

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/callbacks.html b/docs/extensions/callbacks.html deleted file mode 100644 index 43b5687..0000000 --- a/docs/extensions/callbacks.html +++ /dev/null @@ -1,1655 +0,0 @@ - - - - - - - - - - - - - - Callback — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Callback

-
-

-
-

A callback is a self-contained program that can be reused across projects.

-

Lightning has a callback system to execute them when needed. Callbacks should capture NON-ESSENTIAL -logic that is NOT required for your lightning module to run.

-

Here’s the flow of how the callback hooks are executed:

-

An overall Lightning system should have:

-
    -
  1. Trainer for all engineering

  2. -
  3. LightningModule for all research code.

  4. -
  5. Callbacks for non-essential code.

  6. -
-
-

-
-

Example:

-
from pytorch_lightning.callbacks import Callback
-
-
-class MyPrintingCallback(Callback):
-    def on_train_start(self, trainer, pl_module):
-        print("Training is starting")
-
-    def on_train_end(self, trainer, pl_module):
-        print("Training is ending")
-
-
-trainer = Trainer(callbacks=[MyPrintingCallback()])
-
-
-

We successfully extended functionality without polluting our super clean -lightning module research code.

-
-
-

Examples

-

You can do pretty much anything with callbacks.

- -
-
-
-

Built-in Callbacks

-

Lightning has a few built-in callbacks.

-
-

Note

-

For a richer collection of callbacks, check out our -bolts library.

-
- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

BackboneFinetuning

Finetune a backbone model based on a learning rate user-defined scheduling.

BaseFinetuning

This class implements the base logic for writing your own Finetuning Callback.

BasePredictionWriter

Base class to implement how the predictions should be stored.

Callback

Abstract base class used to build new callbacks.

DeviceStatsMonitor

Automatically monitors and logs device stats during training stage.

EarlyStopping

Monitor a metric and stop training when it stops improving.

GradientAccumulationScheduler

Change gradient accumulation factor according to scheduling.

LambdaCallback

Create a simple callback on the fly using lambda functions.

LearningRateMonitor

Automatically monitor and logs learning rate for learning rate schedulers during training.

ModelCheckpoint

Save the model periodically by monitoring a quantity.

ModelPruning

Model pruning Callback, using PyTorch's prune utilities.

ModelSummary

Generates a summary of all layers in a LightningModule.

ProgressBarBase

The base class for progress bars in Lightning.

QuantizationAwareTraining

Quantization allows speeding up inference and decreasing memory requirements by performing computations and storing tensors at lower bitwidths (such as INT8 or FLOAT16) than floating point precision.

RichModelSummary

Generates a summary of all layers in a LightningModule with rich text formatting.

RichProgressBar

Create a progress bar with rich text formatting.

-

StochasticWeightAveraging

Implements the Stochastic Weight Averaging (SWA) Callback to average a model.

Timer

The Timer callback tracks the time spent in the training, validation, and test loops and interrupts the Trainer if the given time limit for the training loop is reached.

TQDMProgressBar

This is the default progress bar used by Lightning.

-
-
-
-

Save Callback state

-

Some callbacks require internal state in order to function properly. You can optionally -choose to persist your callback’s state as part of model checkpoint files using -state_dict() and load_state_dict(). -Note that the returned state must be able to be pickled.

-

When your callback is meant to be used only as a singleton callback then implementing the above two hooks is enough -to persist state effectively. However, if passing multiple instances of the callback to the Trainer is supported, then -the callback must define a state_key property in order for Lightning -to be able to distinguish the different states when loading the callback state. This concept is best illustrated by -the following example.

-
class Counter(Callback):
-    def __init__(self, what="epochs", verbose=True):
-        self.what = what
-        self.verbose = verbose
-        self.state = {"epochs": 0, "batches": 0}
-
-    @property
-    def state_key(self):
-        # note: we do not include `verbose` here on purpose
-        return self._generate_state_key(what=self.what)
-
-    def on_train_epoch_end(self, *args, **kwargs):
-        if self.what == "epochs":
-            self.state["epochs"] += 1
-
-    def on_train_batch_end(self, *args, **kwargs):
-        if self.what == "batches":
-            self.state["batches"] += 1
-
-    def load_state_dict(self, state_dict):
-        self.state.update(state_dict)
-
-    def state_dict(self):
-        return self.state.copy()
-
-
-# two callbacks of the same type are being used
-trainer = Trainer(callbacks=[Counter(what="epochs"), Counter(what="batches")])
-
-
-

A Lightning checkpoint from this Trainer with the two stateful callbacks will include the following information:

-
{
-    "state_dict": ...,
-    "callbacks": {
-        "Counter{'what': 'batches'}": {"batches": 32, "epochs": 0},
-        "Counter{'what': 'epochs'}": {"batches": 0, "epochs": 2},
-        ...
-    }
-}
-
-
-

The implementation of a state_key is essential here. If it were missing, -Lightning would not be able to disambiguate the state for these two callbacks, and state_key -by default only defines the class name as the key, e.g., here Counter.

-
-
-
-

Best Practices

-

The following are best practices when using/designing callbacks.

-
    -
  1. Callbacks should be isolated in their functionality.

  2. -
  3. Your callback should not rely on the behavior of other callbacks in order to work properly.

  4. -
  5. Do not manually call methods from the callback.

  6. -
  7. Directly calling methods (eg. on_validation_end) is strongly discouraged.

  8. -
  9. Whenever possible, your callbacks should not depend on the order in which they are executed.

  10. -
-
-
-
-

Callback API

-

Here is the full API of methods available in the Callback base class.

-

The Callback class is the base for all the callbacks in Lightning just like the LightningModule is the base for all models. -It defines a public interface that each callback implementation must follow, the key ones are:

-
-

Properties

-
-

state_key

-
-
-Callback.state_key
-

Identifier for the state of the callback.

-

Used to store and retrieve a callback’s state from the checkpoint dictionary by -checkpoint["callbacks"][state_key]. Implementations of a callback need to provide a unique state key if 1) -the callback has state and 2) it is desired to maintain the state of multiple instances of that callback.

-
-
Return type
-

str

-
-
-
- -
-
-
-

Hooks

-
-

on_configure_sharded_model

-
-
-Callback.on_configure_sharded_model(trainer, pl_module)[source]
-
-

Deprecated since version v1.6: This callback hook was deprecated in v1.6 and will be removed in v1.8. Use setup() instead.

-
-

Called before configure sharded model.

-
-
Return type
-

None

-
-
-
- -
-
-

setup

-
-
-Callback.setup(trainer, pl_module, stage=None)[source]
-

Called when fit, validate, test, predict, or tune begins.

-
-
Return type
-

None

-
-
-
- -
-
-

teardown

-
-
-Callback.teardown(trainer, pl_module, stage=None)[source]
-

Called when fit, validate, test, predict, or tune ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_init_start

-
-
-Callback.on_init_start(trainer)[source]
-
-

Deprecated since version v1.6: This callback hook was deprecated in v1.6 and will be removed in v1.8.

-
-

Called when the trainer initialization begins, model has not yet been set.

-
-
Return type
-

None

-
-
-
- -
-
-

on_init_end

-
-
-Callback.on_init_end(trainer)[source]
-
-

Deprecated since version v1.6: This callback hook was deprecated in v1.6 and will be removed in v1.8.

-
-

Called when the trainer initialization ends, model has not yet been set.

-
-
Return type
-

None

-
-
-
- -
-
-

on_fit_start

-
-
-Callback.on_fit_start(trainer, pl_module)[source]
-

Called when fit begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_fit_end

-
-
-Callback.on_fit_end(trainer, pl_module)[source]
-

Called when fit ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_sanity_check_start

-
-
-Callback.on_sanity_check_start(trainer, pl_module)[source]
-

Called when the validation sanity check starts.

-
-
Return type
-

None

-
-
-
- -
-
-

on_sanity_check_end

-
-
-Callback.on_sanity_check_end(trainer, pl_module)[source]
-

Called when the validation sanity check ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_batch_start

-
-
-Callback.on_train_batch_start(trainer, pl_module, batch, batch_idx, unused=0)[source]
-

Called when the train batch begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_batch_end

-
-
-Callback.on_train_batch_end(trainer, pl_module, outputs, batch, batch_idx, unused=0)[source]
-

Called when the train batch ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_epoch_start

-
-
-Callback.on_train_epoch_start(trainer, pl_module)[source]
-

Called when the train epoch begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_epoch_end

-
-
-Callback.on_train_epoch_end(trainer, pl_module)[source]
-

Called when the train epoch ends.

-

To access all batch outputs at the end of the epoch, either:

-
    -
  1. Implement training_epoch_end in the LightningModule and access outputs via the module OR

  2. -
  3. Cache data across train batch hooks inside the callback implementation to post-process in this hook.

  4. -
-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_epoch_start

-
-
-Callback.on_validation_epoch_start(trainer, pl_module)[source]
-

Called when the val epoch begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_epoch_end

-
-
-Callback.on_validation_epoch_end(trainer, pl_module)[source]
-

Called when the val epoch ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_epoch_start

-
-
-Callback.on_test_epoch_start(trainer, pl_module)[source]
-

Called when the test epoch begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_epoch_end

-
-
-Callback.on_test_epoch_end(trainer, pl_module)[source]
-

Called when the test epoch ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_epoch_start

-
-
-Callback.on_predict_epoch_start(trainer, pl_module)[source]
-

Called when the predict epoch begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_epoch_end

-
-
-Callback.on_predict_epoch_end(trainer, pl_module, outputs)[source]
-

Called when the predict epoch ends.

-
-
Return type
-

None

-
-
-
- -
-
-Callback.on_epoch_end(trainer, pl_module)[source]
-
-

Deprecated since version v1.6: This callback hook was deprecated in v1.6 and will be removed in v1.8. Use -on_<train/validation/test>_epoch_end instead.

-
-

Called when either of train/val/test epoch ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_batch_start

-
-
-Callback.on_validation_batch_start(trainer, pl_module, batch, batch_idx, dataloader_idx)[source]
-

Called when the validation batch begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_batch_end

-
-
-Callback.on_validation_batch_end(trainer, pl_module, outputs, batch, batch_idx, dataloader_idx)[source]
-

Called when the validation batch ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_batch_start

-
-
-Callback.on_test_batch_start(trainer, pl_module, batch, batch_idx, dataloader_idx)[source]
-

Called when the test batch begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_batch_end

-
-
-Callback.on_test_batch_end(trainer, pl_module, outputs, batch, batch_idx, dataloader_idx)[source]
-

Called when the test batch ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_batch_start

-
-
-Callback.on_predict_batch_start(trainer, pl_module, batch, batch_idx, dataloader_idx)[source]
-

Called when the predict batch begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_batch_end

-
-
-Callback.on_predict_batch_end(trainer, pl_module, outputs, batch, batch_idx, dataloader_idx)[source]
-

Called when the predict batch ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_start

-
-
-Callback.on_train_start(trainer, pl_module)[source]
-

Called when the train begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_train_end

-
-
-Callback.on_train_end(trainer, pl_module)[source]
-

Called when the train ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_start

-
-
-Callback.on_validation_start(trainer, pl_module)[source]
-

Called when the validation loop begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_validation_end

-
-
-Callback.on_validation_end(trainer, pl_module)[source]
-

Called when the validation loop ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_start

-
-
-Callback.on_test_start(trainer, pl_module)[source]
-

Called when the test begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_test_end

-
-
-Callback.on_test_end(trainer, pl_module)[source]
-

Called when the test ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_start

-
-
-Callback.on_predict_start(trainer, pl_module)[source]
-

Called when the predict begins.

-
-
Return type
-

None

-
-
-
- -
-
-

on_predict_end

-
-
-Callback.on_predict_end(trainer, pl_module)[source]
-

Called when predict ends.

-
-
Return type
-

None

-
-
-
- -
-
-

on_keyboard_interrupt

-
-
-Callback.on_keyboard_interrupt(trainer, pl_module)[source]
-
-

Deprecated since version v1.5: This callback hook was deprecated in v1.5 in favor of on_exception and will be removed in v1.7.

-
-

Called when any trainer execution is interrupted by KeyboardInterrupt.

-
-
Return type
-

None

-
-
-
- -
-
-

on_exception

-
-
-Callback.on_exception(trainer, pl_module, exception)[source]
-

Called when any trainer execution is interrupted by an exception.

-
-
Return type
-

None

-
-
-
- -
-
-

state_dict

-
-
-Callback.state_dict()[source]
-

Called when saving a checkpoint, implement to generate callback’s state_dict.

-
-
Return type
-

Dict[str, Any]

-
-
Returns
-

A dictionary containing callback state.

-
-
-
- -
-
-

on_save_checkpoint

-
-
-Callback.on_save_checkpoint(trainer, pl_module, checkpoint)[source]
-

Called when saving a checkpoint to give you a chance to store anything else you might want to save.

-
-
Parameters
-
    -
  • trainer (Trainer) – the current Trainer instance.

  • -
  • pl_module (LightningModule) – the current LightningModule instance.

  • -
  • checkpoint (Dict[str, Any]) – the checkpoint dictionary that will be saved.

  • -
-
-
Return type
-

Optional[dict]

-
-
Returns
-

None or the callback state. Support for returning callback state will be removed in v1.8.

-
-
-
-

Deprecated since version v1.6: Returning a value from this method was deprecated in v1.6 and will be removed in v1.8. -Implement Callback.state_dict instead to return state. -In v1.8 Callback.on_save_checkpoint can only return None.

-
-
- -
-
-

load_state_dict

-
-
-Callback.load_state_dict(state_dict)[source]
-

Called when loading a checkpoint, implement to reload callback state given callback’s state_dict.

-
-
Parameters
-

state_dict (Dict[str, Any]) – the callback state returned by state_dict.

-
-
Return type
-

None

-
-
-
- -
-
-

on_load_checkpoint

-
-
-Callback.on_load_checkpoint(trainer, pl_module, callback_state)[source]
-

Called when loading a model checkpoint, use to reload state.

-
-
Parameters
-
    -
  • trainer (Trainer) – the current Trainer instance.

  • -
  • pl_module (LightningModule) – the current LightningModule instance.

  • -
  • callback_state (Dict[str, Any]) – the callback state returned by on_save_checkpoint.

  • -
-
-
-
-

Note

-

The on_load_checkpoint won’t be called with an undefined state. -If your on_load_checkpoint hook behavior doesn’t rely on a state, -you will still need to override on_save_checkpoint to return a dummy state.

-
-
-

Deprecated since version v1.6: This callback hook will change its signature and behavior in v1.8. -If you wish to load the state of the callback, use Callback.load_state_dict instead. -In v1.8 Callback.on_load_checkpoint(checkpoint) will receive the entire loaded -checkpoint dictionary instead of only the callback state from the checkpoint.

-
-
-
Return type
-

None

-
-
-
- -
-
-

on_before_backward

-
-
-Callback.on_before_backward(trainer, pl_module, loss)[source]
-

Called before loss.backward().

-
-
Return type
-

None

-
-
-
- -
-
-

on_after_backward

-
-
-Callback.on_after_backward(trainer, pl_module)[source]
-

Called after loss.backward() and before optimizers are stepped.

-
-
Return type
-

None

-
-
-
- -
-
-

on_before_optimizer_step

-
-
-Callback.on_before_optimizer_step(trainer, pl_module, optimizer, opt_idx)[source]
-

Called before optimizer.step().

-
-
Return type
-

None

-
-
-
- -
-
-

on_before_zero_grad

-
-
-Callback.on_before_zero_grad(trainer, pl_module, optimizer)[source]
-

Called before optimizer.zero_grad().

-
-
Return type
-

None

-
-
-
- -
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/callbacks_state.html b/docs/extensions/callbacks_state.html deleted file mode 100644 index 81ff9c4..0000000 --- a/docs/extensions/callbacks_state.html +++ /dev/null @@ -1,741 +0,0 @@ - - - - - - - - - - - - - - Save Callback state — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Save Callback state
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Save Callback state

-

Some callbacks require internal state in order to function properly. You can optionally -choose to persist your callback’s state as part of model checkpoint files using -state_dict() and load_state_dict(). -Note that the returned state must be able to be pickled.

-

When your callback is meant to be used only as a singleton callback then implementing the above two hooks is enough -to persist state effectively. However, if passing multiple instances of the callback to the Trainer is supported, then -the callback must define a state_key property in order for Lightning -to be able to distinguish the different states when loading the callback state. This concept is best illustrated by -the following example.

-
class Counter(Callback):
-    def __init__(self, what="epochs", verbose=True):
-        self.what = what
-        self.verbose = verbose
-        self.state = {"epochs": 0, "batches": 0}
-
-    @property
-    def state_key(self):
-        # note: we do not include `verbose` here on purpose
-        return self._generate_state_key(what=self.what)
-
-    def on_train_epoch_end(self, *args, **kwargs):
-        if self.what == "epochs":
-            self.state["epochs"] += 1
-
-    def on_train_batch_end(self, *args, **kwargs):
-        if self.what == "batches":
-            self.state["batches"] += 1
-
-    def load_state_dict(self, state_dict):
-        self.state.update(state_dict)
-
-    def state_dict(self):
-        return self.state.copy()
-
-
-# two callbacks of the same type are being used
-trainer = Trainer(callbacks=[Counter(what="epochs"), Counter(what="batches")])
-
-
-

A Lightning checkpoint from this Trainer with the two stateful callbacks will include the following information:

-
{
-    "state_dict": ...,
-    "callbacks": {
-        "Counter{'what': 'batches'}": {"batches": 32, "epochs": 0},
-        "Counter{'what': 'epochs'}": {"batches": 0, "epochs": 2},
-        ...
-    }
-}
-
-
-

The implementation of a state_key is essential here. If it were missing, -Lightning would not be able to disambiguate the state for these two callbacks, and state_key -by default only defines the class name as the key, e.g., here Counter.

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/datamodules_state.html b/docs/extensions/datamodules_state.html deleted file mode 100644 index 73e7a3b..0000000 --- a/docs/extensions/datamodules_state.html +++ /dev/null @@ -1,700 +0,0 @@ - - - - - - - - - - - - - - Save DataModule state — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Save DataModule state
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Save DataModule state

-

When a checkpoint is created, it asks every DataModule for their state. If your DataModule defines the state_dict and load_state_dict methods, the checkpoint will automatically track and restore your DataModules.

-
class LitDataModule(pl.DataModuler):
-    def state_dict(self):
-        # track whatever you want here
-        state = {"current_train_batch_index": self.current_train_batch_index}
-        return state
-
-    def load_state_dict(self, state_dict):
-        # restore the state based on what you tracked in (def state_dict)
-        self.current_train_batch_index = state_dict["current_train_batch_index"]
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/generated/pytorch_lightning.loggers.CSVLogger.html b/docs/extensions/generated/pytorch_lightning.loggers.CSVLogger.html deleted file mode 100644 index 317a9d3..0000000 --- a/docs/extensions/generated/pytorch_lightning.loggers.CSVLogger.html +++ /dev/null @@ -1,862 +0,0 @@ - - - - - - - - - - - - - - CSVLogger — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

CSVLogger

-
-
-class pytorch_lightning.loggers.CSVLogger(save_dir, name='lightning_logs', version=None, prefix='', flush_logs_every_n_steps=100)[source]
-

Bases: pytorch_lightning.loggers.base.LightningLoggerBase

-

Log to local file system in yaml and CSV format.

-

Logs are saved to os.path.join(save_dir, name, version).

-

Example

-
>>> from pytorch_lightning import Trainer
->>> from pytorch_lightning.loggers import CSVLogger
->>> logger = CSVLogger("logs", name="my_exp_name")
->>> trainer = Trainer(logger=logger)
-
-
-
-
Parameters
-
    -
  • save_dir (str) – Save directory

  • -
  • name (Optional[str]) – Experiment name. Defaults to 'default'.

  • -
  • version (Union[int, str, None]) – Experiment version. If version is not specified the logger inspects the save -directory for existing versions, then automatically assigns the next available version.

  • -
  • prefix (str) – A string to put at the beginning of metric keys.

  • -
  • flush_logs_every_n_steps (int) – How often to flush logs to disk (defaults to every 100 steps).

  • -
-
-
-
-
-finalize(status)[source]
-

Do any processing that is necessary to finalize an experiment.

-
-
Parameters
-

status (str) – Status that the experiment finished with (e.g. success, failed, aborted)

-
-
Return type
-

None

-
-
-
- -
-
-log_hyperparams(params)[source]
-

Record hyperparameters.

-
-
Parameters
-
    -
  • params (Union[Dict[str, Any], Namespace]) – Namespace containing the hyperparameters

  • -
  • args – Optional positional arguments, depends on the specific logger being used

  • -
  • kwargs – Optional keyword arguments, depends on the specific logger being used

  • -
-
-
Return type
-

None

-
-
-
- -
-
-log_metrics(metrics, step=None)[source]
-

Records metrics. -This method logs metrics as as soon as it received them. If you want to aggregate -metrics for one specific step, use the -agg_and_log_metrics() method.

-
-
Parameters
-
    -
  • metrics (Dict[str, float]) – Dictionary with metric names as keys and measured quantities as values

  • -
  • step (Optional[int]) – Step number at which the metrics should be recorded

  • -
-
-
Return type
-

None

-
-
-
- -
-
-save()[source]
-

Save log data.

-
-
Return type
-

None

-
-
-
- -
-
-property experiment: pytorch_lightning.loggers.csv_logs.ExperimentWriter
-

Actual ExperimentWriter object. To use ExperimentWriter features in your -LightningModule do the following.

-

Example:

-
self.logger.experiment.some_experiment_writer_function()
-
-
-
-
Return type
-

ExperimentWriter

-
-
-
- -
-
-property log_dir: str
-

The log directory for this run.

-

By default, it is named 'version_${self.version}' but it can be overridden by passing a string value for the -constructor’s version parameter instead of None or an int.

-
-
Return type
-

str

-
-
-
- -
-
-property name: str
-

Gets the name of the experiment.

-
-
Return type
-

str

-
-
Returns
-

The name of the experiment.

-
-
-
- -
-
-property root_dir: str
-

Parent directory for all checkpoint subdirectories.

-

If the experiment name parameter is an empty string, no experiment subdirectory is used and the checkpoint will -be saved in “save_dir/version”

-
-
Return type
-

str

-
-
-
- -
-
-property save_dir: Optional[str]
-

The current directory where logs are saved.

-
-
Return type
-

Optional[str]

-
-
Returns
-

The path to current directory where logs are saved.

-
-
-
- -
-
-property version: int
-

Gets the version of the experiment.

-
-
Return type
-

int

-
-
Returns
-

The version of the experiment if it is specified, else the next version.

-
-
-
- -
- -
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/generated/pytorch_lightning.loggers.CometLogger.html b/docs/extensions/generated/pytorch_lightning.loggers.CometLogger.html deleted file mode 100644 index 3415fee..0000000 --- a/docs/extensions/generated/pytorch_lightning.loggers.CometLogger.html +++ /dev/null @@ -1,900 +0,0 @@ - - - - - - - - - - - - - - CometLogger — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

CometLogger

-
-
-class pytorch_lightning.loggers.CometLogger(api_key=None, save_dir=None, project_name=None, rest_api_key=None, experiment_name=None, experiment_key=None, offline=False, prefix='', agg_key_funcs=None, agg_default_func=None, **kwargs)[source]
-

Bases: pytorch_lightning.loggers.base.LightningLoggerBase

-

Log using Comet.ml.

-

Install it with pip:

-
pip install comet-ml
-
-
-

Comet requires either an API Key (online mode) or a local directory path (offline mode).

-

ONLINE MODE

-
import os
-from pytorch_lightning import Trainer
-from pytorch_lightning.loggers import CometLogger
-
-# arguments made to CometLogger are passed on to the comet_ml.Experiment class
-comet_logger = CometLogger(
-    api_key=os.environ.get("COMET_API_KEY"),
-    workspace=os.environ.get("COMET_WORKSPACE"),  # Optional
-    save_dir=".",  # Optional
-    project_name="default_project",  # Optional
-    rest_api_key=os.environ.get("COMET_REST_API_KEY"),  # Optional
-    experiment_key=os.environ.get("COMET_EXPERIMENT_KEY"),  # Optional
-    experiment_name="lightning_logs",  # Optional
-)
-trainer = Trainer(logger=comet_logger)
-
-
-

OFFLINE MODE

-
from pytorch_lightning.loggers import CometLogger
-
-# arguments made to CometLogger are passed on to the comet_ml.Experiment class
-comet_logger = CometLogger(
-    save_dir=".",
-    workspace=os.environ.get("COMET_WORKSPACE"),  # Optional
-    project_name="default_project",  # Optional
-    rest_api_key=os.environ.get("COMET_REST_API_KEY"),  # Optional
-    experiment_name="lightning_logs",  # Optional
-)
-trainer = Trainer(logger=comet_logger)
-
-
-
-
Parameters
-
    -
  • api_key (Optional[str]) – Required in online mode. API key, found on Comet.ml. If not given, this -will be loaded from the environment variable COMET_API_KEY or ~/.comet.config -if either exists.

  • -
  • save_dir (Optional[str]) – Required in offline mode. The path for the directory to save local -comet logs. If given, this also sets the directory for saving checkpoints.

  • -
  • project_name (Optional[str]) – Optional. Send your experiment to a specific project. -Otherwise will be sent to Uncategorized Experiments. -If the project name does not already exist, Comet.ml will create a new project.

  • -
  • rest_api_key (Optional[str]) – Optional. Rest API key found in Comet.ml settings. -This is used to determine version number

  • -
  • experiment_name (Optional[str]) – Optional. String representing the name for this particular experiment on Comet.ml.

  • -
  • experiment_key (Optional[str]) – Optional. If set, restores from existing experiment.

  • -
  • offline (bool) – If api_key and save_dir are both given, this determines whether -the experiment will be in online or offline mode. This is useful if you use -save_dir to control the checkpoints directory and have a ~/.comet.config -file but still want to run offline experiments.

  • -
  • prefix (str) – A string to put at the beginning of metric keys.

  • -
  • **kwargs – Additional arguments like workspace, log_code, etc. used by -CometExperiment can be passed as keyword arguments in this logger.

  • -
-
-
Raises
-
    -
  • ModuleNotFoundError – If required Comet package is not installed on the device.

  • -
  • MisconfigurationException – If neither api_key nor save_dir are passed as arguments.

  • -
-
-
-
-
-finalize(status)[source]
-

When calling self.experiment.end(), that experiment won’t log any more data to Comet. -That’s why, if you need to log any more data, you need to create an ExistingCometExperiment. -For example, to log data when testing your model after training, because when training is -finalized CometLogger.finalize() is called.

-

This happens automatically in the experiment() property, when -self._experiment is set to None, i.e. self.reset_experiment().

-
-
Return type
-

None

-
-
-
- -
-
-log_graph(model, input_array=None)[source]
-

Record model graph.

-
-
Parameters
-
    -
  • model (LightningModule) – lightning model

  • -
  • input_array – input passes to model.forward

  • -
-
-
Return type
-

None

-
-
-
- -
-
-log_hyperparams(params)[source]
-

Record hyperparameters.

-
-
Parameters
-
    -
  • params (Union[Dict[str, Any], Namespace]) – Namespace containing the hyperparameters

  • -
  • args – Optional positional arguments, depends on the specific logger being used

  • -
  • kwargs – Optional keyword arguments, depends on the specific logger being used

  • -
-
-
Return type
-

None

-
-
-
- -
-
-log_metrics(metrics, step=None)[source]
-

Records metrics. -This method logs metrics as as soon as it received them. If you want to aggregate -metrics for one specific step, use the -agg_and_log_metrics() method.

-
-
Parameters
-
    -
  • metrics (Dict[str, Union[Tensor, float]]) – Dictionary with metric names as keys and measured quantities as values

  • -
  • step (Optional[int]) – Step number at which the metrics should be recorded

  • -
-
-
Return type
-

None

-
-
-
- -
-
-property experiment: None
-

Actual Comet object. To use Comet features in your -LightningModule do the following.

-

Example:

-
self.logger.experiment.some_comet_function()
-
-
-
-
Return type
-

None

-
-
-
- -
-
-property name: str
-

Gets the project name.

-
-
Return type
-

str

-
-
Returns
-

The project name if it is specified, else “comet-default”.

-
-
-
- -
-
-property save_dir: Optional[str]
-

Gets the save directory.

-
-
Return type
-

Optional[str]

-
-
Returns
-

The path to the save directory.

-
-
-
- -
-
-property version: str
-

Gets the version.

-
-
Return type
-

str

-
-
Returns
-

The first one of the following that is set in the following order

-
    -
  1. experiment id.

  2. -
  3. experiment key.

  4. -
  5. ”COMET_EXPERIMENT_KEY” environment variable.

  6. -
  7. future experiment key.

  8. -
-

If none are present generates a new guid.

-

-
-
-
- -
- -
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.html b/docs/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.html deleted file mode 100644 index 38e87b5..0000000 --- a/docs/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.html +++ /dev/null @@ -1,881 +0,0 @@ - - - - - - - - - - - - - - MLFlowLogger — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

MLFlowLogger

-
-
-class pytorch_lightning.loggers.MLFlowLogger(experiment_name='lightning_logs', run_name=None, tracking_uri=None, tags=None, save_dir='./mlruns', prefix='', artifact_location=None, run_id=None)[source]
-

Bases: pytorch_lightning.loggers.base.LightningLoggerBase

-

Log using MLflow.

-

Install it with pip:

-
pip install mlflow
-
-
-
from pytorch_lightning import Trainer
-from pytorch_lightning.loggers import MLFlowLogger
-
-mlf_logger = MLFlowLogger(experiment_name="lightning_logs", tracking_uri="file:./ml-runs")
-trainer = Trainer(logger=mlf_logger)
-
-
-

Use the logger anywhere in your LightningModule as follows:

-
from pytorch_lightning import LightningModule
-
-
-class LitModel(LightningModule):
-    def training_step(self, batch, batch_idx):
-        # example
-        self.logger.experiment.whatever_ml_flow_supports(...)
-
-    def any_lightning_module_function_or_hook(self):
-        self.logger.experiment.whatever_ml_flow_supports(...)
-
-
-
-
Parameters
-
    -
  • experiment_name (str) – The name of the experiment.

  • -
  • run_name (Optional[str]) – Name of the new run. The run_name is internally stored as a mlflow.runName tag. -If the mlflow.runName tag has already been set in tags, the value is overridden by the run_name.

  • -
  • tracking_uri (Optional[str]) – Address of local or remote tracking server. -If not provided, defaults to MLFLOW_TRACKING_URI environment variable if set, otherwise it falls -back to file:<save_dir>.

  • -
  • tags (Optional[Dict[str, Any]]) – A dictionary tags for the experiment.

  • -
  • save_dir (Optional[str]) – A path to a local directory where the MLflow runs get saved. -Defaults to ./mlflow if tracking_uri is not provided. -Has no effect if tracking_uri is provided.

  • -
  • prefix (str) – A string to put at the beginning of metric keys.

  • -
  • artifact_location (Optional[str]) – The location to store run artifacts. If not provided, the server picks an appropriate -default.

  • -
  • run_id (Optional[str]) – The run identifier of the experiment. If not provided, a new run is started.

  • -
-
-
Raises
-

ModuleNotFoundError – If required MLFlow package is not installed on the device.

-
-
-
-
-finalize(status='FINISHED')[source]
-

Do any processing that is necessary to finalize an experiment.

-
-
Parameters
-

status (str) – Status that the experiment finished with (e.g. success, failed, aborted)

-
-
Return type
-

None

-
-
-
- -
-
-log_hyperparams(params)[source]
-

Record hyperparameters.

-
-
Parameters
-
    -
  • params (Union[Dict[str, Any], Namespace]) – Namespace containing the hyperparameters

  • -
  • args – Optional positional arguments, depends on the specific logger being used

  • -
  • kwargs – Optional keyword arguments, depends on the specific logger being used

  • -
-
-
Return type
-

None

-
-
-
- -
-
-log_metrics(metrics, step=None)[source]
-

Records metrics. -This method logs metrics as as soon as it received them. If you want to aggregate -metrics for one specific step, use the -agg_and_log_metrics() method.

-
-
Parameters
-
    -
  • metrics (Dict[str, float]) – Dictionary with metric names as keys and measured quantities as values

  • -
  • step (Optional[int]) – Step number at which the metrics should be recorded

  • -
-
-
Return type
-

None

-
-
-
- -
-
-property experiment: None
-

Actual MLflow object. To use MLflow features in your -LightningModule do the following.

-

Example:

-
self.logger.experiment.some_mlflow_function()
-
-
-
-
Return type
-

None

-
-
-
- -
-
-property experiment_id: str
-

Create the experiment if it does not exist to get the experiment id.

-
-
Return type
-

str

-
-
Returns
-

The experiment id.

-
-
-
- -
-
-property name: str
-

Get the experiment id.

-
-
Return type
-

str

-
-
Returns
-

The experiment id.

-
-
-
- -
-
-property run_id: str
-

Create the experiment if it does not exist to get the run id.

-
-
Return type
-

str

-
-
Returns
-

The run id.

-
-
-
- -
-
-property save_dir: Optional[str]
-

The root file directory in which MLflow experiments are saved.

-
-
Return type
-

Optional[str]

-
-
Returns
-

Local path to the root experiment directory if the tracking uri is local. -Otherwise returns None.

-
-
-
- -
-
-property version: str
-

Get the run id.

-
-
Return type
-

str

-
-
Returns
-

The run id.

-
-
-
- -
- -
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.html b/docs/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.html deleted file mode 100644 index f8813e5..0000000 --- a/docs/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.html +++ /dev/null @@ -1,989 +0,0 @@ - - - - - - - - - - - - - - NeptuneLogger — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

NeptuneLogger

-
-
-class pytorch_lightning.loggers.NeptuneLogger(*, api_key=None, project=None, name=None, run=None, log_model_checkpoints=True, prefix='training', agg_key_funcs=None, agg_default_func=None, **neptune_run_kwargs)[source]
-

Bases: pytorch_lightning.loggers.base.LightningLoggerBase

-

Log using Neptune.

-

Install it with pip:

-
pip install neptune-client
-
-
-

or conda:

-
conda install -c conda-forge neptune-client
-
-
-

Quickstart

-

Pass NeptuneLogger instance to the Trainer to log metadata with Neptune:

-
from pytorch_lightning import Trainer
-from pytorch_lightning.loggers import NeptuneLogger
-
-neptune_logger = NeptuneLogger(
-    api_key="ANONYMOUS",  # replace with your own
-    project="common/pytorch-lightning-integration",  # format "<WORKSPACE/PROJECT>"
-    tags=["training", "resnet"],  # optional
-)
-trainer = Trainer(max_epochs=10, logger=neptune_logger)
-
-
-

How to use NeptuneLogger?

-

Use the logger anywhere in your LightningModule as follows:

-
from neptune.new.types import File
-from pytorch_lightning import LightningModule
-
-
-class LitModel(LightningModule):
-    def training_step(self, batch, batch_idx):
-        # log metrics
-        acc = ...
-        self.log("train/loss", loss)
-
-    def any_lightning_module_function_or_hook(self):
-        # log images
-        img = ...
-        self.logger.experiment["train/misclassified_images"].log(File.as_image(img))
-
-        # generic recipe
-        metadata = ...
-        self.logger.experiment["your/metadata/structure"].log(metadata)
-
-
-

Note that syntax: self.logger.experiment["your/metadata/structure"].log(metadata) is specific to Neptune -and it extends logger capabilities. Specifically, it allows you to log various types of metadata -like scores, files, images, interactive visuals, CSVs, etc. -Refer to the Neptune docs -for more detailed explanations. -You can also use regular logger methods log_metrics(), and log_hyperparams() with NeptuneLogger -as these are also supported.

-

Log after fitting or testing is finished

-

You can log objects after the fitting or testing methods are finished:

-
neptune_logger = NeptuneLogger(project="common/pytorch-lightning-integration")
-
-trainer = pl.Trainer(logger=neptune_logger)
-model = ...
-datamodule = ...
-trainer.fit(model, datamodule=datamodule)
-trainer.test(model, datamodule=datamodule)
-
-# Log objects after `fit` or `test` methods
-# model summary
-neptune_logger.log_model_summary(model=model, max_depth=-1)
-
-# generic recipe
-metadata = ...
-neptune_logger.experiment["your/metadata/structure"].log(metadata)
-
-
-

Log model checkpoints

-

If you have ModelCheckpoint configured, -Neptune logger automatically logs model checkpoints. -Model weights will be uploaded to the: “model/checkpoints” namespace in the Neptune Run. -You can disable this option:

-
neptune_logger = NeptuneLogger(project="common/pytorch-lightning-integration", log_model_checkpoints=False)
-
-
-

Pass additional parameters to the Neptune run

-

You can also pass neptune_run_kwargs to specify the run in the greater detail, like tags or description:

-
from pytorch_lightning import Trainer
-from pytorch_lightning.loggers import NeptuneLogger
-
-neptune_logger = NeptuneLogger(
-    project="common/pytorch-lightning-integration",
-    name="lightning-run",
-    description="mlp quick run with pytorch-lightning",
-    tags=["mlp", "quick-run"],
-)
-trainer = Trainer(max_epochs=3, logger=neptune_logger)
-
-
-

Check run documentation -for more info about additional run parameters.

-

Details about Neptune run structure

-

Runs can be viewed as nested dictionary-like structures that you can define in your code. -Thanks to this you can easily organize your metadata in a way that is most convenient for you.

-

The hierarchical structure that you apply to your metadata will be reflected later in the UI.

-

You can organize this way any type of metadata - images, parameters, metrics, model checkpoint, CSV files, etc.

-
-

See also

- -
-
-
Parameters
-
    -
  • api_key (Optional[str]) – Optional. -Neptune API token, found on https://neptune.ai upon registration. -Read: how to find and set Neptune API token. -It is recommended to keep it in the NEPTUNE_API_TOKEN -environment variable and then you can drop api_key=None.

  • -
  • project (Optional[str]) – Optional. -Name of a project in a form of “my_workspace/my_project” for example “tom/mask-rcnn”. -If None, the value of NEPTUNE_PROJECT environment variable will be taken. -You need to create the project in https://neptune.ai first.

  • -
  • name (Optional[str]) – Optional. Editable name of the run. -Run name appears in the “all metadata/sys” section in Neptune UI.

  • -
  • run (None) – Optional. Default is None. The Neptune Run object. -If specified, this Run` will be used for logging, instead of a new Run. -When run object is passed you can’t specify other neptune properties.

  • -
  • log_model_checkpoints (Optional[bool]) – Optional. Default is True. Log model checkpoint to Neptune. -Works only if ModelCheckpoint is passed to the Trainer.

  • -
  • prefix (str) – Optional. Default is "training". Root namespace for all metadata logging.

  • -
  • **neptune_run_kwargs – Additional arguments like tags, description, capture_stdout, etc. -used when run is created.

  • -
-
-
Raises
-
    -
  • ModuleNotFoundError – If required Neptune package in version >=0.9 is not installed on the device.

  • -
  • TypeError – If configured project has not been migrated to new structure yet.

  • -
  • ValueError – If argument passed to the logger’s constructor is incorrect.

  • -
-
-
-
-
-after_save_checkpoint(checkpoint_callback)[source]
-

Automatically log checkpointed model. Called after model checkpoint callback saves a new checkpoint.

-
-
Parameters
-

checkpoint_callback – the model checkpoint callback instance

-
-
-
- -
-
-finalize(status)[source]
-

Do any processing that is necessary to finalize an experiment.

-
-
Parameters
-

status (str) – Status that the experiment finished with (e.g. success, failed, aborted)

-
-
Return type
-

None

-
-
-
- -
-
-log_hyperparams(params)[source]
-

Log hyper-parameters to the run.

-

Hyperparams will be logged under the “<prefix>/hyperparams” namespace.

-
-

Note

-

You can also log parameters by directly using the logger instance: -neptune_logger.experiment["model/hyper-parameters"] = params_dict.

-

In this way you can keep hierarchical structure of the parameters.

-
-
-
Parameters
-

params (Union[Dict[str, Any], Namespace]) – dict. -Python dictionary structure with parameters.

-
-
-

Example:

-
from pytorch_lightning.loggers import NeptuneLogger
-
-PARAMS = {
-    "batch_size": 64,
-    "lr": 0.07,
-    "decay_factor": 0.97
-}
-
-neptune_logger = NeptuneLogger(
-    api_key="ANONYMOUS",
-    project="common/pytorch-lightning-integration"
-)
-
-neptune_logger.log_hyperparams(PARAMS)
-
-
-
-
Return type
-

None

-
-
-
- -
-
-log_metrics(metrics, step=None)[source]
-

Log metrics (numeric values) in Neptune runs.

-
-
Parameters
-
    -
  • metrics (Dict[str, Union[Tensor, float]]) – Dictionary with metric names as keys and measured quantities as values.

  • -
  • step (Optional[int]) – Step number at which the metrics should be recorded, currently ignored.

  • -
-
-
Return type
-

None

-
-
-
- -
-
-property experiment: None
-

Actual Neptune run object. Allows you to use neptune logging features in your -LightningModule.

-

Example:

-
class LitModel(LightningModule):
-    def training_step(self, batch, batch_idx):
-        # log metrics
-        acc = ...
-        self.logger.experiment["train/acc"].log(acc)
-
-        # log images
-        img = ...
-        self.logger.experiment["train/misclassified_images"].log(File.as_image(img))
-
-
-

Note that syntax: self.logger.experiment["your/metadata/structure"].log(metadata) -is specific to Neptune and it extends logger capabilities. -Specifically, it allows you to log various types of metadata like scores, files, -images, interactive visuals, CSVs, etc. Refer to the -Neptune docs -for more detailed explanations. -You can also use regular logger methods log_metrics(), and log_hyperparams() -with NeptuneLogger as these are also supported.

-
-
Return type
-

None

-
-
-
- -
-
-property name: str
-

Return the experiment name or ‘offline-name’ when exp is run in offline mode.

-
-
Return type
-

str

-
-
-
- -
-
-property save_dir: Optional[str]
-

Gets the save directory of the experiment which in this case is None because Neptune does not save -locally.

-
-
Return type
-

Optional[str]

-
-
Returns
-

the root directory where experiment logs get saved

-
-
-
- -
-
-property version: str
-

Return the experiment version.

-

It’s Neptune Run’s short_id

-
-
Return type
-

str

-
-
-
- -
- -
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.html b/docs/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.html deleted file mode 100644 index 41d7374..0000000 --- a/docs/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.html +++ /dev/null @@ -1,908 +0,0 @@ - - - - - - - - - - - - - - TensorBoardLogger — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • TensorBoardLogger
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

TensorBoardLogger

-
-
-class pytorch_lightning.loggers.TensorBoardLogger(save_dir, name='lightning_logs', version=None, log_graph=False, default_hp_metric=True, prefix='', sub_dir=None, agg_key_funcs=None, agg_default_func=None, **kwargs)[source]
-

Bases: pytorch_lightning.loggers.base.LightningLoggerBase

-

Log to local file system in TensorBoard format.

-

Implemented using SummaryWriter. Logs are saved to -os.path.join(save_dir, name, version). This is the default logger in Lightning, it comes -preinstalled.

-

Example:

-
from pytorch_lightning import Trainer
-from pytorch_lightning.loggers import TensorBoardLogger
-
-logger = TensorBoardLogger("tb_logs", name="my_model")
-trainer = Trainer(logger=logger)
-
-
-
-
Parameters
-
    -
  • save_dir (str) – Save directory

  • -
  • name (Optional[str]) – Experiment name. Defaults to 'default'. If it is the empty string then no per-experiment -subdirectory is used.

  • -
  • version (Union[int, str, None]) – Experiment version. If version is not specified the logger inspects the save -directory for existing versions, then automatically assigns the next available version. -If it is a string then it is used as the run-specific subdirectory name, -otherwise 'version_${version}' is used.

  • -
  • log_graph (bool) – Adds the computational graph to tensorboard. This requires that -the user has defined the self.example_input_array attribute in their -model.

  • -
  • default_hp_metric (bool) – Enables a placeholder metric with key hp_metric when log_hyperparams is -called without a metric (otherwise calls to log_hyperparams without a metric are ignored).

  • -
  • prefix (str) – A string to put at the beginning of metric keys.

  • -
  • sub_dir (Optional[str]) – Sub-directory to group TensorBoard logs. If a sub_dir argument is passed -then logs are saved in /save_dir/name/version/sub_dir/. Defaults to None in which -logs are saved in /save_dir/name/version/.

  • -
  • **kwargs – Additional arguments used by SummaryWriter can be passed as keyword -arguments in this logger. To automatically flush to disk, max_queue sets the size -of the queue for pending logs before flushing. flush_secs determines how many seconds -elapses before flushing.

  • -
-
-
-
-
-finalize(status)[source]
-

Do any processing that is necessary to finalize an experiment.

-
-
Parameters
-

status (str) – Status that the experiment finished with (e.g. success, failed, aborted)

-
-
Return type
-

None

-
-
-
- -
-
-log_graph(model, input_array=None)[source]
-

Record model graph.

-
-
Parameters
-
    -
  • model (LightningModule) – lightning model

  • -
  • input_array – input passes to model.forward

  • -
-
-
-
- -
-
-log_hyperparams(params, metrics=None)[source]
-

Record hyperparameters. TensorBoard logs with and without saved hyperparameters are incompatible, the -hyperparameters are then not displayed in the TensorBoard. Please delete or move the previously saved logs -to display the new ones with hyperparameters.

-
-
Parameters
-
    -
  • params (Union[Dict[str, Any], Namespace]) – a dictionary-like container with the hyperparameters

  • -
  • metrics (Optional[Dict[str, Any]]) – Dictionary with metric names as keys and measured quantities as values

  • -
-
-
Return type
-

None

-
-
-
- -
-
-log_metrics(metrics, step=None)[source]
-

Records metrics. -This method logs metrics as as soon as it received them. If you want to aggregate -metrics for one specific step, use the -agg_and_log_metrics() method.

-
-
Parameters
-
    -
  • metrics (Dict[str, float]) – Dictionary with metric names as keys and measured quantities as values

  • -
  • step (Optional[int]) – Step number at which the metrics should be recorded

  • -
-
-
Return type
-

None

-
-
-
- -
-
-save()[source]
-

Save log data.

-
-
Return type
-

None

-
-
-
- -
-
-property experiment: torch.utils.tensorboard.writer.SummaryWriter
-

Actual tensorboard object. To use TensorBoard features in your -LightningModule do the following.

-

Example:

-
self.logger.experiment.some_tensorboard_function()
-
-
-
-
Return type
-

SummaryWriter

-
-
-
- -
-
-property log_dir: str
-

The directory for this run’s tensorboard checkpoint.

-

By default, it is named 'version_${self.version}' but it can be overridden by passing a string value for the -constructor’s version parameter instead of None or an int.

-
-
Return type
-

str

-
-
-
- -
-
-property name: str
-

Get the name of the experiment.

-
-
Return type
-

str

-
-
Returns
-

The name of the experiment.

-
-
-
- -
-
-property root_dir: str
-

Parent directory for all tensorboard checkpoint subdirectories.

-

If the experiment name parameter is an empty string, no experiment subdirectory is used and the checkpoint will -be saved in “save_dir/version”

-
-
Return type
-

str

-
-
-
- -
-
-property save_dir: Optional[str]
-

Gets the save directory where the TensorBoard experiments are saved.

-
-
Return type
-

Optional[str]

-
-
Returns
-

The local path to the save directory where the TensorBoard experiments are saved.

-
-
-
- -
-
-property sub_dir: Optional[str]
-

Gets the sub directory where the TensorBoard experiments are saved.

-
-
Return type
-

Optional[str]

-
-
Returns
-

The local path to the sub directory where the TensorBoard experiments are saved.

-
-
-
- -
-
-property version: int
-

Get the experiment version.

-
-
Return type
-

int

-
-
Returns
-

The experiment version if specified else the next version.

-
-
-
- -
- -
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/generated/pytorch_lightning.loggers.WandbLogger.html b/docs/extensions/generated/pytorch_lightning.loggers.WandbLogger.html deleted file mode 100644 index 80acb66..0000000 --- a/docs/extensions/generated/pytorch_lightning.loggers.WandbLogger.html +++ /dev/null @@ -1,1022 +0,0 @@ - - - - - - - - - - - - - - WandbLogger — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

WandbLogger

-
-
-class pytorch_lightning.loggers.WandbLogger(name=None, save_dir=None, offline=False, id=None, anonymous=None, version=None, project=None, log_model=False, experiment=None, prefix='', agg_key_funcs=None, agg_default_func=None, **kwargs)[source]
-

Bases: pytorch_lightning.loggers.base.LightningLoggerBase

-

Log using Weights and Biases.

-

Installation and set-up

-

Install with pip:

-
pip install wandb
-
-
-

Create a WandbLogger instance:

-
from pytorch_lightning.loggers import WandbLogger
-
-wandb_logger = WandbLogger(project="MNIST")
-
-
-

Pass the logger instance to the Trainer:

-
trainer = Trainer(logger=wandb_logger)
-
-
-

A new W&B run will be created when training starts if you have not created one manually before with wandb.init().

-

Log metrics

-

Log from LightningModule:

-
class LitModule(LightningModule):
-    def training_step(self, batch, batch_idx):
-        self.log("train/loss", loss)
-
-
-

Use directly wandb module:

-
wandb.log({"train/loss": loss})
-
-
-

Log hyper-parameters

-

Save LightningModule parameters:

-
class LitModule(LightningModule):
-    def __init__(self, *args, **kwarg):
-        self.save_hyperparameters()
-
-
-

Add other config parameters:

-
# add one parameter
-wandb_logger.experiment.config["key"] = value
-
-# add multiple parameters
-wandb_logger.experiment.config.update({key1: val1, key2: val2})
-
-# use directly wandb module
-wandb.config["key"] = value
-wandb.config.update()
-
-
-

Log gradients, parameters and model topology

-

Call the watch method for automatically tracking gradients:

-
# log gradients and model topology
-wandb_logger.watch(model)
-
-# log gradients, parameter histogram and model topology
-wandb_logger.watch(model, log="all")
-
-# change log frequency of gradients and parameters (100 steps by default)
-wandb_logger.watch(model, log_freq=500)
-
-# do not log graph (in case of errors)
-wandb_logger.watch(model, log_graph=False)
-
-
-

The watch method adds hooks to the model which can be removed at the end of training:

-
wandb_logger.unwatch(model)
-
-
-

Log model checkpoints

-

Log model checkpoints at the end of training:

-
wandb_logger = WandbLogger(log_model=True)
-
-
-

Log model checkpoints as they get created during training:

-
wandb_logger = WandbLogger(log_model="all")
-
-
-

Custom checkpointing can be set up through ModelCheckpoint:

-
# log model only if `val_accuracy` increases
-wandb_logger = WandbLogger(log_model="all")
-checkpoint_callback = ModelCheckpoint(monitor="val_accuracy", mode="max")
-trainer = Trainer(logger=wandb_logger, callbacks=[checkpoint_callback])
-
-
-

latest and best aliases are automatically set to easily retrieve a model checkpoint:

-
# reference can be retrieved in artifacts panel
-# "VERSION" can be a version (ex: "v2") or an alias ("latest or "best")
-checkpoint_reference = "USER/PROJECT/MODEL-RUN_ID:VERSION"
-
-# download checkpoint locally (if not already cached)
-run = wandb.init(project="MNIST")
-artifact = run.use_artifact(checkpoint_reference, type="model")
-artifact_dir = artifact.download()
-
-# load checkpoint
-model = LitModule.load_from_checkpoint(Path(artifact_dir) / "model.ckpt")
-
-
-

Log media

-

Log text with:

-
# using columns and data
-columns = ["input", "label", "prediction"]
-data = [["cheese", "english", "english"], ["fromage", "french", "spanish"]]
-wandb_logger.log_text(key="samples", columns=columns, data=data)
-
-# using a pandas DataFrame
-wandb_logger.log_text(key="samples", dataframe=my_dataframe)
-
-
-

Log images with:

-
# using tensors, numpy arrays or PIL images
-wandb_logger.log_image(key="samples", images=[img1, img2])
-
-# adding captions
-wandb_logger.log_image(key="samples", images=[img1, img2], caption=["tree", "person"])
-
-# using file path
-wandb_logger.log_image(key="samples", images=["img_1.jpg", "img_2.jpg"])
-
-
-

More arguments can be passed for logging segmentation masks and bounding boxes. Refer to -Image Overlays documentation.

-

Log Tables

-

W&B Tables can be used to log, query and analyze tabular data.

-

They support any type of media (text, image, video, audio, molecule, html, etc) and are great for storing, -understanding and sharing any form of data, from datasets to model predictions.

-
columns = ["caption", "image", "sound"]
-data = [["cheese", wandb.Image(img_1), wandb.Audio(snd_1)], ["wine", wandb.Image(img_2), wandb.Audio(snd_2)]]
-wandb_logger.log_table(key="samples", columns=columns, data=data)
-
-
-
-

See also

- -
-
-
Parameters
-
    -
  • name (Optional[str]) – Display name for the run.

  • -
  • save_dir (Optional[str]) – Path where data is saved (wandb dir by default).

  • -
  • offline (Optional[bool]) – Run offline (data can be streamed later to wandb servers).

  • -
  • id (Optional[str]) – Sets the version, mainly used to resume a previous run.

  • -
  • version (Optional[str]) – Same as id.

  • -
  • anonymous (Optional[bool]) – Enables or explicitly disables anonymous logging.

  • -
  • project (Optional[str]) – The name of the project to which this run will belong.

  • -
  • log_model (Union[str, bool]) –

    Log checkpoints created by ModelCheckpoint -as W&B artifacts. latest and best aliases are automatically set.

    -
      -
    • if log_model == 'all', checkpoints are logged during training.

    • -
    • if log_model == True, checkpoints are logged at the end of training, except when -save_top_k == -1 -which also logs every checkpoint during training.

    • -
    • if log_model == False (default), no checkpoint is logged.

    • -
    -

  • -
  • prefix (Optional[str]) – A string to put at the beginning of metric keys.

  • -
  • experiment – WandB experiment object. Automatically set when creating a run.

  • -
  • **kwargs – Arguments passed to wandb.init() like entity, group, tags, etc.

  • -
-
-
Raises
-
    -
  • ModuleNotFoundError – If required WandB package is not installed on the device.

  • -
  • MisconfigurationException – If both log_model and offline is set to True.

  • -
-
-
-
-
-after_save_checkpoint(checkpoint_callback)[source]
-

Called after model checkpoint callback saves a new checkpoint.

-
-
Parameters
-

checkpoint_callback – the model checkpoint callback instance

-
-
-
- -
-
-finalize(status)[source]
-

Do any processing that is necessary to finalize an experiment.

-
-
Parameters
-

status (str) – Status that the experiment finished with (e.g. success, failed, aborted)

-
-
Return type
-

None

-
-
-
- -
-
-log_hyperparams(params)[source]
-

Record hyperparameters.

-
-
Parameters
-
    -
  • params (Union[Dict[str, Any], Namespace]) – Namespace containing the hyperparameters

  • -
  • args – Optional positional arguments, depends on the specific logger being used

  • -
  • kwargs – Optional keyword arguments, depends on the specific logger being used

  • -
-
-
Return type
-

None

-
-
-
- -
-
-log_image(key, images, step=None, **kwargs)[source]
-

Log images (tensors, numpy arrays, PIL Images or file paths).

-

Optional kwargs are lists passed to each image (ex: caption, masks, boxes).

-
-
Return type
-

None

-
-
-
- -
-
-log_metrics(metrics, step=None)[source]
-

Records metrics. -This method logs metrics as as soon as it received them. If you want to aggregate -metrics for one specific step, use the -agg_and_log_metrics() method.

-
-
Parameters
-
    -
  • metrics (Dict[str, float]) – Dictionary with metric names as keys and measured quantities as values

  • -
  • step (Optional[int]) – Step number at which the metrics should be recorded

  • -
-
-
Return type
-

None

-
-
-
- -
-
-log_table(key, columns=None, data=None, dataframe=None, step=None)[source]
-

Log a Table containing any object type (text, image, audio, video, molecule, html, etc).

-

Can be defined either with columns and data or with dataframe.

-
-
Return type
-

None

-
-
-
- -
-
-log_text(key, columns=None, data=None, dataframe=None, step=None)[source]
-

Log text as a Table.

-

Can be defined either with columns and data or with dataframe.

-
-
Return type
-

None

-
-
-
- -
-
-property experiment: None
-

Actual wandb object. To use wandb features in your -LightningModule do the following.

-

Example:

-
.. code-block:: python
-
-
-
-

self.logger.experiment.some_wandb_function()

-
-
-
Return type
-

None

-
-
-
- -
-
-property name: Optional[str]
-

Gets the name of the experiment.

-
-
Return type
-

Optional[str]

-
-
Returns
-

The name of the experiment if the experiment exists else the name given to the constructor.

-
-
-
- -
-
-property save_dir: Optional[str]
-

Gets the save directory.

-
-
Return type
-

Optional[str]

-
-
Returns
-

The path to the save directory.

-
-
-
- -
-
-property version: Optional[str]
-

Gets the id of the experiment.

-
-
Return type
-

Optional[str]

-
-
Returns
-

The id of the experiment if the experiment exists else the id given to the constructor.

-
-
-
- -
- -
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/logging.html b/docs/extensions/logging.html deleted file mode 100644 index 9477e51..0000000 --- a/docs/extensions/logging.html +++ /dev/null @@ -1,1058 +0,0 @@ - - - - - - - - - - - - - - Logging — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Logging

-
-

Supported Loggers

-

The following are loggers we support:

- ---- - - - - - - - - - - - - - - - - - - - - -

CometLogger

Log using Comet.ml.

CSVLogger

Log to local file system in yaml and CSV format.

MLFlowLogger

Log using MLflow.

NeptuneLogger

Log using Neptune.

TensorBoardLogger

Log to local file system in TensorBoard format.

WandbLogger

Log using Weights and Biases.

-

The above loggers will normally plot an additional chart (global_step VS epoch). Depending on the loggers you use, there might be some additional charts too.

-

By default, Lightning uses TensorBoard logger under the hood, and stores the logs to a directory (by default in lightning_logs/).

-
from pytorch_lightning import Trainer
-
-# Automatically logs to a directory (by default ``lightning_logs/``)
-trainer = Trainer()
-
-
-

To see your logs:

-
tensorboard --logdir=lightning_logs/
-
-
-

To visualize tensorboard in a jupyter notebook environment, run the following command in a jupyter cell:

-
%reload_ext tensorboard
-%tensorboard --logdir=lightning_logs/
-
-
-

You can also pass a custom Logger to the Trainer.

-
from pytorch_lightning import loggers as pl_loggers
-
-tb_logger = pl_loggers.TensorBoardLogger(save_dir="logs/")
-trainer = Trainer(logger=tb_logger)
-
-
-

Choose from any of the others such as MLflow, Comet, Neptune, WandB, etc.

-
comet_logger = pl_loggers.CometLogger(save_dir="logs/")
-trainer = Trainer(logger=comet_logger)
-
-
-

To use multiple loggers, simply pass in a list or tuple of loggers.

-
tb_logger = pl_loggers.TensorBoardLogger(save_dir="logs/")
-comet_logger = pl_loggers.CometLogger(save_dir="logs/")
-trainer = Trainer(logger=[tb_logger, comet_logger])
-
-
-
-

Note

-

By default, Lightning logs every 50 steps. Use Trainer flags to Control Logging Frequency.

-
-
-

Note

-

By default, all loggers log to os.getcwd(). You can change the logging path using -Trainer(default_root_dir="/your/path/to/save/checkpoints") without instantiating a logger.

-
-
-
-
-

Logging from a LightningModule

-

Lightning offers automatic log functionalities for logging scalars, or manual logging for anything else.

-
-

Automatic Logging

-

Use the log() or log_dict() -methods to log from anywhere in a LightningModule and callbacks.

-
def training_step(self, batch, batch_idx):
-    self.log("my_metric", x)
-
-
-# or a dict to get multiple metrics on the same plot if the logger supports it
-def training_step(self, batch, batch_idx):
-    self.log("performance", {"acc": acc, "recall": recall})
-
-
-# or a dict to log all metrics at once with individual plots
-def training_step(self, batch, batch_idx):
-    self.log_dict({"acc": acc, "recall": recall})
-
-
-
-

Note

-

Everything explained below applies to both log() or log_dict() methods.

-
-

Depending on where the log() method is called, Lightning auto-determines -the correct logging mode for you. Of course you can override the default behavior by manually setting the -log() parameters.

-
def training_step(self, batch, batch_idx):
-    self.log("my_loss", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True)
-
-
-

The log() method has a few options:

-
    -
  • on_step: Logs the metric at the current step.

  • -
  • on_epoch: Automatically accumulates and logs at the end of the epoch.

  • -
  • prog_bar: Logs to the progress bar (Default: False).

  • -
  • logger: Logs to the logger like Tensorboard, or any other custom logger passed to the Trainer (Default: True).

  • -
  • reduce_fx: Reduction function over step values for end of epoch. Uses torch.mean() by default.

  • -
  • enable_graph: If True, will not auto detach the graph.

  • -
  • sync_dist: If True, reduces the metric across devices. Use with care as this may lead to a significant communication overhead.

  • -
  • sync_dist_group: The DDP group to sync across.

  • -
  • add_dataloader_idx: If True, appends the index of the current dataloader to the name (when using multiple dataloaders). If False, user needs to give unique names for each dataloader to not mix the values.

  • -
  • batch_size: Current batch size used for accumulating logs logged with on_epoch=True. This will be directly inferred from the loaded batch, but for some data structures you might need to explicitly provide it.

  • -
  • rank_zero_only: Whether the value will be logged only on rank 0. This will prevent synchronization which would produce a deadlock as not all processes would perform this log call.

  • -
- - ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Default behavior of logging in Callback or LightningModule

Hook

on_step

on_epoch

on_train_start, on_train_epoch_start, on_train_epoch_end, training_epoch_end

False

True

on_before_backward, on_after_backward, on_before_optimizer_step, on_before_zero_grad

True

False

on_train_batch_start, on_train_batch_end, training_step, training_step_end

True

False

on_validation_start, on_validation_epoch_start, on_validation_epoch_end, validation_epoch_end

False

True

on_validation_batch_start, on_validation_batch_end, validation_step, validation_step_end

False

True

-
-

Note

-

While logging tensor metrics with on_epoch=True inside step-level hooks and using mean-reduction (default) to accumulate the metrics across the current epoch, Lightning tries to extract the -batch size from the current batch. If multiple possible batch sizes are found, a warning is logged and if it fails to extract the batch size from the current batch, which is possible if -the batch is a custom structure/collection, then an error is raised. To avoid this, you can specify the batch_size inside the self.log(... batch_size=batch_size) call.

-
def training_step(self, batch, batch_idx):
-    # extracts the batch size from `batch`
-    self.log("train_loss", loss, on_epoch=True)
-
-
-def validation_step(self, batch, batch_idx):
-    # uses `batch_size=10`
-    self.log("val_loss", loss, batch_size=10)
-
-
-
-
-

Note

-
    -
  • The above config for validation applies for test hooks as well.

  • -
  • Setting on_epoch=True will cache all your logged values during the full training epoch and perform a -reduction in on_train_epoch_end. We recommend using TorchMetrics, when working with custom reduction.

  • -
  • Setting both on_step=True and on_epoch=True will create two keys per metric you log with -suffix _step and _epoch respectively. You can refer to these keys e.g. in the monitor -argument of ModelCheckpoint or in the graphs plotted to the logger of your choice.

  • -
-
-

If your work requires to log in an unsupported method, please open an issue with a clear description of why it is blocking you.

-
-
-

Manual Logging Non-Scalar Artifacts

-

If you want to log anything that is not a scalar, like histograms, text, images, etc., you may need to use the logger object directly.

-
def training_step(self):
-    ...
-    # the logger you used (in this case tensorboard)
-    tensorboard = self.logger.experiment
-    tensorboard.add_image()
-    tensorboard.add_histogram(...)
-    tensorboard.add_figure(...)
-
-
-
-
-
-
-

Make a Custom Logger

-

You can implement your own logger by writing a class that inherits from Logger. -Use the rank_zero_experiment() and rank_zero_only() decorators to make sure that only the first process in DDP training creates the experiment and logs the data respectively.

-
from pytorch_lightning.loggers.logger import Logger, rank_zero_experiment
-from pytorch_lightning.utilities.distributed import rank_zero_only
-
-
-class MyLogger(Logger):
-    @property
-    def name(self):
-        return "MyLogger"
-
-    @property
-    def version(self):
-        # Return the experiment version, int or str.
-        return "0.1"
-
-    @rank_zero_only
-    def log_hyperparams(self, params):
-        # params is an argparse.Namespace
-        # your code to record hyperparameters goes here
-        pass
-
-    @rank_zero_only
-    def log_metrics(self, metrics, step):
-        # metrics is a dictionary of metric names and values
-        # your code to record metrics goes here
-        pass
-
-    @rank_zero_only
-    def save(self):
-        # Optional. Any code necessary to save logger data goes here
-        pass
-
-    @rank_zero_only
-    def finalize(self, status):
-        # Optional. Any code that needs to be run after training
-        # finishes goes here
-        pass
-
-
-

If you write a logger that may be useful to others, please send -a pull request to add it to Lightning!

-
-
-
-

Control Logging Frequency

-
-

Logging frequency

-

It may slow down training to log on every single batch. By default, Lightning logs every 50 rows, or 50 training steps. -To change this behaviour, set the log_every_n_steps Trainer flag.

-
k = 10
-trainer = Trainer(log_every_n_steps=k)
-
-
-
-
-

Log Writing Frequency

-

Individual logger implementations determine their flushing frequency. For example, on the -CSVLogger you can set the flag flush_logs_every_n_steps.

-
-
-
-
-

Progress Bar

-

You can add any metric to the progress bar using log() -method, setting prog_bar=True.

-
def training_step(self, batch, batch_idx):
-    self.log("my_loss", loss, prog_bar=True)
-
-
-

You could learn more about progress bars supported by Lightning here.

-
-

Modifying the Progress Bar

-

The progress bar by default already includes the training loss and version number of the experiment -if you are using a logger. These defaults can be customized by overriding the -get_metrics() hook in your logger.

-
from pytorch_lightning.callbacks.progress import Tqdm
-
-
-class CustomProgressBar(Tqdm):
-    def get_metrics(self, *args, **kwargs):
-        # don't show the version number
-        items = super().get_metrics()
-        items.pop("v_num", None)
-        return items
-
-
-
-
-
-
-

Configure Console Logging

-

Lightning logs useful information about the training process and user warnings to the console. -You can retrieve the Lightning console logger and change it to your liking. For example, adjust the logging level -or redirect output for certain modules to log files:

-
import logging
-
-# configure logging at the root level of Lightning
-logging.getLogger("pytorch_lightning").setLevel(logging.ERROR)
-
-# configure logging on module level, redirect to file
-logger = logging.getLogger("pytorch_lightning.core")
-logger.addHandler(logging.FileHandler("core.log"))
-
-
-

Read more about custom Python logging here.

-
-
-
-

Logging Hyperparameters

-

When training a model, it is useful to know what hyperparams went into that model. -When Lightning creates a checkpoint, it stores a key "hyper_parameters" with the hyperparams.

-
lightning_checkpoint = torch.load(filepath, map_location=lambda storage, loc: storage)
-hyperparams = lightning_checkpoint["hyper_parameters"]
-
-
-

Some loggers also allow logging the hyperparams used in the experiment. For instance, -when using the TensorBoardLogger, all hyperparams will show -in the hparams tab.

-
-

Note

-

If you want to track a metric in the tensorboard hparams tab, log scalars to the key hp_metric. If tracking multiple metrics, initialize TensorBoardLogger with default_hp_metric=False and call log_hyperparams only once with your metric keys and initial values. Subsequent updates can simply be logged to the metric keys. Refer to the examples below for setting up proper hyperparams metrics tracking within the LightningModule.

-
# Using default_hp_metric
-def validation_step(self, batch, batch_idx):
-    self.log("hp_metric", some_scalar)
-
-
-# Using custom or multiple metrics (default_hp_metric=False)
-def on_train_start(self):
-    self.logger.log_hyperparams(self.hparams, {"hp/metric_1": 0, "hp/metric_2": 0})
-
-
-def validation_step(self, batch, batch_idx):
-    self.log("hp/metric_1", some_scalar_1)
-    self.log("hp/metric_2", some_scalar_2)
-
-
-

In the example, using "hp/" as a prefix allows for the metrics to be grouped under “hp” in the tensorboard scalar tab where you can collapse them.

-
-
-
-
-

Managing Remote Filesystems

-

Lightning supports saving logs to a variety of filesystems, including local filesystems and several cloud storage providers.

-

Check out the Remote Filesystems doc for more info.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/loops.html b/docs/extensions/loops.html deleted file mode 100644 index 3a7d110..0000000 --- a/docs/extensions/loops.html +++ /dev/null @@ -1,1219 +0,0 @@ - - - - - - - - - - - - - - Loops — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Loops

-

Loops let advanced users swap out the default gradient descent optimization loop at the core of Lightning with a different optimization paradigm.

-

The Lightning Trainer is built on top of the standard gradient descent optimization loop which works for 90%+ of machine learning use cases:

-
for i, batch in enumerate(dataloader):
-    x, y = batch
-    y_hat = model(x)
-    loss = loss_function(y_hat, y)
-    optimizer.zero_grad()
-    loss.backward()
-    optimizer.step()
-
-
-

However, some new research use cases such as meta-learning, active learning, recommendation systems, etc., require a different loop structure. -For example here is a simple loop that guides the weight updates with a loss from a special validation split:

-
for i, batch in enumerate(train_dataloader):
-    x, y = batch
-    y_hat = model(x)
-    loss = loss_function(y_hat, y)
-    optimizer.zero_grad()
-    loss.backward()
-
-    val_loss = 0
-    for i, val_batch in enumerate(val_dataloader):
-        x, y = val_batch
-        y_hat = model(x)
-        val_loss += loss_function(y_hat, y)
-
-    scale_gradients(model, 1 / val_loss)
-    optimizer.step()
-
-
-

With Lightning Loops, you can customize to non-standard gradient descent optimizations to get the same loop above:

-
trainer = Trainer()
-trainer.fit_loop.epoch_loop = MyGradientDescentLoop()
-
-
-

Think of this as swapping out the engine in a car!

-
-
-

Understanding the Default Trainer Loop

-

The Lightning Trainer automates the standard optimization loop which every PyTorch user is familiar with:

-
for i, batch in enumerate(dataloader):
-    x, y = batch
-    y_hat = model(x)
-    loss = loss_function(y_hat, y)
-    optimizer.zero_grad()
-    loss.backward()
-    optimizer.step()
-
-
-

The core research logic is simply shifted to the LightningModule:

-
for i, batch in enumerate(dataloader):
-    # x, y = batch                      moved to training_step
-    # y_hat = model(x)                  moved to training_step
-    # loss = loss_function(y_hat, y)    moved to training_step
-    loss = lightning_module.training_step(batch, i)
-
-    # Lightning handles automatically:
-    optimizer.zero_grad()
-    loss.backward()
-    optimizer.step()
-
-
-

Under the hood, the above loop is implemented using the Loop API like so:

-
class DefaultLoop(Loop):
-    def advance(self, batch, i):
-        loss = lightning_module.training_step(batch, i)
-        optimizer.zero_grad()
-        loss.backward()
-        optimizer.step()
-
-    def run(self, dataloader):
-        for i, batch in enumerate(dataloader):
-            self.advance(batch, i)
-
-
-

Defining a loop within a class interface instead of hard-coding a raw Python for/while loop has several benefits:

-
    -
  1. You can have full control over the data flow through loops.

  2. -
  3. You can add new loops and nest as many of them as you want.

  4. -
  5. If needed, the state of a loop can be saved and resumed.

  6. -
  7. New hooks can be injected at any point.

  8. -
-Animation showing how to convert a standard training loop to a Lightning loop -
-
-
-

Overriding the default Loops

-

The fastest way to get started with loops, is to override functionality of an existing loop. -Lightning has 4 main loops which relies on : FitLoop for fitting (training and validating), -EvaluationLoop for validating or testing, -PredictionLoop for predicting.

-

For simple changes that don’t require a custom loop, you can modify each of these loops.

-

Each loop has a series of methods that can be modified. -For example with the FitLoop:

-
from pytorch_lightning.loops import FitLoop
-
-
-class MyLoop(FitLoop):
-    def advance(self):
-        """Advance from one iteration to the next."""
-
-    def on_advance_end(self):
-        """Do something at the end of an iteration."""
-
-    def on_run_end(self):
-        """Do something when the loop ends."""
-
-
-

A full list with all built-in loops and subloops can be found here.

-

To add your own modifications to a loop, simply subclass an existing loop class and override what you need. -Here is a simple example how to add a new hook:

-
from pytorch_lightning.loops import FitLoop
-
-
-class CustomFitLoop(FitLoop):
-    def advance(self):
-        # ... whatever code before
-
-        # pass anything you want to the hook
-        self.trainer.call_hook("my_new_hook", *args, **kwargs)
-
-        # ... whatever code after
-
-
-

Now simply attach the correct loop in the trainer directly:

-
trainer = Trainer(...)
-trainer.fit_loop = CustomFitLoop()
-
-# fit() now uses the new FitLoop!
-trainer.fit(...)
-
-# the equivalent for validate()
-val_loop = CustomValLoop()
-trainer = Trainer()
-trainer.validate_loop = val_loop
-trainer.validate(...)
-
-
-

Now your code is FULLY flexible and you can still leverage ALL the best parts of Lightning!

-Animation showing how to replace a loop on the Trainer -
-
-
-

Creating a New Loop From Scratch

-

You can also go wild and implement a full loop from scratch by sub-classing the Loop base class. -You will need to override a minimum of two things:

-
from pytorch_lightning.loop import Loop
-
-
-class MyFancyLoop(Loop):
-    @property
-    def done(self):
-        """Provide a condition to stop the loop."""
-
-    def advance(self):
-        """
-        Access your dataloader/s in whatever way you want.
-        Do your fancy optimization things.
-        Call the LightningModule methods at your leisure.
-        """
-
-
-

Finally, attach it into the Trainer:

-
trainer = Trainer(...)
-trainer.fit_loop = MyFancyLoop()
-
-# fit() now uses your fancy loop!
-trainer.fit(...)
-
-
-

But beware: Loop customization gives you more power and full control over the Trainer and with great power comes great responsibility. -We recommend that you familiarize yourself with overriding the default loops first before you start building a new loop from the ground up.

-
-
-
-

Loop API

-

Here is the full API of methods available in the Loop base class.

-

The Loop class is the base of all loops in the same way as the LightningModule is the base of all models. -It defines a public interface that each loop implementation must follow, the key ones are:

-
-

Properties

-
-

done

-
-
-Loop.done
-

Property indicating when the loop is finished.

-

Example:

-
@property
-def done(self):
-    return self.trainer.global_step >= self.trainer.max_steps
-
-
-
-
Return type
-

bool

-
-
-
- -
-
-

skip (optional)

-
-
-Loop.skip
-

Determine whether to return immediately from the call to run().

-

Example:

-
@property
-def skip(self):
-    return len(self.trainer.train_dataloader) == 0
-
-
-
-
Return type
-

bool

-
-
-
- -
-
-
-

Methods

-
-

reset (optional)

-
-
-abstract Loop.reset()[source]
-

Resets the internal state of the loop at the beginning of each call to run.

-

Example:

-
def reset(self):
-    # reset your internal state or add custom logic
-    # if you expect run() to be called multiple times
-    self.current_iteration = 0
-    self.outputs = []
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

advance

-
-
-abstract Loop.advance(*args, **kwargs)[source]
-

Performs a single step.

-

Accepts all arguments passed to run.

-

Example:

-
def advance(self, iterator):
-    batch = next(iterator)
-    loss = self.trainer.lightning_module.training_step(batch, batch_idx)
-    ...
-
-
-
-
Return type
-

None

-
-
-
- -
-
-

run (optional)

-
-
-Loop.run(*args, **kwargs)[source]
-

The main entry point to the loop.

-

Will frequently check the done condition and calls advance -until done evaluates to True.

-

Override this if you wish to change the default behavior. The default implementation is:

-

Example:

-
def run(self, *args, **kwargs):
-    if self.skip:
-        return self.on_skip()
-
-    self.reset()
-    self.on_run_start(*args, **kwargs)
-
-    while not self.done:
-        self.advance(*args, **kwargs)
-
-    output = self.on_run_end()
-    return output
-
-
-
-
Return type
-

~T

-
-
Returns
-

The output of on_run_end (often outputs collected from each step of the loop)

-
-
-
- -
-
-
-
-
-

Subloops

-

When you want to customize nested loops within loops, use the replace() method:

-
# This takes care of properly instantiating the new Loop and setting all references
-trainer.fit_loop.replace(epoch_loop=MyEpochLoop)
-# Trainer runs the fit loop with your new epoch loop!
-trainer.fit(model)
-
-
-

Alternatively, for more fine-grained control, use the connect() method:

-
# Optional: stitch back the trainer arguments
-epoch_loop = MyEpochLoop(trainer.fit_loop.epoch_loop.min_steps, trainer.fit_loop.epoch_loop.max_steps)
-# Optional: connect children loops as they might have existing state
-epoch_loop.connect(trainer.fit_loop.epoch_loop.batch_loop, trainer.fit_loop.epoch_loop.val_loop)
-# Instantiate and connect the loop.
-trainer.fit_loop.connect(epoch_loop=epoch_loop)
-trainer.fit(model)
-
-
-

More about the built-in loops and how they are composed is explained in the next section.

-Animation showing how to connect a custom subloop -
-
-
-

Built-in Loops

-

The training loop in Lightning is called fit loop and is actually a combination of several loops. -Here is what the structure would look like in plain Python:

-
# FitLoop
-for epoch in range(max_epochs):
-
-    # TrainingEpochLoop
-    for batch_idx, batch in enumerate(train_dataloader):
-
-        # TrainingBatchLoop
-        for split_batch in tbptt_split(batch):
-
-            # OptimizerLoop
-            for optimizer_idx, opt in enumerate(optimizers):
-
-                loss = lightning_module.training_step(batch, batch_idx, optimizer_idx)
-                ...
-
-        # ValidationEpochLoop
-        for batch_idx, batch in enumerate(val_dataloader):
-            lightning_module.validation_step(batch, batch_idx, optimizer_idx)
-            ...
-
-
-

Each of these for-loops represents a class implementing the Loop interface.

- - ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Trainer entry points and associated loops

Built-in loop

Description

FitLoop

The FitLoop is the top-level loop where training starts. -It simply counts the epochs and iterates from one to the next by calling TrainingEpochLoop.run() in its advance() method.

TrainingEpochLoop

The TrainingEpochLoop is the one that iterates over the dataloader that the user returns in their train_dataloader() method. -Its main responsibilities are calling the *_epoch_start and *_epoch_end hooks, accumulating outputs if the user request them in one of these hooks, and running validation at the requested interval. -The validation is carried out by yet another loop, ValidationEpochLoop.

-

In the run() method, the training epoch loop could in theory simply call the LightningModule.training_step already and perform the optimization. -However, Lightning has built-in support for automatic optimization with multiple optimizers and on top of that also supports TBPTT. -For this reason there are actually two more loops nested under TrainingEpochLoop.

-

TrainingBatchLoop

The responsibility of the TrainingBatchLoop is to split a batch given by the TrainingEpochLoop along the time-dimension and iterate over the list of splits. -It also keeps track of the hidden state hiddens returned by the training step. -By default, when truncated back-propagation through time (TBPTT) is turned off, this loop does not do anything except redirect the call to the OptimizerLoop. -Read more about TBPTT.

OptimizerLoop

The OptimizerLoop iterates over one or multiple optimizers and for each one it calls the training_step() method with the batch, the current batch index and the optimizer index if multiple optimizers are requested. -It is the leaf node in the tree of loops and performs the actual optimization (forward, zero grad, backward, optimizer step).

ManualOptimization

Substitutes the OptimizerLoop in case of manual optimization and implements the manual optimization step.

EvaluationLoop

The EvaluationLoop is the top-level loop where validation/testing starts. -It simply iterates over each evaluation dataloader from one to the next by calling EvaluationEpochLoop.run() in its advance() method.

PredictionLoop

The PredictionLoop is the top-level loop where prediction starts. -It simply iterates over each prediction dataloader from one to the next by calling PredictionEpochLoop.run() in its advance() method.

-
-
-
-

Available Loops in Lightning Flash

-

Active Learning is a machine learning practice in which the user interacts with the learner in order to provide new labels when required.

-

You can find a real use case in Lightning Flash.

-

Flash implements the ActiveLearningLoop that you can use together with the ActiveLearningDataModule to label new data on the fly. -To run the following demo, install Flash and BaaL first:

-
pip install lightning-flash baal
-
-
-
import torch
-
-import flash
-from flash.core.classification import Probabilities
-from flash.core.data.utils import download_data
-from flash.image import ImageClassificationData, ImageClassifier
-from flash.image.classification.integrations.baal import ActiveLearningDataModule, ActiveLearningLoop
-
-# 1. Create the DataModule
-download_data("https://pl-flash-data.s3.amazonaws.com/hymenoptera_data.zip", "./data")
-
-# Implement the research use-case where we mask labels from labelled dataset.
-datamodule = ActiveLearningDataModule(
-    ImageClassificationData.from_folders(train_folder="data/hymenoptera_data/train/", batch_size=2),
-    initial_num_labels=5,
-    val_split=0.1,
-)
-
-# 2. Build the task
-head = torch.nn.Sequential(
-    torch.nn.Dropout(p=0.1),
-    torch.nn.Linear(512, datamodule.num_classes),
-)
-model = ImageClassifier(backbone="resnet18", head=head, num_classes=datamodule.num_classes, output=Probabilities())
-
-
-# 3.1 Create the trainer
-trainer = flash.Trainer(max_epochs=3)
-
-# 3.2 Create the active learning loop and connect it to the trainer
-active_learning_loop = ActiveLearningLoop(label_epoch_frequency=1)
-active_learning_loop.connect(trainer.fit_loop)
-trainer.fit_loop = active_learning_loop
-
-# 3.3 Finetune
-trainer.finetune(model, datamodule=datamodule, strategy="freeze")
-
-# 4. Predict what's on a few images! ants or bees?
-predictions = model.predict("data/hymenoptera_data/val/bees/65038344_52a45d090d.jpg")
-print(predictions)
-
-# 5. Save the model!
-trainer.save_checkpoint("image_classification_model.pt")
-
-
-

Here is the Active Learning Loop example and the code for the active learning loop.

-
-
-
-

Advanced Examples

- - ---- - - - - - - - - - - - - - -
Ready-to-run loop examples and tutorials

Link to Example

Description

K-fold Cross Validation

KFold / Cross Validation is a machine learning practice in which the training dataset is being partitioned into num_folds complementary subsets. -One cross validation round will perform fitting where one fold is left out for validation and the other folds are used for training. -To reduce variability, once all rounds are performed using the different folds, the trained models are ensembled and their predictions are -averaged when estimating the model’s predictive performance on the test dataset.

Yielding Training Step

This loop enables you to write the training_step() hook -as a Python Generator for automatic optimization with multiple optimizers, i.e., you can yield loss -values from it instead of returning them. This can enable more elegant and expressive implementations, as shown -shown with a GAN in this example.

-
-
-
-

Advanced Features

-

Next: Advanced loop features

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/loops_advanced.html b/docs/extensions/loops_advanced.html deleted file mode 100644 index 11d6830..0000000 --- a/docs/extensions/loops_advanced.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - - - - - - - - - Loops (Advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Loops (Advanced)

-
-

Persisting the State of Loops

-
-

Note

-

This is an experimental feature and is not activated by default. -Set the environment variable PL_FAULT_TOLERANT_TRAINING = 1 to enable saving the progress of loops. -Read more about fault-tolerant training.

-
-

A powerful property of the class-based loop interface is that it can own an internal state. -Loop instances can save their state to the checkpoint through corresponding hooks and if implemented accordingly, resume the state of execution at the appropriate place. -This design is particularly interesting for fault-tolerant training which is an experimental feature released in Lightning v1.5.

-

The two hooks on_save_checkpoint() and on_load_checkpoint() function very similarly to how LightningModules and Callbacks save and load state.

-
def on_save_checkpoint(self):
-    state_dict["iteration"] = self.iteration
-    return state_dict
-
-
-def on_load_checkpoint(self, state_dict):
-    self.iteration = state_dict["iteration"]
-
-
-

When the Trainer is restarting from a checkpoint (e.g., through trainer.fit(ckpt_path=...)), the loop exposes a boolean attribute restarting. -Based around the value of this variable, the user can write the loop in such a way that it can restart from an arbitrary point given the state loaded from the checkpoint. -For example, the implementation of the reset() method could look like this given our previous example:

-
def reset(self):
-    if not self.restarting:
-        self.iteration = 0
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/plugins.html b/docs/extensions/plugins.html deleted file mode 100644 index 9da9dc2..0000000 --- a/docs/extensions/plugins.html +++ /dev/null @@ -1,834 +0,0 @@ - - - - - - - - - - - - - - Plugins — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Plugins

-

Plugins allow custom integrations to the internals of the Trainer such as custom precision, checkpointing or -cluster environment implementation.

-

Under the hood, the Lightning Trainer is using plugins in the training routine, added automatically -depending on the provided Trainer arguments.

-

There are three types of Plugins in Lightning with different responsibilities:

-
    -
  • Precision Plugins

  • -
  • CheckpointIO Plugins

  • -
  • Cluster Environments

  • -
-

You can make the Trainer use one or multiple plugins by adding it to the plugins argument like so:

-
trainer = Trainer(plugins=[plugin1, plugin2, ...])
-
-
-

By default, the plugins get selected based on the rest of the Trainer settings such as the strategy.

-
-
-

Precision Plugins

-

We provide precision plugins for you to benefit from numerical representations with lower precision than -32-bit floating-point or higher precision, such as 64-bit floating-point.

-
# Training with 16-bit precision
-trainer = Trainer(precision=16)
-
-
-

The full list of built-in precision plugins is listed below.

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ApexMixedPrecisionPlugin

Mixed Precision Plugin based on Nvidia/Apex (https://github.com/NVIDIA/apex)

DeepSpeedPrecisionPlugin

Precision plugin for DeepSpeed integration.

DoublePrecisionPlugin

Plugin for training with double (torch.float64) precision.

FullyShardedNativeMixedPrecisionPlugin

Native AMP for Fully Sharded Training.

HPUPrecisionPlugin

Plugin that enables bfloat/half support on HPUs.

IPUPrecisionPlugin

Precision plugin for IPU integration.

MixedPrecisionPlugin

Base Class for mixed precision.

NativeMixedPrecisionPlugin

Plugin for Native Mixed Precision (AMP) training with torch.autocast.

PrecisionPlugin

Base class for all plugins handling the precision-specific parts of the training.

ShardedNativeMixedPrecisionPlugin

Native AMP for Sharded Training.

TPUBf16PrecisionPlugin

Plugin that enables bfloats on TPUs.

TPUPrecisionPlugin

Precision plugin for TPU integration.

-

More information regarding precision with Lightning can be found here

-
-
-
-

CheckpointIO Plugins

-

As part of our commitment to extensibility, we have abstracted Lightning’s checkpointing logic into the CheckpointIO plugin. -With this, you have the ability to customize the checkpointing logic to match the needs of your infrastructure.

-

Below is a list of built-in plugins for checkpointing.

- ---- - - - - - - - - - - - - - - -

CheckpointIO

Interface to save/load checkpoints as they are saved through the Strategy.

HPUCheckpointIO

CheckpointIO to save checkpoints for HPU training strategies.

TorchCheckpointIO

CheckpointIO that utilizes torch.save() and torch.load() to save and load checkpoints respectively, common for most use cases.

XLACheckpointIO

CheckpointIO that utilizes xm.save() to save checkpoints for TPU training strategies.

-

Learn more about custom checkpointing with Lightning here.

-
-
-
-

Cluster Environments

-

You can define the interface of your own cluster environment based on the requirements of your infrastructure.

- ---- - - - - - - - - - - - - - - - - - - - - -

ClusterEnvironment

Specification of a cluster environment.

KubeflowEnvironment

Environment for distributed training using the PyTorchJob operator from Kubeflow

LightningEnvironment

The default environment used by Lightning for a single node or free cluster (not managed).

LSFEnvironment

An environment for running on clusters managed by the LSF resource manager.

SLURMEnvironment

Cluster environment for training on a cluster managed by SLURM.

TorchElasticEnvironment

Environment for fault-tolerant and elastic training with torchelastic

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/extensions/strategy.html b/docs/extensions/strategy.html deleted file mode 100644 index 599b65d..0000000 --- a/docs/extensions/strategy.html +++ /dev/null @@ -1,825 +0,0 @@ - - - - - - - - - - - - - - What is a Strategy? — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • What is a Strategy?
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

What is a Strategy?

-

Strategy controls the model distribution across training, evaluation, and prediction to be used by the Trainer. It can be controlled by passing different -strategy with aliases ("ddp", "ddp_spawn", "deepspeed" and so on) as well as a custom strategy to the strategy parameter for Trainer.

-

The Strategy in PyTorch Lightning handles the following responsibilities:

-
    -
  • Launch and teardown of training processes (if applicable).

  • -
  • Setup communication between processes (NCCL, GLOO, MPI, and so on).

  • -
  • Provide a unified communication interface for reduction, broadcast, and so on.

  • -
  • Owns the LightningModule

  • -
  • Handles/owns optimizers and schedulers.

  • -
-

Strategy also manages the accelerator, precision, and checkpointing plugins.

-Illustration of the Strategy as a composition of the Accelerator and several plugins -

We expose Strategies mainly for expert users that want to extend Lightning for new hardware support or new distributed backends (e.g. a backend not yet supported by PyTorch itself).

-
-
-
-

Enable Different Strategies

-
# Training with the DistributedDataParallel strategy on 4 GPUs
-trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4)
-
-# Training with the custom DistributedDataParallel strategy on 4 GPUs
-trainer = Trainer(strategy=DDPStrategy(...), accelerator="gpu", devices=4)
-
-# Training with the DDP Spawn strategy using auto accelerator selection
-trainer = Trainer(strategy="ddp_spawn", accelerator="auto", devices=4)
-
-# Training with the DeepSpeed strategy on available GPUs
-trainer = Trainer(strategy="deepspeed", accelerator="gpu", devices="auto")
-
-# Training with the DDP strategy using 3 CPU processes
-trainer = Trainer(strategy="ddp", accelerator="cpu", devices=3)
-
-# Training with the DDP Spawn strategy on 8 TPU cores
-trainer = Trainer(strategy="ddp_spawn", accelerator="tpu", devices=8)
-
-# Training with the default IPU strategy on 8 IPUs
-trainer = Trainer(accelerator="ipu", devices=8)
-
-
-
-
-
-

Create a Custom Strategy

-

Expert users may choose to extend an existing strategy by overriding its methods.

-
from pytorch_lightning.strategies import DDPStrategy
-
-
-class CustomDDPStrategy(DDPStrategy):
-    def configure_ddp(self):
-        self.model = MyCustomDistributedDataParallel(
-            self.model,
-            device_ids=...,
-        )
-
-
-

or by subclassing the base class Strategy to create new ones. These custom strategies -can then be passed into the Trainer directly via the strategy parameter.

-
# custom plugins
-trainer = Trainer(strategy=CustomDDPStrategy())
-
-# fully custom accelerator and plugins
-accelerator = MyAccelerator()
-precision_plugin = MyPrecisionPlugin()
-training_strategy = CustomDDPStrategy(accelerator=accelerator, precision_plugin=precision_plugin)
-trainer = Trainer(strategy=training_strategy)
-
-
-

The complete list of built-in strategies is listed below.

-
-
-
-

Available Training Strategies

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

BaguaStrategy

Strategy for training using the Bagua library, with advanced distributed training algorithms and system optimizations.

DDP2Strategy

DDP2 behaves like DP in one node, but synchronization across nodes behaves like in DDP.

DDPFullyShardedStrategy

Plugin for Fully Sharded Data Parallel provided by FairScale.

DDPShardedStrategy

Optimizer and gradient sharded training provided by FairScale.

DDPSpawnShardedStrategy

Optimizer sharded training provided by FairScale.

DDPSpawnStrategy

Spawns processes using the torch.multiprocessing.spawn() method and joins processes after training finishes.

DDPStrategy

Strategy for multi-process single-device training on one or multiple nodes.

DataParallelStrategy

Implements data-parallel training in a single process, i.e., the model gets replicated to each device and each gets a split of the data.

DeepSpeedStrategy

Provides capabilities to run training using the DeepSpeed library, with training optimizations for large billion parameter models.

HorovodStrategy

Plugin for Horovod distributed training integration.

HPUParallelStrategy

Strategy for distributed training on multiple HPU devices.

IPUStrategy

Plugin for training on IPU devices.

ParallelStrategy

Plugin for training with multiple processes in parallel.

SingleDeviceStrategy

Strategy that handles communication on a single device.

SingleHPUStrategy

Strategy for training on single HPU device.

SingleTPUStrategy

Strategy for training on a single TPU device.

Strategy

Base class for all strategies that change the behaviour of the training, validation and test- loop.

TPUSpawnStrategy

Strategy for training multiple TPU devices using the torch_xla.distributed.xla_multiprocessing.spawn() method.

-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/generate_docs_for_tags.sh b/docs/generate_docs_for_tags.sh new file mode 100644 index 0000000..a354421 --- /dev/null +++ b/docs/generate_docs_for_tags.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# Usage: +# 1. Generate docs with one or more specified versions: +# $ export PACKAGE_NAME=app +# $ bash docs/generate_docs_for_tags.sh 1.9.3 1.9.2 1.9.1 1.9.0 +# OR +# $ PACKAGE_NAME=pytorch bash docs/generate_docs_for_tags.sh 1.8.6 1.8.5 1.8.4 1.8.3 1.8.2 1.8.1 1.8.0 +set -e + +PATH_ROOT=~/Desktop/builds +PATH_ENV=$PATH_ROOT/venv-docs-$PACKAGE_NAME +# export PACKAGE_NAME=app +export FREEZE_REQUIREMENTS=1 + +echo PATH_ROOT: $PATH_ROOT +echo PATH_ENV: $PATH_ENV +echo PYTHONPATH: $PYTHONPATH + +function build_docs { + python --version + pip --version + + pip install -q setuptools wheel python-multipart + pip install -e . -q -r requirements/$PACKAGE_NAME/docs.txt \ + -f ../pypi -f https://download.pytorch.org/whl/cpu/torch_stable.html + pip list + + cd docs/source-$PACKAGE_NAME + make html --jobs=$(nproc) + # make html SPHINXOPTS="-W --keep-going" --jobs=$(nproc) + cd ../.. + + mkdir -p $PATH_ROOT/docs-${PACKAGE_NAME} + mv docs/build/html $PATH_ROOT/docs-${PACKAGE_NAME}/$tag +} + +# iterate over all arguments assuming that each argument is version +for tag in "$@" +do + echo processing version: $tag + + # Don't install/update anything before activating venv + # to avoid breaking any existing environment. + python -m venv $PATH_ENV + source $PATH_ENV/bin/activate + + cd $PATH_ROOT + git clone --single-branch --branch $tag --depth 1 --recurse-submodules \ + https://github.com/Lightning-AI/lightning.git + cd lightning + + build_docs > "$PATH_ROOT/building-${PACKAGE_NAME}_${tag}.log" + + cd .. + rm -rf lightning + + deactivate + rm -rf $PATH_ENV +done diff --git a/docs/genindex.html b/docs/genindex.html deleted file mode 100644 index 7e2a533..0000000 --- a/docs/genindex.html +++ /dev/null @@ -1,2117 +0,0 @@ - - - - - - - - - - - - - Index — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- - -

Index

- -
- Symbols - | A - | B - | C - | D - | E - | F - | G - | H - | I - | K - | L - | M - | N - | O - | P - | Q - | R - | S - | T - | U - | V - | W - | Z - -
-

Symbols

- - - -
- -

A

- - - -
- -

B

- - - -
- -

C

- - - -
- -

D

- - - -
- -

E

- - - -
- -

F

- - - -
- -

G

- - - -
- -

H

- - - -
- -

I

- - - -
- -

K

- - -
- -

L

- - - -
- -

M

- - - -
- -

N

- - - -
- -

O

- - - -
- -

P

- - - -
- -

Q

- - - -
- -

R

- - - -
- -

S

- - - -
- -

T

- - - -
- -

U

- - - -
- -

V

- - - -
- -

W

- - - -
- -

Z

- - - -
- - - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/guides/data.html b/docs/guides/data.html deleted file mode 100644 index 60657e8..0000000 --- a/docs/guides/data.html +++ /dev/null @@ -1,1088 +0,0 @@ - - - - - - - - - - - - - - Managing Data — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Managing Data

-
-

Data Containers in Lightning

-

There are a few different data containers used in Lightning:

- - ---- - - - - - - - - - - - - - - - - - - - -
Data objects

Object

Definition

Dataset

The PyTorch Dataset represents a map from keys to data samples.

IterableDataset

The PyTorch IterableDataset represents a stream of data.

DataLoader

The PyTorch DataLoader represents a Python iterable over a Dataset.

LightningDataModule

A LightningDataModule is simply a collection of: training DataLoader(s), validation DataLoader(s), test DataLoader(s) and predict DataLoader(s), along with the matching transforms and data processing/downloads steps required.

-
-

Why Use LightningDataModule?

-

The LightningDataModule was designed as a way of decoupling data-related hooks from the LightningModule so you can develop dataset agnostic models. The LightningDataModule makes it easy to hot swap different Datasets with your model, so you can test it and benchmark it across domains. It also makes sharing and reusing the exact data splits and transforms across projects possible.

-

Read this for more details on LightningDataModule.

-
-
-
-
-

Multiple Datasets

-

There are a few ways to pass multiple Datasets to Lightning:

-
    -
  1. Create a DataLoader that iterates over multiple Datasets under the hood.

  2. -
  3. In the training loop, you can pass multiple DataLoaders as a dict or list/tuple, and Lightning will -automatically combine the batches from different DataLoaders.

  4. -
  5. In the validation, test, or prediction, you have the option to return multiple DataLoaders as list/tuple, which Lightning will call sequentially -or combine the DataLoaders using CombinedLoader, which Lightning will -automatically combine the batches from different DataLoaders.

  6. -
-
-

Using LightningDataModule

-

You can set more than one DataLoader in your LightningDataModule using its DataLoader hooks -and Lightning will use the correct one.

-
class DataModule(LightningDataModule):
-
-    ...
-
-    def train_dataloader(self):
-        return DataLoader(self.train_dataset)
-
-    def val_dataloader(self):
-        return [DataLoader(self.val_dataset_1), DataLoader(self.val_dataset_2)]
-
-    def test_dataloader(self):
-        return DataLoader(self.test_dataset)
-
-    def predict_dataloader(self):
-        return DataLoader(self.predict_dataset)
-
-
-
-
-

Using LightningModule Hooks

-
-

Concatenated Dataset

-

For training with multiple Datasets, you can create a DataLoader class -which wraps your multiple Datasets using ConcatDataset. This, of course, -also works for testing, validation, and prediction Datasets.

-
from torch.utils.data import ConcatDataset
-
-
-class LitModel(LightningModule):
-    def train_dataloader(self):
-        concat_dataset = ConcatDataset(datasets.ImageFolder(traindir_A), datasets.ImageFolder(traindir_B))
-
-        loader = DataLoader(
-            concat_dataset, batch_size=args.batch_size, shuffle=True, num_workers=args.workers, pin_memory=True
-        )
-        return loader
-
-    def val_dataloader(self):
-        # SAME
-        ...
-
-    def test_dataloader(self):
-        # SAME
-        ...
-
-
-
-
-

Return Multiple DataLoaders

-

You can set multiple DataLoaders in your LightningModule, and Lightning will take care of batch combination.

-

For more details, refer to multiple_trainloader_mode

-
class LitModel(LightningModule):
-    def train_dataloader(self):
-
-        loader_a = DataLoader(range(6), batch_size=4)
-        loader_b = DataLoader(range(15), batch_size=5)
-
-        # pass loaders as a dict. This will create batches like this:
-        # {'a': batch from loader_a, 'b': batch from loader_b}
-        loaders = {"a": loader_a, "b": loader_b}
-
-        # OR:
-        # pass loaders as sequence. This will create batches like this:
-        # [batch from loader_a, batch from loader_b]
-        loaders = [loader_a, loader_b]
-
-        return loaders
-
-
-

Furthermore, Lightning also supports nested lists and dicts (or a combination).

-
class LitModel(LightningModule):
-    def train_dataloader(self):
-
-        loader_a = DataLoader(range(8), batch_size=4)
-        loader_b = DataLoader(range(16), batch_size=2)
-
-        return {"a": loader_a, "b": loader_b}
-
-    def training_step(self, batch, batch_idx):
-        # access a dictionary with a batch from each DataLoader
-        batch_a = batch["a"]
-        batch_b = batch["b"]
-
-
-
class LitModel(LightningModule):
-    def train_dataloader(self):
-
-        loader_a = DataLoader(range(8), batch_size=4)
-        loader_b = DataLoader(range(16), batch_size=4)
-        loader_c = DataLoader(range(32), batch_size=4)
-        loader_c = DataLoader(range(64), batch_size=4)
-
-        # pass loaders as a nested dict. This will create batches like this:
-        loaders = {"loaders_a_b": [loader_a, loader_b], "loaders_c_d": {"c": loader_c, "d": loader_d}}
-        return loaders
-
-    def training_step(self, batch, batch_idx):
-        # access the data
-        batch_a_b = batch["loaders_a_b"]
-        batch_c_d = batch["loaders_c_d"]
-
-        batch_a = batch_a_b[0]
-        batch_b = batch_a_b[1]
-
-        batch_c = batch_c_d["c"]
-        batch_d = batch_c_d["d"]
-
-
-

Alternatively, you can also pass in a CombinedLoader containing multiple DataLoaders.

-
from pytorch_lightning.trainer.supporters import CombinedLoader
-
-
-def train_dataloader(self):
-    loader_a = DataLoader()
-    loader_b = DataLoader()
-    loaders = {"a": loader_a, "b": loader_b}
-    combined_loader = CombinedLoader(loaders, mode="max_size_cycle")
-    return combined_loader
-
-
-def training_step(self, batch, batch_idx):
-    batch_a = batch["a"]
-    batch_b = batch["b"]
-
-
-
-
-
-

Multiple Validation/Test/Predict DataLoaders

-

For validation, test and predict DataLoaders, you can pass a single DataLoader or a list of them. This optional named -parameter can be used in conjunction with any of the above use cases. You can choose to pass -the batches sequentially or simultaneously, as is done for the training step. -The default mode for these DataLoaders is sequential. Note that when using a sequence of DataLoaders you need -to add an additional argument dataloader_idx in their corresponding step specific hook. The corresponding loop will process -the DataLoaders in sequential order; that is, the first DataLoader will be processed completely, then the second one, and so on.

-

Refer to the following for more details for the default sequential option:

-
    -
  • val_dataloader()

  • -
  • test_dataloader()

  • -
  • predict_dataloader()

  • -
-
def val_dataloader(self):
-    loader_1 = DataLoader()
-    loader_2 = DataLoader()
-    return [loader_1, loader_2]
-
-
-def validation_step(self, batch, batch_idx, dataloader_idx):
-    ...
-
-
-

Evaluation DataLoaders are iterated over sequentially. If you want to iterate over them in parallel, PyTorch Lightning provides a CombinedLoader object which supports collections of DataLoaders such as list, tuple, or dictionary. The DataLoaders can be accessed using in the same way as the provided structure:

-
from pytorch_lightning.trainer.supporters import CombinedLoader
-
-
-def val_dataloader(self):
-    loader_a = DataLoader()
-    loader_b = DataLoader()
-    loaders = {"a": loader_a, "b": loader_b}
-    combined_loaders = CombinedLoader(loaders, mode="max_size_cycle")
-    return combined_loaders
-
-
-def validation_step(self, batch, batch_idx):
-    batch_a = batch["a"]
-    batch_b = batch["b"]
-
-
-
-
-

Evaluate with Additional DataLoaders

-

You can evaluate your models using additional DataLoaders even if the DataLoader specific hooks haven’t been defined within your -LightningModule. For example, this would be the case if your test data -set is not available at the time your model was declared. Simply pass the test set to the test() method:

-
# setup your DataLoader
-test = DataLoader(...)
-
-# test (pass in the loader)
-trainer.test(dataloaders=test)
-
-
-
-
-
-
-

Accessing DataLoaders within LightningModule

-

In the case that you require access to the DataLoader or Dataset objects, DataLoaders for each step can be accessed using the Trainer object:

-
from pytorch_lightning import LightningModule
-
-
-class Model(LightningModule):
-    def test_step(self, batch, batch_idx, dataloader_idx):
-        test_dl = self.trainer.test_dataloaders[dataloader_idx]
-        test_dataset = test_dl.dataset
-        test_sampler = test_dl.sampler
-        ...
-        # extract metadata, etc. from the dataset:
-        ...
-
-
-

If you are using a CombinedLoader object which allows you to fetch batches from a collection of DataLoaders -simultaneously which supports collections of DataLoader such as list, tuple, or dictionary. The DataLoaders can be accessed using the same collection structure:

-
from pytorch_lightning.trainer.supporters import CombinedLoader
-
-test_dl1 = ...
-test_dl2 = ...
-
-# If you provided a list of DataLoaders:
-
-combined_loader = CombinedLoader([test_dl1, test_dl2])
-list_of_loaders = combined_loader.loaders
-test_dl1 = list_of_loaders.loaders[0]
-
-
-# If you provided dictionary of DataLoaders:
-
-combined_loader = CombinedLoader({"dl1": test_dl1, "dl2": test_dl2})
-dictionary_of_loaders = combined_loader.loaders
-test_dl1 = dictionary_of_loaders["dl1"]
-
-
-
-
-
-

Sequential Data

-

Lightning has built in support for dealing with sequential data.

-
-

Packed Sequences as Inputs

-

When using PackedSequence, do two things:

-
    -
  1. Return either a padded tensor in dataset or a list of variable length tensors in the DataLoader’s collate_fn (example shows the list implementation).

  2. -
  3. Pack the sequence in forward or training and validation steps depending on use case.

  4. -
-
-

-
-
# For use in DataLoader
-def collate_fn(batch):
-    x = [item[0] for item in batch]
-    y = [item[1] for item in batch]
-    return x, y
-
-
-# In LightningModule
-def training_step(self, batch, batch_idx):
-    x = rnn.pack_sequence(batch[0], enforce_sorted=False)
-    y = rnn.pack_sequence(batch[1], enforce_sorted=False)
-
-
-
-
-

Truncated Backpropagation Through Time (TBPTT)

-

There are times when multiple backwards passes are needed for each batch. -For example, it may save memory to use Truncated Backpropagation Through Time when training RNNs.

-

Lightning can handle TBPTT automatically via this flag.

-
from pytorch_lightning import LightningModule
-
-
-class MyModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        # Important: This property activates truncated backpropagation through time
-        # Setting this value to 2 splits the batch into sequences of size 2
-        self.truncated_bptt_steps = 2
-
-    # Truncated back-propagation through time
-    def training_step(self, batch, batch_idx, hiddens):
-        # the training step must be updated to accept a ``hiddens`` argument
-        # hiddens are the hiddens from the previous truncated backprop step
-        out, hiddens = self.lstm(data, hiddens)
-        return {"loss": ..., "hiddens": hiddens}
-
-
-
-

Note

-

If you need to modify how the batch is split, -override tbptt_split_batch().

-
-
-
-

Iterable Datasets

-

Lightning supports using IterableDataset as well as map-style Datasets. IterableDatasets provide a more natural -option when using sequential data.

-
-

Note

-

When using an IterableDataset you must set the val_check_interval to 1.0 (the default) or an int -(specifying the number of training batches to run before each validation loop) when initializing the Trainer. This is -because the IterableDataset does not have a __len__ and Lightning requires this to calculate the validation -interval when val_check_interval is less than one. Similarly, you can set limit_{mode}_batches to a float or -an int. If it is set to 0.0 or 0, it will set num_{mode}_batches to 0, if it is an int, it will set num_{mode}_batches -to limit_{mode}_batches, if it is set to 1.0 it will run for the whole dataset, otherwise it will throw an exception. -Here mode can be train/val/test/predict.

-
-

When iterable datasets are used, Lightning will pre-fetch 1 batch (in addition to the current batch) so it can detect -when the training will stop and run validation if necessary.

-
# IterableDataset
-class CustomDataset(IterableDataset):
-    def __init__(self, data):
-        self.data_source = data
-
-    def __iter__(self):
-        return iter(self.data_source)
-
-
-# Setup DataLoader
-def train_dataloader(self):
-    seq_data = ["A", "long", "time", "ago", "in", "a", "galaxy", "far", "far", "away"]
-    iterable_dataset = CustomDataset(seq_data)
-
-    dataloader = DataLoader(dataset=iterable_dataset, batch_size=5)
-    return dataloader
-
-
-
# Set val_check_interval
-trainer = Trainer(val_check_interval=100)
-
-# Set limit_val_batches to 0.0 or 0
-trainer = Trainer(limit_val_batches=0.0)
-
-# Set limit_val_batches as an int
-trainer = Trainer(limit_val_batches=100)
-
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/guides/speed.html b/docs/guides/speed.html deleted file mode 100644 index a48523c..0000000 --- a/docs/guides/speed.html +++ /dev/null @@ -1,1090 +0,0 @@ - - - - - - - - - - - - - - Speed Up Model Training — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Speed Up Model Training
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Speed Up Model Training

-

When you are limited with the resources, it becomes hard to speed up model training and reduce the training time -without affecting the model’s performance. There are multiple ways you can speed up your model’s time to convergence.

-
-

Training on Accelerators

-

Use when: Whenever possible!

-

With Lightning, running on GPUs, TPUs, IPUs on multiple nodes is a simple switch of a flag.

-
-

GPU Training

-

Lightning supports a variety of plugins to speed up distributed GPU training. Most notably:

-
    -
  • DDPStrategy

  • -
  • DDPShardedStrategy

  • -
  • DeepSpeedStrategy

  • -
-
# run on 1 gpu
-trainer = Trainer(accelerator="gpu", devices=1)
-
-# train on 8 GPUs, using the DDP strategy
-trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp")
-
-# train on multiple GPUs across nodes (uses 8 GPUs in total)
-trainer = Trainer(accelerator="gpu", devices=2, num_nodes=4)
-
-
-
-

GPU Training Speedup Tips

-

When training on single or multiple GPU machines, Lightning offers a host of advanced optimizations to improve throughput, memory efficiency, and model scaling. -Refer to Advanced GPU Optimized Training for more details.

-
-
Prefer DDP Over DP
-

DataParallelStrategy performs three GPU transfers for EVERY batch:

-
    -
  1. Copy the model to the device.

  2. -
  3. Copy the data to the device.

  4. -
  5. Copy the outputs of each device back to the main device.

  6. -
-Animation showing DP execution. -
-

-
-

Whereas DDPStrategy only performs two transfer operations, making DDP much faster than DP:

-
    -
  1. Moving data to the device.

  2. -
  3. Transfer and sync gradients.

  4. -
-Animation showing DDP execution. -
-

-
-

For more details on how to tune performance with DDP, please see the DDP Optimizations section.

-
-
-
DataLoaders
-

When building your DataLoader set num_workers>0 and pin_memory=True (only for GPUs).

-
Dataloader(dataset, num_workers=8, pin_memory=True)
-
-
-
-
-
num_workers
-

The question of how many workers to specify in num_workers is tricky. Here’s a summary of some references, and our suggestions:

-
    -
  1. num_workers=0 means ONLY the main process will load batches (that can be a bottleneck).

  2. -
  3. num_workers=1 means ONLY one worker (just not the main process) will load data, but it will still be slow.

  4. -
  5. The performance of high num_workers depends on the batch size and your machine.

  6. -
  7. A general place to start is to set num_workers equal to the number of CPU cores on that machine. You can get the number of CPU cores in python using os.cpu_count(), but note that depending on your batch size, you may overflow RAM memory.

  8. -
-
-

Warning

-

Increasing num_workers will ALSO increase your CPU memory consumption.

-
-

The best thing to do is to increase the num_workers slowly and stop once there is no more improvement in your training speed.

-

For debugging purposes or for dataloaders that load very small datasets, it is desirable to set num_workers=0. However, this will always log a warning for every dataloader with num_workers <= min(2, os.cpu_count()). In such cases, you can specifically filter this warning by using:

-
import warnings
-
-warnings.filterwarnings("ignore", ".*Consider increasing the value of the `num_workers` argument*")
-
-# or to ignore all warnings that could be false positives
-from pytorch_lightning.utilities.warnings import PossibleUserWarning
-
-warnings.filterwarnings("ignore", category=PossibleUserWarning)
-
-
-
-
-
Spawn
-

When using strategy="ddp_spawn" or training on TPUs, the way multiple GPUs/TPU cores are used is by calling torch.multiprocessing -.spawn() under the hood. The problem is that PyTorch has issues with num_workers>0 when using .spawn(). For this reason, we recommend you -use strategy="ddp" so you can increase the num_workers, however since DDP doesn’t work in an interactive environment like IPython/Jupyter notebooks -your script has to be callable like so:

-
python my_program.py
-
-
-

However, using strategy="ddp_spawn" enables to reduce memory usage with In-Memory Dataset and shared memory tensors. For more info, checkout -Sharing Datasets Across Process Boundaries section.

-
-
-
Persistent Workers
-

When using strategy="ddp_spawn" and num_workers>0, consider setting persistent_workers=True inside your DataLoader since it can result in data-loading bottlenecks and slowdowns. -This is a limitation of Python .spawn() and PyTorch.

-
-
-
-
-

TPU Training

-

You can set the devices trainer argument to 1, [7] (specific core) or eight cores.

-
# train on 1 TPU core
-trainer = Trainer(accelerator="tpu", devices=1)
-
-# train on 7th TPU core
-trainer = Trainer(accelerator="tpu", devices=[7])
-
-# train on 8 TPU cores
-trainer = Trainer(accelerator="tpu", devices=8)
-
-
-

To train on more than eight cores (a POD), -submit this script using the xla_dist script.

-

Example:

-
python -m torch_xla.distributed.xla_dist
---tpu=$TPU_POD_NAME
---conda-env=torch-xla-nightly
---env=XLA_USE_BF16=1
--- python your_trainer_file.py
-
-
-

Read more in our Speed Up Model Training and Plugins guides.

-
-
-
-
-

Early Stopping

-

Usually, long training epochs can lead to either overfitting or no major improvements in your metrics due to no limited convergence. -Here EarlyStopping callback can help you stop the training entirely by monitoring a metric of your choice.

-

You can read more about it here.

-
-
-
-

Mixed Precision (16-bit) Training

-

Lower precision, such as the 16-bit floating-point, enables the training and deployment of large neural networks since they require less memory, enhance data transfer operations since they required -less memory bandwidth and run match operations much faster on GPUs that support Tensor Core.

-

Use when:

-
    -
  • You want to optimize for memory usage on a GPU.

  • -
  • You have a GPU that supports 16-bit precision (NVIDIA pascal architecture or newer).

  • -
  • Your optimization algorithm (training_step) is numerically stable.

  • -
  • You want to be the cool person in the lab :p

  • -
-
-

-
-

Mixed precision combines the use of both 32 and 16-bit floating points to reduce memory footprint during model training, resulting in improved performance, achieving upto +3X speedups on modern GPUs.

-

Lightning offers mixed precision training for GPUs and CPUs, as well as bfloat16 mixed precision training for TPUs.

-
# 16-bit precision
-trainer = Trainer(precision=16, accelerator="gpu", devices=4)
-
-
-

Read more about mixed-precision training.

-
-
-
-

Control Training Epochs

-

Use when: You run a hyperparameter search to find good initial parameters and want to save time, cost (money), or power (environment). -It can allow you to be more cost efficient and also run more experiments at the same time.

-

You can use Trainer flags to force training for a minimum number of epochs or limit it to a max number of epochs. Use the min_epochs and max_epochs Trainer flags to set the number of epochs to run. -Setting min_epochs=N makes sure that the training will run for at least N epochs. Setting max_epochs=N will ensure that training won’t happen after -N epochs.

-
# DEFAULT
-trainer = Trainer(min_epochs=1, max_epochs=1000)
-
-
-

If running iteration based training, i.e., infinite / iterable DataLoader, you can also control the number of steps with the min_steps and max_steps flags:

-
trainer = Trainer(max_steps=1000)
-
-trainer = Trainer(min_steps=100)
-
-
-

You can also interrupt training based on training time:

-
# Stop after 12 hours of training or when reaching 10 epochs (string)
-trainer = Trainer(max_time="00:12:00:00", max_epochs=10)
-
-# Stop after 1 day and 5 hours (dict)
-trainer = Trainer(max_time={"days": 1, "hours": 5})
-
-
-

Learn more in our Trainer flags guide.

-
-
-
-

Control Validation Frequency

-
-

Check Validation Every n Epochs

-

Use when: You have a small dataset and want to run fewer validation checks.

-

You can limit validation check to only run every n epochs using the check_val_every_n_epoch Trainer flag.

-
# default
-trainer = Trainer(check_val_every_n_epoch=1)
-
-# runs validation after every 7th Epoch
-trainer = Trainer(check_val_every_n_epoch=7)
-
-
-
-
-

Validation Within Training Epoch

-

Use when: You have a large training dataset and want to run mid-epoch validation checks.

-

For large datasets, it’s often desirable to check validation multiple times within a training epoch. -Pass in a float to check that often within one training epoch. Pass in an int K to check every K training batch. -Must use an int if using an IterableDataset.

-
# default
-trainer = Trainer(val_check_interval=1.0)
-
-# check every 1/4 th of an epoch
-trainer = Trainer(val_check_interval=0.25)
-
-# check every 100 train batches (ie: for IterableDatasets or fixed frequency)
-trainer = Trainer(val_check_interval=100)
-
-
-

Learn more in our Trainer flags guide.

-
-
-
-
-

Preload Data Into RAM

-

Use when: You need access to all samples in a dataset at once.

-

When your training or preprocessing requires many operations to be performed on entire dataset(s), it can -sometimes be beneficial to store all data in RAM given there is enough space. -However, loading all data at the beginning of the training script has the disadvantage that it can take a long -time, and hence, it slows down the development process. Another downside is that in multiprocessing (e.g., DDP) -the data would get copied in each process. -One can overcome these problems by copying the data into RAM in advance. -Most UNIX-based operating systems provide direct access to tmpfs through a mount point typically named /dev/shm.

-

Increase shared memory if necessary. Refer to the documentation of your OS on how to do this.

-
    -
  1. Copy training data to shared memory:

    -
    cp -r /path/to/data/on/disk /dev/shm/
    -
    -
    -
  2. -
  3. Refer to the new data root in your script or command-line arguments:

    -
    datamodule = MyDataModule(data_root="/dev/shm/my_data")
    -
    -
    -
  4. -
-
-
-
-

Model Toggling

-

Use when: Performing gradient accumulation with multiple optimizers in a -distributed setting.

-

Here is an explanation of what it does:

-
    -
  • Considering the current optimizer as A and all other optimizers as B.

  • -
  • Toggling, which means all parameters from B exclusive to A will have their requires_grad attribute set to False.

  • -
  • Restoring their original state when exiting the context manager.

  • -
-

When performing gradient accumulation, there is no need to perform grad synchronization during the accumulation phase. -Setting sync_grad to False will block this synchronization and improve your training speed.

-

LightningOptimizer provides a -toggle_model() function as a -contextlib.contextmanager() for advanced users.

-

Here is an example of an advanced use case:

-
# Scenario for a GAN with gradient accumulation every two batches and optimized for multiple GPUs.
-class SimpleGAN(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.automatic_optimization = False
-
-    def training_step(self, batch, batch_idx):
-        # Implementation follows the PyTorch tutorial:
-        # https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html
-        g_opt, d_opt = self.optimizers()
-
-        X, _ = batch
-        X.requires_grad = True
-        batch_size = X.shape[0]
-
-        real_label = torch.ones((batch_size, 1), device=self.device)
-        fake_label = torch.zeros((batch_size, 1), device=self.device)
-
-        # Sync and clear gradients
-        # at the end of accumulation or
-        # at the end of an epoch.
-        is_last_batch_to_accumulate = (batch_idx + 1) % 2 == 0 or self.trainer.is_last_batch
-
-        g_X = self.sample_G(batch_size)
-
-        ##########################
-        # Optimize Discriminator #
-        ##########################
-        with d_opt.toggle_model(sync_grad=is_last_batch_to_accumulate):
-            d_x = self.D(X)
-            errD_real = self.criterion(d_x, real_label)
-
-            d_z = self.D(g_X.detach())
-            errD_fake = self.criterion(d_z, fake_label)
-
-            errD = errD_real + errD_fake
-
-            self.manual_backward(errD)
-            if is_last_batch_to_accumulate:
-                d_opt.step()
-                d_opt.zero_grad()
-
-        ######################
-        # Optimize Generator #
-        ######################
-        with g_opt.toggle_model(sync_grad=is_last_batch_to_accumulate):
-            d_z = self.D(g_X)
-            errG = self.criterion(d_z, real_label)
-
-            self.manual_backward(errG)
-            if is_last_batch_to_accumulate:
-                g_opt.step()
-                g_opt.zero_grad()
-
-        self.log_dict({"g_loss": errG, "d_loss": errD}, prog_bar=True)
-
-
-
-
-
-

Set Grads to None

-

In order to improve performance, you can override optimizer_zero_grad().

-

For a more detailed explanation of the pros / cons of this technique, -read the documentation for zero_grad() by the PyTorch team.

-
class Model(LightningModule):
-    def optimizer_zero_grad(self, epoch, batch_idx, optimizer, optimizer_idx):
-        optimizer.zero_grad(set_to_none=True)
-
-
-
-
-
-

Things to Avoid

-
-

.item(), .numpy(), .cpu()

-

Don’t call .item() anywhere in your code. Use .detach() instead to remove the connected graph calls. Lightning -takes a great deal of care to be optimized for this.

-
-
-

Clear Cache

-

Don’t call torch.cuda.empty_cache() unnecessarily! Every time you call this, ALL your GPUs have to wait to sync.

-
-
-

Transferring Tensors to Device

-

LightningModules know what device they are on! Construct tensors on the device directly to avoid CPU->Device transfer.

-
# bad
-t = torch.rand(2, 2).cuda()
-
-# good (self is LightningModule)
-t = torch.rand(2, 2, device=self.device)
-
-
-

For tensors that need to be model attributes, it is best practice to register them as buffers in the module’s -__init__ method:

-
# bad
-self.t = torch.rand(2, 2, device=self.device)
-
-# good
-self.register_buffer("t", torch.rand(2, 2))
-
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index f3c9f7a..0000000 --- a/docs/index.html +++ /dev/null @@ -1,946 +0,0 @@ - - - - - - - - - - - - - - ⚡ PyTorch Lightning에 오신 것을 환영합니다! — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • ⚡ PyTorch Lightning에 오신 것을 환영합니다!
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

⚡ PyTorch Lightning에 오신 것을 환영합니다!

-

-
Animation showing how to convert a standard training loop to a Lightning loop
-

PyTorch Lightning(파이토치 라이트닝))은 대규모에서 성능을 포기하지 않으면서 최대한의 유연성을 필요로 하는 전문적인 AI 연구자들과 머신러닝 엔지니어들을 위한 딥러닝 프레임워크입니다. -Lightning(라이트닝)은 프로젝트가 생각으로부터 문서 / 제품화에 이르는 동안 함께 발전합니다.

-
-

-
-

Lightning 설치하기

-
-

Pip 사용자라면,

-
pip install pytorch-lightning
-
-
-
-

Conda 사용자라면,

-
conda install pytorch-lightning -c conda-forge
-
-
-
-

또는 advanced install guide 참조하세요.

-
-
-

처음이신가요?

-
-
-

이미 Lightning 사용자라면?

-
- - - - - -
-
-

색인 및 검색

- -
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced.html b/docs/levels/advanced.html deleted file mode 100644 index 3842804..0000000 --- a/docs/levels/advanced.html +++ /dev/null @@ -1,796 +0,0 @@ - - - - - - - - - - - - - - Advanced skills — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced_level_15.html b/docs/levels/advanced_level_15.html deleted file mode 100644 index 10c6e31..0000000 --- a/docs/levels/advanced_level_15.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Level 15: Customize configs to run in production — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 15: Customize configs to run in production
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced_level_16.html b/docs/levels/advanced_level_16.html deleted file mode 100644 index 18abbab..0000000 --- a/docs/levels/advanced_level_16.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Level 16: Customize the trainer — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 16: Customize the trainer
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced_level_17.html b/docs/levels/advanced_level_17.html deleted file mode 100644 index b51ba7f..0000000 --- a/docs/levels/advanced_level_17.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - Level 17: Own the training loop — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 17: Own the training loop
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level 17: Own the training loop

-

Learn all the ways of owning your raw PyTorch loops with Lighting.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced_level_18.html b/docs/levels/advanced_level_18.html deleted file mode 100644 index 1653dac..0000000 --- a/docs/levels/advanced_level_18.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Level 18: Enable advanced checkpointing — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 18: Enable advanced checkpointing
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced_level_19.html b/docs/levels/advanced_level_19.html deleted file mode 100644 index 616430b..0000000 --- a/docs/levels/advanced_level_19.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - Level 19: Explore IPUs — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 19: Explore IPUs
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced_level_20.html b/docs/levels/advanced_level_20.html deleted file mode 100644 index 851524a..0000000 --- a/docs/levels/advanced_level_20.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Level 19: Explore HPUs — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 19: Explore HPUs
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced_level_21.html b/docs/levels/advanced_level_21.html deleted file mode 100644 index 7c04b94..0000000 --- a/docs/levels/advanced_level_21.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - Level 21: Master TPUs — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 21: Master TPUs
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/advanced_level_22.html b/docs/levels/advanced_level_22.html deleted file mode 100644 index 7d373d5..0000000 --- a/docs/levels/advanced_level_22.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Level 22: Reach 1 trillion parameters on GPUs — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 22: Reach 1 trillion parameters on GPUs
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/basic_level_2.html b/docs/levels/basic_level_2.html deleted file mode 100644 index dca840d..0000000 --- a/docs/levels/basic_level_2.html +++ /dev/null @@ -1,720 +0,0 @@ - - - - - - - - - - - - - - Level 2: Add a validation and test set — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 2: Add a validation and test set
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/basic_level_5.html b/docs/levels/basic_level_5.html deleted file mode 100644 index 98244bd..0000000 --- a/docs/levels/basic_level_5.html +++ /dev/null @@ -1,720 +0,0 @@ - - - - - - - - - - - - - - Level 5: Debug, visualize and find performance bottlenecks — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 5: Debug, visualize and find performance bottlenecks
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/core_level_3.html b/docs/levels/core_level_3.html deleted file mode 100644 index fb1870d..0000000 --- a/docs/levels/core_level_3.html +++ /dev/null @@ -1,710 +0,0 @@ - - - - - - - - - - - - - - Level 3: Visualize training progress — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 3: Visualize training progress
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/core_level_6.html b/docs/levels/core_level_6.html deleted file mode 100644 index 231c0cf..0000000 --- a/docs/levels/core_level_6.html +++ /dev/null @@ -1,720 +0,0 @@ - - - - - - - - - - - - - - Level 6: Predict with your model — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 6: Predict with your model
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/core_skills.html b/docs/levels/core_skills.html deleted file mode 100644 index 47ba28c..0000000 --- a/docs/levels/core_skills.html +++ /dev/null @@ -1,776 +0,0 @@ - - - - - - - - - - - - - - Basic skills — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
- - -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/expert.html b/docs/levels/expert.html deleted file mode 100644 index e040593..0000000 --- a/docs/levels/expert.html +++ /dev/null @@ -1,766 +0,0 @@ - - - - - - - - - - - - - - Expert skills — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Expert skills

-

Customize and extend Lightning for things like custom hardware or distributed strategies.

-

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/expert_level_23.html b/docs/levels/expert_level_23.html deleted file mode 100644 index 539eb93..0000000 --- a/docs/levels/expert_level_23.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Level 23: Extend the Lightning CLI — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 23: Extend the Lightning CLI
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/expert_level_24.html b/docs/levels/expert_level_24.html deleted file mode 100644 index 0b70438..0000000 --- a/docs/levels/expert_level_24.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Level 24: Integrate a custom cluster — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 24: Integrate a custom cluster
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/expert_level_27.html b/docs/levels/expert_level_27.html deleted file mode 100644 index b2b3110..0000000 --- a/docs/levels/expert_level_27.html +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - - - - - - - - - Level 27: Add a new accelerator or Strategy — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 27: Add a new accelerator or Strategy
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level 27: Add a new accelerator or Strategy

-

Integrate a new accelerator or distributed strategy.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate.html b/docs/levels/intermediate.html deleted file mode 100644 index d7949f9..0000000 --- a/docs/levels/intermediate.html +++ /dev/null @@ -1,796 +0,0 @@ - - - - - - - - - - - - - - Intermediate skills — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Intermediate skills
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
- - -
-
- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate_level_10.html b/docs/levels/intermediate_level_10.html deleted file mode 100644 index 5f62d2e..0000000 --- a/docs/levels/intermediate_level_10.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - Level 10: Understand your model — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 10: Understand your model
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level 10: Understand your model

-

Find the best model using advanced visualizations for deeper insights.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate_level_11.html b/docs/levels/intermediate_level_11.html deleted file mode 100644 index dec821e..0000000 --- a/docs/levels/intermediate_level_11.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - - Level 11: Explore SOTA scaling techniques — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 11: Explore SOTA scaling techniques
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate_level_12.html b/docs/levels/intermediate_level_12.html deleted file mode 100644 index aedeadd..0000000 --- a/docs/levels/intermediate_level_12.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - Level 12: Deploy your models — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 12: Deploy your models
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level 12: Deploy your models

-

In this level you’ll learn a few options for deploying models into production.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate_level_13.html b/docs/levels/intermediate_level_13.html deleted file mode 100644 index d124a7e..0000000 --- a/docs/levels/intermediate_level_13.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - Level 13: Optimize training speed — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 13: Optimize training speed
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level 13: Optimize training speed

-

In this level you’ll use advanced profilers and mixed precision techniques to train bigger models faster.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate_level_14.html b/docs/levels/intermediate_level_14.html deleted file mode 100644 index 4fcbe45..0000000 --- a/docs/levels/intermediate_level_14.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - Level 14: Run on on-prem clusters — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 14: Run on on-prem clusters
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate_level_7.html b/docs/levels/intermediate_level_7.html deleted file mode 100644 index 00c7344..0000000 --- a/docs/levels/intermediate_level_7.html +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - - - - - - - - - Level 7: Interactive cloud development — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 7: Interactive cloud development
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level 7: Interactive cloud development

-

Learn to develop models on cloud GPUs and TPUs.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate_level_8.html b/docs/levels/intermediate_level_8.html deleted file mode 100644 index b4f7af8..0000000 --- a/docs/levels/intermediate_level_8.html +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - - - - - - - - - Level 8: Run models on the cloud — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 8: Run models on the cloud
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level 8: Run models on the cloud

-

Learn to run models on the cloud in the background asynchroneously.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/levels/intermediate_level_9.html b/docs/levels/intermediate_level_9.html deleted file mode 100644 index cd7e491..0000000 --- a/docs/levels/intermediate_level_9.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - - - - - Level 9: Modularize your projects — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Level 9: Modularize your projects
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Level 9: Modularize your projects

-

This module teaches you how to setup complex projects that can be controlled via the CLI.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/links.html b/docs/links.html deleted file mode 100644 index e5b6b99..0000000 --- a/docs/links.html +++ /dev/null @@ -1,684 +0,0 @@ - - - - - - - - - - - - - - <no title> — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- - - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
-
    -
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/model/build_model.html b/docs/model/build_model.html deleted file mode 100644 index bfff626..0000000 --- a/docs/model/build_model.html +++ /dev/null @@ -1,751 +0,0 @@ - - - - - - - - - - - - - - Build a Model — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
- - -
-
-
- - -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/model/build_model_advanced.html b/docs/model/build_model_advanced.html deleted file mode 100644 index a308c90..0000000 --- a/docs/model/build_model_advanced.html +++ /dev/null @@ -1,983 +0,0 @@ - - - - - - - - - - - - - - Own your loop (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Own your loop (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Own your loop (advanced)

-
-

Customize training loop

-Injecting custom code in a training loop -

Inject custom code anywhere in the Training loop using any of the 20+ methods (Hooks) available in the LightningModule.

-
class LitModel(pl.LightningModule):
-    def backward(self, loss, optimizer, optimizer_idx):
-        loss.backward()
-
-
-
-
-
-

Manual Optimization

-

For advanced research topics like reinforcement learning, sparse coding, or GAN research, it may be desirable to -manually manage the optimization process.

-

This is only recommended for experts who need ultimate flexibility. -Lightning will handle only accelerator, precision and strategy logic. -The users are left with optimizer.zero_grad(), gradient accumulation, model toggling, etc..

-

To manually optimize, do the following:

-
    -
  • Set self.automatic_optimization=False in your LightningModule’s __init__.

  • -
  • Use the following functions and call them manually:

    -
      -
    • self.optimizers() to access your optimizers (one or multiple)

    • -
    • optimizer.zero_grad() to clear the gradients from the previous training step

    • -
    • self.manual_backward(loss) instead of loss.backward()

    • -
    • optimizer.step() to update your model parameters

    • -
    -
  • -
-

Here is a minimal example of manual optimization.

-
from pytorch_lightning import LightningModule
-
-
-class MyModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        # Important: This property activates manual optimization.
-        self.automatic_optimization = False
-
-    def training_step(self, batch, batch_idx):
-        opt = self.optimizers()
-        opt.zero_grad()
-        loss = self.compute_loss(batch)
-        self.manual_backward(loss)
-        opt.step()
-
-
-
-

Warning

-

Before 1.2, optimizer.step() was calling optimizer.zero_grad() internally. -From 1.2, it is left to the user’s expertise.

-
-
-

Tip

-

Be careful where you call optimizer.zero_grad(), or your model won’t converge. -It is good practice to call optimizer.zero_grad() before self.manual_backward(loss).

-
-
-

Access your Own Optimizer

-

The provided optimizer is a LightningOptimizer object wrapping your own optimizer -configured in your configure_optimizers(). You can access your own optimizer -with optimizer.optimizer. However, if you use your own optimizer to perform a step, Lightning won’t be able to -support accelerators, precision and profiling for you.

-
class Model(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.automatic_optimization = False
-        ...
-
-    def training_step(self, batch, batch_idx):
-        optimizer = self.optimizers()
-
-        # `optimizer` is a `LightningOptimizer` wrapping the optimizer.
-        # To access it, do the following.
-        # However, it won't work on TPU, AMP, etc...
-        optimizer = optimizer.optimizer
-        ...
-
-
-
-
-

Gradient Accumulation

-

You can accumulate gradients over batches similarly to accumulate_grad_batches argument in -Trainer for automatic optimization. To perform gradient accumulation with one optimizer -after every N steps, you can do as such.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    opt = self.optimizers()
-
-    loss = self.compute_loss(batch)
-    self.manual_backward(loss)
-
-    # accumulate gradients of N batches
-    if (batch_idx + 1) % N == 0:
-        opt.step()
-        opt.zero_grad()
-
-
-
-
-

Use Multiple Optimizers (like GANs)

-

Here is an example training a simple GAN with multiple optimizers using manual optimization.

-
import torch
-from torch import Tensor
-from pytorch_lightning import LightningModule
-
-
-class SimpleGAN(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.G = Generator()
-        self.D = Discriminator()
-
-        # Important: This property activates manual optimization.
-        self.automatic_optimization = False
-
-    def sample_z(self, n) -> Tensor:
-        sample = self._Z.sample((n,))
-        return sample
-
-    def sample_G(self, n) -> Tensor:
-        z = self.sample_z(n)
-        return self.G(z)
-
-    def training_step(self, batch, batch_idx):
-        # Implementation follows the PyTorch tutorial:
-        # https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html
-        g_opt, d_opt = self.optimizers()
-
-        X, _ = batch
-        batch_size = X.shape[0]
-
-        real_label = torch.ones((batch_size, 1), device=self.device)
-        fake_label = torch.zeros((batch_size, 1), device=self.device)
-
-        g_X = self.sample_G(batch_size)
-
-        ##########################
-        # Optimize Discriminator #
-        ##########################
-        d_x = self.D(X)
-        errD_real = self.criterion(d_x, real_label)
-
-        d_z = self.D(g_X.detach())
-        errD_fake = self.criterion(d_z, fake_label)
-
-        errD = errD_real + errD_fake
-
-        d_opt.zero_grad()
-        self.manual_backward(errD)
-        d_opt.step()
-
-        ######################
-        # Optimize Generator #
-        ######################
-        d_z = self.D(g_X)
-        errG = self.criterion(d_z, real_label)
-
-        g_opt.zero_grad()
-        self.manual_backward(errG)
-        g_opt.step()
-
-        self.log_dict({"g_loss": errG, "d_loss": errD}, prog_bar=True)
-
-    def configure_optimizers(self):
-        g_opt = torch.optim.Adam(self.G.parameters(), lr=1e-5)
-        d_opt = torch.optim.Adam(self.D.parameters(), lr=1e-5)
-        return g_opt, d_opt
-
-
-
-
-

Learning Rate Scheduling

-

Every optimizer you use can be paired with any -Learning Rate Scheduler. Please see the -documentation of configure_optimizers() for all the available options

-

You can call lr_scheduler.step() at arbitrary intervals. -Use self.lr_schedulers() in your LightningModule to access any learning rate schedulers -defined in your configure_optimizers().

-
-

Warning

-
    -
  • Before v1.3, Lightning automatically called lr_scheduler.step() in both automatic and manual optimization. From -1.3, lr_scheduler.step() is now for the user to call at arbitrary intervals.

  • -
  • Note that the lr_scheduler_config keys, such as "frequency" and "interval", will be ignored even if they are provided in -your configure_optimizers() during manual optimization.

  • -
-
-

Here is an example calling lr_scheduler.step() every step.

-
# step every batch
-def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    # do forward, backward, and optimization
-    ...
-
-    # single scheduler
-    sch = self.lr_schedulers()
-    sch.step()
-
-    # multiple schedulers
-    sch1, sch2 = self.lr_schedulers()
-    sch1.step()
-    sch2.step()
-
-
-

If you want to call lr_scheduler.step() every N steps/epochs, do the following.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    # do forward, backward, and optimization
-    ...
-
-    sch = self.lr_schedulers()
-
-    # step every N batches
-    if (batch_idx + 1) % N == 0:
-        sch.step()
-
-    # step every N epochs
-    if self.trainer.is_last_batch and (self.trainer.current_epoch + 1) % N == 0:
-        sch.step()
-
-
-

If you want to call schedulers that require a metric value after each epoch, consider doing the following:

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_epoch_end(self, outputs):
-    sch = self.lr_schedulers()
-
-    # If the selected scheduler is a ReduceLROnPlateau scheduler.
-    if isinstance(sch, torch.optim.lr_scheduler.ReduceLROnPlateau):
-        sch.step(self.trainer.callback_metrics["loss"])
-
-
-
-
-

Use Closure for LBFGS-like Optimizers

-

It is a good practice to provide the optimizer with a closure function that performs a forward, zero_grad and -backward of your model. It is optional for most optimizers, but makes your code compatible if you switch to an -optimizer which requires a closure, such as LBFGS.

-

See the PyTorch docs for more about the closure.

-

Here is an example using a closure function.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def configure_optimizers(self):
-    return torch.optim.LBFGS(...)
-
-
-def training_step(self, batch, batch_idx):
-    opt = self.optimizers()
-
-    def closure():
-        loss = self.compute_loss(batch)
-        opt.zero_grad()
-        self.manual_backward(loss)
-        return loss
-
-    opt.step(closure=closure)
-
-
-
-

Warning

-

The LBFGS optimizer is not supported for apex AMP, native AMP, IPUs, or DeepSpeed.

-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/model/build_model_expert.html b/docs/model/build_model_expert.html deleted file mode 100644 index 1a1cc9d..0000000 --- a/docs/model/build_model_expert.html +++ /dev/null @@ -1,1355 +0,0 @@ - - - - - - - - - - - - - - Raw PyTorch loop (expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Raw PyTorch loop (expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Raw PyTorch loop (expert)

-
-
-

LightningLite (Stepping Stone to Lightning)

-

LightningLite enables pure PyTorch users to scale their existing code -on any kind of device while retaining full control over their own loops and optimization logic.

-Animation showing how to convert your PyTorch code to LightningLite. -
-

-
-

LightningLite is the right tool for you if you match one of the two following descriptions:

-
    -
  • I want to quickly scale my existing code to multiple devices with minimal code changes.

  • -
  • I would like to convert my existing code to the Lightning API, but a full path to Lightning transition might be too complex. I am looking for a stepping stone to ensure reproducibility during the transition.

  • -
-
-

Warning

-

LightningLite is currently a beta feature. Its API is subject to change based on your feedback.

-
-
-
-

Learn by example

-
-

My Existing PyTorch Code

-

The run function contains custom training loop used to train MyModel on MyDataset for num_epochs epochs.

-
import torch
-from torch import nn
-from torch.utils.data import DataLoader, Dataset
-
-
-class MyModel(nn.Module):
-    ...
-
-
-class MyDataset(Dataset):
-    ...
-
-
-def run(args):
-    device = "cuda" if torch.cuda.is_available() else "cpu"
-
-    model = MyModel(...).to(device)
-    optimizer = torch.optim.SGD(model.parameters(), ...)
-
-    dataloader = DataLoader(MyDataset(...), ...)
-
-    model.train()
-    for epoch in range(args.num_epochs):
-        for batch in dataloader:
-            batch = batch.to(device)
-            optimizer.zero_grad()
-            loss = model(batch)
-            loss.backward()
-            optimizer.step()
-
-
-run(args)
-
-
-
-
-
-

Convert to LightningLite

-

Here are five required steps to convert to LightningLite.

-
    -
  1. Subclass LightningLite and override its run() method.

  2. -
  3. Move the body of your existing run function into LightningLite run method.

  4. -
  5. Remove all .to(...), .cuda() etc calls since LightningLite will take care of it.

  6. -
  7. Apply setup() over each model and optimizers pair and setup_dataloaders() on all your dataloaders and replace loss.backward() by self.backward(loss).

  8. -
  9. Instantiate your LightningLite subclass and call its run() method.

  10. -
-
-

-
-
import torch
-from torch import nn
-from torch.utils.data import DataLoader, Dataset
-from pytorch_lightning.lite import LightningLite
-
-
-class MyModel(nn.Module):
-    ...
-
-
-class MyDataset(Dataset):
-    ...
-
-
-class Lite(LightningLite):
-    def run(self, args):
-
-        model = MyModel(...)
-        optimizer = torch.optim.SGD(model.parameters(), ...)
-        model, optimizer = self.setup(model, optimizer)  # Scale your model / optimizers
-
-        dataloader = DataLoader(MyDataset(...), ...)
-        dataloader = self.setup_dataloaders(dataloader)  # Scale your dataloaders
-
-        model.train()
-        for epoch in range(args.num_epochs):
-            for batch in dataloader:
-                optimizer.zero_grad()
-                loss = model(batch)
-                self.backward(loss)  # instead of loss.backward()
-                optimizer.step()
-
-
-Lite(...).run(args)
-
-
-

That’s all. You can now train on any kind of device and scale your training. Check out this full MNIST training example with LightningLite.

-

LightningLite takes care of device management, so you don’t have to. -You should remove any device-specific logic within your code.

-

Here is how to train on eight GPUs with torch.bfloat16 precision:

-
Lite(strategy="ddp", devices=8, accelerator="gpu", precision="bf16").run(10)
-
-
-

Here is how to use DeepSpeed Zero3 with eight GPUs and precision 16:

-
Lite(strategy="deepspeed", devices=8, accelerator="gpu", precision=16).run(10)
-
-
-

LightningLite can also figure it out automatically for you!

-
Lite(devices="auto", accelerator="auto", precision=16).run(10)
-
-
-

You can also easily use distributed collectives if required. -Here is an example while running on 256 GPUs (eight GPUs times 32 nodes).

-
class Lite(LightningLite):
-    def run(self):
-
-        # Transfer and concatenate tensors across processes
-        self.all_gather(...)
-
-        # Transfer an object from one process to all the others
-        self.broadcast(..., src=...)
-
-        # The total number of processes running across all devices and nodes.
-        self.world_size
-
-        # The global index of the current process across all devices and nodes.
-        self.global_rank
-
-        # The index of the current process among the processes running on the local node.
-        self.local_rank
-
-        # The index of the current node.
-        self.node_rank
-
-        # Wether this global rank is rank zero.
-        if self.is_global_zero:
-            # do something on rank 0
-            ...
-
-        # Wait for all processes to enter this call.
-        self.barrier()
-
-
-Lite(strategy="ddp", devices=8, num_nodes=32, accelerator="gpu").run()
-
-
-

If you require custom data or model device placement, you can deactivate -LightningLite automatic placement by doing -self.setup_dataloaders(..., move_to_device=False) for the data and -self.setup(..., move_to_device=False) for the model. -Furthermore, you can access the current device from self.device or -rely on to_device() -utility to move an object to the current device.

-
-

Note

-

We recommend instantiating the models within the run() method as large models would cause an out-of-memory error otherwise.

-
-
-

Tip

-

If you have hundreds or thousands of lines within your run() function -and you are feeling unsure about them, then that is the correct feeling. -In 2019, our LightningModule was getting larger -and we got the same feeling, so we started to organize our code for simplicity, interoperability and standardization. -This is definitely a good sign that you should consider refactoring your code and / or switching to -LightningModule ultimately.

-
-
-
-
-

Distributed Training Pitfalls

-

The LightningLite provides you with the tools to scale your training, -but there are several major challenges ahead of you now:

- ---- - - - - - - - - - - - - - - - - - - - - -

Processes divergence

This happens when processes execute a different section of the code due to different if/else conditions, race conditions on existing files and so on, resulting in hanging.

Cross processes reduction

Miscalculated metrics or gradients due to errors in their reduction.

Large sharded models

Instantiation, materialization and state management of large models.

Rank 0 only actions

Logging, profiling, and so on.

Checkpointing / Early stopping / Callbacks / Logging

Ability to customize your training behavior easily and make it stateful.

Fault-tolerant training

Ability to resume from a failure as if it never happened.

-

If you are facing one of those challenges, then you are already meeting the limit of LightningLite. -We recommend you to convert to Lightning, so you never have to worry about those.

-
-
-
-

Convert to Lightning

-

LightningLite is a stepping stone to transition fully to the Lightning API and benefit -from its hundreds of features.

-

You can see our LightningLite class as a -future LightningModule, and slowly refactor your code into its API. -Below, the training_step(), forward(), -configure_optimizers(), train_dataloader() methods -are implemented.

-
class Lite(LightningLite):
-
-    # 1. This would become the LightningModule `__init__` function.
-    def run(self, args):
-        self.args = args
-
-        self.model = MyModel(...)
-
-        self.fit()  # This would be automated by the Lightning Trainer.
-
-    # 2. This can be fully removed as Lightning creates its own fitting loop,
-    # and sets up the model, optimizer, dataloader, etc for you.
-    def fit(self):
-        # setup everything
-        optimizer = self.configure_optimizers()
-        self.model, optimizer = self.setup(self.model, optimizer)
-        dataloader = self.setup_dataloaders(self.train_dataloader())
-
-        # start fitting
-        self.model.train()
-        for epoch in range(num_epochs):
-            for batch in enumerate(dataloader):
-                optimizer.zero_grad()
-                loss = self.training_step(batch, batch_idx)
-                self.backward(loss)
-                optimizer.step()
-
-    # 3. This stays here as it belongs to the LightningModule.
-    def forward(self, x):
-        return self.model(x)
-
-    def training_step(self, batch, batch_idx):
-        return self.forward(batch)
-
-    def configure_optimizers(self):
-        return torch.optim.SGD(self.model.parameters(), ...)
-
-    # 4. [Optionally] This can stay here or be extracted to the LightningDataModule to enable higher composability.
-    def train_dataloader(self):
-        return DataLoader(MyDataset(...), ...)
-
-
-Lite(...).run(args)
-
-
-

Finally, change the run() into a -__init__() and drop the fit call from inside.

-
from pytorch_lightning import LightningDataModule, LightningModule, Trainer
-
-
-class LightningModel(LightningModule):
-    def __init__(self, args):
-        super().__init__()
-        self.model = MyModel(...)
-
-    def forward(self, x):
-        return self.model(x)
-
-    def training_step(self, batch, batch_idx):
-        loss = self(batch)
-        self.log("train_loss", loss)
-        return loss
-
-    def configure_optimizers(self):
-        return torch.optim.SGD(self.model.parameters(), lr=0.001)
-
-
-class BoringDataModule(LightningDataModule):
-    def train_dataloader(self):
-        return DataLoader(MyDataset(...), ...)
-
-
-trainer = Trainer(max_epochs=10)
-trainer.fit(LightningModel(), datamodule=BoringDataModule())
-
-
-

You have successfully converted to PyTorch Lightning, and can now benefit from its hundred of features!

-
-
-
-
-

Lightning Lite Flags

-

Lite is specialized in accelerated distributed training and inference. It offers you convenient ways to configure -your device and communication strategy and to switch seamlessly from one to the other. The terminology and usage are -identical to Lightning, which means minimum effort for you to convert when you decide to do so.

-
-

accelerator

-

Choose one of "cpu", "gpu", "tpu", "auto" (IPU support is coming soon).

-
# CPU accelerator
-lite = Lite(accelerator="cpu")
-
-# Running with GPU Accelerator using 2 GPUs
-lite = Lite(devices=2, accelerator="gpu")
-
-# Running with TPU Accelerator using 8 tpu cores
-lite = Lite(devices=8, accelerator="tpu")
-
-# Running with GPU Accelerator using the DistributedDataParallel strategy
-lite = Lite(devices=4, accelerator="gpu", strategy="ddp")
-
-
-

The "auto" option recognizes the machine you are on and selects the available accelerator.

-
# If your machine has GPUs, it will use the GPU Accelerator
-lite = Lite(devices=2, accelerator="auto")
-
-
-
-
-

strategy

-

Choose a training strategy: "dp", "ddp", "ddp_spawn", "tpu_spawn", "deepspeed", "ddp_sharded", or "ddp_sharded_spawn".

-
# Running with the DistributedDataParallel strategy on 4 GPUs
-lite = Lite(strategy="ddp", accelerator="gpu", devices=4)
-
-# Running with the DDP Spawn strategy using 4 cpu processes
-lite = Lite(strategy="ddp_spawn", accelerator="cpu", devices=4)
-
-
-

Additionally, you can pass in your custom strategy by configuring additional parameters.

-
from pytorch_lightning.strategies import DeepSpeedStrategy
-
-lite = Lite(strategy=DeepSpeedStrategy(stage=2), accelerator="gpu", devices=2)
-
-
-

Support for Horovod and Fully Sharded training strategies are coming soon.

-
-
-

devices

-

Configure the devices to run on. Can be of type:

-
    -
  • int: the number of devices (e.g., GPUs) to train on

  • -
  • list of int: which device index (e.g., GPU ID) to train on (0-indexed)

  • -
  • str: a string representation of one of the above

  • -
-
# default used by Lite, i.e., use the CPU
-lite = Lite(devices=None)
-
-# equivalent
-lite = Lite(devices=0)
-
-# int: run on two GPUs
-lite = Lite(devices=2, accelerator="gpu")
-
-# list: run on GPUs 1, 4 (by bus ordering)
-lite = Lite(devices=[1, 4], accelerator="gpu")
-lite = Lite(devices="1, 4", accelerator="gpu")  # equivalent
-
-# -1: run on all GPUs
-lite = Lite(devices=-1, accelerator="gpu")
-lite = Lite(devices="-1", accelerator="gpu")  # equivalent
-
-
-
-
-

gpus

-
-

Warning

-

gpus=x has been deprecated in v1.7 and will be removed in v2.0. -Please use accelerator='gpu' and devices=x instead.

-
-

Shorthand for setting devices=X and accelerator="gpu".

-
# Run on two GPUs
-lite = Lite(accelerator="gpu", devices=2)
-
-# Equivalent
-lite = Lite(devices=2, accelerator="gpu")
-
-
-
-
-

tpu_cores

-
-

Warning

-

tpu_cores=x has been deprecated in v1.7 and will be removed in v2.0. -Please use accelerator='tpu' and devices=x instead.

-
-

Shorthand for devices=X and accelerator="tpu".

-
# Run on eight TPUs
-lite = Lite(accelerator="tpu", devices=8)
-
-# Equivalent
-lite = Lite(devices=8, accelerator="tpu")
-
-
-
-
-

num_nodes

-

Number of cluster nodes for distributed operation.

-
# Default used by Lite
-lite = Lite(num_nodes=1)
-
-# Run on 8 nodes
-lite = Lite(num_nodes=8)
-
-
-

Learn more about distributed multi-node training on clusters here.

-
-
-

precision

-

Lightning Lite supports double precision (64), full precision (32), or half precision (16) operation (including bfloat16). -Half precision, or mixed precision, is the combined use of 32 and 16-bit floating points to reduce the memory footprint during model training. -This can result in improved performance, achieving significant speedups on modern GPUs.

-
# Default used by the Lite
-lite = Lite(precision=32, devices=1)
-
-# 16-bit (mixed) precision
-lite = Lite(precision=16, devices=1)
-
-# 16-bit bfloat precision
-lite = Lite(precision="bf16", devices=1)
-
-# 64-bit (double) precision
-lite = Lite(precision=64, devices=1)
-
-
-
-
-

plugins

-

Plugins allow you to connect arbitrary backends, precision libraries, clusters etc. For example: -To define your own behavior, subclass the relevant class and pass it in. Here’s an example linking up your own -ClusterEnvironment.

-
from pytorch_lightning.plugins.environments import ClusterEnvironment
-
-
-class MyCluster(ClusterEnvironment):
-    @property
-    def main_address(self):
-        return your_main_address
-
-    @property
-    def main_port(self):
-        return your_main_port
-
-    def world_size(self):
-        return the_world_size
-
-
-lite = Lite(plugins=[MyCluster()], ...)
-
-
-
-
-
-
-

Lightning Lite Methods

-
-

run

-

The run method serves two purposes:

-
    -
  1. Override this method from the LightningLite class and put your -training (or inference) code inside.

  2. -
  3. Launch the training procedure by calling the run method. Lite will take care of setting up the distributed backend.

  4. -
-

You can optionally pass arguments to the run method. For example, the hyperparameters or a backbone for the model.

-
from pytorch_lightning.lite import LightningLite
-
-
-class Lite(LightningLite):
-
-    # Input arguments are optional; put whatever you need
-    def run(self, learning_rate, num_layers):
-        """Here goes your training loop"""
-
-
-lite = Lite(accelerator="gpu", devices=2)
-lite.run(learning_rate=0.01, num_layers=12)
-
-
-
-
-

setup

-

Set up a model and corresponding optimizer(s). If you need to set up multiple models, call setup() on each of them. -Moves the model and optimizer to the correct device automatically.

-
model = nn.Linear(32, 64)
-optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
-
-# Set up model and optimizer for accelerated training
-model, optimizer = self.setup(model, optimizer)
-
-# If you don't want Lite to set the device
-model, optimizer = self.setup(model, optimizer, move_to_device=False)
-
-
-

The setup method also prepares the model for the selected precision choice so that operations during forward() get -cast automatically.

-
-
-

setup_dataloaders

-

Set up one or multiple dataloaders for accelerated operation. If you are running a distributed strategy (e.g., DDP), Lite -replaces the sampler automatically for you. In addition, the dataloader will be configured to move the returned -data tensors to the correct device automatically.

-
train_data = torch.utils.DataLoader(train_dataset, ...)
-test_data = torch.utils.DataLoader(test_dataset, ...)
-
-train_data, test_data = self.setup_dataloaders(train_data, test_data)
-
-# If you don't want Lite to move the data to the device
-train_data, test_data = self.setup_dataloaders(train_data, test_data, move_to_device=False)
-
-# If you don't want Lite to replace the sampler in the context of distributed training
-train_data, test_data = self.setup_dataloaders(train_data, test_data, replace_sampler=False)
-
-
-
-
-

backward

-

This replaces any occurrences of loss.backward() and makes your code accelerator and precision agnostic.

-
output = model(input)
-loss = loss_fn(output, target)
-
-# loss.backward()
-self.backward(loss)
-
-
-
-
-

to_device

-

Use to_device() to move models, tensors or collections of tensors to -the current device. By default setup() and -setup_dataloaders() already move the model and data to the correct -device, so calling this method is only necessary for manual operation when needed.

-
data = torch.load("dataset.pt")
-data = self.to_device(data)
-
-
-
-
-

seed_everything

-

Make your code reproducible by calling this method at the beginning of your run.

-
# Instead of `torch.manual_seed(...)`, call:
-self.seed_everything(1234)
-
-
-

This covers PyTorch, NumPy and Python random number generators. In addition, Lite takes care of properly initializing -the seed of dataloader worker processes (can be turned off by passing workers=False).

-
-
-

autocast

-

Let the precision backend autocast the block of code under this context manager. This is optional and already done by -Lite for the model’s forward method (once the model was setup()). -You need this only if you wish to autocast more operations outside the ones in model forward:

-
model, optimizer = self.setup(model, optimizer)
-
-# Lite handles precision automatically for the model
-output = model(inputs)
-
-with self.autocast():  # optional
-    loss = loss_function(output, target)
-
-self.backward(loss)
-...
-
-
-
-
-

print

-

Print to the console via the built-in print function, but only on the main process. -This avoids excessive printing and logs when running on multiple devices/nodes.

-
# Print only on the main process
-self.print(f"{epoch}/{num_epochs}| Train Epoch Loss: {loss}")
-
-
-
-
-

save

-

Save contents to a checkpoint. Replaces all occurrences of torch.save(...) in your code. Lite will take care of -handling the saving part correctly, no matter if you are running a single device, multi-devices or multi-nodes.

-
# Instead of `torch.save(...)`, call:
-self.save(model.state_dict(), "path/to/checkpoint.ckpt")
-
-
-
-
-

load

-

Load checkpoint contents from a file. Replaces all occurrences of torch.load(...) in your code. Lite will take care of -handling the loading part correctly, no matter if you are running a single device, multi-device, or multi-node.

-
# Instead of `torch.load(...)`, call:
-self.load("path/to/checkpoint.ckpt")
-
-
-
-
-

barrier

-

Call this if you want all processes to wait and synchronize. Once all processes have entered this call, -execution continues. Useful for example when you want to download data on one process and make all others wait until -the data is written to disk.

-
# Download data only on one process
-if self.global_rank == 0:
-    download_data("http://...")
-
-# Wait until all processes meet up here
-self.barrier()
-
-# All processes are allowed to read the data now
-
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/model/build_model_intermediate.html b/docs/model/build_model_intermediate.html deleted file mode 100644 index 5db5ebf..0000000 --- a/docs/model/build_model_intermediate.html +++ /dev/null @@ -1,724 +0,0 @@ - - - - - - - - - - - - - - Supercharge training (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Supercharge training (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Supercharge training (intermediate)

-
-

Enable training features

-

Enable advanced training features using Trainer arguments. These are SOTA techniques that are automatically integrated into your training loop without changes to your code.

-
# train 1TB+ parameter models with Deepspeed/fsdp
-trainer = Trainer(
-    devices=4,
-    accelerator="gpu",
-    strategy="deepspeed_stage_2",
-    precision=16
- )
-
-# 20+ helpful arguments for rapid idea iteration
-trainer = Trainer(
-    max_epochs=10,
-    min_epochs=5,
-    overfit_batches=1
- )
-
-# access the latest state of the art techniques
-trainer = Trainer(callbacks=[StochasticWeightAveraging(...)])
-
-
-
-
-
-

Extend the Trainer

-

If you have multiple lines of code with similar functionalities, you can use callbacks to easily group them together and toggle all of those lines on or off at the same time.

-
trainer = Trainer(callbacks=[AWSCheckpoints()])
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/model/manual_optimization.html b/docs/model/manual_optimization.html deleted file mode 100644 index f34ef9a..0000000 --- a/docs/model/manual_optimization.html +++ /dev/null @@ -1,965 +0,0 @@ - - - - - - - - - - - - - - Manual Optimization — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Manual Optimization
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Manual Optimization

-

For advanced research topics like reinforcement learning, sparse coding, or GAN research, it may be desirable to -manually manage the optimization process.

-

This is only recommended for experts who need ultimate flexibility. -Lightning will handle only accelerator, precision and strategy logic. -The users are left with optimizer.zero_grad(), gradient accumulation, model toggling, etc..

-

To manually optimize, do the following:

-
    -
  • Set self.automatic_optimization=False in your LightningModule’s __init__.

  • -
  • Use the following functions and call them manually:

    -
      -
    • self.optimizers() to access your optimizers (one or multiple)

    • -
    • optimizer.zero_grad() to clear the gradients from the previous training step

    • -
    • self.manual_backward(loss) instead of loss.backward()

    • -
    • optimizer.step() to update your model parameters

    • -
    -
  • -
-

Here is a minimal example of manual optimization.

-
from pytorch_lightning import LightningModule
-
-
-class MyModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        # Important: This property activates manual optimization.
-        self.automatic_optimization = False
-
-    def training_step(self, batch, batch_idx):
-        opt = self.optimizers()
-        opt.zero_grad()
-        loss = self.compute_loss(batch)
-        self.manual_backward(loss)
-        opt.step()
-
-
-
-

Warning

-

Before 1.2, optimizer.step() was calling optimizer.zero_grad() internally. -From 1.2, it is left to the user’s expertise.

-
-
-

Tip

-

Be careful where you call optimizer.zero_grad(), or your model won’t converge. -It is good practice to call optimizer.zero_grad() before self.manual_backward(loss).

-
-
-

Access your Own Optimizer

-

The provided optimizer is a LightningOptimizer object wrapping your own optimizer -configured in your configure_optimizers(). You can access your own optimizer -with optimizer.optimizer. However, if you use your own optimizer to perform a step, Lightning won’t be able to -support accelerators, precision and profiling for you.

-
class Model(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.automatic_optimization = False
-        ...
-
-    def training_step(self, batch, batch_idx):
-        optimizer = self.optimizers()
-
-        # `optimizer` is a `LightningOptimizer` wrapping the optimizer.
-        # To access it, do the following.
-        # However, it won't work on TPU, AMP, etc...
-        optimizer = optimizer.optimizer
-        ...
-
-
-
-
-

Gradient Accumulation

-

You can accumulate gradients over batches similarly to accumulate_grad_batches argument in -Trainer for automatic optimization. To perform gradient accumulation with one optimizer -after every N steps, you can do as such.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    opt = self.optimizers()
-
-    loss = self.compute_loss(batch)
-    self.manual_backward(loss)
-
-    # accumulate gradients of N batches
-    if (batch_idx + 1) % N == 0:
-        opt.step()
-        opt.zero_grad()
-
-
-
-
-

Use Multiple Optimizers (like GANs)

-

Here is an example training a simple GAN with multiple optimizers using manual optimization.

-
import torch
-from torch import Tensor
-from pytorch_lightning import LightningModule
-
-
-class SimpleGAN(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.G = Generator()
-        self.D = Discriminator()
-
-        # Important: This property activates manual optimization.
-        self.automatic_optimization = False
-
-    def sample_z(self, n) -> Tensor:
-        sample = self._Z.sample((n,))
-        return sample
-
-    def sample_G(self, n) -> Tensor:
-        z = self.sample_z(n)
-        return self.G(z)
-
-    def training_step(self, batch, batch_idx):
-        # Implementation follows the PyTorch tutorial:
-        # https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html
-        g_opt, d_opt = self.optimizers()
-
-        X, _ = batch
-        batch_size = X.shape[0]
-
-        real_label = torch.ones((batch_size, 1), device=self.device)
-        fake_label = torch.zeros((batch_size, 1), device=self.device)
-
-        g_X = self.sample_G(batch_size)
-
-        ##########################
-        # Optimize Discriminator #
-        ##########################
-        d_x = self.D(X)
-        errD_real = self.criterion(d_x, real_label)
-
-        d_z = self.D(g_X.detach())
-        errD_fake = self.criterion(d_z, fake_label)
-
-        errD = errD_real + errD_fake
-
-        d_opt.zero_grad()
-        self.manual_backward(errD)
-        d_opt.step()
-
-        ######################
-        # Optimize Generator #
-        ######################
-        d_z = self.D(g_X)
-        errG = self.criterion(d_z, real_label)
-
-        g_opt.zero_grad()
-        self.manual_backward(errG)
-        g_opt.step()
-
-        self.log_dict({"g_loss": errG, "d_loss": errD}, prog_bar=True)
-
-    def configure_optimizers(self):
-        g_opt = torch.optim.Adam(self.G.parameters(), lr=1e-5)
-        d_opt = torch.optim.Adam(self.D.parameters(), lr=1e-5)
-        return g_opt, d_opt
-
-
-
-
-

Learning Rate Scheduling

-

Every optimizer you use can be paired with any -Learning Rate Scheduler. Please see the -documentation of configure_optimizers() for all the available options

-

You can call lr_scheduler.step() at arbitrary intervals. -Use self.lr_schedulers() in your LightningModule to access any learning rate schedulers -defined in your configure_optimizers().

-
-

Warning

-
    -
  • Before v1.3, Lightning automatically called lr_scheduler.step() in both automatic and manual optimization. From -1.3, lr_scheduler.step() is now for the user to call at arbitrary intervals.

  • -
  • Note that the lr_scheduler_config keys, such as "frequency" and "interval", will be ignored even if they are provided in -your configure_optimizers() during manual optimization.

  • -
-
-

Here is an example calling lr_scheduler.step() every step.

-
# step every batch
-def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    # do forward, backward, and optimization
-    ...
-
-    # single scheduler
-    sch = self.lr_schedulers()
-    sch.step()
-
-    # multiple schedulers
-    sch1, sch2 = self.lr_schedulers()
-    sch1.step()
-    sch2.step()
-
-
-

If you want to call lr_scheduler.step() every N steps/epochs, do the following.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_step(self, batch, batch_idx):
-    # do forward, backward, and optimization
-    ...
-
-    sch = self.lr_schedulers()
-
-    # step every N batches
-    if (batch_idx + 1) % N == 0:
-        sch.step()
-
-    # step every N epochs
-    if self.trainer.is_last_batch and (self.trainer.current_epoch + 1) % N == 0:
-        sch.step()
-
-
-

If you want to call schedulers that require a metric value after each epoch, consider doing the following:

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def training_epoch_end(self, outputs):
-    sch = self.lr_schedulers()
-
-    # If the selected scheduler is a ReduceLROnPlateau scheduler.
-    if isinstance(sch, torch.optim.lr_scheduler.ReduceLROnPlateau):
-        sch.step(self.trainer.callback_metrics["loss"])
-
-
-
-
-

Use Closure for LBFGS-like Optimizers

-

It is a good practice to provide the optimizer with a closure function that performs a forward, zero_grad and -backward of your model. It is optional for most optimizers, but makes your code compatible if you switch to an -optimizer which requires a closure, such as LBFGS.

-

See the PyTorch docs for more about the closure.

-

Here is an example using a closure function.

-
def __init__(self):
-    super().__init__()
-    self.automatic_optimization = False
-
-
-def configure_optimizers(self):
-    return torch.optim.LBFGS(...)
-
-
-def training_step(self, batch, batch_idx):
-    opt = self.optimizers()
-
-    def closure():
-        loss = self.compute_loss(batch)
-        opt.zero_grad()
-        self.manual_backward(loss)
-        return loss
-
-    opt.step(closure=closure)
-
-
-
-

Warning

-

The LBFGS optimizer is not supported for apex AMP, native AMP, IPUs, or DeepSpeed.

-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/model/own_your_loop.html b/docs/model/own_your_loop.html deleted file mode 100644 index 7e701c0..0000000 --- a/docs/model/own_your_loop.html +++ /dev/null @@ -1,731 +0,0 @@ - - - - - - - - - - - - - - Use a pure PyTorch training loop — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Use a pure PyTorch training loop
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/model/train_model_basic.html b/docs/model/train_model_basic.html deleted file mode 100644 index e24fa03..0000000 --- a/docs/model/train_model_basic.html +++ /dev/null @@ -1,801 +0,0 @@ - - - - - - - - - - - - - - Train a model (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Train a model (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Train a model (basic)

-

Audience: Users who need to train a model without coding their own training loops.

-
-
-

Add imports

-

Add the relevant imports at the top of the file

-
import os
-import torch
-from torch import nn
-import torch.nn.functional as F
-from torchvision import transforms
-from torchvision.datasets import MNIST
-from torch.utils.data import DataLoader, random_split
-import pytorch_lightning as pl
-
-
-
-
-
-

Define the PyTorch nn.Modules

-
class Encoder(nn.Module):
-    def __init__(self):
-        self.l1 = nn.Sequential(nn.Linear(28 * 28, 64), nn.ReLU(), nn.Linear(64, 3))
-
-    def forward(self, x):
-        return self.l1(x)
-
-
-class Decoder(nn.Module):
-    def __init__(self):
-        self.l1 = nn.Sequential(nn.Linear(3, 64), nn.ReLU(), nn.Linear(64, 28 * 28))
-
-    def forward(self, x):
-        return self.l1(x)
-
-
-
-
-
-

Define a LightningModule

-

The LightningModule is the full recipe that defines how your nn.Modules interact.

-
    -
  • The training_step defines how the nn.Modules interact together.

  • -
  • In the configure_optimizers define the optimizer(s) for your models.

  • -
-
class LitAutoEncoder(pl.LightningModule):
-    def __init__(self, encoder, decoder):
-        super().__init__()
-        self.encoder = encoder
-        self.decoder = decoder
-
-    def training_step(self, batch, batch_idx):
-        # training_step defines the train loop.
-        x, y = batch
-        x = x.view(x.size(0), -1)
-        z = self.encoder(x)
-        x_hat = self.decoder(z)
-        loss = F.mse_loss(x_hat, x)
-        return loss
-
-    def configure_optimizers(self):
-        optimizer = torch.optim.Adam(self.parameters(), lr=1e-3)
-        return optimizer
-
-
-
-
-
-

Define the training dataset

-

Define a PyTorch DataLoader which contains your training dataset.

-
dataset = MNIST(os.getcwd(), download=True, transform=transforms.ToTensor())
-train_loader = DataLoader(dataset)
-
-
-
-
-
-

Train the model

-

To train the model use the Lightning Trainer which handles all the engineering and abstracts away all the complexity needed for scale.

-
# model
-autoencoder = LitAutoEncoder(Encoder(), Decoder())
-
-# train model
-trainer = pl.Trainer()
-trainer.fit(model=autoencoder, train_dataloaders=train_loader)
-
-
-
-
-
-

Eliminate the training loop

-

Under the hood, the Lightning Trainer runs the following training loop on your behalf

-
autoencoder = LitAutoEncoder(encoder, decoder)
-optimizer = autoencoder.configure_optimizers()
-
-for batch, batch_idx in enumerate(train_loader):
-    loss = autoencoder(batch, batch_idx)
-
-    loss.backward()
-    optimizer.step()
-    optimizer.zero_grad()
-
-
-

The power of Lightning comes when the training loop gets complicated as you add validation/test splits, schedulers, distributed training and all the latest SOTA techniques.

-

With Lightning, you can add mix all these techniques together without needing to rewrite a new loop every time.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/objects.inv b/docs/objects.inv deleted file mode 100644 index 08ed8c9..0000000 Binary files a/docs/objects.inv and /dev/null differ diff --git a/docs/rtfd-build.sh b/docs/rtfd-build.sh new file mode 100644 index 0000000..edfc5b3 --- /dev/null +++ b/docs/rtfd-build.sh @@ -0,0 +1,12 @@ +# building for PRs and skip stable and latest states + +if ! [ $READTHEDOCS_VERSION == "latest" -o $READTHEDOCS_VERSION == "stable" ]; +then + cd ./docs/source-pytorch ; + export PL_FAST_DOCS_DEV=1 ; + make html --jobs $(nproc) ; + ls -lh ../build +else + echo "Void build... :-]" ; + mkdir -p ./docs/build/html +fi diff --git a/docs/search.html b/docs/search.html deleted file mode 100644 index 75efebc..0000000 --- a/docs/search.html +++ /dev/null @@ -1,696 +0,0 @@ - - - - - - - - - - - - - Search — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- - - - -
- -
- -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/searchindex.js b/docs/searchindex.js deleted file mode 100644 index ed1e556..0000000 --- a/docs/searchindex.js +++ /dev/null @@ -1 +0,0 @@ -Search.setIndex({docnames:["accelerators/accelerator_prepare","accelerators/gpu","accelerators/gpu_advanced","accelerators/gpu_basic","accelerators/gpu_expert","accelerators/gpu_faq","accelerators/gpu_intermediate","accelerators/hpu","accelerators/hpu_basic","accelerators/hpu_intermediate","accelerators/ipu","accelerators/ipu_advanced","accelerators/ipu_basic","accelerators/ipu_intermediate","accelerators/tpu","accelerators/tpu_advanced","accelerators/tpu_basic","accelerators/tpu_faq","accelerators/tpu_intermediate","advanced/model_parallel","advanced/pruning_quantization","advanced/strategy_registry","advanced/training_tricks","advanced/transfer_learning","benchmarking/benchmarks","cli/lightning_cli","cli/lightning_cli_advanced","cli/lightning_cli_advanced_2","cli/lightning_cli_advanced_3","cli/lightning_cli_expert","cli/lightning_cli_faq","cli/lightning_cli_intermediate","cli/lightning_cli_intermediate_2","clouds/cloud_training","clouds/cloud_training_intermediate","clouds/cluster","clouds/cluster_advanced","clouds/cluster_expert","clouds/cluster_intermediate_1","clouds/cluster_intermediate_2","clouds/fault_tolerant_training","clouds/fault_tolerant_training_basic","clouds/fault_tolerant_training_expert","clouds/fault_tolerant_training_faq","clouds/grid_costs","clouds/run_advanced","clouds/run_basic","clouds/run_expert","clouds/run_intermediate","clouds/session_basic","clouds/session_intermediate","common/checkpointing","common/checkpointing_advanced","common/checkpointing_basic","common/checkpointing_expert","common/checkpointing_intermediate","common/child_modules","common/console_logs","common/early_stopping","common/evaluation","common/evaluation_basic","common/evaluation_intermediate","common/gradient_accumulation","common/hyperparameters","common/lightning_module","common/optimization","common/precision","common/precision_basic","common/precision_expert","common/precision_intermediate","common/progress_bar","common/remote_fs","common/trainer","common_usecases","data/datamodule","debug/debugging","debug/debugging_advanced","debug/debugging_basic","debug/debugging_intermediate","deploy/production","deploy/production_advanced","deploy/production_advanced_2","deploy/production_basic","deploy/production_intermediate","ecosystem/asr_nlp_tts","ecosystem/bolts","ecosystem/community_examples","ecosystem/ecosystem-ci","ecosystem/flash","ecosystem/metrics","ecosystem/transformers","expertise_levels","extensions/accelerator","extensions/callbacks","extensions/callbacks_state","extensions/datamodules_state","extensions/generated/pytorch_lightning.loggers.CSVLogger","extensions/generated/pytorch_lightning.loggers.CometLogger","extensions/generated/pytorch_lightning.loggers.MLFlowLogger","extensions/generated/pytorch_lightning.loggers.NeptuneLogger","extensions/generated/pytorch_lightning.loggers.TensorBoardLogger","extensions/generated/pytorch_lightning.loggers.WandbLogger","extensions/logging","extensions/loops","extensions/loops_advanced","extensions/plugins","extensions/strategy","guides/data","guides/speed","index","levels/advanced","levels/advanced_level_15","levels/advanced_level_16","levels/advanced_level_17","levels/advanced_level_18","levels/advanced_level_19","levels/advanced_level_20","levels/advanced_level_21","levels/advanced_level_22","levels/basic_level_2","levels/basic_level_5","levels/core_level_3","levels/core_level_6","levels/core_skills","levels/expert","levels/expert_level_23","levels/expert_level_24","levels/expert_level_27","levels/intermediate","levels/intermediate_level_10","levels/intermediate_level_11","levels/intermediate_level_12","levels/intermediate_level_13","levels/intermediate_level_14","levels/intermediate_level_7","levels/intermediate_level_8","levels/intermediate_level_9","links","model/build_model","model/build_model_advanced","model/build_model_expert","model/build_model_intermediate","model/manual_optimization","model/own_your_loop","model/train_model_basic","starter/converting","starter/installation","starter/introduction","starter/lightning_lite","starter/style_guide","tuning/profiler","tuning/profiler_advanced","tuning/profiler_basic","tuning/profiler_expert","tuning/profiler_intermediate","visualize/experiment_managers","visualize/loggers","visualize/logging_advanced","visualize/logging_basic","visualize/logging_expert","visualize/logging_intermediate","visualize/supported_exp_managers"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":5,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,"sphinx.ext.todo":2,"sphinx.ext.viewcode":1,nbsphinx:4,sphinx:56},filenames:["accelerators/accelerator_prepare.rst","accelerators/gpu.rst","accelerators/gpu_advanced.rst","accelerators/gpu_basic.rst","accelerators/gpu_expert.rst","accelerators/gpu_faq.rst","accelerators/gpu_intermediate.rst","accelerators/hpu.rst","accelerators/hpu_basic.rst","accelerators/hpu_intermediate.rst","accelerators/ipu.rst","accelerators/ipu_advanced.rst","accelerators/ipu_basic.rst","accelerators/ipu_intermediate.rst","accelerators/tpu.rst","accelerators/tpu_advanced.rst","accelerators/tpu_basic.rst","accelerators/tpu_faq.rst","accelerators/tpu_intermediate.rst","advanced/model_parallel.rst","advanced/pruning_quantization.rst","advanced/strategy_registry.rst","advanced/training_tricks.rst","advanced/transfer_learning.rst","benchmarking/benchmarks.rst","cli/lightning_cli.rst","cli/lightning_cli_advanced.rst","cli/lightning_cli_advanced_2.rst","cli/lightning_cli_advanced_3.rst","cli/lightning_cli_expert.rst","cli/lightning_cli_faq.rst","cli/lightning_cli_intermediate.rst","cli/lightning_cli_intermediate_2.rst","clouds/cloud_training.rst","clouds/cloud_training_intermediate.rst","clouds/cluster.rst","clouds/cluster_advanced.rst","clouds/cluster_expert.rst","clouds/cluster_intermediate_1.rst","clouds/cluster_intermediate_2.rst","clouds/fault_tolerant_training.rst","clouds/fault_tolerant_training_basic.rst","clouds/fault_tolerant_training_expert.rst","clouds/fault_tolerant_training_faq.rst","clouds/grid_costs.rst","clouds/run_advanced.rst","clouds/run_basic.rst","clouds/run_expert.rst","clouds/run_intermediate.rst","clouds/session_basic.rst","clouds/session_intermediate.rst","common/checkpointing.rst","common/checkpointing_advanced.rst","common/checkpointing_basic.rst","common/checkpointing_expert.rst","common/checkpointing_intermediate.rst","common/child_modules.rst","common/console_logs.rst","common/early_stopping.rst","common/evaluation.rst","common/evaluation_basic.rst","common/evaluation_intermediate.rst","common/gradient_accumulation.rst","common/hyperparameters.rst","common/lightning_module.rst","common/optimization.rst","common/precision.rst","common/precision_basic.rst","common/precision_expert.rst","common/precision_intermediate.rst","common/progress_bar.rst","common/remote_fs.rst","common/trainer.rst","common_usecases.rst","data/datamodule.rst","debug/debugging.rst","debug/debugging_advanced.rst","debug/debugging_basic.rst","debug/debugging_intermediate.rst","deploy/production.rst","deploy/production_advanced.rst","deploy/production_advanced_2.rst","deploy/production_basic.rst","deploy/production_intermediate.rst","ecosystem/asr_nlp_tts.rst","ecosystem/bolts.rst","ecosystem/community_examples.rst","ecosystem/ecosystem-ci.rst","ecosystem/flash.rst","ecosystem/metrics.rst","ecosystem/transformers.rst","expertise_levels.rst","extensions/accelerator.rst","extensions/callbacks.rst","extensions/callbacks_state.rst","extensions/datamodules_state.rst","extensions/generated/pytorch_lightning.loggers.CSVLogger.rst","extensions/generated/pytorch_lightning.loggers.CometLogger.rst","extensions/generated/pytorch_lightning.loggers.MLFlowLogger.rst","extensions/generated/pytorch_lightning.loggers.NeptuneLogger.rst","extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.rst","extensions/generated/pytorch_lightning.loggers.WandbLogger.rst","extensions/logging.rst","extensions/loops.rst","extensions/loops_advanced.rst","extensions/plugins.rst","extensions/strategy.rst","guides/data.rst","guides/speed.rst","index.rst","levels/advanced.rst","levels/advanced_level_15.rst","levels/advanced_level_16.rst","levels/advanced_level_17.rst","levels/advanced_level_18.rst","levels/advanced_level_19.rst","levels/advanced_level_20.rst","levels/advanced_level_21.rst","levels/advanced_level_22.rst","levels/basic_level_2.rst","levels/basic_level_5.rst","levels/core_level_3.rst","levels/core_level_6.rst","levels/core_skills.rst","levels/expert.rst","levels/expert_level_23.rst","levels/expert_level_24.rst","levels/expert_level_27.rst","levels/intermediate.rst","levels/intermediate_level_10.rst","levels/intermediate_level_11.rst","levels/intermediate_level_12.rst","levels/intermediate_level_13.rst","levels/intermediate_level_14.rst","levels/intermediate_level_7.rst","levels/intermediate_level_8.rst","levels/intermediate_level_9.rst","links.rst","model/build_model.rst","model/build_model_advanced.rst","model/build_model_expert.rst","model/build_model_intermediate.rst","model/manual_optimization.rst","model/own_your_loop.rst","model/train_model_basic.rst","starter/converting.rst","starter/installation.rst","starter/introduction.rst","starter/lightning_lite.rst","starter/style_guide.rst","tuning/profiler.rst","tuning/profiler_advanced.rst","tuning/profiler_basic.rst","tuning/profiler_expert.rst","tuning/profiler_intermediate.rst","visualize/experiment_managers.rst","visualize/loggers.rst","visualize/logging_advanced.rst","visualize/logging_basic.rst","visualize/logging_expert.rst","visualize/logging_intermediate.rst","visualize/supported_exp_managers.rst"],objects:{"pytorch_lightning.callbacks.BackboneFinetuning.params":[[93,0,1,"","backbone_initial_lr"],[93,0,1,"","backbone_initial_ratio_lr"],[93,0,1,"","initial_denom_lr"],[93,0,1,"","lambda_func"],[93,0,1,"","rounding"],[93,0,1,"","should_align"],[93,0,1,"","train_bn"],[93,0,1,"","unfreeze_backbone_at_epoch"],[93,0,1,"","verbose"]],"pytorch_lightning.callbacks.BasePredictionWriter.params":[[93,0,1,"","write_interval"]],"pytorch_lightning.callbacks.Callback.load_state_dict.params":[[93,0,1,"","state_dict"]],"pytorch_lightning.callbacks.Callback.on_load_checkpoint.params":[[93,0,1,"","callback_state"],[93,0,1,"","pl_module"],[93,0,1,"","trainer"]],"pytorch_lightning.callbacks.Callback.on_save_checkpoint.params":[[93,0,1,"","checkpoint"],[93,0,1,"","pl_module"],[93,0,1,"","trainer"]],"pytorch_lightning.callbacks.EarlyStopping.params":[[93,0,1,"","check_finite"],[93,0,1,"","check_on_train_epoch_end"],[93,0,1,"","divergence_threshold"],[93,0,1,"","min_delta"],[93,0,1,"","mode"],[93,0,1,"","monitor"],[93,0,1,"","patience"],[93,0,1,"","stopping_threshold"],[93,0,1,"","strict"],[93,0,1,"","verbose"]],"pytorch_lightning.callbacks.GradientAccumulationScheduler.params":[[93,0,1,"","scheduling"]],"pytorch_lightning.callbacks.LambdaCallback.params":[[93,0,1,"","**kwargs"]],"pytorch_lightning.callbacks.LearningRateMonitor.params":[[93,0,1,"","log_momentum"],[93,0,1,"","logging_interval"]],"pytorch_lightning.callbacks.ModelCheckpoint.params":[[93,0,1,"","auto_insert_metric_name"],[93,0,1,"","dirpath"],[93,0,1,"","every_n_epochs"],[93,0,1,"","every_n_train_steps"],[93,0,1,"","filename"],[93,0,1,"","mode"],[93,0,1,"","monitor"],[93,0,1,"","save_last"],[93,0,1,"","save_on_train_epoch_end"],[93,0,1,"","save_top_k"],[93,0,1,"","save_weights_only"],[93,0,1,"","train_time_interval"],[93,0,1,"","verbose"]],"pytorch_lightning.callbacks.ModelPruning.params":[[93,0,1,"","amount"],[93,0,1,"","apply_pruning"],[93,0,1,"","make_pruning_permanent"],[93,0,1,"","parameter_names"],[93,0,1,"","parameters_to_prune"],[93,0,1,"","prune_on_train_epoch_end"],[93,0,1,"","pruning_dim"],[93,0,1,"","pruning_fn"],[93,0,1,"","pruning_norm"],[93,0,1,"","resample_parameters"],[93,0,1,"","use_global_unstructured"],[93,0,1,"","use_lottery_ticket_hypothesis"],[93,0,1,"","verbose"]],"pytorch_lightning.callbacks.ModelSummary.params":[[93,0,1,"","max_depth"]],"pytorch_lightning.callbacks.QuantizationAwareTraining.params":[[93,0,1,"","collect_quantization"],[93,0,1,"","input_compatible"],[93,0,1,"","modules_to_fuse"],[93,0,1,"","observer_enabled_stages"],[93,0,1,"","observer_type"],[93,0,1,"","qconfig"],[93,0,1,"","quantize_on_fit_end"]],"pytorch_lightning.callbacks.RichModelSummary.params":[[93,0,1,"","max_depth"]],"pytorch_lightning.callbacks.RichProgressBar.params":[[93,0,1,"","console_kwargs"],[93,0,1,"","leave"],[93,0,1,"","refresh_rate"],[93,0,1,"","theme"]],"pytorch_lightning.callbacks.StochasticWeightAveraging.params":[[93,0,1,"","annealing_epochs"],[93,0,1,"","annealing_strategy"],[93,0,1,"","avg_fn"],[93,0,1,"","device"],[93,0,1,"","swa_epoch_start"],[93,0,1,"","swa_lrs"]],"pytorch_lightning.callbacks.TQDMProgressBar.params":[[93,0,1,"","process_position"],[93,0,1,"","refresh_rate"]],"pytorch_lightning.callbacks.Timer.params":[[93,0,1,"","duration"],[93,0,1,"","interval"],[93,0,1,"","verbose"]],"pytorch_lightning.core.datamodule.LightningDataModule.load_state_dict.params":[[74,0,1,"","state_dict"]],"pytorch_lightning.core.datamodule.LightningDataModule.on_after_batch_transfer.params":[[74,0,1,"","batch"],[74,0,1,"","dataloader_idx"]],"pytorch_lightning.core.datamodule.LightningDataModule.on_before_batch_transfer.params":[[74,0,1,"","batch"],[74,0,1,"","dataloader_idx"]],"pytorch_lightning.core.datamodule.LightningDataModule.teardown.params":[[74,0,1,"","stage"]],"pytorch_lightning.core.datamodule.LightningDataModule.transfer_batch_to_device.params":[[74,0,1,"","batch"],[74,0,1,"","dataloader_idx"],[74,0,1,"","device"]],"pytorch_lightning.core.lightning.LightningModule.add_to_queue.params":[[64,0,1,"","queue"]],"pytorch_lightning.core.lightning.LightningModule.all_gather.params":[[64,0,1,"","data"],[64,0,1,"","group"],[64,0,1,"","sync_grads"]],"pytorch_lightning.core.lightning.LightningModule.backward.params":[[64,0,1,"","loss"],[64,0,1,"","optimizer"],[64,0,1,"","optimizer_idx"]],"pytorch_lightning.core.lightning.LightningModule.configure_gradient_clipping.params":[[64,0,1,"","gradient_clip_algorithm"],[64,0,1,"","gradient_clip_val"],[64,0,1,"","optimizer"],[64,0,1,"","optimizer_idx"]],"pytorch_lightning.core.lightning.LightningModule.forward.params":[[64,0,1,"","**kwargs"],[64,0,1,"","*args"]],"pytorch_lightning.core.lightning.LightningModule.get_from_queue.params":[[64,0,1,"","queue"]],"pytorch_lightning.core.lightning.LightningModule.load_from_checkpoint.params":[[64,0,1,"","checkpoint_path"],[64,0,1,"","hparams_file"],[64,0,1,"","kwargs"],[64,0,1,"","map_location"],[64,0,1,"","strict"]],"pytorch_lightning.core.lightning.LightningModule.log.params":[[64,0,1,"","add_dataloader_idx"],[64,0,1,"","batch_size"],[64,0,1,"","enable_graph"],[64,0,1,"","logger"],[64,0,1,"","metric_attribute"],[64,0,1,"","name"],[64,0,1,"","on_epoch"],[64,0,1,"","on_step"],[64,0,1,"","prog_bar"],[64,0,1,"","rank_zero_only"],[64,0,1,"","reduce_fx"],[64,0,1,"","sync_dist"],[64,0,1,"","sync_dist_group"],[64,0,1,"","value"]],"pytorch_lightning.core.lightning.LightningModule.log_dict.params":[[64,0,1,"","add_dataloader_idx"],[64,0,1,"","batch_size"],[64,0,1,"","dictionary"],[64,0,1,"","enable_graph"],[64,0,1,"","logger"],[64,0,1,"","on_epoch"],[64,0,1,"","on_step"],[64,0,1,"","prog_bar"],[64,0,1,"","rank_zero_only"],[64,0,1,"","reduce_fx"],[64,0,1,"","sync_dist"],[64,0,1,"","sync_dist_group"]],"pytorch_lightning.core.lightning.LightningModule.manual_backward.params":[[64,0,1,"","**kwargs"],[64,0,1,"","*args"],[64,0,1,"","loss"]],"pytorch_lightning.core.lightning.LightningModule.on_after_batch_transfer.params":[[64,0,1,"","batch"],[64,0,1,"","dataloader_idx"]],"pytorch_lightning.core.lightning.LightningModule.on_before_backward.params":[[64,0,1,"","loss"]],"pytorch_lightning.core.lightning.LightningModule.on_before_batch_transfer.params":[[64,0,1,"","batch"],[64,0,1,"","dataloader_idx"]],"pytorch_lightning.core.lightning.LightningModule.on_before_optimizer_step.params":[[64,0,1,"","optimizer"],[64,0,1,"","optimizer_idx"]],"pytorch_lightning.core.lightning.LightningModule.on_before_zero_grad.params":[[64,0,1,"","optimizer"]],"pytorch_lightning.core.lightning.LightningModule.on_hpc_load.params":[[64,0,1,"","checkpoint"]],"pytorch_lightning.core.lightning.LightningModule.on_hpc_save.params":[[64,0,1,"","checkpoint"]],"pytorch_lightning.core.lightning.LightningModule.on_load_checkpoint.params":[[64,0,1,"","checkpoint"]],"pytorch_lightning.core.lightning.LightningModule.on_predict_batch_end.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_idx"],[64,0,1,"","outputs"]],"pytorch_lightning.core.lightning.LightningModule.on_predict_batch_start.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_idx"]],"pytorch_lightning.core.lightning.LightningModule.on_save_checkpoint.params":[[64,0,1,"","checkpoint"]],"pytorch_lightning.core.lightning.LightningModule.on_test_batch_end.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_idx"],[64,0,1,"","outputs"]],"pytorch_lightning.core.lightning.LightningModule.on_test_batch_start.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_idx"]],"pytorch_lightning.core.lightning.LightningModule.on_train_batch_end.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","outputs"],[64,0,1,"","unused"]],"pytorch_lightning.core.lightning.LightningModule.on_train_batch_start.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","unused"]],"pytorch_lightning.core.lightning.LightningModule.on_validation_batch_end.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_idx"],[64,0,1,"","outputs"]],"pytorch_lightning.core.lightning.LightningModule.on_validation_batch_start.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_idx"]],"pytorch_lightning.core.lightning.LightningModule.optimizer_step.params":[[64,0,1,"","batch_idx"],[64,0,1,"","epoch"],[64,0,1,"","on_tpu"],[64,0,1,"","optimizer"],[64,0,1,"","optimizer_closure"],[64,0,1,"","optimizer_idx"],[64,0,1,"","using_lbfgs"],[64,0,1,"","using_native_amp"]],"pytorch_lightning.core.lightning.LightningModule.optimizer_zero_grad.params":[[64,0,1,"","batch_idx"],[64,0,1,"","epoch"],[64,0,1,"","optimizer"],[64,0,1,"","optimizer_idx"]],"pytorch_lightning.core.lightning.LightningModule.optimizers.params":[[64,0,1,"","use_pl_optimizer"]],"pytorch_lightning.core.lightning.LightningModule.predict_step.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_idx"]],"pytorch_lightning.core.lightning.LightningModule.print.params":[[64,0,1,"","**kwargs"],[64,0,1,"","*args"]],"pytorch_lightning.core.lightning.LightningModule.save_hyperparameters.params":[[64,0,1,"","args"],[64,0,1,"","frame"],[64,0,1,"","ignore"],[64,0,1,"","logger"]],"pytorch_lightning.core.lightning.LightningModule.setup.params":[[64,0,1,"","stage"]],"pytorch_lightning.core.lightning.LightningModule.tbptt_split_batch.params":[[64,0,1,"","batch"],[64,0,1,"","split_size"]],"pytorch_lightning.core.lightning.LightningModule.teardown.params":[[64,0,1,"","stage"]],"pytorch_lightning.core.lightning.LightningModule.test_epoch_end.params":[[64,0,1,"","outputs"]],"pytorch_lightning.core.lightning.LightningModule.test_step.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_id"]],"pytorch_lightning.core.lightning.LightningModule.test_step_end.params":[[64,0,1,"","step_output"]],"pytorch_lightning.core.lightning.LightningModule.to_onnx.params":[[64,0,1,"","**kwargs"],[64,0,1,"","file_path"],[64,0,1,"","input_sample"]],"pytorch_lightning.core.lightning.LightningModule.to_torchscript.params":[[64,0,1,"","**kwargs"],[64,0,1,"","example_inputs"],[64,0,1,"","file_path"],[64,0,1,"","method"]],"pytorch_lightning.core.lightning.LightningModule.toggle_optimizer.params":[[64,0,1,"","optimizer"],[64,0,1,"","optimizer_idx"]],"pytorch_lightning.core.lightning.LightningModule.training_epoch_end.params":[[64,0,1,"","outputs"]],"pytorch_lightning.core.lightning.LightningModule.training_step.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","hiddens"],[64,0,1,"","optimizer_idx"]],"pytorch_lightning.core.lightning.LightningModule.training_step_end.params":[[64,0,1,"","step_output"]],"pytorch_lightning.core.lightning.LightningModule.transfer_batch_to_device.params":[[64,0,1,"","batch"],[64,0,1,"","dataloader_idx"],[64,0,1,"","device"]],"pytorch_lightning.core.lightning.LightningModule.untoggle_optimizer.params":[[64,0,1,"","optimizer_idx"]],"pytorch_lightning.core.lightning.LightningModule.validation_epoch_end.params":[[64,0,1,"","outputs"]],"pytorch_lightning.core.lightning.LightningModule.validation_step.params":[[64,0,1,"","batch"],[64,0,1,"","batch_idx"],[64,0,1,"","dataloader_idx"]],"pytorch_lightning.core.lightning.LightningModule.validation_step_end.params":[[64,0,1,"","step_output"]],"pytorch_lightning.loggers":[[96,1,1,"","CSVLogger"],[97,1,1,"","CometLogger"],[98,1,1,"","MLFlowLogger"],[99,1,1,"","NeptuneLogger"],[100,1,1,"","TensorBoardLogger"],[101,1,1,"","WandbLogger"]],"pytorch_lightning.loggers.CSVLogger":[[96,2,1,"","experiment"],[96,3,1,"","finalize"],[96,2,1,"","log_dir"],[96,3,1,"","log_hyperparams"],[96,3,1,"","log_metrics"],[96,2,1,"","name"],[96,2,1,"","root_dir"],[96,3,1,"","save"],[96,2,1,"","save_dir"],[96,2,1,"","version"]],"pytorch_lightning.loggers.CSVLogger.finalize.params":[[96,0,1,"","status"]],"pytorch_lightning.loggers.CSVLogger.log_hyperparams.params":[[96,0,1,"","args"],[96,0,1,"","kwargs"],[96,0,1,"","params"]],"pytorch_lightning.loggers.CSVLogger.log_metrics.params":[[96,0,1,"","metrics"],[96,0,1,"","step"]],"pytorch_lightning.loggers.CSVLogger.params":[[102,0,1,"","flush_logs_every_n_steps"],[102,0,1,"","name"],[102,0,1,"","prefix"],[102,0,1,"","save_dir"],[102,0,1,"","version"]],"pytorch_lightning.loggers.CometLogger":[[97,2,1,"","experiment"],[97,3,1,"","finalize"],[97,3,1,"","log_graph"],[97,3,1,"","log_hyperparams"],[97,3,1,"","log_metrics"],[97,2,1,"","name"],[97,2,1,"","save_dir"],[97,2,1,"","version"]],"pytorch_lightning.loggers.CometLogger.log_graph.params":[[97,0,1,"","input_array"],[97,0,1,"","model"]],"pytorch_lightning.loggers.CometLogger.log_hyperparams.params":[[97,0,1,"","args"],[97,0,1,"","kwargs"],[97,0,1,"","params"]],"pytorch_lightning.loggers.CometLogger.log_metrics.params":[[97,0,1,"","metrics"],[97,0,1,"","step"]],"pytorch_lightning.loggers.CometLogger.params":[[102,0,1,"","**kwargs"],[102,0,1,"","api_key"],[102,0,1,"","experiment_key"],[102,0,1,"","experiment_name"],[102,0,1,"","offline"],[102,0,1,"","prefix"],[102,0,1,"","project_name"],[102,0,1,"","rest_api_key"],[102,0,1,"","save_dir"]],"pytorch_lightning.loggers.MLFlowLogger":[[98,2,1,"","experiment"],[98,2,1,"","experiment_id"],[98,3,1,"","finalize"],[98,3,1,"","log_hyperparams"],[98,3,1,"","log_metrics"],[98,2,1,"","name"],[98,2,1,"","run_id"],[98,2,1,"","save_dir"],[98,2,1,"","version"]],"pytorch_lightning.loggers.MLFlowLogger.finalize.params":[[98,0,1,"","status"]],"pytorch_lightning.loggers.MLFlowLogger.log_hyperparams.params":[[98,0,1,"","args"],[98,0,1,"","kwargs"],[98,0,1,"","params"]],"pytorch_lightning.loggers.MLFlowLogger.log_metrics.params":[[98,0,1,"","metrics"],[98,0,1,"","step"]],"pytorch_lightning.loggers.MLFlowLogger.params":[[102,0,1,"","artifact_location"],[102,0,1,"","experiment_name"],[102,0,1,"","prefix"],[102,0,1,"","run_id"],[102,0,1,"","run_name"],[102,0,1,"","save_dir"],[102,0,1,"","tags"],[102,0,1,"","tracking_uri"]],"pytorch_lightning.loggers.NeptuneLogger":[[99,3,1,"","after_save_checkpoint"],[99,2,1,"","experiment"],[99,3,1,"","finalize"],[99,3,1,"","log_hyperparams"],[99,3,1,"","log_metrics"],[99,2,1,"","name"],[99,2,1,"","save_dir"],[99,2,1,"","version"]],"pytorch_lightning.loggers.NeptuneLogger.after_save_checkpoint.params":[[99,0,1,"","checkpoint_callback"]],"pytorch_lightning.loggers.NeptuneLogger.finalize.params":[[99,0,1,"","status"]],"pytorch_lightning.loggers.NeptuneLogger.log_hyperparams.params":[[99,0,1,"","params"]],"pytorch_lightning.loggers.NeptuneLogger.log_metrics.params":[[99,0,1,"","metrics"],[99,0,1,"","step"]],"pytorch_lightning.loggers.NeptuneLogger.params":[[102,0,1,"","**neptune_run_kwargs"],[102,0,1,"","api_key"],[102,0,1,"","log_model_checkpoints"],[102,0,1,"","name"],[102,0,1,"","prefix"],[102,0,1,"","project"],[102,0,1,"","run"]],"pytorch_lightning.loggers.TensorBoardLogger":[[100,2,1,"","experiment"],[100,3,1,"","finalize"],[100,2,1,"","log_dir"],[100,3,1,"","log_graph"],[100,3,1,"","log_hyperparams"],[100,3,1,"","log_metrics"],[100,2,1,"","name"],[100,2,1,"","root_dir"],[100,3,1,"","save"],[100,2,1,"","save_dir"],[100,2,1,"","sub_dir"],[100,2,1,"","version"]],"pytorch_lightning.loggers.TensorBoardLogger.finalize.params":[[100,0,1,"","status"]],"pytorch_lightning.loggers.TensorBoardLogger.log_graph.params":[[100,0,1,"","input_array"],[100,0,1,"","model"]],"pytorch_lightning.loggers.TensorBoardLogger.log_hyperparams.params":[[100,0,1,"","metrics"],[100,0,1,"","params"]],"pytorch_lightning.loggers.TensorBoardLogger.log_metrics.params":[[100,0,1,"","metrics"],[100,0,1,"","step"]],"pytorch_lightning.loggers.TensorBoardLogger.params":[[102,0,1,"","**kwargs"],[102,0,1,"","default_hp_metric"],[102,0,1,"","log_graph"],[102,0,1,"","name"],[102,0,1,"","prefix"],[102,0,1,"","save_dir"],[102,0,1,"","sub_dir"],[102,0,1,"","version"]],"pytorch_lightning.loggers.WandbLogger":[[101,3,1,"","after_save_checkpoint"],[101,2,1,"","experiment"],[101,3,1,"","finalize"],[101,3,1,"","log_hyperparams"],[101,3,1,"","log_image"],[101,3,1,"","log_metrics"],[101,3,1,"","log_table"],[101,3,1,"","log_text"],[101,2,1,"","name"],[101,2,1,"","save_dir"],[101,2,1,"","version"]],"pytorch_lightning.loggers.WandbLogger.after_save_checkpoint.params":[[101,0,1,"","checkpoint_callback"]],"pytorch_lightning.loggers.WandbLogger.finalize.params":[[101,0,1,"","status"]],"pytorch_lightning.loggers.WandbLogger.log_hyperparams.params":[[101,0,1,"","args"],[101,0,1,"","kwargs"],[101,0,1,"","params"]],"pytorch_lightning.loggers.WandbLogger.log_metrics.params":[[101,0,1,"","metrics"],[101,0,1,"","step"]],"pytorch_lightning.loggers.WandbLogger.params":[[102,0,1,"","**kwargs"],[102,0,1,"","anonymous"],[102,0,1,"","experiment"],[102,0,1,"","id"],[102,0,1,"","log_model"],[102,0,1,"","name"],[102,0,1,"","offline"],[102,0,1,"","prefix"],[102,0,1,"","project"],[102,0,1,"","save_dir"],[102,0,1,"","version"]],"pytorch_lightning.plugins.environments.SLURMEnvironment.params":[[105,0,1,"","auto_requeue"]],"pytorch_lightning.plugins.precision.HPUPrecisionPlugin.params":[[105,0,1,"","bf16_file_path"],[105,0,1,"","fp32_file_path"],[105,0,1,"","opt_level"],[105,0,1,"","precision"],[105,0,1,"","verbose"]],"pytorch_lightning.plugins.precision.NativeMixedPrecisionPlugin.params":[[105,0,1,"","device"],[105,0,1,"","precision"],[105,0,1,"","scaler"]],"pytorch_lightning.strategies.BaguaStrategy.params":[[106,0,1,"","algorithm"],[106,0,1,"","bagua_kwargs"],[106,0,1,"","flatten"]],"pytorch_lightning.strategies.DDPFullyShardedStrategy.params":[[106,0,1,"","bucket_cap_mb"],[106,0,1,"","compute_dtype"],[106,0,1,"","cpu_offload"],[106,0,1,"","flatten_parameters"],[106,0,1,"","fp32_reduce_scatter"],[106,0,1,"","min_num_params"],[106,0,1,"","move_grads_to_cpu"],[106,0,1,"","reshard_after_forward"],[106,0,1,"","state_dict_to_cpu"]],"pytorch_lightning.strategies.DeepSpeedStrategy.params":[[106,0,1,"","allgather_bucket_size"],[106,0,1,"","allgather_partitions"],[106,0,1,"","block_size"],[106,0,1,"","config"],[106,0,1,"","contiguous_gradients"],[106,0,1,"","contiguous_memory_optimization"],[106,0,1,"","cpu_checkpointing"],[106,0,1,"","hysteresis"],[106,0,1,"","initial_scale_power"],[106,0,1,"","load_full_weights"],[106,0,1,"","logging_batch_size_per_gpu"],[106,0,1,"","logging_level"],[106,0,1,"","loss_scale"],[106,0,1,"","loss_scale_window"],[106,0,1,"","max_in_cpu"],[106,0,1,"","min_loss_scale"],[106,0,1,"","nvme_path"],[106,0,1,"","offload_optimizer"],[106,0,1,"","offload_optimizer_device"],[106,0,1,"","offload_parameters"],[106,0,1,"","offload_params_device"],[106,0,1,"","optimizer_buffer_count"],[106,0,1,"","overlap_comm"],[106,0,1,"","overlap_events"],[106,0,1,"","params_buffer_count"],[106,0,1,"","params_buffer_size"],[106,0,1,"","partition_activations"],[106,0,1,"","pin_memory"],[106,0,1,"","queue_depth"],[106,0,1,"","reduce_bucket_size"],[106,0,1,"","reduce_scatter"],[106,0,1,"","remote_device"],[106,0,1,"","single_submit"],[106,0,1,"","stage"],[106,0,1,"","sub_group_size"],[106,0,1,"","synchronize_checkpoint_boundary"],[106,0,1,"","thread_count"],[106,0,1,"","zero_allow_untested_optimizer"],[106,0,1,"","zero_optimization"]],"pytorch_lightning.strategies.IPUStrategy.params":[[106,0,1,"","autoreport"],[106,0,1,"","autoreport_dir"],[106,0,1,"","device_iterations"],[106,0,1,"","inference_opts"],[106,0,1,"","training_opts"]],"pytorch_lightning.trainer.Trainer.fit.params":[[72,0,1,"","ckpt_path"],[72,0,1,"","datamodule"],[72,0,1,"","model"],[72,0,1,"","train_dataloaders"],[72,0,1,"","val_dataloaders"]],"pytorch_lightning.trainer.Trainer.params":[[72,0,1,"","accelerator"],[72,0,1,"","accumulate_grad_batches"],[72,0,1,"","amp_backend"],[72,0,1,"","amp_level"],[72,0,1,"","auto_lr_find"],[72,0,1,"","auto_scale_batch_size"],[72,0,1,"","auto_select_gpus"],[72,0,1,"","benchmark"],[72,0,1,"","callbacks"],[72,0,1,"","check_val_every_n_epoch"],[72,0,1,"","checkpoint_callback"],[72,0,1,"","default_root_dir"],[72,0,1,"","detect_anomaly"],[72,0,1,"","deterministic"],[72,0,1,"","devices"],[72,0,1,"","enable_checkpointing"],[72,0,1,"","enable_model_summary"],[72,0,1,"","enable_progress_bar"],[72,0,1,"","fast_dev_run"],[72,0,1,"","flush_logs_every_n_steps"],[72,0,1,"","gpus"],[72,0,1,"","gradient_clip_algorithm"],[72,0,1,"","gradient_clip_val"],[72,0,1,"","ipus"],[72,0,1,"","limit_predict_batches"],[72,0,1,"","limit_test_batches"],[72,0,1,"","limit_train_batches"],[72,0,1,"","limit_val_batches"],[72,0,1,"","log_every_n_steps"],[72,0,1,"","log_gpu_memory"],[72,0,1,"","logger"],[72,0,1,"","max_epochs"],[72,0,1,"","max_steps"],[72,0,1,"","max_time"],[72,0,1,"","min_epochs"],[72,0,1,"","min_steps"],[72,0,1,"","move_metrics_to_cpu"],[72,0,1,"","multiple_trainloader_mode"],[72,0,1,"","num_nodes"],[72,0,1,"","num_processes"],[72,0,1,"","num_sanity_val_steps"],[72,0,1,"","overfit_batches"],[72,0,1,"","plugins"],[72,0,1,"","precision"],[72,0,1,"","prepare_data_per_node"],[72,0,1,"","process_position"],[72,0,1,"","profiler"],[72,0,1,"","progress_bar_refresh_rate"],[72,0,1,"","reload_dataloaders_every_n_epochs"],[72,0,1,"","replace_sampler_ddp"],[72,0,1,"","resume_from_checkpoint"],[72,0,1,"","stochastic_weight_avg"],[72,0,1,"","strategy"],[72,0,1,"","sync_batchnorm"],[72,0,1,"","terminate_on_nan"],[72,0,1,"","tpu_cores"],[72,0,1,"","track_grad_norm"],[72,0,1,"","val_check_interval"],[72,0,1,"","weights_save_path"],[72,0,1,"","weights_summary"]],"pytorch_lightning.trainer.Trainer.predict.params":[[72,0,1,"","ckpt_path"],[72,0,1,"","dataloaders"],[72,0,1,"","datamodule"],[72,0,1,"","model"],[72,0,1,"","return_predictions"]],"pytorch_lightning.trainer.Trainer.test.params":[[72,0,1,"","ckpt_path"],[72,0,1,"","dataloaders"],[72,0,1,"","datamodule"],[72,0,1,"","model"],[72,0,1,"","verbose"]],"pytorch_lightning.trainer.Trainer.tune.params":[[72,0,1,"","datamodule"],[72,0,1,"","lr_find_kwargs"],[72,0,1,"","model"],[72,0,1,"","scale_batch_size_kwargs"],[72,0,1,"","train_dataloaders"],[72,0,1,"","val_dataloaders"]],"pytorch_lightning.trainer.Trainer.validate.params":[[72,0,1,"","ckpt_path"],[72,0,1,"","dataloaders"],[72,0,1,"","datamodule"],[72,0,1,"","model"],[72,0,1,"","verbose"]]},objnames:{"0":["py","parameter","Python parameter"],"1":["py","class","Python class"],"2":["py","property","Python property"],"3":["py","method","Python method"]},objtypes:{"0":"py:parameter","1":"py:class","2":"py:property","3":"py:method"},terms:{"0":[0,3,6,11,16,17,18,19,20,22,23,24,26,27,28,31,32,36,38,39,43,48,55,58,60,62,64,65,72,74,76,77,78,80,81,82,84,85,88,93,94,99,102,103,104,107,108,139,140,142,144,145,147,148,149,152,154,155,157,160,161],"00":[36,49,58,72,108,152],"000":152,"0001":[28,152],"00014637":152,"00024388":152,"00028":64,"000u":154,"001":[26,36,140,148],"0010246":152,"0017477":152,"001ms110":[16,17],"001ms228":[16,17],"001ms32":[16,17],"001ms339":[16,17],"001ms434":[16,17],"001ms61":[16,17],"001ms79":[16,17],"001u":[16,17,154],"0022506":152,"002ms921":[16,17],"008":152,"01":[20,27,28,31,32,48,64,72,77,78,140,148],"010":152,"011":152,"012":152,"012234":152,"015754":152,"01f":159,"02":[26,27,31,36,55,64],"020":152,"022":152,"02d":55,"03":[31,154],"03977392":48,"044m":154,"04835479":48,"05":[6,154],"05200016":48,"055m":154,"059u":154,"06":[24,152,154],"060u":154,"062u":[16,17],"063u":[16,17],"06m09s401ms746":[16,17],"07":99,"074":152,"08":49,"084":152,"0b1":84,"0x2b599e088ae8":0,"1":[1,2,5,6,9,11,15,18,20,22,23,25,31,32,33,36,39,41,42,43,48,55,56,58,60,61,62,64,65,69,72,73,77,78,80,81,82,83,84,91,93,94,99,101,102,103,104,107,108,109,110,111,123,127,129,130,135,136,138,139,140,141,142,144,146,148,152,153,154,155,157,158,160,161],"10":[5,11,15,16,17,20,23,26,27,31,36,39,43,48,49,53,55,58,63,64,69,70,72,77,78,89,91,99,102,108,128,140,141,145,147,148,152,154,157],"100":[6,19,23,26,27,31,72,96,101,107,108,147,159],"1000":[72,108],"100m":[6,19],"101":109,"105u":154,"10th":[33,41,135],"11":[19,49,84,91,128,154],"113":88,"12":[27,28,36,49,63,72,91,108,128,140,148,154],"1234":[140,148],"125":[16,17],"128":[19,22,53,63,64,84,145],"128x128":[16,17],"13":[91,128,152,154],"1307":74,"131":77,"132":77,"135u":154,"14":[64,91,128,154],"148":84,"15":[19,48,88,91,107,110,154],"1558":152,"15\ubd84":109,"15e":152,"16":[9,12,19,20,36,39,43,48,49,53,61,64,68,69,84,85,91,105,107,110,140,141,147,148,154],"167":152,"16\ube44\ud2b8":147,"17":[91,110],"1704":64,"173u":[16,17],"175u":154,"18":[16,17,86,91,110,152,154],"182":152,"183u":[16,17],"187":[16,17],"1875":152,"1876":[84,152],"19":[49,91,110],"192":84,"1e":[19,22,36,48,63,64,65,139,142,144,145,147,149],"1tb":[19,141,147],"2":[0,3,4,11,12,16,17,20,21,22,25,26,28,31,33,36,39,47,48,49,55,61,63,64,65,69,72,76,77,78,83,84,91,92,93,94,103,107,108,111,123,127,129,130,135,136,138,139,140,142,148,149,152,154,155,157,160,161],"20":[11,16,17,19,26,49,60,72,77,91,110,139,141,147,152,154],"200":[16,17],"2019":[140,148],"202":[16,17],"2022":47,"2048":[16,17,72],"205":[16,17],"206":[16,17,152],"209":[16,17],"20\uac1c":147,"21":[88,91,110,154],"2158432006835938":85,"21s102ms853":[16,17],"22":[91,110,154],"22050hz":84,"226e":152,"228u":154,"23":[91,124,154],"236u":[16,17],"24":[6,28,29,63,84,91,124],"25":[20,22,64,72,84,91,108,124],"2541470527648926":85,"256":[77,84,88,140,148],"26":[91,124],"260":152,"267":152,"27":[91,124,154,160],"273":[16,17],"28":[56,63,64,77,144,145,147,154,155,158,160,161],"283u":[16,17],"290":152,"29500":39,"2e8":19,"2f":55,"2vm":[16,17],"3":[0,3,4,6,11,16,17,21,22,23,25,33,36,48,52,55,58,61,64,65,71,72,84,91,99,103,106,123,127,129,135,136,138,139,140,142,144,146,148,149,154,155,157,158,160,161],"30":[19,31,48,64],"305u":[16,17],"3081":74,"3090":69,"32":[6,9,11,13,15,16,17,18,19,22,31,36,38,48,53,55,56,64,69,72,74,77,93,94,105,107,108,140,148,154,155,157,158,160,161],"33":154,"3300":84,"336u":154,"34":154,"344":152,"35":154,"350":84,"351u":154,"353u":154,"362u":154,"3752":152,"38":154,"383":152,"387u":154,"39":[16,17],"393u":154,"3d":86,"3e":19,"3x":[6,67,72,108],"4":[3,4,5,6,11,12,19,21,22,27,28,33,36,38,43,48,49,55,62,63,64,65,69,72,80,81,84,91,103,106,107,108,109,123,127,135,140,141,148,154],"40":58,"400":84,"403e":152,"40\uc885\ub958":147,"40b":19,"41":154,"42":[16,17,60,72,152,154],"425201":[16,17],"426e":152,"432u":154,"44":[152,154],"45":[19,154],"456u":154,"461u":154,"464u":154,"47":0,"481u":154,"484u":154,"4863767":152,"4869394":152,"492u":154,"4\uac1c\uc758":147,"5":[0,11,16,17,19,20,22,27,28,29,33,43,48,64,65,72,74,77,78,84,89,91,93,103,104,107,108,123,139,141,142,146,152,154],"50":[16,17,20,26,43,48,72,102,157],"500":[64,65,101,157],"5000":[19,74],"500m":19,"51011":[16,17,18],"512":[19,77,103],"516e":152,"517u":154,"52":49,"523u":154,"526u":154,"543":154,"549":154,"55000":74,"566u":154,"58":154,"5\ub2e8\uacc4\uc758":147,"5e8":19,"5th":[16,17,22,62,65],"5x":19,"6":[0,16,17,64,69,72,91,93,107,123,152,154],"60":[0,48,89,154],"60000":152,"6006":[84,147],"62":154,"6206e0":70,"63":19,"638u":154,"64":[20,36,48,49,63,64,72,74,80,81,82,83,84,99,105,107,140,144,147,148,154],"644e":152,"65038344_52a45d090d":103,"651":152,"657u":154,"669u":154,"67108864":84,"68":[152,154],"681m":154,"69":154,"694u":154,"6gb":19,"7":[5,22,36,48,62,64,65,72,74,77,80,88,91,93,108,128,140,146,148,152,154],"721":152,"734":152,"74":154,"743u":154,"75":20,"756u":154,"759":152,"76":152,"768":84,"77":154,"773u":[16,17],"778ms572":[16,17],"778u":[16,17],"782u":154,"783u":154,"786u":154,"7\ub2e8\uacc4\ub97c":[109,147],"7gb":19,"7th":108,"7x":19,"8":[4,5,6,8,11,12,13,15,16,17,18,19,21,22,29,36,38,43,48,60,62,64,65,72,82,91,93,106,107,108,128,140,148],"80":[16,17,33,48,135,152],"80m":19,"81":154,"839":152,"8470":[16,17],"85":48,"859u":154,"869":152,"877":152,"887":152,"8888":84,"893":152,"8g":84,"9":[16,17,19,36,48,91,99,128],"90":[16,17,36,48,103,154],"9001":151,"93":152,"95":[16,17,48],"961u":154,"967u":154,"97":99,"973u":[16,17],"99":[16,17,20,55,64],"998":19,"999":19,"9th":[22,62,65],"\uac00\ub2a5\ud55c":147,"\uac00\ub2a5\ud574\uc9d1\ub2c8\ub2e4":147,"\uac00\ub3c5\uc131":147,"\uac00\ub3c5\uc131\uacfc":149,"\uac00\ub3c5\uc131\uc744":147,"\uac00\uc774\ub4dc\uc758":149,"\uac00\uc911\uce58\ub97c":147,"\uac00\uc9dc":147,"\uac04\ub2e8\ud55c":147,"\uac15\ud654\ud559\uc2b5":147,"\uac16\ub294":147,"\uac16\ucd94\uace0":147,"\uac1c\uc120\ud558\ub294":149,"\uac1d\uccb4":147,"\uac80\uc99d":147,"\uac83\ub4e4\uc774":147,"\uac83\uc785\ub2c8\ub2e4":[147,149],"\uacbd\uc0ac\ub3c4":147,"\uacbd\uc6b0":147,"\uace0\uae09":147,"\uacfc":147,"\uad00\uc2ec\uc788\ub294":149,"\uad00\uc5ec\ud569\ub2c8\ub2e4":147,"\uad6c\uc131\ub418\ub3c4\ub85d":149,"\uad6c\ud604\ud569\ub2c8\ub2e4":147,"\uad8c\uc7a5\ud558\ub294\ub370":149,"\uadf8\ub300\ub85c":147,"\uadf8\ub8f9\uc73c\ub85c":147,"\uae30\ub2a5\uc744":147,"\uae30\ubc95\ub4e4":147,"\uae30\uc220\uc744":147,"\uae30\uc220\uc785\ub2c8\ub2e4":147,"\uae30\uc874":147,"\ub04c":147,"\ub098\ub204\uc5b4":147,"\ub098\uc740":147,"\ub0b4\ubcf4\ub0b4\uae30":147,"\ub0b4\uc5d0\uc11c":147,"\ub0b4\uc6a9\ub4e4":147,"\ub294":147,"\ub2e4\ub978":147,"\ub2e4\uc591\ud55c":147,"\ub2e4\uc911":147,"\ub2e8\uacc4\ub294":147,"\ub2e8\uacc4\ub85c":147,"\ub2e8\uacc4\ub97c":147,"\ub2e8\uacc4\uc5d0\uc11c":147,"\ub2e8\uc21c\ud788":147,"\ub300\uaddc\ubaa8\ub85c":147,"\ub300\uaddc\ubaa8\uc5d0\uc11c":109,"\ub354":147,"\ub370\uc774\ud130\ub97c":147,"\ub370\uc774\ud130\uc14b\uc744":147,"\ub3c4":147,"\ub3c4\uad6c":147,"\ub3c4\uc6c0\uc774":147,"\ub3c4\uc911":147,"\ub3d9\uc2dc\uc5d0":147,"\ub3d9\uc548":109,"\ub3d9\uc791\ud560":147,"\ub418\ub294":147,"\ub4a4\uc5d0\ub294":147,"\ub4f1":147,"\ub4f1\uc744":147,"\ub525\ub7ec\ub2dd":[109,147],"\ub530\ub77c":147,"\ub610\ub294":[109,147],"\ub77c\uc774\ud2b8":147,"\ub77c\uc774\ud2b8\ub2dd":[109,147,149],"\ub97c":147,"\ub9cc\uc5d0":109,"\ub9ce\uc740":147,"\ub9e4\uac1c\ubcc0\uc218\ub97c":147,"\uba38\uc2e0\ub7ec\ub2dd":[109,147],"\uba54\uc18c\ub4dc":147,"\uba54\ud0c0\ud559\uc2b5":147,"\uba85\ub839\uc904":147,"\ubaa8\ub378\uc744":147,"\ubaa8\ub4e0":147,"\ubaa9\ud45c":147,"\ubaa9\ud45c\ub294":149,"\ubb36\uc5b4\uc11c":147,"\ubb38\uc11c":109,"\ubb38\uc11c\uc5d0\uc11c\ub294":147,"\ubc0f":147,"\ubc18\ubcf5\ub418\ub294":147,"\ubc18\ubcf5\uc5d0":147,"\ubc18\ubcf5\uc744":147,"\ubc18\ubcf5\uc801\uc73c\ub85c":147,"\ubc1c\uacac\ud558\uace0":149,"\ubc1c\uc804\ud569\ub2c8\ub2e4":109,"\ubc29\uc2dd\uc73c\ub85c":147,"\ubc30\uacbd\uc9c0\uc2dd":147,"\ubc30\uc6c1\ub2c8\ub2e4":109,"\ubc30\uc6cc\ubcf4\uae30":109,"\ubc30\uce58":147,"\ubc30\ud130\ub9ac\uac00":147,"\ubcc0\uacbd":147,"\ubcc0\uacbd\ud558\uc9c0":147,"\ubcf5\uc7a1\ub3c4\uc5d0":147,"\ubcf5\uc7a1\uc131\ub4e4\uc744":147,"\ubcf5\uc7a1\ud55c":147,"\ubd10\uc57c\ud560\uc9c0":149,"\ubd80\ubd84\ub3c4":147,"\ubd80\ubd84\uc744":149,"\ubd84\ub9ac\ud558\uc5ec":147,"\ubd88\ub7ec\uc624\uace0":147,"\ubd88\ub7ec\uc624\uae30":147,"\ubd88\ub7ec\uc635\ub2c8\ub2e4":147,"\ube0c\ub77c\uc6b0\uc800\uc5d0\uc11c":147,"\ube44\ud65c\uc131\ud654\ub97c":147,"\ube60\ub978":147,"\ubfd0\ub9cc":147,"\uc0ac\ub840\uc5d0":147,"\uc0ac\uc6a9":147,"\uc0ac\uc6a9\uc790\uac00":147,"\uc0ac\uc6a9\uc790\ub77c\uba74":147,"\uc0ac\uc6a9\ud558\ub294":147,"\uc0ac\uc6a9\ud558\uc5ec":147,"\uc0ac\uc6a9\ud560":147,"\uc0ac\uc6a9\ud569\ub2c8\ub2e4":147,"\uc0b4\ud3b4\ubcf4\uc138\uc694":147,"\uc0bd\uc785\ud560":147,"\uc0c1\uc0c1\ud574\ubcf4\uc138\uc694":149,"\uc0c1\uc6a9":147,"\uc0dd\uac01\uc73c\ub85c\ubd80\ud130":109,"\uc120\ud0dd\ud569\ub2c8\ub2e4":147,"\uc124\uc815\ud569\ub2c8\ub2e4":147,"\uc131\ub2a5\uc744":[109,147],"\uc190\uc27d\uac8c":147,"\uc218":147,"\uc21c\ud68c":147,"\uc228\uae30\uc9c0":147,"\uc2dc":147,"\uc2dc\uac01\ud654\ud558\ub294\ub370":147,"\uc2e4\ud589\ud558\uace0":147,"\uc2e4\ud589\ud560":147,"\uc2e4\ud5d8\uc744":147,"\uc544\ub2c8\ub77c":147,"\uc544\ub798":147,"\uc544\ub798\ub97c":147,"\uc544\uc774\ub514\uc5b4":147,"\uc544\uc774\ub514\uc5b4\ub97c":147,"\uc548\ub0b4\ud569\ub2c8\ub2e4":147,"\uc54a\uc73c\uba74\uc11c":[109,147],"\uc54c\uace0":149,"\uc5b4\ub514\ub97c":149,"\uc5b4\ub514\uc5d0\ub4e0":147,"\uc5b4\ub5a0\ud55c":147,"\uc5b8\uc81c\ub098":147,"\uc5c6\uc74c":147,"\uc5c6\uc774":147,"\uc5d0":147,"\uc5d0\uc11c":147,"\uc5d0\ud3ed":147,"\uc5d4\uc9c0\ub2c8\uc5b4\ub4e4\uc744":[109,147],"\uc5d4\uc9c0\ub2c8\uc5b4\ub9c1":147,"\uc5d4\uc9c0\ub2c8\uc5b4\ub9c1\uc801":147,"\uc5ec\ub7ec\uac1c\uc758":147,"\uc5ec\ub7ec\uc904\uc758":147,"\uc5f0\uad6c":[147,149],"\uc5f0\uad6c\uc6a9":147,"\uc5f0\uad6c\uc790\ub4e4\uacfc":[109,147],"\uc5f4\uc5b4\ubcf4\uc138\uc694":147,"\uc608\uc2dc\ub4e4\uc744":147,"\uc608\uce21":147,"\uc608\uce21\uc6a9\uc73c\ub85c":147,"\uc608\uce21\uc744":147,"\uc624\ud1a0\uc778\ucf54\ub354":147,"\uc635\uc158":147,"\uc644\ub8cc":147,"\uc644\ub8cc\ud558\uc5ec":147,"\uc644\ubcbd\ud788":147,"\uc644\uc804\ud55c":147,"\uc644\uc804\ud788":147,"\uc694\uad6c\ud558\uba74\uc11c":147,"\uc6cc\ud06c\ud50c\ub85c\uc6b0\uc758":[109,147],"\uc6d0\uce59\uc740":147,"\uc6d0\ud558\ub294\ub9cc\ud07c\uc758":147,"\uc704\ud55c":[109,147],"\uc704\ud574":[147,149],"\uc720\uc0ac\ud558\uac8c":149,"\uc720\uc0ac\ud55c":147,"\uc720\uc5f0\uc131\uc744":[109,147],"\uc720\uc6a9\ud55c":147,"\uc720\ud615\uc758":147,"\uc740":[109,147],"\uc744":[147,149],"\uc758":[147,149],"\uc774":[147,149],"\uc774\uad00\ud558\uae30":147,"\uc774\ub294":147,"\uc774\ub7f0":147,"\uc774\ub974\ub294":109,"\uc774\ubbf8":147,"\uc774\ubbf8\uc9c0\ub85c":147,"\uc774\uc0c1\uc758":147,"\uc778\uc790":147,"\uc778\uc790\ub4e4\uc744":147,"\uc77c\ubc18\uc801\uc778":[109,147],"\uc77c\ubd80\ub97c":147,"\uc788\ub294":147,"\uc788\ub2e4\uace0":149,"\uc788\ub3c4\ub85d":147,"\uc788\uc2b5\ub2c8\ub2e4":[147,149],"\uc788\uc73c\uba70":147,"\uc790\ub3d9\uc73c\ub85c":147,"\uc790\ub3d9\ud654\ub41c":147,"\uc790\ub3d9\ud654\ud569\ub2c8\ub2e4":147,"\uc791\uc5c5\ub4e4\uc744":147,"\uc7ac\uad6c\uc131\ud568\uc73c\ub85c\uc368":147,"\uc7ac\uad6c\uc131\ud569\ub2c8\ub2e4":147,"\uc7ac\ud604\uc131":147,"\uc7ac\ud604\uc131\uc744":[147,149],"\uc800\uc7a5\uc18c\ub098":149,"\uc800\uc7a5\ud558\uae30":147,"\uc804\ubb38\uac00\ub4e4\uc774":147,"\uc804\ubb38\uc801\uc778":[109,147],"\uc815\ubc00\ub3c4":147,"\uc815\uc758":147,"\uc815\uc758\ud569\ub2c8\ub2e4":147,"\uc815\ud655\ud788":149,"\uc81c\uac70\ud558\uace0":147,"\uc81c\uacf5\ud558\ub294":147,"\uc81c\uacf5\ud569\ub2c8\ub2e4":147,"\uc81c\uc5b4\ud560":147,"\uc81c\uc5b4\ud569\ub2c8\ub2e4":147,"\uc81c\ud488\ud654\uc5d0":109,"\uc8fc\uc694\ud55c":[109,147,149],"\uc911":147,"\uc9c0\uc6d0":147,"\uc9c1\uc811":147,"\ucc38\uace0\ud558\uc138\uc694":147,"\ucc38\uc870":147,"\ucc38\uc870\ud558\uc138\uc694":[109,147],"\ucc3e\uae30":149,"\uccb4\ud06c\ud3ec\uc778\ud2b8":147,"\ucd08\uae30\ud654\ud569\ub2c8\ub2e4":147,"\ucd5c\ub300\ud55c\uc758":[109,147],"\ucd5c\uc2e0":147,"\ucd5c\uc801\ud654":147,"\ucd5c\ucca8\ub2e8":147,"\ucd94\uac00\uc801\uc778":147,"\ucd94\uc0c1\ud654":147,"\ucf1c\uac70\ub098":147,"\ucf54\ub4dc":147,"\ucf54\ub4dc\uac00":[147,149],"\ucf54\ub4dc\ub4e4\uc744":147,"\ucf54\ub4dc\ub97c":147,"\ucf54\ub4dc\uc640":147,"\ucf5c\ubc31":147,"\ud14c\uc2a4\ud2b8":147,"\ud14c\uc2a4\ud2b8\ub97c":147,"\ud150\uc11c\ubcf4\ub4dc":147,"\ud1b5\ud569\ud560":147,"\ud2b9\uc815":147,"\ud30c\uc774\ud1a0\uce58":[109,147,149],"\ud3c9\uac00":147,"\ud3ec\uae30\ud558\uc9c0":109,"\ud3ec\ud568\ub418\uc5b4":147,"\ud3ec\ud568\ub41c":147,"\ud3ec\ud568\ud558\uac70\ub098":147,"\ud3ec\ud568\ud558\uc5ec":147,"\ud504\ub808\uc784\uc6cc\ud06c\uc785\ub2c8\ub2e4":[109,147],"\ud504\ub85c\uc81d\ud2b8\uac00":109,"\ud504\ub85c\uc81d\ud2b8\ub97c":147,"\ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c":149,"\ud504\ub85c\uc81d\ud2b8\uc758":147,"\ud50c\ub798\uadf8":147,"\ud544\uc694\ub85c":[109,147],"\ud544\uc694\uc5c6\uc2b5\ub2c8\ub2e4":147,"\ud544\uc694\ud55c":147,"\ud558\ub098\ub97c":147,"\ud558\ub098\uc785\ub2c8\ub2e4":147,"\ud558\ub294":[109,147],"\ud558\uc5ec":147,"\ud559\uc2b5\ud55c":147,"\ud559\uc2b5\ud560":147,"\ud559\uc2b5\ud569\ub2c8\ub2e4":147,"\ud560":147,"\ud568\uaed8":[109,147],"\ud569\ub2c8\ub2e4":147,"\ud574\ub2f9\ud558\ub294":147,"\ud575\uc2ec":147,"\ud638\ucd9c":147,"\ud655\ubcf4\ud558\ub3c4\ub85d":147,"\ud655\uc7a5\uc131":147,"\ud655\uc7a5\uc5d0":147,"\ud658\uacbd\uc5d0":147,"\ud65c\uc131\ud654":147,"\ud6c8\ub828":147,"\ud78c\ud2b8":147,"abstract":[64,72,90,103,105,144,147],"boolean":104,"break":87,"byte":84,"case":[0,6,16,17,19,20,22,23,26,27,28,29,31,43,48,53,54,55,56,58,61,63,64,65,67,69,72,74,84,89,99,101,102,103,107,108],"catch":72,"class":[0,4,5,6,8,11,15,19,20,21,22,23,30,31,32,37,43,52,53,54,55,56,58,60,61,63,64,65,68,69,70,74,76,77,78,80,81,82,83,84,85,89,92,93,94,95,96,97,98,99,100,101,102,103,104,106,107,108,109,139,140,142,144,145,147,148,149,153,155,157,158,159,160,161],"default":[3,4,6,8,9,11,13,18,19,22,30,31,36,52,55,58,62,63,64,65,67,69,70,71,72,78,84,90,93,94,96,97,98,99,100,101,104,105,106,107,108,140,145,147,148,149,151,154,158],"do":[0,3,6,8,11,12,15,18,19,22,28,36,38,47,53,54,56,58,60,62,63,64,65,69,70,72,82,84,85,93,94,96,97,98,99,100,101,103,107,108,109,139,140,142,148,149,157],"export":[16,17,18,19,27,36,64,80,81,147],"final":[22,39,62,63,65,92,96,97,98,99,100,101,102,103,140,148,157],"float":[20,27,31,36,61,64,65,66,67,69,72,96,97,98,99,100,101,105,107,108,130,140,148,149,157],"fsdp\ub97c":147,"function":[0,11,12,19,20,22,28,29,39,54,55,63,64,65,69,72,77,80,91,93,94,102,103,104,108,124,125,126,139,140,141,142,144,145,147,148,153,154,155,157,158,160,161],"gan\uc744":147,"gpu\uc5d0\uc11c":147,"import":[0,4,5,6,9,11,13,15,16,17,18,19,20,21,22,23,28,29,31,32,36,37,38,43,54,55,57,58,60,62,63,64,65,70,71,72,74,76,77,80,83,84,85,88,89,92,93,96,97,98,99,100,101,102,103,106,107,108,139,140,142,145,147,148,149,151,152,153,154,155,157,159,160,161],"int":[3,27,29,31,37,43,60,63,64,72,74,92,96,97,98,99,100,101,102,107,108,140,148,153],"lightning\uc5d0\ub294":147,"lightning\uc5d0\uc11c\ub294":147,"lightning\uc740":147,"lightning\uc758":[147,149],"lightningmodule\uc5d0\uc11c":147,"lightningmodule\uc744":147,"long":[23,30,41,85,107,108,152],"module\uc744":147,"module\uc774":147,"new":[0,1,4,6,11,16,17,18,21,49,64,66,68,70,72,74,84,85,89,91,92,97,98,99,100,101,106,108,124,144,156,159],"null":[26,84],"public":[33,93,103],"pytorch\ub97c":147,"pytorch\uc758":147,"return":[0,4,5,6,11,12,15,18,19,20,21,22,23,28,31,32,37,43,56,58,61,63,64,65,69,70,72,74,80,81,82,83,84,85,92,93,94,95,96,97,98,99,100,101,102,103,104,139,140,142,144,145,147,148,149,153,157,159],"short":[19,22,30],"super":[11,15,19,20,22,23,28,29,31,32,53,56,63,64,65,70,74,80,81,82,83,93,102,107,108,139,140,142,144,145,147,148,149,153,157,159,160],"switch":[16,17,20,64,65,69,76,84,92,108,139,140,142,148],"throw":[15,107],"torchscript\ub85c":147,"trainer\uc5d0":147,"trainer\uc758":147,"true":[0,3,4,18,19,20,21,22,23,26,27,28,31,36,37,54,55,60,61,64,65,70,72,74,77,78,80,84,89,93,94,99,100,101,102,103,107,108,139,142,144,147,149,154,157,158,159],"try":[5,16,17,19,22,28,29,36,37,38,39,48,64,69,72,78,149,151],"validation_step\uc774\ub098":147,"var":[27,63],"while":[8,16,17,18,20,22,30,43,61,64,65,69,72,74,102,103,140,148,151],A:[3,5,16,17,19,22,26,28,29,30,32,38,48,49,53,61,64,65,72,74,77,78,85,89,93,94,96,97,98,100,101,103,104,107,108,149],AND:[0,64],And:[16,17,23,56,64,82,84],As:[18,19,22,29,31,32,41,45,46,55,60,65,105],At:[16,17,64],BE:36,Be:[12,65,139,142],But:[19,53,64,74,103],By:[6,9,11,13,16,17,18,19,22,55,63,64,69,72,82,96,100,102,103,105,140,148,157,158],For:[2,3,4,6,8,9,11,15,16,17,19,20,22,26,27,28,29,30,31,39,43,45,46,49,53,55,56,57,62,64,65,67,69,70,72,74,77,82,83,84,87,88,93,97,99,102,103,104,107,108,139,140,142,145,148,149,157,158],IS:[0,63,64],If:[5,6,11,12,15,18,19,22,28,29,30,32,36,41,43,44,45,46,47,49,53,55,58,61,63,64,65,67,70,74,77,78,81,83,92,93,94,95,96,97,98,99,100,101,102,103,107,108,139,140,141,142,145,146,148,152,155,157,158,159,160,161],In:[5,6,9,11,16,17,19,22,23,24,27,28,29,36,43,45,46,48,55,56,58,61,63,64,65,69,72,74,77,84,85,89,90,93,99,102,103,107,108,112,130,131,132,133,140,144,145,148,149,152,158],Is:149,It:[0,4,6,8,16,17,19,21,22,26,28,29,36,37,53,55,58,61,63,64,65,67,69,70,71,72,74,81,87,88,89,93,99,102,103,106,107,108,139,140,142,146,148,149,151,154,157],Its:[103,140,148],NOT:[6,22,60,63,64,72,93,157],No:[16,17,67],Not:55,OR:[64,93,107],Of:102,On:[8,28,43,49,72,76,77],One:[16,17,28,65,78,86,103,108],Or:[22,62,65,84,92],THE:[0,63],TO:[0,63],That:[6,18,97,140,148],The:[0,3,4,5,6,8,9,12,13,16,17,18,19,20,21,22,26,27,28,29,30,31,36,41,42,43,46,48,53,54,55,56,58,60,61,62,63,64,65,70,71,72,74,77,81,82,85,89,92,93,94,96,97,98,99,100,101,102,103,104,105,106,107,108,139,140,142,144,145,148,149,152,154,157,159],Then:[16,17,22,64,70,84,154,159],There:[3,6,16,17,19,43,61,64,74,105,107,108],These:[4,16,17,19,47,63,64,72,84,102,106,141,149],To:[0,3,5,6,8,9,12,13,15,16,17,18,19,20,22,26,27,28,29,31,36,38,41,42,43,45,46,49,52,53,55,58,60,61,63,64,65,70,72,74,76,77,80,87,88,90,93,96,97,98,100,101,102,103,108,139,140,142,144,145,148,149,151,152,153,154,155,157,158,159,160,161],WITH:6,Will:[64,72,103],With:[19,28,29,30,31,41,48,64,65,77,84,103,105,108,144,154,158],_:[23,43,56,63,64,65,81,82,84,108,139,142,149,159],__call__:152,__dict__:[16,17],__file__:36,__getitem__:[43,152],__init__:[0,11,15,19,20,22,23,28,29,31,43,53,56,63,64,65,72,74,77,80,81,82,83,89,93,94,101,107,108,139,140,142,144,145,147,148,149,153,157,159,160],__iter__:[43,107],__len__:[43,107],__main__:[0,31,36,63,72],__name__:[31,36,63,72],__next__:[43,152],_action_count:153,_action_first_occurr:153,_batch:107,_devic:[16,17],_epoch:[102,157],_epoch_end:[93,103],_epoch_start:103,_experi:97,_fakequeu:64,_for_each_instance_rewrit:[16,17],_generate_state_kei:[93,94],_init:28,_input_format:157,_launch:0,_loss:[56,64],_lrfinder:72,_lrschedul:[28,64],_model:72,_next_data:152,_optim:84,_pickl:0,_run_early_stopping_check:58,_sample_r:84,_shape_input:85,_shared_ev:[56,64],_shared_eval_step:64,_step:[6,102,157],_wer:84,_xla_get_devic:[16,17],_xlac:[16,17],_z:[65,139,142],a100:69,a_float32:69,a_val:72,ab:64,abc:64,abf:71,abil:[19,55,78,105,140,148],abl:[19,43,55,65,69,87,93,94,139,142],abort:[96,98,99,100,101],about:[1,4,6,14,16,17,18,19,21,22,24,25,29,30,36,40,57,59,64,65,71,72,77,84,87,88,90,92,99,102,103,104,105,108,118,139,140,142,148,149],abov:[3,16,17,19,22,28,29,31,36,48,52,61,64,65,69,84,87,93,94,102,103,107,140,148],academ:[23,33,38,47,91,128,149],acc:[64,89,99,102,158,160],acceler:[3,4,5,6,15,16,17,18,19,21,22,36,38,51,63,64,65,69,76,82,83,84,90,91,106,109,124,139,141,142,147],accelerator_registri:92,accept:[28,64,103,107],access:[0,3,5,13,18,19,22,33,37,41,43,44,46,47,49,53,63,64,72,74,88,91,92,93,103,108,128,134,140,141,145,148,155,160,161],accommod:19,accomplish:[69,71],accord:[5,48],accordingli:[19,28,29,104],account:[20,41,42,44,45,46,47,48,49,70],accumul:[19,55,62,64,72,89,102,103,108,157],accumulate_grad_batch:[22,62,64,65,139,142],accur:[5,67],accuraci:[19,20,48,64,69,89,157],achiev:[6,16,17,20,22,72,84,108,140,148],across:[0,4,6,8,11,12,19,20,28,32,36,38,39,43,49,50,55,62,64,65,67,72,74,84,85,88,89,90,92,93,102,106,107,108,136,140,146,147,148,149,152,157,158],act:11,action:[30,87,140,148,152,154],action_nam:153,actioncountprofil:153,activ:[12,20,36,64,65,70,84,103,104,107,139,142,146],active_learning_loop:103,activelearningdatamodul:103,activelearningloop:103,actual:[5,6,18,38,64,92,96,97,98,99,100,101,103],ad:[0,11,19,28,29,64,72,101,105,149],adam:[19,22,28,31,32,64,65,74,139,142,144,145,147],adamw:[19,28,32,90],add:[0,6,9,18,19,27,28,29,31,32,36,38,43,47,55,63,64,65,72,73,74,77,82,87,90,91,92,93,100,101,102,103,107,123,124,138,147,157,158,159],add_argparse_arg:[63,72],add_argu:[29,31,63,72],add_argument_group:63,add_arguments_to_pars:[28,29],add_class_argu:29,add_command:36,add_confusion_matrix:84,add_dataloader_idx:[64,102],add_figur:[102,155,160],add_help:36,add_histogram:[64,102,155,160],add_imag:[64,102,155,160,161],add_lightning_class_arg:29,add_lr_scheduler_arg:28,add_model_specific_arg:63,add_optimizer_arg:28,add_predict:84,add_slurm_cmd:36,add_stat:157,addhandl:[57,102],addit:[8,9,19,22,29,58,63,64,69,97,99,100,102,140,148],addition:[4,19,21,22,28,61,71,72,140,145,148],addmm:154,address:[6,12,16,17,29,38,64,98],adjust:[19,22,39,57,64,69,72,102],adl:[52,71,157],adlf:71,advanc:[1,6,7,9,10,14,31,51,64,65,66,72,73,75,88,108,109,111,112,113,116,117,118,121,128,129,131,132,141,142,143,147,150,152,155,156,160,161],advancedprofil:[72,152],advantag:[6,8,28,64,69],adventur:84,advis:26,advprop:88,affect:[19,43,108],after:[0,6,15,16,17,18,19,22,26,27,29,62,63,64,65,72,74,83,84,93,97,99,101,102,103,108,139,142],after_:29,after_fit:29,after_save_checkpoint:[99,101],against:[24,87],agg_and_log_metr:[96,97,98,100,101],agg_default_func:[97,99,100,101],agg_key_func:[97,99,100,101],aggreg:[6,64,96,97,98,100,101,157],agnost:[23,61,64,107,140,148,149],ago:107,ahead:[87,140,148],ai:[8,16,17,33,41,42,44,45,46,47,49,72,86,88,99,109,147],aim:19,aio_bench_perf_sweep:19,al:64,alert:87,algorithm:[6,19,22,64,72,108],alia:[16,17,101],alias:[4,72,101,106],align:87,all:[0,3,5,6,11,13,16,17,18,19,20,22,25,26,27,28,29,30,31,32,38,41,42,43,46,48,52,53,55,61,63,64,65,67,69,70,71,72,74,76,78,82,83,84,85,87,88,89,90,91,93,96,99,100,101,102,103,108,109,110,111,113,136,139,140,141,142,144,145,146,148,149,152,155,157,160,161],all_gath:[0,140,148],all_pr:64,all_result:6,all_test_pr:64,all_test_step_out:64,allevi:19,allgather_bucket_s:19,alloc:[19,22],allow:[4,6,9,12,16,17,18,19,20,21,27,28,29,36,37,38,39,49,53,61,63,64,65,72,74,77,80,81,99,102,105,107,108,140,148],allreduc:[6,19],almost:19,alon:64,along:[8,53,64,65,74,84,103,107],alpha:[16,17],alreadi:[0,19,29,31,32,42,64,72,97,98,101,102,103,109,140,148,155,158,160,161],also:[0,4,6,9,11,13,18,19,20,21,22,23,27,28,29,31,36,45,46,52,53,55,61,63,64,67,68,69,70,71,72,74,78,80,83,84,85,87,97,99,101,102,103,106,107,108,140,146,148,149,159],alter:[64,74,129],altern:[28,64,65,103,107],although:[42,67,69,149],alwai:[28,29,64,67,72,108],am:[140,148],amaz:[19,84],amazon:71,amazonaw:[45,46,103],amdim:85,among:[6,15,20,140,148],amount:[19,20,22,32,38,72],amp:[64,65,69,72,139,142,147],amp_backend:69,amp_level:69,amper:69,an:[3,4,5,6,9,11,15,16,17,22,27,28,29,30,33,41,43,45,46,49,55,56,60,61,62,64,65,72,73,77,84,86,89,93,96,97,98,99,100,101,102,103,104,106,107,108,109,133,139,140,142,148,149,152,154,157,158,160],an_imag:158,analys:[10,115],analysi:[13,84],analyt:152,analyz:101,anchormod:11,ani:[0,1,3,4,6,7,10,11,12,14,15,16,17,19,20,21,22,27,28,29,31,40,41,45,46,47,55,58,61,64,65,71,72,74,77,80,82,83,84,87,90,92,93,96,97,98,99,100,101,102,103,107,115,134,139,140,142,146,148,149,153,155,157,159,160,161],annot:11,anomali:72,anonym:[99,101,155,160,161],anoth:[20,29,63,64,77,103,108,152],another_paramet:[53,160],answer:[84,86],ant:103,any_extra_hook:149,any_flag:64,any_lightning_module_function_or_hook:[98,99,155,160,161],any_trainer_arg:72,any_trainer_flag:90,anymor:58,anyon:[45,46,49,50],anyth:[31,32,36,38,64,69,72,74,93,102,103,149,158],anywai:64,anywher:[0,6,74,98,99,102,108,139,145],apart:61,apex:[6,53,65,139,142],api:[6,19,27,29,51,54,61,65,84,88,89,97,99,138,140,148,155,156,160,161],api_kei:[97,99,155,160,161],appear:[30,99],append:[6,64,71,72,102,157],appli:[6,15,16,17,18,19,20,22,30,64,65,71,72,74,84,88,99,102,140,148],applic:[4,6,16,17,84,88,106],apply_on:29,apply_to_collect:[64,74],approach:[48,52,56,64],appropri:[6,9,18,36,39,72,98,104],apt:84,ar:[0,3,4,5,6,8,12,13,15,16,17,19,20,21,22,23,24,28,29,30,38,39,41,42,44,45,46,47,48,49,53,55,56,58,61,63,64,65,67,71,74,76,77,78,84,85,88,92,93,94,96,97,98,99,100,101,102,103,105,107,108,139,140,141,142,145,146,148,149,151,157,158],arbitrari:[0,22,28,29,64,72,74,92,104,139,140,142,148],arbitrarili:64,architectur:[8,11,12,15,16,17,18,69,84,108,145],archiv:146,area:[69,109],aren:[16,17,65,72],arg1:[6,39,64],arg2:64,arg3:64,arg:[6,26,28,31,32,39,45,46,53,64,72,74,77,93,94,96,97,98,101,102,103,107,140,148,157,159,160],argmax:[64,84],argpars:[29,30,31,53,72,102],argument:[4,6,8,9,12,16,17,19,21,22,26,29,30,31,32,41,45,53,55,63,64,65,70,72,74,77,78,84,90,92,96,97,98,99,100,101,102,103,105,107,108,139,140,141,142,147,148,154,155,156,157,158,159,160,161],argumentpars:[27,31,36,72],arithmet:69,around:[19,22,82,84,104],arrai:[77,84,101],art:[2,7,15,66,85,116,132,138,141,147],artifact:[98,101,155,161],artifact_dir:101,artifact_loc:98,artifici:12,arxiv:64,as_imag:99,as_strid:154,asic:[16,17],ask:[1,14,25,30,40,74,95,149],aspect:[64,72,84,91,110],asr_model:84,asrmodel:84,assert:[72,157],assign:[4,6,16,17,21,64,74,96,100],assist:72,associ:[8,28,43,61,64,65],assum:[19,39,64,72,74,109],asteroid:86,async:6,asynchron:[6,135,154],atla:86,attach:[45,46,64,103],attempt:[55,72],attent:[72,86],attention_mask:[23,84],attn:[23,84],attribut:[0,22,29,43,63,64,65,72,77,100,104,108],audienc:[2,3,4,6,8,9,11,12,13,15,16,17,18,23,26,29,31,32,33,38,41,42,45,46,47,48,49,50,53,55,57,60,67,68,69,76,77,78,80,81,82,83,144,151,152,153,154,157,158,159,160],audio:[84,86,90,101,158],audio_eltyp:84,audio_sign:84,audio_signal_len:84,audiosign:84,augment:[63,64,72,74,84],augmentor:84,author:6,author_email:6,auto:[4,8,19,22,64,72,92,102,106,140,148,157],auto_device_count:[72,92],auto_encod:[56,64,83,149],auto_lr_find:22,auto_registri:28,auto_requeu:36,auto_resum:41,auto_scale_batch_s:22,auto_select_gpu:3,auto_wrap:19,autocast:69,autoencod:[23,53,56,64,144,147,149],autoencoderprod:83,autoencodersystem:[83,149],autograd:[72,154],autom:[36,64,65,72,87,103,140,148],automat:[0,3,6,18,19,22,27,28,29,30,36,41,42,47,48,53,61,63,64,69,70,72,74,89,95,96,97,99,100,101,103,105,107,139,140,141,142,145,148,152,158,160],automatic_optim:[65,108,139,142],automaticargsmodel:64,autoreport_dir:13,autoscal:22,autosummari:77,autotoken:84,autotun:6,avail:[3,6,16,17,19,28,29,31,32,43,61,63,64,65,71,72,74,84,88,93,96,100,107,139,140,142,148,158],available_backbon:88,available_devic:92,available_head:88,available_pretrained_weight:88,averag:[6,19,24,64,72,81,82,103,158],average_valu:158,avg:154,avoid:[0,22,28,29,55,59,60,64,73,77,91,102,109,119,123,138,140,147,148],aw:[19,41,44,46,49,52,71,157],awai:[84,107,144],awar:[0,20,64],awk:[16,17],awscheckpoint:[141,147],az:71,azur:[52,71,87,157],b0:88,b:[65,84,101,107,108],b_float32:69,baal:103,back:[16,17,63,64,74,76,90,98,103,107,108],backbon:[23,28,29,55,88,90,103,140,148,149],backend:[4,6,64,71,72,106,140,148],background:[16,17,33,36,37,38,39,49,88,91,128,135],backprop:[64,107],backpropag:64,backpropog:64,backward:[6,19,22,62,65,68,72,93,103,107,139,142,144,147,154],bad:[5,28,29,58,64,108],baguarun:6,baguastrategi:6,balanc:[19,84],bandwidth:[12,108],bar:[64,72,73,91,109,110,112,156,158],bard:159,barrier:[74,76],base:[4,6,8,12,19,22,23,28,30,43,48,51,55,58,64,69,72,74,84,89,91,93,95,96,97,98,99,100,101,103,104,105,106,108,110,114,140,148],base_cl:32,baselin:88,basepredictionwrit:64,baseprofil:72,basepruningmethod:20,bash:36,basi:19,basic:[1,7,10,14,33,40,42,45,46,51,59,64,66,73,75,109,115,116,119,120,121,122,130,134,138,147,150,156],batch:[0,5,6,11,12,16,17,19,20,29,31,41,43,48,55,56,58,60,61,62,64,69,70,72,74,76,77,78,81,82,83,84,85,89,93,94,98,99,101,102,103,107,108,139,140,142,144,145,147,148,149,157,158],batch_a:107,batch_a_b:107,batch_b:107,batch_c:107,batch_c_d:107,batch_cifar:64,batch_d:107,batch_first:64,batch_idx:[0,6,43,55,56,58,60,64,65,69,72,76,81,82,83,84,85,89,93,98,99,101,102,103,107,108,139,140,142,144,145,147,148,149,157,158,159],batch_loop:103,batch_mnist:64,batch_nb:[31,84],batch_part:64,batch_progress:70,batch_siz:[5,9,16,17,18,22,29,43,48,64,65,72,74,84,99,102,103,107,108,139,142],batch_split:[6,64],batchnorm1d:77,batchnorm:[64,72],batteri:147,beauti:[70,112,159],becaus:[6,12,19,23,30,38,41,48,64,65,69,97,99,107,149],becom:[19,31,32,43,58,77,84,108,140,148,152],bee:103,been:[11,15,19,20,22,28,29,55,60,61,64,72,74,93,98,99,107,140,148],befor:[0,6,9,11,18,22,26,27,28,29,30,31,36,43,52,58,61,62,64,65,72,74,77,84,93,100,101,103,107,139,142],before_:29,before_fit:29,beforehand:28,begin:[6,32,64,72,77,93,96,97,98,100,101,103,108,140,148],beginblock:11,beginn:[38,65,88,108,139,142],behalf:[41,44,46,49,144],behav:[6,145,158],behavior:[27,30,36,51,52,64,72,73,93,103,112,129,140,148],behaviour:[0,58,64,102,157],behind:[19,36,149],being:[6,12,22,29,30,55,58,61,64,72,84,93,94,96,97,98,101,103,149],believ:[43,58],bellow:24,belong:[64,74,101,140,148],below:[3,4,6,9,11,19,22,29,36,39,54,55,64,72,74,87,89,102,105,106,140,148,154],benchmark:[19,43,61,64,84,85,107,109,149],benefici:[19,20,108],benefit:[19,22,28,47,58,64,69,89,90,103,105,140,148],bert:[84,149],bert_model:84,bertmnlifinetun:23,bertmodel:23,best:[0,19,29,30,33,46,48,53,61,64,72,77,78,91,94,101,103,108,128,129,135,145,149],best_model:[64,82,83],best_model_path:55,beta:[19,20,140,148],better:[6,18,22,29,58,67,69,149,151],between:[4,8,12,16,17,19,20,22,24,32,38,39,55,64,72,74,84,106,149,159],bewar:103,beyond:58,bf16:[9,69,72,140,148],bf16_file_path:9,bfloat16:[18,67,72,108,140,148],bfloat:[140,148],bia:15,bias:[19,84,101],bidirectionalrnn:64,big:64,bigger:[19,91,118,128,132],bin:[6,36],binari:[22,71,72],bind_al:84,binsearch:[22,72],bio:84,bit:[9,19,53,61,64,85,105,140,148],bite:109,bitsandbyt:69,bitwidth:20,blackhc:22,bleed:[113,143,146,147],blob:64,block:[11,19,101,102,108,140,148,157],block_1:19,block_2:19,blog:[84,109],blogpost:69,bodi:[140,148],boi:84,boilerpl:[73,82,89,109,147],bolt:[86,93],bonu:32,bool:[37,61,64,72,92,97,99,100,101,103],bootstrap:85,boring_class:31,boringdatamodul:[31,32,140,148],boringmodel:[43,152],both:[19,22,28,29,33,43,60,64,65,72,84,97,101,102,108,139,142,149],bottleneck:[6,16,17,19,67,72,73,91,108,109,123,132,149],bottom:[16,17],bought:84,bound:[19,101],boundari:108,box:[32,64,69,74,90,101,146],branch:[12,84,87],breakdown:154,breakpoint:76,bridg:8,bring:[69,84],broadcast:[4,106,140,148],broadcast_tensor:154,brows:151,browser:151,bu:[72,140,148],bucket:[27,46,72],buffer:[0,19,108],bug:[72,77,146],bui:[41,44,46,49],build:[0,12,64,73,74,84,85,90,103,108,109,150,157],built:[0,4,8,12,19,47,63,64,65,72,78,88,105,106,107,140,148,152,154,159],builtin:152,bump:19,burden:29,button:151,byol:86,bytegrad:6,c10d:6,c:[27,31,72,81,99,107,109,146,147,154,155,160,161],c_float16:69,cach:[64,84,93,101,102],calc_all_result:64,calcul:[19,55,64,89,107],call:[6,12,15,16,17,19,22,28,31,43,53,58,60,61,62,63,64,65,72,74,77,80,84,87,92,93,97,99,100,101,102,103,107,108,139,140,142,148,152,153,157,158,160],call_hook:103,callabl:[20,64,108],callback:[0,20,22,43,52,53,54,55,61,62,63,64,65,70,77,90,99,101,104,108,109,112,140,141,147,148,152,159],callback_1_args_1:28,callback_1_args_2:28,callback_1_nam:28,callback_metr:[64,65,139,142],callback_n_args_1:28,callback_n_nam:28,callback_registri:28,callback_st:93,camerargb:88,cameraseg:88,can:[0,3,4,6,8,9,11,12,13,16,17,18,19,20,21,22,23,26,27,28,29,30,31,32,36,38,39,41,43,45,46,47,48,49,52,53,54,55,56,57,58,60,61,62,63,64,65,67,68,69,70,71,72,74,76,77,78,80,81,83,84,85,87,88,91,92,93,94,96,97,99,100,101,102,103,104,105,106,107,108,123,136,139,140,141,142,144,145,146,148,149,151,152,154,157,158,159],cannot:[0,6,16,17,19,22,43,55,58],capabl:[11,28,99,158],capac:152,capit:84,caption:[101,155,160,161],captur:[43,57,72,93,152],capture_stdout:99,car:[26,103,158],care:[28,64,65,102,103,107,108,139,140,142,148,157],carlo:[64,81,82],carri:[16,17,20,103],cast:[9,64,69,140,148],cat:6,categori:108,caus:[16,17,30,140,148],cc:[16,17],cd:[6,27,46],cell:[16,17,102],central:[6,43],certain:[6,19,27,29,57,58,64,67,72,102,157],cfg:84,challeng:[140,148],chanc:[64,69,78,93],chang:[0,4,16,17,18,19,20,21,22,28,29,30,31,32,38,52,53,54,55,56,57,58,64,65,72,73,76,77,87,90,92,93,101,102,103,140,141,148,158],channel:[87,158],chart:[24,102,157,158],cheap:41,cheaper:18,check:[5,8,15,16,17,19,28,29,58,64,70,72,74,84,85,93,99,102,103,140,145,146,148,149],check_finit:58,check_on_train_epoch_end:58,check_val_every_n_epoch:[58,64,108],checkout:[9,108,149],checkpoint:[0,4,6,21,36,61,63,64,71,72,73,74,77,84,91,93,94,95,96,97,99,100,101,102,104,105,106,109,110,122,127,129,140,145,147,148],checkpoint_callback:[55,99,101],checkpoint_io:[4,21,54],checkpoint_path:[55,61,64],checkpoint_refer:101,checkpoint_wrapp:19,checkpointio:[4,21,54,71,72],chees:101,child:[77,109],children:[23,103],chip:[8,9],choic:[16,17,22,45,46,58,63,65,102,108,140,148],choos:[4,6,18,22,32,48,64,84,93,94,102,106,107,140,148],chosen:[22,72,88],chunk:[6,18],ci:[24,27,146],cifar10:74,cifar10classifi:23,cifar10datamodul:[74,85],cifar5:[45,46,48],cifar:[23,43,63,64],cifar_load:64,circuit:[16,17],ckpt:[19,52,53,55,61,64,71,72,82,83,101,140,147,148],ckpt_callback:72,ckpt_path:[19,26,27,31,52,53,61,71,72,104],cl:[84,92],clara:84,class_3:26,class_balanc:84,class_path:[28,29],classif:[29,48,84,86,89,93,103],classifi:[23,24,45,46,48,52,71,84],classificationtask:64,classmethod:[64,88,92],clean:[72,74,93,149],clear:[16,17,30,64,65,72,102,139,142,157],cli:[25,27,28,29,32,36,37,38,39,49,73,84,91,92,109,111,124,126,136],click:[16,17,151],client:[16,17,99,155,160,161],clip:[12,64,72,78],clip_grad_norm_:22,clip_grad_value_:22,clip_gradi:[64,65],clock:154,clone:[30,45,46,84],close:[4,64],closur:[32,64],cloud:[5,14,18,20,36,37,38,39,40,42,44,51,71,73,91,102,109,110,114,117,126,128,156],cluster:[3,8,33,40,41,72,73,91,92,109,124,128,140,148],cluster_email:63,clusterenviron:[37,72,140,148],cmd:36,cnn:[24,31],code:[0,1,5,7,9,10,12,14,16,17,18,19,26,28,29,30,31,32,38,45,46,49,55,64,65,72,73,74,90,91,92,93,99,101,102,103,108,109,110,112,115,117,134,139,141,142,144,149],codeown:87,coef_x:149,colab:[6,72,84,101,146,155,158,160,161],collabor:[47,91,128,149],collaps:102,collate_fn:107,colleagu:74,collect:[6,8,19,43,64,72,74,84,85,89,90,93,102,103,107,140,148,149,153],color:159,column:101,com:[6,16,17,22,29,45,46,63,64,84,103,146,159],combin:[19,28,36,48,69,70,72,84,87,103,107,108,140,148],combined_load:107,combinedload:107,come:[18,19,22,43,60,63,64,74,84,88,100,103,140,144,145,148,155,160,161],comet:[97,102],comet_api_kei:97,comet_experiment_kei:97,comet_logg:[97,102,155,160,161],comet_ml:97,comet_rest_api_kei:97,comet_workspac:97,cometexperi:97,cometlogg:[102,155,160,161],comma:3,command:[5,6,16,17,18,19,28,29,30,31,36,39,45,46,48,63,84,102,108,146,154,158],commandlin:[30,31,32,45,46,91,123,147],comment:[31,36],commit:[30,105],common:[15,22,24,26,29,43,48,54,60,71,90,99,155,160,161],commonli:[16,17],commun:[0,1,4,8,11,36,38,39,41,44,46,49,64,73,85,91,92,102,106,109,110,123,124,128,140,147,148,157],compact:[28,29],compani:[33,47,48,49,84,126,135],compar:[16,17,19,69],comparison:43,compat:[19,20,53,61,65,84,89,139,142],compens:[6,19],compil:[12,16,17,64],compiletim:[16,17],complementari:103,complet:[4,19,20,23,28,29,53,61,64,65,69,106,107,152,159],complex:[19,25,26,29,30,31,32,42,43,53,59,64,74,86,88,113,125,136,140,143,144,148,157,160],complianc:47,compliant:5,complic:[45,46,47,144,145],compon:[19,52,70,86,90,92],compos:[25,51,64,74,84,91,103,110,111,114,140,148],composit:[77,90],compress:[6,19,20,79,131],comput:[0,3,6,12,16,17,19,20,22,35,38,43,48,64,65,67,69,72,84,89,100,109,133,147,149,157,158],computation_cli:[16,17],compute_amount:20,compute_loss:[65,139,142],con:108,concat_dataset:107,concatdataset:107,concaten:[140,148],concept:[19,29,84,93,94],concern:22,conda:[18,36,72,99,108,109,147,155,160,161],conda_env:63,condit:[58,64,103,129,140,148],condition:72,conduct:[69,113,143],conf:84,config1:27,config:[23,28,73,84,87,90,91,97,101,102,109,110,125],config_nam:84,config_path:84,configur:[3,6,8,16,17,18,19,22,26,27,28,31,32,36,37,38,39,47,55,57,61,64,69,70,72,73,86,87,90,91,93,99,109,110,139,140,142,148,155,157,160,161],configure_callback:72,configure_column:70,configure_ddp:[4,6,72,106],configure_gradient_clip:65,configure_optim:[6,19,22,28,31,32,65,74,139,140,142,144,145,147,148,149],configure_sharded_model:19,conjunct:[61,107],connect:[18,25,32,36,38,39,42,47,72,92,103,108,125,140,148],consid:[27,63,65,67,72,108,139,140,142,148],consider:[19,65,69],consist:[12,37,64,84],consol:[22,109,140,148],constant:[12,43],constraint:22,construct:[18,19,22,108],constructor:[6,96,99,100,101],consum:67,consumpt:[15,19,22,43,67,108],contact:[41,44,46,47,49,87],contain:[3,19,53,64,72,74,84,93,96,97,98,100,101,112,140,144,146,148],content:[63,64,140,148],context:[11,16,17,19,64,68,78,108,140,148],contextlib:108,contextmanag:108,contextu:86,contiguous_gradi:19,continu:[43,53,64,85,87,140,148],contrast:[28,29,36,67],contribut:[84,85],contributor:72,control:[4,11,19,22,25,26,30,31,32,55,64,69,72,73,97,103,106,111,113,129,136,140,143,147,148],conv_mask:84,convasrencod:84,conveni:[63,64,72,99,140,148],convent:12,converg:[5,8,22,65,72,91,108,128,130,139,142],convert:[27,64,84,92,109],convert_spectrogram_to_audio:84,convert_zero_checkpoint_to_fp32_state_dict:19,convnet:31,convolut:[16,17,84,86],cool:[6,108],copi:[6,15,19,26,38,83,93,94,108],copy_:154,copy_model_to_gpu:6,core:[4,7,8,12,14,15,18,57,64,67,69,72,84,102,103,106,108,116,134,140,148],corpor:[47,149],correct:[6,18,19,36,64,72,74,78,85,102,103,107,140,148,157],correctli:[19,55,140,148],correspond:[16,17,22,30,53,61,64,72,84,104,107,140,148],corrupt:[64,74],cosineann:64,cosineannealinglr:32,cost:[19,22,33,47,108,135,154],cotatron:86,could:[12,16,17,18,28,29,30,64,70,71,102,103,104,108],count:[43,58,74,103],counter:[16,17,93,94],coupl:[28,29,52,149],cours:[74,102,107],cover:[55,140,148],cp37:[16,17],cp37m:[16,17],cp:108,cpcv2:85,cprofil:152,cpu:[0,4,6,12,15,16,17,19,21,22,49,54,64,67,69,72,74,76,84,85,87,92,106,140,146,148,154],cpu_checkpoint:19,cpu_count:108,cpuacceler:72,crash:77,creat:[0,6,11,16,17,19,20,22,28,29,30,36,47,49,53,55,62,64,65,66,70,74,80,81,83,84,87,88,89,91,95,97,98,99,101,102,107,112,127,128,140,148,159],create_checkpoint_callback:84,create_tensorboard_logg:84,creates_processes_extern:37,creation:43,creativ:158,credenti:[47,48,49,72],credit:[41,44,46,49],criterion:[65,85,108,139,142],cross:[103,140,148],cross_entropi:[55,64,145],crucial:69,csv:[96,99],csv_log:96,csvlogger:102,ctc:84,ctrl:72,cuda:[3,19,36,64,69,72,84,108,140,148,154],cuda_awar:19,cudnn:72,cumtim:152,cumul:152,curl:45,current:[11,12,13,16,17,19,22,41,53,58,61,64,72,74,84,87,89,92,93,96,99,102,103,107,108,109,140,145,148,157,158],current_epoch:[65,139,142],current_iter:103,current_train_batch_index:[74,95],custom:[0,10,11,12,20,21,22,25,28,37,39,47,51,55,57,58,62,64,68,71,72,73,74,84,89,90,91,101,103,105,109,110,113,124,125,127,128,140,143,148,150,158],custom_checkpoint_io:[4,21,54],custom_column:70,custom_processing_step:153,custombatch:[64,74],customcallback:28,customcheckpointio:[4,21,54],customdatamodul:74,customdataset:107,customddpstrategi:[4,72,106],customfitloop:103,customiz:[31,72,84],customprecisionplugin:68,customprogressbar:[102,157],customrichprogressbar:70,customvalloop:103,cut:67,cvpr:86,cycl:64,cython:84,d:[19,39,47,55,64,65,84,107,108,139,142,153,159],d_loss:[65,108,139,142],d_opt:[65,108,139,142],d_x:[65,108,139,142],d_z:[65,108,139,142],dai:[49,72,77,84,108],dangl:64,dash:30,dashboard:[158,160],data:[5,9,11,16,17,18,19,22,23,26,27,28,29,30,31,32,43,45,46,52,53,61,63,64,65,67,71,72,74,77,82,84,86,89,93,96,97,100,101,102,103,119,138,140,144,147,148,152,153,157,158],data_dir:[22,26,31,45,46,48,74,84],data_load:82,data_modul:64,data_path:63,data_queu:43,data_root:108,data_sourc:107,data_split:64,databas:64,datafram:101,dataload:[0,5,6,8,16,17,18,22,32,60,64,72,74,82,84,102,103,140,144,145,147,148,152,157],dataloader_i_output:64,dataloader_id:64,dataloader_idx:[64,74,82,93,107],dataloader_out:64,dataloader_output:64,dataloader_output_result:64,datamodul:[9,22,26,28,52,53,61,72,85,88,91,99,103,107,108,128,136,140,148,152],datamodule_class:32,datamodule_registri:32,dataparallel:6,dataparallelstrategi:108,dataset:[5,6,18,23,25,41,47,56,60,61,64,70,72,74,77,78,84,85,90,91,101,103,108,128,136,140,145,147,148,149],datastor:[45,46,48],datastore_nam:[45,46,48],datatyp:[16,17],datetim:72,dcgan_faces_tutori:[65,108,139,142],dd:72,ddp2:[5,64],ddp:[4,5,8,18,21,22,36,38,55,61,62,64,65,72,74,76,83,84,102,106,140,148,157],ddp_comm_hook:19,ddp_comm_stat:19,ddp_comm_wrapp:19,ddp_custom_checkpoint_io:[4,21],ddp_find_unused_parameters_fals:[4,21],ddp_shard:[5,19,140,148],ddp_sharded_spawn:[140,148],ddp_spawn:[4,5,6,22,64,72,106,108,140,148,149],ddp_train:6,ddpshardedstrategi:108,ddpspawnstrategi:[19,22],ddpstrategi:[4,6,19,21,72,106,108],de:19,deactiv:[140,148],deadlock:[0,55,64,102,157],deal:[47,67,78,107,108],debug:[4,19,21,36,72,108,109],debugging_messag:76,decay_factor:99,decentr:6,decid:[39,64,119,140,148,149],declar:[11,61,107],decod:[23,28,53,56,60,64,83,84,144,147,149],decoder_lay:[27,28],decoder_weight:53,decor:[55,64,81,102],decoupl:[22,107,149],decreas:[20,22],deep:[3,6,8,16,17,19,22,67,69,77,86,88],deeper:[6,129],deeplabv3:88,deeplabv3plu:88,deepspe:[4,5,21,55,64,65,90,106,118,139,140,141,142,147,148],deepspeed_config:19,deepspeed_stage_1:19,deepspeed_stage_2:[19,141,147],deepspeed_stage_2_offload:[4,19,21],deepspeed_stage_3:19,deepspeed_stage_3_offload:[4,19,21],deepspeedcpuadam:19,deepspeedstrategi:[19,54,108,140,148],def:[0,4,5,6,11,15,18,19,20,21,22,23,28,29,31,32,36,37,43,52,53,54,55,56,58,60,61,63,64,65,69,70,72,74,76,77,80,81,82,83,84,85,89,92,93,94,95,98,99,101,102,103,104,106,107,108,139,140,142,144,145,147,148,149,153,155,157,158,159,160,161],default_backbon:[28,29],default_config_fil:27,default_hook:19,default_hp_metr:[100,102],default_project:97,default_root_dir:[52,53,71,102,158],defaultdict:153,defaultloop:103,defin:[4,6,11,18,19,21,28,29,38,39,56,61,63,64,65,72,74,84,87,93,94,95,99,100,101,103,105,107,139,140,142,147,148,149],definit:[0,12,37,48,107,140,148,149],degrad:19,degre:61,delet:[19,64,100],deliv:69,demo:[31,32,45,46,101,103,155,160,161],demomodel:[31,32],denomin:64,densenet121:88,depend:[22,29,31,36,38,43,45,46,48,49,72,74,79,83,84,88,93,96,97,98,101,102,105,107,108,122,149,157],deploi:[1,20,67,73,91,109,127,128,147],deploy:[20,47,79,108,131],deprec:[64,72,74,93,140,148],depth:[91,124,138,156],deriv:[29,65,72],descent:103,describ:[6,11,19,22,26,28,29,39,64,87],descript:[4,6,21,29,30,54,64,70,92,99,102,103,109,140,148,157,159],design:[3,11,16,17,28,29,33,38,49,63,84,85,93,104,107,149],desir:[28,29,31,32,36,65,93,108,139,142],destroi:[16,17],detach:[64,65,102,108,139,142,157],detach_:19,detail:[3,6,9,16,17,19,22,24,29,30,31,36,41,43,64,71,72,74,87,91,99,107,108,109,124],detect:[6,8,36,48,72,84,86,107,152],detect_anomali:[72,78],detectron2:19,determin:[22,29,54,60,64,97,100,102,103],determinc:19,determinist:19,dev:[84,108],develop:[1,2,4,8,16,17,28,29,30,33,41,53,80,91,107,108,123,127,128,149,158],devic:[0,4,6,8,9,11,12,13,15,16,17,18,19,20,21,22,36,38,39,54,55,61,62,63,65,69,74,76,82,83,84,89,92,97,98,99,101,102,106,139,141,142,147,157],device_count:[3,72],device_id:[4,72,106],device_iter:11,device_pars:[16,17],device_typ:69,deviceiter:11,devicestatsmonitor:[8,72,77,152],devot:77,diagnos:[16,17],dict:[4,6,21,28,43,61,64,72,74,84,92,93,96,97,98,99,100,101,102,107,108],dict_arg:63,dictconfig:84,dictionari:[22,28,29,61,62,64,65,72,74,93,96,97,98,99,100,101,102,107,160],dictionary_of_load:107,did:[6,74],didn:64,die:8,differ:[1,6,11,12,16,17,19,22,29,32,48,53,54,56,58,61,62,63,64,65,66,69,70,71,72,73,74,81,84,93,94,103,105,107,118,130,140,146,148,149,158],differenti:149,difficult:43,dig:[51,138,156],dilat:84,dim:[6,11,23,64,81,82,84,89],dimens:[31,64,103],dir:[74,101],direct:[18,108],directli:[4,11,19,22,28,32,41,44,46,49,54,63,64,72,74,81,83,93,99,101,102,103,106,108,155,157,160],directori:[13,19,53,72,96,97,98,99,100,101,102],dirnam:36,dirpath:[55,72,152,153],dirti:149,dis_opt:64,dis_sch:64,disabl:[19,28,58,64,70,72,77,78,99,101,159],disadvantag:108,disambigu:[93,94],disc_loss:64,discard:19,discourag:[6,93],discoveri:87,discret:146,discrimin:[64,65,108,139,142],discuss:[19,84],disk:[55,64,72,74,84,96,100,108,140,148,157],displai:[12,64,70,77,100,101,159],dist:[16,17,157],dist_reduce_fx:157,dist_sync_on_step:157,distilbert:84,distinguish:[93,94],distribut:[0,1,4,5,8,16,17,22,35,36,37,38,39,51,53,61,62,64,65,71,72,73,74,75,89,91,92,102,106,108,109,118,124,127,133,144],distributed_forward:6,distributeddataparallel:[4,6,19,72,106,140,148],distributedsampl:[0,61,64,72],div:[36,37,38,39,45,46,48,49],div_:154,dive:[14,117],diverg:[140,148],divergence_threshold:58,divid:64,dl1:[8,107],dl2:107,dl:8,dm:[9,61,64,74,88],doc:[8,19,64,65,69,70,72,99,102,139,142],docker0:36,docker:84,document:[6,11,12,15,19,27,29,30,60,64,65,72,85,88,90,99,101,108,139,142,149,155,160,161],doe:[6,16,17,18,19,22,28,36,43,58,64,65,72,97,98,99,103,107,108,149],doesn:[5,6,18,22,55,62,65,69,71,93,108,149],domain:[84,85,88,107,149],don:[0,3,16,17,18,22,28,31,36,37,48,53,54,64,65,72,78,102,103,108,140,146,148,157,158,159],done:[0,11,15,16,17,19,22,28,29,30,61,64,72,107,140,148,149],doubl:[19,22,67,72,140,148],doubledistilbert:86,down:[0,30,41,72,102,108,149,152,157],download:[13,16,17,18,22,45,46,60,64,74,84,101,107,140,144,147,148],download_data:[64,103,140,148],downsid:108,downstream:[0,84],downward:22,dozen:48,dp:[5,22,61,62,64,65,74,140,148],dp_out:64,draft:87,drastic:19,drive:[19,158],driver:6,drop:[20,64,83,99,140,148,149],drop_prob:64,dropdown:151,dropout:[53,64,81,82,84,103],dtype:69,due:[6,11,12,19,22,108,140,148,152,154],dummi:[45,93],dump:[0,5,13,22,53,64],duplic:[64,72],durat:[72,84,151,152],dure:[0,4,6,13,16,17,19,20,21,22,41,43,53,60,61,64,65,69,70,72,77,84,101,102,108,139,140,142,145,148],dynam:[13,55,64,69],e:[4,6,11,12,16,17,22,28,29,30,61,62,63,64,65,72,74,93,94,96,97,98,99,100,101,102,103,104,106,108,140,148],each:[0,6,8,16,17,18,19,22,25,26,28,30,31,36,38,39,43,48,53,61,62,63,64,65,72,77,84,93,101,102,103,107,108,109,125,139,140,142,148,149,153,154,157],earli:[72,77,109,119,140,148],earliest:72,early_stop:[58,64],early_stop_callback:58,early_stopping_callback:63,earlystop:[28,29,64,108],eas:[29,70],easi:[16,17,56,64,84,87,89,107,149],easier:149,easiest:82,easili:[9,28,36,37,38,39,41,84,86,99,101,140,141,148],ec2:8,ecosystem:84,edg:[20,89,113,143,146,147],edit:99,effect:[5,15,20,49,62,64,65,72,73,93,94,98,109],effici:[8,20,22,64,72,108,149,157],efficientnet:88,effort:[19,140,148],eg:[22,62,64,65,74,93],egg:84,eight:[108,140,148],either:[8,22,30,54,61,64,72,74,84,93,97,101,107,108,154,159],elaps:100,eleg:103,element:19,elif:[20,31,32,63,64,65,74],elimin:[20,73,109],els:[22,64,65,72,74,84,93,96,97,100,101,102,140,148],email:[29,42,47,63],emb:147,embed:[64,72,147,149],emit_nvtx:154,emot:86,emploi:[19,69],empti:[30,96,100,154],emptibl:41,empty_cach:108,empty_lik:154,empty_strid:154,en:84,enabl:[2,6,7,8,12,13,16,17,18,20,22,25,29,32,38,40,41,43,47,51,53,58,63,64,66,67,69,70,71,72,73,78,81,84,86,91,100,101,103,104,108,110,111,113,116,119,121,123,127,128,129,130,132,138,140,143,145,148,154,156,158,159],enable_checkpoint:[53,63],enable_graph:[64,102],enable_model_summari:77,enable_progress_bar:70,encapsul:[43,54,74],encdecctcmodel:84,encod:[23,28,53,56,60,63,64,83,84,86,144,145,147,149],encoded_len:84,encoded_length:84,encoder_lay:[27,28,29,63],encoder_output:84,encoder_weight:53,encount:[22,72],encourag:[30,74],end:[0,6,22,31,36,43,53,55,58,64,70,72,74,84,86,93,97,101,102,103,108,154,157],endpoint:20,energi:20,enforc:64,enforce_sort:107,engin:[8,26,47,57,72,78,80,81,91,93,103,123,144],english:101,enhanc:108,enough:[29,91,93,94,108],ensembl:103,ensur:[0,11,12,15,19,39,43,55,64,69,72,74,108,140,148,152,157],enter:[30,140,148,151],enterpris:[27,38,47,79,80,81,131],entir:[19,22,53,58,84,93,108],entiti:101,entri:[20,28,29,64],enumer:[64,103,140,144,148],env:[6,18,27,36,72,108,146],env_pars:27,environ:[6,16,17,19,25,29,36,37,38,39,40,41,42,47,53,60,72,79,80,81,97,98,99,102,104,108,111,131,140,146,148,157,158],ep:19,epoch:[19,20,22,24,28,30,31,32,41,43,53,55,61,62,65,70,72,82,85,93,94,102,103,139,140,142,147,148,158],epoch_loop:103,epoch_metr:64,equal:[19,30,108],equip:84,equival:[3,8,48,63,64,72,74,103,140,148],errd:[65,108,139,142],errd_fak:[65,108,139,142],errd_real:[65,108,139,142],errg:[65,108,139,142],error:[0,6,12,16,17,19,22,29,38,57,64,101,102,140,148],error_handl:30,es:72,esoter:65,especi:[3,67,72,149],essenti:[12,22,43,69,84,93,94],estim:[20,22,72,103],estimated_stepping_batch:65,et:64,eta:47,etc:[6,16,17,20,22,43,53,55,61,63,64,65,68,72,74,77,84,97,99,101,102,103,107,139,140,142,147,148,149,152],ethernet:8,eval:[23,53,64,82,83,145,147],evalu:[4,16,17,19,22,30,31,61,64,69,72,84,103,106,147,149],evaluate_from_fil:84,evaluationepochloop:103,evaluationloop:103,even:[0,19,22,41,46,47,53,61,64,65,107,139,142],event:[41,72,84],eventu:149,ever:[6,74,77],everi:[6,19,22,27,30,53,55,58,62,63,64,65,70,72,74,84,95,96,101,102,103,139,142,144,157,158],every_n_epoch:55,every_n_training_step:55,everyth:[6,41,43,53,72,76,84,102,109,140,148],everywher:47,evolv:87,ex:101,exact:[30,43,55,72,74,83,107,149],exactli:[6,41,64],exampl:[0,3,4,6,9,11,12,15,16,17,19,20,22,27,28,29,36,37,39,43,45,46,49,53,55,56,57,58,63,65,69,72,74,77,82,84,88,92,94,96,97,98,99,100,101,102,104,107,108,139,142,147,149,153,160],example_imag:64,example_input:64,example_input_arrai:[77,80,100],except:[6,16,17,19,30,38,72,93,101,103,107,145,155,160,161],excess:[140,148],exchang:6,exclus:[3,11,72,108],execut:[8,9,11,16,17,29,64,65,72,74,77,93,104,140,148],exist:[4,22,72,96,97,98,100,101,103,106,109],existingcometexperi:97,exit:[20,30,31,108],exp:99,exp_dir:84,exp_manag:84,expand:154,expect:[19,22,28,43,74,103],expens:43,experi:[6,12,29,30,36,37,38,39,45,46,61,64,72,73,78,96,97,98,99,100,101,102,108,109,121,129,161],experiment:[4,12,16,17,21,29,41,54,69,90,92,104],experiment_id:98,experiment_kei:97,experiment_nam:[97,98,155,160,161],experimentwrit:96,expert:[1,2,25,33,35,40,48,49,51,65,66,78,84,92,106,109,113,125,126,127,135,139,142,143,147,150,156],expertis:[65,91,109,139,142],explain:[19,29,102,103],explan:[22,64,99,108],explicit:[16,17,30,61,145,149],explicitli:[6,63,64,72,74,101,102,145,157],explod:22,explor:[73,91,110,114,117,124,128,132],exponentiallr:[28,64],export_param:[64,80],expos:[4,19,43,92,104,106],express:103,extend:[4,25,28,29,54,64,72,73,84,86,91,92,93,99,106,124,126,154],extens:[29,105],extra:[19,31,43,64],extra_parameters_her:22,extract:[56,64,84,102,107,140,148],extractor:[23,56,64],extrem:[19,58,64,84],ey:0,f:[20,55,56,60,64,76,84,89,92,140,144,145,148,153,159],face:[140,148],facebook:[4,72],facilit:12,factor:[53,65,158],factori:86,fail:[0,30,31,41,72,96,98,99,100,101,102],failur:[19,30,41,43,140,148],fairli:19,fairscal:90,fake_data:18,fake_imag:[155,160,161],fake_image_batch:147,fake_label:[65,108,139,142],fakedataset1:32,fakedataset2:32,fall:[69,98],fals:[4,9,15,21,28,31,36,53,58,60,64,65,70,72,74,77,84,97,99,100,101,102,107,108,139,140,142,148,157,159],familiar:103,fanci:103,fancier:64,fancyadam:32,faq:[1,14,25,40],far:107,fashionmnistdatamodul:85,fast:[16,17,22,43,79,88,131,149],fast_dev_run:[31,63,77],faster:[3,6,8,12,19,22,49,66,67,72,73,77,91,108,128,130,132,157],fasterrcnn:86,fastest:103,fault:[6,33,47,53,91,104,109,124,135,140,148],favor:93,fc:23,fc_dropout:84,feat_dim:32,feat_in:84,featur:[6,19,22,23,28,29,41,43,55,56,64,81,88,96,97,98,99,100,101,104,114,138,140,146,148],feature_extractor:23,feedback:[19,140,148],feel:[6,55,72,83,84,140,148],fetch:[43,72,107,152],few:[0,6,19,30,31,45,46,63,64,72,77,78,84,93,102,103,107,131],fewer:108,ffmpeg:84,fiction:92,field:22,fig:22,figur:[5,22,30,63,140,148,149,152],file:[0,16,17,22,25,28,30,31,32,38,45,46,48,52,55,57,63,64,71,72,74,87,93,94,96,97,98,99,100,101,102,140,144,148,149,152,155,157,160,161],file_path:[64,81],filehandl:[57,102],filenam:[55,152,153,154],filepath:[72,80,102],filesystem:[27,52,109,114],fill_:154,filter:[84,108],filter_channel:84,filter_channels_dp:84,filterwarn:108,final_block:19,final_metr:64,final_valu:64,financ:84,find:[0,3,6,19,22,33,46,48,53,55,72,73,77,91,93,99,103,108,109,128,129,132,135],find_packag:6,find_unused_paramet:[4,21],finder:72,fine:[15,45,46,55,84,88,90,93,103],finetun:[22,23,73,84,103,109],finish:[16,17,29,60,72,74,96,98,99,100,101,102,103,151],finit:69,firewal:[38,39],first:[3,6,8,16,17,19,27,28,29,38,39,40,41,42,45,46,48,63,64,65,70,72,74,84,97,99,102,103,107,149,153,155,157,159,160,161],fit:[6,9,11,13,16,17,18,19,20,22,23,26,27,28,29,30,31,32,36,41,52,53,54,55,56,58,60,63,64,71,74,77,83,84,92,93,99,103,104,140,144,145,147,148,152,153,157],fit_loop:[64,103],fitloop:103,five:[16,17,74,140,148],fix:[0,6,12,19,72,78,87,108,146],flag:[3,9,11,13,16,17,18,19,27,36,58,63,64,102,107,108,109,147,157],flash:[45,46,86,109],flatten:[23,69],flavor:86,flavour:19,flexibl:[65,69,90,103,139,142],float16:[20,69],float32:[69,72],flow:[19,38,39,93,103,149],flush:[72,96,100,102,159],flush_logs_every_n_step:[96,102,157],flush_sec:100,fly:103,fn:[16,17,72],fname:84,focus:[4,20],fold:103,folder:[46,87,88],follow:[0,4,5,6,16,17,18,19,26,28,29,30,31,36,38,41,43,45,53,63,64,65,72,74,80,87,89,90,93,94,96,97,98,99,100,101,102,103,106,107,108,139,140,142,144,145,146,148,151,154,155,158,160,161],foo:[64,72],footprint:[22,69,72,108,140,148],forc:[6,41,72,108,154],forecast:86,forg:[99,109,146,147,155,160,161],forget:[0,36,159],fork:87,forkingpickl:0,form:[12,19,99,101],format:[3,9,16,17,19,26,29,31,43,64,69,70,72,80,96,99,100,155,159,160,161],former:64,forward:[0,6,11,15,19,20,22,23,31,43,56,62,65,68,69,72,76,80,81,83,84,97,100,103,107,139,140,142,144,145,147,148],found:[16,17,19,22,29,39,53,64,82,97,99,102,103,105],four:[70,84],fp16:19,fp16_compress_hook:19,fp16_compress_wrapp:19,fp32:[9,69],fp32_file_path:9,fp:0,fpn:88,fraction:[20,72,77],fragment:19,frame:64,framework:[6,8,30,63,69,88],free:[6,16,17,19,36,37,38,39,41,44,45,46,49,55,72,82,83,158],freed:19,freez:[23,88,103],french:101,freq:84,frequenc:[58,64,65,101,139,142],frequent:[1,14,25,40,103],friction:90,friend:77,from:[4,6,9,11,12,13,15,16,17,19,20,21,22,24,25,28,29,30,31,36,37,39,41,43,46,54,55,58,60,61,62,64,65,67,70,71,72,73,74,76,77,78,85,86,88,91,92,93,94,96,97,98,99,100,101,104,105,106,107,108,109,123,136,139,140,142,144,145,147,148,149,151,152,153,154,155,157,159,160,161],from_:88,from_argparse_arg:[63,72],from_fold:[88,103],from_pretrain:[23,84],fromag:101,fsdp:[4,19,118,141],fsspec:[52,71,109,157],fuel_capac:26,full:[5,6,11,16,17,18,19,26,27,30,31,53,61,64,72,77,84,85,93,102,103,105,140,144,148,149,152,153,155,158,160,161],full_out:6,fulli:[4,5,6,8,11,12,16,17,20,29,47,53,84,103,106,140,145,148],function_to_debug:77,further:[11,20,58,69],furthermor:[28,29,107,140,148],fusedadam:19,futur:[12,19,43,64,74,87,97,140,146,148],g:[4,16,17,22,28,29,61,64,65,72,74,93,94,96,98,99,100,101,102,104,106,108,139,140,142,148],g_loss:[65,108,139,142],g_opt:[65,108,139,142],g_x:[65,108,139,142],gain:[13,26,113,129,143],galaxi:107,game:3,gamma:28,gan:[32,63,64,85,103,108,149],ganmodel:61,gantask:64,gather:[6,19,64],gaudi:[9,91,110],gb:[45,46],gc:[52,71,157],gcloud:[16,17],gcp:19,gcsf:71,gear:84,gen:84,gen_discrimin:28,gen_imag:64,gen_loss:64,gen_opt:64,gen_optim:28,gen_sch:64,gener:[16,17,22,26,28,29,35,36,38,43,48,60,61,64,65,72,74,77,84,93,97,99,103,108,133,139,140,142,148,153,154,157,158],generate_spect:84,generate_spectrogram:84,generated_imag:[155,160,161],generator_network:63,geograph:84,geometr:[22,86],geopolit:84,get:[0,6,8,12,16,17,19,22,28,43,47,53,64,67,70,72,77,78,82,84,88,96,97,98,99,100,101,102,103,105,108,134,140,144,148,149],get_device_stat:92,get_input:80,get_lm_model:84,get_metr:[102,157],get_ordin:18,get_parallel_devic:92,get_pretrained_lm_models_list:84,get_tokenizer_list:84,get_train_batch:152,getcwd:[18,64,72,74,102,144,147],getlogg:[57,102],git:[46,84],github:[6,22,46,55,64,84,87,146,149,159],give:[16,17,29,30,48,53,55,64,78,93,102,103,157],given:[19,27,28,29,30,64,72,74,93,97,101,103,104,108],gke:146,global:[22,29,37,53,64,72,78,140,148],global_rank:[37,76,140,148,157],global_step:[55,65,102,103,158],global_zero:64,gloo:[4,6,106],glossari:12,glow:84,glow_tt:84,glowtt:84,glowttsmodel:84,glue:84,go:[16,17,28,48,49,58,78,103,149],goal:63,goe:[64,102,111,140,148],gone:158,good:[0,22,28,29,30,36,64,65,78,108,139,140,142,148,149],goodgan:63,googl:[6,52,71,72,84,101,146,155,157,160,161],googleapi:[16,17],got:[140,148],gpt2:85,gpt:[19,85],gpu:[0,12,15,16,17,18,21,33,36,38,39,49,63,64,67,69,73,74,76,82,83,84,85,87,91,92,106,109,110,128,134,141,146,147,152],gpu_0_batch:6,gpu_0_pr:64,gpu_0_predict:64,gpu_1_batch:6,gpu_1_pr:64,gpu_1_predict:64,gpu_2_batch:6,gpu_3_batch:6,gpu_model:6,gpu_n_pr:64,gpu_num:6,gpu_transform:[64,74],gpuacceler:72,grace:72,grad:[22,62,64,65,69,72,103,147],grade:84,gradient:[6,12,62,64,68,69,72,101,103,108,140,148,155,160,161],gradient_allreduc:6,gradient_as_bucket_view:19,gradient_clip_algorithm:[22,64,65,72,78],gradient_clip_v:[22,64,65,78],gradientaccumulationschedul:[22,62,65],grain:[55,103],granular:[129,154],graph:[6,12,16,17,22,64,86,97,100,101,102,108,155,157,160],graphcor:[12,13],graphic:3,gre:36,great:[48,64,101,103,108],greater:[72,99],greedy_predict:84,green1:[70,159],green_yellow:[70,159],grep:[16,17],grey82:70,grid:[33,36,37,38,39,41,42,44,47,49,64,109],grid_search:36,griffin:84,ground:[19,24,103],group:[6,18,28,29,64,72,100,101,102,141,157],grow:[31,32,74,84],gs:71,guarante:[30,85],guess:149,guesswork:22,guid:[3,5,6,8,12,15,16,17,18,24,30,38,47,64,72,84,86,97,99,103,108,109,146,147,151],guidanc:22,h:[6,23,27,31,64,153],h_cl:23,ha:[0,6,8,11,12,15,16,17,19,20,22,23,26,28,29,30,38,43,45,46,48,49,53,55,60,61,63,64,69,72,77,78,81,84,89,90,93,98,99,100,102,103,107,108,140,148,149,152,158],habana:[8,9],habit:0,had:58,hadoop:71,half:[13,67,69,72,73,109,130,140,148],hand:149,handi:81,handl:[0,4,16,17,19,22,27,28,41,42,43,52,64,65,69,71,72,74,84,103,106,107,139,140,142,144,148,149,157],hang:[140,148],happen:[22,61,62,64,65,69,72,97,108,140,148,154],happi:55,hard:[11,103,108],harder:22,hardwar:[1,3,4,6,7,8,10,14,16,17,41,64,72,73,91,92,106,115,124,134,145],hasattr:84,hasn:[28,61],havana:[91,110],have:[0,5,6,8,9,12,13,15,19,22,23,24,26,28,29,30,31,32,39,41,42,43,47,48,52,55,61,63,64,65,72,74,80,81,84,85,92,93,97,99,101,103,105,107,108,109,140,141,145,146,148,149,159],haven:107,hccl:8,hdf:[71,72],head:[84,88,103,146],healthcar:84,heavili:12,height:158,hello:158,help:[0,3,13,19,22,27,28,29,30,31,47,55,61,63,64,65,78,84,91,108,128,130,141,149,151,152],helper:[39,147],henc:[18,108],here:[0,6,18,19,20,22,23,27,28,32,36,37,38,43,45,46,48,49,57,61,62,63,64,65,69,72,74,77,82,84,91,92,93,94,95,102,103,105,107,108,123,139,140,142,148,149,153,154,155,158,160,161],heterogen:8,hf:90,hh:72,hidden:[64,103,107],hidden_channel:84,hidden_s:[23,64],hidden_st:[64,84],hierarch:[64,99],high:[12,19,88,90,108,157],higher:[6,19,67,105,140,148,157],highest:19,highli:[12,19,55,67,84],hint:[28,29,30,145],histogram:[20,101,102,155,160],hit:19,hmp:9,hoc:149,hold:[4,21,22,55,64],home:[16,17],hood:[6,18,19,20,64,65,69,102,103,105,107,108,109,144],hook:[6,15,58,61,65,72,74,94,101,102,103,104,139,145,147,149],horovod:[5,140,148],horovod_fusion_threshold:72,horovodrun:6,host:[11,16,17,18,19,22,27,108],host_list:6,host_node_addr:6,hostname1:6,hostname2:6,hot:[107,149],hour:[5,49,72,84,108],how:[1,3,18,19,22,25,28,29,30,31,33,35,36,38,40,45,46,48,49,54,60,61,63,64,71,72,74,89,91,92,93,96,99,100,103,104,107,108,109,114,120,121,123,125,126,128,134,135,136,140,144,147,148,149,152,156,158],howev:[0,6,15,19,22,29,41,43,44,46,49,58,63,64,65,67,69,72,93,94,103,108,139,142,149,154,158],hp:102,hp_metric:[100,102],hparam:[22,36,53,55,61,63,65,72,74,102],hparams_fil:[61,64],hpu:[72,73,91,92,109,110,147,152],hpu_exampl:9,hpuparallelstrategi:8,hpuprecisionplugin:9,html:[65,101,108,139,142],http:[6,16,17,22,45,46,64,65,84,99,103,108,139,140,142,146,147,148,151,159],huge:[5,64],huggingfac:[23,84,86,90],hundr:[140,148],hunt:149,hydra:[84,86,90],hydra_runn:84,hymenoptera_data:103,hyper:[36,99,101],hyper_paramet:[53,64,83,102],hyperoptargumentpars:36,hyperparam:[36,99,102],hyperparam_optim:36,hyperparamat:48,hyperparamet:[29,30,31,36,48,61,64,72,73,83,96,97,98,100,101,108,109,140,148,155,159,161],hyperparametersmixin:64,hypothesi:20,i:[11,12,20,22,30,48,54,62,63,64,65,89,97,103,108,140,147,148],id:[3,11,37,38,97,98,101,140,148],idea:[36,138,141,149],ideal:30,idempot:64,ident:[140,148],identifi:[4,16,17,21,30,72,84,93,98],idx:92,ie:[0,2,6,22,41,60,61,62,63,65,72,78,108,157],ignor:[19,63,64,65,72,99,100,108,139,142],illustr:[6,29,93,94],imag:[23,48,56,64,72,86,88,93,99,101,102,103,120,121,147,155,156,157,160,161],image_classification_model:103,image_s:88,imageclassifi:103,imageclassificationdata:103,imagefold:107,imagegpt:85,imagenet:[5,32,63,74,77,88],imagenetdatamodul:[74,85],imagenettransferlearn:23,imagin:[56,64,158],img1:101,img2:101,img:[72,99],img_1:101,img_2:101,immedi:[58,84,103,149],impact:[0,19,72],implement:[16,17,19,20,22,28,29,30,43,60,64,65,70,74,78,83,85,87,89,92,93,94,100,102,103,104,105,107,108,139,140,142,148,149,157],imposs:[28,29,30,74],impract:22,improv:[11,19,20,22,29,58,63,64,67,69,72,108,140,148,149,151,157],in_dim:53,in_featur:[23,64,80,81],inbuilt:[15,65],includ:[4,6,8,16,17,19,20,21,28,29,30,52,54,64,65,71,72,77,84,93,94,102,140,147,148,152,157],include_extern:84,incompat:100,incorrect:99,increas:[6,11,13,19,20,22,43,62,65,69,72,89,101,108],independ:[15,22,27,29,55,62,65,80,147,149],index:[3,16,17,22,43,62,64,65,74,102,103,109,140,148,157],indic:[3,43,64,92,103,157],individu:[12,22,64,72,102],indivis:72,industri:[49,91,128],inf:72,infer:[4,11,20,30,31,43,61,72,79,80,84,102,109,131,140,148,149,157],inference_opt:11,inferencesess:80,infin:72,infinit:[58,70,72,108],info:[36,38,72,84,99,102,108,146],inform:[0,4,6,8,11,12,15,16,17,19,21,28,30,37,47,57,63,64,69,74,84,93,94,102,105,154,159],infrastructur:[84,105],inherit:[56,64,102],init:[6,9,23,28,29,30,55,56,58,61,63,64,83,101,155,157,159,160,161],init_arg:[28,29],init_dist_connect:6,init_process_group:157,init_validation_tqdm:[70,159],initi:[6,9,16,17,22,28,29,53,58,60,64,65,72,89,93,102,107,108,140,145,148,157],initial_num_label:103,initialis:[4,21],initialize_distribut:64,inject:[55,63,91,103,110,139],inlin:26,inner:64,inp:81,inpaint:86,input:[3,16,17,19,20,29,61,64,72,80,84,93,97,100,101,140,148],input_arrai:[97,100,160],input_compat:20,input_id:[23,84],input_length:84,input_mask:84,input_nam:80,input_s:64,input_sampl:[64,80],input_sign:84,input_signal_length:84,input_spec:84,input_typ:84,input_type_id:84,insert:[16,17,18,64],insid:[22,28,53,55,64,72,74,84,93,102,108,140,145,148,157,158],insight:[129,151],inspect:[22,64,77,96,100],inspir:22,instabl:[69,72],instal:[6,16,17,18,19,27,30,31,38,64,70,71,72,74,81,85,88,89,90,97,98,99,101,103,109,147,155,159,160,161],install_requir:6,instanc:[6,8,18,28,29,61,64,72,93,94,99,101,102,104,145],instanti:[19,29,30,63,102,103,140,148],instantiate_class:28,instantli:64,instead:[6,9,19,22,30,36,52,53,55,58,63,64,65,72,93,96,99,100,103,108,139,140,142,148,149,157,158],instruct:[8,12,16,17,87,149,151],int8:20,integ:[3,64,72,77,84],integr:[4,8,13,16,17,19,35,52,55,68,85,91,92,99,103,105,124,127,141,155,156,157,160,161],intellig:[12,91,110,115,116],intend:[19,28,29],intent:84,interact:[33,45,46,49,63,91,92,99,103,108,128,144,149],interconnect:19,interest:[23,48,64,65,104,158],interfac:[4,8,15,16,17,19,30,31,36,37,63,89,90,93,103,104,105,106],intermedi:[1,7,10,14,19,25,33,35,36,37,51,59,66,75,77,79,109,115,116,117,118,121,122,129,132,133,135,136,138,150,156],intern:[8,9,16,17,19,22,43,52,53,61,62,65,69,70,71,72,89,93,94,98,103,104,105,139,142,149,157],interoper:[140,148],interpol:[29,93],interpret:3,interrupt:[41,53,72,93,108],interv:[6,55,64,103,107,139,142],intro:84,introduc:[19,32,69],introduct:[69,149],introductori:109,intuit:69,invalid:69,invert:84,invok:[22,64],involv:[64,74],io:[4,21,64,72,84],ip:[6,16,17],ipu:[4,64,65,67,72,73,91,92,106,108,109,110,116,139,140,142,146,148,152],ipu_id:11,ipustrategi:[11,13],ipython:108,irregular:86,is_avail:[92,140,148],is_complex:154,is_global_zero:[0,140,148],is_last_batch:[65,108,139,142],is_last_batch_to_accumul:108,isfil:64,isinst:[64,65,74,84,139,142],isn:145,isol:93,issu:[0,5,6,12,16,17,19,30,38,55,64,87,102,108,157],item:[16,17,64,102,107,157,158],iter:[6,11,19,20,22,55,64,72,81,82,103,104,108,141,147,149],iterable_dataset:107,iterabledataset:[43,72,107,108],its:[4,6,8,22,26,28,29,30,43,45,46,53,58,64,72,92,93,103,106,107,140,148,149,153,155,157,159,160,161],itself:[4,38,41,43,64,65,106],jasper:84,jit:[64,81],job:[6,36,47,91],job_id:6,job_nam:36,join:[73,84,87,91,96,100,109,110,123,124,128,147],jpg:[101,103],json:[19,31,84],jsonargpars:[28,29,31],jupyt:[6,16,17,102,108,158],just:[22,28,30,45,46,53,55,64,74,87,93,108],k:[3,6,16,17,22,55,62,64,65,72,77,102,103,108,157],kaggl:[6,146,158],kaggler:88,kaushikbokka:[16,17],keep:[19,22,29,43,64,69,72,78,87,99,103,149],keepdim:84,kei:[16,17,22,29,53,62,63,64,65,72,83,84,93,94,96,97,98,99,100,101,102,103,107,139,142,152,155,160,161],keithito:84,kept:[64,157],kernel:[16,17,84],key1:101,key2:101,keyboard:72,keyboardinterrupt:[72,93],keyword:[64,96,97,98,100,101],kfold:103,kill:[16,17],kind:[28,31,32,140,148],kind_devic:[16,17],know:[0,19,29,48,58,63,64,74,102,108,149,158],knowledg:109,known:[28,29,67],known_implement:71,kwarg:[53,63,64,74,77,93,94,96,97,98,100,101,102,103,157,159,160],kxn:[22,62,65],l1:[31,64,80,81,144],l1_unstructur:20,l:36,l_length:84,l_mle:84,lab:[33,72,91,108,128],label:[84,101,103],label_epoch_frequ:103,labels_fil:84,labels_hat:64,labelstyp:84,lambda:[0,16,17,53,102],lambdalr:64,landscap:22,languag:[20,86],language_model:[84,90],laptop:72,larg:[3,5,6,8,12,16,17,19,20,22,62,64,65,67,78,84,92,108,140,148],larger:[1,11,12,19,22,48,67,69,77,127,140,148],largest:[22,72],last:[11,16,17,26,53,55,64,72],last_batch_iter:19,latent:93,later:[0,19,55,63,64,69,99,101],latest:[18,36,64,72,81,84,101,141,144],latter:64,launch:[4,5,6,37,84,106,140,148,158],launcher:6,layer1:11,layer2:11,layer3:11,layer4:11,layer:[1,11,15,19,20,23,29,36,48,63,69,72],layer_0:20,layer_0a:20,layer_1:[0,15,20,63,145,149],layer_1_dim:63,layer_1a:20,layer_2:[15,145,149],layer_3:[15,149],layer_:20,layer_dim:63,layer_end:20,layersync:72,lazi:74,lazy_inst:[28,29],lazyproperti:[16,17],lbfg:64,lead:[0,12,16,17,18,19,55,64,67,102,108,151,157],leaf:103,learn:[1,3,6,7,8,10,12,14,16,17,18,19,24,25,26,28,29,30,32,33,35,36,37,38,39,40,43,45,46,48,49,51,53,59,63,64,67,69,71,72,73,75,77,78,79,80,81,86,87,88,89,90,91,92,102,103,105,108,109,110,112,113,115,116,117,118,119,120,121,122,123,125,126,128,129,131,132,133,134,135,136,138,143,147,149,150,152,156,158],learner:103,learning_r:[22,26,27,31,36,48,53,63,64,65,72,84,140,148,160],learning_rate_1:31,learning_rate_2:31,learningratelogg:72,learningratemonitor:[28,64,77],least:[3,6,11,30,38,58,72,108],leav:[12,64,70,82,149],left:[28,30,65,103,139,142,151],legaci:145,leisur:103,len:[16,17,43,60,64,84,103,153],length:[15,43,61,64,72,84,107],length_scal:84,lengthi:77,lengthstyp:84,less:[8,11,19,43,67,72,84,107,108],let:[5,6,22,23,26,45,46,48,53,61,62,63,64,65,74,92,103,140,148],level:[6,19,22,28,57,58,63,69,72,73,78,88,102,103,110,123,124,128,147],leverag:[19,84,103],lib:[0,16,17],librari:[8,15,16,17,18,19,32,38,69,70,71,72,84,86,92,93,140,148,159],librispeech:84,libsndfile1:84,libtpu:[16,17],lifecycl:13,light:[42,48,91,110,113],lightn:[0,2,3,4,6,8,9,13,15,16,17,18,19,20,21,23,24,25,27,28,29,32,33,36,37,38,39,41,42,43,44,47,49,52,53,54,55,56,57,61,64,65,66,68,69,70,71,72,73,78,79,82,84,86,87,91,92,93,94,97,99,100,102,104,105,106,108,110,111,113,122,123,124,126,127,138,139,142,143,144,149,155,157,158,159,160,161],lightning_checkpoint:102,lightning_log:[19,72,96,97,98,100,102,147,155,158,160,161],lightning_model:19,lightning_modul:[56,64,103],lightning_script:41,lightningargumentpars:29,lightningcli:[27,28,31,32],lightningdatamodul:[22,25,28,31,61,72,109,136,140,145,148,149],lightninglit:109,lightningloggerbas:[28,72,96,97,98,99,100,101,159],lightningmodel:[140,148],lightningmodul:[0,4,5,6,11,15,16,17,19,20,22,25,28,29,31,36,38,43,52,55,56,58,60,61,65,69,72,74,76,80,81,83,84,89,93,96,97,98,99,100,101,103,104,106,108,109,113,122,136,138,139,140,142,143,145,148,152,153,155,156,158,160,161],lightningoptim:[64,65,108,139,142],lightningtemplatemodel:36,lightweight:87,like:[0,6,16,17,19,22,27,28,29,30,31,32,36,39,43,48,52,53,54,55,57,61,63,64,69,70,71,72,73,74,77,84,91,92,93,97,99,100,101,102,103,104,105,107,108,113,124,128,140,143,148,149,152,154,157,158,159],lim:84,limit:[0,5,6,16,17,19,20,55,64,67,72,81,108,140,148,158],limit_:107,limit_predict_batch:72,limit_train:72,limit_train_batch:[77,147],limit_val_batch:[77,107],line:[0,6,15,16,17,19,28,29,30,31,36,63,64,77,84,108,140,141,148],linear:[11,15,19,20,23,31,63,64,77,80,81,103,140,144,145,147,148,149],linear_lay:19,linearli:22,linearlr:32,linearregress:85,lineno:152,link:[6,16,17,28,29,46,72,87,103,140,148],link_argu:29,link_to:28,linux:[84,87],linux_x86_64:[16,17],list:[3,4,6,9,16,17,22,23,28,43,55,61,64,72,74,83,84,87,101,102,103,105,106,107,140,148,152,155,160,161],list_available_model:84,list_of_load:107,listcomp:152,litadam:32,litautoencod:[55,56,60,64,144,147],litcallback:52,litclassifi:[9,64,74],litdatamodul:[22,74,95],lite:147,liter:64,litlogg:159,litlrschedul:32,litmcdropoutmodel:[64,81,82],litmnist:[63,64],litmodel:[0,5,22,43,52,53,55,58,61,63,64,69,76,77,82,98,99,107,139,145,149,155,157,158,160,161],litmodul:101,litprogressbar:[70,159],littl:[88,90],live:149,lj:84,ljspeech:84,ll:[19,28,39,42,45,46,48,53,112,130,131,132,133,152],lm:84,lm_checkpoint:84,lo:36,load:[12,19,22,36,43,51,52,53,54,55,60,61,63,64,71,72,73,74,81,83,84,93,94,97,101,102,104,108,109,122,145,157],load_checkpoint:[4,21,54],load_data:64,load_dataset:74,load_dataset_from_disk:74,load_from_checkpoint:[23,53,55,61,82,101,147],load_modul:36,load_nvprof:154,load_state_dict:[83,94,95],loader:[18,61,64,72,84,107],loader_1:107,loader_2:107,loader_a:[43,64,107],loader_b:[43,64,107],loader_c:107,loader_d:107,loader_n:64,loaders_a_b:107,loaders_c_d:107,loc:[53,102],local:[0,8,16,17,19,22,30,37,46,52,71,72,76,84,96,97,98,99,100,101,102,140,148,157],local_devic:[16,17],local_nvm:19,local_rank:[6,37,72,74,140,148],localhost:[6,16,17,18,147,151],localservic:[16,17,18],localsgd:19,locat:[22,98],lock:6,log:[12,22,43,52,55,56,58,60,61,63,71,72,84,85,89,93,96,97,98,99,100,101,108,109,129,140,145,147,148,154,155,156,158,159,160,161],log_cod:97,log_dict:[65,84,102,108,139,142,158],log_dir:[96,100],log_every_n_step:[102,157],log_freq:101,log_gpu_memori:72,log_graph:[97,100,101,160],log_hyperparam:[96,97,98,99,100,101,102,159],log_imag:[64,101,155,160,161],log_metr:[64,72,96,97,98,99,100,101,102,159],log_model:[101,155,160,161],log_model_checkpoint:99,log_model_summari:99,log_path:36,log_prob:84,log_tabl:101,log_text:101,logdet:84,logdir:[84,102,147,151,158],logger1:[155,160,161],logger2:[155,160,161],logger:[26,28,31,57,63,71,77,90,92,96,97,98,99,100,101,109,147,155,159,160,161],logger_registri:28,logging_interv:28,logic:[12,22,28,43,54,61,64,65,72,74,92,93,103,105,139,140,142,148,149],login:[6,45,46],logisticregress:85,logit:[0,23,84,85],logprobstyp:84,logw:84,logw_:84,longer:[22,43,64,151],look:[2,3,6,8,9,11,12,13,15,16,17,18,19,22,23,26,29,30,31,32,42,45,46,48,49,50,55,57,67,68,69,71,72,83,84,103,104,140,148,149],lookup:0,loop:[19,24,43,53,58,59,72,73,77,78,84,91,92,93,107,109,110,120,141,145,148,150],loss:[0,6,11,12,13,20,22,31,55,56,58,62,64,65,69,72,83,84,85,93,99,101,102,103,107,139,140,142,144,145,147,148,158],loss_fn:[140,148],loss_funct:[103,140,148],loss_fx:63,loss_mask:84,loss_valu:84,lost:[30,69],lot:[16,17,19,63,77,82,84,149],lotteri:20,low:[6,16,17,19,41,157],low_precision_decentr:6,lower:[16,17,19,20,55,67,69,105,108],lowest:22,lr:[6,19,28,31,36,53,64,65,72,74,84,99,139,140,142,144,147,148,149],lr_find:[22,72],lr_find_kwarg:72,lr_finder:22,lr_scale:[64,65],lr_schedul:[28,32,65,139,142,145],lr_scheduler_config:[64,65,139,142],lr_scheduler_registri:32,lr_scheduler_step:65,lrschedul:32,lsof:[16,17],lstm:[64,107],m60:49,m:[6,16,17,18,39,48,72,84,108,153],machin:[3,5,12,18,19,20,22,26,33,36,37,38,39,41,44,45,46,50,55,64,72,75,78,80,81,84,89,91,92,103,108,123,134,135,140,148],made:[19,43,64,97],magnitud:[19,22,78],mai:[0,4,6,16,17,19,22,36,41,63,64,65,69,72,84,87,102,106,107,108,139,142,157],mailto:[41,42,44,46,47,49],main:[6,16,17,22,26,27,30,31,32,36,39,48,55,63,64,70,72,74,84,103,108,140,148,149],main_address:[37,72,140,148],main_port:[37,72,140,148],mainli:[4,37,92,101,106],maintain:[6,19,69,72,93,146],major:[5,33,52,65,78,108,140,146,148,149,157],make:[3,5,9,12,16,17,19,20,22,28,29,30,32,36,40,42,43,53,55,60,61,62,63,64,65,72,74,84,88,91,105,107,108,113,124,139,140,142,143,145,148,149,151,156,159],make_grid:64,manag:[4,11,19,31,35,37,54,64,65,68,73,78,92,106,108,109,121,129,133,139,140,142,148,149,156,158],mani:[3,6,12,15,16,17,19,22,28,29,30,32,33,36,38,41,45,46,48,49,50,55,63,64,65,69,72,74,77,84,85,90,100,103,108,135,149,154,155,157,160],manifest_filepath:84,manual:[0,6,28,36,64,69,72,74,93,101,103,113,140,143,147,148],manual_backward:[65,108,139,142],manual_se:[60,140,148],manuallyargsmodel:64,manualoptim:103,map:[43,64,72,107],map_loc:[53,61,64,102],mappingproxi:[16,17],mark:84,mask:[86,99,101,103],massiv:[2,10,11,14,19,117],master:[64,91,110,146],master_addr:[6,38,39],master_address:37,master_port:[6,36,37,38,39],match:[15,19,31,32,64,74,91,105,107,108,140,148],materi:[140,148],math:8,mathemat:[3,78],matrix:[8,16,17],matrix_approximation_rank:19,matter:[6,28,64,140,148],max:[55,58,64,72,101,108,153,158],max_depth:[72,77,99],max_epoch:[15,16,17,26,27,64,84,88,99,103,108,140,141,147,148,153],max_len:153,max_lr:65,max_passeng:26,max_queu:100,max_seq_length:84,max_siz:6,max_size_cycl:[72,107],max_spe:26,max_step:[27,52,64,71,84,103,108],max_tim:108,max_trial:22,maxim:77,maximum:[12,22,72,78,149],mayb:74,mc_iter:[64,81,82],md:64,mean:[0,3,6,11,16,17,19,22,28,29,30,38,64,69,72,81,82,85,102,108,140,146,148,152,154,157,158],meant:[29,93,94],measur:[43,96,97,98,99,100,101,154],mechan:[5,28,65],media:101,medic:86,meet:[140,148],megatron:84,mel:84,melspectrogramtyp:84,mem:36,memlock:84,memori:[8,9,15,16,17,20,22,62,64,65,66,67,69,72,73,84,90,107,108,109,130,140,148,152,157],mention:[18,30,64,87],merg:[20,64],messag:[15,28,29,30,31],met:58,meta:[4,103,109,113,143,147],metadata:[16,17,99,107,155,160,161],method:[0,4,6,15,22,29,32,38,43,53,54,56,58,60,61,63,65,70,74,81,83,84,88,92,93,95,96,97,98,99,100,101,102,104,106,107,108,113,139,143,145,152,153,157,158,159],metric:[0,6,13,16,17,43,55,56,58,61,65,70,72,96,97,98,99,100,101,102,108,109,120,121,129,139,140,142,148,156,159],metric_1:102,metric_2:102,metric_attribut:64,metric_n:[64,158],metric_to_track:[64,65],metric_v:64,micro:[11,47],microsoft:[4,71,80],mid:[72,108],middl:[22,41],might:[6,16,17,19,29,30,36,63,64,72,74,93,102,103,140,148,149,157],migrat:[99,113,143],millisecond:151,mimic:[20,72],min:[55,58,64,65,108,158],min_:72,min_delta:58,min_epoch:[27,108,141,147],min_max:72,min_siz:[6,72],min_step:[27,103,108],mind:[12,69],mingpt:19,mini:22,minibatch:5,minim:[9,19,20,28,65,90,139,140,142,148],minimum:[19,22,69,72,103,108,140,148,157],minut:[48,64,72],misbehavior:6,miscalcul:[140,148],misclassified_imag:99,misconfigurationexcept:[16,17,64,74,97,101],miss:[8,16,17,93,94],mississippi:84,mistak:30,mit:72,mix:[7,10,22,25,31,64,66,72,84,91,102,115,116,128,132,136,140,144,147,148,157],mk1:146,mkldnn:69,ml:[49,88,97,98],mle:83,mlf_logger:[98,155,160,161],mlflow:[98,102],mlflow_tracking_uri:98,mlflowlogg:[155,160,161],mlp:[93,99],mlrun:98,mm:[69,72],mnist:[9,12,18,22,24,32,55,60,63,64,74,86,101,140,144,147,148,152,155,160,161],mnist_ful:74,mnist_load:64,mnist_predict:74,mnist_test:74,mnist_train:74,mnist_val:74,mnistdatamodul:[9,22,74],mobil:20,moco:86,mocov2:85,modal:90,mode:[0,3,5,6,18,22,38,55,58,64,65,72,97,99,101,102,107],model1:[32,61],model2:[32,61],model:[1,2,3,4,5,6,8,9,10,12,13,14,15,16,17,18,20,22,23,25,26,27,29,33,35,36,37,38,39,41,42,43,47,48,52,53,54,55,56,58,63,64,65,66,67,69,70,71,72,73,74,86,89,90,91,93,94,97,99,100,101,102,103,106,107,109,110,111,115,116,117,118,119,120,123,127,128,130,132,133,134,136,139,140,141,142,145,147,148,153,155,156,158,161],model_averaging_period:19,model_backbon:48,model_backward:152,model_config:27,model_copy_gpu_0:6,model_copy_gpu_1:6,model_copy_gpu_2:6,model_copy_gpu_3:6,model_di:64,model_gen:64,model_nam:[63,84],model_registri:32,model_trac:64,model_weight:83,modelcheckpoint:[51,54,55,64,72,99,101,102],modelprun:20,modelpt:84,modelsummari:[72,77],modern:[72,108,140,148],modif:[30,72,103],modifi:[5,6,9,19,54,58,64,72,74,91,93,103,107,110,112],modul:[0,6,15,22,28,29,32,36,56,57,61,72,74,77,81,84,93,101,102,108,109,136,140,145,147,148,149],modular:[26,31,91,128,145,149],modulenotfounderror:[97,98,99,101],modules_to_fus:20,molecul:101,moment:[16,17,22,28,29],monai:86,monei:[3,8,12,40,108],monitor:[29,32,55,58,64,65,72,101,102,108,129,158],monolingu:20,mont:[64,81,82],month:[41,63],more:[0,3,6,8,9,11,12,15,16,17,18,19,20,22,24,27,28,29,30,31,32,43,47,48,53,57,59,64,65,67,69,71,72,74,87,88,90,92,97,99,101,102,103,104,105,107,108,129,139,140,142,145,146,147,148,153,154,158,160],most:[6,12,16,17,19,24,30,53,54,64,65,69,71,72,99,108,139,142,152,158],mount:[19,108],move:[0,12,15,16,17,18,64,72,74,76,100,103,108,140,148],move_data_to_devic:[64,74],move_metrics_to_cpu:72,move_to_devic:[140,148],mp:6,mpi:[4,106],mpirun:6,mse_loss:[60,144,147,154],mseloss:[56,64],much:[6,19,22,64,69,72,74,84,85,93,108,152],multi:[1,7,10,14,36,38,39,47,64,72,74,84,115,116,134,140,147,148],multipl:[0,6,19,20,22,27,29,30,32,36,38,46,48,64,70,72,73,74,84,89,90,92,93,94,99,101,102,103,105,108,109,118,134,140,141,146,148,157,158],multiple_trainloader_mod:[72,107],multipli:[16,17,19],multiprocess:[0,22,108],must:[8,11,12,19,26,28,29,31,32,36,41,42,44,46,49,58,64,69,72,74,93,94,103,107,108],mutabl:[28,29],mutual:11,my:[48,55,72,84,147,159],my_bucket:[52,71,157],my_checkpoint:[53,61,72],my_cli_default:27,my_cool_pickable_object:[52,64],my_custom_act:153,my_data:108,my_datafram:101,my_dataload:20,my_early_stop:29,my_env:[63,146],my_exp_nam:96,my_fil:6,my_fit_default:27,my_loss:102,my_lr_arg:72,my_metr:[55,102],my_model:[16,17,18,100],my_new_hook:103,my_path:74,my_program:108,my_project:99,my_reduced_metr:0,my_saved_deepspeed_checkpoint:19,my_valu:22,my_workspac:99,myacceler:[4,106],myaccuraci:157,mybucket:72,myclassmodel:29,myclust:[72,140,148],myclusterenviron:37,mycod:28,mycustomdistributeddataparallel:[4,72,106],mydatamodul:[27,28,29,61,108],mydatamodulebaseclass:28,mydataset:[140,148],mydecod:28,myearlystop:58,myencod:28,myepochloop:103,myfancyloop:103,mygener:63,mygradientdescentloop:103,mylightningcli:[28,29],mylightningmodul:[11,13,16,17,18,53,55,61,64,72,160],mylogg:102,myloop:103,mymainmodel:[28,29],mymodel:[6,19,22,27,28,29,30,54,64,65,82,89,107,139,140,142,148,153],mymodelbaseclass:28,mymodelclass:22,mymodul:[155,160,161],myownacc:72,myprecisionplugin:[4,106],myprintingcallback:93,n:[6,22,55,62,64,65,72,84,139,142,147,153,157],n_batch:[64,89],n_critic:64,n_mel:84,n_optim:64,n_vocab:84,name:[6,28,29,45,46,61,63,64,72,77,80,87,92,93,94,96,97,98,99,100,101,102,107,108,154,157,159],name_for_squeu:36,named_paramet:64,namedtemporaryfil:64,namespac:[53,63,64,96,97,98,99,100,101,102],nan:[58,72],nativ:[20,36,49,64,65,72,139,142],natur:107,navig:[45,46],nb_trial:36,ncall:152,nccl:[4,6,36,38,106,157],nccl_debug:[36,38],nccl_min_nchannel:19,nccl_nsocks_perthread:19,nccl_socket_ifnam:36,nccl_socket_nthread:19,nce:[6,64],nce_loss:64,nearli:22,necessari:[0,19,64,96,98,99,100,101,102,107,108,140,148],need:[0,3,5,6,16,17,18,19,20,22,27,28,29,30,32,36,38,39,43,45,46,48,52,53,55,58,61,62,63,64,65,71,72,82,84,92,93,97,99,102,103,105,107,108,139,140,142,144,145,147,148,149,157],neg:[6,72],neighbor:22,neither:97,nemo_experi:84,nemo_nlp:84,nemo_toolkit:84,neptun:[99,102],neptune_api_token:99,neptune_logg:[99,155,160,161],neptune_project:99,neptune_run_kwarg:99,neptunelogg:[155,160,161],ner:86,nermodel:84,nest:[6,19,64,74,99,103,107],nested_kei:28,net:[0,6,36,64,77,86],network:[16,17,19,22,36,38,39,63,64,69,84,93,108],neural:[16,17,22,63,86,108],neuraltextur:86,neuraltyp:84,never:[61,64,70,72,140,148],nevertheless:[29,30],new_batch_s:22,new_lr:22,new_model:55,new_path:64,new_x:64,newer:108,newli:146,next:[6,11,16,17,20,43,64,72,96,100,103,152,157],ngc:84,nightli:[18,72,87,108,146],nlp:[16,17,74,90,109,147],nn:[11,15,19,20,22,28,29,31,56,63,64,80,81,82,103,140,145,147,148,149],nnode:[6,39],no_grad:[23,64,82,83,84,145],node1:6,node2:6,node:[3,5,6,8,16,17,22,36,37,38,39,47,64,72,74,84,103,108,140,148],node_rank:[6,37,38,39,64,72,74,140,148],noise_scal:84,non:[16,17,22,23,28,29,30,72,78,81,93,103],none:[4,18,19,21,22,30,54,61,64,69,72,74,80,84,93,96,97,98,99,100,101,102,103,140,148,153,157,159],nor:[64,74,97],norm:[19,22,72,78],normal:[16,17,19,22,28,29,55,60,64,74,102,159],normalize_confusion_matrix:84,notabl:108,notat:28,note:[0,6,11,16,17,19,22,28,29,55,58,62,64,65,69,72,74,87,93,94,99,107,108,139,142,146],notebook:[6,16,17,84,102,108,158],notic:[19,43,64,78],notifi:87,notification_email:[29,63],novel:84,now:[0,6,27,31,32,42,45,46,48,55,56,63,64,65,74,92,103,139,140,142,146,148,149,151,152],np:[6,80],npredict:147,nproc:6,nproc_per_nod:[6,39],nprofil:153,ntask:36,nuanc:42,num_:107,num_batch:72,num_class:[23,29,64,74,88,103],num_epoch:[26,140,148],num_fc_lay:84,num_featur:145,num_filt:23,num_fold:103,num_gpu:[6,64],num_lay:[63,64,140,148],num_nod:[5,6,36,38,63,84,108],num_process:6,num_replica:18,num_sanity_val_step:77,num_target_class:23,num_trial:48,num_work:[6,84,107,149],number:[0,3,6,8,11,12,16,17,18,19,22,28,29,36,39,43,48,58,60,61,64,65,70,72,74,77,84,96,97,98,99,100,101,102,107,108,140,148,149,151,157,158],numel:157,numer:[69,99,105,108],numpi:[64,72,84,101,140,147,148],nvcc:19,nvcr:84,nvidia:[3,6,19,72,84,108,146],nvme_path:19,nvprof:154,nvvp:154,nyu:72,o1:[9,72],o2:[69,72],o:[20,45,154],obj:0,object:[16,17,19,26,29,31,36,54,55,61,64,65,72,86,92,96,97,98,99,100,101,102,139,140,142,148],observ:[19,29,43,58],observer_typ:20,obtain:[19,22],occupi:[3,72],occur:[55,64],occurr:[140,148,153],odd:65,off:[19,36,72,77,78,103,140,141,148,154],offer:[2,8,19,22,36,37,38,39,41,65,69,88,89,90,102,108,140,148,157],offici:[6,16,17,33,85],offlin:[97,99,101],offload:[4,21],offload_optim:19,offload_optimizer_devic:19,offload_paramet:19,offload_params_devic:19,offset:19,often:[16,17,19,22,32,48,63,64,72,96,103,108,149],old:[16,17,22],omegaconf:[27,64],on_:93,on_advance_end:103,on_after_backward:[102,152,157],on_before_backward:[102,157],on_before_optimizer_step:[102,157],on_before_zero_grad:[102,157],on_epoch:[0,64,89,102],on_epoch_end:93,on_fit_end:152,on_fit_start:152,on_init_start:64,on_load_checkpoint:[52,54,104],on_post_move_to_devic:15,on_run_end:103,on_run_start:103,on_save_checkpoint:[52,54,104],on_skip:103,on_step:[0,64,102],on_tpu:[64,65],on_train_batch_end:[94,102,152,157,159],on_train_batch_start:[58,72,102,152,157],on_train_end:[58,72,152],on_train_epoch_end:[94,102,152,157],on_train_epoch_start:[102,152,157],on_train_start:[72,102,152,157],on_training_end:152,on_validation_batch_end:[102,157],on_validation_batch_start:[102,157],on_validation_end:58,on_validation_epoch_end:[102,157],on_validation_epoch_start:[102,157],on_validation_start:[102,157],onc:[13,15,16,17,19,27,28,29,33,39,45,46,47,48,49,50,53,58,60,61,64,72,74,80,81,88,102,103,108,135,140,148,151,152,153,158],one:[3,6,11,12,16,17,19,28,30,31,32,48,55,61,63,64,65,70,72,74,77,78,96,97,98,100,101,103,105,107,108,139,140,142,148,155,160],onebitadam:19,onecyclelr:65,ones:[4,26,30,64,65,93,100,103,106,108,139,140,142,148],ones_lik:154,onli:[0,3,6,12,16,17,18,19,20,22,24,27,29,30,31,32,45,46,48,55,56,58,60,61,63,64,65,69,70,72,74,76,77,78,87,93,94,99,101,102,108,139,140,142,145,146,148,149,157,158],onlin:97,onnx:[64,79,91,128,131,147],onnxruntim:80,onprem:[33,41,42,44,46,47,49,133],onto:[11,19,22,72],oom:[22,64],op:[9,16,17,19,154],open:[13,22,43,55,102,151,157],opensourc:158,oper:[6,9,12,16,17,19,52,65,68,69,71,72,74,75,108,132,140,148,150,157],ops_bf16_mnist:9,ops_fp32_mnist:9,opt1:65,opt2:65,opt:[64,65,74,103,139,142],opt_a:64,opt_b:64,opt_idx:93,opt_level:9,opt_list:36,optim:[0,1,4,8,11,12,14,20,25,30,31,43,45,46,47,53,55,58,61,62,63,68,72,74,79,80,81,84,85,90,91,92,93,103,106,108,113,115,116,117,128,129,131,136,140,143,144,147,148,149,156,157],optimize_parallel_cluster_gpu:36,optimizer1:[28,64],optimizer1_init:28,optimizer2:[28,64],optimizer2_init:28,optimizer_closur:[64,65],optimizer_idx:[64,65,103,108,139,147],optimizer_on:64,optimizer_registri:[28,32],optimizer_step:[65,152],optimizer_two:64,optimizer_zero_grad:108,optimizerloop:103,optimum:19,option:[0,1,4,6,7,9,10,14,18,19,21,22,28,29,30,31,36,61,63,64,65,72,74,84,87,90,92,93,94,96,97,98,99,100,101,102,107,115,131,134,139,140,142,148,149,155,160,161],option_1:26,option_2:26,or_a_fold:6,order:[22,28,29,43,64,65,72,93,94,97,103,107,108,140,148,152],ordinari:19,org:[64,65,108,139,142],organ:[22,64,72,74,87,99,109,140,148],organiz:84,origin:[20,58,108],orphan:4,ort:19,ort_input:80,ort_out:80,ort_sess:80,os:[18,36,37,64,72,74,84,87,96,97,100,102,108,144,147],other:[3,5,11,12,16,17,19,20,22,28,29,32,36,55,63,64,69,72,74,76,84,86,92,93,99,101,102,103,108,140,148,149,155,157],other_stuff:64,otherwis:[32,45,46,52,61,64,72,97,98,100,107,140,148],our:[9,19,23,36,41,44,46,47,49,61,73,84,85,87,88,91,93,104,105,108,109,110,123,124,128,140,147,148,158,159],out:[5,6,8,15,16,17,19,20,22,24,30,31,32,36,37,38,39,53,63,64,69,72,74,77,83,90,93,102,103,107,140,145,146,148,149,152],out_channel:84,out_dim:[26,27,31,53],out_featur:[64,80,81],outer:64,output:[0,6,20,23,26,36,57,64,65,81,84,93,102,103,108,139,140,142,148,152,154,159,160],output_attent:23,output_dir:84,output_path:19,output_result:64,output_typ:84,outsid:[16,17,72,140,148],over:[6,8,18,19,22,28,30,31,43,55,61,64,65,70,72,86,87,88,89,91,102,103,107,111,123,129,139,140,142,147,148,157,158],overal:[6,19,74,93,149],overcom:[12,108],overfit:[59,60,61,72,73,108,109,119,138],overfit_batch:[78,141,147],overflow:108,overhead:[0,19,22,43,62,64,65,102,157],overidden:9,overlai:101,overlap:19,overlap_comm:19,overrid:[4,6,9,11,19,22,29,30,58,64,65,70,72,74,93,102,106,107,108,140,148,153,157,159],overridden:[6,28,29,61,64,96,98,100],overwrit:[53,72],own:[4,9,11,19,20,22,26,28,33,35,36,41,44,46,48,49,64,68,70,72,73,84,90,91,99,102,103,104,105,106,110,124,126,135,140,144,146,148,150,155,156,158,160,161],p100:146,p1:64,p2:64,p3:64,p:[22,62,65,72,84,103,108],pack_sequ:107,packag:[6,9,12,16,17,30,38,39,69,70,80,86,97,98,99,101,109,155,160,161],packedsequ:107,pad:107,page:[49,109,146,151],pai:[6,41,44,46,49],pair:[19,63,64,65,84,139,140,142,148],panda:101,panel:101,paper:[22,60,61,64,72],paradigm:103,parallel:[12,19,48,55,64,72,74,107,109],param:[9,19,36,64,65,77,84,91,96,97,98,99,100,101,102,110,118,149,159],param_group:[64,65,84],param_requires_grad_st:64,paramet:[0,1,2,4,6,8,9,15,20,21,22,26,28,29,30,31,58,61,63,64,65,72,73,74,90,91,93,96,97,98,99,100,101,102,106,107,108,109,110,123,139,140,141,142,144,145,147,148],parameter_valid:64,params_dict:99,parent:[96,100],parent_pars:[36,63],pariti:19,pars:[3,19,26,28,29,30,37,84,92],parse_arg:[31,36,63,72],parse_devic:92,parse_known_arg:63,parse_tpu_cor:[16,17],parser:[28,29,31,36,63,72,84],parser_kwarg:[27,30],part:[6,29,30,45,46,58,60,61,64,72,84,92,93,94,103,105,140,148,153],parti:[38,69,121,156,160],partial:86,particular:[16,17,28,30,64,67,87,97],particularli:[20,104],partit:[19,103],pascal:108,pass:[4,6,9,11,19,20,21,22,26,27,28,29,36,43,53,54,58,60,61,62,63,64,65,68,70,71,72,74,80,82,83,84,90,92,93,94,96,97,99,100,101,102,103,106,107,108,140,148,149,155,157,159,160,161],passthroughprofil:153,passwordless:6,past:19,patch:146,path:[4,19,21,23,27,28,29,30,31,36,52,53,54,55,61,63,64,71,72,74,83,84,87,96,97,98,100,101,102,108,140,148,158],path_to_data:84,paths2audio_fil:84,patienc:[28,29,58],pattern:64,pdb:[76,77],peak:19,pend:100,peopl:30,per:[3,6,19,24,27,32,36,39,49,55,64,72,74,78,100,102,154,158],per_experiment_nb_gpu:36,per_experiment_nb_nod:36,percal:152,percent:159,percentil:[16,17],perf:154,perf_log:152,perform:[0,6,8,10,13,15,18,19,20,22,30,31,33,48,53,58,61,64,65,68,69,72,74,84,88,90,91,102,103,108,115,128,132,135,139,140,142,145,148,151,157,160],performan:46,permut:36,persist:[93,94],persistent_work:108,person:[84,101,108],pg:[64,65],phase:[61,64,72,108],phenomenon:84,pick:[22,48,63,72,90,98],pickl:[0,93,94],picklabl:6,pickleabl:64,picklingerror:0,piec:[0,6,43,84,150],pil:101,pin_memori:[107,108],pip:[6,16,17,19,27,31,45,46,70,71,84,85,88,89,90,97,98,99,101,103,109,147,155,159,160,161],pipe:19,pipelin:[11,87],pixelcnn:85,pl:[6,9,11,12,13,16,17,18,19,22,45,46,52,53,55,60,61,64,74,81,82,84,85,95,99,103,139,144,145,147,149,158],pl_bolt:[74,85],pl_cli:32,pl_deepspeed_config_path:19,pl_exampl:9,pl_fault_tolerant_train:[42,104],pl_logger:[102,155,160],pl_modul:[58,72,93,159],place:[11,28,29,30,64,65,72,104,108,149],placehold:100,placement:[140,148],plagu:78,plai:[22,63],plain:[53,74,103],plane:26,platform:[45,46],pleas:[5,6,9,12,19,29,30,41,43,44,46,47,49,64,65,72,74,84,87,88,90,100,102,108,139,140,142,148,157],plot:[22,78,102,158,160],plu:19,plug:4,plugin1:105,plugin2:105,plugin:[4,9,11,21,36,37,64,71,106,108,109],pod:[12,16,17,72,108],point:[19,20,22,53,58,63,64,66,67,69,72,104,105,108,130,140,148,158],pointer:64,pollut:[63,93],pool:19,pop:[16,17,83,102,157],popart:12,popen_spawn_posix:0,poplar:12,poptorch:11,popular:[26,84],port1:6,port:[6,36,38,151],portal:12,portion:[6,60,64,78],pose:86,posit:[28,30,64,72,96,97,98,101,108],posix:30,possibl:[0,3,6,16,17,27,28,29,30,48,63,64,69,92,93,102,107,108,149],possibleuserwarn:108,post:[19,22,68,82,93],post_localsgd:19,post_localsgd_hook:19,postlocalsgdst:19,postprocess:84,potenti:[30,55],power:[12,14,22,36,49,72,90,103,104,108,117,144,158],powersgd:19,powersgd_hook:19,powersgdst:19,pr:87,practic:[22,28,29,30,53,56,60,61,64,65,72,78,103,108,139,142,149],practicion:49,practition:88,pre:[18,22,26,31,32,40,41,42,65,68,82,88,107],prebuilt:84,preced:72,precis:[4,7,10,16,17,19,20,22,53,65,73,84,85,91,106,109,115,116,127,128,130,132,139,141,142,147],precision_plugin:[4,106],precisionplugin:[68,72],pred:[64,81,82,89,145,149,157],predict:[4,19,20,22,23,30,31,53,74,77,79,83,84,91,93,101,103,106,109,123,147,149],predict_data:74,predict_dataload:[72,107],predict_dataset:107,predict_load:74,predict_step:[12,81,82,145,154],predict_step_end:64,predictionepochloop:103,predictionloop:103,predicts_step:64,preemptibl:41,prefer:[47,83],prefix:[30,56,64,96,97,98,99,100,101,102],preinstal:100,prem:[42,47,73,91,109,128],prepar:[1,7,10,14,64,72,74,115,134,140,148,152],prepare_data:[22,72,152],prepare_data_per_nod:72,prepend:[52,157],preprocess:[43,84,108],preprocessor:84,present:[29,64,97],preserv:[30,64],press:72,pretend:[53,92],pretrain:[64,73,84,88,91,123],pretrained_ckpt_path:64,pretrained_model:64,pretrained_model_nam:84,pretrained_model_name_or_path:90,pretrained_ner_model:84,pretti:93,prevent:[0,22,64,76,102,157],previou:[28,30,43,48,61,64,65,72,101,104,107,139,142],previous:[18,29,100],price:[8,33,41,135],primari:[43,64],primarili:19,primit:[6,12,152],principl:149,print:[16,17,22,28,30,31,32,53,61,70,71,72,76,84,88,89,93,103,147,154,159],print_config:[26,28,30,31],printcallback:72,printtablemetricscallback:85,prioriti:64,privat:[33,38,47,49,126,135],pro:[72,108],probabl:[48,64,103],problem:[12,28,29,30,72,78,88,89,90,108],problemat:30,proce:74,procedur:[22,64,84,140,148],process:[0,3,4,6,8,11,12,16,17,19,28,29,30,36,37,39,43,53,55,57,61,64,65,72,74,76,82,86,91,93,96,98,99,100,101,102,106,107,108,110,115,116,139,140,142,148,157,158],process_group:19,process_group_backend:6,process_obj:0,processed_sign:84,processed_signal_len:84,processing_spe:70,processor:8,produc:[6,19,22,26,64,102,157],product:[25,26,48,49,60,72,73,77,84,91,109,110,131,147],prof:154,profession:26,profil:[64,65,91,109,117,124,128,132,139,140,142,148,150],prog_bar:[64,65,72,84,102,108,139,142,158],program:[16,17,26,63,72,93],programm:8,progress:[12,43,45,46,47,64,72,73,91,104,109,110,112,119,149,156,158],progress_bar:[70,159],progress_bar_finish:70,progress_bar_puls:70,progress_bar_refresh_r:72,progressbarbas:[70,159],prohibit:48,project:[6,16,17,25,26,31,32,56,63,64,74,91,93,97,99,101,107,113,125,128,143,145,149,155,160,161],project_id:[16,17],project_nam:97,prone:29,proof:41,propag:[29,64,103,107],proper:[28,64,102],properli:[43,64,93,94,103,140,148],properti:[37,55,65,80,84,94,96,97,98,99,100,101,102,104,107,139,140,142,148,159],protocol:[0,52,71,157],prototyp:[88,149],prototype_arrai:160,provid:[4,6,8,9,11,12,13,15,19,22,28,29,30,37,39,41,44,46,49,52,63,64,65,68,69,71,72,87,93,98,102,103,105,106,107,108,129,139,140,142,148,154,157,158],prune:[79,109,131],pseudo:[64,72],pseudocod:[6,64,72],pt:[19,24,64,81,88,103,140,148],ptl:84,publish:[60,72,146],pull:[53,63,83,84,87,102],punctuat:84,pure:[13,73,79,109,122,140,148],purpos:[36,38,61,72,93,94,108,140,148],push:[2,72,113,143],put:[3,64,72,92,96,97,98,100,101,140,147,148],py3:146,py:[0,6,16,17,18,19,22,26,27,28,29,30,31,32,36,38,39,41,42,45,46,48,63,72,84,90,92,108,152],pyarrow:71,python3:[0,16,17,36],python:[0,5,6,9,16,17,18,19,26,27,28,29,30,31,32,38,39,42,45,46,48,57,63,64,70,81,84,87,90,92,99,101,102,103,107,108,109,140,146,148,152,154],pythonfaulthandl:36,pytorch:[0,4,6,8,9,15,16,17,18,19,20,22,28,31,32,33,38,39,52,53,64,65,71,72,73,74,78,79,80,81,84,85,87,88,89,90,91,99,103,106,107,108,110,113,122,132,139,142,146,149,150,155,157,160,161],pytorch_checkpoint:61,pytorch_lightn:[4,6,9,11,13,15,16,17,18,19,20,21,22,28,29,31,32,36,37,54,55,57,58,62,64,65,70,71,72,74,77,85,92,93,96,97,98,99,100,101,102,103,106,107,108,139,140,142,144,145,147,148,151,152,153,154,155,157,159,160,161],pytorchlightn:[41,42,44,46,47,49,146],pytorchprofil:154,pytorh:78,q3:47,q_adam:6,qadam:6,qadamoptim:6,qat:20,qcb:20,qmodel:20,qualiti:20,quant:20,quantiti:[55,58,96,97,98,99,100,101],quantiz:[79,109,131],quantizationawaretrain:20,quartznet15x5bas:84,quartznet:84,quartznet_15x5:84,queri:[84,101],question:[1,14,25,40,74,84,86,108,149],queue:[64,100],quick:[88,99],quickli:[64,72,84,140,148],quickstart:99,quirk:64,r:[16,17,36,108,159],race:[140,148],rack:12,rais:[16,17,30,64,72,74,97,98,99,101,102],ram:19,ran:[19,48],rand:[64,69,81,108],randint:89,randn:[43,64,80,82,83,89,145],random:[6,43,53,72,80,140,148],random_search:48,random_split:[60,74,144],random_train:32,random_unstructur:20,randomdataset:43,randomiterabledataset:43,randomsampl:43,randomstructur:20,rang:[3,20,43,64,69,72,81,82,89,103,107,140,148,149],rank:[0,18,37,39,55,64,76,102,140,148,154,157],rank_zero_experi:102,rank_zero_onli:[0,55,64,102],rapid:[90,141],rate:[6,16,17,25,28,32,36,43,53,63,64,70,72,136,149],rather:[19,22,54,62,65,69,72],raw:[91,103,109,110,113,143,147],rcnn:99,rdma:8,rdzv_backend:6,rdzv_endpoint:6,rdzv_id:6,re:[3,12,19,48,63,67,72,152,153,158],reach:[22,58,69,72,91,108,110],read:[6,19,20,22,26,30,32,36,41,42,52,57,71,72,84,87,99,102,103,104,107,108,140,148,157],readabl:[30,63],readi:[45,46,89],real:[60,61,64,103],real_label:[65,108,139,142],realist:[29,43,74],realli:[5,6,19],realpath:36,reason:[5,6,16,17,18,19,29,43,52,72,103,108,149],recal:[48,102,160],recap:63,receiv:[93,96,97,98,100,101],recent:[16,17,26,72,146],recip:[86,88,99,144],recogn:[19,69,72,84,140,148],recognit:[20,86],recommend:[6,19,20,22,30,38,45,46,48,49,55,61,64,65,67,69,72,74,76,81,83,84,99,102,103,108,139,140,142,148,149,154,157],reconstruct:[64,86],record:[19,96,97,98,99,100,101,102,154],record_funct:154,recov:[41,58],recurr:[64,86],recurs:19,red:22,redirect:[57,102,103],reduc:[0,15,20,22,64,69,72,89,102,103,108,140,148,149,152,157],reduce_bucket_s:19,reduce_fx:[64,102,158],reducelronplateau:[32,64,65,139,142],reduct:[0,4,19,43,64,102,106,140,148,157],redund:22,ref:[39,146],refactor:[64,140,148,149],refer:[0,9,16,17,19,22,27,29,47,60,64,74,87,88,90,99,101,102,103,107,108,109,153],refin:69,reflect:99,refresh:[72,151],refresh_r:[70,72],regard:[64,105],regardless:[6,64,72],regist:[0,4,21,25,28,108,127,136,145],register_acceler:92,register_buff:[108,145],register_class:32,registr:[4,21,99],registri:[25,28,32,71,109,125],regressionmodel:20,regular:[24,55,74,76,83,99,145,154],reinforc:[64,65,139,142,147],reinstal:84,relat:[29,107,149],releas:[30,69,84,87,104,146],relev:[6,53,72,140,144,148,149],reli:[6,12,19,22,30,61,64,65,69,93,103,140,148],reload:[63,64,72,74,93],reload_dataloaders_every_n_epoch:64,reload_ext:[102,158],relu:[11,19,20,31,64,80,81,84,144,145,147],remain:[0,5,19,22,62,65,69],rememb:[56,63,64],remot:[27,52,72,98,109,114,157],remote_devic:19,remov:[19,64,72,74,79,82,91,93,101,108,123,140,148,152,155,160,161],remove_checkpoint:54,repeat:[72,84],repeatedli:58,replac:[6,64,72,78,83,99,103,140,148,155,160,161],replace_sampl:[140,148],replace_sampler_ddp:0,replic:[11,16,17,61,64],replica:[6,22],replication_devic:[16,17],repo:87,report:[13,16,17,19,30,152,154],report_dir:13,repositori:[30,45,46,87],repres:[22,62,64,65,97,103,107,154],represent:[23,56,64,86,105,140,148],reproduc:[24,28,29,52,63,74,84,89,109,140,148],req:[26,31,32],request:[6,16,17,43,55,58,64,72,74,87,102,103],requeu:36,requir:[5,6,11,12,15,16,17,18,19,20,28,29,30,32,38,43,55,64,65,67,69,72,74,78,84,87,88,91,93,94,97,98,99,100,101,102,103,105,107,108,139,140,142,148,149],requires_grad:108,requisit:[40,41,42],research:[4,16,17,26,33,45,46,49,56,61,65,67,68,72,83,84,86,88,90,91,93,103,109,113,123,128,139,142,143,147],reset:[64,72,89,104],reset_experi:97,resnet18:[48,103,149],resnet50:[19,23,43,48,64],resnet:[23,99],resolv:[12,19],resourc:[16,17,36,67,72,108],respect:[19,29,54,61,65,72,102,145],respons:[4,64,103,105,106],rest:[58,64,69,97,105,149],rest_api_kei:97,restart:[41,42,43,104],restor:[6,19,22,41,43,53,55,64,74,83,84,95,97,108],restrict:152,result:[0,6,12,16,17,19,22,55,61,64,69,72,74,84,93,108,140,148,157],resum:[71,72,101,103,104,140,148],retain:[140,148],retriev:[57,64,93,101,102],return_predict:72,reus:[74,93,107,136,149],reusabl:[74,91,112,128,149],rewrit:144,rich:[70,159],rich_progress:[70,159],richer:93,richmodelsummari:70,richprogressbarthem:[70,159],right:[16,17,18,22,30,37,43,61,62,64,65,72,84,140,148],rigor:[85,89,146],river:84,rl:[64,109,113,143,147],rm:84,rnn:[24,107,149],roberta:19,robust:55,roce:8,root:[6,30,57,60,64,84,98,99,102,108],root_dir:[36,52,96,100,157],rotat:74,roughli:[6,19],round:103,routin:[26,27,30,31,72,105,145],row:[72,102,157],rule:[38,39,60],run:[0,1,3,5,6,7,10,11,14,18,19,22,28,29,30,31,33,41,44,48,55,58,60,61,62,63,64,65,67,69,70,71,72,73,74,76,80,81,84,86,87,91,93,96,97,98,99,100,101,102,107,108,109,110,115,117,123,126,128,134,144,145,146,151,154,155,158,159,160,161],run_fast:31,run_id:[98,101],run_nam:98,run_training_batch:152,run_training_epoch:152,runnam:98,running_mean:145,runtim:[6,11,16,17,19,32,64,80,81,84,87],runtime_vers:[16,17],runtimeerror:[16,17],rust:6,rwmap:[16,17],s3:[27,45,46,52,71,72,103,157],s3f:71,s:[0,3,5,6,8,16,17,18,19,20,23,26,27,28,29,36,43,45,46,48,49,53,54,55,60,61,62,63,64,65,71,72,74,77,78,84,88,92,93,94,96,97,99,100,103,105,107,108,139,140,142,144,148,149,152,153,155,158,160,161],sacrific:19,safe:[64,74],sai:[5,22,26,48,62,65,78,84],sale:47,same:[0,6,19,26,28,29,30,38,43,48,55,56,61,64,72,74,84,93,94,101,102,103,107,108,140,141,148,155,161],sampl:[6,22,29,43,55,61,64,65,72,74,78,80,101,107,108,139,142,149,158],sample_g:[65,108,139,142],sample_img:64,sample_z:[65,139,142],sampler:[18,43,64,72,78,107,140,148],sampler_it:43,saniti:[64,70,72,93],sanity_check:72,santa:84,satisfi:61,satur:11,save:[3,6,8,9,12,16,17,19,20,22,26,30,33,36,40,51,54,63,64,66,69,71,72,73,81,83,84,96,97,98,99,100,101,102,103,104,107,108,109,119,130,135,149,154,157],save_checkpoint:[4,21,54,55,64,83,88,103],save_dir:[71,72,96,97,98,99,100,101,102,157],save_full_weight:19,save_hyperparamet:[22,53,74,101,160],save_img:72,save_last:[54,55],save_on_train_epoch_end:55,save_path:19,save_top_k:[55,101],save_weights_onli:55,sawyer:84,sbatch:36,scalabl:[89,91,128,130,145,147,149],scale:[0,2,4,6,7,8,9,13,14,19,22,42,45,46,47,53,64,66,68,69,72,73,79,80,81,90,91,92,108,110,115,116,117,118,128,131,132,140,144,148],scale_batch_s:[22,72],scale_batch_size_kwarg:72,scale_gradi:103,scaler:[22,69],scatter:[6,69,74],scenario:[24,43,108],scene:[19,86],sch1:[65,139,142],sch2:[65,139,142],sch:[65,139,142],schedul:[4,6,19,22,25,28,36,43,53,62,64,84,90,106,136,144,149],scheduler1:64,scheduler2:64,scientif:67,scientist:88,scope:[30,154],score:99,scratch:41,screenshot:158,script:[6,16,17,19,30,39,41,42,45,46,48,64,81,84,91,108,109,123],scripted_modul:81,scriptmodul:64,sdk:12,seamless:[12,90],seamlessli:[63,140,148],search:[22,36,72,101,108,109,155,160,161],second:[16,17,36,39,64,72,84,100,107,152],section:[16,17,18,19,24,26,28,29,63,64,72,99,103,108,140,148],see:[0,3,6,11,12,13,15,18,19,22,24,30,31,36,38,48,53,60,61,64,65,69,72,77,78,84,102,108,109,139,140,142,147,148,149,152,154],seed:[43,60,72,140,148],seed_everyth:[26,28,29,31,72],seem:0,seen:[19,55,65,72],segment:[86,88,101],select:[3,4,6,16,17,20,22,43,61,65,72,92,105,106,139,140,142,148,155,160],select_fn:[16,17],self:[0,4,5,6,11,12,15,18,19,20,21,22,23,28,29,31,32,37,43,52,53,54,55,56,58,60,61,63,64,65,69,70,72,74,76,77,80,81,82,83,84,85,86,89,92,93,94,95,96,97,98,99,100,101,102,103,104,106,107,108,112,139,140,142,144,145,147,148,153,154,155,156,158,159,160,161],self_supervis:85,semantic_segmentation_model:88,semanticsegment:88,semanticsegmentationdata:88,send:[16,17,22,29,64,97,102,157],send_email:29,sensibl:149,sensit:67,sent:[12,72,97,157],sentencepiec:84,sentencepiecetoken:84,sentiment:84,separ:[3,18,19,23,30,43,61,64,72,74,84,86,149],seq2seq:[64,149],seq_data:107,sequenc:[6,61,64,72],sequenti:[16,17,19,23,26,64,65,72,77,103,144,147],seri:[86,103],serial:[5,81],serializ:63,serv:[8,20,61,140,148],server:[8,98,101,151],servic:[6,151],session:49,set:[3,6,7,8,9,10,11,12,13,16,17,18,20,22,24,28,29,30,31,36,37,38,39,47,58,60,61,62,64,65,66,67,69,72,74,78,84,91,93,97,98,99,100,101,102,103,104,105,107,116,123,130,132,139,140,142,147,148,149,157,158],set_default:[28,29],set_descript:[70,159],set_grad_en:[64,72],set_repl:[16,17],set_to_non:[64,108],set_trac:[76,77],set_train:84,setlevel:[57,102],setup:[4,6,16,17,19,22,39,61,106,107,136,145,152],setuptool:6,sever:[28,43,52,64,65,71,102,103,140,148,157],sgd:[5,6,32,64,65,140,148],sh:[36,84],shall:20,shape:[16,17,64,65,84,108,139,142,157],shard:[55,64,90,93,140,148],share:[18,19,27,43,63,64,74,101,107,108,149],shareabl:74,sharpest:22,shell:[5,20,28,30],shift:103,shirt:84,shm:[84,108],short_id:99,shortcut:32,shorthand:[28,92,140,148],shot:86,should:[6,11,15,19,22,28,29,30,43,52,55,56,62,63,64,65,70,72,74,84,91,93,96,97,98,99,100,101,123,140,145,148,149,157],should_check_v:64,show:[9,19,22,27,28,29,31,38,64,70,72,74,89,102,107,114,154,157,158,160],shown:[19,20,29,54,64,65,72,103,157],shuffl:[18,41,64,72,78,107],shut:[41,72],shutdown:72,siames:86,side:8,sigma:0,sign:[30,78,140,148],signal:[19,36],signatur:[31,93],signific:[0,16,17,19,20,32,64,69,102,140,148,157],significantli:[18,19],sigusr1:36,simclr:85,simclr_featur:85,simd:8,similar:[6,28,64,69,78,141],similarli:[22,28,65,104,107,139,142],simpl:[19,22,24,28,30,45,46,65,72,74,84,88,89,103,108,139,142,152,153],simple_mnist:9,simplegan:[65,108,139,142],simplemodel:[64,80,81],simpleprofil:[72,153,154],simpler:[19,30],simplest:[31,42],simpli:[8,16,17,19,20,47,49,53,64,74,90,102,103,107,158],simplic:[64,140,148],simplif:28,simplifi:[28,63],simul:[72,89],simultan:107,sinc:[6,12,22,28,29,30,61,62,64,65,67,69,72,74,84,93,108,140,145,148],singl:[1,3,6,7,8,9,10,12,14,16,17,22,28,43,48,61,64,65,67,69,72,73,74,84,87,102,103,107,108,109,115,116,134,139,140,142,148,157,158],singleargmodel:64,singledevicestrategi:54,singleton:[93,94],situat:6,size:[5,6,12,16,17,19,20,31,37,43,48,60,61,62,64,65,72,77,80,81,84,85,100,102,107,108,118,131,144,145,147,154,157],skill:109,skip:[18,19,58,64,65,69,74,80],skip_default:31,skip_nul:31,slack:87,slice:[16,17,72],slope:22,slot:84,slow:[0,6,19,72,102,108,149,152,157],slowdown:[16,17,55,108],slower:[16,17,19,24,72,157],slowest:152,slowli:[108,140,148],slurm:[35,64,91,109,128,133],slurmclust:36,slurmenviron:36,small:[16,17,19,22,60,62,65,108,109],smaller:[19,20,72,157],smooth:[22,64],smoother:22,snd_1:101,snd_2:101,snippet:[9,28,65],so:[4,6,8,16,17,19,21,22,23,27,28,29,31,43,47,48,53,55,62,63,64,65,67,69,72,74,77,84,91,92,103,105,106,107,108,123,140,146,148,149,154],soc:47,softmax:[6,11,64,89],softwar:[0,8,41],solut:[19,28,29,33,36,49],solv:88,some:[0,12,16,17,18,19,20,28,29,30,36,38,45,46,47,53,54,58,61,63,64,69,72,84,87,93,94,102,103,108,153,157,159],some_comet_funct:97,some_experiment_writer_funct:96,some_fil:6,some_imag:[56,64],some_images_from_cifar10:23,some_mlflow_funct:98,some_nam:63,some_object:0,some_other_st:64,some_result:64,some_scalar:102,some_scalar_1:102,some_scalar_2:102,some_st:64,some_tensorboard_funct:100,some_text:158,some_valu:158,some_wandb_funct:101,someon:149,someotherloss:63,someth:[0,6,22,29,64,72,74,78,85,103,140,148,149],something_cool_i_want_to_sav:[52,64],sometim:[0,16,17,19,29,63,64,67,77,108],somewher:0,soon:[19,22,58,96,97,98,100,101,140,148],sort:[46,72],sota:[90,91,128,141,144],sound:101,sourc:[19,29,30,36,46,61,64,72,74,86,93,96,97,98,99,100,101,103],space:[19,30,55,93,108],span:90,spanish:101,spars:[64,65,139,142],spawn:[4,16,17,21,22,39,64,72,106,140,148],speaker:84,spec:[26,84],spec_augment:84,spec_gen:84,spec_gen_model:84,special:[3,16,17,19,28,29,48,103,140,148],specif:[6,12,19,20,28,36,38,58,61,63,64,69,70,72,83,84,87,96,97,98,99,100,101,107,108,140,148,149,153,155,157,160,161],specifi:[3,6,11,16,17,20,28,38,39,55,61,63,64,72,74,80,90,96,97,99,100,102,107,108],specmodel:84,spect:84,spect_length:84,spectrogram:84,spectrogramgener:84,speech:[20,86],speech_to_text:84,speed:[3,6,11,19,20,67,72,85,90,91,128,129,156],speedup:[19,69,72,140,148],spend:8,spent:[16,17],split:[6,11,22,62,63,64,65,74,103,107,119,138,144,149,158],split_batch:[6,64,103],split_batches_for_dp:64,split_siz:64,split_x:64,squad:86,squeez:64,squeezewav:84,sram:8,src:[6,140,148],srun:36,ss:72,ssh:[6,16,17,18],ssh_port1:6,ssh_port2:6,stabil:[69,91,128,130],stabl:[6,29,30,67,108],stack:[8,22,30,62,64,65,84],stacktrac:0,stage:[4,21,22,61,64,72,74,93,140,148,153],stai:[87,140,148],standalon:19,standard:[19,26,30,63,72,85,89,103,140,148,152],stanford:72,stare:6,start:[6,8,11,12,16,17,19,22,26,28,29,36,39,47,63,72,84,91,93,98,101,103,108,123,140,148,153,154],start_localsgd_it:19,start_powersgd_it:19,starter:109,startup:[16,17],stat:[19,72,152,154],state:[2,6,7,15,16,17,19,22,43,52,55,64,66,85,89,103,108,116,119,132,138,140,141,147,148,157],state_dict:[53,83,94,95,104,140,148],state_kei:94,statement:76,static_graph:19,staticmethod:[63,72,92],statist:[92,158],statu:[45,46,72,96,97,98,99,100,101,102],stdlib:72,stdout:[70,159],step:[0,6,11,12,16,17,19,22,32,53,55,56,62,64,68,69,72,73,74,77,84,89,93,96,97,98,99,100,101,102,103,107,108,109,139,142,144,147,151,159],step_output:[6,64],step_siz:145,steplr:[65,145],steps_per_tri:22,stick:19,still:[16,17,18,19,26,28,61,64,67,69,72,82,93,97,103,108,145],stitch:103,stochast:72,stochastic_weight_avg:72,stochasticweightaverag:[22,72,141,147],stone:109,stop:[64,72,77,78,103,107,109,119,140,148,149,153],stopping_threshold:58,storag:[16,17,52,53,54,71,102,157],storage_opt:54,store:[0,19,20,22,29,43,62,63,64,65,71,72,84,88,93,98,101,102,108],str:[3,4,21,22,31,37,61,63,64,72,74,84,92,93,96,97,98,99,100,101,102,140,148,159],str_input:84,strategi:[1,5,8,9,11,13,16,17,22,36,38,51,54,55,61,64,65,73,74,76,83,88,91,92,103,105,108,109,118,124,139,141,142,147,149],strategyregistri:[4,21],stream:[72,101,107,152],strftime:153,strict:64,strictli:64,stride:[84,154],string:[3,4,20,21,28,29,64,72,96,97,98,100,101,108,140,148,149],strip:84,strive:30,strong:[16,17],strongli:[6,69,93],structur:[22,28,29,36,64,74,86,99,102,103,107,149,155,157,160,161],style:[6,30,107,109],sub:[43,100,103],sub_batch:64,sub_dir:100,subclass:[4,20,23,29,58,68,72,103,106,140,148,153,159],subclass_mode_data:28,subclass_mode_model:28,subclassess:28,subcommand:[27,28,29,31],subdirectori:[96,100],subgroup:19,subject:[4,19,20,21,54,140,148],submit:[18,36,47,72,108],submodul:72,subprocess:[6,72],subsequ:[11,20,28,69,102],subset:[6,16,17,48,103],substanti:[8,19],substitut:103,subtokens_mask:84,success:[19,46,96,98,99,100,101],successfulli:[93,140,148],suffix:[64,102],suggest:[19,22,55,72,108],suitabl:72,sum:[20,31,64,84,157],sum_out:154,summar:72,summari:[19,70,72,99,108,153,158],summarywrit:100,supercharg:138,supermicro:8,supervis:[64,86,93],support:[4,6,8,12,13,15,16,17,18,19,20,22,27,28,29,32,33,43,47,48,54,63,64,65,69,70,71,72,74,81,84,90,92,93,94,99,101,103,106,107,108,139,140,142,146,148,155,158,160],sure:[3,5,19,22,36,40,42,53,55,60,61,62,63,64,65,72,102,108,145,149,151],swa:[22,72,109],swa_lr:22,swap:[90,103,107,149],sweep:[33,45,46,47,109,135],sy:[99,159],synapseai:8,sync:[0,6,19,22,62,64,65,76,102,108,157],sync_dist:[0,64,102],sync_dist_group:[64,102],sync_grad:[64,108],sync_interval_m:6,synchron:[6,64,72,89,102,108,140,148,154,157],syntax:[6,99],system:[6,8,12,16,17,27,52,64,71,72,93,96,100,103,108,157],t:[0,3,5,6,15,16,17,18,22,28,31,36,37,43,48,53,54,55,61,62,64,65,69,71,72,74,78,84,93,97,99,102,103,107,108,139,140,142,145,146,148,149,153,154,157,158,159],t_max:64,tab:102,tabl:[3,70,77,101,157],tabular:[86,101],tacotron:84,tag:[64,98,99,101],tailor:8,take:[19,28,29,49,64,69,78,81,82,84,103,107,108,140,148,152],taken:[19,20,64,72,99],talk:[36,92],tanhlrschedul:65,tar:46,target:[30,64,69,74,84,89,140,148,157],target_length:84,task:[0,16,17,29,36,64,69,74,84,88,90,103],tb_log:100,tb_logger:102,tbptt:[64,72,103],tbptt_split:103,tbptt_split_batch:107,tbptt_step:64,teach:136,team:[4,19,22,41,44,46,47,49,55,85,108,149],teardown:[4,106,152,153],teaser:30,technic:19,techniqu:[1,2,4,6,10,14,15,19,20,65,66,68,73,78,91,108,109,117,127,128,129,132,138,141,144,152],tell:[31,64],temp_arg:63,tempfil:64,temporari:36,tempt:[28,29],tend:[56,64,149],tensor:[6,8,16,17,20,22,43,56,64,65,67,69,72,74,77,84,97,99,101,102,107,139,140,142,145,147,148,155,157,158,160,161],tensorboard:[45,46,64,72,84,93,100,102,147,157,158],tensorboard_logg:[64,155,160,161],tensorboardlogg:[71,72,102,155,157,160,161],tensorflow:[16,17],term:[19,48],termin:[16,17,30,70,72],terminate_on_nan:[26,72],terminolog:[12,140,148],test:[6,18,19,27,30,31,43,56,70,73,74,77,84,85,89,91,93,97,99,102,103,109,123,138,144,146,147,149,158],test_acc:64,test_batch:[64,72],test_data:[64,74,140,148],test_dataload:[61,72,107],test_dataset:[107,140,148],test_dl1:107,test_dl2:107,test_dl:107,test_epoch_end:[0,61,72,149,157],test_load:74,test_loss:[0,60,64,145],test_out:64,test_sampl:107,test_set:60,test_step:[0,6,12,56,60,61,72,107,145,147,149,154,157,158],test_step_end:[6,149,157],test_step_out:64,test_step_output:64,test_train_imagenet:18,test_tub:61,text:[64,70,74,86,101,102,120,121,156],text_fil:84,text_length:84,text_to_gener:84,textcolumn:70,textencod:84,textual:159,tf:64,th:108,than:[6,18,19,20,22,28,29,54,58,64,69,72,74,77,84,105,107,108,129,151,153],thank:99,the_other_valu:53,the_valu:53,the_world_s:[72,140,148],thei:[0,3,12,19,29,30,61,63,65,72,74,92,93,101,103,108,139,142,149],them:[3,4,9,12,19,21,26,28,32,38,43,61,63,64,65,66,72,74,83,85,93,96,97,98,100,101,102,103,107,108,127,139,140,141,142,145,148],theme:[70,159],themselv:11,theori:103,therebi:28,therefor:[6,28,30,58,64,145],thereof:64,thi:[0,3,5,6,8,9,11,12,13,15,16,17,18,19,20,22,24,26,27,28,29,30,31,32,36,38,39,42,43,45,46,48,52,53,54,55,56,58,60,61,63,64,65,67,69,70,71,72,74,77,78,81,82,83,84,85,92,93,94,96,97,98,99,100,101,102,103,104,105,107,108,111,112,114,130,131,132,133,136,139,140,142,146,148,149,151,152,154,157,158,159],thing:[0,16,17,19,29,56,64,65,73,74,77,91,103,107,124],think:[65,103,158],third:[38,69,121,156,160],those:[6,16,17,28,63,64,69,74,140,141,148],though:29,thousand:[84,140,148],three:[16,17,63,105,108],threshold:58,through:[6,16,17,28,29,40,43,54,64,66,72,77,92,101,103,104,108,127,149,151,158],throughout:[53,158],throughput:[6,11,19,22,69,108],thu:[22,29,64],thumb:60,ti:15,tib:[16,17],ticket:20,tie:64,tile:12,till:[11,12,22,62,65],time:[3,6,11,16,17,20,22,30,41,43,48,49,55,61,63,64,65,70,72,74,77,86,87,103,108,140,141,144,148,149,151,153,154,155,158,160,161],time_dim:64,timedelta:72,timer:72,timeseri:[157,158],timm:65,tini:[23,43,78],tinycifar5:46,tip:72,tmpdir:[52,71],tmpf:108,tmpfile:64,to_onnx:80,to_tensor:152,to_torchscript:[20,81],todai:[15,36,37,38,39],todo:[54,157,158,160],togeth:[20,22,72,84,103,141,144],toggl:[27,64,65,72,139,141,142],toggle_model:108,token:[64,74,99],token_classif:84,token_classification_config:84,token_type_id:[23,84],tokenclassificationmodel:84,tokenindex:84,tokenizer_nam:84,toler:[6,33,47,53,91,104,109,124,135,140,148],tolranc:42,tom:[84,99],toma:22,ton:31,too:[16,17,22,31,48,83,102,140,148,152],tool:[8,13,26,28,29,30,31,77,140,148,151],toolkit:[84,86],top:[6,55,72,103,144,151,152,154],topic:[30,64,65,139,142],topolog:[101,155,161],torch:[0,3,11,15,18,19,20,22,23,28,29,31,32,35,39,43,53,54,56,60,61,63,64,65,69,72,74,77,80,81,82,83,84,89,92,100,102,103,107,108,109,133,139,140,142,144,145,147,148,154,155,157,158,160,161],torch_api:6,torch_xla:[16,17,18,72,108],torchcheckpointio:54,torchelast:[1,72],torchio:86,torchmetr:[0,64,102,157],torchpoint3d:69,torchscript:[20,64,79,91,128,131],torchtext:[64,74],torchvis:[23,60,64,74,144],total:[6,11,19,43,64,70,72,108,140,148,152,153,154,157],total_fit_batch:72,total_step:65,total_train_batch:[72,159],total_train_sampl:72,total_val_batch:72,total_val_sampl:72,totalsampl:[16,17],totensor:[18,22,64,74,144],tottim:152,touch:[31,90],tpc:8,tpu:[0,4,6,21,54,64,65,67,69,72,73,74,85,86,91,92,106,109,110,128,134,139,140,142,146,147,148,150,152],tpu_ip_address:[16,17],tpu_nam:[16,17],tpu_pod_nam:[18,72,108],tpu_spawn:[140,148],tpu_spawn_debug:[4,16,17,21],tpu_work:[16,17],tpuspawn:[16,17],tpuv2:146,tpuvm:[16,17,18],tqdm:[70,102,157],tqdmprogressbar:72,trace:[12,30,64,72,151],trace_nam:154,traceback:[16,17],track:[0,19,30,43,61,72,73,74,95,98,101,102,103,120,121,155,161],track_grad_norm:78,tracking_uri:[98,155,160,161],trade:19,tradeoff:19,tradition:61,traffic:[38,39],train:[20,21,24,26,27,30,35,37,39,54,56,57,58,62,63,65,66,67,69,70,71,73,74,77,78,81,82,83,88,89,90,91,92,93,97,99,101,102,103,104,105,107,109,110,115,116,117,119,120,123,127,128,130,133,134,135,138,142,147,149,150],train_acc_step:[89,157],train_batch:64,train_batch_idx:159,train_batch_s:72,train_d:84,train_data:[64,74,140,148],train_dataload:[5,18,22,32,43,56,72,83,103,107,140,144,147,148],train_dataset:[22,74,84,107,140,148],train_fold:[88,103],train_load:[22,64,72,74,144,147],train_loss:[64,84,85,102,140,147,148],train_on_devic:64,train_out:64,train_set:60,train_set_s:60,train_step:65,train_target_fold:88,train_time_interv:55,trainabl:19,traindir_a:107,traindir_b:107,trainer:[0,3,4,5,6,8,9,11,12,13,15,16,17,18,19,20,21,22,23,26,27,29,31,32,36,37,38,41,52,53,54,55,56,58,60,61,62,65,67,68,69,70,71,74,76,77,78,82,83,84,85,88,90,91,92,93,94,96,97,98,99,100,101,102,104,105,106,107,108,109,110,138,139,140,142,144,145,148,149,151,152,153,154,155,157,158,159,160,161],trainer_2:26,trainer_config:27,trainer_main:63,trainers_per_nod:6,training_batch:72,training_batch_w:84,training_epoch_end:[65,93,102,139,142,149,157],training_opt:11,training_script_setup:39,training_step:[6,12,31,43,55,56,60,65,68,69,72,76,83,84,85,89,98,99,101,102,103,107,108,139,140,142,144,145,147,148,154,155,157,158,160],training_step_end:[6,102,149,152,157],training_step_output:64,training_strategi:[4,106],trainingbatchloop:103,trainingepochloop:103,trainingtypeplugin:54,trajectori:64,transcrib:84,transcript:[84,86],transcript_len:84,transfer:[16,17,19,64,74,86,109,140,148],transfer_batch_to_devic:6,transform:[0,18,19,22,23,31,32,43,48,64,74,84,85,86,107,109,144,149,152],transit:[140,148],translat:90,transpos:154,travers:72,treat:[19,64],tree:[101,103],tri:[6,22,30,48,72,102],trial:48,trick:93,tricki:108,trigger:22,trillion:[1,2,73,91,109,110],trivial:[36,37,38,39],troubleshoot:[15,64],truncat:[64,103],truncated_bptt_step:107,trust:85,tsmodel:20,tunabl:36,tune:[6,10,22,30,31,84,88,90,93,108,115,132,149],tuner:[22,72,77],tupl:[64,74,84,102,107],ture:69,turn:[36,58,72,77,78,103,140,148],tutori:[6,48,64,65,84,108,139,142],twain:84,tweak:[19,67],twice:29,two:[6,11,15,22,26,28,30,43,45,46,48,60,64,65,70,72,84,93,94,102,103,104,107,108,140,146,148,154,157],txt:[9,38],type:[3,16,17,18,19,27,31,36,49,61,63,64,69,70,72,74,77,93,94,96,97,98,99,100,101,103,105,140,148,149,157],type_a:64,typecheck:84,typeerror:[16,17,99],typic:[19,22,72,84,108],u:31,ui:[36,37,38,39,99],ulimit:84,ultim:[65,139,140,142,148],unavail:[16,17],unbalanc:84,uncas:84,uncategor:97,uncommit:30,undefin:93,under:[6,16,17,18,19,20,61,63,64,65,69,92,99,102,103,105,107,108,109,140,144,148],underfit:[91,123,147],understand:[6,29,31,54,91,101,123,124,128,149,154,158],unetplusplu:88,uneven:[61,64],unexpect:[30,55],unfinish:[16,17],unifi:[4,74,106],uniform:48,uninterrupt:72,union:[4,21,61,64,72,92,96,97,98,99,100,101],uniqu:[64,72,93,102,157],unit:[3,12,16,17,64,72,91,110,115,116],univers:[33,47,48,49,126,135],unix:108,unless:[19,64,69,72,74],unlik:[19,53],unnecessarili:108,unscal:[22,64,72],unseen:[60,61],unseri:5,unset:28,unsqueez:[64,81,82],unstabl:67,unsupport:[16,17,102,157],unsur:[140,148],unsustain:31,until:[22,29,61,62,65,72,76,103,140,145,148,157],untoggl:64,unus:[19,64,93],unusu:19,unwatch:101,unzip:45,up:[3,8,11,16,17,18,19,20,22,31,33,36,37,38,39,43,47,49,62,64,65,70,72,74,78,84,101,102,103,128,135,140,148,160],updat:[0,6,19,22,43,55,62,64,65,70,72,83,84,93,94,101,102,103,107,139,142,157],upgrad:18,upload:99,upon:[74,99],upto:108,uri:98,url:[6,52,64,72,151],us:[3,4,6,8,9,11,12,13,14,16,17,18,20,28,29,30,31,32,33,36,38,42,44,45,46,47,48,49,52,53,54,55,56,57,58,60,61,62,63,64,67,69,70,71,73,76,77,78,79,81,82,85,86,87,89,91,92,93,94,96,97,98,99,100,101,102,103,105,106,108,109,111,112,113,117,119,121,122,123,127,128,129,132,136,140,141,144,146,148,149,151,152,153,154,157,158],usabl:[18,53],usag:[19,27,31,108,140,148],use_artifact:101,use_pl_optim:64,use_spot:41,use_tpu:18,usecas:[91,110],user:[2,3,4,6,8,9,11,12,13,15,16,17,18,19,22,23,26,28,29,30,31,32,33,36,38,41,42,47,48,53,55,57,60,61,64,65,67,69,72,76,77,78,82,84,87,92,99,100,101,102,103,104,106,108,139,140,142,144,148,149,151,152,153,154,157,158,159,160],using_lbfg:[64,65],using_native_amp:[64,65],usr:[6,16,17,18],usual:[0,6,18,19,31,32,38,48,61,64,74,108],util:[8,12,13,15,16,17,18,19,20,22,28,31,32,43,54,60,61,63,64,72,74,84,100,102,103,107,108,140,144,147,148],v100:[16,17],v1:[64,65,72,74,84,93,104,139,140,142,148],v2:[8,72,101,140,148],v3:[16,17,72],v:[16,17,64,84],v_num:[102,157],vae:[85,86],val1:101,val2:101,val:[31,56,58,61,64,72,74,77,84,93,103,107],val_acc:64,val_accuraci:[58,101],val_batch:[64,72,103],val_batch_idx:64,val_batch_s:72,val_check_batch:72,val_check_interv:[58,70,107,108],val_checks_per_epoch:72,val_data:[64,74],val_dataload:[56,72,83,103,107,152],val_dataset:74,val_dataset_1:107,val_dataset_2:107,val_load:74,val_loop:[64,103],val_loss:[29,55,58,60,64,72,85,102,103,145],val_out:64,val_set:60,val_split:[88,103],val_step_output:64,valid:[6,19,30,31,41,55,56,58,70,74,77,78,91,93,102,103,109,123,138,144,147,149,158,159],valid_set:60,valid_set_s:60,validate_at_some_point:64,validate_loop:103,validation_d:84,validation_dataset:84,validation_epoch_end:[61,72,102,149,157],validation_loss:[0,55,158],validation_step:[0,6,12,55,56,58,60,61,72,102,103,107,145,149,154,157,158],validation_step_end:[6,102,149,157],validation_step_output:64,validationepochloop:103,valu:[12,16,17,19,20,22,28,29,30,36,53,55,58,62,63,64,65,72,78,92,93,96,97,98,99,100,101,102,103,104,107,108,139,142,149,157,158],valueerror:[72,99],valuer:[16,17],vari:[19,60,157],variabl:[6,16,17,19,29,37,38,39,42,64,77,97,98,99,103,104,107],variant:19,variat:[22,93],varieti:[71,102,108],variou:[2,19,22,49,58,99],ve:[19,40,72,77,88],verbos:[9,58,61,72,93,94],veri:[0,6,19,28,29,55,56,63,64,84,88,104,108],verifi:15,version:[6,16,17,18,19,28,29,30,36,48,53,61,63,64,72,74,76,81,87,93,96,97,98,99,100,101,102,146,157,159],version_0:[19,147,159],version_:[96,100],vgg16:64,via:[0,4,5,6,11,13,19,22,25,29,31,32,36,37,38,39,41,44,45,46,47,49,55,69,72,73,78,84,85,88,92,93,106,107,111,136,140,148],video:[16,17,19,84,101,109],videogpt:85,view:[31,60,64,80,81,84,85,88,99,144,145,147],virtual:[22,43,158],visibl:[5,6,57,70],vision:[90,109,147],visit:84,visual:[19,72,73,91,99,102,123,128,129],vliw:8,vm:[16,17],vocab:[74,84],vocab_fil:84,vocabulari:74,vocder_model:84,vocod:84,voic:84,volta:69,volum:[16,17,19,67],vram:19,vs:[22,102],vstack:[64,81,82],w:[16,17,19,23,101],wa:[16,17,30,41,43,53,61,63,64,65,72,84,93,107,139,140,142,145,148,149],wai:[4,6,12,16,17,19,22,28,30,31,42,43,49,61,63,64,74,80,81,82,88,91,99,103,104,107,108,110,113,140,148,149,158,159],wait:[72,77,108,140,148],wall:154,wandb:[101,102,155,160,161],wandb_logg:[101,155,160,161],wandblogg:[155,160,161],want:[4,5,6,19,27,28,29,31,33,36,41,44,46,47,49,53,55,56,58,60,61,63,64,65,67,70,72,74,76,77,78,81,83,84,87,88,92,93,95,96,97,98,100,101,102,103,106,107,108,139,140,142,145,146,148,151,152,153,154,157,158,159,160],warm:[64,65],warmup_max_lr:19,warmup_min_lr:19,warmup_num_step:19,warmup_step:6,warmuplr:19,warn:[15,57,64,102,108],wasserstein:64,watch:[16,17,84,101,155,160,161],wav:84,waveglow:84,waveglowmodel:84,we:[0,4,6,11,12,16,17,18,19,22,23,24,26,27,28,31,32,38,43,45,46,47,48,53,55,56,58,60,61,62,63,64,65,69,72,74,76,77,78,82,83,84,85,89,90,92,93,94,102,103,105,106,108,140,146,148,149,152,157,158],web:[36,37,38,39],websit:[13,84],week:[41,84],weigh:19,weight:[13,19,20,53,55,61,64,69,72,82,83,84,99,101,103,122],weight_decai:19,weighted_loss:84,weightsharingmodul:15,welcom:[46,84],well:[4,19,28,61,63,71,72,74,102,106,107,108,149,157],went:102,wer_denom:84,wer_num:84,were:[4,16,17,19,21,28,29,63,64,93,94,149],wether:[140,148],what:[0,6,23,28,29,32,54,63,64,65,72,84,93,94,95,99,102,103,108,149],whatev:[61,64,72,74,95,103,140,148],whatever_ml_flow_support:98,wheel:[16,17],when:[0,3,6,11,12,16,17,20,22,26,28,30,36,38,41,43,48,52,53,58,60,61,62,63,64,65,67,69,70,72,74,76,77,78,82,84,92,93,94,95,97,99,100,101,102,103,104,107,108,119,140,144,148,149,154,157,158],whenev:[77,92,93,108,149],where:[6,12,13,15,16,17,19,22,27,28,36,41,43,53,58,62,63,64,65,72,74,88,96,98,99,100,101,102,103,139,142,149,158],wherea:[64,108],whether:[64,72,97,102,103,157],which:[0,3,6,8,11,12,13,15,16,17,19,20,22,26,27,28,29,30,31,36,37,38,39,41,42,43,44,45,46,47,49,52,61,62,63,64,65,69,70,72,74,78,81,84,88,92,93,96,97,98,99,100,101,102,103,104,107,108,139,140,142,144,148,149,154,157,158],whilst:[11,19],whl:[16,17],who:[29,30,31,32,33,38,41,47,60,65,76,77,78,87,92,139,142,144,151,152,153,154,157,158,159,160],whole:[64,72,107,158],whose:[29,64],why:[6,43,97,102,157],width:[74,158],wild:103,william:64,williamfalcon:46,window:158,wine:101,wish:[61,64,70,72,74,92,93,103,140,148],within:[0,6,8,11,19,41,43,58,61,63,64,69,70,72,74,87,90,102,103,140,145,148,149],without:[0,6,19,20,28,30,31,32,45,46,48,64,65,72,81,83,90,92,93,100,102,108,122,141,144,149,158],won:[6,15,43,55,64,65,72,74,78,93,97,108,139,142],word:[11,84],work:[4,6,12,15,16,17,18,19,22,25,32,40,42,43,47,53,55,61,63,64,65,71,72,74,76,78,85,87,92,93,99,102,103,107,108,139,142,145,147,149,158],worker:[0,6,16,17,19,43,72,107,140,148,149],workflow:[45,46,48,49,59],workload:[8,12,16,17,27,33,43],workspac:[97,99,155,160,161],world:[19,23,37,60,61,64,72,158],world_siz:[6,37,38,64,72,140,148,157],worri:[19,140,148,149],wors:58,worst:43,worth:6,would:[6,16,17,18,19,22,23,28,29,30,48,61,64,72,88,93,94,102,103,107,108,140,145,148,157],wrap:[19,20,31,43,64,65,74,107,139,142],wrapper:[6,19],write:[28,29,30,52,64,71,72,103,104,157,159],writer:100,written:[6,19,22,72,140,148],wrong:6,x12:8,x:[0,6,11,15,19,20,23,26,31,53,55,56,60,64,65,72,74,77,80,81,82,83,84,85,89,102,103,107,108,139,140,142,144,145,146,147,148,149,153,157],x_hat:[0,56,60,64,144,147],x_length:84,xarg:[16,17],xception:88,xla:[14,16,17,18,64,72,108,117,151],xla_client:[16,17],xla_devic:[16,17],xla_dist:[16,17,18,72,108],xla_model:[16,17,18],xla_replication_devic:[16,17],xla_use_bf16:[72,108],xlacheckpointio:54,xlaprofil:151,xlm:19,xm:[16,17,18,54],xmp:[16,17],xpu:92,xpuacceler:92,xpulib:92,xrt:[16,17,18],xrt_tpu_config:[16,17,18],xrt_world_siz:18,xu:[16,17],y:[0,6,26,55,60,64,74,77,83,84,85,89,103,107,144,145,147,153,157],y_0:6,y_1:6,y_2:6,y_3:6,y_hat:[6,53,55,64,82,83,103,145],y_length:84,y_log:84,y_m:84,yaml:[25,29,31,61,63,64,73,87,91,96,110,111],ye:67,yet:[4,22,55,64,69,93,99,103,106,146],yield:[22,43,103],you:[0,3,4,5,6,8,9,11,12,13,16,17,18,19,20,21,22,23,26,27,28,31,32,36,37,38,39,40,41,42,43,44,45,46,47,48,49,52,53,55,57,58,61,62,63,64,65,67,68,69,70,71,74,76,77,78,80,81,82,83,84,85,86,87,88,91,92,93,94,95,96,97,98,99,100,101,102,103,105,107,108,109,112,114,123,130,131,132,133,136,139,140,141,142,144,146,148,149,151,152,154,155,157,158,159,160,161],young:84,your:[0,1,4,5,7,10,11,12,13,14,16,17,18,19,20,21,22,23,27,28,29,30,31,32,33,35,38,39,41,43,44,45,46,48,53,54,55,57,58,61,63,64,66,67,68,69,70,72,73,74,83,84,85,86,87,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,105,107,108,109,110,113,115,119,120,123,124,126,128,130,134,135,140,141,144,146,147,148,149,155,156,157,158,160,161],your_class_path:28,your_comet_api_kei:[155,160,161],your_lightning_training_script:6,your_main_address:[72,140,148],your_main_port:[72,140,148],your_trainer_fil:[72,108],yourmodel:5,yourse:6,yourself:[18,28,37,64,103,149],z:[0,60,64,65,84,139,142,144,145,147,149],zero3:[140,148],zero:[4,21,22,30,62,64,65,72,90,103,108,139,140,142,145,148],zero_allow_untested_optim:19,zero_grad:[19,64,65,72,93,103,108,139,140,142,144,147,148],zero_optim:19,zip:[45,46,84,103,146],zone:[16,17]},titles:["Hardware agnostic training (preparation)","Accelerator: GPU training","GPU training (Advanced)","GPU training (Basic)","GPU training (Expert)","GPU training (FAQ)","GPU training (Intermediate)","Accelerator: HPU training","Accelerator: HPU training","Accelerator: HPU training","Accelerator: IPU training","Accelerator: IPU training","Accelerator: IPU training","Accelerator: IPU training","Accelerator: TPU training","TPU training (Advanced)","TPU training (Basic)","TPU training (Basic)","TPU training (Intermediate)","Train 1 trillion+ parameter models","Pruning and Quantization","Strategy Registry","Effective Training Techniques","Transfer Learning","Benchmark with vanilla PyTorch","Eliminate config boilerplate","Eliminate config boilerplate (Advanced)","Eliminate config boilerplate (Advanced)","Instantiation only mode","Eliminate config boilerplate (Advanced)","Eliminate config boilerplate (expert)","Eliminate config boilerplate (Intermediate)","Eliminate config boilerplate (intermediate)","Train on the cloud","Train on the cloud (intermediate)","Run on an on-prem cluster","Run on an on-prem cluster (advanced)","Run on an on-prem cluster (expert)","Run on an on-prem cluster (intermediate)","Run on an on-prem cluster (intermediate)","Fault-tolerant Training","Fault-tolerant Training (basic)","Fault-tolerant Training (expert)","Fault-tolerant Training (FAQ)","Cost","Train on the cloud (advanced)","Train on the cloud (intermediate)","Train on the cloud (expert)","Train on the cloud (intermediate)","Train on the cloud (basic)","Train on the cloud (basic)","Checkpointing","Checkpointing (advanced)","Checkpointing (basic)","Checkpointing (expert)","Checkpointing (intermediate)","<no title>","Console logging","Early Stopping","Add validation and test datasets","Validate and test a model (basic)","Validate and test a model (intermediate)","<no title>","Configure hyperparameters from the CLI","LightningModule","Optimization","N-Bit Precision","N-Bit Precision (Basic)","N-Bit Precision (Expert)","N-Bit Precision (Intermediate)","Customize the progress bar","Remote Filesystems","Trainer","Common Workflows","LightningDataModule","Debug your model","Debug your model (advanced)","Debug your model (basic)","Debug your model (intermediate)","Deploy models into production","Deploy models into production (advanced)","Deploy models into production (advanced)","Deploy models into production (basic)","Deploy models into production (intermediate)","Conversational AI","Lightning Bolts","Community Examples","Ecosystem CI","Lightning Flash","TorchMetrics","Lightning Transformers","Level up","Accelerator","Callback","Save Callback state","Save DataModule state","CSVLogger","CometLogger","MLFlowLogger","NeptuneLogger","TensorBoardLogger","WandbLogger","Logging","Loops","Loops (Advanced)","Plugins","What is a Strategy?","Managing Data","Speed Up Model Training","\u26a1 PyTorch Lightning\uc5d0 \uc624\uc2e0 \uac83\uc744 \ud658\uc601\ud569\ub2c8\ub2e4!","Advanced skills","Level 15: Customize configs to run in production","Level 16: Customize the trainer","Level 17: Own the training loop","Level 18: Enable advanced checkpointing","Level 19: Explore IPUs","Level 19: Explore HPUs","Level 21: Master TPUs","Level 22: Reach 1 trillion parameters on GPUs","Level 2: Add a validation and test set","Level 5: Debug, visualize and find performance bottlenecks","Level 3: Visualize training progress","Level 6: Predict with your model","Basic skills","Expert skills","Level 23: Extend the Lightning CLI","Level 24: Integrate a custom cluster","Level 27: Add a new accelerator or Strategy","Intermediate skills","Level 10: Understand your model","Level 11: Explore SOTA scaling techniques","Level 12: Deploy your models","Level 13: Optimize training speed","Level 14: Run on on-prem clusters","Level 7: Interactive cloud development","Level 8: Run models on the cloud","Level 9: Modularize your projects","<no title>","Build a Model","Own your loop (advanced)","Raw PyTorch loop (expert)","Supercharge training (intermediate)","Manual Optimization","Use a pure PyTorch training loop","Train a model (basic)","How to Organize PyTorch Into Lightning","Installation","Lightning 15\ubd84 \ub9cc\uc5d0 \ubc30\uc6cc\ubcf4\uae30","LightningLite (Stepping Stone to Lightning)","\uc2a4\ud0c0\uc77c \uac00\uc774\ub4dc","Find bottlenecks in your code","Find bottlenecks in your code (advanced)","Find bottlenecks in your code (basic)","Find bottlenecks in your code (expert)","Find bottlenecks in your code (intermediate)","Manage Experiments","Track and Visualize Experiments","Track and Visualize Experiments (advanced)","Track and Visualize Experiments (basic)","Track and Visualize Experiments (expert)","Track and Visualize Experiments (intermediate)","Comet.ml"],titleterms:{"0":[45,46,151],"1":[3,8,12,16,17,19,45,46,49,85,88,89,118,145,147,151],"10":[19,129],"11":130,"12":131,"13":132,"14":133,"15":111,"15\ubd84":147,"16":[6,13,18,67,72,108,112],"17":113,"18":114,"19":[115,116],"2":[6,19,45,46,85,88,89,119,145,147,151],"21":117,"22":118,"23":125,"24":126,"27":127,"3":[19,45,46,85,88,89,121,145,147,151],"32":67,"4":[45,46,145,147,151],"5":[120,145,147],"6":[122,145,147],"64":67,"7":[134,145,147],"8":[69,135,145],"9":136,"\uac00\uc774\ub4dc":149,"\uac80\uc0c9":109,"\uac83\uc744":109,"\uadf9\ub300\ud654\ud558\uae30":147,"\ub2e4\uc74c":147,"\ub2e8\uacc4":147,"\ub370\uc774\ud130\uc14b":147,"\ub9cc\uc5d0":147,"\ubaa8\ub378":147,"\ubc0f":109,"\ubc18\ubcf5":147,"\ubc30\uc6cc\ubcf4\uae30":147,"\ube60\ub974\uac8c":147,"\uc0ac\uc6a9\uc790":147,"\uc0ac\uc6a9\uc790\ub77c\uba74":109,"\uc0ac\uc6a9\ud558\uae30":147,"\uc0c9\uc778":109,"\uc124\uce58\ud558\uae30":[109,147],"\uc2a4\ud0c0\uc77c":149,"\uc2dc\uac01\ud654\ud558\uae30":147,"\uc5c4\uccad":147,"\uc624\uc2e0":109,"\uc720\uc5f0\uc131":147,"\uc774\ubbf8":109,"\uc790\uccb4\uc758":147,"\uc815\uc758\ud558\uae30":147,"\ucc98\uc74c\uc774\uc2e0\uac00\uc694":109,"\ud559\uc2b5":147,"\ud559\uc2b5\ud558\uae30":147,"\ud655\uc7a5\ud558\uae30":147,"\ud658\uc601\ud569\ub2c8\ub2e4":109,"class":[28,29,72],"default":[27,28,29,102,103,157],"do":[5,32,43,49,74,152,158],"function":[89,152],"import":144,"lightning\uc5d0":109,"new":[87,103,127],"return":107,"static":19,If:72,In:157,Into:[108,145],Of:84,The:84,To:84,acceler:[1,7,8,9,10,11,12,13,14,67,72,92,108,127,140,148,152],access:[8,12,16,17,65,107,139,142],accumul:[22,65,139,142,158],accumulate_grad_batch:72,across:22,action:153,activ:19,add:[59,60,119,127,144],add_dataloader_idx:157,add_to_queu:64,addit:[61,107],adjust:5,advanc:[2,11,15,19,22,25,26,27,29,36,45,52,76,79,80,81,91,103,104,110,114,139,151,157],after:61,agnost:0,ai:[84,155,160,161],all:77,all_gath:64,amp_backend:72,amp_level:72,an:[8,12,19,35,36,37,38,39,58,159],analys:13,ani:[23,32,145],anomali:78,anywher:[42,52],apex:[69,72],api:[64,72,74,92,93,103,109],ar:[43,72],arbitrari:65,arg:63,argpars:63,argument:[27,28],argumentpars:63,art:84,artifact:[102,160],asr:84,associ:103,audio:160,auto:36,auto_lr_find:72,auto_scale_batch_s:72,auto_select_gpu:72,autocast:[140,148],autograd:78,automat:[8,65,84,102,157],automatic_optim:64,avail:[4,103,106],averag:22,avoid:108,aw:8,backend:18,background:[45,46],backpropag:107,backward:[64,140,148],bagua:6,bar:[70,102,157,159],barrier:[140,148],basic:[3,16,17,25,41,49,50,53,60,67,72,77,79,82,91,123,144,152,158],batch:[22,65],batch_siz:157,behavior:[42,55,102,157],benchmark:[24,72],bert:23,best:[63,93],bfloat16:69,bias:[155,160,161],billion:19,bit:[6,13,18,66,67,68,69,72,108],boilerpl:[25,26,27,29,30,31,32],bolt:85,both:157,bottleneck:[120,150,151,152,153,154],boundari:22,breakpoint:77,bring:65,browser:158,bucket:19,build:[36,138,153,159],built:[22,54,93,103],cach:108,call:[0,145],callback:[28,29,58,72,85,93,94,102,157],callback_metr:72,captur:151,caveat:6,chang:[157,159],chart:160,check:[77,108],check_val_every_n_epoch:72,checkpoint:[19,51,52,53,54,55,82,83,114],checkpoint_callback:72,checkpointio:105,child:64,choos:[3,19,45,46],ci:87,clear:108,cli:[26,30,31,63,125],clip:[22,65],clone:49,closur:[65,139,142],cloud:[16,17,27,33,34,41,45,46,47,48,49,50,52,134,135,151,157],cluster:[19,35,36,37,38,39,42,47,105,126,133],code:[77,78,140,145,148,150,151,152,153,154],colab:[5,16,17],collat:19,comet:[155,160,161],cometlogg:97,command:27,commandlin:158,common:[73,109],commun:[6,19,86],comparison:24,compil:[80,81],complic:82,compos:26,comput:[23,145],concaten:107,conda:146,condit:55,config:[19,25,26,27,29,30,31,32,111],configur:[29,63,65,84,88,102,145,158],configure_callback:64,configure_gradient_clip:64,configure_optim:64,configure_sharded_model:64,connect:[29,31],consol:[57,102],contain:[107,149],content:53,control:[85,102,108],convers:84,convert:[140,148],core:[16,17,109],cost:[41,44,46,48,49],coverag:146,cpu:108,creat:[4,45,46,92,103,106],csvlogger:96,cuda:[0,145],current_epoch:[64,72],custom:[4,9,19,27,29,54,65,70,92,102,106,111,112,126,139,153,157,159],data:[6,60,78,88,107,108,145,149],dataload:[43,61,107,108,149],datamodul:[32,74,95,149],dataparallel:64,dataset:[22,28,32,43,45,46,59,107,144],ddp2:6,ddp:[6,19,108],debug:[38,75,76,77,78,120],deepspe:19,default_root_dir:72,defin:[60,144],delet:0,deploi:[79,80,81,82,83,131],design:36,detect:78,determinist:72,develop:[84,134],devic:[3,5,64,72,108,140,145,148],differ:[4,106],dimens:77,directli:27,directori:158,disabl:53,distribut:[6,18,19,55,76,82,140,148,154,157],distributedsampl:18,doe:77,don:151,done:103,dp:[6,108],earli:[58,108],earlystop:58,ecosystem:[86,87],effect:22,effici:19,elast:6,elimin:[25,26,27,29,30,31,32,144],enabl:[4,9,19,27,31,36,42,57,82,106,114,141,157],enable_checkpoint:72,enable_graph:157,enable_model_summari:72,enable_progress_bar:72,entiti:84,entri:103,environ:[27,105],epoch:[58,64,77,108,157],error:5,estimated_stepping_batch:72,evalu:107,everi:[108,152],exampl:[23,64,85,86,89,93,103,109,140,148],example_input_arrai:64,exclud:63,exist:[140,148],exp:[155,160,161],experi:[84,155,156,157,158,159,160],expert:[4,30,37,42,47,54,68,91,124,140,153,159],explod:78,explor:[115,116,130],extend:[85,125,141],extract:83,fairscal:19,fals:19,faq:[5,16,17,43],fast_dev_run:72,faster:85,fault:[40,41,42,43],featur:[103,141],file:[19,26,27,29,84],filesystem:[71,102,157],find:[60,120,150,151,152,153,154],find_unused_paramet:19,finder:22,fine:19,finetun:88,fit:[61,72],flag:[72,140,148],flash:[88,103],flush:157,flush_logs_every_n_step:72,forc:29,forward:[64,149],fp16:69,free:47,freez:64,frequenc:[102,108,157],from:[26,27,32,52,53,63,83,84,102,103,146],fulli:19,gan:[65,139,142],gaudi:8,gcp:[16,17],get:[5,36,37,38,39,109],get_from_queu:64,global_rank:64,global_step:[64,72],glossari:109,good:145,googl:[16,17],gpu:[1,2,3,4,5,6,19,22,72,108,118,140,148],grad:108,gradient:[19,22,65,78,139,142],gradient_clip_v:72,graph:[13,19],grid:[45,46,48],group:27,hand:109,happen:43,hardwar:0,hassl:47,help:[36,37,38,39,77],hood:[72,84],hook:[19,64,93,107],horovod:6,how:[5,8,12,16,17,43,77,145],hparam:64,hpu:[7,8,9,116],hyperparamet:[53,63,74,102,160],i:[5,19,32,43,49,74,78,152,158],ightningmodul:157,imag:158,imagenet:23,impact:43,implement:6,infer:[64,82],infin:19,init:[0,53,72,149],initi:19,input:[77,107],instal:[45,46,84,146,151],instanti:28,instantli:19,integr:[37,87,126,159],interact:134,interest:[72,153],intermedi:[6,18,31,32,34,38,39,46,48,55,61,69,78,83,91,128,141,154,160],interpol:27,interv:65,io:54,ipu:[10,11,12,13,115],is_global_zero:72,item:108,iter:[43,107],job:38,jupyt:[5,49],kaggl:[16,17],keep:145,know:145,known:[8,12],languag:84,layer:77,lbfg:[65,139,142],learn:[5,22,23,65,84,139,140,142,148],length:77,level:[64,91,109,111,112,113,114,115,116,117,118,119,120,121,122,125,126,127,129,130,131,132,133,134,135,136],lightn:[22,31,45,46,63,74,77,83,85,88,89,90,103,107,109,125,140,145,146,147,148],lightningcli:[29,30],lightningdatamodul:[74,107],lightninglit:[140,148],lightningmodul:[23,32,53,63,64,77,82,102,107,144,147,149,157],like:[65,139,142],limit:[8,12],limit_test_batch:72,limit_train_batch:72,limit_val_batch:72,list:48,lite:[140,148],load:[82,88,140,148],load_from_checkpoint:[63,64],load_state_dict:[74,93],local_rank:64,log:[0,57,64,102,151,157],log_dict:64,log_dir:72,log_every_n_step:72,logged_metr:72,logger:[64,72,102,157],logic:[82,145],look:78,loop:[60,64,103,104,113,139,140,143,144,147,152],lr:[22,32,145],lr_schedul:64,m:5,machin:[6,49],make:[0,102],manag:[36,45,46,84,102,107,155,159,160,161],manual:[55,65,102,139,142],manual_backward:64,master:117,max_epoch:72,max_step:72,max_tim:72,maximum:19,me:77,measur:152,memori:19,method:[64,72,103,140,148,149],metric:[64,89,157,158,160],min_epoch:72,min_step:72,minut:49,mix:[9,13,32,69,108],ml:[78,155,160,161],mlflow:[155,160,161],mlflowlogg:98,mode:28,model:[0,11,19,28,31,32,45,46,49,60,61,75,76,77,78,79,80,81,82,83,84,85,88,108,122,129,131,135,138,144,149,151,154,157,160],modifi:[52,55,102,157],modul:[19,23,53,63,64,83,89,144],modular:[52,136],monei:41,monitor:[45,46],more:84,move:145,multi:[6,19],multipl:[3,5,8,12,16,17,28,43,61,63,65,107,139,142,155,160,161],my:[43,140,148],n:[66,67,68,69,108],name:84,nativ:69,natur:84,nccl:19,need:[49,74,152,158],nemo:84,neptun:[155,160,161],neptunelogg:99,ner:84,neural:84,next:[45,46,48,49],nlp:[23,84],nn:[23,53,83,144],node:19,non:102,none:108,normal:83,notebook:[5,49],num_nod:[72,140,148],num_process:72,num_sanity_val_step:72,num_work:108,numpi:108,nvidia:69,nvme:19,object:107,offload:19,on_after_backward:[64,93],on_after_batch_transf:[64,74],on_before_backward:[64,93],on_before_batch_transf:[64,74],on_before_optimizer_step:[64,93],on_before_zero_grad:[64,93],on_configure_sharded_model:93,on_epoch:157,on_except:93,on_fit_end:[64,93],on_fit_start:[64,93],on_hpc_load:64,on_hpc_sav:64,on_init_end:93,on_init_start:93,on_keyboard_interrupt:93,on_load_checkpoint:[64,93],on_post_move_to_devic:64,on_predict_batch_end:[64,93],on_predict_batch_start:[64,93],on_predict_dataload:[64,74],on_predict_end:[64,93],on_predict_epoch_end:[64,93],on_predict_epoch_start:[64,93],on_predict_start:[64,93],on_sanity_check_end:93,on_sanity_check_start:93,on_save_checkpoint:[64,93],on_step:157,on_test_batch_end:[64,93],on_test_batch_start:[64,93],on_test_dataload:[64,74],on_test_end:[64,93],on_test_epoch_end:[64,93],on_test_epoch_start:[64,93],on_test_model_ev:64,on_test_model_train:64,on_test_start:[64,93],on_train_batch_end:[64,93],on_train_batch_start:[64,93],on_train_dataload:[64,74],on_train_end:[64,93],on_train_epoch_end:[64,93],on_train_epoch_start:[64,93],on_train_start:[64,93],on_val_dataload:[64,74],on_validation_batch_end:[64,93],on_validation_batch_start:[64,93],on_validation_end:[64,93],on_validation_epoch_end:[64,93],on_validation_epoch_start:[64,93],on_validation_model_ev:64,on_validation_model_train:64,on_validation_start:[64,93],onc:77,onli:28,onnx:80,open:49,oper:[64,154],optim:[6,16,17,19,22,28,32,64,65,69,132,139,142,145],optimizer:65,optimizer_step:64,optimizer_zero_grad:64,option:[11,27,103,145],order:149,organ:145,other:[53,160],out:78,output:77,over:108,overfit:78,overfit_batch:72,overrid:103,own:[6,37,42,47,65,113,139,142,145,153,159],pack:107,packag:32,parallel:[6,11],paramet:[19,53,118],part:43,perform:[16,17,43,120],persist:[104,108],pickl:5,pickleabl:0,pip:146,pitfal:[140,148],plugin:[54,68,72,105,140,148],pod:18,point:103,popvis:13,practic:[63,93],pre:[19,61,84],prebuilt:85,precis:[6,9,13,18,64,66,67,68,69,72,105,108,140,148],predict:[64,72,82,107,122,145],predict_dataload:[64,74],predict_step:64,prefer:108,preload:108,prem:[35,36,37,38,39,133],prepar:0,prepare_data:[64,74],prepare_data_per_nod:[64,74],pretrain:[23,85],print:[26,64,77,140,148],process:[22,84],process_posit:72,product:[64,79,80,81,82,83,111],profil:[72,151,152,153,154],prog_bar:157,progress:[70,102,121,157,159],progress_bar_metr:72,project:[87,136],properti:[64,72,93,103],prune:20,pure:143,python:72,pytorch:[23,24,69,83,86,109,140,143,144,145,147,148,154],qualiti:85,quantiz:20,quickli:77,ram:108,random:48,rang:48,rank_zero_onli:157,rate:[5,22,65,139,142],raw:140,reach:[19,118],readi:103,recognit:84,reduc:19,reduce_fx:157,regist:[32,92],register_buff:0,registr:28,registri:[4,21],relat:5,reload_dataloaders_every_n_epoch:72,remot:[71,102],remov:[0,145],replace_sampler_ddp:72,reproduc:[30,72],requir:151,research:[64,85],reset:103,resubmit:36,resum:[52,53],resume_from_checkpoint:72,richprogressbar:[70,159],run:[8,12,16,17,26,27,35,36,37,38,39,45,46,47,49,77,103,111,133,135,140,148],s:[22,145],same:160,sampl:48,sampler:0,saniti:77,save:[41,52,53,55,74,93,94,95,140,148,158],save_hyperparamet:[63,64],scalar:102,scale:130,schedul:[32,65,139,142,145],scratch:[84,103],script:[36,38,72],search:48,seed_everyth:[140,148],select:8,self:[149,157],sequenc:107,sequenti:107,set:[19,27,77,108,119],setup:[38,64,74,93,140,148,151],setup_dataload:[140,148],shard:19,share:[15,22],shortcut:48,shorten:77,should:[5,78],shouldn:19,shuffl:43,singl:[19,26],size:22,skill:[91,110,123,124,128],skip:103,slrum:36,slurm:36,sota:130,sourc:146,spawn:[6,108],specif:[16,17],specifi:84,speech:84,speed:[48,49,108,132,157],speedup:108,split:60,stage:19,start:[48,49,109,151],starter:64,state:[53,72,74,84,93,94,95,104],state_dict:[74,93],state_kei:93,step:[45,46,48,49,65,82,88,140,148,157],stochast:22,stone:[140,148],stop:[58,108,151],strategi:[4,6,19,21,48,72,106,127,140,148],string:48,subclass:28,subcommand:30,subloop:103,submit:38,submodul:28,subset:78,summari:77,supercharg:141,support:[67,102],sweep:48,sync_batchnorm:72,sync_dist:157,sync_dist_group:157,synchron:0,syntax:48,system:149,t:[19,151],tbptt:107,tbptt_split_batch:64,teardown:[64,74,93],techniqu:[22,130],tensor:[0,108],tensorboard:[151,155,160,161],tensorboardlogg:100,test:[0,59,60,61,64,72,107,119,145],test_dataload:[64,74],test_epoch_end:64,test_step:64,test_step_end:64,text:[84,158],thing:108,through:107,time:[19,24,36,107,152],tip:[19,108],to_devic:[140,148],to_onnx:64,to_torchscript:64,toggl:108,toggle_optim:64,token:84,toler:[40,41,42,43],topolog:160,torch:6,torchdistribut:39,torchmetr:89,torchscript:81,total:65,tpu:[14,15,16,17,18,108,117,151],tpu_cor:[72,140,148],tqdmprogressbar:[70,159],track:[156,157,158,159,160],track_grad_norm:72,train:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,22,31,33,34,36,38,40,41,42,43,45,46,47,48,49,50,52,53,55,60,61,64,72,84,106,108,113,121,132,139,140,141,143,144,145,148,152,157],train_dataload:[64,74],trainer:[28,63,64,72,103,112,141,147],training_epoch_end:64,training_step:[64,149],training_step_end:64,transfer:[23,108],transfer_batch_to_devic:[64,74],transform:90,trillion:[19,118],troubleshoot:30,truncat:107,truncated_bptt_step:64,tt:84,tune:[19,72],tutori:103,two:29,ty:15,type:[28,29,84],type_a:0,under:[72,84],understand:[103,129,157],unfreez:64,untoggle_optim:64,up:[91,108,109,157],us:[0,5,19,22,23,25,26,27,41,43,65,72,74,83,84,88,90,107,139,142,143,145,155,159,160,161],usag:152,val_check_interv:72,val_dataload:[64,74],valid:[0,59,60,61,64,72,107,108,119,145],validation_epoch_end:64,validation_step:64,validation_step_end:64,vanilla:24,variabl:27,via:27,view:[19,151,158],vision:23,visual:[120,121,154,156,157,158,159,160],vm:18,vs:[19,48,49,149],wall:36,wandblogg:101,want:32,weight:[15,22,77,155,160,161],weights_save_path:72,weights_summari:72,what:[3,4,5,8,12,16,17,26,30,31,41,43,45,46,48,53,55,69,74,106],when:[5,19,55],where:55,which:55,why:[32,49,74,78,107,152,158],within:[107,108,152],without:74,worker:108,workflow:[73,109],write:[26,102],xla:15,yaml:[26,27,84],you:[72,145],your:[6,36,37,42,47,49,52,65,75,76,77,78,80,81,82,88,122,129,131,136,139,142,145,150,151,152,153,154,159],zero:19}}) \ No newline at end of file diff --git a/docs/source-app/Makefile b/docs/source-app/Makefile new file mode 100644 index 0000000..268e095 --- /dev/null +++ b/docs/source-app/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -T -W +SPHINXBUILD = sphinx-build +SOURCEDIR = . +BUILDDIR = ../build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/source/_static/copybutton.js b/docs/source-app/_static/copybutton.js similarity index 100% rename from source/_static/copybutton.js rename to docs/source-app/_static/copybutton.js diff --git a/docs/source-app/_static/images/icon.svg b/docs/source-app/_static/images/icon.svg new file mode 100644 index 0000000..e88fc19 --- /dev/null +++ b/docs/source-app/_static/images/icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/source-app/_static/images/logo-large.svg b/docs/source-app/_static/images/logo-large.svg new file mode 100644 index 0000000..39531f9 --- /dev/null +++ b/docs/source-app/_static/images/logo-large.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/source-app/_static/images/logo-small.svg b/docs/source-app/_static/images/logo-small.svg new file mode 100644 index 0000000..1f523a5 --- /dev/null +++ b/docs/source-app/_static/images/logo-small.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/source-app/_static/images/logo.png b/docs/source-app/_static/images/logo.png new file mode 100644 index 0000000..392c965 Binary files /dev/null and b/docs/source-app/_static/images/logo.png differ diff --git a/docs/source-app/_static/images/logo.svg b/docs/source-app/_static/images/logo.svg new file mode 100644 index 0000000..60efaa2 --- /dev/null +++ b/docs/source-app/_static/images/logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/docs/_static/main.css b/docs/source-app/_static/main.css similarity index 100% rename from docs/_static/main.css rename to docs/source-app/_static/main.css diff --git a/docs/source-app/_templates/classtemplate.rst b/docs/source-app/_templates/classtemplate.rst new file mode 100644 index 0000000..5b7f465 --- /dev/null +++ b/docs/source-app/_templates/classtemplate.rst @@ -0,0 +1,9 @@ +.. role:: hidden + :class: hidden-section +.. currentmodule:: {{ module }} + + +{{ name | underline }} + +.. autoclass:: {{ name }} + :members: diff --git a/docs/source-app/_templates/classtemplate_no_index.rst b/docs/source-app/_templates/classtemplate_no_index.rst new file mode 100644 index 0000000..858c37b --- /dev/null +++ b/docs/source-app/_templates/classtemplate_no_index.rst @@ -0,0 +1,12 @@ +:orphan: + +.. role:: hidden + :class: hidden-section +.. currentmodule:: {{ module }} + + +{{ name | underline }} + +.. autoclass:: {{ name }} + :members: + :noindex: diff --git a/docs/source-app/_templates/layout.html b/docs/source-app/_templates/layout.html new file mode 100644 index 0000000..dfb2c26 --- /dev/null +++ b/docs/source-app/_templates/layout.html @@ -0,0 +1,10 @@ +{% extends "!layout.html" %} + + +{% block footer %} +{{ super() }} + + +{% endblock %} diff --git a/docs/source-app/_templates/theme_variables.jinja b/docs/source-app/_templates/theme_variables.jinja new file mode 100644 index 0000000..914f8dc --- /dev/null +++ b/docs/source-app/_templates/theme_variables.jinja @@ -0,0 +1,18 @@ +{%- set external_urls = { + 'github': 'https://github.com/Lightning-AI/lightning', + 'github_issues': 'https://github.com/Lightning-AI/lightning/issues', + 'contributing': 'https://github.com/Lightning-AI/lightning/blob/master/.github/CONTRIBUTING.md', + 'governance': 'https://github.com/Lightning-AI/lightning/blob/master/docs/source-pytorch/governance.rst', + 'docs': 'https://lightning.rtfd.io/en/latest', + 'twitter': 'https://twitter.com/PyTorchLightnin', + 'discuss': 'https://discord.gg/VptPCZkGNa', + 'tutorials': 'https://pt-lightning.readthedocs.io/en/latest/#tutorials', + 'previous_pytorch_versions': 'https://pt-lightning.rtfd.io/en/latest/', + 'home': 'https://lightning.ai/', + 'get_started': 'https://pt-lightning.readthedocs.io/en/latest/introduction_guide.html', + 'features': 'https://pt-lightning.rtfd.io/en/latest/', + 'blog': 'https://www.pytorchlightning.ai/blog', + 'resources': 'https://pt-lightning.readthedocs.io/en/latest/#community-examples', + 'support': 'https://pt-lightning.rtfd.io/en/latest/', +} +-%} diff --git a/docs/source-app/api_reference/components.rst b/docs/source-app/api_reference/components.rst new file mode 100644 index 0000000..69d53b7 --- /dev/null +++ b/docs/source-app/api_reference/components.rst @@ -0,0 +1,35 @@ +######################## +lightning.app.components +######################## + +.. contents:: + :depth: 1 + :local: + :backlinks: top + +.. currentmodule:: lightning.app.components + + +Built-in Components +___________________ + +.. autosummary:: + :toctree: generated/ + :nosignatures: + :template: classtemplate.rst + + ~database.client.DatabaseClient + ~database.server.Database + ~python.popen.PopenPythonScript + ~python.tracer.TracerPythonScript + ~training.LightningTrainerScript + ~serve.gradio_server.ServeGradio + ~serve.serve.ModelInferenceAPI + ~serve.python_server.PythonServer + ~serve.streamlit.ServeStreamlit + ~multi_node.base.MultiNode + ~multi_node.fabric.FabricMultiNode + ~multi_node.pytorch_spawn.PyTorchSpawnMultiNode + ~multi_node.trainer.LightningTrainerMultiNode + ~serve.auto_scaler.AutoScaler + ~serve.auto_scaler.ColdStartProxy diff --git a/docs/source-app/api_reference/core.rst b/docs/source-app/api_reference/core.rst new file mode 100644 index 0000000..324f3c4 --- /dev/null +++ b/docs/source-app/api_reference/core.rst @@ -0,0 +1,26 @@ +:orphan: + +################## +lightning.app.core +################## + +.. contents:: + :depth: 1 + :local: + :backlinks: top + +.. currentmodule:: lightning.app.core + +Core APIs +___________________ + +.. autosummary:: + :toctree: api/ + :nosignatures: + :template: classtemplate.rst + + LightningApp + LightningFlow + LightningWork + +Learn more about :ref:`Lightning Core `. diff --git a/docs/source-app/api_reference/frontend.rst b/docs/source-app/api_reference/frontend.rst new file mode 100644 index 0000000..514b2cf --- /dev/null +++ b/docs/source-app/api_reference/frontend.rst @@ -0,0 +1,25 @@ +###################### +lightning.app.frontend +###################### + +.. contents:: + :depth: 1 + :local: + :backlinks: top + +.. currentmodule:: lightning.app.frontend + +Lightning FrontEnds +___________________ + +.. autosummary:: + :toctree: generated/ + :nosignatures: + :template: classtemplate.rst + + ~frontend.Frontend + ~web.StaticWebFrontend + ~stream_lit.StreamlitFrontend + ~panel.PanelFrontend + +Learn more about :ref:`Frontend's `. diff --git a/docs/source-app/api_reference/runners.rst b/docs/source-app/api_reference/runners.rst new file mode 100644 index 0000000..f7e550b --- /dev/null +++ b/docs/source-app/api_reference/runners.rst @@ -0,0 +1,21 @@ +##################### +lightning.app.runners +##################### + +.. contents:: + :depth: 1 + :local: + :backlinks: top + +.. currentmodule:: lightning.app.runners + +Lightning Core +______________ + +.. autosummary:: + :toctree: generated/ + :nosignatures: + :template: classtemplate.rst + + ~cloud.CloudRuntime + ~multiprocess.MultiProcessRuntime diff --git a/docs/source-app/api_reference/storage.rst b/docs/source-app/api_reference/storage.rst new file mode 100644 index 0000000..3173914 --- /dev/null +++ b/docs/source-app/api_reference/storage.rst @@ -0,0 +1,71 @@ +##################### +lightning.app.storage +##################### + +Lightning Core +______________ + +.. contents:: + :depth: 1 + :local: + :backlinks: top + +.. currentmodule:: lightning.app.storage + +.. autosummary:: + :toctree: generated/ + :nosignatures: + :template: classtemplate.rst + + ~path.Path + ~drive.Drive + ~payload.Payload + ~mount.Mount + +---- + +************************ +Learn more about Storage +************************ + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Learn about the differences between Drive vs Path. + :description: Learn about their differences. + :col_css: col-md-4 + :button_link: ../glossary/storage/differences.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: The Drive Object. + :description: Put, List and Get Files From a Shared Drive Disk. + :col_css: col-md-4 + :button_link: ../glossary/storage/drive.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: The Path Object. + :description: Transfer Files From One Component to Another by Reference. + :col_css: col-md-4 + :button_link: ../glossary/storage/path.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: The Mount Object. + :description: Mount an AWS S3 Bucket When Running on the Cloud. + :col_css: col-md-4 + :button_link: ../workflows/mount_aws_s3_bucket.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/basics.rst b/docs/source-app/basics.rst new file mode 100644 index 0000000..8a64df8 --- /dev/null +++ b/docs/source-app/basics.rst @@ -0,0 +1,259 @@ +:orphan: + +.. _basics: + +###### +Basics +###### + +In this guide, we'll cover the basic terminology associated with the Lightning framework. + +---- + +************** +Lightning App +************** + +The :class:`~lightning.app.core.app.LightningApp` runs a tree of one or more components that interact to create end-to-end applications. There are two kinds of components: :class:`~lightning.app.core.flow.LightningFlow` and :class:`~lightning.app.core.work.LightningWork`. This modular design enables you to reuse components created by other users. + +---- + +Lightning Work +^^^^^^^^^^^^^^ + +The :class:`~lightning.app.core.work.LightningWork` component is a building block optimized for long-running jobs or integrating third-party services. LightningWork can be used for training large models, downloading a dataset, or any long-lasting operation. + +---- + +Lightning Flow +^^^^^^^^^^^^^^ + +The :class:`~lightning.app.core.flow.LightningFlow` component coordinates long-running tasks :class:`~lightning.app.core.work.LightningWork` and runs its children :class:`~lightning.app.core.flow.LightningFlow` components. + +---- + +Lightning App Tree +^^^^^^^^^^^^^^^^^^ + +Components can be nested to form component trees where the LightningFlows are its branches and LightningWorks are its leaves. + +Here's a basic application with four flows and two works: + +.. literalinclude:: code_samples/quickstart/app_comp.py + +And here's its associated tree structure: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/tree.gif + :alt: Basic App Components + :width: 100 % + +A Lightning App runs all flows into a single process. Its flows coordinate the execution of the works each running in their own independent processes. + +---- + +Lightning Distributed Event Loop +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Drawing inspiration from modern web frameworks like `React.js `_, the Lightning app runs all flows in an **event loop** (forever), which is triggered every 0.1 seconds after collecting any works' state change. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/lightning_loop.gif + +When running an app in the cloud, the :class:`~lightning.app.core.work.LightningWork` run on different machines. Lightning communicates any :class:`~lightning.app.core.work.LightningWork` state changes to the **event loop** which re-executes the flow with the newly-collected works' state. + +---- + +Lightning App State +^^^^^^^^^^^^^^^^^^^ + +By design, each component is stateful and its state is composed of all its attributes. The **Lightning App State** is the collection of all its components state. + +With this mechanism, any component can **react** to any other component **state changes**, simply by relying on its attributes within the flow. + +For example, here we define two flow components, **RootFlow** and **ChildFlow**, where the child flow prints and increments a counter indefinitely and gets reflected in **RootFlow** state. + +You can easily check the state of your entire app: + +.. literalinclude:: code_samples/quickstart/app_01.py + +Here's the entire tree structure associated with your app: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/parent_child.png + :alt: Parent Child Components + :width: 100 % + +And here's the output you get when running the above application using **Lightning CLI**: + +.. code-block:: console + + $ lightning run app docs/source/code_samples/quickstart/app_01.py + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + State: {'works': {'w_1': {'vars': {'counter': 1}}, 'w_2': {'vars': {'counter': 0}}}} + + State: {'works': {'w_1': {'vars': {'counter': 3}}, 'w_2': {'vars': {'counter': 1}}}} + + State: {'works': {'w_1': {'vars': {'counter': 4}}, 'w_2': {'vars': {'counter': 1}}}} + + State: {'works': {'w_1': {'vars': {'counter': 5}}, 'w_2': {'vars': {'counter': 2}}}} + + State: {'works': {'w_1': {'vars': {'counter': 6}}, 'w_2': {'vars': {'counter': 2}}}} + + State: {'works': {'w_1': {'vars': {'counter': 7}}, 'w_2': {'vars': {'counter': 3}}}} + ... + +This app will count forever because the **lightning event loop** indefinitely calls the root flow run method. + +---- + +******************************* +Controlling the Execution Flow +******************************* + + +LightningWork: To Cache or Not to Cache Calls +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +With Lightning, you can control how to run your components. + +By default, the :class:`~lightning.app.core.flow.LightningFlow` is executed infinitely by the **Lightning Infinite Loop** and the :class:`~lightning.app.core.work.LightningWork` does not run in **parallel**, +meaning the **Lightning Infinite Loop** (a.k.a the flow) waits until that long-running work is completed to continue. + +Similar to `React.js Components and Props `_, the :class:`~lightning.app.core.work.LightningWork` +component accepts arbitrary inputs (the "props") to its **run** method and by default runs **once** for each unique input provided. + +Here's an example of this behavior: + +.. literalinclude:: code_samples/basics/0.py + :language: python + :emphasize-lines: 10, 19 + +And you should see the following by running the code above: + +.. code-block:: console + + $ python example.py + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + # After you have clicked `run` on the UI. + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 10} + +As you can see, the intermediate run didn't execute as already cached. + +To disable this behavior, set ``cache_calls=False`` to make any LightningWork run infinitely. + +.. literalinclude:: code_samples/basics/1.py + :diff: code_samples/basics/0.py + +.. code-block:: console + + $ python example.py + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + # After you have clicked `run` on the UI. + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 10} + + +.. note:: Passing a sequence of different props to the work run method queues their execution. We recommend avoiding this behavior as it can be hard to debug. Instead, wait for the previous run to execute. + +---- + +LightningWork: Parallel vs Non Parallel +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The LightningWork component is made for long-running jobs. + +As an example, let's create a long-running **LightningWork** component that will take 1 hour to do its "work". + +.. literalinclude:: code_samples/quickstart/app_02.py + :language: python + :emphasize-lines: 15 + +Here's the output you get when running the above application using **Lightning CLI**: + +.. code-block:: console + + $ lightning run app docs/source/code_samples/quickstart/app_02.py + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + # After you have clicked `run` on the UI. + 0.0 0.0 + ... + 0.0003 0.0003 + ... + 1.0 1.0 + ... + 1 hour later! + 1.0 1.0 + 1 hour later! + 1.0 1.0 + 1 hour later! + ... + +The child work runs only once, hence why the progress counter stops increasing once the work is completed. + +This is useful for monitoring the progress of a long-running operation, like training a big model. + +.. note :: + The Lightning Infinite Loop runs multiple cycles per second. + It is good practice to keep the loop running fast, so that your application stays responsive, + especially when it contains user-interface components. + +---- + +**************** +Multiple works +**************** + +In practical use cases, you might want to execute multiple long-running works in parallel. + +To enable this behavior, set ``parallel=True`` in the ``__init__`` method of +your :class:`~lightning.app.core.work.LightningWork`. + +Here's an example of the interaction between parallel and non-parallel behaviors: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/blocking_non_blocking.gif + :alt: mnist GPU bar + :width: 100 % + +Below, we reuse the **HourLongWork** work defined in the previous example, but modify the **RootFlow** +to run two **HourLongWork** works in a parallel way. + +.. literalinclude:: code_samples/quickstart/app/app_0.py + :emphasize-lines: 21 + +Above, both ``child_work_1`` and ``child_work_2`` are long-running works that are executed +asynchronously in parallel. + +When running the above app, we see the following logs: + +.. code-block:: console + + $ lightning run app docs/source/code_samples/quickstart/app/app_0.py + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + # After you have clicked `run` on the UI. + 0.0, 0.0 + ... + 0.0003, 0.0003 + ... + 1.0, 1.0 + ... + 1 hour later `child_work_1` started! + 1 hour later `child_work_2` started! + 0.0, 0.0 + ... + 0.0003, 0.0003 + ... + 1.0, 1.0 + 1 hour later `child_work_1` started! + 1 hour later `child_work_2` started! + ... + +---- + +*********** +Next Steps +*********** + +To keep learning about Lightning, build a :ref:`ui_and_frontends`. diff --git a/docs/source-app/cloud/customize_a_lightning_cluster.rst b/docs/source-app/cloud/customize_a_lightning_cluster.rst new file mode 100644 index 0000000..6a2f12a --- /dev/null +++ b/docs/source-app/cloud/customize_a_lightning_cluster.rst @@ -0,0 +1,13 @@ +********************************* +Take full control of your cluster +********************************* +If you are an experienced user, we can enable you to fully own your cluster +configuration using terraform directly. Please email support@lightning.ai for more information. + +---- + +************************************** +Get help building an optimized cluster +************************************** +For enterprise and academic use-cases, we offer tailored cluster set up in case you don't +have experts in-house. Email support@lightning.ai. diff --git a/docs/source-app/code_samples/basics/0.py b/docs/source-app/code_samples/basics/0.py new file mode 100644 index 0000000..75959d9 --- /dev/null +++ b/docs/source-app/code_samples/basics/0.py @@ -0,0 +1,19 @@ +import lightning as L + + +class ExampleWork(L.LightningWork): + def run(self, *args, **kwargs): + print(f"I received the following props: args: {args} kwargs: {kwargs}") + + +work = ExampleWork() +work.run(value=1) + +# Providing the same value. This won't run as already cached. +work.run(value=1) +work.run(value=1) +work.run(value=1) +work.run(value=1) + +# Changing the provided value. This isn't cached and will run again. +work.run(value=10) diff --git a/docs/source-app/code_samples/basics/1.py b/docs/source-app/code_samples/basics/1.py new file mode 100644 index 0000000..3a249c8 --- /dev/null +++ b/docs/source-app/code_samples/basics/1.py @@ -0,0 +1,22 @@ +import lightning as L + + +class ExampleWork(L.LightningWork): + def __init__(self): + super().__init__(cache_calls=False) + + def run(self, *args, **kwargs): + print(f"I received the following props: args: {args} kwargs: {kwargs}") + + +work = ExampleWork() +work.run(value=1) + +# Providing the same value. This won't run as already cached. +work.run(value=1) +work.run(value=1) +work.run(value=1) +work.run(value=1) + +# Changing the provided value. This isn't cached and will run again. +work.run(value=10) diff --git a/docs/source-app/code_samples/convert_pl_to_app/app.py b/docs/source-app/code_samples/convert_pl_to_app/app.py new file mode 100644 index 0000000..e83a428 --- /dev/null +++ b/docs/source-app/code_samples/convert_pl_to_app/app.py @@ -0,0 +1,17 @@ +import lightning as L +from lightning.app.components import TracerPythonScript + + +class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.runner = TracerPythonScript( + "train.py", + cloud_compute=L.CloudCompute("gpu"), + ) + + def run(self): + self.runner.run() + + +app = L.LightningApp(RootFlow()) diff --git a/docs/source-app/code_samples/convert_pl_to_app/requirements.txt b/docs/source-app/code_samples/convert_pl_to_app/requirements.txt new file mode 100644 index 0000000..e8fb43e --- /dev/null +++ b/docs/source-app/code_samples/convert_pl_to_app/requirements.txt @@ -0,0 +1,3 @@ +torch +torchvision +pytorch_lightning diff --git a/docs/source-app/code_samples/convert_pl_to_app/train.py b/docs/source-app/code_samples/convert_pl_to_app/train.py new file mode 100644 index 0000000..6dd3042 --- /dev/null +++ b/docs/source-app/code_samples/convert_pl_to_app/train.py @@ -0,0 +1,46 @@ +import os + +import torch +import torch.nn.functional as F +from torch import nn +from torch.utils.data import DataLoader, random_split +from torchvision import transforms as T +from torchvision.datasets import MNIST + +import pytorch_lightning as pl + + +class LitAutoEncoder(pl.LightningModule): + def __init__(self): + super().__init__() + self.encoder = nn.Sequential(nn.Linear(28 * 28, 128), nn.ReLU(), nn.Linear(128, 3)) + self.decoder = nn.Sequential(nn.Linear(3, 128), nn.ReLU(), nn.Linear(128, 28 * 28)) + + def forward(self, x): + # in lightning, + # forward defines the prediction/inference actions + embedding = self.encoder(x) + return embedding + + def training_step(self, batch, batch_idx): + # training_step defines the train loop. + # It is independent of forward + x, y = batch + x = x.view(x.size(0), -1) + z = self.encoder(x) + x_hat = self.decoder(z) + loss = F.mse_loss(x_hat, x) + self.log("train_loss", loss) + return loss + + def configure_optimizers(self): + optimizer = torch.optim.Adam(self.parameters(), lr=1e-3) + return optimizer + + +dataset = MNIST(os.getcwd(), download=True, transform=T.ToTensor()) +train, val = random_split(dataset, [55000, 5000]) + +autoencoder = LitAutoEncoder() +trainer = pl.Trainer(accelerator="auto") +trainer.fit(autoencoder, DataLoader(train), DataLoader(val)) diff --git a/docs/.nojekyll b/docs/source-app/code_samples/quickstart/__init__.py similarity index 100% rename from docs/.nojekyll rename to docs/source-app/code_samples/quickstart/__init__.py diff --git a/docs/source-app/code_samples/quickstart/app/__init__.py b/docs/source-app/code_samples/quickstart/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/source-app/code_samples/quickstart/app/app_0.py b/docs/source-app/code_samples/quickstart/app/app_0.py new file mode 100644 index 0000000..82b687b --- /dev/null +++ b/docs/source-app/code_samples/quickstart/app/app_0.py @@ -0,0 +1,21 @@ +import lightning as L +from docs.quickstart.app_02 import HourLongWork + + +class RootFlow(L.LightningFlow): + def __init__(self, child_work_1: L.LightningWork, child_work_2: L.LightningWork): + super().__init__() + self.child_work_1 = child_work_1 + self.child_work_2 = child_work_2 + + def run(self): + print(round(self.child_work_1.progress, 4), round(self.child_work_2.progress, 4)) + self.child_work_1.run() + self.child_work_2.run() + if self.child_work_1.progress == 1.0: + print("1 hour later `child_work_1` started!") + if self.child_work_2.progress == 1.0: + print("1 hour later `child_work_2` started!") + + +app = L.LightningApp(RootFlow(HourLongWork(parallel=True), HourLongWork(parallel=True))) diff --git a/docs/source-app/code_samples/quickstart/app/app_1.py b/docs/source-app/code_samples/quickstart/app/app_1.py new file mode 100644 index 0000000..29d8db2 --- /dev/null +++ b/docs/source-app/code_samples/quickstart/app/app_1.py @@ -0,0 +1,92 @@ +import flash +from flash.core.data.utils import download_data +from flash.image import ImageClassificationData, ImageClassifier + +import lightning as L +from pytorch_lightning.callbacks import ModelCheckpoint + + +# Step 1: Create a training LightningWork component that gets a backbone as input +# and saves the best model and its score +class ImageClassifierTrainWork(L.LightningWork): + def __init__(self, max_epochs: int, backbone: str, cloud_compute: L.CloudCompute): + # parallel is set to True to run asynchronously + super().__init__(parallel=True, cloud_compute=cloud_compute) + # Number of epochs to run + self.max_epochs = max_epochs + # The model backbone to train on + self.backbone = backbone + self.best_model_path = None + self.best_model_score = None + + def run(self, train_folder): + # Create a datamodule from the given dataset + datamodule = ImageClassificationData.from_folders( + train_folder=train_folder, + batch_size=1, + val_split=0.5, + ) + # Create an image classfier task with the given backbone + model = ImageClassifier(datamodule.num_classes, backbone=self.backbone) + # Start a Lightning trainer, with 1 training batch and 4 validation batches + trainer = flash.Trainer( + max_epochs=self.max_epochs, + limit_train_batches=1, + limit_val_batches=4, + callbacks=[ModelCheckpoint(monitor="val_cross_entropy")], + ) + # Train the model + trainer.fit(model, datamodule=datamodule) + # Save the model path + self.best_model_path = trainer.checkpoint_callback.best_model_path + # Save the model score + self.best_model_score = trainer.checkpoint_callback.best_model_score.item() + + +# Step 2: Create a serving LightningWork component that gets a model input and serves it +class ImageClassifierServeWork(L.LightningWork): + def run(self, best_model_path: str): + # Load the model from the model path + model = ImageClassifier.load_from_checkpoint(best_model_path) + model.serve(output="labels") + + +# Step 3: Create a root LightningFlow component that gets number of epochs and a path to +# a dataset as inputs, initialize 2 training components and serves the best model +class RootFlow(L.LightningFlow): + def __init__(self, max_epochs: int, data_dir: str): + super().__init__() + self.data_dir = data_dir + # Init an image classifier with resnet18 backbone + self.train_work_1 = ImageClassifierTrainWork( + max_epochs, + "resnet18", + ) + # Init an image classifier with resnet26 backbone + self.train_work_2 = ImageClassifierTrainWork( + max_epochs, + "resnet26", + ) + # Init the serving component + self.server_work = ImageClassifierServeWork() + + def run(self): + # running both `train_work_1` and `train_work_2` in parallel and asynchronously. + self.train_work_1.run(self.data_dir) + self.train_work_2.run(self.data_dir) + + # run serve_work only when both `best_model_score` are available. + if self.train_work_1.best_model_score and self.train_work_2.best_model_score: + # serve only the best model between `train_work_1` and `train_work_2`. + self.server_work.run( + self.train_work_1.best_model_path + if self.train_work_1.best_model_score < self.train_work_2.best_model_score + else self.train_work_2.best_model_path + ) + + +# Step 4: download a dataset to your local directory under `/data` +download_data("https://pl-flash-data.s3.amazonaws.com/hymenoptera_data.zip", "./data") + +# Initialize your Lightning app with 5 epochs +app = L.LightningApp(RootFlow(5, "./data/hymenoptera_data")) diff --git a/docs/source-app/code_samples/quickstart/app_01.py b/docs/source-app/code_samples/quickstart/app_01.py new file mode 100644 index 0000000..0a435c6 --- /dev/null +++ b/docs/source-app/code_samples/quickstart/app_01.py @@ -0,0 +1,27 @@ +import lightning as L +from lightning.app.utilities.app_helpers import pretty_state + + +class Work(L.LightningWork): + def __init__(self): + super().__init__(cache_calls=False) + # Attributes are registered automatically in the state. + self.counter = 0 + + def run(self): + # Incrementing an attribute gets reflected in the `Flow` state. + self.counter += 1 + + +class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.w = Work() + + def run(self): + if self.w.has_started: + print(f"State: {pretty_state(self.state)} \n") + self.w.run() + + +app = L.LightningApp(Flow()) diff --git a/docs/source-app/code_samples/quickstart/app_02.py b/docs/source-app/code_samples/quickstart/app_02.py new file mode 100644 index 0000000..e756019 --- /dev/null +++ b/docs/source-app/code_samples/quickstart/app_02.py @@ -0,0 +1,32 @@ +from time import sleep + +import lightning as L + + +# This work takes an hour to run +class HourLongWork(L.LightningWork): + def __init__(self, parallel: bool = False): + super().__init__(parallel=parallel) + self.progress = 0.0 + + def run(self): + self.progress = 0.0 + for _ in range(3600): + self.progress += 1.0 / 3600 # Reporting my progress to the Flow. + sleep(1) + + +class RootFlow(L.LightningFlow): + def __init__(self, child_work: L.LightningWork): + super().__init__() + self.child_work = child_work + + def run(self): + # prints the progress from the child work + print(round(self.child_work.progress, 4)) + self.child_work.run() + if self.child_work.counter == 1.0: + print("1 hour later!") + + +app = L.LightningApp(RootFlow(HourLongWork())) diff --git a/docs/source-app/code_samples/quickstart/app_03.py b/docs/source-app/code_samples/quickstart/app_03.py new file mode 100644 index 0000000..ee2047a --- /dev/null +++ b/docs/source-app/code_samples/quickstart/app_03.py @@ -0,0 +1,31 @@ +from time import sleep + +import lightning as L + + +class HourLongWork(L.LightningWork): + def __init__(self): + super().__init__(cache_calls=False) + self.progress = 0.0 + + def run(self): + self.progress = 0.0 + for _ in range(3600): + self.progress += 1.0 / 3600 + sleep(1) + + +class RootFlow(L.LightningFlow): + def __init__(self, child_work: L.LightningWork): + super().__init__() + self.child_work = child_work + + def run(self): + # prints the progress from the child work + print(round(self.child_work.progress, 4)) + self.child_work.run() + if self.child_work.counter == 1.0: + print("1 hour later!") + + +app = L.LightningApp(RootFlow(HourLongWork())) diff --git a/docs/source-app/code_samples/quickstart/app_comp.py b/docs/source-app/code_samples/quickstart/app_comp.py new file mode 100644 index 0000000..d09c89b --- /dev/null +++ b/docs/source-app/code_samples/quickstart/app_comp.py @@ -0,0 +1,26 @@ +import lightning as L +from lightning.app.testing import EmptyFlow, EmptyWork + + +class FlowB(L.LightningFlow): + def __init__(self): + super().__init__() + self.flow_d = EmptyFlow() + self.work_b = EmptyWork() + + def run(self): + ... + + +class FlowA(L.LightningFlow): + def __init__(self): + super().__init__() + self.flow_b = FlowB() + self.flow_c = EmptyFlow() + self.work_a = EmptyWork() + + def run(self): + ... + + +app = L.LightningApp(FlowA()) diff --git a/docs/source-app/code_samples/quickstart/hello_world/app.py b/docs/source-app/code_samples/quickstart/hello_world/app.py new file mode 100644 index 0000000..94ac255 --- /dev/null +++ b/docs/source-app/code_samples/quickstart/hello_world/app.py @@ -0,0 +1,15 @@ +import lightning as L + + +# Step 1: Subclass LightningFlow component to define the app flow. +class HelloWorld(L.LightningFlow): + # Step 2: Add the app logic to the LightningFlow run method to + # ``print("Hello World!")`. + # The LightningApp executes the run method of the main LightningFlow + # within an infinite loop. + def run(self): + print("Hello World!") + + +# Step 3: Initialize a LightningApp with the LightningFlow you defined (in step 1) +app = L.LightningApp(HelloWorld()) diff --git a/docs/source-app/code_samples/quickstart/hello_world/app_ui.py b/docs/source-app/code_samples/quickstart/hello_world/app_ui.py new file mode 100644 index 0000000..67fa6aa --- /dev/null +++ b/docs/source-app/code_samples/quickstart/hello_world/app_ui.py @@ -0,0 +1,57 @@ +import os + +import lightning as L +from lightning.app.frontend import StaticWebFrontend, StreamlitFrontend +from lightning.app.utilities.state import AppState + + +# Step 1: Define your LightningFlow component with the app UI +class UIStreamLit(L.LightningFlow): + def __init__(self): + super().__init__() + self.should_print = False + + # Step 2: Override `configure_layout` to define the layout of the UI + # In this example, we are using `StreamlitFrontend` + def configure_layout(self): + return StreamlitFrontend(render_fn=render_fn) + + +# Step 3: Implement the StreamLit render method +def render_fn(state: AppState): + import streamlit as st + from streamlit_autorefresh import st_autorefresh + + st_autorefresh(interval=2000, limit=None, key="refresh") + + state.should_print = st.select_slider( + "Should the Application print 'Hello World !' to the terminal:", + [False, True], + ) + + +# Step 4: Implement a Static Web Frontend. This could be react, vue, etc. +class UIStatic(L.LightningFlow): + # Step 5: + def configure_layout(self): + return StaticWebFrontend(os.path.join(os.path.dirname(__file__), "ui")) + + +# Step 6: Implement the root flow. +class HelloWorld(L.LightningFlow): + def __init__(self): + super().__init__() + self.static_ui = UIStatic() + self.streamlit_ui = UIStreamLit() + + def run(self): + print("Hello World!" if self.streamlit_ui.should_print else "") + + def configure_layout(self): + return [ + {"name": "StreamLit", "content": self.streamlit_ui}, + {"name": "Static", "content": self.static_ui}, + ] + + +app = L.LightningApp(HelloWorld()) diff --git a/docs/source-app/code_samples/quickstart/hello_world/ui/index.html b/docs/source-app/code_samples/quickstart/hello_world/ui/index.html new file mode 100644 index 0000000..fe38c43 --- /dev/null +++ b/docs/source-app/code_samples/quickstart/hello_world/ui/index.html @@ -0,0 +1 @@ +
Hello from component UIStatic
diff --git a/docs/source-app/conf.py b/docs/source-app/conf.py new file mode 100644 index 0000000..3541b27 --- /dev/null +++ b/docs/source-app/conf.py @@ -0,0 +1,398 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +import glob +import inspect +import os +import shutil +import sys + +import pt_lightning_sphinx_theme +from lightning_utilities.docs import fetch_external_assets + +import lightning + +_PATH_HERE = os.path.abspath(os.path.dirname(__file__)) +_PATH_ROOT = os.path.realpath(os.path.join(_PATH_HERE, "..", "..")) +sys.path.insert(0, os.path.abspath(_PATH_ROOT)) + +SPHINX_MOCK_REQUIREMENTS = int(os.environ.get("SPHINX_MOCK_REQUIREMENTS", True)) + +# -- Project information ----------------------------------------------------- + +# this name shall match the project name in Github as it is used for linking to code +project = "lightning" +copyright = lightning.__copyright__ +author = lightning.__author__ + +# The short X.Y version +version = lightning.__version__ +# The full version, including alpha/beta/rc tags +release = lightning.__version__ + +# Options for the linkcode extension +# ---------------------------------- +github_user = "Lightning-AI" +github_repo = project + +# -- Project documents ------------------------------------------------------- + + +fetch_external_assets( + docs_folder=_PATH_HERE, assets_folder="_static/fetched-s3-assets", retrieve_pattern=r"https?://[-a-zA-Z0-9_]+\.s3\.[-a-zA-Z0-9()_\\+.\\/=]+" +) + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. + +needs_sphinx = "4.5" + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx_toolbox.collapse", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.linkcode", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx.ext.imgmath", + # 'sphinxcontrib.mockautodoc', # raises error: directive 'automodule' is already registered ... + # 'sphinxcontrib.fulltoc', # breaks pytorch-theme with unexpected kw argument 'titles_only' + "sphinxcontrib.video", + "myst_parser", + "sphinx.ext.autosectionlabel", + "nbsphinx", + "sphinx_autodoc_typehints", + "sphinx_copybutton", + "sphinx_paramlinks", + "sphinx_togglebutton", + "sphinx.ext.githubpages", + # "lai_sphinx_theme.extensions.lightning", + "pt_lightning_sphinx_theme.extensions.lightning", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# myst-parser, forcing to parse all html pages with mathjax +# https://github.com/executablebooks/MyST-Parser/issues/394 +myst_update_mathjax = False +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html?highlight=anchor#auto-generated-header-anchors +myst_heading_anchors = 3 + +# https://berkeley-stat159-f17.github.io/stat159-f17/lectures/14-sphinx..html#conf.py-(cont.) +# https://stackoverflow.com/questions/38526888/embed-ipython-notebook-in-sphinx-document +# I execute the notebooks manually in advance. If notebooks test the code, +# they should be run at build time. +nbsphinx_execute = "never" +nbsphinx_allow_errors = True +nbsphinx_requirejs_path = "" + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +# source_suffix = ['.rst', '.md', '.ipynb'] +source_suffix = { + ".rst": "restructuredtext", + ".txt": "markdown", + ".md": "markdown", + ".ipynb": "nbsphinx", +} + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# List of patterns, relative to source-app directory, that match files and +# directories to ignore when looking for source-app files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + "PULL_REQUEST_TEMPLATE.md", + "**/README.md/*", + "readme.md", + "_templates", + "code_samples/convert_pl_to_app/requirements.txt", + "**/_static/*" +] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +# html_theme = "lai_sphinx_theme" +html_theme = "pt_lightning_sphinx_theme" +html_theme_path = [os.environ.get('LIT_SPHINX_PATH', pt_lightning_sphinx_theme.get_html_theme_path())] + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. + +html_theme_options = { + "pytorch_project": lightning.__homepage__, + "analytics_id": "G-D3Q2ESCTZR", + "canonical_url": lightning.__homepage__, + "collapse_navigation": False, + "display_version": True, + "logo_only": False, +} + +html_favicon = "_static/images/icon.svg" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_templates", "_static"] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = project + "-doc" + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', + # Latex figure (float) alignment + "figure_align": "htbp", +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source-app start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, project + ".tex", project + " Documentation", author, "manual"), +] + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source-app start file, name, description, authors, manual section). +man_pages = [(master_doc, project, project + " Documentation", [author], 1)] + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source-app start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + project, + project + " Documentation", + author, + project, + lightning.__docs__, + "Miscellaneous", + ), +] + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ["search.html"] + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "torch": ("https://pytorch.org/docs/stable/", None), + # "numpy": ("https://docs.scipy.org/doc/numpy/", None), +} + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +def setup(app): + # this is for hiding doctest decoration, + # see: http://z4r.github.io/python/2011/12/02/hides-the-prompts-and-output/ + app.add_js_file("copybutton.js") + app.add_css_file("main.css") + + +# copy all notebooks to local folder +path_nbs = os.path.join(_PATH_HERE, "notebooks") +if not os.path.isdir(path_nbs): + os.mkdir(path_nbs) +for path_ipynb in glob.glob(os.path.join(_PATH_ROOT, "notebooks", "*.ipynb")): + path_ipynb2 = os.path.join(path_nbs, os.path.basename(path_ipynb)) + shutil.copy(path_ipynb, path_ipynb2) + +# copy all examples to local folder +path_examples = os.path.join(_PATH_HERE, "..", "examples") +if not os.path.isdir(path_examples): + os.mkdir(path_examples) +for path_app_example in glob.glob(os.path.join(_PATH_ROOT, "examples", "app_*")): + path_app_example2 = os.path.join(path_examples, os.path.basename(path_app_example)) + if not os.path.isdir(path_app_example2): + shutil.copytree(path_app_example, path_app_example2, dirs_exist_ok=True) + + +# Ignoring Third-party packages +# https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule +def _package_list_from_file(file): + list_pkgs = [] + with open(file) as fp: + lines = fp.readlines() + for ln in lines: + found = [ln.index(ch) for ch in list(",=<>#") if ch in ln] + pkg = ln[: min(found)] if found else ln + if pkg.rstrip(): + list_pkgs.append(pkg.rstrip()) + return list_pkgs + + +# define mapping from PyPI names to python imports +PACKAGE_MAPPING = { + "PyYAML": "yaml", +} +MOCK_PACKAGES = [] +if SPHINX_MOCK_REQUIREMENTS: + # mock also base packages when we are on RTD since we don't install them there + MOCK_PACKAGES += _package_list_from_file(os.path.join(_PATH_ROOT, "requirements.txt")) +MOCK_PACKAGES = [PACKAGE_MAPPING.get(pkg, pkg) for pkg in MOCK_PACKAGES] + +autodoc_mock_imports = MOCK_PACKAGES + + +# Resolve function +# This function is used to populate the (source-app) links in the API +def linkcode_resolve(domain, info): + def find_source(): + # try to find the file and line number, based on code from numpy: + # https://github.com/numpy/numpy/blob/master/doc/source/conf.py#L286 + obj = sys.modules[info["module"]] + for part in info["fullname"].split("."): + obj = getattr(obj, part) + fname = inspect.getsourcefile(obj) + # https://github.com/rtfd/readthedocs.org/issues/5735 + if any(s in fname for s in ("readthedocs", "rtfd", "checkouts")): + # /home/docs/checkouts/readthedocs.org/user_builds/pytorch_lightning/checkouts/ + # devel/pytorch_lightning/utilities/cls_experiment.py#L26-L176 + path_top = os.path.abspath(os.path.join("..", "..", "..")) + fname = os.path.relpath(fname, start=path_top) + else: + # Local build, imitate master + fname = "master/" + os.path.relpath(fname, start=os.path.abspath("..")) + source, lineno = inspect.getsourcelines(obj) + return fname, lineno, lineno + len(source) - 1 + + if domain != "py" or not info["module"]: + return None + try: + filename = "%s#L%d-L%d" % find_source() + except Exception: + filename = info["module"].replace(".", "/") + ".py" + # import subprocess + # tag = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE, + # universal_newlines=True).communicate()[0][:-1] + branch = filename.split("/")[0] + # do mapping from latest tags to master + branch = {"latest": "master", "stable": "master"}.get(branch, branch) + filename = "/".join([branch] + filename.split("/")[1:]) + return f"https://github.com/{github_user}/{github_repo}/blob/{filename}" + + +autosummary_generate = True + +autodoc_member_order = "groupwise" +autoclass_content = "both" +# the options are fixed and will be soon in release, +# see https://github.com/sphinx-doc/sphinx/issues/5459 +autodoc_default_options = { + "members": None, + "methods": None, + # 'attributes': None, + "special-members": "__call__", + "exclude-members": "_abc_impl", + "show-inheritance": True, + "private-members": True, + "noindex": True, +} + +# Sphinx will add “permalinks” for each heading and description environment as paragraph signs that +# become visible when the mouse hovers over them. +# This value determines the text for the permalink; it defaults to "¶". Set it to None or the empty +# string to disable permalinks. +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_permalinks +# html_add_permalinks = "¶" +# True to prefix each section label with the name of the document it is in, followed by a colon. +# For example, index:Introduction for a section called Introduction that appears in document index.rst. +# Useful for avoiding ambiguity when the same section heading appears in different documents. +# http://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html +autosectionlabel_prefix_document = True + +# only run doctests marked with a ".. doctest::" directive +doctest_test_doctest_blocks = "" +doctest_global_setup = """ +import importlib +import os +import lightning as L + +from lightning.fabric.loggers.tensorboard import _TENSORBOARD_AVAILABLE, _TENSORBOARDX_AVAILABLE +""" +coverage_skip_undoc_in_source = True + +# skip false positive linkcheck errors from anchors +linkcheck_anchors = False + +# ignore all links in any CHANGELOG file +linkcheck_exclude_documents = [r"^(.*\/)*CHANGELOG.*$"] diff --git a/docs/source-app/contribute_app.rst b/docs/source-app/contribute_app.rst new file mode 100644 index 0000000..2f690e8 --- /dev/null +++ b/docs/source-app/contribute_app.rst @@ -0,0 +1,7 @@ +:orphan: + +################# +Contribute an app +################# + +Show off your work! Contribute and example to be highlighted in our documentation and App gallery. diff --git a/docs/source-app/core_api/lightning_app/app.py b/docs/source-app/core_api/lightning_app/app.py new file mode 100644 index 0000000..0a435c6 --- /dev/null +++ b/docs/source-app/core_api/lightning_app/app.py @@ -0,0 +1,27 @@ +import lightning as L +from lightning.app.utilities.app_helpers import pretty_state + + +class Work(L.LightningWork): + def __init__(self): + super().__init__(cache_calls=False) + # Attributes are registered automatically in the state. + self.counter = 0 + + def run(self): + # Incrementing an attribute gets reflected in the `Flow` state. + self.counter += 1 + + +class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.w = Work() + + def run(self): + if self.w.has_started: + print(f"State: {pretty_state(self.state)} \n") + self.w.run() + + +app = L.LightningApp(Flow()) diff --git a/docs/source-app/core_api/lightning_app/communication.rst b/docs/source-app/core_api/lightning_app/communication.rst new file mode 100644 index 0000000..9a823b0 --- /dev/null +++ b/docs/source-app/core_api/lightning_app/communication.rst @@ -0,0 +1,15 @@ +:orphan: + +########################################## +Communication between Lightning Components +########################################## + +**Audience:** Users that want to create interactive applications. + +**Level:** Intermediate + +**Prerequisite**: Read the `Communication in Lightning Apps article <../../access_app_state.html>`_. + +---- + +.. include:: ../../core_api/lightning_app/communication_content.rst diff --git a/docs/source-app/core_api/lightning_app/communication_content.rst b/docs/source-app/core_api/lightning_app/communication_content.rst new file mode 100644 index 0000000..ea39749 --- /dev/null +++ b/docs/source-app/core_api/lightning_app/communication_content.rst @@ -0,0 +1,160 @@ + +******************************** +Communication Between Components +******************************** + +When creating interactive Lightning Apps (App) with multiple components, you may need your components to share information with each other and rely on that information to control their execution, share progress in the UI, trigger a sequence of operations, etc. + +To accomplish that, Lightning components can communicate using the App State. The App State is composed of all attributes defined within each component's **__init__** method e.g anything attached to the component with **self.x = y**. + +All attributes of all **LightningWork (Work)** components are accessible in the **LightningFlow (Flow)** components in real-time. + +By design, the Flows communicate to all **Works** within the application. However, Works can't communicate with each other directly, they must use Flows as a proxy to communicate. + +Once a Work is running, any updates to the Work's state is automatically communicated to the Flow, as a delta (using `DeepDiff `_). The state communication isn't bi-directional, communication is only done from Work to Flow. + +Internally, the App is alternatively collecting deltas sent from all the registered Works and/or UI, and running the root Flow run method of the App. + +---- + +************************************************* +Communication from LightningWork to LightningFlow +************************************************* + +LightningFlow (Flow) can access their children's LightningWork (Work) state. + +When a running Work attribute gets updated inside its method (separate process locally or remote machine), the app re-executes Flow's run method once it receives the state update from the Work. + +Here's an example to better understand communication from Work to Flow. + +The ``WorkCounter`` increments a counter until 1 million and the ``Flow`` prints the work counter. + +As the Work is running its own process, its state changes are sent to the Flow which contains the latest value of the counter. + +.. code-block:: python + + import lightning as L + + + class WorkCounter(L.LightningWork): + def __init__(self): + super().__init__(parallel=True) + self.counter = 0 + + def run(self): + for _ in range(int(10e6)): + self.counter += 1 + + + class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.w = WorkCounter() + + def run(self): + self.w.run() + print(self.w.counter) + + + app = L.LightningApp(Flow()) + + +A delta sent from the Work to the Flow looks like this: + +.. code-block:: python + + {"values_changed": {"root['works']['w']['vars']['counter']": {"new_value": 425}}} + +Here is the associated illustration: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/deltas.gif + :alt: Mechanism showing how delta are sent. + :width: 100 % + +Here's another example that is slightly different. Here we define a Flow and Work, where the Work increments a counter indefinitely and the Flow prints its state which contain the Work. + +You can easily check the state of your entire app as follows: + +.. literalinclude:: ../../core_api/lightning_app/app.py + +Run the app with: + +.. code-block:: bash + + lightning run app docs/source/core_api/lightning_app/app.py + +And here's the output you get when running the App using the **Lightning CLI**: + +.. code-block:: console + + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + State: {'works': {'w': {'vars': {'counter': 1}}}} + State: {'works': {'w': {'vars': {'counter': 2}}}} + State: {'works': {'w': {'vars': {'counter': 3}}}} + State: {'works': {'w': {'vars': {'counter': 3}}}} + State: {'works': {'w': {'vars': {'counter': 4}}}} + ... + +---- + +************************************************* +Communication from LightningFlow to LightningWork +************************************************* + +Communication from the LightningFlow (Flow) to the LightningWork (Work) while running **isn't supported yet**. If your application requires this feature, please open an issue on Github. + +Here's an example of what would happen if you try to have the Flow communicate with the Work: + +.. code-block:: python + + import lightning as L + from time import sleep + + + class WorkCounter(L.LightningWork): + def __init__(self): + super().__init__(parallel=True) + self.counter = 0 + + def run(self): + while True: + sleep(1) + print(f"Work {self.counter}") + + + class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.w = WorkCounter() + + def run(self): + self.w.run() + sleep(1) + print(f"Flow {self.w.counter}") + self.w.counter += 1 + + + app = L.LightningApp(Flow()) + +As you can see, there is a divergence between the values within the Work and the Flow. + +.. code-block:: console + + Flow 0 + Flow 1 + Flow 2 + Flow 3 + Work 0 + Flow 4 + Work 0 + Flow 5 + Work 0 + Flow 6 + Work 0 + Flow 7 + Work 0 + Flow 8 + Work 0 + Flow 9 + Work 0 + Flow 10 diff --git a/docs/source-app/core_api/lightning_app/compute_content.rst b/docs/source-app/core_api/lightning_app/compute_content.rst new file mode 100644 index 0000000..8bb2e70 --- /dev/null +++ b/docs/source-app/core_api/lightning_app/compute_content.rst @@ -0,0 +1,40 @@ +:orphan: + +*************************** +Customize my Flow resources +*************************** + +In the cloud, you can simply configure which machine to run on by passing +a :class:`~lightning.app.utilities.packaging.cloud_compute.CloudCompute` to your work ``__init__`` method: + +.. code-block:: python + + import lightning as L + + # Run on a small, shared CPU machine. This is the default for every LightningFlow. + app = L.LightningApp(L.Flow(), flow_cloud_compute=L.CloudCompute()) + + +Here is the full list of supported machine names: + +.. list-table:: Hardware by Accelerator Type + :widths: 25 25 25 + :header-rows: 1 + + * - Name + - # of CPUs + - Memory + * - flow-lite + - 0.3 + - 4 GB + +The up-to-date prices for these instances can be found `here `_. + +---- + +************ +CloudCompute +************ + +.. autoclass:: lightning.app.utilities.packaging.cloud_compute.CloudCompute + :noindex: diff --git a/docs/source-app/core_api/lightning_app/dynamic_work.rst b/docs/source-app/core_api/lightning_app/dynamic_work.rst new file mode 100644 index 0000000..bf202aa --- /dev/null +++ b/docs/source-app/core_api/lightning_app/dynamic_work.rst @@ -0,0 +1,15 @@ +:orphan: + +.. _dynamic_work: + +##################### +Dynamic LightningWork +##################### + +**Audience:** Users who want to create applications that adapt to user demands. + +**Level:** Advanced + +---- + +.. include:: dynamic_work_content.rst diff --git a/docs/source-app/core_api/lightning_app/dynamic_work_content.rst b/docs/source-app/core_api/lightning_app/dynamic_work_content.rst new file mode 100644 index 0000000..309e607 --- /dev/null +++ b/docs/source-app/core_api/lightning_app/dynamic_work_content.rst @@ -0,0 +1,202 @@ +*************************************** +What Dynamic LightningWork does for you +*************************************** + +Dynamic LightningWork (Work) changes the resources your application uses while the application is running (aka at runtime). + +For example, imagine you want to create a research notebook app for your team. You want every member to be able to create multiple `JupyterLab `_ sessions on their hardware of choice. + +To allow every notebook to choose hardware, it needs to be set up in it's own :class:`~lightning.app.core.work.LightningWork`, but you can't know the number of notebooks user will need in advance. In this case you'll need to add ``LightningWorks`` dynamically at run time. + +---- + +***************** +Use Dynamic Works +***************** + +Dynamic Works should be used anytime you want change the resources your application is using while it is running (aka at runtime). + +You're usually going to use the ``start`` and ``stop`` methods together. + +---- + +Add a Dynamic Work +^^^^^^^^^^^^^^^^^^ + +There are a couple of ways you can add a dynamic Work: + +- Option 1: Attach your components in the **run** method using the Python functions. +- Option 2: Use the Lightning built-in classes :class:`~lightning.structures.Dict` or :class:`~lightning.structures.List`. + +.. note:: Using the Lightning built-in classes is usually easier to read. + +---- + +**OPTION 1:** Attach your components in the run method of a flow using the Python functions **hasattr**, **setattr**, and **getattr**: + +.. code-block:: python + + class RootFlow(lapp.LightningFlow): + + def run(self): + + if not hasattr(self, "work"): + # The `Work` component is created and attached here. + setattr(self, "work", Work()) + # Run the `Work` component. + getattr(self, "work").run() + +**OPTION 2:** Use the built-in Lightning classes :class:`~lightning.app.structures.Dict` or :class:`~lightning.app.structures.List` + +.. code-block:: python + + from lightning.app.structures import Dict + + class RootFlow(lapp.LightningFlow): + + def __init__(self): + super().__init__() + self.dict = Dict() + + def run(self): + if "work" not in self.dict: + # The `Work` component is attached here. + self.dict["work"] = Work() + self.dict["work"].run() + +---- + +Stop a Work +^^^^^^^^^^^ +Stop a work when you are concerned about cost. + +To stop a work, use the work ``stop`` method: + +.. code-block:: python + + class RootFlow(L.LightningFlow): + + def __init__(self): + super().__init__() + self.work = Work() + + def run(self): + self.work.stop() + +---- + +********************* +Dynamic Work Examples +********************* + +.. + The entire application can be found `here `_. + +---- + +Dynamic Work with Jupyter Notebooks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In this example, we are dynamically creating ``JupyterLabWork`` every time a user clicks the **Create Jupyter Notebook** button. + +In order to do that, we are iterating over the list of ``jupyter_config_requests`` infinitely. + +.. code-block:: python + + import lightning as L + + + class JupyterLabManager(L.LightningFlow): + + """This flow manages the users notebooks running within works."""" + + def __init__(self): + super().__init__() + self.jupyter_works = L.structures.Dict() + self.jupyter_config_requests = [] + + def run(self): + for idx, jupyter_config in enumerate(self.jupyter_config_requests): + + # The Jupyter Config has this form is: + # {"use_gpu": False/True, "token": None, "username": ..., "stop": False} + + # Step 1: Check if JupyterWork already exists for this username + username = jupyter_config["username"] + if username not in self.jupyter_works: + jupyter_config["ready"] = False + + # Set the hardware selected by the user: GPU or CPU. + cloud_compute = L.CloudCompute("gpu" if jupyter_config["use_gpu"] else "cpu-small") + + # Step 2: Create new JupyterWork dynamically ! + self.jupyter_works[username] = JupyterLabWork(cloud_compute=cloud_compute) + + # Step 3: Run the JupyterWork + self.jupyter_works[username].run() + + # Step 4: Store the notebook token in the associated config. + # We are using this to know when the notebook is ready + # and display the stop button on the UI. + if self.jupyter_works[username].token: + jupyter_config["token"] = self.jupyter_works[username].token + + # Step 5: Stop the work if the user requested it. + if jupyter_config['stop']: + self.jupyter_works[username].stop() + self.jupyter_config_requests.pop(idx) + + def configure_layout(self): + return L.app.frontend.StreamlitFrontend(render_fn=render_fn) + +---- + +Dynamic Works with StreamLit UI +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Continuing from the Jupyter Notebook example, in the UI, we receive the **state** of the Jupyter Manager and the state can be modified directly from the UI. + +.. code-block:: python + + import streamlit as st + + + def render_fn(state): + + # Step 1: Enable users to select their notebooks and create them + column_1, column_2, column_3 = st.columns(3) + with column_1: + create_jupyter = st.button("Create Jupyter Notebook") + with column_2: + username = st.text_input('Enter your username', "tchaton") + assert username + with column_3: + use_gpu = st.checkbox('Use GPU') + + # Step 2: If a user clicked the button, add an element to the list of configs + # Note: state.jupyter_config_requests = ... will sent the state update to the component. + if create_jupyter: + new_config = [{"use_gpu": use_gpu, "token": None, "username": username, "stop": False}] + state.jupyter_config_requests = state.jupyter_config_requests + new_config + + # Step 3: List of running notebooks. + for idx, config in enumerate(state.jupyter_config_requests): + column_1, column_2, column_3 = st.columns(3) + with column_1: + if not idx: + st.write(f"Idx") + st.write(f"{idx}") + with column_2: + if not idx: + st.write(f"Use GPU") + st.write(config['use_gpu']) + with column_3: + if not idx: + st.write(f"Stop") + if config["token"]: + should_stop = st.button("Stop this notebook") + + # Step 4: Change stop if the user clicked the button + if should_stop: + config["stop"] = should_stop + state.jupyter_config_requests = state.jupyter_config_requests diff --git a/docs/source-app/core_api/lightning_app/index.rst b/docs/source-app/core_api/lightning_app/index.rst new file mode 100644 index 0000000..bf0430f --- /dev/null +++ b/docs/source-app/core_api/lightning_app/index.rst @@ -0,0 +1,94 @@ +############# +Lightning App +############# +**Audience:** Users who want to know how an app works under the hood 🤯. + +**Lightning App:** We call workflows composed of multiple LightningWorks a **Lightning App**. + +---- + +******************* +Peek under the hood +******************* + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: App Components Tree (Basic) + :description: Learn more component composition and nesting. + :col_css: col-md-4 + :button_link: ../../glossary/app_tree.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: The event loop (Basic) + :description: Learn more about the event loop. + :col_css: col-md-4 + :button_link: ../../glossary/event_loop.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Communication between Flow and Works + :description: Learn more about components communicate. + :col_css: col-md-4 + :button_link: communication.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Customize Flow compute resources + :description: Learn more about Flow customizations. + :col_css: col-md-4 + :button_link: compute_content.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Dynamically create, execute and stop Work + :description: Learn more about components creation. + :col_css: col-md-4 + :button_link: dynamic_work.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Sharing My Components (Intermediate) + :description: Learn more component composition and nesting. + :col_css: col-md-4 + :button_link: ../../glossary/sharing_components.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
+ +---- + +***************** +Lightning App API +***************** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: LightningApp API + :description: Look into the Lightning API reference. + :col_css: col-md-4 + :button_link: lightning_app.html + :height: 180 + +.. raw:: html + +
+
diff --git a/docs/source-app/core_api/lightning_app/lightning_app.rst b/docs/source-app/core_api/lightning_app/lightning_app.rst new file mode 100644 index 0000000..af95926 --- /dev/null +++ b/docs/source-app/core_api/lightning_app/lightning_app.rst @@ -0,0 +1,12 @@ +:orphan: + +.. _lightning_app: + +############ +LightningApp +############ + + +.. autoclass:: lightning.app.core.app.LightningApp + :exclude-members: _run, connect, get_component_by_name, maybe_apply_changes, set_state + :noindex: diff --git a/docs/source-app/core_api/lightning_flow.rst b/docs/source-app/core_api/lightning_flow.rst new file mode 100644 index 0000000..642112a --- /dev/null +++ b/docs/source-app/core_api/lightning_flow.rst @@ -0,0 +1,8 @@ +.. _lightning_flow: + +############# +LightningFlow +############# + +.. autoclass:: lightning.app.core.flow.LightningFlow + :exclude-members: _attach_backend, _exit, _is_state_attribute, set_state diff --git a/docs/source-app/core_api/lightning_work/compute.rst b/docs/source-app/core_api/lightning_work/compute.rst new file mode 100644 index 0000000..89313c4 --- /dev/null +++ b/docs/source-app/core_api/lightning_work/compute.rst @@ -0,0 +1,15 @@ +:orphan: + +.. _cloud_compute: + +############################ +Customize your Cloud Compute +############################ + +**Audience:** Users who want to select the hardware to run in the cloud. + +**Level:** Intermediate + +---- + +.. include:: compute_content.rst diff --git a/docs/source-app/core_api/lightning_work/compute_content.rst b/docs/source-app/core_api/lightning_work/compute_content.rst new file mode 100644 index 0000000..1ca6442 --- /dev/null +++ b/docs/source-app/core_api/lightning_work/compute_content.rst @@ -0,0 +1,94 @@ + +*************************** +Customize my Work resources +*************************** + +In the cloud, you can simply configure which machine to run on by passing +a :class:`~lightning.app.utilities.packaging.cloud_compute.CloudCompute` to your work ``__init__`` method: + +.. code-block:: python + + import lightning as L + + # Run on a free, shared CPU machine. This is the default for every LightningWork. + MyCustomWork(cloud_compute=L.CloudCompute()) + + # Run on a dedicated, medium-size CPU machine (see specs below) + MyCustomWork(cloud_compute=L.CloudCompute("cpu-medium")) + + # Run on cheap GPU machine with a single GPU (see specs below) + MyCustomWork(cloud_compute=L.CloudCompute("gpu")) + + # Run on a fast multi-GPU machine (see specs below) + MyCustomWork(cloud_compute=L.CloudCompute("gpu-fast-multi")) + +.. warning:: + Custom base images are not supported with the default CPU cloud compute. For example: + + .. code-block:: py + + class MyWork(LightningWork): + def __init__(self): + super().__init__(cloud_build_config=BuildConfig(image="my-custom-image")) # no cloud compute, for example default work + + +Here is the full list of supported machine names: + +.. list-table:: Hardware by Accelerator Type + :widths: 25 25 25 25 + :header-rows: 1 + + * - Name + - # of CPUs + - GPUs + - Memory + * - default + - 1 + - 0 + - 4 GB + * - cpu-small + - 2 + - 0 + - 8 GB + * - cpu-medium + - 8 + - 0 + - 32 GB + * - gpu + - 4 + - 1 (T4, 16 GB) + - 16 GB + * - gpu-fast + - 8 + - 1 (V100, 16 GB) + - 61 GB + * - gpu-fast-multi + - 32 + - 4 (V100 16 GB) + - 244 GB + +The up-to-date prices for these instances can be found `here `_. + +---- + +********************** +Stop my work when idle +********************** + +By providing **idle_timeout=X Seconds**, the work is automatically stopped **X seconds** after doing nothing. + +.. code-block:: python + + import lightning as L + + # Run on a single CPU and turn down immediately when idle. + MyCustomWork(cloud_compute=L.CloudCompute("gpu", idle_timeout=0)) + +---- + +************ +CloudCompute +************ + +.. autoclass:: lightning.app.utilities.packaging.cloud_compute.CloudCompute + :noindex: diff --git a/docs/source-app/core_api/lightning_work/handling_app_exception.rst b/docs/source-app/core_api/lightning_work/handling_app_exception.rst new file mode 100644 index 0000000..20c9b61 --- /dev/null +++ b/docs/source-app/core_api/lightning_work/handling_app_exception.rst @@ -0,0 +1,13 @@ +:orphan: + +############################### +Handle Lightning App exceptions +############################### + +**Audience:** Users who want to make Lightning Apps more robust to potential issues. + +**Level:** Advanced + +---- + +.. include:: handling_app_exception_content.rst diff --git a/docs/source-app/core_api/lightning_work/handling_app_exception_content.rst b/docs/source-app/core_api/lightning_work/handling_app_exception_content.rst new file mode 100644 index 0000000..4840cf5 --- /dev/null +++ b/docs/source-app/core_api/lightning_work/handling_app_exception_content.rst @@ -0,0 +1,74 @@ + +*************************************************** +What handling Lightning App exceptions does for you +*************************************************** + +Imagine you are creating a Lightning App (App) where your team can launch model training by providing their own Github Repo any time they want. + +As the App admin, you don't want the App to go down if their code has a bug and breaks. + +Instead, you would like the LightningWork (Work) to capture the exception and present the issue to users. + +---- + +**************************** +Configure exception handling +**************************** + +The LightningWork (Work) accepts an argument **raise_exception** which is **True** by default. This aligns with Python default behaviors. + +However, for the user case stated in the previous section, we want to capture the Work exceptions. This is done by providing ``raise_exception=False`` to the work ``__init__`` method. + +.. code-block:: python + + import lightning as L + + MyCustomWork(raise_exception=False) # <== HERE: The exception is captured. + + # Default behavior + MyCustomWork(raise_exception=True) # <== HERE: The exception is raised within the flow and terminates the app + + +And you can customize this behavior by overriding the ``on_exception`` hook to the Work. + +.. code-block:: python + + import lightning as L + + class MyCustomWork(L.LightningWork): + + def on_exception(self, exception: Exception): + # do something when an exception is triggered. + +---- + +************************** +Exception handling example +************************** + +This is the pseudo-code for the application described above. + +.. code-block:: python + + import lightning as L + + class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.user_jobs = L.structures.Dict() + self.requested_jobs = [] + + def run(self): + for request in self.requested_jobs: + job_id = request["id"] + if job_id not in self.user_jobs: + # Note: The `GithubRepoLauncher` doesn't exist yet. + self.user_jobs[job_id] = GithubRepoLauncher( + **request, + raise_exception=False, # <== HERE: The exception is captured. + ) + self.user_jobs[job_id].run() + + if self.user_jobs[job_id].status.stage == "failed" and "printed" not in request: + print(self.user_jobs[job_id].status) # <== HERE: Print the user exception. + request["printed"] = True diff --git a/docs/source-app/core_api/lightning_work/index.rst b/docs/source-app/core_api/lightning_work/index.rst new file mode 100644 index 0000000..0b660f2 --- /dev/null +++ b/docs/source-app/core_api/lightning_work/index.rst @@ -0,0 +1,112 @@ +############## +Lightning Work +############## + +**Audience:** Users who want to know how Lightning Work works under the hood 🤯. + +---- + +******************* +Peek under the hood +******************* + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: To Cache or Not to Cache Calls + :description: Learn more about work execution and internal caching. + :col_css: col-md-4 + :button_link: ../../workflows/run_work_once.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Parallel Work + :description: Learn more about work parallelization. + :col_css: col-md-4 + :button_link: ../../workflows/run_work_in_parallel.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Sharing files between works + :description: Learn more about data transfer works. + :col_css: col-md-4 + :button_link: ../../glossary/storage/storage.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Sharing Python Objects between works + :description: Learn more about sharing objects. + :col_css: col-md-4 + :button_link: payload.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Checking Work Status + :description: Learn more about work status. + :col_css: col-md-4 + :button_link: status.html + :height: 180 + :tag: Advanced + +.. displayitem:: + :header: Handling App Exceptions + :description: Learn more about exception capturing. + :col_css: col-md-4 + :button_link: handling_app_exception.html + :height: 180 + :tag: Advanced + +.. raw:: html + +
+
+ +.. raw:: html + +
+
+ +.. displayitem:: + :header: Customize your Cloud Compute + :description: Learn more about changing hardware and requirements. + :col_css: col-md-4 + :button_link: compute.html + :height: 180 + :tag: Cloud + +.. raw:: html + +
+
+ + +---- + +****************** +Lightning Work API +****************** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: LightningWork API + :description: Look into the Lightning API reference. + :col_css: col-md-4 + :button_link: lightning_work.html + :height: 180 + +.. raw:: html + +
+
diff --git a/docs/source-app/core_api/lightning_work/lightning_work.rst b/docs/source-app/core_api/lightning_work/lightning_work.rst new file mode 100644 index 0000000..54c7328 --- /dev/null +++ b/docs/source-app/core_api/lightning_work/lightning_work.rst @@ -0,0 +1,11 @@ +:orphan: + +.. _lightning_work: + +############# +LightningWork +############# + +.. autoclass:: lightning.app.core.work.LightningWork + :exclude-members: _aggregate_status_timeout, _is_state_attribute, _is_state_attribute, set_state + :noindex: diff --git a/docs/source-app/core_api/lightning_work/payload.rst b/docs/source-app/core_api/lightning_work/payload.rst new file mode 100644 index 0000000..b23bf7c --- /dev/null +++ b/docs/source-app/core_api/lightning_work/payload.rst @@ -0,0 +1,15 @@ +:orphan: + +###################################### +Sharing Objects between LightningWorks +###################################### + +**Audience:** Users who want to know how to transfer Python objects between their LightningWorks. + +**Level:** Advanced + +**Prerequisite**: Reach Level 16+, know about the `pandas DataFrames `_ and read and read the `Access app state guide <../../access_app_state.html>`_. + +---- + +.. include:: payload_content.rst diff --git a/docs/source-app/core_api/lightning_work/payload_content.rst b/docs/source-app/core_api/lightning_work/payload_content.rst new file mode 100644 index 0000000..780f398 --- /dev/null +++ b/docs/source-app/core_api/lightning_work/payload_content.rst @@ -0,0 +1,75 @@ + +************************************** +What transferring objects does for you +************************************** + +Imagine your application is processing some data using `pandas DaFrame `_ and you want to pass that data to another LightningWork (Work). This is what the **Payload API** is meant for. + +---- + +************************* +Use the Lightning Payload +************************* + +The Payload enables non JSON-serializable attribute objects to be part of your Work's state and to be communicated to other Works. + +Here is an example: + +.. code-block:: python + + import lightning as L + import pandas as pd + + + class SourceWork(L.LightningWork): + def __init__(self): + super().__init__() + self.df = None + + def run(self): + # do some processing + + df = pd.DataFrame(data={"col1": [1, 2], "col2": [3, 4]}) + + # The object you care about needs to be wrapped into a Payload object. + self.df = L.storage.Payload(df) + + # You can access the original object from the payload using its value property. + print("src", self.df.value) + # src col1 col2 + # 0 1 3 + # 1 2 4 + +Once the Payload object is attached to your Work's state, it can be passed to another work using the LightningFlow (Flow) as follows: + +.. code-block:: python + + import lightning as L + import pandas as pd + + + class DestinationWork(L.LightningWork): + def run(self, df: L.storage.Payload): + # You can access the original object from the payload using its value property. + print("dst", df.value) + # dst col1 col2 + # 0 1 3 + # 1 2 4 + + + class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.src = SourceWork() + self.dst = DestinationWork() + + def run(self): + self.src.run() + # The pandas DataFrame created by the ``SourceWork`` + # is passed to the ``DestinationWork``. + # Internally, Lightning pickles and un-pickle the python object, + # so you receive a copy of the original object. + self.dst.run(df=self.src.df) + + + app = L.LightningApp(Flow()) diff --git a/docs/source-app/core_api/lightning_work/status.rst b/docs/source-app/core_api/lightning_work/status.rst new file mode 100644 index 0000000..af3a27a --- /dev/null +++ b/docs/source-app/core_api/lightning_work/status.rst @@ -0,0 +1,13 @@ +:orphan: + +#################### +LightningWork Status +#################### + +**Audience:** Users who want to understand ``LightningWork`` under the hood. + +**Level:** Advanced + +---- + +.. include:: status_content.rst diff --git a/docs/source-app/core_api/lightning_work/status_content.rst b/docs/source-app/core_api/lightning_work/status_content.rst new file mode 100644 index 0000000..bb1f2f0 --- /dev/null +++ b/docs/source-app/core_api/lightning_work/status_content.rst @@ -0,0 +1,197 @@ + +************************************* +Everything about LightningWork Status +************************************* + +Statuses indicate transition points in the life of a LightningWork (Work) and contain metadata. + +The different stages are: + +.. code-block:: python + + class WorkStageStatus: + NOT_STARTED = "not_started" + STOPPED = "stopped" + PENDING = "pending" + RUNNING = "running" + SUCCEEDED = "succeeded" + FAILED = "failed" + +And a single status is as follows: + +.. code-block:: python + + @dataclass + class WorkStatus: + stage: WorkStageStatus + timestamp: float + reason: Optional[str] = None + message: Optional[str] = None + count: int = 1 + + +On creation, the Work's status flags all evaluate to ``False`` (in particular ``has_started``) and when calling ``work.run`` in your Lightning Flow (Flow), +the Work transitions from ``is_pending`` to ``is_running`` and then to ``has_succeeded`` if everything went well or ``has_failed`` otherwise. + +.. code-block:: python + + from time import sleep + import lightning as L + + + class Work(L.LightningWork): + def run(self, value: int): + sleep(1) + if value == 0: + return + raise Exception(f"The provided value was {value}") + + + class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.work = Work(raise_exception=False) + self.counter = 0 + + def run(self): + if not self.work.has_started: + print("NOT STARTED") + + elif self.work.is_pending: + print("PENDING") + + elif self.work.is_running: + print("RUNNING") + + elif self.work.has_succeeded: + print("SUCCESS") + + elif self.work.has_failed: + print("FAILED") + + elif self.work.has_stopped: + print("STOPPED") + self.stop() + + print(self.work.status) + self.work.run(self.counter) + self.counter += 1 + + + app = L.LightningApp(Flow()) + +Run this app as follows: + +.. code-block:: bash + + lightning run app test.py > app_log.txt + +And here is the expected output inside ``app_log.txt`` and as expected, +we are observing the following transition ``has_started``, ``is_pending``, ``is_running``, ``has_succeeded``, ``is_running`` and ``has_failed`` + +.. code-block:: console + + NOT STARTED + WorkStatus(stage='not_started', timestamp=1653498225.18468, reason=None, message=None, count=1) + PENDING + WorkStatus(stage='pending', timestamp=1653498225.217413, reason=None, message=None, count=1) + PENDING + WorkStatus(stage='pending', timestamp=1653498225.217413, reason=None, message=None, count=1) + PENDING + ... + PENDING + WorkStatus(stage='pending', timestamp=1653498225.217413, reason=None, message=None, count=1) + PENDING + WorkStatus(stage='pending', timestamp=1653498225.217413, reason=None, message=None, count=1) + RUNNING + WorkStatus(stage='running', timestamp=1653498228.825194, reason=None, message=None, count=1) + ... + SUCCESS + WorkStatus(stage='succeeded', timestamp=1653498229.831793, reason=None, message=None, count=1) + SUCCESS + WorkStatus(stage='succeeded', timestamp=1653498229.831793, reason=None, message=None, count=1) + SUCCESS + WorkStatus(stage='succeeded', timestamp=1653498229.831793, reason=None, message=None, count=1) + RUNNING + WorkStatus(stage='running', timestamp=1653498229.846451, reason=None, message=None, count=1) + RUNNING + ... + WorkStatus(stage='running', timestamp=1653498229.846451, reason=None, message=None, count=1) + RUNNING + WorkStatus(stage='running', timestamp=1653498229.846451, reason=None, message=None, count=1) + FAILED + WorkStatus(stage='failed', timestamp=1653498230.852565, reason='user_exception', message='The provided value was 1', count=1) + FAILED + WorkStatus(stage='failed', timestamp=1653498230.852565, reason='user_exception', message='The provided value was 1', count=1) + FAILED + WorkStatus(stage='failed', timestamp=1653498230.852565, reason='user_exception', message='The provided value was 1', count=1) + FAILED + WorkStatus(stage='failed', timestamp=1653498230.852565, reason='user_exception', message='The provided value was 1', count=1) + ... + +In order to access all statuses: + +.. code-block:: python + + from time import sleep + import lightning as L + + + class Work(L.LightningWork): + def run(self, value: int): + sleep(1) + if value == 0: + return + raise Exception(f"The provided value was {value}") + + + class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.work = Work(raise_exception=False) + self.counter = 0 + + def run(self): + print(self.statuses) + self.work.run(self.counter) + self.counter += 1 + + + app = L.LightningApp(Flow()) + + +Run this app as follows: + +.. code-block:: bash + + lightning run app test.py > app_log.txt + +And here is the expected output inside ``app_log.txt``: + + +.. code-block:: console + + # First execution with value = 0 + + [] + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1)] + ... + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498626.185683, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498626.185683, reason=None, message=None, count=1)] + ... + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498626.185683, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498626.185683, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498626.185683, reason=None, message=None, count=1), WorkStatus(stage='succeeded', timestamp=1653498627.191053, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498626.185683, reason=None, message=None, count=1), WorkStatus(stage='succeeded', timestamp=1653498627.191053, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498622.252016, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498626.185683, reason=None, message=None, count=1), WorkStatus(stage='succeeded', timestamp=1653498627.191053, reason=None, message=None, count=1)] + + # Second execution with value = 1 + + [WorkStatus(stage='pending', timestamp=1653498627.204636, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498627.205509, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498627.204636, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498627.205509, reason=None, message=None, count=1)] + ... + [WorkStatus(stage='pending', timestamp=1653498627.204636, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498627.205509, reason=None, message=None, count=1)] + [WorkStatus(stage='pending', timestamp=1653498627.204636, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498627.205509, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498627.205509, reason=None, message=None, count=1), WorkStatus(stage='failed', timestamp=1653498628.210164, reason='user_exception', message='The provided value was 1', count=1)] + [WorkStatus(stage='pending', timestamp=1653498627.204636, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498627.205509, reason=None, message=None, count=1), WorkStatus(stage='running', timestamp=1653498627.205509, reason=None, message=None, count=1), WorkStatus(stage='failed', timestamp=1653498628.210164, reason='user_exception', message='The provided value was 1', count=1)] diff --git a/docs/source-app/core_api/overview.rst b/docs/source-app/core_api/overview.rst new file mode 100644 index 0000000..594433a --- /dev/null +++ b/docs/source-app/core_api/overview.rst @@ -0,0 +1,40 @@ +:orphan: + +.. _core_api: + +############################### +Learn more about Lightning Core +############################### + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Level-up with Lightning Apps + :description: From Basics to Advanced Skills + :col_css: col-md-6 + :button_link: ../levels/basic/index.html + :height: 180 + +.. displayitem:: + :header: Understand Lightning App + :description: Detailed description + :col_css: col-md-6 + :button_link: lightning_app/index.html + :height: 180 + +.. displayitem:: + :header: Understand Lightning Flow + :description: Detailed description + :col_css: col-md-6 + :button_link: lightning_flow.html + :height: 180 + +.. displayitem:: + :header: Understand Lightning Work + :description: Detailed description + :col_css: col-md-6 + :button_link: lightning_work/index.html + :height: 180 diff --git a/docs/source-app/examples/dag/dag.rst b/docs/source-app/examples/dag/dag.rst new file mode 100644 index 0000000..0df028e --- /dev/null +++ b/docs/source-app/examples/dag/dag.rst @@ -0,0 +1,81 @@ +:orphan: + +###################################### +Develop a Directed Acyclic Graph (DAG) +###################################### + +.. _dag_example: + +**Audience:** Users coming from MLOps to Lightning Apps, looking for more flexibility. + +A typical ML training workflow can be implemented with a simple DAG. + +Below is a pseudo-code using the lightning framework that uses a LightningFlow to orchestrate the serial workflow: process data, train a model, and serve the model. + +.. code-block:: python + + import lightning as L + + class DAGFlow(L.LightningFlow): + + def __init__(self): + super().__init__() + self.processor = DataProcessorWork(...) + self.train_work = TrainingWork(...) + self.serve_work = ServeWork(...) + + def run(self): + self.processor.run(...) + self.train_work.run(...) + self.serve_work.run(...) + +Below is a pseudo-code to run several works in parallel using a built-in :class:`~lightning.app.structures.Dict`. + +.. code-block:: python + + import lightning as L + + class DAGFlow(L.LightningFlow): + + def __init__(self): + super().__init__() + ... + self.train_works = L.structures.Dict(**{ + "1": TrainingWork(..., parallel=True), + "2": TrainingWork(..., parallel=True), + "3": TrainingWork(..., parallel=True), + ... + }) + ... + + def run(self): + self.processor.run(...) + + # The flow runs through them all, so we need to guard self.serve_work.run + for work in self.train_works.values(): + work.run(...) + + # Wait for all to have finished without errors. + if not all(w.has_succeeded for w in self.train_works): + continue + + self.serve_work.run(...) + +---- + +********** +Next Steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Scheduled DAG with pandas and sklearn from scratch. + :description: DAG example in pure Lightning. + :col_css: col-md-4 + :button_link: dag_from_scratch.html + :height: 180 + :tag: intermediate diff --git a/docs/source-app/examples/dag/dag_from_scratch.rst b/docs/source-app/examples/dag/dag_from_scratch.rst new file mode 100644 index 0000000..ac843ab --- /dev/null +++ b/docs/source-app/examples/dag/dag_from_scratch.rst @@ -0,0 +1,53 @@ +:orphan: + +################################################### +Scheduled DAG with pandas and sklearn from scratch. +################################################### + +**Audience:** Users coming from MLOps to Lightning Apps, looking for more flexibility. + +**Level:** Intermediate. + +In this example, you will learn how to create a simple DAG which: + +* Download and process some data +* Train several models and report their associated metrics + +and learn how to schedule this entire process. + +Find the complete example `here `_. + +---- + +************************** +Step 1: Implement your DAG +************************** + +Here is an illustration of the DAG to implement: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/simple_dag.png + :alt: Simple DAG + :width: 100 % + +First, let's define the component we need: + +* DataCollector is responsible to download the data +* Processing is responsible to execute a ``processing.py`` script. +* A collection of model work to train all models in parallel. + +.. literalinclude:: ../../../../examples/app/dag/app.py + :lines: 53-75 + +And its run method executes the steps described above. + +.. literalinclude:: ../../../../examples/app/dag/app.py + :lines: 77-100 + +---- + +***************************** +Step 2: Define the scheduling +***************************** + +.. literalinclude:: ../../../../examples/app/dag/app.py + :lines: 103-131 diff --git a/docs/source-app/examples/data_explore_app.rst b/docs/source-app/examples/data_explore_app.rst new file mode 100644 index 0000000..cd7011a --- /dev/null +++ b/docs/source-app/examples/data_explore_app.rst @@ -0,0 +1,5 @@ +:orphan: + +########################## +Build a Data Exploring App +########################## diff --git a/docs/source-app/examples/etl_app.rst b/docs/source-app/examples/etl_app.rst new file mode 100644 index 0000000..5b494e9 --- /dev/null +++ b/docs/source-app/examples/etl_app.rst @@ -0,0 +1,5 @@ +:orphan: + +############### +Build a ETL App +############### diff --git a/docs/source-app/examples/file_server/app.py b/docs/source-app/examples/file_server/app.py new file mode 100644 index 0000000..3afba48 --- /dev/null +++ b/docs/source-app/examples/file_server/app.py @@ -0,0 +1,241 @@ +import json +import os +import tarfile +import uuid +import zipfile +from pathlib import Path + +import lightning as L +from lightning.app.storage import Drive + + +class FileServer(L.LightningWork): + def __init__( + self, + drive: Drive, + base_dir: str = "file_server", + chunk_size=10240, + **kwargs + ): + """This component uploads, downloads files to your application. + + Arguments: + drive: The drive can share data inside your application. + base_dir: The local directory where the data will be stored. + chunk_size: The quantity of bytes to download/upload at once. + """ + super().__init__( + cloud_build_config=L.BuildConfig(["flask, flask-cors"]), + parallel=True, + **kwargs, + ) + # 1: Attach the arguments to the state. + self.drive = drive + self.base_dir = base_dir + self.chunk_size = chunk_size + + # 2: Create a folder to store the data. + os.makedirs(self.base_dir, exist_ok=True) + + # 3: Keep a reference to the uploaded filenames. + self.uploaded_files = dict() + + def get_filepath(self, path: str) -> str: + """Returns file path stored on the file server.""" + return os.path.join(self.base_dir, path) + + def get_random_filename(self) -> str: + """Returns a random hash for the file name.""" + return uuid.uuid4().hex + + def upload_file(self, file): + """Upload a file while tracking its progress.""" + # 1: Track metadata about the file + filename = file.filename + uploaded_file = self.get_random_filename() + meta_file = uploaded_file + ".meta" + self.uploaded_files[filename] = { + "progress": (0, None), "done": False + } + + # 2: Create a stream and write bytes of + # the file to the disk under `uploaded_file` path. + with open(self.get_filepath(uploaded_file), "wb") as out_file: + content = file.read(self.chunk_size) + while content: + # 2.1 Write the file bytes + size = out_file.write(content) + + # 2.2 Update the progress metadata + self.uploaded_files[filename]["progress"] = ( + self.uploaded_files[filename]["progress"][0] + size, + None, + ) + # 4: Read next chunk of data + content = file.read(self.chunk_size) + + # 3: Update metadata that the file has been uploaded. + full_size = self.uploaded_files[filename]["progress"][0] + self.drive.put(self.get_filepath(uploaded_file)) + self.uploaded_files[filename] = { + "progress": (full_size, full_size), + "done": True, + "uploaded_file": uploaded_file, + } + + # 4: Write down the metadata about the file to the disk + meta = { + "original_path": filename, + "display_name": os.path.splitext(filename)[0], + "size": full_size, + "drive_path": uploaded_file, + } + with open(self.get_filepath(meta_file), "w") as f: + json.dump(meta, f) + + # 5: Put the file to the drive. + # It means other components can access get or list them. + self.drive.put(self.get_filepath(meta_file)) + return meta + + def list_files(self, file_path: str): + # 1: Get the local file path of the file server. + file_path = self.get_filepath(file_path) + + # 2: If the file exists in the drive, transfer it locally. + if not os.path.exists(file_path): + self.drive.get(file_path) + + if os.path.isdir(file_path): + result = set() + for _, _, f in os.walk(file_path): + for file in f: + if not file.endswith(".meta"): + for filename, meta in self.uploaded_files.items(): + if meta["uploaded_file"] == file: + result.add(filename) + return {"asset_names": [v for v in result]} + + # 3: If the filepath is a tar or zip file, list their contents + if zipfile.is_zipfile(file_path): + with zipfile.ZipFile(file_path, "r") as zf: + result = zf.namelist() + elif tarfile.is_tarfile(file_path): + with tarfile.TarFile(file_path, "r") as tf: + result = tf.getnames() + else: + raise ValueError("Cannot open archive file!") + + # 4: Returns the matching files. + return {"asset_names": result} + + def run(self): + # 1: Imports flask requirements. + from flask import Flask, request + from flask_cors import CORS + + # 2: Create a flask app + flask_app = Flask(__name__) + CORS(flask_app) + + # 3: Define the upload file endpoint + @flask_app.post("/upload_file/") + def upload_file(): + """Upload a file directly as form data.""" + f = request.files["file"] + return self.upload_file(f) + + @flask_app.get("/") + def list_files(): + return self.list_files(str(Path(self.base_dir).resolve())) + + # 5: Start the flask app while providing the `host` and `port`. + flask_app.run(host=self.host, port=self.port, load_dotenv=False) + + def alive(self): + """Hack: Returns whether the server is alive.""" + return self.url != "" + + +import requests + +from lightning import LightningWork + + +class TestFileServer(LightningWork): + def __init__(self, drive: Drive): + super().__init__(cache_calls=True) + self.drive = drive + + def run(self, file_server_url: str, first=True): + if first: + with open("test.txt", "w") as f: + f.write("Some text.") + + response = requests.post( + file_server_url + "/upload_file/", + files={'file': open("test.txt", 'rb')} + ) + assert response.status_code == 200 + else: + response = requests.get(file_server_url) + assert response.status_code == 200 + assert response.json() == {"asset_names": ["test.txt"]} + + +from lightning import LightningApp, LightningFlow + + +class Flow(LightningFlow): + def __init__(self): + super().__init__() + # 1: Create a drive to share data between works + self.drive = Drive("lit://file_server") + # 2: Create the filer server + self.file_server = FileServer(self.drive) + # 3: Create the file ser + self.test_file_server = TestFileServer(self.drive) + + def run(self): + # 1: Start the file server. + self.file_server.run() + + # 2: Trigger the test file server work when ready. + if self.file_server.alive(): + # 3 Execute the test file server work. + self.test_file_server.run(self.file_server.url) + self.test_file_server.run(self.file_server.url, first=False) + + # 4 When both execution are successful, exit the app. + if self.test_file_server.num_successes == 2: + self.stop() + + def configure_layout(self): + # Expose the file_server component + # in the UI using its `/` endpoint. + return {"name": "File Server", "content": self.file_server} + + +from lightning.app.runners import MultiProcessRuntime + + +def test_file_server(): + app = LightningApp(Flow()) + MultiProcessRuntime(app).dispatch() + + +from lightning.app.testing import run_app_in_cloud + + +def test_file_server_in_cloud(): + # You need to provide the directory containing the app file. + app_dir = "docs/source-app/examples/file_server" + with run_app_in_cloud(app_dir) as (admin_page, view_page, get_logs_fn, name): + """# 1. `admin_page` and `view_page` are playwright Page Objects. + + # Check out https://playwright.dev/python/ doc to learn more. + # You can click the UI and trigger actions. + + # 2. By calling logs = get_logs_fn(), + # you get all the logs currently on the admin page. + """ diff --git a/docs/source-app/examples/file_server/file_server.rst b/docs/source-app/examples/file_server/file_server.rst new file mode 100644 index 0000000..f9f800a --- /dev/null +++ b/docs/source-app/examples/file_server/file_server.rst @@ -0,0 +1,13 @@ +:orphan: + +.. _fileserver_example: + +##################### +Develop a File Server +##################### + +**Prerequisite**: Reach :ref:`level 16+ ` and read the :ref:`Drive article `. + +---- + +.. include:: file_server_content.rst diff --git a/docs/source-app/examples/file_server/file_server_content.rst b/docs/source-app/examples/file_server/file_server_content.rst new file mode 100644 index 0000000..e9e9017 --- /dev/null +++ b/docs/source-app/examples/file_server/file_server_content.rst @@ -0,0 +1,85 @@ + + +********* +Our Goal +********* + +Create a simple Lightning App (App) that allows users to upload files and list the uploaded files. + +---- + +************* +Completed App +************* + +Here is a recording of the final App built in this example, tested with pytest. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/file_server.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/file_server.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +---- + +********** +App Design +********** + +In order to create this App, we need to develop two components and an App: + +* A **File Server Component** that gives you the ability to download or list files shared with your App. This is particularly useful when you want to trigger an ML job but your users need to provide their own data or if the user wants to download the trained checkpoints. + +* A **Test File Server** Component to interact with the file server. + +* An App putting everything together and the App's associated pytest tests. + +---- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 1: Implement the File Server general structure + :description: Put together the shape of the Component + :col_css: col-md-4 + :button_link: file_server_step_1.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Step 2: Implement the File Server upload and list files methods + :description: Add the core functionalities to the Component + :col_css: col-md-4 + :button_link: file_server_step_2.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Step 3: Implement a File Server Testing Component + :description: Create a Component to test the file server + :col_css: col-md-4 + :button_link: file_server_step_3.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Step 4: Implement tests for the File Server component with pytest + :description: Create an App to validate the upload and list files endpoints + :col_css: col-md-4 + :button_link: file_server_step_4.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/file_server/file_server_step_1.rst b/docs/source-app/examples/file_server/file_server_step_1.rst new file mode 100644 index 0000000..8703a1d --- /dev/null +++ b/docs/source-app/examples/file_server/file_server_step_1.rst @@ -0,0 +1,49 @@ +:orphan: + +################################################## +Step 1: Implement the FileServer general structure +################################################## + +Let’s dive in on how to develop the component with the following code: + +.. literalinclude:: ./app.py + :lines: 1-41, 132-158 + :emphasize-lines: 16, 51- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 2: Implement the File Server upload and list files methods + :description: Add the core functionalities to the Component + :col_css: col-md-4 + :button_link: file_server_step_2.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Step 3: Implement a File Server Testing Component + :description: Create a Component to test the file server + :col_css: col-md-4 + :button_link: file_server_step_3.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Step 4: Implement tests for the File Server component with pytest + :description: Create an App to validate the upload and list files endpoints + :col_css: col-md-4 + :button_link: file_server_step_4.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/file_server/file_server_step_2.rst b/docs/source-app/examples/file_server/file_server_step_2.rst new file mode 100644 index 0000000..8647160 --- /dev/null +++ b/docs/source-app/examples/file_server/file_server_step_2.rst @@ -0,0 +1,75 @@ +:orphan: + +################################################################ +Step 2: Implement the File Server upload and list_files methods +################################################################ + +Let's dive in on how to implement these methods. + +*************************** +Implement the upload method +*************************** + +In this method, we are creating a stream between the uploaded file and the uploaded file stored on the file server disk. + +Once the file is uploaded, we are putting the file into the :class:`~lightning.app.storage.drive.Drive`, so it becomes persistent and accessible to all Components. + +.. literalinclude:: ./app.py + :lines: 12, 51-99 + :emphasize-lines: 49 + +******************************* +Implement the fist_files method +******************************* + +First, in this method, we get the file in the file server filesystem, if available in the Drive. Once done, we list the the files under the provided paths and return the results. + +.. literalinclude:: ./app.py + :lines: 12, 100-130 + :emphasize-lines: 9 + + +******************* +Implement utilities +******************* + +.. literalinclude:: ./app.py + :lines: 12, 43-49 + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 1: Implement the File Server general structure + :description: Put together the shape of the Component + :col_css: col-md-4 + :button_link: file_server_step_1.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Step 3: Implement a File Server Testing Component + :description: Create a Component to test the file server + :col_css: col-md-4 + :button_link: file_server_step_3.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Step 4: Implement tests for the File Server component with pytest + :description: Create an App to validate the upload and list files endpoints + :col_css: col-md-4 + :button_link: file_server_step_4.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/file_server/file_server_step_3.rst b/docs/source-app/examples/file_server/file_server_step_3.rst new file mode 100644 index 0000000..4703ef0 --- /dev/null +++ b/docs/source-app/examples/file_server/file_server_step_3.rst @@ -0,0 +1,54 @@ +:orphan: + +################################################# +Step 3: Implement a File Server Testing Component +################################################# + +Let's dive in on how to implement a testing component for a server. + +This component needs to test two things: + +* The **/upload_file/** endpoint by creating a file and sending its content to it. + +* The **/** endpoint listing files, by validating the that previously uploaded file is present in the response. + +.. literalinclude:: ./app.py + :lines: 165-182 + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 1: Implement the File Server general structure + :description: Put together the shape of the Component + :col_css: col-md-4 + :button_link: file_server_step_1.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Step 2: Implement the File Server upload and list files methods + :description: Add the core functionalities to the Component + :col_css: col-md-4 + :button_link: file_server_step_2.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Step 4: Implement tests for the File Server component with pytest + :description: Create an App to validate the upload and list files endpoints + :col_css: col-md-4 + :button_link: file_server_step_4.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/file_server/file_server_step_4.rst b/docs/source-app/examples/file_server/file_server_step_4.rst new file mode 100644 index 0000000..04517df --- /dev/null +++ b/docs/source-app/examples/file_server/file_server_step_4.rst @@ -0,0 +1,127 @@ +:orphan: + +################################################################# +Step 4: Implement tests for the File Server component with pytest +################################################################# + +Let's create a simple App with our **File Server** and **File Server Test** components. + +Once the File Server is up and running, we'll execute the **test_file_server** LightningWork and when both calls are successful, we exit the App using ``self._exit``. + +.. literalinclude:: ./app.py + :lines: 187-218 + + +Simply create a ``test.py`` file with the following code and run ``pytest tests.py``: + +.. literalinclude:: ./app.py + :lines: 221-226 + +To test the App in the cloud, create a ``cloud_test.py`` file with the following code and run ``pytest cloud_test.py``. +Under the hood, we are using the end-to-end testing `playwright `_ library, so you can interact with the UI. + +.. literalinclude:: ./app.py + :lines: 229- + +---- + +******************** +Test the application +******************** + +Clone the Lightning repo and run the following command: + +.. code-block:: bash + + pytest docs/source/examples/file_server/app.py --capture=no -v + +---- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 1: Implement the File Server general structure + :description: Put together the shape of the Component + :col_css: col-md-4 + :button_link: file_server_step_1.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Step 2: Implement the File Server upload and list files methods + :description: Add the core functionalities to the Component + :col_css: col-md-4 + :button_link: file_server_step_2.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Step 3: Implement a File Server Testing Component + :description: Create a Component to test the file server + :col_css: col-md-4 + :button_link: file_server_step_3.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
+ +---- + +****************** +Find more examples +****************** + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Develop a DAG + :description: Create a dag pipeline + :col_css: col-md-4 + :button_link: ../dag/dag.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Develop a Github Repo Script Runner + :description: Run any script on github in the cloud + :col_css: col-md-4 + :button_link: ../github_repo_runner/github_repo_runner.html + :height: 150 + :tag: Intermediate + + +.. displayitem:: + :header: Develop a HPO Sweeper + :description: Train multiple models with different parameters + :col_css: col-md-4 + :button_link: ../hpo/hpo.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Develop a Model Server + :description: Serve multiple models with different parameters + :col_css: col-md-4 + :button_link: ../model_server_app/model_server_app.html + :height: 150 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/github_repo_runner/app.py b/docs/source-app/examples/github_repo_runner/app.py new file mode 100644 index 0000000..486055f --- /dev/null +++ b/docs/source-app/examples/github_repo_runner/app.py @@ -0,0 +1,308 @@ +import io +import os +import subprocess +import sys +from copy import deepcopy +from functools import partial +from subprocess import Popen +from typing import Dict, List, Optional + +from lightning import BuildConfig, CloudCompute, LightningApp, LightningFlow +from lightning.app import structures +from lightning.app.components import TracerPythonScript +from lightning.app.frontend import StreamlitFrontend +from lightning.app.storage import Path +from lightning.app.utilities.state import AppState + + +class GithubRepoRunner(TracerPythonScript): + def __init__( + self, + id: str, + github_repo: str, + script_path: str, + script_args: List[str], + requirements: List[str], + cloud_compute: Optional[CloudCompute] = None, + **kwargs, + ): + """The GithubRepoRunner Component clones a repo, runs a specific script with provided arguments and collect + logs. + + Arguments: + id: Identified of the component. + github_repo: The Github Repo URL to clone. + script_path: The path to the script to execute. + script_args: The arguments to be provided to the script. + requirements: The python requirements tp run the script. + cloud_compute: The object to select the cloud instance. + """ + super().__init__( + script_path=script_path, + script_args=script_args, + cloud_compute=cloud_compute, + cloud_build_config=BuildConfig(requirements=requirements), + **kwargs, + ) + self.id = id + self.github_repo = github_repo + self.logs = [] + + def run(self, *args, **kwargs): + # 1. Hack: Patch stdout so we can capture the logs. + string_io = io.StringIO() + sys.stdout = string_io + + # 2: Use git command line to clone the repo. + repo_name = self.github_repo.split("/")[-1].replace(".git", "") + cwd = os.path.dirname(__file__) + subprocess.Popen( + f"git clone {self.github_repo}", cwd=cwd, shell=True).wait() + + # 3: Execute the parent run method of the TracerPythonScript class. + os.chdir(os.path.join(cwd, repo_name)) + super().run(*args, **kwargs) + + # 4: Get all the collected logs and add them to the state. + # This isn't optimal as heavy, but works for this demo purpose. + self.logs = string_io.getvalue() + string_io.close() + + def configure_layout(self): + return {"name": self.id, "content": self} + + +class PyTorchLightningGithubRepoRunner(GithubRepoRunner): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.best_model_path = None + self.best_model_score = None + + def configure_tracer(self): + from pytorch_lightning import Trainer + from pytorch_lightning.callbacks import Callback + + tracer = super().configure_tracer() + + class TensorboardServerLauncher(Callback): + def __init__(self, work): + # The provided `work` is the + # current ``PyTorchLightningScript`` work. + self.w = work + + def on_train_start(self, trainer, *_): + # Add `host` and `port` for tensorboard to work in the cloud. + cmd = f"tensorboard --logdir='{trainer.logger.log_dir}'" + server_args = f"--host {self.w.host} --port {self.w.port}" + Popen(cmd + " " + server_args, shell=True) + + def trainer_pre_fn(self, *args, work=None, **kwargs): + # Intercept Trainer __init__ call + # and inject a ``TensorboardServerLauncher`` component. + kwargs["callbacks"].append(TensorboardServerLauncher(work)) + return {}, args, kwargs + + # 5. Patch the `__init__` method of the Trainer + # to inject our callback with a reference to the work. + tracer.add_traced( + Trainer, "__init__", pre_fn=partial(trainer_pre_fn, work=self)) + return tracer + + def on_after_run(self, end_script_globals): + import torch + + # 1. Once the script has finished to execute, + # we can collect its globals and access any objects. + trainer = end_script_globals["cli"].trainer + checkpoint_callback = trainer.checkpoint_callback + lightning_module = trainer.lightning_module + + # 2. From the checkpoint_callback, + # we are accessing the best model weights + checkpoint = torch.load(checkpoint_callback.best_model_path) + + # 3. Load the best weights and torchscript the model. + lightning_module.load_state_dict(checkpoint["state_dict"]) + lightning_module.to_torchscript(f"{self.name}.pt") + + # 4. Use lightning.app.storage.Pathto create a reference to the + # torch scripted model. In the cloud with multiple machines, + # by simply passing this reference to another work, + # it triggers automatically a file transfer. + self.best_model_path = Path(f"{self.name}.pt") + + # 5. Keep track of the metrics. + self.best_model_score = float(checkpoint_callback.best_model_score) + + +class KerasGithubRepoRunner(GithubRepoRunner): + """Left to the users to implement.""" + + +class TensorflowGithubRepoRunner(GithubRepoRunner): + """Left to the users to implement.""" + + +GITHUB_REPO_RUNNERS = { + "PyTorch Lightning": PyTorchLightningGithubRepoRunner, + "Keras": KerasGithubRepoRunner, + "Tensorflow": TensorflowGithubRepoRunner, +} + + +class Flow(LightningFlow): + def __init__(self): + super().__init__() + # 1: Keep track of the requests within the state + self.requests = [] + # 2: Create a dictionary of components. + self.ws = structures.Dict() + + def run(self): + # Iterate continuously over all requests + for request_id, request in enumerate(self.requests): + self._handle_request(request_id, deepcopy(request)) + + def _handle_request(self, request_id: int, request: Dict): + # 1: Create a name and find selected framework + name = f"w_{request_id}" + ml_framework = request["train"].pop("ml_framework") + + # 2: If the component hasn't been created yet, create it. + if name not in self.ws: + work_cls = GITHUB_REPO_RUNNERS[ml_framework] + work = work_cls(id=request["id"], **request["train"]) + self.ws[name] = work + + # 3: Run the component + self.ws[name].run() + + # 4: Once the component has finished, + # add metadata to the original request for the UI. + if self.ws[name].best_model_path: + request = self.requests[request_id] + request["best_model_score"] = self.ws[name].best_model_score + request["best_model_path"] = self.ws[name].best_model_path + + def configure_layout(self): + # Create a StreamLit UI for the user to run his Github Repo. + return StreamlitFrontend(render_fn=render_fn) + + +def page_1__create_new_run(state): + import streamlit as st + + st.markdown("# Create a new Run 🎈") + + # 1: Collect arguments from the users + id = st.text_input("Name your run", value="my_first_run") + github_repo = st.text_input( + "Enter a Github Repo URL", value="https://github.com/Lightning-AI/lightning-quick-start.git" + ) + + default_script_args = ( + "--trainer.max_epochs=5" + " --trainer.limit_train_batches=4" + " --trainer.limit_val_batches=4" + " --trainer.callbacks=ModelCheckpoint" + " --trainer.callbacks.monitor=val_acc" + ) + default_requirements = "torchvision, pytorch_lightning, jsonargparse[signatures]" + + script_path = st.text_input("Enter your script to run", value="train_script.py") + script_args = st.text_input("Enter your base script arguments", value=default_script_args) + requirements = st.text_input("Enter your requirements", value=default_requirements) + ml_framework = st.radio( + "Select your ML Training Frameworks", options=["PyTorch Lightning", "Keras", "Tensorflow"] + ) + + if ml_framework not in ("PyTorch Lightning"): + st.write(f"{ml_framework} isn't supported yet.") + return + + clicked = st.button("Submit") + + # 2: If clicked, create a new request. + if clicked: + new_request = { + "id": id, + "train": { + "github_repo": github_repo, + "script_path": script_path, + "script_args": script_args.split(" "), + "requirements": requirements.split(" "), + "ml_framework": ml_framework, + }, + } + # 3: IMPORTANT: Add a new request to the state in-place. + # The flow receives the UI request and dynamically create + # and run the associated work from the request information. + state.requests = state.requests + [new_request] + + +def page_2__view_run_lists(state): + import streamlit as st + + st.markdown("# Run Lists 🎈") + # 1: Iterate through all the requests in the state. + for i, r in enumerate(state.requests): + i = str(i) + # 2: Display information such as request, logs, work state, model score. + work = state._state["structures"]["ws"]["works"][f"w_{i}"] + with st.expander(f"Expand to view Run {i}", expanded=False): + if st.checkbox("Expand to view your configuration", key=i): + st.json(r) + if st.checkbox("Expand to view logs", key=i): + st.code(body=work["vars"]["logs"]) + if st.checkbox("Expand to view your work state", key=i): + work["vars"].pop("logs") + st.json(work) + best_model_score = r.get("best_model_score", None) + if best_model_score: + if st.checkbox("Expand to view your run performance", key=i): + st.json({"best_model_score": best_model_score, "best_model_path": r.get("best_model_path")}) + + +def page_3__view_app_state(state): + import streamlit as st + + st.markdown("# App State 🎈") + st.write(state._state) + + +def render_fn(state: AppState): + import streamlit as st + + page_names_to_funcs = { + "Create a new Run": partial(page_1__create_new_run, state=state), + "View your Runs": partial(page_2__view_run_lists, state=state), + "View the App state": partial(page_3__view_app_state, state=state), + } + selected_page = st.sidebar.selectbox( + "Select a page", page_names_to_funcs.keys()) + page_names_to_funcs[selected_page]() + + +class RootFlow(LightningFlow): + def __init__(self): + super().__init__() + # Create the flow + self.flow = Flow() + + def run(self): + # Run the flow + self.flow.run() + + def configure_layout(self): + # 1: Add the main StreamLit UI + selection_tab = [{ + "name": "Run your Github Repo", + "content": self.flow, + }] + # 2: Add a new tab whenever a new work is dynamically created + run_tabs = [e.configure_layout() for e in self.flow.ws.values()] + # 3: Returns the list of tabs. + return selection_tab + run_tabs + + +app = LightningApp(RootFlow()) diff --git a/docs/source-app/examples/github_repo_runner/github_repo_runner.rst b/docs/source-app/examples/github_repo_runner/github_repo_runner.rst new file mode 100644 index 0000000..7e239b2 --- /dev/null +++ b/docs/source-app/examples/github_repo_runner/github_repo_runner.rst @@ -0,0 +1,15 @@ +:orphan: + +.. _github_repo_script_runner_example: + +################################### +Develop a Github Repo Script Runner +################################### + +**Audience:** Users that want to develop interactive applications which runs Github Repo in the cloud at any scale for multiple users. + +**Prerequisite**: Reach :ref:`level 16+ ` and read the docstring of of :class:`~lightning.app.components.python.tracer.TracerPythonScript` component. + +---- + +.. include:: github_repo_runner_content.rst diff --git a/docs/source-app/examples/github_repo_runner/github_repo_runner_content.rst b/docs/source-app/examples/github_repo_runner/github_repo_runner_content.rst new file mode 100644 index 0000000..2a0a3aa --- /dev/null +++ b/docs/source-app/examples/github_repo_runner/github_repo_runner_content.rst @@ -0,0 +1,97 @@ + +******** +Our Goal +******** + +Create a simple Lightning App (App) where users can enter information in a UI to run a given PyTorch Lightning Script from a given Github Repo with some optional extra Python requirements and arguments. + +Users should be able to monitor their training progress in real-time, view the logs, and get the best monitored metric and associated checkpoint for their models. + +---- + +Completed App +^^^^^^^^^^^^^ + +Here is a recording of the final application built in this example. The example is around 200 lines in total and should give you a great foundation to build your own Lightning App. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/github_app.mp4 + :poster: "https://pl-public-data.s3.amazonaws.com/assets_lightning/github_app.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +---- + +********** +App Design +********** + +In order to develop the App, we need to build several components: + +* A GithubRepoRunner Component that clones a repo, runs a specific script with provided arguments and collect logs. + +* A PyTorch Lightning GithubRepoRunner Component that augments the GithubRepoRunner component to track PyTorch Lightning Trainer. + +* A UI for the users to provide to trigger dynamically a new execution. + +* A Flow to dynamically create GithubRepoRunner once a user submits information from the UI. + +---- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 1: Implement the GithubRepoRunner Component + :description: Clone and execute script from a GitHub Repo. + :col_css: col-md-4 + :button_link: github_repo_runner_step_1.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Step 2: Implement the PyTorch Lightning GithubRepoRunner Component + :description: Automate PyTorch Lightning execution + :col_css: col-md-4 + :button_link: github_repo_runner_step_2.html + :height: 180 + :tag: Advanced + +.. displayitem:: + :header: Step 3: Implement the Flow to manage user requests + :description: Dynamically create GithubRepoRunner + :col_css: col-md-4 + :button_link: github_repo_runner_step_3.html + :height: 180 + :tag: Intermediate + + +.. displayitem:: + :header: Step 4: Implement the UI with StreamLit + :description: Several pages application + :col_css: col-md-4 + :button_link: github_repo_runner_step_4.html + :height: 180 + :tag: Intermediate + + +.. displayitem:: + :header: Step 5: Put it all together + :description: + :col_css: col-md-4 + :button_link: github_repo_runner_step_5.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/github_repo_runner/github_repo_runner_step_1.rst b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_1.rst new file mode 100644 index 0000000..e85ecc9 --- /dev/null +++ b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_1.rst @@ -0,0 +1,62 @@ +:orphan: + +************************************************ +Step 1: Implement the GithubRepoRunner Component +************************************************ + +The GithubRepoRunner Component clones a repo, runs a specific script with provided arguments, and collect logs. + +Let's dive in on how to develop the component with the following code: + +.. literalinclude:: ./app.py + :lines: -72 + +---- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 2: Implement the PyTorch Lightning GithubRepoRunner Component + :description: Automate PyTorch Lightning execution + :col_css: col-md-4 + :button_link: github_repo_runner_step_2.html + :height: 180 + :tag: Advanced + +.. displayitem:: + :header: Step 3: Implement the Flow to manage user requests + :description: Dynamically create GithubRepoRunner + :col_css: col-md-4 + :button_link: github_repo_runner_step_3.html + :height: 180 + :tag: Intermediate + + +.. displayitem:: + :header: Step 4: Implement the UI with StreamLit + :description: Several pages application + :col_css: col-md-4 + :button_link: github_repo_runner_step_4.html + :height: 180 + :tag: Intermediate + + +.. displayitem:: + :header: Step 5: Put it all together + :description: + :col_css: col-md-4 + :button_link: github_repo_runner_step_5.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/github_repo_runner/github_repo_runner_step_2.rst b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_2.rst new file mode 100644 index 0000000..deae884 --- /dev/null +++ b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_2.rst @@ -0,0 +1,68 @@ +:orphan: + +****************************************************************** +Step 2: Implement the PyTorch Lightning GithubRepoRunner Component +****************************************************************** + +The PyTorch Lightning GithubRepoRunner Component subclasses the GithubRepoRunner but tailors the execution experience to PyTorch Lightning. + +As a matter of fact, this component adds two primary tailored features for PyTorch Lightning users: + +* It injects dynamically a custom callback ``TensorboardServerLauncher`` in the PyTorch Lightning Trainer to start a tensorboard server so it can be exposed in Lightning App UI. + +* Once the script has run, the ``on_after_run`` hook of the :class:`~lightning.app.components.python.tracer.TracerPythonScript` is invoked with the script globals, meaning we can collect anything we need. In particular, we are reloading the best model, torch scripting it, and storing its path in the state along side the best metric score. + +Let's dive in on how to develop the component with the following code: + +.. literalinclude:: ./app.py + :lines: 75-136 + +---- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 1: Implement the GithubRepoRunner Component + :description: Clone and execute script from a GitHub Repo. + :col_css: col-md-4 + :button_link: github_repo_runner_step_1.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Step 3: Implement the Flow to manage user requests + :description: Dynamically create GithubRepoRunner + :col_css: col-md-4 + :button_link: github_repo_runner_step_3.html + :height: 180 + :tag: Intermediate + + +.. displayitem:: + :header: Step 4: Implement the UI with StreamLit + :description: Several pages application + :col_css: col-md-4 + :button_link: github_repo_runner_step_4.html + :height: 180 + :tag: Intermediate + + +.. displayitem:: + :header: Step 5: Put it all together + :description: + :col_css: col-md-4 + :button_link: github_repo_runner_step_5.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/github_repo_runner/github_repo_runner_step_3.rst b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_3.rst new file mode 100644 index 0000000..44cf7dd --- /dev/null +++ b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_3.rst @@ -0,0 +1,62 @@ +:orphan: + +************************************************** +Step 3: Implement the Flow to manage user requests +************************************************** + +In step 1 and 2, we have implemented the ``GithubRepoRunner`` and ``PyTorchLightningGithubRepoRunner`` components. + +Now, we are going to develop a component to dynamically handle user requests. +Let's dive in on how to develop the component with the following code: + +.. literalinclude:: ./app.py + :lines: 142-190 + +---- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Step 1: Implement the GithubRepoRunner Component + :description: Clone and execute script from a GitHub Repo. + :col_css: col-md-4 + :button_link: github_repo_runner_step_1.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Step 2: Implement the PyTorch Lightning GithubRepoRunner Component + :description: Automate PyTorch Lightning execution + :col_css: col-md-4 + :button_link: github_repo_runner_step_2.html + :height: 180 + :tag: Advanced + +.. displayitem:: + :header: Step 4: Implement the UI with StreamLit + :description: Several pages application + :col_css: col-md-4 + :button_link: github_repo_runner_step_4.html + :height: 180 + :tag: Intermediate + + +.. displayitem:: + :header: Step 5: Put it all together + :description: + :col_css: col-md-4 + :button_link: github_repo_runner_step_5.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/github_repo_runner/github_repo_runner_step_4.rst b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_4.rst new file mode 100644 index 0000000..16893aa --- /dev/null +++ b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_4.rst @@ -0,0 +1,86 @@ +:orphan: + +*************************************** +Step 4: Implement the UI with StreamLit +*************************************** + +In step 3, we have implemented a Flow which dynamically creates a Work when a new request is added to the requests list. + +From the UI, we create 3 pages with `StreamLit `_: + +* **Page 1**: Create a form with add a new request to the Flow state **requests**. + +* **Page 2**: Iterate through all the requests and display the associated information. + +* **Page 3**: Display the entire App State. + + +Render All Pages +^^^^^^^^^^^^^^^^ + +.. literalinclude:: ./app.py + :lines: 274-284 + +**Page 1** + +.. literalinclude:: ./app.py + :lines: 193-241 + :emphasize-lines: 43 + +**Page 2** + +.. literalinclude:: ./app.py + :lines: 244-264 + +**Page 3** + +.. literalinclude:: ./app.py + :lines: 267-271 + +---- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: 1. Implement the GithubRepoRunner Component + :description: Clone and execute script from a GitHub Repo. + :col_css: col-md-4 + :button_link: github_repo_runner_step_1.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: 2. Implement the PyTorch Lightning GithubRepoRunner Component + :description: Automate PyTorch Lightning execution + :col_css: col-md-4 + :button_link: github_repo_runner_step_2.html + :height: 180 + :tag: Advanced + +.. displayitem:: + :header: 3. Implement the Flow to manage user requests + :description: Dynamically create GithubRepoRunner + :col_css: col-md-4 + :button_link: github_repo_runner_step_3.html + :height: 180 + :tag: Intermediate + +.. displayitem:: + :header: Step 5: Put it all together + :description: + :col_css: col-md-4 + :button_link: github_repo_runner_step_5.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/github_repo_runner/github_repo_runner_step_5.rst b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_5.rst new file mode 100644 index 0000000..9b5b469 --- /dev/null +++ b/docs/source-app/examples/github_repo_runner/github_repo_runner_step_5.rst @@ -0,0 +1,75 @@ +:orphan: + +*************************** +Step 5: Put it all together +*************************** + +Let's dive in on how to develop the component with the following code: + +.. literalinclude:: ./app.py + :lines: 287- + +Run the application +^^^^^^^^^^^^^^^^^^^ + +Clone the Lightning repo and run the following command: + +.. code-block:: bash + + lightning run app docs/source/examples/github_repo_runner/app.py + +Add ``--cloud`` to run this application in the cloud. + +.. code-block:: bash + + lightning run app docs/source/examples/github_repo_runner/app.py --cloud + +---- + +********************** +More hands-on examples +********************** + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Develop a DAG + :description: Create a dag pipeline + :col_css: col-md-4 + :button_link: ../dag/dag.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Develop a File Server + :description: Train multiple models with different parameters + :col_css: col-md-4 + :button_link: ../file_server/file_server.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Develop a HPO Sweeper + :description: Train multiple models with different parameters + :col_css: col-md-4 + :button_link: ../hpo/hpo.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Develop a Model Server + :description: Serve multiple models with different parameters + :col_css: col-md-4 + :button_link: ../model_server/model_server.html + :height: 150 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/hands_on_example.rst b/docs/source-app/examples/hands_on_example.rst new file mode 100644 index 0000000..57fa1e5 --- /dev/null +++ b/docs/source-app/examples/hands_on_example.rst @@ -0,0 +1,50 @@ +:orphan: + +################# +Hands-on Examples +################# + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Build a DAG + :description: Learn how to orchestrate workflows + :col_css: col-md-6 + :button_link: dag/dag.html + :height: 180 + +.. displayitem:: + :header: Build a File Server + :description: Learn how to upload and download files + :col_css: col-md-6 + :button_link: file_server/file_server.html + :height: 180 + +.. displayitem:: + :header: Build a Github Repo Script Runner + :description: Learn how to configure dynamic execution from the UI + :col_css: col-md-6 + :button_link: github_repo_runner/github_repo_runner.html + :height: 180 + +.. displayitem:: + :header: Build a HPO Sweeper + :description: Learn how to scale your training + :col_css: col-md-6 + :button_link: hpo/hpo.html + :height: 180 + +.. displayitem:: + :header: Build a Model Server + :description: Learn how to server your models + :col_css: col-md-6 + :button_link: model_server_app_content.html + :height: 180 + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/index.rst b/docs/source-app/examples/index.rst new file mode 100644 index 0000000..bb7e645 --- /dev/null +++ b/docs/source-app/examples/index.rst @@ -0,0 +1,36 @@ +######## +Examples +######## + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Develop a DAG workflow + :description: Develop sequential, non-reactive workflows + :col_css: col-md-4 + :button_link: dag/dag.html + :height: 150 + +.. displayitem:: + :header: Develop a File Server + :description: Develop a file server + :col_css: col-md-4 + :button_link: file_server/file_server.html + :height: 150 + +.. displayitem:: + :header: Develop a Github Repo Script Runner + :description: Build an app to run a Github repo + :col_css: col-md-4 + :button_link: github_repo_runner/github_repo_runner.html + :height: 150 + +.. displayitem:: + :header: Deploy a model + :description: Learn how to deploy a model + :col_css: col-md-4 + :button_link: model_server_app/model_server_app.html + :height: 150 diff --git a/docs/source-app/examples/model_server_app/app.py b/docs/source-app/examples/model_server_app/app.py new file mode 100644 index 0000000..9985014 --- /dev/null +++ b/docs/source-app/examples/model_server_app/app.py @@ -0,0 +1,34 @@ +from locust_component import Locust +from model_server import MLServer +from train import TrainModel + +from lightning import LightningApp, LightningFlow + + +class TrainAndServe(LightningFlow): + def __init__(self): + super().__init__() + self.train_model = TrainModel() + self.model_server = MLServer( + name="mnist-svm", + implementation="mlserver_sklearn.SKLearnModel", + workers=8, + ) + self.performance_tester = Locust(num_users=100) + + def run(self): + self.train_model.run() + self.model_server.run(self.train_model.best_model_path) + if self.model_server.alive(): + # The performance tester needs the model server to be up + # and running to be started, so the URL is added in the UI. + self.performance_tester.run(self.model_server.url) + + def configure_layout(self): + return [ + {"name": "Server", "content": self.model_server.url + "/docs"}, + {"name": "Server Testing", "content": self.performance_tester}, + ] + + +app = LightningApp(TrainAndServe()) diff --git a/docs/source-app/examples/model_server_app/load_testing.rst b/docs/source-app/examples/model_server_app/load_testing.rst new file mode 100644 index 0000000..97345de --- /dev/null +++ b/docs/source-app/examples/model_server_app/load_testing.rst @@ -0,0 +1,57 @@ +:orphan: + +*********************************** +3. Build the Load Testing Component +*********************************** + +Now, we are going to create a component to test the performance of your model server. + +We are going to use a python performance testing tool called `Locust `_. + +.. literalinclude:: ./locust_component.py + + +Finally, once the component is done, we need to crate a ``locustfile.py`` file which defines the format of the request to send to your model server. + +The endpoint to hit has the following format: ``/v2/models/{MODEL_NAME}/versions/{VERSION}/infer``. + +.. literalinclude:: ./locustfile.py + + +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1. Build a Train Component + :description: Train a model and store its checkpoints with SKlearn + :col_css: col-md-4 + :button_link: train.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 2. Build a Model Server Component + :description: Use MLServer to server your models + :col_css: col-md-4 + :button_link: model_server.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 4. Putting everything together. + :description: Ensemble the components together and run the app + :col_css: col-md-4 + :button_link: putting_everything_together.html + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/model_server_app/locust_component.py b/docs/source-app/examples/model_server_app/locust_component.py new file mode 100644 index 0000000..4351506 --- /dev/null +++ b/docs/source-app/examples/model_server_app/locust_component.py @@ -0,0 +1,42 @@ +import os +import subprocess + +from lightning import BuildConfig, LightningWork + + +class Locust(LightningWork): + def __init__(self, num_users: int = 100): + """This component checks the performance of a server. The server url is passed to its run method. + + Arguments: + num_users: Number of users emulated by Locust + """ + # Note: Using the default port 8089 of Locust. + super().__init__( + port=8089, + parallel=True, + cloud_build_config=BuildConfig(requirements=["locust"]), + ) + self.num_users = num_users + + def run(self, load_tested_url: str): + # 1: Create the locust command line. + cmd = " ".join( + [ + "locust", + "--master-host", + str(self.host), + "--master-port", + str(self.port), + "--host", + str(load_tested_url), + "-u", + str(self.num_users), + ] + ) + # 2: Create another process with locust + process = subprocess.Popen(cmd, cwd=os.path.dirname(__file__), shell=True) + + # 3: Wait for the process to finish. As locust is a server, + # this waits infinitely or if killed. + process.wait() diff --git a/docs/source-app/examples/model_server_app/locustfile.py b/docs/source-app/examples/model_server_app/locustfile.py new file mode 100644 index 0000000..198d6de --- /dev/null +++ b/docs/source-app/examples/model_server_app/locustfile.py @@ -0,0 +1,41 @@ +from locust import FastHttpUser, task +from sklearn import datasets +from sklearn.model_selection import train_test_split + + +class HelloWorldUser(FastHttpUser): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._prepare_inference_request() + + @task + def predict(self): + self.client.post( + "/v2/models/mnist-svm/versions/v0.0.1/infer", + json=self.inference_request, + ) + + def _prepare_inference_request(self): + # The digits dataset + digits = datasets.load_digits() + + # To apply a classifier on this data, + # we need to flatten the image, to + # turn the data in a (samples, feature) matrix: + n_samples = len(digits.images) + data = digits.images.reshape((n_samples, -1)) + + # Split data into train and test subsets + _, X_test, _, _ = train_test_split(data, digits.target, test_size=0.5, shuffle=False) + + x_0 = X_test[0:1] + self.inference_request = { + "inputs": [ + { + "name": "predict", + "shape": x_0.shape, + "datatype": "FP32", + "data": x_0.tolist(), + } + ] + } diff --git a/docs/source-app/examples/model_server_app/model_server.py b/docs/source-app/examples/model_server_app/model_server.py new file mode 100644 index 0000000..8562c63 --- /dev/null +++ b/docs/source-app/examples/model_server_app/model_server.py @@ -0,0 +1,88 @@ +import json +import subprocess + +from lightning import BuildConfig, LightningWork +from lightning.app.storage import Path + +# ML_SERVER_URL = https://github.com/SeldonIO/MLServer + + +class MLServer(LightningWork): + """This components uses SeldonIO MLServer library. + + The model endpoint: /v2/models/{MODEL_NAME}/versions/{VERSION}/infer. + + Arguments: + name: The name of the model for the endpoint. + implementation: The model loader class. + Example: "mlserver_sklearn.SKLearnModel". + Learn more here: $ML_SERVER_URL/tree/master/runtimes + workers: Number of server worker. + """ + + def __init__( + self, + name: str, + implementation: str, + workers: int = 1, + **kwargs, + ): + super().__init__( + parallel=True, + cloud_build_config=BuildConfig( + requirements=["mlserver", "mlserver-sklearn"], + ), + **kwargs, + ) + # 1: Collect the config's. + self.settings = { + "debug": True, + "parallel_workers": workers, + } + self.model_settings = { + "name": name, + "implementation": implementation, + } + # 2: Keep track of latest version + self.version = 1 + + def run(self, model_path: Path): + """The model is downloaded when the run method is invoked. + + Arguments: + model_path: The path to the trained model. + """ + # 1: Use the host and port at runtime so it works in the cloud. + # $ML_SERVER_URL/blob/master/mlserver/settings.py#L50 + if self.version == 1: + # TODO: Reload the next version model of the model. + + self.settings.update({"host": self.host, "http_port": self.port}) + + with open("settings.json", "w") as f: + json.dump(self.settings, f) + + # 2. Store the model-settings + # $ML_SERVER_URL/blob/master/mlserver/settings.py#L120 + self.model_settings["parameters"] = { + "version": f"v0.0.{self.version}", + "uri": str(model_path.absolute()), + } + with open("model-settings.json", "w") as f: + json.dump(self.model_settings, f) + + # 3. Launch the Model Server + subprocess.Popen("mlserver start .", shell=True) + + # 4. Increment the version for the next time run is called. + self.version += 1 + + else: + # TODO: Load the next model and unload the previous one. + pass + + def alive(self): + # Current hack, when the url is available, + # the server is up and running. + # This would be cleaned out and automated. + return self.url != "" diff --git a/docs/source-app/examples/model_server_app/model_server.rst b/docs/source-app/examples/model_server_app/model_server.rst new file mode 100644 index 0000000..283dc97 --- /dev/null +++ b/docs/source-app/examples/model_server_app/model_server.rst @@ -0,0 +1,48 @@ +:orphan: + +************************************* +2. Develop the Model Server Component +************************************* + +In the code below, we use `MLServer `_ which aims to provide an easy way to start serving your machine learning models through a REST and gRPC interface, +fully compliant with KFServing's V2 Dataplane spec. + +.. literalinclude:: ./model_server.py + +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1. Develop a Train Component + :description: Train a model and store its checkpoints with SKlearn + :col_css: col-md-4 + :button_link: train.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 3. Develop a Load Testing Component + :description: Use Locust to test your model servers + :col_css: col-md-4 + :button_link: load_testing.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 4. Putting everything together. + :description: Ensemble the Components together and run the App + :col_css: col-md-4 + :button_link: putting_everything_together.html + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/model_server_app/model_server_app.rst b/docs/source-app/examples/model_server_app/model_server_app.rst new file mode 100644 index 0000000..933c89d --- /dev/null +++ b/docs/source-app/examples/model_server_app/model_server_app.rst @@ -0,0 +1,15 @@ +:orphan: + +.. _model_server_example: + +###################### +Develop a Model Server +###################### + +**Audience:** Users who want to serve their trained models. + +**Prerequisite**: Reach :ref:`level 16+ `. + +---- + +.. include:: model_server_app_content.rst diff --git a/docs/source-app/examples/model_server_app/model_server_app_content.rst b/docs/source-app/examples/model_server_app/model_server_app_content.rst new file mode 100644 index 0000000..0a9280c --- /dev/null +++ b/docs/source-app/examples/model_server_app/model_server_app_content.rst @@ -0,0 +1,84 @@ + +********* +Objective +********* + +Create a simple application that trains and serves a `Sklearn `_ machine learning model with `MLServer from SeldonIO `_ + +---- + +***************** +Final Application +***************** + +Here is a gif of the final application built in this example. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/ml_server_2.gif + +---- + +************* +System Design +************* + +In order to create such application, we need to build several components: + +* A Model Train Component that trains a model and provides its trained weights + +* A Model Server Component that serves as an API endpoint for the model generated by the **Model Train Component**. + +* A Load Testing Component that tests the model server works as expected. This could be used to CI/CD the performance of newly generated models (left to the users). + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/model_server_app_2.png + +Let's dive into the tutorial. + +---- + +******** +Tutorial +******** + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1. Build a Train Component + :description: Train a model and store its checkpoints with SKlearn + :col_css: col-md-4 + :button_link: train.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 2. Build a Model Server Component + :description: Use MLServer to server your models + :col_css: col-md-4 + :button_link: model_server.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 3. Build a Load Testing Component + :description: Use Locust to test your model servers + :col_css: col-md-4 + :button_link: load_testing.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 4. Putting everything together. + :description: Ensemble the components together and run the app + :col_css: col-md-4 + :button_link: putting_everything_together.html + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/model_server_app/putting_everything_together.rst b/docs/source-app/examples/model_server_app/putting_everything_together.rst new file mode 100644 index 0000000..48162a9 --- /dev/null +++ b/docs/source-app/examples/model_server_app/putting_everything_together.rst @@ -0,0 +1,80 @@ +:orphan: + +****************************** +4. Putting everything together +****************************** + +In the code below, we put together the **TrainWork**, the **MLServer** and the **Locust** components in an ``app.py`` file. + +.. literalinclude:: ./app.py + + +*********** +Run the App +*********** + +To run the app, simply open a terminal and execute this command: + +.. code-block:: bash + + lightning run app docs/source/examples/model_deploy_app/app.py + +Here is a gif of the UI. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/ml_server_2.gif + +.. raw:: html + +
+ +Congrats, you have finished the **Build a Model Server** example ! + +---- + +****************** +Find more examples +****************** + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Develop a DAG + :description: Develop a DAG pipeline + :col_css: col-md-4 + :button_link: ../dag/dag.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Develop a File Server + :description: Train multiple models with different parameters + :col_css: col-md-4 + :button_link: ../file_server/file_server.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Develop a Github Repo Script Runner + :description: Run code from the internet in the cloud + :col_css: col-md-4 + :button_link: ../github_repo_runner/github_repo_runner.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Develop a HPO Sweeper + :description: Train multiple models with different parameters + :col_css: col-md-4 + :button_link: ../hpo/hpo.html + :height: 150 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/model_server_app/train.py b/docs/source-app/examples/model_server_app/train.py new file mode 100644 index 0000000..698e0d8 --- /dev/null +++ b/docs/source-app/examples/model_server_app/train.py @@ -0,0 +1,41 @@ +import joblib +from sklearn import datasets, svm +from sklearn.model_selection import train_test_split + +from lightning import LightningWork +from lightning.app.storage import Path + + +class TrainModel(LightningWork): + """This component trains a Sklearn SVC model on digits dataset.""" + + def __init__(self): + super().__init__() + # 1: Add element to the state. + self.best_model_path = None + + def run(self): + # 2: Load the Digits + digits = datasets.load_digits() + + # 3: To apply a classifier on this data, + # we need to flatten the image, to + # turn the data in a (samples, feature) matrix: + n_samples = len(digits.images) + data = digits.images.reshape((n_samples, -1)) + + # 4: Create a classifier: a support vector classifier + classifier = svm.SVC(gamma=0.001) + + # 5: Split data into train and test subsets + X_train, _, y_train, _ = train_test_split(data, digits.target, test_size=0.5, shuffle=False) + + # 6: We learn the digits on the first half of the digits + classifier.fit(X_train, y_train) + + # 7: Save the Sklearn model with `joblib`. + model_file_name = "mnist-svm.joblib" + joblib.dump(classifier, model_file_name) + + # 8: Keep a reference the the generated model. + self.best_model_path = Path("mnist-svm.joblib") diff --git a/docs/source-app/examples/model_server_app/train.rst b/docs/source-app/examples/model_server_app/train.rst new file mode 100644 index 0000000..fdb6f6a --- /dev/null +++ b/docs/source-app/examples/model_server_app/train.rst @@ -0,0 +1,49 @@ +:orphan: + +**************************** +1. Build the Train Component +**************************** + +In the code below, we create a work which trains a simple `SVC `_ model on the digits dataset (classification). + +Once the model is trained, it is saved and a reference :class:`~lightning.app.storage.path.Path` with ``best_model_path`` state attribute. + +.. literalinclude:: ./train.py + +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 2. Build a Model Server Component + :description: Use MLServer to server your models + :col_css: col-md-4 + :button_link: model_server.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 3. Build a Load Testing Component + :description: Use Locust to test your model servers + :col_css: col-md-4 + :button_link: load_testing.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: 4. Putting everything together. + :description: Ensemble the components together and run the app + :col_css: col-md-4 + :button_link: putting_everything_together.html + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-app/examples/research_demo_app.rst b/docs/source-app/examples/research_demo_app.rst new file mode 100644 index 0000000..90276f9 --- /dev/null +++ b/docs/source-app/examples/research_demo_app.rst @@ -0,0 +1,5 @@ +:orphan: + +######################### +Build a Research Demo App +######################### diff --git a/docs/source-app/get_started/add_an_interactive_demo.rst b/docs/source-app/get_started/add_an_interactive_demo.rst new file mode 100644 index 0000000..0ad0e6b --- /dev/null +++ b/docs/source-app/get_started/add_an_interactive_demo.rst @@ -0,0 +1,15 @@ +:orphan: + +####################### +Add an Interactive Demo +####################### + +.. _add_an_interactive_Demo: + +**Required background:** Basic Python familiarity and complete the install guide. + +**Goal:** We'll walk you through the 4 key steps to run a Lightning App that trains and demos a model. + +---- + +.. include:: go_beyond_training_content.rst diff --git a/docs/source-app/get_started/build_model.rst b/docs/source-app/get_started/build_model.rst new file mode 100644 index 0000000..300b220 --- /dev/null +++ b/docs/source-app/get_started/build_model.rst @@ -0,0 +1,73 @@ +:orphan: + +.. _build_model: + +####################### +Build and Train a Model +####################### + +**Required background:** Basic Python familiarity and complete the guide. + +**Goal:** We'll walk you through the creation of a model using PyTorch Lightning. + +---- + +********************************* +A simple PyTorch Lightning script +********************************* + +Let's assume you already have a folder with those two files. + +.. code-block:: bash + + pl_project/ + train.py # your own script to train your models + requirements.txt # your python requirements. + +If you don't, simply create a ``pl_project`` folder with those two files and add the following `PyTorch Lightning `_ code in the ``train.py`` file. This code trains a simple ``AutoEncoder`` on `MNIST Dataset `_. + +.. literalinclude:: ../code_samples/convert_pl_to_app/train.py + +Add the following to the ``requirements.txt`` file. + +.. literalinclude:: ../code_samples/convert_pl_to_app/requirements.txt + +Simply run the following commands in your terminal to install the requirements and train the model. + +.. code-block:: bash + + pip install -r requirements.txt + python train.py + +Get through `PyTorch Lightning Introduction `_ to learn more. + +---- + +********** +Next Steps +********** + +.. raw:: html + +
+
+
+ +.. displayitem:: + :header: Evolve a Model into an ML System + :description: Develop an App to train a model in the cloud + :col_css: col-md-6 + :button_link: training_with_apps.html + :height: 180 + +.. displayitem:: + :header: Start from a Template ML System + :description: Learn about Apps, from a template. + :col_css: col-md-6 + :button_link: go_beyond_training.html + :height: 180 + +.. raw:: html + +
+
diff --git a/docs/source-app/get_started/go_beyond_training.rst b/docs/source-app/get_started/go_beyond_training.rst new file mode 100644 index 0000000..f45e7f9 --- /dev/null +++ b/docs/source-app/get_started/go_beyond_training.rst @@ -0,0 +1,14 @@ +:orphan: + +################################ +Start from an ML system template +################################ + +.. _go_beyond_training: + +**Required background:** Basic Python familiarity and complete the install guide. + +**Goal:** We'll walk you through the 4 key steps to run a Lightning App that trains and demos a model. + + +.. include:: go_beyond_training_content.rst diff --git a/docs/source-app/get_started/go_beyond_training_content.rst b/docs/source-app/get_started/go_beyond_training_content.rst new file mode 100644 index 0000000..769513a --- /dev/null +++ b/docs/source-app/get_started/go_beyond_training_content.rst @@ -0,0 +1,405 @@ +************************************************ +The *Train & Demo PyTorch Lightning* Application +************************************************ + +Find the *Train & Demo PyTorch Lightning* application in the `Lightning.ai App Gallery `_. + +Here is a recording of this App running locally and in the cloud with the same behavior. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/lightning_app_experience_cut.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/lightning_app_experience_cut.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +In the steps below, we are going to show you how to build this application. + +Here are `the entire App's code `_ and `its commented components. `_ + +---- + +************************* +Step 1: Install Lightning +************************* + +If you are using a virtual env, don't forget to activate it before running commands. +You must do so in every new shell. + +.. tip:: We highly recommend using virtual environments. + +.. code:: bash + + pip install lightning + +---- + +**************************************** +Step 2: Install the *Train and Demo* App +**************************************** +The first Lightning App we'll explore is an App to train and demo a machine learning model. + +.. + [|qs_code|], [|qs_live_app|]. + + .. |qs_live_app| raw:: html + + live app + + .. |qs_code| raw:: html + + code + + +Install this App by typing: + +.. code-block:: bash + + lightning install app lightning/quick-start + +Verify the App was succesfully installed: + +.. code-block:: bash + + cd lightning-quick-start + +---- + +*************************** +Step 3: Run the App locally +*************************** + +Run the app locally with the ``run`` command 🤯 + +.. code:: bash + + lightning run app app.py + +---- + +******************************** +Step 4: Run the App in the cloud +******************************** + +Add the ``--cloud`` argument to run on the `Lightning.AI cloud `_. 🤯🤯🤯 + +.. code:: bash + + lightning run app app.py --cloud + +.. + Your app should look like this one (|qs_live_app|) + +---- + +******************* +Understand the code +******************* +The App that we just launched trained a PyTorch Lightning model (although any framework works), then added an interactive demo. + +This is the App's code: + +.. code:: python + + # lightning-quick-start/app.py + import os.path as ops + import lightning as L + from quick_start.components import PyTorchLightningScript, ImageServeGradio + + class TrainDeploy(L.LightningFlow): + def __init__(self): + super().__init__() + self.train_work = PyTorchLightningScript( + script_path=ops.join(ops.dirname(__file__), "./train_script.py"), + script_args=["--trainer.max_epochs=5"], + ) + + self.serve_work = ImageServeGradio(L.CloudCompute()) + + def run(self): + # 1. Run the python script that trains the model + self.train_work.run() + + # 2. when a checkpoint is available, deploy + if self.train_work.best_model_path: + self.serve_work.run(self.train_work.best_model_path) + + def configure_layout(self): + tab_1 = {"name": "Model training", "content": self.train_work} + tab_2 = {"name": "Interactive demo", "content": self.serve_work} + return [tab_1, tab_2] + + app = L.LightningApp(TrainDeploy()) + +Let's break down the code section by section to understand what it is doing. + +---- + +1: Define root component +^^^^^^^^^^^^^^^^^^^^^^^^ + +A Lightning App provides a cohesive product experience for a set of unrelated components. + +The top-level component (Root) must subclass ``L.LightningFlow`` + + +.. code:: python + :emphasize-lines: 6 + + # lightning-quick-start/app.py + import os.path as ops + import lightning as L + from quick_start.components import PyTorchLightningScript, ImageServeGradio + + class TrainDeploy(L.LightningFlow): + def __init__(self): + super().__init__() + self.train_work = PyTorchLightningScript( + script_path=ops.join(ops.dirname(__file__), "./train_script.py"), + script_args=["--trainer.max_epochs=5"], + ) + + self.serve_work = ImageServeGradio(L.CloudCompute("cpu-small")) + + def run(self): + # 1. Run the python script that trains the model + self.train_work.run() + + # 2. when a checkpoint is available, deploy + if self.train_work.best_model_path: + self.serve_work.run(self.train_work.best_model_path) + + def configure_layout(self): + tab_1 = {"name": "Model training", "content": self.train_work} + tab_2 = {"name": "Interactive demo", "content": self.serve_work} + return [tab_1, tab_2] + + app = L.LightningApp(TrainDeploy()) + +---- + +2: Define components +^^^^^^^^^^^^^^^^^^^^ +In the __init__ method, we define the components that make up the App. In this case, we have 2 components, +a component to execute any PyTorch Lightning script (model training) and a second component to +start a Gradio server for demo purposes. + +.. code:: python + :emphasize-lines: 9, 14 + + # lightning-quick-start/app.py + import os.path as ops + import lightning as L + from quick_start.components import PyTorchLightningScript, ImageServeGradio + + class TrainDeploy(L.LightningFlow): + def __init__(self): + super().__init__() + self.train_work = PyTorchLightningScript( + script_path=ops.join(ops.dirname(__file__), "./train_script.py"), + script_args=["--trainer.max_epochs=5"], + ) + + self.serve_work = ImageServeGradio(L.CloudCompute("cpu-small")) + + def run(self): + # 1. Run the python script that trains the model + self.train_work.run() + + # 2. when a checkpoint is available, deploy + if self.train_work.best_model_path: + self.serve_work.run(self.train_work.best_model_path) + + def configure_layout(self): + tab_1 = {"name": "Model training", "content": self.train_work} + tab_2 = {"name": "Interactive demo", "content": self.serve_work} + return [tab_1, tab_2] + + app = L.LightningApp(TrainDeploy()) + +---- + +3: Define how components Flow +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Every component has a ``run`` method. The run method defines the 🌊 Flow 🌊 of how components interact together. + +In this case, we train a model (until completion). When it's done AND there exists a checkpoint, we launch a +demo server: + +.. code:: python + :emphasize-lines: 18, 21, 22 + + # lightning-quick-start/app.py + import os.path as ops + import lightning as L + from quick_start.components import PyTorchLightningScript, ImageServeGradio + + class TrainDeploy(L.LightningFlow): + def __init__(self): + super().__init__() + self.train_work = PyTorchLightningScript( + script_path=ops.join(ops.dirname(__file__), "./train_script.py"), + script_args=["--trainer.max_epochs=5"], + ) + + self.serve_work = ImageServeGradio(L.CloudCompute("cpu-small")) + + def run(self): + # 1. Run the python script that trains the model + self.train_work.run() + + # 2. when a checkpoint is available, deploy + if self.train_work.best_model_path: + self.serve_work.run(self.train_work.best_model_path) + + def configure_layout(self): + tab_1 = {"name": "Model training", "content": self.train_work} + tab_2 = {"name": "Interactive demo", "content": self.serve_work} + return [tab_1, tab_2] + + app = L.LightningApp(TrainDeploy()) + +.. note:: If you've used other ML systems you'll be pleasantly surprised to not find decorators or YAML files. + +---- + +4: Connect web user interfaces +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +All our favorite tools normally have their own web user interfaces (UI). + +Implement the ``configure_layout`` method to connect them together: + +.. code:: python + :emphasize-lines: 24-27 + + # lightning-quick-start/app.py + import os.path as ops + import lightning as L + from quick_start.components import PyTorchLightningScript, ImageServeGradio + + class TrainDeploy(L.LightningFlow): + def __init__(self): + super().__init__() + self.train_work = PyTorchLightningScript( + script_path=ops.join(ops.dirname(__file__), "./train_script.py"), + script_args=["--trainer.max_epochs=5"], + ) + + self.serve_work = ImageServeGradio(L.CloudCompute("cpu-small")) + + def run(self): + # 1. Run the python script that trains the model + self.train_work.run() + + # 2. when a checkpoint is available, deploy + if self.train_work.best_model_path: + self.serve_work.run(self.train_work.best_model_path) + + def configure_layout(self): + tab_1 = {"name": "Model training", "content": self.train_work} + tab_2 = {"name": "Interactive demo", "content": self.serve_work} + return [tab_1, tab_2] + + app = L.LightningApp(TrainDeploy()) + +---- + +5: Init the ``app`` object +^^^^^^^^^^^^^^^^^^^^^^^^^^ +Initialize an ``app`` object with the ``TrainDeploy`` component (this won't run the App yet): + +.. code:: python + :emphasize-lines: 29 + + # lightning-quick-start/app.py + import os.path as ops + import lightning as L + from quick_start.components import PyTorchLightningScript, ImageServeGradio + + class TrainDeploy(L.LightningFlow): + def __init__(self): + super().__init__() + self.train_work = PyTorchLightningScript( + script_path=ops.join(ops.dirname(__file__), "./train_script.py"), + script_args=["--trainer.max_epochs=5"], + ) + + self.serve_work = ImageServeGradio(L.CloudCompute("cpu-small")) + + def run(self): + # 1. Run the python script that trains the model + self.train_work.run() + + # 2. when a checkpoint is available, deploy + if self.train_work.best_model_path: + self.serve_work.run(self.train_work.best_model_path) + + def configure_layout(self): + tab_1 = {"name": "Model training", "content": self.train_work} + tab_2 = {"name": "Interactive demo", "content": self.serve_work} + return [tab_1, tab_2] + + app = L.LightningApp(TrainDeploy()) + +---- + +****************************** +What components are supported? +****************************** +Any component can work with Lightning AI! + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/Lightning.gif + :alt: What is Lightning gif. + :width: 100 % + +---- + +********** +Next Steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Add components to your App + :description: Expand your App by adding components. + :col_css: col-md-4 + :button_link: ../workflows/extend_app.html + :height: 180 + +.. displayitem:: + :header: Build a component + :description: Learn to build your own component. + :col_css: col-md-4 + :button_link: ../workflows/build_lightning_component/index.html + :height: 180 + +.. displayitem:: + :header: Explore more Apps + :description: Explore more apps for inspiration. + :col_css: col-md-4 + :button_link: https://lightning.ai/apps + :height: 180 + +.. displayitem:: + :header: Under the hood + :description: Explore how it works under the hood. + :col_css: col-md-4 + :button_link: ../core_api/lightning_app/index.html + :height: 180 + +.. displayitem:: + :header: Run on your private cloud + :description: Run Lightning Apps on your private VPC or on-prem. + :button_link: ../workflows/run_on_private_cloud.html + :col_css: col-md-4 + :height: 180 + +.. raw:: html + +
+
diff --git a/docs/source-app/get_started/jumpstart_from_app_gallery.rst b/docs/source-app/get_started/jumpstart_from_app_gallery.rst new file mode 100644 index 0000000..5a21833 --- /dev/null +++ b/docs/source-app/get_started/jumpstart_from_app_gallery.rst @@ -0,0 +1,123 @@ +:orphan: + +##################################### +Start from Ready-to-Run Template Apps +##################################### + +.. _jumpstart_from_app_gallery: + +Anyone can build Apps for their own use cases and promote them on the `App Gallery `_. + +In return, you can benefit from the work of others and get started faster by re-using a ready-to-run App close to your own use case. + + +************* +User Workflow +************* + +#. Visit the `App Gallery `_ and look for an App close to your own use case. + + .. raw:: html + +
+ +#. If **Launch** is available, it means the App is live and ready to be used! Take it for a spin. + + .. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/launch_button.png + :alt: Launch Button on lightning.ai + :width: 100 % + +#. By clicking **Clone & Run**, a copy of the App is added to your account and an instance starts running. + + + .. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/clone_and_run.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/clone_and_run.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +#. If you found an App that matches what you need, move to **step 5**! Otherwise, go back to **step 1**. + + .. raw:: html + +
+ +#. Copy the installation command (optionally from the clipboard on the right). + + .. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/install_command.png + :alt: Install command on lightning.ai + :width: 100 % + +#. Copy the command to your local terminal. + + .. code-block:: bash + + lightning install app lightning/hackernews-app + +#. Go through the installation steps. + + .. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/install_an_app.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/install_an_app.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +#. Run the App locally. + + .. code-block:: bash + + cd LAI-Hackernews-App + lightning run app app.py + + .. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/hackernews.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/hackernews.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +#. Open the code with your favorite IDE, modify it, and run it back in the cloud. + + .. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/hackernews_modified.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/hackernews_modified.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +---- + +********** +Next Steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Add Component made by others to your App + :description: Add more functionality to your projects + :col_css: col-md-6 + :button_link: jumpstart_from_component_gallery.html + :height: 180 + +.. displayitem:: + :header: Level-up your skills with Lightning Apps + :description: From Basic to Advanced Skills + :col_css: col-md-6 + :button_link: ../levels/basic/index.html + :height: 180 + +.. raw:: html + +
+
+
diff --git a/docs/source-app/get_started/jumpstart_from_component_gallery.rst b/docs/source-app/get_started/jumpstart_from_component_gallery.rst new file mode 100644 index 0000000..95f7d57 --- /dev/null +++ b/docs/source-app/get_started/jumpstart_from_component_gallery.rst @@ -0,0 +1,151 @@ +:orphan: + +######################################## +Add Component made by others to your App +######################################## + +.. _jumpstart_from_component_gallery: + +Anyone can build components for their own use case and promote them on the `Component Gallery `_. + +In return, you can benefit from the work of others and add new functionalities to your Apps with minimal effort. + + +************* +User Workflow +************* + +#. Visit the `Component Gallery `_ and look for a Component close to something you want to do. + + .. raw:: html + +
+ +#. Check out the code for inspiration or simply install the component from PyPi and use it. + +---- + +************* +Success Story +************* + +The default `Train and Demo Application `_ trains a PyTorch Lightning +model and then starts a demo with `Gradio `_. + +.. code-block:: python + + import os.path as ops + import lightning as L + from quick_start.components import PyTorchLightningScript, ImageServeGradio + + + class TrainDeploy(L.LightningFlow): + def __init__(self): + super().__init__() + self.train_work = PyTorchLightningScript( + script_path=ops.join(ops.dirname(__file__), "./train_script.py"), + script_args=["--trainer.max_epochs=5"], + ) + + self.serve_work = ImageServeGradio(L.CloudCompute("cpu")) + + def run(self): + # 1. Run the python script that trains the model + self.train_work.run() + + # 2. when a checkpoint is available, deploy + if self.train_work.best_model_path: + self.serve_work.run(self.train_work.best_model_path) + + def configure_layout(self): + tab_1 = {"name": "Model training", "content": self.train_work} + tab_2 = {"name": "Interactive demo", "content": self.serve_work} + return [tab_1, tab_2] + + + app = L.LightningApp(TrainDeploy()) + +However, someone who wants to use this Aop (maybe you) found `Lightning HPO `_ +from browsing the `Component Gallery `_ and decided to give it a spin after checking the associated +`Github Repository `_. + +Once ``lightning_hpo`` installed, they improved the default App by easily adding HPO support to their project. + +Here is the resulting App. It is almost the same code, but it's way more powerful now! + +This is the power of `lightning.ai `_ ecosystem 🔥⚡🔥 + +.. code-block:: python + + import os.path as ops + import lightning as L + from quick_start.components import PyTorchLightningScript, ImageServeGradio + import optuna + from optuna.distributions import LogUniformDistribution + from lightning_hpo import Optimizer, BaseObjective + + + class HPOPyTorchLightningScript(PyTorchLightningScript, BaseObjective): + @staticmethod + def distributions(): + return {"model.lr": LogUniformDistribution(0.0001, 0.1)} + + + class TrainDeploy(L.LightningFlow): + def __init__(self): + super().__init__() + self.train_work = Optimizer( + script_path=ops.join(ops.dirname(__file__), "./train_script.py"), + script_args=["--trainer.max_epochs=5"], + objective_cls=HPOPyTorchLightningScript, + n_trials=4, + ) + + self.serve_work = ImageServeGradio(L.CloudCompute("cpu")) + + def run(self): + # 1. Run the python script that trains the model + self.train_work.run() + + # 2. when a checkpoint is available, deploy + if self.train_work.best_model_path: + self.serve_work.run(self.train_work.best_model_path) + + def configure_layout(self): + tab_1 = {"name": "Model training", "content": self.train_work.hi_plot} + tab_2 = {"name": "Interactive demo", "content": self.serve_work} + return [tab_1, tab_2] + + + app = L.LightningApp(TrainDeploy()) + +---- + +********** +Next Steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Start from Ready-to-Run Template Apps + :description: Jump-start your projects development + :col_css: col-md-6 + :button_link: jumpstart_from_app_gallery.html + :height: 180 + +.. displayitem:: + :header: Level-up your skills with Lightning Apps + :description: From Basic to Advanced Skills + :col_css: col-md-6 + :button_link: ../levels/basic/index.html + :height: 180 + +.. raw:: html + +
+
+
diff --git a/docs/source-app/get_started/training_with_apps.rst b/docs/source-app/get_started/training_with_apps.rst new file mode 100644 index 0000000..b7bf450 --- /dev/null +++ b/docs/source-app/get_started/training_with_apps.rst @@ -0,0 +1,125 @@ +:orphan: + +################################ +Evolve a model into an ML system +################################ + +.. _convert_pl_to_app: + +**Required background:** Basic Python familiarity and complete the :ref:`build_model` guide. + +**Goal:** We'll walk you through the two key steps to build your first Lightning App from your existing PyTorch Lightning scripts. + + +******************* +Training and beyond +******************* + +With `PyTorch Lightning `__, we abstracted distributed training and hardware, by organizing PyTorch code. +With `Lightning Apps `__, we unified the local and cloud experience while abstracting infrastructure. + +By using `PyTorch Lightning `__ and `Lightning Apps `__ +together, a completely new world of possibilities emerges. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/pl_to_app_4.png + :alt: From PyTorch Lightning to Lightning App + :width: 100 % + +---- + +****************************************** +1. Write an App to run the train.py script +****************************************** + +This article continues where the :ref:`build_model` guide finished. + +Create an additional file ``app.py`` in the ``pl_project`` folder as follows: + +.. code-block:: bash + + pl_project/ + app.py + train.py + requirements.txt + +Inside the ``app.py`` file, add the following code. + +.. literalinclude:: ../code_samples/convert_pl_to_app/app.py + +This App runs the PyTorch Lightning script contained in the ``train.py`` file using the powerful :class:`~lightning.app.components.python.tracer.TracerPythonScript` component. This is really worth checking out! + +---- + +************************************************ +2. Run the train.py file locally or in the cloud +************************************************ + +First, go to the ``pl_folder`` folder from the local terminal and install the requirements. + +.. code-block:: bash + + cd pl_folder + pip install -r requirements.txt + +To run your app, copy the following command to your local terminal: + +.. code-block:: bash + + lightning run app app.py + +Simply add ``--cloud`` to run this application in the cloud with a GPU machine 🤯 + +.. code-block:: bash + + lightning run app app.py --cloud + + +Congratulations! Now, you know how to run a `PyTorch Lightning `_ script with Lightning Apps. + +Lightning Apps can make your ML system way more powerful, keep reading to learn how. + +---- + +********** +Next Steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Level-up with Lightning Apps + :description: From Basics to Advanced Skills + :col_css: col-md-4 + :button_link: ../levels/basic/index.html + :height: 180 + +.. displayitem:: + :header: Add an Interactive Demo + :description: Add a Gradio Demo once the training is finished + :col_css: col-md-4 + :button_link: add_an_interactive_demo.html + :height: 180 + +.. displayitem:: + :header: Add Model Serving + :description: Serve and load testing with MLServer and Locust + :col_css: col-md-4 + :button_link: ../examples/model_server_app/model_server_app.html + :height: 180 + +.. displayitem:: + :header: Add DAG Orchestration + :description: Organize your processing, training and metrics collection + :col_css: col-md-4 + :button_link: ../examples/dag/dag.html + :height: 180 + +.. displayitem:: + :header: Add Team Collaboration + :description: Create an app to run any PyTorch Lightning Script from Github + :col_css: col-md-4 + :button_link: ../examples/github_repo_runner/github_repo_runner.html + :height: 180 diff --git a/docs/source-app/get_started/what_app_can_do.rst b/docs/source-app/get_started/what_app_can_do.rst new file mode 100644 index 0000000..dc940c3 --- /dev/null +++ b/docs/source-app/get_started/what_app_can_do.rst @@ -0,0 +1,187 @@ +:orphan: + +############################################ +Discover what Lightning Apps can do in 5 min +############################################ + +.. _what_app_can_do: + +Lightning Apps can be plenty things, and while a picture is worth a thousand words, videos showing you examples should be worth even more. + + +***************************** +Flashy - Auto ML App (Public) +***************************** + +Train a model on any image or text dataset without writing any code. Flashy uses `React.js `_ for its frontend. + +Find `Flashy `_ on the App Gallery and the `Flashy codebase. `_ on GitHub. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/flashy.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/flashy.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +.. ---- + +.. *************************************** +.. NVIDIA Omniverse Sampling App (Private) +.. *************************************** + +.. Use `Nvidia Sampling Omniverse `_ to generate synthetic samples from 3D meshes and train an object detector on that data. + +.. .. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/Omniverse-Sampling.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/Omniverse-Sampling.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +---- + +********************* +Research App (Public) +********************* + +Share your paper ``bundled`` with the arxiv link, poster, live jupyter notebook, interactive demo to try the model, and more! + +Find the `Research App `_ on the App Gallery and the `Research App codebase. `_ on GitHub. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/research_app.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/research_app.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +---- + +************************************************ +ScratchPad - Notebook Manager for Team (Public) +************************************************ + +Run multiple Jupyter Notebooks on cloud CPUs or machines with multiple GPUs. + +Find the `ScratchPad App `_ on the App Gallery and the `ScratchPad App codebase `_ on GitHub. + +.. note:: ScratchPad is `tested end-to-end `_ on every Lightning App commit with `pytest `_. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/notebook_apps.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/notebook_apps.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +---- + +*********************** +InVideo Search (Public) +*********************** + +This App lets you find anything you're looking for inside a video. The engine is powered by `Open AI CLIP `_. + +Find the `InVideo Search App `_ on the App Gallery and the `InVideo Search App codebase. `_ in GitHub. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/video_search_2.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/video_search_2.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +---- + +****************************** +AI-powered HackerNews (Public) +****************************** + +Save yourself time, and get Hacker News story recommendations, chosen for you specifically. This Lightning App was designed to illustrate a full end-to-end MLOPs workflow aimed at enterprise recommendation systems. + +Find the `AI-powered HackerNews App `_ on the App Gallery and the `AI-powered HackerNews App codebase. `_ on GitHub. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/hackernews_app.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/hackernews_app.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +---- + +********************************************************************* +Lightning Apps can turn ML into scalable systems in days — not months +********************************************************************* + +Use the Lightning framework to develop any ML system: train and deploy a model, create an ETL pipeline, +or spin up a research demo — using the intuitive principles we pioneered with PyTorch Lightning. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/apps_logos_2.png + :alt: Apps with Logos + :width: 100 % + +Anyone who knows Python can build a Lightning App, even without machine learning experience. + +Lightning Apps are: + +- cloud agnostic +- fault-tolerant, distributed, cost optimized +- production ready +- local and cloud debuggable +- highly reactive & interactive +- connect multiple UIs together +- built for team collaboration +- framework agnostic, use your own stack +- and much more + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/lightning_app_experience_cut.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/lightning_app_experience_cut.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +********** +Next Steps +********** + +.. raw:: html + +
+
+
+ +.. displayitem:: + :header: Build & Train a Model + :description: Discover PyTorch Lightning and train your first Model. + :col_css: col-md-4 + :button_link: build_model.html + :height: 180 + +.. displayitem:: + :header: Evolve a Model into an ML System + :description: Develop an App to train a model in the cloud + :col_css: col-md-4 + :button_link: training_with_apps.html + :height: 180 + +.. displayitem:: + :header: Start from an ML system template + :description: Learn about Apps, from a template. + :col_css: col-md-4 + :button_link: go_beyond_training.html + :height: 180 + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/app_tree.rst b/docs/source-app/glossary/app_tree.rst new file mode 100644 index 0000000..c60d5d8 --- /dev/null +++ b/docs/source-app/glossary/app_tree.rst @@ -0,0 +1,113 @@ +:orphan: + +.. _app_component_tree: + +################### +App Component Tree +################### + +**Audience:** Users who want to know how components can be composed with each other. + +**Level:** Basic + +---- + +************************************** +What is an Application Component Tree? +************************************** + +Components can be nested to form component trees where the LightningFlows are its branches and LightningWorks are its leaves. + +This design enables users to organize and maintain their code with more ease, but more importantly, this helps creating an ecosystem with re-usable components. + +Here's a basic application with four flows and two works (associated tree structure): + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/tree.gif + :alt: Basic App Components + :width: 100 % + + +.. literalinclude:: ../code_samples/quickstart/app_comp.py + +A Lightning app runs all flows into a single process. Its flows coordinate the execution of the works each running in their own independent processes. + +---- + +*********************************************** +How do I define my application component tree? +*********************************************** + +In order to define your application component tree, you need create a tree of components and attach them to your root flow. + +You can attach your components in the **__init__** method of a flow. + +.. code-block:: python + + import lightning as L + + + class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + # The `Work` component is attached here. + self.work = Work() + + # The `NestedFlow` component is attached here. + self.nested_flow = NestedFlow() + +Once done, simply add the root flow to a Lightning app as follows: + +.. code-block:: python + + app = L.LightningApp(RootFlow()) + +---- + +****************************************** +Is my application component tree static? +****************************************** + +No, Lightning supports dynamic flows and works. + +You can simply attach your components in the **run** method of a flow using the Python functions **hasattr**, **setattr**, and **getattr**. + +.. code-block:: python + + class RootFlow(L.LightningFlow): + def run(self): + + if not hasattr(self, "work"): + # The `Work` component is attached here. + setattr(self, "work", Work()) + # Run the `Work` component. + getattr(self, "work").run() + + if not hasattr(self, "nested_flow"): + # The `NestedFlow` component is attached here. + setattr(self, "nested_flow", NestedFlow()) + # Run the `NestedFlow` component. + getattr(self, "wonested_flowrk").run() + + +But it is usually more readable to use Lightning built-in :class:`~lightning.app.structures.Dict` or :class:`~lightning.app.structures.List` as follows: + +.. code-block:: python + + from lightning.app.structures import Dict + + + class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.dict = Dict() + + def run(self): + if "work" not in self.dict: + # The `Work` component is attached here. + self.dict["work"] = Work() + self.dict["work"].run() + + if "nested_flow" not in self.dict: + # The `NestedFlow` component is attached here. + self.dict["nested_flow"] = NestedFlow() + self.dict["nested_flow"].run() diff --git a/docs/source-app/glossary/aws_arn.rst b/docs/source-app/glossary/aws_arn.rst new file mode 100644 index 0000000..fc136b5 --- /dev/null +++ b/docs/source-app/glossary/aws_arn.rst @@ -0,0 +1,83 @@ +:orphan: + +.. _aws_arn: + +####### +AWS ARN +####### + +**Audience:** Users who want to run on their AWS account + +**Level:** Intermediate + +---- + +******************* +What is an AWS ARN? +******************* +An AWS Amazon Resource Name (ARN) are unique identifiers of Amazon resources (datasets, buckets, machines, clusters) with +customized access controls. + +---- + +************* +Create an ARN +************* +To create an ARN, first install the AWS CLI + +.. code:: bash + + # Linux + curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" + unzip awscliv2.zip + sudo ./aws/install + + # MAC + curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" + sudo installer -pkg AWSCLIV2.pkg -target / + + # WINDOWS + msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2.msi + +Or `follow the AWS guide `_. + +Then enter the following commands: + +.. code:: bash + + # TODO + +---- + +*********************** +Run on your AWS account +*********************** +To run on your own AWS account, set up a Lightning cluster (here we name it pikachu): + +.. code:: bash + + lightning create cluster pikachu --provider aws --role-arn arn:aws:iam::1234567890:role/lai-byoc --external-id dummy --region us-west-2 + +Run your code on the pikachu cluster by passing it into CloudCompute: + +.. code:: python + + # app.py + import lightning as L + + class LitWorker(L.LightningWork): + def run(self): + message = """ + ANY python code can run here such as: + - train a model + - launch a deployment server + - label data + - run a react app, dash app, streamlit app, etc... + - start a jupyter notebook + - subprocess.Popen('echo run any shell script, python scripts or non python files') + """ + print(message) + + # uses 1 cloud GPU (or your own hardware) + compute = L.CloudCompute('gpu', clusters=['pikachu']) + app = L.LightningApp(LitWorker(cloud_compute=compute)) diff --git a/docs/source-app/glossary/build_config/build_config.rst b/docs/source-app/glossary/build_config/build_config.rst new file mode 100644 index 0000000..43ba0b0 --- /dev/null +++ b/docs/source-app/glossary/build_config/build_config.rst @@ -0,0 +1,43 @@ +:orphan: + +.. _build_config: + +################### +Build Configuration +################### + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Basic + :description: Learn how to manage Python dependencies for an individual LightningWork + :col_css: col-md-6 + :button_link: build_config_basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Intermediate + :description: Learn how to run custom build commands for a LightningWork + :col_css: col-md-6 + :button_link: build_config_intermediate.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Advanced + :description: Learn how to use a custom Docker image for a LightningWork + :col_css: col-md-6 + :button_link: build_config_advanced.html + :height: 150 + :tag: advanced + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/build_config/build_config_advanced.rst b/docs/source-app/glossary/build_config/build_config_advanced.rst new file mode 100644 index 0000000..bc6f5a2 --- /dev/null +++ b/docs/source-app/glossary/build_config/build_config_advanced.rst @@ -0,0 +1,63 @@ +:orphan: + +############################## +Build Configuration (Advanced) +############################## + +**Audience:** Users who want full control over the docker image that is being installed in the cloud. + +**Level:** Advanced + +Advanced users who need full control over the environment a LightningWork runs in can specify a custom docker image that will be deployed in the cloud. + + +---- + +****************** +Use a docker image +****************** + +Create a :class:`~lightning.app.utilities.packaging.build_config.BuildConfig` and provide a **publicly accessible** link to where the image is hosted: + +.. code-block:: python + + from lightning.app import LightningWork, BuildConfig + + + class MyWork(LightningWork): + def __init__(self): + super().__init__() + + # Using a publicly hosted docker image: + self.cloud_build_config = BuildConfig( + # This is one of the base images Lightning uses by default + image="ghcr.io/gridai/base-images:v1.8-gpu" + ) + + # Can also be combined with extra requirements + self.cloud_build_config = BuildConfig(image="...", requirements=["torchmetrics"]) + + +.. warning:: + Many public hosters like DockerHub apply rate limits for public images. We recommend to pull images from your own registry. + For example, you can set up a + `docker registry on GitHub `_. + + +.. note:: + - The build config only applies when running in the cloud and gets ignored otherwise. A local build config is currently not supported. + - Images from private registries are currently not supported. + +.. note:: + Custom docker images must have python installed. We'll use `virtualenv` from this system python to create a virtual environment. + We'll also configure the `virtualenv` to use the packages installed under system's python so your packages are not lost + +---- + + +********************* +Provide a docker file +********************* + +.. note:: + Not yet supported. Coming soon. diff --git a/docs/source-app/glossary/build_config/build_config_basic.rst b/docs/source-app/glossary/build_config/build_config_basic.rst new file mode 100644 index 0000000..633c173 --- /dev/null +++ b/docs/source-app/glossary/build_config/build_config_basic.rst @@ -0,0 +1,68 @@ +:orphan: + +########################### +Build Configuration (Basic) +########################### + +**Audience:** Users who need to install Python packages for an individual LightningWork. + +**Level:** Basic + +---- + +*********************************** +List dependencies in separate files +*********************************** + +If you are building an app with multiple LightningWorks that have different or even conflicting requirements, split your dependencies into individual files +for more granular control. + +.. code-block:: bash + + ├── app.py + ├── requirements.txt # Global requirements for the entire app + └── works + ├── serve + │ ├── requirements.txt # Requirements specific to the 'serve' work + │ └── serve.py # Source file for the LightningWork + └── train + ├── requirements.txt # Requirements specific to the 'train' work + └── train.py # Source file for the LightningWork + +The requirements.txt file must be located in the same directry as the source file of the LightningWork. +When the LightningWork starts up, it will pick up the requirements file if present and install all listed packages. + +.. note:: + This only applies when running in the cloud. The requirements.txt files get ignored when running locally. + +---- + +*********************************** +Define the requirements in the code +*********************************** + +Instead of listing the requirements in a file, you can also pass them to the LightningWork at runtime using the +:class:`~lightning.app.utilities.packaging.build_config.BuildConfig`: + +.. code-block:: python + :emphasize-lines: 7 + + from lightning.app import LightningWork, BuildConfig + + + class MyWork(LightningWork): + def __init__(self): + super().__init__() + self.cloud_build_config = BuildConfig(requirements=["torch>=1.8", "torchmetrics"]) + +.. note:: + The build config only applies when running in the cloud and gets ignored otherwise. A local build config is currently not supported. + +.. warning:: + Custom base images are not supported with the default CPU cloud compute. For example: + + .. code-block:: py + + class MyWork(LightningWork): + def __init__(self): + super().__init__(cloud_build_config=BuildConfig(image="my-custom-image")) # no cloud compute, for example default work diff --git a/docs/source-app/glossary/build_config/build_config_intermediate.rst b/docs/source-app/glossary/build_config/build_config_intermediate.rst new file mode 100644 index 0000000..de301c6 --- /dev/null +++ b/docs/source-app/glossary/build_config/build_config_intermediate.rst @@ -0,0 +1,56 @@ +:orphan: + +################################## +Build Configuration (Intermediate) +################################## + +**Audience:** Users who need to execute commands to configure the machine before a LightningWork runs on it. + +**Level:** Intermediate + +When a LightningWork machine starts up in the cloud, it uses a lightweight operating system with essential packages pre-installed. +If you need to install additional system packages or run other configuration steps before your code executes on that machine, it is possible to do so by createing a custom +:class:`~lightning.app.utilities.packaging.build_config.BuildConfig`: + +1. Subclass :class:`~lightning.app.utilities.packaging.build_config.BuildConfig`: + + .. code-block:: python + + from lightning.app import BuildConfig + + + @dataclass + class CustomBuildConfig(BuildConfig): + def build_commands(self): + return ["sudo apt-get install libsparsehash-dev"] + + +2. Set the build config on the LightningWork: + + .. code-block:: python + + from lightning.app import LightningWork + + + class MyWork(LightningWork): + def __init__(self): + super().__init__() + + # Use the custom build config + self.cloud_build_config = CustomBuildConfig() + + # Can also be combined with extra requirements + self.cloud_build_config = CustomBuildConfig(requirements=["torchmetrics"]) + +.. note:: + - When you need to execute commands or install tools that require more privileges than the current user has, you can use ``sudo`` without needing to provide a password, e.g., when installing system packages. + - The build config only applies when running in the cloud and gets ignored otherwise. A local build config is currently not supported. + +.. warning:: + Custom base images are not supported with the default CPU cloud compute. For example: + + .. code-block:: py + + class MyWork(LightningWork): + def __init__(self): + super().__init__(cloud_build_config=BuildConfig(image="my-custom-image")) # no cloud compute, for example default work diff --git a/docs/source-app/glossary/command_lines/command_lines.rst b/docs/source-app/glossary/command_lines/command_lines.rst new file mode 100644 index 0000000..1ad4cdf --- /dev/null +++ b/docs/source-app/glossary/command_lines/command_lines.rst @@ -0,0 +1,76 @@ +:orphan: + +############################ +Command-line Interface (CLI) +############################ + +**Audience:** Users looking to create a command line interface (CLI) for their application. + +---- + +************** +What is a CLI? +************** + +A Command-line Interface (CLI) is an user interface (UI) in a terminal to interact with a specific program. + +.. note:: + + The Lightning guideline to build CLI is `lightning ...` or ` ...`. + +As an example, Lightning provides a CLI to interact with your Lightning Apps and the `lightning.ai `_ platform as follows: + +.. code-block:: bash + + main + ├── create - Creates Lightning AI self-managed resources (clusters, etc…) + │ └── cluster - Creates a Lightning AI BYOC compute cluster with your cloud provider credentials. + ├── delete - Deletes Lightning AI self-managed resources (clusters, etc…) + │ └── cluster - Deletes a Lightning AI BYOC compute cluster and all associated cloud provider resources. + ├── fork - Forks an App. + ├── init - Initializes a Lightning App and/or Component. + │ ├── app + │ ├── component + │ ├── pl-app - Creates an App from your PyTorch Lightning source files. + │ └── react-ui - Creates a React UI to give a Lightning Component a React.js web UI + ├── install - Installs a Lightning App and/or Component. + │ ├── app + │ └── component + ├── list - Lists Lightning AI self-managed resources (clusters, etc…) + │ ├── apps - Lists your Lightning AI Apps. + │ └── clusters - Lists your Lightning AI BYOC compute clusters. + ├── login - Logs in to your lightning.ai account. + ├── logout - Logs out of your lightning.ai account. + ├── run - Runs a Lightning App locally or on the cloud. + │ └── app - Runs an App from a file. + ├── show - Shows given resource. + │ ├── cluster - Groups cluster commands inside show. + │ │ └── logs - Shows cluster logs. + │ └── logs - Shows cloud application logs. By default prints logs for all currently available Components. + ├── stop - Stops your App. + └── tree - Shows the command tree of your CLI. + +Learn more about `Command-line interfaces here `_. + +---- + +********** +Learn more +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Develop a Command Line Interface + :description: Learn how to develop a CLI for your App. + :col_css: col-md-6 + :button_link: ../../workflows/build_command_line_interface/index_content.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/dag.rst b/docs/source-app/glossary/dag.rst new file mode 100644 index 0000000..ef85d33 --- /dev/null +++ b/docs/source-app/glossary/dag.rst @@ -0,0 +1,46 @@ +###################### +Directed Acyclic Graph +###################### +**Audience:** Users coming from MLOps to Lightning Apps, looking for more flexibility. + +---- + +***************************** +Is Lightning a DAG framework? +***************************** +No. + +A Lightning App enables developers to express complex, interactive applications that are impossible to create with DAGs. + +---- + +********************************* +Can I Build a DAG with Lightning? +********************************* +Yes! + +DAGs are one of the easiest Lightning Apps to build. For example, here's a `full app that defines a DAG <../examples/dag/dag.html>`_. + +---- + +******** +Examples +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Build a DAG + :description: Learn how to create a DAG with Lightning + :col_css: col-md-4 + :button_link: ../examples/dag/dag.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/debug_app.rst b/docs/source-app/glossary/debug_app.rst new file mode 100644 index 0000000..2d5c0d1 --- /dev/null +++ b/docs/source-app/glossary/debug_app.rst @@ -0,0 +1,3 @@ +:orphan: + +.. include:: ../workflows/debug_locally.rst diff --git a/docs/source-app/glossary/distributed_fe.rst b/docs/source-app/glossary/distributed_fe.rst new file mode 100644 index 0000000..36d64b0 --- /dev/null +++ b/docs/source-app/glossary/distributed_fe.rst @@ -0,0 +1,5 @@ +:orphan: + +##################### +Distributed Front-End +##################### diff --git a/docs/source-app/glossary/distributed_hardware.rst b/docs/source-app/glossary/distributed_hardware.rst new file mode 100644 index 0000000..0a64f5f --- /dev/null +++ b/docs/source-app/glossary/distributed_hardware.rst @@ -0,0 +1,5 @@ +:orphan: + +#################### +Distributed Hardware +#################### diff --git a/docs/source-app/glossary/environment_variables.rst b/docs/source-app/glossary/environment_variables.rst new file mode 100644 index 0000000..10c3e9a --- /dev/null +++ b/docs/source-app/glossary/environment_variables.rst @@ -0,0 +1,27 @@ +.. _environment_variables: + +********************* +Environment Variables +********************* + +If your App is using configuration values you don't want to commit with your App source code, you can use environment variables. + +Lightning allows you to set environment variables when running the App from the CLI with the `lightning run app` command. You can use environment variables to pass any values to the App, and avoiding sticking those values in the source code. + +Set one or multiple variables using the **--env** option: + +.. code:: bash + + lightning run app app.py --cloud --env FOO=BAR --env BAZ=FAZ + +Environment variables are available in all Flows and Works, and can be accessed as follows: + +.. code:: python + + import os + + print(os.environ["FOO"]) # BAR + print(os.environ["BAZ"]) # FAZ + +.. note:: + Environment variables are not encrypted. For sensitive values, we recommend using :ref:`Encrypted Secrets `. diff --git a/docs/source-app/glossary/event_loop.rst b/docs/source-app/glossary/event_loop.rst new file mode 100644 index 0000000..30d1bd3 --- /dev/null +++ b/docs/source-app/glossary/event_loop.rst @@ -0,0 +1,11 @@ +########## +Event loop +########## + +Drawing inspiration from modern web frameworks like `React.js `_, the Lightning App runs all flows in an **event loop** (forever), which is triggered several times a second after collecting any works' state change. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/lightning_loop.gif + +When running a Lightning App in the cloud, the ``LightningWork`` run on different machines. LightningWork communicates any state changes to the **event loop** which re-executes the flow with the newly-collected works' state. + +.. _app_event_loop: diff --git a/docs/source-app/glossary/fault_tolerance.rst b/docs/source-app/glossary/fault_tolerance.rst new file mode 100644 index 0000000..b0ee6df --- /dev/null +++ b/docs/source-app/glossary/fault_tolerance.rst @@ -0,0 +1,7 @@ +:orphan: + +############### +Fault tolerance +############### + +.. note:: documentation under construction diff --git a/docs/source-app/glossary/index.rst b/docs/source-app/glossary/index.rst new file mode 100644 index 0000000..0f92d4b --- /dev/null +++ b/docs/source-app/glossary/index.rst @@ -0,0 +1,163 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + ios_and_android + app_tree + aws_arn + build_config/build_config + command_lines/command_lines + dag + event_loop + environment_variables + secrets + front ends <../workflows/add_web_ui/glossary_front_end> + Lightning app <../core_api/lightning_app/index> + sharing_components + scheduling + storage/storage + restful_api/restful_api + add web ui <../workflows/add_web_ui/glossary_ui> + use_local_lightning + +######## +Glossary +######## + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Android Lightning App + :description: Use Lightning with android apps. + :col_css: col-md-12 + :button_link: ios_and_android.html + :height: 100 + +.. displayitem:: + :header: App Components Tree + :description: Learn how components can be nested to form component trees where the LightningFlows are its branches and LightningWorks are its leaves. + :col_css: col-md-12 + :button_link: app_tree.html + :height: 100 + +.. displayitem:: + :header: AWS ARN + :description: Create an AWS ARN + :col_css: col-md-12 + :button_link: aws_arn.html + :height: 100 + +.. displayitem:: + :header: Build Configuration + :description: Prepare your requirements, add custom build commands or use docker image + :col_css: col-md-12 + :button_link: build_config/build_config.html + :height: 100 + +.. displayitem:: + :header: Command Line Interface (CLI) + :description: Learn about the Lightning CLI + :col_css: col-md-12 + :button_link: command_lines/command_lines.html + :height: 100 + +.. displayitem:: + :header: DAG + :description: Learn about directed acyclic graph, their properties and usage + :col_css: col-md-12 + :button_link: dag.html + :height: 100 + +.. displayitem:: + :header: Event Loop + :description: Learn how the Infinite Event Loop enables high distributed reactivity by triggering after collecting state changes. + :col_css: col-md-12 + :button_link: event_loop.html + :height: 100 + +.. displayitem:: + :header: Environment Variables + :description: Add secrets such as API keys or access tokens + :col_css: col-md-12 + :button_link: environment_variables.html + :height: 100 + +.. displayitem:: + :header: Encrypted Secrets + :description: Learn how to add passwords to your Lightning apps + :col_css: col-md-12 + :button_link: secrets.html + :height: 100 + +.. displayitem:: + :header: Frontend + :description: Customize your App View with any framework you want + :col_css: col-md-12 + :button_link: ../workflows/add_web_ui/glossary_front_end.html + :height: 100 + +.. displayitem:: + :header: iOS Lightning App + :description: Use Lightning with iOS apps. + :col_css: col-md-12 + :button_link: ios_and_android.html + :height: 100 + +.. displayitem:: + :header: Lightning App + :description: A Lightning app is a collection of connected components that form a workflow + :col_css: col-md-12 + :button_link: ../core_api/lightning_app/index.html + :height: 100 + +.. displayitem:: + :header: Mounts + :description: Mount Cloud Data + :col_css: col-md-6 + :button_link: mount.html + :height: 180 + +.. displayitem:: + :header: Sharing Components + :description: Let's create an ecosystem altogether + :col_css: col-md-12 + :button_link: sharing_components.html + :height: 100 + +.. displayitem:: + :header: Scheduling + :description: Orchestrate execution at specific times + :col_css: col-md-12 + :button_link: scheduling.html + :height: 100 + +.. displayitem:: + :header: Storage + :description: Easily share files even across multiple machines + :col_css: col-md-12 + :button_link: storage/storage.html + :height: 100 + +.. displayitem:: + :header: REST API + :description: Learn how to set up a RESTful API endpoint + :col_css: col-md-12 + :button_link: restful_api/restful_api.html + :height: 100 + +.. displayitem:: + :header: UI + :description: Combine multiple frameworks to create your own UI + :col_css: col-md-12 + :button_link: ../workflows/add_web_ui/glossary_ui.html + :height: 100 + +.. displayitem:: + :header: Using a development branch of Lightning on the Cloud + :description: Learn how to contribute to the Lightning framework in the cloud + :col_css: col-md-12 + :button_link: use_local_lightning.html + :height: 100 diff --git a/docs/source-app/glossary/ios_and_android.rst b/docs/source-app/glossary/ios_and_android.rst new file mode 100644 index 0000000..90aeecb --- /dev/null +++ b/docs/source-app/glossary/ios_and_android.rst @@ -0,0 +1,26 @@ + +############################################### +Apple and Android mobile devices with Lightning +############################################### + +Audience: Users who want to develop Lightning Apps for Apple or Android mobile devices. + +---- + +*********************************************************** +Develop a Lightning App for Apple or Android mobile devices +*********************************************************** + +There are a couple of ways you can go about building Lightning Apps that work on Apple or Android mobile devices. + +Option 1 +^^^^^^^^ + +You can develop a Lightning App that interacts with an iOS or Android app. +The ML and backend services live on the Lightning App, but the iOS or Android code (obj-c/swift or android) lives on the mobile devices. + +Option 2 +^^^^^^^^ + +You can build a mobile-first React Lightning App that works on both Apple and Android mobile devices. +The `InVideo app `_ is a good example of a Lightning App that does just that. diff --git a/docs/source-app/glossary/lightning_app_overview/index.rst b/docs/source-app/glossary/lightning_app_overview/index.rst new file mode 100644 index 0000000..09de273 --- /dev/null +++ b/docs/source-app/glossary/lightning_app_overview/index.rst @@ -0,0 +1,11 @@ +:orphan: + +########################### +Lightning Apps Key concepts +########################### + +**Audience:** Users who want to know how the 🤯 magic works under the hood. + +---- + +.. note:: This page is under construction diff --git a/docs/source-app/glossary/mount.rst b/docs/source-app/glossary/mount.rst new file mode 100644 index 0000000..a62d72b --- /dev/null +++ b/docs/source-app/glossary/mount.rst @@ -0,0 +1 @@ +.. include:: ../workflows/mount_cloud_object_store.rst diff --git a/docs/source-app/glossary/restful_api/restful_api.rst b/docs/source-app/glossary/restful_api/restful_api.rst new file mode 100644 index 0000000..a1128f2 --- /dev/null +++ b/docs/source-app/glossary/restful_api/restful_api.rst @@ -0,0 +1,53 @@ +:orphan: + +########### +RESTful API +########### + +**Audience:** Users looking to create an API in their App to allow users to activate functionalities from external sources. + +---- + +********************** +What is a RESTful API? +********************** + +A RESTful API is a set of external URL routes exposed by a server that enables clients to trigger some functionalities, such as getting or putting some data, uploading files, etc.. + +This provides great flexibility for users as they can easily discover functionalities made available by the App Builders. + +The Lightning App framework supports the four primary HTTP methods: `GET`, `POST`, `PUT`, `DELETE`. + +These methods are guidelines to organize your RESTful Services and help users understand your functionalities. + +* **`GET`:** Reads data from the server. +* **`POST`:** Creates new resources. +* **`PUT`:** Updates/replaces existing resources. +* **`DELETE`:** Deletes resources. + +Learn more about `HTTP Methods for RESTful Services here `_. + +The Lightning App framework uses the popular `FastAPI `_ and `Pydantic `_ frameworks under the hood. This means you can use all their features while building your App. + +---- + +********** +Learn more +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Develop a RESTful API + :description: Learn how to develop an API for your App. + :col_css: col-md-6 + :button_link: ../../workflows/build_rest_api/index_content.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/scheduling.rst b/docs/source-app/glossary/scheduling.rst new file mode 100644 index 0000000..0e04dc3 --- /dev/null +++ b/docs/source-app/glossary/scheduling.rst @@ -0,0 +1,185 @@ +:orphan: + +########## +Scheduling +########## + +The Lightning Scheduling system makes it easy to schedule your components execution with any arbitrary conditions. + + +---- + +************************ +Schedule your components +************************ + +The LightningFlow has a ``schedule`` method which can be used to schedule your components. + +.. code-block:: python + + from lightning.app import LightningWork, LightningFlow + from lightning.app.storage import Path + + + class MyFlow(LightningFlow): + + def run(self): + if self.schedule("hourly"): + # run some code once every hour. + + if self.schedule("daily"): + # run some code once day. + + if self.schedule("daily") and anything_else: + # run some code once day if the anything else is also True. + + if self.schedule("2 4 * * mon,fri"): + # defined with cron syntax, run some code at 04:02 on every Monday and Friday. + +Learn more about the cron syntax `here `_ + +---- + +************** +Best Practices +************** + +In the example above, the line ``self.schedule("hourly")`` will return ``True`` for a **single** flow execution every hour. Mathematically, this is known as a dirac. + +1. Instantiate your component under the schedule method and run outside as follows: + +.. code-block:: python + + from lightning.app import LightningFlow + from lightning.app.structures import List + + class ScheduledDAG(LightningFlow): + def __init__(self): + super().__init__() + self.list = List() + + def run(self): + if self.schedule("hourly"): + # dynamically instantiate + # don't forget to always attach + # your components to the flow !!! + self.list.append(MyDAGFlow(...)) + + # run all dags, but the completed ones + # are cached and don't re-execute. + for dag in self.list: + dag.run() + + +2. Run a single work under the schedule with different arguments to have it re-run. + +.. code-block:: python + + from lightning.app import LightningFlow + from time import time + + class ScheduledDAG(LightningFlow): + def __init__(self): + super().__init__() + self.data_processor = DataProcessorWork(...) + + def run(self): + ... + if self.schedule("hourly"): + self.data_processor.run(trigger_time=time()) + + +3. Capture the event in the state and execute your sequential works outside. + +.. code-block:: python + + from lightning.app import LightningFlow + from time import time + + class ScheduledDAG(LightningFlow): + def __init__(self): + super().__init__() + self.should_execute = False + self.data_processor = DataProcessorWork(...) + self.training_work = KerasTrainingWork(...) + + def run(self): + ... + if self.schedule("hourly"): + self.should_execute = True + + # Runs in 10 min + if self.should_execute: + # Runs in 5 min + self.data_processor.run(trigger_time=time()) + if self.data_processor.has_succeeded: + # Runs in 5 min + self.training_work.run(self.data_processor.data) + if self.training_work.has_succeeded: + self.should_execute = False + +---- + +*********** +Limitations +*********** + +As stated above, the schedule acts as a dirac and is **True** for a single flow execution. +Therefore, sequential works execution under the schedule won't work as they don't complete within a single flow execution. + +Here is an example of something which **WON'T** work: + +.. code-block:: python + + from lightning.app import LightningFlow + from time import time + + class ScheduledDAG(LightningFlow): + def __init__(self): + super().__init__() + self.data_processor = DataProcessorWork(...) + self.training_work = KerasTrainingWork(...) + + def run(self): + ... + if self.schedule("hourly"): + # This finishes 5 min later + self.data_processor.run(trigger_time=time()) + if self.data_processor.has_succeeded: + # This will never be reached as the + # data processor will keep processing forever... + self.training_work.run(self.data_processor.data) + +---- + +************************** +Frequently Asked Questions +************************** + +- **Q: Can I use multiple nested scheduler?** No, as they might cancel themselves out, but you can capture the event of one to trigger the next one. + +- **Q: Can I use any arbitrary logic to schedule?** Yes, this design enables absolute flexibility, but you need to be careful to avoid bad practices. + +---- + +******** +Examples +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Build a DAG + :description: Learn how to schedule a DAG execution + :col_css: col-md-4 + :button_link: ../examples/dag/dag.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/secrets.rst b/docs/source-app/glossary/secrets.rst new file mode 100644 index 0000000..09a69ec --- /dev/null +++ b/docs/source-app/glossary/secrets.rst @@ -0,0 +1,74 @@ +.. _secrets: + +################# +Encrypted Secrets +################# + +Encrypted Secrets allow you to pass private data to your apps, like API keys, access tokens, database passwords, or other credentials, in a secure way without exposing them in your code. +Secrets provide you with a secure way to store this data in a way that is accessible to Apps so that they can authenticate third-party services/solutions. + +.. tip:: + For non-sensitive configuration values, we recommend using :ref:`plain-text Environment Variables `. + +************ +Add a secret +************ + +Add the secret to your profile on lightning.ai. +Log in to your lightning.ai account > **Profile** > **Secrets** tab > click the **+New** button. +Provide a name and value to your secret, for example, name could be "github_api_token". + +.. note:: + Secret names must start with a letter and can only contain letters, numbers, dashes, and periods. The Secret names must comply with `RFC1123 naming conventions `_. The Secret value has no restrictions. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning//encrypted_secrets_login.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning//encrypted_secrets_login.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +************ +Use a secret +************ + +1. Add an environment variable to your app to read the secret. For example, add an "api_token" environment variable: + +.. code:: python + + import os + + component.connect(api_token=os.environ["api_token"]) + +2. Pass the secret to your app run with the following command: + +.. code:: bash + + lightning run app app.py --cloud --secret = + +In this example, the command would be: + +.. code:: bash + + lightning run app app.py --cloud --secret api_token=github_api_token + + +The ``--secret`` option can be used for multiple Secrets, and alongside the ``--env`` option. + +Here's an example: + +.. code:: bash + + lightning run app app.py --cloud --env FOO=bar --secret MY_APP_SECRET=my-secret --secret ANOTHER_SECRET=another-secret + + +---- + +****************** +How does this work +****************** + +When a Lightning App (App) **runs in the cloud**, a Secret can be exposed to the App using environment variables. +The value of the Secret is encrypted in the Lightning.ai database, and is only decrypted and accessible to +LightningFlow (Flow) or LightningWork (Work) processes in the cloud (when you use the ``--cloud`` option running your App). diff --git a/docs/source-app/glossary/sharing_components.rst b/docs/source-app/glossary/sharing_components.rst new file mode 100644 index 0000000..f9cc48c --- /dev/null +++ b/docs/source-app/glossary/sharing_components.rst @@ -0,0 +1,50 @@ +##################### +Sharing my components +##################### + +**Audience:** Users who want to know how to share component. + +**Level:** Basic + +---- + +******************************************** +Why should I consider sharing my components? +******************************************** + +Lightning is community driven and its core objective is to make AI accessible to everyone. + +By creating components and sharing them with everyone else, the barrier to entry will go down. + +---- + +************************************ +How should I organize my components? +************************************ + +By design, Lightning components are nested to form component trees where the ``LightningFlows`` are its branches and ``LightningWorks`` are its leaves. + +This design has two primary advantages: + +* This helps users organize and maintain their code with more ease. +* This also helps create an ecosystem with **reusable** components. + + +Now, imagine you have implemented a **KerasScriptRunner** component for training any `Keras `_ model with `Tensorboard UI `_ integrated. + +Here are the best practices steps before sharing the component: + +* **Testing**: Ensure your component is well tested by following the ref:`../testing` guide. +* **Documented**: Ensure your component has a docstring and comes with some usage explications. + +.. Note:: As a Lightning user, it helps to implement your components thinking someone else is going to use them. + +---- + +***************************************** +How should I proceed to share components? +***************************************** + +Once your component is ready, create a *PiPy* package with your own library and then it can be reused by anyone else. + +Here is a `Component Template `_ from `William Falcon `_ to guide your component. diff --git a/docs/source-app/glossary/storage/differences.rst b/docs/source-app/glossary/storage/differences.rst new file mode 100644 index 0000000..ed45edd --- /dev/null +++ b/docs/source-app/glossary/storage/differences.rst @@ -0,0 +1,78 @@ +:orphan: + +################################## +Differences between Drive and Path +################################## + +**Audience:** Users who want to share files between components. + + +The Lightning Storage system makes it easy to share files between LightningWork so you can run your app both locally and in the cloud without changing the code. + + +Lightning storage provides two solutions :class:`~lightning.app.storage.drive.Drive` and :class:`~lightning.app.storage.path.Path` to deal with files locally and in the cloud likewise. + + +---- + +***************** +What is a Drive ? +***************** + +The Drive object provides a central place for your components to share data. + +The drive acts as an isolate folder and any component can access it by knowing its name. + +Your components can put, list, get, delete files from and to the Drive (except LightningFlow's). + +---- + +**************** +What is a Path ? +**************** + +The Path object is a reference to a specific file or directory from a LightningWork and can be used to transfer those files to another LightningWork (one way, from source to destination). + +A good mental representation of the Path Object usage is the `relay race `_. +To make a transfer, the LightningWork Receiver asks (e.g when the path object is passed by the flow to the Receiver) +for a copy of the files (baton) owned by their LightningWork Producer (e.g the work that created the files). + +---- + +********************************* +When should I use Drive vs Path ? +********************************* + +The Drive should be used when you want to easily share data between components but the Path enables to create cleaner shareable +component where you want to exposes some files to be transferred (like an HPO component sharing the best model weights) for anyone else to use. + +The Drive is more intuitive and easier to get on-boarded with, but in more advanced use cases, you might appreciate the Path Object +which makes uni-directional files transfer simpler. + +---- + +.. raw:: html + +
+
+ +.. displayitem:: + :header: The Drive Object. + :description: Put, List and Get Files From a Shared Drive Disk. + :col_css: col-md-4 + :button_link: drive.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: The Path Object. + :description: Transfer Files From One Component to Another by Reference. + :col_css: col-md-4 + :button_link: path.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/storage/drive.rst b/docs/source-app/glossary/storage/drive.rst new file mode 100644 index 0000000..dffdb97 --- /dev/null +++ b/docs/source-app/glossary/storage/drive.rst @@ -0,0 +1,13 @@ +:orphan: + +.. _drive_storage: + +############# +Drive Storage +############# + +**Audience:** Users who want to put, list, and get files from a shared disk space. + +---- + +.. include:: ../../glossary/storage/drive_content_old.rst diff --git a/docs/source-app/glossary/storage/drive_content.rst b/docs/source-app/glossary/storage/drive_content.rst new file mode 100644 index 0000000..8ab7015 --- /dev/null +++ b/docs/source-app/glossary/storage/drive_content.rst @@ -0,0 +1,223 @@ +:orphan: + +************************** +What are Lightning Drives? +************************** + +Lightning Drives are shared app storage that allow you to share files between `LightningWork (Work) <../../core_api/lightning_work/index.html>`_ components, so that you distributed components can share files when running on the cloud. Using drives, you can run your Lightning App both locally and in the cloud without changing the code. + +The Drive object provides a central place for your components to share data. + +The Drive acts as an isolated folder and any component can access it by knowing its name. + +We currently support two types of Drives: Lightning-managed (``lit://``) and S3 (``s3://``). + ++-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------+ +| Lightning-managed (``lit://``) | Allows read-write operations and are accessible through the Drive API from a Work. | +| | | +| | They allow your components to put, list, get, and delete files from and to the Drive (except LightningFlows). | ++-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------+ +| S3 (``s3://``) | S3 is AWS S3 storage mounted at a filesystem mount point. S3 is read-only (for now) and its primary purpose is | +| | to give you a permanent location to access your training data. | +| | | +| | They allow your components to list and get files located on the Drive. | ++-----------------------------------+-------------------------------------------------------------------------------------------------------------------------------+ + +---- + +********************** +What Drives do for you +********************** + +Think of every instance of the Drive object acting like a Google Drive or like Dropbox. + +By sharing the Drive between components through the LightningFlow, +several components can have a shared place to read (S3 Drives) or read and write (Lightning-managed Drives) files from. + +S3 Drive Limitations +^^^^^^^^^^^^^^^^^^^^ + +These limitations only apply to S3 Drives: + +* There is no top level “shareable” S3 drive object. Each S3 Drive is owned by a particular Work. However, it’s possible to create a Drive with the same location across multiple Works. + +* S3 buckets cannot be mounted as Drives once a Work has been instantiated. The `Drive` object must be initialized passed to a Work at creation time. + +* Whenever a Drive is mounted to a Work, an indexing process will be done again for the provided S3 bucket. This may lead to performance issues with particularly large S3 buckets. For context, 1M files with 2-3 levels of nesting takes less than 1 second to index. + +---- + +************** +Create a Drive +************** + +In order to create a Drive, you simply need to pass its name with the prefix ``lit://`` or ``s3://``. + +.. note:: We do not support mounting single objects for S3 buckets, so there must be a trailing `/` in the s3:// URL. For example: ``s3://foo/bar/``. + +.. code-block:: python + + from lightning.app.storage import Drive + + # The identifier of this Drive is ``drive_1`` + # Note: You need to add Lightning protocol ``lit://`` as a prefix. + + drive_1 = Drive("lit://drive_1") + + # The identifier of this Drive is ``drive_2`` + drive_2 = Drive("s3://drive_2/") + +Any component can create a drive object for ``lit://`` Drives. + +.. code-block:: python + + from lightning.app import LightningFlow, LightningWork + from lightning.app.storage import Drive + + + class Flow(LightningFlow): + def __init__(self): + super().__init__() + self.drive_1 = Drive("lit://drive_1") + + def run(self): + ... + + + class Work(LightningWork): + def __init__(self): + super().__init__() + self.drive_1 = Drive("lit://drive_1") + + def run(self): + ... + +---- + +***************************** +Supported actions with Drives +***************************** + +A Lightning-managed Drive supports put, list, get, and delete actions. + +An S3 Drive supports list and get actions (for now). + +.. code-block:: python + + from lightning.app.storage import Drive + + drive = Drive("lit://drive") + + drive.list(".") # Returns [] as empty + + # Created file. + with open("a.txt", "w") as f: + f.write("Hello World !") + + drive.put("a.txt") + + drive.list(".") # Returns ["a.txt"] as the file copied in the Drive during the put action. + + drive.get("a.txt") # Get the file into the current worker + + drive.delete("a.txt") + + drive.list(".") # Returns [] as empty + +---- + +********************************** +Component interactions with Drives +********************************** + +Here is an illustrated code example on how to create drives within Works. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/drive_2.png + +.. code-block:: python + + from lightning.app import LightningFlow, LightningWork, LightningApp + from lightning.app.storage import Drive + + + class Work_A(LightningWork): + def __init__(self): + super().__init__() + # The identifier of the Drive is ``drive_1`` + # Note: You need to add Lightning protocol ``lit://`` as a prefix. + self.drive_1 = Drive("lit://drive_1") + + def run(self): + # 1. Create a file. + with open("a.txt", "w") as f: + f.write("Hello World !") + + # 2. Put the file into the drive. + self.drive_1.put("a.txt") + + + class Work_B(LightningWork): + def __init__(self): + super().__init__() + + # Note: Work B has access 2 drives. + + # The identifier of this Drive is ``drive_1`` + self.drive_1 = Drive("lit://drive_1") + # The identifier of this Drive is ``drive_2`` + self.drive_2 = Drive("lit://drive_2") + + def run(self): + # 1. Create a file. + with open("b.txt", "w") as f: + f.write("Hello World !") + + # 2. Put the file into both drives. + self.drive_1.put("b.txt") + self.drive_2.put("b.txt") + + + class Work_C(LightningWork): + def __init__(self): + super().__init__() + self.drive_2 = Drive("lit://drive_2") + + def run(self): + # 1. Create a file. + with open("c.txt", "w") as f: + f.write("Hello World !") + + # 2. Put the file into the drive. + self.drive_2.put("c.txt") + +---- + +************************* +Transfer files with Drive +************************* + +In the example below, the Drive is created by the Flow and passed to its Works. + +The ``Work_1`` put a file **a.txt** in the **Drive("lit://this_drive_id")** and the ``Work_2`` can list and get the **a.txt** file from it. + +.. literalinclude:: ../../../../examples/app/drive/app.py + +---- + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Learn about the Path Object. + :description: Transfer Files From One Component to Another by Reference. + :col_css: col-md-4 + :button_link: path.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/storage/drive_content_old.rst b/docs/source-app/glossary/storage/drive_content_old.rst new file mode 100644 index 0000000..3c37f88 --- /dev/null +++ b/docs/source-app/glossary/storage/drive_content_old.rst @@ -0,0 +1,199 @@ +:orphan: + + +************ +About Drives +************ + +Lightning Drive storage makes it easy to share files between LightningWorks so you can run your Lightning App both locally and in the cloud without changing the code. + +The Drive object provides a central place for your components to share data. + +The Drive acts as an isolate folder and any component can access it by knowing its name. + +Your components can put, list, get, and delete files from and to the Drive (except LightningFlows). + +---- + +*********************** +What Drive does for you +*********************** + +Think of every instance of the Drive object acting like a Google Drive or like Dropbox. + +By sharing the Drive between components through the LightningFlow, +several components can have a shared place to read and write files from. + +---- + +************** +Create a Drive +************** + +In order to create a Drive, you simply need to pass its name with the prefix ``lit://`` as follows: + +.. code-block:: python + + from lightning.app.storage import Drive + + # The identifier of this Drive is ``drive_1`` + # Note: You need to add Lightning protocol ``lit://`` as a prefix. + + drive_1 = Drive("lit://drive_1") + + # The identifier of this Drive is ``drive_2`` + drive_2 = Drive("lit://drive_2") + +Any components can create a drive object. + +.. code-block:: python + + from lightning.app import LightningFlow, LightningWork + from lightning.app.storage import Drive + + + class Flow(LightningFlow): + def __init__(self): + super().__init__() + self.drive_1 = Drive("lit://drive_1") + + def run(self): + ... + + + class Work(LightningWork): + def __init__(self): + super().__init__() + self.drive_1 = Drive("lit://drive_1") + + def run(self): + ... + +---- + +***************************** +Supported actions with Drives +***************************** + +A Drive supports put, list, get, and delete actions. + +.. code-block:: python + + from lightning.app.storage import Drive + + drive = Drive("lit://drive") + + drive.list(".") # Returns [] as empty + + # Created file. + with open("a.txt", "w") as f: + f.write("Hello World !") + + drive.put("a.txt") + + drive.list(".") # Returns ["a.txt"] as the file copied in the Drive during the put action. + + drive.get("a.txt") # Get the file into the current worker + + drive.delete("a.txt") + + drive.list(".") # Returns [] as empty + +---- + +********************************** +Component interactions with Drives +********************************** + +Here is an illustrated code example on how to create drives within works. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/drive_2.png + +.. code-block:: python + + from lightning.app import LightningFlow, LightningWork, LightningApp + from lightning.app.storage import Drive + + + class Work_A(LightningWork): + def __init__(self): + super().__init__() + # The identifier of the Drive is ``drive_1`` + # Note: You need to add Lightning protocol ``lit://`` as a prefix. + self.drive_1 = Drive("lit://drive_1") + + def run(self): + # 1. Create a file. + with open("a.txt", "w") as f: + f.write("Hello World !") + + # 2. Put the file into the drive. + self.drive_1.put("a.txt") + + + class Work_B(LightningWork): + def __init__(self): + super().__init__() + + # Note: Work B has access 2 drives. + + # The identifier of this Drive is ``drive_1`` + self.drive_1 = Drive("lit://drive_1") + # The identifier of this Drive is ``drive_2`` + self.drive_2 = Drive("lit://drive_2") + + def run(self): + # 1. Create a file. + with open("b.txt", "w") as f: + f.write("Hello World !") + + # 2. Put the file into both drives. + self.drive_1.put("b.txt") + self.drive_2.put("b.txt") + + + class Work_C(LightningWork): + def __init__(self): + super().__init__() + self.drive_2 = Drive("lit://drive_2") + + def run(self): + # 1. Create a file. + with open("c.txt", "w") as f: + f.write("Hello World !") + + # 2. Put the file into the drive. + self.drive_2.put("c.txt") + +---- + +***************************** +Transfer files with Drive +***************************** + +In the example below, the Drive is created by the flow and passed to its LightningWork's. + +The ``Work_1`` put a file **a.txt** in the **Drive("lit://this_drive_id")** and the ``Work_2`` can list and get the **a.txt** file from it. + +.. literalinclude:: ../../../../examples/app/drive/app.py + + +---- + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Learn about the Path Object. + :description: Transfer Files From One Component to Another by Reference. + :col_css: col-md-4 + :button_link: path.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/storage/path.rst b/docs/source-app/glossary/storage/path.rst new file mode 100644 index 0000000..3f4cba6 --- /dev/null +++ b/docs/source-app/glossary/storage/path.rst @@ -0,0 +1,326 @@ +:orphan: + +############ +Path Storage +############ + +**Audience:** Users who want to share files between components. + + +The Lightning Storage system makes it easy to share files between LightningWork so you can run your app both locally and in the cloud without changing the code. + +---- + +*********************** +What is a Path Object ? +*********************** + +The Path object is a reference to a specific file or directory from a LightningWork and can be used to transfer those files to another LightningWork (one way, from source to destination). + +A good mental representation of the Path Object usage is the `relay race `_. +To make a transfer, the receiver asks (e.g when the path object is passed by the flow to the receiver) +for a copy of the files (baton) owned by their producer (e.g the LightningWork which created the files). + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/path2.png + +******************************************* +How does the Path Object works internally ? +******************************************* + +To understand the Path Object internal, let's first answer this question: How do you locate a specific file or folder within a distributed system made of multiple machines ? + +You need to know on which machine the file or folder is located (e.g the LightningWork name uniquely identify its own machine in the cloud) and +then you need the local path of the file or folder on that machine. + +In simple words, the Lightning Path augments :class:`pathlib.Path` object by tracking on which machine the file or folder is located. + +---- + +************************** +When to use Path storage ? +************************** + +In the cloud, every :class:`~lightning.app.core.work.LightningWork` runs in a separate machine with its own filesystem. +This means files in one Work cannot be directly accessed in another like you would be able to when running the app locally. +But with Lightning Storage, this is easy: Simply declare which files need to be shared and Lightning will take care of the rest. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/path.mp4 + :width: 600 + :autoplay: + :loop: + :muted: + + +---- + + +*********************************** +Tell Lightning where your files are +*********************************** + +Convert every filesystem path you want to share with other LightningWorks to by adding ``lit://`` in front of it. + +.. code-block:: python + + from lightning.app import LightningWork + from lightning.app.storage import Path + + + class SourceWork(LightningWork): + def __init__(self): + super().__init__() + self.checkpoint_dir = None + + def run(self): + # Normally you would do: + # self.checkpoint_dir = "outputs/checkpoints" + # os.makedirs("outputs/checkpoints") + # ... + + # In Lightning, do: + self.checkpoint_dir = "lit://outputs/checkpoints" + os.makedirs(self.checkpoint_dir) + ... + + +Under the hood, we convert this string to a :class:`~lightning.app.storage.path.Path` object, which is a drop-in replacement for :class:`pathlib.Path` meaning it will work with :mod:`os`, :mod:`os.path` and :mod:`pathlib` filesystem operations out of the box! + + +---- + + +**************************** +Access files in another Work +**************************** + +Accessing files from another LightningWork is as easy as handing the path over by reference. +For example, share a directory by passing it as an input to the run method of the destination work: + +.. code-block:: python + :emphasize-lines: 12 + + from lightning.app import LightningFlow + + + class Flow(LightningFlow): + def __init__(self): + super().__init__() + self.source = SourceWork() + self.destination = DestinationWork() + + def run(self): + self.source.run() + # Pass the Path reference from one work to another + self.destination.run(self.source.checkpoint_dir) + + +When the destination Work starts, Lightning will automatically transfer the files to its filesystem (if they exist on the other end): + +.. code-block:: python + + class DestinationWork(LightningWork): + def run(self, checkpoint_dir): + # The directory is now accessible inside this Work + files = os.listdir(checkpoint_dir) + ... + + +The automatic transfer only happens if the referenced files already exist in the originating LightningWork and it will overwrite any files that already exist locally. +In all other cases, you can trigger the transfer manually. + + +---- + + +****************** +Get files manually +****************** + +If you need to access files at a specific time or transfer them multiple times, use ``.get()`` method: + +.. code-block:: python + + def run(self, checkpoint_dir): + ... + # Make the directory available + checkpoint_dir.get() + + # If the path already exists locally, you can force overwriting it + checkpoint_dir.get(overwrite=True) + + files = os.listdir(checkpoint_dir) + ... + + +Multiple calls to the ``.get()`` method will always result in file transfers, regardless of whether the files have changed or not. +If the path does not exist remotely, it will raise a ``FileNotFoundError``. +If you need to handle this case, the Path also offers a method to check if files exist remotely. + +---- + + +******************************** +Check if a file or folder exists +******************************** + +You can check if a path exists locally or remotely in the source Work using the ``.exists_local()`` and ``.exists_remote()`` methods: + +.. code-block:: python + + def run(self, checkpoint_dir): + if checkpoint_dir.exists_remote(): + # Get the file only if it exists in the source Work + checkpoint_dir.get() + + # OR + + if checkpoint_dir.exists_local(): + # Do something with the file if it exists locally + files = os.listdir(checkpoint_dir) + + +---- + + +************* +Persist files +************* + +If a LightningWork finishes or stops due to an interruption (e.g., due to insufficient credits), the filesystem and all files in it get deleted (unless running locally). +Lightning makes sure all Paths that are part of the state get stored and made accessible to the other Works that still need these files. + +.. code-block:: python + + from lightning.app.storage import Path + + + class Work(LightningWork): + def __init__(self): + super().__init__() + # The files in this path will be saved as an artifact when the Work finishes + self.checkpoint_dir = "lit://outputs/checkpoints" + + # The files in this path WON'T be saved because it is not declared as a Lightning Path + self.log_dir = "outputs/logs" + + +---- + + +********************************* +Example: Share a model checkpoint +********************************* + +A common workflow in ML is to use a checkpoint created by another component. +First, define a component that saves a checkpoint: + +.. code:: python + :emphasize-lines: 14-18 + + from lightning.app import LightningFlow, LightningWork + from lightning.app.storage import Path + import torch + import os + + + class ModelTraining(LightningWork): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.checkpoint_dir = "lit://outputs/checkpoints" + + def run(self): + # create a directory + os.makedirs(self.model_checkpoints_path, exist_ok=True) + # make fake checkpoints + checkpoint_1 = torch.tensor([0, 1, 2, 3, 4]) + checkpoint_2 = torch.tensor([0, 1, 2, 3, 4]) + torch.save(checkpoint_1, os.path.join(self.checkpoint_dir, "checkpoint_1.ckpt")) + torch.save(checkpoint_2, os.path.join(self.checkpoint_dir, "checkpoint_2.ckpt")) + + +Next, define a component that needs the checkpoints: + +.. code:: python + :emphasize-lines: 4, 7 + + class ModelDeploy(LightningWork): + def __init__(self, *args, **kwargs): + super().__init__() + + def run(self, checkpoint_dir): + ckpts = os.listdir(checkpoint_dir) + checkpoint_1 = torch.load(ckpts[0]) + checkpoint_2 = torch.load(ckpts[1]) + +Link both components via a parent component: + +.. code:: python + :emphasize-lines: 7 + + class Flow(LightningFlow): + def __init__(self): + super().__init__() + self.train = ModelTraining() + + # pass the checkpoint path + self.deploy = ModelDeploy() + + def run(self): + self.train.run() + self.deploy.run(checkpoint_dir=self.train.checkpoint_dir) + + + app = L.LightningApp(Flow()) + + +---- + +************************** +Frequently Asked Questions +************************** + +- **Q: Can files in a LightningWork be accessed inside the LightningFlow too?** + + No, LightningFlow is intentionally designed not to perform filesystem operations and computations and is intended to exclusively orchestrate Flow and Work. + +- **Q: Is it possible to reference any file using the Lightning lit:// path notation?** + + Yes, but only files for which the app has write permissions can be copied from Work to Work (apps don't run with root priviliges). + +- **Q: Can I access the Lightning Storage in my UI (StreamLit, Web, ...)?** + + This is currently not supported but will be in the future. + +- **Q: Should I define my lit:// path in the __init__ or the run method?** + + You can declare a Lightning path anywhere you'd like. However, the ``.get()`` and ``.exists_*()`` methods only work inside of the run method of a LightningWork. + +- **Q:How often does Lightning synchronize the files between my Work?** + + Lightning does not synchronize the files between works. It only transfers the files once when the Work ``run`` method starts. + But you can call ``Path.get()`` as many times as you wish to transfer the latest file into the current Work. + +- **Does Lightning provide me direct access to the shared cloud folder?** + + No, and this is on purpose. This restriction forces developers to build modular components that can be shared and integrated + into apps easily. This would be much harder to achieve if file paths in these components would reference a global shared storage. + +---- + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Learn about the Drive Object. + :description: Put, List and Get Files From a Shared Drive Disk. + :col_css: col-md-4 + :button_link: drive.html + :height: 180 + :tag: Basic + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/storage/storage.rst b/docs/source-app/glossary/storage/storage.rst new file mode 100644 index 0000000..af115a8 --- /dev/null +++ b/docs/source-app/glossary/storage/storage.rst @@ -0,0 +1,77 @@ +.. _storage: + +####### +Storage +####### + +**Audience:** Users who want to share files between components. + + +The Lightning Storage system makes it easy to share files between LightningWork so you can run your app both locally and in the cloud without changing the code. + + +Lightning storage provides two solutions :class:`~lightning.app.storage.drive.Drive` and :class:`~lightning.app.storage.path.Path` to deal with files locally and in the cloud likewise. + + +---- + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Learn about the differences between Drive vs Path. + :description: Learn about their differences. + :col_css: col-md-4 + :button_link: differences.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: The Drive Object. + :description: Put, List and Get Files From a Shared Drive Disk. + :col_css: col-md-4 + :button_link: drive.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: The Path Object. + :description: Transfer Files From One Component to Another by Reference. + :col_css: col-md-4 + :button_link: path.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
+ + +---- + +******** +Examples +******** + + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Build a File Server + :description: Learn how to use Drive to upload / download files to your app. + :col_css: col-md-4 + :button_link: ../../examples/file_server/file_server.html + :height: 180 + :tag: Intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/glossary/use_local_lightning.rst b/docs/source-app/glossary/use_local_lightning.rst new file mode 100644 index 0000000..0987dd3 --- /dev/null +++ b/docs/source-app/glossary/use_local_lightning.rst @@ -0,0 +1,15 @@ +################################################################ +How to run an app on the cloud with a local version of lightning +################################################################ + +The lightning cloud uses the latest release by default. However, you might want to run your app with some local changes you've made to the lightning framework. To use your local version of lightning on the cloud, set the following environment variable: + +```bash +git clone https://github.com/Lightning-AI/lightning.git +cd lightning +pip install -e . +export PACKAGE_LIGHTNING=1 # <- this is the magic to use your version (not mainstream PyPI)! +lightning run app app.py --cloud +``` + +By seting `PACKAGE_LIGHTNING=1`, lightning packages the lightning source code in your local directory in addition to your app source code and uploads them to the cloud. diff --git a/docs/source-app/index.rst b/docs/source-app/index.rst new file mode 100644 index 0000000..8cebcd3 --- /dev/null +++ b/docs/source-app/index.rst @@ -0,0 +1,156 @@ +.. lightning documentation master file, created by + sphinx-quickstart on Sat Sep 19 16:37:02 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +####################### +Welcome to ⚡ Lightning +####################### +Build models, ML components and full stack AI apps ⚡ *Lightning fast*. + +**Featured examples of what you can do with Lightning:** + +| + +.. raw:: html + +
+
+ +.. app_card:: + :title: Develop and Train + :description: Train a model (32 GPUs) + :width: 280 + :image: https://lightning-ai-docs.s3.amazonaws.com/develop_n_train_v1.jpg + :target: levels/basic/real_lightning_component_implementations.html#ex-pytorch-lightning-trainer + :preview: levels/basic/real_lightning_component_implementations.html#ex-pytorch-lightning-trainer + :tags: Training + +.. app_card:: + :title: Serve and deploy + :description: Production diffusion server (<2s latency) + :width: 280 + :app_id: HvUwbEG90E + :image: https://lightning-ai-docs.s3.amazonaws.com/serve_n_deploy_v1.jpg + :deploy: https://lightning.ai + :target: https://01gbx4m78rbkpczdf5cpz2hpbh.litng-ai-03.litng.ai/root.api_component/ + :tags: Serving + +.. app_card:: + :title: Scale and build a product + :description: Production-ready generative AI app + :width: 280 + :app_id: HvUwbEG90E + :image: https://lightning-ai-docs.s3.amazonaws.com/scale_n_build_v1.jpg + :target: https://lightning.ai/muse + :tags: AI App + +.. raw:: html + +
+
+ +---- + +******************************** +Build self-contained, components +******************************** +Use Lightning, the hyper-minimalistic framework, to build machine learning components that can plug into existing ML workflows. +A Lightning component organizes arbitrary code to run on the cloud, manage its own infrastructure, cloud costs, networking, and more. +Focus on component logic and not engineering. + +Use components on their own, or compose them into full-stack AI apps with our next-generation Lightning orchestrator. + +.. raw:: html + +
+ +
+ +| + +| + +**Run an example component on the cloud**: + +.. include:: ./levels/basic/hero_components.rst + +| + +Components run the same on the cloud and locally on your choice of hardware. + +.. lit_tabs:: + :code_files: landing_app_run.bash + :highlights: 5 + :height: 150px + :code_only: True + +Explore pre-built community components in `our gallery `_. + +| + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Get started + :description: Learn to build Lightning components step-by-step. + :col_css: col-md-12 + :button_link: levels/basic/index.html + :height: 160 + :tag: 10 minutes + +.. raw:: html + +
+
+ +.. raw:: html + +
+ +.. toctree:: + :maxdepth: 1 + :caption: Home + + self + Install + +.. toctree:: + :maxdepth: 1 + :caption: Get started in steps + + Basic + Intermediate + Advanced + +.. toctree:: + :maxdepth: 1 + :caption: Core API Reference + + LightningApp + LightningFlow + LightningWork + +.. toctree:: + :maxdepth: 1 + :caption: Addons API Reference + + api_reference/components + api_reference/frontend + api_reference/runners + api_reference/storage + +.. toctree:: + :maxdepth: 1 + :caption: More + + Start from component templates + Start from app templates + Examples + Glossary + How-to diff --git a/docs/source-app/install/install_beginner.rst b/docs/source-app/install/install_beginner.rst new file mode 100644 index 0000000..f690ef7 --- /dev/null +++ b/docs/source-app/install/install_beginner.rst @@ -0,0 +1,117 @@ +:orphan: + +.. _install_beginner: + +############################# +What is a virtual environment +############################# +A virtual environment keeps the packages you install isolated from the rest of your system. +This allows you to work on multiple projects that have different and potentially conflicting requirements, and it +keeps your system Python installation clean. + +.. raw:: html + + + +---- + +We will describe two choices here, pick one: + + +1. :ref:`Python virtualenv `. +2. :ref:`Conda virtual environment `. + +---- + +.. _python-virtualenv: + +******************** +1. Python Virtualenv +******************** + +First, make sure that you have Python 3.8+ installed on your system. + +.. code-block:: bash + + python3 --version + +If you can't run the command above or it returns a version older than 3.8, +`install the latest version of Python `_. +After installing it, make sure you can run the above command without errors. + +---- + +Creating a Virtual Environment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When starting with a new Python project, you typically want to create a new Python virtual environment. +Navigate to the location of your project and run the following command: + +.. code-block:: bash + + python3 -m venv lightning + +The name of the environment here is *lightning* but you can choose any other name you like. +By running the above command, Python will create a new folder *lightning* in the current working directory. + +---- + +Activating the Virtual Environment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Before you can install packages into the environment, you need to activate it: + +.. code-block:: bash + + source lightning/bin/activate + +You need to do this step every time you want to work on your project / open the terminal. +With your virtual environment activated, you are now ready to +:doc:`install Lightning ` and get started with Apps! + +---- + +.. _conda: + +******** +2. Conda +******** + +To get started, you first need to download and install the `Miniconda package manager `_. +To check that the installation was successful, open an new terminal and run: + +.. code:: bash + + conda + +It should return a list of commands. + +---- + +Creating a Conda Environment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When starting with a new Python project, you typically want to create a new Conda virtual environment. +Navigate to the location of your project and run the following command: + +.. code-block:: bash + + conda create --yes --name lightning python=3.8 + +The name of the environment here is *lightning* but you can choose any other name you like. +Note how we can also specify the Python version here. + +---- + +Activating the Conda Environment +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Before you can install packages into the environment, you need to activate it: + +.. code-block:: bash + + conda activate lightning + +You need to do this step every time you want to work on your project / open the terminal. +With your virtual environment activated, you are now ready to +:doc:`install Lightning ` and get started with Apps! diff --git a/docs/source-app/install/installation.rst b/docs/source-app/install/installation.rst new file mode 100644 index 0000000..294e268 --- /dev/null +++ b/docs/source-app/install/installation.rst @@ -0,0 +1,29 @@ + +.. _install: + + +############ +Installation +############ + +**Prerequisites**: Use Python 3.8.x or later (3.8.x, 3.9.x, 3.10.x). We also recommend you install in a virtual environment (learn how). + +.. lit_tabs:: + :descriptions: Pip; Macs, Apple Silicon (M1/M2/M3); Windows + :code_files: pip.bash; mac.bash; windows.bash + :tab_rows: 4 + :height: 180px + +---- + +************ +Troubleshoot +************ +If you encounter issues during installation join our community discord and share the output of the following command: + +.. code:: bash + + pip list | grep lightning + +.. join_slack:: + :align: left diff --git a/docs/source-app/install/mac.bash b/docs/source-app/install/mac.bash new file mode 100644 index 0000000..22825bb --- /dev/null +++ b/docs/source-app/install/mac.bash @@ -0,0 +1,5 @@ +# needed for M1/M2/M3 +export GRPC_PYTHON_BUILD_SYSTEM_OPENSSL=1 +export GRPC_PYTHON_BUILD_SYSTEM_ZLIB=1 + +pip install lightning diff --git a/docs/source-app/install/pip.bash b/docs/source-app/install/pip.bash new file mode 100644 index 0000000..f6d38b7 --- /dev/null +++ b/docs/source-app/install/pip.bash @@ -0,0 +1 @@ +pip install lightning diff --git a/docs/source-app/install/windows.bash b/docs/source-app/install/windows.bash new file mode 100644 index 0000000..150b04e --- /dev/null +++ b/docs/source-app/install/windows.bash @@ -0,0 +1,4 @@ +# install pip +# install git +# setup an alias for Python: python=python3 +# Add the root folder of Lightning to the Environment Variables to PATH diff --git a/docs/source-app/intro.rst b/docs/source-app/intro.rst new file mode 100644 index 0000000..c975ee7 --- /dev/null +++ b/docs/source-app/intro.rst @@ -0,0 +1,88 @@ +:orphan: + +.. _what: + +################### +What is Lightning? +################### + +Lightning is a free, modular, distributed, and open-source framework for building +AI applications where the components you want to use interact together. + +Lightning apps can be built for **any AI use case**, ranging from AI research to +production-ready pipelines (and everything in between!). + +By abstracting the engineering boilerplate, Lightning allows researchers, data scientists, and software engineers to +build highly-scalable, production-ready AI apps using the tools and technologies of their choice, +regardless of their level of engineering expertise. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/Lightning.gif + :alt: What is Lightning gif. + :width: 100 % + +---- + +.. _why: + +*************** +Why Lightning? +*************** + + +Easy to learn +^^^^^^^^^^^^^ + +Lightning was built for creating AI apps, not for dev-ops. It offers an intuitive, pythonic +and highly composable interface that allows you to focus on solving the problems that are important to you. + +---- + +Quick to deliver +^^^^^^^^^^^^^^^^ + +Lightning speeds the development process by offering testable templates you can build from, +accelerating the process of moving from idea to prototype and finally to market. + +---- + +Easy to scale +^^^^^^^^^^^^^ + +Lightning provides a mirrored experience locally and in the cloud. The `lightning.ai `_. +cloud platform abstracts the infrastructure, so you can run your apps at any scale. + +---- + +Easy to collaborate +^^^^^^^^^^^^^^^^^^^ + +Lightning was built for collaboration. +By following the best MLOps practices provided through our documentation and example use cases, +you can deploy state-of-the-art ML applications that are ready to be used by teams of all sizes. + +---- + +***************************** +What's Novel With Lightning? +***************************** + + +Cloud Infra Made Simple and Pythonic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Lightning is for building reactive, scalable, cost effective, easy-to-maintain and reliable ML products in the cloud without worrying about infrastructure. Lightning provides several engineering novelties to enable this: + +#. **Reactivity**: Lightning allows you to run stateful components distributed across different machines, so you can design async, dynamic and reactive workflows in python, without having to define DAGs. + +#. **Scalable & Cost-Effective**: Lightning provides a granular and simple way to run components preemptively or on-demand and on any desired resource such as CPU or GPU. It also enables you to easily transfer artifacts from one machine to another. + +#. **Reliability**: + + #. **Checkpointing**: Lightning apps can be paused and resumed from generated state and artifact-based checkpoints. + #. **Resilience**: Lightning has a strong fault-tolerance foundation. Your application can be written and tested to be resilient for cloud hazards at the component level. + #. **Testing Tools**: Lightning provides you with tools and best practices you can use to develop and test your application. All of our built-in templates have unit integration and end-to-end tests. + +#. **Easy to maintain**: + + #. **Easy Debugging**: Lightning apps can be debugged locally and in the cloud with **breakpoints** in any components. + #. **Non-Invasive**: Lightning is the glue that connects all parts of your workflow, but this is done in a non-invasive way by formalizing API contracts between components. In other words, your application can run someone else's code with little assumption. diff --git a/docs/source-app/landing_app.py b/docs/source-app/landing_app.py new file mode 100644 index 0000000..d4f4ed8 --- /dev/null +++ b/docs/source-app/landing_app.py @@ -0,0 +1,12 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + + +# run on a cloud machine ("cpu", "gpu", ...) +component = YourComponent(cloud_compute=L.CloudCompute("cpu")) +app = L.LightningApp(component) diff --git a/docs/source-app/landing_app_run.bash b/docs/source-app/landing_app_run.bash new file mode 100644 index 0000000..fd30621 --- /dev/null +++ b/docs/source-app/landing_app_run.bash @@ -0,0 +1,5 @@ +# install lightning +pip install lightning + +# run the app on the --cloud (--setup installs deps automatically) +lightning run app app.py --setup --cloud diff --git a/docs/source-app/levels/advanced/index.rst b/docs/source-app/levels/advanced/index.rst new file mode 100644 index 0000000..4ba7d09 --- /dev/null +++ b/docs/source-app/levels/advanced/index.rst @@ -0,0 +1,94 @@ +.. _advanced_level: + +.. toctree:: + :maxdepth: 1 + :hidden: + + start_dynamic_components + level_16 + level_17 + level_18 + level_19 + level_20 + +############### +Advanced skills +############### +Learn to build nested components with advanced functionality. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 9: Start dynamic components + :description: Learn to start works dynamically + :button_link: start_dynamic_components.html + :col_css: col-md-6 + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level 10: Check component status + :description: Learn to use work status to coordinate complex apps. + :button_link: level_16.html + :col_css: col-md-6 + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level: Nest flows + :description: Learn to nest flows into other flows. + :button_link: level_14.html + :col_css: col-md-6 + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level: Develop reactive apps. + :description: Learn to develop reactive Lightning Apps. Lightning shines with reactive workflows. + :button_link: level_14.html + :col_css: col-md-6 + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level: Enable CLI commands for your app + :description: Speak to your app from a CLI over the network + :button_link: level_17.html + :col_css: col-md-6 + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 11: Connect two components over the network + :description: Connect two LightningWorks over the network. + :button_link: level_14.html + :col_css: col-md-6 + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level 13: Rerun components + :description: Learn to reuse components by passing different variables. + :button_link: level_17.html + :col_css: col-md-6 + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 14: Handle Lightning App exceptions + :description: Learn to handle Lightning App exceptions. + :button_link: level_19.html + :col_css: col-md-6 + :height: 150 + :tag: advanced + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/advanced/level_16.rst b/docs/source-app/levels/advanced/level_16.rst new file mode 100644 index 0000000..58c1e1f --- /dev/null +++ b/docs/source-app/levels/advanced/level_16.rst @@ -0,0 +1,10 @@ +########################### +Level 16: Check Work status +########################### +**Audience:** Users who want to stop/start Lightning Work based on a status. + +**Prereqs:** Level 16+ + +---- + +.. include:: ../../core_api/lightning_work/status_content.rst diff --git a/docs/source-app/levels/advanced/level_17.rst b/docs/source-app/levels/advanced/level_17.rst new file mode 100644 index 0000000..7f0f505 --- /dev/null +++ b/docs/source-app/levels/advanced/level_17.rst @@ -0,0 +1,10 @@ +########################## +Level 17: Rerun components +########################## +**Audience:** Users who want Work.run() to activate multiple times in an app. + +**Prereqs:** Level 16+ and read the `Event Loop guide <../glossary/event_loop.html>`_. + +---- + +.. include:: ../../workflows/run_work_once_content.rst diff --git a/docs/source-app/levels/advanced/level_18.rst b/docs/source-app/levels/advanced/level_18.rst new file mode 100644 index 0000000..c934d2f --- /dev/null +++ b/docs/source-app/levels/advanced/level_18.rst @@ -0,0 +1,10 @@ +############################################## +Level 18: Share objects between LightningWorks +############################################## +**Audience:** Users moving DataFrames or outputs, between Lightning Works (usually data engineers). + +**Prereqs:** Level 16+ and know about the Pandas library and read the `Access app state guide <../../access_app_state.html>`_. + +---- + +.. include:: ../../core_api/lightning_work/payload_content.rst diff --git a/docs/source-app/levels/advanced/level_19.rst b/docs/source-app/levels/advanced/level_19.rst new file mode 100644 index 0000000..99a859e --- /dev/null +++ b/docs/source-app/levels/advanced/level_19.rst @@ -0,0 +1,11 @@ +######################################### +Level 19: Handle Lightning App exceptions +######################################### + +**Audience:** Users who want to make Lightning Apps more robust to potential issues. + +**Prereqs:** Level 16+ + +---- + +.. include:: ../../core_api/lightning_work/handling_app_exception_content.rst diff --git a/docs/source-app/levels/advanced/level_20.rst b/docs/source-app/levels/advanced/level_20.rst new file mode 100644 index 0000000..1d045e8 --- /dev/null +++ b/docs/source-app/levels/advanced/level_20.rst @@ -0,0 +1,11 @@ +####################################### +Level 20: Enable dynamic LightningWorks +####################################### + +**Audience:** Users who want to create/run/stop multiple LightningWorks not defined at app instantiation. + +**Prereqs:** Level 16+ + +---- + +.. include:: ../../core_api/lightning_app/dynamic_work_content.rst diff --git a/docs/source-app/levels/advanced/start_dynamic_components.rst b/docs/source-app/levels/advanced/start_dynamic_components.rst new file mode 100644 index 0000000..39bc153 --- /dev/null +++ b/docs/source-app/levels/advanced/start_dynamic_components.rst @@ -0,0 +1,38 @@ +############################### +Level: Start dynamic components +############################### +**Audience:** Users who want to run a Lightning Component in parallel (asynchroneously). + +**Prereqs:** You must have finished the `Basic levels <../basic/>`_. + +---- + +.. include:: ../../workflows/run_work_in_parallel_content.rst + +---- + +********************************************** +Next steps: Share variables between components +********************************************** +Now that you know how to run components in parallel, we'll learn to share variables +across components to simplify complex workflows. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 6: Share variables between components + :description: Learn to connect components + :col_css: col-md-12 + :button_link: share_variables_between_lightning_components.html + :height: 150 + :tag: 10 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/basic/build_a_dag.rst b/docs/source-app/levels/basic/build_a_dag.rst new file mode 100644 index 0000000..dec9d40 --- /dev/null +++ b/docs/source-app/levels/basic/build_a_dag.rst @@ -0,0 +1,21 @@ +:orphan: + +########################### +Example: Deploy a model API +########################### +**Audience:** TODO: + +**Prereqs:** You have an app already running locally. + +---- + +**************************** +What is the Lightning Cloud? +**************************** +The Lightning Cloud is the platform that we've created to interface with the cloud providers. Today +the Lightning Cloud supports AWS. + +.. note:: Support for GCP and Azure is coming soon! + +To use the Lightning Cloud, you buy credits that are used to pay the cloud providers. If you want to run +on your own AWS credentials, please contact us (support@lightning.ai) so we can get your clusters set up for you. diff --git a/docs/source-app/levels/basic/build_a_lightning_component.rst b/docs/source-app/levels/basic/build_a_lightning_component.rst new file mode 100644 index 0000000..5ff60ed --- /dev/null +++ b/docs/source-app/levels/basic/build_a_lightning_component.rst @@ -0,0 +1,161 @@ +############################################## +Level 1: Package code in a lightning component +############################################## + +**Prereqs:** You know *basic* Python. + +**Goal:** In this guide you'll learn to develop `a Lightning component `_. + + +********************************* +Why you need Lightning components +********************************* +A Lightning component is a self-contained, modular machine-learning component +that you can plug into your existing ML workflows. A Lightning component organizes arbitrary code so it can run on the cloud, manages +its own infrastructure, cloud costs, networking and more. Connect components using your current workflow management tools or +our `next-generation reactive orchestrator <../intermediate/index.html>`_. + +Components run on the cloud or your laptop without code changes 🤯🤯. + +.. raw:: html + +
+ +
+ +| + +Organizing your code into Lightning components offers these benefits: + +.. collapse:: Build systems not scripts + + | + + The Lightning structure forces best practices so you don't have to be an expert production engineer. + Although it feels like you're writing a script, you are actually building a production-ready system. + +.. collapse:: Cost control + + | + + The component run-time has been optimized for cost management to support the largest machine-learning workloads. + Lower your cloud bill with machines that shut down or spin up faster. + +.. collapse:: For beginners: Code like an expert + + | + + Lightning embeds the best practices of building production-ready full stack AI apps into your + coding experience. You can write code like you normally do, and the Lightning structure + ensures your code is implicitly production ready... even if you're just doing research. + +.. collapse:: For experts: Scale with full control + + | + + if you know what you are doing, Lightning gives you full control to manage your own + scaling logic, fault-tolerance and even pre-provisioning, all from Python. We even give you + full flexibility to use tools like `terraform <../../cloud/customize_a_lightning_cluster.html>`_ to optimize cloud clusters for your Lightning apps. + +.. collapse:: Integrate into your current workflow tools + + | + + Lightning components are self-contained pieces of functionality. Add them to your current workflow + tools to quickly fill in gaps in your ML workflow such as monitoring drift, training LLMs and more. + You can (optionally) use the Lightning App to integrate components into a cohesive workflow. + +.. collapse:: Packaged code + + | + + Lightning apps bundles components into an app that runs in any environment. The same code will run on your laptop, + or any cloud or private clusters. You don't have to think about the cluster or know anything about the cloud. + +.. collapse:: Rapid iteration + + | + + Iterate through ideas in hours not months because you don't have to learn a million other concepts that the components + handle for you such as kubernetes, cost management, auto-scaling and more. + +.. collapse:: Modularity + + | + + Components are modular and inter-operable by design. Leverage our vibrant community of components so you don't + have to build each piece of the system yourself. + +---- + +***************** +Install Lightning +***************** +First, install Lightning. + +.. lit_tabs:: + :descriptions: Pip; Macs, Apple Silicon (M1/M2/M3); Windows + :code_files: /install/pip.bash; /install/mac.bash; /install/windows.bash + :tab_rows: 4 + :height: 180px + +---- + +************************** +Build your first component +************************** +A Lightning component organizes arbitrary code so it can run on the cloud, manages its own infrastructure, cloud costs, networking and more + +**Run one of these components!** + +.. include:: ./hero_components.rst + +| + +Components run the same on the cloud and locally on your choice of hardware. + +.. include:: /levels/basic/hero_run_setup.rst + +---- + +************ +Key features +************ +You now know enough to build a self-contained component that runs any Python code on the cloud that can be connected to form a +powerful Lightning app. Here are a few key features available to super-charge your work: + +.. lit_tabs:: + :titles: 15+ accelerators; Auto-stop idle machines; Auto-timeout submitted work; Use spot machines (~70% discount); Work with massive datasets; Mount cloud storage; Use a custom container + :code_files: ./key_features/accelerators.py; ./key_features/idle_machine.py; ./key_features/auto_timeout.py; ./key_features/spot.py; ./key_features/massive_dataset.py; ./key_features/mount_data.py; ./key_features/custom_container.py; + :highlights: 11;11;11;11;11;2,7,10, 11; 11 + :enable_run: true + :tab_rows: 3 + :height: 430px + +---- + +******************************************** +Next: Explore real component implementations +******************************************** +In this section we introduced components. Let's explore +real component implementations in-depth. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 2: Explore real component implementations + :description: Go deep into real component implementations. + :col_css: col-md-12 + :button_link: real_lightning_component_implementations.html + :height: 150 + :tag: beginner + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/basic/create_a_model_demo.rst b/docs/source-app/levels/basic/create_a_model_demo.rst new file mode 100644 index 0000000..da6f28c --- /dev/null +++ b/docs/source-app/levels/basic/create_a_model_demo.rst @@ -0,0 +1,21 @@ +:orphan: + +############################ +Example: Create a model demo +############################ +**Audience:** TODO: + +**Prereqs:** You have an app already running locally. + +---- + +**************************** +What is the Lightning Cloud? +**************************** +The Lightning Cloud is the platform that we've created to interface with the cloud providers. Today +the Lightning Cloud supports AWS. + +.. note:: Support for GCP and Azure is coming soon! + +To use the Lightning Cloud, you buy credits that are used to pay the cloud providers. If you want to run +on your own AWS credentials, please contact us (support@lightning.ai) so we can get your clusters set up for you. diff --git a/docs/source-app/levels/basic/deploy_ai_model_api.rst b/docs/source-app/levels/basic/deploy_ai_model_api.rst new file mode 100644 index 0000000..dec9d40 --- /dev/null +++ b/docs/source-app/levels/basic/deploy_ai_model_api.rst @@ -0,0 +1,21 @@ +:orphan: + +########################### +Example: Deploy a model API +########################### +**Audience:** TODO: + +**Prereqs:** You have an app already running locally. + +---- + +**************************** +What is the Lightning Cloud? +**************************** +The Lightning Cloud is the platform that we've created to interface with the cloud providers. Today +the Lightning Cloud supports AWS. + +.. note:: Support for GCP and Azure is coming soon! + +To use the Lightning Cloud, you buy credits that are used to pay the cloud providers. If you want to run +on your own AWS credentials, please contact us (support@lightning.ai) so we can get your clusters set up for you. diff --git a/docs/source-app/levels/basic/hello_components/code_run_cloud.bash b/docs/source-app/levels/basic/hello_components/code_run_cloud.bash new file mode 100644 index 0000000..6594fe0 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/code_run_cloud.bash @@ -0,0 +1 @@ +lightning run app app.py --cloud diff --git a/docs/source-app/levels/basic/hello_components/code_run_cloud_setup.bash b/docs/source-app/levels/basic/hello_components/code_run_cloud_setup.bash new file mode 100644 index 0000000..ed69b30 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/code_run_cloud_setup.bash @@ -0,0 +1 @@ +lightning run app app.py --setup --cloud diff --git a/docs/source-app/levels/basic/hello_components/code_run_cloud_yours.bash b/docs/source-app/levels/basic/hello_components/code_run_cloud_yours.bash new file mode 100644 index 0000000..ff74f6c --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/code_run_cloud_yours.bash @@ -0,0 +1,5 @@ +# first create a cluster (creation could take ~30 minutes) +lightning create cluster pikachu --provider aws --role-arn arn:aws:iam::1234567890:role/lai-byoc + +# run on that cluster +lightning run app app.py --cloud pikachu diff --git a/docs/source-app/levels/basic/hello_components/code_run_cloud_yours_setup.bash b/docs/source-app/levels/basic/hello_components/code_run_cloud_yours_setup.bash new file mode 100644 index 0000000..f2ff22d --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/code_run_cloud_yours_setup.bash @@ -0,0 +1,5 @@ +# first create a cluster (creation could take ~30 minutes) +lightning create cluster pikachu --provider aws --role-arn arn:aws:iam::1234567890:role/lai-byoc + +# run on that cluster +lightning run app app.py --setup --cloud pikachu diff --git a/docs/source-app/levels/basic/hello_components/code_run_local.bash b/docs/source-app/levels/basic/hello_components/code_run_local.bash new file mode 100644 index 0000000..8a00b45 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/code_run_local.bash @@ -0,0 +1 @@ +lightning run app app.py diff --git a/docs/source-app/levels/basic/hello_components/code_run_local_setup.bash b/docs/source-app/levels/basic/hello_components/code_run_local_setup.bash new file mode 100644 index 0000000..11cc8a4 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/code_run_local_setup.bash @@ -0,0 +1 @@ +lightning run app app.py --setup diff --git a/docs/source-app/levels/basic/hello_components/deploy_model.py b/docs/source-app/levels/basic/hello_components/deploy_model.py new file mode 100644 index 0000000..9847db3 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/deploy_model.py @@ -0,0 +1,31 @@ +# !pip install torchvision +import lightning as L +from lightning.app.components.serve import PythonServer, Image, Number +import base64, io, torchvision, torch +from PIL import Image as PILImage + + +class PyTorchServer(PythonServer): + def setup(self): + self._model = torchvision.models.resnet18(pretrained=True) + self._device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + self._model.to(self._device) + + def predict(self, request): + image = base64.b64decode(request.image.encode("utf-8")) + image = PILImage.open(io.BytesIO(image)) + transforms = torchvision.transforms.Compose([ + torchvision.transforms.Resize(224), + torchvision.transforms.ToTensor(), + torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) + ]) + image = transforms(image) + image = image.to(self._device) + prediction = self._model(image.unsqueeze(0)) + return {"prediction": prediction.argmax().item()} + + +component = PyTorchServer( + input_type=Image, output_type=Number, cloud_compute=L.CloudCompute('gpu') +) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/hello_components/hello_world.py b/docs/source-app/levels/basic/hello_components/hello_world.py new file mode 100644 index 0000000..92b1063 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/hello_world.py @@ -0,0 +1,12 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + + + +component = YourComponent() +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/hello_components/hello_world_gpu.py b/docs/source-app/levels/basic/hello_components/hello_world_gpu.py new file mode 100644 index 0000000..aa316f9 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/hello_world_gpu.py @@ -0,0 +1,12 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + +# run on a cloud machine ("cpu", "gpu", ...) +compute = L.CloudCompute("gpu") +component = YourComponent(cloud_compute=compute) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/hello_components/multi_node.py b/docs/source-app/levels/basic/hello_components/multi_node.py new file mode 100644 index 0000000..ad1bbb4 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/multi_node.py @@ -0,0 +1,29 @@ +# !pip install torch +import lightning as L +from lightning.app.components import MultiNode + + +class MultiNodeComponent(L.LightningWork): + def run( + self, + main_address: str, + main_port: int, + node_rank: int, + world_size: int, + ): + print(f"ADD YOUR DISTRIBUTED CODE: {main_address=} {main_port=} {node_rank=} {world_size=}") + print("supports ANY ML library") + + + + + + + + + + +# gpu-multi-fast has 4 GPUs x 8 nodes = 32 GPUs +component = MultiNodeComponent(cloud_compute=L.CloudCompute("gpu-multi-fast")) +component = MultiNode(component, nodes=8) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/hello_components/pl_multinode.py b/docs/source-app/levels/basic/hello_components/pl_multinode.py new file mode 100644 index 0000000..9df12ec --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/pl_multinode.py @@ -0,0 +1,19 @@ +# app.py +import lightning as L +from lightning.app.components import LightningTrainerMultiNode +from lightning.pytorch.demos.boring_classes import BoringModel + + +class LightningTrainerDistributed(L.LightningWork): + def run(self): + model = BoringModel() + trainer = L.Trainer(max_epochs=10, strategy="ddp") + trainer.fit(model) + +# 8 GPUs: (2 nodes of 4 x v100) +component = LightningTrainerMultiNode( + LightningTrainerDistributed, + num_nodes=4, + cloud_compute=L.CloudCompute("gpu-fast-multi"), # 4 x v100 +) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/hello_components/pt_multinode.py b/docs/source-app/levels/basic/hello_components/pt_multinode.py new file mode 100644 index 0000000..8b39c74 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/pt_multinode.py @@ -0,0 +1,60 @@ +# app.py +# ! pip install torch +import lightning as L +from lightning.app.components import MultiNode +import torch +from torch.nn.parallel.distributed import DistributedDataParallel + + +def distributed_train(local_rank: int, main_address: str, main_port: int, num_nodes: int, node_rank: int, nprocs: int): + # 1. SET UP DISTRIBUTED ENVIRONMENT + global_rank = local_rank + node_rank * nprocs + world_size = num_nodes * nprocs + + if torch.distributed.is_available() and not torch.distributed.is_initialized(): + torch.distributed.init_process_group( + "nccl" if torch.cuda.is_available() else "gloo", + rank=global_rank, + world_size=world_size, + init_method=f"tcp://{main_address}:{main_port}", + ) + + # 2. PREPARE DISTRIBUTED MODEL + model = torch.nn.Linear(32, 2) + device = torch.device(f"cuda:{local_rank}") if torch.cuda.is_available() else torch.device("cpu") + model = DistributedDataParallel(model, device_ids=[local_rank] if torch.cuda.is_available() else None).to(device) + + # 3. SETUP LOSS AND OPTIMIZER + criterion = torch.nn.MSELoss() + optimizer = torch.optim.SGD(model.parameters(), lr=0.01) + + # 4.TRAIN THE MODEL FOR 50 STEPS + for step in range(50): + model.zero_grad() + x = torch.randn(64, 32).to(device) + output = model(x) + loss = criterion(output, torch.ones_like(output)) + print(f"global_rank: {global_rank} step: {step} loss: {loss}") + loss.backward() + optimizer.step() + + # 5. VERIFY ALL COPIES OF THE MODEL HAVE THE SAME WEIGTHS AT END OF TRAINING + weight = model.module.weight.clone() + torch.distributed.all_reduce(weight) + assert torch.equal(model.module.weight, weight / world_size) + + print("Multi Node Distributed Training Done!") + +class PyTorchDistributed(L.LightningWork): + def run(self, main_address: str, main_port: int, num_nodes: int, node_rank: int): + nprocs = torch.cuda.device_count() if torch.cuda.is_available() else 1 + torch.multiprocessing.spawn( + distributed_train, + args=(main_address, main_port, num_nodes, node_rank, nprocs), + nprocs=nprocs + ) + +# 32 GPUs: (8 nodes x 4 v 100) +compute = L.CloudCompute("gpu-fast-multi") # 4xV100 +component = MultiNode(PyTorchDistributed, num_nodes=8, cloud_compute=compute) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/hello_components/run_ptl_script.py b/docs/source-app/levels/basic/hello_components/run_ptl_script.py new file mode 100644 index 0000000..f1d3497 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/run_ptl_script.py @@ -0,0 +1,13 @@ +# app.py +# !curl https://raw.githubusercontent.com/Lightning-AI/lightning/master/examples/app/multi_node/pl_boring_script.py -o pl_boring_script.py +import lightning as L +from lightning.app.components.training import LightningTrainerScript + +# run script that trains PyTorch with the Lightning Trainer +model_script = 'pl_boring_script.py' +component = LightningTrainerScript( + model_script, + num_nodes=1, + cloud_compute=L.CloudCompute("gpu") +) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/hello_components/streamlit_demo.py b/docs/source-app/levels/basic/hello_components/streamlit_demo.py new file mode 100644 index 0000000..d5fb3b0 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/streamlit_demo.py @@ -0,0 +1,30 @@ +# app.py +# !pip install streamlit omegaconf scipy +# !pip install torch +import lightning as L +import torch +from io import BytesIO +from functools import partial +from scipy.io.wavfile import write +import streamlit as st + + +class StreamlitApp(L.app.components.ServeStreamlit): + def build_model(self): + sample_rate = 48000 + model, _ = torch.hub.load('snakers4/silero-models', model='silero_tts',speaker="v3_en") + return partial(model.apply_tts, sample_rate=sample_rate, speaker="en_0"), sample_rate + + def render(self): + st.title("Text To Speech") + text = st.text_input("Text:", "Lightning Apps are the best!") + + if text: + model, sample_rate = self.model + audio_numpy = model(text).numpy() + audio = BytesIO() + write(audio, sample_rate, audio_numpy) + audio.seek(0) + st.audio(audio) + +app = L.LightningApp(StreamlitApp()) diff --git a/docs/source-app/levels/basic/hello_components/terraform_example.bash b/docs/source-app/levels/basic/hello_components/terraform_example.bash new file mode 100644 index 0000000..af88ef2 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/terraform_example.bash @@ -0,0 +1,4 @@ +# TODO: show how to use terraform to create a cluster called pikachu + +# run the cluster +lightning run app app.py --cloud pickachu diff --git a/docs/source-app/levels/basic/hello_components/train_ptl.py b/docs/source-app/levels/basic/hello_components/train_ptl.py new file mode 100644 index 0000000..75c65ba --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/train_ptl.py @@ -0,0 +1,15 @@ +# A hello world component +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + + + +# run on a cloud machine +compute = L.CloudCompute("cpu") +worker = YourComponent(cloud_compute=compute) +app = L.LightningApp(worker) diff --git a/docs/source-app/levels/basic/hello_components/train_pytorch.py b/docs/source-app/levels/basic/hello_components/train_pytorch.py new file mode 100644 index 0000000..7bfc3b5 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/train_pytorch.py @@ -0,0 +1,28 @@ +# app.py +# ! pip install torch +import lightning as L +import torch + +class PyTorchComponent(L.LightningWork): + def run(self): + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + model = torch.nn.Sequential(torch.nn.Linear(1, 1), + torch.nn.ReLU(), + torch.nn.Linear(1, 1)) + model.to(device) + criterion = torch.nn.MSELoss() + optimizer = torch.optim.SGD(model.parameters(), lr=0.1) + + for step in range(10000): + model.zero_grad() + x = torch.tensor([0.8]).to(device) + target = torch.tensor([1.0]).to(device) + output = model(x) + loss = criterion(output, target) + print(f'step: {step}. loss {loss}') + loss.backward() + optimizer.step() + +compute = L.CloudCompute('gpu') +componet = PyTorchComponent(cloud_compute=compute) +app = L.LightningApp(componet) diff --git a/docs/source-app/levels/basic/hello_components/xgboost.py b/docs/source-app/levels/basic/hello_components/xgboost.py new file mode 100644 index 0000000..fae593a --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/xgboost.py @@ -0,0 +1,21 @@ +# app.py +# !pip install scikit-learn xgboost +import lightning as L +from sklearn import datasets +from sklearn.model_selection import train_test_split +from xgboost import XGBClassifier + +class XGBoostComponent(L.LightningWork): + def run(self): + iris = datasets.load_iris() + X, y = iris.data, iris.target + + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) + + bst = XGBClassifier(verbosity=3) + bst.fit(X_train, y_train) + preds = bst.predict(X_test) + print(f'preds: {preds}') + + +app = L.LightningApp(XGBoostComponent()) diff --git a/docs/source-app/levels/basic/hello_components/xgboost_gpu.py b/docs/source-app/levels/basic/hello_components/xgboost_gpu.py new file mode 100644 index 0000000..2b52716 --- /dev/null +++ b/docs/source-app/levels/basic/hello_components/xgboost_gpu.py @@ -0,0 +1,22 @@ +# app.py +# !pip install sklearn xgboost +# !conda install py-xgboost-gpu +import lightning as L +from sklearn import datasets +from sklearn.model_selection import train_test_split +from xgboost import XGBClassifier + +class XGBoostComponent(L.LightningWork): + def run(self): + iris = datasets.load_iris() + X, y = iris.data, iris.target + + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) + + bst = XGBClassifier(tree_method='gpu_hist', gpu_id=0, verbosity=3) + bst.fit(X_train, y_train) + preds = bst.predict(X_test) + print(f'preds: {preds}') + +compute = L.CloudCompute('gpu') +app = L.LightningApp(XGBoostComponent(cloud_compute=compute)) diff --git a/docs/source-app/levels/basic/hero_components.rst b/docs/source-app/levels/basic/hero_components.rst new file mode 100644 index 0000000..da1d9d0 --- /dev/null +++ b/docs/source-app/levels/basic/hero_components.rst @@ -0,0 +1,8 @@ +.. lit_tabs:: + :titles: Hello world; Hello GPU world; PyTorch & ⚡⚡⚡ Trainer (1+ cloud GPUs); Train PyTorch (cloud GPU); Train PyTorch (32 cloud GPUs); Deploy a model on cloud GPUs; Run a model script; XGBoost; Streamlit demo + :code_files: /levels/basic/hello_components/hello_world.py; /levels/basic/hello_components/hello_world_gpu.py; /levels/basic/hello_components/pl_multinode.py; /levels/basic/hello_components/train_pytorch.py; /levels/basic/hello_components/pt_multinode.py; /levels/basic/hello_components/deploy_model.py; /levels/basic/hello_components/run_ptl_script.py; /levels/basic/hello_components/xgboost.py; /levels/basic/hello_components/streamlit_demo.py + :highlights: 7; 10, 11; 9-11, 16, 17; 4, 8, 12, 18-19, 26; 5, 10, 22, 27, 31, 41, 57-59; 3, 11-12, 25, 29; 7, 10; 15, 21; 9, 15, 24 + :works: [{"name":"root.work","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"default","preemptible":false,"shmSize":0},"networkConfig":[{"name":"dzodf","port":61304}]}}];[{"name":"root.work","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"qnlgd","port":61516}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu","preemptible":false,"shmSize":0}}}];[{"name":"root.ws.0","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"ajfrc","port":61553}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.1","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"ttyqc","port":61554}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.2","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"svyej","port":61555}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.3","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"parme","port":61556}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}}];[{"name":"root.work","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"cutdu","port":61584}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu","preemptible":false,"shmSize":0}}}];[{"name":"root.ws.0","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"whhby","port":61613}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.1","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"yhjtf","port":61614}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.2","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"rqwkt","port":61615}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.3","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"pjdsj","port":61616}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.4","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"efdor","port":61617}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.5","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"pxmso","port":61618}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.6","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"feevy","port":61619}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}},{"name":"root.ws.7","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"tbmse","port":61620}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu-fast-multi","preemptible":false,"shmSize":0}}}];[{"name":"root.work","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"umqqg","port":7777}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"gpu","preemptible":false,"shmSize":0}}}];[];[{"name":"root.work","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"tggba","port":61729}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"default","preemptible":false,"shmSize":0}}}];[{"name":"root.work","spec":{"buildSpec":{"commands":[],"pythonDependencies":{"packageManager":"PACKAGE_MANAGER_PIP","packages":""}},"drives":[],"networkConfig":[{"name":"hpyaz","port":61763}],"userRequestedComputeConfig":{"count":1,"diskSize":0,"name":"default","preemptible":false,"shmSize":0}}}] + :enable_run: true + :tab_rows: 3 + :height: 620px diff --git a/docs/source-app/levels/basic/hero_run.rst b/docs/source-app/levels/basic/hero_run.rst new file mode 100644 index 0000000..4fdabf1 --- /dev/null +++ b/docs/source-app/levels/basic/hero_run.rst @@ -0,0 +1,6 @@ +.. lit_tabs:: + :titles: Lightning Cloud (fully-managed); Your AWS account; Your own hardware + :code_files: /levels/basic/hello_components/code_run_cloud.bash; /levels/basic/hello_components/code_run_cloud_yours.bash; /levels/basic/hello_components/code_run_local.bash + :tab_rows: 4 + :highlights: ; 5; 0 + :height: 195px diff --git a/docs/source-app/levels/basic/hero_run_setup.rst b/docs/source-app/levels/basic/hero_run_setup.rst new file mode 100644 index 0000000..2200f17 --- /dev/null +++ b/docs/source-app/levels/basic/hero_run_setup.rst @@ -0,0 +1,6 @@ +.. lit_tabs:: + :titles: Lightning Cloud (fully-managed); Your AWS account; Your own hardware + :code_files: /levels/basic/hello_components/code_run_cloud_setup.bash; /levels/basic/hello_components/code_run_cloud_yours_setup.bash; /levels/basic/hello_components/code_run_local_setup.bash + :tab_rows: 4 + :highlights: ; 5; 0 + :height: 195px diff --git a/docs/source-app/levels/basic/index.rst b/docs/source-app/levels/basic/index.rst new file mode 100644 index 0000000..2912b69 --- /dev/null +++ b/docs/source-app/levels/basic/index.rst @@ -0,0 +1,54 @@ +.. _level_basic: + +.. toctree:: + :maxdepth: 1 + :hidden: + + build_a_lightning_component + real_lightning_component_implementations + save_money_on_cloud_costs + +############ +Basic skills +############ +Learn to package your code into Lightning components which can plug into your existing ML workflows. + +A Lightning component organizes arbitrary code so it can run on the cloud, manages +its own infrastructure, cloud costs, networking and more. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 1: Package code in a Lightning component + :description: Learn to package your code into Lightning components which can plug into your existing ML workflows. + :button_link: build_a_lightning_component.html + :col_css: col-md-6 + :height: 170 + :tag: 10 minutes + +.. displayitem:: + :header: Level 2: Explore real component implementations + :description: Go deep into real component implementations. + :button_link: real_lightning_component_implementations.html + :col_css: col-md-6 + :height: 170 + :tag: 10 minutes + +.. displayitem:: + :header: Level 3: Save money on cloud costs + :description: Explore key Lightning features that save you cloud costs and improve performance. + :button_link: save_money_on_cloud_costs.html + :col_css: col-md-6 + :height: 150 + :tag: 10 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/basic/key_features/accelerators.py b/docs/source-app/levels/basic/key_features/accelerators.py new file mode 100644 index 0000000..abfb4be --- /dev/null +++ b/docs/source-app/levels/basic/key_features/accelerators.py @@ -0,0 +1,21 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + + +# custom accelerators +compute = L.CloudCompute('gpu') +component = YourComponent(cloud_compute=compute) +app = L.LightningApp(component) + +# OTHER ACCELERATORS: +# compute = L.CloudCompute('default') # 1 CPU +# compute = L.CloudCompute('cpu-medium') # 8 CPUs +# compute = L.CloudCompute('gpu') # 1 T4 GPU +# compute = L.CloudCompute('gpu-fast-multi') # 4 V100 GPU +# compute = L.CloudCompute('p4d.24xlarge') # AWS instance name (8 A100 GPU) +# compute = ... diff --git a/docs/source-app/levels/basic/key_features/auto_timeout.py b/docs/source-app/levels/basic/key_features/auto_timeout.py new file mode 100644 index 0000000..8d5e927 --- /dev/null +++ b/docs/source-app/levels/basic/key_features/auto_timeout.py @@ -0,0 +1,13 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + + +# if the machine hasn't started after 60 seconds, cancel the work +compute = L.CloudCompute('gpu', wait_timeout=60) +component = YourComponent(cloud_compute=compute) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/key_features/custom_container.py b/docs/source-app/levels/basic/key_features/custom_container.py new file mode 100644 index 0000000..c1a5e61 --- /dev/null +++ b/docs/source-app/levels/basic/key_features/custom_container.py @@ -0,0 +1,13 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + + +# custom image (from any provider) +config= L.BuildConfig(image="gcr.io/google-samples/hello-app:1.0") +component = YourComponent(cloud_build_config=config) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/key_features/idle_machine.py b/docs/source-app/levels/basic/key_features/idle_machine.py new file mode 100644 index 0000000..dc01572 --- /dev/null +++ b/docs/source-app/levels/basic/key_features/idle_machine.py @@ -0,0 +1,13 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + + +# stop the machine when idle for 10 seconds +compute = L.CloudCompute('gpu', idle_timeout=10) +component = YourComponent(cloud_compute=compute) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/key_features/massive_dataset.py b/docs/source-app/levels/basic/key_features/massive_dataset.py new file mode 100644 index 0000000..e8673e3 --- /dev/null +++ b/docs/source-app/levels/basic/key_features/massive_dataset.py @@ -0,0 +1,13 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + + +# use 100 GB of space on that machine (max size: 64 TB) +compute = L.CloudCompute('gpu', disk_size=100) +component = YourComponent(cloud_compute=compute) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/key_features/mount_data.py b/docs/source-app/levels/basic/key_features/mount_data.py new file mode 100644 index 0000000..1141983 --- /dev/null +++ b/docs/source-app/levels/basic/key_features/mount_data.py @@ -0,0 +1,13 @@ +import lightning as L +import os + + +class YourComponent(L.LightningWork): + def run(self): + os.listdir('/foo') + +# mount the files on the s3 bucket under this path +mount = L.Mount(source="s3://lightning-example-public/", mount_path="/foo") +compute = L.CloudCompute(mounts=mount) +component = YourComponent(cloud_compute=compute) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/key_features/spot.py b/docs/source-app/levels/basic/key_features/spot.py new file mode 100644 index 0000000..d3b0ac0 --- /dev/null +++ b/docs/source-app/levels/basic/key_features/spot.py @@ -0,0 +1,13 @@ +# app.py +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print('RUN ANY PYTHON CODE HERE') + +# spot machines can be turned off without notice, use for non-critical, resumable work +# request a spot machine, after 60 seconds of waiting switch to full-price +compute = L.CloudCompute('gpu', wait_timeout=60, spot=True) +component = YourComponent(cloud_compute=compute) +app = L.LightningApp(component) diff --git a/docs/source-app/levels/basic/real_lightning_component_implementations.rst b/docs/source-app/levels/basic/real_lightning_component_implementations.rst new file mode 100644 index 0000000..5e2fbc3 --- /dev/null +++ b/docs/source-app/levels/basic/real_lightning_component_implementations.rst @@ -0,0 +1,87 @@ +############################################### +Level 2: Explore real component implementations +############################################### +**Audience:** Users who want to deeply understand what is possible with Lightning components. + +**Prereqs:** You must have finished `level 1 <../basic/build_a_lightning_component.html>`_. + +---- + +*********************** +Real component examples +*********************** +Use this guide to understand what is happening in each type of component. +These are a few prototypical components. Since each component organizes +Python, you can build virtually infinite components for any use-case +you can think of. + +---- + +******************************* +Ex: PyTorch + Lightning Trainer +******************************* +This example shows how to train PyTorch with the Lightning trainer on your machine +or cloud GPUs without code changes. + +.. lit_tabs:: + :descriptions: import Lightning; We're using a demo LightningModule; Move your training code here (usually your main.py); Pass your component to the multi-node executor (it works on CPU or single GPUs also); Select the number of machines (nodes). Here we choose 4.; Choose from over 15+ machine types. This one has 4 v100 GPUs.; Initialize the App object that executes the component logic. + :code_files: /levels/basic/hello_components/pl_multinode.py; /levels/basic/hello_components/pl_multinode.py; /levels/basic/hello_components/pl_multinode.py; /levels/basic/hello_components/pl_multinode.py; /levels/basic/hello_components/pl_multinode.py; /levels/basic/hello_components/pl_multinode.py; /levels/basic/hello_components/pl_multinode.py; + :highlights: 2; 4; 9-11; 14-17; 16; 17; 19 + :enable_run: true + :tab_rows: 5 + :height: 420px + +| + +Run the component on the cloud: + +.. include:: /levels/basic/hero_run.rst + +---- + +********************************* +Ex: Deploy a PyTorch API endpoint +********************************* +This example shows how to deploy PyTorch and create an API + +.. lit_tabs:: + :descriptions: Shortcut to list dependencies without a requirements.txt file.; Import one of our serving components (high-performance ones are available on the enterprise tiers); Define the setup function to load your favorite pretrained models and do any kind of pre-processing.; Define the predict function which is called when the endpoint is hit.; Initialize the server and define the type of cloud machine to use. + :code_files: /levels/basic/hello_components/deploy_model.py; /levels/basic/hello_components/deploy_model.py; /levels/basic/hello_components/deploy_model.py; /levels/basic/hello_components/deploy_model.py; /levels/basic/hello_components/deploy_model.py; + :highlights: 1; 3; 10-12; 15-25; 28-30 + :enable_run: true + :tab_rows: 4 + :height: 620px + +| + +Run the component on the cloud: + +.. include:: /levels/basic/hero_run.rst + +---- + +************************* +Next: Save on cloud costs +************************* +Let's review key lightning features to help you run components more efficiently on the cloud +so you can save on cloud costs. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 3: Save money on cloud costs + :description: Explore key Lightning features that save you cloud costs and improve performance. + :button_link: save_money_on_cloud_costs.html + :col_css: col-md-12 + :height: 150 + :tag: 10 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/basic/run_jupyter_notebook_on_the_cloud.rst b/docs/source-app/levels/basic/run_jupyter_notebook_on_the_cloud.rst new file mode 100644 index 0000000..b39dfa1 --- /dev/null +++ b/docs/source-app/levels/basic/run_jupyter_notebook_on_the_cloud.rst @@ -0,0 +1,21 @@ +:orphan: + +############################################# +Example: Develop a Jupyter Notebook component +############################################# +**Audience:** TODO: + +**Prereqs:** You have an app already running locally. + +---- + +**************************** +What is the Lightning Cloud? +**************************** +The Lightning Cloud is the platform that we've created to interface with the cloud providers. Today +the Lightning Cloud supports AWS. + +.. note:: Support for GCP and Azure is coming soon! + +To use the Lightning Cloud, you buy credits that are used to pay the cloud providers. If you want to run +on your own AWS credentials, please contact us (support@lightning.ai) so we can get your clusters set up for you. diff --git a/docs/source-app/levels/basic/run_on_aws_account.rst b/docs/source-app/levels/basic/run_on_aws_account.rst new file mode 100644 index 0000000..ecf1542 --- /dev/null +++ b/docs/source-app/levels/basic/run_on_aws_account.rst @@ -0,0 +1,21 @@ +:orphan: + +To run on your own AWS account, first `create an AWS ARN <../../glossary/aws_arn.rst>`_. + +Next, set up a Lightning cluster (here we name it pikachu): + +.. code:: bash + + # TODO: need to remove --external-id dummy --region us-west-2 + lightning create cluster pikachu --provider aws --role-arn arn:aws:iam::1234567890:role/lai-byoc + +Run your code on the pikachu cluster by passing it into CloudCompute: + +.. code:: python + + compute = L.CloudCompute('gpu', clusters=['pikachu']) + app = L.LightningApp(LitWorker(cloud_compute=compute)) + +.. warning:: + + This feature is available only under early-access. Request access by emailing support@lightning.ai. diff --git a/docs/source-app/levels/basic/save_money_on_cloud_costs.rst b/docs/source-app/levels/basic/save_money_on_cloud_costs.rst new file mode 100644 index 0000000..5e75295 --- /dev/null +++ b/docs/source-app/levels/basic/save_money_on_cloud_costs.rst @@ -0,0 +1,56 @@ +################################## +Level 3: Save money on cloud costs +################################## +**Audience:** Users who want to use the AWS cloud efficiently. + +**Prereqs:** You must have finished `level 1 <../basic/build_a_lightning_component.html>`_. + +---- + +*********************************** +Save money with these optimizations +*********************************** +A Lightning component gives you fine-grain control over the cloud lifecycle of that component. + +Here are a few features that will enable you save a lot on your cloud costs: + +.. lit_tabs:: + :titles: 15+ accelerators; Auto-stop idle machines; Auto-timeout submitted work; Use spot machines (~70% discount); Work with massive datasets; Mount cloud storage; Use a custom container + :code_files: ./key_features/accelerators.py; ./key_features/idle_machine.py; ./key_features/auto_timeout.py; ./key_features/spot.py; ./key_features/massive_dataset.py; ./key_features/mount_data.py; ./key_features/custom_container.py; + :highlights: 11;11;11;11;11;1,7, 10, 11; 11 + :enable_run: true + :tab_rows: 3 + :height: 430px + +---- + +.. include:: ../../cloud/customize_a_lightning_cluster.rst + + +---- + +****************************** +Next: Coordinate 2+ components +****************************** +Now that you know how to organize arbitrary code inside a Lightning component, +learn to coordinate 2 or more components into workflows which we call Lightning apps. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Intermediate skills + :description: Learn to coordinate 2+ components into workflows which we call Lightning apps. + :button_link: ../intermediate/index.html + :col_css: col-md-12 + :height: 170 + :tag: 15 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/basic/scripts/toy_app_1_component.py b/docs/source-app/levels/basic/scripts/toy_app_1_component.py new file mode 100644 index 0000000..122f38c --- /dev/null +++ b/docs/source-app/levels/basic/scripts/toy_app_1_component.py @@ -0,0 +1,17 @@ +# app.py +import lightning as L + +class Component(L.LightningWork): + def run(self, x): + print(x) + + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.component = Component() + + def run(self): + self.component.run('i love Lightning') + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/basic/scripts/toy_app_1_component_pdb.py b/docs/source-app/levels/basic/scripts/toy_app_1_component_pdb.py new file mode 100644 index 0000000..93fef4f --- /dev/null +++ b/docs/source-app/levels/basic/scripts/toy_app_1_component_pdb.py @@ -0,0 +1,17 @@ +# app.py +import lightning as L + +class Component(L.LightningWork): + def run(self, x): + print(x) + L.pdb.set_trace() + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.component = Component() + + def run(self): + self.component.run('i love Lightning') + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/basic/train_pytorch_on_the_cloud.rst b/docs/source-app/levels/basic/train_pytorch_on_the_cloud.rst new file mode 100644 index 0000000..c3ef841 --- /dev/null +++ b/docs/source-app/levels/basic/train_pytorch_on_the_cloud.rst @@ -0,0 +1,21 @@ +:orphan: + +################################### +Example: Train PyTorch on the cloud +################################### +**Audience:** TODO: + +**Prereqs:** You have an app already running locally. + +---- + +**************************** +What is the Lightning Cloud? +**************************** +The Lightning Cloud is the platform that we've created to interface with the cloud providers. Today +the Lightning Cloud supports AWS. + +.. note:: Support for GCP and Azure is coming soon! + +To use the Lightning Cloud, you buy credits that are used to pay the cloud providers. If you want to run +on your own AWS credentials, please contact us (support@lightning.ai) so we can get your clusters set up for you. diff --git a/docs/source-app/levels/expert/index.rst b/docs/source-app/levels/expert/index.rst new file mode 100644 index 0000000..28cd56d --- /dev/null +++ b/docs/source-app/levels/expert/index.rst @@ -0,0 +1,90 @@ +:orphan: + +.. _expert_level: + +.. toctree:: + :maxdepth: 1 + :hidden: + +############# +Expert skills +############# + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Level : Use custom containers + :description: Learn to use a custom cloud container. + :button_link: build_a_machine_learning_workflow.html + :col_css: col-md-6 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Level : Customize your cluster creation + :description: Learn to use a custom cloud container. + :button_link: build_a_machine_learning_workflow.html + :col_css: col-md-6 + :height: 150 + :tag: basic + + +.. raw:: html + +
+
+ +---- + +********************* +Intermediate Examples +********************* +As you work through the intermediate levels, try these examples: + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example: Develop a Github Repo Script Runner + :description: Develop a workflow to execute Github Repos + :button_link: ../../examples/github_repo_runner/github_repo_runner.html + :col_css: col-md-6 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Example: Develop a file server + :description: Create a simple Lightning App (App) that allows users to upload files and list the uploaded files. + :button_link: ../../examples/file_server/file_server.html + :col_css: col-md-6 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Example: Develop a Jupyter Notebook component + :description: Develop a LightningWork that runs a notebook on the cloud. + :button_link: run_jupyter_notebook_on_the_cloud.html + :col_css: col-md-6 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Example: Create a model demo + :description: Demo POCs and MVPs which can be shared with a public web user interface. + :button_link: create_a_model_demo.html + :col_css: col-md-6 + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/intermediate/connect_lightning_components.rst b/docs/source-app/levels/intermediate/connect_lightning_components.rst new file mode 100644 index 0000000..14c2e9d --- /dev/null +++ b/docs/source-app/levels/intermediate/connect_lightning_components.rst @@ -0,0 +1,116 @@ +#################################################### +Level 4: Connect components into a full stack AI app +#################################################### + +**Audience:** Users who want to build apps with multiple components. + +**Prereqs:** You know how to `build a component <../basic/build_a_lightning_component.html>`_. + +---- + +**************************** +What is a full stack AI app? +**************************** +In the ML world, workflows coordinate multiple pieces of code working together. In Lightning, +when we coordinate 2 or more `Lightning components <../basic/build_a_lightning_component.html>`_ working together, +we instead call it a Lightning App. The difference will become more obvious when we introduce reactive +workflows in the advanced section. + +For the time being, we'll go over how to coordinate 2 components together in a traditional workflow setting +and explain how it works. + +.. note:: If you've used workflow tools for Python, this page describes conventional DAGs. + In `level 6 <./run_lightning_work_in_parallel.html>`_, we introduce reactive workflows that generalize beyond DAGs + so you can build complex systems without much effort. + +---- + +*********** +The toy app +*********** + +In this app, we define two components that run across 2 separate machines. One to train a model on a GPU machine and one to analyze the model +on a separate CPU machine. We save money by stopping the GPU machine when the work is done. + +.. lit_tabs:: + :titles: Import Lightning; Define Component 1; Define Component 2; Orchestrator; Connect component 1; Connect component 2; Implement run; Train; Analyze; Define app placeholder + :descriptions: First, import Lightning; This component trains a model on a GPU machine; This component analyzes a model on a CPU machine; Define the LightningFlow that orchestrates components; Component 1 will run on a CPU machine; Component 2 will run on an accelerated GPU machine; Describe the workflow in the run method; Training runs first and completes; Analyze runs after training completes; This allows the app to be runnable + :code_files: ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py + :highlights: 2; 5-7; 9-11; 13; 16; 17; 19; 20; 21; 23 + :enable_run: true + :tab_rows: 4 + :height: 460px + +| + +Now run the app: + +.. lit_tabs:: + :titles: Run on Lightning cloud; Your own hardware + :descriptions: Run to see these 2 components execute on separate machines 🤯; Run it locally without code changes 🤯🤯; + :code_files: ./level_2_scripts/code_run_cloud.bash; ./level_2_scripts/code_run_local.bash + :tab_rows: 7 + :height: 195px + +| + +Now you can develop distributed cloud apps on your laptop 🤯🤯🤯🤯! + +---- + +************* +Now you know: +************* + +Without going out of your way, you're now doing the following: (Hint: Click **visualize** to see an animation describing the code). + +.. lit_tabs:: + :titles: Orchestration; Distributed cloud computing; Multi-machine communication; Multi-machine communication; Multi-cloud; + :descriptions: Define orchestration in Python with full control-flow; The two pieces of independent Python code ran on separate machines 🤯🤯; The text "CPU machine 1" was sent from the flow machine to the machine running the TrainComponent; The text "GPU machine 2" was sent from the flow machine to the machine running the AnalyzeComponent; The full Lightning app can move across clusters and clouds + :code_files: ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/hello_app.py; ./level_2_scripts/multi_cloud.bash + :tab_rows: 4 + :highlights: 19-21; 16-17; 20; 21; 2, 6, 10 + :images: | | | | + :height: 470px + +---- + +********************* +Maintain full control +********************* +Although we've abstracted the infrastructure, you still have full control when you need it: + +.. lit_tabs:: + :titles: Scheduler; Crontab syntax; Auto-scaling; Organized Python; Full terraform control; + :descriptions: Although you can use Python timers, we have a scheduler short-hand; You can also use full cron syntax; Code your own auto-scaling syntax (Lightning plays well with Kubernetes); *Remember* components organize ANY Python code which can even call external non-python scripts such as optimized C++ model servers ;Experts have the option to use terraform to configure Lightning clusters + :code_files: ./level_2_scripts/hello_app_scheduler.py; ./level_2_scripts/hello_app_cron.py; ./level_2_scripts/hello_app_auto_scale.py; ./level_2_scripts/organized_app_python.py; ./level_2_scripts/tr.bash + :tab_rows: 4 + :highlights: 24; 24; 21, 24, 27, 28; 9, 16, 17; 5 + :height: 700px + +---- + +************************* +Next: Review how to debug +************************* +The next levels does a 2 minute review to make sure you know how to debug a Lightning app. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 5: Debug a Lightning App + :description: Learn to debug a lightning app. + :button_link: debug_a_lightning_app.html + :col_css: col-md-12 + :height: 170 + :tag: 10 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/intermediate/debug_a_lightning_app.rst b/docs/source-app/levels/intermediate/debug_a_lightning_app.rst new file mode 100644 index 0000000..856be5a --- /dev/null +++ b/docs/source-app/levels/intermediate/debug_a_lightning_app.rst @@ -0,0 +1,48 @@ +############################## +Level 5: Debug A Lightning app +############################## +**Audience:** Users who want to debug a distributed app locally. + +**Prereqs:** You must have finished the `Basic levels <../basic/>`_. + +---- + +****************** +Enable breakpoints +****************** +To enable a breakpoint, use `L.pdb.set_trace()` (note direct python pdb support is work in progress and open to contributions). + +.. lit_tabs:: + :descriptions: Toy app; Add a breakpoint. When the program runs, it will stop at this line. + :code_files: ./debug_app_scripts/toy_app_1_component.py; ./debug_app_scripts/toy_app_1_component_pdb.py + :highlights: ; 7 + :enable_run: true + :tab_rows: 3 + :height: 350px + +---- + +********************************* +Next: Run a component in parallel +********************************* +Learn to run components in parallel to enable more powerful workflows. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 6: Run a Lightning component in parallel + :description: Learn when and how to run Components in parallel (asynchronous). + :button_link: run_lightning_work_in_parallel.html + :col_css: col-md-12 + :height: 150 + :tag: 15 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/intermediate/debug_app_scripts/debug_app.py b/docs/source-app/levels/intermediate/debug_app_scripts/debug_app.py new file mode 100644 index 0000000..9e3d9c9 --- /dev/null +++ b/docs/source-app/levels/intermediate/debug_app_scripts/debug_app.py @@ -0,0 +1,25 @@ +# app.py +import lightning as L +from lightning.app.runners import MultiProcessRuntime + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f'train a model on {x}') + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f'analyze model on {x}') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent() + self.analyze = AnalyzeComponent() + + def run(self): + self.train.run("GPU machine 1") + self.analyze.run("CPU machine 2") + +app = L.LightningApp(WorkflowOrchestrator()) +MultiProcessRuntime(app).dispatch() diff --git a/docs/source-app/levels/intermediate/debug_app_scripts/toy_app.py b/docs/source-app/levels/intermediate/debug_app_scripts/toy_app.py new file mode 100644 index 0000000..44273e6 --- /dev/null +++ b/docs/source-app/levels/intermediate/debug_app_scripts/toy_app.py @@ -0,0 +1,24 @@ +# app.py +import lightning as L + + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f'train a model on {x}') + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f'analyze model on {x}') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent() + self.analyze = AnalyzeComponent() + + def run(self): + self.train.run("CPU machine 1") + self.analyze.run("CPU machine 2") + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/debug_app_scripts/toy_app_1_component.py b/docs/source-app/levels/intermediate/debug_app_scripts/toy_app_1_component.py new file mode 100644 index 0000000..122f38c --- /dev/null +++ b/docs/source-app/levels/intermediate/debug_app_scripts/toy_app_1_component.py @@ -0,0 +1,17 @@ +# app.py +import lightning as L + +class Component(L.LightningWork): + def run(self, x): + print(x) + + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.component = Component() + + def run(self): + self.component.run('i love Lightning') + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/debug_app_scripts/toy_app_1_component_pdb.py b/docs/source-app/levels/intermediate/debug_app_scripts/toy_app_1_component_pdb.py new file mode 100644 index 0000000..93fef4f --- /dev/null +++ b/docs/source-app/levels/intermediate/debug_app_scripts/toy_app_1_component_pdb.py @@ -0,0 +1,17 @@ +# app.py +import lightning as L + +class Component(L.LightningWork): + def run(self, x): + print(x) + L.pdb.set_trace() + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.component = Component() + + def run(self): + self.component.run('i love Lightning') + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/embed_web_ui_into_lightningwork.rst b/docs/source-app/levels/intermediate/embed_web_ui_into_lightningwork.rst new file mode 100644 index 0000000..3be6a9e --- /dev/null +++ b/docs/source-app/levels/intermediate/embed_web_ui_into_lightningwork.rst @@ -0,0 +1,40 @@ +###################################### +Level 9: Embed graphical UIs into work +###################################### +**Audience:** Users who need to embed a Graphical UI in their Lightning Apps. + +**Prereqs:** You have finished `Level 8 `_. + +---- + +.. include:: ../../workflows/add_web_ui/index_content.rst + +---- + +******************************************* +Next steps: Practice adapting app templates +******************************************* +One of the most exciting powers of Lightning is the ability +to start an app from a template, replace or add components +and build a powerful system. + +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 10: Practice adapting app templates + :description: Practice starting apps from templates and evolving them by replacing or adding components. + :button_link: start_from_lightning_app_templates.html + :col_css: col-md-12 + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/intermediate/index.rst b/docs/source-app/levels/intermediate/index.rst new file mode 100644 index 0000000..ed5c391 --- /dev/null +++ b/docs/source-app/levels/intermediate/index.rst @@ -0,0 +1,87 @@ +.. _intermediate_level: + +.. toctree:: + :maxdepth: 1 + :hidden: + + connect_lightning_components + debug_a_lightning_app + run_lightning_work_in_parallel + share_variables_between_lightning_components + share_files_between_components + embed_web_ui_into_lightningwork + start_from_lightning_app_templates + +################### +Intermediate skills +################### +Learn to coordinate 2+ components into workflows which we call Lightning apps. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 4: Coordinate 2+ components in a workflow + :description: Learn to coordinate 2_ components in a workflow which we call a Lightning app. + :button_link: connect_lightning_components.html + :col_css: col-md-6 + :height: 170 + :tag: 15 minutes + +.. displayitem:: + :header: Level 5: Debug a Lightning App + :description: Learn to debug a lightning app. + :button_link: debug_a_lightning_app.html + :col_css: col-md-6 + :height: 170 + :tag: 2 minutes + +.. displayitem:: + :header: Level 6: Run a Lightning component in parallel + :description: Learn when and how to run Components in parallel (asynchronous). + :button_link: run_lightning_work_in_parallel.html + :col_css: col-md-6 + :height: 150 + :tag: 10 minutes + +.. displayitem:: + :header: Level 7: Share variables between components + :description: Share variables between Lightning components. + :button_link: share_variables_between_lightning_components.html + :col_css: col-md-6 + :height: 150 + :tag: 15 minutes + +.. displayitem:: + :header: Level 8: Share files between components + :description: Learn how Drives share files between components + :button_link: share_files_between_components.html + :col_css: col-md-6 + :height: 150 + :tag: 20 minutes + +.. displayitem:: + :header: Level 9: Render a web UI with other components + :description: Learn how to embed graphical UIs like react, vue, streamlit and notebook UIs into a lightning workflow. + :button_link: embed_web_ui_into_lightningwork.html + :col_css: col-md-6 + :height: 150 + :tag: 15 minutes + +.. displayitem:: + :header: Level 10: Practice adapting app templates + :description: Practice starting apps from templates and evolving them by replacing or adding components. + :button_link: start_from_lightning_app_templates.html + :col_css: col-md-6 + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/intermediate/level_12.rst b/docs/source-app/levels/intermediate/level_12.rst new file mode 100644 index 0000000..60c2390 --- /dev/null +++ b/docs/source-app/levels/intermediate/level_12.rst @@ -0,0 +1,12 @@ +:orphan: + +###################### +Level 12: Flow vs Work +###################### +**Audience:** Users who need to do non trivial workloads in their apps. + +**Prereqs:** Level 8+ + +---- + +.. include:: ../../workflows/build_lightning_component/from_scratch_component_content.rst diff --git a/docs/source-app/levels/intermediate/level_2_scripts/code_run_cloud.bash b/docs/source-app/levels/intermediate/level_2_scripts/code_run_cloud.bash new file mode 100644 index 0000000..6594fe0 --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/code_run_cloud.bash @@ -0,0 +1 @@ +lightning run app app.py --cloud diff --git a/docs/source-app/levels/intermediate/level_2_scripts/code_run_local.bash b/docs/source-app/levels/intermediate/level_2_scripts/code_run_local.bash new file mode 100644 index 0000000..8a00b45 --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/code_run_local.bash @@ -0,0 +1 @@ +lightning run app app.py diff --git a/docs/source-app/levels/intermediate/level_2_scripts/hello_app.py b/docs/source-app/levels/intermediate/level_2_scripts/hello_app.py new file mode 100644 index 0000000..d6ce3ea --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/hello_app.py @@ -0,0 +1,23 @@ +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f'train a model on {x}') + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f'analyze model on {x}') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute('cpu')) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute('gpu')) + + def run(self): + self.train.run("CPU machine 1") + self.analyze.run("GPU machine 2") + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/level_2_scripts/hello_app_auto_scale.py b/docs/source-app/levels/intermediate/level_2_scripts/hello_app_auto_scale.py new file mode 100644 index 0000000..b26d49c --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/hello_app_auto_scale.py @@ -0,0 +1,30 @@ +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f'train a model on {x}') + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f'analyze model on {x}') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute('cpu')) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute('gpu')) + + def run(self): + # run() starts the machine + self.train.run("GPU machine 1") + + # stop() stops the machine + self.train.stop() + + # run analysis ONLY when machine 1 stopped + if self.train.status.STOPPED: + self.analyze.run("CPU machine 2") + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/level_2_scripts/hello_app_cron.py b/docs/source-app/levels/intermediate/level_2_scripts/hello_app_cron.py new file mode 100644 index 0000000..a7a3ee6 --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/hello_app_cron.py @@ -0,0 +1,27 @@ +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f'train a model on {x}') + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f'analyze model on {x}') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute('cpu')) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute('gpu')) + + def run(self): + # run training once + self.train.run("GPU machine 1") + + # run analysis once, then every hour again... + if self.schedule("5 4 * * *"): + self.analyze.run("CPU machine 2") + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/level_2_scripts/hello_app_scheduler.py b/docs/source-app/levels/intermediate/level_2_scripts/hello_app_scheduler.py new file mode 100644 index 0000000..1e655c2 --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/hello_app_scheduler.py @@ -0,0 +1,27 @@ +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f'train a model on {x}') + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f'analyze model on {x}') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute('cpu')) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute('gpu')) + + def run(self): + # run training once + self.train.run("GPU machine 1") + + # run analysis once, then every hour again... + if self.schedule("hourly"): + self.analyze.run("CPU machine 2") + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/level_2_scripts/multi_cloud.bash b/docs/source-app/levels/intermediate/level_2_scripts/multi_cloud.bash new file mode 100644 index 0000000..840208d --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/multi_cloud.bash @@ -0,0 +1,10 @@ +# run on lightning cloud (fully managed) +lightning run app app.py --cloud + +# run on a cluster you created called pikachu +lightning create cluster pikachu --provider aws --role-arn arn:aws:iam::1234567890:role/lai-byoc +lightning run app app.py --cloud pikachu + +# run on a cluster you created called bolt +lightning create cluster bolt --provider aws --role-arn arn:aws:iam::1234567890:role/lai-byoc +lightning run app app.py --cloud bolt diff --git a/docs/source-app/levels/intermediate/level_2_scripts/organized_app_python.py b/docs/source-app/levels/intermediate/level_2_scripts/organized_app_python.py new file mode 100644 index 0000000..8b174ad --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/organized_app_python.py @@ -0,0 +1,36 @@ +# app.py +import subprocess +import lightning as L + + +class ExternalModelServer(L.LightningWork): + def run(self, x): + # compile + process = subprocess.Popen('g++ model_server.cpp -o model_server') + process.wait() + process = subprocess.Popen('./model_server') + process.wait() + +class LocustLoadTester(L.LightningWork): + def run(self, x): + cmd = f'locust --master-host {self.host} --master-port {self.port}' + process = subprocess.Popen(cmd) + process.wait() + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.serve = ExternalModelServer( + cloud_compute=L.CloudCompute('cpu'), parallel=True + ) + self.load_test = LocustLoadTester(cloud_compute=L.CloudCompute('cpu')) + + def run(self): + # start the server (on a CPU machine 1) + self.serve.run() + + # load testing when the server is up (on a separate cpu machine 2) + if self.serve.state.RUNNING: + self.load_test.run() + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/level_2_scripts/tr.bash b/docs/source-app/levels/intermediate/level_2_scripts/tr.bash new file mode 100644 index 0000000..30c8d19 --- /dev/null +++ b/docs/source-app/levels/intermediate/level_2_scripts/tr.bash @@ -0,0 +1,5 @@ +# custom control for optimized clusters with tools like terraform +# are only supported on the enterprise tier (support@lightning.ai) + +# once the cluster is created you can run any app on it +lightning run app app.py --cloud my-custom-optimized-cluster diff --git a/docs/source-app/levels/intermediate/level_9.rst b/docs/source-app/levels/intermediate/level_9.rst new file mode 100644 index 0000000..344c321 --- /dev/null +++ b/docs/source-app/levels/intermediate/level_9.rst @@ -0,0 +1,16 @@ +:orphan: + +################### +Level 9: Event loop +################### +**Audience:** Users who want to build reactive Lightning Apps and move beyond DAGs. + +**Prereqs:** Level 8+ + +---- + +Drawing inspiration from modern web frameworks like `React.js `_, the Lightning App runs all flows in an **event loop** (forever), which is triggered several times a second after collecting any works' state change. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/lightning_loop.gif + +When running a Lightning App in the cloud, the ``LightningWork`` run on different machines. LightningWork communicates any state changes to the **event loop** which re-executes the flow with the newly-collected works' state. diff --git a/docs/source-app/levels/intermediate/run_lightning_work_in_parallel.rst b/docs/source-app/levels/intermediate/run_lightning_work_in_parallel.rst new file mode 100644 index 0000000..b74fdac --- /dev/null +++ b/docs/source-app/levels/intermediate/run_lightning_work_in_parallel.rst @@ -0,0 +1,38 @@ +############################################## +Level 6: Run a Lightning component in parallel +############################################## +**Audience:** Users who want to run a Lightning Component in parallel (asynchroneously). + +**Prereqs:** You must have finished `Level 5 `_. + +---- + +.. include:: ../../workflows/run_work_in_parallel_content.rst + +---- + +********************************************** +Next steps: Share variables between components +********************************************** +Now that you know how to run components in parallel, we'll learn to share variables +across components to simplify complex workflows. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 7: Share variables between components + :description: Learn to connect components + :col_css: col-md-12 + :button_link: share_variables_between_lightning_components.html + :height: 150 + :tag: 10 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/intermediate/scripts/.storage/a b/docs/source-app/levels/intermediate/scripts/.storage/a new file mode 100644 index 0000000..1c6c4cc Binary files /dev/null and b/docs/source-app/levels/intermediate/scripts/.storage/a differ diff --git a/docs/source-app/levels/intermediate/scripts/.storage/embeddings b/docs/source-app/levels/intermediate/scripts/.storage/embeddings new file mode 100644 index 0000000..af3ee63 Binary files /dev/null and b/docs/source-app/levels/intermediate/scripts/.storage/embeddings differ diff --git a/docs/source-app/levels/intermediate/scripts/a b/docs/source-app/levels/intermediate/scripts/a new file mode 100644 index 0000000..1c6c4cc Binary files /dev/null and b/docs/source-app/levels/intermediate/scripts/a differ diff --git a/docs/source-app/levels/intermediate/scripts/comms_1.py b/docs/source-app/levels/intermediate/scripts/comms_1.py new file mode 100644 index 0000000..0411820 --- /dev/null +++ b/docs/source-app/levels/intermediate/scripts/comms_1.py @@ -0,0 +1,18 @@ +# app.py +import lightning as L + +class Component(L.LightningWork): + def run(self, x): + print(f'MACHINE 1: this string came from machine 0: "{x}"') + print('MACHINE 1: this string is on machine 1') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.component = Component() + + def run(self): + x = 'hello from machine 0' + self.component.run(x) + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/scripts/debug_app.py b/docs/source-app/levels/intermediate/scripts/debug_app.py new file mode 100644 index 0000000..9e3d9c9 --- /dev/null +++ b/docs/source-app/levels/intermediate/scripts/debug_app.py @@ -0,0 +1,25 @@ +# app.py +import lightning as L +from lightning.app.runners import MultiProcessRuntime + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f'train a model on {x}') + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f'analyze model on {x}') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent() + self.analyze = AnalyzeComponent() + + def run(self): + self.train.run("GPU machine 1") + self.analyze.run("CPU machine 2") + +app = L.LightningApp(WorkflowOrchestrator()) +MultiProcessRuntime(app).dispatch() diff --git a/docs/source-app/levels/intermediate/scripts/embeddings b/docs/source-app/levels/intermediate/scripts/embeddings new file mode 100644 index 0000000..af3ee63 Binary files /dev/null and b/docs/source-app/levels/intermediate/scripts/embeddings differ diff --git a/docs/source-app/levels/intermediate/scripts/toy_app.py b/docs/source-app/levels/intermediate/scripts/toy_app.py new file mode 100644 index 0000000..44273e6 --- /dev/null +++ b/docs/source-app/levels/intermediate/scripts/toy_app.py @@ -0,0 +1,24 @@ +# app.py +import lightning as L + + + +class TrainComponent(L.LightningWork): + def run(self, x): + print(f'train a model on {x}') + +class AnalyzeComponent(L.LightningWork): + def run(self, x): + print(f'analyze model on {x}') + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent() + self.analyze = AnalyzeComponent() + + def run(self): + self.train.run("CPU machine 1") + self.analyze.run("CPU machine 2") + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/scripts/toy_payload.py b/docs/source-app/levels/intermediate/scripts/toy_payload.py new file mode 100644 index 0000000..f6c744c --- /dev/null +++ b/docs/source-app/levels/intermediate/scripts/toy_payload.py @@ -0,0 +1,31 @@ +# app.py +import lightning as L + + +class EmbeddingProcessor(L.LightningWork): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.embeddings = None + + def run(self): + print('PROCESSOR: Generating embeddings...') + fake_embeddings = [[1, 2, 3], [2, 3, 4]] + self.embeddings = L.storage.Payload(fake_embeddings) + +class EmbeddingServer(L.LightningWork): + def run(self, payload): + print('SERVER: Using embeddings from processor', payload) + embeddings = payload.value + print('serving embeddings sent from EmbeddingProcessor: ', embeddings) + +class WorkflowOrchestrator(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.processor = EmbeddingProcessor() + self.server = EmbeddingServer() + + def run(self): + self.processor.run() + self.server.run(self.processor.embeddings) + +app = L.LightningApp(WorkflowOrchestrator()) diff --git a/docs/source-app/levels/intermediate/scripts/two_comms_non_ml.py b/docs/source-app/levels/intermediate/scripts/two_comms_non_ml.py new file mode 100644 index 0000000..23e8cd6 --- /dev/null +++ b/docs/source-app/levels/intermediate/scripts/two_comms_non_ml.py @@ -0,0 +1,36 @@ +# app.py +import lightning as L +import time + +class A(L.LightningWork): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.msg_changed = False + self.new_msg = '' + + def run(self): + # pretend to train and save a checkpoint every 10 steps + for step in (range(1000)): + time.sleep(1.0) + if step % 10 == 0: + self.msg_changed = True + self.new_msg = f'A is at step: {step}' + print(self.new_msg) + +class B(L.LightningWork): + def run(self, msg): + print(f'B: message from A: {msg}') + +class Example(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.a = A(parallel=True) + self.b = B(parallel=True) + + def run(self): + self.a.run() + if self.a.msg_changed: + self.a.msg_changed = False + self.b.run(self.a.new_msg) + +app = L.LightningApp(Example()) diff --git a/docs/source-app/levels/intermediate/scripts/two_work_comms.py b/docs/source-app/levels/intermediate/scripts/two_work_comms.py new file mode 100644 index 0000000..5e02b33 --- /dev/null +++ b/docs/source-app/levels/intermediate/scripts/two_work_comms.py @@ -0,0 +1,35 @@ +# app.py +import lightning as L +import time + +class TrainComponent(L.LightningWork): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.last_checkpoint_path = None + + def run(self): + # pretend to train and save a checkpoint every 10 steps + for step in (range(1000)): + time.sleep(1.0) + fake_loss = round(1/(step + 0.00001), 4) + print(f'{step=}: {fake_loss=} ') + if step % 10 == 0: + self.last_checkpoint_path = f'/some/path/{step=}_{fake_loss=}' + print(f'TRAIN COMPONENT: saved new checkpoint: {self.last_checkpoint_path}') + +class ModelDeploymentComponent(L.LightningWork): + def run(self, new_checkpoint): + print(f'DEPLOY COMPONENT: load new model from checkpoint: {new_checkpoint}') + +class ContinuousDeployment(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(parallel=True) + self.model_deployment = ModelDeploymentComponent(parallel=True) + + def run(self): + self.train.run() + if self.train.last_checkpoint_path: + self.model_deployment.run(self.train.last_checkpoint_path) + +app = L.LightningApp(ContinuousDeployment()) diff --git a/docs/source-app/levels/intermediate/share_files_between_components.rst b/docs/source-app/levels/intermediate/share_files_between_components.rst new file mode 100644 index 0000000..3fa24de --- /dev/null +++ b/docs/source-app/levels/intermediate/share_files_between_components.rst @@ -0,0 +1,41 @@ +####################################### +Level 8: Share files between components +####################################### +**Audience:** Users who are moving large files such as artifacts or datasets. + +**Prereqs:** Level 6+ + +---- + +**** +TODO +**** +TODO + +---- + +************************************************* +Next steps: Render a web UI with other components +************************************************* +Now that we know the key ways of sharing files and variables, +we'll apply it to embed web UIs alongside components. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 9: Render a web UI with other components + :description: Learn how to embed graphical UIs like react, vue, streamlit and notebook UIs into a lightning workflow. + :button_link: embed_web_ui_into_lightningwork.html + :col_css: col-md-12 + :height: 150 + :tag: 15 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/intermediate/share_variables_between_lightning_components.rst b/docs/source-app/levels/intermediate/share_variables_between_lightning_components.rst new file mode 100644 index 0000000..b0ce06d --- /dev/null +++ b/docs/source-app/levels/intermediate/share_variables_between_lightning_components.rst @@ -0,0 +1,162 @@ +########################################### +Level 7: Share variables between components +########################################### +**Audience:** Users who want to share variables and files across Lightning components. + +**Prereqs:** You must have finished `intermediate level 5+ `_. + +---- + +**************************************** +Send a variable from Flow to a Component +**************************************** +When a variable is defined on the LightningFlow (orchestrator), and +then it's passed into functions for the work components, under the hood +Lightning sends the variables across the machines for you automatically. + +.. lit_tabs:: + :descriptions: Remember this component may live on its own machine; The flow may be on a separate machine as well; This variable is on the flow machine; When passed to the work component, it is actually sent across the network under the hood.; When it prints here, it prints on the work component machine (not the flow machine); The second string was directly created on machine 1 + :code_files: ./scripts/comms_1.py; ./scripts/comms_1.py; ./scripts/comms_1.py; ./scripts/comms_1.py; ./scripts/comms_1.py; ./scripts/comms_1.py + :highlights: 4-7; 9-16; 15; 16; 6; 7; + :enable_run: true + :tab_rows: 3 + :height: 380px + +| + +.. collapse:: CLI output + + .. code-block:: + + $ lightning run app app.py --open-ui=false + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + MACHINE 1: this string came from machine 0: "hello from machine 0" + MACHINE 1: this string is on machine 1 + +| + +In this example, we learned that we can send variables to components like in regular Python. +On a local machine, it will behave like Python. When the workflow is distributed on the cloud, +it makes network calls under the hood, but still functions like Python to you. + +---- + +************************************** +Send a variable between two components +************************************** +A majority of workflows (especially in ML), require components to respond to a change in a component +likely running on a separate machine or even cluster. + +Example Continuous deployment: Every time a model saves a checkpoint, we redeploy a model: + +.. lit_tabs:: + :descriptions: Define a component that simulates training; Define a component that simulates deployment; Training will happen in parallel over a long period; The deployment server also runs in parallel forever; Start training in parallel (could take months); Whenever the model has a checkpoint deploy; When the checkpoint is updated, model re-deploys + :code_files: ./scripts/two_work_comms.py; ./scripts/two_work_comms.py; ./scripts/two_work_comms.py; ./scripts/two_work_comms.py; ./scripts/two_work_comms.py; ./scripts/two_work_comms.py; ./scripts/two_work_comms.py + :highlights: 5-18; 20-22; 27; 28; 31; 32, 33; 33 + :enable_run: true + :tab_rows: 3 + :height: 690px + +| + +.. collapse:: CLI output: + + .. code:: + + $ lightning run app app.py --open-ui=false + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + step=0: fake_loss=100000.0 + TRAIN COMPONENT: saved new checkpoint: /some/path/step=0_fake_loss=100000.0 + step=1: fake_loss=1.0 + DEPLOY COMPONENT: load new model from checkpoint: /some/path/step=0_fake_loss=100000.0 + step=2: fake_loss=0.5 + step=3: fake_loss=0.3333 + step=4: fake_loss=0.25 + step=5: fake_loss=0.2 + step=6: fake_loss=0.1667 + step=7: fake_loss=0.1429 + step=8: fake_loss=0.125 + step=9: fake_loss=0.1111 + step=10: fake_loss=0.1 + TRAIN COMPONENT: saved new checkpoint: /some/path/step=10_fake_loss=0.1 + DEPLOY COMPONENT: load new model from checkpoint: /some/path/step=10_fake_loss=0.1 + step=11: fake_loss=0.0909 + step=12: fake_loss=0.0833 + step=13: fake_loss=0.0769 + step=14: fake_loss=0.0714 + step=15: fake_loss=0.0667 + step=16: fake_loss=0.0625 + step=17: fake_loss=0.0588 + step=18: fake_loss=0.0556 + step=19: fake_loss=0.0526 + step=20: fake_loss=0.05 + TRAIN COMPONENT: saved new checkpoint: /some/path/step=20_fake_loss=0.05 + DEPLOY COMPONENT: load new model from checkpoint: /some/path/step=20_fake_loss=0.05 + step=21: fake_loss=0.0476 + +---- + +******************************************** +Send a large variable between two components +******************************************** +For large variables such as arrays, tensors, embeddings and so on, use Payload to enable +transfering them across components. + +.. lit_tabs:: + :descriptions: Let's define a component to simulate generating embeddings (from a DB, feature store, etc...); This component simulates a server that will use the embeddings.; Run the component to generate the embeddings; Simulate embeddings as an array. Here you would query a DB, load from a feature store or disk or even use a neural network to extract the embedding.; Allow the embeddings to be transfered efficiently by wrapping them in the Payload object.; Pass the variable to the EmbeddingServer (just the pointer).; The data gets transfered once you use the .value attribute in the other component. + :code_files: ./scripts/toy_payload.py; ./scripts/toy_payload.py; ./scripts/toy_payload.py; ./scripts/toy_payload.py; ./scripts/toy_payload.py; ./scripts/toy_payload.py; ./scripts/toy_payload.py; + :highlights: 5-13; 15-19; 28; 12; 13; 29; 18 + :enable_run: true + :tab_rows: 3 + :height: 600px + +| + +.. collapse:: CLI output + + .. code:: + + $ lightning run app app.py --open-ui=false + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + PROCESSOR: Generating embeddings... + SERVER: Using embeddings from processor + serving embeddings sent from EmbeddingProcessor: [[1, 2, 3], [2, 3, 4]] + +| + +The payload object keeps the data on the machine and passes a pointer +to the data around the app until the data is needed by a component. + +---- + +****************************************** +Next steps: Share files between components +****************************************** +Now that you know how to run components in parallel, we'll learn to share variables +across components to simplify complex workflows. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 8: Share files between components + :description: Learn to share files between components. + :col_css: col-md-12 + :button_link: share_files_between_components.html + :height: 150 + :tag: 10 minutes + +.. raw:: html + +
+
diff --git a/docs/source-app/levels/intermediate/start_from_lightning_app_templates.rst b/docs/source-app/levels/intermediate/start_from_lightning_app_templates.rst new file mode 100644 index 0000000..ceb497c --- /dev/null +++ b/docs/source-app/levels/intermediate/start_from_lightning_app_templates.rst @@ -0,0 +1,34 @@ +############################################ +Level 10: Start from lightning app templates +############################################ +**Audience:** All users who want to move quickly with Lightning + +**Prereqs:** You have finished `Level 9 `_. + +---- + +**************************************************** +Next step: Learn to build powerful nested components +**************************************************** +Now that you can build powerful apps, learn to build nested components +that can do things like start dynamic works and connect to each other +via networking or CLI commands. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Advanced skills + :description: Learn to build nested components with advanced functionality. + :button_link: ../advanced/index.html + :col_css: col-md-12 + :height: 170 + +.. raw:: html + +
+
diff --git a/docs/source-app/make.bat b/docs/source-app/make.bat new file mode 100644 index 0000000..9b56514 --- /dev/null +++ b/docs/source-app/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=../build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/source-app/moving_to_the_cloud.rst b/docs/source-app/moving_to_the_cloud.rst new file mode 100644 index 0000000..86478fc --- /dev/null +++ b/docs/source-app/moving_to_the_cloud.rst @@ -0,0 +1,122 @@ +:orphan: + +.. _moving_to_the_cloud: + +#################### +Moving to the Cloud +#################### + +.. warning:: This is in progress and not yet fully supported. + +In the :ref:`quick_start` guide, you learned how to implement a simple app +that trains an image classifier and serve it once trained. + +In this tutorial, you'll learn how to extend that application so that it works seamlessly +both locally and in the cloud. + +---- + +******************************** +Step 1: Distributed Application +******************************** + + +Distributed Storage +^^^^^^^^^^^^^^^^^^^ + +When running your application in a fully-distributed setting, the data available on one machine won't necessarily be available on another. + +To solve this problem, Lightning introduces the :class:`~lightning.app.storage.Path` object. +This ensures that your code can run both locally and in the cloud. + +The :class:`~lightning.app.storage.Path` object keeps track of the work which creates +the path. This enables Lightning to transfer the files correctly in a distributed setting. + +Instead of passing a string representing a file or directory, Lightning simply wraps +them into a :class:`~lightning.app.storage.Path` object and makes them an attribute of your LightningWork. + +Without doing this conscientiously for every single path, your application will fail in the cloud. + +In the example below, a file written by **SourceFileWork** is being transferred by the flow +to the **DestinationFileAndServeWork** work. The Path object is the reference to the file. + +.. literalinclude:: ../../examples/app/boring/app.py + :emphasize-lines: 5, 22, 28, 48 + +In the ``scripts/serve.py`` file, we are creating a **FastApi Service** running on port ``1111`` +that returns the content of the file received from **SourceFileWork** when +a post request is sent to ``/file``. + +.. literalinclude:: ../../examples/app/boring/scripts/serve.py + :emphasize-lines: 21, 23-26 + +---- + +Distributed Frontend +^^^^^^^^^^^^^^^^^^^^ + +In the above example, the **FastAPI Service** was running on one machine, +and the frontend UI in another. + +In order to assemble them, you need to do two things: + +* Provide **port** argument to your work's ``__init__`` method to expose a single service. + +Here's how to expose the port: + +.. literalinclude:: ../../examples/app/boring/app.py + :emphasize-lines: 8 + :lines: 33-44 + + +And here's how to expose your services within the ``configure_layout`` flow hook: + +.. literalinclude:: ../../examples/app/boring/app.py + :emphasize-lines: 5 + :lines: 53-57 + +In this example, we're appending ``/file`` to our **FastApi Service** url. +This means that our ``Boring Tab`` triggers the ``get_file_content`` from the **FastAPI Service** +and embeds its content as an `IFrame `_. + +.. literalinclude:: ../../examples/app/boring/scripts/serve.py + :lines: 23-26 + + +Here's a visualization of the application described above: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/storage_ui.gif + :alt: Storage API Animation + :width: 100 % + +---- + +***************************** +Step 2: Scalable Application +***************************** + +The benefit of defining long-running code inside a +:class:`~lightning.app.core.work.LightningWork` +component is that you can run it on different hardware +by providing :class:`~lightning.app.utilities.packaging.cloud_compute.CloudCompute` to +the ``__init__`` method of your :class:`~lightning.app.core.work.LightningWork`. + +By adapting the :ref:`quick_start` example as follows, you can easily run your component on multiple GPUs: + + +Without doing much, you’re now running a script on its own cluster of machines! 🤯 + +---- + +***************************** +Step 3: Resilient Application +***************************** + +We designed Lightning with a strong emphasis on supporting failure cases. +The framework shines when the developer embraces our fault-tolerance best practices, +enabling them to create ML applications with a high degree of complexity as well as a strong support +for unhappy cases. + +An entire section would be dedicated to this concept. + +TODO diff --git a/docs/source-app/quickstart.rst b/docs/source-app/quickstart.rst new file mode 100644 index 0000000..6df9b26 --- /dev/null +++ b/docs/source-app/quickstart.rst @@ -0,0 +1,126 @@ +:orphan: + +.. _quick_start: + +############ +Quick Start +############ + +In this guide, we'll run an application that trains +an image classification model with the `MNIST Dataset `_, +and uses `Gradio `_ to serve it. + +---- + +********************** +Step 1 - Installation +********************** + +First, you'll need to install Lightning. You can find the complete guide here. + +Then, you'll need to install the `Lightning Quick Start package `_. + +.. code-block:: bash + + lightning install app lightning/quick-start + +And download the training script used by the App: + + +---- + +********************** +Step 2 - Run the app +********************** + +To run your app, copy the following command to your local terminal: + +.. code-block:: bash + + lightning run app app.py + +And that's it! + +.. admonition:: You should see the app logs in your terminal. + :class: dropdown + + .. code-block:: console + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + + Global seed set to 42 + + GPU available: True (mps), used: False + TPU available: False, using: 0 TPU cores + IPU available: False, using: 0 IPUs + + | Name | Type | Params | In sizes | Out sizes + ------------------------------------------------------------------ + 0 | model | Net | 1.2 M | [1, 1, 28, 28] | [1, 10] + 1 | val_acc | Accuracy | 0 | ? | ? + ------------------------------------------------------------------ + 1.2 M Trainable params + 0 Non-trainable params + 1.2 M Total params + Epoch 4: 100%|█████████████████████████| 16/16 [00:00<00:00, 32.31it/s, loss=0.0826, v_num=0] + `Trainer.fit` stopped: `max_epochs=5` reached. + + Running on local URL: http://127.0.0.1:62782/ + ... + + +The app will open your browser and show an interactive demo: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/qiuck-start-tensorboard-tab.png + :alt: Quick Start UI - Model Training Tab + :width: 100 % + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/quick-start-gradio-tab.png + :alt: Quick Start UI - Interactive Demo Tab + :width: 100 % + +---- + +This app behind the scenes +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This application has one flow component which coordinates two works executing their own python script. +Once the training is finished, the trained model weights are passed to the serve component. + + +Here is how the components of a Lightning app are structured: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/quick_start_components.gif + :alt: Quick Start Application + :width: 100 % + +Here is the application timeline: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/timeline.gif + :alt: Quick Start Timeline Application + :width: 100 % + +---- + +************************************** +Steps 3 - Build your app in the cloud +************************************** + +Simply add ``--cloud`` to run this application in the cloud 🤯 + +.. code-block:: bash + + lightning run app app.py --cloud + +Congratulations! You've now run your first application with Lightning. + +---- + +*********** +Next Steps +*********** + +To learn how to build and modify apps, go to the :ref:`basics`. + +To learn how to create UIs for your apps, read :ref:`ui_and_frontends`. diff --git a/docs/source-app/testing.rst b/docs/source-app/testing.rst new file mode 100644 index 0000000..ed2d6db --- /dev/null +++ b/docs/source-app/testing.rst @@ -0,0 +1,155 @@ +:orphan: + +.. _testing: + +####################### +Productionize your Apps +####################### + +TODO: Cleanup + +At the core of our system is an integration testing framework that will allow for a first-class experience creating integration tests for Lightning Apps. This document will explain how we can create a lightning app test, how we can execute it, and where to find more information. + +---- + +*********** +Philosophy +*********** + +Testing a Lightning app is unique. It is a superset of an application that converges machine learning, API development, and UI development. With that in mind, there are several philosophies (or "best practices") that you should adhere to: + + +#. **Control your app state** - With integration and end to end tests, you have the capabilities of controlling your app's state through dependency injection. Use it! +#. **Integration focuses on the work, End to End focuses on the app** - When writing tests, think of the depth and breath of what you are writing. Write many integration tests since they are relatively cheap, while keeping the end to end tests for holistic app testing. +#. **Don't overthink it** - What needs to be tested? What is the order of risk? These are the questions you should build with before writing your first line of code. Writing tests for the sake of writing tests is an exercise in futility. Write meaningful, impactful tests. +#. **Test Isolation** - Write your tests in an isolated manner. No two tests should ever depend on each other. +#. **Use your framework** - Testing apps should be framework agnostic. +#. **Have fun!** - At the heart of testing is experimentation. Like any experiment, tests begin with a hypothesis of workability, but you can extend that to be more inclusive. Ask the question, write the test to answer your question, and make sure you have fun while doing it. + +---- + +**************************************** +Anatomy of a Lightning integration test +**************************************** + +The following is a PyTest example of an integration test using the ``lightning.app.testing`` module. + +.. code-block:: python + + import os + + from lightning.app import _PROJECT_ROOT + from lightning.app.testing import application_testing, LightningTestApp + from lightning.app.utilities.enum import AppStage + + + class TestLightningAppInt(TestLightningApp): + def run_once(self) -> bool: + if self.root.counter > 1: + print("V0 App End") + self.stage = AppStage.STOPPING + return True + return super().run_once() + + + def test_v0_app_example(): + command_line = [ + os.path.join(_PROJECT_ROOT, "examples/app_v0/app.py"), + "--blocking", + "False", + "--multiprocess", + "--open-ui", + "False", + ] + result = application_testing(TestLightningAppInt, command_line) + assert "V0 App End" in str(result.stdout_bytes) + assert result.exit_code == 0 + +---- + +Setting up the app +^^^^^^^^^^^^^^^^^^ + +Lightning apps are unique in that they represent a full stack model for your machine learning application. To be clear, the integration tests are *NOT* going to touch the UI flow. Instead we inject your application with helper methods that, when executed, can assist in validating your application. + +To get started, you simply need to import the following: + +.. code-block:: python + + from lightning.app.testing import application_testing, LightningTestApp + +We will discuss ``application_testing`` in a bit, but first let's review the structure of ``LightningTestApp``. + +---- + +LightningTestApp +^^^^^^^^^^^^^^^^^ + +The :class:`lightning.app.testing.testing.LightningTestApp` class is available to use for provisioning and setting up your testing needs. Note that you do not need this class to move forward with testing. Any application that inherits ``LightningApp`` should suffice as long as you override the correct methods. Reviewing the TestLightnigApp we see some overrides that are already there. Please revuew the class for more information. + +.. code-block:: python + + class TestLightningAppInt(LightningTestApp): + def run_once(self) -> bool: + if self.root.counter > 1: + print("V0 App End") + self.stage = AppStage.STOPPING + return True + return super().run_once() + +We create a test class overriding the ``run_once`` function. This function helps control the flow of your application and is ran first. In this example we are calling ``self.root.counter`` and checking if the job has executed once. If so, we want to print out ``V0 App End`` and set the ``self.stage`` to ``AppStage.STOPPING``. This is how we control the flow through state. Your situation might be different, so experiment and see what you can do! + +Besides ``run_once`` there are a few other overrides available: + + +* ``on_before_run_once`` - This runs before your ``run_once`` function kicks off. You can set up your application pre-conditions here. +* ``on_after_run_once`` - Similar to ``on_before_run_once`` but after the ``run_once`` method is called. + +These methods will skew your tests, so use them when needed. + +---- + +The Test +^^^^^^^^ + +We provide ``application_testing`` as a helper funtion to get your application up and running for testing. It uses ``click``\ 's invocation tooling underneath. + +.. code-block:: + + command_line = [ + os.path.join(_PROJECT_ROOT, "examples/app_v0/app.py"), + "--blocking", + "False", + "--open-ui", + "False", + ] + +First in the list for ``command_line`` is the location of your script. It is an external file. In this example we have ``_PROJECT_ROOT`` but this is *not* a helper constant for you to utilize. You will need to provide the location yourself. + +Next there are a couple of options you can leverage: + +* ``blocking`` - Blocking is an app status that says "Do not run until I click run in the UI". For our integration test, since we are not using the UI, we are setting this to "False". +* ``open-ui`` - We set this to false since this is the routine that opens a browser for your local execution. + +Once you have your commandline ready, you will then be able to kick off the test and gather results: + +.. code-block:: python + + result = application_testing(TestLightningAppInt, command_line) + +As mentioned earlier, ``application_testing`` is a helper method that allows you to inject your TestLightningApp class (with overrides) and the commandline flags. Once the process is done it returns the results back for parsing. + +.. code-block:: python + + assert "V0 App End" in str(result.stdout_bytes) + assert result.exit_code == 0 + +Since we injected "V0 App End" to the end of our test flow. The state was changed to ``AppStatus.STOPPING`` which means the process is done. Finally, we check the result's exit code to make sure that we did not throw an error during execution. + +---- + +************ +End to End +************ + +TODO diff --git a/docs/source-app/ui_and_frontends.rst b/docs/source-app/ui_and_frontends.rst new file mode 100644 index 0000000..53e50cd --- /dev/null +++ b/docs/source-app/ui_and_frontends.rst @@ -0,0 +1,23 @@ +:orphan: + +.. _ui_and_frontends: + +################ +UI and Frontends +################ + + +The Lightning framework allows you to create customized, interactive UIs with the framework of your choice. + +You can easily embed other tools and services (like a GitHub repo, a `FastAPI Service `_, an `arXiv `_ paper or a `Dask Cluster `_ Admin page), or create a complete UI from scratch. + + +To get started, you can use built-in templates for the following frameworks: + +* `React.js `_ +* `StreamLit `_ + + + +To keep learning about Lightning, check out :ref:`moving_to_the_cloud`. +This section covers best practices to seamlessly make your Lightning code work both locally and in the cloud. diff --git a/docs/source-app/workflows/access_app_state/access_app_state.rst b/docs/source-app/workflows/access_app_state/access_app_state.rst new file mode 100644 index 0000000..1e23328 --- /dev/null +++ b/docs/source-app/workflows/access_app_state/access_app_state.rst @@ -0,0 +1,59 @@ +.. _access_app_state: + +################ +Access App State +################ + +**Audience:** Users who want to know how the App State can be accessed. + +**Level:** Basic + +********************** +What is the App State? +********************** + +In Lightning, each component is stateful and their state is composed of all attributes defined within their **__init__** method. + +The **App State** is the collection of all the components' states forming the App. + +************************************ +What is special about the App State? +************************************ + +The **App State** is always up-to-date, even running an App in the cloud on multiple machines. +This means that every time an attribute is modified in a Work, that information is automatically +broadcasted to the Flow. With this mechanism, any Component can **react** to any other +Component's **state changes** through the Flow and complex systems can be easily implemented. +Lightning requires a state based driven mindset when implementing the Flow. + +*************************************** +When do I need to access the App State? +*************************************** + +As a user, you are interacting with your component attributes, so most likely, +you won't need to access the Component's state directly, but it can be helpful to +understand how the state works under the hood. + +For example, here we define a **Flow** component and **Work** component, where the Work increments a counter indefinitely and the Flow prints its state which contains the Work. + +You can easily check the state of your entire App as follows: + +.. literalinclude:: ../../code_samples/quickstart/app_01.py + +Run the App with: + +.. code-block:: bash + + lightning run app docs/quickstart/app_01.py + +And here's the output you get when running the App using **Lightning CLI**: + +.. code-block:: console + + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + State: {'works': {'w': {'vars': {'counter': 1}}}} + State: {'works': {'w': {'vars': {'counter': 2}}}} + State: {'works': {'w': {'vars': {'counter': 3}}}} + State: {'works': {'w': {'vars': {'counter': 3}}}} + State: {'works': {'w': {'vars': {'counter': 4}}}} + ... diff --git a/docs/source-app/workflows/add_components/index.rst b/docs/source-app/workflows/add_components/index.rst new file mode 100644 index 0000000..ca95e44 --- /dev/null +++ b/docs/source-app/workflows/add_components/index.rst @@ -0,0 +1,31 @@ +:orphan: + +########################### +Add a component to your app +########################### +**Audience:** Users looking to expand the functionality of their Lightning apps. + +---- + +******************* +Install a component +******************* + +Any Lightning component can be installed with: + +.. code:: python + + lightning install component org/the-component-name + +`Browse all community-built components here `_. + +.. note:: Components are being populated daily + +---- + +********************** +Contribute a component +********************** +One of the first principles of the Lightning community is to code something *once* for the benefit or everyone! + +To contribute a component, `follow this guide <../build_lightning_component/index.html>`_. diff --git a/docs/source-app/workflows/add_server/any_server.rst b/docs/source-app/workflows/add_server/any_server.rst new file mode 100644 index 0000000..2e26b88 --- /dev/null +++ b/docs/source-app/workflows/add_server/any_server.rst @@ -0,0 +1,185 @@ +######################### +Enable any server (basic) +######################### +**Audience:** Users who want to enable an arbitrary server/UI. + +**Prereqs:** Basic python knowledge. + +---- + +***************** +What is a server? +***************** +A server is a program that enables other programs or users to connect to it. As long as your server can listen on a port, +you can enable it with a Lightning App. + +---- + +*************************** +Add a server to a component +*************************** +Any server that listens on a port, can be enabled via a work. For example, here's a plain python server: + +.. code:: python + :emphasize-lines: 11-12 + + import socketserver + from http import HTTPStatus, server + + + class PlainServer(server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(HTTPStatus.OK) + self.end_headers() + # Data must be passed as bytes to the `self.wfile.write` call + html = b"

Hello lit world

" + self.wfile.write(html) + + + httpd = socketserver.TCPServer(("localhost", "3000"), PlainServer) + httpd.serve_forever() + +To enable the server inside the component, start the server in the run method and use the ``self.host`` and ``self.port`` properties: + +.. code:: python + :emphasize-lines: 14-15 + + import lightning as L + import socketserver + from http import HTTPStatus, server + + + class PlainServer(server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(HTTPStatus.OK) + self.end_headers() + # Data must be passed as bytes to the `self.wfile.write` call + html = b"

Hello lit world

" + self.wfile.write(html) + + + class LitServer(L.LightningWork): + def run(self): + httpd = socketserver.TCPServer((self.host, self.port), PlainServer) + httpd.serve_forever() + +---- + +************************************** +Route the server in the root component +************************************** +The final step, is to tell the Root component in which tab to render this component's output: +In this case, we render the ``LitServer`` output in the ``home`` tab of the application. + +.. code:: python + :emphasize-lines: 20, 23, 28 + + import lightning as L + import socketserver + from http import HTTPStatus, server + + + class PlainServer(server.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(HTTPStatus.OK) + self.end_headers() + # Data must be passed as bytes to the `self.wfile.write` call + html = b"

Hello lit world

" + self.wfile.write(html) + + + class LitServer(L.LightningWork): + def run(self): + httpd = socketserver.TCPServer((self.host, self.port), PlainServer) + httpd.serve_forever() + + + class Root(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_server = LitServer(parallel=True) + + def run(self): + self.lit_server.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_server} + return tab1 + + + app = L.LightningApp(Root()) + +We use the ``parallel=True`` argument of ``LightningWork`` to run the server in parallel +while the rest of the Lightning App runs everything else. + +---- + +*********** +Run the app +*********** +Start the app to see your new UI! + +.. code:: bash + + lightning run app app.py + +To run the app on the cloud, use the ``--cloud`` argument. + +.. code:: bash + + lightning run app app.py --cloud + +---- + +***************************************** +Interact with a component from the server +***************************************** +TODO: how do we do this? + + +---- + +***************************************** +Interact with the server from a component +***************************************** +TODO: how do we do this? + +---- + +******** +Examples +******** +Here are a few example apps that expose a server via a component: + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example: Tensorboard + :description: TODO + :col_css: col-md-4 + :button_link: example_app.html + :height: 150 + +.. displayitem:: + :header: Example: Streamlit + :description: TODO + :col_css: col-md-4 + :button_link: example_app.html + :height: 150 + +.. displayitem:: + :header: Example: React + :description: TODO + :col_css: col-md-4 + :button_link: example_app.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_server/flask_basic.rst b/docs/source-app/workflows/add_server/flask_basic.rst new file mode 100644 index 0000000..38ca282 --- /dev/null +++ b/docs/source-app/workflows/add_server/flask_basic.rst @@ -0,0 +1,155 @@ +############################### +Add a web UI with Flask (basic) +############################### +**Audience:** Users who want to enable a flask app within a component. + +**Prereqs:** Basic python knowledge. + +---- + +************** +What is Flask? +************** +Flask is a web framework, that lets you develop web applications in Python easily. + +---- + +************************ +Add Flask to a component +************************ +First, define your flask app as you normally would without Lightning: + +.. code:: python + :emphasize-lines: 9 + + from flask import Flask + + flask_app = Flask(__name__) + + + @flask_app.route("/") + def hello(): + return "Hello, World!" + + + flask_app.run(host="0.0.0.0", port=80) + +To enable the server inside the component, start the Flask server in the run method and use the ``self.host`` and ``self.port`` properties: + +.. code:: python + :emphasize-lines: 12 + + import lightning as L + from flask import Flask + + + class LitFlask(L.LightningWork): + def run(self): + flask_app = Flask(__name__) + + @flask_app.route("/") + def hello(): + return "Hello, World!" + + flask_app.run(host=self.host, port=self.port) + +---- + +************************************** +Route the server in the root component +************************************** +The final step, is to tell the Root component in which tab to render this component's output: +In this case, we render the ``LitFlask`` output in the ``home`` tab of the application. + +.. code:: python + :emphasize-lines: 17, 23 + + import lightning as L + from flask import Flask + + + class LitFlask(L.LightningWork): + def run(self): + flask_app = Flask(__name__) + + @flask_app.route("/") + def hello(): + return "Hello, World!" + + flask_app.run(host=self.host, port=self.port) + + + class Root(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_flask = LitFlask(parallel=True) + + def run(self): + self.lit_flask.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_flask} + return tab1 + + + app = L.LightningApp(Root()) + +We use the ``parallel=True`` argument of ``LightningWork`` to run the server in the background +while the rest of the Lightning App runs everything else. + +---- + +*********** +Run the app +*********** +Start the app to see your new UI! + +.. code:: bash + + lightning run app app.py + +To run the app on the cloud, use the ``--cloud`` argument. + +.. code:: bash + + lightning run app app.py --cloud + +---- + +******** +Examples +******** +Here are a few example apps that expose a Flask server via a component: + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example 1 + :description: TODO + :col_css: col-md-4 + :button_link: example_app.html + :height: 150 + +.. displayitem:: + :header: Example 2 + :description: TODO + :col_css: col-md-4 + :button_link: example_app.html + :height: 150 + +.. displayitem:: + :header: Example 3 + :description: TODO + :col_css: col-md-4 + :button_link: example_app.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_server/index.rst b/docs/source-app/workflows/add_server/index.rst new file mode 100644 index 0000000..1429b08 --- /dev/null +++ b/docs/source-app/workflows/add_server/index.rst @@ -0,0 +1,8 @@ +################################### +Run a server within a Lightning App +################################### +Any type of server can run inside a Lightning App. + +---- + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/add_server/index_content.rst b/docs/source-app/workflows/add_server/index_content.rst new file mode 100644 index 0000000..d362369 --- /dev/null +++ b/docs/source-app/workflows/add_server/index_content.rst @@ -0,0 +1,35 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + any_server + flask_basic + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Any server + :description: Learn how to enable any server inside a Lightning App. + :col_css: col-md-6 + :button_link: any_server.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Flask + :description: Learn how to add a Flask server inside a Lightning App. + :col_css: col-md-6 + :button_link: flask_basic.html + :height: 150 + :tag: basic + + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_link.rst b/docs/source-app/workflows/add_web_link.rst new file mode 100644 index 0000000..01ffdf6 --- /dev/null +++ b/docs/source-app/workflows/add_web_link.rst @@ -0,0 +1,54 @@ +############## +Add a web link +############## +**Audience:** Users who want to link to other pages from their app. + +---- + +************** +Add a url link +************** +In this example we'll replicate |urls_link|. + +To add a url link to an app, simply specify it in the ``configure_layout`` method +and connect the UIs. Create a file named **app.py** with this code: + +.. |urls_link| raw:: html + + the app running here + +.. code:: python + :emphasize-lines: 7,11 + + import lightning as L + + class LitApp(L.LightningFlow): + def configure_layout(self): + tab_1 = { + "name": "Logger", + "content": "https://bit.ly/tb-aasae" + } + tab_2 = { + "name": "Paper", + "content": "https://arxiv.org/pdf/2107.12329.pdf" + } + return tab_1, tab_2 + + app = L.LightningApp(LitApp()) + +---- + +*********** +Run the app +*********** +Run the app locally to see it! + +.. code:: python + + lightning run app app.py + +Now run it on the cloud as well: + +.. code:: python + + lightning run app app.py --cloud diff --git a/docs/source-app/workflows/add_web_ui/angular_js_intermediate.rst b/docs/source-app/workflows/add_web_ui/angular_js_intermediate.rst new file mode 100644 index 0000000..095dee3 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/angular_js_intermediate.rst @@ -0,0 +1,6 @@ +:orphan: + +########################################### +Add a web UI with Angular.js (intermediate) +########################################### +coming... diff --git a/docs/source-app/workflows/add_web_ui/dash/basic.rst b/docs/source-app/workflows/add_web_ui/dash/basic.rst new file mode 100644 index 0000000..4316fc1 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/dash/basic.rst @@ -0,0 +1,221 @@ +############################## +Add a web UI with Dash (basic) +############################## +**Audience:** Users who want to add a web UI with Dash by Plotly. + +**Prereqs:** Basic python knowledge. + +---- + +************* +What is Dash? +************* +`Dash `_ is the original low-code framework for rapidly building data apps in Python, R, Julia, and F# (experimental). + +Install Dash with: + +.. code:: bash + + pip install dash + +---- + +************************ +Create the dash demo app +************************ + +To explain how to use Dash with Lightning, let's build a simple app with Dash. + + +.. + To explain how to use Dash with Lightning, let's replicate the |dash_link|. + + .. |dash_link| raw:: html + + example running here + +In the next few sections we'll build an app step-by-step. +First **create a file named app.py** with the app content: + +.. code:: bash + + import lightning as L + import dash + import plotly.express as px + + class LitDash(L.LightningWork): + def run(self): + dash_app = dash.Dash(__name__) + X = [1, 2, 3, 4, 5, 6] + Y = [2, 4, 8, 16, 32, 64] + fig = px.line(x=X, y=Y) + + dash_app.layout = dash.html.Div(children=[ + dash.html.H1(children='⚡ Hello Dash + Lightning⚡'), + dash.html.Div(children='The Dash framework running inside a ⚡ Lightning App'), + dash.dcc.Graph(id='example-graph', figure=fig) + ]) + + dash_app.run_server(host=self.host, port=self.port) + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_dash = LitDash(parallel=True) + + def run(self): + self.lit_dash.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_dash} + return tab1 + + app = L.LightningApp(LitApp()) + + +add 'dash' to a requirements.txt file: + +.. code:: bash + + echo "dash" >> requirements.txt + +this is a best practice to make apps reproducible. + +---- + +*********** +Run the app +*********** +Run the app locally to see it! + +.. code:: python + + lightning run app app.py + +Now run it on the cloud as well: + +.. code:: python + + lightning run app app.py --cloud + +---- + +************************ +Step-by-step walkthrough +************************ +In this section, we explain each part of this code in detail. + +---- + +0. Define a Dash app +^^^^^^^^^^^^^^^^^^^^ +First, find the dash app you want to integrate. In this example, that app looks like: + +.. code:: python + + import dash + import plotly.express as px + + dash_app = dash.Dash(__name__) + X = [1, 2, 3, 4, 5, 6] + Y = [2, 4, 8, 16, 32, 64] + fig = px.line(x=X, y=Y) + + dash_app.layout = dash.html.Div(children=[ + dash.html.H1(children='⚡ Hello Dash + Lightning⚡'), + dash.html.Div(children='The Dash framework running inside a ⚡ Lightning App'), + dash.dcc.Graph(id='example-graph', figure=fig) + ]) + + dash_app.run_server(host='0.0.0.0', port=80) + +This dash app plots a simple line curve along with some HTMlapp. +`Visit the Dash documentation for the full API `_. + +---- + +1. Add Dash to a component +^^^^^^^^^^^^^^^^^^^^^^^^^^ +Add the dash app to the run method of a ``LightningWork`` component and run the server on that component's **host** and **port**: + +.. code:: python + :emphasize-lines: 6, 18 + + import lightning as L + import dash + import plotly.express as px + + class LitDash(L.LightningWork): + def run(self): + dash_app = dash.Dash(__name__) + X = [1, 2, 3, 4, 5, 6] + Y = [2, 4, 8, 16, 32, 64] + fig = px.line(x=X, y=Y) + + dash_app.layout = dash.html.Div(children=[ + dash.html.H1(children='⚡ Hello Dash + Lightning⚡'), + dash.html.Div(children='The Dash framework running inside a ⚡ Lightning App'), + dash.dcc.Graph(id='example-graph', figure=fig) + ]) + + dash_app.run_server(host=self.host, port=self.port) + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_dash = LitDash(parallel=True) + + def run(self): + self.lit_dash.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_dash} + return tab1 + + app = L.LightningApp(LitApp()) + +---- + +2. Route the UI in the root component +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The final step, is to tell the Root component in which tab to render this component's UI. +In this case, we render the ``LitDash`` UI in the ``home`` tab of the application. + +.. code:: python + :emphasize-lines: 23, 29 + + import lightning as L + import dash + import plotly.express as px + + class LitDash(L.LightningWork): + def run(self): + dash_app = dash.Dash(__name__) + X = [1, 2, 3, 4, 5, 6] + Y = [2, 4, 8, 16, 32, 64] + fig = px.line(x=X, y=Y) + + dash_app.layout = dash.html.Div(children=[ + dash.html.H1(children='⚡ Hello Dash + Lightning⚡'), + dash.html.Div(children='The Dash framework running inside a ⚡ Lightning App'), + dash.dcc.Graph(id='example-graph', figure=fig) + ]) + + dash_app.run_server(host=self.host, port=self.port) + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_dash = LitDash(parallel=True) + + def run(self): + self.lit_dash.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_dash} + return tab1 + + app = L.LightningApp(LitApp()) + +We use the ``parallel=True`` argument of ``LightningWork`` to run the server in the background +while the rest of the Lightning App runs everything else. diff --git a/docs/source-app/workflows/add_web_ui/dash/index.rst b/docs/source-app/workflows/add_web_ui/dash/index.rst new file mode 100644 index 0000000..5abb444 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/dash/index.rst @@ -0,0 +1,84 @@ +:orphan: + +.. toctree:: + :maxdepth: 1 + :hidden: + + basic + intermediate + +###################### +Add a web UI with Dash +###################### + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1: Connect Dash + :description: Learn how to connect a Dash app. + :col_css: col-md-6 + :button_link: basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: 2: Enable two-way communication + :description: Enable two-way communication between the dash app and a Lightning App. + :col_css: col-md-6 + :button_link: intermediate.html + :height: 150 + :tag: [docs coming soon] + +.. raw:: html + +
+
+ +---- + +******** +Examples +******** +Here are a few example apps that use a Dash web UI. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example 1 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 2 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 3 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/dash/intermediate.rst b/docs/source-app/workflows/add_web_ui/dash/intermediate.rst new file mode 100644 index 0000000..c13b34b --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/dash/intermediate.rst @@ -0,0 +1,42 @@ +##################################### +Add a web UI with Dash (intermediate) +##################################### +**Audience:** Users who want to communicate between the Lightning App and Dash. + +**Prereqs:** Must have read the `dash basic `_ guide. + +---- + +******************************* +Interact with the App from Dash +******************************* + +In the example below, every time you change the select year on the dashboard, this is directly communicated to the flow +and another work process the associated data frame with the provided year. + +.. literalinclude:: intermediate_plot.py + +Here is how the app looks like once running: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/dash_plot.gif + +---- + +*********************************** +Interact with Dash from a component +*********************************** + +In the example below, when you click the toggle, the state of the work appears. + +Install the following libraries if you want to run the app. + +```bash +pip install dash_daq dash_renderjson +``` + +.. literalinclude:: intermediate_state.py + + +Here is how the app looks like once running: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/dash_state.gif diff --git a/docs/source-app/workflows/add_web_ui/dash/intermediate_plot.py b/docs/source-app/workflows/add_web_ui/dash/intermediate_plot.py new file mode 100644 index 0000000..4d8733b --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/dash/intermediate_plot.py @@ -0,0 +1,86 @@ +from typing import Optional + +import pandas as pd +import plotly.express as px +from dash import Dash, dcc, html, Input, Output + +import lightning as L +from lightning.app.storage import Payload + + +class LitDash(L.LightningWork): + def __init__(self): + super().__init__(parallel=True) + self.df = None + self.selected_year = None + + def run(self): + df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv") + self.df = Payload(df) + + dash_app = Dash(__name__) + + dash_app.layout = html.Div( + [ + dcc.Graph(id="graph-with-slider"), + dcc.Slider( + df["year"].min(), + df["year"].max(), + step=None, + value=df["year"].min(), + marks={str(year): str(year) for year in df["year"].unique()}, + id="year-slider", + ), + ] + ) + + @dash_app.callback(Output("graph-with-slider", "figure"), Input("year-slider", "value")) + def update_figure(selected_year): + self.selected_year = selected_year + filtered_df = df[df.year == selected_year] + + fig = px.scatter( + filtered_df, + x="gdpPercap", + y="lifeExp", + size="pop", + color="continent", + hover_name="country", + log_x=True, + size_max=55, + ) + + fig.update_layout(transition_duration=500) + + return fig + + dash_app.run_server(host=self.host, port=self.port) + + +class Processor(L.LightningWork): + def run(self, df: Payload, selected_year: Optional[str]): + if selected_year: + df = df.value + filtered_df = df[df.year == selected_year] + print(f"[PROCESSOR|selected_year={selected_year}]") + print(filtered_df) + + +class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_dash = LitDash() + self.processor = Processor(parallel=True) + + def run(self): + self.lit_dash.run() + + # Launch some processing based on the Dash Dashboard. + self.processor.run(self.lit_dash.df, self.lit_dash.selected_year) + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_dash} + return tab1 + + +app = L.LightningApp(LitApp()) diff --git a/docs/source-app/workflows/add_web_ui/dash/intermediate_state.py b/docs/source-app/workflows/add_web_ui/dash/intermediate_state.py new file mode 100644 index 0000000..d2e37a5 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/dash/intermediate_state.py @@ -0,0 +1,39 @@ +import dash +import dash_daq as daq +import dash_renderjson +from dash import html, Input, Output + +import lightning as L +from lightning.app.utilities.state import AppState + + +class LitDash(L.LightningWork): + def run(self): + dash_app = dash.Dash(__name__) + + dash_app.layout = html.Div([daq.ToggleSwitch(id="my-toggle-switch", value=False), html.Div(id="output")]) + + @dash_app.callback(Output("output", "children"), [Input("my-toggle-switch", "value")]) + def display_output(value): + if value: + state = AppState() + state._request_state() + return dash_renderjson.DashRenderjson(id="input", data=state._state, max_depth=-1, invert_theme=True) + + dash_app.run_server(host=self.host, port=self.port) + + +class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_dash = LitDash(parallel=True) + + def run(self): + self.lit_dash.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_dash} + return tab1 + + +app = L.LightningApp(LitApp()) diff --git a/docs/source-app/workflows/add_web_ui/example_app.rst b/docs/source-app/workflows/add_web_ui/example_app.rst new file mode 100644 index 0000000..e5d2cbb --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/example_app.rst @@ -0,0 +1,7 @@ +:orphan: + +########### +Example App +########### + +This is an example app that needs to be built for this part of the docs. diff --git a/docs/source-app/workflows/add_web_ui/glossary_front_end.rst b/docs/source-app/workflows/add_web_ui/glossary_front_end.rst new file mode 100644 index 0000000..ce51ef1 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/glossary_front_end.rst @@ -0,0 +1,9 @@ +######## +Frontend +######## +Web pages visible to users are also known as **front-ends**. Lightning Apps can have multiple +types of Frontends. + +---- + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/add_web_ui/glossary_ui.rst b/docs/source-app/workflows/add_web_ui/glossary_ui.rst new file mode 100644 index 0000000..bc9e4f5 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/glossary_ui.rst @@ -0,0 +1,9 @@ +################### +UI (User Interface) +################### +We use (UI) as short for a **web page** with interactions. Lightning Apps can have multiple +types of UIs. + +---- + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/add_web_ui/gradio/basic.rst b/docs/source-app/workflows/add_web_ui/gradio/basic.rst new file mode 100644 index 0000000..4f5ab87 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/gradio/basic.rst @@ -0,0 +1,217 @@ +################################ +Add a web UI with Gradio (basic) +################################ +**Audience:** Users who want to add a web UI written with Python. + +**Prereqs:** Basic python knowledge. + +---- + +*************** +What is Gradio? +*************** +Gradio is a Python library that automatically generates a web interface to demo a machine learning model. + +---- + +***************** +Install gradio +***************** +First, install gradio. + +.. code:: bash + + pip install gradio + +---- + +************************** +Create the gradio demo app +************************** +To explain how to use Gradio with Lightning, let's replicate the |gradio_link|. + +.. |gradio_link| raw:: html + + example running here + +In the next few sections we'll build an app step-by-step. +First **create a file named app.py** with the app content: + +.. code:: python + + import lightning as L + from lightning.app.components import ServeGradio + import gradio as gr + + class LitGradio(ServeGradio): + + inputs = gr.inputs.Textbox(default='lightning', label='name input') + outputs = gr.outputs.Textbox(label='output') + examples = [["hello lightning"]] + + def predict(self, input_text): + return self.model(input_text) + + def build_model(self): + fake_model = lambda x: f"hello {x}" + return fake_model + + class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_gradio = LitGradio() + + def run(self): + self.lit_gradio.run() + + def configure_layout(self): + return [{"name": "home", "content": self.lit_gradio}] + + app = L.LightningApp(RootFlow()) + +add "gradio" to a requirements.txt file: + +.. code:: bash + + echo 'gradio' >> requirements.txt + +this is a best practice to make apps reproducible. + +---- + +*********** +Run the app +*********** +Run the app locally to see it! + +.. code:: python + + lightning run app app.py + +Now run it on the cloud as well: + +.. code:: python + + lightning run app app.py --cloud + +---- + +************************ +Step-by-step walkthrough +************************ +In this section, we explain each part of this code in detail. + +---- + +Create a Gradio component +^^^^^^^^^^^^^^^^^^^^^^^^^ +To create a Gradio component, simply take any Gradio app and subclass it from ``ServeGradio``. +If you haven't created a Gradio demo, you have to implement the following elements: + +1. Input which is text. +2. Output which is text. +3. A build_model function. +4. A predict function. + +| + +Here's an example: + +.. code:: python + :emphasize-lines: 4 + + from lightning.app.components import ServeGradio + import gradio as gr + + class LitGradio(ServeGradio): + + inputs = gr.inputs.Textbox(default='lightning', label='name input') + outputs = gr.outputs.Textbox(label='output') + + def predict(self, input_text): + return self.model(input_text) + + def build_model(self): + fake_model = lambda x: f"hello {x}" + return fake_model + +This fake model simply concatenates 2 strings. + +---- + +Route the UI in the root component +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Now, tell the Root component in which tab to render this component's UI. +In this case, we render the ``LitGradio`` UI in the ``home`` tab of the application. + +.. code:: python + :emphasize-lines: 21, 27 + + import lightning as L + from lightning.app.components import ServeGradio + import gradio as gr + + class LitGradio(ServeGradio): + + inputs = gr.inputs.Textbox(default='lightning', label='name input') + outputs = gr.outputs.Textbox(label='output') + examples = [["hello lightning"]] + + def predict(self, input_text): + return self.model(input_text) + + def build_model(self): + fake_model = lambda x: f"hello {x}" + return fake_model + + class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_gradio = LitGradio() + + def run(self): + self.lit_gradio.run() + + def configure_layout(self): + return [{"name": "home", "content": self.lit_gradio}] + + app = L.LightningApp(RootFlow()) + +---- + +Call run +^^^^^^^^ +Finally, don't forget to call run inside the Root Flow to serve the Gradio app. + +.. code:: python + :emphasize-lines: 24 + + import lightning as L + from lightning.app.components import ServeGradio + import gradio as gr + + class LitGradio(ServeGradio): + + inputs = gr.inputs.Textbox(default='lightning', label='name input') + outputs = gr.outputs.Textbox(label='output') + examples = [["hello lightning"]] + + def predict(self, input_text): + return self.model(input_text) + + def build_model(self): + fake_model = lambda x: f"hello {x}" + return fake_model + + class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_gradio = LitGradio() + + def run(self): + self.lit_gradio.run() + + def configure_layout(self): + return [{"name": "home", "content": self.lit_gradio}] + + app = L.LightningApp(RootFlow()) diff --git a/docs/source-app/workflows/add_web_ui/gradio/index.rst b/docs/source-app/workflows/add_web_ui/gradio/index.rst new file mode 100644 index 0000000..740ae93 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/gradio/index.rst @@ -0,0 +1,84 @@ +:orphan: + +.. toctree:: + :maxdepth: 1 + :hidden: + + basic + intermediate + +######################## +Add a web UI with Gradio +######################## + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1: Connect Gradio + :description: Learn how to connect Gradio to a Lightning Component. + :col_css: col-md-6 + :button_link: basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: 2: Enable two-way communication + :description: Enable two-way communication between Gradio and a Lightning App. + :col_css: col-md-6 + :button_link: intermediate.html + :height: 150 + :tag: [documentation coming soon] + +.. raw:: html + +
+
+ +---- + +******** +Examples +******** +Here are a few example apps that use a Gradio web UI. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example 1 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 2 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 3 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/gradio/intermediate.rst b/docs/source-app/workflows/add_web_ui/gradio/intermediate.rst new file mode 100644 index 0000000..bb20d56 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/gradio/intermediate.rst @@ -0,0 +1,21 @@ +####################################### +Add a web UI with Gradio (intermediate) +####################################### + +.. note:: documentation coming soon. + + +************************************* +Interact with a component from the UI +************************************* + +.. warning:: is there such a thing for this with gradio? + + +---- + +************************************* +Interact with the UI from a component +************************************* + +.. warning:: is there such a thing for this with gradio? diff --git a/docs/source-app/workflows/add_web_ui/html/basic.rst b/docs/source-app/workflows/add_web_ui/html/basic.rst new file mode 100644 index 0000000..cb9bb52 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/html/basic.rst @@ -0,0 +1,166 @@ +############################## +Add a web UI with HTML (basic) +############################## +**Audience:** Users who want to add a web UI written in HTMlapp. + +**Prereqs:** Basic html knowledge. + +---- + +************* +What is HTML? +************* +HyperText Markup Language (HTML) is the Language used to create web pages. Use HTML for simple +web user interfaces that tend to be more static. + +For reactive web applications, we recommend using: React.js, Angular.js or Vue.js + +---- + +******************* +Create an HTML page +******************* +The first step is to create an HTML file named **index.html**: + +.. code:: html + + + + + + +

Hello World

+ + + +---- + +************************ +Create the HTML demo app +************************ + +.. + To explain how to use html with Lightning, let's replicate the |html_app_link|. + + .. |html_app_link| raw:: html + + example running here + +In the next few sections we'll build an app step-by-step. +First **create a file named app.py** with the app content (in the same folder as index.html): + +.. code:: bash + + # app.py + import lightning as L + import lightning.app.frontend as frontend + + + class HelloComponent(L.LightningFlow): + def configure_layout(self): + return frontend.StaticWebFrontend(serve_dir='.') + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.hello_component = HelloComponent() + + def run(self): + self.hello_component.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.hello_component} + return tab1 + + + app = L.LightningApp(LitApp()) + +---- + +*********** +Run the app +*********** +Run the app locally to see it! + +.. code:: python + + lightning run app app.py + +Now run it on the cloud as well: + +.. code:: python + + lightning run app app.py --cloud + +---- + +************************ +Step-by-step walkthrough +************************ +In this section, we explain each part of this code in detail. + +---- + +Enable an HTML UI for the component +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Give the component an HTML UI, by returning a ``StaticWebFrontend`` object from the ``configure_layout`` method: + +.. code:: bash + :emphasize-lines: 6,7 + + # app.py + import lightning as L + import lightning.app.frontend as frontend + + class HelloComponent(L.LightningFlow): + def configure_layout(self): + return frontend.StaticWebFrontend(serve_dir='.') + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.hello_component = HelloComponent() + + def run(self): + self.hello_component.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.hello_component} + return tab1 + + app = L.LightningApp(LitApp()) + +The folder path given in ``StaticWebFrontend(serve_dir=)`` must point to a folder with an ``index.html`` page. + +---- + +Route the UI in the root component +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The final step, is to tell the Root component in which tab to render this component's UI. +In this case, we render the ``HelloComponent`` UI in the ``home`` tab of the application. + +.. code:: python + :emphasize-lines: 18, 19 + + # app.py + import lightning as L + import lightning.app.frontend as frontend + + class HelloComponent(L.LightningFlow): + def configure_layout(self): + return frontend.StaticWebFrontend(serve_dir='.') + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.hello_component = HelloComponent() + + def run(self): + self.hello_component.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.hello_component} + return tab1 + + app = L.LightningApp(LitApp()) diff --git a/docs/source-app/workflows/add_web_ui/html/index.rst b/docs/source-app/workflows/add_web_ui/html/index.rst new file mode 100644 index 0000000..0eae930 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/html/index.rst @@ -0,0 +1,87 @@ +:orphan: + +.. toctree:: + :maxdepth: 1 + :hidden: + + basic + intermediate + +###################### +Add a web UI with HTML +###################### +**Audience:** Users who want to add a web UI using plain html. + +**Prereqs:** Basic html knowledge. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1: Connect HTML + :description: Learn how to connect an HTML app. + :col_css: col-md-6 + :button_link: basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: 2: Enable two-way communication + :description: Enable two-way communication between HTML and a Lightning App. + :col_css: col-md-6 + :button_link: intermediate.html + :height: 150 + :tag: [docs coming soon] + +.. raw:: html + +
+
+ +---- + +******** +Examples +******** +Here are a few example apps that use an HTML web UI. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example 1 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 2 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 3 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/html/intermediate.rst b/docs/source-app/workflows/add_web_ui/html/intermediate.rst new file mode 100644 index 0000000..e2d0f96 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/html/intermediate.rst @@ -0,0 +1,20 @@ +##################################### +Add a web UI with HTML (intermediate) +##################################### +**Audience:** Users who want to add a web UI using plain html. + +**Prereqs:** Must have read the `html basic `_ guide. + +---- + +******************************* +Interact with the App from HTML +******************************* +.. note:: documentation in progress + +---- + +*********************************** +Interact with HTML from a component +*********************************** +.. note:: documentation in progress diff --git a/docs/source-app/workflows/add_web_ui/index.rst b/docs/source-app/workflows/add_web_ui/index.rst new file mode 100644 index 0000000..79c0f16 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/index.rst @@ -0,0 +1,10 @@ + +############################# +Add a web user interface (UI) +############################# + +**Audience:** Users who want to add a UI to their Lightning Apps + +---- + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/add_web_ui/index_content.rst b/docs/source-app/workflows/add_web_ui/index_content.rst new file mode 100644 index 0000000..f3d516c --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/index_content.rst @@ -0,0 +1,121 @@ +************************************* +Web UIs for non Javascript Developers +************************************* + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Dash + :description: Learn how to add a web UI built in Python with Dash. + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/dash/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Gradio + :description: Learn how to add a web UI built in Python with Gradio. + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/gradio/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Panel + :description: Learn how to add a web UI built in Python with Panel. + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/panel/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Jupyter Notebook + :description: Learn how to enable a web UI that is a Jupyter Notebook. + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/jupyter_basic.html + :height: 150 + :tag: [docs coming soon] + +.. displayitem:: + :header: Streamlit + :description: Learn how to add a web UI built in Python with Streamlit. + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/streamlit/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: JustPy + :description: Learn how to add a web UI built in Python with JustPy. + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/justpy/index.html + :height: 150 + :tag: basic + +.. raw:: html + +
+
+ +---- + +********************************* +Web UIs for Javascript Developers +********************************* + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Any javascript framework + :description: Learn how to link up any javascript framework to a Lightning app. + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/integrate_any_javascript_framework.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Angular.js + :description: Learn how to add a web UI built in Javascript with Angular.js + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/angular_js_intermediate.html + :height: 150 + :tag: [Docs coming soon] + +.. displayitem:: + :header: HTML + :description: Learn how to add a web UI built with html. + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/html/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: React.js + :description: Learn how to add a web UI built in Javascript with React.js + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/react/index.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Vue.js + :description: Learn how to add a web UI built in Javascript with Vue.js + :col_css: col-md-4 + :button_link: ../../workflows/add_web_ui/vue_js_intermediate.html + :height: 150 + :tag: [Docs coming soon] + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/integrate_any_javascript_framework.rst b/docs/source-app/workflows/add_web_ui/integrate_any_javascript_framework.rst new file mode 100644 index 0000000..f1da660 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/integrate_any_javascript_framework.rst @@ -0,0 +1,164 @@ +:orphan: + +################################## +Integrate any javascript framework +################################## +**Audience:** Advanced web developers with complex apps that may not have been covered by the other tutorials + +**Pre-requisites:** Intermediate knowledge of html and javascript + +---- + +************************ +Import LightningState.js +************************ +To connect any javascript framework, import the `LightningState.js `_ library. +LightningState.js enables two-way communication between a javascript framework and a Lightning app. + +To import this library, add this to your html: + +.. code:: html + + + +Once it's imported, use it inside your app, this example uses it inside a React App: + +.. code-block:: + :emphasize-lines: 1, 5 + + import { useLightningState } from "./hooks/useLightningState"; + import cloneDeep from "lodash/cloneDeep"; + + function App() { + const { lightningState, updateLightningState } = useLightningState(); + + const modify_and_send_back_the_state = async (event: ChangeEvent) => { + if (lightningState) { + const newLightningState = cloneDeep(lightningState); + // Update the state and send it back. + newLightningState.flows.counter += 1 + + updateLightningState(newLightningState); + } + }; + + return ( +
+
+ ); + } + + export default App; + +---- + +************************ +Update the Lightning app +************************ +Use `updateLightningState` to update the lightning app. Here we update a vairable called counter. + +.. code-block:: + :emphasize-lines: 11 + + import { useLightningState } from "./hooks/useLightningState"; + import cloneDeep from "lodash/cloneDeep"; + + function App() { + const { lightningState, updateLightningState } = useLightningState(); + + const modify_and_send_back_the_state = async (event: ChangeEvent) => { + if (lightningState) { + const newLightningState = cloneDeep(lightningState); + // Update the state and send it back. + newLightningState.flows.counter += 1 + + updateLightningState(newLightningState); + } + }; + + return ( +
+
+ ); + } + + export default App; + +---- + +************************************** +Receive updates from the Lightning app +************************************** +Whenever a variable in the Lightning app changes, the javascript app will receive those values via `lightningState`. + +Extract any variable from the state and update the javascript app: + +.. code-block:: + :emphasize-lines: 5 + + import { useLightningState } from "./hooks/useLightningState"; + import cloneDeep from "lodash/cloneDeep"; + + function App() { + const { lightningState, updateLightningState } = useLightningState(); + + const modify_and_send_back_the_state = async (event: ChangeEvent) => { + if (lightningState) { + const newLightningState = cloneDeep(lightningState); + // Update the state and send it back. + newLightningState.flows.counter += 1 + + updateLightningState(newLightningState); + } + }; + + return ( +
+
+ ); + } + + export default App; + +---- + +******** +Examples +******** + +See this in action in these examples: + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: React.js + :description: Explore how React.js uses lightningState.js + :col_css: col-md-4 + :button_link: react/communicate_between_react_and_lightning.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Example 2 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 3 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :height: 150 + :tag: Waiting for contributed example + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/jupyter_basic.rst b/docs/source-app/workflows/add_web_ui/jupyter_basic.rst new file mode 100644 index 0000000..61f58ab --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/jupyter_basic.rst @@ -0,0 +1,70 @@ +:orphan: + +##################################### +Add a Jupyter Notebook web UI (basic) +##################################### +**Audience:** Users who want to enable a Jupyter notebook UI. + +**Prereqs:** Basic python knowledge. + +TODO + +---- + +*************************** +What is a Jupyter Notebook? +*************************** + +TODO + +---- + +******************* +Install Jupyter Lab +******************* + +First, install Jupyter Lab. + +.. code:: bash + + pip install jupyterlab + +---- + +******** +Examples +******** +Here are a few example apps that use Jupyter Lab. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example 1 + :description: TODO + :col_css: col-md-4 + :button_link: angular_js_intermediate.html + :height: 150 + +.. displayitem:: + :header: Example 2 + :description: TODO + :col_css: col-md-4 + :button_link: angular_js_intermediate.html + :height: 150 + +.. displayitem:: + :header: Example 3 + :description: TODO + :col_css: col-md-4 + :button_link: angular_js_intermediate.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/justpy/index.rst b/docs/source-app/workflows/add_web_ui/justpy/index.rst new file mode 100644 index 0000000..bf25a18 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/justpy/index.rst @@ -0,0 +1,92 @@ +:orphan: + +######################## +Add a web UI with JustPy +######################## + + +****** +JustPy +****** + +The `JustPy `_ framework is an object oriented high-level Python Web Framework that requires no JavaScript programming, while at the same time providing the full flexibility of a frontend framework. + +Additionally, it provides a higher level API called `Quasar `_ with stylized components. + + +You can install ``justpy`` from PyPi. + +.. code-block:: + + pip install justpy + +******* +Example +******* + + +In the following example, we are creating a simple UI with 2 buttons. +When clicking the first button, the flow state ``counter`` is incremented and re-rendered on the UI. + + +First of all, you would need to import the ``JustPyFrontend`` and return it from the ``configure_layout`` hook of the flow. + +.. code-block:: + + from typing import Callable + + from lightning import LightningApp, LightningFlow + from lightning.app.frontend import JustPyFrontend + + + class Flow(LightningFlow): + def __init__(self): + super().__init__() + self.counter = 0 + + def run(self): + print(self.counter) + + def configure_layout(self): + return JustPyFrontend(render_fn=render_fn) + +Secondly, you would need to implement a ``render_fn`` that takes as input a ``get_state`` function and return a function. + + +.. code-block:: + + def render_fn(get_state: Callable) -> Callable: + import justpy as jp + + def webpage(): + wp = jp.QuasarPage(dark=True) + # the `a=wp` argument adds the div to the web page + d = jp.Div(classes="q-pa-md q-gutter-sm", a=wp) + container = jp.QBtn(color="primary", text="Counter: 0") + + async def click(*_): + state = get_state() + state.counter += 1 + container.text = f"Counter: {state.counter}" + + button = jp.QBtn(color="primary", text="Click Me!", click=click) + + d.add(button) + d.add(container) + + return wp + + return webpage + + +Finally, you can wrap your flow in a LightningAp. + +.. code-block:: + + app = LightningApp(Flow()) + +Now, you can run the Lightning App with: + +.. code-block:: + + lightning run app app.py diff --git a/docs/source-app/workflows/add_web_ui/panel/basic.rst b/docs/source-app/workflows/add_web_ui/panel/basic.rst new file mode 100644 index 0000000..8e72036 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/panel/basic.rst @@ -0,0 +1,369 @@ +:orphan: + +############################### +Add a web UI with Panel (basic) +############################### + +**Audience:** Users who want to add a web UI written with Python and Panel. + +**Prereqs:** Basic Python knowledge. + +---- + +************** +What is Panel? +************** + +`Panel`_ and the `HoloViz`_ ecosystem provide unique and powerful +features such as big data visualization using `DataShader`_, easy cross filtering +using `HoloViews`_, streaming and much more. + +* Panel is highly flexible and ties into the PyData and Jupyter ecosystems as you can develop in notebooks and use ipywidgets. You can also develop in .py files. + +* Panel is one of the most popular data app frameworks in Python with `more than 400.000 downloads a month `_. It's especially popular in the scientific community. + +* Panel is used, for example, by Rapids to power `CuxFilter`_, a CuDF based big data visualization framework. + +* Panel can be deployed on your favorite server or cloud including `Lightning`_. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/panel-intro.gif + :alt: Example Panel App + + Example Panel App + +Panel is **particularly well suited for Lightning Apps** that need to display live progress. This is because the Panel server can react +to state changes and asynchronously push messages from the server to the client using web socket communication. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/panel-streaming-intro.gif + :alt: Example Panel Streaming App + + Example Panel Streaming App + +Install Panel with: + +.. code:: bash + + pip install panel + +---- + +********************* +Run a basic Panel App +********************* + +In the next few sections, we'll build an App step-by-step. + +First, create a file named ``app_panel.py`` with the App content: + +.. code:: python + + # app_panel.py + + import panel as pn + + pn.panel("Hello **Panel ⚡** World").servable() + +Then, create a file named ``app.py`` with the following App content: + +.. code:: python + + # app.py + + import lightning as L + from lightning.app.frontend import PanelFrontend + + + class LitPanel(L.LightningFlow): + + def configure_layout(self): + return PanelFrontend("app_panel.py") + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_panel = LitPanel() + + def run(self): + self.lit_panel.run() + + def configure_layout(self): + return {"name": "home", "content": self.lit_panel} + + + app = L.LightningApp(LitApp()) + +Finally, add ``panel`` to your ``requirements.txt`` file: + +.. code:: bash + + echo 'panel' >> requirements.txt + +.. note:: This is a best practice to make Apps reproducible. + +---- + +*********** +Run the App +*********** + +Run the App locally: + +.. code:: bash + + lightning run app app.py + +The App should look like this: + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/panel-lightning-basic.png + :alt: Basic Panel Lightning App + + Basic Panel Lightning App + +Now, run it on the cloud: + +.. code:: bash + + lightning run app app.py --cloud + +---- + +************************* +Step-by-step walk-through +************************* + +In this section, we explain each part of the code in detail. + +---- + +0. Define a Panel app +^^^^^^^^^^^^^^^^^^^^^ + +First, find the Panel app you want to integrate. In this example, that app looks like: + +.. code:: python + + import panel as pn + + pn.panel("Hello **Panel ⚡** World").servable() + +Refer to the `Panel documentation `_ and `awesome-panel.org `_ for more complex examples. + +---- + +1. Add Panel to a Component +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Link this app to the Lightning App by using the ``PanelFrontend`` class which needs to be returned from +the ``configure_layout`` method of the Lightning Component you want to connect to Panel. + +.. code:: python + :emphasize-lines: 7-10 + + import lightning as L + from lightning.app.frontend import PanelFrontend + + + class LitPanel(L.LightningFlow): + + def configure_layout(self): + return PanelFrontend("app_panel.py") + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_panel = LitPanel() + + def run(self): + self.lit_panel.run() + + def configure_layout(self): + return {"name": "home", "content": self.lit_panel} + + + app = L.LightningApp(LitApp()) + +The argument of the ``PanelFrontend`` class, points to the script, notebook, or function that +runs your Panel app. + +---- + +2. Route the UI in the root component +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The second step, is to tell the Root component in which tab to render this component's UI. +In this case, we render the ``LitPanel`` UI in the ``home`` tab of the app. + +.. code:: python + :emphasize-lines: 19-20 + + import lightning as L + from lightning.app.frontend import PanelFrontend + + + class LitPanel(L.LightningFlow): + + def configure_layout(self): + return PanelFrontend("app_panel.py") + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_panel = LitPanel() + + def run(self): + self.lit_panel.run() + + def configure_layout(self): + return {"name": "home", "content": self.lit_panel} + + app = L.LightningApp(LitApp()) + +---- + +************* +Tips & Tricks +************* + +0. Use autoreload while developing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To speed up your development workflow, you can run your Lightning App with Panel **autoreload** by +setting the environment variable ``PANEL_AUTORELOAD`` to ``yes``. + +Try running the following: + +.. code-block:: + + PANEL_AUTORELOAD=yes lightning run app app.py + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/panel-lightning-autoreload.gif + :alt: Basic Panel Lightning App with autoreload + + Basic Panel Lightning App with autoreload + +1. Theme your App +^^^^^^^^^^^^^^^^^ + +To theme your App you, can use the Lightning accent color ``#792EE5`` with the `FastListTemplate`_. + +Try replacing the contents of ``app_panel.py`` with the following: + +.. code:: bash + + # app_panel.py + + import panel as pn + import plotly.express as px + + ACCENT = "#792EE5" + + pn.extension("plotly", sizing_mode="stretch_width", template="fast") + pn.state.template.param.update( + title="⚡ Hello Panel + Lightning ⚡", accent_base_color=ACCENT, header_background=ACCENT + ) + + pn.config.raw_css.append( + """ + .bk-root:first-of-type { + height: calc( 100vh - 200px ) !important; + } + """ + ) + + + def get_panel_theme(): + """Returns 'default' or 'dark'""" + return pn.state.session_args.get("theme", [b"default"])[0].decode() + + + def get_plotly_template(): + if get_panel_theme() == "dark": + return "plotly_dark" + return "plotly_white" + + + def get_plot(length=5): + xseries = [index for index in range(length + 1)] + yseries = [x**2 for x in xseries] + fig = px.line( + x=xseries, + y=yseries, + template=get_plotly_template(), + color_discrete_sequence=[ACCENT], + range_x=(0, 10), + markers=True, + ) + fig.layout.autosize = True + return fig + + + length = pn.widgets.IntSlider(value=5, start=1, end=10, name="Length") + dynamic_plot = pn.panel( + pn.bind(get_plot, length=length), sizing_mode="stretch_both", config={"responsive": True} + ) + pn.Column(length, dynamic_plot).servable() + + +Install some additional libraries and remember to add the dependencies to the ``requirements.txt`` file: + + +.. code:: bash + + echo 'plotly' >> requirements.txt + echo 'pandas' >> requirements.txt + +Finally run the App + +.. code:: bash + + lightning run app app.py + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/panel-lightning-theme.gif + :alt: Basic Panel Plotly Lightning App with theming + + Basic Panel Plotly Lightning App with theming + +.. _Panel: https://panel.holoviz.org/ +.. _FastListTemplate: https://panel.holoviz.org/reference/templates/FastListTemplate.html#templates-gallery-fastlisttemplate +.. _HoloViz: https://holoviz.org/ +.. _DataShader: https://datashader.org/ +.. _HoloViews: https://holoviews.org/ +.. _Lightning: https://lightning.ai/ +.. _CuxFilter: https://github.com/rapidsai/cuxfilter +.. _AwesomePanel: https://awesome-panel.org/home + + +---- + +********** +Next Steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: 2: Enable two-way communication + :description: Enable two-way communication between Panel and a Lightning App. + :col_css: col-md-6 + :button_link: intermediate.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Add a web user interface (UI) + :description: Users who want to add a UI to their Lightning Apps + :col_css: col-md-6 + :button_link: ../index.html + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/panel/index.rst b/docs/source-app/workflows/add_web_ui/panel/index.rst new file mode 100644 index 0000000..0d48a1d --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/panel/index.rst @@ -0,0 +1,85 @@ +:orphan: + +.. toctree:: + :maxdepth: 1 + :hidden: + + basic + intermediate + +####################### +Add a web UI with Panel +####################### + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1: Connect Panel + :description: Learn how to connect Panel to a Lightning Component. + :col_css: col-md-6 + :button_link: basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: 2: Enable two-way communication + :description: Enable two-way communication between Panel and a Lightning App. + :col_css: col-md-6 + :button_link: intermediate.html + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
+ +---- + +******** +Examples +******** + +Here are a few example apps that use a Panel web UI. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example 1 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 2 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 3 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/panel/intermediate.rst b/docs/source-app/workflows/add_web_ui/panel/intermediate.rst new file mode 100644 index 0000000..be09e98 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/panel/intermediate.rst @@ -0,0 +1,210 @@ +:orphan: + +###################################### +Add a web UI with Panel (intermediate) +###################################### + +**Audience:** Users who want to communicate between the Lightning App and Panel. + +**Prereqs:** Must have read the `Panel basic `_ guide. + +---- + +************************************** +Interact with the Component from Panel +************************************** + +The ``PanelFrontend`` enables user interactions with the Lightning App using widgets. +You can modify the state variables of a Lightning Component using the ``AppStateWatcher``. + +For example, here we increase the ``count`` variable of the Lightning Component every time a user +presses a button: + +.. code:: python + + # app_panel.py + + import panel as pn + from lightning.app.frontend import AppStateWatcher + + pn.extension(sizing_mode="stretch_width") + + app = AppStateWatcher() + + submit_button = pn.widgets.Button(name="submit") + + @pn.depends(submit_button, watch=True) + def submit(_): + app.state.count += 1 + + @pn.depends(app.param.state) + def current_count(_): + return f"current count: {app.state.count}" + + pn.Column( + submit_button, + current_count, + ).servable() + + + +.. code:: python + + # app.py + + import lightning as L + from lightning.app.frontend import PanelFrontend + + class LitPanel(L.LightningFlow): + def __init__(self): + super().__init__() + self.count = 0 + self.last_count = 0 + + def run(self): + if self.count != self.last_count: + self.last_count = self.count + print("Count changed to: ", self.count) + + def configure_layout(self): + return PanelFrontend("app_panel.py") + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_panel = LitPanel() + + def run(self): + self.lit_panel.run() + + def configure_layout(self): + return {"name": "home", "content": self.lit_panel} + + + app = L.LightningApp(LitApp()) + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/panel-lightning-counter-from-frontend.gif + :alt: Panel Lightning App updating a counter from the frontend + + Panel Lightning App updating a counter from the frontend + +---- + +************************************ +Interact with Panel from a Component +************************************ + +To update the `PanelFrontend` from any Lightning Component, update the property in the Component. +Make sure to call the ``run`` method from the parent component. + +In this example, we update the ``count`` value of the Component: + +.. code:: python + + # app_panel.py + + import panel as pn + from lightning.app.frontend import AppStateWatcher + + app = AppStateWatcher() + + pn.extension(sizing_mode="stretch_width") + + def counter(state): + return f"Counter: {state.count}" + + last_update = pn.bind(counter, app.param.state) + + pn.panel(last_update).servable() + +.. code:: python + + # app.py + + from datetime import datetime as dt + from lightning.app.frontend import PanelFrontend + + import lightning as L + + + class LitPanel(L.LightningFlow): + def __init__(self): + super().__init__() + self.count = 0 + self._last_update = dt.now() + + def run(self): + now = dt.now() + if (now - self._last_update).microseconds >= 250: + self.count += 1 + self._last_update = now + print("Counter changed to: ", self.count) + + def configure_layout(self): + return PanelFrontend("app_panel.py") + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_panel = LitPanel() + + def run(self): + self.lit_panel.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_panel} + return tab1 + + app = L.LightningApp(LitApp()) + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/panel-lightning-counter-from-component.gif + :alt: Panel Lightning App updating a counter from the component + + Panel Lightning App updating a counter from the Component + +---- + +************* +Tips & Tricks +************* + +* Caching: Panel provides the easy to use ``pn.state.cache`` memory based, ``dict`` caching. If you are looking for something persistent try `DiskCache `_ its really powerful and simple to use. You can use it to communicate large amounts of data between the components and frontend(s). + +* Notifications: Panel provides easy to use `notifications `_. You can for example use them to provide notifications about runs starting or ending. + +* Tabulator Table: Panel provides the `Tabulator table `_ which features expandable rows. The table is useful to provide for example an overview of you runs. But you can dig into the details by clicking and expanding the row. + +* Task Scheduling: Panel provides easy to use `task scheduling `_. You can use this to for example read and display files created by your components on a scheduled basis. + +* Terminal: Panel provides the `Xterm.js terminal `_ which can be used to display live logs from your components and allow you to provide a terminal interface to your component. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/panel-lightning-github-runner.gif + :alt: Panel Lightning App running models on github + + Panel Lightning App running models on GitHub + +---- + +********** +Next Steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Add a web user interface (UI) + :description: Users who want to add a UI to their Lightning Apps + :col_css: col-md-6 + :button_link: ../index.html + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/react/communicate_between_react_and_lightning.rst b/docs/source-app/workflows/add_web_ui/react/communicate_between_react_and_lightning.rst new file mode 100644 index 0000000..be92d75 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/react/communicate_between_react_and_lightning.rst @@ -0,0 +1,58 @@ +####################################### +Communicate Between React and Lightning +####################################### +**Audience:** Anyone who wants to add a web user interface (UI) written in react to their app. + +**pre-requisites:** Make sure you've already connected the React and Lightning app. + +**Difficulty level:** intermediate. + +---- + +************ +Example code +************ +To illustrate how to communicate between a React app and a lightning App, we'll be using the `example_app.py` file +which `lightning init react-ui `_ created: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/example_app.py + +and the App.tsx file also created by `lightning init react-ui `_: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/ui/src/App.tsx + +---- + +****************************** +Update React --> Lightning app +****************************** +To change the Lightning app from the React app, use `updateLightningState`. + +In this example, when you press **Start printing** in the React UI, it toggles +the `react_ui.vars.should_print`: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/ui/src/App.tsx + :emphasize-lines: 20, 21, 23 + +By changing that variable in the Lightning app state, it sets **react_ui.should_print** to True, which enables the +Lightning app to print: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/example_app.py + :emphasize-lines: 10, 22 + +---- + +****************************** +Update React <-- Lightning app +****************************** +To change the React app from the Lightning app, use the values from the `lightningState`. + +In this example, when the `react_ui.counter`` increaes in the Lightning app: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/example_app.py + :emphasize-lines: 18, 24 + +The React UI updates the text on the screen to reflect the count + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/ui/src/App.tsx + :emphasize-lines: 15 diff --git a/docs/source-app/workflows/add_web_ui/react/connect_react_and_lightning.rst b/docs/source-app/workflows/add_web_ui/react/connect_react_and_lightning.rst new file mode 100644 index 0000000..1fb78d1 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/react/connect_react_and_lightning.rst @@ -0,0 +1,107 @@ +################################ +Connect React to a Lightning app +################################ +**Audience:** Users who already have a react app and want to connect it to a Lightning app. + +**pre-requisites:** Make sure you already have a react app you want to connect. + +**Difficulty level:** intermediate. + +---- + +************ +Example code +************ +To illustrate how to connect a React app and a lightning App, we'll be using the `example_app.py` file +which `lightning init react-ui `_ created: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/example_app.py + +and the App.tsx file also created by `lightning init react-ui `_: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/ui/src/App.tsx + +---- + +************************************* +Connect the component to the react UI +************************************* +The first step is to connect the dist folder of the react app using `StaticWebFrontend`: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/example_app.py + :emphasize-lines: 13 + +the dist folder must contain an index.html file which is generated by the compilating command `yarn build` which +we'll explore later. + +---- + +********************************** +Connect component to the root flow +********************************** +Next, connect your component to the root flow. Display the react app on the tab of your choice +using `configure_layout`: + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/example_app.py + :emphasize-lines: 19, 27 + +---- + +********************************* +Connect React and Lightning state +********************************* +At this point, the React app will render in the Lightning app. Test it out! + +.. code:: bash + + lightning run app example_app.py + +However, to make powerful React+Lightning apps, you must also connect the Lightning App state to the react app. +These lines enable two-way communication between the react app and the Lightning app. + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/ui/src/App.tsx + :emphasize-lines: 10, 13 + +---- + +**************** +Component vs App +**************** +Notice that in this guide, we connected a single react app to a single component. + +.. literalinclude:: ../../../../../src/lightning/app/cli/react-ui-template/example_app.py + :emphasize-lines: 6-13 + +You can use this single react app for the FULL Lightning app, or you can specify a React app for EACH component. + +.. code:: python + :emphasize-lines: 5, 9, 18-20 + + import lightning as L + + + class ComponentA(L.LightningFlow): + def configure_layout(self): + return L.app.frontend.StaticWebFrontend(Path(__file__).parent / "react_app_1/dist") + + + class ComponentB(L.LightningFlow): + def configure_layout(self): + return L.app.frontend.StaticWebFrontend(Path(__file__).parent / "react_app_2/dist") + + + class HelloLitReact(L.LightningFlow): + def __init__(self): + super().__init__() + self.react_app_1 = ComponentA() + self.react_app_2 = ComponentB() + + def configure_layout(self): + tab_1 = {"name": "App 1", "content": self.react_app_1} + tab_2 = {"name": "App 2", "content": self.react_app_2} + return tab_1, tab_2 + + + app = L.LightningApp(HelloLitReact()) + +This is a powerful idea that allows each Lightning component to have a self-contained web UI. diff --git a/docs/source-app/workflows/add_web_ui/react/create_react_template.rst b/docs/source-app/workflows/add_web_ui/react/create_react_template.rst new file mode 100644 index 0000000..4e48f39 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/react/create_react_template.rst @@ -0,0 +1,51 @@ +###################################### +Create a React Template (intermediate) +###################################### +**Audience:** Anyone who wants to add a web user interface (UI) written in react to their app. + +---- + +************** +What is react? +************** +`React.js `_ is a JavaScript library for building user interfaces. +A huge number of websites are written in React.js (like Facebook). + +---- + +************************ +Bring your own React app +************************ +If you already have a React.js app, then you don't need the section below. However, it might be helpful +to see our React template so you can understand how to connect it to a Lightning app. + +---- + +**************************** +Create the react-ui template +**************************** +Lightning can generate a react-ui template out of the box (generated with `Vite `_). + +Run this command to set up a react-ui template for a component: + +.. code:: bash + + lightning init react-ui + +If everything was succesful, run the example_app.py listed in the output of the command: + +.. code:: bash + + INFO: Checking pre-requisites for react + INFO: + found npm version: 8.5.5 + found node version: 16.15.0 + found yarn version: 1.22.10 + + ... + ... + + ⚡ run the example_app.py to see it live! + lightning run app react-ui/example_app.py + +If the command didn't work, make sure to install `npm+nodejs `_, and `yarn `_. diff --git a/docs/source-app/workflows/add_web_ui/react/index.rst b/docs/source-app/workflows/add_web_ui/react/index.rst new file mode 100644 index 0000000..ba0f8d9 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/react/index.rst @@ -0,0 +1,106 @@ +:orphan: + +.. toctree:: + :maxdepth: 1 + :hidden: + + create_react_template + connect_react_and_lightning + communicate_between_react_and_lightning + react_development_workflow + +########################## +Add a web UI with React.js +########################## +**Audience:** Anyone who wants to add a web user interface (UI) written in react to their app. + +**Prereqs:** Basic html knowledge. + +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1: Create a React project template + :description: Use our React template to start a react app or bring your own. + :col_css: col-md-6 + :button_link: create_react_template.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: 2: Connect a React app and a Lightning app + :description: Learn how to connect a React app to a Lightning app. + :col_css: col-md-6 + :button_link: connect_react_and_lightning.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: 3: Communicate between React and Lightning + :description: Learn how to communicate between a React app and a Lightning app. + :col_css: col-md-6 + :button_link: communicate_between_react_and_lightning.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: 4: Develop like a React pro + :description: Learn the development workflow of a React developer. + :col_css: col-md-6 + :button_link: react_development_workflow.html + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
+ +---- + +******** +Examples +******** +Here are a few example apps that use a React web UI. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example 1 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 2 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 3 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/react/react_development_workflow.rst b/docs/source-app/workflows/add_web_ui/react/react_development_workflow.rst new file mode 100644 index 0000000..02d855f --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/react/react_development_workflow.rst @@ -0,0 +1,27 @@ +######################################### +Add a web UI with React.js (intermediate) +######################################### +**Audience:** Anyone who wants to add a web user interface (UI) written in react to their app. + +**pre-requisites:** You already have a React app connected with a Lightning app. + +---- + +********************** +Develop your react app +********************** +Every time you make a change to your React.js app, you must call `yarn build` to apply the changes (this is a React.js thing): + +.. code:: bash + + # if you're lost, the right folder has a package.json in it + cd folder-with-ui-folder/ui + yarn build + +This can get very repetitive, there is a "hot reload" command that you can enable with: + +.. code:: bash + + # TODO + +There are many other tricks that React.js developers use to improve their development speed. diff --git a/docs/source-app/workflows/add_web_ui/streamlit/basic.rst b/docs/source-app/workflows/add_web_ui/streamlit/basic.rst new file mode 100644 index 0000000..ced0314 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/streamlit/basic.rst @@ -0,0 +1,186 @@ +################################### +Add a web UI with Streamlit (basic) +################################### +**Audience:** Users who want to add a web UI written with Python. + +**Prereqs:** Basic python knowledge. + +---- + +****************** +What is Streamlit? +****************** +Streamlit is a web user interface builder for Python developers. Streamlit builds beautiful web pages +directly from Python. + +Install Streamlit with: + +.. code:: bash + + pip install streamlit + +---- + +************************* +Run a basic streamlit app +************************* + +.. + To explain how to use Streamlit with Lightning, let's replicate the |st_link|. + + .. |st_link| raw:: html + + example running here + +In the next few sections we'll build an app step-by-step. +First **create a file named app.py** with the app content: + +.. code:: python + + # app.py + import lightning as L + import lightning.app.frontend as frontend + import streamlit as st + + def your_streamlit_app(lightning_app_state): + st.write('hello world') + + class LitStreamlit(L.LightningFlow): + def configure_layout(self): + return frontend.StreamlitFrontend(render_fn=your_streamlit_app) + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_streamlit = LitStreamlit() + + def run(self): + self.lit_streamlit.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_streamlit} + return tab1 + + app = L.LightningApp(LitApp()) + +add "streamlit" to a requirements.txt file: + +.. code:: bash + + echo 'streamlit' >> requirements.txt + +this is a best practice to make apps reproducible. + +---- + +*********** +Run the app +*********** +Run the app locally to see it! + +.. code:: python + + lightning run app app.py + +Now run it on the cloud as well: + +.. code:: python + + lightning run app app.py --cloud + +---- + +************************ +Step-by-step walkthrough +************************ +In this section, we explain each part of this code in detail. + +---- + +0. Define a streamlit app +^^^^^^^^^^^^^^^^^^^^^^^^^ +First, find the streamlit app you want to integrate. In this example, that app looks like: + +.. code:: python + + import streamlit as st + + def your_streamlit_app(): + st.write('hello world') + +Refer to the `Streamlit documentation `_ for more complex examples. + +---- + +1. Add Streamlit to a component +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Link this function to the Lightning App by using the ``StreamlitFrontend`` class which needs to be returned from +the ``configure_layout`` method of the Lightning component you want to connect to Streamlit. + +.. code:: python + :emphasize-lines: 9-11 + + # app.py + import lightning as L + import lightning.app.frontend as frontend + import streamlit as st + + def your_streamlit_app(lightning_app_state): + st.write('hello world') + + class LitStreamlit(L.LightningFlow): + def configure_layout(self): + return frontend.StreamlitFrontend(render_fn=your_streamlit_app) + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_streamlit = LitStreamlit() + + def run(self): + self.lit_streamlit.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_streamlit} + return tab1 + + app = L.LightningApp(LitApp()) + +The ``render_fn`` argument of the ``StreamlitFrontend`` class, points to a function that runs your Streamlit app. +The first argument to the function is the lightning app state. Any changes to the app state update the app. + +---- + +2. Route the UI in the root component +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The second step, is to tell the Root component in which tab to render this component's UI. +In this case, we render the ``LitStreamlit`` UI in the ``home`` tab of the application. + +.. code:: python + :emphasize-lines: 22 + + # app.py + import lightning as L + import lightning.app.frontend as frontend + import streamlit as st + + def your_streamlit_app(lightning_app_state): + st.write('hello world') + + class LitStreamlit(L.LightningFlow): + def configure_layout(self): + return frontend.StreamlitFrontend(render_fn=your_streamlit_app) + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_streamlit = LitStreamlit() + + def run(self): + self.lit_streamlit.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_streamlit} + return tab1 + + app = L.LightningApp(LitApp()) diff --git a/docs/source-app/workflows/add_web_ui/streamlit/index.rst b/docs/source-app/workflows/add_web_ui/streamlit/index.rst new file mode 100644 index 0000000..2496729 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/streamlit/index.rst @@ -0,0 +1,84 @@ +:orphan: + +.. toctree:: + :maxdepth: 1 + :hidden: + + basic + intermediate + +########################### +Add a web UI with Streamlit +########################### + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1: Connect Streamlit + :description: Learn how to connect Streamlit to a Lightning Component. + :col_css: col-md-6 + :button_link: basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: 2: Enable two-way communication + :description: Enable two-way communication between Streamlit and a Lightning App. + :col_css: col-md-6 + :button_link: intermediate.html + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
+ +---- + +******** +Examples +******** +Here are a few example apps that use a Streamlit web UI. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Example 1 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 2 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. displayitem:: + :header: Example 3 + :description: Show off your work! Contribute an example. + :col_css: col-md-4 + :button_link: ../../../contribute_app.html + :height: 150 + :tag: Waiting for contributed example + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/add_web_ui/streamlit/intermediate.rst b/docs/source-app/workflows/add_web_ui/streamlit/intermediate.rst new file mode 100644 index 0000000..bd10fe6 --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/streamlit/intermediate.rst @@ -0,0 +1,105 @@ +########################################## +Add a web UI with Streamlit (intermediate) +########################################## +**Audience:** Users who want to communicate between the Lightning App and Streamlit. + +**Prereqs:** Must have read the `streamlit basic `_ guide. + +---- + +************************************ +Interact with the App from Streamlit +************************************ +The streamlit UI enables user interactions with the Lightning App via UI elements like buttons. +To modify the variables of a Lightning component, access the ``lightning_app_state`` variable in . + +For example, here we increase the count variable of the Lightning Component every time a user presses a button: + +.. code:: python + :emphasize-lines: 8, 14 + + # app.py + import lightning as L + import lightning.app.frontend as frontend + import streamlit as st + + + def your_streamlit_app(lightning_app_state): + if st.button("press to increase count"): + lightning_app_state.count += 1 + st.write(f"current count: {lightning_app_state.count}") + + + class LitStreamlit(L.LightningFlow): + def __init__(self): + super().__init__() + self.count = 0 + + def configure_layout(self): + return frontend.StreamlitFrontend(render_fn=your_streamlit_app) + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_streamlit = LitStreamlit() + + def run(self): + self.lit_streamlit.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_streamlit} + return tab1 + + + app = L.LightningApp(LitApp()) + +---- + +**************************************** +Interact with Streamlit from a component +**************************************** +To update the streamlit UI from any Lightning component, update the property in the component and make sure to call ``run`` from the +parent component. + +In this example we update the value of the counter from the component: + +.. code:: python + :emphasize-lines: 7, 15 + + # app.py + import lightning as L + import lightning.app.frontend as frontend + import streamlit as st + + + def your_streamlit_app(lightning_app_state): + st.write(f"current count: {lightning_app_state.count}") + + + class LitStreamlit(L.LightningFlow): + def __init__(self): + super().__init__() + self.count = 0 + + def run(self): + self.count += 1 + + def configure_layout(self): + return frontend.StreamlitFrontend(render_fn=your_streamlit_app) + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_streamlit = LitStreamlit() + + def run(self): + self.lit_streamlit.run() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_streamlit} + return tab1 + + + app = L.LightningApp(LitApp()) diff --git a/docs/source-app/workflows/add_web_ui/vue_js_intermediate.rst b/docs/source-app/workflows/add_web_ui/vue_js_intermediate.rst new file mode 100644 index 0000000..e8d9f3e --- /dev/null +++ b/docs/source-app/workflows/add_web_ui/vue_js_intermediate.rst @@ -0,0 +1,6 @@ +:orphan: + +####################################### +Add a web UI with Vue.js (intermediate) +####################################### +coming... diff --git a/docs/source-app/workflows/arrange_tabs/arrange_app_basic.rst b/docs/source-app/workflows/arrange_tabs/arrange_app_basic.rst new file mode 100644 index 0000000..91c0e53 --- /dev/null +++ b/docs/source-app/workflows/arrange_tabs/arrange_app_basic.rst @@ -0,0 +1,69 @@ +######################## +Arrange app tabs (basic) +######################## +**Audience:** Users who want to control the layout of their app user interface. + +---- + +***************************** +Enable a full-page single tab +***************************** + +To enable a single tab on the app UI, return a single dictionary from the ``configure_layout`` method: + +.. code:: python + :emphasize-lines: 9 + + import lightning as L + + + class DemoComponent(L.demo.dumb_component): + def configure_layout(self): + tab1 = {"name": "THE TAB NAME", "content": self.component_a} + return tab1 + + + app = L.LightningApp(DemoComponent()) + + +The "name" key defines the visible name of the tab on the UI. It also shows up in the URL. +The **"content"** key defines the target component to render in that tab. +When returning a single tab element like shown above, the UI will display it in full-page mode. + + +---- + +******************** +Enable multiple tabs +******************** + +.. code:: python + :emphasize-lines: 7 + + import lightning as L + + + class DemoComponent(L.demo.dumb_component): + def configure_layout(self): + tab1 = {"name": "Tab A", "content": self.component_a} + tab2 = {"name": "Tab B", "content": self.component_b} + return tab1, tab2 + + + app = L.LightningApp(DemoComponent()) + +The order matters! Try any of the following configurations: + +.. code:: python + :emphasize-lines: 4, 9 + + def configure_layout(self): + tab1 = {"name": "Tab A", "content": self.component_a} + tab2 = {"name": "Tab B", "content": self.component_b} + return tab1, tab2 + + + def configure_layout(self): + tab1 = {"name": "Tab A", "content": self.component_a} + tab2 = {"name": "Tab B", "content": self.component_b} + return tab2, tab1 diff --git a/docs/source-app/workflows/arrange_tabs/arrange_app_intermediate.rst b/docs/source-app/workflows/arrange_tabs/arrange_app_intermediate.rst new file mode 100644 index 0000000..c87965b --- /dev/null +++ b/docs/source-app/workflows/arrange_tabs/arrange_app_intermediate.rst @@ -0,0 +1,20 @@ +############################### +Arrange app tabs (intermediate) +############################### +TODO: + +---- + +*********************************** +Render components with a defined UI +*********************************** + +component directly + +---- + +************* +Render a link +************* + +tensorboard link diff --git a/docs/source-app/workflows/arrange_tabs/index.rst b/docs/source-app/workflows/arrange_tabs/index.rst new file mode 100644 index 0000000..f639c02 --- /dev/null +++ b/docs/source-app/workflows/arrange_tabs/index.rst @@ -0,0 +1,5 @@ +################ +Arrange App Tabs +################ + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/arrange_tabs/index_content.rst b/docs/source-app/workflows/arrange_tabs/index_content.rst new file mode 100644 index 0000000..66ac932 --- /dev/null +++ b/docs/source-app/workflows/arrange_tabs/index_content.rst @@ -0,0 +1,34 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + arrange_app_basic + arrange_app_intermediate + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Basic + :description: Learn how to enable and layout your app UI + :col_css: col-md-6 + :button_link: arrange_app_basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Intermediate + :description: Learn about all the possible ways of rendering a component. + :col_css: col-md-6 + :button_link: arrange_app_intermediate.html + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/build_command_line_interface/app.py b/docs/source-app/workflows/build_command_line_interface/app.py new file mode 100644 index 0000000..e5abeee --- /dev/null +++ b/docs/source-app/workflows/build_command_line_interface/app.py @@ -0,0 +1,35 @@ +from commands.notebook.run import RunNotebook, RunNotebookConfig +from lit_jupyter import JupyterLab + +import lightning as L +from lightning.app.structures import Dict + + +class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.notebooks = Dict() + + # 1. Annotates the handler input with the Notebook config. + def run_notebook(self, config: RunNotebookConfig): + if config.name in self.notebooks: + return f"The Notebook {config.name} already exists." + else: + # 2. Dynamically creates the Notebook if it doesn't exist and runs it. + self.notebooks[config.name] = JupyterLab( + cloud_compute=L.CloudCompute(config.cloud_compute) + ) + self.notebooks[config.name].run() + return f"The Notebook {config.name} was created." + + def configure_commands(self): + # 3. Returns a list of dictionaries with the format: + # {"command_name": CustomClientCommand(method=self.custom_server_handler)} + return [{"run notebook": RunNotebook(method=self.run_notebook)}] + + def configure_layout(self): + # 4. Dynamically displays the Notebooks in the Lightning App View. + return [{"name": n, "content": w} for n, w in self.notebooks.items()] + + +app = L.LightningApp(Flow()) diff --git a/docs/source-app/workflows/build_command_line_interface/cli.rst b/docs/source-app/workflows/build_command_line_interface/cli.rst new file mode 100644 index 0000000..ffd9bc4 --- /dev/null +++ b/docs/source-app/workflows/build_command_line_interface/cli.rst @@ -0,0 +1,144 @@ +:orphan: + +########################################### +1. Develop a CLI with server side code only +########################################### + +We are going to learn how to create a simple command-line interface. + +Lightning provides a flexible way to create complex CLI without much effort. + +---- + +************************* +1. Implement a simple CLI +************************* + +To create your first CLI, you need to override the :class:`~lightning.app.core.flow.LightningFlow.configure_commands` hook and return a list of dictionaries where the keys are the commands and the values are the server side handlers. + +First, create a file ``app.py`` and copy-paste the following code in to the file: + +.. literalinclude:: example_command.py + +---- + +************** +2. Run the App +************** + +Execute the following command in a terminal: + +.. code-block:: + + lightning run app app.py + +The following appears the terminal: + +.. code-block:: + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + [] + +---- + +*************************** +3. Connect to a running App +*************************** + +In another terminal, connect to the running App. +When you connect to an App, the Lightning CLI is replaced by the App CLI. To exit the App CLI, you need to run ``lightning disconnect``. + +.. code-block:: + + lightning connect localhost + +To see a list of available commands: + +.. code-block:: + + lightning --help + You are connected to the cloud Lightning App: localhost. + Usage: lightning [OPTIONS] COMMAND [ARGS]... + + --help Show this message and exit. + + Lightning App Commands + add Add a name. + +To find the arguments of the commands: + +.. code-block:: + + lightning add --help + You are connected to the cloud Lightning App: localhost. + Usage: lightning add [ARGS]... + + Options + name: Add description + +---- + +******************** +4. Execute a command +******************** + +Trigger the command line exposed by your App: + +.. code-block:: + + lightning add --name=my_name + WARNING: Lightning Command Line Interface is an experimental feature and unannounced changes are likely. + +In your first terminal, **Received name: my_name** and **["my_name"]** are printed. + +.. code-block:: + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + [] + Received name: my_name + ["my_name] + +---- + +************************** +5. Disconnect from the App +************************** + +To exit the App CLI, you need to run ``lightning disconnect``. + +.. code-block:: + + lightning disconnect + You are disconnected from the local Lightning App. + +---- + +********** +Learn more +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: 2. Implement a CLI with client side code execution + :description: Learn how to develop a complex API for your application + :col_css: col-md-6 + :button_link: cli_client.html + :height: 150 + +.. displayitem:: + :header: Develop a RESTful API + :description: Learn how to develop an API for your application. + :col_css: col-md-6 + :button_link: ../build_rest_api/index.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/build_command_line_interface/cli_client.rst b/docs/source-app/workflows/build_command_line_interface/cli_client.rst new file mode 100644 index 0000000..f97062c --- /dev/null +++ b/docs/source-app/workflows/build_command_line_interface/cli_client.rst @@ -0,0 +1,175 @@ +:orphan: + +###################################################### +2. Develop a CLI with server and client code execution +###################################################### + +We've learned how to create a simple command-line interface. But in real-world use-cases, an App Builder wants to provide more complex functionalities where trusted code is executed on the client side. + +Lightning provides a flexible way to create complex CLI without much effort. + +In this example, we’ll create a CLI to dynamically run Notebooks: + + +---- + +************************** +1. Implement a complex CLI +************************** + +First of all, lets' create the following file structure: + +.. code-block:: python + + app_folder/ + commands/ + notebook/ + run.py + app.py + +We'll use the `Jupyter-Component `_. Follow the installation steps on the repo to install the Component. + +Add the following code to ``commands/notebook/run.py``: + +.. literalinclude:: commands/notebook/run.py + +Add the following code to ``app.py``: + +.. literalinclude:: app.py + +---- + +********************************************** +2. Run the App and check the API documentation +********************************************** + +In a terminal, run the following command and open ``http://127.0.0.1:7501/docs`` in a browser. + +.. code-block:: python + + lightning run app app.py + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + +---- + +*************************** +3. Connect to a running App +*************************** + +In another terminal, connect to the running App. +When you connect to an App, the Lightning CLI is replaced by the App CLI. To exit the App CLI, you need to run ``lightning disconnect``. + +.. code-block:: + + lightning connect localhost + + Storing `run_notebook` under /Users/thomas/.lightning/lightning_connection/commands/run_notebook.py + You can review all the downloaded commands under /Users/thomas/.lightning/lightning_connection/commands folder. + You are connected to the local Lightning App. + +To see a list of available commands: + +.. code-block:: + + lightning --help + + You are connected to the cloud Lightning App: localhost. + Usage: lightning [OPTIONS] COMMAND [ARGS]... + + --help Show this message and exit. + + Lightning App Commands + run notebook Run a Notebook. + + +To find the arguments of the commands: + +.. code-block:: + + lightning run notebook --help + + You are connected to the cloud Lightning App: localhost. + usage: notebook [-h] [--name NAME] [--cloud_compute CLOUD_COMPUTE] + + Run Notebook Parser + + optional arguments: + -h, --help show this help message and exit + --name NAME + --cloud_compute CLOUD_COMPUTE + +---- + +******************** +4. Execute a command +******************** + +And then you can trigger the command-line exposed by your App. + +Run the first Notebook with the following command: + +.. code-block:: python + + lightning run notebook --name="my_notebook" + WARNING: Lightning Command Line Interface is an experimental feature and unannounced changes are likely. + The notebook my_notebook was created. + +And run a second notebook. + +.. code-block:: python + + lightning run notebook --name="my_notebook_2" + WARNING: Lightning Command Line Interface is an experimental feature and unannounced changes are likely. + The notebook my_notebook_2 was created. + +Here is a recording of the Lightning App: + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/commands_1.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/commands_1.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +************************** +5. Disconnect from the App +************************** + +To exit the App CLI, you need to run **lightning disconnect**. + +.. code-block:: + + lightning disconnect + You are disconnected from the local Lightning App. + +---- + +********** +Learn more +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: 1. Develop a CLI with server side code only + :description: Learn how to develop a simple CLI for your App. + :col_css: col-md-6 + :button_link: cli.html + :height: 150 + +.. displayitem:: + :header: Develop a RESTful API + :description: Learn how to develop an API for your App. + :col_css: col-md-6 + :button_link: ../build_rest_api/index.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/build_command_line_interface/commands/__init__.py b/docs/source-app/workflows/build_command_line_interface/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/source-app/workflows/build_command_line_interface/commands/notebook/__init__.py b/docs/source-app/workflows/build_command_line_interface/commands/notebook/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/source-app/workflows/build_command_line_interface/commands/notebook/run.py b/docs/source-app/workflows/build_command_line_interface/commands/notebook/run.py new file mode 100644 index 0000000..e0a6463 --- /dev/null +++ b/docs/source-app/workflows/build_command_line_interface/commands/notebook/run.py @@ -0,0 +1,33 @@ +from argparse import ArgumentParser +from uuid import uuid4 + +from pydantic import BaseModel + +from lightning.app.utilities.commands import ClientCommand + + +class RunNotebookConfig(BaseModel): + name: str + cloud_compute: str + + +class RunNotebook(ClientCommand): + description = "Run a Notebook." + + def run(self): + # 1. Define your own argument parser. You can use argparse, click, etc... + parser = ArgumentParser(description='Run Notebook Parser') + parser.add_argument("--name", type=str, default=None) + parser.add_argument("--cloud_compute", type=str, default="cpu") + hparams = parser.parse_args() + + # 2. Invoke the server side handler by sending a payload. + response = self.invoke_handler( + config=RunNotebookConfig( + name=hparams.name or str(uuid4()), + cloud_compute=hparams.cloud_compute, + ), + ) + + # 3. Print the server response. + print(response) diff --git a/docs/source-app/workflows/build_command_line_interface/example_command.py b/docs/source-app/workflows/build_command_line_interface/example_command.py new file mode 100644 index 0000000..4d837fc --- /dev/null +++ b/docs/source-app/workflows/build_command_line_interface/example_command.py @@ -0,0 +1,25 @@ +from lightning import LightningApp, LightningFlow + + +class Flow(LightningFlow): + def __init__(self): + super().__init__() + self.names = [] + + def run(self): + print(self.names) + + def add_name(self, name: str): + """Add a name.""" + print(f"Received name: {name}") + self.names.append(name) + + def configure_commands(self): + # This can be invoked with `lightning add --name=my_name` + commands = [ + {"add": self.add_name}, + ] + return commands + + +app = LightningApp(Flow()) diff --git a/docs/source-app/workflows/build_command_line_interface/index.rst b/docs/source-app/workflows/build_command_line_interface/index.rst new file mode 100644 index 0000000..1f1b1b1 --- /dev/null +++ b/docs/source-app/workflows/build_command_line_interface/index.rst @@ -0,0 +1,55 @@ +############################ +Command-line Interface (CLI) +############################ + +**Audience:** Users looking to create a command line interface (CLI) for their application. + +---- + +************** +What is a CLI? +************** + +A Command-line Interface (CLI) is an user interface (UI) in a terminal to interact with a specific program. + +.. note:: + + The Lightning guideline to build CLI is `lightning ...` or ` ...`. + +As an example, Lightning provides a CLI to interact with your Lightning Apps and the `lightning.ai `_ platform as follows: + +.. code-block:: bash + + main + ├── create - Creates Lightning AI self-managed resources (clusters, etc…) + │ └── cluster - Creates a Lightning AI BYOC compute cluster with your cloud provider credentials. + ├── delete - Deletes Lightning AI self-managed resources (clusters, etc…) + │ └── cluster - Deletes a Lightning AI BYOC compute cluster and all associated cloud provider resources. + ├── fork - Forks an App. + ├── init - Initializes a Lightning App and/or Component. + │ ├── app + │ ├── component + │ ├── pl-app - Creates an App from your PyTorch Lightning source files. + │ └── react-ui - Creates a React UI to give a Lightning Component a React.js web UI + ├── install - Installs a Lightning App and/or Component. + │ ├── app + │ └── component + ├── list - Lists Lightning AI self-managed resources (clusters, etc…) + │ ├── apps - Lists your Lightning AI Apps. + │ └── clusters - Lists your Lightning AI BYOC compute clusters. + ├── login - Logs in to your lightning.ai account. + ├── logout - Logs out of your lightning.ai account. + ├── run - Runs a Lightning App locally or on the cloud. + │ └── app - Runs an App from a file. + ├── show - Shows given resource. + │ ├── cluster - Groups cluster commands inside show. + │ │ └── logs - Shows cluster logs. + │ └── logs - Shows cloud application logs. By default prints logs for all currently available Components. + ├── stop - Stops your App. + └── tree - Shows the command tree of your CLI. + +Learn more about `Command-line interfaces here `_. + +---- + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/build_command_line_interface/index_content.rst b/docs/source-app/workflows/build_command_line_interface/index_content.rst new file mode 100644 index 0000000..ced369d --- /dev/null +++ b/docs/source-app/workflows/build_command_line_interface/index_content.rst @@ -0,0 +1,51 @@ +************************************** +Develop a command line interface (CLI) +************************************** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: 1. Develop a CLI with server side code only + :description: Learn how to develop a simple CLI for your application + :col_css: col-md-6 + :button_link: cli.html + :height: 150 + +.. displayitem:: + :header: 2. Develop a CLI with server and client code execution + :description: Learn how to develop a complex CLI for your application + :col_css: col-md-6 + :button_link: cli_client.html + :height: 150 + +.. raw:: html + +
+
+ + +---- + +********** +Learn more +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Develop a RESTful API + :description: Learn how to develop an API for your application. + :col_css: col-md-6 + :button_link: ../build_rest_api/index.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/build_command_line_interface/post_example.py b/docs/source-app/workflows/build_command_line_interface/post_example.py new file mode 100644 index 0000000..4597b3d --- /dev/null +++ b/docs/source-app/workflows/build_command_line_interface/post_example.py @@ -0,0 +1,26 @@ +import lightning as L +from lightning.app.api import Post + + +class Flow(L.LightningFlow): + # 1. Define the state + def __init__(self): + super().__init__() + self.names = [] + + # 2. Optional, but used to validate names + def run(self): + print(self.names) + + # 3. Method executed when a request is received. + def handle_post(self, name: str): + self.names.append(name) + return f'The name {name} was registered' + + # 4. Defines this Component's Restful API. You can have several routes. + def configure_api(self): + # Your own defined route and handler + return [Post(route="/name", method=self.handle_post)] + + +app = L.LightningApp(Flow()) diff --git a/docs/source-app/workflows/build_lightning_app/from_pytorch_lightning_script.rst b/docs/source-app/workflows/build_lightning_app/from_pytorch_lightning_script.rst new file mode 100644 index 0000000..2fcfb71 --- /dev/null +++ b/docs/source-app/workflows/build_lightning_app/from_pytorch_lightning_script.rst @@ -0,0 +1,109 @@ +####################################################### +Develop a Lightning App from a PyTorch Lightning script +####################################################### + +**Audience:** Users who want to develop a Lightning App (App) from their PyTorch Lightning (PL) scripts. + +---- + +************************************************************* +What developing a Lightning App from a PL script does for you +************************************************************* + +Developing an App from a PL script allows you to immediately run on the cloud and share the progress with friends. +Once you're happy with your model, you can immediately expand beyond just model development to things like +making your own inference APIs, research demos, or even speeding up your data pipeline. + +The PyTorch Lightning App is your entry point to the full end-to-end ML licefycle. + +---- + +****************** +Develop a template +****************** + +To develop a template from a PyTorch Lightning script, use this command: + +.. code:: bash + + lightning init pl-app path/to/the/pl_script.py + + +If your script is not at the root of the project folder, and you'd like to include all source files within that folder, you can specify the root path as the first argument: + +.. code:: bash + + lightning init pl-app path/to/project/root path/to/the/pl_script.py + + +The default trainer App lets you train a model with a beautiful UI locally and on the cloud with zero effort! + +---- + +*********** +Run the App +*********** + +.. note:: This section is under construction. + +Run the App locally: + +.. code:: bash + + lightning run app pl-app/app.py + +Or run the App on the cloud so you can share with collaborators and even use all the cloud GPUs you want. + +.. code:: bash + + lightning run app pl-app/app.py --cloud + + +.. figure:: https://storage.googleapis.com/grid-packages/pytorch-lightning-app/docs-thumbnail.png + :alt: Screenshot of the PyTorch Lightning app running in the cloud + + +---- + +******************* +Modify the template +******************* + +The command above generates an App file like this: + +.. note:: TODO: list the file and show how to extend it + +.. code:: python + + from your_app_name import ComponentA, ComponentB + + import lightning as L + + + class LitApp(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.component_a = ComponentA() + self.component_b = ComponentB() + + def run(self): + self.component_a.run() + self.component_b.run() + + + app = L.LightningApp(LitApp()) + +Now you can add your own components as you wish! + +---- + +************ +Known issues +************ + +- The UI takes a couple seconds to load when opening the App, so please be patient. +- The timer resets when refreshing the page. +- The UI for adding new environment variables does not provide an option to delete an entry. +- A bug exists that leaves the script hanging at the start of training when using the DDP strategy. +- DDP-spawn is not supported due to pickling issues. +- It is currently not possible to submit a new run once the script has finished or failed. diff --git a/docs/source-app/workflows/build_lightning_app/from_scratch.rst b/docs/source-app/workflows/build_lightning_app/from_scratch.rst new file mode 100644 index 0000000..9042f10 --- /dev/null +++ b/docs/source-app/workflows/build_lightning_app/from_scratch.rst @@ -0,0 +1,11 @@ +#################################### +Develop a Lightning App from Scratch +#################################### + +**Audience:** Users who want to develop a Lightning App from scratch. + +**Prereqs:** You must have finished the `Basic levels `_. + +---- + +.. include:: from_scratch_content.rst diff --git a/docs/source-app/workflows/build_lightning_app/from_scratch_content.rst b/docs/source-app/workflows/build_lightning_app/from_scratch_content.rst new file mode 100644 index 0000000..7641b4f --- /dev/null +++ b/docs/source-app/workflows/build_lightning_app/from_scratch_content.rst @@ -0,0 +1,60 @@ + +************** +WAIT! +************** +Before you build a Lightning App from scratch, see if you can find an app that is similar to what you need +in the `Lightning App Gallery `_. + +Once you find the Lightning App you want, press "Clone & Run" to see it running on the cloud, then download the code +and change what you want! + +---- + +****************** +Build from scratch +****************** +If you didn't find a Lightning App similar to the one you need, simply create a file named **app.py** with these contents: + +.. code:: python + + import lightning as L + + + class WordComponent(L.LightningWork): + def __init__(self, word): + super().__init__() + self.word = word + + def run(self): + print(self.word) + + + class LitApp(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.hello = WordComponent("hello") + self.world = WordComponent("world") + + def run(self): + print("This is a simple Lightning app, make a better app!") + self.hello.run() + self.world.run() + + + app = L.LightningApp(LitApp()) + +---- + +Run the Lightning App +^^^^^^^^^^^^^^^^^^^^^ +Run the Lightning App locally: + +.. code:: bash + + lightning run app app.py + +Run the Lightning App on the cloud: + +.. code:: bash + + lightning run app app.py --cloud diff --git a/docs/source-app/workflows/build_lightning_app/index.rst b/docs/source-app/workflows/build_lightning_app/index.rst new file mode 100644 index 0000000..e60f035 --- /dev/null +++ b/docs/source-app/workflows/build_lightning_app/index.rst @@ -0,0 +1,11 @@ +:orphan: + +####################### +Develop a Lightning App +####################### + +A Lightning App (App) is a collection of components interacting together. Learn how to develop a basic App template. + +---- + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/build_lightning_app/index_content.rst b/docs/source-app/workflows/build_lightning_app/index_content.rst new file mode 100644 index 0000000..45264d8 --- /dev/null +++ b/docs/source-app/workflows/build_lightning_app/index_content.rst @@ -0,0 +1,32 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + from_scratch + from_pytorch_lightning_script + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Develop a Lightning App from scratch + :description: Learn how to Develop a Lightning App from scratch + :col_css: col-md-6 + :button_link: from_scratch.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Develop an App from a PyTorch Lightning script + :description: Share your PyTorch Lightning training on the cloud, run on cloud GPUs, or extend your App + :col_css: col-md-6 + :button_link: from_pytorch_lightning_script.html + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/build_lightning_component/basic.rst b/docs/source-app/workflows/build_lightning_component/basic.rst new file mode 100644 index 0000000..07fac58 --- /dev/null +++ b/docs/source-app/workflows/build_lightning_component/basic.rst @@ -0,0 +1,9 @@ +############################# +Develop a Lightning Component +############################# + +**Audience:** Users who want to develop a Lightning Component. + +---- + +.. include:: from_scratch_component_content.rst diff --git a/docs/source-app/workflows/build_lightning_component/from_scratch_component_content.rst b/docs/source-app/workflows/build_lightning_component/from_scratch_component_content.rst new file mode 100644 index 0000000..a42be9b --- /dev/null +++ b/docs/source-app/workflows/build_lightning_component/from_scratch_component_content.rst @@ -0,0 +1,153 @@ +******************************* +LightningFlow vs. LightningWork +******************************* + +.. _flow_vs_work: + +.. raw:: html + + Choosing between LightningFlow and LightningWork + +There are two types of components in Lightning, **LightningFlow** and **LightningWork**. + +Use a **LightningFlow** component for any programming logic that runs in less than 1 second. + +.. code:: python + + for i in range(10): + print(f"{i}: this kind of code belongs in a LightningFlow") + +Use a **LightningWork** component for any programming logic that takes more than 1 second or requires its own hardware. + +.. code:: python + + from time import sleep + + for i in range(100000): + sleep(2.0) + print(f"{i} LightningWork: work that is long running or may never end (like a server)") + +---- + +************************************************** +What developing a Lightning Component does for you +************************************************** +Lightning Components break up complex systems into modular components. The first obvious benefit is that components +can be reused across other apps. This means you can build once, test it and forget it. + +As a researcher it also means that your code can be taken to production without needing a team of engineers to help +productionize it. + +As a machine learning engineer, it means that your cloud system is: + +- fault tolerant +- cloud agnostic +- testable (unlike YAML/CI/CD code) +- version controlled +- enables cross-functional collaboration + +---- + +************** +WAIT! +************** +Before you build a Lightning component from scratch, see if you can find a component that is similar to what you need +in the `Lightning component Gallery `_. + +Once you find the component you want, download the code and change what you want! + +---- + +***************************************** +Build a Lighitning component from scratch +***************************************** +If you didn't find a Lightning component similar to the one you need, you can build one from scratch. + +---- + +Build a LightningFlow +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To implement a LightningFlow, simply subclass ``LightningFlow`` and define the run method: + +.. code:: python + :emphasize-lines: 5 + + # app.py + import lightning as L + + + class LitFlow(L.LightningFlow): + def run(self): + for i in range(10): + print(f"{i}: this kind of code belongs in a LightningFlow") + + + app = L.LightningApp(LitFlow()) + +run the app + +.. code:: bash + + lightning run app app.py + +---- + +Build a LightningWork +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Only implement a LightningWork if this particular piece of code: + +- takes more than 1 second to execute +- requires its own set of cloud resources +- or both + +To implement a LightningWork, simply subclass ``LightningWork`` and define the run method: + +.. code:: python + :emphasize-lines: 6 + + # app.py + from time import sleep + import lightning as L + + + class LitWork(L.LightningWork): + def run(self): + for i in range(100000): + sleep(2.0) + print(f"{i} LightningWork: work that is long running or may never end (like a server)") + +A LightningWork must always be attached to a LightningFlow and explicitely asked to ``run()``: + +.. code:: python + :emphasize-lines: 13, 16 + + from time import sleep + import lightning as L + + + class LitWork(L.LightningWork): + def run(self): + for i in range(100000): + sleep(2.0) + print(f"{i} LightningWork: work that is long running or may never end (like a server)") + + + class LitFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_work = LitWork() + + def run(self): + self.lit_work.run() + + + app = L.LightningApp(LitFlow()) + +run the app + +.. code:: bash + + lightning run app app.py diff --git a/docs/source-app/workflows/build_lightning_component/index.rst b/docs/source-app/workflows/build_lightning_component/index.rst new file mode 100644 index 0000000..8620a9b --- /dev/null +++ b/docs/source-app/workflows/build_lightning_component/index.rst @@ -0,0 +1,11 @@ +:orphan: + +############################# +Develop a Lightning Component +############################# + +A Lightning App (App) is a collection of components interacting together. Learn how to build a Lightning Component (Component) in this section. + +---- + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/build_lightning_component/index_content.rst b/docs/source-app/workflows/build_lightning_component/index_content.rst new file mode 100644 index 0000000..abf26e3 --- /dev/null +++ b/docs/source-app/workflows/build_lightning_component/index_content.rst @@ -0,0 +1,122 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + basic + ../add_components/index + +.. toctree:: + :maxdepth: 1 + :hidden: + + intermediate + ../run_work_in_parallel + ../run_work_once + +.. toctree:: + :maxdepth: 1 + :hidden: + + ../enable_fault_tolerance + +****** +Basics +****** +.. raw:: html + +
+
+ +.. displayitem:: + :header: Develop a Lightning Component + :description: Learn the basics of developing a Lightning Component + :col_css: col-md-4 + :button_link: basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Explore community Lightning Components + :description: Discover community-built Lightning Components + :col_css: col-md-4 + :button_link: https://lightning.ai/components + :height: 150 + :tag: basic + +.. raw:: html + +
+
+ +---- + +************ +Intermediate +************ + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Add a UI to a component + :description: Learn about all the possible ways of rendering a component. + :col_css: col-md-4 + :button_link: intermediate.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Run LightningWork in parallel + :description: Learn about running LightningWork in parallel. + :col_css: col-md-4 + :button_link: ../run_work_in_parallel.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Run LightningWork once + :description: Learn about running LightningWork multiple times or once. + :col_css: col-md-4 + :button_link: ../run_work_once.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Publish a Lightning component + :description: Learn the basics of publishing a Lightning component. + :col_css: col-md-4 + :button_link: publish_a_component.html + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
+ + +---- + +******** +Advanced +******** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Enable fault tolerance + :description: Learn how to make a component fault tolerant. + :col_css: col-md-4 + :button_link: ../enable_fault_tolerance.html + :height: 150 + :tag: advanced + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/build_lightning_component/intermediate.rst b/docs/source-app/workflows/build_lightning_component/intermediate.rst new file mode 100644 index 0000000..f38f7c7 --- /dev/null +++ b/docs/source-app/workflows/build_lightning_component/intermediate.rst @@ -0,0 +1,71 @@ +############################################ +Develop a Lightning Component (intermediate) +############################################ + +**Audience:** Users who want to connect a UI to a Lightning Component (Component). + +---- + +***************************** +Add a web user interface (UI) +***************************** +Every lightning component can have its own user interface (UI). Lightning components support any kind +of UI interface such as dash, gradio, panel, react.js, streamlit, vue.js, web urls, +etc...(`full list here <../add_web_ui/index.html>`_). + +Let's say that we have a user interface defined in html: + +.. code:: html + + + + + + +

Hello World

+ + + +To *connect* this user interface to the Component, define the configure_layout method: + +.. code:: python + :emphasize-lines: 5, 6 + + import lightning as L + + + class LitHTMLComponent(L.LightningFlow): + def configure_layout(self): + return L.app.frontend.StaticWebFrontend(serve_dir="path/to/folder/with/index.html/inside") + +Finally, route the Component's UI through the root Component's **configure_layout** method: + +.. code:: python + :emphasize-lines: 14 + + # app.py + import lightning as L + + + class LitHTMLComponent(L.LightningFlow): + def configure_layout(self): + return L.app.frontend.StaticWebFrontend(serve_dir="path/to/folder/with/index.html/inside") + + + class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.lit_html_component = LitHTMLComponent() + + def configure_layout(self): + tab1 = {"name": "home", "content": self.lit_html_component} + return tab1 + + + app = L.LightningApp(LitApp()) + +Run your App and you'll see the UI on the Lightning App view: + +.. code:: bash + + lightning run app app.py diff --git a/docs/source-app/workflows/build_lightning_component/publish_a_component.rst b/docs/source-app/workflows/build_lightning_component/publish_a_component.rst new file mode 100644 index 0000000..bb5ec75 --- /dev/null +++ b/docs/source-app/workflows/build_lightning_component/publish_a_component.rst @@ -0,0 +1,59 @@ +############################# +Publish a Lightning Component +############################# + +**Audience:** Users who want to build a Ligthtning Component (Component) to publish to the Lightning Gallery + +---- + +*********************************** +Develop a Component from a template +*********************************** + +The fastest way to build a Component that is ready to be published to the component Gallery is to use +the default template. + +Generate your Component template with this command: + +.. code:: python + + lightning init component your-component-name + +---- + +***************** +Run the Component +***************** + +To test that your Component works, first install all dependencies: + +.. code:: bash + + cd your-component + pip install -r requirements.txt + pip install -e . + +Now import your Component and use it in a Lightning App: + +.. code:: python + + # app.py + from your_component import TemplateComponent + import lightning as L + + class LitApp(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.your_component = TemplateComponent() + + def run(self): + print('this is a simple Lightning app to verify your component is working as expected') + self.your_component.run() + + app = L.LightningApp(LitApp()) + +and run the app: + +.. code:: bash + + lightning run app app.py diff --git a/docs/source-app/workflows/build_rest_api/add_api.rst b/docs/source-app/workflows/build_rest_api/add_api.rst new file mode 100644 index 0000000..eeb91b0 --- /dev/null +++ b/docs/source-app/workflows/build_rest_api/add_api.rst @@ -0,0 +1,104 @@ +:orphan: + +############################ +Add an API Route to your App +############################ + +In order to add a new route, you need to override the :class:`~lightning.app.core.flow.LightningFlow.configure_api` hook and return a list of :class:`~lightning.app.api.:class:`~lightning.app.api.http_methods.HttpMethod` such as :class:`~lightning.app.api.:class:`~lightning.app.api.http_methods.Get`, :class:`~lightning.app.api.:class:`~lightning.app.api.http_methods.Post`, :class:`~lightning.app.api.:class:`~lightning.app.api.http_methods.Put`, :class:`~lightning.app.api.:class:`~lightning.app.api.http_methods.Delete`. + +---- + +********************** +1. Create a simple App +********************** + +We're going to create a single route ``/name`` that takes a string input ``name`` and stores the value within the ``names`` attribute of the flow state. + +Create a file called ``app.py`` and copy-paste the following code in to the file: + +.. literalinclude:: post_example.py + +---- + +************** +2. Run the App +************** + +Execute the following command in a terminal: + +.. code-block:: + + lightning run app app.py + +The following appears: + +.. code-block:: + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + +---- + +**************** +3. Check the API +**************** + +The Lightning App framework automatically generates API documentation from your App using `Swagger UI `_. + +You can access it by accessing the following URL: ``http://127.0.0.1:7501/docs`` in your browser and validate your API with the route ``/name`` directly from the documentation page as shown below. + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/rest_post.mp4 + :poster: https://pl-public-data.s3.amazonaws.com/assets_lightning/rest_png.png + :width: 600 + :class: background-video + :autoplay: + :loop: + :muted: + +Alternatively, you can invoke the route directly from a second terminal using `curl `_. + +.. code-block:: + + curl -X 'POST' \ + 'http://127.0.0.1:7501/name?name=my_name' \ + -H 'accept: application/json' \ + -d '' + + "The name my_name was registered" + +And you can see the following in your first terminal running your App. + +.. code-block:: + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + [] + ["my_name"] + +************************************** +Develop a command line interface (CLI) +************************************** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Add Requests Validation + :description: Learn how to use pydantic with your API. + :col_css: col-md-6 + :button_link: request_validation.html + :height: 150 + +.. displayitem:: + :header: Develop a Command Line Interface (CLI) + :description: Learn how to develop an CLI for your App. + :col_css: col-md-6 + :button_link: ../build_command_line_interface/index.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/build_rest_api/index.rst b/docs/source-app/workflows/build_rest_api/index.rst new file mode 100644 index 0000000..8b9192f --- /dev/null +++ b/docs/source-app/workflows/build_rest_api/index.rst @@ -0,0 +1,34 @@ +:orphan: + +########### +RESTful API +########### + +**Audience:** Users looking to create an API in their App to allow users to activate functionalities from external sources. + +---- + +********************** +What is a RESTful API? +********************** + +A RESTful API is a set of external URL routes exposed by a server that enables clients to trigger some functionalities, such as getting or putting some data, uploading files, etc.. + +This provides great flexibility for users as they can easily discover functionalities made available by the App Builders. + +The Lightning App framework supports the four primary HTTP methods: `GET`, `POST`, `PUT`, `DELETE`. + +These methods are guidelines to organize your RESTful Services and help users understand your functionalities. + +* **`GET`:** Reads data from the server. +* **`POST`:** Creates new resources. +* **`PUT`:** Updates/replaces existing resources. +* **`DELETE`:** Deletes resources. + +Learn more about `HTTP Methods for RESTful Services here `_. + +The Lightning App framework uses the popular `FastAPI `_ and `Pydantic `_ frameworks under the hood. This means you can use all their features while building your App. + +---- + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/build_rest_api/index_content.rst b/docs/source-app/workflows/build_rest_api/index_content.rst new file mode 100644 index 0000000..9f77225 --- /dev/null +++ b/docs/source-app/workflows/build_rest_api/index_content.rst @@ -0,0 +1,50 @@ +************** +Develop an API +************** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Add an API Route to your App + :description: Learn how to develop a simple API for your App. + :col_css: col-md-6 + :button_link: add_api.html + :height: 150 + +.. displayitem:: + :header: Add Requests Validation + :description: Learn how to use pydantic with your API. + :col_css: col-md-6 + :button_link: cli_client.html + :height: 150 + +.. raw:: html + +
+
+ +---- + +********** +Learn more +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Develop a Command-line Interface + :description: Learn how to develop an CLI for your App. + :col_css: col-md-6 + :button_link: ../build_command_line_interface/index.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/build_rest_api/models.py b/docs/source-app/workflows/build_rest_api/models.py new file mode 100644 index 0000000..7ebb3ac --- /dev/null +++ b/docs/source-app/workflows/build_rest_api/models.py @@ -0,0 +1,6 @@ +from pydantic import BaseModel + + +# 1. Subclass the BaseModel and defines your payload format. +class NamePostConfig(BaseModel): + name: str diff --git a/docs/source-app/workflows/build_rest_api/post_example.py b/docs/source-app/workflows/build_rest_api/post_example.py new file mode 100644 index 0000000..0ba2405 --- /dev/null +++ b/docs/source-app/workflows/build_rest_api/post_example.py @@ -0,0 +1,25 @@ +import lightning as L +from lightning.app.api import Post + + +class Flow(L.LightningFlow): + # 1. Define the state + def __init__(self): + super().__init__() + self.names = [] + + # 2. Optional, but used to validate names + def run(self): + print(self.names) + + # 3. Method executed when a request is received. + def handle_post(self, name: str): + self.names.append(name) + return f'The name {name} was registered' + + # 4. Defines this Component's Restful API. You can have several routes. + def configure_api(self): + return [Post(route="/name", method=self.handle_post)] + + +app = L.LightningApp(Flow()) diff --git a/docs/source-app/workflows/build_rest_api/post_example_pydantic.py b/docs/source-app/workflows/build_rest_api/post_example_pydantic.py new file mode 100644 index 0000000..f32dc3f --- /dev/null +++ b/docs/source-app/workflows/build_rest_api/post_example_pydantic.py @@ -0,0 +1,32 @@ +from models import NamePostConfig # 2. Import your custom model. + +import lightning as L +from lightning.app.api import Post + + +class Flow(L.LightningFlow): + # 1. Define the state + def __init__(self): + super().__init__() + self.names = [] + + # 2. Optional, but used to validate names + def run(self): + print(self.names) + + # 3. Annotate your input with your custom pydantic model. + def handle_post(self, config: NamePostConfig): + self.names.append(config.name) + return f'The name {config} was registered' + + # 4. Defines this Component's Restful API. You can have several routes. + def configure_api(self): + return [ + Post( + route="/name", + method=self.handle_post, + ) + ] + + +app = L.LightningApp(Flow()) diff --git a/docs/source-app/workflows/build_rest_api/request_validation.rst b/docs/source-app/workflows/build_rest_api/request_validation.rst new file mode 100644 index 0000000..6caaccd --- /dev/null +++ b/docs/source-app/workflows/build_rest_api/request_validation.rst @@ -0,0 +1,69 @@ +:orphan: + +*********************** +Add Requests Validation +*********************** + +The Lightning App framework uses the popular `FastAPI `_ and `Pydantic `_ frameworks under the hood. This means you can use all their features while building your App. + +pydantic enables fast data validation and settings management using Python type annotations and FastAPI is a modern, fast (high-performance), web framework for building APIs. + +You can easily use pydantic by defining your own payload format. + +.. literalinclude:: models.py + +Then, type your handler input with your custom model. + +.. literalinclude:: post_example_pydantic.py + +After running the updated App, the App documentation ``/name`` has changed and takes JSON with ``{"name": ...}`` as input. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/rest_post_pydantic.png + :alt: Rest API with pydantic + :width: 100 % + +You can invoke the RESTful API route ``/name`` with the following command: + +.. code-block:: bash + + curl -X 'POST' \ + 'http://127.0.0.1:7501/name' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "my_name" + }' + +.. note:: + + Using curl, you can pass a JSON payload using the ``-d`` argument. + +---- + +********** +Learn more +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Add an API Route to your App + :description: Learn how to develop a simple API for your App. + :col_css: col-md-6 + :button_link: add_api.html + :height: 150 + +.. displayitem:: + :header: Develop a Command Line Interface (CLI) + :description: Learn how to develop an CLI for your App. + :col_css: col-md-6 + :button_link: ../build_command_line_interface/index.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/byoc/aws_cli.rst b/docs/source-app/workflows/byoc/aws_cli.rst new file mode 100644 index 0000000..4bfea35 --- /dev/null +++ b/docs/source-app/workflows/byoc/aws_cli.rst @@ -0,0 +1,135 @@ +:orphan: + +.. _aws_cli: + + +############################ +Create AWS role with AWS CLI +############################ + +1. Install AWS CLI (see instructions `here `_). + +2. Protect your role by creating a hard to guess password that will be used to authenticate Lightning (You will need to pass it to Lightning for authentication). In our example we will use `dummy`. + +3. Create a role called `lightning-cloud` using the following command (replace with your own): + +.. code:: bash + + aws iam create-role \ + --role-name lightning-cloud \ + --assume-role-policy-document '{"Statement":[{"Action":"sts:AssumeRole","Effect": "Allow", "Principal": {"AWS": "arn:aws:iam::748115360335:root"}, "Condition": {"StringEquals": {"sts:ExternalId": ""}}}]}' \ + --description " " \ + --max-session-duration 43200 + +4. Create a file `iam-policy.json` with the following permissions required for Lightning to manage cloud infrastructure for you: + +.. code:: json + + { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "autoscaling:*", + "cloudwatch:*", + "codebuild:*", + "ec2:*", + "ecr:*", + "eks:*", + "elasticloadbalancing:*", + "events:*", + "guardduty:*", + "iam:*", + "logs:*", + "route53resolver:*", + "s3:*", + "sns:*", + "sqs:*", + "tag:GetResources", + "resource-groups:SearchResources" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": "iam:CreateServiceLinkedRole", + "Resource": "*", + "Condition": { + "StringLike": { + "iam:AWSServiceName": [ + "guardduty.amazonaws.com", + "malware-protection.guardduty.amazonaws.com" + ] + } + } + }, + { + "Effect": "Allow", + "Action": "iam:CreateServiceLinkedRole", + "Resource": "*", + "Condition": { + "StringEquals": { + "iam:AWSServiceName": [ + "autoscaling.amazonaws.com", + "ec2scheduled.amazonaws.com", + "elasticloadbalancing.amazonaws.com", + "spot.amazonaws.com", + "spotfleet.amazonaws.com", + "transitgateway.amazonaws.com" + ] + } + } + } + ] + } + +5. Create a IAM policy and associate it with the role we just created, and pass in the path to your new file: + +.. code:: bash + + aws iam create-policy \ + --policy-name lightning-cloud \ + --description "policy granting lightning controlplane permissions" \ + --policy-document file:///my_dir/iam-policy.json + +6. Fetch the role ARN so you can attach the policy: + +.. code:: bash + + aws iam get-role --role-name lightning-cloud --output json --query Role.Arn + +7. Attach the policy to the IAM role you just created: + +.. code:: bash + + aws iam attach-role-policy \ + --role-name lightning-cloud \ + --policy-arn arn:aws:iam::1234567890:policy/lightning-cloud + +------ + +********************** +Next: Create a cluster +********************** + +You are now ready to create a Lightning cluster! + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Create cluster + :description: Create an AWS cluster for running ligthning apps, skip to step 2 + :button_link: create_cluster.html + :col_css: col-md-12 + :height: 170 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/byoc/aws_console.rst b/docs/source-app/workflows/byoc/aws_console.rst new file mode 100644 index 0000000..75877f9 --- /dev/null +++ b/docs/source-app/workflows/byoc/aws_console.rst @@ -0,0 +1,101 @@ +:orphan: + +.. _aws_console: + + +########################################### +Create AWS role with AWS management console +########################################### + +See the following video for instructions: + +.. video:: https://lightningaidev.wpengine.com/wp-content/uploads/2022/12/byoc.mp4 + :width: 600 + :autoplay: + :loop: + :muted: + + +1.1- Create a role +------------------ + +The created role will give Lightning AI access (by specifying Ligthning account id), and will + +1. Sign in to your AWS account. (If you don’t have an AWS account, create one). + +2. On AWS console search for IAM > Roles > Create role. + + In your new role set the following: + + * Trusted entity type -> AWS account. + + * An AWS account -> Another AWS account -> Account id: 748115360335. (This is Lightning's account id). + +3. Protect your role by creating a hard to guess password that will be used to authenticate Lightning (You will need to pass it to Lightning for authnetication). + + * Set Require external ID: + +4. Click "Next" + + +1.2- Create a policy +-------------------- + +1. Click "Create policy" -> A new tab opens to the Create policy page. + +2. Click the JSON tab and copy-paste the `contents of this JSON file ` into the JSON workspace. + +.. note: Ignore the warnings generated by AWS. + +3. Click "Next:Tags" (you can skip this step) + +4. Complete policy creation by adding a meaningful name and description to the policy. + + For example: + + Name -> "lightning-cloud" + + Description -> "permission to manage EC2 instances" + +5. Add policy to the role: Return to the browser tab that you were using to create a role. +Refresh the policy list and select the custom policy you just created (in this case “lai-byoc-policy”). The policy should appear at the top of the Policy List. + +6. Click "Next". + +7. Provide a meaningful name and description for the role, and click “Create Role”. + + For example: + + Name -> "lightning-cloud" + + Description -> "Role for Lightning cloud permissions" + +Congrats! You now successfully added the set up permissions needed for lightning to create a cluster. + + +------ + +********************** +Next: Create a cluster +********************** + +You are now ready to create a Lightning cluster! + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Create cluster + :description: Create an AWS cluster for running ligthning apps, skip to step 2 + :button_link: create_cluster.html + :col_css: col-md-12 + :height: 170 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/byoc/create_cluster.rst b/docs/source-app/workflows/byoc/create_cluster.rst new file mode 100644 index 0000000..4e7994b --- /dev/null +++ b/docs/source-app/workflows/byoc/create_cluster.rst @@ -0,0 +1,146 @@ +:orphan: + +.. _create_cluster: + + +################## +Create AWS cluster +################## + +**Audience:** Users looking to create a cluster to run Lightning Apps on their own private cloud infrastructure. + +**Prereqs:** basic familiarity with cloud provider infrastructure management. + +.. note:: This feature is currently available for early access! To create your own cluster `contact us `_. + + +---- + +******************************************* +Step 1- Create roles and permissions on AWS +******************************************* + +In this step you’ll be creating a role on your cloud provider that allows Lightning to manage resources on your behalf (for example, creating EC2 instances for your cluster). +To do this you can use the AWS CLI or the AWS management console. + +You will only have to preform this step once, and the same role can be used to create multiple clusters. + +---- + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Create role with AWS CLI + :description: Create role with AWS CLI + :col_css: col-md-4 + :button_link: aws_cli.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Create role with AWS console + :description: Create role with AWS console + :col_css: col-md-4 + :button_link: aws_console.html + :height: 180 + :tag: Basic + +.. raw:: html + +
+
+ + +---- + + +**************************** +Step 2- Get ARN for new role +**************************** + +To start a cluster, Lightning will need the external-id you set in step 1, and the ARN role. Click on your new role to find them (note: you need the ARN listed at the top of the page, not the one in “trusted entitles”). + + +Record the ARN and the external ID. You’ll need them for your next step. + +----- + +************************************** +Step 3-Create a Lightning BYOC cluster +************************************** + +Now that you have created a role and policy on AWS, you can start creating Lightning clusters. + +Create a Lightning BYOC cluster using the following command: + +.. code:: bash + + lightning create cluster --role-arn --external-id + +Here's an example: + +.. code:: bash + + lightning create cluster my-byoc-cluster --role-arn arn:aws:iam::1234567890:role/lai-byoc --external-id dummy + +.. note:: Cluster creation is going to take an hour or more after you run this command. +.. note:: Only us-east-1, us-east-2, us-west-1 and us-west-2 are supported today. + + +Parameters +========== + ++------------------------+----------------------------------------------------------------------------------------------------+ +|Parameter | Description | ++========================+====================================================================================================+ +| cluster_id | The name of the cluster to be created. | +| | | +| | Cluster names can only contain lowercase letters, numbers, and periodic hyphens ( - ). | ++------------------------+----------------------------------------------------------------------------------------------------+ +| role-arn | AWS IAM Role ARN used to provision resources | ++------------------------+----------------------------------------------------------------------------------------------------+ +| external-id | AWS IAM Role external ID | +| | | +| | To read more on what the AWS external ID is and why it's useful go | +| | `here `_| ++------------------------+----------------------------------------------------------------------------------------------------+ + +---- + +******************************************* +View a list of your Lightning BYOC clusters +******************************************* + +.. code:: bash + + lightning list clusters + +--- + +****************************** +Next: Run apps on your cluster +****************************** + +Once your cluster is running, you can start running Lightning apps on your cluster. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Run apps on your cluster + :description: Learn how to start apps on your Lightning cluster + :button_link: run_on_cluster.html + :col_css: col-md-12 + :height: 170 + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/byoc/delete_cluster.rst b/docs/source-app/workflows/byoc/delete_cluster.rst new file mode 100644 index 0000000..c71a7c9 --- /dev/null +++ b/docs/source-app/workflows/byoc/delete_cluster.rst @@ -0,0 +1,28 @@ +:orphan: + +.. _delete_cluster: + + +******************************* +Delete a Lightning BYOC cluster +******************************* + +Once you no longer need a Lightning cluster you can delete it with the following command: + +.. code:: bash + + lightning delete cluster + +Deleting a cluster will remove any apps data from Lighting (including logs and metadata) and all reources associated with the cluster. Any artifacts created in the object storage of your cluster will not be deleted. + +.. warning:: Using the ``--force`` parameter when deleting a cluster does not clean up any resources managed by Lightning AI. Check your cloud provider to verify that existing cloud resources are deleted. + +.. warning:: This process may take a few minutes to complete, but once started it CANNOT be rolled back. Deletion permanently removes not only the BYOC cluster from being managed by Lightning AI, but tears down every BYOC resource Lightning AI managed (for that cluster id) in the host cloud. All object stores, container registries, logs, compute nodes, volumes, etc. are deleted and cannot be recovered. + +.. warning:: + + Under the hood the deletion selects cloud provider resources via the tags + `lightning/cluster` and + `kubernetes.io/cluster/` + + Do not use these tags in any cloud resources you create yourself, as they will be subject to deletion when the cluster is deleted. diff --git a/docs/source-app/workflows/byoc/index.rst b/docs/source-app/workflows/byoc/index.rst new file mode 100644 index 0000000..494a700 --- /dev/null +++ b/docs/source-app/workflows/byoc/index.rst @@ -0,0 +1,60 @@ +.. _byoc: + +################################# +Run Apps on your own cloud (BYOC) +################################# + +**Audience:** Users looking to run Lightning Apps on their own private cloud infrastructure. + +.. note:: This feature is currently available for early access! To create your own cluster `contact us `_. + + +---- + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Create an AWS cluster + :description: Create an AWS cluster + :col_css: col-md-4 + :button_link: create_cluster.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Run app on your cluster + :description: How to run apps on your Lighnting Cluster + :col_css: col-md-4 + :button_link: run_on_cluster.html + :height: 180 + :tag: Basic + +.. displayitem:: + :header: Delete a cluster + :description: Delete a cluster + :col_css: col-md-4 + :button_link: delete_cluster.html + :height: 180 + :tag: Basic + +.. raw:: html + +
+
+ + +---- + + + +******************** +Why create a cluster +******************** + +You can use Lightning clusters to run Lightning apps on your own cloud provider account in order to protect your data and use your cloud provider's credits. The control for these clusters runs on the Lightning managed cloud, but the data plane, including the clusters, services, and apps, is located within your own cloud provider account. + +Once the cluster is created, Lightning Cloud controlplane will take over, +managing the lifecycle of the cloud infrastructure required to run Lightning Apps. diff --git a/docs/source-app/workflows/byoc/run_on_cluster.rst b/docs/source-app/workflows/byoc/run_on_cluster.rst new file mode 100644 index 0000000..209ab41 --- /dev/null +++ b/docs/source-app/workflows/byoc/run_on_cluster.rst @@ -0,0 +1,27 @@ +:orphan: + +.. _run_on_cluster: + + +********************************* +Run app on your Lightning Cluster +********************************* + +Once your cluster is running, you can run any Lightning App on your cluster. To run an App on the Lightning BYOC cluster, use ``--cloud –cluster-id `` in the command: + +.. code:: bash + + lightning run app app.py --cloud --cluster-id + +Here’s an example: + +.. code:: bash + + lightning run app app.py --cloud --cluster-id my-byoc-cluster + + +View the status of your App using the following command: + +.. code:: bash + + lightning list apps diff --git a/docs/source-app/workflows/debug_locally.rst b/docs/source-app/workflows/debug_locally.rst new file mode 100644 index 0000000..cd5a5a8 --- /dev/null +++ b/docs/source-app/workflows/debug_locally.rst @@ -0,0 +1,5 @@ +:orphan: + +##################################### +Debug a Distributed Cloud App Locally +##################################### diff --git a/docs/source-app/workflows/enable_fault_tolerance.rst b/docs/source-app/workflows/enable_fault_tolerance.rst new file mode 100644 index 0000000..b1630d4 --- /dev/null +++ b/docs/source-app/workflows/enable_fault_tolerance.rst @@ -0,0 +1,5 @@ +:orphan: + +###################### +Enable Fault Tolerance +###################### diff --git a/docs/source-app/workflows/extend_app.rst b/docs/source-app/workflows/extend_app.rst new file mode 100644 index 0000000..2e17af4 --- /dev/null +++ b/docs/source-app/workflows/extend_app.rst @@ -0,0 +1,59 @@ +###################### +Extend an Existing App +###################### +You can extend a Lightning App by using community components or building your own. + +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Add more Components + :description: Extend an App by adding a prebuilt component. + :col_css: col-md-4 + :button_link: add_components/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Add a web user interface (UI) + :description: Extend an App by adding a web user interface (UI) + :col_css: col-md-4 + :button_link: add_web_ui/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Add a URL link + :description: Extend an App by adding a web URL link + :col_css: col-md-4 + :button_link: add_web_link.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Build a Component + :description: Extend an App by building a Component + :col_css: col-md-4 + :button_link: build_lightning_component/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Add a server + :description: Extend an App by adding a server to a Component. + :col_css: col-md-4 + :button_link: add_server/index.html + :height: 150 + :tag: Intermediate + + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/index.rst b/docs/source-app/workflows/index.rst new file mode 100644 index 0000000..801c6cc --- /dev/null +++ b/docs/source-app/workflows/index.rst @@ -0,0 +1,202 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + access_app_state/access_app_state + add_web_ui/index + add_web_link + secrets <../glossary/secrets> + arrange_tabs/index + connect components <../levels/intermediate/connect_lightning_components> + build components <../levels/basic/build_a_lightning_component> + run_work_once + cloud compute <../core_api/lightning_work/compute> + build_command_line_interface/index + rest API <../glossary/restful_api/restful_api> + extend_app + build_lightning_component/publish_a_component + byoc/index + ssh/index + add_server/index + run_app_on_cloud/index + run_work_in_parallel + drive <../glossary/storage/drive> + share_app + share_files_between_components + +####### +How to: +####### + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Access the App State + :description: Learn to work with the app state + :col_css: col-md-4 + :button_link: access_app_state/access_app_state.html + :height: 180 + +.. displayitem:: + :header: Add a web user interface + :description: Learn how to add React, StreamLit, Dash to your App. + :col_css: col-md-4 + :button_link: add_web_ui/index.html + :height: 180 + +.. displayitem:: + :header: Add a web link + :description: Learn how to embed external websites + :col_css: col-md-4 + :button_link: add_web_link.html + :height: 180 + +.. displayitem:: + :header: Add encrypted secrets + :description: Learn how to organize your UI + :col_css: col-md-4 + :button_link: ../glossary/secrets.html + :height: 180 + +.. displayitem:: + :header: Arrange App tabs + :description: Learn how to organize your UI + :col_css: col-md-4 + :button_link: arrange_tabs/index.html + :height: 180 + +.. displayitem:: + :header: Build a Lightning App + :description: Simple App to get started + :col_css: col-md-4 + :button_link: ../levels/basic/connect_lightning_components.html + :height: 180 + +.. displayitem:: + :header: Build a Lightning Component + :description: Understand how to separated the glue from the actual work + :col_css: col-md-4 + :button_link: ../levels/basic/build_a_lightning_component.html + :height: 180 + +.. displayitem:: + :header: Cache Work run calls + :description: Understand how to trigger a work run method + :col_css: col-md-4 + :button_link: run_work_once.html + :height: 180 + +.. displayitem:: + :header: Customize your cloud compute + :description: Select machines to run on + :col_css: col-md-4 + :button_link: ../core_api/lightning_work/compute.html + :height: 180 + +.. displayitem:: + :header: Develop a Command Line Interface (CLI) + :description: Learn to develop a CLI + :col_css: col-md-4 + :button_link: build_command_line_interface/index.html + :height: 180 + +.. displayitem:: + :header: Develop a Lightning App + :description: Learn to connect components together into a Lightning App + :col_css: col-md-4 + :button_link: ../levels/basic/connect_lightning_components.html + :height: 180 + +.. displayitem:: + :header: Develop a REST API + :description: Learn to deploy a model behind a REST API + :col_css: col-md-4 + :button_link: ../glossary/restful_api/restful_api.html + :height: 180 + +.. displayitem:: + :header: Extend an existing App + :description: Learn where to go next with an App + :col_css: col-md-4 + :button_link: extend_app.html + :height: 180 + +.. displayitem:: + :header: Publish a Lightning Component + :description: Share your components with others + :col_css: col-md-4 + :button_link: build_lightning_component/publish_a_component.html + :height: 180 + +.. displayitem:: + :header: Run Apps on your cloud account (BYOC) + :description: Share your components with others + :col_css: col-md-4 + :button_link: byoc/index.html + :height: 180 + +.. displayitem:: + :header: Run a server within a Lightning App + :description: Lightning Work can be infinite jobs + :col_css: col-md-4 + :button_link: add_server/index.html + :height: 180 + +.. displayitem:: + :header: Run an App on the cloud + :description: Learn how to get things done in the cloud with ease + :col_css: col-md-4 + :button_link: run_app_on_cloud/index.html + :height: 180 + +.. displayitem:: + :header: Run Works in parallel + :description: Learn how to make your Work non blocking + :col_css: col-md-4 + :button_link: run_work_in_parallel.html + :height: 180 + +.. displayitem:: + :header: Save files + :description: Learn how to save files in a work by using Drive + :col_css: col-md-4 + :button_link: ../glossary/storage/drive.html + :height: 180 + +.. displayitem:: + :header: Share an App + :description: Learn how to share your work with others + :col_css: col-md-4 + :button_link: share_app.html + :height: 180 + +.. displayitem:: + :header: Share files between components + :description: Learn how Lightning Storage emulates a single filesystem in a distributed setting + :col_css: col-md-4 + :button_link: share_files_between_components.html + :height: 180 + +.. displayitem:: + :header: Debug cloud apps via SSH + :description: Learn how to get SSH access to your App + :col_css: col-md-4 + :button_link: ssh/index.html + :height: 180 + +.. displayitem:: + :header: Mount Cloud Data + :description: Learn how Lightning Mounts are used to make the contents of an cloud object store bucket available on disk when running in the cloud. + :col_css: col-md-4 + :button_link: mount_cloud_object_store.html + :height: 180 + + + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/mount_cloud_object_store.rst b/docs/source-app/workflows/mount_cloud_object_store.rst new file mode 100644 index 0000000..72e6fa0 --- /dev/null +++ b/docs/source-app/workflows/mount_cloud_object_store.rst @@ -0,0 +1,141 @@ +:orphan: + +############## +Add Cloud Data +############## + +**Audience:** Users who want to read files stored in a Cloud Object Bucket in an app. + +****************************** +Mounting Public AWS S3 Buckets +****************************** + +=================== +Add Mount to a Work +=================== + +To mount data from a cloud bucket to your app compute, initialize a :class:`~lightning.app.storage.mount.Mount` +object with the source path of the s3 bucket and the absolute directory path where it should be mounted and +pass the :class:`~lightning.app.storage.mount.Mount` to the :class:`~lightning.app.utilities.packaging.cloud_compute.CloudCompute` +of the :class:`~lightning.app.core.work.LightningWork` it should be mounted on. + +In this example, we will mount an S3 bucket: ``s3://ryft-public-sample-data/esRedditJson/`` to ``/content/esRedditJson/``. + +.. code-block:: python + + from lightning.app import CloudCompute + from lightning.app.storage import Mount + + self.my_work = MyWorkClass( + cloud_compute=CloudCompute( + mounts=Mount( + source="s3://ryft-public-sample-data/esRedditJson/", + mount_path="/content/esRedditJson/", + ), + ) + ) + +You can also pass multiple mounts to a single work by passing a ``List[Mount(...), ...]`` to the +``CloudCompute(mounts=...)`` argument. + +.. note:: + + * Mounts supported up to 1 Million files, 5GB per file. Need larger mounts? Contact support@lightning.ai + * When adding multiple mounts, each one should have a unique ``mount_path``. + * A maximum of 10 :class:`~lightning.app.storage.mount.Mount`\s can be added to a :class:`~lightning.app.core.work.LightningWork`. + +======================= +Read Files From a Mount +======================= + +Once a :class:`~lightning.app.storage.mount.Mount` object is passed to :class:`~lightning.app.utilities.packaging.cloud_compute.CloudCompute`, +you can access, list, or read any file from the mount under the specified ``mount_path``, just like you would if it +was on your local machine. + +Assuming your ``mount_path`` is ``"/content/esRedditJson/"`` you can do the following: + +---------- +Read Files +---------- + +.. code-block:: python + + with open("/content/esRedditJson/esRedditJson1", "r") as f: + some_data = f.read() + + # do something with "some_data"... + +---------- +List Files +---------- + +.. code-block:: python + + files = os.listdir("/content/esRedditJson/") + +-------------------- +See the Full Example +-------------------- + +.. code-block:: python + :emphasize-lines: 10,15 + + import os + + import lightning as L + from lightning.app import CloudCompute + from lightning.app.storage import Mount + + class ReadMount(L.LightningWork): + def run(self): + # Print a list of files stored in the mounted S3 Bucket. + files = os.listdir("/content/esRedditJson/") + for file in files: + print(file) + + # Read the contents of a particular file in the bucket "esRedditJson1" + with open("/content/esRedditJson/esRedditJson1", "r") as f: + some_data = f.read() + # do something with "some_data"... + + class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.my_work = ReadMount( + cloud_compute=CloudCompute( + mounts=Mount( + source="s3://ryft-public-sample-data/esRedditJson/", + mount_path="/content/esRedditJson/", + ), + ) + ) + + def run(self): + self.my_work.run() + +.. note:: + + When running a Lighting App on your local machine, any :class:`~lightning.app.utilities.packaging.cloud_compute.CloudCompute` + configuration (including a :class:`~lightning.app.storage.mount.Mount`) is ignored at runtime. If you need access to + these files on your local disk, you should download a copy of them to your machine. + +.. note:: + + Mounted files from an S3 bucket are ``read-only``. Any modifications, additions, or deletions + to files in the mounted directory will not be reflected in the cloud object store. + +---- + +********************************************** +Mounting Private AWS S3 Buckets - Coming Soon! +********************************************** + +We'll Let you know when this feature is ready! + +---- + +************************************************ +Mounting Google Cloud GCS Buckets - Coming Soon! +************************************************ + +We'll Let you know when this feature is ready! diff --git a/docs/source-app/workflows/run_app_on_cloud/cloud_files.rst b/docs/source-app/workflows/run_app_on_cloud/cloud_files.rst new file mode 100644 index 0000000..dfef0dc --- /dev/null +++ b/docs/source-app/workflows/run_app_on_cloud/cloud_files.rst @@ -0,0 +1,69 @@ +.. _ignore: + +################################## +Configure Your Lightning Cloud App +################################## + +**Audience:** Users who want to control Lightning App files on the cloud. + +---- + +************************************** +Ignore file uploads to Lightning cloud +************************************** +Running Lightning Apps on the cloud will upload the source code of your app to the cloud. You can use ``.lightningignore`` file(s) to ignore files or directories while uploading. The `.lightningignore` file follows the same format as a `.gitignore` +file. + +For example, the source code directory below with the ``.lightningignore`` file will ignore the file named +``model.pt`` and directory named ``data_dir``. + +.. code:: bash + + . + ├── README.md + ├── app.py + ├── data_dir + │ ├── image1.png + │ ├── image2.png + │ └── ... + ├── .lightningignore + ├── requirements.txt + └── model.pt + +.. code:: bash + + ~/project/home ❯ cat .lightningignore + model.pt + data_dir + +A sample ``.lightningignore`` file can be found `here `_. + +If you are a component author and your components creates local files that you want to ignore, you can do: + +.. code-block:: python + + class MyComponent(L.LightningWork): # or L.LightningFlow + def __init__(self): + super().__init__() + self.lightningignore = ("model.pt", "data_dir") + + +This has the benefit that the files will be ignored automatically for all the component users, making an easier +transition between running locally vs in the cloud. + +---- + +******************* +Structure app files +******************* + +We recommend your app contain the following files: + +.. code:: bash + + . + ├── .lightning (auto-generated- conatins Lightning configuration) + ├── .lightningignore (contains files not to upload to the cloud) + ├── app.py + ├── README.md (optional- a markdown description of your app) + └── requirements.txt (optional- conatins all your app dependencies) diff --git a/docs/source-app/workflows/run_app_on_cloud/index.rst b/docs/source-app/workflows/run_app_on_cloud/index.rst new file mode 100644 index 0000000..55bc3b6 --- /dev/null +++ b/docs/source-app/workflows/run_app_on_cloud/index.rst @@ -0,0 +1,5 @@ +##################### +Run apps on the cloud +##################### + +.. include:: index_content.rst diff --git a/docs/source-app/workflows/run_app_on_cloud/index_content.rst b/docs/source-app/workflows/run_app_on_cloud/index_content.rst new file mode 100644 index 0000000..b0f6757 --- /dev/null +++ b/docs/source-app/workflows/run_app_on_cloud/index_content.rst @@ -0,0 +1,115 @@ +.. _run_app_in_cloud: + +.. toctree:: + :maxdepth: 1 + :hidden: + + cloud_files + lightning_cloud + on_prem + on_your_own_machine + +**Audience:** Users who want to share or scale Lightning Apps. + +---- + +***************************** +Run on Lightning Public Cloud +***************************** + +You can run Lightning Apps for free on the Public Lightning cloud with a single flag! + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Run on Lightning Cloud + :description: Learn how to run on the Lightning public cloud + :col_css: col-md-4 + :button_link: lightning_cloud.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Choose Hardware + :description: Configure you app cloud resources + :col_css: col-md-4 + :button_link: ../../core_api/lightning_work/compute.html + :height: 150 + :tag: Basic + +.. displayitem:: + :header: Set Environment Variables + :description: Manage your environment variables in the cloud + :col_css: col-md-4 + :button_link: ../../glossary/environment_variables.html + :height: 150 + :tag: Basic + +.. displayitem:: + :header: Configure Your Lightning Cloud App + :description: Customize your cloud apps files + :col_css: col-md-4 + :button_link: cloud_files.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Manage App Dependancies + :description: Configure your python requirements or use a custom docker image + :col_css: col-md-4 + :button_link: ../../glossary/build_config/build_config.html + :height: 150 + :tag: Intermediate + +.. displayitem:: + :header: Share Files Between Works + :description: Learn more about data transfering + :col_css: col-md-4 + :button_link: ../../glossary/storage/storage.html + :height: 150 + :tag: Intermediate + +.. raw:: html + +
+
+ +---- + +************ +Other Clouds +************ + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Run On Your Own Machine + :description: Run Lightning Apps on any machine + :col_css: col-md-4 + :button_link: on_your_own_machine.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Run On Your Private Cloud + :description: Run Lightning Apps on your own cloud + :col_css: col-md-4 + :button_link: on_prem.html + :height: 150 + :tag: basic + + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/run_app_on_cloud/lightning_cloud.rst b/docs/source-app/workflows/run_app_on_cloud/lightning_cloud.rst new file mode 100644 index 0000000..aaa9d7b --- /dev/null +++ b/docs/source-app/workflows/run_app_on_cloud/lightning_cloud.rst @@ -0,0 +1,67 @@ +####################### +Run an App on the Cloud +####################### + +**Audience:** Users who want to share their apps or run on specialized hardware (like GPUs). + +---- + +********************************* +Run on the public Lightning cloud +********************************* +To run any app on the public lightning cloud use the ``--cloud`` argument: + +.. code:: bash + + lightning run app app.py --cloud + + +.. note:: + By default, running your apps on the public Lightning cloud is free of charge using default CPUs, and any app uploaded to the Lightning cloud will be shared with the community (source code and app view will be public). If you would like to make your apps private please `contact us `_. + +If your app contains ``LightningWork`` components that require more compute resources, such as larger CPUs or **GPUs**, you'll need to add credits to your Lightning AI account. + + +---- + +************************** +Add dependencies to my app +************************** + + +Add all dependencies required to run your app to a `requirements.txt` file in your app's directory. Read :ref:`build_config` for more details. + + + +---- + + +******** +Name app +******** + +Simply use the ``--name`` flag when running your app, for example: + +.. code:: bash + + lightning run app app.py --cloud --name my-awesome-app + +Alternatively, you can change the name of the app in the ``.lightning`` file: + +.. code:: bash + + ~/project/home ❯ cat .lightning + name: my-awesome-app + +The ``.lightning`` file is a general configuration file. +To learn more about optional configuration file parameters, see :class:`~lightning.utilities.packaging.app_config.AppConfig`. + +------ + +******************** +Choose Cloud Compute +******************** + +You can configure the hardware your app is running on by setting a :class:`~lightning.utilities.packaging.cloud_compute.CloudCompute` object onto the ``cloud_compute`` property of your work's. + +Learn more with the :ref:`cloud_compute` guide diff --git a/docs/source-app/workflows/run_app_on_cloud/on_prem.rst b/docs/source-app/workflows/run_app_on_cloud/on_prem.rst new file mode 100644 index 0000000..be0a954 --- /dev/null +++ b/docs/source-app/workflows/run_app_on_cloud/on_prem.rst @@ -0,0 +1,6 @@ +########################### +Run an App on Private Cloud +########################### + + +To run Lightning apps on a private or on-prem cluster, `contact us `_. diff --git a/docs/source-app/workflows/run_app_on_cloud/on_your_own_machine.rst b/docs/source-app/workflows/run_app_on_cloud/on_your_own_machine.rst new file mode 100644 index 0000000..8226a1a --- /dev/null +++ b/docs/source-app/workflows/run_app_on_cloud/on_your_own_machine.rst @@ -0,0 +1,26 @@ +####################### +Run on your own machine +####################### + +**Audience:** Users who want to run Lightning App on a remote machine. + +---- + +*********** +Run via ssh +*********** +To run a Lightning App on any machine, simply ssh to the machine and run the app directly + +.. code:: bash + + # Copy over credentials from your local machine to your cloud machine + scp ~/.lightning/credentials.json your_name@your_cloud_machine:~/.lightning + + # log into your cloud machine + ssh your_name@your_cloud_machine + + # get your code on the machine and install deps + ... + + # start the app + lightning run app app.py diff --git a/docs/source-app/workflows/run_app_snippet.rst b/docs/source-app/workflows/run_app_snippet.rst new file mode 100644 index 0000000..57d2ae0 --- /dev/null +++ b/docs/source-app/workflows/run_app_snippet.rst @@ -0,0 +1,33 @@ +:orphan: + +*********** +Run the app +*********** + +.. raw:: html + +
+
+ +Run the app with the ``run`` command + +.. code:: bash + + lightning run app app.py + +.. raw:: html + +
+
+ + +Add the ``--cloud`` argument to run on the `lightning cloud `_. + +.. code:: bash + + lightning run app app.py --cloud + +.. raw:: html + +
+
diff --git a/docs/source-app/workflows/run_components_on_different_hardware.rst b/docs/source-app/workflows/run_components_on_different_hardware.rst new file mode 100644 index 0000000..9685c34 --- /dev/null +++ b/docs/source-app/workflows/run_components_on_different_hardware.rst @@ -0,0 +1,5 @@ +:orphan: + +#################################### +Run components on different hardware +#################################### diff --git a/docs/source-app/workflows/run_on_private_cloud.rst b/docs/source-app/workflows/run_on_private_cloud.rst new file mode 100644 index 0000000..84d64e9 --- /dev/null +++ b/docs/source-app/workflows/run_on_private_cloud.rst @@ -0,0 +1,26 @@ +:orphan: + +###################### +Run on a private cloud +###################### +**Audience:** Users looking to run Lightning apps on their private cloud accounts. + +---- + +****************************** +Run on a private cloud account +****************************** +For enterprise, startups and University use-cases, Lightning AI can run on your own AWS account (with your own credentials), with all the infrastructure fully managed by us. +To enable this, contact our support team to get started: + +onprem@lightning.ai + +---- + + +*********** +Run on-prem +*********** +For enterprise-level security with full control of the Lightning AI system on your own on-prem cluster, contact our support team to get started: + +onprem@lightning.ai diff --git a/docs/source-app/workflows/run_work_in_parallel.rst b/docs/source-app/workflows/run_work_in_parallel.rst new file mode 100644 index 0000000..90c7b7b --- /dev/null +++ b/docs/source-app/workflows/run_work_in_parallel.rst @@ -0,0 +1,10 @@ +############################# +Run LightningWork in parallel +############################# +**Audience:** Users who want to run a LightningWork in parallel (asynchroneously). + +**Prereqs:** You must have finished the `Basic levels <../basic/>`_. + +---- + +.. include:: run_work_in_parallel_content.rst diff --git a/docs/source-app/workflows/run_work_in_parallel_content.rst b/docs/source-app/workflows/run_work_in_parallel_content.rst new file mode 100644 index 0000000..1c8d5b3 --- /dev/null +++ b/docs/source-app/workflows/run_work_in_parallel_content.rst @@ -0,0 +1,41 @@ + + + +************************************ +When to run a Components in parallel +************************************ +Run LightningWork in parallel when you want to execute work in the background or at the same time as another work. +An example of when this comes up in machine learning is when data streams-in while a model trains. + +---- + +************ +Toy example +************ +By default, a Component must complete before the next one runs. We can enable one +component to start in parallel which allows the code to proceed without having +to wait for the first one to finish. + +.. lit_tabs:: + :descriptions: No parallel components; Allow the train component to run in parallel; When the component runs, it will run in parallel; The next component is unblocked and can now immediately run. + :code_files: /workflows/scripts/parallel/toy_app.py; /workflows/scripts/parallel/toy_parallel.py; /workflows/scripts/parallel/toy_parallel.py; /workflows/scripts/parallel/toy_parallel.py; + :highlights: ; 18; 23; 24; + :enable_run: true + :tab_rows: 3 + :height: 540px + +---- + +******************************* +Multiple components in parallel +******************************* +In this example, we start all 3 components at once. The first two start in parallel, which +allows the third component to run without waiting for the others to finish. + +.. lit_tabs:: + :descriptions: No parallel components; Enable 2 components to run in parallel; Start both components together in parallel; Last component is not blocked and can start immediately. + :code_files: /workflows/scripts/parallel/toy_two_parallel_not_started.py; /workflows/scripts/parallel/toy_two_parallel.py; /workflows/scripts/parallel/toy_two_parallel.py; /workflows/scripts/parallel/toy_two_parallel.py + :highlights: ; 18, 19; 23, 24; 25 + :enable_run: true + :tab_rows: 3 + :height: 540px diff --git a/docs/source-app/workflows/run_work_once.rst b/docs/source-app/workflows/run_work_once.rst new file mode 100644 index 0000000..240cde3 --- /dev/null +++ b/docs/source-app/workflows/run_work_once.rst @@ -0,0 +1,13 @@ +########################## +Cache LightningWork Runs +########################## + +**Audience:** Users who want to know how ``LightningWork`` works. + +**Level:** Advanced + +**Prereqs**: Level 16+ and read the `Event Loop guide <../glossary/event_loop.html>`_. + +---- + +.. include:: run_work_once_content.rst diff --git a/docs/source-app/workflows/run_work_once_content.rst b/docs/source-app/workflows/run_work_once_content.rst new file mode 100644 index 0000000..c1890fe --- /dev/null +++ b/docs/source-app/workflows/run_work_once_content.rst @@ -0,0 +1,151 @@ + +******************************************************** +What caching the calls of Work's run method does for you +******************************************************** + +By default, the run method in a LightningWork (Work) "remembers" (caches) the input arguments it is getting called with and does not execute again if called with the same arguments again. +In other words, the run method only executes when the input arguments have never been seen before. + +You can turn caching on or off: + +.. code-block:: python + + # Run only when the input arguments change (default) + work = MyWork(cache_calls=True) + + # Run everytime regardless of whether input arguments change or not + work = MyWork(cache_calls=False) + +To better understand this, imagine that every day you want to sequentially download and process some data and then train a model on that data. +As explained in the `Event Loop guide <../glossary/event_loop.html>`_, the Lightning App runs within an infinite while loop, so the pseudo-code of your application might looks like this: + +.. code-block:: python + + from datetime import datetime + + # Lightning code + while True: # This is the Lightning Event Loop + + # Your code + today = datetime.now().strftime("%D") # '05/25/22' + data_processor.run(today) + train_model.run(data_processor.data) + +In this scenario, you want your components to run ``once`` a day, and no more than that! But your code is running within an infinite loop, how can this even work? +This is where the Work's internal caching mechanism comes in. By default, Lightning caches a hash of the input provided to its run method and won't re-execute the method if the same input is provided again. +In the example above, the **data_processor** component run method receives the string **"05/25/22"**. It runs one time and any further execution during the day is skipped until tomorrow is reached and the work run method receives **06/25/22**. This logic applies everyday. +This caching mechanism is inspired from how `React.js Components and Props `_ renders websites. Only changes to the inputs re-trigger execution. + +*************** +Caching Example +*************** + +Here's an example of this behavior with LightningWork: + +.. code:: python + :emphasize-lines: 11, 17 + + import lightning as L + + + class ExampleWork(L.LightningWork): + def run(self, *args, **kwargs): + print(f"I received the following props: args: {args} kwargs: {kwargs}") + + + work = ExampleWork() + work.run(value=1) + + # Providing the same value. This won't run as already cached. + work.run(value=1) + work.run(value=1) + work.run(value=1) + work.run(value=1) + + # Changing the provided value. This isn't cached and will run again. + work.run(value=10) + +And you should see the following by running the code above: + +.. code-block:: console + + $ python example.py + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + # After you have clicked `run` on the UI. + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 10} + +As you can see, the intermediate run didn't execute, as we would expected when ``cache_calls=True``. + +*********************************** +Implications of turning caching off +*********************************** + +By setting ``cache_calls=False``, Lightning won't cache the return value and re-execute the run method on every call. + +.. code:: python + :emphasize-lines: 7 + + from lightning.app import LightningWork + + + class ExampleWork(LightningWork): + def run(self, *args, **kwargs): + print(f"I received the following props: args: {args} kwargs: {kwargs}") + + + work = ExampleWork(cache_calls=False) + work.run(value=1) + + # Providing the same value. This won't run as already cached. + work.run(value=1) + work.run(value=1) + work.run(value=1) + work.run(value=1) + + # Changing the provided value. This isn't cached and will run again. + work.run(value=10) + +.. code-block:: console + + $ python example.py + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + # After you have clicked `run` on the UI. + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 1} + I received the following props: args: () kwargs: {'value': 10} + +Be aware than when setting both ``cache_calls=False`` and ``parallel=False`` to a work, the code after the ``self.work.run()`` is unreachable +as the work continuously execute in a blocking way. + +.. code-block:: python + :emphasize-lines: 9-10 + + from lightning.app import LightningApp, LightningFlow, LightningWork + + + class Flow(LightningFlow): + def __init__(self): + super().__init__() + + self.work = Work(cache_calls=False, parallel=False) + + def run(self): + print("HERE BEFORE") + self.work.run() + print("HERE AFTER") + + + app = LightningApp(Flow()) + +.. code-block:: console + + $ lightning run app app.py + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + print("HERE BEFORE") + print("HERE BEFORE") + print("HERE BEFORE") + ... diff --git a/docs/source-app/workflows/schedule_apps.rst b/docs/source-app/workflows/schedule_apps.rst new file mode 100644 index 0000000..7b596cd --- /dev/null +++ b/docs/source-app/workflows/schedule_apps.rst @@ -0,0 +1,5 @@ +:orphan: + +################# +Schedule App Runs +################# diff --git a/docs/source-app/workflows/scripts/parallel/toy_app.py b/docs/source-app/workflows/scripts/parallel/toy_app.py new file mode 100644 index 0000000..05f25fd --- /dev/null +++ b/docs/source-app/workflows/scripts/parallel/toy_app.py @@ -0,0 +1,27 @@ +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, message): + for i in range(100000000000): + print(message, i) + +class AnalyzeComponent(L.LightningWork): + def run(self, message): + for i in range(100000000000): + print(message, i) + +class LitWorkflow(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute('cpu')) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute('cpu')) + + + def run(self): + self.train.run("machine A counting") + self.analyze.run("machine B counting") + + +app = L.LightningApp(LitWorkflow()) diff --git a/docs/source-app/workflows/scripts/parallel/toy_parallel.py b/docs/source-app/workflows/scripts/parallel/toy_parallel.py new file mode 100644 index 0000000..00d6178 --- /dev/null +++ b/docs/source-app/workflows/scripts/parallel/toy_parallel.py @@ -0,0 +1,27 @@ +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, message): + for i in range(100000000000): + print(message, i) + +class AnalyzeComponent(L.LightningWork): + def run(self, message): + for i in range(100000000000): + print(message, i) + +class LitWorkflow(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute('cpu'), parallel=True) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute('cpu')) + + + def run(self): + self.train.run("machine A counting") + self.analyze.run("machine B counting") + + +app = L.LightningApp(LitWorkflow()) diff --git a/docs/source-app/workflows/scripts/parallel/toy_two_parallel.py b/docs/source-app/workflows/scripts/parallel/toy_two_parallel.py new file mode 100644 index 0000000..1b04e4e --- /dev/null +++ b/docs/source-app/workflows/scripts/parallel/toy_two_parallel.py @@ -0,0 +1,27 @@ +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, message): + for i in range(100000000000): + print(message, i) + +class AnalyzeComponent(L.LightningWork): + def run(self, message): + for i in range(100000000000): + print(message, i) + +class LitWorkflow(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute('cpu'), parallel=True) + self.baseline_1 = TrainComponent(cloud_compute=L.CloudCompute('cpu'), parallel=True) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute('cpu')) + + def run(self): + self.train.run("machine A counting") + self.baseline_1.run("machine C counting") + self.analyze.run("machine B counting") + +app = L.LightningApp(LitWorkflow()) diff --git a/docs/source-app/workflows/scripts/parallel/toy_two_parallel_not_started.py b/docs/source-app/workflows/scripts/parallel/toy_two_parallel_not_started.py new file mode 100644 index 0000000..61fc9d1 --- /dev/null +++ b/docs/source-app/workflows/scripts/parallel/toy_two_parallel_not_started.py @@ -0,0 +1,27 @@ +# app.py +import lightning as L + + +class TrainComponent(L.LightningWork): + def run(self, message): + for i in range(100000000000): + print(message, i) + +class AnalyzeComponent(L.LightningWork): + def run(self, message): + for i in range(100000000000): + print(message, i) + +class LitWorkflow(L.LightningFlow): + def __init__(self) -> None: + super().__init__() + self.train = TrainComponent(cloud_compute=L.CloudCompute('cpu')) + self.baseline_1 = TrainComponent(cloud_compute=L.CloudCompute('cpu')) + self.analyze = AnalyzeComponent(cloud_compute=L.CloudCompute('cpu')) + + def run(self): + self.train.run("machine A counting") + self.baseline_1.run("machine C counting") + self.analyze.run("machine B counting") + +app = L.LightningApp(LitWorkflow()) diff --git a/docs/source-app/workflows/share_app.rst b/docs/source-app/workflows/share_app.rst new file mode 100644 index 0000000..87045bd --- /dev/null +++ b/docs/source-app/workflows/share_app.rst @@ -0,0 +1,33 @@ +############ +Share an App +############ +**Audience:** Users who want to show off their work. + +---- + +*********************************** +Option 1: Run on the cloud to share +*********************************** +To share an app, simply run your app on the cloud: + +.. code:: bash + + lightning run app app.py --cloud + +Then share the link that's generated (`like this one `_). + +---- + +********************************** +Option 2: Expose a tunnel to share +********************************** +If you'd like to share yourself, feel free to run the app in local mode +and expose the URlapp. + +Run local: + +.. code:: bash + + lightning run app app.py + +And then, use one of the many guides to `expose a tunnel `_. diff --git a/docs/source-app/workflows/share_files_between_components.rst b/docs/source-app/workflows/share_files_between_components.rst new file mode 100644 index 0000000..4eddc68 --- /dev/null +++ b/docs/source-app/workflows/share_files_between_components.rst @@ -0,0 +1,120 @@ +:orphan: + +############################## +Share Files Between Components +############################## + +.. note:: The contents of this page is still in progress! + +**Audience:** Users who want to share files between components. + +---- + +********************************** +Why do I need distributed storage? +********************************** +In a Lightning App some components can be executed on their own hardware. Distributed storage +enables a file saved by a component on one machine to be used by components in other machines (transparently). + +If you've asked the question "how do I use the checkpoint from this model to deploy this other thing", you've +needed distributed storage. + +---- + +************ +Write a file +************ +To write a file, first create a reference to the file with the :class:`~lightning.app.storage.Path` class, then write to it: + +.. code:: python + + from lightning.app.storage import Path + + # file reference + boring_file_reference = Path("boring_file.txt") + + # write to that file + with open(self.boring_file_reference, "w") as f: + f.write("yolo") + + +---- + +********** +Use a file +********** +To use a file, pass the reference to the file: + +.. code:: python + + f = open(boring_file_reference, "r") + print(f.read()) + +---- + +.. + ******************************** + Create a directory - coming soon + ******************************** + + + ---- + + ****************************** + Use a directory - coming soon + ****************************** + TODO + + ---- + +********************************* +Example: Share a model checkpoint +********************************* +A common workflow in ML is to use a checkpoint created by another component. +First, define a component that saves a checkpoint: + +.. literalinclude:: ./share_files_between_components/app.py + :lines: -19 + +Next, define a component that needs the checkpoints: + +.. literalinclude:: ./share_files_between_components/app.py + :lines: 20-31 + +Link both components via a parent component: + +.. literalinclude:: ./share_files_between_components/app.py + :lines: 32- + + +Run the app above with the following command: + +.. code-block:: bash + + lightning run app docs/source/workflows/share_files_between_components/app.py + +.. code-block:: console + + Your Lightning App is starting. This won't take long. + INFO: Your app has started. View it in your browser: http://127.0.0.1:7501/view + Loaded checkpoint_1: tensor([0, 1, 2, 3, 4]) + Loaded checkpoint_2: tensor([0, 1, 2, 3, 4]) + + +For example, here we save a file on one component and use it in another component: + +.. code:: python + + from lightning.app.storage import Path + + + class ComponentA(LightningWork): + def __init__(self): + super().__init__() + self.boring_path = None + + def run(self): + # This should be used as a REFERENCE to the file. + self.boring_path = Path("boring_file.txt") + with open(self.boring_path, "w") as f: + f.write(FILE_CONTENT) diff --git a/docs/source-app/workflows/share_files_between_components/app.py b/docs/source-app/workflows/share_files_between_components/app.py new file mode 100644 index 0000000..6b89248 --- /dev/null +++ b/docs/source-app/workflows/share_files_between_components/app.py @@ -0,0 +1,48 @@ +import os + +import torch + +import lightning as L +from lightning.app.storage import Path + + +class ModelTraining(L.LightningWork): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.checkpoints_path = Path("./checkpoints") + + def run(self): + # make fake checkpoints + checkpoint_1 = torch.tensor([0, 1, 2, 3, 4]) + checkpoint_2 = torch.tensor([0, 1, 2, 3, 4]) + os.makedirs(self.checkpoints_path, exist_ok=True) + checkpoint_path = str(self.checkpoints_path / "checkpoint_{}.ckpt") + torch.save(checkpoint_1, str(checkpoint_path).format("1")) + torch.save(checkpoint_2, str(checkpoint_path).format("2")) + + +class ModelDeploy(L.LightningWork): + def __init__(self, ckpt_path, *args, **kwargs): + super().__init__() + self.ckpt_path = ckpt_path + + def run(self): + ckpts = os.listdir(self.ckpt_path) + checkpoint_1 = torch.load(os.path.join(self.ckpt_path, ckpts[0])) + checkpoint_2 = torch.load(os.path.join(self.ckpt_path, ckpts[1])) + print(f"Loaded checkpoint_1: {checkpoint_1}") + print(f"Loaded checkpoint_2: {checkpoint_2}") + + +class LitApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.train = ModelTraining() + self.deploy = ModelDeploy(ckpt_path=self.train.checkpoints_path) + + def run(self): + self.train.run() + self.deploy.run() + + +app = L.LightningApp(LitApp()) diff --git a/docs/source-app/workflows/ssh/index.rst b/docs/source-app/workflows/ssh/index.rst new file mode 100644 index 0000000..e0bcbc1 --- /dev/null +++ b/docs/source-app/workflows/ssh/index.rst @@ -0,0 +1,119 @@ + +######################## +Debug cloud apps via SSH +######################## + +**Audience:** Users that want to debug their cloud apps on their own machine. + +SSH allows you to access Lightning components running on Lightning cloud in real-time using your own local machine. +This can be used for debugging apps running on the cloud: inspecting file system or monitoring the runtime environment. +This is often the case with applications that rely on GPUs. + +You can inspect the entire file system and monitor the runtime environment. + +---- + +************************ +Add SSH key to Lightning +************************ + +Before you can SSH to cloud machines, you will need to generate a new private SSH key, add it to the SSH agent, and add the public SSH key to your account on Lightning. +The following steps are one-time setup to allow you to SSH into your cloud machines. + + +Step 1: Create an SSH key +========================== + +Open a terminal and run the following command (replace email with the address you used in your lightning.ai account): + +.. code:: bash + + # make the ssh key (if you don't have one) + $ ssh-keygen -t ed25519 -C "your_email@example.com" + +This creates a new SSH key, using the provided email as a label. + +At the prompt, type a secure passphrase. + +.. code:: bash + + > Enter passphrase (empty for no passphrase): [Type a passphrase] + > Enter same passphrase again: [Type passphrase again] + + +Step 2: Add your key to Lightning +================================= + +You can add SSH keys using Lightning.ai website (Lightning.ai > Profile > Keys) or via this CLI command: + +.. code:: bash + + $ lightning create ssh-key --public-key ~/.ssh/id_ed25519.pub + +You are now ready to access your Lightning Flow and Work containers. + +---- + +********************************************************** +SSH to your cloud app +********************************************************** + +Ensure you have a running Lightning application in the cloud: + +.. code:: bash + + $ lightning run app app.py --cloud --name my-app + + +Next, start your ssh-agent in the background: + +.. code:: bash + + # add the key to the ssh-agent (to avoid having to explicitly state key on each connection) + # to start the agent, run the following + $ eval "$(ssh-agent -s)" + > Agent pid 12345 + +Add your generated ssh key: + +.. code:: bash + + $ ssh-add ~/.ssh/id_ed25519 + +Verify your ssh-key is properly loaded: + +.. code:: bash + + $ ssh-add -L + ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAn8mYRnRG1banQcfXPCUC6R8FvQS+YgfIsl70/dD3Te your_email@example.com + + +You can now SSH any app you are running on the cloud. + +To view all apps you can simple use this following: + +.. code:: bash + + $ lightning list apps + +Or, to select an app via a prompt: + +.. code:: bash + + $ lightning ssh + +To connect to a specific app flow use: + +.. code:: bash + + $ lightning list apps + $ lightning ssh --app-name # taken from previous app listing + +To connect to a LightningWork component use: + +.. code:: bash + + $ lightning ssh --app-name --component-name + +The component name is the variable name of your LightningWork instances in Python. +If you want to access your flow, use "flow" as component name. diff --git a/docs/source-app/workflows/test_an_app.rst b/docs/source-app/workflows/test_an_app.rst new file mode 100644 index 0000000..c51ae3a --- /dev/null +++ b/docs/source-app/workflows/test_an_app.rst @@ -0,0 +1,5 @@ +:orphan: + +########### +Test an App +########### diff --git a/docs/source-fabric/Makefile b/docs/source-fabric/Makefile new file mode 100644 index 0000000..268e095 --- /dev/null +++ b/docs/source-fabric/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -T -W +SPHINXBUILD = sphinx-build +SOURCEDIR = . +BUILDDIR = ../build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/source-fabric/_static/copybutton.js b/docs/source-fabric/_static/copybutton.js new file mode 100644 index 0000000..453363c --- /dev/null +++ b/docs/source-fabric/_static/copybutton.js @@ -0,0 +1,64 @@ +/* Copied from the official Python docs: https://docs.python.org/3/_static/copybutton.js */ +$(document).ready(function() { + /* Add a [>>>] button on the top-right corner of code samples to hide + * the >>> and ... prompts and the output and thus make the code + * copyable. */ + var div = $('.highlight-python .highlight,' + + '.highlight-python3 .highlight,' + + '.highlight-pycon .highlight,' + + '.highlight-default .highlight'); + var pre = div.find('pre'); + + // get the styles from the current theme + pre.parent().parent().css('position', 'relative'); + var hide_text = 'Hide the prompts and output'; + var show_text = 'Show the prompts and output'; + var border_width = pre.css('border-top-width'); + var border_style = pre.css('border-top-style'); + var border_color = pre.css('border-top-color'); + var button_styles = { + 'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0', + 'border-color': border_color, 'border-style': border_style, + 'border-width': border_width, 'color': border_color, 'text-size': '75%', + 'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em', + 'border-radius': '0 3px 0 0' + } + + // create and add the button to all the code blocks that contain >>> + div.each(function(index) { + var jthis = $(this); + if (jthis.find('.gp').length > 0) { + var button = $('>>>'); + button.css(button_styles) + button.attr('title', hide_text); + button.data('hidden', 'false'); + jthis.prepend(button); + } + // tracebacks (.gt) contain bare text elements that need to be + // wrapped in a span to work with .nextUntil() (see later) + jthis.find('pre:has(.gt)').contents().filter(function() { + return ((this.nodeType == 3) && (this.data.trim().length > 0)); + }).wrap(''); + }); + + // define the behavior of the button when it's clicked + $('.copybutton').click(function(e){ + e.preventDefault(); + var button = $(this); + if (button.data('hidden') === 'false') { + // hide the code output + button.parent().find('.go, .gp, .gt').hide(); + button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden'); + button.css('text-decoration', 'line-through'); + button.attr('title', show_text); + button.data('hidden', 'true'); + } else { + // show the code output + button.parent().find('.go, .gp, .gt').show(); + button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible'); + button.css('text-decoration', 'none'); + button.attr('title', hide_text); + button.data('hidden', 'false'); + } + }); +}); diff --git a/docs/source-fabric/_static/images/icon.svg b/docs/source-fabric/_static/images/icon.svg new file mode 100644 index 0000000..e88fc19 --- /dev/null +++ b/docs/source-fabric/_static/images/icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/source-fabric/_static/images/logo-large.svg b/docs/source-fabric/_static/images/logo-large.svg new file mode 100644 index 0000000..39531f9 --- /dev/null +++ b/docs/source-fabric/_static/images/logo-large.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/source-fabric/_static/images/logo-small.svg b/docs/source-fabric/_static/images/logo-small.svg new file mode 100644 index 0000000..1f523a5 --- /dev/null +++ b/docs/source-fabric/_static/images/logo-small.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/source-fabric/_static/images/logo.png b/docs/source-fabric/_static/images/logo.png new file mode 100644 index 0000000..392c965 Binary files /dev/null and b/docs/source-fabric/_static/images/logo.png differ diff --git a/docs/source-fabric/_static/images/logo.svg b/docs/source-fabric/_static/images/logo.svg new file mode 100644 index 0000000..7c0920c --- /dev/null +++ b/docs/source-fabric/_static/images/logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/source/_static/main.css b/docs/source-fabric/_static/main.css similarity index 100% rename from source/_static/main.css rename to docs/source-fabric/_static/main.css diff --git a/docs/_static/autosummary/module.rst b/docs/source-fabric/_templates/autosummary/module.rst similarity index 100% rename from docs/_static/autosummary/module.rst rename to docs/source-fabric/_templates/autosummary/module.rst diff --git a/docs/source-fabric/_templates/classtemplate.rst b/docs/source-fabric/_templates/classtemplate.rst new file mode 100644 index 0000000..482db13 --- /dev/null +++ b/docs/source-fabric/_templates/classtemplate.rst @@ -0,0 +1,14 @@ +.. role:: hidden + :class: hidden-section +.. currentmodule:: {{ module }} + + +{{ name | underline }} + +.. autoclass:: {{ name }} + :members: + + +.. + autogenerated from source-fabric/_templates/classtemplate.rst + note it does not have :inherited-members: diff --git a/docs/source-fabric/_templates/classtemplate_no_index.rst b/docs/source-fabric/_templates/classtemplate_no_index.rst new file mode 100644 index 0000000..858c37b --- /dev/null +++ b/docs/source-fabric/_templates/classtemplate_no_index.rst @@ -0,0 +1,12 @@ +:orphan: + +.. role:: hidden + :class: hidden-section +.. currentmodule:: {{ module }} + + +{{ name | underline }} + +.. autoclass:: {{ name }} + :members: + :noindex: diff --git a/docs/_static/layout.html b/docs/source-fabric/_templates/layout.html similarity index 100% rename from docs/_static/layout.html rename to docs/source-fabric/_templates/layout.html diff --git a/docs/source-fabric/_templates/theme_variables.jinja b/docs/source-fabric/_templates/theme_variables.jinja new file mode 100644 index 0000000..cce7263 --- /dev/null +++ b/docs/source-fabric/_templates/theme_variables.jinja @@ -0,0 +1,15 @@ +{%- set external_urls = { + 'github': 'https://github.com/Lightning-AI/lightning', + 'github_issues': 'https://github.com/Lightning-AI/lightning/issues', + 'contributing': 'https://github.com/Lightning-AI/lightning/blob/master/.github/CONTRIBUTING.md', + 'governance': 'https://lightning.ai/docs/pytorch/latest/community/governance.html', + 'docs': 'https://lightning.ai/docs/fabric/', + 'twitter': 'https://twitter.com/LightningAI', + 'home': 'https://lightning.ai/docs/fabric/', + 'get_started': '', + 'blog': 'https://lightning.ai/pages/blog/', + 'support': '', + 'community': 'https://www.pytorchlightning.ai/community', + 'forums': 'https://lightning.ai/forums/', +} +-%} diff --git a/docs/source-fabric/advanced/distributed_communication.rst b/docs/source-fabric/advanced/distributed_communication.rst new file mode 100644 index 0000000..83aac5c --- /dev/null +++ b/docs/source-fabric/advanced/distributed_communication.rst @@ -0,0 +1,286 @@ +########################################### +Communication between distributed processes +########################################### + +With Fabric, you can easily access information about a process or send data between processes with a standardized API and agnostic to the distributed strategy. + + +---- + + +******************* +Rank and world size +******************* + +The rank assigned to a process is a zero-based index in the range of *0, ..., world size - 1*, where *world size* is the total number of distributed processes. +If you are using multi-GPU, think of the rank as the *GPU ID* or *GPU index*, although rank generally extends to distributed processing. + +The rank is unique across all processes, regardless of how they are distributed across machines, and it is therefore also called **global rank**. +We can also identify processes by their **local rank**, which is unique among processes running on the same machine but is not unique globally across all machines. +Finally, each process is associated with a **node rank** in the range *0, ..., num nodes - 1*, which identifies which machine (node) the process is running on. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric_collectives_ranks.jpeg + :alt: The different type of process ranks: Local, global, node. + :width: 100% + +Here is how you launch multiple processes in Fabric: + +.. code-block:: python + + from lightning.fabric import Fabric + + # Devices and num_nodes determine how many processes there are + fabric = Fabric(devices=2, num_nodes=3) + fabric.launch() + +Learn more about :doc:`launching distributed training <../fundamentals/launch>`. +And here is how you access all rank and world size information: + +.. code-block:: python + + # The total number of processes running across all devices and nodes + fabric.world_size # 2 * 3 = 6 + + # The global index of the current process across all devices and nodes + fabric.global_rank # -> {0, 1, 2, 3, 4, 5} + + # The index of the current process among the processes running on the local node + fabric.local_rank # -> {0, 1} + + # The index of the current node + fabric.node_rank # -> {0, 1, 2} + + # Do something only on rank 0 + if fabric.global_rank == 0: + ... + + +.. _race conditions: + +Avoid race conditions +===================== + +Access to the rank information helps you avoid *race conditions* which could crash your script or lead to corrupted data. +Such conditions can occur when multiple processes try to write to the same file simultaneously, for example, writing a checkpoint file or downloading a dataset. +Avoid this from happening by guarding your logic with a rank check: + +.. code-block:: python + + # Only write files from one process (rank 0) ... + if fabric.global_rank == 0: + with open("output.txt", "w") as file: + file.write(...) + + # ... or save from all processes but don't write to the same file + with open(f"output-{fabric.global_rank}.txt", "w") as file: + file.write(...) + + # Multi-node: download a dataset, the filesystem between nodes is shared + if fabric.global_rank == 0: + download_dataset() + + # Multi-node: download a dataset, the filesystem between nodes is NOT shared + if fabric.local_rank == 0: + download_dataset() + +Another type of race condition is when one or multiple processes try to access a resource before it is available. +For example, when rank 0 downloads a dataset, all other processes should *wait* for the download to complete before they start reading the contents. +This can be achieved with a **barrier**. + + +---- + + +******* +Barrier +******* + +The barrier forces every process to wait until all processes have reached it. +In other words, it is a **synchronization**. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric_collectives_barrier.jpeg + :alt: The barrier for process synchronization + :width: 100% + +A barrier is needed when processes do different amounts of work and as a result fall out of sync. + +.. code-block:: python + + fabric = Fabric(accelerator="cpu", devices=4) + fabric.launch() + + # Simulate each process taking a different amount of time + sleep(2 * fabric.global_rank) + print(f"Process {fabric.global_rank} is done.") + + # Wait for all processes to reach the barrier + fabric.barrier() + print("All processes reached the barrier!") + + +A more realistic scenario is when downloading data. +Here, we need to ensure that processes only start to load the data once it has completed downloading. +Since downloading should be done on rank 0 only to :ref:`avoid race conditions `, we need a barrier: + +.. code-block:: python + + if fabric.global_rank == 0: + print("Downloading dataset. This can take a while ...") + download_dataset("http://...") + + # All other processes wait here until rank 0 is done with downloading: + fabric.barrier() + + # After everyone reached the barrier, they can access the downloaded files: + dataset = load_dataset() + + +Specifically for the use case of downloading and reading data, there is a convenience context manager that combines both the rank-check and the barrier: + +.. code-block:: python + + with fabric.rank_zero_first(): + if not dataset_exists(): + download_dataset("http://...") + dataset = load_dataset() + +With :meth:`~lightning.fabric.fabric.Fabric.rank_zero_first`, it is guaranteed that process 0 executes the code block first before all others can enter it. + + +---- + +.. _broadcast collective: + +********* +Broadcast +********* + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric_collectives_broadcast.jpeg + :alt: The broadcast collective operation + :width: 100% + +The broadcast operation sends a tensor of data from one process to all other processes so that all end up with the same data. + +.. code-block:: python + + fabric = Fabric(...) + + # Transfer a tensor from one process to all the others + result = fabric.broadcast(tensor) + + # By default, the source is the process rank 0 ... + result = fabric.broadcast(tensor, src=0) + + # ... which can be change to a different rank + result = fabric.broadcast(tensor, src=3) + + +Full example: + +.. code-block:: python + + fabric = Fabric(devices=4, accelerator="cpu") + fabric.launch() + + # Data is different on each process + learning_rate = torch.rand(1) + print("Before broadcast:", learning_rate) + + # Transfer the tensor from one process to all the others + learning_rate = fabric.broadcast(learning_rate) + print("After broadcast:", learning_rate) + + +---- + + +****** +Gather +****** + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric_collectives_all-gather.jpeg + :alt: The All-gather collective operation + :width: 100% + +The gather operation transfers the tensors from each process to every other process and stacks the results. +As opposed to the :ref:`broadcast `, every process gets the data from every other process, not just from a particular rank. + +.. code-block:: python + + fabric = Fabric(...) + + # Gather the data from + result = fabric.all_gather(tensor) + + # Tip: Turn off gradient syncing if you don't need to back-propagate through it + with torch.no_grad(): + result = fabric.all_gather(tensor) + + # Also works with a (nested) collection of tensors (dict, list, tuple): + collection = {"loss": torch.tensor(...), "data": ...} + gathered_collection = fabric.all_gather(collection) + + +Full example: + +.. code-block:: python + + fabric = Fabric(devices=4, accelerator="cpu") + fabric.launch() + + # Data is different in each process + data = torch.tensor(10 * fabric.global_rank) + + # Every process gathers the tensors from all other processes + # and stacks the result: + result = fabric.all_gather(data) + print("Result of all-gather:", result) # tensor([ 0, 10, 20, 30]) + + +---- + + +****** +Reduce +****** + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric_collectives_all-reduce.jpeg + :alt: The All-reduce collective operation + :width: 100% + + +The reduction is an operation that takes multiple values (tensors) as input and returns a single value. +An example of a reduction is *summation*, e.g., ``torch.sum()``. +The :meth:`~lightning.fabric.fabric.Fabric.all_reduce` operation allows you to apply a reduction across multiple processes: + +.. code-block:: python + + fabric = Fabric(...) + + # Compute the mean of a tensor across processes: + result = fabric.all_reduce(tensor, reduce_op="mean") + + # Or the sum: + result = fabric.all_reduce(tensor, reduce_op="sum") + + # Also works with a (nested) collection of tensors (dict, list, tuple): + collection = {"loss": torch.tensor(...), "data": ...} + reduced_collection = fabric.all_reduce(collection) + +The support of options for ``reduce_op`` depends on the strategy used, but all strategies support *sum* and *mean*. + +Full example: + +.. code-block:: python + + fabric = Fabric(devices=4, accelerator="cpu") + fabric.launch() + + # Data is different in each process + data = torch.tensor(10 * fabric.global_rank) + + # Sum the tensors from every process + result = fabric.all_reduce(data, reduce_op="sum") + + # sum(0 + 10 + 20 + 30) = tensor(60) + print("Result of all-reduce:", result) diff --git a/docs/source-fabric/advanced/gradient_accumulation.rst b/docs/source-fabric/advanced/gradient_accumulation.rst new file mode 100644 index 0000000..0fb5a31 --- /dev/null +++ b/docs/source-fabric/advanced/gradient_accumulation.rst @@ -0,0 +1,55 @@ +############################### +Efficient Gradient Accumulation +############################### + +Gradient accumulation works the same way with Fabric as in PyTorch. +You are in control of which model accumulates and at what frequency: + +.. code-block:: python + + for iteration, batch in enumerate(dataloader): + # Accumulate gradient 8 batches at a time + is_accumulating = iteration % 8 != 0 + + output = model(input) + loss = ... + + # .backward() accumulates when .zero_grad() wasn't called + fabric.backward(loss) + ... + + if not is_accumulating: + # Step the optimizer after the accumulation phase is over + optimizer.step() + optimizer.zero_grad() + + +However, in a distributed setting, for example, when training across multiple GPUs or machines, doing it this way can significantly slow down your training loop. +To optimize this code, we should skip the synchronization in ``.backward()`` during the accumulation phase. +We only need to synchronize the gradients when the accumulation phase is over! +This can be achieved by adding the :meth:`~lightning.fabric.fabric.Fabric.no_backward_sync` context manager over the :meth:`~lightning.fabric.fabric.Fabric.backward` call: + +.. code-block:: diff + + for iteration, batch in enumerate(dataloader): + + # Accumulate gradient 8 batches at a time + is_accumulating = iteration % 8 != 0 + + + with fabric.no_backward_sync(model, enabled=is_accumulating): + output = model(input) + loss = ... + + # .backward() accumulates when .zero_grad() wasn't called + fabric.backward(loss) + + ... + + if not is_accumulating: + # Step the optimizer after accumulation phase is over + optimizer.step() + optimizer.zero_grad() + + +For those strategies that don't support it, a warning is emitted. For single-device strategies, it is a no-op. +Both the model's ``.forward()`` and the ``fabric.backward()`` call need to run under this context. diff --git a/docs/source-fabric/advanced/model_parallel/fsdp.rst b/docs/source-fabric/advanced/model_parallel/fsdp.rst new file mode 100644 index 0000000..5035554 --- /dev/null +++ b/docs/source-fabric/advanced/model_parallel/fsdp.rst @@ -0,0 +1,520 @@ +########################################### +Training models with billions of parameters +########################################### + +Use Fully Shared Data Parallel (FSDP) to train large models with billions or trillions of parameters efficiently on multiple GPUs and across multiple machines. + +.. note:: This is an experimental feature. + + +Today, large models with billions of parameters are trained with many GPUs across several machines in parallel. +Even a single A100 GPU with 80 GB of VRAM (the biggest today) is not enough to train just a 30B parameter model (even with batch size 1 and 16-bit precision). +The memory consumption for training is generally made up of + +1. the model parameters, +2. the optimizer states (e.g., Adam has two additional exponential averages per parameter), +3. the layer activations (forward) and +4. the gradients (backward). + +| + +When the sum of these memory components exceed the VRAM of a single GPU, regular data-parallel training (DDP) can no longer be employed. +One of the methods that can alleviate this limitation is called **model-parallel** training, and known as **FSDP** in PyTorch, and in this guide, you will learn how to effectively scale large models with it. + + +---- + + +*************************** +Checklist: When to use FSDP +*************************** + +| + +✅ I have multiple GPUs + +✅ I have tried regular DDP training with batch size 1 but I run out of memory + +✅ I have PyTorch 2.0 or newer installed + + +---- + + +********************* +Enable FSDP in Fabric +********************* + + +To enable model-parallel training with FSDP in a single-line change, set ``strategy="fsdp"``: + +.. code-block:: python + + fabric = L.Fabric(accelerator="cuda", devices=2, strategy="fsdp") + +As we will see in the next sections, there are many settings we can tune to optimize memory usage and throughput, scaling to massively large models. +This is equivalent to the above, but will let us configure additional settings later: + +.. code-block:: python + + from lightning.fabric.strategies import FSDPStrategy + + fabric = L.Fabric(accelerator="cuda", devices=2, strategy=FSDPStrategy()) + + +Here is a full code example: + +.. code-block:: python + + import torch + import torch.nn as nn + import torch.nn.functional as F + + import lightning as L + from lightning.fabric.strategies import FSDPStrategy + from lightning.pytorch.demos import Transformer, WikiText2 + + fabric = L.Fabric(accelerator="cuda", devices=2, strategy=FSDPStrategy()) + fabric.launch() + + fabric.seed_everything(42) + + with fabric.rank_zero_first(): + dataset = WikiText2() + + # 1B parameters + model = Transformer(vocab_size=dataset.vocab_size, nlayers=32, nhid=4096, ninp=1024, nhead=64) + optimizer = torch.optim.Adam(model.parameters(), lr=0.1) + + model, optimizer = fabric.setup(model, optimizer) + + for i in range(10): + input, target = fabric.to_device(dataset[i]) + output = model(input.unsqueeze(0), target.unsqueeze(0)) + loss = F.nll_loss(output, target.view(-1)) + fabric.backward(loss) + optimizer.step() + optimizer.zero_grad() + fabric.print(loss.item()) + + fabric.print(torch.cuda.memory_summary()) + + +We will reuse this Transformer example throughout the guide, optimize speed and memory usage, and compare it to regular DDP training. + + +---- + + +********************* +Identify large layers +********************* + +Models that have many large layers like linear layers in LLMs, ViTs, etc. with >100M parameters will benefit the most from FSDP because the memory they consume through parameters, activations and corresponding optimizer states can be evenly split across all GPUs. +However, one should avoid splitting small layers that have a few thousand parameters because communication overhead would dominate and slow the training down. +We can specify a list of layer classes in the **wrapping policy** to inform FSDP which parameters it should wrap: + +.. code-block:: python + + # 1. Define a set of layers that FSDP should manage + # Here we are choosing the large encoder and decoder layers + policy = {nn.TransformerEncoderLayer, nn.TransformerDecoderLayer} + + # 2. Pass the policy to the FSDPStrategy object + strategy = FSDPStrategy(auto_wrap_policy=policy) + + fabric = L.Fabric(..., strategy=strategy) + +.. collapse:: Alternative ways to define the policy (Lightning < 2.1) + + The ``auto_wrap_policy`` argument also accepts the old-style function-policies. For example: + + .. code-block:: python + + from functools import partial + + # 1. Import a suiting wrapping policy from PyTorch + from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy + + # 2. Configure the policy + policy = partial(size_based_auto_wrap_policy, min_num_params=10000) + + # 3. Pass it to the FSDPStrategy object + strategy = FSDPStrategy(auto_wrap_policy=policy) + + PyTorch provides several of these functional policies under :mod:`torch.distributed.fsdp.wrap`. + +| + +Verify that FSDP works with your model by comparing the peak memory usage printed in the CUDA memory summary (see example above) with regular DDP training. +You should see a decrease in allocated memory and a slight increase in iteration time: + +.. list-table:: + :widths: 25 25 25 + :header-rows: 1 + + * - + - DDP + - FSDP + * - Memory (MB) + - 26’953 + - 11’578 + * - Iteration time (sec) + - 0.26 + - 0.36 + +---- + + +***************************** +Speed up model initialization +***************************** + +The standard practice in PyTorch is to put all model parameters into CPU memory first and then in a second step move them to the GPU device. +However, the larger the model the longer these two steps take. With the :meth:`~lightning.fabric.fabric.Fabric.init_module` context manager, you can initialize very large models quickly and reduce memory peaks. + +Before: + +.. code-block:: python + + # Slow: Places the model on CPU first + model = Transformer(vocab_size=dataset.vocab_size) + +After: + +.. code-block:: python + + # Fast: Creates the model on the GPU directly + with fabric.init_module(): + model = Transformer(vocab_size=dataset.vocab_size) + + +---- + + +****************************** +Optimize the sharding strategy +****************************** + +By default, FSDP will automatically shard 1) the model weights 2) the gradients during backward and 3) the optimizer states across all GPUs of the corresponding layers selected by the auto-wrap-policy. +You can configure the following options to trade-off memory for speed: + +.. code-block:: python + + strategy = FSDPStrategy( + # Default: Shard weights, gradients, optimizer state (1 + 2 + 3) + sharding_strategy="FULL_SHARD", + # Shard gradients, optimizer state (2 + 3) + sharding_strategy="SHARD_GRAD_OP", + # Don't shard anything (similar to DDP) + sharding_strategy="NO_SHARD", + ) + fabric = L.Fabric(..., strategy=strategy) + + +**Recipe for choosing a sharding strategy:** + +1. Try the default settings first (FULL_SHARD). This is the slowest but will save you the most memory. +2. Try SHARD_GRAD_OP. If you run out of memory, revert back to the default (FULL_SHARD). Otherwise you should expect to see an increase in iteration speed. + +| + +Here is the memory and speed impact for each option when configured in our example code: + +.. list-table:: + :widths: 25 25 25 25 25 + :header-rows: 1 + + * - + - DDP + - NO_SHARD + - SHARD_GRAD_OP + - FULL_SHARD + * - Memory (MB) + - 26’953 + - 23’181 + - 11’815 + - 11’578 + * - Iteration time (sec) + - 0.26 + - 0.30 + - 0.31 + - 0.36 + + +---- + + +************************** +Trade-off speed for memory +************************** + +If you are short on GPU memory because you are training large models with 10+ billion parameters or require extreme batch sizes, consider trading off speed for more memory by enabling activation checkpointing or CPU offload. + + +Activation checkpointing +======================== + +Activations, the intermediate outputs of layers, are stored during the forward pass and needed during the backward pass to compute the gradients. +By enabling activation checkpointing, we can choose to discard and recompute selected layer activations dynamically during the backward pass when they are required, instead of storing them throughout the forward pass. +While this approach may slightly reduce training speed, it significantly reduces memory consumption. +The freed-up memory can then be allocated to increase the model's capacity or accommodate larger batch sizes, resulting in potential performance improvements. + +To enable activation checkpointing, pass in the list of layers to checkpoint. +This is typically your transformer block (including attention + feed-forward): + +.. code-block:: python + + strategy = FSDPStrategy( + # Enable activation checkpointing on these layers + activation_checkpointing_policy={ + nn.TransformerEncoderLayer, + nn.TransformerDecoderLayer, + }, + ) + fabric = L.Fabric(..., strategy=strategy) + + +Offload parameters to CPU +========================= + +The most drastic GPU memory savings can be achieved by offloading parameters to the CPU: + +.. code-block:: python + + # 1. Set `cpu_offload=True` + strategy = FSDPStrategy(..., cpu_offload=True) + fabric = L.Fabric(..., strategy=strategy) + + # 2. Set `move_to_device=False` (won't be required in future versions) + model, optimizer = setup(model, optimizer, move_to_device=False) + +The drawback is a much slower training speed due to the added communication between CPU and GPU for transferring parameters in every forward pass. +You should use this only if you have enough CPU memory and other scaling methods don’t give you enough memory savings. +In our example, we see a 4x memory saving, but a 10x increase in iteration time: + +.. list-table:: + :widths: 25 25 25 25 + :header-rows: 1 + + * - + - DDP + - FSDP + - FSDP + CPU offload + * - Memory (MB) + - 26’953 + - 11’578 + - 2’825 + * - Iteration time (sec) + - 0.26 + - 0.36 + - 3.24 + + +---- + + +***************** +Save a checkpoint +***************** + +Since training large models can be very expensive, it is best practice to include checkpointing logic into the training loop to save the progress periodically in case it gets interrupted unexpectedly. +Fabric offers a convenient and efficient method to save large model checkpoints and other state to a checkpoint file. +Simply add the following calls to your training loop: + +.. code-block:: python + + # 1. Define model, optimizer, and other training loop state + state = {"model": model, "optimizer": optimizer, "iter": iteration} + + # DON'T do this (inefficient): + # state = {"model": model.state_dict(), "optimizer": optimizer.state_dict(), ...} + + # 2. Save using Fabric's method + fabric.save("path/to/checkpoint/file", state) + + # DON'T do this (inefficient): + # torch.save("path/to/checkpoint/file", state) + +To reduce memory peaks and speed up the saving to disk, each process/GPU will save its own file into a folder at the given path by default. +The resulting checkpoint folder will have this structure: + +.. code-block:: text + + path/to/checkpoint/file + ├── .metadata + ├── __0_0.distcp + ├── __1_0.distcp + └── meta.pt + +The “sharded” checkpoint format is the most efficient to save and load in Fabric. +However, if you prefer to have a single consolidated file instead, you can configure this by setting the ``state_dict_type`` flag in the strategy: + +.. code-block:: python + + # Default: Save individual files with state from each process + strategy = FSDPStrategy(state_dict_type="sharded") + + # Save a single, consolidated checkpoint file + strategy = FSDPStrategy(state_dict_type="full") + + +**Which checkpoint format should I use?** + +- ``state_dict_type="sharded"``: Use for pre-training very large models. It is fast and uses less memory, but it is less portable - you can’t easily load the checkpoint in raw PyTorch (in the future, Lightning will provide utilities to convert the checkpoint though). +- ``state_dict_type="full"``: Use when pre-training small to moderately large models (less than 10B parameters), when fine-tuning, and when portability is required. + + +---- + + +***************** +Load a checkpoint +***************** + +You can easily load checkpoints saved by Fabric to resume training: + +.. code-block:: python + + # 1. Define model, optimizer, and other training loop state + state = {"model": model, "optimizer": optimizer, "iter": iteration} + + # 2. Load using Fabric's method + fabric.load("path/to/checkpoint/file", state) + + # DON'T do this (inefficient): + # model.load_state_dict(torch.load("path/to/checkpoint/file")) + +Fabric will automatically recognize whether the provided path contains a checkpoint saved with ``state_dict_type="full"`` or ``state_dict_type="sharded"``. + +.. warning:: + + Loading a full-state checkpoint will replicate the file in CPU RAM for every GPU. + For very large checkpoints/models, you may run out of memory and your program will crash. + If this happens, save using the “sharded” checkpoint format instead (default). + + +---- + + +********************************** +Advanced performance optimizations +********************************** + +If you’ve reached a good understanding of how the different FSDP settings impact the memory usage and speed of your model, here are a few more to squeeze out the last bit of performance. +These settings really depend on the specific use cases, so you will have to turn them on and off to see the impact on your model. + +Overlap backward and optimizer’s step +===================================== + +Fabric provides a context manager that allows you to overlap the backward and optimizer step to save significant memory and speed up the iteration time too. +By overlapping the two, we eliminate the need to store all gradients at once in memory. +Instead, the optimizer step updates are applied directly during backward as gradients become available, and the memory for gradients is immediately freed up. + +Here is the recipe: + +.. code-block:: python + + # 1. Import the context manager + from lightning.fabric.strategies.fsdp import fsdp_overlap_step_with_backward + + # 2. Create one optimizer instance per parameter + optimizers = [torch.optim.Adam([p], ...) for p in model.parameters()] + model, *optimizers = fabric.setup(model, *optimizers) + + ... + + for i in range(max_iters): + loss = ... + + # 3. Instead of calling `optimizer.step()`, call `fabric.backward(loss)` + # within the context manager + with fsdp_overlap_step_with_backward(optimizers, model): + fabric.backward(loss) + + # optimizer.step() + + +.. collapse:: Full example + + .. code-block:: python + + import torch + import torch.nn as nn + import torch.nn.functional as F + + import lightning as L + from lightning.fabric.strategies.fsdp import FSDPStrategy, fsdp_overlap_step_with_backward + from lightning.pytorch.demos import Transformer, WikiText2 + + policy = {nn.TransformerEncoderLayer, nn.TransformerDecoderLayer} + strategy = FSDPStrategy(auto_wrap_policy=policy) + fabric = L.Fabric(accelerator="cuda", devices=2, strategy=strategy) + fabric.launch() + + fabric.seed_everything(42) + + with fabric.rank_zero_first(): + dataset = WikiText2() + + # 1B parameters + model = Transformer(vocab_size=dataset.vocab_size, nlayers=32, nhid=4096, ninp=1024, nhead=64) + optimizers = [torch.optim.Adam([p], lr=0.1) for p in model.parameters()] + + model, *optimizers = fabric.setup(model, *optimizers) + + for i in range(10): + input, target = fabric.to_device(dataset[i]) + output = model(input.unsqueeze(0), target.unsqueeze(0)) + loss = F.nll_loss(output, target.view(-1)) + + with fsdp_overlap_step_with_backward(optimizers, model): + fabric.backward(loss) + # no `optimizer.step()` here! + + fabric.print(loss.item()) + + fabric.print(torch.cuda.memory_summary()) + +| + +`Read the detailed blog post here `_. +Note that this feature cannot work with gradient accumulation! + + +Disable foreach in the optimizer +================================ + +The commonly used optimizers in PyTorch have a setting ``foreach=True|False`` that speeds up the parameter and state updates when enabled. +However, you might see a slight memory peak and the larger the model is, the more noticeable it can be. +Consider disabling the ``foreach`` option if undesired memory patterns occur: + +.. code-block:: python + + optimizer = torch.optim.AdamW(model.parameters(), foreach=False) + +`See the full list of optimizers that support this `_. + + +Limit all-gathers +================= + +If you are running training close to the max. +GPU memory limit, you might be getting so-called CUDA malloc retries. +This is essentially the GPU running out of memory but before crashing completely, it tries to find some unused or cached memory it can free. +When they happen frequently, these retries can have a significant impact on speed. +Normally, you would decrease the batch size slightly to avoid it. +With FSDP, you have one more knob you can tweak to combat the issue, by setting ``limit_all_gathers=True``: + +.. code-block:: python + + strategy = FSDPStrategy( + # Default: The CPU will schedule the transfer of weights between GPUs + # at will, sometimes too aggressively + limit_all_gathers=False, + # Enable this if you are close to the max. GPU memory usage + limit_all_gathers=True, + ) + fabric = L.Fabric(..., strategy=strategy) + +You can monitor CUDA malloc retries in the output of ``torch.cuda.memory_summary()`` for example, or through the PyTorch profiler. diff --git a/docs/source-fabric/advanced/multiple_setup.rst b/docs/source-fabric/advanced/multiple_setup.rst new file mode 100644 index 0000000..1ecdcc9 --- /dev/null +++ b/docs/source-fabric/advanced/multiple_setup.rst @@ -0,0 +1,116 @@ +:orphan: + +############################## +Multiple Models and Optimizers +############################## + +Fabric makes it very easy to work with multiple models and/or optimizers at once in your training workflow. +Examples of where this comes in handy are Generative Adversarial Networks (GANs), Auto-encoders, meta-learning and more. + + +---- + +************************ +One model, one optimizer +************************ + +Fabric has a simple guideline you should follow: +If you have an optimizer, you should set it up together with the model to make your code truly strategy-agnostic. + +.. code-block:: python + + import torch + from lightning.fabric import Fabric + + fabric = Fabric() + + # Instantiate model and optimizer + model = LitModel() + optimizer = torch.optim.Adam(model.parameters()) + + # Set up the model and optimizer together + model, optimizer = fabric.setup(model, optimizer) + + +Depending on the selected strategy, the :meth:`~lightning.fabric.fabric.Fabric.setup` method will wrap and link the model with the optimizer. + + +---- + + +****************************** +One model, multiple optimizers +****************************** + +You can also have multiple optimizers over a single model. +This is useful if you need specific optimizers or learning rates for parts of the model. + +.. code-block:: python + + # Instantiate model and optimizers + model = LitModel() + optimizer1 = torch.optim.SGD(model.layer1.parameters(), lr=0.003) + optimizer2 = torch.optim.SGD(model.layer2.parameters(), lr=0.01) + + # Set up the model and optimizers together + model, optimizer1, optimizer2 = fabric.setup(model, optimizer1, optimizer2) + + + +---- + + +****************************** +Multiple models, one optimizer +****************************** + +Using a single optimizer to update multiple models is possible too. +The best way to do this is to group all your individual models under one top level ``nn.Module``: + +.. code-block:: python + + class AutoEncoder(torch.nn.Module): + def __init__(self): + super().__init__() + + # Group all models under a common nn.Module + self.encoder = Encoder() + self.decoder = Decoder() + +Now all of these models can be treated as a single one: + +.. code-block:: python + + # Instantiate the big model + autoencoder = AutoEncoder() + optimizer = ... + + # Set up the model(s) and optimizer together + autoencoder, optimizer = fabric.setup(autoencoder, optimizer) + + +---- + + +************************************ +Multiple models, multiple optimizers +************************************ + +You can pair up as many models and optimizers as you want. For example, two models with one optimizer each: + +.. code-block:: python + + # Two models + generator = Generator() + discriminator = Discriminator() + + # Two optimizers + optimizer_gen = torch.optim.SGD(generator.parameters(), lr=0.01) + optimizer_dis = torch.optim.SGD(discriminator.parameters(), lr=0.001) + + # Set up generator + generator, optimizer_gen = fabric.setup(generator, optimizer_gen) + # Set up discriminator + discriminator, optimizer_dis = fabric.setup(discriminator, optimizer_dis) + +For a full example of this use case, see our `GAN example `_. diff --git a/docs/source-fabric/api/accelerators.rst b/docs/source-fabric/api/accelerators.rst new file mode 100644 index 0000000..7e8444d --- /dev/null +++ b/docs/source-fabric/api/accelerators.rst @@ -0,0 +1,22 @@ +.. include:: ../links.rst + +############################# +lightning.fabric.accelerators +############################# + + +Accelerators +^^^^^^^^^^^^ + +.. currentmodule:: lightning.fabric.accelerators + +.. autosummary:: + :toctree: ./generated + :nosignatures: + :template: classtemplate.rst + + Accelerator + CPUAccelerator + CUDAAccelerator + MPSAccelerator + XLAAccelerator diff --git a/docs/source-fabric/api/collectives.rst b/docs/source-fabric/api/collectives.rst new file mode 100644 index 0000000..21a7734 --- /dev/null +++ b/docs/source-fabric/api/collectives.rst @@ -0,0 +1,23 @@ +.. include:: ../links.rst + +#################################### +lightning.fabric.plugins.collectives +#################################### + +.. warning:: + This is an `experimental `__ feature. + + +Collectives +^^^^^^^^^^^ + +.. currentmodule:: lightning.fabric.plugins.collectives + +.. autosummary:: + :toctree: ./generated + :nosignatures: + :template: classtemplate.rst + + Collective + TorchCollective + SingleDeviceCollective diff --git a/docs/source-fabric/api/environments.rst b/docs/source-fabric/api/environments.rst new file mode 100644 index 0000000..f824ed8 --- /dev/null +++ b/docs/source-fabric/api/environments.rst @@ -0,0 +1,25 @@ +.. include:: ../links.rst + +##################################### +lightning.fabric.plugins.environments +##################################### + + +Environments +^^^^^^^^^^^^ + +.. currentmodule:: lightning.fabric.plugins.environments + +.. autosummary:: + :toctree: ./generated + :nosignatures: + :template: classtemplate_noindex.rst + + ~cluster_environment.ClusterEnvironment + ~kubeflow.KubeflowEnvironment + ~lightning.LightningEnvironment + ~lsf.LSFEnvironment + ~mpi.MPIEnvironment + ~slurm.SLURMEnvironment + ~torchelastic.TorchElasticEnvironment + ~xla.XLAEnvironment diff --git a/docs/source-fabric/api/fabric.rst b/docs/source-fabric/api/fabric.rst new file mode 100644 index 0000000..41036e3 --- /dev/null +++ b/docs/source-fabric/api/fabric.rst @@ -0,0 +1,18 @@ +.. include:: ../links.rst + +####################### +lightning.fabric.Fabric +####################### + + +Fabric +^^^^^^ + +.. currentmodule:: lightning.fabric.fabric + +.. autosummary:: + :toctree: ./generated + :nosignatures: + :template: classtemplate.rst + + Fabric diff --git a/docs/source-fabric/api/fabric_args.rst b/docs/source-fabric/api/fabric_args.rst new file mode 100644 index 0000000..b285fc2 --- /dev/null +++ b/docs/source-fabric/api/fabric_args.rst @@ -0,0 +1,228 @@ +################ +Fabric Arguments +################ + + +accelerator +=========== + +Choose one of ``"cpu"``, ``"gpu"``, ``"tpu"``, ``"auto"``. + +.. code-block:: python + + # CPU accelerator + fabric = Fabric(accelerator="cpu") + + # Running with GPU Accelerator using 2 GPUs + fabric = Fabric(devices=2, accelerator="gpu") + + # Running with TPU Accelerator using 8 TPU cores + fabric = Fabric(devices=8, accelerator="tpu") + + # Running with GPU Accelerator using the DistributedDataParallel strategy + fabric = Fabric(devices=4, accelerator="gpu", strategy="ddp") + +The ``"auto"`` option recognizes the machine you are on and selects the available accelerator. + +.. code-block:: python + + # If your machine has GPUs, it will use the GPU Accelerator + fabric = Fabric(devices=2, accelerator="auto") + + +See also: :doc:`../fundamentals/accelerators` + + +strategy +======== + +Choose a training strategy: ``"dp"``, ``"ddp"``, ``"ddp_spawn"``, ``"xla"``, ``"deepspeed"``, ``"fsdp"````. + +.. code-block:: python + + # Running with the DistributedDataParallel strategy on 4 GPUs + fabric = Fabric(strategy="ddp", accelerator="gpu", devices=4) + + # Running with the DDP Spawn strategy using 4 CPU processes + fabric = Fabric(strategy="ddp_spawn", accelerator="cpu", devices=4) + + +Additionally, you can pass in your custom strategy by configuring additional parameters. + +.. code-block:: python + + from lightning.fabric.strategies import DeepSpeedStrategy + + fabric = Fabric(strategy=DeepSpeedStrategy(stage=2), accelerator="gpu", devices=2) + +See also: :doc:`../fundamentals/launch` + + +devices +======= + +Configure the devices to run on. Can be of type: + +- int: the number of devices (e.g., GPUs) to train on +- list of int: which device index (e.g., GPU ID) to train on (0-indexed) +- str: a string representation of one of the above + +.. code-block:: python + + # default used by Fabric, i.e., use the CPU + fabric = Fabric(devices=None) + + # equivalent + fabric = Fabric(devices=0) + + # int: run on two GPUs + fabric = Fabric(devices=2, accelerator="gpu") + + # list: run on the 2nd (idx 1) and 5th (idx 4) GPUs (by bus ordering) + fabric = Fabric(devices=[1, 4], accelerator="gpu") + fabric = Fabric(devices="1, 4", accelerator="gpu") # equivalent + + # -1: run on all GPUs + fabric = Fabric(devices=-1, accelerator="gpu") + fabric = Fabric(devices="-1", accelerator="gpu") # equivalent + +See also: :doc:`../fundamentals/launch` + + +num_nodes +========= + + +The number of cluster nodes for distributed operation. + +.. code-block:: python + + # Default used by Fabric + fabric = Fabric(num_nodes=1) + + # Run on 8 nodes + fabric = Fabric(num_nodes=8) + + +Learn more about :ref:`distributed multi-node training on clusters `. + + +precision +========= + +There are two different techniques to set the mixed precision. "True" precision and "Mixed" precision. +For an extensive guide into their differences, please see: :doc:`../fundamentals/precision` + +Fabric supports doing floating point operations in 64-bit precision ("double"), 32-bit precision ("full"), or 16-bit ("half") with both regular and `bfloat16 `_). +This selected precision will have a direct impact in the performance and memory usage based on your hardware. +Automatic mixed precision settings are denoted by a ``"-mixed"`` suffix, while "true" precision settings have a ``"-true"`` suffix: + +.. code-block:: python + + # Default used by the Fabric + fabric = Fabric(precision="32-true", devices=1) + + # the same as: + fabric = Fabric(precision="32", devices=1) + + # 16-bit mixed precision (model weights remain in torch.float32) + fabric = Fabric(precision="16-mixed", devices=1) + + # 16-bit bfloat mixed precision (model weights remain in torch.float32) + fabric = Fabric(precision="bf16-mixed", devices=1) + + # 8-bit mixed precision via TransformerEngine (model weights remain in torch.float32) + fabric = Fabric(precision="transformer-engine", devices=1) + + # 16-bit precision (model weights get cast to torch.float16) + fabric = Fabric(precision="16-true", devices=1) + + # 16-bit bfloat precision (model weights get cast to torch.bfloat16) + fabric = Fabric(precision="bf16-true", devices=1) + + # 64-bit (double) precision (model weights get cast to torch.float64) + fabric = Fabric(precision="64-true", devices=1) + + +plugins +======= + +Plugins allow you to connect arbitrary backends, precision libraries, clusters, etc. For example: +To define your own behavior, subclass the relevant class and pass it in. Here's an example linking up your own +:class:`~lightning.fabric.plugins.environments.ClusterEnvironment`. + +.. code-block:: python + + from lightning.fabric.plugins.environments import ClusterEnvironment + + + class MyCluster(ClusterEnvironment): + @property + def main_address(self): + return your_main_address + + @property + def main_port(self): + return your_main_port + + def world_size(self): + return the_world_size + + + fabric = Fabric(plugins=[MyCluster()], ...) + + +callbacks +========= + +A callback class is a collection of methods that the training loop can call at a specific time, for example, at the end of an epoch. +Add callbacks to Fabric to inject logic into your training loop from an external callback class. + +.. code-block:: python + + class MyCallback: + def on_train_epoch_end(self, results): + ... + +You can then register this callback or multiple ones directly in Fabric: + +.. code-block:: python + + fabric = Fabric(callbacks=[MyCallback()]) + + +Then, in your training loop, you can call a hook by its name. Any callback objects that have this hook will execute it: + +.. code-block:: python + + # Call any hook by name + fabric.call("on_train_epoch_end", results={...}) + +See also: :doc:`../guide/callbacks` + + +loggers +======= + +Attach one or several loggers/experiment trackers to Fabric for convenient metrics logging. + +.. code-block:: python + + # Default used by Fabric; no loggers are active + fabric = Fabric(loggers=[]) + + # Log to a single logger + fabric = Fabric(loggers=TensorBoardLogger(...)) + + # Or multiple instances + fabric = Fabric(loggers=[logger1, logger2, ...]) + +Anywhere in your training loop, you can log metrics to all loggers at once: + +.. code-block:: python + + fabric.log("loss", loss) + fabric.log_dict({"loss": loss, "accuracy": acc}) + + +See also: :doc:`../guide/logging` diff --git a/docs/source-fabric/api/fabric_methods.rst b/docs/source-fabric/api/fabric_methods.rst new file mode 100644 index 0000000..9411966 --- /dev/null +++ b/docs/source-fabric/api/fabric_methods.rst @@ -0,0 +1,429 @@ +############## +Fabric Methods +############## + +launch +====== + +With :meth:`~lightning.fabric.fabric.Fabric.launch` you can conveniently launch your script or a function +into multiple processes for distributed training on a single machine. + +.. code-block:: python + + # Launch the script on 2 devices and init distributed backend + fabric = Fabric(devices=2) + fabric.launch() + +The same can be done with code inside a function: + +.. code-block:: python + + def run(fabric): + # Your distributed code here + ... + + + # Launch a function on 2 devices and init distributed backend + fabric = Fabric(devices=2) + fabric.launch(run) + +For example, you can use the latter for multi-GPU training inside a :doc:`Jupyter notebook <../fundamentals/notebooks>`. +For launching distributed training with the CLI, multi-node cluster, or cloud, see :doc:`../fundamentals/launch`. + +setup +===== + +Set up a model and corresponding optimizer(s). If you need to set up multiple models, call ``setup()`` on each of them. +Moves the model and optimizer to the correct device automatically. + +.. code-block:: python + + model = nn.Linear(32, 64) + optimizer = torch.optim.SGD(model.parameters(), lr=0.001) + + # Set up model and optimizer for accelerated training + model, optimizer = fabric.setup(model, optimizer) + + # If you don't want Fabric to set the device + model, optimizer = fabric.setup(model, optimizer, move_to_device=False) + + +The setup method also prepares the model for the selected precision choice so that operations during ``forward()`` get +cast automatically. + +setup_dataloaders +================= + +Set up one or multiple data loaders for accelerated operation. If you run a distributed strategy (e.g., DDP), Fabric +automatically replaces the sampler. In addition, the data loader will be configured to move the returned +data tensors to the correct device automatically. + +.. code-block:: python + + train_data = torch.utils.DataLoader(train_dataset, ...) + test_data = torch.utils.DataLoader(test_dataset, ...) + + train_data, test_data = fabric.setup_dataloaders(train_data, test_data) + + # If you don't want Fabric to move the data to the device + train_data, test_data = fabric.setup_dataloaders(train_data, test_data, move_to_device=False) + + # If you don't want Fabric to replace the sampler in the context of distributed training + train_data, test_data = fabric.setup_dataloaders(train_data, test_data, use_distributed_sampler=False) + + +backward +======== + +This replaces any occurrences of ``loss.backward()`` and makes your code accelerator and precision agnostic. + +.. code-block:: python + + output = model(input) + loss = loss_fn(output, target) + + # loss.backward() + fabric.backward(loss) + + +clip_gradients +============== + +Clip the gradients of the model to a given max value or max norm. +This is useful if your model experiences *exploding gradients* during training. + +.. code-block:: python + + # Clip gradients to a max value of +/- 0.5 + fabric.clip_gradients(model, optimizer, clip_val=0.5) + + # Clip gradients such that their total norm is no bigger than 2.0 + fabric.clip_gradients(model, optimizer, max_norm=2.0) + + # By default, clipping by norm uses the 2-norm + fabric.clip_gradients(model, optimizer, max_norm=2.0, norm_type=2) + + # You can also choose the infinity-norm, which clips the largest + # element among all + fabric.clip_gradients(model, optimizer, max_norm=2.0, norm_type="inf") + +The :meth:`~lightning.fabric.fabric.Fabric.clip_gradients` method is agnostic to the precision and strategy being used. +Note: Gradient clipping with FSDP is not yet fully supported. + + +to_device +========= + +Use :meth:`~lightning.fabric.fabric.Fabric.to_device` to move models, tensors, or collections of tensors to +the current device. By default :meth:`~lightning.fabric.fabric.Fabric.setup` and +:meth:`~lightning.fabric.fabric.Fabric.setup_dataloaders` already move the model and data to the correct +device, so calling this method is only necessary for manual operation when needed. + +.. code-block:: python + + data = torch.load("dataset.pt") + data = fabric.to_device(data) + + +seed_everything +=============== + +Make your code reproducible by calling this method at the beginning of your run. + +.. code-block:: python + + # Instead of `torch.manual_seed(...)`, call: + fabric.seed_everything(1234) + + +This covers PyTorch, NumPy, and Python random number generators. In addition, Fabric takes care of properly initializing +the seed of data loader worker processes (can be turned off by passing ``workers=False``). + +init_module +=========== + +Instantiating a ``nn.Module`` in PyTorch creates all parameters on CPU in float32 precision by default. +To speed up initialization, you can force PyTorch to create the model directly on the target device and with the desired precision without changing your model code. + +.. code-block:: python + + fabric = Fabric(accelerator="cuda", precision="16-true") + + with fabric.init_module(): + # models created here will be on GPU and in float16 + model = MyModel() + +This eliminates the waiting time to transfer the model parameters from the CPU to the device. +For strategies that handle large sharded models (FSDP, DeepSpeed), the :meth:`~lightning.fabric.fabric.Fabric.init_module` method will allocate the model parameters on the meta device first before sharding. +This makes it possible to work with models that are larger than the memory of a single device. + +When loading a model from a checkpoint, for example when fine-tuning, set `empty_init=True` to avoid expensive +and redundant memory initialization: + +.. code-block:: python + + with fabric.init_module(empty_init=True): + # creation of the model is very fast + # and depending on the strategy allocates no memory, or uninitialized memory + model = MyModel() + + # weights get loaded into the model + model.load_state_dict(checkpoint["state_dict"]) + + +autocast +======== + +Let the precision backend autocast the block of code under this context manager. This is optional and already done by +Fabric for the model's forward method (once the model was :meth:`~lightning.fabric.fabric.Fabric.setup`). +You need this only if you wish to autocast more operations outside the ones in model forward: + +.. code-block:: python + + model, optimizer = fabric.setup(model, optimizer) + + # Fabric handles precision automatically for the model + output = model(inputs) + + with fabric.autocast(): # optional + loss = loss_function(output, target) + + fabric.backward(loss) + ... + +See also: :doc:`../fundamentals/precision` + + +print +===== + +Print to the console via the built-in print function, but only on the main process. +This avoids excessive printing and logs when running on multiple devices/nodes. + + +.. code-block:: python + + # Print only on the main process + fabric.print(f"{epoch}/{num_epochs}| Train Epoch Loss: {loss}") + + +save +==== + +Save the state of objects to a checkpoint file. +Replaces all occurrences of ``torch.save(...)`` in your code. +Fabric will handle the saving part correctly, whether running a single device, multi-devices, or multi-nodes. + +.. code-block:: python + + # Define the state of your program/loop + state = { + "model1": model1, + "model2": model2, + "optimizer": optimizer, + "iteration": iteration, + } + + # Instead of `torch.save(...)` + fabric.save("path/to/checkpoint.ckpt", state) + +You should pass the model and optimizer objects directly into the dictionary so Fabric can unwrap them and automatically retrieve their *state-dict*. + +See also: :doc:`../guide/checkpoint` + + +load +==== + +Load checkpoint contents from a file and restore the state of objects in your program. +Replaces all occurrences of ``torch.load(...)`` in your code. +Fabric will handle the loading part correctly, whether running a single device, multi-device, or multi-node. + +.. code-block:: python + + # Define the state of your program/loop + state = { + "model1": model1, + "model2": model2, + "optimizer": optimizer, + "iteration": iteration, + } + + # Restore the state of objects (in-place) + fabric.load("path/to/checkpoint.ckpt", state) + + # Or load everything and restore your objects manually + checkpoint = fabric.load("./checkpoints/version_2/checkpoint.ckpt") + model.load_state_dict(checkpoint["model"]) + ... + + +To load the state of your model or optimizer from a raw PyTorch checkpoint (not saved with Fabric), use :meth:`~lightning.fabric.fabric.Fabric.load_raw` instead. +See also: :doc:`../guide/checkpoint` + + +load_raw +======== + +Load the state-dict of a model or optimizer from a raw PyTorch checkpoint not saved by Fabric. + +.. code-block:: python + + model = MyModel() + + # A model weights file saved by your friend who doesn't use Fabric + fabric.load_raw("path/to/model.pt", model) + + # Equivalent to this: + # model.load_state_dict(torch.load("path/to/model.pt")) + + +See also: :doc:`../guide/checkpoint` + + +barrier +======= + +Call this if you want all processes to wait and synchronize. Once all processes have entered this call, +execution continues. Useful for example, when you want to download data on one process and make all others wait until +the data is written to disk. + +.. code-block:: python + + if fabric.global_rank == 0: + print("Downloading dataset. This can take a while ...") + download_dataset("http://...") + + # All other processes wait here until rank 0 is done with downloading: + fabric.barrier() + + # After everyone reached the barrier, they can access the downloaded files: + load_dataset() + +See also: :doc:`../advanced/distributed_communication` + + +all_gather, all_reduce, broadcast +================================= + +You can send tensors and other data between processes using collective operations. +The three most common ones, :meth:`~lightning.fabric.fabric.Fabric.broadcast`, :meth:`~lightning.fabric.fabric.Fabric.all_gather` and :meth:`~lightning.fabric.fabric.Fabric.all_reduce` are available directly on the Fabric object for convenience: + +- :meth:`~lightning.fabric.fabric.Fabric.broadcast`: Send a tensor from one process to all others. +- :meth:`~lightning.fabric.fabric.Fabric.all_gather`: Gather tensors from every process and stack them. +- :meth:`~lightning.fabric.fabric.Fabric.all_reduce`: Apply a reduction function on tensors across processes (sum, mean, etc.). + +.. code-block:: python + + # Send the value of a tensor from rank 0 to all others + result = fabric.broadcast(tensor, src=0) + + # Every process gets the stack of tensors from everybody else + all_tensors = fabric.all_gather(tensor) + + # Sum a tensor across processes (everyone gets the result) + reduced_tensor = fabric.all_reduce(tensor, reduce_op="sum") + + # Also works with a collection of tensors (dict, list, tuple): + collection = {"loss": torch.tensor(...), "data": ...} + gathered_collection = fabric.all_gather(collection, ...) + reduced_collection = fabric.all_reduce(collection, ...) + + +.. important:: + + Every process needs to enter the collective calls, and tensors need to have the same shape across all processes. + Otherwise, the program will hang! + +Learn more about :doc:`distributed communication <../advanced/distributed_communication>`. + + +no_backward_sync +================ + +Use this context manager when performing gradient accumulation and using a distributed strategy (e.g., DDP). +It will speed up your training loop by cutting redundant communication between processes during the accumulation phase. + +.. code-block:: python + + # Accumulate gradient 8 batches at a time + is_accumulating = batch_idx % 8 != 0 + + with fabric.no_backward_sync(model, enabled=is_accumulating): + output = model(input) + loss = ... + fabric.backward(loss) + ... + + # Step the optimizer every 8 batches + if not is_accumulating: + optimizer.step() + optimizer.zero_grad() + +Both the model's `.forward()` and the `fabric.backward()` call need to run under this context as shown in the example above. +For single-device strategies, it is a no-op. Some strategies don't support this: + +- deepspeed +- dp +- xla + +For these, the context manager falls back to a no-op and emits a warning. + + +call +==== + +Use this to run all registered callback hooks with a given name and inputs. +It is useful when building a Trainer that allows the user to run arbitrary code at fixed points in the training loop. + +.. code-block:: python + + class MyCallback: + def on_train_start(self): + ... + + def on_train_epoch_end(self, model, results): + ... + + + fabric = Fabric(callbacks=[MyCallback()]) + + # Call any hook by name + fabric.call("on_train_start") + + # Pass in additional arguments that the hook requires + fabric.call("on_train_epoch_end", model=..., results={...}) + + # Only the callbacks that have this method defined will be executed + fabric.call("undefined") + + +See also: :doc:`../guide/callbacks` + + +log and log_dict +================ + +These methods allow you to send scalar metrics to a logger registered in Fabric. + +.. code-block:: python + + # Set the logger in Fabric + fabric = Fabric(loggers=TensorBoardLogger(...)) + + # Anywhere in your training loop or model: + fabric.log("loss", loss) + + # Or send multiple metrics at once: + fabric.log_dict({"loss": loss, "accuracy": acc}) + +If no loggers are given to Fabric (default), ``log`` and ``log_dict`` won't do anything. +Here is what's happening under the hood (pseudo code) when you call ``.log()`` or ``log_dict``: + +.. code-block:: python + + # When you call .log() or .log_dict(), we do this: + for logger in fabric.loggers: + logger.log_metrics(metrics=metrics, step=step) + +See also: :doc:`../guide/logging` diff --git a/docs/source-fabric/api/io.rst b/docs/source-fabric/api/io.rst new file mode 100644 index 0000000..8253a4b --- /dev/null +++ b/docs/source-fabric/api/io.rst @@ -0,0 +1,24 @@ +.. include:: ../links.rst + +########################### +lightning.fabric.plugins.io +########################### + + +.. warning:: + This is an `experimental `__ feature. + + +IO +^^ + +.. currentmodule:: lightning.fabric.plugins.io + +.. autosummary:: + :toctree: ./generated + :nosignatures: + :template: classtemplate.rst + + ~checkpoint_io.CheckpointIO + ~torch_io.TorchCheckpointIO + ~xla.XLACheckpointIO diff --git a/docs/source-fabric/api/loggers.rst b/docs/source-fabric/api/loggers.rst new file mode 100644 index 0000000..3c4936c --- /dev/null +++ b/docs/source-fabric/api/loggers.rst @@ -0,0 +1,20 @@ +.. include:: ../links.rst + +######################## +lightning.fabric.loggers +######################## + + +Loggers +^^^^^^^ + +.. currentmodule:: lightning.fabric.loggers + +.. autosummary:: + :toctree: ./generated + :nosignatures: + :template: classtemplate.rst + + Logger + CSVLogger + TensorBoardLogger diff --git a/docs/source-fabric/api/precision.rst b/docs/source-fabric/api/precision.rst new file mode 100644 index 0000000..b48d397 --- /dev/null +++ b/docs/source-fabric/api/precision.rst @@ -0,0 +1,25 @@ +.. include:: ../links.rst + +################################## +lightning.fabric.plugins.precision +################################## + + +Precision +^^^^^^^^^ + +.. TODO(fabric): include DeepSpeedPrecision + +.. currentmodule:: lightning.fabric.plugins.precision + +.. autosummary:: + :toctree: ./generated + :nosignatures: + :template: classtemplate.rst + + Precision + DoublePrecision + MixedPrecision + XLAPrecision + XLABf16Precision + FSDPPrecision diff --git a/docs/source-fabric/api/strategies.rst b/docs/source-fabric/api/strategies.rst new file mode 100644 index 0000000..3547483 --- /dev/null +++ b/docs/source-fabric/api/strategies.rst @@ -0,0 +1,26 @@ +.. include:: ../links.rst + +########################### +lightning.fabric.strategies +########################### + + +Strategies +^^^^^^^^^^ + +.. TODO(fabric): include DeepSpeedStrategy, XLAStrategy + +.. currentmodule:: lightning.fabric.strategies + +.. autosummary:: + :toctree: ./generated + :nosignatures: + :template: classtemplate.rst + + Strategy + DDPStrategy + DataParallelStrategy + FSDPStrategy + ParallelStrategy + SingleDeviceStrategy + SingleDeviceXLAStrategy diff --git a/docs/source-fabric/conf.py b/docs/source-fabric/conf.py new file mode 100644 index 0000000..5c6028f --- /dev/null +++ b/docs/source-fabric/conf.py @@ -0,0 +1,383 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +import glob +import inspect +import os +import shutil +import sys + +import pt_lightning_sphinx_theme +from lightning_utilities.docs import fetch_external_assets + +import lightning + +_PATH_HERE = os.path.abspath(os.path.dirname(__file__)) +_PATH_ROOT = os.path.realpath(os.path.join(_PATH_HERE, "..", "..")) +sys.path.insert(0, os.path.abspath(_PATH_ROOT)) + +SPHINX_MOCK_REQUIREMENTS = int(os.environ.get("SPHINX_MOCK_REQUIREMENTS", True)) + +# -- Project information ----------------------------------------------------- + +# this name shall match the project name in Github as it is used for linking to code +project = "lightning" +copyright = lightning.__copyright__ +author = lightning.__author__ + +# The short X.Y version +version = lightning.__version__ +# The full version, including alpha/beta/rc tags +release = lightning.__version__ + +# Options for the linkcode extension +# ---------------------------------- +github_user = "Lightning-AI" +github_repo = project + +# -- Project documents ------------------------------------------------------- + +fetch_external_assets( + docs_folder=_PATH_HERE, + assets_folder="_static/fetched-s3-assets", + retrieve_pattern=r"https?://[-a-zA-Z0-9_]+\.s3\.[-a-zA-Z0-9()_\\+.\\/=]+", +) + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. + +needs_sphinx = "4.5" + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx_toolbox.collapse", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx.ext.imgmath", + "sphinx.ext.autosectionlabel", + # 'sphinxcontrib.mockautodoc', # raises error: directive 'automodule' is already registered ... + # 'sphinxcontrib.fulltoc', # breaks pytorch-theme with unexpected kw argument 'titles_only' + "sphinxcontrib.video", + "myst_parser", + "sphinx_autodoc_typehints", + "sphinx_copybutton", + "sphinx_paramlinks", + "sphinx_togglebutton", + # "lai_sphinx_theme.extensions.lightning", + "pt_lightning_sphinx_theme.extensions.lightning", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# myst-parser, forcing to parse all html pages with mathjax +# https://github.com/executablebooks/MyST-Parser/issues/394 +myst_update_mathjax = False +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html?highlight=anchor#auto-generated-header-anchors +myst_heading_anchors = 3 + +# https://berkeley-stat159-f17.github.io/stat159-f17/lectures/14-sphinx..html#conf.py-(cont.) +# https://stackoverflow.com/questions/38526888/embed-ipython-notebook-in-sphinx-document +# I execute the notebooks manually in advance. If notebooks test the code, +# they should be run at build time. +nbsphinx_execute = "never" +nbsphinx_allow_errors = True +nbsphinx_requirejs_path = "" + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +# source_suffix = ['.rst', '.md', '.ipynb'] +source_suffix = { + ".rst": "restructuredtext", + ".txt": "markdown", + ".md": "markdown", + ".ipynb": "nbsphinx", +} + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = "en" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + "PULL_REQUEST_TEMPLATE.md", + "**/README.md/*", + "readme.md", + "_templates", + "code_samples/convert_pl_to_app/requirements.txt", + "**/_static/*", +] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +# html_theme = "lai_sphinx_theme" +html_theme = "pt_lightning_sphinx_theme" +html_theme_path = [os.environ.get("LIT_SPHINX_PATH", pt_lightning_sphinx_theme.get_html_theme_path())] + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. + +html_theme_options = { + "pytorch_project": lightning.__homepage__, + "analytics_id": "G-D3Q2ESCTZR", + "canonical_url": lightning.__homepage__, + "collapse_navigation": False, + "display_version": True, + "logo_only": False, +} + +html_favicon = "_static/images/icon.svg" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_templates", "_static"] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = project + "-doc" + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', + # Latex figure (float) alignment + "figure_align": "htbp", +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, project + ".tex", project + " Documentation", author, "manual"), +] + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [(master_doc, project, project + " Documentation", [author], 1)] + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + project, + project + " Documentation", + author, + project, + lightning.__docs__, + "Miscellaneous", + ), +] + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ["search.html"] + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "torch": ("https://pytorch.org/docs/stable/", None), + "pytorch_lightning": ("https://lightning.ai/docs/pytorch/stable/", None), +} + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +def setup(app): + # this is for hiding doctest decoration, + # see: http://z4r.github.io/python/2011/12/02/hides-the-prompts-and-output/ + app.add_js_file("copybutton.js") + app.add_css_file("main.css") + + +# Ignoring Third-party packages +# https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule +def _package_list_from_file(file): + list_pkgs = [] + with open(file) as fp: + lines = fp.readlines() + for ln in lines: + found = [ln.index(ch) for ch in list(",=<>#") if ch in ln] + pkg = ln[: min(found)] if found else ln + if pkg.rstrip(): + list_pkgs.append(pkg.rstrip()) + return list_pkgs + + +# define mapping from PyPI names to python imports +PACKAGE_MAPPING = { + "PyYAML": "yaml", +} +MOCK_PACKAGES = [] +if SPHINX_MOCK_REQUIREMENTS: + # mock also base packages when we are on RTD since we don't install them there + MOCK_PACKAGES += _package_list_from_file(os.path.join(_PATH_ROOT, "requirements.txt")) +MOCK_PACKAGES = [PACKAGE_MAPPING.get(pkg, pkg) for pkg in MOCK_PACKAGES] + +autodoc_mock_imports = MOCK_PACKAGES + + +# Resolve function +# This function is used to populate the (source) links in the API +def linkcode_resolve(domain, info): + def find_source(): + # try to find the file and line number, based on code from numpy: + # https://github.com/numpy/numpy/blob/master/doc/source/conf.py#L286 + obj = sys.modules[info["module"]] + for part in info["fullname"].split("."): + obj = getattr(obj, part) + fname = inspect.getsourcefile(obj) + # https://github.com/rtfd/readthedocs.org/issues/5735 + if any(s in fname for s in ("readthedocs", "rtfd", "checkouts")): + # /home/docs/checkouts/readthedocs.org/user_builds/pytorch_lightning/checkouts/ + # devel/pytorch_lightning/utilities/cls_experiment.py#L26-L176 + path_top = os.path.abspath(os.path.join("..", "..", "..")) + fname = os.path.relpath(fname, start=path_top) + else: + # Local build, imitate master + fname = "master/" + os.path.relpath(fname, start=os.path.abspath("..")) + source, lineno = inspect.getsourcelines(obj) + return fname, lineno, lineno + len(source) - 1 + + if domain != "py" or not info["module"]: + return None + try: + filename = "%s#L%d-L%d" % find_source() + except Exception: + filename = info["module"].replace(".", "/") + ".py" + # import subprocess + # tag = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE, + # universal_newlines=True).communicate()[0][:-1] + branch = filename.split("/")[0] + # do mapping from latest tags to master + branch = {"latest": "master", "stable": "master"}.get(branch, branch) + filename = "/".join([branch] + filename.split("/")[1:]) + return f"https://github.com/{github_user}/{github_repo}/blob/{filename}" + + +autosummary_generate = True + +autodoc_member_order = "groupwise" +autoclass_content = "both" +# the options are fixed and will be soon in release, +# see https://github.com/sphinx-doc/sphinx/issues/5459 +autodoc_default_options = { + "members": None, + "methods": None, + # 'attributes': None, + "special-members": "__call__", + "exclude-members": "_abc_impl", + "show-inheritance": True, + "private-members": True, + "noindex": True, +} + +# Sphinx will add “permalinks” for each heading and description environment as paragraph signs that +# become visible when the mouse hovers over them. +# This value determines the text for the permalink; it defaults to "¶". Set it to None or the empty +# string to disable permalinks. +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_permalinks +# html_add_permalinks = "¶" +# True to prefix each section label with the name of the document it is in, followed by a colon. +# For example, index:Introduction for a section called Introduction that appears in document index.rst. +# Useful for avoiding ambiguity when the same section heading appears in different documents. +# http://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html +autosectionlabel_prefix_document = True + +# only run doctests marked with a ".. doctest::" directive +doctest_test_doctest_blocks = "" +doctest_global_setup = """ +import importlib +import os +import lightning as L + +from lightning_utilities.core.imports import package_available +from lightning import LightningModule, Trainer +from lightning.fabric.loggers.tensorboard import _TENSORBOARD_AVAILABLE, _TENSORBOARDX_AVAILABLE + +_TORCHVISION_AVAILABLE = package_available("torchvision") +""" +coverage_skip_undoc_in_source = True + +# skip false positive linkcheck errors from anchors +linkcheck_anchors = False + +# ignore all links in any CHANGELOG file +linkcheck_exclude_documents = [r"^(.*\/)*CHANGELOG.*$"] diff --git a/docs/source-fabric/examples/index.rst b/docs/source-fabric/examples/index.rst new file mode 100644 index 0000000..3936f2e --- /dev/null +++ b/docs/source-fabric/examples/index.rst @@ -0,0 +1,74 @@ +######## +Examples +######## + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Image Classification + :description: Train an image classifier on the MNIST dataset + :button_link: https://github.com/Lightning-AI/lightning/blob/master/examples/fabric/image_classifier + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Transformer Language Model + :description: A simple language model that learns to predict the next word in a sentence + :button_link: https://github.com/Lightning-AI/lightning/blob/master/examples/fabric/language_model + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: GAN + :description: Train a GAN that generates realistic human faces + :button_link: https://github.com/Lightning-AI/lightning/blob/master/examples/fabric/dcgan + :col_css: col-md-4 + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Meta-Learning + :description: Distributed training with the MAML algorithm on the Omniglot and MiniImagenet datasets + :button_link: https://github.com/Lightning-AI/lightning/blob/master/examples/fabric/meta_learning + :col_css: col-md-4 + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Large Language Models + :description: Pre-train a GPT-2 language model on OpenWebText data + :button_link: https://github.com/Lightning-AI/nanoGPT/blob/master/train_fabric.py + :col_css: col-md-4 + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Reinforcement Learning + :description: Implementation of the Proximal Policy Optimization (PPO) algorithm with multi-GPU support + :button_link: https://github.com/Lightning-AI/lightning/blob/master/examples/fabric/reinforcement_learning + :col_css: col-md-4 + :height: 150 + +.. displayitem:: + :header: K-Fold Cross Validation + :description: Cross validation helps you estimate the generalization error of a model and select the best one. + :button_link: https://github.com/Lightning-AI/lightning/tree/master/examples/fabric/kfold_cv + :col_css: col-md-4 + :height: 150 + +.. displayitem:: + :header: Active Learning + :description: Coming soon + :col_css: col-md-4 + :height: 150 + + +.. raw:: html + +
+
diff --git a/docs/source-fabric/fundamentals/accelerators.rst b/docs/source-fabric/fundamentals/accelerators.rst new file mode 100644 index 0000000..c1496a0 --- /dev/null +++ b/docs/source-fabric/fundamentals/accelerators.rst @@ -0,0 +1,78 @@ +################################ +Accelerate your code with Fabric +################################ + + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric/animations/accelerators.mp4 + :width: 800 + :autoplay: + :loop: + :muted: + :nocontrols: + + +*************************** +Set accelerator and devices +*************************** + +Fabric enables you to take full advantage of the hardware on your system. It supports + +- CPU +- GPU (NVIDIA, AMD, Apple Silicon) +- TPU + +By default, Fabric tries to maximize the hardware utilization of your system + +.. code-block:: python + + # Default settings + fabric = Fabric(accelerator="auto", devices="auto", strategy="auto") + + # Same as + fabric = Fabric() + +This is the most flexible option and makes your code run on most systems. +You can also explicitly set which accelerator to use: + +.. code-block:: python + + # CPU (slow) + fabric = Fabric(accelerator="cpu") + + # GPU + fabric = Fabric(accelerator="gpu", devices=1) + + # GPU (multiple) + fabric = Fabric(accelerator="gpu", devices=8) + + # GPU: Apple M1/M2 only + fabric = Fabric(accelerator="mps") + + # GPU: NVIDIA CUDA only + fabric = Fabric(accelerator="cuda", devices=8) + + # TPU + fabric = Fabric(accelerator="tpu", devices=8) + + +For running on multiple devices in parallel, also known as "distributed", read our guide for :doc:`Launching Multiple Processes <./launch>`. + + +---- + + +***************** +Access the Device +***************** + +You can access the device anytime through ``fabric.device``. +This lets you replace boilerplate code like this: + +.. code-block:: diff + + - if torch.cuda.is_available(): + - device = torch.device("cuda") + - else: + - device = torch.device("cpu") + + + device = fabric.device diff --git a/docs/source-fabric/fundamentals/code_structure.rst b/docs/source-fabric/fundamentals/code_structure.rst new file mode 100644 index 0000000..36b11ff --- /dev/null +++ b/docs/source-fabric/fundamentals/code_structure.rst @@ -0,0 +1,168 @@ +###################################### +How to structure your code with Fabric +###################################### + +Fabric is flexible enough to adapt to any project structure, regardless of whether you are experimenting with a simple script or an extensive framework, because it makes no assumptions about how your code is organized. +Despite the ultimate freedom, this page is meant to give beginners a template for how to organize a typical training script with Fabric: +We also have several :doc:`examples <../examples/index>` that you can take inspiration from. + + +---- + + +***************** +The Main Function +***************** + +At the highest level, every Python script should contain the following boilerplate code to guard the entry point for the main function: + +.. code-block:: python + + def main(): + # Here goes all the rest of the code + ... + + + if __name__ == "__main__": + # This is the entry point of your program + main() + + +This ensures that any form of multiprocessing will work properly (for example, ``DataLoader(num_workers=...)`` etc.) + + +---- + + +************** +Model Training +************** + +Here is a skeleton for training a model in a function ``train()``: + +.. code-block:: python + + import lightning as L + + + def train(fabric, model, optimizer, dataloader): + # Training loop + model.train() + for epoch in range(num_epochs): + for i, batch in enumerate(dataloader): + ... + + + def main(): + # (Optional) Parse command line options + args = parse_args() + + # Configure Fabric + fabric = L.Fabric(...) + + # Instantiate objects + model = ... + optimizer = ... + train_dataloader = ... + + # Set up objects + model, optimizer = fabric.setup(model, optimizer) + train_dataloader = fabric.setup_dataloaders(train_dataloader) + + # Run training loop + train(fabric, model, optimizer, train_dataloader) + + + if __name__ == "__main__": + main() + + +---- + + +***************************** +Training, Validation, Testing +***************************** + +Often it is desired to evaluate the ability of the model to generalize on unseen data. +Here is how the code would be structured if we did that periodically during training (called validation) and after training (called testing). + + +.. code-block:: python + + import lightning as L + + + def train(fabric, model, optimizer, train_dataloader, val_dataloader): + # Training loop with validation every few epochs + model.train() + for epoch in range(num_epochs): + for i, batch in enumerate(train_dataloader): + ... + + if epoch % validate_every_n_epoch == 0: + validate(fabric, model, val_dataloader) + + + def validate(fabric, model, dataloader): + # Validation loop + model.eval() + for i, batch in enumerate(dataloader): + ... + + + def test(fabric, model, dataloader): + # Test/Prediction loop + model.eval() + for i, batch in enumerate(dataloader): + ... + + + def main(): + ... + + # Run training loop with validation + train(fabric, model, optimizer, train_dataloader, val_dataloader) + + # Test on unseen data + test(fabric, model, test_dataloader) + + + if __name__ == "__main__": + main() + + + +---- + + +************ +Full Trainer +************ + +Building a fully-fledged, personalized Trainer can be a lot of work. +To get started quickly, copy `this `_ Trainer template and adapt it to your needs. + +- Only ~500 lines of code, all in one file +- Relies on Fabric to configure accelerator, devices, strategy +- Simple epoch based training with validation loop +- Only essential features included: Checkpointing, loggers, progress bar, callbacks, gradient accumulation + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Trainer Template + :description: Take our Fabric Trainer template and customize it for your needs + :button_link: https://github.com/Lightning-AI/lightning/tree/master/examples/fabric/build_your_own_trainer + :col_css: col-md-4 + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-fabric/fundamentals/convert.rst b/docs/source-fabric/fundamentals/convert.rst new file mode 100644 index 0000000..8b80cb4 --- /dev/null +++ b/docs/source-fabric/fundamentals/convert.rst @@ -0,0 +1,129 @@ +############################## +Convert PyTorch code to Fabric +############################## + +Here are five easy steps to let :class:`~lightning.fabric.fabric.Fabric` scale your PyTorch models. + +**Step 1:** Create the :class:`~lightning.fabric.fabric.Fabric` object at the beginning of your training code. + +.. code-block:: python + + from lightning.fabric import Fabric + + fabric = Fabric() + +**Step 2:** Call :meth:`~lightning.fabric.fabric.Fabric.launch` if you intend to use multiple devices (e.g., multi-GPU). + +.. code-block:: python + + fabric.launch() + +**Step 3:** Call :meth:`~lightning.fabric.fabric.Fabric.setup` on each model and optimizer pair and :meth:`~lightning.fabric.fabric.Fabric.setup_dataloaders` on all your data loaders. + +.. code-block:: python + + model, optimizer = fabric.setup(model, optimizer) + dataloader = fabric.setup_dataloaders(dataloader) + +**Step 4:** Remove all ``.to`` and ``.cuda`` calls since :class:`~lightning.fabric.fabric.Fabric` will take care of it. + +.. code-block:: diff + + - model.to(device) + - batch.to(device) + +**Step 5:** Replace ``loss.backward()`` by ``fabric.backward(loss)``. + +.. code-block:: diff + + - loss.backward() + + fabric.backward(loss) + + +These are all code changes required to prepare your script for Fabric. +You can now simply run from the terminal: + +.. code-block:: bash + + python path/to/your/script.py + +| + +All steps combined, this is how your code will change: + +.. code-block:: diff + + import torch + from lightning.pytorch.demos import WikiText2, Transformer + + import lightning as L + + - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + + fabric = L.Fabric(accelerator="cuda", devices=8, strategy="ddp") + + fabric.launch() + + dataset = WikiText2() + dataloader = torch.utils.data.DataLoader(dataset) + model = Transformer(vocab_size=dataset.vocab_size) + optimizer = torch.optim.SGD(model.parameters(), lr=0.1) + + - model = model.to(device) + + model, optimizer = fabric.setup(model, optimizer) + + dataloader = fabric.setup_dataloaders(dataloader) + + model.train() + for epoch in range(20): + for batch in dataloader: + input, target = batch + - input, target = input.to(device), target.to(device) + optimizer.zero_grad() + output = model(input, target) + loss = torch.nn.functional.nll_loss(output, target.view(-1)) + - loss.backward() + + fabric.backward(loss) + optimizer.step() + + +That's it! You can now train on any device at any scale with a switch of a flag. +Check out our before-and-after example for `image classification `_ and many more :doc:`examples <../examples/index>` that use Fabric. + + +---- + + +********** +Next steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Examples + :description: See examples across computer vision, NLP, RL, etc. + :col_css: col-md-4 + :button_link: ../examples/index.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Accelerators + :description: Take advantage of your hardware with a switch of a flag + :button_link: accelerators.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Build your own Trainer + :description: Learn how to build a trainer tailored for you + :col_css: col-md-4 + :button_link: ../levels/intermediate + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-fabric/fundamentals/installation.rst b/docs/source-fabric/fundamentals/installation.rst new file mode 100644 index 0000000..f7c35f7 --- /dev/null +++ b/docs/source-fabric/fundamentals/installation.rst @@ -0,0 +1,77 @@ +################# +Install Lightning +################# + +Fabric is part of the `Lightning `_ package. Here is how you get it! + +| + +.. raw:: html + +
+
+ +**Pip users** + +.. code-block:: bash + + pip install lightning + +.. raw:: html + +
+
+ +**Conda users** + +.. code-block:: bash + + conda install lightning -c conda-forge + +.. raw:: html + +
+
+ +| + + +If you don't already have it, this command will also install the latest `stable PyTorch version `_. + +You can find our the list of supported PyTorch versions in our `compatibility matrix `__. + + +---- + + +********** +Next steps +********** + +With the installation done, let's get your PyTorch code to the next level. + +.. raw:: html + +
+
+ +.. displayitem:: + :header: From PyTorch to Fabric + :description: Learn how to add Fabric to your PyTorch code + :button_link: ./convert.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Examples + :description: See examples across computer vision, NLP, RL, etc. + :col_css: col-md-4 + :button_link: ../examples/index.html + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-fabric/fundamentals/launch.rst b/docs/source-fabric/fundamentals/launch.rst new file mode 100644 index 0000000..bc4a5ba --- /dev/null +++ b/docs/source-fabric/fundamentals/launch.rst @@ -0,0 +1,243 @@ +########################### +Launch distributed training +########################### + +To run your code distributed across many devices and many machines, you need to do two things: + +1. Configure Fabric with the number of devices and number of machines you want to use +2. Launch your code in multiple processes + + +---- + + +************* +Simple Launch +************* + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric/animations/launch.mp4 + :width: 800 + :autoplay: + :loop: + :muted: + :nocontrols: + +You can configure and launch processes on your machine directly with Fabric's :meth:`~lightning.fabric.fabric.Fabric.launch` method: + +.. code-block:: python + + # train.py + ... + + # Configure accelerator, devices, num_nodes, etc. + fabric = Fabric(devices=4, ...) + + # This launches itself into multiple processes + fabric.launch() + + +In the command line, you run this like any other Python script: + +.. code-block:: bash + + python train.py + + +This is the recommended way for running on a single machine and is the most convenient method for development and debugging. + +It is also possible to use Fabric in a Jupyter notebook (including Google Colab, Kaggle, etc.) and launch multiple processes there. +You can learn more about it :ref:`here `. + + +---- + + +******************* +Launch with the CLI +******************* + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric/animations/launch-cli.mp4 + :width: 800 + :autoplay: + :loop: + :muted: + :nocontrols: + +An alternative way to launch your Python script in multiple processes is to use the dedicated command line interface (CLI): + +.. code-block:: bash + + lightning run model path/to/your/script.py + +This is essentially the same as running ``python path/to/your/script.py``, but it also lets you configure the following settings externally without changing your code: + +- ``--accelerator``: The accelerator to use +- ``--devices``: The number of devices to use (per machine) +- ``--num_nodes``: The number of machines (nodes) to use +- ``--precision``: Which type of precision to use +- ``--strategy``: The strategy (communication layer between processes) + + +.. code-block:: bash + + lightning run model --help + + Usage: lightning run model [OPTIONS] SCRIPT [SCRIPT_ARGS]... + + Run a Lightning Fabric script. + + SCRIPT is the path to the Python script with the code to run. The script + must contain a Fabric object. + + SCRIPT_ARGS are the remaining arguments that you can pass to the script + itself and are expected to be parsed there. + + Options: + --accelerator [cpu|gpu|cuda|mps|tpu] + The hardware accelerator to run on. + --strategy [ddp|dp|deepspeed] Strategy for how to run across multiple + devices. + --devices TEXT Number of devices to run on (``int``), which + devices to run on (``list`` or ``str``), or + ``'auto'``. The value applies per node. + --num-nodes, --num_nodes INTEGER + Number of machines (nodes) for distributed + execution. + --node-rank, --node_rank INTEGER + The index of the machine (node) this command + gets started on. Must be a number in the + range 0, ..., num_nodes - 1. + --main-address, --main_address TEXT + The hostname or IP address of the main + machine (usually the one with node_rank = + 0). + --main-port, --main_port INTEGER + The main port to connect to the main + machine. + --precision [16-mixed|bf16-mixed|32-true|64-true|64|32|16|bf16] + Double precision (``64-true`` or ``64``), + full precision (``32-true`` or ``64``), half + precision (``16-mixed`` or ``16``) or + bfloat16 precision (``bf16-mixed`` or + ``bf16``) + --help Show this message and exit. + + + +Here is how you run DDP with 8 GPUs and `torch.bfloat16 `_ precision: + +.. code-block:: bash + + lightning run model ./path/to/train.py \ + --strategy=ddp \ + --devices=8 \ + --accelerator=cuda \ + --precision="bf16" + +Or `DeepSpeed Zero3 `_ with mixed precision: + +.. code-block:: bash + + lightning run model ./path/to/train.py \ + --strategy=deepspeed_stage_3 \ + --devices=8 \ + --accelerator=cuda \ + --precision=16 + +:class:`~lightning.fabric.fabric.Fabric` can also figure it out automatically for you! + +.. code-block:: bash + + lightning run model ./path/to/train.py \ + --devices=auto \ + --accelerator=auto \ + --precision=16 + + +---- + + +.. _Fabric Cluster: + +******************* +Launch on a Cluster +******************* + +Fabric enables distributed training across multiple machines in several ways. +Choose from the following options based on your expertise level and available infrastructure. + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Lightning Cloud + :description: The easiest way to scale models in the cloud. No infrastructure setup required. + :col_css: col-md-4 + :button_link: ../guide/multi_node/cloud.html + :height: 160 + :tag: basic + +.. displayitem:: + :header: SLURM Managed Cluster + :description: Most popular for academic and private enterprise clusters. + :col_css: col-md-4 + :button_link: ../guide/multi_node/slurm.html + :height: 160 + :tag: intermediate + +.. displayitem:: + :header: Bare Bones Cluster + :description: Train across machines on a network using `torchrun`. + :col_css: col-md-4 + :button_link: ../guide/multi_node/barebones.html + :height: 160 + :tag: advanced + +.. displayitem:: + :header: Other Cluster Environments + :description: MPI, LSF, Kubeflow + :col_css: col-md-4 + :button_link: ../guide/multi_node/other.html + :height: 160 + :tag: advanced + +.. raw:: html + +
+
+ + +---- + + +********** +Next steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Mixed Precision Training + :description: Save memory and speed up training using mixed precision + :col_css: col-md-4 + :button_link: ../fundamentals/precision.html + :height: 160 + :tag: basic + +.. displayitem:: + :header: Distributed Communication + :description: Learn all about communication primitives for distributed operation. Gather, reduce, broadcast, etc. + :button_link: ../advanced/distributed_communication.html + :col_css: col-md-4 + :height: 160 + :tag: advanced + +.. raw:: html + +
+
diff --git a/docs/source-fabric/fundamentals/notebooks.rst b/docs/source-fabric/fundamentals/notebooks.rst new file mode 100644 index 0000000..7cc9fab --- /dev/null +++ b/docs/source-fabric/fundamentals/notebooks.rst @@ -0,0 +1,28 @@ +.. _Fabric in Notebooks: + +################### +Fabric in Notebooks +################### + +Fabric works the same way in notebooks (Jupyter, Google Colab, Kaggle, etc.) if you only run in a single process or GPU. +If you want to use multiprocessing, for example, multi-GPU, you can put your code in a function and pass that function to the +:meth:`~lightning.fabric.fabric.Fabric.launch` method: + + +.. code-block:: python + + + # Notebook Cell + def train(fabric): + model = ... + optimizer = ... + model, optimizer = fabric.setup(model, optimizer) + ... + + + # Notebook Cell + fabric = Fabric(accelerator="cuda", devices=2) + fabric.launch(train) # Launches the `train` function on two GPUs + + +As you can see, this function accepts one argument, the ``Fabric`` object, and it gets launched on as many devices as specified. diff --git a/docs/source-fabric/fundamentals/precision.rst b/docs/source-fabric/fundamentals/precision.rst new file mode 100644 index 0000000..0ab7d46 --- /dev/null +++ b/docs/source-fabric/fundamentals/precision.rst @@ -0,0 +1,235 @@ +################################ +Save memory with mixed precision +################################ + +.. video:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric/animations/precision.mp4 + :width: 800 + :autoplay: + :loop: + :muted: + :nocontrols: + + +************************ +What is Mixed Precision? +************************ + +Like most deep learning frameworks, PyTorch runs on 32-bit floating-point (FP32) arithmetic by default. +However, many deep learning models do not require this to reach complete accuracy during training. +Mixed precision training delivers significant computational speedup by conducting operations in half-precision while keeping minimum information in single-precision to maintain as much information as possible in crucial areas of the network. +Switching to mixed precision has resulted in considerable training speedups since the introduction of Tensor Cores in the Volta and Turing architectures. +It combines FP32 and lower-bit floating points (such as FP16) to reduce memory footprint and increase performance during model training and evaluation. +It accomplishes this by recognizing the steps that require complete accuracy and employing a 32-bit floating point for those steps only while using a 16-bit floating point for the rest. +Compared to complete precision training, mixed precision training delivers all these benefits while ensuring no task-specific accuracy is lost `[1] `_. + +This is how you select the precision in Fabric: + +.. code-block:: python + + from lightning.fabric import Fabric + + # This is the default + fabric = Fabric(precision="32-true") + + # Also FP32 (legacy) + fabric = Fabric(precision=32) + + # FP32 as well (legacy) + fabric = Fabric(precision="32") + + # Float16 mixed precision + fabric = Fabric(precision="16-mixed") + + # Float16 true half precision + fabric = Fabric(precision="16-true") + + # BFloat16 mixed precision (Volta GPUs and later) + fabric = Fabric(precision="bf16-mixed") + + # BFloat16 true half precision (Volta GPUs and later) + fabric = Fabric(precision="bf16-true") + + # 8-bit mixed precision via TransformerEngine (Hopper GPUs and later) + fabric = Fabric(precision="transformer-engine") + + # Double precision + fabric = Fabric(precision="64-true") + + # Or (legacy) + fabric = Fabric(precision="64") + + # Or (legacy) + fabric = Fabric(precision=64) + + +The same values can also be set through the :doc:`command line interface `: + +.. code-block:: bash + + lightning run model train.py --precision=bf16-mixed + + +.. note:: + + In some cases, it is essential to remain in FP32 for numerical stability, so keep this in mind when using mixed precision. + For example, when running scatter operations during the forward (such as torchpoint3d), the computation must remain in FP32. + + +---- + + +******************** +FP16 Mixed Precision +******************** + +In most cases, mixed precision uses FP16. +Supported `PyTorch operations `_ automatically run in FP16, saving memory and improving throughput on the supported accelerators. +Since computation happens in FP16, which has a very limited "dynamic range", there is a chance of numerical instability during training. +This is handled internally by a dynamic grad scaler which skips invalid steps and adjusts the scaler to ensure subsequent steps fall within a finite range. +For more information `see the autocast docs `_. + +This is how you enable FP16 in Fabric: + +.. code-block:: python + + # Select FP16 mixed precision + fabric = Fabric(precision="16-mixed") + +.. note:: + + When using TPUs, setting ``precision="16-mixed"`` will enable bfloat16 based mixed precision, the only supported half-precision type on TPUs. + + +---- + + +************************ +BFloat16 Mixed Precision +************************ + +BFloat16 Mixed precision is similar to FP16 mixed precision. However, it maintains more of the "dynamic range" that FP32 offers. +This means it can improve numerical stability than FP16 mixed precision. +For more information, see `this TPU performance blog post `_. + +.. code-block:: python + + # Select BF16 precision + fabric = Fabric(precision="bf16-mixed") + + +Under the hood, we use `torch.autocast `__ with the dtype set to ``bfloat16``, with no gradient scaling. +It is also possible to use BFloat16 mixed precision on the CPU, relying on MKLDNN. + +.. note:: + + BFloat16 may not provide significant speedups or memory improvements, offering better numerical stability. + For GPUs, the most significant benefits require `Ampere `_ based GPUs or newer, such as A100s or 3090s. + + +---- + + +***************************************************** +Float8 Mixed Precision via Nvidia's TransformerEngine +***************************************************** + +`Transformer Engine `__ (TE) is a library for accelerating models on the +latest NVIDIA GPUs using 8-bit floating point (FP8) precision on Hopper GPUs, to provide better performance with lower +memory utilization in both training and inference. It offers improved performance over half precision with no degradation in accuracy. + +Using TE requires replacing some of the layers in your model. Fabric automatically replaces the :class:`torch.nn.Linear` +and :class:`torch.nn.LayerNorm` layers in your model with their TE alternatives, however, TE also offers +`fused layers `__ +to squeeze out all the possible performance. If Fabric detects that any layer has been replaced already, automatic +replacement is not done. + +This plugin is a mix of "mixed" and "true" precision. The computation is downcasted to FP8 precision on the fly, but +the model and inputs can be kept in true full or half precision. + +.. code-block:: python + + # Select 8bit mixed precision via TransformerEngine + fabric = Fabric(precision="transformer-engine") + + # Customize the fp8 recipe or set a different base precision: + from lightning.fabric.plugins.precision import TransformerEnginePrecision + + recipe = {"fp8_format": "HYBRID", "amax_history_len": 16, "amax_compute_algo": "max"} + precision = TransformerEnginePrecision(dtype=torch.bfloat16, recipe=recipe) + fabric = Fabric(plugins=precision) + + +Under the hood, we use `transformer_engine.pytorch.fp8_autocast `__ with the default fp8 recipe. + +.. note:: + + This requires `Hopper `_ based GPUs or newer, such the H100. + + +---- + + +******************* +True Half Precision +******************* + +As mentioned before, for numerical stability mixed precision keeps the model weights in full float32 precision while casting only supported operations to lower bit precision. +However, in some cases it is indeed possible to train completely in half precision. Similarly, for inference the model weights can often be cast to half precision without a loss in accuracy (even when trained with mixed precision). + +.. code-block:: python + + # Select FP16 precision + fabric = Fabric(precision="16-true") + model = MyModel() + model = fabric.setup(model) # model gets cast to torch.float16 + + # Select BF16 precision + fabric = Fabric(precision="bf16-true") + model = MyModel() + model = fabric.setup(model) # model gets cast to torch.bfloat16 + +Tip: For faster initialization, you can create model parameters with the desired dtype directly on the device: + +.. code-block:: python + + fabric = Fabric(precision="bf16-true") + + # init the model directly on the device and with parameters in half-precision + with fabric.init_module(): + model = MyModel() + + model = fabric.setup(model) + + +---- + + +************************************ +Control where precision gets applied +************************************ + +Fabric automatically casts the data type and operations in the ``forward`` of your model: + +.. code-block:: python + + fabric = Fabric(precision="bf16-mixed") + + model = ... + optimizer = ... + + # Here, Fabric sets up the `model.forward` for precision auto-casting + model, optimizer = fabric.setup(model, optimizer) + + # Precision casting gets handled in your forward, no code changes required + output = model.forward(input) + + # Precision does NOT get applied here (only in forward) + loss = loss_function(output, target) + +If you want to enable operations in lower bit-precision **outside** your model's ``forward()``, you can use the :meth:`~lightning.fabric.fabric.Fabric.autocast` context manager: + +.. code-block:: python + + # Precision now gets also handled in this part of the code: + with fabric.autocast(): + loss = loss_function(output, target) diff --git a/docs/source-fabric/glossary/index.rst b/docs/source-fabric/glossary/index.rst new file mode 100644 index 0000000..4b23fce --- /dev/null +++ b/docs/source-fabric/glossary/index.rst @@ -0,0 +1,185 @@ +######## +Glossary +######## + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Accelerator + :button_link: ../fundamentals/accelerators.html + :col_css: col-md-4 + +.. displayitem:: + :header: Apple Silicon + :button_link: ../fundamentals/accelerators.html + :col_css: col-md-4 + +.. displayitem:: + :header: Autocast + :button_link: ../fundamentals/precision.html + :col_css: col-md-4 + +.. displayitem:: + :header: Barrier + :button_link: ../advanced/distributed_communication.html + :col_css: col-md-4 + +.. displayitem:: + :header: Broadcast + :button_link: ../advanced/distributed_communication.html + :col_css: col-md-4 + +.. displayitem:: + :header: Callback + :button_link: ../guide/callbacks.html + :col_css: col-md-4 + +.. displayitem:: + :header: Checkpoint + :button_link: ../guide/checkpoint.html + :col_css: col-md-4 + +.. displayitem:: + :header: CLI + :button_link: ../fundamentals/launch.html + :col_css: col-md-4 + +.. displayitem:: + :header: Cloud + :button_link: ../guide/multi_node/cloud.html + :col_css: col-md-4 + +.. displayitem:: + :header: Collective + :button_link: ../advanced/distributed_communication.html + :col_css: col-md-4 + +.. displayitem:: + :header: CUDA + :button_link: ../fundamentals/accelerators.html + :col_css: col-md-4 + +.. displayitem:: + :header: FSDP + :button_link: ../advanced/model_parallel/fsdp.html + :col_css: col-md-4 + +.. displayitem:: + :header: Gather + :button_link: ../advanced/distributed_communication.html + :col_css: col-md-4 + +.. displayitem:: + :header: Gradient Accumulation + :button_link: ../advanced/gradient_accumulation.html + :col_css: col-md-4 + +.. displayitem:: + :header: GPU + :button_link: ../fundamentals/accelerators.html + :col_css: col-md-4 + +.. displayitem:: + :header: Jypyter + :button_link: ../launch/notebooks.html + :col_css: col-md-4 + +.. displayitem:: + :header: Launch + :button_link: ../fundamentals/launch.html + :col_css: col-md-4 + +.. displayitem:: + :header: LightningModule + :button_link: ../guide/lightning_module.html + :col_css: col-md-4 + +.. displayitem:: + :header: Logger + :button_link: ../guide/logging.html + :col_css: col-md-4 + +.. displayitem:: + :header: Mixed Precision + :button_link: ../fundamentals/precision.html + :col_css: col-md-4 + +.. displayitem:: + :header: MPI + :button_link: ../guide/multi_node/other.html + :col_css: col-md-4 + +.. displayitem:: + :header: MPS + :button_link: ../fundamentals/accelerators.html + :col_css: col-md-4 + +.. displayitem:: + :header: Multi-GPU + :button_link: ../fundamentals/launch.html + :col_css: col-md-4 + +.. displayitem:: + :header: Multi-Node + :button_link: ../fundamentals/launch.html + :col_css: col-md-4 + +.. displayitem:: + :header: Notebook + :button_link: ../launch/notebook.html + :col_css: col-md-4 + +.. displayitem:: + :header: Optimizers + :button_link: ../advanced/multiple_setup.html + :col_css: col-md-4 + +.. displayitem:: + :header: Precision + :button_link: ../fundamentals/precision.html + :col_css: col-md-4 + +.. displayitem:: + :header: Reduce + :button_link: ../advanced/distributed_communication.html + :col_css: col-md-4 + +.. displayitem:: + :header: SLURM + :button_link: ../guide/multi_node/slurm.html + :col_css: col-md-4 + +.. displayitem:: + :header: TensorBoard + :button_link: ../guide/logging.html + :col_css: col-md-4 + +.. displayitem:: + :header: TorchElastic + :button_link: ../guide/multi_node/barebones.html + :col_css: col-md-4 + +.. displayitem:: + :header: TorchRun + :button_link: ../guide/multi_node/barebones.html + :col_css: col-md-4 + +.. displayitem:: + :header: TPU + :button_link: ../fundamentals/accelerators.html + :col_css: col-md-4 + +.. displayitem:: + :header: Trainer + :button_link: ../guide/trainer_template.html + :col_css: col-md-4 + + +.. raw:: html + +
+
diff --git a/docs/source-fabric/guide/callbacks.rst b/docs/source-fabric/guide/callbacks.rst new file mode 100644 index 0000000..87fd58e --- /dev/null +++ b/docs/source-fabric/guide/callbacks.rst @@ -0,0 +1,113 @@ +######### +Callbacks +######### + +Callbacks enable you, or the users of your code, to add new behavior to the training loop without needing to modify the source code. + + +---- + + +************************************* +Add a callback interface to your loop +************************************* + +Suppose we want to enable anyone to run some arbitrary code at the end of a training iteration. +Here is how that gets done in Fabric: + +.. code-block:: python + :caption: my_callbacks.py + + class MyCallback: + def on_train_batch_end(self, loss, output): + # Here, put any code you want to run at the end of a training step + ... + + +.. code-block:: python + :caption: train.py + :emphasize-lines: 4,7,18 + + from lightning.fabric import Fabric + + # The code of a callback can live anywhere, away from the training loop + from my_callbacks import MyCallback + + # Add one or several callbacks: + fabric = Fabric(callbacks=[MyCallback()]) + + ... + + for iteration, batch in enumerate(train_dataloader): + ... + fabric.backward(loss) + optimizer.step() + + # Let a callback add some arbitrary processing at the appropriate place + # Give the callback access to some varibles + fabric.call("on_train_batch_end", loss=loss, output=...) + + +As you can see, the code inside the callback method is completely decoupled from the trainer code. +This enables flexibility in extending the loop in arbitrary ways. + +**Exercise**: Implement a callback that computes and prints the time to complete an iteration. + + +---- + + +****************** +Multiple callbacks +****************** + +The callback system is designed to easily run multiple callbacks at the same time. +You can pass a list to Fabric: + +.. code-block:: python + + # Add multiple callback implementations in a list + callback1 = LearningRateMonitor() + callback2 = Profiler() + fabric = Fabric(callbacks=[callback1, callback2]) + + # Let Fabric call the implementations (if they exist) + fabric.call("any_callback_method", arg1=..., arg2=...) + + # fabric.call is the same as doing this + callback1.any_callback_method(arg1=..., arg2=...) + callback2.any_callback_method(arg1=..., arg2=...) + + +The :meth:`~lightning.fabric.fabric.Fabric.call` calls the callback objects in the order they were given to Fabric. +Not all objects registered via ``Fabric(callbacks=...)`` must implement a method with the given name. +The ones that have a matching method name will get called. + + +---- + + +********** +Next steps +********** + +Callbacks are a powerful tool for building a Trainer. +See a real example of how they can be integrated in our Trainer template based on Fabric: + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Trainer Template + :description: Take our Fabric Trainer template and customize it for your needs + :button_link: https://github.com/Lightning-AI/lightning/tree/master/examples/fabric/build_your_own_trainer + :col_css: col-md-4 + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-fabric/guide/checkpoint.rst b/docs/source-fabric/guide/checkpoint.rst new file mode 100644 index 0000000..50e4cf5 --- /dev/null +++ b/docs/source-fabric/guide/checkpoint.rst @@ -0,0 +1,204 @@ +############################## +Saving and Loading Checkpoints +############################## + +Fabric makes it easy and efficient to save the state of your training loop into a checkpoint file, no matter how large your model is. + +---- + + +******************************** +Define the state of your program +******************************** + +To save and resume your training, you need to define which variables in your program you want to have saved. +Put everything into a dictionary, including models and optimizers and whatever metadata you have: + +.. code-block:: python + + # Define the state of your program/loop + state = {"model1": model1, "model2": model2, "optimizer": optimizer, "iteration": iteration, "hparams": ...} + + +---- + + +***************** +Save a checkpoint +***************** + +To save the state to the filesystem, pass it to the :meth:`~lightning.fabric.fabric.Fabric.save` method: + +.. code-block:: python + + fabric.save("path/to/checkpoint.ckpt", state) + +This will unwrap your model and optimizer and automatically convert their ``state_dict`` for you. +Fabric and the underlying strategy will decide in which format your checkpoint gets saved. +For example, ``strategy="ddp"`` saves a single file on rank 0, while ``strategy="fsdp"`` saves multiple files from all ranks. + + +---- + + +************************* +Restore from a checkpoint +************************* + +From a checkpoint saved by Fabric +================================= + +You can restore the state by loading a saved checkpoint back with :meth:`~lightning.fabric.fabric.Fabric.load`: + +.. code-block:: python + + fabric.load("path/to/checkpoint.ckpt", state) + +Fabric will replace the state of your objects in-place. +You can also request only to restore a portion of the checkpoint. +For example, you want only to restore the model weights in your inference script: + +.. code-block:: python + + state = {"model1": model1} + remainder = fabric.load("path/to/checkpoint.ckpt", state) + +The remainder of the checkpoint that wasn't restored gets returned in case you want to do something else with it. +If you want to be in complete control of how states get restored, you can omit passing a state and get the entire raw checkpoint dictionary returned: + +.. code-block:: python + + # Request the raw checkpoint + full_checkpoint = fabric.load("path/to/checkpoint.ckpt") + + model.load_state_dict(full_checkpoint["model"]) + optimizer.load_state_dict(full_checkpoint["optimizer"]) + ... + + +From a raw state-dict file +========================== + +You can load a raw weights file into a model directly using the :meth:`~lightning.fabric.fabric.Fabric.load_raw` method: + +.. code-block:: python + + model = MyModel() + + # A model weights file saved by your friend who doesn't use Fabric + fabric.load_raw("path/to/model.pt", model) + + # Equivalent to this: + # model.load_state_dict(torch.load("path/to/model.pt")) + + # Also supports optimizers + optimizer = torch.optim.Adam(model.parameters()) + fabric.load_raw("path/to/optimizer.pt", optimizer) + +The file to load must contain a valid state-dict for the model/optimizer. +If your checkpoint has a different format, you will have to convert it manually first. + + +---- + + +************************* +Load a partial checkpoint +************************* + +Loading a checkpoint is normally "strict", meaning parameter names in the checkpoint must match the parameter names in the model. +However, when loading checkpoints for fine-tuning or transfer learning, it can happen that only a portion of the parameters match the model. +For this case, you can disable strict loading to avoid errors: + +.. code-block:: python + + state = {"model": model} + + # strict loading is the default + fabric.load("path/to/checkpoint.ckpt", state, strict=True) + + # disable strict loading + fabric.load("path/to/checkpoint.ckpt", state, strict=False) + + +Here is a trivial example to illustrate how it works: + +.. code-block:: python + + import torch + import lightning as L + + fabric = L.Fabric() + + # Save a checkpoint of a trained model + model1 = torch.nn.Linear(2, 2, bias=True) + state = {"model": model1} + fabric.save("state.ckpt", state) + + # Later on, make a new model that misses a parameter + model2 = torch.nn.Linear(2, 2, bias=False) + state = {"model": model2} + + # `strict=True` would lead to an error, because the bias + # parameter is missing, but we can load the rest of the + # parameters successfully + fabric.load("state.ckpt", state, strict=False) + + +The :meth:`~lightning.fabric.fabric.Fabric.load_raw` method also supports the ``strict`` argument. +See also: `Saving and loading models in PyTorch `_. + + +---- + +************************* +Save a partial checkpoint +************************* + +When saving a checkpoint using Fabric, you have the flexibility to choose which parameters to include in the saved file. +This can be useful in scenarios such as fine-tuning, where you only want to save a subset of the parameters, reducing +the size of the checkpoint and saving disk space. + +To accomplish this, you can use filters during the saving process. The filter is a function that determines whether +an item should be saved (returning ``True``) or excluded (returning ``False``). +The filter operates on dictionary objects and evaluates each key-value pair individually. + +Here's an example of using a filter when saving a checkpoint: + +.. code-block:: python + + state = {"model": model, "optimizer": optimizer, "foo": 123} + + # save only the model weights + filter = {"model": lambda k, v: "weight"} + fabric.save("path/to/checkpoint.ckpt", state, filter=filter) + # This will save {"model": {"layer.weight": ...}, "optimizer": ..., "foo": 123} + # note that the optimizer params corresponding to the excluded model params are not filtered + + +---- + + +********** +Next steps +********** + +Learn from our template how Fabrics checkpoint mechanism can be integrated into a full Trainer: + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Trainer Template + :description: Take our Fabric Trainer template and customize it for your needs + :button_link: https://github.com/Lightning-AI/lightning/tree/master/examples/fabric/build_your_own_trainer + :col_css: col-md-4 + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-fabric/guide/index.rst b/docs/source-fabric/guide/index.rst new file mode 100644 index 0000000..4d68aa7 --- /dev/null +++ b/docs/source-fabric/guide/index.rst @@ -0,0 +1,171 @@ +############# +How-to Guides +############# + + +****** +Basics +****** + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Convert to Fabric in 5 minutes + :description: Learn how to add Fabric to your PyTorch code + :button_link: ../fundamentals/convert.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Scale your model with Accelerators + :description: Take advantage of your hardware with a switch of a flag + :button_link: ../fundamentals/accelerators.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Structure your Fabric code + :description: Best practices for setting up your training script with Fabric + :button_link: ../fundamentals/code_structure.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Launch distributed training + :description: Launch a Python script on multiple devices and machines + :button_link: ../fundamentals/launch.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Launch Fabric in a notebook + :description: Launch on multiple devices from within a Jupyter notebook + :button_link: ../fundamentals/notebooks.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Improve performance with Mixed-Precision training + :description: Save memory and speed up training using mixed precision + :button_link: ../fundamentals/precision.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. raw:: html + +
+
+ + + +********************** +Build your own Trainer +********************** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Organize your model code with with LightningModule + :description: Organize your code in a LightningModule and use it with Fabric + :button_link: lightning_module.html + :col_css: col-md-4 + :height: 170 + :tag: intermediate + +.. displayitem:: + :header: Encapsulate code into Callbacks + :description: Make use of the Callback system in Fabric + :button_link: callbacks.html + :col_css: col-md-4 + :height: 170 + :tag: intermediate + +.. displayitem:: + :header: Track and visualize experiments + :description: Learn how Fabric helps you remove boilerplate code for tracking metrics with a logger + :button_link: logging.html + :col_css: col-md-4 + :height: 170 + :tag: intermediate + +.. displayitem:: + :header: Save and load model progress + :description: Efficient saving and loading of model weights, training state, hyperparameters and more. + :button_link: checkpoint.html + :col_css: col-md-4 + :height: 170 + :tag: intermediate + +.. displayitem:: + :header: Build your own Trainer + :description: Take our Fabric Trainer template and customize it for your needs + :button_link: https://github.com/Lightning-AI/lightning/tree/master/examples/fabric/build_your_own_trainer + :col_css: col-md-4 + :height: 170 + :tag: intermediate + +.. raw:: html + +
+
+ + +*************** +Advanced Topics +*************** + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Use efficient gradient accumulation + :description: Learn how to perform efficient gradient accumulation in distributed settings + :button_link: ../advanced/gradient_accumulation.html + :col_css: col-md-4 + :height: 160 + :tag: advanced + +.. displayitem:: + :header: Distribute communication + :description: Learn all about communication primitives for distributed operation. Gather, reduce, broadcast, etc. + :button_link: ../advanced/distributed_communication.html + :col_css: col-md-4 + :height: 160 + :tag: advanced + +.. displayitem:: + :header: Use multiple models and optimizers + :description: See how flexible Fabric is to work with multiple models and optimizers! + :button_link: ../advanced/multiple_setup.html + :col_css: col-md-4 + :height: 160 + :tag: advanced + +.. displayitem:: + :header: Train models with billions of parameters + :description: Train the largest models with FSDP across multiple GPUs and machines + :button_link: ../advanced/model_parallel/fsdp.html + :col_css: col-md-4 + :height: 160 + :tag: advanced + +.. raw:: html + +
+
diff --git a/docs/source-fabric/guide/lightning_module.rst b/docs/source-fabric/guide/lightning_module.rst new file mode 100644 index 0000000..6140a97 --- /dev/null +++ b/docs/source-fabric/guide/lightning_module.rst @@ -0,0 +1,148 @@ +################## +Organize Your Code +################## + +Any raw PyTorch can be converted to Fabric with zero refactoring required, giving maximum flexibility in how you want to organize your projects. + +However, when developing a project in a team or sharing the code publicly, it can be beneficial to conform to a standard format of how core pieces of the code are organized. +This is what the `LightningModule `_ was made for! + +Here is how you can neatly separate the research code (model, loss, optimization, etc.) from the "trainer" code (training loop, checkpointing, logging, etc.). + + +---- + + +************************************************* +Step 1: Move your code into LightningModule hooks +************************************************* + +Take these main ingredients and put them in a LightningModule: + +- The PyTorch model(s) as an attribute (e.g. ``self.model``) +- The forward, including loss computation, goes into ``training_step()`` +- Setup of optimizer(s) goes into ``configure_optimizers()`` +- Setup of the training data loader goes into ``train_dataloader()`` + + +.. code-block:: python + + import lightning as L + + + class LitModel(L.LightningModule): + def __init__(self): + super().__init__() + self.model = ... + + def training_step(self, batch, batch_idx): + # Main forward, loss computation, and metrics goes here + x, y = batch + y_hat = self.model(x) + loss = self.loss_fn(y, y_hat) + acc = self.accuracy(y, y_hat) + ... + return loss + + def configure_optimizers(self): + # Return one or several optimizers + return torch.optim.Adam(self.parameters(), ...) + + def train_dataloader(self): + # Return your dataloader for training + return DataLoader(...) + + def on_train_start(self): + # Do something at the beginning of training + ... + + def any_hook_you_like(self, *args, **kwargs): + ... + + +This is a minimal LightningModule, but there are `many other useful hooks `_ you can use. + + +---- + + +**************************************** +Step 2: Call hooks from your Fabric code +**************************************** + +In your Fabric training loop, you can now call the hooks of the LightningModule interface. +It is up to you to call everything at the right place. + +.. code-block:: python + + import lightning as L + + fabric = L.Fabric(...) + + # Instantiate the LightningModule + model = LitModel() + + # Get the optimizer(s) from the LightningModule + optimizer = model.configure_optimizers() + + # Get the training data loader from the LightningModule + train_dataloader = model.train_dataloader() + + # Set up objects + model, optimizer = fabric.setup(model, optimizer) + train_dataloader = fabric.setup_dataloaders(train_dataloader) + + # Call the hooks at the right time + model.on_train_start() + + model.train() + for epoch in range(num_epochs): + for i, batch in enumerate(dataloader): + optimizer.zero_grad() + loss = model.training_step(batch, i) + fabric.backward(loss) + optimizer.step() + + # Control when hooks are called + if condition: + model.any_hook_you_like() + + +Your code is now modular. You can switch out the entire LightningModule implementation for another one, and you don't need to touch the training loop: + +.. code-block:: diff + + # Instantiate the LightningModule + - model = LitModel() + + model = DopeModel() + + ... + + +---- + + +************************************ +Access Fabric inside LightningModule +************************************ + +You can access the Fabric instance in any of the LightningModule hooks via ``self.fabric``, provided that you called +``fabric.setup()`` on the module. + +.. code-block:: python + + import lightning as L + + + class LitModel(L.LightningModule): + def on_train_start(self): + # Access Fabric and its attributes + print(self.fabric.world_size) + + + fabric = L.Fabric() + model = fabric.setup(LitModel()) + model.on_train_start() + +To maximize compatibility with LightningModules written for the Lightning Trainer, ``self.trainer`` is also available and will +reroute to ``self.fabric``. diff --git a/docs/source-fabric/guide/logging.rst b/docs/source-fabric/guide/logging.rst new file mode 100644 index 0000000..36b9513 --- /dev/null +++ b/docs/source-fabric/guide/logging.rst @@ -0,0 +1,123 @@ +############################### +Track and Visualize Experiments +############################### + +******************************* +Why do I need to track metrics? +******************************* + +In model development, we track values of interest, such as the *validation_loss* to visualize the learning process for our models. +Model development is like driving a car without windows. Charts and logs provide the *windows* to know where to drive the car. + +With Lightning, you can visualize virtually anything you can think of: numbers, text, images, and audio. + +---- + +************* +Track metrics +************* + +Metric visualization is the most basic but powerful way to understand how your model is doing throughout development. +To track a metric, add the following: + +**Step 1:** Pick a logger. + +.. code-block:: python + + from lightning.fabric import Fabric + from lightning.fabric.loggers import TensorBoardLogger + + # Pick a logger and add it to Fabric + logger = TensorBoardLogger(root_dir="logs") + fabric = Fabric(loggers=logger) + + +Built-in loggers you can choose from: + +- :class:`~lightning.fabric.loggers.TensorBoardLogger` +- :class:`~lightning.fabric.loggers.CSVLogger` + +| + +**Step 2:** Add :meth:`~lightning.fabric.fabric.Fabric.log` calls in your code. + +.. code-block:: python + + value = ... # Python scalar or tensor scalar + fabric.log("some_value", value) + + +To log multiple metrics at once, use :meth:`~lightning.fabric.fabric.Fabric.log_dict`: + +.. code-block:: python + + values = {"loss": loss, "acc": acc, "other": other} + fabric.log_dict(values) + + +---- + + +******************* +View logs dashboard +******************* + +How you can view the metrics depends on the individual logger you choose. +Most have a dashboard that lets you browse everything you log in real time. + +For the :class:`~lightning.fabric.loggers.tensorboard.TensorBoardLogger` shown above, you can open it by running + +.. code-block:: bash + + tensorboard --logdir=./logs + +If you're using a notebook environment such as *Google Colab* or *Kaggle* or *Jupyter*, launch TensorBoard with this command + +.. code-block:: bash + + %reload_ext tensorboard + %tensorboard --logdir=./logs + + +---- + + +************************* +Control logging frequency +************************* + +Logging a metric in every iteration can slow down the training. +Reduce the added overhead by logging less frequently: + +.. code-block:: python + :emphasize-lines: 3 + + for iteration in range(num_iterations): + if iteration % log_every_n_steps == 0: + value = ... + fabric.log("some_value", value) + + +---- + + +******************** +Use multiple loggers +******************** + +You can add as many loggers as you want without changing the logging code in your loop. + +.. code-block:: python + :emphasize-lines: 8 + + from lightning.fabric import Fabric + from lightning.fabric.loggers import CSVLogger, TensorBoardLogger + + tb_logger = TensorBoardLogger(root_dir="logs/tensorboard") + csv_logger = CSVLogger(root_dir="logs/csv") + + # Add multiple loggers in a list + fabric = Fabric(loggers=[tb_logger, csv_logger]) + + # Calling .log() or .log_dict() always logs to all loggers simultaneously + fabric.log("some_value", value) diff --git a/docs/source-fabric/guide/multi_node/barebones.rst b/docs/source-fabric/guide/multi_node/barebones.rst new file mode 100644 index 0000000..825f150 --- /dev/null +++ b/docs/source-fabric/guide/multi_node/barebones.rst @@ -0,0 +1,161 @@ +:orphan: + +################## +Bare Bones Cluster +################## + +**Audience**: Users who want to train on multiple machines that aren't part of a managed cluster. + +This guide shows how to run a training job on a general-purpose cluster. +It assumes that you can log in to each machine and run commands. + +Don't want to maintain your own infrastructure? Try the :doc:`Lightning cloud <./cloud>` instead. + + +---- + + +************ +Requirements +************ + +To set up a multi-node computing cluster, you need the following: + +1. Multiple computers with Lightning installed +2. A network connectivity between the machines with firewall rules that allow traffic flow on a specified port. + +| + +We highly recommend setting up a shared filesystem to avoid the cumbersome copying of files between machines. + + +---- + + +*************************** +Prepare the training script +*************************** + +.. code-block:: python + :caption: train.py + + from lightning.fabric import Fabric + + fabric = Fabric() + + # The rest of the training script + ... + +We intentionally omit to specify ``strategy``, ``devices``, and ``num_nodes`` here because these settings will get supplied through the CLI in the later steps. +You can still hard-code other options if you like. + + +---- + + +********************************* +Launch the script on your cluster +********************************* + +**Step 1**: Upload the training script and all needed files to the cluster. +Each node needs access to the same files. +If the nodes don't attach to a shared network drive, you'll need to upload the files to each node separately. + +**Step 2**: Pick one of the nodes as your main node and write down its IP address. +Example: 10.10.10.16 + +**Step 3**: Launch the script on each node using the Lightning CLI. + +In this example, we want to launch training across two nodes, each with 8 GPUs. +Log in to the **first node** and run this command: + +.. code-block:: bash + :emphasize-lines: 2,3 + + lightning run model \ + --node-rank=0 \ + --main-address=10.10.10.16 \ + --accelerator=cuda \ + --devices=8 \ + --num-nodes=2 \ + train.py + +Log in to the **second node** and run this command: + +.. code-block:: bash + :emphasize-lines: 2,3 + + lightning run model \ + --node-rank=1 \ + --main-address=10.10.10.16 \ + --accelerator=cuda \ + --devices=8 \ + --num-nodes=2 \ + train.py + +Note: The only difference between the two commands is the ``--node-rank`` setting, which identifies each node. +After executing these commands, you should immediately see an output like this: + +.. code-block:: + + Initializing distributed: GLOBAL_RANK: 0, MEMBER: 1/16 + Initializing distributed: GLOBAL_RANK: 1, MEMBER: 2/16 + ... + + +---- + + +*************** +Troubleshooting +*************** + + +**My program is stuck initializing at startup. What is causing this?** + +You are seeing a message like this in the logs, but nothing happens: + +.. code-block:: + + Initializing distributed: GLOBAL_RANK: 0, MEMBER: 1/4 + +The most likely reasons and how to fix it: + +- **Wrong network interface:** Some servers have multiple network interfaces. + There is usually only one that can send and receive traffic from the network of the other nodes, but sometimes it is not set as the default. + In this case, you need to set it manually: + + .. code-block:: bash + + export GLOO_SOCKET_IFNAME=eno1 + export NCCL_SOCKET_IFNAME=eno1 + lightning run model ... + + You can find the interface name by parsing the output of the ``ifconfig`` command. + The name of this interface **may differ on each node**. + +- **NCCL can't communicate between the nodes:** + + Follow the steps in the `NCCL troubleshooting guide `_. + In particular, take note of the network section that describes restricting the port range and firewall rules. + + .. code-block:: bash + + echo "net.ipv4.ip_local_port_range = 50000 51000" >> /etc/sysctl.conf + sysctl --system + ufw allow 50000:51000/tcp + + +**My program crashes with an NCCL error, but it is not helpful** + +Launch your command by prepending ``NCCL_DEBUG=INFO`` to get more info. + +.. code-block:: bash + + NCCL_DEBUG=INFO lightning run model ... + + +---- + +If you are sick of troubleshooting cluster problems, give :doc:`Lightning cloud <./cloud>` a try! +For other questions, please don't hesitate to join the `Discord `_. diff --git a/docs/source-fabric/guide/multi_node/cloud.rst b/docs/source-fabric/guide/multi_node/cloud.rst new file mode 100644 index 0000000..833bb92 --- /dev/null +++ b/docs/source-fabric/guide/multi_node/cloud.rst @@ -0,0 +1,115 @@ +:orphan: + +########################## +Run in the Lightning Cloud +########################## + +**Audience**: Users who don't want to waste time on cluster configuration and maintenance. + + +The Lightning AI cloud is a platform where you can build, train, finetune and deploy models without worrying about infrastructure, cost management, scaling, and other technical headaches. +In this guide, and within just 10 minutes, you will learn how to run a Fabric training script across multiple nodes in the cloud. + + +---- + + +************* +Initial Setup +************* + +First, create a free `Lightning AI account `_. +Then, log in from the CLI: + +.. code-block:: bash + + lightning login + +A page opens in your browser where you can follow the instructions to complete the setup. + + +---- + + +*************************************** +Launch multi-node training in the cloud +*************************************** + +**Step 1:** Put your code inside a :class:`~lightning_app.core.work.LightningWork`: + +.. code-block:: python + :emphasize-lines: 5 + :caption: app.py + + import lightning as L + from lightning.app.components import FabricMultiNode + + + # 1. Put your code inside a LightningWork + class MyTrainingComponent(L.LightningWork): + def run(self): + # Set up Fabric + # The `devices` and `num_nodes` gets set by Lightning automatically + fabric = L.Fabric(strategy="ddp", precision="16-mixed") + + # Your training code + model = ... + optimizer = ... + model, optimizer = fabric.setup(model, optimizer) + ... + +**Step 2:** Init a :class:`~lightning_app.core.app.LightningApp` with the ``FabricMultiNode`` component. +Configure the number of nodes, the number of GPUs per node, and the type of GPU: + +.. code-block:: python + :emphasize-lines: 5,7 + :caption: app.py + + # 2. Create the app with the FabricMultiNode component inside + app = L.LightningApp( + FabricMultiNode( + MyTrainingComponent, + # Run with 2 nodes + num_nodes=2, + # Each with 4 x V100 GPUs, total 8 GPUs + cloud_compute=L.CloudCompute("gpu-fast-multi"), + ) + ) + + +**Step 3:** Run your code from the CLI: + +.. code-block:: bash + + lightning run app app.py --cloud + +This command will upload your Python file and then opens the app admin view, where you can see the logs of what's happening. + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric/fabric-multi-node-admin.png + :alt: The Lightning AI admin page of an app running a multi-node fabric training script + :width: 100% + + +---- + + +********** +Next steps +********** + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Lightning Platform + :description: Develop, Train and Deploy models on the cloud + :col_css: col-md-4 + :button_link: https://lightning.ai + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-fabric/guide/multi_node/other.rst b/docs/source-fabric/guide/multi_node/other.rst new file mode 100644 index 0000000..6bddf04 --- /dev/null +++ b/docs/source-fabric/guide/multi_node/other.rst @@ -0,0 +1,66 @@ +:orphan: + +########################## +Other Cluster Environments +########################## + +**Audience**: Users who want to run on a cluster that launches the training script via MPI, LSF, Kubeflow, etc. + +Lightning automates the details behind training on the most common cluster environments. +While :doc:`SLURM <./slurm>` is the most popular choice for on-prem clusters, there are other systems that Lightning can detect automatically. + +Don't have access to an enterprise cluster? Try the :doc:`Lightning cloud <./cloud>`. + + +---- + + +*** +MPI +*** + +`MPI (Message Passing Interface) `_ is a communication system for parallel computing. +There are many implementations available, the most popular among them are `OpenMPI `_ and `MPICH `_. +To support all these, Lightning relies on the `mpi4py package `_: + +.. code-block:: bash + + pip install mpi4py + +If the package is installed and the Python script gets launched by MPI, Fabric will automatically detect it and parse the process information from the environment. +There is nothing you have to change in your code: + +.. code-block:: python + + fabric = Fabric(...) # automatically detects MPI + print(fabric.world_size) # world size provided by MPI + print(fabric.global_rank) # rank provided by MPI + ... + +If you want to bypass the automatic detection, you can explicitly set the MPI environment as a plugin: + +.. code-block:: python + + from lightning.fabric.plugins.environments import MPIEnvironment + + fabric = Fabric(..., plugins=[MPIEnvironment()]) + + +---- + + +*** +LSF +*** + +Coming soon. + + +---- + + +******** +Kubeflow +******** + +Coming soon. diff --git a/docs/source-fabric/guide/multi_node/slurm.rst b/docs/source-fabric/guide/multi_node/slurm.rst new file mode 100644 index 0000000..62025ce --- /dev/null +++ b/docs/source-fabric/guide/multi_node/slurm.rst @@ -0,0 +1,136 @@ +:orphan: + +############################## +Run on a SLURM Managed Cluster +############################## + +**Audience**: Users who need to run on an academic or enterprise private cluster. + +Lightning automates the details behind training on a SLURM-powered cluster. +Unlike the :doc:`general-purpose cluster <./barebones>`, with SLURM the users don't need to start the jobs manually on each node but instead submit it to SLURM, which schedules the resources and time for which the job is allowed to run. + +Don't have access to an enterprise cluster? Try the :doc:`Lightning cloud <./cloud>`. + +---- + + +********************************* +Submit a training script to SLURM +********************************* + +To train a model using multiple nodes, do the following: + +**Step 1:** Set the number of devices per node and how many nodes the training will run on. + +.. code-block:: python + + from lightning.fabric import Fabric + + # Train on 32 GPUs across 4 nodes + fabric = Fabric(accelerator="gpu", devices=8, num_nodes=4) + +By default, this will run classic *distributed data-parallel*. +Optionally, explore other strategies too: + +.. code-block:: python + + # DeepSpeed + fabric = Fabric(accelerator="gpu", devices=8, num_nodes=4, strategy="deepspeed") + + # Fully Sharded Data Parallel (FSDP) + fabric = Fabric(accelerator="gpu", devices=8, num_nodes=4, strategy="fsdp") + + +**Step 2:** Call :meth:`~lightning.fabric.fabric.Fabric.launch` to initialize the communication between devices and nodes. + +.. code-block:: python + + fabric = Fabric(...) + fabric.launch() + + +**Step 3:** Create the appropriate SLURM job configuration: + +.. code-block:: bash + :caption: submit.sh + :emphasize-lines: 4,5,21 + + #!/bin/bash -l + + # SLURM SUBMIT SCRIPT + #SBATCH --nodes=4 # This needs to match Fabric(num_nodes=...) + #SBATCH --ntasks-per-node=8 # This needs to match Fabric(devices=...) + #SBATCH --gres=gpu:8 # Request N GPUs per machine + #SBATCH --mem=0 + #SBATCH --time=0-02:00:00 + + # Activate conda environment + source activate $1 + + # Debugging flags (optional) + export NCCL_DEBUG=INFO + export PYTHONFAULTHANDLER=1 + + # On your cluster you might need this: + # export NCCL_SOCKET_IFNAME=^docker0,lo + + # Run your training script + srun python train.py + + +**Step 4:** Submit the job to SLURM + +.. code-block:: bash + + sbatch submit.sh + + +---- + + +**************** +Interactive Mode +**************** + +You can also let SLURM schedule a machine for you and then log in to the machine to run scripts manually. +This is useful for development and debugging. +If you set the job name to *bash* or *interactive*, and then log in and run scripts, Lightning's SLURM auto-detection will get bypassed and it can launch processes normally: + +.. code-block:: bash + + # make sure to set `--job-name "interactive"` + srun --account --pty bash --job-name "interactive" ... + + # now run scripts normally + python train.py ... + + +---- + + +*************** +Troubleshooting +*************** + +**My program is stuck initializing at startup. What is causing this?** + +You are seeing a message like this in the logs, but nothing happens: + +.. code-block:: + + Initializing distributed: GLOBAL_RANK: 0, MEMBER: 1/4 + + +The most likely reasons and how to fix it: + +- You forgot to run the ``python train.py`` command with ``srun``: + Please have a look at the SLURM template script above, which includes the ``srun`` at the bottom of the script. + +- The number of nodes or the number of devices per node is misconfigured: + Two parameters in the SLURM submission script determine how many processes will run your training, the ``#SBATCH --nodes=X`` setting and ``#SBATCH --ntasks-per-node=Y`` settings. + The numbers there need to match what is configured in Fabric in the code: ``Fabric(num_nodes=X, devices=Y)``. + If you change the numbers, update them in BOTH places. + + +If you are sick of troubleshooting SLURM settings, give :doc:`Lightning cloud <./cloud>` a try! +For other questions, please don't hesitate to join the `Discord `_. diff --git a/docs/source-fabric/guide/trainer_template.rst b/docs/source-fabric/guide/trainer_template.rst new file mode 100644 index 0000000..f13eccf --- /dev/null +++ b/docs/source-fabric/guide/trainer_template.rst @@ -0,0 +1,7 @@ +:orphan: + +################ +Template Trainer +################ + +TODO: Write a guide explaining how to build a template like the one in https://github.com/Lightning-AI/lightning/tree/master/examples/fabric/build_your_own_trainer diff --git a/docs/source-fabric/index.rst b/docs/source-fabric/index.rst new file mode 100644 index 0000000..08d37e8 --- /dev/null +++ b/docs/source-fabric/index.rst @@ -0,0 +1,158 @@ +.. include:: links.rst + +#################### +Welcome to ⚡ Fabric +#################### + +Fabric is the fast and lightweight way to scale PyTorch models without boilerplate code. + +- Easily switch from running on CPU to GPU (Apple Silicon, CUDA, ...), TPU, multi-GPU or even multi-node training +- State-of-the-art distributed training strategies (DDP, FSDP, DeepSpeed) and mixed precision out of the box +- Handles all the boilerplate device logic for you +- Brings useful tools to help you build a trainer (callbacks, logging, checkpoints, ...) +- Designed with multi-billion parameter models in mind + +| + +.. code-block:: diff + + import torch + from lightning.pytorch.demos import WikiText2, Transformer + + import lightning as L + + - device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + + fabric = L.Fabric(accelerator="cuda", devices=8, strategy="ddp") + + fabric.launch() + + dataset = WikiText2() + dataloader = torch.utils.data.DataLoader(dataset) + model = Transformer(vocab_size=dataset.vocab_size) + optimizer = torch.optim.SGD(model.parameters(), lr=0.1) + + - model = model.to(device) + + model, optimizer = fabric.setup(model, optimizer) + + dataloader = fabric.setup_dataloaders(dataloader) + + model.train() + for epoch in range(20): + for batch in dataloader: + input, target = batch + - input, target = input.to(device), target.to(device) + optimizer.zero_grad() + output = model(input, target) + loss = torch.nn.functional.nll_loss(output, target.view(-1)) + - loss.backward() + + fabric.backward(loss) + optimizer.step() + + +---- + + +*********** +Why Fabric? +*********** + +| +| + +.. figure:: https://pl-public-data.s3.amazonaws.com/assets_lightning/fabric/PyTorch-to-Fabric-Spectrum-2.svg + :alt: Fabric spans across a large spectrum - from raw PyTorch all the way to high-level PyTorch Lightning + :width: 100% + +| +| + +Fabric differentiates itself from a fully-fledged trainer like Lightning's `Trainer`_ in these key aspects: + +**Fast to implement** +There is no need to restructure your code: Just change a few lines in the PyTorch script and you'll be able to leverage Fabric features. + +**Maximum Flexibility** +Write your own training and/or inference logic down to the individual optimizer calls. +You aren't forced to conform to a standardized epoch-based training loop like the one in Lightning `Trainer`_. +You can do flexible iteration based training, meta-learning, cross-validation and other types of optimization algorithms without digging into framework internals. +This also makes it super easy to adopt Fabric in existing PyTorch projects to speed-up and scale your models without the compromise on large refactors. +Just remember: With great power comes a great responsibility. + +**Maximum Control** +The Lightning `Trainer`_ has many built-in features to make research simpler with less boilerplate, but debugging it requires some familiarity with the framework internals. +In Fabric, everything is opt-in. Think of it as a toolbox: You take out the tools (Fabric functions) you need and leave the other ones behind. +This makes it easier to develop and debug your PyTorch code as you gradually add more features to it. +Fabric provides important tools to remove undesired boilerplate code (distributed, hardware, checkpoints, logging, ...), but leaves the design and orchestration fully up to you. + + +---- + +************ +Installation +************ + +Fabric ships directly with Lightning. Install it with + +.. code-block:: bash + + pip install lightning + +For alternative ways to install, read the :doc:`installation guide `. + + + +.. raw:: html + +
+ +.. toctree:: + :maxdepth: 1 + :name: start + :caption: Home + + self + Install + + +.. toctree:: + :maxdepth: 1 + :caption: Get started in steps + + Basic skills + Intermediate skills + Advanced skills + + +.. toctree:: + :maxdepth: 1 + :caption: Core API Reference + + Fabric Arguments + Fabric Methods + + +.. toctree:: + :maxdepth: 1 + :caption: Full API Reference + + Accelerators + Collectives + Environments + Fabric + IO + Loggers + Precision + Strategies + + +.. toctree:: + :maxdepth: 1 + :name: more + :caption: More + + Examples + Glossary + How-tos + Style Guide + + +.. raw:: html + +
diff --git a/docs/source-fabric/levels/advanced.rst b/docs/source-fabric/levels/advanced.rst new file mode 100644 index 0000000..b8fb45d --- /dev/null +++ b/docs/source-fabric/levels/advanced.rst @@ -0,0 +1,55 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + <../advanced/gradient_accumulation> + <../advanced/distributed_communication> + <../advanced/multiple_setup> + <../advanced/model_parallel/fsdp> + + +############### +Advanced skills +############### + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Use efficient gradient accumulation + :description: Learn how to perform efficient gradient accumulation in distributed settings + :button_link: ../advanced/gradient_accumulation.html + :col_css: col-md-4 + :height: 170 + :tag: advanced + +.. displayitem:: + :header: Distribute communication + :description: Learn all about communication primitives for distributed operation. Gather, reduce, broadcast, etc. + :button_link: ../advanced/distributed_communication.html + :col_css: col-md-4 + :height: 170 + :tag: advanced + +.. displayitem:: + :header: Use multiple models and optimizers + :description: See how flexible Fabric is to work with multiple models and optimizers! + :button_link: ../advanced/multiple_setup.html + :col_css: col-md-4 + :height: 170 + :tag: advanced + +.. displayitem:: + :header: Train models with billions of parameters + :description: Train the largest models with FSDP across multiple GPUs and machines + :button_link: ../advanced/model_parallel/fsdp.html + :col_css: col-md-4 + :height: 170 + :tag: advanced + +.. raw:: html + +
+
diff --git a/docs/source-fabric/levels/basic.rst b/docs/source-fabric/levels/basic.rst new file mode 100644 index 0000000..cf53eff --- /dev/null +++ b/docs/source-fabric/levels/basic.rst @@ -0,0 +1,74 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + <../fundamentals/convert> + <../fundamentals/accelerators> + <../fundamentals/code_structure> + <../fundamentals/launch> + <../fundamentals/notebooks> + <../fundamentals/precision> + + +############ +Basic skills +############ + + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Convert to Fabric in 5 minutes + :description: Learn how to add Fabric to your PyTorch code + :button_link: ../fundamentals/convert.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Scale your model with Accelerators + :description: Take advantage of your hardware with a switch of a flag + :button_link: ../fundamentals/accelerators.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Structure your Fabric code + :description: Best practices for setting up your training script with Fabric + :button_link: ../fundamentals/code_structure.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Launch distributed training + :description: Launch a Python script on multiple devices and machines + :button_link: ../fundamentals/launch.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Launch Fabric in a notebook + :description: Launch on multiple devices from within a Jupyter notebook + :button_link: ../fundamentals/notebooks.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. displayitem:: + :header: Improve performance with Mixed-Precision training + :description: Save memory and speed up training using mixed precision + :button_link: ../fundamentals/precision.html + :col_css: col-md-4 + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-fabric/levels/intermediate.rst b/docs/source-fabric/levels/intermediate.rst new file mode 100644 index 0000000..2d2037a --- /dev/null +++ b/docs/source-fabric/levels/intermediate.rst @@ -0,0 +1,64 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + <../guide/lightning_module> + <../guide/callbacks> + <../guide/logging> + <../guide/checkpoint> + <../guide/trainer_template> + + +################### +Intermediate skills +################### + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Organize your model code with with LightningModule + :description: Organize your code in a LightningModule and use it with Fabric + :button_link: ../guide/lightning_module.html + :col_css: col-md-4 + :height: 180 + :tag: intermediate + +.. displayitem:: + :header: Encapsulate code into Callbacks + :description: Make use of the Callback system in Fabric + :button_link: ../guide/callbacks.html + :col_css: col-md-4 + :height: 180 + :tag: intermediate + +.. displayitem:: + :header: Track and visualize experiments + :description: Learn how Fabric helps you remove boilerplate code for tracking metrics with a logger + :button_link: ../guide/logging.html + :col_css: col-md-4 + :height: 180 + :tag: intermediate + +.. displayitem:: + :header: Save and load model progress + :description: Efficient saving and loading of model weights, training state, hyperparameters and more. + :button_link: ../guide/checkpoint.html + :col_css: col-md-4 + :height: 180 + :tag: intermediate + +.. displayitem:: + :header: Build your own Trainer + :description: Take our Fabric Trainer template and customize it for your needs + :button_link: https://github.com/Lightning-AI/lightning/tree/master/examples/fabric/build_your_own_trainer + :col_css: col-md-4 + :height: 180 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/docs/source-fabric/links.rst b/docs/source-fabric/links.rst new file mode 100644 index 0000000..d8e1d12 --- /dev/null +++ b/docs/source-fabric/links.rst @@ -0,0 +1,3 @@ +.. _PyTorchJob: https://www.kubeflow.org/docs/components/training/pytorch/ +.. _Kubeflow: https://www.kubeflow.org +.. _Trainer: https://lightning.ai/docs/pytorch/stable/common/trainer.html diff --git a/docs/source-fabric/make.bat b/docs/source-fabric/make.bat new file mode 100644 index 0000000..9b56514 --- /dev/null +++ b/docs/source-fabric/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=../build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/source-pytorch/Makefile b/docs/source-pytorch/Makefile new file mode 100644 index 0000000..68be4c9 --- /dev/null +++ b/docs/source-pytorch/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SOURCEDIR = . +BUILDDIR = ../build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/source-pytorch/_static/copybutton.js b/docs/source-pytorch/_static/copybutton.js new file mode 100644 index 0000000..453363c --- /dev/null +++ b/docs/source-pytorch/_static/copybutton.js @@ -0,0 +1,64 @@ +/* Copied from the official Python docs: https://docs.python.org/3/_static/copybutton.js */ +$(document).ready(function() { + /* Add a [>>>] button on the top-right corner of code samples to hide + * the >>> and ... prompts and the output and thus make the code + * copyable. */ + var div = $('.highlight-python .highlight,' + + '.highlight-python3 .highlight,' + + '.highlight-pycon .highlight,' + + '.highlight-default .highlight'); + var pre = div.find('pre'); + + // get the styles from the current theme + pre.parent().parent().css('position', 'relative'); + var hide_text = 'Hide the prompts and output'; + var show_text = 'Show the prompts and output'; + var border_width = pre.css('border-top-width'); + var border_style = pre.css('border-top-style'); + var border_color = pre.css('border-top-color'); + var button_styles = { + 'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0', + 'border-color': border_color, 'border-style': border_style, + 'border-width': border_width, 'color': border_color, 'text-size': '75%', + 'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em', + 'border-radius': '0 3px 0 0' + } + + // create and add the button to all the code blocks that contain >>> + div.each(function(index) { + var jthis = $(this); + if (jthis.find('.gp').length > 0) { + var button = $('>>>'); + button.css(button_styles) + button.attr('title', hide_text); + button.data('hidden', 'false'); + jthis.prepend(button); + } + // tracebacks (.gt) contain bare text elements that need to be + // wrapped in a span to work with .nextUntil() (see later) + jthis.find('pre:has(.gt)').contents().filter(function() { + return ((this.nodeType == 3) && (this.data.trim().length > 0)); + }).wrap(''); + }); + + // define the behavior of the button when it's clicked + $('.copybutton').click(function(e){ + e.preventDefault(); + var button = $(this); + if (button.data('hidden') === 'false') { + // hide the code output + button.parent().find('.go, .gp, .gt').hide(); + button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden'); + button.css('text-decoration', 'line-through'); + button.attr('title', show_text); + button.data('hidden', 'true'); + } else { + // show the code output + button.parent().find('.go, .gp, .gt').show(); + button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible'); + button.css('text-decoration', 'none'); + button.attr('title', hide_text); + button.data('hidden', 'false'); + } + }); +}); diff --git a/docs/_images/profiler.png b/docs/source-pytorch/_static/images/accelerator/ipus/profiler.png similarity index 100% rename from docs/_images/profiler.png rename to docs/source-pytorch/_static/images/accelerator/ipus/profiler.png diff --git a/docs/_images/figure-parity-times.png b/docs/source-pytorch/_static/images/benchmarks/figure-parity-times.png similarity index 100% rename from docs/_images/figure-parity-times.png rename to docs/source-pytorch/_static/images/benchmarks/figure-parity-times.png diff --git a/docs/_static/images/general/PTL101_youtube_thumbnail.jpg b/docs/source-pytorch/_static/images/general/PTL101_youtube_thumbnail.jpg similarity index 100% rename from docs/_static/images/general/PTL101_youtube_thumbnail.jpg rename to docs/source-pytorch/_static/images/general/PTL101_youtube_thumbnail.jpg diff --git a/docs/_static/images/general/fast_2.gif b/docs/source-pytorch/_static/images/general/fast_2.gif similarity index 100% rename from docs/_static/images/general/fast_2.gif rename to docs/source-pytorch/_static/images/general/fast_2.gif diff --git a/docs/_static/images/general/pl_overview.gif b/docs/source-pytorch/_static/images/general/pl_overview.gif similarity index 100% rename from docs/_static/images/general/pl_overview.gif rename to docs/source-pytorch/_static/images/general/pl_overview.gif diff --git a/docs/_static/images/general/pl_overview_flat.jpg b/docs/source-pytorch/_static/images/general/pl_overview_flat.jpg similarity index 100% rename from docs/_static/images/general/pl_overview_flat.jpg rename to docs/source-pytorch/_static/images/general/pl_overview_flat.jpg diff --git a/docs/_static/images/general/pl_quick_start_full_compressed.gif b/docs/source-pytorch/_static/images/general/pl_quick_start_full_compressed.gif similarity index 100% rename from docs/_static/images/general/pl_quick_start_full_compressed.gif rename to docs/source-pytorch/_static/images/general/pl_quick_start_full_compressed.gif diff --git a/docs/_static/images/general/tf_loss.jpg b/docs/source-pytorch/_static/images/general/tf_loss.jpg similarity index 100% rename from docs/_static/images/general/tf_loss.jpg rename to docs/source-pytorch/_static/images/general/tf_loss.jpg diff --git a/docs/_static/images/general/tf_tags.jpg b/docs/source-pytorch/_static/images/general/tf_tags.jpg similarity index 100% rename from docs/_static/images/general/tf_tags.jpg rename to docs/source-pytorch/_static/images/general/tf_tags.jpg diff --git a/docs/_static/images/general/tutorial_cover.jpg b/docs/source-pytorch/_static/images/general/tutorial_cover.jpg similarity index 100% rename from docs/_static/images/general/tutorial_cover.jpg rename to docs/source-pytorch/_static/images/general/tutorial_cover.jpg diff --git a/docs/_static/images/icon.svg b/docs/source-pytorch/_static/images/icon.svg similarity index 100% rename from docs/_static/images/icon.svg rename to docs/source-pytorch/_static/images/icon.svg diff --git a/docs/_static/images/lightning_lite/lite.gif b/docs/source-pytorch/_static/images/lightning_lite/lite.gif similarity index 100% rename from docs/_static/images/lightning_lite/lite.gif rename to docs/source-pytorch/_static/images/lightning_lite/lite.gif diff --git a/docs/_static/images/lightning_module/pt_to_pl.png b/docs/source-pytorch/_static/images/lightning_module/pt_to_pl.png similarity index 100% rename from docs/_static/images/lightning_module/pt_to_pl.png rename to docs/source-pytorch/_static/images/lightning_module/pt_to_pl.png diff --git a/docs/_static/images/lightning_module/pt_trainer.png b/docs/source-pytorch/_static/images/lightning_module/pt_trainer.png similarity index 100% rename from docs/_static/images/lightning_module/pt_trainer.png rename to docs/source-pytorch/_static/images/lightning_module/pt_trainer.png diff --git a/docs/_static/images/logo.png b/docs/source-pytorch/_static/images/logo.png similarity index 100% rename from docs/_static/images/logo.png rename to docs/source-pytorch/_static/images/logo.png diff --git a/docs/_static/images/logo.svg b/docs/source-pytorch/_static/images/logo.svg similarity index 100% rename from docs/_static/images/logo.svg rename to docs/source-pytorch/_static/images/logo.svg diff --git a/docs/_static/images/logo_light.svg b/docs/source-pytorch/_static/images/logo_light.svg similarity index 100% rename from docs/_static/images/logo_light.svg rename to docs/source-pytorch/_static/images/logo_light.svg diff --git a/docs/_static/images/mnist_imgs/mnist_cpu_bar.png b/docs/source-pytorch/_static/images/mnist_imgs/mnist_cpu_bar.png similarity index 100% rename from docs/_static/images/mnist_imgs/mnist_cpu_bar.png rename to docs/source-pytorch/_static/images/mnist_imgs/mnist_cpu_bar.png diff --git a/docs/_static/images/mnist_imgs/mnist_gpu.png b/docs/source-pytorch/_static/images/mnist_imgs/mnist_gpu.png similarity index 100% rename from docs/_static/images/mnist_imgs/mnist_gpu.png rename to docs/source-pytorch/_static/images/mnist_imgs/mnist_gpu.png diff --git a/docs/_static/images/mnist_imgs/mnist_tb.png b/docs/source-pytorch/_static/images/mnist_imgs/mnist_tb.png similarity index 100% rename from docs/_static/images/mnist_imgs/mnist_tb.png rename to docs/source-pytorch/_static/images/mnist_imgs/mnist_tb.png diff --git a/docs/_static/images/mnist_imgs/pt_to_pl.jpg b/docs/source-pytorch/_static/images/mnist_imgs/pt_to_pl.jpg similarity index 100% rename from docs/_static/images/mnist_imgs/pt_to_pl.jpg rename to docs/source-pytorch/_static/images/mnist_imgs/pt_to_pl.jpg diff --git a/docs/_static/images/mnist_imgs/restart_runtime.png b/docs/source-pytorch/_static/images/mnist_imgs/restart_runtime.png similarity index 100% rename from docs/_static/images/mnist_imgs/restart_runtime.png rename to docs/source-pytorch/_static/images/mnist_imgs/restart_runtime.png diff --git a/docs/_static/images/mnist_imgs/runtime_tpu.png b/docs/source-pytorch/_static/images/mnist_imgs/runtime_tpu.png similarity index 100% rename from docs/_static/images/mnist_imgs/runtime_tpu.png rename to docs/source-pytorch/_static/images/mnist_imgs/runtime_tpu.png diff --git a/docs/_static/images/mnist_imgs/tpu_fast.png b/docs/source-pytorch/_static/images/mnist_imgs/tpu_fast.png similarity index 100% rename from docs/_static/images/mnist_imgs/tpu_fast.png rename to docs/source-pytorch/_static/images/mnist_imgs/tpu_fast.png diff --git a/docs/_static/images/mnist_imgs/tpu_start.png b/docs/source-pytorch/_static/images/mnist_imgs/tpu_start.png similarity index 100% rename from docs/_static/images/mnist_imgs/tpu_start.png rename to docs/source-pytorch/_static/images/mnist_imgs/tpu_start.png diff --git a/docs/_images/lr_finder.png b/docs/source-pytorch/_static/images/trainer/lr_finder.png similarity index 100% rename from docs/_images/lr_finder.png rename to docs/source-pytorch/_static/images/trainer/lr_finder.png diff --git a/docs/source-pytorch/_static/main.css b/docs/source-pytorch/_static/main.css new file mode 100644 index 0000000..82aa8b3 --- /dev/null +++ b/docs/source-pytorch/_static/main.css @@ -0,0 +1,3 @@ +col { + width: 50% !important; +} diff --git a/source/_templates/autosummary/module.rst b/docs/source-pytorch/_templates/autosummary/module.rst similarity index 100% rename from source/_templates/autosummary/module.rst rename to docs/source-pytorch/_templates/autosummary/module.rst diff --git a/docs/source-pytorch/_templates/classtemplate.rst b/docs/source-pytorch/_templates/classtemplate.rst new file mode 100644 index 0000000..dc11b74 --- /dev/null +++ b/docs/source-pytorch/_templates/classtemplate.rst @@ -0,0 +1,14 @@ +.. role:: hidden + :class: hidden-section +.. currentmodule:: {{ module }} + + +{{ name | underline }} + +.. autoclass:: {{ name }} + :members: + + +.. + autogenerated from source-pytorch/_templates/classtemplate.rst + note it does not have :inherited-members: diff --git a/source/_templates/layout.html b/docs/source-pytorch/_templates/layout.html similarity index 100% rename from source/_templates/layout.html rename to docs/source-pytorch/_templates/layout.html diff --git a/docs/source-pytorch/_templates/theme_variables.jinja b/docs/source-pytorch/_templates/theme_variables.jinja new file mode 100644 index 0000000..912c188 --- /dev/null +++ b/docs/source-pytorch/_templates/theme_variables.jinja @@ -0,0 +1,20 @@ +{%- set external_urls = { + 'github': 'https://github.com/Lightning-AI/lightning', + 'github_issues': 'https://github.com/Lightning-AI/lightning/issues', + 'contributing': 'https://github.com/Lightning-AI/lightning/blob/master/.github/CONTRIBUTING.md', + 'governance': 'https://lightning.ai/docs/pytorch/latest/community/governance.html', + 'docs': 'https://lightning.ai/docs/pytorch/latest/', + 'twitter': 'https://twitter.com/LightningAI', + 'discuss': 'https://www.pytorchlightning.ai/community', + 'tutorials': 'https://lightning.ai/docs/pytorch/latest/#tutorials', + 'home': 'https://lightning.ai/docs/pytorch/latest/', + 'get_started': 'https://lightning.ai/docs/pytorch/latest/starter/introduction.html', + 'features': 'https://lightning.ai/docs/pytorch/latest/', + 'blog': 'https://lightning.ai/pages/blog/', + 'resources': 'https://lightning.ai/docs/pytorch/latest/#community-examples', + 'support': 'https://lightning.ai/docs/pytorch/latest/', + 'community': 'https://www.pytorchlightning.ai/community', + 'forums': 'https://lightning.ai/forums/', + 'versions': 'https://lightning.ai/docs/pytorch/latest/past_versions.html' +} +-%} diff --git a/source/accelerators/accelerator_prepare.rst b/docs/source-pytorch/accelerators/accelerator_prepare.rst similarity index 86% rename from source/accelerators/accelerator_prepare.rst rename to docs/source-pytorch/accelerators/accelerator_prepare.rst index 38921f4..4035443 100644 --- a/source/accelerators/accelerator_prepare.rst +++ b/docs/source-pytorch/accelerators/accelerator_prepare.rst @@ -31,10 +31,10 @@ Delete any calls to .cuda() or .to(device). ---- -********************************************** -Init tensors using type_as and register_buffer -********************************************** -When you need to create a new tensor, use ``type_as``. +************************************************ +Init tensors using Tensor.to and register_buffer +************************************************ +When you need to create a new tensor, use ``Tensor.to``. This will make your code scale to any arbitrary number of GPUs or TPUs with Lightning. .. testcode:: @@ -48,9 +48,9 @@ This will make your code scale to any arbitrary number of GPUs or TPUs with Ligh # with lightning def forward(self, x): z = torch.Tensor(2, 3) - z = z.type_as(x) + z = z.to(x) -The :class:`~pytorch_lightning.core.lightning.LightningModule` knows what device it is on. You can access the reference via ``self.device``. +The :class:`~lightning.pytorch.core.module.LightningModule` knows what device it is on. You can access the reference via ``self.device``. Sometimes it is necessary to store tensors as module attributes. However, if they are not parameters they will remain on the CPU even if the module gets moved to a new device. To prevent that and remain device agnostic, register the tensor as a buffer in your modules' ``__init__`` method with :meth:`~torch.nn.Module.register_buffer`. @@ -105,19 +105,27 @@ Note if you use any built in metrics or custom metrics that use `TorchMetrics +
+ +.. displayitem:: + :header: Train 1 trillion+ parameter models + :description: + :col_css: col-md-4 + :button_link: ../advanced/model_parallel.html + :height: 150 + :tag: advanced + + +.. raw:: html + +
+ diff --git a/docs/source-pytorch/accelerators/gpu_basic.rst b/docs/source-pytorch/accelerators/gpu_basic.rst new file mode 100644 index 0000000..25b026b --- /dev/null +++ b/docs/source-pytorch/accelerators/gpu_basic.rst @@ -0,0 +1,113 @@ +:orphan: + +.. _gpu_basic: + +GPU training (Basic) +==================== +**Audience:** Users looking to save money and run large models faster using single or multiple + +---- + +What is a GPU? +-------------- +A Graphics Processing Unit (GPU), is a specialized hardware accelerator designed to speed up mathematical computations used in gaming and deep learning. + +---- + +.. _multi_gpu: + +Train on GPUs +------------- + +The Trainer will run on all available GPUs by default. Make sure you're running on a machine with at least one GPU. +There's no need to specify any NVIDIA flags as Lightning will do it for you. + +.. code-block:: python + + # run on as many GPUs as available by default + trainer = Trainer(accelerator="auto", devices="auto", strategy="auto") + # equivalent to + trainer = Trainer() + + # run on one GPU + trainer = Trainer(accelerator="gpu", devices=1) + # run on multiple GPUs + trainer = Trainer(accelerator="gpu", devices=8) + # choose the number of devices automatically + trainer = Trainer(accelerator="gpu", devices="auto") + +.. note:: + Setting ``accelerator="gpu"`` will also automatically choose the "mps" device on Apple sillicon GPUs. + If you want to avoid this, you can set ``accelerator="cuda"`` instead. + +Choosing GPU devices +^^^^^^^^^^^^^^^^^^^^ + +You can select the GPU devices using ranges, a list of indices or a string containing +a comma separated list of GPU ids: + +.. testsetup:: + + k = 1 + +.. testcode:: + :skipif: torch.cuda.device_count() < 2 + + # DEFAULT (int) specifies how many GPUs to use per node + Trainer(accelerator="gpu", devices=k) + + # Above is equivalent to + Trainer(accelerator="gpu", devices=list(range(k))) + + # Specify which GPUs to use (don't use when running on cluster) + Trainer(accelerator="gpu", devices=[0, 1]) + + # Equivalent using a string + Trainer(accelerator="gpu", devices="0, 1") + + # To use all available GPUs put -1 or '-1' + # equivalent to `list(range(torch.cuda.device_count())) and `"auto"` + Trainer(accelerator="gpu", devices=-1) + +The table below lists examples of possible input formats and how they are interpreted by Lightning. + ++------------------+-----------+---------------------+---------------------------------+ +| `devices` | Type | Parsed | Meaning | ++==================+===========+=====================+=================================+ +| 3 | int | [0, 1, 2] | first 3 GPUs | ++------------------+-----------+---------------------+---------------------------------+ +| -1 | int | [0, 1, 2, ...] | all available GPUs | ++------------------+-----------+---------------------+---------------------------------+ +| [0] | list | [0] | GPU 0 | ++------------------+-----------+---------------------+---------------------------------+ +| [1, 3] | list | [1, 3] | GPU index 1 and 3 (0-based) | ++------------------+-----------+---------------------+---------------------------------+ +| "3" | str | [0, 1, 2] | first 3 GPUs | ++------------------+-----------+---------------------+---------------------------------+ +| "1, 3" | str | [1, 3] | GPU index 1 and 3 (0-based) | ++------------------+-----------+---------------------+---------------------------------+ +| "-1" | str | [0, 1, 2, ...] | all available GPUs | ++------------------+-----------+---------------------+---------------------------------+ + + +Find usable CUDA devices +^^^^^^^^^^^^^^^^^^^^^^^^ + +If you want to run several experiments at the same time on your machine, for example for a hyperparameter sweep, then you can +use the following utility function to pick GPU indices that are "accessible", without having to change your code every time. + +.. code-block:: python + + from lightning.pytorch.accelerators import find_usable_cuda_devices + + # Find two GPUs on the system that are not already occupied + trainer = Trainer(accelerator="cuda", devices=find_usable_cuda_devices(2)) + + from lightning.fabric.accelerators import find_usable_cuda_devices + + # Works with Fabric too + fabric = Fabric(accelerator="cuda", devices=find_usable_cuda_devices(2)) + + +This is especially useful when GPUs are configured to be in "exclusive compute mode", such that only one process at a time is allowed access to the device. +This special mode is often enabled on server GPUs or systems shared among multiple users. diff --git a/source/accelerators/gpu_expert.rst b/docs/source-pytorch/accelerators/gpu_expert.rst similarity index 87% rename from source/accelerators/gpu_expert.rst rename to docs/source-pytorch/accelerators/gpu_expert.rst index a2178a3..9ef4e0e 100644 --- a/source/accelerators/gpu_expert.rst +++ b/docs/source-pytorch/accelerators/gpu_expert.rst @@ -6,16 +6,20 @@ GPU training (Expert) ===================== **Audience:** Experts creating new scaling techniques such as Deepspeed or FSDP +.. warning:: This is an :ref:`experimental ` feature. + ---- Lightning enables experts focused on researching new ways of optimizing distributed training/inference strategies to create new strategies and plug them into Lightning. For example, Lightning worked closely with the Microsoft team to develop a Deepspeed integration and with the Facebook(Meta) team to develop a FSDP integration. + ---- -.. include:: ../advanced/strategy_registry.rst +.. include:: ../extensions/strategy.rst + ---- -.. include:: ../extensions/strategy.rst +.. include:: ../advanced/strategy_registry.rst diff --git a/docs/source-pytorch/accelerators/gpu_faq.rst b/docs/source-pytorch/accelerators/gpu_faq.rst new file mode 100644 index 0000000..605cfb3 --- /dev/null +++ b/docs/source-pytorch/accelerators/gpu_faq.rst @@ -0,0 +1,77 @@ +:orphan: + +.. _gpu_faq: + +GPU training (FAQ) +================== + +****************************************************************** +How should I adjust the learning rate when using multiple devices? +****************************************************************** + +When using distributed training make sure to modify your learning rate according to your effective +batch size. + +Let's say you have a batch size of 7 in your dataloader. + +.. testcode:: + + class LitModel(LightningModule): + def train_dataloader(self): + return Dataset(..., batch_size=7) + +Whenever you use multiple devices and/or nodes, your effective batch size will be 7 * devices * num_nodes. + +.. code-block:: python + + # effective batch size = 7 * 8 + Trainer(accelerator="gpu", devices=8, strategy=...) + + # effective batch size = 7 * 8 * 10 + Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy=...) + + +.. note:: Huge batch sizes are actually really bad for convergence. Check out: + `Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour `_ + +---- + + +********************************************************* +How do I use multiple GPUs on Jupyter or Colab notebooks? +********************************************************* + +To use multiple GPUs on notebooks, use the *DDP_NOTEBOOK* mode. + +.. code-block:: python + + Trainer(accelerator="gpu", devices=4, strategy="ddp_notebook") + +If you want to use other strategies, please launch your training via the command-shell. + +---- + +***************************************************** +I'm getting errors related to Pickling. What do I do? +***************************************************** + +Pickle is Python's mechanism for serializing and unserializing data. Some distributed modes require that your code is fully pickle compliant. If you run into an issue with pickling, try the following to figure out the issue. + +.. code-block:: python + + import pickle + + model = YourModel() + pickle.dumps(model) + +For example, the `ddp_spawn` strategy has the pickling requirement. This is a limitation of Python. + +.. code-block:: python + + Trainer(accelerator="gpu", devices=4, strategy="ddp_spawn") + +If you use `ddp`, your code doesn't need to be pickled: + +.. code-block:: python + + Trainer(accelerator="gpu", devices=4, strategy="ddp") diff --git a/docs/source-pytorch/accelerators/gpu_intermediate.rst b/docs/source-pytorch/accelerators/gpu_intermediate.rst new file mode 100644 index 0000000..9baec7b --- /dev/null +++ b/docs/source-pytorch/accelerators/gpu_intermediate.rst @@ -0,0 +1,284 @@ +:orphan: + +.. _gpu_intermediate: + +GPU training (Intermediate) +=========================== +**Audience:** Users looking to train across machines or experiment with different scaling techniques. + +---- + +Distributed Training strategies +------------------------------- +Lightning supports multiple ways of doing distributed training. + +.. video:: ../_static/fetched-s3-assets/Trainer+flags+4-+multi+node+training_3.mp4 + :poster: ../_static/fetched-s3-assets/thumb_multi_gpus.png + :width: 400 + +- DistributedDataParallel (multiple-gpus across many machines) + - Regular (``strategy='ddp'``) + - Spawn (``strategy='ddp_spawn'``) + - Notebook/Fork (``strategy='ddp_notebook'``) + +.. note:: + If you request multiple GPUs or nodes without setting a strategy, DDP will be automatically used. + +For a deeper understanding of what Lightning is doing, feel free to read this +`guide `_. + + +Distributed Data Parallel +^^^^^^^^^^^^^^^^^^^^^^^^^ +:class:`~torch.nn.parallel.DistributedDataParallel` (DDP) works as follows: + +1. Each GPU across each node gets its own process. + +2. Each GPU gets visibility into a subset of the overall dataset. It will only ever see that subset. + +3. Each process inits the model. + +4. Each process performs a full forward and backward pass in parallel. + +5. The gradients are synced and averaged across all processes. + +6. Each process updates its optimizer. + +.. code-block:: python + + # train on 8 GPUs (same machine (ie: node)) + trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp") + + # train on 32 GPUs (4 nodes) + trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp", num_nodes=4) + +This Lightning implementation of DDP calls your script under the hood multiple times with the correct environment +variables: + +.. code-block:: bash + + # example for 3 GPUs DDP + MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=0 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc + MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=1 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc + MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=2 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc + +We use DDP this way because `ddp_spawn` has a few limitations (due to Python and PyTorch): + +1. Since `.spawn()` trains the model in subprocesses, the model on the main process does not get updated. +2. Dataloader(num_workers=N), where N is large, bottlenecks training with DDP... ie: it will be VERY slow or won't work at all. This is a PyTorch limitation. +3. Forces everything to be picklable. + +There are cases in which it is NOT possible to use DDP. Examples are: + +- Jupyter Notebook, Google COLAB, Kaggle, etc. +- You have a nested script without a root package + +In these situations you should use `ddp_notebook` or `dp` instead. + +Distributed Data Parallel Spawn +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +`ddp_spawn` is exactly like `ddp` except that it uses .spawn to start the training processes. + +.. warning:: It is STRONGLY recommended to use `DDP` for speed and performance. + +.. code-block:: python + + mp.spawn(self.ddp_train, nprocs=self.num_processes, args=(model,)) + +If your script does not support being called from the command line (ie: it is nested without a root +project module) you can use the following method: + +.. code-block:: python + + # train on 8 GPUs (same machine (ie: node)) + trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp_spawn") + +We STRONGLY discourage this use because it has limitations (due to Python and PyTorch): + +1. The model you pass in will not update. Please save a checkpoint and restore from there. +2. Set Dataloader(num_workers=0) or it will bottleneck training. + +`ddp` is MUCH faster than `ddp_spawn`. We recommend you + +1. Install a top-level module for your project using setup.py + +.. code-block:: python + + # setup.py + #!/usr/bin/env python + + from setuptools import setup, find_packages + + setup( + name="src", + version="0.0.1", + description="Describe Your Cool Project", + author="", + author_email="", + url="https://github.com/YourSeed", # REPLACE WITH YOUR OWN GITHUB PROJECT LINK + install_requires=["lightning"], + packages=find_packages(), + ) + +2. Setup your project like so: + +.. code-block:: bash + + /project + /src + some_file.py + /or_a_folder + setup.py + +3. Install as a root-level package + +.. code-block:: bash + + cd /project + pip install -e . + +You can then call your scripts anywhere + +.. code-block:: bash + + cd /project/src + python some_file.py --accelerator 'gpu' --devices 8 --strategy 'ddp' + + +Distributed Data Parallel in Notebooks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +DDP Notebook/Fork is an alternative to Spawn that can be used in interactive Python and Jupyter notebooks, Google Colab, Kaggle notebooks, and so on: +The Trainer enables it by default when such environments are detected. + +.. code-block:: python + + # train on 8 GPUs in a Jupyter notebook + trainer = Trainer(accelerator="gpu", devices=8) + + # can be set explicitly + trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp_notebook") + + # can also be used in non-interactive environments + trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp_fork") + +Among the native distributed strategies, regular DDP (``strategy="ddp"``) is still recommended as the go-to strategy over Spawn and Fork/Notebook for its speed and stability but it can only be used with scripts. + + +Comparison of DDP variants and tradeoffs +**************************************** + +.. list-table:: DDP variants and their tradeoffs + :widths: 40 20 20 20 + :header-rows: 1 + + * - + - DDP + - DDP Spawn + - DDP Notebook/Fork + * - Works in Jupyter notebooks / IPython environments + - No + - No + - Yes + * - Supports multi-node + - Yes + - Yes + - Yes + * - Supported platforms + - Linux, Mac, Win + - Linux, Mac, Win + - Linux, Mac + * - Requires all objects to be picklable + - No + - Yes + - No + * - Limitations in the main process + - None + - The state of objects is not up-to-date after returning to the main process (`Trainer.fit()` etc). Only the model parameters get transferred over. + - GPU operations such as moving tensors to the GPU or calling ``torch.cuda`` functions before invoking ``Trainer.fit`` is not allowed. + * - Process creation time + - Slow + - Slow + - Fast + + +Distributed and 16-bit precision +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Below are the possible configurations we support. + ++-------+---------+-----+--------+-----------------------------------------------------------------------+ +| 1 GPU | 1+ GPUs | DDP | 16-bit | command | ++=======+=========+=====+========+=======================================================================+ +| Y | | | | `Trainer(accelerator="gpu", devices=1)` | ++-------+---------+-----+--------+-----------------------------------------------------------------------+ +| Y | | | Y | `Trainer(accelerator="gpu", devices=1, precision=16)` | ++-------+---------+-----+--------+-----------------------------------------------------------------------+ +| | Y | Y | | `Trainer(accelerator="gpu", devices=k, strategy='ddp')` | ++-------+---------+-----+--------+-----------------------------------------------------------------------+ +| | Y | Y | Y | `Trainer(accelerator="gpu", devices=k, strategy='ddp', precision=16)` | ++-------+---------+-----+--------+-----------------------------------------------------------------------+ + +DDP can also be used with 1 GPU, but there's no reason to do so other than debugging distributed-related issues. + + +Implement Your Own Distributed (DDP) training +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If you need your own way to init PyTorch DDP you can override :meth:`lightning.pytorch.strategies.ddp.DDPStrategy.setup_distributed`. + +If you also need to use your own DDP implementation, override :meth:`lightning.pytorch.strategies.ddp.DDPStrategy.configure_ddp`. + +---------- + +Torch Distributed Elastic +------------------------- +Lightning supports the use of Torch Distributed Elastic to enable fault-tolerant and elastic distributed job scheduling. To use it, specify the 'ddp' backend and the number of GPUs you want to use in the trainer. + +.. code-block:: python + + Trainer(accelerator="gpu", devices=8, strategy="ddp") + +To launch a fault-tolerant job, run the following on all nodes. + +.. code-block:: bash + + python -m torch.distributed.run + --nnodes=NUM_NODES + --nproc_per_node=TRAINERS_PER_NODE + --rdzv_id=JOB_ID + --rdzv_backend=c10d + --rdzv_endpoint=HOST_NODE_ADDR + YOUR_LIGHTNING_TRAINING_SCRIPT.py (--arg1 ... train script args...) + +To launch an elastic job, run the following on at least ``MIN_SIZE`` nodes and at most ``MAX_SIZE`` nodes. + +.. code-block:: bash + + python -m torch.distributed.run + --nnodes=MIN_SIZE:MAX_SIZE + --nproc_per_node=TRAINERS_PER_NODE + --rdzv_id=JOB_ID + --rdzv_backend=c10d + --rdzv_endpoint=HOST_NODE_ADDR + YOUR_LIGHTNING_TRAINING_SCRIPT.py (--arg1 ... train script args...) + +See the official `Torch Distributed Elastic documentation `_ for details +on installation and more use cases. + +Optimize multi-machine communication +------------------------------------ + +By default, Lightning will select the ``nccl`` backend over ``gloo`` when running on GPUs. +Find more information about PyTorch's supported backends `here `__. + +Lightning allows explicitly specifying the backend via the `process_group_backend` constructor argument on the relevant Strategy classes. By default, Lightning will select the appropriate process group backend based on the hardware used. + +.. code-block:: python + + from lightning.pytorch.strategies import DDPStrategy + + # Explicitly specify the process group backend if you choose to + ddp = DDPStrategy(process_group_backend="nccl") + + # Configure the strategy on the Trainer + trainer = Trainer(strategy=ddp, accelerator="gpu", devices=8) diff --git a/source/accelerators/ipu.rst b/docs/source-pytorch/accelerators/ipu.rst similarity index 100% rename from source/accelerators/ipu.rst rename to docs/source-pytorch/accelerators/ipu.rst diff --git a/source/accelerators/ipu_advanced.rst b/docs/source-pytorch/accelerators/ipu_advanced.rst similarity index 94% rename from source/accelerators/ipu_advanced.rst rename to docs/source-pytorch/accelerators/ipu_advanced.rst index 1dc4e71..98c1de5 100644 --- a/source/accelerators/ipu_advanced.rst +++ b/docs/source-pytorch/accelerators/ipu_advanced.rst @@ -6,6 +6,8 @@ Accelerator: IPU training ========================= **Audience:** Users looking to customize IPU training for massive models. +.. warning:: This is an :ref:`experimental ` feature. + ---- Advanced IPU options @@ -19,8 +21,8 @@ IPUs provide further optimizations to speed up training. By using the ``IPUStrat .. code-block:: python - import pytorch_lightning as pl - from pytorch_lightning.strategies import IPUStrategy + import lightning.pytorch as pl + from lightning_graphcore import IPUStrategy model = MyLightningModule() trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(device_iterations=32)) @@ -31,8 +33,8 @@ Note that by default we return the last device iteration loss. You can override .. code-block:: python import poptorch - import pytorch_lightning as pl - from pytorch_lightning.strategies import IPUStrategy + import lightning.pytorch as pl + from lightning_graphcore import IPUStrategy model = MyLightningModule() inference_opts = poptorch.Options() @@ -71,7 +73,7 @@ Below is an example using the block annotation in a LightningModule. .. code-block:: python - import pytorch_lightning as pl + import lightning.pytorch as pl import poptorch @@ -104,7 +106,7 @@ You can also use the block context manager within the forward function, or any o .. code-block:: python - import pytorch_lightning as pl + import lightning.pytorch as pl import poptorch @@ -120,7 +122,6 @@ You can also use the block context manager within the forward function, or any o self.softmax = torch.nn.Softmax(dim=1) def forward(self, x): - with poptorch.Block(ipu_id=0): x = self.act(self.layer1(x)) diff --git a/docs/source-pytorch/accelerators/ipu_basic.rst b/docs/source-pytorch/accelerators/ipu_basic.rst new file mode 100644 index 0000000..8381b26 --- /dev/null +++ b/docs/source-pytorch/accelerators/ipu_basic.rst @@ -0,0 +1,72 @@ +:orphan: + +.. _ipu_basic: + +Accelerator: IPU training +========================= +**Audience:** Users looking to save money and run large models faster using single or multiple IPU devices. + +.. warning:: This is an :ref:`experimental ` feature. + +---- + +What is an IPU? +--------------- + +The Graphcore `Intelligence Processing Unit (IPU) `__, built for Artificial Intelligence and Machine Learning, consists of many individual cores, called *tiles*, allowing highly parallel computation. Due to the high bandwidth between tiles, IPUs facilitate machine learning loads where parallelization is essential. Because computation is heavily parallelized, + +IPUs operate in a different way to conventional accelerators such as CPU/GPUs. IPUs do not require large batch sizes for maximum parallelization, can provide optimizations across the compiled graph and rely on model parallelism to fully utilize tiles for larger models. + +IPUs are used to build IPU-PODs, rack-based systems of IPU-Machines for larger workloads. See the `IPU Architecture `__ for more information. + +See the `Graphcore Glossary `__ for the definitions of other IPU-specific terminology. + +---- + +Run on IPU +---------- + +To enable PyTorch Lightning to utilize the IPU accelerator, simply provide ``accelerator="ipu"`` parameter to the Trainer class. + +To use multiple IPUs set the devices to a number that is a power of 2 (i.e: 2, 4, 8, 16, ...) + +.. code-block:: python + + # run on as many IPUs as available by default + trainer = Trainer(accelerator="auto", devices="auto", strategy="auto") + # equivalent to + trainer = Trainer() + + # run on one IPU + trainer = Trainer(accelerator="ipu", devices=1) + # run on multiple IPUs + trainer = Trainer(accelerator="ipu", devices=8) + # choose the number of devices automatically + trainer = Trainer(accelerator="ipu", devices="auto") + +---- + +How to access IPUs +------------------ + +To use IPUs you must have access to a system with IPU devices. To get access see `get started `__. + +You must ensure that the IPU system has enabled the PopART and Poplar packages from the SDK. Instructions are in the Get Started guide for your IPU system, on the Graphcore `documents portal `__. + +---- + +.. _known-limitations: + +Known limitations +----------------- + +Currently there are some known limitations that are being addressed in the near future to make the experience seamless when moving from different devices. + +Please see the `MNIST example `__ which displays most of the limitations and how to overcome them till they are resolved. + +* ``self.log`` is not supported in the ``training_step``, ``validation_step``, ``test_step`` or ``predict_step``. This is due to the step function being traced and sent to the IPU devices. +* Since the step functions are traced, branching logic or any form of primitive values are traced into constants. Be mindful as this could lead to errors in your custom code. +* Clipping gradients is not supported. +* It is not possible to use :class:`torch.utils.data.BatchSampler` in your dataloaders if you are using multiple IPUs. +* IPUs handle the data transfer to the device on the host, hence the hooks :meth:`~lightning.pytorch.core.hooks.ModelHooks.transfer_batch_to_device` and + :meth:`~lightning.pytorch.core.hooks.ModelHooks.on_after_batch_transfer` do not apply here and if you have overridden any of them, an exception will be raised. diff --git a/source/accelerators/ipu_intermediate.rst b/docs/source-pytorch/accelerators/ipu_intermediate.rst similarity index 86% rename from source/accelerators/ipu_intermediate.rst rename to docs/source-pytorch/accelerators/ipu_intermediate.rst index 68c866e..251004f 100644 --- a/source/accelerators/ipu_intermediate.rst +++ b/docs/source-pytorch/accelerators/ipu_intermediate.rst @@ -6,6 +6,8 @@ Accelerator: IPU training ========================= **Audience:** IPU users looking to increase performance via mixed precision and analysis tools. +.. warning:: This is an :ref:`experimental ` feature. + ---- Mixed precision & 16 bit precision @@ -20,7 +22,7 @@ set the precision flag. .. code-block:: python - import pytorch_lightning as pl + import lightning.pytorch as pl model = MyLightningModule() trainer = pl.Trainer(accelerator="ipu", devices=8, precision=16) @@ -30,8 +32,8 @@ You can also use pure 16-bit training, where the weights are also in 16-bit prec .. code-block:: python - import pytorch_lightning as pl - from pytorch_lightning.strategies import IPUStrategy + import lightning.pytorch as pl + from lightning_graphcore import IPUStrategy model = MyLightningModule() model = model.half() @@ -53,11 +55,11 @@ Lightning supports dumping all reports to a directory to open using the tool. .. code-block:: python - import pytorch_lightning as pl - from pytorch_lightning.strategies import IPUStrategy + import lightning.pytorch as pl + from lightning_graphcore import IPUStrategy model = MyLightningModule() trainer = pl.Trainer(accelerator="ipu", devices=8, strategy=IPUStrategy(autoreport_dir="report_dir/")) trainer.fit(model) -This will dump all reports to ``report_dir/`` which can then be opened using the Graph Analyser Tool, see `Opening Reports `__. +This will dump all reports to ``report_dir/`` which can then be opened using the Graph Analyser Tool, see `Opening Reports `__. diff --git a/docs/source-pytorch/accelerators/mps.rst b/docs/source-pytorch/accelerators/mps.rst new file mode 100644 index 0000000..53e8609 --- /dev/null +++ b/docs/source-pytorch/accelerators/mps.rst @@ -0,0 +1,32 @@ +.. _mps: + +Accelerator: Apple Silicon training +=================================== + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Prepare your code (Optional) + :description: Prepare your code to run on any hardware + :col_css: col-md-4 + :button_link: accelerator_prepare.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Basic + :description: Learn the basics of Apple silicon gpu training. + :col_css: col-md-4 + :button_link: mps_basic.html + :height: 150 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/accelerators/mps_basic.rst b/docs/source-pytorch/accelerators/mps_basic.rst new file mode 100644 index 0000000..eec8967 --- /dev/null +++ b/docs/source-pytorch/accelerators/mps_basic.rst @@ -0,0 +1,63 @@ +:orphan: + +.. _mps_basic: + +MPS training (basic) +==================== +**Audience:** Users looking to train on their Apple silicon GPUs. + +.. warning:: + + Both the MPS accelerator and the PyTorch backend are still experimental. + As such, not all operations are currently supported. However, with ongoing development from the PyTorch team, an increasingly large number of operations are becoming available. + You can use ``PYTORCH_ENABLE_MPS_FALLBACK=1 python your_script.py`` to fall back to cpu for unsupported operations. + + +---- + +What is Apple silicon? +---------------------- +Apple silicon chips are a unified system on a chip (SoC) developed by Apple based on the ARM design. +Among other things, they feature CPU-cores, GPU-cores, a neural engine and shared memory between all of these features. + +---- + +So it's a CPU? +-------------- +Apple silicon includes CPU-cores among several other features. However, the full potential for the hardware acceleration of which the M-Socs are capable is unavailable when running on the ``CPUAccelerator``. This is because they also feature a GPU and a neural engine. + +To use them, Lightning supports the ``MPSAccelerator``. + +---- + +Run on Apple silicon gpus +------------------------- +Enable the following Trainer arguments to run on Apple silicon gpus (MPS devices). + +.. code-block:: python + + trainer = Trainer(accelerator="mps", devices=1) + +.. note:: + The ``MPSAccelerator`` only supports 1 device at a time. Currently there are no machines with multiple MPS-capable GPUs. + +---- + +What does MPS stand for? +------------------------ +MPS is short for `Metal Performance Shaders `_ which is the technology used in the back for gpu communication and computing. + +---- + +Troubleshooting +--------------- + + +If Lightning can't detect the Apple Silicon hardware, it will raise this exception: + +.. code:: + + MisconfigurationException: `MPSAccelerator` can not run on your system since the accelerator is not available. + +If you are seeing this despite running on an ARM-enabled Mac, the most likely cause is that your Python is being emulated and thinks it is running on an Intel CPU. +To solve this, re-install your python executable (and if using environment managers like conda, you have to reinstall these as well) by downloading the Apple M1/M2 build (not Intel!), for example `here `_. diff --git a/source/accelerators/tpu.rst b/docs/source-pytorch/accelerators/tpu.rst similarity index 97% rename from source/accelerators/tpu.rst rename to docs/source-pytorch/accelerators/tpu.rst index 6809277..f8ca8f5 100644 --- a/source/accelerators/tpu.rst +++ b/docs/source-pytorch/accelerators/tpu.rst @@ -46,7 +46,7 @@ Accelerator: TPU training :header: FAQ :description: Frequently asked questions about TPU training. :col_css: col-md-4 - :button_link: gpu_faq.html + :button_link: tpu_faq.html :height: 150 .. raw:: html diff --git a/docs/source-pytorch/accelerators/tpu_advanced.rst b/docs/source-pytorch/accelerators/tpu_advanced.rst new file mode 100644 index 0000000..e410c6e --- /dev/null +++ b/docs/source-pytorch/accelerators/tpu_advanced.rst @@ -0,0 +1,64 @@ +:orphan: + +TPU training (Advanced) +======================= +**Audience:** Users looking to apply advanced performance techniques to TPU training. + +.. warning:: This is an :ref:`experimental ` feature. + +---- + +Weight Sharing/Tying +-------------------- +Weight Tying/Sharing is a technique where in the module weights are shared among two or more layers. +This is a common method to reduce memory consumption and is utilized in many State of the Art +architectures today. + +PyTorch XLA requires these weights to be tied/shared after moving the model to the XLA device. +To support this requirement, Lightning automatically finds these weights and ties them after +the modules are moved to the XLA device under the hood. It will ensure that the weights among +the modules are shared but not copied independently. + +PyTorch Lightning has an inbuilt check which verifies that the model parameter lengths +match once the model is moved to the device. If the lengths do not match Lightning +throws a warning message. + +Example: + +.. code-block:: python + + from lightning.pytorch.core.module import LightningModule + from torch import nn + from lightning.pytorch.trainer.trainer import Trainer + + + class WeightSharingModule(LightningModule): + def __init__(self): + super().__init__() + self.layer_1 = nn.Linear(32, 10, bias=False) + self.layer_2 = nn.Linear(10, 32, bias=False) + self.layer_3 = nn.Linear(32, 10, bias=False) + # Lightning automatically ties these weights after moving to the XLA device, + # so all you need is to write the following just like on other accelerators. + self.layer_3.weight = self.layer_1.weight + + def forward(self, x): + x = self.layer_1(x) + x = self.layer_2(x) + x = self.layer_3(x) + return x + + + model = WeightSharingModule() + trainer = Trainer(max_epochs=1, accelerator="tpu") + +See `XLA Documentation `_ + +---- + +XLA +--- +XLA is the library that interfaces PyTorch with the TPUs. +For more information check out `XLA `_. + +Guide for `troubleshooting XLA `_ diff --git a/docs/source-pytorch/accelerators/tpu_basic.rst b/docs/source-pytorch/accelerators/tpu_basic.rst new file mode 100644 index 0000000..36cd478 --- /dev/null +++ b/docs/source-pytorch/accelerators/tpu_basic.rst @@ -0,0 +1,114 @@ +:orphan: + +TPU training (Basic) +==================== +**Audience:** Users looking to train on single or multiple TPU cores. + +.. warning:: This is an :ref:`experimental ` feature. + +---- + +.. video:: ../_static/fetched-s3-assets/tpu_cores.mp4 + :poster: ../_static/fetched-s3-assets/thumb_tpus.png + :width: 400 + :muted: + +Lightning supports running on TPUs. At this moment, TPUs are available +on Google Cloud (GCP), Google Colab and Kaggle Environments. For more information on TPUs +`watch this video `_. + +---------------- + +What is a TPU? +-------------- +Tensor Processing Unit (TPU) is an AI accelerator application-specific integrated circuit (ASIC) developed by Google specifically for neural networks. + +A TPU has 8 cores where each core is optimized for 128x128 matrix multiplies. In general, a single TPU is about as fast as 5 V100 GPUs! + +A TPU pod hosts many TPUs on it. Currently, TPU v3 Pod has up to 2048 TPU cores and 32 TiB of memory! +You can request a full pod from Google cloud or a "slice" which gives you +some subset of those 2048 cores. + +---- + +Run on TPU cores +---------------- + +To run on different cores, modify the ``devices`` argument. + +.. code-block:: python + + # run on as many TPUs as available by default + trainer = Trainer(accelerator="auto", devices="auto", strategy="auto") + # equivalent to + trainer = Trainer() + + # run on one TPU core + trainer = Trainer(accelerator="tpu", devices=1) + # run on multiple TPU cores + trainer = Trainer(accelerator="tpu", devices=8) + # run on one specific TPU core: the 2nd core (index 1) + trainer = Trainer(accelerator="tpu", devices=[1]) + # choose the number of cores automatically + trainer = Trainer(accelerator="tpu", devices="auto") + +---- + +How to access TPUs +------------------ +To access TPUs, there are three main ways. + +Google Colab +^^^^^^^^^^^^ +Colab is like a jupyter notebook with a free GPU or TPU +hosted on GCP. + +To get a TPU on colab, follow these steps: + +1. Go to `Google Colab `_. + +2. Click "new notebook" (bottom right of pop-up). + +3. Click runtime > change runtime settings. Select Python 3, and hardware accelerator "TPU". + This will give you a TPU with 8 cores. + +4. Next, insert this code into the first cell and execute. + This will install the xla library that interfaces between PyTorch and the TPU. + + .. code-block:: + + !pip install cloud-tpu-client https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.13-cp38-cp38m-linux_x86_64.whl + +5. Once the above is done, install PyTorch Lightning. + + .. code-block:: + + !pip install lightning + +6. Then set up your LightningModule as normal. + +Google Cloud (GCP) +^^^^^^^^^^^^^^^^^^ +You could refer to this `page `_ for getting started with Cloud TPU resources on GCP. + +---- + +Optimize Performance +-------------------- + +The TPU was designed for specific workloads and operations to carry out large volumes of matrix multiplication, +convolution operations and other commonly used ops in applied deep learning. +The specialization makes it a strong choice for NLP tasks, sequential convolutional networks, and under low precision operation. +There are cases in which training on TPUs is slower when compared with GPUs, for possible reasons listed: + +- Too small batch size. +- Explicit evaluation of tensors during training, e.g. ``tensor.item()`` +- Tensor shapes (e.g. model inputs) change often during training. +- Limited resources when using TPU's with PyTorch `Link `_ +- XLA Graph compilation during the initial steps `Reference `_ +- Some tensor ops are not fully supported on TPU, or not supported at all. These operations will be performed on CPU (context switch). + +The official PyTorch XLA `performance guide `_ +has more detailed information on how PyTorch code can be optimized for TPU. In particular, the +`metrics report `_ allows +one to identify operations that lead to context switching. diff --git a/docs/source-pytorch/accelerators/tpu_faq.rst b/docs/source-pytorch/accelerators/tpu_faq.rst new file mode 100644 index 0000000..8a26899 --- /dev/null +++ b/docs/source-pytorch/accelerators/tpu_faq.rst @@ -0,0 +1,117 @@ +:orphan: + +.. _tpu_faq: + +TPU training (FAQ) +================== + +***************************** +XLA configuration is missing? +***************************** + +.. code-block:: + + File "/usr/local/lib/python3.8/dist-packages/torch_xla/core/xla_model.py", line 18, in + _DEVICES = xu.LazyProperty(lambda: torch_xla._XLAC._xla_get_devices()) + RuntimeError: tensorflow/compiler/xla/xla_client/computation_client.cc:273 : Missing XLA configuration + Traceback (most recent call last): + ... + File "/home/kaushikbokka/pytorch-lightning/pytorch_lightning/utilities/device_parser.py", line 125, in parse_tpu_cores + raise MisconfigurationException('No TPU devices were found.') + lightning.pytorch.utilities.exceptions.MisconfigurationException: No TPU devices were found. + +This means the system is missing XLA configuration. You would need to set up XRT TPU device configuration. + +For TPUVM architecture, you could set it in your terminal by: + +.. code-block:: bash + + export XRT_TPU_CONFIG="localservice;0;localhost:51011" + +And for the old TPU + 2VM architecture, you could set it by: + +.. code-block:: bash + + export TPU_IP_ADDRESS=10.39.209.42 # You could get the IP Address in the GCP TPUs section + export XRT_TPU_CONFIG="tpu_worker;0;$TPU_IP_ADDRESS:8470" + +---- + +********************************************************** +How to clear up the programs using TPUs in the background? +********************************************************** + +.. code-block:: bash + + pgrep python | awk '{print $2}' | xargs -r kill -9 + +Sometimes, there can still be old programs running on the TPUs, which would make the TPUs unavailable to use. You could use the above command in the terminal to kill the running processes. + +---- + +************************************* +How to resolve the replication issue? +************************************* + +.. code-block:: + + File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 200, in set_replication + replication_devices = xla_replication_devices(devices) + File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 187, in xla_replication_devices + .format(len(local_devices), len(kind_devices))) + RuntimeError: Cannot replicate if number of devices (1) is different from 8 + +This error is raised when the XLA device is called outside the spawn process. Internally in the XLA-Strategy for training on multiple tpu cores, we use XLA's `xmp.spawn`. +Don't use ``xm.xla_device()`` while working on Lightning + TPUs! + +---- + +************************************** +Unsupported datatype transfer to TPUs? +************************************** + +.. code-block:: + + File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 205, in _for_each_instance_rewrite + v = _for_each_instance_rewrite(result.__dict__[k], select_fn, fn, rwmap) + File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 206, in _for_each_instance_rewrite + result.__dict__[k] = v + TypeError: 'mappingproxy' object does not support item assignment + +PyTorch XLA only supports Tensor objects for CPU to TPU data transfer. Might cause issues if the User is trying to send some non-tensor objects through the DataLoader or during saving states. + +---- + +************************************************* +How to setup the debug mode for Training on TPUs? +************************************************* + +.. code-block:: python + + import lightning.pytorch as pl + + my_model = MyLightningModule() + trainer = pl.Trainer(accelerator="tpu", devices=8, strategy="xla_debug") + trainer.fit(my_model) + +Example Metrics report: + +.. code-block:: + + Metric: CompileTime + TotalSamples: 202 + Counter: 06m09s401ms746.001us + ValueRate: 778ms572.062us / second + Rate: 0.425201 / second + Percentiles: 1%=001ms32.778us; 5%=001ms61.283us; 10%=001ms79.236us; 20%=001ms110.973us; 50%=001ms228.773us; 80%=001ms339.183us; 90%=001ms434.305us; 95%=002ms921.063us; 99%=21s102ms853.173us + + +A lot of PyTorch operations aren't lowered to XLA, which could lead to significant slowdown of the training process. +These operations are moved to the CPU memory and evaluated, and then the results are transferred back to the XLA device(s). +By using the `xla_debug` Strategy, users could create a metrics report to diagnose issues. + +The report includes things like (`XLA Reference `_): + +* how many times we issue XLA compilations and time spent on issuing. +* how many times we execute and time spent on execution +* how many device data handles we create/destroy etc. diff --git a/docs/source-pytorch/accelerators/tpu_intermediate.rst b/docs/source-pytorch/accelerators/tpu_intermediate.rst new file mode 100644 index 0000000..579d2cc --- /dev/null +++ b/docs/source-pytorch/accelerators/tpu_intermediate.rst @@ -0,0 +1,122 @@ +:orphan: + +TPU training (Intermediate) +=========================== +**Audience:** Users looking to use cloud TPUs. + +.. warning:: This is an :ref:`experimental ` feature. + +---- + +DistributedSamplers +------------------- +Lightning automatically inserts the correct samplers - no need to do this yourself! + +Usually, with TPUs (and DDP), you would need to define a DistributedSampler to move the right +chunk of data to the appropriate TPU. As mentioned, this is not needed in Lightning + +.. note:: Don't add distributedSamplers. Lightning does this automatically + +If for some reason you still need to, this is how to construct the sampler +for TPU use + +.. code-block:: python + + import torch_xla.core.xla_model as xm + + + def train_dataloader(self): + dataset = MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()) + + # required for TPU support + sampler = None + if use_tpu: + sampler = torch.utils.data.distributed.DistributedSampler( + dataset, num_replicas=xm.xrt_world_size(), rank=xm.get_ordinal(), shuffle=True + ) + + loader = DataLoader(dataset, sampler=sampler, batch_size=32) + + return loader + +Configure the number of TPU cores in the trainer. You can only choose 1 or 8. +To use a full TPU pod skip to the TPU pod section. + +.. code-block:: python + + import lightning.pytorch as pl + + my_model = MyLightningModule() + trainer = pl.Trainer(accelerator="tpu", devices=8) + trainer.fit(my_model) + +That's it! Your model will train on all 8 TPU cores. + +---------------- + +TPU VM +------ +Lightning supports training on the new Cloud TPU VMs. +Previously, we needed separate VMs to connect to the TPU machines, but as +Cloud TPU VMs run on the TPU Host machines, it allows direct SSH access +for the users. Hence, this architecture upgrade leads to cheaper and significantly +better performance and usability while working with TPUs. + +The TPUVMs come pre-installed with latest versions of PyTorch and PyTorch XLA. +After connecting to the VM and before running your Lightning code, you would need +to set the `XRT TPU device configuration `__. + +.. code-block:: bash + + export XRT_TPU_CONFIG="localservice;0;localhost:51011" + + # Set the environment variable to visible devices. + # You might need to change the value depending on how many chips you have + export TPU_NUM_DEVICES=4 + + # Allow LIBTPU LOAD by multiple processes + export ALLOW_MULTIPLE_LIBTPU_LOAD=1 + +You can learn more about the Cloud TPU VM architecture `here `_ + +---------------- + +TPU Pod +------- +To train on more than the number of cores in a node, your code actually doesn't change! + +All TPU VMs in a Pod setup are required to access the model code and data. +One easy way to achieve this is to use the following startup script when creating the TPU VM pod. +It will perform the data downloading on all TPU VMs. Note that you need to export the corresponding environment variables following the instruction in Create TPU Node. + +.. code-block:: bash + + gcloud alpha compute tpus tpu-vm create ${TPU_POD_NAME} --zone ${ZONE} --project ${PROJECT_ID} --accelerator-type ${ACCELERATOR_TYPE} --version ${RUNTIME_VERSION} --metadata startup-script=setup.py + +Then you could ssh to any TPU worker, e.g. worker 0, check if data/model downloading is finished and +start the training after generating the ssh-keys to ssh between VM workers on a pod. +All you need to do is submit the following command: + +.. code-block:: bash + + python3 -m torch_xla.distributed.xla_dist --tpu=$TPU_POD_NAME -- python3 train.py --max_epochs=5 --batch_size=32 + +See `this guide `_ +on how to set up the instance groups and VMs needed to run TPU Pods. + +---------------- + +16 bit precision +---------------- +Lightning also supports training in 16-bit precision with TPUs. +By default, TPU training will use 32-bit precision. To enable it, do + +.. code-block:: python + + import lightning.pytorch as pl + + my_model = MyLightningModule() + trainer = pl.Trainer(accelerator="tpu", precision="16-mixed") + trainer.fit(my_model) + +Under the hood the xla library will use the `bfloat16 type `_. diff --git a/docs/source-pytorch/advanced/finetuning.rst b/docs/source-pytorch/advanced/finetuning.rst new file mode 100644 index 0000000..4809e6e --- /dev/null +++ b/docs/source-pytorch/advanced/finetuning.rst @@ -0,0 +1 @@ +.. include:: transfer_learning.rst diff --git a/docs/source-pytorch/advanced/model_parallel.rst b/docs/source-pytorch/advanced/model_parallel.rst new file mode 100644 index 0000000..3f40241 --- /dev/null +++ b/docs/source-pytorch/advanced/model_parallel.rst @@ -0,0 +1,922 @@ +.. _model-parallel: + +################################## +Train 1 trillion+ parameter models +################################## + +When training large models, fitting larger batch sizes, or trying to increase throughput using multi-GPU compute, Lightning provides advanced optimized distributed training strategies to support these cases and offer substantial improvements in memory usage. + +Note that some of the extreme memory saving configurations will affect the speed of training. This Speed/Memory trade-off in most cases can be adjusted. + +Some of these memory-efficient strategies rely on offloading onto other forms of memory, such as CPU RAM or NVMe. This means you can even see memory benefits on a **single GPU**, using a strategy such as :ref:`deepspeed-zero-stage-3-offload`. + +Check out this amazing video explaining model parallelism and how it works behind the scenes: + +.. raw:: html + + + + +********************************************* +Choosing an Advanced Distributed GPU Strategy +********************************************* + +If you would like to stick with PyTorch DDP, see :ref:`ddp-optimizations`. + +Unlike :class:`~torch.nn.parallel.DistributedDataParallel` (DDP) where the maximum trainable model size and batch size do not change with respect to the number of GPUs, memory-optimized strategies can accommodate bigger models and larger batches as more GPUs are used. This means as you scale up the number of GPUs, you can reach the number of model parameters you'd like to train. + +There are many considerations when choosing a strategy as described below. In addition, check out the visualization of various strategy benchmarks using `minGPT `__ `here `__. + +Pre-training vs Fine-tuning +=========================== + +When fine-tuning, we often use a magnitude less data compared to pre-training a model. This is important when choosing a distributed strategy as usually for pre-training, **we are compute-bound**. +This means we cannot sacrifice throughput as much as if we were fine-tuning, because in fine-tuning the data requirement is smaller. + +Overall: + +* When **fine-tuning** a model, use advanced memory efficient strategies such as :ref:`fully-sharded-training`, :ref:`deepspeed-zero-stage-3` or :ref:`deepspeed-zero-stage-3-offload`, allowing you to fine-tune larger models if you are limited on compute +* When **pre-training** a model, use simpler optimizations such as :ref:`deepspeed-zero-stage-2`, scaling the number of GPUs to reach larger parameter sizes +* For both fine-tuning and pre-training, use :ref:`deepspeed-activation-checkpointing` as the throughput degradation is not significant + +For example when using 128 GPUs, you can **pre-train** large 10 to 20 Billion parameter models using :ref:`deepspeed-zero-stage-2` without having to take a performance hit with more advanced optimized multi-gpu strategy. + +But for **fine-tuning** a model, you can reach 10 to 20 Billion parameter models using :ref:`deepspeed-zero-stage-3-offload` on a **single GPU**. This does come with a significant throughput hit, which needs to be weighed accordingly. + +When Shouldn't I use an Optimized Distributed Strategy? +======================================================= + +Sharding techniques help when model sizes are fairly large; roughly 500M+ parameters is where we've seen benefits. However, in the following cases, we recommend sticking to ordinary distributed strategies + +* When your model is small (ResNet50 of around 80M Parameters), unless you are using unusually large batch sizes or inputs. +* Due to high distributed communication between devices, if running on a slow network/interconnect, the training might be much slower than expected and then it's up to you to determince the tradeoff here. + + +Cutting-edge and third-party Strategies +======================================= + +Cutting-edge Lightning strategies are being developed by third-parties outside of Lightning. + +If you want to try some of the latest and greatest features for model-parallel training, check out the :doc:`Colossal-AI Strategy <../integrations/strategies/colossalai>` integration. + +Another integration is :doc:`Bagua Strategy <../integrations/strategies/bagua>`, deep learning training acceleration framework for PyTorch, with advanced distributed training algorithms and system optimizations. + +For training on unreliable mixed GPUs across the internet check out the :doc:`Hivemind Strategy <../integrations/strategies/hivemind>` integration. + +---- + + +************************ +Efficient initialization +************************ + +Instantiating a ``nn.Module`` in PyTorch creates all parameters on CPU in float32 precision by default. +To speed up initialization, you can force PyTorch to create the model directly on the target device and with the desired precision without changing your model code. + +.. code-block:: python + + fabric = Trainer(accelerator="cuda", precision="16-true") + + with trainer.init_module(): + # models created here will be on GPU and in float16 + model = MyModel() + + trainer.fit(model) + +This eliminates the waiting time to transfer the model parameters from the CPU to the device. + +When loading a model from a checkpoint, for example when fine-tuning, set `empty_init=True` to avoid expensive +and redundant memory initialization: + +.. code-block:: python + + with trainer.init_module(empty_init=True): + # creation of the model is very fast + model = MyModel.load_from_checkpoint("my/checkpoint/path.ckpt") + + trainer.fit(model) + +For strategies that handle large sharded models (FSDP, DeepSpeed), the :meth:`~lightning.pytorch.trainer.trainer.Trainer.init_module` +should not be used, instead override the :meth:`~lightning.pytorch.core.hooks.ModelHooks.configure_model` hook: + +.. code-block:: python + + class MyModel(LightningModule): + def __init__(self): + super().__init__() + # don't instantiate layers here + # move the creation of layers to `configure_model` + + def configure_model(self): + # create all your layers here + self.layers = nn.Sequential(...) + +This makes it possible to work with models that are larger than the memory of a single device. + + +.. _fully-sharded-training: + +********************** +Fully Sharded Training +********************** + +PyTorch has it's own version of `FSDP `_ which is upstreamed from their `fairscale `__ project. +It was introduced in their `v1.11.0 release `_ but it is recommended to use it with PyTorch v1.12 or more and that's what +Lightning supports. + +.. warning:: This is an :ref:`experimental ` feature. + +Auto Wrapping +============= + +Model layers should be wrapped in FSDP in a nested way to save peak memory and enable communication and computation overlapping. The +simplest way to do it is auto wrapping, which can serve as a drop-in replacement for DDP without changing the rest of the code. You don't +have to ``wrap`` layers manually as in the case of manual wrapping. + +.. note:: + For users of PyTorch < 2.0: While initializing the optimizers inside ``configure_optimizers`` hook, make sure to use ``self.trainer.model.parameters()``, else + PyTorch will raise an error. This is required because when you use auto-wrap, the model layers are sharded and your + ``lightning_module.parameters()`` will return a generator with no params. + +.. code-block:: python + + model = BoringModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="fsdp", precision=16) + trainer.fit(model) + + +You can customize the strategy configuration by adjusting the arguments of :class:`~lightning.pytorch.strategies.FSDPStrategy` and pass that to the ``strategy`` argument inside the ``Trainer``. + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import FSDPStrategy + + # equivalent to passing `"fsdp_cpu_offload"` + fsdp = FSDPStrategy(cpu_offload=True) + trainer = pl.Trainer(strategy=fsdp, accelerator="gpu", devices=4) + + # configure the wrapping condition + fsdp = FSDPStrategy(auto_wrap_policy={MyTransformerBlock}) + trainer = pl.Trainer(strategy=fsdp, accelerator="gpu", devices=4) + + +Read more `here `__. + + +Manual Wrapping +=============== + +Manual wrapping can be useful to explore complex sharding strategies by applying ``wrap`` selectively to some parts of the model. To activate +parameter sharding with manual wrapping, you can wrap your model using the ``wrap`` function. Internally in Lightning, we enable a context manager around the ``configure_model`` hook to make sure the ``wrap`` parameters are passed correctly. + +When not using Fully Sharded, these ``wrap`` calls are a no-op. This means once the changes have been made, there is no need to remove the changes for other strategies. + +``wrap`` simply wraps the module with a Fully Sharded Parallel class with the correct parameters from the Lightning context manager. + +Here's an example using that uses ``wrap`` to create your model: + +.. code-block:: python + + import torch + import torch.nn as nn + import lightning.pytorch as pl + from lightning.pytorch import Trainer + from torch.distributed.fsdp.wrap import wrap + + + class MyModel(pl.LightningModule): + def configure_model(self): + self.linear_layer = nn.Linear(32, 32) + self.block = nn.Sequential(nn.Linear(32, 32), nn.Linear(32, 32)) + + # modules are sharded across processes + # as soon as they are wrapped with `wrap`. + # During the forward/backward passes, weights get synced across processes + # and de-allocated once computation is complete, saving memory. + + # Wraps the layer in a Fully Sharded Wrapper automatically + linear_layer = wrap(self.linear_layer) + + for i, layer in enumerate(self.block): + self.block[i] = wrap(layer) + + self.model = nn.Sequential(linear_layer, nn.ReLU(), self.block) + + def configure_optimizers(self): + return torch.optim.AdamW(self.model.parameters()) + + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="fsdp", precision=16) + trainer.fit(model) + +In this case, Lightning will not re-wrap your model, so you don't need to set ``FSDPStrategy(auto_wrap_policy=...)``. + +Check out `this tutorial `__ to learn more about it. + +---- + + +Activation Checkpointing +======================== + +Activation checkpointing reduces GPU memory usage by avoiding the storage of intermediate activation tensors in +selected layers. The tradeoff is that computation cost for the backpropagation increases, as the dropped activations +need to be recomputed. + +Enable checkpointing on large layers (like Transformers) by providing a policy: + +.. code-block:: python + + from lightning.pytorch.strategies import FSDPStrategy + + fsdp = FSDPStrategy(activation_checkpointing_policy={MyTransformerBlock}) + trainer = pl.Trainer(strategy=fsdp, accelerator="gpu", devices=4) + + +You could also configure activation checkpointing manually inside the ``configure_model`` hook: + +.. code-block:: python + + from torch.distributed.algorithms._checkpoint.checkpoint_wrapper import apply_activation_checkpointing + + + class MyModel(pl.LightningModule): + ... + + def configure_model(self): + # Same code as in the "Manual wrapping" snippet above + ... + apply_activation_checkpointing(self.model) + +In this case, Lightning will not re-configure activation checkpointing, so you don't need to set ``FSDPStrategy(activation_checkpointing=...)``. + + +---- + + +.. _deepspeed_advanced: + +********* +DeepSpeed +********* + +`DeepSpeed `__ is a deep learning training optimization library, providing the means to train massive billion parameter models at scale. +Using the DeepSpeed strategy, we were able to **train model sizes of 10 Billion parameters and above**, with a lot of useful information in this `benchmark `_ and the `DeepSpeed docs `__. +DeepSpeed also offers lower level training optimizations, and efficient optimizers such as `1-bit Adam `_. We recommend using DeepSpeed in environments where speed and memory optimizations are important (such as training large billion parameter models). + +.. warning:: This is an :ref:`experimental ` feature. + +Below is a summary of all the configurations of DeepSpeed. + +* :ref:`deepspeed-zero-stage-1` - **Shard optimizer states**, remains at speed parity with DDP whilst providing memory improvement + +* :ref:`deepspeed-zero-stage-2` - **Shard optimizer states and gradients**, remains at speed parity with DDP whilst providing even more memory improvement + +* :ref:`deepspeed-zero-stage-2-offload` - **Offload optimizer states and gradients to CPU**. Increases distributed communication volume and GPU-CPU device transfer, but provides significant memory improvement + +* :ref:`deepspeed-zero-stage-3` - **Shard optimizer states, gradients, parameters and optionally activations**. Increases distributed communication volume, but provides even more memory improvement + +* :ref:`deepspeed-zero-stage-3-offload` - **Offload optimizer states, gradients, parameters and optionally activations to CPU**. Increases distributed communication volume and GPU-CPU device transfer, but even more significant memory improvement. + +* :ref:`deepspeed-activation-checkpointing` - **Free activations after forward pass**. Increases computation, but provides memory improvement for all stages. + +To use DeepSpeed, you first need to install DeepSpeed using the commands below. + +.. code-block:: bash + + pip install deepspeed + +If you run into an issue with the install or later in training, ensure that the CUDA version of the PyTorch you've installed matches your locally installed CUDA (you can see which one has been recognized by running ``nvcc --version``). + +.. note:: + + DeepSpeed currently only supports single optimizer, single scheduler within the training loop. + + When saving a checkpoint we rely on DeepSpeed which saves a directory containing the model and various components. + + +.. _deepspeed-zero-stage-1: + +DeepSpeed ZeRO Stage 1 +====================== + +`DeepSpeed ZeRO Stage 1 `_ partitions your optimizer states (Stage 1) across your GPUs to reduce memory. + +It is recommended to skip Stage 1 and use Stage 2, which comes with larger memory improvements and still remains efficient. Stage 1 is useful to pair with certain optimizations such as `Torch ORT `__. + +.. code-block:: python + + from lightning.pytorch import Trainer + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_1", precision=16) + trainer.fit(model) + + +.. _deepspeed-zero-stage-2: + +DeepSpeed ZeRO Stage 2 +====================== + +`DeepSpeed ZeRO Stage 2 `_ partitions your optimizer states (Stage 1) and your gradients (Stage 2) across your GPUs to reduce memory. In most cases, this is more efficient or at parity with DDP, primarily due to the optimized custom communications written by the DeepSpeed team. +As a result, benefits can also be seen on a single GPU. Do note that the default bucket sizes allocate around ``3.6GB`` of VRAM to use during distributed communications, which can be tweaked when instantiating the strategy described in a few sections below. + +.. code-block:: python + + from lightning.pytorch import Trainer + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2", precision=16) + trainer.fit(model) + +.. code-block:: bash + + python train.py --strategy deepspeed_stage_2 --precision 16 --accelerator 'gpu' --devices 4 + + +.. _deepspeed-zero-stage-2-offload: + +DeepSpeed ZeRO Stage 2 Offload +------------------------------ + +Below we show an example of running `ZeRO-Offload `_. ZeRO-Offload leverages the host CPU to offload optimizer memory/computation, reducing the overall memory consumption. + +.. code-block:: python + + from lightning.pytorch import Trainer + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2_offload", precision=16) + trainer.fit(model) + + +This can also be done via the command line using a PyTorch Lightning script: + +.. code-block:: bash + + python train.py --strategy deepspeed_stage_2_offload --precision 16 --accelerator 'gpu' --devices 4 + + +You can also modify the ZeRO-Offload parameters via the strategy as below. + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DeepSpeedStrategy + + model = MyModel() + trainer = Trainer( + accelerator="gpu", + devices=4, + strategy=DeepSpeedStrategy(offload_optimizer=True, allgather_bucket_size=5e8, reduce_bucket_size=5e8), + precision=16, + ) + trainer.fit(model) + + +.. note:: + We suggest tuning the ``allgather_bucket_size`` parameter and ``reduce_bucket_size`` parameter to find optimum parameters based on your model size. + These control how large a buffer we limit the model to using when reducing gradients/gathering updated parameters. Smaller values will result in less memory, but tradeoff with speed. + + DeepSpeed allocates a reduce buffer size `multiplied by 1.5x `_ so take that into consideration when tweaking the parameters. + + The strategy sets a reasonable default of ``2e8``, which should work for most low VRAM GPUs (less than ``7GB``), allocating roughly ``3.6GB`` of VRAM as buffer. Higher VRAM GPUs should aim for values around ``5e8``. + +For even more speed benefit, DeepSpeed offers an optimized CPU version of ADAM called `DeepSpeedCPUAdam `_ to run the offloaded computation, which is faster than the standard PyTorch implementation. + +.. code-block:: python + + import lightning.pytorch + from lightning.pytorch import Trainer + from deepspeed.ops.adam import DeepSpeedCPUAdam + + + class MyModel(pl.LightningModule): + ... + + def configure_optimizers(self): + # DeepSpeedCPUAdam provides 5x to 7x speedup over torch.optim.adam(w) + return DeepSpeedCPUAdam(self.parameters()) + + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2_offload", precision=16) + trainer.fit(model) + + +.. _deepspeed-zero-stage-3: + +DeepSpeed ZeRO Stage 3 +====================== + +DeepSpeed ZeRO Stage 3 shards the optimizer states, gradients and the model parameters (also optionally activations). Sharding model parameters and activations comes with an increase in distributed communication, however allows you to scale your models massively from one GPU to multiple GPUs. +**The DeepSpeed team report the ability to fine-tune models with over 40B parameters on a single GPU and over 2 Trillion parameters on 512 GPUs.** For more information we suggest checking the `DeepSpeed ZeRO-3 Offload documentation `__. + +We've ran benchmarks for all these features and given a simple example of how all these features work in Lightning, which you can see at `minGPT `_. + +To reach the highest memory efficiency or model size, you must: + +1. Use the DeepSpeed strategy with the stage 3 parameter +2. Use CPU Offloading to offload weights to CPU, plus have a reasonable amount of CPU RAM to offload onto +3. Use DeepSpeed Activation Checkpointing to shard activations + +Below we describe how to enable all of these to see benefit. **With all these improvements we reached 45 Billion parameters training a GPT model on 8 GPUs with ~1TB of CPU RAM available**. + +Also please have a look at our :ref:`deepspeed-zero-stage-3-tips` which contains a lot of helpful information when configuring your own models. + +.. note:: + + When saving a model using DeepSpeed and Stage 3, model states and optimizer states will be saved in separate sharded states (based on the world size). See :ref:`deepspeed-zero-stage-3-single-file` to obtain a single checkpoint file. + +.. code-block:: python + + from lightning.pytorch import Trainer + from deepspeed.ops.adam import FusedAdam + + + class MyModel(pl.LightningModule): + ... + + def configure_optimizers(self): + return FusedAdam(self.parameters()) + + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) + trainer.fit(model) + + trainer.test() + trainer.predict() + + +You can also use the Lightning Trainer to run predict or evaluate with DeepSpeed once the model has been trained. + +.. code-block:: python + + from lightning.pytorch import Trainer + + + class MyModel(pl.LightningModule): + ... + + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) + trainer.test(ckpt_path="my_saved_deepspeed_checkpoint.ckpt") + + +Shard Model Instantly to Reduce Initialization Time/Memory +---------------------------------------------------------- + +When instantiating really large models, it is sometimes necessary to shard the model layers instantly. + +This is the case if layers may not fit on one single machines CPU or GPU memory, but would fit once sharded across multiple machines. +We expose a hook that layers initialized within the hook will be sharded instantly on a per layer basis, allowing you to instantly shard models. + +This reduces the time taken to initialize very large models, as well as ensure we do not run out of memory when instantiating larger models. For more information you can refer to the DeepSpeed docs for `Constructing Massive Models `_. + +.. code-block:: python + + import torch.nn as nn + from lightning.pytorch import Trainer + from deepspeed.ops.adam import FusedAdam + + + class MyModel(pl.LightningModule): + ... + + def configure_model(self): + # Created within sharded model context, modules are instantly sharded across processes + # as soon as they are made. + self.block = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) + + def configure_optimizers(self): + return FusedAdam(self.parameters()) + + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) + trainer.fit(model) + + trainer.test() + trainer.predict() + + +.. _deepspeed-zero-stage-3-offload: + +DeepSpeed ZeRO Stage 3 Offload +------------------------------ + +DeepSpeed ZeRO Stage 3 Offloads optimizer state, gradients to the host CPU to reduce memory usage as ZeRO Stage 2 does, however additionally allows you to offload the parameters as well for even more memory saving. + +.. note:: + + When saving a model using DeepSpeed and Stage 3, model states and optimizer states will be saved in separate sharded states (based on the world size). See :ref:`deepspeed-zero-stage-3-single-file` to obtain a single checkpoint file. + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DeepSpeedStrategy + + # Enable CPU Offloading + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) + trainer.fit(model) + + # Enable CPU Offloading, and offload parameters to CPU + model = MyModel() + trainer = Trainer( + accelerator="gpu", + devices=4, + strategy=DeepSpeedStrategy( + stage=3, + offload_optimizer=True, + offload_parameters=True, + ), + precision=16, + ) + trainer.fit(model) + + +DeepSpeed Infinity (NVMe Offloading) +------------------------------------ + +Additionally, DeepSpeed supports offloading to NVMe drives for even larger models, utilizing the large memory space found in NVMes. DeepSpeed `reports `__ the ability to fine-tune 1 Trillion+ parameters using NVMe Offloading on one 8 GPU machine. Below shows how to enable this, assuming the NVMe drive is mounted in a directory called ``/local_nvme``. + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DeepSpeedStrategy + + # Enable CPU Offloading + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) + trainer.fit(model) + + # Enable CPU Offloading, and offload parameters to CPU + model = MyModel() + trainer = Trainer( + accelerator="gpu", + devices=4, + strategy=DeepSpeedStrategy( + stage=3, + offload_optimizer=True, + offload_parameters=True, + remote_device="nvme", + offload_params_device="nvme", + offload_optimizer_device="nvme", + nvme_path="/local_nvme", + ), + precision=16, + ) + trainer.fit(model) + +When offloading to NVMe you may notice that the speed is slow. There are parameters that need to be tuned based on the drives that you are using. Running the `aio_bench_perf_sweep.py `__ script can help you to find optimum parameters. See the `issue `__ for more information on how to parse the information. + +.. _deepspeed-activation-checkpointing: + +DeepSpeed Activation Checkpointing +---------------------------------- + +Activation checkpointing frees activations from memory as soon as they are not needed during the forward pass. +They are then re-computed for the backwards pass as needed. + +Activation checkpointing is very useful when you have intermediate layers that produce large activations. + +This saves memory when training larger models, however requires using a checkpoint function to run modules as shown below. + +.. warning:: + + Ensure to not wrap the entire model with activation checkpointing. This is not the intended usage of activation checkpointing, and will lead to failures as seen in `this discussion `__. + +.. code-block:: python + + from lightning.pytorch import Trainer + import deepspeed + + + class MyModel(LightningModule): + ... + + def __init__(self): + super().__init__() + self.block_1 = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) + self.block_2 = torch.nn.Linear(32, 2) + + def forward(self, x): + # Use the DeepSpeed checkpointing function instead of calling the module directly + # checkpointing self.block_1 means the activations are deleted after use, + # and re-calculated during the backward passes + x = deepspeed.checkpointing.checkpoint(self.block_1, x) + return self.block_2(x) + + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DeepSpeedStrategy + import deepspeed + + + class MyModel(pl.LightningModule): + ... + + def configure_model(self): + self.block_1 = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) + self.block_2 = torch.nn.Linear(32, 2) + + def forward(self, x): + # Use the DeepSpeed checkpointing function instead of calling the module directly + x = deepspeed.checkpointing.checkpoint(self.block_1, x) + return self.block_2(x) + + + model = MyModel() + + trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) + + # Enable CPU Activation Checkpointing + trainer = Trainer( + accelerator="gpu", + devices=4, + strategy=DeepSpeedStrategy( + stage=3, + offload_optimizer=True, # Enable CPU Offloading + cpu_checkpointing=True, # (Optional) offload activations to CPU + ), + precision=16, + ) + trainer.fit(model) + + +.. _deepspeed-zero-stage-3-tips: + +DeepSpeed ZeRO Stage 3 Tips +--------------------------- + +Here is some helpful information when setting up DeepSpeed ZeRO Stage 3 with Lightning. + +* If you're using Adam or AdamW, ensure to use FusedAdam or DeepSpeedCPUAdam (for CPU Offloading) rather than the default torch optimizers as they come with large speed benefits +* Treat your GPU/CPU memory as one large pool. In some cases, you may not want to offload certain things (like activations) to provide even more space to offload model parameters +* When offloading to the CPU, make sure to bump up the batch size as GPU memory will be freed +* We also support sharded checkpointing. By passing ``save_full_weights=False`` to the ``DeepSpeedStrategy``, we'll save shards of the model which allows you to save extremely large models. However to load the model and run test/validation/predict you must use the Trainer object. + +.. _deepspeed-zero-stage-3-single-file: + +Collating Single File Checkpoint for DeepSpeed ZeRO Stage 3 +----------------------------------------------------------- + +After training using ZeRO Stage 3, you'll notice that your checkpoints are a directory of sharded model and optimizer states. If you'd like to collate a single file from the checkpoint directory please use the below command, which handles all the Lightning states additionally when collating the file. + +.. code-block:: python + + from lightning.pytorch.utilities.deepspeed import convert_zero_checkpoint_to_fp32_state_dict + + # lightning deepspeed has saved a directory instead of a file + save_path = "lightning_logs/version_0/checkpoints/epoch=0-step=0.ckpt/" + output_path = "lightning_model.pt" + convert_zero_checkpoint_to_fp32_state_dict(save_path, output_path) + + +.. warning:: + + This single file checkpoint does not include the optimizer/lr-scheduler states. This means we cannot restore training via the ``trainer.fit(ckpt_path=)`` call. Ensure to keep the sharded checkpoint directory if this is required. + +Custom DeepSpeed Config +======================= + +In some cases you may want to define your own DeepSpeed Config, to access all parameters defined. We've exposed most of the important parameters, however, there may be debugging parameters to enable. Also, DeepSpeed allows the use of custom DeepSpeed optimizers and schedulers defined within a config file that is supported. + +.. note:: + All strategy default parameters will be ignored when a config object is passed. + All compatible arguments can be seen in the `DeepSpeed docs `_. + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DeepSpeedStrategy + + deepspeed_config = { + "zero_allow_untested_optimizer": True, + "optimizer": { + "type": "OneBitAdam", + "params": { + "lr": 3e-5, + "betas": [0.998, 0.999], + "eps": 1e-5, + "weight_decay": 1e-9, + "cuda_aware": True, + }, + }, + "scheduler": { + "type": "WarmupLR", + "params": { + "last_batch_iteration": -1, + "warmup_min_lr": 0, + "warmup_max_lr": 3e-5, + "warmup_num_steps": 100, + }, + }, + "zero_optimization": { + "stage": 2, # Enable Stage 2 ZeRO (Optimizer/Gradient state partitioning) + "offload_optimizer": {"device": "cpu"}, # Enable Offloading optimizer state/calculation to the host CPU + "contiguous_gradients": True, # Reduce gradient fragmentation. + "overlap_comm": True, # Overlap reduce/backward operation of gradients for speed. + "allgather_bucket_size": 2e8, # Number of elements to all gather at once. + "reduce_bucket_size": 2e8, # Number of elements we reduce/allreduce at once. + }, + } + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy=DeepSpeedStrategy(config=deepspeed_config), precision=16) + trainer.fit(model) + + +We support taking the config as a json formatted file: + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DeepSpeedStrategy + + model = MyModel() + trainer = Trainer( + accelerator="gpu", devices=4, strategy=DeepSpeedStrategy(config="/path/to/deepspeed_config.json"), precision=16 + ) + trainer.fit(model) + + +You can use also use an environment variable via your PyTorch Lightning script: + +.. code-block:: bash + + PL_DEEPSPEED_CONFIG_PATH=/path/to/deepspeed_config.json python train.py --strategy deepspeed + +---------- + +.. _ddp-optimizations: + +***************** +DDP Optimizations +***************** + + +DDP Static Graph +================ + +`DDP static graph `__ assumes that your model +employs the same set of used/unused parameters in every iteration, so that it can deterministically know the flow of +training and apply special optimizations during runtime. + +.. note:: + DDP static graph support requires PyTorch>=1.11.0 + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DDPStrategy + + trainer = Trainer(devices=4, strategy=DDPStrategy(static_graph=True)) + + +When Using DDP on a Multi-node Cluster, Set NCCL Parameters +=========================================================== + +`NCCL `__ is the NVIDIA Collective Communications Library that is used by PyTorch to handle communication across nodes and GPUs. There are reported benefits in terms of speedups when adjusting NCCL parameters as seen in this `issue `__. In the issue, we see a 30% speed improvement when training the Transformer XLM-RoBERTa and a 15% improvement in training with Detectron2. + +NCCL parameters can be adjusted via environment variables. + +.. note:: + + AWS and GCP already set default values for these on their clusters. This is typically useful for custom cluster setups. + +* `NCCL_NSOCKS_PERTHREAD `__ +* `NCCL_SOCKET_NTHREADS `__ +* `NCCL_MIN_NCHANNELS `__ + +.. code-block:: bash + + export NCCL_NSOCKS_PERTHREAD=4 + export NCCL_SOCKET_NTHREADS=2 + + +Gradients as Bucket View +======================== + +Enabling ``gradient_as_bucket_view=True`` in the ``DDPStrategy`` will make gradients views point to different offsets of the ``allreduce`` communication buckets. See :class:`~torch.nn.parallel.DistributedDataParallel` for more information. + +This can reduce peak memory usage and throughput as saved memory will be equal to the total gradient memory + removes the need to copy gradients to the ``allreduce`` communication buckets. + +.. note:: + + When ``gradient_as_bucket_view=True`` you cannot call ``detach_()`` on gradients. If hitting such errors, please fix it by referring to the :meth:`~torch.optim.Optimizer.zero_grad` function in ``torch/optim/optimizer.py`` as a solution (`source `__). + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DDPStrategy + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy=DDPStrategy(gradient_as_bucket_view=True)) + trainer.fit(model) + + +DDP Communication Hooks +======================= + +DDP Communication hooks is an interface to control how gradients are communicated across workers, overriding the standard allreduce in DistributedDataParallel. This allows you to enable performance improving communication hooks when using multiple nodes. + +Enable `FP16 Compress Hook for multi-node throughput improvement `__: + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DDPStrategy + from torch.distributed.algorithms.ddp_comm_hooks import default_hooks as default + + model = MyModel() + trainer = Trainer(accelerator="gpu", devices=4, strategy=DDPStrategy(ddp_comm_hook=default.fp16_compress_hook)) + trainer.fit(model) + +Enable `PowerSGD for multi-node throughput improvement `__: + +.. note:: + + PowerSGD typically requires extra memory of the same size as the model’s gradients to enable error feedback, which can compensate for biased compressed communication and improve accuracy (`source `__). + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DDPStrategy + from torch.distributed.algorithms.ddp_comm_hooks import powerSGD_hook as powerSGD + + model = MyModel() + trainer = Trainer( + accelerator="gpu", + devices=4, + strategy=DDPStrategy( + ddp_comm_state=powerSGD.PowerSGDState( + process_group=None, + matrix_approximation_rank=1, + start_powerSGD_iter=5000, + ), + ddp_comm_hook=powerSGD.powerSGD_hook, + ), + ) + trainer.fit(model) + + +Combine hooks for accumulated benefit: + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DDPStrategy + from torch.distributed.algorithms.ddp_comm_hooks import ( + default_hooks as default, + powerSGD_hook as powerSGD, + ) + + model = MyModel() + trainer = Trainer( + accelerator="gpu", + devices=4, + strategy=DDPStrategy( + ddp_comm_state=powerSGD.PowerSGDState( + process_group=None, + matrix_approximation_rank=1, + start_powerSGD_iter=5000, + ), + ddp_comm_hook=powerSGD.powerSGD_hook, + ddp_comm_wrapper=default.fp16_compress_wrapper, + ), + ) + trainer.fit(model) + + +When using Post-localSGD, you must also pass ``model_averaging_period`` to allow for model parameter averaging: + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.strategies import DDPStrategy + from torch.distributed.algorithms.ddp_comm_hooks import post_localSGD_hook as post_localSGD + + model = MyModel() + trainer = Trainer( + accelerator="gpu", + devices=4, + strategy=DDPStrategy( + ddp_comm_state=post_localSGD.PostLocalSGDState( + process_group=None, + subgroup=None, + start_localSGD_iter=8, + ), + ddp_comm_hook=post_localSGD.post_localSGD_hook, + model_averaging_period=4, + ), + ) + trainer.fit(model) diff --git a/docs/source-pytorch/advanced/post_training_quantization.rst b/docs/source-pytorch/advanced/post_training_quantization.rst new file mode 100644 index 0000000..611014e --- /dev/null +++ b/docs/source-pytorch/advanced/post_training_quantization.rst @@ -0,0 +1,185 @@ +:orphan: + +.. _post_training_quantization: + +########################## +Post-training Quantization +########################## + +Most deep learning applications are using 32-bits of floating-point precision for inference. But low precision data types, especially INT8, are attracting more attention due to significant performance margin. One of the essential concerns of adopting low precision is how to easily mitigate the possible accuracy loss and reach predefined accuracy requirements. + +Intel® Neural Compressor, is an open-source Python library that runs on Intel CPUs and GPUs, which could address the aforementioned concern by extending the PyTorch Lightning model with accuracy-driven automatic quantization tuning strategies to help users quickly find out the best-quantized model on Intel hardware. It also supports multiple popular network compression technologies such as sparse, pruning, and knowledge distillation. + +**Audience** : Machine learning engineers optimizing models for a better model inference speed and lower memory usage. + +Visit the Intel® Neural Compressor online document website at: ``_. + +****************** +Model Quantization +****************** + +Model quantization is an efficient model optimization tool that can accelerate the model inference speed and decrease the memory load while still maintaining the model accuracy. + +Intel® Neural Compressor provides a convenient model quantization API to quantize the already-trained Lightning module with Post-training Quantization and Quantization Aware Training. This extension API exhibits the merits of an ease-of-use coding environment and multi-functional quantization options. The user can easily quantize their fine-tuned model by adding a few clauses to their original code. We only introduce post-training quantization in this document. + +There are two post-training quantization types in Intel® Neural Compressor, post-training static quantization and post-training dynamic quantization. Post-training dynamic quantization is a recommended starting point because it provides reduced memory usage and faster computation without additional calibration datasets. This type of quantization statically quantizes only the weights from floating point to integer at conversion time. This optimization provides latencies close to post-training static quantization. But the outputs of ops are still stored with the floating point, so the increased speed of dynamic-quantized ops is less than a static-quantized computation. + +Post-training static quantization saves the output of ops via INT8 bit. It can tackle the accuracy and latency loss caused by "quant" and "dequant" operations. For Post-training static quantization, the user needs to estimate the min-max range of all FP32 tensors in the model. Unlike constant tensors such as weights and biases, variable tensors such as model input, activations and model output cannot be calibrated unless the model run a few inference cycles. As a result, the converter requires a calibration dataset to estimate that range. This dataset can be a small subset (default 100 samples) of the training or the validation data. + +************ +Installation +************ + +Prerequisites +============= + +Python version: 3.8, 3.9, 3.10 + +Install Intel® Neural Compressor +================================ + +Release binary install: + +.. code-block:: bash + + # Install stable basic version from pip + pip install neural-compressor + # Or install stable full version from pip (including GUI) + pip install neural-compressor-full + +More installation methods can be found in the `Installation Guide `_. + +***** +Usage +***** + +Minor code changes are required for the user to get started with Intel® Neural Compressor quantization API. To construct the quantization process, users can specify the below settings via the Python code: + +1. Calibration Dataloader (Needed for post-training static quantization) +2. Evaluation Dataloader and Metric + +The code changes that are required for Intel® Neural Compressor are highlighted with comments in the line above. + +PyTorch Lightning model +======================= + +Load the pretrained model with PyTorch Lightning: + +.. code-block:: python + + import torch + from lightning.pytorch import LightningModule + from transformers import AutoConfig, AutoModelForSequenceClassification, AutoTokenizer + + + # BERT Model definition + class GLUETransformer(LightningModule): + def __init__(self): + self.config = AutoConfig.from_pretrained(model_name_or_path, num_labels=num_labels) + self.model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, config=self.config) + + def forward(self, **inputs): + return self.model(**inputs) + + + model = GLUETransformer(model_name_or_path="Intel/bert-base-uncased-mrpc") + +The fine-tuned model from Intel could be downloaded from `Intel Hugging Face repository `_. + +Accuracy-driven quantization config +=================================== + +Intel® Neural Compressor supports accuracy-driven automatic tuning to generate the optimal INT8 model which meets a predefined accuracy goal. The default tolerance of accuracy loss in the accuracy criterion is 0.01. And the maximum trial number of quantization is 600. The user can specifically define their own criteria by: + +.. code-block:: python + + from neural_compressor.config import PostTrainingQuantConfig, TuningCriterion, AccuracyCriterion + + accuracy_criterion = AccuracyCriterion(tolerable_loss=0.01) + tuning_criterion = TuningCriterion(max_trials=600) + conf = PostTrainingQuantConfig( + approach="static", backend="default", tuning_criterion=tuning_criterion, accuracy_criterion=accuracy_criterion + ) + +The "approach" parameter in PostTrainingQuantConfig is defined by the user to make a choice from post-training static quantization and post-training dynamic by "static" or "dynamic". + +Quantize the model +================== + +The model can be qutized by Intel® Neural Compressor with: + +.. code-block:: python + + from neural_compressor.quantization import fit + + q_model = fit(model=model.model, conf=conf, calib_dataloader=val_dataloader(), eval_func=eval_func) + +Users can define the evaluation function "eval_func" by themselves. + +At last, the quantized model can be saved by: + +.. code-block:: python + + q_model.save("./saved_model/") + +***************** +Hands-on Examples +***************** + +Based on the `given example code `_, we show how Intel Neural Compressor conduct model quantization on PyTorch Lightning. We first define the basic config of the quantization process. + +.. code-block:: python + + from neural_compressor.quantization import fit as fit + from neural_compressor.config import PostTrainingQuantConfig + + + def eval_func_for_nc(model_n, trainer_n): + setattr(model, "model", model_n) + result = trainer_n.validate(model=model, dataloaders=dm.val_dataloader()) + return result[0]["accuracy"] + + + def eval_func(model): + return eval_func_for_nc(model, trainer) + + + conf = PostTrainingQuantConfig() + q_model = fit(model=model.model, conf=conf, calib_dataloader=dm.val_dataloader(), eval_func=eval_func) + + q_model.save("./saved_model/") + +We define the evaluation function as: + +.. code-block:: python + + def eval_func_for_nc(model_n, trainer_n): + setattr(model, "model", model_n) + result = trainer_n.validate(model=model, dataloaders=dm.val_dataloader()) + return result[0]["accuracy"] + + + def eval_func(model): + return eval_func_for_nc(model, trainer) + +Following is the performance comparison between FP32 model and INT8 model: + + ++-------------+-----------------+------------------+ +| Info Type | Baseline FP32 | Quantized INT8 | ++=============+=================+==================+ +| Accuracy | 0.8603 | 0.8578 | ++-------------+-----------------+------------------+ +| Duration(s) | 5.8973 | 3.5952 | ++-------------+-----------------+------------------+ +| Memory(MB) | 417.73 | 113.28 | ++-------------+-----------------+------------------+ + + +For more model quantization performance, please refer to `our model list `_ + +***************** +Technical Support +***************** + +Welcome to visit Intel® Neural Compressor website at: https://intel.github.io/neural-compressor to find technical support or contribute your code. diff --git a/docs/source-pytorch/advanced/pretrained.rst b/docs/source-pytorch/advanced/pretrained.rst new file mode 100644 index 0000000..4809e6e --- /dev/null +++ b/docs/source-pytorch/advanced/pretrained.rst @@ -0,0 +1 @@ +.. include:: transfer_learning.rst diff --git a/docs/source-pytorch/advanced/pruning_quantization.rst b/docs/source-pytorch/advanced/pruning_quantization.rst new file mode 100644 index 0000000..f8b0996 --- /dev/null +++ b/docs/source-pytorch/advanced/pruning_quantization.rst @@ -0,0 +1,54 @@ +.. _pruning_quantization: + +######################## +Pruning and Quantization +######################## + +Pruning and Quantization are techniques to compress model size for deployment, allowing inference speed up and energy saving without significant accuracy losses. + +******* +Pruning +******* + +.. warning:: This is an :ref:`experimental ` feature. + +Pruning is a technique which focuses on eliminating some of the model weights to reduce the model size and decrease inference requirements. + +Pruning has been shown to achieve significant efficiency improvements while minimizing the drop in model performance (prediction quality). Model pruning is recommended for cloud endpoints, deploying models on edge devices, or mobile inference (among others). + +To enable pruning during training in Lightning, simply pass in the :class:`~lightning.pytorch.callbacks.ModelPruning` callback to the Lightning Trainer. PyTorch's native pruning implementation is used under the hood. + +This callback supports multiple pruning functions: pass any `torch.nn.utils.prune `_ function as a string to select which weights to prune (`random_unstructured `_, `RandomStructured `_, etc) or implement your own by subclassing `BasePruningMethod `_. + +.. code-block:: python + + from lightning.pytorch.callbacks import ModelPruning + + # set the amount to be the fraction of parameters to prune + trainer = Trainer(callbacks=[ModelPruning("l1_unstructured", amount=0.5)]) + +You can also perform iterative pruning, apply the `lottery ticket hypothesis `__, and more! + +.. code-block:: python + + def compute_amount(epoch): + # the sum of all returned values need to be smaller than 1 + if epoch == 10: + return 0.5 + + elif epoch == 50: + return 0.25 + + elif 75 < epoch < 99: + return 0.01 + + + # the amount can be also be a callable + trainer = Trainer(callbacks=[ModelPruning("l1_unstructured", amount=compute_amount)]) + + + +Post-training Quantization +========================== + +If you want to quantize a fine-tuned model with PTQ, it is recommended to adopt a third party API names Intel® Neural Compressor, read more :doc:`here <./post_training_quantization>`, which provides a convenient tool for accelerating the model inference speed on Intel CPUs and GPUs. diff --git a/source/guides/speed.rst b/docs/source-pytorch/advanced/speed.rst similarity index 90% rename from source/guides/speed.rst rename to docs/source-pytorch/advanced/speed.rst index 1020755..1cbbf86 100644 --- a/source/guides/speed.rst +++ b/docs/source-pytorch/advanced/speed.rst @@ -2,7 +2,7 @@ .. testsetup:: * - from pytorch_lightning.callbacks.early_stopping import EarlyStopping + from lightning.pytorch.callbacks.early_stopping import EarlyStopping .. _training-speedup: @@ -27,9 +27,9 @@ GPU Training Lightning supports a variety of plugins to speed up distributed GPU training. Most notably: -* :class:`~pytorch_lightning.strategies.DDPStrategy` -* :class:`~pytorch_lightning.strategies.DDPShardedStrategy` -* :class:`~pytorch_lightning.strategies.DeepSpeedStrategy` +* :class:`~lightning.pytorch.strategies.DDPStrategy` +* :class:`~lightning.pytorch.strategies.FSDPStrategy` +* :class:`~lightning.pytorch.strategies.DeepSpeedStrategy` .. code-block:: python @@ -49,27 +49,14 @@ GPU Training Speedup Tips When training on single or multiple GPU machines, Lightning offers a host of advanced optimizations to improve throughput, memory efficiency, and model scaling. Refer to :doc:`Advanced GPU Optimized Training for more details <../advanced/model_parallel>`. -Prefer DDP Over DP -^^^^^^^^^^^^^^^^^^ -:class:`~pytorch_lightning.strategies.dp.DataParallelStrategy` performs three GPU transfers for EVERY batch: - -1. Copy the model to the device. -2. Copy the data to the device. -3. Copy the outputs of each device back to the main device. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/distributed_training/dp.gif - :alt: Animation showing DP execution. - :width: 500 - :align: center - | -Whereas :class:`~pytorch_lightning.strategies.ddp.DDPStrategy` only performs two transfer operations, making DDP much faster than DP: +:class:`~lightning.pytorch.strategies.ddp.DDPStrategy` only performs two transfer operations for each step, making it the simplest distributed training strategy: 1. Moving data to the device. 2. Transfer and sync gradients. -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/distributed_training/ddp.gif +.. image:: ../_static/fetched-s3-assets/ddp.gif :alt: Animation showing DDP execution. :width: 500 :align: center @@ -110,7 +97,7 @@ For debugging purposes or for dataloaders that load very small datasets, it is d warnings.filterwarnings("ignore", ".*Consider increasing the value of the `num_workers` argument*") # or to ignore all warnings that could be false positives - from pytorch_lightning.utilities.warnings import PossibleUserWarning + from lightning.pytorch.utilities.warnings import PossibleUserWarning warnings.filterwarnings("ignore", category=PossibleUserWarning) @@ -174,7 +161,7 @@ Early Stopping ************** Usually, long training epochs can lead to either overfitting or no major improvements in your metrics due to no limited convergence. -Here :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback can help you stop the training entirely by monitoring a metric of your choice. +Here :class:`~lightning.pytorch.callbacks.early_stopping.EarlyStopping` callback can help you stop the training entirely by monitoring a metric of your choice. You can read more about it :ref:`here `. @@ -196,11 +183,9 @@ less memory bandwidth and run match operations much faster on GPUs that support * Your optimization algorithm (training_step) is numerically stable. * You want to be the cool person in the lab :p -.. raw:: html - - +.. video:: ../_static/fetched-s3-assets/Trainer+flags+9+-+precision_1.mp4 + :width: 400 + :poster: ../_static/fetched-s3-assets/thumb_precision.png | @@ -352,8 +337,8 @@ Here is an explanation of what it does: When performing gradient accumulation, there is no need to perform grad synchronization during the accumulation phase. Setting ``sync_grad`` to ``False`` will block this synchronization and improve your training speed. -:class:`~pytorch_lightning.core.optimizer.LightningOptimizer` provides a -:meth:`~pytorch_lightning.core.optimizer.LightningOptimizer.toggle_model` function as a +:class:`~lightning.pytorch.core.optimizer.LightningOptimizer` provides a +:meth:`~lightning.pytorch.core.optimizer.LightningOptimizer.toggle_model` function as a :func:`contextlib.contextmanager` for advanced users. Here is an example of an advanced use case: @@ -422,15 +407,16 @@ Here is an example of an advanced use case: Set Grads to None ***************** -In order to improve performance, you can override :meth:`~pytorch_lightning.core.lightning.LightningModule.optimizer_zero_grad`. +In order to improve performance, you can override :meth:`~lightning.pytorch.core.module.LightningModule.optimizer_zero_grad`. For a more detailed explanation of the pros / cons of this technique, read the documentation for :meth:`~torch.optim.Optimizer.zero_grad` by the PyTorch team. +This is enabled by default on ``torch>=2.0.0``. .. testcode:: class Model(LightningModule): - def optimizer_zero_grad(self, epoch, batch_idx, optimizer, optimizer_idx): + def optimizer_zero_grad(self, epoch, batch_idx, optimizer): optimizer.zero_grad(set_to_none=True) diff --git a/source/advanced/strategy_registry.rst b/docs/source-pytorch/advanced/strategy_registry.rst similarity index 78% rename from source/advanced/strategy_registry.rst rename to docs/source-pytorch/advanced/strategy_registry.rst index d92069a..1a66890 100644 --- a/source/advanced/strategy_registry.rst +++ b/docs/source-pytorch/advanced/strategy_registry.rst @@ -1,8 +1,6 @@ Strategy Registry ================= -.. warning:: The Strategy Registry is experimental and subject to change. - Lightning includes a registry that holds information about Training strategies and allows for the registration of new custom strategies. The Strategies are assigned strings that identify them, such as "ddp", "deepspeed_stage_2_offload", and so on. @@ -11,21 +9,21 @@ It also returns the optional description and parameters for initialising the Str .. code-block:: python - # Training with the DDP Strategy with `find_unused_parameters` as False - trainer = Trainer(strategy="ddp_find_unused_parameters_false", accelerator="gpu", devices=4) + # Training with the DDP Strategy + trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4) # Training with DeepSpeed ZeRO Stage 3 and CPU Offload trainer = Trainer(strategy="deepspeed_stage_3_offload", accelerator="gpu", devices=3) # Training with the TPU Spawn Strategy with `debug` as True - trainer = Trainer(strategy="tpu_spawn_debug", accelerator="tpu", devices=8) + trainer = Trainer(strategy="xla_debug", accelerator="tpu", devices=8) Additionally, you can pass your custom registered training strategies to the ``strategy`` argument. .. code-block:: python - from pytorch_lightning.strategies import DDPStrategy, StrategyRegistry, CheckpointIO + from lightning.pytorch.strategies import DDPStrategy, StrategyRegistry, CheckpointIO class CustomCheckpointIO(CheckpointIO): diff --git a/docs/source-pytorch/advanced/training_tricks.rst b/docs/source-pytorch/advanced/training_tricks.rst new file mode 100644 index 0000000..a5c3bfc --- /dev/null +++ b/docs/source-pytorch/advanced/training_tricks.rst @@ -0,0 +1,438 @@ +.. testsetup:: * + + from lightning.pytorch.callbacks import StochasticWeightAveraging + +.. _training_tricks: + +############################# +Effective Training Techniques +############################# + +Lightning implements various techniques to help during training that can help make the training smoother. + +---------- + +******************** +Accumulate Gradients +******************** + +.. include:: ../common/gradient_accumulation.rst + +---------- + +***************** +Gradient Clipping +***************** + +Gradient clipping can be enabled to avoid exploding gradients. By default, this will clip the gradient norm by calling +:func:`torch.nn.utils.clip_grad_norm_` computed over all model parameters together. +If the Trainer's ``gradient_clip_algorithm`` is set to ``'value'`` (``'norm'`` by default), this will use instead +:func:`torch.nn.utils.clip_grad_value_` for each parameter instead. + +.. note:: + If using mixed precision, the ``gradient_clip_val`` does not need to be changed as the gradients are unscaled + before applying the clipping function. + +.. seealso:: :class:`~lightning.pytorch.trainer.trainer.Trainer` + +.. testcode:: + + # DEFAULT (ie: don't clip) + trainer = Trainer(gradient_clip_val=0) + + # clip gradients' global norm to <=0.5 using gradient_clip_algorithm='norm' by default + trainer = Trainer(gradient_clip_val=0.5) + + # clip gradients' maximum magnitude to <=0.5 + trainer = Trainer(gradient_clip_val=0.5, gradient_clip_algorithm="value") + +Read more about :ref:`Configuring Gradient Clipping ` for advanced use-cases. + +---------- + +*************************** +Stochastic Weight Averaging +*************************** + +Stochastic Weight Averaging (SWA) can make your models generalize better at virtually no additional cost. +This can be used with both non-trained and trained models. The SWA procedure smooths the loss landscape thus making +it harder to end up in a local minimum during optimization. + +For a more detailed explanation of SWA and how it works, +read `this post `__ by the PyTorch team. + +.. seealso:: The :class:`~lightning.pytorch.callbacks.StochasticWeightAveraging` callback + +.. testcode:: + + # Enable Stochastic Weight Averaging using the callback + trainer = Trainer(callbacks=[StochasticWeightAveraging(swa_lrs=1e-2)]) + +---------- + +.. _batch_size_finder: + +***************** +Batch Size Finder +***************** + +Auto-scaling of batch size can be enabled to find the largest batch size that fits into +memory. Large batch size often yields a better estimation of the gradients, but may also result in +longer training time. Inspired by https://github.com/BlackHC/toma. + +.. seealso:: :class:`~lightning.pytorch.tuner.tuning.Tuner` + +.. code-block:: python + + from lightning.pytorch.tuner import Tuner + + # Create a tuner for the trainer + trainer = Trainer(...) + tuner = Tuner(trainer) + + # Auto-scale batch size by growing it exponentially (default) + tuner.scale_batch_size(model, mode="power") + + # Auto-scale batch size with binary search + tuner.scale_batch_size(model, mode="binsearch") + + # Fit as normal with new batch size + trainer.fit(model) + + +Currently, this feature supports two modes ``'power'`` scaling and ``'binsearch'`` +scaling. In ``'power'`` scaling, starting from a batch size of 1 keeps doubling +the batch size until an out-of-memory (OOM) error is encountered. Setting the +argument to ``'binsearch'`` will initially also try doubling the batch size until +it encounters an OOM, after which it will do a binary search that will finetune the +batch size. Additionally, it should be noted that the batch size scaler cannot +search for batch sizes larger than the size of the training dataset. + +.. note:: + + This feature expects that a ``batch_size`` field is either located as a model attribute + i.e. ``model.batch_size`` or as a field in your ``hparams`` i.e. ``model.hparams.batch_size``. + Similarly it can work with datamodules too. The field should exist and will be updated by + the results of this algorithm. Additionally, your ``train_dataloader()`` method should depend + on this field for this feature to work i.e. + + .. code-block:: python + + # using LightningModule + class LitModel(LightningModule): + def __init__(self, batch_size): + super().__init__() + self.save_hyperparameters() + # or + self.batch_size = batch_size + + def train_dataloader(self): + return DataLoader(train_dataset, batch_size=self.batch_size | self.hparams.batch_size) + + + model = LitModel(batch_size=32) + trainer = Trainer(...) + tuner = Tuner(trainer) + tuner.scale_batch_size(model) + + + # using LightningDataModule + class LitDataModule(LightningDataModule): + def __init__(self, batch_size): + super().__init__() + self.save_hyperparameters() + # or + self.batch_size = batch_size + + def train_dataloader(self): + return DataLoader(train_dataset, batch_size=self.batch_size | self.hparams.batch_size) + + + model = MyModel() + datamodule = LitDataModule(batch_size=32) + + trainer = Trainer(...) + tuner = Tuner(trainer) + tuner.scale_batch_size(model, datamodule=datamodule) + + Note that the ``train_dataloader`` can be either part of + the ``LightningModule`` or ``LightningDataModule`` + as shown above. If both the ``LightningModule`` + and the ``LightningDataModule`` contain a ``train_dataloader``, + the ``LightningDataModule`` takes precedence. + +The algorithm in short works by: + 1. Dumping the current state of the model and trainer + 2. Iteratively until convergence or maximum number of tries ``max_trials`` (default 25) has been reached: + - Call ``fit()`` method of trainer. This evaluates ``steps_per_trial`` (default 3) number of + optimization steps. Each training step can trigger an OOM error if the tensors + (training batch, weights, gradients, etc.) allocated during the steps have a + too large memory footprint. + - If an OOM error is encountered, decrease batch size else increase it. + How much the batch size is increased/decreased is determined by the chosen + strategy. + 3. The found batch size is saved to either ``model.batch_size`` or ``model.hparams.batch_size`` + 4. Restore the initial state of model and trainer + +.. warning:: Batch size finder is not yet supported for DDP or any of its variations, it is coming soon. + + +Customizing Batch Size Finder +============================= + +.. warning:: This is an :ref:`experimental ` feature. + +1. You can also customize the :class:`~lightning.pytorch.callbacks.batch_size_finder.BatchSizeFinder` callback to run + at different epochs. This feature is useful while fine-tuning models since you can't always use the same batch size after + unfreezing the backbone. + +.. code-block:: python + + from lightning.pytorch.callbacks import BatchSizeFinder + + + class FineTuneBatchSizeFinder(BatchSizeFinder): + def __init__(self, milestones, *args, **kwargs): + super().__init__(*args, **kwargs) + self.milestones = milestones + + def on_fit_start(self, *args, **kwargs): + return + + def on_train_epoch_start(self, trainer, pl_module): + if trainer.current_epoch in self.milestones or trainer.current_epoch == 0: + self.scale_batch_size(trainer, pl_module) + + + trainer = Trainer(callbacks=[FineTuneBatchSizeFinder(milestones=(5, 10))]) + trainer.fit(...) + + +2. Run batch size finder for ``validate``/``test``/``predict``. + +.. code-block:: python + + from lightning.pytorch.callbacks import BatchSizeFinder + + + class EvalBatchSizeFinder(BatchSizeFinder): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def on_fit_start(self, *args, **kwargs): + return + + def on_test_start(self, trainer, pl_module): + self.scale_batch_size(trainer, pl_module) + + + trainer = Trainer(callbacks=[EvalBatchSizeFinder()]) + trainer.test(...) + + +---------- + +.. _learning_rate_finder: + +******************** +Learning Rate Finder +******************** + +For training deep neural networks, selecting a good learning rate is essential +for both better performance and faster convergence. Even optimizers such as +:class:`~torch.optim.Adam` that are self-adjusting the learning rate can benefit from more optimal +choices. + +To reduce the amount of guesswork concerning choosing a good initial learning +rate, a `learning rate finder` can be used. As described in `this paper `_ +a learning rate finder does a small run where the learning rate is increased +after each processed batch and the corresponding loss is logged. The result of +this is a ``lr`` vs. ``loss`` plot that can be used as guidance for choosing an optimal +initial learning rate. + +.. warning:: + + For the moment, this feature only works with models having a single optimizer. + + +.. note:: + + With DDP: Since all the processes run in isolation, only process with ``global_rank=0`` will make the decision to stop the + learning rate finder and broadcast its results to all other ranks. That means, at the end of LR finder, each process will be running with + the learning rate found on ``global_rank=0``. + + +Using Lightning's built-in LR finder +==================================== + +To enable the learning rate finder, your :doc:`lightning module <../common/lightning_module>` needs to +have a ``learning_rate`` or ``lr`` attribute (or as a field in your ``hparams`` i.e. +``hparams.learning_rate`` or ``hparams.lr``). Then, create the :class:`~lightning.pytorch.tuner.tuning.Tuner` via ``tuner = Tuner(trainer)`` +and call ``tuner.lr_find(model)`` to run the LR finder. +The suggested ``learning_rate`` will be written to the console and will be automatically +set to your :doc:`lightning module <../common/lightning_module>`, which can be accessed +via ``self.learning_rate`` or ``self.lr``. + +.. code-block:: python + + from lightning.pytorch.tuner import Tuner + + + class LitModel(LightningModule): + def __init__(self, learning_rate): + super().__init__() + self.learning_rate = learning_rate + self.model = Model(...) + + def configure_optimizers(self): + return Adam(self.parameters(), lr=(self.lr or self.learning_rate)) + + + model = LitModel() + trainer = Trainer(...) + + # Create a Tuner + tuner = Tuner(trainer) + + # finds learning rate automatically + # sets hparams.lr or hparams.learning_rate to that learning rate + tuner.lr_find(model) + + +If your model is using an arbitrary value instead of ``self.lr`` or ``self.learning_rate``, set that value in ``lr_find``: + +.. code-block:: python + + model = LitModel() + trainer = Trainer(...) + tuner = Tuner(trainer) + + # to set to your own hparams.my_value + tuner.lr_find(model, attr_name="my_value") + + +You can also inspect the results of the learning rate finder or just play around +with the parameters of the algorithm. A typical example of this would look like: + +.. code-block:: python + + model = MyModelClass(hparams) + trainer = Trainer() + tuner = Tuner(trainer) + + # Run learning rate finder + lr_finder = tuner.lr_find(model) + + # Results can be found in + print(lr_finder.results) + + # Plot with + fig = lr_finder.plot(suggest=True) + fig.show() + + # Pick point based on plot, or get suggestion + new_lr = lr_finder.suggestion() + + # update hparams of the model + model.hparams.lr = new_lr + + # Fit model + trainer.fit(model) + +The figure produced by ``lr_finder.plot()`` should look something like the figure +below. It is recommended to not pick the learning rate that achieves the lowest +loss, but instead something in the middle of the sharpest downward slope (red point). +This is the point returned py ``lr_finder.suggestion()``. + + +Customizing Learning Rate Finder +================================ + +.. warning:: This is an :ref:`experimental ` feature. + +You can also customize the :class:`~lightning.pytorch.callbacks.lr_finder.LearningRateFinder` callback to run at different epochs. This feature is useful while fine-tuning models. + +.. code-block:: python + + from lightning.pytorch.callbacks import LearningRateFinder + + + class FineTuneLearningRateFinder(LearningRateFinder): + def __init__(self, milestones, *args, **kwargs): + super().__init__(*args, **kwargs) + self.milestones = milestones + + def on_fit_start(self, *args, **kwargs): + return + + def on_train_epoch_start(self, trainer, pl_module): + if trainer.current_epoch in self.milestones or trainer.current_epoch == 0: + self.lr_find(trainer, pl_module) + + + trainer = Trainer(callbacks=[FineTuneLearningRateFinder(milestones=(5, 10))]) + trainer.fit(...) + + +.. figure:: ../_static/images/trainer/lr_finder.png + +---------- + +************************** +Advanced GPU Optimizations +************************** + +When training on single or multiple GPU machines, Lightning offers a host of advanced optimizations to improve throughput, memory efficiency, and model scaling. +Refer to :doc:`Advanced GPU Optimized Training <../advanced/model_parallel>` for more details. + +---------- + + +.. _ddp_spawn_shared_memory: + +****************************************** +Sharing Datasets Across Process Boundaries +****************************************** + +The :class:`~lightning.pytorch.core.datamodule.LightningDataModule` class provides an organized way to decouple data loading from training logic, with :meth:`~lightning.pytorch.core.hooks.DataHooks.prepare_data` being used for downloading and pre-processing the dataset on a single process, and :meth:`~lightning.pytorch.core.hooks.DataHooks.setup` loading the pre-processed data for each process individually: + +.. code-block:: python + + class MNISTDataModule(pl.LightningDataModule): + def prepare_data(self): + MNIST(self.data_dir, download=True) + + def setup(self, stage: str): + self.mnist = MNIST(self.data_dir) + + def train_loader(self): + return DataLoader(self.mnist, batch_size=128) + +However, for in-memory datasets, that means that each process will hold a (redundant) replica of the dataset in memory, which may be impractical when using many processes while utilizing datasets that nearly fit into CPU memory, as the memory consumption will scale up linearly with the number of processes. +For example, when training Graph Neural Networks, a common strategy is to load the entire graph into CPU memory for fast access to the entire graph structure and its features, and to then perform neighbor sampling to obtain mini-batches that fit onto the GPU. + +A simple way to prevent redundant dataset replicas is to rely on :obj:`torch.multiprocessing` to share the `data automatically between spawned processes via shared memory `_. +For this, all data pre-loading should be done on the main process inside :meth:`DataModule.__init__`. As a result, all tensor-data will get automatically shared when using the ``'ddp_spawn'`` strategy. + +.. warning:: + + :obj:`torch.multiprocessing` will send a handle of each individual tensor to other processes. + In order to prevent any errors due to too many open file handles, try to reduce the number of tensors to share, *e.g.*, by stacking your data into a single tensor. + +.. code-block:: python + + class MNISTDataModule(pl.LightningDataModule): + def __init__(self, data_dir: str): + self.mnist = MNIST(data_dir, download=True, transform=T.ToTensor()) + + def train_loader(self): + return DataLoader(self.mnist, batch_size=128) + + + model = Model(...) + datamodule = MNISTDataModule("data/MNIST") + + trainer = Trainer(accelerator="gpu", devices=2, strategy="ddp_spawn") + trainer.fit(model, datamodule) + +See the `graph-level `_ and `node-level `_ prediction examples in PyTorch Geometric for practical use-cases. diff --git a/source/advanced/transfer_learning.rst b/docs/source-pytorch/advanced/transfer_learning.rst similarity index 98% rename from source/advanced/transfer_learning.rst rename to docs/source-pytorch/advanced/transfer_learning.rst index caa739b..102555b 100644 --- a/source/advanced/transfer_learning.rst +++ b/docs/source-pytorch/advanced/transfer_learning.rst @@ -62,7 +62,7 @@ Example: Imagenet (Computer Vision) super().__init__() # init a pretrained resnet - backbone = models.resnet50(pretrained=True) + backbone = models.resnet50(weights="DEFAULT") num_filters = backbone.fc.in_features layers = list(backbone.children())[:-1] self.feature_extractor = nn.Sequential(*layers) @@ -120,7 +120,6 @@ Here's a model that uses `Huggingface transformers `__. + + +---- + +Find performance bottlenecks +============================= + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Find bottlenecks in your models + :description: Benchmark your own Lightning models + :button_link: ../tuning/profiler.html + :col_css: col-md-3 + :height: 180 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/cli/lightning_cli.rst b/docs/source-pytorch/cli/lightning_cli.rst new file mode 100644 index 0000000..e3220a6 --- /dev/null +++ b/docs/source-pytorch/cli/lightning_cli.rst @@ -0,0 +1,121 @@ +:orphan: + +.. _lightning-cli: + +###################################### +Configure hyperparameters from the CLI +###################################### + +************* +Why use a CLI +************* + +When running deep learning experiments, there are a couple of good practices that are recommended to follow: + +- Separate configuration from source code +- Guarantee reproducibility of experiments + +Implementing a command line interface (CLI) makes it possible to execute an experiment from a shell terminal. By having +a CLI, there is a clear separation between the Python source code and what hyperparameters are used for a particular +experiment. If the CLI corresponds to a stable version of the code, reproducing an experiment can be achieved by +installing the same version of the code plus dependencies and running with the same configuration (CLI arguments). + +---- + +********* +Basic use +********* + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1: Control it all from the CLI + :description: Learn to control a LightningModule and LightningDataModule from the CLI + :col_css: col-md-4 + :button_link: lightning_cli_intermediate.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: 2: Mix models, datasets and optimizers + :description: Support multiple models, datasets, optimizers and learning rate schedulers + :col_css: col-md-4 + :button_link: lightning_cli_intermediate_2.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: 3: Control it all via YAML + :description: Enable composable YAMLs + :col_css: col-md-4 + :button_link: lightning_cli_advanced.html + :height: 150 + :tag: advanced + +.. raw:: html + +
+
+ +---- + +************ +Advanced use +************ + +.. raw:: html + +
+
+ +.. displayitem:: + :header: YAML for production + :description: Use the Lightning CLI with YAMLs for production environments + :col_css: col-md-4 + :button_link: lightning_cli_advanced_2.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Customize for complex projects + :description: Learn how to implement CLIs for complex projects + :col_css: col-md-4 + :button_link: lightning_cli_advanced_3.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Extend the Lightning CLI + :description: Customize the Lightning CLI + :col_css: col-md-4 + :button_link: lightning_cli_expert.html + :height: 150 + :tag: expert + +---- + +************* +Miscellaneous +************* + +.. raw:: html + +
+
+ +.. displayitem:: + :header: FAQ + :description: Frequently asked questions about working with the Lightning CLI and YAML files + :col_css: col-md-6 + :button_link: lightning_cli_faq.html + :height: 150 + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/cli/lightning_cli_advanced.rst b/docs/source-pytorch/cli/lightning_cli_advanced.rst new file mode 100644 index 0000000..e64b961 --- /dev/null +++ b/docs/source-pytorch/cli/lightning_cli_advanced.rst @@ -0,0 +1,259 @@ +:orphan: + +################################################# +Configure hyperparameters from the CLI (Advanced) +################################################# +**Audience:** Users looking to modularize their code for a professional project. + +**Pre-reqs:** You must have read :doc:`(Mix models and datasets) `. + +As a project becomes more complex, the number of configurable options becomes very large, making it inconvenient to +control through individual command line arguments. To address this, CLIs implemented using +:class:`~lightning.pytorch.cli.LightningCLI` always support receiving input from configuration files. The default format +used for config files is YAML. + +.. tip:: + + If you are unfamiliar with YAML, it is recommended that you first read :ref:`what-is-a-yaml-config-file`. + + +---- + +*********************** +Run using a config file +*********************** +To run the CLI using a yaml config, do: + +.. code:: bash + + python main.py fit --config config.yaml + +Individual arguments can be given to override options in the config file: + +.. code:: bash + + python main.py fit --config config.yaml --trainer.max_epochs 100 + +---- + +************************ +Automatic save of config +************************ + +To ease experiment reporting and reproducibility, by default ``LightningCLI`` automatically saves the full YAML +configuration in the log directory. After multiple fit runs with different hyperparameters, each one will have in its +respective log directory a ``config.yaml`` file. These files can be used to trivially reproduce an experiment, e.g.: + +.. code:: bash + + python main.py fit --config lightning_logs/version_7/config.yaml + +The automatic saving of the config is done by the special callback :class:`~lightning.pytorch.cli.SaveConfigCallback`. +This callback is automatically added to the ``Trainer``. To disable the save of the config, instantiate ``LightningCLI`` +with ``save_config_callback=None``. + +.. tip:: + + To change the file name of the saved configs to e.g. ``name.yaml``, do: + + .. code:: python + + cli = LightningCLI(..., save_config_kwargs={"config_filename": "name.yaml"}) + +It is also possible to extend the :class:`~lightning.pytorch.cli.SaveConfigCallback` class, for instance to additionally +save the config in a logger. An example of this is: + + .. code:: python + + class LoggerSaveConfigCallback(SaveConfigCallback): + def save_config(self, trainer: Trainer, pl_module: LightningModule, stage: str) -> None: + if isinstance(trainer.logger, Logger): + config = self.parser.dump(self.config, skip_none=False) # Required for proper reproducibility + trainer.logger.log_hyperparams({"config": config}) + + + cli = LightningCLI(..., save_config_callback=LoggerSaveConfigCallback) + +.. tip:: + + If you want to disable the standard behavior of saving the config to the ``log_dir``, then you can either implement + ``__init__`` and call ``super().__init__(*args, save_to_log_dir=False, **kwargs)`` or instantiate the + ``LightningCLI`` as: + + .. code:: python + + cli = LightningCLI(..., save_config_kwargs={"save_to_log_dir": False}) + +.. note:: + + The ``save_config``method is only called on rank zero. This allows to implement a custom save config without having + to worry about ranks or race conditions. Since it only runs on rank zero, any collective call will make the process + hang waiting for a broadcast. If you need to make collective calls, implement the ``setup`` method instead. + + +---- + +********************************* +Prepare a config file for the CLI +********************************* +The ``--help`` option of the CLIs can be used to learn which configuration options are available and how to use them. +However, writing a config from scratch can be time-consuming and error-prone. To alleviate this, the CLIs have the +``--print_config`` argument, which prints to stdout the configuration without running the command. + +For a CLI implemented as ``LightningCLI(DemoModel, BoringDataModule)``, executing: + +.. code:: bash + + python main.py fit --print_config + +generates a config with all default values like the following: + +.. code:: bash + + seed_everything: null + trainer: + logger: true + ... + model: + out_dim: 10 + learning_rate: 0.02 + data: + data_dir: ./ + ckpt_path: null + +Other command line arguments can be given and considered in the printed configuration. A use case for this is CLIs that +accept multiple models. By default, no model is selected, meaning the printed config will not include model settings. To +get a config with the default values of a particular model would be: + +.. code:: bash + + python main.py fit --model DemoModel --print_config + +which generates a config like: + +.. code:: bash + + seed_everything: null + trainer: + ... + model: + class_path: lightning.pytorch.demos.boring_classes.DemoModel + init_args: + out_dim: 10 + learning_rate: 0.02 + ckpt_path: null + +.. tip:: + + A standard procedure to run experiments can be: + + .. code:: bash + + # Print a configuration to have as reference + python main.py fit --print_config > config.yaml + # Modify the config to your liking - you can remove all default arguments + nano config.yaml + # Fit your model using the edited configuration + python main.py fit --config config.yaml + +Configuration items can be either simple Python objects such as int and str, +or complex objects comprised of a ``class_path`` and ``init_args`` arguments. The ``class_path`` refers +to the complete import path of the item class, while ``init_args`` are the arguments to be passed +to the class constructor. For example, your model is defined as: + +.. code:: python + + # model.py + class MyModel(pl.LightningModule): + def __init__(self, criterion: torch.nn.Module): + self.criterion = criterion + +Then the config would be: + +.. code:: yaml + + model: + class_path: model.MyModel + init_args: + criterion: + class_path: torch.nn.CrossEntropyLoss + init_args: + reduction: mean + ... + +``LightningCLI`` uses `jsonargparse `_ under the hood for parsing +configuration files and automatic creation of objects, so you don't need to do it yourself. + +.. note:: + + Lighting automatically registers all subclasses of :class:`~lightning.pytorch.core.module.LightningModule`, + so the complete import path is not required for them and can be replaced by the class name. + +.. note:: + + Parsers make a best effort to determine the correct names and types that the parser should accept. + However, there can be cases not yet supported or cases for which it would be impossible to support. + To somewhat overcome these limitations, there is a special key ``dict_kwargs`` that can be used + to provide arguments that will not be validated during parsing, but will be used for class instantiation. + + For example, then using the ``pytorch_lightning.profilers.PyTorchProfiler`` profiler, + the ``profile_memory`` argument has a type that is determined dynamically. As a result, it's not possible + to know the expected type during parsing. To account for this, your config file should be set up like this: + + .. code:: yaml + + trainer: + profiler: + class_path: pytorch_lightning.profilers.PyTorchProfiler + dict_kwargs: + profile_memory: true + +---- + +******************** +Compose config files +******************** +Multiple config files can be provided, and they will be parsed sequentially. Let's say we have two configs with common +settings: + +.. code:: yaml + + # config_1.yaml + trainer: + num_epochs: 10 + ... + + # config_2.yaml + trainer: + num_epochs: 20 + ... + +The value from the last config will be used, ``num_epochs = 20`` in this case: + +.. code-block:: bash + + $ python main.py fit --config config_1.yaml --config config_2.yaml + +---- + +********************* +Use groups of options +********************* +Groups of options can also be given as independent config files. For configs like: + +.. code:: yaml + + # trainer.yaml + num_epochs: 10 + + # model.yaml + out_dim: 7 + + # data.yaml + data_dir: ./data + +a fit command can be run as: + +.. code-block:: bash + + $ python main.py fit --trainer trainer.yaml --model model.yaml --data data.yaml [...] diff --git a/docs/source-pytorch/cli/lightning_cli_advanced_2.rst b/docs/source-pytorch/cli/lightning_cli_advanced_2.rst new file mode 100644 index 0000000..749ce9e --- /dev/null +++ b/docs/source-pytorch/cli/lightning_cli_advanced_2.rst @@ -0,0 +1,209 @@ +:orphan: + +.. testsetup:: * + :skipif: not _JSONARGPARSE_AVAILABLE + + import torch + from unittest import mock + from typing import List + import lightning.pytorch.cli as pl_cli + from lightning.pytorch import LightningModule, LightningDataModule, Trainer, Callback + + + class NoFitTrainer(Trainer): + def fit(self, *_, **__): + pass + + + class LightningCLI(pl_cli.LightningCLI): + def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): + super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) + + + class MyModel(LightningModule): + def __init__( + self, + encoder_layers: int = 12, + decoder_layers: List[int] = [2, 4], + batch_size: int = 8, + ): + pass + + + class MyDataModule(LightningDataModule): + def __init__(self, batch_size: int = 8): + self.num_classes = 5 + + + mock_argv = mock.patch("sys.argv", ["any.py"]) + mock_argv.start() + +.. testcleanup:: * + + mock_argv.stop() + +################################################# +Configure hyperparameters from the CLI (Advanced) +################################################# + +********************************* +Customize arguments by subcommand +********************************* +To customize arguments by subcommand, pass the config *before* the subcommand: + +.. code-block:: bash + + $ python main.py [before] [subcommand] [after] + $ python main.py ... fit ... + +For example, here we set the Trainer argument [max_steps = 100] for the full training routine and [max_steps = 10] for +testing: + +.. code-block:: bash + + # config.yaml + fit: + trainer: + max_steps: 100 + test: + trainer: + max_epochs: 10 + +now you can toggle this behavior by subcommand: + +.. code-block:: bash + + # full routine with max_steps = 100 + $ python main.py --config config.yaml fit + + # test only with max_epochs = 10 + $ python main.py --config config.yaml test + +---- + +*************************** +Run from cloud yaml configs +*************************** +For certain enterprise workloads, Lightning CLI supports running from hosted configs: + +.. code-block:: bash + + $ python main.py [subcommand] --config s3://bucket/config.yaml + +For more options, refer to :doc:`Remote filesystems <../common/remote_fs>`. + +---- + +************************************** +Use a config via environment variables +************************************** +For certain CI/CD systems, it's useful to pass in raw yaml config as environment variables: + +.. code-block:: bash + + $ python main.py fit --trainer "$TRAINER_CONFIG" --model "$MODEL_CONFIG" [...] + +---- + +*************************************** +Run from environment variables directly +*************************************** +The Lightning CLI can convert every possible CLI flag into an environment variable. To enable this, add to +``parser_kwargs`` the ``default_env`` argument: + +.. code:: python + + cli = LightningCLI(..., parser_kwargs={"default_env": True}) + +now use the ``--help`` CLI flag with any subcommand: + +.. code:: bash + + $ python main.py fit --help + +which will show you ALL possible environment variables that can be set: + +.. code:: bash + + usage: main.py [options] fit [-h] [-c CONFIG] + ... + + optional arguments: + ... + ARG: --model.out_dim OUT_DIM + ENV: PL_FIT__MODEL__OUT_DIM + (type: int, default: 10) + ARG: --model.learning_rate LEARNING_RATE + ENV: PL_FIT__MODEL__LEARNING_RATE + (type: float, default: 0.02) + +now you can customize the behavior via environment variables: + +.. code:: bash + + # set the options via env vars + $ export PL_FIT__MODEL__LEARNING_RATE=0.01 + $ export PL_FIT__MODEL__OUT_DIM=5 + + $ python main.py fit + +---- + +************************ +Set default config files +************************ +To set a path to a config file of defaults, use the ``default_config_files`` argument: + +.. testcode:: + + cli = LightningCLI(MyModel, MyDataModule, parser_kwargs={"default_config_files": ["my_cli_defaults.yaml"]}) + +or if you want defaults per subcommand: + +.. testcode:: + + cli = LightningCLI(MyModel, MyDataModule, parser_kwargs={"fit": {"default_config_files": ["my_fit_defaults.yaml"]}}) + +---- + +***************************** +Enable variable interpolation +***************************** +In certain cases where multiple settings need to share a value, consider using variable interpolation. For instance: + +.. code-block:: yaml + + model: + encoder_layers: 12 + decoder_layers: + - ${model.encoder_layers} + - 4 + +To enable variable interpolation, first install omegaconf: + +.. code:: bash + + pip install omegaconf + +Then set omegaconf when instantiating the ``LightningCLI`` class: + +.. code:: python + + cli = LightningCLI(MyModel, parser_kwargs={"parser_mode": "omegaconf"}) + +After this, the CLI will automatically perform interpolation in yaml files: + +.. code:: bash + + python main.py --model.encoder_layers=12 + +For more details about the interpolation support and its limitations, have a look at the `jsonargparse +`__ and the `omegaconf +`__ documentations. + +.. note:: + + There are many use cases in which variable interpolation is not the correct approach. When a parameter **must + always** be derived from other settings, it shouldn't be up to the CLI user to do this in a config file. For + example, if the data and model both require ``batch_size`` and must be the same value, then + :ref:`cli_link_arguments` should be used instead of interpolation. diff --git a/docs/source-pytorch/cli/lightning_cli_advanced_3.rst b/docs/source-pytorch/cli/lightning_cli_advanced_3.rst new file mode 100644 index 0000000..06e5d70 --- /dev/null +++ b/docs/source-pytorch/cli/lightning_cli_advanced_3.rst @@ -0,0 +1,393 @@ +:orphan: + +.. testsetup:: * + :skipif: not _JSONARGPARSE_AVAILABLE + + import torch + from unittest import mock + from typing import List + import lightning.pytorch.cli as pl_cli + from lightning.pytorch import LightningModule, LightningDataModule, Trainer, Callback + + + class NoFitTrainer(Trainer): + def fit(self, *_, **__): + pass + + + class LightningCLI(pl_cli.LightningCLI): + def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): + super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) + + + class MyModel(LightningModule): + def __init__( + self, + encoder_layers: int = 12, + decoder_layers: List[int] = [2, 4], + batch_size: int = 8, + ): + pass + + + class MyDataModule(LightningDataModule): + def __init__(self, batch_size: int = 8): + self.num_classes = 5 + + + MyModelBaseClass = MyModel + MyDataModuleBaseClass = MyDataModule + + mock_argv = mock.patch("sys.argv", ["any.py"]) + mock_argv.start() + +.. testcleanup:: * + + mock_argv.stop() + +################################################# +Configure hyperparameters from the CLI (Advanced) +################################################# + +Instantiation only mode +^^^^^^^^^^^^^^^^^^^^^^^ + +The CLI is designed to start fitting with minimal code changes. On class instantiation, the CLI will automatically call +the trainer function associated with the subcommand provided, so you don't have to do it. To avoid this, you can set the +following argument: + +.. testcode:: + + cli = LightningCLI(MyModel, run=False) # True by default + # you'll have to call fit yourself: + cli.trainer.fit(cli.model) + +In this mode, subcommands are **not** added to the parser. This can be useful to implement custom logic without having +to subclass the CLI, but still, use the CLI's instantiation and argument parsing capabilities. + + +Trainer Callbacks and arguments with class type +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A very important argument of the :class:`~lightning.pytorch.trainer.trainer.Trainer` class is the ``callbacks``. In +contrast to simpler arguments that take numbers or strings, ``callbacks`` expects a list of instances of subclasses of +:class:`~lightning.pytorch.callbacks.Callback`. To specify this kind of argument in a config file, each callback must be +given as a dictionary, including a ``class_path`` entry with an import path of the class and optionally an ``init_args`` +entry with arguments to use to instantiate. Therefore, a simple configuration file that defines two callbacks is the +following: + +.. code-block:: yaml + + trainer: + callbacks: + - class_path: lightning.pytorch.callbacks.EarlyStopping + init_args: + patience: 5 + - class_path: lightning.pytorch.callbacks.LearningRateMonitor + init_args: + ... + +Similar to the callbacks, any parameter in :class:`~lightning.pytorch.trainer.trainer.Trainer` and user extended +:class:`~lightning.pytorch.core.module.LightningModule` and +:class:`~lightning.pytorch.core.datamodule.LightningDataModule` classes that have as type hint a class, can be +configured the same way using ``class_path`` and ``init_args``. If the package that defines a subclass is imported +before the :class:`~lightning.pytorch.cli.LightningCLI` class is run, the name can be used instead of the full import +path. + +From command line the syntax is the following: + +.. code-block:: bash + + $ python ... \ + --trainer.callbacks+={CALLBACK_1_NAME} \ + --trainer.callbacks.{CALLBACK_1_ARGS_1}=... \ + --trainer.callbacks.{CALLBACK_1_ARGS_2}=... \ + ... + --trainer.callbacks+={CALLBACK_N_NAME} \ + --trainer.callbacks.{CALLBACK_N_ARGS_1}=... \ + ... + +Note the use of ``+`` to append a new callback to the list and that the ``init_args`` are applied to the previous +callback appended. Here is an example: + +.. code-block:: bash + + $ python ... \ + --trainer.callbacks+=EarlyStopping \ + --trainer.callbacks.patience=5 \ + --trainer.callbacks+=LearningRateMonitor \ + --trainer.callbacks.logging_interval=epoch + +.. note:: + + Serialized config files (e.g. ``--print_config`` or :class:`~lightning.pytorch.cli.SaveConfigCallback`) always have + the full ``class_path``, even when class name shorthand notation is used in the command line or in input config + files. + + +Multiple models and/or datasets +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A CLI can be written such that a model and/or a datamodule is specified by an import path and init arguments. For +example, with a tool implemented as: + +.. code-block:: python + + cli = LightningCLI(MyModelBaseClass, MyDataModuleBaseClass, subclass_mode_model=True, subclass_mode_data=True) + +A possible config file could be as follows: + +.. code-block:: yaml + + model: + class_path: mycode.mymodels.MyModel + init_args: + decoder_layers: + - 2 + - 4 + encoder_layers: 12 + data: + class_path: mycode.mydatamodules.MyDataModule + init_args: + ... + trainer: + callbacks: + - class_path: lightning.pytorch.callbacks.EarlyStopping + init_args: + patience: 5 + ... + +Only model classes that are a subclass of ``MyModelBaseClass`` would be allowed, and similarly, only subclasses of +``MyDataModuleBaseClass``. If as base classes :class:`~lightning.pytorch.core.module.LightningModule` and +:class:`~lightning.pytorch.core.datamodule.LightningDataModule` is given, then the CLI would allow any lightning module +and data module. + +.. tip:: + + Note that with the subclass modes, the ``--help`` option does not show information for a specific subclass. To get + help for a subclass, the options ``--model.help`` and ``--data.help`` can be used, followed by the desired class + path. Similarly, ``--print_config`` does not include the settings for a particular subclass. To include them, the + class path should be given before the ``--print_config`` option. Examples for both help and print config are: + + .. code-block:: bash + + $ python trainer.py fit --model.help mycode.mymodels.MyModel + $ python trainer.py fit --model mycode.mymodels.MyModel --print_config + + +Models with multiple submodules +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Many use cases require to have several modules, each with its own configurable options. One possible way to handle this +with ``LightningCLI`` is to implement a single module having as init parameters each of the submodules. This is known as +`dependency injection `__ which is a good approach to improve +decoupling in your code base. + +Since the init parameters of the model have as a type hint a class, in the configuration, these would be specified with +``class_path`` and ``init_args`` entries. For instance, a model could be implemented as: + +.. testcode:: + + class MyMainModel(LightningModule): + def __init__(self, encoder: nn.Module, decoder: nn.Module): + """Example encoder-decoder submodules model + + Args: + encoder: Instance of a module for encoding + decoder: Instance of a module for decoding + """ + super().__init__() + self.encoder = encoder + self.decoder = decoder + +If the CLI is implemented as ``LightningCLI(MyMainModel)`` the configuration would be as follows: + +.. code-block:: yaml + + model: + encoder: + class_path: mycode.myencoders.MyEncoder + init_args: + ... + decoder: + class_path: mycode.mydecoders.MyDecoder + init_args: + ... + +It is also possible to combine ``subclass_mode_model=True`` and submodules, thereby having two levels of ``class_path``. + + +Fixed optimizer and scheduler +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In some cases, fixing the optimizer and/or learning scheduler might be desired instead of allowing multiple. For this, +you can manually add the arguments for specific classes by subclassing the CLI. The following code snippet shows how to +implement it: + +.. testcode:: + + class MyLightningCLI(LightningCLI): + def add_arguments_to_parser(self, parser): + parser.add_optimizer_args(torch.optim.Adam) + parser.add_lr_scheduler_args(torch.optim.lr_scheduler.ExponentialLR) + +With this, in the config, the ``optimizer`` and ``lr_scheduler`` groups would accept all of the options for the given +classes, in this example, ``Adam`` and ``ExponentialLR``. Therefore, the config file would be structured like: + +.. code-block:: yaml + + optimizer: + lr: 0.01 + lr_scheduler: + gamma: 0.2 + model: + ... + trainer: + ... + +where the arguments can be passed directly through the command line without specifying the class. For example: + +.. code-block:: bash + + $ python trainer.py fit --optimizer.lr=0.01 --lr_scheduler.gamma=0.2 + + +Multiple optimizers and schedulers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +By default, the CLIs support multiple optimizers and/or learning schedulers, automatically implementing +``configure_optimizers``. This behavior can be disabled by providing ``auto_configure_optimizers=False`` on +instantiation of :class:`~lightning.pytorch.cli.LightningCLI`. This would be required for example to support multiple +optimizers, for each selecting a particular optimizer class. Similar to multiple submodules, this can be done via +`dependency injection `__. Unlike the submodules, it is not possible +to expect an instance of a class, because optimizers require the module's parameters to optimize, which are only +available after instantiation of the module. Learning schedulers are a similar situation, requiring an optimizer +instance. For these cases, dependency injection involves providing a function that instantiates the respective class +when called. + +An example of a model that uses two optimizers is the following: + +.. code-block:: python + + from typing import Iterable + from torch.optim import Optimizer + + + OptimizerCallable = Callable[[Iterable], Optimizer] + + + class MyModel(LightningModule): + def __init__(self, optimizer1: OptimizerCallable, optimizer2: OptimizerCallable): + super().__init__() + self.optimizer1 = optimizer1 + self.optimizer2 = optimizer2 + + def configure_optimizers(self): + optimizer1 = self.optimizer1(self.parameters()) + optimizer2 = self.optimizer2(self.parameters()) + return [optimizer1, optimizer2] + + + cli = MyLightningCLI(MyModel, auto_configure_optimizers=False) + +Note the type ``Callable[[Iterable], Optimizer]``, which denotes a function that receives a singe argument, some +learnable parameters, and returns an optimizer instance. With this, from the command line it is possible to select the +class and init arguments for each of the optimizers, as follows: + +.. code-block:: bash + + $ python trainer.py fit \ + --model.optimizer1=Adam \ + --model.optimizer1.lr=0.01 \ + --model.optimizer2=AdamW \ + --model.optimizer2.lr=0.0001 + +In the example above, the ``OptimizerCallable`` type alias was created to illustrate what the type hint means. For +convenience, this type alias and one for learning schedulers is available in the ``cli`` module. An example of a model +that uses dependency injection for an optimizer and a learning scheduler is: + +.. code-block:: python + + from lightning.pytorch.cli import OptimizerCallable, LRSchedulerCallable, LightningCLI + + + class MyModel(LightningModule): + def __init__( + self, + optimizer: OptimizerCallable = torch.optim.Adam, + scheduler: LRSchedulerCallable = torch.optim.lr_scheduler.ConstantLR, + ): + super().__init__() + self.optimizer = optimizer + self.scheduler = scheduler + + def configure_optimizers(self): + optimizer = self.optimizer(self.parameters()) + scheduler = self.scheduler(optimizer) + return {"optimizer": optimizer, "lr_scheduler": scheduler} + + + cli = MyLightningCLI(MyModel, auto_configure_optimizers=False) + +Note that for this example, classes are used as defaults. This is compatible with the type hints, since they are also +callables that receive the same first argument and return an instance of the class. Classes that have more than one +required argument will not work as default. For these cases a lambda function can be used, e.g. ``optimizer: +OptimizerCallable = lambda p: torch.optim.SGD(p, lr=0.01)``. + + +Run from Python +^^^^^^^^^^^^^^^ + +Even though the :class:`~lightning.pytorch.cli.LightningCLI` class is designed to help in the implementation of command +line tools, for some use cases it is desired to run directly from Python. To allow this there is the ``args`` parameter. +An example could be to first implement a normal CLI script, but adding an ``args`` parameter with default ``None`` to +the main function as follows: + +.. code:: python + + from lightning.pytorch.cli import ArgsType, LightningCLI + + + def cli_main(args: ArgsType = None): + cli = LightningCLI(MyModel, ..., args=args) + ... + + + if __name__ == "__main__": + cli_main() + +Then it is possible to import the ``cli_main`` function to run it. Executing in a shell ``my_cli.py +--trainer.max_epochs=100 --model.encoder_layers=24`` would be equivalent to: + +.. code:: python + + from my_module.my_cli import cli_main + + cli_main(["--trainer.max_epochs=100", "--model.encoder_layers=24"]) + +All the features that are supported from the command line can be used when giving ``args`` as a list of strings. It is +also possible to provide a ``dict`` or `jsonargparse.Namespace +`__. For example in a jupyter notebook someone +might do: + +.. code:: python + + args = { + "trainer": { + "max_epochs": 100, + }, + "model": {}, + } + + args["model"]["encoder_layers"] = 8 + cli_main(args) + args["model"]["encoder_layers"] = 12 + cli_main(args) + args["trainer"]["max_epochs"] = 200 + cli_main(args) + +.. note:: + + The ``args`` parameter must be ``None`` when running from command line so that ``sys.argv`` is used as arguments. + Also, note that the purpose of ``trainer_defaults`` is different to ``args``. It is okay to use ``trainer_defaults`` + in the ``cli_main`` function to modify the defaults of some trainer parameters. diff --git a/docs/source-pytorch/cli/lightning_cli_expert.rst b/docs/source-pytorch/cli/lightning_cli_expert.rst new file mode 100644 index 0000000..eff4093 --- /dev/null +++ b/docs/source-pytorch/cli/lightning_cli_expert.rst @@ -0,0 +1,267 @@ +:orphan: + +.. testsetup:: * + :skipif: not _JSONARGPARSE_AVAILABLE + + import torch + from unittest import mock + from typing import List + import lightning.pytorch.cli as pl_cli + from lightning.pytorch import LightningModule, LightningDataModule, Trainer, Callback + + + class NoFitTrainer(Trainer): + def fit(self, *_, **__): + pass + + + class LightningCLI(pl_cli.LightningCLI): + def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): + super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) + + + class MyModel(LightningModule): + def __init__( + self, + encoder_layers: int = 12, + decoder_layers: List[int] = [2, 4], + batch_size: int = 8, + ): + pass + + + class MyClassModel(LightningModule): + def __init__(self, num_classes: int): + pass + + + class MyDataModule(LightningDataModule): + def __init__(self, batch_size: int = 8): + self.num_classes = 5 + + + def send_email(address, message): + pass + + + mock_argv = mock.patch("sys.argv", ["any.py"]) + mock_argv.start() + +.. testcleanup:: * + + mock_argv.stop() + +############################################### +Configure hyperparameters from the CLI (Expert) +############################################### +**Audience:** Users who already understand the LightningCLI and want to customize it. + +---- + +************************** +Customize the LightningCLI +************************** + +The init parameters of the :class:`~lightning.pytorch.cli.LightningCLI` class can be used to customize some things, +e.g., the description of the tool, enabling parsing of environment variables, and additional arguments to instantiate +the trainer and configuration parser. + +Nevertheless, the init arguments are not enough for many use cases. For this reason, the class is designed so that it +can be extended to customize different parts of the command line tool. The argument parser class used by +:class:`~lightning.pytorch.cli.LightningCLI` is :class:`~lightning.pytorch.cli.LightningArgumentParser`, which is an +extension of python's argparse, thus adding arguments can be done using the :func:`add_argument` method. In contrast to +argparse, it has additional methods to add arguments. For example :func:`add_class_arguments` add all arguments from the +init of a class. For more details, see the `respective documentation +`_. + +The :class:`~lightning.pytorch.cli.LightningCLI` class has the +:meth:`~lightning.pytorch.cli.LightningCLI.add_arguments_to_parser` method can be implemented to include more arguments. +After parsing, the configuration is stored in the ``config`` attribute of the class instance. The +:class:`~lightning.pytorch.cli.LightningCLI` class also has two methods that can be used to run code before and after +the trainer runs: ``before_`` and ``after_``. A realistic example of this would be to send an +email before and after the execution. The code for the ``fit`` subcommand would be something like this: + +.. testcode:: + + class MyLightningCLI(LightningCLI): + def add_arguments_to_parser(self, parser): + parser.add_argument("--notification_email", default="will@email.com") + + def before_fit(self): + send_email(address=self.config["notification_email"], message="trainer.fit starting") + + def after_fit(self): + send_email(address=self.config["notification_email"], message="trainer.fit finished") + + + cli = MyLightningCLI(MyModel) + +Note that the config object ``self.config`` is a namespace whose keys are global options or groups of options. It has +the same structure as the YAML format described previously. This means that the parameters used for instantiating the +trainer class can be found in ``self.config['fit']['trainer']``. + +.. tip:: + + Have a look at the :class:`~lightning.pytorch.cli.LightningCLI` class API reference to learn about other methods + that can be extended to customize a CLI. + +---- + +************************** +Configure forced callbacks +************************** +As explained previously, any Lightning callback can be added by passing it through the command line or including it in +the config via ``class_path`` and ``init_args`` entries. + +However, certain callbacks **must** be coupled with a model so they are always present and configurable. This can be +implemented as follows: + +.. testcode:: + + from lightning.pytorch.callbacks import EarlyStopping + + + class MyLightningCLI(LightningCLI): + def add_arguments_to_parser(self, parser): + parser.add_lightning_class_args(EarlyStopping, "my_early_stopping") + parser.set_defaults({"my_early_stopping.monitor": "val_loss", "my_early_stopping.patience": 5}) + + + cli = MyLightningCLI(MyModel) + +To change the parameters for ``EarlyStopping`` in the config it would be: + +.. code-block:: yaml + + model: + ... + trainer: + ... + my_early_stopping: + patience: 5 + +.. note:: + + The example above overrides a default in ``add_arguments_to_parser``. This is included to show that defaults can be + changed if needed. However, note that overriding defaults in the source code is not intended to be used to store the + best hyperparameters for a task after experimentation. To guarantee reproducibility, the source code should be + stable. It is better to practice storing the best hyperparameters for a task in a configuration file independent + from the source code. + +---- + +******************* +Class type defaults +******************* + +The support for classes as type hints allows to try many possibilities with the same CLI. This is a useful feature, but +it is tempting to use an instance of a class as a default. For example: + +.. testcode:: + + class MyMainModel(LightningModule): + def __init__( + self, + backbone: torch.nn.Module = MyModel(encoder_layers=24), # BAD PRACTICE! + ): + super().__init__() + self.backbone = backbone + +Normally classes are mutable, as in this case. The instance of ``MyModel`` would be created the moment that the module +that defines ``MyMainModel`` is first imported. This means that the default of ``backbone`` will be initialized before +the CLI class runs ``seed_everything``, making it non-reproducible. Furthermore, if ``MyMainModel`` is used more than +once in the same Python process and the ``backbone`` parameter is not overridden, the same instance would be used in +multiple places. Most likely, this is not what the developer intended. Having an instance as default also makes it +impossible to generate the complete config file since it is not known which arguments were used to instantiate it for +arbitrary classes. + +An excellent solution to these problems is not to have a default or set the default to a unique value (e.g., a string). +Then check this value and instantiate it in the ``__init__`` body. If a class parameter has no default and the CLI is +subclassed, then a default can be set as follows: + +.. testcode:: + + default_backbone = { + "class_path": "import.path.of.MyModel", + "init_args": { + "encoder_layers": 24, + }, + } + + + class MyLightningCLI(LightningCLI): + def add_arguments_to_parser(self, parser): + parser.set_defaults({"model.backbone": default_backbone}) + +A more compact version that avoids writing a dictionary would be: + +.. testcode:: + + from jsonargparse import lazy_instance + + + class MyLightningCLI(LightningCLI): + def add_arguments_to_parser(self, parser): + parser.set_defaults({"model.backbone": lazy_instance(MyModel, encoder_layers=24)}) + +---- + +.. _cli_link_arguments: + +**************** +Argument linking +**************** +Another case in which it might be desired to extend :class:`~lightning.pytorch.cli.LightningCLI` is that the model and +data module depends on a common parameter. For example, in some cases, both classes require to know the ``batch_size``. +It is a burden and error-prone to give the same value twice in a config file. To avoid this, the parser can be +configured so that a value is only given once and then propagated accordingly. With a tool implemented like the one +shown below, the ``batch_size`` only has to be provided in the ``data`` section of the config. + +.. testcode:: + + class MyLightningCLI(LightningCLI): + def add_arguments_to_parser(self, parser): + parser.link_arguments("data.batch_size", "model.batch_size") + + + cli = MyLightningCLI(MyModel, MyDataModule) + +The linking of arguments is observed in the help of the tool, which for this example would look like: + +.. code-block:: bash + + $ python trainer.py fit --help + ... + --data.batch_size BATCH_SIZE + Number of samples in a batch (type: int, default: 8) + + Linked arguments: + data.batch_size --> model.batch_size + Number of samples in a batch (type: int) + +Sometimes a parameter value is only available after class instantiation. An example could be that your model requires +the number of classes to instantiate its fully connected layer (for a classification task). But the value is not +available until the data module has been instantiated. The code below illustrates how to address this. + +.. testcode:: + + class MyLightningCLI(LightningCLI): + def add_arguments_to_parser(self, parser): + parser.link_arguments("data.num_classes", "model.num_classes", apply_on="instantiate") + + + cli = MyLightningCLI(MyClassModel, MyDataModule) + +Instantiation links are used to automatically determine the order of instantiation, in this case data first. + +.. note:: + + The linking of arguments is intended for things that are meant to be non-configurable. This improves the CLI user + experience since it avoids the need to provide more parameters. A related concept is a variable interpolation that + keeps things configurable. + +.. tip:: + + The linking of arguments can be used for more complex cases. For example to derive a value via a function that takes + multiple settings as input. For more details have a look at the API of `link_arguments + `_. diff --git a/docs/source-pytorch/cli/lightning_cli_faq.rst b/docs/source-pytorch/cli/lightning_cli_faq.rst new file mode 100644 index 0000000..6ebd78d --- /dev/null +++ b/docs/source-pytorch/cli/lightning_cli_faq.rst @@ -0,0 +1,122 @@ +:orphan: + +########################################### +Frequently asked questions for LightningCLI +########################################### + +************************ +What does CLI stand for? +************************ +CLI is short for command line interface. This means it is a tool intended to be run from a terminal, similar to commands +like ``git``. + +---- + +.. _what-is-a-yaml-config-file: + +*************************** +What is a yaml config file? +*************************** +A YAML is a standard for configuration files used to describe parameters for sections of a program. It is a common tool +in engineering and has recently started to gain popularity in machine learning. An example of a YAML file is the +following: + +.. code:: yaml + + # file.yaml + car: + max_speed:100 + max_passengers:2 + plane: + fuel_capacity: 50 + class_3: + option_1: 'x' + option_2: 'y' + +If you are unfamiliar with YAML, the short introduction at `realpython.com#yaml-syntax +`__ might be a good starting point. + +---- + +********************* +What is a subcommand? +********************* +A subcommand is what is the action the LightningCLI applies to the script: + +.. code:: bash + + python main.py [subcommand] + +See the Potential subcommands with: + +.. code:: bash + + python main.py --help + +which prints: + +.. code:: bash + + ... + + fit Runs the full optimization routine. + validate Perform one evaluation epoch over the validation set. + test Perform one evaluation epoch over the test set. + predict Run inference on your data. + +use a subcommand as follows: + +.. code:: bash + + python main.py fit + python main.py test + +---- + +******************************************************* +What is the relation between LightningCLI and argparse? +******************************************************* + +:class:`~lightning.pytorch.cli.LightningCLI` makes use of `jsonargparse `__ +which is an extension of `argparse `__. Due to this, +:class:`~lightning.pytorch.cli.LightningCLI` follows the same arguments style as many POSIX command line tools. Long +options are prefixed with two dashes and its corresponding values are separated by space or an equal sign, as ``--option +value`` or ``--option=value``. Command line options are parsed from left to right, therefore if a setting appears +multiple times, the value most to the right will override the previous ones. + +---- + +******************************************* +What is the override order of LightningCLI? +******************************************* + +The final configuration of CLIs implemented with :class:`~lightning.pytorch.cli.LightningCLI` can depend on default +config files (if defined), environment variables (if enabled) and command line arguments. The override order between +these is the following: + +1. Defaults defined in the source code. +2. Existing default config files in the order defined in ``default_config_files``, e.g. ``~/.myapp.yaml``. +3. Entire config environment variable, e.g. ``PL_FIT__CONFIG``. +4. Individual argument environment variables, e.g. ``PL_FIT__SEED_EVERYTHING``. +5. Command line arguments in order left to right (might include config files). + +---- + +**************************** +How do I troubleshoot a CLI? +**************************** +The standard behavior for CLIs, when they fail, is to terminate the process with a non-zero exit code and a short +message to hint the user about the cause. This is problematic while developing the CLI since there is no information to +track down the root of the problem. To troubleshoot set the environment variable ``JSONARGPARSE_DEBUG`` to any value +before running the CLI: + +.. code:: bash + + export JSONARGPARSE_DEBUG=true + python main.py fit + +.. note:: + + When asking about problems and reporting issues, please set the ``JSONARGPARSE_DEBUG`` and include the stack trace + in your description. With this, users are more likely to help identify the cause without needing to create a + reproducible script. diff --git a/docs/source-pytorch/cli/lightning_cli_intermediate.rst b/docs/source-pytorch/cli/lightning_cli_intermediate.rst new file mode 100644 index 0000000..b43a838 --- /dev/null +++ b/docs/source-pytorch/cli/lightning_cli_intermediate.rst @@ -0,0 +1,156 @@ +:orphan: + +##################################################### +Configure hyperparameters from the CLI (Intermediate) +##################################################### +**Audience:** Users who want advanced modularity via a command line interface (CLI). + +**Pre-reqs:** You must already understand how to use the command line and :doc:`LightningDataModule <../data/datamodule>`. + +---- + +************************* +LightningCLI requirements +************************* + +The :class:`~lightning.pytorch.cli.LightningCLI` class is designed to significantly ease the implementation of CLIs. To +use this class, an additional Python requirement is necessary than the minimal installation of Lightning provides. To +enable, either install all extras: + +.. code:: bash + + pip install "pytorch-lightning[extra]" + +or if only interested in ``LightningCLI``, just install jsonargparse: + +.. code:: bash + + pip install "jsonargparse[signatures]" + +---- + +****************** +Implementing a CLI +****************** +Implementing a CLI is as simple as instantiating a :class:`~lightning.pytorch.cli.LightningCLI` object giving as +arguments classes for a ``LightningModule`` and optionally a ``LightningDataModule``: + +.. code:: python + + # main.py + from lightning.pytorch.cli import LightningCLI + + # simple demo classes for your convenience + from lightning.pytorch.demos.boring_classes import DemoModel, BoringDataModule + + + def cli_main(): + cli = LightningCLI(DemoModel, BoringDataModule) + # note: don't call fit!! + + + if __name__ == "__main__": + cli_main() + # note: it is good practice to implement the CLI in a function and call it in the main if block + +Now your model can be managed via the CLI. To see the available commands type: + +.. code:: bash + + $ python main.py --help + +which prints out: + +.. code:: bash + + usage: main.py [-h] [-c CONFIG] [--print_config [={comments,skip_null,skip_default}+]] + {fit,validate,test,predict} ... + + pytorch-lightning trainer command line tool + + optional arguments: + -h, --help Show this help message and exit. + -c CONFIG, --config CONFIG + Path to a configuration file in json or yaml format. + --print_config [={comments,skip_null,skip_default}+] + Print configuration and exit. + + subcommands: + For more details of each subcommand add it as argument followed by --help. + + {fit,validate,test,predict} + fit Runs the full optimization routine. + validate Perform one evaluation epoch over the validation set. + test Perform one evaluation epoch over the test set. + predict Run inference on your data. + + +The message tells us that we have a few available subcommands: + +.. code:: bash + + python main.py [subcommand] + +which you can use depending on your use case: + +.. code:: bash + + $ python main.py fit + $ python main.py validate + $ python main.py test + $ python main.py predict + +---- + +************************** +Train a model with the CLI +************************** +To train a model, use the ``fit`` subcommand: + +.. code:: bash + + python main.py fit + +View all available options with the ``--help`` argument given after the subcommand: + +.. code:: bash + + $ python main.py fit --help + + usage: main.py [options] fit [-h] [-c CONFIG] + [--seed_everything SEED_EVERYTHING] [--trainer CONFIG] + ... + [--ckpt_path CKPT_PATH] + --trainer.logger LOGGER + + optional arguments: + : + --model.out_dim OUT_DIM + (type: int, default: 10) + --model.learning_rate LEARNING_RATE + (type: float, default: 0.02) + : + --data CONFIG Path to a configuration file. + --data.data_dir DATA_DIR + (type: str, default: ./) + +With the Lightning CLI enabled, you can now change the parameters without touching your code: + +.. code:: bash + + # change the learning_rate + python main.py fit --model.learning_rate 0.1 + + # change the output dimensions also + python main.py fit --model.out_dim 10 --model.learning_rate 0.1 + + # change trainer and data arguments too + python main.py fit --model.out_dim 2 --model.learning_rate 0.1 --data.data_dir '~/' --trainer.logger False + +.. tip:: + + The options that become available in the CLI are the ``__init__`` parameters of the ``LightningModule`` and + ``LightningDataModule`` classes. Thus, to make hyperparameters configurable, just add them to your class's + ``__init__``. It is highly recommended that these parameters are described in the docstring so that the CLI shows + them in the help. Also, the parameters should have accurate type hints so that the CLI can fail early and give + understandable error messages when incorrect values are given. diff --git a/docs/source-pytorch/cli/lightning_cli_intermediate_2.rst b/docs/source-pytorch/cli/lightning_cli_intermediate_2.rst new file mode 100644 index 0000000..db06f85 --- /dev/null +++ b/docs/source-pytorch/cli/lightning_cli_intermediate_2.rst @@ -0,0 +1,276 @@ +:orphan: + +##################################################### +Configure hyperparameters from the CLI (Intermediate) +##################################################### +**Audience:** Users who have multiple models and datasets per project. + +**Pre-reqs:** You must have read :doc:`(Control it all from the CLI) `. + +---- + +*************************** +Why mix models and datasets +*************************** +Lightning projects usually begin with one model and one dataset. As the project grows in complexity and you introduce +more models and more datasets, it becomes desirable to mix any model with any dataset directly from the command line +without changing your code. + +.. code:: bash + + # Mix and match anything + $ python main.py fit --model=GAN --data=MNIST + $ python main.py fit --model=Transformer --data=MNIST + +``LightningCLI`` makes this very simple. Otherwise, this kind of configuration requires a significant amount of +boilerplate that often looks like this: + +.. code:: python + + # choose model + if args.model == "gan": + model = GAN(args.feat_dim) + elif args.model == "transformer": + model = Transformer(args.feat_dim) + ... + + # choose datamodule + if args.data == "MNIST": + datamodule = MNIST() + elif args.data == "imagenet": + datamodule = Imagenet() + ... + + # mix them! + trainer.fit(model, datamodule) + +It is highly recommended that you avoid writing this kind of boilerplate and use ``LightningCLI`` instead. + +---- + +************************* +Multiple LightningModules +************************* +To support multiple models, when instantiating ``LightningCLI`` omit the ``model_class`` parameter: + +.. code:: python + + # main.py + from lightning.pytorch.cli import LightningCLI + from lightning.pytorch.demos.boring_classes import DemoModel, BoringDataModule + + + class Model1(DemoModel): + def configure_optimizers(self): + print("⚡", "using Model1", "⚡") + return super().configure_optimizers() + + + class Model2(DemoModel): + def configure_optimizers(self): + print("⚡", "using Model2", "⚡") + return super().configure_optimizers() + + + cli = LightningCLI(datamodule_class=BoringDataModule) + +Now you can choose between any model from the CLI: + +.. code:: bash + + # use Model1 + python main.py fit --model Model1 + + # use Model2 + python main.py fit --model Model2 + +.. tip:: + + Instead of omitting the ``model_class`` parameter, you can give a base class and ``subclass_mode_model=True``. This + will make the CLI only accept models which are a subclass of the given base class. + +---- + +***************************** +Multiple LightningDataModules +***************************** +To support multiple data modules, when instantiating ``LightningCLI`` omit the ``datamodule_class`` parameter: + +.. code:: python + + # main.py + import torch + from lightning.pytorch.cli import LightningCLI + from lightning.pytorch.demos.boring_classes import DemoModel, BoringDataModule + + + class FakeDataset1(BoringDataModule): + def train_dataloader(self): + print("⚡", "using FakeDataset1", "⚡") + return torch.utils.data.DataLoader(self.random_train) + + + class FakeDataset2(BoringDataModule): + def train_dataloader(self): + print("⚡", "using FakeDataset2", "⚡") + return torch.utils.data.DataLoader(self.random_train) + + + cli = LightningCLI(DemoModel) + +Now you can choose between any dataset at runtime: + +.. code:: bash + + # use Model1 + python main.py fit --data FakeDataset1 + + # use Model2 + python main.py fit --data FakeDataset2 + +.. tip:: + + Instead of omitting the ``datamodule_class`` parameter, you can give a base class and ``subclass_mode_data=True``. + This will make the CLI only accept data modules that are a subclass of the given base class. + +---- + +******************* +Multiple optimizers +******************* +Standard optimizers from ``torch.optim`` work out of the box: + +.. code:: bash + + python main.py fit --optimizer AdamW + +If the optimizer you want needs other arguments, add them via the CLI (no need to change your code)! + +.. code:: bash + + python main.py fit --optimizer SGD --optimizer.lr=0.01 + +Furthermore, any custom subclass of :class:`torch.optim.Optimizer` can be used as an optimizer: + +.. code:: python + + # main.py + import torch + from lightning.pytorch.cli import LightningCLI + from lightning.pytorch.demos.boring_classes import DemoModel, BoringDataModule + + + class LitAdam(torch.optim.Adam): + def step(self, closure): + print("⚡", "using LitAdam", "⚡") + super().step(closure) + + + class FancyAdam(torch.optim.Adam): + def step(self, closure): + print("⚡", "using FancyAdam", "⚡") + super().step(closure) + + + cli = LightningCLI(DemoModel, BoringDataModule) + +Now you can choose between any optimizer at runtime: + +.. code:: bash + + # use LitAdam + python main.py fit --optimizer LitAdam + + # use FancyAdam + python main.py fit --optimizer FancyAdam + +---- + +******************* +Multiple schedulers +******************* +Standard learning rate schedulers from ``torch.optim.lr_scheduler`` work out of the box: + +.. code:: bash + + python main.py fit --optimizer=Adam --lr_scheduler CosineAnnealingLR + +Please note that ``--optimizer`` must be added for ``--lr_scheduler`` to have an effect. + +If the scheduler you want needs other arguments, add them via the CLI (no need to change your code)! + +.. code:: bash + + python main.py fit --optimizer=Adam --lr_scheduler=ReduceLROnPlateau --lr_scheduler.monitor=epoch + +Furthermore, any custom subclass of ``torch.optim.lr_scheduler.LRScheduler`` can be used as learning rate scheduler: + +.. code:: python + + # main.py + import torch + from lightning.pytorch.cli import LightningCLI + from lightning.pytorch.demos.boring_classes import DemoModel, BoringDataModule + + + class LitLRScheduler(torch.optim.lr_scheduler.CosineAnnealingLR): + def step(self): + print("⚡", "using LitLRScheduler", "⚡") + super().step() + + + cli = LightningCLI(DemoModel, BoringDataModule) + +Now you can choose between any learning rate scheduler at runtime: + +.. code:: bash + + # LitLRScheduler + python main.py fit --optimizer=Adam --lr_scheduler LitLRScheduler + + +---- + +************************ +Classes from any package +************************ +In the previous sections, custom classes to select were defined in the same python file where the ``LightningCLI`` class +is run. To select classes from any package by using only the class name, import the respective package: + +.. code:: python + + from lightning.pytorch.cli import LightningCLI + import my_code.models # noqa: F401 + import my_code.data_modules # noqa: F401 + import my_code.optimizers # noqa: F401 + + cli = LightningCLI() + +Now use any of the classes: + +.. code:: bash + + python main.py fit --model Model1 --data FakeDataset1 --optimizer LitAdam --lr_scheduler LitLRScheduler + +The ``# noqa: F401`` comment avoids a linter warning that the import is unused. + +It is also possible to select subclasses that have not been imported by giving the full import path: + +.. code:: bash + + python main.py fit --model my_code.models.Model1 + +---- + +************************* +Help for specific classes +************************* +When multiple models or datasets are accepted, the main help of the CLI does not include their specific parameters. To +show this specific help, additional help arguments expect the class name or its import path. For example: + +.. code:: bash + + python main.py fit --model.help Model1 + python main.py fit --data.help FakeDataset2 + python main.py fit --optimizer.help Adagrad + python main.py fit --lr_scheduler.help StepLR diff --git a/source/clouds/cluster.rst b/docs/source-pytorch/clouds/cluster.rst similarity index 100% rename from source/clouds/cluster.rst rename to docs/source-pytorch/clouds/cluster.rst diff --git a/docs/source-pytorch/clouds/cluster_advanced.rst b/docs/source-pytorch/clouds/cluster_advanced.rst new file mode 100644 index 0000000..0bcadfa --- /dev/null +++ b/docs/source-pytorch/clouds/cluster_advanced.rst @@ -0,0 +1,177 @@ +#################################### +Run on an on-prem cluster (advanced) +#################################### + +.. _slurm: + +---- + +****************************** +Run on a SLURM managed cluster +****************************** +Lightning automates the details behind training on a SLURM-powered cluster. In contrast to the general purpose +cluster above, the user does not start the jobs manually on each node and instead submits it to SLURM which +schedules the resources and time for which the job is allowed to run. + +---- + +*************************** +Design your training script +*************************** + +To train a model using multiple nodes, do the following: + +1. Design your :ref:`lightning_module` (no need to add anything specific here). + +2. Enable DDP in the trainer + + .. code-block:: python + + # train on 32 GPUs across 4 nodes + trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp") + +3. It's a good idea to structure your training script like this: + + .. testcode:: + + # train.py + def main(args): + model = YourLightningModule(args) + + trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp") + + trainer.fit(model) + + + if __name__ == "__main__": + args = ... # you can use your CLI parser of choice, or the `LightningCLI` + # TRAIN + main(args) + +4. Create the appropriate SLURM job: + + .. code-block:: bash + + # (submit.sh) + #!/bin/bash -l + + # SLURM SUBMIT SCRIPT + #SBATCH --nodes=4 # This needs to match Trainer(num_nodes=...) + #SBATCH --gres=gpu:8 + #SBATCH --ntasks-per-node=8 # This needs to match Trainer(devices=...) + #SBATCH --mem=0 + #SBATCH --time=0-02:00:00 + + # activate conda env + source activate $1 + + # debugging flags (optional) + export NCCL_DEBUG=INFO + export PYTHONFAULTHANDLER=1 + + # on your cluster you might need these: + # set the network interface + # export NCCL_SOCKET_IFNAME=^docker0,lo + + # might need the latest CUDA + # module load NCCL/2.4.7-1-cuda.10.0 + + # run script from above + srun python3 train.py + +5. If you want auto-resubmit (read below), add this line to the submit.sh script + + .. code-block:: bash + + #SBATCH --signal=SIGUSR1@90 + +6. Submit the SLURM job + + .. code-block:: bash + + sbatch submit.sh + +---- + +********************************** +Enable auto wall-time resubmitions +********************************** +When you use Lightning in a SLURM cluster, it automatically detects when it is about +to run into the wall time and does the following: + +1. Saves a temporary checkpoint. +2. Requeues the job. +3. When the job starts, it loads the temporary checkpoint. + +To get this behavior make sure to add the correct signal to your SLURM script + +.. code-block:: bash + + # 90 seconds before training ends + SBATCH --signal=SIGUSR1@90 + +You can change this signal if your environment requires the use of a different one, for example + +.. code-block:: bash + + #SBATCH --signal=SIGHUP@90 + +Then, when you make your trainer, pass the `requeue_signal` option to the :class:`~lightning.pytorch.plugins.environments.slurm_environment.SLURMEnvironment` plugin: + +.. code-block:: python + + trainer = Trainer(plugins=[SLURMEnvironment(requeue_signal=signal.SIGHUP)]) + +If auto-resubmit is not desired, it can be turned off in the :class:`~lightning.pytorch.plugins.environments.slurm_environment.SLURMEnvironment` plugin: + +.. code-block:: python + + from lightning.pytorch.plugins.environments import SLURMEnvironment + + trainer = Trainer(plugins=[SLURMEnvironment(auto_requeue=False)]) + +---- + + +**************** +Interactive Mode +**************** + +You can also let SLURM schedule a machine for you and then log in to the machine to run scripts manually. +This is useful for development and debugging. +If you set the job name to *bash* or *interactive*, and then log in and run scripts, Lightning's SLURM auto-detection will get bypassed and it can launch processes normally: + +.. code-block:: bash + + # make sure to set `--job-name "interactive"` + srun --account --pty bash --job-name "interactive" ... + + # now run scripts normally + python train.py ... + + +---- + + +*************** +Troubleshooting +*************** + +**The Trainer is stuck initializing at startup, what is causing this?** + +You are seeing a message like this in the logs but nothing happens: + +.. code-block:: + + Initializing distributed: GLOBAL_RANK: 0, MEMBER: 1/4 + + +The most likely reasons and how to fix it: + +- You forgot to run the ``python train.py`` command with ``srun``: + Please have a look at the SLURM template script above which includes the ``srun`` at the botton of the script. + +- The number of nodes or number of devices per node is configured incorrectly: + There are two parametres in the SLURM submission script that determine how many processes will run your training, the ``#SBATCH --nodes=X`` setting and ``#SBATCH --ntasks-per-node=Y`` settings. + The numbers there need to match what is configured in your Trainer in the code: ``Trainer(num_nodes=X, devices=Y)``. + If you change the numbers, update them in BOTH places. diff --git a/docs/source-pytorch/clouds/cluster_expert.rst b/docs/source-pytorch/clouds/cluster_expert.rst new file mode 100644 index 0000000..4306b3f --- /dev/null +++ b/docs/source-pytorch/clouds/cluster_expert.rst @@ -0,0 +1,51 @@ +:orphan: + +################################## +Run on an on-prem cluster (expert) +################################## + +.. _custom-cluster: + +---- + +************************** +Integrate your own cluster +************************** + +Lightning provides an interface for providing your own definition of a cluster environment. It mainly consists of +parsing the right environment variables to access information such as world size, global and local rank (process id), +and node rank (node id). Here is an example of a custom +:class:`~lightning.pytorch.plugins.environments.cluster_environment.ClusterEnvironment`: + +.. code-block:: python + + import os + from lightning.pytorch.plugins.environments import ClusterEnvironment + + + class MyClusterEnvironment(ClusterEnvironment): + @property + def creates_processes_externally(self) -> bool: + """Return True if the cluster is managed (you don't launch processes yourself)""" + return True + + def world_size(self) -> int: + return int(os.environ["WORLD_SIZE"]) + + def global_rank(self) -> int: + return int(os.environ["RANK"]) + + def local_rank(self) -> int: + return int(os.environ["LOCAL_RANK"]) + + def node_rank(self) -> int: + return int(os.environ["NODE_RANK"]) + + def main_address(self) -> str: + return os.environ["MASTER_ADDRESS"] + + def main_port(self) -> int: + return int(os.environ["MASTER_PORT"]) + + + trainer = Trainer(plugins=[MyClusterEnvironment()]) diff --git a/source/clouds/cluster_intermediate_1.rst b/docs/source-pytorch/clouds/cluster_intermediate_1.rst similarity index 77% rename from source/clouds/cluster_intermediate_1.rst rename to docs/source-pytorch/clouds/cluster_intermediate_1.rst index c2d92e2..d668b2b 100644 --- a/source/clouds/cluster_intermediate_1.rst +++ b/docs/source-pytorch/clouds/cluster_intermediate_1.rst @@ -24,7 +24,7 @@ PyTorch Lightning follows the design of `PyTorch distributed communication packa - *MASTER_PORT* - required; has to be a free port on machine with NODE_RANK 0 - *MASTER_ADDR* - required (except for NODE_RANK 0); address of NODE_RANK 0 node -- *WORLD_SIZE* - required; how many nodes are in the cluster +- *WORLD_SIZE* - required; the total number of GPUs/processes that you will use - *NODE_RANK* - required; id of the node in the cluster .. _training_script_setup: @@ -68,32 +68,3 @@ Set the ``NCCL_DEBUG=INFO`` environment variable to see the ACTUAL error. .. code-block:: bash NCCL_DEBUG=INFO python train.py ... - ----- - -******** -Get help -******** -Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI. - -Try it out for free today: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: cloud_training.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
`__ provides helper functions to setup distributed environment variables from the `PyTorch distributed communication package `__ that need to be defined on each node. + +Once the script is setup like described in :ref:` Training Script Setup`, you can run the below command across your nodes to start multi-node training. + +Like a custom cluster, you have to ensure that there is network connectivity between the nodes with firewall rules that allow traffic flow on a specified *MASTER_PORT*. + +Finally, you'll need to decide which node you'd like to be the main node (*MASTER_ADDR*), and the ranks of each node (*NODE_RANK*). + +For example: + +* *MASTER_ADDR* 10.10.10.16 +* *MASTER_PORT* 29500 +* *NODE_RANK* 0 for the first node, 1 for the second node + +Run the below command with the appropriate variables set on each node. + +.. code-block:: bash + + python -m torch.distributed.run + --nnodes=2 # number of nodes you'd like to run with + --master_addr + --master_port + --node_rank + train.py (--arg1 ... train script args...) + +.. note:: + + ``torch.distributed.run`` assumes that you'd like to spawn a process per GPU if GPU devices are found on the node. This can be adjusted with ``-nproc_per_node``. diff --git a/docs/source-pytorch/common/checkpointing.rst b/docs/source-pytorch/common/checkpointing.rst new file mode 100644 index 0000000..fdb4e85 --- /dev/null +++ b/docs/source-pytorch/common/checkpointing.rst @@ -0,0 +1,78 @@ +.. _checkpointing: + +############# +Checkpointing +############# + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Saving and loading checkpoints + :description: Learn to save and load checkpoints + :col_css: col-md-4 + :button_link: checkpointing_basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Customize checkpointing behavior + :description: Learn how to change the behavior of checkpointing + :col_css: col-md-4 + :button_link: checkpointing_intermediate.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Upgrading checkpoints + :description: Learn how to upgrade old checkpoints to the newest Lightning version + :col_css: col-md-4 + :button_link: checkpointing_migration.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Cloud-based checkpoints + :description: Enable cloud-based checkpointing and composable checkpoints. + :col_css: col-md-4 + :button_link: checkpointing_advanced.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Distributed checkpoints + :description: Customize checkpointing for custom distributed strategies and accelerators. + :col_css: col-md-4 + :button_link: checkpointing_expert.html + :height: 150 + :tag: expert + +.. raw:: html + +
+
+ +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: ModelCheckpoint API + :description: Dig into the ModelCheckpoint API + :col_css: col-md-4 + :button_link: ../api/lightning.pytorch.callbacks.ModelCheckpoint.html + :height: 150 + +.. raw:: html + +
+
diff --git a/source/common/checkpointing_advanced.rst b/docs/source-pytorch/common/checkpointing_advanced.rst similarity index 85% rename from source/common/checkpointing_advanced.rst rename to docs/source-pytorch/common/checkpointing_advanced.rst index 561ca95..80b10e1 100644 --- a/source/common/checkpointing_advanced.rst +++ b/docs/source-pytorch/common/checkpointing_advanced.rst @@ -1,8 +1,8 @@ .. _checkpointing_advanced: -######################## -Checkpointing (advanced) -######################## +################################## +Cloud-based checkpoints (advanced) +################################## ***************** @@ -52,7 +52,7 @@ Checkpoints can also save the state of :doc:`datamodules <../extensions/datamodu **************************** Modify a checkpoint anywhere **************************** -When you need to change the components of a checkpoint before saving or loading, use the :meth:`~pytorch_lightning.core.hooks.CheckpointHooks.on_save_checkpoint` and :meth:`~pytorch_lightning.core.hooks.CheckpointHooks.on_load_checkpoint` of your ``LightningModule``. +When you need to change the components of a checkpoint before saving or loading, use the :meth:`~lightning.pytorch.core.hooks.CheckpointHooks.on_save_checkpoint` and :meth:`~lightning.pytorch.core.hooks.CheckpointHooks.on_load_checkpoint` of your ``LightningModule``. .. code:: python @@ -63,7 +63,7 @@ When you need to change the components of a checkpoint before saving or loading, def on_load_checkpoint(self, checkpoint): my_cool_pickable_object = checkpoint["something_cool_i_want_to_save"] -Use the above approach when you need to couple this behavior to your LightningModule for reproducibility reasons. Otherwise, Callbacks also have the :meth:`~pytorch_lightning.callbacks.base.Callback.on_save_checkpoint` and :meth:`~pytorch_lightning.callbacks.base.Callback.on_load_checkpoint` which you should use instead: +Use the above approach when you need to couple this behavior to your LightningModule for reproducibility reasons. Otherwise, Callbacks also have the :meth:`~lightning.pytorch.callbacks.callback.Callback.on_save_checkpoint` and :meth:`~lightning.pytorch.callbacks.callback.Callback.on_load_checkpoint` which you should use instead: .. code:: python diff --git a/source/common/checkpointing_basic.rst b/docs/source-pytorch/common/checkpointing_basic.rst similarity index 92% rename from source/common/checkpointing_basic.rst rename to docs/source-pytorch/common/checkpointing_basic.rst index 899de91..ab45d93 100644 --- a/source/common/checkpointing_basic.rst +++ b/docs/source-pytorch/common/checkpointing_basic.rst @@ -2,9 +2,9 @@ .. _checkpointing_basic: -##################### -Checkpointing (basic) -##################### +###################################### +Saving and loading checkpoints (basic) +###################################### **Audience:** All users ---- @@ -35,8 +35,9 @@ Inside a Lightning checkpoint you'll find: - State of all learning rate schedulers - State of all callbacks (for stateful callbacks) - State of datamodule (for stateful datamodules) -- The hyperparameters used for that model if passed in as hparams (Argparse.Namespace) -- State of Loops (if using Fault-Tolerant training) +- The hyperparameters (init arguments) with which the model was created +- The hyperparameters (init arguments) with which the datamodule was created +- State of Loops ---- @@ -105,8 +106,8 @@ The LightningModule also has access to the Hyperparameters ---- -Initalize with other parameters -=============================== +Initialize with other parameters +================================ If you used the *self.save_hyperparameters()* method in the init of the LightningModule, you can initialize the model with different hyperparameters. .. code-block:: python @@ -185,5 +186,5 @@ If you don't just want to load weights, but instead restore the full training, d model = LitModel() trainer = Trainer() - # automatically restores model, epoch, step, LR schedulers, apex, etc... + # automatically restores model, epoch, step, LR schedulers, etc... trainer.fit(model, ckpt_path="some/path/to/my_checkpoint.ckpt") diff --git a/docs/source-pytorch/common/checkpointing_expert.rst b/docs/source-pytorch/common/checkpointing_expert.rst new file mode 100644 index 0000000..721066d --- /dev/null +++ b/docs/source-pytorch/common/checkpointing_expert.rst @@ -0,0 +1,126 @@ +:orphan: + +.. _checkpointing_expert: + +################################ +Distributed checkpoints (expert) +################################ + +********************************* +Writing your own Checkpoint class +********************************* + +We provide ``Checkpoint`` class, for easier subclassing. Users may want to subclass this class in case of writing custom ``ModelCheckpoint`` callback, so that the ``Trainer`` recognizes the custom class as a checkpointing callback. + + +*********************** +Customize Checkpointing +*********************** + +.. warning:: This is an :ref:`experimental ` feature. + +Lightning supports modifying the checkpointing save/load functionality through the ``CheckpointIO``. This encapsulates the save/load logic +that is managed by the ``Strategy``. ``CheckpointIO`` is different from :meth:`~lightning.pytorch.core.hooks.CheckpointHooks.on_save_checkpoint` +and :meth:`~lightning.pytorch.core.hooks.CheckpointHooks.on_load_checkpoint` methods as it determines how the checkpoint is saved/loaded to storage rather than +what's saved in the checkpoint. + + +TODO: I don't understand this... + +****************************** +Built-in Checkpoint IO Plugins +****************************** + +.. list-table:: Built-in Checkpoint IO Plugins + :widths: 25 75 + :header-rows: 1 + + * - Plugin + - Description + * - :class:`~lightning.pytorch.plugins.io.TorchCheckpointIO` + - CheckpointIO that utilizes :func:`torch.save` and :func:`torch.load` to save and load checkpoints + respectively, common for most use cases. + * - :class:`~lightning.pytorch.plugins.io.XLACheckpointIO` + - CheckpointIO that utilizes :func:`xm.save` to save checkpoints for TPU training strategies. + * - :class:`~lightning.pytorch.plugins.io.AsyncCheckpointIO` + - ``AsyncCheckpointIO`` enables saving the checkpoints asynchronously in a thread. + + +*************************** +Custom Checkpoint IO Plugin +*************************** + +``CheckpointIO`` can be extended to include your custom save/load functionality to and from a path. The ``CheckpointIO`` object can be passed to either a ``Trainer`` directly or a ``Strategy`` as shown below: + +.. code-block:: python + + from lightning.pytorch import Trainer + from lightning.pytorch.callbacks import ModelCheckpoint + from lightning.pytorch.plugins import CheckpointIO + from lightning.pytorch.strategies import SingleDeviceStrategy + + + class CustomCheckpointIO(CheckpointIO): + def save_checkpoint(self, checkpoint, path, storage_options=None): + ... + + def load_checkpoint(self, path, storage_options=None): + ... + + def remove_checkpoint(self, path): + ... + + + custom_checkpoint_io = CustomCheckpointIO() + + # Either pass into the Trainer object + model = MyModel() + trainer = Trainer( + plugins=[custom_checkpoint_io], + callbacks=ModelCheckpoint(save_last=True), + ) + trainer.fit(model) + + # or pass into Strategy + model = MyModel() + device = torch.device("cpu") + trainer = Trainer( + strategy=SingleDeviceStrategy(device, checkpoint_io=custom_checkpoint_io), + callbacks=ModelCheckpoint(save_last=True), + ) + trainer.fit(model) + +.. note:: + + Some ``Strategy``s like ``DeepSpeedStrategy`` do not support custom ``CheckpointIO`` as checkpointing logic is not modifiable. + + +************************** +Asynchronous Checkpointing +************************** + +.. warning:: This is an :ref:`experimental ` feature. + +To enable saving the checkpoints asynchronously without blocking your training, you can configure +:class:`~lightning.pytorch.plugins.io.async_plugin.AsyncCheckpointIO` plugin to ``Trainer``. + +.. code-block:: python + + from lightning.pytorch.plugins.io import AsyncCheckpointIO + + + async_ckpt_io = AsyncCheckpointIO() + trainer = Trainer(plugins=[async_ckpt_io]) + + +It uses its base ``CheckpointIO`` plugin's saving logic to save the checkpoint but performs this operation asynchronously. +By default, this base ``CheckpointIO`` will be set-up for you and all you need to provide is the ``AsyncCheckpointIO`` instance to the ``Trainer``. +But if you want the plugin to use your own custom base ``CheckpointIO`` and want the base to behave asynchronously, pass it as an argument while initializing ``AsyncCheckpointIO``. + +.. code-block:: python + + from lightning.pytorch.plugins.io import AsyncCheckpointIO + + base_ckpt_io = MyCustomCheckpointIO() + async_ckpt_io = AsyncCheckpointIO(checkpoint_io=base_ckpt_io) + trainer = Trainer(plugins=[async_ckpt_io]) diff --git a/source/common/checkpointing_intermediate.rst b/docs/source-pytorch/common/checkpointing_intermediate.rst similarity index 75% rename from source/common/checkpointing_intermediate.rst rename to docs/source-pytorch/common/checkpointing_intermediate.rst index 7796575..c6b1951 100644 --- a/source/common/checkpointing_intermediate.rst +++ b/docs/source-pytorch/common/checkpointing_intermediate.rst @@ -1,10 +1,10 @@ :orphan: -.. _checkpointing_intermediate: +.. _checkpointing_intermediate_1: -############################ -Checkpointing (intermediate) -############################ +############################################### +Customize checkpointing behavior (intermediate) +############################################### **Audience:** Users looking to customize the checkpointing behavior ---- @@ -12,11 +12,11 @@ Checkpointing (intermediate) ***************************** Modify checkpointing behavior ***************************** -For fine-grain control over checkpointing behavior, use the :class:`~pytorch_lightning.callbacks.ModelCheckpoint` object +For fine-grained control over checkpointing behavior, use the :class:`~lightning.pytorch.callbacks.ModelCheckpoint` object .. code-block:: python - from pytorch_lightning.callbacks import ModelCheckpoint + from lightning.pytorch.callbacks import ModelCheckpoint checkpoint_callback = ModelCheckpoint(dirpath="my/path/", save_top_k=2, monitor="val_loss") trainer = Trainer(callbacks=[checkpoint_callback]) @@ -40,15 +40,15 @@ Any value that has been logged via *self.log* in the LightningModule can be moni ***************************** Save checkpoints by condition ***************************** -To save checkpoints based on a (*when/which/what/where*) condition (for example *when* the validation_loss is lower) modify the :class:`~pytorch_lightning.callbacks.ModelCheckpoint` properties. +To save checkpoints based on a (*when/which/what/where*) condition (for example *when* the validation_loss is lower) modify the :class:`~lightning.pytorch.callbacks.ModelCheckpoint` properties. When ==== -- When using iterative training which doesn't have an epoch, you can checkpoint at every ``N`` training steps by specifying ``every_n_training_steps=N``. -- You can also control the interval of epochs between checkpoints using ``every_n_epochs`` between checkpoints, to avoid slowdowns. -- You can checkpoint at a regular time interval using ``train_time_interval`` argument independent of the steps or epochs. -- In case you are monitoring a training metrics, we'd suggest using ``save_on_train_epoch_end=True`` to ensure the required metric is being accumulated correctly for creating a checkpoint. +- When using iterative training which doesn't have an epoch, you can checkpoint at every ``N`` training steps by specifying ``every_n_train_steps=N``. +- You can also control the interval of epochs between checkpoints using ``every_n_epochs``, to avoid slowdowns. +- You can checkpoint at a regular time interval using the ``train_time_interval`` argument independent of the steps or epochs. +- In case you are monitoring a training metric, we'd suggest using ``save_on_train_epoch_end=True`` to ensure the required metric is being accumulated correctly for creating a checkpoint. Which @@ -61,7 +61,7 @@ Which .. testcode:: - from pytorch_lightning.callbacks import ModelCheckpoint + from lightning.pytorch.callbacks import ModelCheckpoint # saves top-K checkpoints based on "val_loss" metric @@ -89,7 +89,7 @@ Which .. testcode:: - from pytorch_lightning.callbacks import ModelCheckpoint + from lightning.pytorch.callbacks import ModelCheckpoint class LitAutoEncoder(LightningModule): @@ -120,13 +120,13 @@ What Where ===== -- It gives you the ability to specify the ``dirpath`` and ``filename`` for your checkpoints. Filename can also be dynamic so you can inject the metrics that are being logged using :meth:`~pytorch_lightning.core.lightning.LightningModule.log`. +- By default, the ``ModelCheckpoint`` will save files into the ``Trainer.log_dir``. It gives you the ability to specify the ``dirpath`` and ``filename`` for your checkpoints. Filename can also be dynamic so you can inject the metrics that are being logged using :meth:`~lightning.pytorch.core.module.LightningModule.log`. | .. testcode:: - from pytorch_lightning.callbacks import ModelCheckpoint + from lightning.pytorch.callbacks import ModelCheckpoint # saves a file like: my/path/sample-mnist-epoch=02-val_loss=0.32.ckpt @@ -137,7 +137,7 @@ Where | -The :class:`~pytorch_lightning.callbacks.ModelCheckpoint` callback is very robust and should cover 99% of the use-cases. If you find a use-case that is not configured yet, feel free to open an issue with a feature request on GitHub +The :class:`~lightning.pytorch.callbacks.ModelCheckpoint` callback is very robust and should cover 99% of the use-cases. If you find a use-case that is not configured yet, feel free to open an issue with a feature request on GitHub and the Lightning Team will be happy to integrate/help integrate it. ---- @@ -146,8 +146,8 @@ and the Lightning Team will be happy to integrate/help integrate it. Save checkpoints manually ************************* -You can manually save checkpoints and restore your model from the checkpointed state using :meth:`~pytorch_lightning.trainer.trainer.Trainer.save_checkpoint` -and :meth:`~pytorch_lightning.core.saving.ModelIO.load_from_checkpoint`. +You can manually save checkpoints and restore your model from the checkpointed state using :meth:`~lightning.pytorch.trainer.trainer.Trainer.save_checkpoint` +and :meth:`~lightning.pytorch.core.module.LightningModule.load_from_checkpoint`. .. code-block:: python @@ -170,6 +170,6 @@ In distributed training cases where a model is running across many machines, Lig # Saves only on the main process trainer.save_checkpoint("example.ckpt") -Not using :meth:`~pytorch_lightning.trainer.trainer.Trainer.save_checkpoint` can lead to unexpected behavior and potential deadlock. Using other saving functions will result in all devices attempting to save the checkpoint. As a result, we highly recommend using the Trainer's save functionality. -If using custom saving functions cannot be avoided, we recommend using the :func:`~pytorch_lightning.utilities.rank_zero.rank_zero_only` decorator to ensure saving occurs only on the main process. Note that this will only work if all ranks hold the exact same state and won't work when using +Not using :meth:`~lightning.pytorch.trainer.trainer.Trainer.save_checkpoint` can lead to unexpected behavior and potential deadlock. Using other saving functions will result in all devices attempting to save the checkpoint. As a result, we highly recommend using the Trainer's save functionality. +If using custom saving functions cannot be avoided, we recommend using the :func:`~lightning.pytorch.utilities.rank_zero.rank_zero_only` decorator to ensure saving occurs only on the main process. Note that this will only work if all ranks hold the exact same state and won't work when using model parallel distributed strategies such as deepspeed or sharded training. diff --git a/docs/source-pytorch/common/checkpointing_migration.rst b/docs/source-pytorch/common/checkpointing_migration.rst new file mode 100644 index 0000000..5c536cc --- /dev/null +++ b/docs/source-pytorch/common/checkpointing_migration.rst @@ -0,0 +1,51 @@ +:orphan: + +.. _checkpointing_intermediate_2: + +#################################### +Upgrading checkpoints (intermediate) +#################################### +**Audience:** Users who are upgrading Lightning and their code and want to reuse their old checkpoints. + +---- + +************************************** +Resume training from an old checkpoint +************************************** + +Next to the model weights and trainer state, a Lightning checkpoint contains the version number of Lightning with which the checkpoint was saved. +When you load a checkpoint file, either by resuming training + +.. code-block:: python + + trainer = Trainer(...) + trainer.fit(model, ckpt_path="path/to/checkpoint.ckpt") + +or by loading the state directly into your model, + +.. code-block:: python + + model = LitModel.load_from_checkpoint("path/to/checkpoint.ckpt") + +Lightning will automatically recognize that it is from an older version and migrates the internal structure so it can be loaded properly. +This is done without any action required by the user. + +---- + +************************************ +Upgrade checkpoint files permanently +************************************ + +When Lightning loads a checkpoint, it applies the version migration on-the-fly as explained above, but it does not modify your checkpoint files. +You can upgrade checkpoint files permanently with the following command + +.. code-block:: + + python -m lightning.pytorch.utilities.upgrade_checkpoint path/to/model.ckpt + + +or a folder with multiple files: + +.. code-block:: + + python -m lightning.pytorch.utilities.upgrade_checkpoint /path/to/checkpoints/folder diff --git a/source/common/child_modules.rst b/docs/source-pytorch/common/child_modules.rst similarity index 96% rename from source/common/child_modules.rst rename to docs/source-pytorch/common/child_modules.rst index d3c1832..ab6e395 100644 --- a/source/common/child_modules.rst +++ b/docs/source-pytorch/common/child_modules.rst @@ -61,7 +61,7 @@ and we can train this using the ``Trainer``: trainer = Trainer() trainer.fit(lightning_module, train_dataloader, val_dataloader) -And remember that the forward method should define the practical use of a :class:`~pytorch_lightning.core.lightning.LightningModule`. +And remember that the forward method should define the practical use of a :class:`~lightning.pytorch.core.module.LightningModule`. In this case, we want to use the ``LitAutoEncoder`` to extract image representations: .. code-block:: python diff --git a/source/common/console_logs.rst b/docs/source-pytorch/common/console_logs.rst similarity index 86% rename from source/common/console_logs.rst rename to docs/source-pytorch/common/console_logs.rst index 6761432..210f14b 100644 --- a/source/common/console_logs.rst +++ b/docs/source-pytorch/common/console_logs.rst @@ -17,10 +17,10 @@ or redirect output for certain modules to log files: import logging # configure logging at the root level of Lightning - logging.getLogger("pytorch_lightning").setLevel(logging.ERROR) + logging.getLogger("lightning.pytorch").setLevel(logging.ERROR) # configure logging on module level, redirect to file - logger = logging.getLogger("pytorch_lightning.core") + logger = logging.getLogger("lightning.pytorch.core") logger.addHandler(logging.FileHandler("core.log")) Read more about custom Python logging `here `_. diff --git a/docs/source-pytorch/common/early_stopping.rst b/docs/source-pytorch/common/early_stopping.rst new file mode 100644 index 0000000..c1dc00b --- /dev/null +++ b/docs/source-pytorch/common/early_stopping.rst @@ -0,0 +1,98 @@ +.. testsetup:: * + + from lightning.pytorch.callbacks.early_stopping import EarlyStopping + +.. _early_stopping: + + +############## +Early Stopping +############## + +.. video:: ../_static/fetched-s3-assets/Trainer+flags+19-+early+stopping_1.mp4 + :poster: ../_static/fetched-s3-assets/thumb_earlystop.png + :width: 400 + :muted: + + +*********************** +Stopping an Epoch Early +*********************** + +You can stop and skip the rest of the current epoch early by overriding :meth:`~lightning.pytorch.core.hooks.ModelHooks.on_train_batch_start` to return ``-1`` when some condition is met. + +If you do this repeatedly, for every epoch you had originally requested, then this will stop your entire training. + + +********************** +EarlyStopping Callback +********************** + +The :class:`~lightning.pytorch.callbacks.early_stopping.EarlyStopping` callback can be used to monitor a metric and stop the training when no improvement is observed. + +To enable it: + +- Import :class:`~lightning.pytorch.callbacks.early_stopping.EarlyStopping` callback. +- Log the metric you want to monitor using :meth:`~lightning.pytorch.core.module.LightningModule.log` method. +- Init the callback, and set ``monitor`` to the logged metric of your choice. +- Set the ``mode`` based on the metric needs to be monitored. +- Pass the :class:`~lightning.pytorch.callbacks.early_stopping.EarlyStopping` callback to the :class:`~lightning.pytorch.trainer.trainer.Trainer` callbacks flag. + +.. code-block:: python + + from lightning.pytorch.callbacks.early_stopping import EarlyStopping + + + class LitModel(LightningModule): + def validation_step(self, batch, batch_idx): + loss = ... + self.log("val_loss", loss) + + + model = LitModel() + trainer = Trainer(callbacks=[EarlyStopping(monitor="val_loss", mode="min")]) + trainer.fit(model) + +You can customize the callbacks behaviour by changing its parameters. + +.. testcode:: + + early_stop_callback = EarlyStopping(monitor="val_accuracy", min_delta=0.00, patience=3, verbose=False, mode="max") + trainer = Trainer(callbacks=[early_stop_callback]) + + +Additional parameters that stop training at extreme points: + +- ``stopping_threshold``: Stops training immediately once the monitored quantity reaches this threshold. + It is useful when we know that going beyond a certain optimal value does not further benefit us. +- ``divergence_threshold``: Stops training as soon as the monitored quantity becomes worse than this threshold. + When reaching a value this bad, we believes the model cannot recover anymore and it is better to stop early and run with different initial conditions. +- ``check_finite``: When turned on, it stops training if the monitored metric becomes NaN or infinite. +- ``check_on_train_epoch_end``: When turned on, it checks the metric at the end of a training epoch. Use this only when you are monitoring any metric logged within + training-specific hooks on epoch-level. + + +In case you need early stopping in a different part of training, subclass :class:`~lightning.pytorch.callbacks.early_stopping.EarlyStopping` +and change where it is called: + +.. testcode:: + + class MyEarlyStopping(EarlyStopping): + def on_validation_end(self, trainer, pl_module): + # override this to disable early stopping at the end of val loop + pass + + def on_train_end(self, trainer, pl_module): + # instead, do it at the end of training loop + self._run_early_stopping_check(trainer) + +.. note:: + The :class:`~lightning.pytorch.callbacks.early_stopping.EarlyStopping` callback runs + at the end of every validation epoch by default. However, the frequency of validation + can be modified by setting various parameters in the :class:`~lightning.pytorch.trainer.trainer.Trainer`, + for example :paramref:`~lightning.pytorch.trainer.trainer.Trainer.check_val_every_n_epoch` + and :paramref:`~lightning.pytorch.trainer.trainer.Trainer.val_check_interval`. + It must be noted that the ``patience`` parameter counts the number of + validation checks with no improvement, and not the number of training epochs. + Therefore, with parameters ``check_val_every_n_epoch=10`` and ``patience=3``, the trainer + will perform at least 40 training epochs before being stopped. diff --git a/source/common/evaluation.rst b/docs/source-pytorch/common/evaluation.rst similarity index 100% rename from source/common/evaluation.rst rename to docs/source-pytorch/common/evaluation.rst diff --git a/source/common/evaluation_basic.rst b/docs/source-pytorch/common/evaluation_basic.rst similarity index 90% rename from source/common/evaluation_basic.rst rename to docs/source-pytorch/common/evaluation_basic.rst index 5f933ee..80da873 100644 --- a/source/common/evaluation_basic.rst +++ b/docs/source-pytorch/common/evaluation_basic.rst @@ -24,10 +24,12 @@ Datasets come with two splits. Refer to the dataset documentation to find the *t import torch.utils.data as data from torchvision import datasets + import torchvision.transforms as transforms # Load data sets - train_set = datasets.MNIST(root="MNIST", download=True, train=True) - test_set = datasets.MNIST(root="MNIST", download=True, train=False) + transform = transforms.ToTensor() + train_set = datasets.MNIST(root="MNIST", download=True, train=True, transform=transform) + test_set = datasets.MNIST(root="MNIST", download=True, train=False, transform=transform) ---- @@ -107,8 +109,8 @@ To add a validation loop, implement the **validation_step** method of the Lightn x = x.view(x.size(0), -1) z = self.encoder(x) x_hat = self.decoder(z) - test_loss = F.mse_loss(x_hat, x) - self.log("val_loss", test_loss) + val_loss = F.mse_loss(x_hat, x) + self.log("val_loss", val_loss) ---- @@ -120,9 +122,9 @@ To run the validation loop, pass in the validation set to **.fit** from torch.utils.data import DataLoader - train_set = DataLoader(train_set) - val_set = DataLoader(val_set) + train_loader = DataLoader(train_set) + valid_loader = DataLoader(valid_set) # train with both splits trainer = Trainer() - trainer.fit(model, train_set, val_set) + trainer.fit(model, train_loader, valid_loader) diff --git a/source/common/evaluation_intermediate.rst b/docs/source-pytorch/common/evaluation_intermediate.rst similarity index 84% rename from source/common/evaluation_intermediate.rst rename to docs/source-pytorch/common/evaluation_intermediate.rst index 7c0ca00..91f018c 100644 --- a/source/common/evaluation_intermediate.rst +++ b/docs/source-pytorch/common/evaluation_intermediate.rst @@ -22,12 +22,12 @@ Testing ******* Lightning allows the user to test their models with any compatible test dataloaders. This can be done before/after training -and is completely agnostic to :meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` call. The logic used here is defined under -:meth:`~pytorch_lightning.core.lightning.LightningModule.test_step`. +and is completely agnostic to :meth:`~lightning.pytorch.trainer.trainer.Trainer.fit` call. The logic used here is defined under +:meth:`~lightning.pytorch.core.module.LightningModule.test_step`. Testing is performed using the ``Trainer`` object's ``.test()`` method. -.. automethod:: pytorch_lightning.trainer.Trainer.test +.. automethod:: lightning.pytorch.trainer.Trainer.test :noindex: @@ -44,10 +44,13 @@ To run the test set after training completes, use this method. # (1) load the best checkpoint automatically (lightning tracks this for you) trainer.test(ckpt_path="best") - # (2) test using a specific checkpoint + # (2) load the last available checkpoint + trainer.test(ckpt_path="last") + + # (3) test using a specific checkpoint trainer.test(ckpt_path="/path/to/my_checkpoint.ckpt") - # (3) test with an explicit model (will use this model and not load a checkpoint) + # (4) test with an explicit model (will use this model and not load a checkpoint) trainer.test(model) .. warning:: @@ -82,7 +85,7 @@ To run the test set on a pre-trained model, use this method. model = MyLightningModule.load_from_checkpoint( checkpoint_path="/path/to/pytorch_checkpoint.ckpt", - hparams_file="/path/to/test_tube/experiment/version/hparams.yaml", + hparams_file="/path/to/experiment/version/hparams.yaml", map_location=None, ) @@ -99,7 +102,7 @@ running the test set (ie: 16-bit, dp, ddp, etc...) Test with Additional DataLoaders ================================ -You can still run inference on a test dataset even if the :meth:`~pytorch_lightning.core.hooks.DataHooks.test_dataloader` method hasn't been +You can still run inference on a test dataset even if the :meth:`~lightning.pytorch.core.hooks.DataHooks.test_dataloader` method hasn't been defined within your :doc:`lightning module <../common/lightning_module>` instance. This would be the case when your test data is not available at the time your model was declared. @@ -138,13 +141,13 @@ Validation ********** Lightning allows the user to validate their models with any compatible ``val dataloaders``. This can be done before/after training. -The logic associated to the validation is defined within the :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step`. +The logic associated to the validation is defined within the :meth:`~lightning.pytorch.core.module.LightningModule.validation_step`. -Apart from this ``.validate`` has same API as ``.test``, but would rely respectively on :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step` and :meth:`~pytorch_lightning.core.lightning.LightningModule.test_step`. +Apart from this ``.validate`` has same API as ``.test``, but would rely respectively on :meth:`~lightning.pytorch.core.module.LightningModule.validation_step` and :meth:`~lightning.pytorch.core.module.LightningModule.test_step`. .. note:: ``.validate`` method uses the same validation logic being used under validation happening within - :meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` call. + :meth:`~lightning.pytorch.trainer.trainer.Trainer.fit` call. .. warning:: @@ -153,5 +156,5 @@ Apart from this ``.validate`` has same API as ``.test``, but would rely respecti make sure all devices have same batch size in case of uneven inputs. This is helpful to make sure benchmarking for research papers is done the right way. -.. automethod:: pytorch_lightning.trainer.Trainer.validate +.. automethod:: lightning.pytorch.trainer.Trainer.validate :noindex: diff --git a/docs/source-pytorch/common/gradient_accumulation.rst b/docs/source-pytorch/common/gradient_accumulation.rst new file mode 100644 index 0000000..2a71d56 --- /dev/null +++ b/docs/source-pytorch/common/gradient_accumulation.rst @@ -0,0 +1,33 @@ +Accumulated gradients run K small batches of size ``N`` before doing a backward pass. The effect is a large effective batch size of size ``KxN``, where ``N`` is the batch size. +Internally it doesn't stack up the batches and do a forward pass rather it accumulates the gradients for K batches and then do an ``optimizer.step`` to make sure the +effective batch size is increased but there is no memory overhead. + +.. warning:: + + When using distributed training for eg. DDP, with let's say with ``P`` devices, each device accumulates independently i.e. it stores the gradients + after each ``loss.backward()`` and doesn't sync the gradients across the devices until we call ``optimizer.step()``. So for each accumulation + step, the effective batch size on each device will remain ``N*K`` but right before the ``optimizer.step()``, the gradient sync will make the effective + batch size as ``P*N*K``. For DP, since the batch is split across devices, the final effective batch size will be ``N*K``. + +.. testcode:: + + # DEFAULT (ie: no accumulated grads) + trainer = Trainer(accumulate_grad_batches=1) + + # Accumulate gradients for 7 batches + trainer = Trainer(accumulate_grad_batches=7) + +Optionally, you can make the ``accumulate_grad_batches`` value change over time by using the :class:`~lightning.pytorch.callbacks.gradient_accumulation_scheduler.GradientAccumulationScheduler`. +Pass in a scheduling dictionary, where the key represents the epoch at which the value for gradient accumulation should be updated. + +.. testcode:: + + from lightning.pytorch.callbacks import GradientAccumulationScheduler + + # till 5th epoch, it will accumulate every 8 batches. From 5th epoch + # till 9th epoch it will accumulate every 4 batches and after that no accumulation + # will happen. Note that you need to use zero-indexed epoch keys here + accumulator = GradientAccumulationScheduler(scheduling={0: 8, 4: 4, 8: 1}) + trainer = Trainer(callbacks=accumulator) + +Note: Not all strategies and accelerators support variable gradient accumulation windows. diff --git a/docs/source-pytorch/common/hyperparameters.rst b/docs/source-pytorch/common/hyperparameters.rst new file mode 100644 index 0000000..ce35627 --- /dev/null +++ b/docs/source-pytorch/common/hyperparameters.rst @@ -0,0 +1,52 @@ +:orphan: + +Configure hyperparameters from the CLI +-------------------------------------- + +You can use any CLI tool you want with Lightning. +For beginners, we recommand using Python's built-in argument parser. + + +---- + + +ArgumentParser +^^^^^^^^^^^^^^ + +The :class:`~argparse.ArgumentParser` is a built-in feature in Python that let's you build CLI programs. +You can use it to make hyperparameters and other training settings available from the command line: + +.. code-block:: python + + from argparse import ArgumentParser + + parser = ArgumentParser() + + # Trainer arguments + parser.add_argument("--devices", type=int, default=2) + + # Hyperparameters for the model + parser.add_argument("--layer_1_dim", type=int, default=128) + + # Parse the user inputs and defaults (returns a argparse.Namespace) + args = parser.parse_args() + + # Use the parsed arguments in your program + trainer = Trainer(devices=args.devices) + model = MyModel(layer_1_dim=args.layer_1_dim) + +This allows you to call your program like so: + +.. code-block:: bash + + python trainer.py --layer_1_dim 64 --devices 1 + +---- + + +LightningCLI +^^^^^^^^^^^^ + +Python's argument parser works well for simple use cases, but it can become cumbersome to maintain for larger projects. +For example, every time you add, change, or delete an argument from your model, you will have to add, edit, or remove the corresponding ``parser.add_argument`` code. +The :doc:`Lightning CLI <../cli/lightning_cli>` provides a seamless integration with the Trainer and LightningModule for which the CLI arguments get generated automatically for you! diff --git a/docs/source-pytorch/common/index.rst b/docs/source-pytorch/common/index.rst new file mode 100644 index 0000000..b5f44f7 --- /dev/null +++ b/docs/source-pytorch/common/index.rst @@ -0,0 +1,200 @@ +.. toctree:: + :maxdepth: 1 + :hidden: + + evaluation + ../model/build_model + ../cli/lightning_cli + progress_bar + ../deploy/production + ../advanced/training_tricks + ../tuning/profiler + Manage experiments <../visualize/logging_intermediate> + Organize existing PyTorch into Lightning <../starter/converting> + ../clouds/cluster + Save and load model progress + Save memory with half-precision + ../advanced/model_parallel + Train on single or multiple GPUs <../accelerators/gpu> + Train on single or multiple HPUs <../integrations/hpu/index> + Train on single or multiple IPUs <../accelerators/ipu> + Train on single or multiple TPUs <../accelerators/tpu> + Train on MPS <../accelerators/mps> + Use a pretrained model <../advanced/pretrained> + ../data/data + ../model/own_your_loop + +############# +How-to Guides +############# + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Avoid overfitting + :description: Learn how to add validation and test loops + :button_link: ../common/evaluation.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Build a model + :description: Step by step guide to build your model + :button_link: ../model/build_model.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Configure hyperparameters from the CLI + :description: Make your experiments modular via command line interface + :button_link: ../cli/lightning_cli.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Customize the progress bar + :description: Change the progress bar monitoring and tracking + :button_link: ../common/progress_bar.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Deploy models into production + :description: Deploy models with different levels of scal + :button_link: ../deploy/production.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Optimize training + :description: Explore advanced training techniques + :button_link: ../advanced/training_tricks.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Find bottlenecks in your code + :description: Learn how to profile your experiments to find bottlenecks + :button_link: ../tuning/profiler.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Finetune a model + :description: Learn how to use pretrained models + :col_css: col-md-4 + :button_link: ../advanced/transfer_learning.html + :height: 180 + +.. displayitem:: + :header: Manage data + :description: How to use basic to advanced data techniques + :button_link: ../data/data.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Manage experiments + :description: Learn to track and visualize with experiment managers + :button_link: ../visualize/logging_intermediate.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Organize existing PyTorch into Lightning + :description: Convert your vanila PyTorch to Lightning + :button_link: ../starter/converting.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Run on an on-prem cluster + :description: Learn to run on your own cluster + :button_link: ../clouds/cluster.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Save and load model progress + :description: Save and load progress with checkpoints + :button_link: ../common/checkpointing.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Save memory with half-precision + :description: Use precision techniques to train faster and save memory + :button_link: ../common/precision.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Train 1 trillion+ parameter models + :description: Scale GPU training to 1 trillion + parameter models + :button_link: ../advanced/model_parallel.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Train on single or multiple GPUs + :description: Train models faster with GPU accelerators + :button_link: ../accelerators/gpu.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Train on single or multiple HPUs + :description: Train models faster with HPU accelerators + :button_link: ../integrations/hpu/index.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Train on single or multiple IPUs + :description: Train models faster with IPU accelerators + :button_link: ../accelerators/ipu.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Train on single or multiple TPUs + :description: TTrain models faster with TPU accelerators + :button_link: ../accelerators/tpu.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Train on MPS + :description: Train models faster with Apple Silicon GPUs + :button_link: ../accelerators/mps.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Track and Visualize Experiments + :description: Learn to track and visualize experiments + :col_css: col-md-4 + :button_link: ../visualize/logging_intermediate.html + :height: 180 + +.. displayitem:: + :header: Use a pretrained model + :description: Improve results with transer learning on pretrained models + :button_link: ../advanced/pretrained.html + :col_css: col-md-4 + :height: 180 + +.. displayitem:: + :header: Use a pure PyTorch training loop + :description: Run your pure PyTorch loop with Lightning + :button_link: ../model/own_your_loop.html + :col_css: col-md-4 + :height: 180 + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/common/lightning_module.rst b/docs/source-pytorch/common/lightning_module.rst new file mode 100644 index 0000000..4b42375 --- /dev/null +++ b/docs/source-pytorch/common/lightning_module.rst @@ -0,0 +1,1442 @@ +.. role:: hidden + :class: hidden-section + +.. _lightning_module: + +############### +LightningModule +############### + +A :class:`~lightning.pytorch.core.module.LightningModule` organizes your PyTorch code into 6 sections: + +- Initialization (``__init__`` and :meth:`~lightning.pytorch.core.hooks.ModelHooks.setup`). +- Train Loop (:meth:`~lightning.pytorch.core.module.LightningModule.training_step`) +- Validation Loop (:meth:`~lightning.pytorch.core.module.LightningModule.validation_step`) +- Test Loop (:meth:`~lightning.pytorch.core.module.LightningModule.test_step`) +- Prediction Loop (:meth:`~lightning.pytorch.core.module.LightningModule.predict_step`) +- Optimizers and LR Schedulers (:meth:`~lightning.pytorch.core.module.LightningModule.configure_optimizers`) + +When you convert to use Lightning, the code IS NOT abstracted - just organized. +All the other code that's not in the :class:`~lightning.pytorch.core.module.LightningModule` +has been automated for you by the :class:`~lightning.pytorch.trainer.trainer.Trainer`. + +| + + .. code-block:: python + + net = MyLightningModuleNet() + trainer = Trainer() + trainer.fit(net) + +There are no ``.cuda()`` or ``.to(device)`` calls required. Lightning does these for you. + +| + + .. code-block:: python + + # don't do in Lightning + x = torch.Tensor(2, 3) + x = x.cuda() + x = x.to(device) + + # do this instead + x = x # leave it alone! + + # or to init a new tensor + new_x = torch.Tensor(2, 3) + new_x = new_x.to(x) + +When running under a distributed strategy, Lightning handles the distributed sampler for you by default. + +| + + .. code-block:: python + + # Don't do in Lightning... + data = MNIST(...) + sampler = DistributedSampler(data) + DataLoader(data, sampler=sampler) + + # do this instead + data = MNIST(...) + DataLoader(data) + +A :class:`~lightning.pytorch.core.module.LightningModule` is a :class:`torch.nn.Module` but with added functionality. Use it as such! + +| + + .. code-block:: python + + net = Net.load_from_checkpoint(PATH) + net.freeze() + out = net(x) + +Thus, to use Lightning, you just need to organize your code which takes about 30 minutes, +(and let's be real, you probably should do anyway). + +------------ + +*************** +Starter Example +*************** + +Here are the only required methods. + +.. code-block:: python + + import lightning.pytorch as pl + import torch.nn as nn + import torch.nn.functional as F + + + class LitModel(pl.LightningModule): + def __init__(self): + super().__init__() + self.l1 = nn.Linear(28 * 28, 10) + + def forward(self, x): + return torch.relu(self.l1(x.view(x.size(0), -1))) + + def training_step(self, batch, batch_idx): + x, y = batch + y_hat = self(x) + loss = F.cross_entropy(y_hat, y) + return loss + + def configure_optimizers(self): + return torch.optim.Adam(self.parameters(), lr=0.02) + +Which you can train by doing: + +.. code-block:: python + + train_loader = DataLoader(MNIST(os.getcwd(), download=True, transform=transforms.ToTensor())) + trainer = pl.Trainer(max_epochs=1) + model = LitModel() + + trainer.fit(model, train_dataloaders=train_loader) + +The LightningModule has many convenience methods, but the core ones you need to know about are: + +.. list-table:: + :widths: 50 50 + :header-rows: 1 + + * - Name + - Description + * - ``__init__`` and :meth:`~lightning.pytorch.core.hooks.ModelHooks.setup` + - Define initialization here + * - :meth:`~lightning.pytorch.core.module.LightningModule.forward` + - To run data through your model only (separate from ``training_step``) + * - :meth:`~lightning.pytorch.core.module.LightningModule.training_step` + - the complete training step + * - :meth:`~lightning.pytorch.core.module.LightningModule.validation_step` + - the complete validation step + * - :meth:`~lightning.pytorch.core.module.LightningModule.test_step` + - the complete test step + * - :meth:`~lightning.pytorch.core.module.LightningModule.predict_step` + - the complete prediction step + * - :meth:`~lightning.pytorch.core.module.LightningModule.configure_optimizers` + - define optimizers and LR schedulers + +---------- + +******** +Training +******** + +Training Loop +============= + +To activate the training loop, override the :meth:`~lightning.pytorch.core.module.LightningModule.training_step` method. + +.. code-block:: python + + class LitClassifier(pl.LightningModule): + def __init__(self, model): + super().__init__() + self.model = model + + def training_step(self, batch, batch_idx): + x, y = batch + y_hat = self.model(x) + loss = F.cross_entropy(y_hat, y) + return loss + +Under the hood, Lightning does the following (pseudocode): + +.. code-block:: python + + # put model in train mode and enable gradient calculation + model.train() + torch.set_grad_enabled(True) + + for batch_idx, batch in enumerate(train_dataloader): + loss = training_step(batch, batch_idx) + + # clear gradients + optimizer.zero_grad() + + # backward + loss.backward() + + # update parameters + optimizer.step() + + +Train Epoch-level Metrics +========================= + +If you want to calculate epoch-level metrics and log them, use :meth:`~lightning.pytorch.core.module.LightningModule.log`. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + x, y = batch + y_hat = self.model(x) + loss = F.cross_entropy(y_hat, y) + + # logs metrics for each training_step, + # and the average across the epoch, to the progress bar and logger + self.log("train_loss", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True) + return loss + +The :meth:`~lightning.pytorch.core.module.LightningModule.log` method automatically reduces the +requested metrics across a complete epoch and devices. Here's the pseudocode of what it does under the hood: + +.. code-block:: python + + outs = [] + for batch_idx, batch in enumerate(train_dataloader): + # forward + loss = training_step(batch, batch_idx) + outs.append(loss.detach()) + + # clear gradients + optimizer.zero_grad() + # backward + loss.backward() + # update parameters + optimizer.step() + + # note: in reality, we do this incrementally, instead of keeping all outputs in memory + epoch_metric = torch.mean(torch.stack(outs)) + +Train Epoch-level Operations +============================ + +In the case that you need to make use of all the outputs from each :meth:`~lightning.pytorch.LightningModule.training_step`, +override the :meth:`~lightning.pytorch.LightningModule.on_train_epoch_end` method. + +.. code-block:: python + + def __init__(self): + super().__init__() + self.training_step_outputs = [] + + + def training_step(self, batch, batch_idx): + x, y = batch + y_hat = self.model(x) + loss = F.cross_entropy(y_hat, y) + preds = ... + self.training_step_outputs.append(preds) + return loss + + + def on_train_epoch_end(self): + all_preds = torch.stack(self.training_step_outputs) + # do something with all preds + ... + self.training_step_outputs.clear() # free memory + + +------------------ + +********** +Validation +********** + +Validation Loop +=============== + +To activate the validation loop while training, override the :meth:`~lightning.pytorch.core.module.LightningModule.validation_step` method. + +.. code-block:: python + + class LitModel(pl.LightningModule): + def validation_step(self, batch, batch_idx): + x, y = batch + y_hat = self.model(x) + loss = F.cross_entropy(y_hat, y) + self.log("val_loss", loss) + +Under the hood, Lightning does the following (pseudocode): + +.. code-block:: python + + # ... + for batch_idx, batch in enumerate(train_dataloader): + loss = model.training_step(batch, batch_idx) + loss.backward() + # ... + + if validate_at_some_point: + # disable grads + batchnorm + dropout + torch.set_grad_enabled(False) + model.eval() + + # ----------------- VAL LOOP --------------- + for val_batch_idx, val_batch in enumerate(val_dataloader): + val_out = model.validation_step(val_batch, val_batch_idx) + # ----------------- VAL LOOP --------------- + + # enable grads + batchnorm + dropout + torch.set_grad_enabled(True) + model.train() + +You can also run just the validation loop on your validation dataloaders by overriding :meth:`~lightning.pytorch.core.module.LightningModule.validation_step` +and calling :meth:`~lightning.pytorch.trainer.trainer.Trainer.validate`. + +.. code-block:: python + + model = Model() + trainer = Trainer() + trainer.validate(model) + +.. note:: + + It is recommended to validate on single device to ensure each sample/batch gets evaluated exactly once. + This is helpful to make sure benchmarking for research papers is done the right way. Otherwise, in a + multi-device setting, samples could occur duplicated when :class:`~torch.utils.data.distributed.DistributedSampler` + is used, for eg. with ``strategy="ddp"``. It replicates some samples on some devices to make sure all devices have + same batch size in case of uneven inputs. + + +Validation Epoch-level Metrics +============================== + +In the case that you need to make use of all the outputs from each :meth:`~lightning.pytorch.LightningModule.validation_step`, +override the :meth:`~lightning.pytorch.LightningModule.on_validation_epoch_end` method. +Note that this method is called before :meth:`~lightning.pytorch.LightningModule.on_train_epoch_end`. + +.. code-block:: python + + def __init__(self): + super().__init__() + self.validation_step_outputs = [] + + + def validation_step(self, batch, batch_idx): + x, y = batch + y_hat = self.model(x) + loss = F.cross_entropy(y_hat, y) + pred = ... + self.validation_step_outputs.append(pred) + return pred + + + def on_validation_epoch_end(self): + all_preds = torch.stack(self.validation_step_outputs) + # do something with all preds + ... + self.validation_step_outputs.clear() # free memory + +---------------- + +******* +Testing +******* + +Test Loop +========= + +The process for enabling a test loop is the same as the process for enabling a validation loop. Please refer to +the section above for details. For this you need to override the :meth:`~lightning.pytorch.core.module.LightningModule.test_step` method. + +The only difference is that the test loop is only called when :meth:`~lightning.pytorch.trainer.trainer.Trainer.test` is used. + +.. code-block:: python + + model = Model() + trainer = Trainer() + trainer.fit(model) + + # automatically loads the best weights for you + trainer.test(model) + +There are two ways to call ``test()``: + +.. code-block:: python + + # call after training + trainer = Trainer() + trainer.fit(model) + + # automatically auto-loads the best weights from the previous run + trainer.test(dataloaders=test_dataloader) + + # or call with pretrained model + model = MyLightningModule.load_from_checkpoint(PATH) + trainer = Trainer() + trainer.test(model, dataloaders=test_dataloader) + +.. note:: + + It is recommended to validate on single device to ensure each sample/batch gets evaluated exactly once. + This is helpful to make sure benchmarking for research papers is done the right way. Otherwise, in a + multi-device setting, samples could occur duplicated when :class:`~torch.utils.data.distributed.DistributedSampler` + is used, for eg. with ``strategy="ddp"``. It replicates some samples on some devices to make sure all devices have + same batch size in case of uneven inputs. + + +---------- + +********* +Inference +********* + +Prediction Loop +=============== + +By default, the :meth:`~lightning.pytorch.core.module.LightningModule.predict_step` method runs the +:meth:`~lightning.pytorch.core.module.LightningModule.forward` method. In order to customize this behaviour, +simply override the :meth:`~lightning.pytorch.core.module.LightningModule.predict_step` method. + +For the example let's override ``predict_step`` and try out `Monte Carlo Dropout `_: + +.. code-block:: python + + class LitMCdropoutModel(pl.LightningModule): + def __init__(self, model, mc_iteration): + super().__init__() + self.model = model + self.dropout = nn.Dropout() + self.mc_iteration = mc_iteration + + def predict_step(self, batch, batch_idx): + # enable Monte Carlo Dropout + self.dropout.train() + + # take average of `self.mc_iteration` iterations + pred = torch.vstack([self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)]).mean(dim=0) + return pred + +Under the hood, Lightning does the following (pseudocode): + +.. code-block:: python + + # disable grads + batchnorm + dropout + torch.set_grad_enabled(False) + model.eval() + all_preds = [] + + for batch_idx, batch in enumerate(predict_dataloader): + pred = model.predict_step(batch, batch_idx) + all_preds.append(pred) + +There are two ways to call ``predict()``: + +.. code-block:: python + + # call after training + trainer = Trainer() + trainer.fit(model) + + # automatically auto-loads the best weights from the previous run + predictions = trainer.predict(dataloaders=predict_dataloader) + + # or call with pretrained model + model = MyLightningModule.load_from_checkpoint(PATH) + trainer = Trainer() + predictions = trainer.predict(model, dataloaders=test_dataloader) + +Inference in Research +===================== + +If you want to perform inference with the system, you can add a ``forward`` method to the LightningModule. + +.. note:: When using forward, you are responsible to call :func:`~torch.nn.Module.eval` and use the :func:`~torch.no_grad` context manager. + +.. code-block:: python + + class Autoencoder(pl.LightningModule): + def forward(self, x): + return self.decoder(x) + + + model = Autoencoder() + model.eval() + with torch.no_grad(): + reconstruction = model(embedding) + +The advantage of adding a forward is that in complex systems, you can do a much more involved inference procedure, +such as text generation: + +.. code-block:: python + + class Seq2Seq(pl.LightningModule): + def forward(self, x): + embeddings = self(x) + hidden_states = self.encoder(embeddings) + for h in hidden_states: + # decode + ... + return decoded + +In the case where you want to scale your inference, you should be using +:meth:`~lightning.pytorch.core.module.LightningModule.predict_step`. + +.. code-block:: python + + class Autoencoder(pl.LightningModule): + def forward(self, x): + return self.decoder(x) + + def predict_step(self, batch, batch_idx, dataloader_idx=0): + # this calls forward + return self(batch) + + + data_module = ... + model = Autoencoder() + trainer = Trainer(accelerator="gpu", devices=2) + trainer.predict(model, data_module) + +Inference in Production +======================= + +For cases like production, you might want to iterate different models inside a LightningModule. + +.. code-block:: python + + from torchmetrics.functional import accuracy + + + class ClassificationTask(pl.LightningModule): + def __init__(self, model): + super().__init__() + self.model = model + + def training_step(self, batch, batch_idx): + x, y = batch + y_hat = self.model(x) + loss = F.cross_entropy(y_hat, y) + return loss + + def validation_step(self, batch, batch_idx): + loss, acc = self._shared_eval_step(batch, batch_idx) + metrics = {"val_acc": acc, "val_loss": loss} + self.log_dict(metrics) + return metrics + + def test_step(self, batch, batch_idx): + loss, acc = self._shared_eval_step(batch, batch_idx) + metrics = {"test_acc": acc, "test_loss": loss} + self.log_dict(metrics) + return metrics + + def _shared_eval_step(self, batch, batch_idx): + x, y = batch + y_hat = self.model(x) + loss = F.cross_entropy(y_hat, y) + acc = accuracy(y_hat, y) + return loss, acc + + def predict_step(self, batch, batch_idx, dataloader_idx=0): + x, y = batch + y_hat = self.model(x) + return y_hat + + def configure_optimizers(self): + return torch.optim.Adam(self.model.parameters(), lr=0.02) + +Then pass in any arbitrary model to be fit with this task + +.. code-block:: python + + for model in [resnet50(), vgg16(), BidirectionalRNN()]: + task = ClassificationTask(model) + + trainer = Trainer(accelerator="gpu", devices=2) + trainer.fit(task, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader) + +Tasks can be arbitrarily complex such as implementing GAN training, self-supervised or even RL. + +.. code-block:: python + + class GANTask(pl.LightningModule): + def __init__(self, generator, discriminator): + super().__init__() + self.generator = generator + self.discriminator = discriminator + + ... + +When used like this, the model can be separated from the Task and thus used in production without needing to keep it in +a ``LightningModule``. + +The following example shows how you can run inference in the Python runtime: + +.. code-block:: python + + task = ClassificationTask(model) + trainer = Trainer(accelerator="gpu", devices=2) + trainer.fit(task, train_dataloader, val_dataloader) + trainer.save_checkpoint("best_model.ckpt") + + # use model after training or load weights and drop into the production system + model = ClassificationTask.load_from_checkpoint("best_model.ckpt") + x = ... + model.eval() + with torch.no_grad(): + y_hat = model(x) + +Check out :ref:`Inference in Production ` guide to learn about the possible ways to perform inference in production. + + +----------- + + +******************** +Save Hyperparameters +******************** + +Often times we train many versions of a model. You might share that model or come back to it a few months later at which +point it is very useful to know how that model was trained (i.e.: what learning rate, neural network, etc...). + +Lightning has a standardized way of saving the information for you in checkpoints and YAML files. The goal here is to +improve readability and reproducibility. + +save_hyperparameters +==================== + +Use :meth:`~lightning.pytorch.core.module.LightningModule.save_hyperparameters` within your +:class:`~lightning.pytorch.core.module.LightningModule`'s ``__init__`` method. It will enable Lightning to store all the +provided arguments under the ``self.hparams`` attribute. These hyperparameters will also be stored within the model +checkpoint, which simplifies model re-instantiation after training. + +.. code-block:: python + + class LitMNIST(LightningModule): + def __init__(self, layer_1_dim=128, learning_rate=1e-2): + super().__init__() + # call this to save (layer_1_dim=128, learning_rate=1e-4) to the checkpoint + self.save_hyperparameters() + + # equivalent + self.save_hyperparameters("layer_1_dim", "learning_rate") + + # Now possible to access layer_1_dim from hparams + self.hparams.layer_1_dim + + +In addition, loggers that support it will automatically log the contents of ``self.hparams``. + +Excluding hyperparameters +========================= + +By default, every parameter of the ``__init__`` method will be considered a hyperparameter to the LightningModule. +However, sometimes some parameters need to be excluded from saving, for example when they are not serializable. Those +parameters should be provided back when reloading the LightningModule. In this case, exclude them explicitly: + +.. code-block:: python + + class LitMNIST(LightningModule): + def __init__(self, loss_fx, generator_network, layer_1_dim=128): + super().__init__() + self.layer_1_dim = layer_1_dim + self.loss_fx = loss_fx + + # call this to save only (layer_1_dim=128) to the checkpoint + self.save_hyperparameters("layer_1_dim") + + # equivalent + self.save_hyperparameters(ignore=["loss_fx", "generator_network"]) + + +load_from_checkpoint +==================== + +LightningModules that have hyperparameters automatically saved with +:meth:`~lightning.pytorch.core.module.LightningModule.save_hyperparameters` can conveniently be loaded and instantiated +directly from a checkpoint with :meth:`~lightning.pytorch.core.module.LightningModule.load_from_checkpoint`: + +.. code-block:: python + + # to load specify the other args + model = LitMNIST.load_from_checkpoint(PATH, loss_fx=torch.nn.SomeOtherLoss, generator_network=MyGenerator()) + + +If parameters were excluded, they need to be provided at the time of loading: + +.. code-block:: python + + # the excluded parameters were `loss_fx` and `generator_network` + model = LitMNIST.load_from_checkpoint(PATH, loss_fx=torch.nn.SomeOtherLoss, generator_network=MyGenerator()) + + +----------- + + +************* +Child Modules +************* + +.. include:: ../common/child_modules.rst + +----------- + +******************* +LightningModule API +******************* + + +Methods +======= + +all_gather +~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.all_gather + :noindex: + +configure_callbacks +~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.configure_callbacks + :noindex: + +configure_optimizers +~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.configure_optimizers + :noindex: + +forward +~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.forward + :noindex: + +freeze +~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.freeze + :noindex: + +.. _lm-log: + +log +~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.log + :noindex: + +log_dict +~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.log_dict + :noindex: + +lr_schedulers +~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.lr_schedulers + :noindex: + +manual_backward +~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.manual_backward + :noindex: + +optimizers +~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.optimizers + :noindex: + +print +~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.print + :noindex: + +predict_step +~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.predict_step + :noindex: + +save_hyperparameters +~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.save_hyperparameters + :noindex: + +toggle_optimizer +~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.toggle_optimizer + :noindex: + +test_step +~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.test_step + :noindex: + +to_onnx +~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.to_onnx + :noindex: + +to_torchscript +~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.to_torchscript + :noindex: + +training_step +~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.training_step + :noindex: + +unfreeze +~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.unfreeze + :noindex: + +untoggle_optimizer +~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.untoggle_optimizer + :noindex: + +validation_step +~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.validation_step + :noindex: + +----------- + +Properties +========== + +These are properties available in a LightningModule. + +current_epoch +~~~~~~~~~~~~~ + +The number of epochs run. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + if self.current_epoch == 0: + ... + +device +~~~~~~ + +The device the module is on. Use it to keep your code device agnostic. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + z = torch.rand(2, 3, device=self.device) + +global_rank +~~~~~~~~~~~ + +The ``global_rank`` is the index of the current process across all nodes and devices. +Lightning will perform some operations such as logging, weight checkpointing only when ``global_rank=0``. You +usually do not need to use this property, but it is useful to know how to access it if needed. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + if self.global_rank == 0: + # do something only once across all the nodes + ... + +global_step +~~~~~~~~~~~ + +The number of optimizer steps taken (does not reset each epoch). +This includes multiple optimizers (if enabled). + +.. code-block:: python + + def training_step(self, batch, batch_idx): + self.logger.experiment.log_image(..., step=self.global_step) + +hparams +~~~~~~~ + +The arguments passed through ``LightningModule.__init__()`` and saved by calling +:meth:`~lightning.pytorch.core.mixins.hparams_mixin.HyperparametersMixin.save_hyperparameters` could be accessed by the ``hparams`` attribute. + +.. code-block:: python + + def __init__(self, learning_rate): + self.save_hyperparameters() + + + def configure_optimizers(self): + return Adam(self.parameters(), lr=self.hparams.learning_rate) + +logger +~~~~~~ + +The current logger being used (tensorboard or other supported logger) + +.. code-block:: python + + def training_step(self, batch, batch_idx): + # the generic logger (same no matter if tensorboard or other supported logger) + self.logger + + # the particular logger + tensorboard_logger = self.logger.experiment + +loggers +~~~~~~~ + +The list of loggers currently being used by the Trainer. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + # List of Logger objects + loggers = self.loggers + for logger in loggers: + logger.log_metrics({"foo": 1.0}) + +local_rank +~~~~~~~~~~~ + +The ``local_rank`` is the index of the current process across all the devices for the current node. +You usually do not need to use this property, but it is useful to know how to access it if needed. +For example, if using 10 machines (or nodes), the GPU at index 0 on each machine has local_rank = 0. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + if self.local_rank == 0: + # do something only once across each node + ... + +precision +~~~~~~~~~ + +The type of precision used: + +.. code-block:: python + + def training_step(self, batch, batch_idx): + if self.precision == 16: + ... + +trainer +~~~~~~~ + +Pointer to the trainer + +.. code-block:: python + + def training_step(self, batch, batch_idx): + max_steps = self.trainer.max_steps + any_flag = self.trainer.any_flag + +prepare_data_per_node +~~~~~~~~~~~~~~~~~~~~~ + +If set to ``True`` will call ``prepare_data()`` on LOCAL_RANK=0 for every node. +If set to ``False`` will only call from NODE_RANK=0, LOCAL_RANK=0. + +.. testcode:: + + class LitModel(LightningModule): + def __init__(self): + super().__init__() + self.prepare_data_per_node = True + +automatic_optimization +~~~~~~~~~~~~~~~~~~~~~~ + +When set to ``False``, Lightning does not automate the optimization process. This means you are responsible for handling +your optimizers. However, we do take care of precision and any accelerators used. + +See :ref:`manual optimization ` for details. + +.. code-block:: python + + def __init__(self): + self.automatic_optimization = False + + + def training_step(self, batch, batch_idx): + opt = self.optimizers(use_pl_optimizer=True) + + loss = ... + opt.zero_grad() + self.manual_backward(loss) + opt.step() + +Manual optimization is most useful for research topics like reinforcement learning, sparse coding, and GAN research. +It is required when you are using 2+ optimizers because with automatic optimization, you can only use one optimizer. + +.. code-block:: python + + def __init__(self): + self.automatic_optimization = False + + + def training_step(self, batch, batch_idx): + # access your optimizers with use_pl_optimizer=False. Default is True + opt_a, opt_b = self.optimizers(use_pl_optimizer=True) + + gen_loss = ... + opt_a.zero_grad() + self.manual_backward(gen_loss) + opt_a.step() + + disc_loss = ... + opt_b.zero_grad() + self.manual_backward(disc_loss) + opt_b.step() + +example_input_array +~~~~~~~~~~~~~~~~~~~ + +Set and access example_input_array, which basically represents a single batch. + +.. code-block:: python + + def __init__(self): + self.example_input_array = ... + self.generator = ... + + + def on_train_epoch_end(self): + # generate some images using the example_input_array + gen_images = self.generator(self.example_input_array) + +-------------- + +.. _lightning_hooks: + +Hooks +===== + +This is the pseudocode to describe the structure of :meth:`~lightning.pytorch.trainer.Trainer.fit`. +The inputs and outputs of each function are not represented for simplicity. Please check each function's API reference +for more information. + +.. code-block:: python + + def fit(self): + if global_rank == 0: + # prepare data is called on GLOBAL_ZERO only + prepare_data() + + configure_callbacks() + + with parallel(devices): + # devices can be GPUs, TPUs, ... + train_on_device(model) + + + def train_on_device(model): + # called PER DEVICE + setup("fit") + configure_optimizers() + on_fit_start() + + # the sanity check runs here + + on_train_start() + for epoch in epochs: + fit_loop() + on_train_end() + + on_fit_end() + teardown("fit") + + + def fit_loop(): + model.train() + torch.set_grad_enabled(True) + + on_train_epoch_start() + + for batch in train_dataloader(): + on_train_batch_start() + + on_before_batch_transfer() + transfer_batch_to_device() + on_after_batch_transfer() + + out = training_step() + + on_before_zero_grad() + optimizer_zero_grad() + + on_before_backward() + backward() + on_after_backward() + + on_before_optimizer_step() + configure_gradient_clipping() + optimizer_step() + + on_train_batch_end(out, batch, batch_idx) + + if should_check_val: + val_loop() + + on_train_epoch_end() + + + def val_loop(): + on_validation_model_eval() # calls `model.eval()` + torch.set_grad_enabled(False) + + on_validation_start() + on_validation_epoch_start() + + for batch_idx, batch in enumerate(val_dataloader()): + on_validation_batch_start(batch, batch_idx) + + batch = on_before_batch_transfer(batch) + batch = transfer_batch_to_device(batch) + batch = on_after_batch_transfer(batch) + + out = validation_step(batch, batch_idx) + + on_validation_batch_end(out, batch, batch_idx) + + on_validation_epoch_end() + on_validation_end() + + # set up for train + on_validation_model_train() # calls `model.train()` + torch.set_grad_enabled(True) + +backward +~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.backward + :noindex: + +on_before_backward +~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_before_backward + :noindex: + +on_after_backward +~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_after_backward + :noindex: + +on_before_zero_grad +~~~~~~~~~~~~~~~~~~~ +.. automethod:: lightning.pytorch.core.module.LightningModule.on_before_zero_grad + :noindex: + +on_fit_start +~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_fit_start + :noindex: + +on_fit_end +~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_fit_end + :noindex: + + +on_load_checkpoint +~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_load_checkpoint + :noindex: + +on_save_checkpoint +~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_save_checkpoint + :noindex: + +load_from_checkpoint +~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.load_from_checkpoint + :noindex: + +on_train_start +~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_train_start + :noindex: + +on_train_end +~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_train_end + :noindex: + +on_validation_start +~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_validation_start + :noindex: + +on_validation_end +~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_validation_end + :noindex: + +on_test_batch_start +~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_test_batch_start + :noindex: + +on_test_batch_end +~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_test_batch_end + :noindex: + +on_test_epoch_start +~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_test_epoch_start + :noindex: + +on_test_epoch_end +~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_test_epoch_end + :noindex: + +on_test_start +~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_test_start + :noindex: + +on_test_end +~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_test_end + :noindex: + +on_predict_batch_start +~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_predict_batch_start + :noindex: + +on_predict_batch_end +~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_predict_batch_end + :noindex: + +on_predict_epoch_start +~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_predict_epoch_start + :noindex: + +on_predict_epoch_end +~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_predict_epoch_end + :noindex: + +on_predict_start +~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_predict_start + :noindex: + +on_predict_end +~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_predict_end + :noindex: + +on_train_batch_start +~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_train_batch_start + :noindex: + +on_train_batch_end +~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_train_batch_end + :noindex: + +on_train_epoch_start +~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_train_epoch_start + :noindex: + +on_train_epoch_end +~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_train_epoch_end + :noindex: + +on_validation_batch_start +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_validation_batch_start + :noindex: + +on_validation_batch_end +~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_validation_batch_end + :noindex: + +on_validation_epoch_start +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_validation_epoch_start + :noindex: + +on_validation_epoch_end +~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_validation_epoch_end + :noindex: + +configure_model +~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.configure_model + :noindex: + +on_validation_model_eval +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_validation_model_eval + :noindex: + +on_validation_model_train +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_validation_model_train + :noindex: + +on_test_model_eval +~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_test_model_eval + :noindex: + +on_test_model_train +~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_test_model_train + :noindex: + +on_before_optimizer_step +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_before_optimizer_step + :noindex: + +configure_gradient_clipping +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.configure_gradient_clipping + :noindex: + +optimizer_step +~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.optimizer_step + :noindex: + +optimizer_zero_grad +~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.optimizer_zero_grad + :noindex: + +prepare_data +~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.prepare_data + :noindex: + +setup +~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.setup + :noindex: + +teardown +~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.teardown + :noindex: + +train_dataloader +~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.train_dataloader + :noindex: + +val_dataloader +~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.val_dataloader + :noindex: + +test_dataloader +~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.test_dataloader + :noindex: + +predict_dataloader +~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.predict_dataloader + :noindex: + +transfer_batch_to_device +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.transfer_batch_to_device + :noindex: + +on_before_batch_transfer +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_before_batch_transfer + :noindex: + +on_after_batch_transfer +~~~~~~~~~~~~~~~~~~~~~~~ + +.. automethod:: lightning.pytorch.core.module.LightningModule.on_after_batch_transfer + :noindex: diff --git a/docs/source-pytorch/common/optimization.rst b/docs/source-pytorch/common/optimization.rst new file mode 100644 index 0000000..d29415b --- /dev/null +++ b/docs/source-pytorch/common/optimization.rst @@ -0,0 +1,173 @@ +:orphan: + +.. _optimization: + +############ +Optimization +############ + +Lightning offers two modes for managing the optimization process: + +- Manual Optimization +- Automatic Optimization + +For the majority of research cases, **automatic optimization** will do the right thing for you and it is what most +users should use. + +For more advanced use cases like multiple optimizers, esoteric optimization schedules or techniques, use **manual optimization**. + +.. _manual_optimization: + +---- + +.. include:: ../model/manual_optimization.rst + +----- + +********************** +Automatic Optimization +********************** + +With Lightning, most users don't have to think about when to call ``.zero_grad()``, ``.backward()`` and ``.step()`` +since Lightning automates that for you. + +Under the hood, Lightning does the following: + +.. code-block:: python + + for epoch in epochs: + for batch in data: + + def closure(): + loss = model.training_step(batch, batch_idx) + optimizer.zero_grad() + loss.backward() + return loss + + optimizer.step(closure) + + lr_scheduler.step() + +As can be seen in the code snippet above, Lightning defines a closure with ``training_step()``, ``optimizer.zero_grad()`` +and ``loss.backward()`` for the optimization. This mechanism is in place to support optimizers which operate on the +output of the closure (e.g. the loss) or need to call the closure several times (e.g. :class:`~torch.optim.LBFGS`). + +Should you still require the flexibility of calling ``.zero_grad()``, ``.backward()``, or ``.step()`` yourself, you can +always switch to :ref:`manual optimization `. +Manual optimization is required if you wish to work with multiple optimizers. + + +.. _gradient_accumulation: + +Gradient Accumulation +===================== + +.. include:: ../common/gradient_accumulation.rst + + +Access your Own Optimizer +========================= + +The provided ``optimizer`` is a :class:`~lightning.pytorch.core.optimizer.LightningOptimizer` object wrapping your own optimizer +configured in your :meth:`~lightning.pytorch.core.module.LightningModule.configure_optimizers`. +You can access your own optimizer with ``optimizer.optimizer``. However, if you use your own optimizer +to perform a step, Lightning won't be able to support accelerators, precision and profiling for you. + +.. testcode:: python + + # function hook in LightningModule + def optimizer_step( + self, + epoch, + batch_idx, + optimizer, + optimizer_closure, + ): + optimizer.step(closure=optimizer_closure) + + + # `optimizer` is a `LightningOptimizer` wrapping the optimizer. + # To access it, do the following. + # However, it won't work on TPU, AMP, etc... + def optimizer_step( + self, + epoch, + batch_idx, + optimizer, + optimizer_closure, + ): + optimizer = optimizer.optimizer + optimizer.step(closure=optimizer_closure) + +----- + + +Bring your own Custom Learning Rate Schedulers +============================================== + +Lightning allows using custom learning rate schedulers that aren't available in `PyTorch natively `_. +One good example is `Timm Schedulers `_. When using custom learning rate schedulers +relying on a different API from Native PyTorch ones, you should override the :meth:`~lightning.pytorch.core.module.LightningModule.lr_scheduler_step` with your desired logic. +If you are using native PyTorch schedulers, there is no need to override this hook since Lightning will handle it automatically by default. + +.. code-block:: python + + from timm.scheduler import TanhLRScheduler + + + def configure_optimizers(self): + optimizer = ... + scheduler = TanhLRScheduler(optimizer, ...) + return [optimizer], [{"scheduler": scheduler, "interval": "epoch"}] + + + def lr_scheduler_step(self, scheduler, metric): + scheduler.step(epoch=self.current_epoch) # timm's scheduler need the epoch value + + +.. _configure_gradient_clipping: + +Configure Gradient Clipping +=========================== + +To configure custom gradient clipping, consider overriding +the :meth:`~lightning.pytorch.core.module.LightningModule.configure_gradient_clipping` method. +The attributes ``gradient_clip_val`` and ``gradient_clip_algorithm`` from Trainer will be passed in the +respective arguments here and Lightning will handle gradient clipping for you. In case you want to set +different values for your arguments of your choice and let Lightning handle the gradient clipping, you can +use the inbuilt :meth:`~lightning.pytorch.core.module.LightningModule.clip_gradients` method and pass +the arguments along with your optimizer. + +.. warning:: + Make sure to not override :meth:`~lightning.pytorch.core.module.LightningModule.clip_gradients` + method. If you want to customize gradient clipping, consider using + :meth:`~lightning.pytorch.core.module.LightningModule.configure_gradient_clipping` method. + +For example, here we will apply a stronger gradient clipping after a certain number of epochs: + +.. testcode:: python + + def configure_gradient_clipping(self, optimizer, gradient_clip_val, gradient_clip_algorithm): + if self.current_epoch > 5: + gradient_clip_val = gradient_clip_val * 2 + + # Lightning will handle the gradient clipping + self.clip_gradients(optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm) + + +Total Stepping Batches +====================== + +You can use built-in trainer property :paramref:`~lightning.pytorch.trainer.trainer.Trainer.estimated_stepping_batches` to compute +total number of stepping batches for the complete training. The property is computed considering gradient accumulation factor and +distributed setting into consideration so you don't have to derive it manually. One good example where this can be helpful is while using +:class:`~torch.optim.lr_scheduler.OneCycleLR` scheduler, which requires pre-computed ``total_steps`` during initialization. + +.. code-block:: python + + def configure_optimizers(self): + optimizer = ... + scheduler = torch.optim.lr_scheduler.OneCycleLR( + optimizer, max_lr=1e-3, total_steps=self.trainer.estimated_stepping_batches + ) + return optimizer, scheduler diff --git a/source/common/precision.rst b/docs/source-pytorch/common/precision.rst similarity index 100% rename from source/common/precision.rst rename to docs/source-pytorch/common/precision.rst diff --git a/docs/source-pytorch/common/precision_basic.rst b/docs/source-pytorch/common/precision_basic.rst new file mode 100644 index 0000000..033f580 --- /dev/null +++ b/docs/source-pytorch/common/precision_basic.rst @@ -0,0 +1,104 @@ +:orphan: + +.. _precision_basic: + +####################### +N-Bit Precision (Basic) +####################### +**Audience:** Users looking to train models faster and consume less memory. + +---- + +If you're looking to run models faster or consume less memory, consider tweaking the precision settings of your models. + +Lower precision, such as 16-bit floating-point, requires less memory and enables training and deploying larger models. +Higher precision, such as the 64-bit floating-point, can be used for highly sensitive use-cases. + +---- + +**************** +16-bit Precision +**************** + +Use 16-bit mixed precision to lower your memory consumption by up to half so that you can train and deploy larger models. If your GPUs are [`Tensor Core `_] GPUs, you can also get a ~3x speed improvement. Half precision can sometimes lead to unstable training. + +.. code:: + + Trainer(precision='16-mixed') + +---- + +**************** +32-bit Precision +**************** + +32-bit precision is the default used across all models and research. This precision is known to be stable in contrast to lower precision settings. + +.. testcode:: + + Trainer(precision="32-true") + + # or + Trainer(precision="32") + + # or + Trainer(precision=32) + +---- + +**************** +64-bit Precision +**************** + +For certain scientific computations, 64-bit precision enables more accurate models. However, doubling the precision from 32 to 64 bit also doubles the memory requirements. + +.. testcode:: + + Trainer(precision="64-true") + + # or + Trainer(precision="64") + + # or + Trainer(precision=64) + +.. note:: + + Since in deep learning, memory is always a bottleneck, especially when dealing with a large volume of data and with limited resources. + It is recommended using single precision for better speed. Although you can still use it if you want for your particular use-case. + +---- + +******************************** +Precision support by accelerator +******************************** + +.. list-table:: Precision with Accelerators + :widths: 20 20 20 20 20 + :header-rows: 1 + + * - Precision + - CPU + - GPU + - TPU + - IPU + * - 16 Mixed + - No + - Yes + - No + - Yes + * - BFloat16 Mixed + - Yes + - Yes + - Yes + - No + * - 32 True + - Yes + - Yes + - Yes + - Yes + * - 64 True + - Yes + - Yes + - No + - No diff --git a/source/common/precision_expert.rst b/docs/source-pytorch/common/precision_expert.rst similarity index 87% rename from source/common/precision_expert.rst rename to docs/source-pytorch/common/precision_expert.rst index 34bc955..a450297 100644 --- a/source/common/precision_expert.rst +++ b/docs/source-pytorch/common/precision_expert.rst @@ -12,7 +12,7 @@ N-Bit Precision (Expert) Precision Plugins ***************** -You can also customize and pass your own Precision Plugin by subclassing the :class:`~pytorch_lightning.plugins.precision.precision_plugin.PrecisionPlugin` class. +You can also customize and pass your own Precision Plugin by subclassing the :class:`~lightning.pytorch.plugins.precision.precision_plugin.PrecisionPlugin` class. - Perform pre and post backward/optimizer step operations such as scaling gradients. - Provide context managers for forward, training_step, etc. @@ -20,7 +20,7 @@ You can also customize and pass your own Precision Plugin by subclassing the :cl .. code-block:: python class CustomPrecisionPlugin(PrecisionPlugin): - precision = 16 + precision = "16-mixed" ... diff --git a/docs/source-pytorch/common/precision_intermediate.rst b/docs/source-pytorch/common/precision_intermediate.rst new file mode 100644 index 0000000..e0590df --- /dev/null +++ b/docs/source-pytorch/common/precision_intermediate.rst @@ -0,0 +1,113 @@ +:orphan: + +.. _precision_intermediate: + +############################## +N-Bit Precision (Intermediate) +############################## +**Audience:** Users looking to scale larger models or take advantage of optimized accelerators. + +---- + +************************ +What is Mixed Precision? +************************ + +PyTorch, like most deep learning frameworks, trains on 32-bit floating-point (FP32) arithmetic by default. However, many deep learning models do not require this to reach complete accuracy. By conducting +operations in half-precision format while keeping minimum information in single-precision to maintain as much information as possible in crucial areas of the network, mixed precision training delivers +significant computational speedup. Switching to mixed precision has resulted in considerable training speedups since the introduction of Tensor Cores in the Volta and Turing architectures. It combines +FP32 and lower-bit floating-points (such as FP16) to reduce memory footprint and increase performance during model training and evaluation. It accomplishes this by recognizing the steps that require +complete accuracy and employing a 32-bit floating-point for those steps only, while using a 16-bit floating-point for the rest. When compared to complete precision training, mixed precision training +delivers all of these benefits while ensuring that no task-specific accuracy is lost. [`2 `_]. + +.. note:: + + In some cases, it is essential to remain in FP32 for numerical stability, so keep this in mind when using mixed precision. + For example, when running scatter operations during the forward (such as torchpoint3d), computation must remain in FP32. + +.. warning:: + + Do not cast anything to other dtypes manually using ``torch.autocast`` or ``tensor.half()`` when using native precision because + this can bring instability. + + .. code-block:: python + + class LitModel(LightningModule): + def training_step(self, batch, batch_idx): + outs = self(batch) + + a_float32 = torch.rand((8, 8), device=self.device, dtype=self.dtype) + b_float32 = torch.rand((8, 4), device=self.device, dtype=self.dtype) + + # casting to float16 manually + with torch.autocast(device_type=self.device.type): + c_float16 = torch.mm(a_float32, b_float32) + target = self.layer(c_float16.flatten()[None]) + + # here outs is of type float32 and target is of type float16 + loss = torch.mm(target @ outs).float() + return loss + + + trainer = Trainer(accelerator="gpu", devices=1, precision=32) + +---- + +******************** +FP16 Mixed Precision +******************** + +In most cases, mixed precision uses FP16. Supported `PyTorch operations `__ automatically run in FP16, saving memory and improving throughput on the supported accelerators. +Since computation happens in FP16, there is a chance of numerical instability during training. This is handled internally by a dynamic grad scaler which skips invalid steps and adjusts the scaler to ensure subsequent steps fall within a finite range. For more information `see the autocast docs `__. + + +.. note:: + + When using TPUs, setting ``precision='16-mixed'`` will enable bfloat16, the only supported half precision type on TPUs. + +.. testcode:: + :skipif: not torch.cuda.is_available() + + Trainer(accelerator="gpu", devices=1, precision=16) + +************************ +BFloat16 Mixed Precision +************************ + +.. warning:: + + BFloat16 may not provide significant speedups or memory improvements or offer better numerical stability. + Do note for GPUs, the most significant benefits require `Ampere `__ based GPUs, such as A100s or 3090s. + +BFloat16 Mixed precision is similar to FP16 mixed precision, however, it maintains more of the "dynamic range" that FP32 offers. This means it is able to improve numerical stability than FP16 mixed precision. For more information, see `this TPU performance blogpost `__. + +Under the hood, we use `torch.autocast `__ with the dtype set to ``bfloat16``, with no gradient scaling. + +.. testcode:: + :skipif: not torch.cuda.is_available() + + Trainer(accelerator="gpu", devices=1, precision="bf16") + +It is also possible to use BFloat16 mixed precision on the CPU, relying on MKLDNN under the hood. + +.. testcode:: + + Trainer(precision="bf16") + +---- + +*************** +8-bit Optimizer +*************** + +It is possible to further reduce the precision using third-party libraries like `bitsandbytes `_. Although, +Lightning doesn't support it out of the box yet but you can still use it by configuring it in your LightningModule and setting ``Trainer(precision=32)``. + +.. code-block:: python + + import bitsandbytes as bnb + + + # in your LightningModule, return the 8-bit optimizer + def configure_optimizers(self): + return bnb.optim.Adam8bit(model.parameters(), lr=0.001, betas=(0.9, 0.995)) diff --git a/docs/source-pytorch/common/progress_bar.rst b/docs/source-pytorch/common/progress_bar.rst new file mode 100644 index 0000000..385d17e --- /dev/null +++ b/docs/source-pytorch/common/progress_bar.rst @@ -0,0 +1,138 @@ +.. testsetup:: * + + from lightning.pytorch.trainer.trainer import Trainer + +.. _progress_bar: + + +Customize the progress bar +========================== + +Lightning supports two different types of progress bars (`tqdm `_ and `rich `_). :class:`~lightning.pytorch.callbacks.TQDMProgressBar` is used by default, +but you can override it by passing a custom :class:`~lightning.pytorch.callbacks.TQDMProgressBar` or :class:`~lightning.pytorch.callbacks.RichProgressBar` to the ``callbacks`` argument of the :class:`~lightning.pytorch.trainer.trainer.Trainer`. + +You could also use the :class:`~lightning.pytorch.callbacks.ProgressBar` class to implement your own progress bar. + +------------- + +TQDMProgressBar +--------------- + +The :class:`~lightning.pytorch.callbacks.TQDMProgressBar` uses the `tqdm `_ library internally and is the default progress bar used by Lightning. +It prints to ``stdout`` and shows up to four different bars: + +- **sanity check progress:** the progress during the sanity check run +- **train progress:** shows the training progress. It will pause if validation starts and will resume when it ends, and also accounts for multiple validation runs during training when :paramref:`~lightning.pytorch.trainer.trainer.Trainer.val_check_interval` is used. +- **validation progress:** only visible during validation; shows total progress over all validation datasets. +- **test progress:** only active when testing; shows total progress over all test datasets. + +For infinite datasets, the progress bar never ends. + +You can update ``refresh_rate`` (rate (number of batches) at which the progress bar get updated) for :class:`~lightning.pytorch.callbacks.TQDMProgressBar` by: + +.. code-block:: python + + from lightning.pytorch.callbacks import TQDMProgressBar + + trainer = Trainer(callbacks=[TQDMProgressBar(refresh_rate=10)]) + +If you want to customize the default :class:`~lightning.pytorch.callbacks.TQDMProgressBar` used by Lightning, you can override +specific methods of the callback class and pass your custom implementation to the :class:`~lightning.pytorch.trainer.trainer.Trainer`. + +.. code-block:: python + + class LitProgressBar(TQDMProgressBar): + def init_validation_tqdm(self): + bar = super().init_validation_tqdm() + bar.set_description("running validation...") + return bar + + + trainer = Trainer(callbacks=[LitProgressBar()]) + +.. seealso:: + - :class:`~lightning.pytorch.callbacks.TQDMProgressBar` docs. + - `tqdm library `__ + +---------------- + +RichProgressBar +--------------- + +`Rich `_ is a Python library for rich text and beautiful formatting in the terminal. +To use the :class:`~lightning.pytorch.callbacks.RichProgressBar` as your progress bar, first install the package: + +.. code-block:: bash + + pip install rich + +Then configure the callback and pass it to the :class:`~lightning.pytorch.trainer.trainer.Trainer`: + +.. code-block:: python + + from lightning.pytorch.callbacks import RichProgressBar + + trainer = Trainer(callbacks=[RichProgressBar()]) + +Customize the theme for your :class:`~lightning.pytorch.callbacks.RichProgressBar` like this: + +.. code-block:: python + + from lightning.pytorch.callbacks import RichProgressBar + from lightning.pytorch.callbacks.progress.rich_progress import RichProgressBarTheme + + # create your own theme! + progress_bar = RichProgressBar( + theme=RichProgressBarTheme( + description="green_yellow", + progress_bar="green1", + progress_bar_finished="green1", + progress_bar_pulse="#6206E0", + batch_progress="green_yellow", + time="grey82", + processing_speed="grey82", + metrics="grey82", + ) + ) + + trainer = Trainer(callbacks=progress_bar) + +You can customize the components used within :class:`~lightning.pytorch.callbacks.RichProgressBar` with ease by overriding the +:func:`~lightning.pytorch.callbacks.RichProgressBar.configure_columns` method. + +.. code-block:: python + + from rich.progress import TextColumn + + custom_column = TextColumn("[progress.description]Custom Rich Progress Bar!") + + + class CustomRichProgressBar(RichProgressBar): + def configure_columns(self, trainer): + return [custom_column] + + + progress_bar = CustomRichProgressBar() + +If you wish for a new progress bar to be displayed at the end of every epoch, you should enable +:paramref:`RichProgressBar.leave ` by passing ``True`` + +.. code-block:: python + + from lightning.pytorch.callbacks import RichProgressBar + + trainer = Trainer(callbacks=[RichProgressBar(leave=True)]) + +.. seealso:: + - :class:`~lightning.pytorch.callbacks.RichProgressBar` docs. + - :class:`~lightning.pytorch.callbacks.RichModelSummary` docs to customize the model summary table. + - `Rich library `__. + + +.. note:: + + Progress bar is automatically enabled with the Trainer, and to disable it, one should do this: + + .. code-block:: python + + trainer = Trainer(enable_progress_bar=False) diff --git a/source/common/remote_fs.rst b/docs/source-pytorch/common/remote_fs.rst similarity index 91% rename from source/common/remote_fs.rst rename to docs/source-pytorch/common/remote_fs.rst index 29a4fe7..1b842ca 100644 --- a/source/common/remote_fs.rst +++ b/docs/source-pytorch/common/remote_fs.rst @@ -17,11 +17,12 @@ Working with different filesystems can be accomplished by appending a protocol l trainer = Trainer(default_root_dir="s3://my_bucket/data/") trainer.fit(model) -You could pass custom paths to loggers for logging data. + +For logging, remote filesystem support depends on the particular logger integration being used. Consult :ref:`the documentation of the individual logger ` for more details. .. code-block:: python - from pytorch_lightning.loggers import TensorBoardLogger + from lightning.pytorch.loggers import TensorBoardLogger logger = TensorBoardLogger(save_dir="s3://my_bucket/logs/") diff --git a/docs/source-pytorch/common/trainer.rst b/docs/source-pytorch/common/trainer.rst new file mode 100644 index 0000000..742555b --- /dev/null +++ b/docs/source-pytorch/common/trainer.rst @@ -0,0 +1,1355 @@ +.. role:: hidden + :class: hidden-section + +.. testsetup:: * + + import os + from lightning.pytorch import Trainer, LightningModule, seed_everything + +.. _trainer: + +Trainer +======= + +Once you've organized your PyTorch code into a :class:`~lightning.pytorch.core.module.LightningModule`, the ``Trainer`` automates everything else. + +The ``Trainer`` achieves the following: + +1. You maintain control over all aspects via PyTorch code in your :class:`~lightning.pytorch.core.module.LightningModule`. + +2. The trainer uses best practices embedded by contributors and users + from top AI labs such as Facebook AI Research, NYU, MIT, Stanford, etc... + +3. The trainer allows disabling any key part that you don't want automated. + +| + +----------- + +Basic use +--------- + +This is the basic use of the trainer: + +.. code-block:: python + + model = MyLightningModule() + + trainer = Trainer() + trainer.fit(model, train_dataloader, val_dataloader) + +-------- + +Under the hood +-------------- + +The Lightning ``Trainer`` does much more than just "training". Under the hood, it handles all loop details for you, some examples include: + +- Automatically enabling/disabling grads +- Running the training, validation and test dataloaders +- Calling the Callbacks at the appropriate times +- Putting batches and computations on the correct devices + +Here's the pseudocode for what the trainer does under the hood (showing the train loop only) + +.. code-block:: python + + # put model in train mode + model.train() + torch.set_grad_enabled(True) + + losses = [] + for batch in train_dataloader: + # calls hooks like this one + on_train_batch_start() + + # train step + loss = training_step(batch) + + # clear gradients + optimizer.zero_grad() + + # backward + loss.backward() + + # update parameters + optimizer.step() + + losses.append(loss) + + +-------- + +Trainer in Python scripts +------------------------- +In Python scripts, it's recommended you use a main function to call the Trainer. + +.. code-block:: python + + from argparse import ArgumentParser + + + def main(hparams): + model = LightningModule() + trainer = Trainer(accelerator=hparams.accelerator, devices=hparams.devices) + trainer.fit(model) + + + if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument("--accelerator", default=None) + parser.add_argument("--devices", default=None) + args = parser.parse_args() + + main(args) + +So you can run it like so: + +.. code-block:: bash + + python main.py --accelerator 'gpu' --devices 2 + +.. note:: + + Pro-tip: You don't need to define all flags manually. + You can let the :doc:`LightningCLI <../cli/lightning_cli>` create the Trainer and model with arguments supplied from the CLI. + + +If you want to stop a training run early, you can press "Ctrl + C" on your keyboard. +The trainer will catch the ``KeyboardInterrupt`` and attempt a graceful shutdown. The trainer object will also set +an attribute ``interrupted`` to ``True`` in such cases. If you have a callback which shuts down compute +resources, for example, you can conditionally run the shutdown logic for only uninterrupted runs by overriding :meth:`lightning.pytorch.Callback.on_exception`. + +------------ + +Validation +---------- +You can perform an evaluation epoch over the validation set, outside of the training loop, +using :meth:`~lightning.pytorch.trainer.trainer.Trainer.validate`. This might be +useful if you want to collect new metrics from a model right at its initialization +or after it has already been trained. + +.. code-block:: python + + trainer.validate(model=model, dataloaders=val_dataloaders) + +------------ + +Testing +------- +Once you're done training, feel free to run the test set! +(Only right before publishing your paper or pushing to production) + +.. code-block:: python + + trainer.test(dataloaders=test_dataloaders) + +------------ + +Reproducibility +--------------- + +To ensure full reproducibility from run to run you need to set seeds for pseudo-random generators, +and set ``deterministic`` flag in ``Trainer``. + +Example:: + + from lightning.pytorch import Trainer, seed_everything + + seed_everything(42, workers=True) + # sets seeds for numpy, torch and python.random. + model = Model() + trainer = Trainer(deterministic=True) + + +By setting ``workers=True`` in :func:`~lightning.pytorch.seed_everything`, Lightning derives +unique seeds across all dataloader workers and processes for :mod:`torch`, :mod:`numpy` and stdlib +:mod:`random` number generators. When turned on, it ensures that e.g. data augmentations are not repeated across workers. + +------- + +.. _trainer_flags: + +Trainer flags +------------- + +accelerator +^^^^^^^^^^^ + +Supports passing different accelerator types (``"cpu", "gpu", "tpu", "ipu", "auto"``) +as well as custom accelerator instances. + +.. code-block:: python + + # CPU accelerator + trainer = Trainer(accelerator="cpu") + + # Training with GPU Accelerator using 2 GPUs + trainer = Trainer(devices=2, accelerator="gpu") + + # Training with TPU Accelerator using 8 tpu cores + trainer = Trainer(devices=8, accelerator="tpu") + + # Training with GPU Accelerator using the DistributedDataParallel strategy + trainer = Trainer(devices=4, accelerator="gpu", strategy="ddp") + +.. note:: The ``"auto"`` option recognizes the machine you are on, and selects the appropriate ``Accelerator``. + +.. code-block:: python + + # If your machine has GPUs, it will use the GPU Accelerator for training + trainer = Trainer(devices=2, accelerator="auto") + +You can also modify hardware behavior by subclassing an existing accelerator to adjust for your needs. + +Example:: + + class MyOwnAcc(CPUAccelerator): + ... + + Trainer(accelerator=MyOwnAcc()) + +.. note:: + + If the ``devices`` flag is not defined, it will assume ``devices`` to be ``"auto"`` and fetch the ``auto_device_count`` + from the accelerator. + + .. code-block:: python + + # This is part of the built-in `CUDAAccelerator` + class CUDAAccelerator(Accelerator): + """Accelerator for GPU devices.""" + + @staticmethod + def auto_device_count() -> int: + """Get the devices when set to auto.""" + return torch.cuda.device_count() + + + # Training with GPU Accelerator using total number of gpus available on the system + Trainer(accelerator="gpu") + +accumulate_grad_batches +^^^^^^^^^^^^^^^^^^^^^^^ + +Accumulates gradients over k batches before stepping the optimizer. + +.. testcode:: + + # default used by the Trainer (no accumulation) + trainer = Trainer(accumulate_grad_batches=1) + +Example:: + + # accumulate every 4 batches (effective batch size is batch*4) + trainer = Trainer(accumulate_grad_batches=4) + +See also: :ref:`gradient_accumulation` to enable more fine-grained accumulation schedules. + + +benchmark +^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/benchmark.mp4 + :poster: ../_static/fetched-s3-assets/benchmark.jpg + :width: 400 + :muted: + +The value (``True`` or ``False``) to set ``torch.backends.cudnn.benchmark`` to. The value for +``torch.backends.cudnn.benchmark`` set in the current session will be used (``False`` if not manually set). +If :paramref:`~lightning.pytorch.trainer.Trainer.deterministic` is set to ``True``, this will default to ``False``. +You can read more about the interaction of ``torch.backends.cudnn.benchmark`` and ``torch.backends.cudnn.deterministic`` +`here `__ + +Setting this flag to ``True`` can increase the speed of your system if your input sizes don't +change. However, if they do, then it might make your system slower. The CUDNN auto-tuner will try to find the best +algorithm for the hardware when a new input size is encountered. This might also increase the memory usage. +Read more about it `here `__. + +Example:: + + # Will use whatever the current value for torch.backends.cudnn.benchmark, normally False + trainer = Trainer(benchmark=None) # default + + # you can overwrite the value + trainer = Trainer(benchmark=True) + +deterministic +^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/deterministic.mp4 + :poster: ../_static/fetched-s3-assets/deterministic.jpg + :width: 400 + :muted: + +This flag sets the ``torch.backends.cudnn.deterministic`` flag. +Might make your system slower, but ensures reproducibility. + +For more info check `PyTorch docs `_. + +Example:: + + # default used by the Trainer + trainer = Trainer(deterministic=False) + +callbacks +^^^^^^^^^ + +This argument can be used to add a :class:`~lightning.pytorch.callbacks.callback.Callback` or a list of them. +Callbacks run sequentially in the order defined here +with the exception of :class:`~lightning.pytorch.callbacks.model_checkpoint.ModelCheckpoint` callbacks which run +after all others to ensure all states are saved to the checkpoints. + +.. code-block:: python + + # single callback + trainer = Trainer(callbacks=PrintCallback()) + + # a list of callbacks + trainer = Trainer(callbacks=[PrintCallback()]) + +Example:: + + from lightning.pytorch.callbacks import Callback + + class PrintCallback(Callback): + def on_train_start(self, trainer, pl_module): + print("Training is started!") + def on_train_end(self, trainer, pl_module): + print("Training is done.") + + +Model-specific callbacks can also be added inside the ``LightningModule`` through +:meth:`~lightning.pytorch.core.module.LightningModule.configure_callbacks`. +Callbacks returned in this hook will extend the list initially given to the ``Trainer`` argument, and replace +the trainer callbacks should there be two or more of the same type. +:class:`~lightning.pytorch.callbacks.model_checkpoint.ModelCheckpoint` callbacks always run last. + + +check_val_every_n_epoch +^^^^^^^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/check_val_every_n_epoch.mp4 + :poster: ../_static/fetched-s3-assets/check_val_every_n_epoch.jpg + :width: 400 + :muted: + +Check val every n train epochs. + +Example:: + + # default used by the Trainer + trainer = Trainer(check_val_every_n_epoch=1) + + # run val loop every 10 training epochs + trainer = Trainer(check_val_every_n_epoch=10) + + +default_root_dir +^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/default_root_dir.mp4 + :poster: ../_static/fetched-s3-assets/default%E2%80%A8_root_dir.jpg + :width: 400 + :muted: + +Default path for logs and weights when no logger or +:class:`lightning.pytorch.callbacks.ModelCheckpoint` callback passed. On +certain clusters you might want to separate where logs and checkpoints are +stored. If you don't then use this argument for convenience. Paths can be local +paths or remote paths such as ``s3://bucket/path`` or ``hdfs://path/``. Credentials +will need to be set up to use remote filepaths. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(default_root_dir=os.getcwd()) + +devices +^^^^^^^ + +Number of devices to train on (``int``), which devices to train on (``list`` or ``str``), or ``"auto"``. + +.. code-block:: python + + # Training with CPU Accelerator using 2 processes + trainer = Trainer(devices=2, accelerator="cpu") + + # Training with GPU Accelerator using GPUs 1 and 3 + trainer = Trainer(devices=[1, 3], accelerator="gpu") + + # Training with TPU Accelerator using 8 tpu cores + trainer = Trainer(devices=8, accelerator="tpu") + +.. tip:: The ``"auto"`` option recognizes the devices to train on, depending on the ``Accelerator`` being used. + +.. code-block:: python + + # Use whatever hardware your machine has available + trainer = Trainer(devices="auto", accelerator="auto") + + # Training with CPU Accelerator using 1 process + trainer = Trainer(devices="auto", accelerator="cpu") + + # Training with TPU Accelerator using 8 tpu cores + trainer = Trainer(devices="auto", accelerator="tpu") + + # Training with IPU Accelerator using 4 ipus + trainer = Trainer(devices="auto", accelerator="ipu") + +.. note:: + + If the ``devices`` flag is not defined, it will assume ``devices`` to be ``"auto"`` and fetch the ``auto_device_count`` + from the accelerator. + + .. code-block:: python + + # This is part of the built-in `CUDAAccelerator` + class CUDAAccelerator(Accelerator): + """Accelerator for GPU devices.""" + + @staticmethod + def auto_device_count() -> int: + """Get the devices when set to auto.""" + return torch.cuda.device_count() + + + # Training with GPU Accelerator using total number of gpus available on the system + Trainer(accelerator="gpu") + +enable_checkpointing +^^^^^^^^^^^^^^^^^^^^ + +By default Lightning saves a checkpoint for you in your current working directory, with the state of your last training epoch, +Checkpoints capture the exact value of all parameters used by a model. +To disable automatic checkpointing, set this to `False`. + +.. code-block:: python + + # default used by Trainer, saves the most recent model to a single checkpoint after each epoch + trainer = Trainer(enable_checkpointing=True) + + # turn off automatic checkpointing + trainer = Trainer(enable_checkpointing=False) + + +You can override the default behavior by initializing the :class:`~lightning.pytorch.callbacks.ModelCheckpoint` +callback, and adding it to the :paramref:`~lightning.pytorch.trainer.trainer.Trainer.callbacks` list. +See :doc:`Saving and Loading Checkpoints <../common/checkpointing>` for how to customize checkpointing. + +.. testcode:: + + from lightning.pytorch.callbacks import ModelCheckpoint + + # Init ModelCheckpoint callback, monitoring 'val_loss' + checkpoint_callback = ModelCheckpoint(monitor="val_loss") + + # Add your callback to the callbacks list + trainer = Trainer(callbacks=[checkpoint_callback]) + +fast_dev_run +^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/fast_dev_run.mp4 + :poster: ../_static/fetched-s3-assets/fast_dev_run.jpg + :width: 400 + :muted: + +Runs n if set to ``n`` (int) else 1 if set to ``True`` batch(es) to ensure your code will execute without errors. This +applies to fitting, validating, testing, and predicting. This flag is **only** recommended for debugging purposes and +should not be used to limit the number of batches to run. + +.. code-block:: python + + # default used by the Trainer + trainer = Trainer(fast_dev_run=False) + + # runs only 1 training and 1 validation batch and the program ends + trainer = Trainer(fast_dev_run=True) + trainer.fit(...) + + # runs 7 predict batches and program ends + trainer = Trainer(fast_dev_run=7) + trainer.predict(...) + +This argument is different from ``limit_{train,val,test,predict}_batches`` because side effects are avoided to reduce the +impact to subsequent runs. These are the changes enabled: + +- Sets ``Trainer(max_epochs=1)``. +- Sets ``Trainer(max_steps=...)`` to 1 or the number passed. +- Sets ``Trainer(num_sanity_val_steps=0)``. +- Sets ``Trainer(val_check_interval=1.0)``. +- Sets ``Trainer(check_every_n_epoch=1)``. +- Disables all loggers. +- Disables passing logged metrics to loggers. +- The :class:`~lightning.pytorch.callbacks.model_checkpoint.ModelCheckpoint` callbacks will not trigger. +- The :class:`~lightning.pytorch.callbacks.early_stopping.EarlyStopping` callbacks will not trigger. +- Sets ``limit_{train,val,test,predict}_batches`` to 1 or the number passed. +- Disables the tuning callbacks (:class:`~lightning.pytorch.callbacks.batch_size_finder.BatchSizeFinder`, :class:`~lightning.pytorch.callbacks.lr_finder.LearningRateFinder`). +- If using the CLI, the configuration file is not saved. + + +gradient_clip_val +^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/gradient_clip_val.mp4 + :poster: ../_static/fetched-s3-assets/gradient+_clip_val.jpg + :width: 400 + :muted: + +Gradient clipping value + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(gradient_clip_val=None) + +limit_train_batches +^^^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/limit_batches.mp4 + :poster: ../_static/fetched-s3-assets/limit_train_batches.jpg + :width: 400 + :muted: + +How much of training dataset to check. +Useful when debugging or testing something that happens at the end of an epoch. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(limit_train_batches=1.0) + +Example:: + + # default used by the Trainer + trainer = Trainer(limit_train_batches=1.0) + + # run through only 25% of the training set each epoch + trainer = Trainer(limit_train_batches=0.25) + + # run through only 10 batches of the training set each epoch + trainer = Trainer(limit_train_batches=10) + +limit_test_batches +^^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/limit_batches.mp4 + :poster: ../_static/fetched-s3-assets/limit_test_batches.jpg + :width: 400 + :muted: + +How much of test dataset to check. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(limit_test_batches=1.0) + + # run through only 25% of the test set each epoch + trainer = Trainer(limit_test_batches=0.25) + + # run for only 10 batches + trainer = Trainer(limit_test_batches=10) + +In the case of multiple test dataloaders, the limit applies to each dataloader individually. + +limit_val_batches +^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/limit_batches.mp4 + :poster: ../_static/fetched-s3-assets/limit_val_batches.jpg + :width: 400 + :muted: + +How much of validation dataset to check. +Useful when debugging or testing something that happens at the end of an epoch. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(limit_val_batches=1.0) + + # run through only 25% of the validation set each epoch + trainer = Trainer(limit_val_batches=0.25) + + # run for only 10 batches + trainer = Trainer(limit_val_batches=10) + + # disable validation + trainer = Trainer(limit_val_batches=0) + +In the case of multiple validation dataloaders, the limit applies to each dataloader individually. + +log_every_n_steps +^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/log_every_n_steps.mp4 + :poster: ../_static/fetched-s3-assets/log_every_n_steps.jpg + :width: 400 + :muted: + +How often to add logging rows (does not write to disk) + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(log_every_n_steps=50) + +See Also: + - :doc:`logging <../extensions/logging>` + +logger +^^^^^^ + +:doc:`Logger <../visualize/loggers>` (or iterable collection of loggers) for experiment tracking. A ``True`` value uses the default ``TensorBoardLogger`` shown below. ``False`` will disable logging. + +.. testcode:: + :skipif: not _TENSORBOARD_AVAILABLE and not _TENSORBOARDX_AVAILABLE + + from lightning.pytorch.loggers import TensorBoardLogger + + # default logger used by trainer (if tensorboard is installed) + logger = TensorBoardLogger(save_dir=os.getcwd(), version=1, name="lightning_logs") + Trainer(logger=logger) + +max_epochs +^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/min_max_epochs.mp4 + :poster: ../_static/fetched-s3-assets/max_epochs.jpg + :width: 400 + :muted: + +Stop training once this number of epochs is reached + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(max_epochs=1000) + +If both ``max_epochs`` and ``max_steps`` aren't specified, ``max_epochs`` will default to ``1000``. +To enable infinite training, set ``max_epochs = -1``. + +min_epochs +^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/min_max_epochs.mp4 + :poster: ../_static/fetched-s3-assets/min_epochs.jpg + :width: 400 + :muted: + +Force training for at least these many epochs + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(min_epochs=1) + +max_steps +^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/min_max_steps.mp4 + :poster: ../_static/fetched-s3-assets/max_steps.jpg + :width: 400 + :muted: + +Stop training after this number of :ref:`global steps `. +Training will stop if max_steps or max_epochs have reached (earliest). + +.. testcode:: + + # Default (disabled) + trainer = Trainer(max_steps=-1) + + # Stop after 100 steps + trainer = Trainer(max_steps=100) + +If ``max_steps`` is not specified, ``max_epochs`` will be used instead (and ``max_epochs`` defaults to +``1000`` if ``max_epochs`` is not specified). To disable this default, set ``max_steps = -1``. + +min_steps +^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/min_max_steps.mp4 + :poster: ../_static/fetched-s3-assets/min_steps.jpg + :width: 400 + :muted: + +Force training for at least this number of :ref:`global steps `. +Trainer will train model for at least min_steps or min_epochs (latest). + +.. testcode:: + + # Default (disabled) + trainer = Trainer(min_steps=None) + + # Run at least for 100 steps (disable min_epochs) + trainer = Trainer(min_steps=100, min_epochs=0) + +max_time +^^^^^^^^ + +Set the maximum amount of time for training. Training will get interrupted mid-epoch. +For customizable options use the :class:`~lightning.pytorch.callbacks.timer.Timer` callback. + +.. testcode:: + + # Default (disabled) + trainer = Trainer(max_time=None) + + # Stop after 12 hours of training or when reaching 10 epochs (string) + trainer = Trainer(max_time="00:12:00:00", max_epochs=10) + + # Stop after 1 day and 5 hours (dict) + trainer = Trainer(max_time={"days": 1, "hours": 5}) + +In case ``max_time`` is used together with ``min_steps`` or ``min_epochs``, the ``min_*`` requirement +always has precedence. + +num_nodes +^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/num_nodes.mp4 + :poster: ../_static/fetched-s3-assets/num_nodes.jpg + :width: 400 + :muted: + +Number of GPU nodes for distributed training. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(num_nodes=1) + + # to train on 8 nodes + trainer = Trainer(num_nodes=8) + + +num_sanity_val_steps +^^^^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/num_sanity_val_steps.mp4 + :poster: ../_static/fetched-s3-assets/num_sanity%E2%80%A8_val_steps.jp + :width: 400 + :muted: + +Sanity check runs n batches of val before starting the training routine. +This catches any bugs in your validation without having to wait for the first validation check. +The Trainer uses 2 steps by default. Turn it off or modify it here. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(num_sanity_val_steps=2) + + # turn it off + trainer = Trainer(num_sanity_val_steps=0) + + # check all validation data + trainer = Trainer(num_sanity_val_steps=-1) + + +This option will reset the validation dataloader unless ``num_sanity_val_steps=0``. + +overfit_batches +^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/overfit_batches.mp4 + :poster: ../_static/fetched-s3-assets/overfit_batches.jpg + :width: 400 + :muted: + +Uses this much data of the training & validation set. +If the training & validation dataloaders have ``shuffle=True``, Lightning will automatically disable it. + +Useful for quickly debugging or trying to overfit on purpose. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(overfit_batches=0.0) + + # use only 1% of the train & val set + trainer = Trainer(overfit_batches=0.01) + + # overfit on 10 of the same batches + trainer = Trainer(overfit_batches=10) + +plugins +^^^^^^^ + +:ref:`Plugins` allow you to connect arbitrary backends, precision libraries, clusters etc. For example: + +- :ref:`Checkpoint IO ` +- `TorchElastic `_ +- :ref:`Precision Plugins ` + +To define your own behavior, subclass the relevant class and pass it in. Here's an example linking up your own +:class:`~lightning.pytorch.plugins.environments.ClusterEnvironment`. + +.. code-block:: python + + from lightning.pytorch.plugins.environments import ClusterEnvironment + + + class MyCluster(ClusterEnvironment): + def main_address(self): + return your_main_address + + def main_port(self): + return your_main_port + + def world_size(self): + return the_world_size + + + trainer = Trainer(plugins=[MyCluster()], ...) + +precision +^^^^^^^^^ + +Lightning supports either double (64), float (32), bfloat16 (bf16), or half (16) precision training. + +Half precision, or mixed precision, is the combined use of 32 and 16 bit floating points to reduce memory footprint during model training. This can result in improved performance, achieving +3X speedups on modern GPUs. + +.. testcode:: + :skipif: not torch.cuda.is_available() + + # default used by the Trainer + trainer = Trainer(precision=32) + + # 16-bit precision + trainer = Trainer(precision="16-mixed", accelerator="gpu", devices=1) # works only on CUDA + + # bfloat16 precision + trainer = Trainer(precision="bf16-mixed") + + # 64-bit precision + trainer = Trainer(precision=64) + + +.. note:: When running on TPUs, torch.bfloat16 will be used but tensor printing will still show torch.float32. + +profiler +^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/profiler.mp4 + :poster: ../_static/fetched-s3-assets/profiler.jpg + :width: 400 + :muted: + +To profile individual steps during training and assist in identifying bottlenecks. + +See the :doc:`profiler documentation <../tuning/profiler>`. for more details. + +.. testcode:: + + from lightning.pytorch.profilers import SimpleProfiler, AdvancedProfiler + + # default used by the Trainer + trainer = Trainer(profiler=None) + + # to profile standard training events, equivalent to `profiler=SimpleProfiler()` + trainer = Trainer(profiler="simple") + + # advanced profiler for function-level stats, equivalent to `profiler=AdvancedProfiler()` + trainer = Trainer(profiler="advanced") + +enable_progress_bar +^^^^^^^^^^^^^^^^^^^ + +Whether to enable or disable the progress bar. Defaults to True. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(enable_progress_bar=True) + + # disable progress bar + trainer = Trainer(enable_progress_bar=False) + +reload_dataloaders_every_n_epochs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/reload_dataloaders_every_epoch.mp4 + :poster: ../_static/fetched-s3-assets/reload_%E2%80%A8dataloaders_%E2%80%A8every_epoch.jpg + :width: 400 + :muted: + +Set to a positive integer to reload dataloaders every n epochs from your currently used data source. +DataSource can be a ``LightningModule`` or a ``LightningDataModule``. + + +.. code-block:: python + + # if 0 (default) + train_loader = model.train_dataloader() + # or if using data module: datamodule.train_dataloader() + for epoch in epochs: + for batch in train_loader: + ... + + # if a positive integer + for epoch in epochs: + if not epoch % reload_dataloaders_every_n_epochs: + train_loader = model.train_dataloader() + # or if using data module: datamodule.train_dataloader() + for batch in train_loader: + ... + +The pseudocode applies also to the ``val_dataloader``. + +.. _replace-sampler-ddp: + +use_distributed_sampler +^^^^^^^^^^^^^^^^^^^^^^^ + +See :paramref:`lightning.pytorch.trainer.Trainer.params.use_distributed_sampler`. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(use_distributed_sampler=True) + +By setting to False, you have to add your own distributed sampler: + +.. code-block:: python + + # in your LightningModule or LightningDataModule + def train_dataloader(self): + dataset = ... + # default used by the Trainer + sampler = torch.utils.data.DistributedSampler(dataset, shuffle=True) + dataloader = DataLoader(dataset, batch_size=32, sampler=sampler) + return dataloader + + +strategy +^^^^^^^^ + +Supports passing different training strategies with aliases (ddp, fsdp, etc) as well as configured strategies. + +.. code-block:: python + + # Data-parallel training with the DDP strategy on 4 GPUs + trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4) + + # Model-parallel training with the FSDP strategy on 4 GPUs + trainer = Trainer(strategy="fsdp", accelerator="gpu", devices=4) + +Additionally, you can pass a strategy object. + +.. code-block:: python + + from lightning.pytorch.strategies import DDPStrategy + + trainer = Trainer(strategy=DDPStrategy(static_graph=True), accelerator="gpu", devices=2) + +See Also: + - :ref:`Multi GPU Training `. + - :doc:`Model Parallel GPU training guide <../advanced/model_parallel>`. + - :doc:`TPU training guide <../accelerators/tpu>`. + + +sync_batchnorm +^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/sync_batchnorm.mp4 + :poster: ../_static/fetched-s3-assets/sync_batchnorm.jpg + :width: 400 + :muted: + +Enable synchronization between batchnorm layers across all GPUs. + +.. testcode:: + + trainer = Trainer(sync_batchnorm=True) + + +val_check_interval +^^^^^^^^^^^^^^^^^^ + +.. video:: ../_static/fetched-s3-assets/val_check_interval.mp4 + :poster: ../_static/fetched-s3-assets/val_check_interval.jpg + :width: 400 + :muted: + +How often within one training epoch to check the validation set. +Can specify as float or int. + +- pass a ``float`` in the range [0.0, 1.0] to check after a fraction of the training epoch. +- pass an ``int`` to check after a fixed number of training batches. An ``int`` value can only be higher than the number of training + batches when ``check_val_every_n_epoch=None``, which validates after every ``N`` training batches across epochs or iteration-based training. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(val_check_interval=1.0) + + # check validation set 4 times during a training epoch + trainer = Trainer(val_check_interval=0.25) + + # check validation set every 1000 training batches in the current epoch + trainer = Trainer(val_check_interval=1000) + + # check validation set every 1000 training batches across complete epochs or during iteration-based training + # use this when using iterableDataset and your dataset has no length + # (ie: production cases with streaming data) + trainer = Trainer(val_check_interval=1000, check_val_every_n_epoch=None) + + +.. code-block:: python + + # Here is the computation to estimate the total number of batches seen within an epoch. + + # Find the total number of train batches + total_train_batches = total_train_samples // (train_batch_size * world_size) + + # Compute how many times we will call validation during the training loop + val_check_batch = max(1, int(total_train_batches * val_check_interval)) + val_checks_per_epoch = total_train_batches / val_check_batch + + # Find the total number of validation batches + total_val_batches = total_val_samples // (val_batch_size * world_size) + + # Total number of batches run + total_fit_batches = total_train_batches + total_val_batches + + +enable_model_summary +^^^^^^^^^^^^^^^^^^^^ + +Whether to enable or disable the model summarization. Defaults to True. + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(enable_model_summary=True) + + # disable summarization + trainer = Trainer(enable_model_summary=False) + + # enable custom summarization + from lightning.pytorch.callbacks import ModelSummary + + trainer = Trainer(enable_model_summary=True, callbacks=[ModelSummary(max_depth=-1)]) + + +inference_mode +^^^^^^^^^^^^^^ + +Whether to use :func:`torch.inference_mode` or :func:`torch.no_grad` mode during evaluation +(``validate``/``test``/``predict``) + +.. testcode:: + + # default used by the Trainer + trainer = Trainer(inference_mode=True) + + # Use `torch.no_grad` instead + trainer = Trainer(inference_mode=False) + + +With :func:`torch.inference_mode` disabled, you can enable the grad of your model layers if required. + +.. code-block:: python + + class LitModel(LightningModule): + def validation_step(self, batch, batch_idx): + preds = self.layer1(batch) + with torch.enable_grad(): + grad_preds = preds.requires_grad_() + preds2 = self.layer2(grad_preds) + + + model = LitModel() + trainer = Trainer(inference_mode=False) + trainer.validate(model) + + +----- + +Trainer class API +----------------- + +Methods +^^^^^^^ + +init +**** + +.. automethod:: lightning.pytorch.trainer.Trainer.__init__ + :noindex: + +fit +**** + +.. automethod:: lightning.pytorch.trainer.Trainer.fit + :noindex: + +validate +******** + +.. automethod:: lightning.pytorch.trainer.Trainer.validate + :noindex: + +test +**** + +.. automethod:: lightning.pytorch.trainer.Trainer.test + :noindex: + +predict +******* + +.. automethod:: lightning.pytorch.trainer.Trainer.predict + :noindex: + + +Properties +^^^^^^^^^^ + +callback_metrics +**************** + +The metrics available to callbacks. + +This includes metrics logged via :meth:`~lightning.pytorch.core.module.LightningModule.log`. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + self.log("a_val", 2.0) + + + callback_metrics = trainer.callback_metrics + assert callback_metrics["a_val"] == 2.0 + +logged_metrics +************** + +The metrics sent to the loggers. + +This includes metrics logged via :meth:`~lightning.pytorch.core.module.LightningModule.log` with the +:paramref:`~lightning.pytorch.core.module.LightningModule.log.logger` argument set. + +progress_bar_metrics +******************** + +The metrics sent to the progress bar. + +This includes metrics logged via :meth:`~lightning.pytorch.core.module.LightningModule.log` with the +:paramref:`~lightning.pytorch.core.module.LightningModule.log.prog_bar` argument set. + +current_epoch +************* + +The current epoch, updated after the epoch end hooks are run. + +datamodule +********** + +The current datamodule, which is used by the trainer. + +.. code-block:: python + + used_datamodule = trainer.datamodule + +is_last_batch +************* + +Whether trainer is executing the last batch. + +global_step +*********** + +The number of optimizer steps taken (does not reset each epoch). + +This includes multiple optimizers (if enabled). + +logger +******* + +The first :class:`~lightning.pytorch.loggers.logger.Logger` being used. + +loggers +******** + +The list of :class:`~lightning.pytorch.loggers.logger.Logger` used. + +.. code-block:: python + + for logger in trainer.loggers: + logger.log_metrics({"foo": 1.0}) + +log_dir +******* + +The directory for the current experiment. Use this to save images to, etc... + +.. code-block:: python + + def training_step(self, batch, batch_idx): + img = ... + save_img(img, self.trainer.log_dir) + +is_global_zero +************** + +Whether this process is the global zero in multi-node training. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + if self.trainer.is_global_zero: + print("in node 0, accelerator 0") + +estimated_stepping_batches +************************** + +The estimated number of batches that will ``optimizer.step()`` during training. + +This accounts for gradient accumulation and the current trainer configuration. This might sets up your training +dataloader if hadn't been set up already. + +.. code-block:: python + + def configure_optimizers(self): + optimizer = ... + stepping_batches = self.trainer.estimated_stepping_batches + scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr=1e-3, total_steps=stepping_batches) + return [optimizer], [scheduler] + +state +***** + +The current state of the Trainer, including the current function that is running, the stage of +execution within that function, and the status of the Trainer. + +.. code-block:: python + + # fn in ("fit", "validate", "test", "predict") + trainer.state.fn + # status in ("initializing", "running", "finished", "interrupted") + trainer.state.status + # stage in ("train", "sanity_check", "validate", "test", "predict") + trainer.state.stage + +should_stop +*********** + +If you want to terminate the training during ``.fit``, you can set ``trainer.should_stop=True`` to terminate the training +as soon as possible. Note that, it will respect the arguments ``min_steps`` and ``min_epochs`` to check whether to stop. If these +arguments are set and the ``current_epoch`` or ``global_step`` don't meet these minimum conditions, training will continue until +both conditions are met. If any of these arguments is not set, it won't be considered for the final decision. + + +.. code-block:: python + + # setting `trainer.should_stop` at any point of training will terminate it + class LitModel(LightningModule): + def training_step(self, *args, **kwargs): + self.trainer.should_stop = True + + + trainer = Trainer() + model = LitModel() + trainer.fit(model) + +.. code-block:: python + + # setting `trainer.should_stop` will stop training only after at least 5 epochs have run + class LitModel(LightningModule): + def training_step(self, *args, **kwargs): + if self.current_epoch == 2: + self.trainer.should_stop = True + + + trainer = Trainer(min_epochs=5, max_epochs=100) + model = LitModel() + trainer.fit(model) + +.. code-block:: python + + # setting `trainer.should_stop` will stop training only after at least 5 steps have run + class LitModel(LightningModule): + def training_step(self, *args, **kwargs): + if self.global_step == 2: + self.trainer.should_stop = True + + + trainer = Trainer(min_steps=5, max_epochs=100) + model = LitModel() + trainer.fit(model) + +.. code-block:: python + + # setting `trainer.should_stop` at any until both min_steps and min_epochs are satisfied + class LitModel(LightningModule): + def training_step(self, *args, **kwargs): + if self.global_step == 7: + self.trainer.should_stop = True + + + trainer = Trainer(min_steps=5, min_epochs=5, max_epochs=100) + model = LitModel() + trainer.fit(model) + +sanity_checking +*************** + +Indicates if the trainer is currently running sanity checking. This property can be useful to disable some hooks, +logging or callbacks during the sanity checking. + +.. code-block:: python + + def validation_step(self, batch, batch_idx): + ... + if not self.trainer.sanity_checking: + self.log("value", value) + +num_training_batches +******************** + +The number of training batches that will be used during ``trainer.fit()``. + +num_sanity_val_batches +********************** + +The number of validation batches that will be used during the sanity-checking part of ``trainer.fit()``. + +num_val_batches +*************** + +The number of validation batches that will be used during ``trainer.fit()`` or ``trainer.validate()``. + +num_test_batches +**************** + +The number of test batches that will be used during ``trainer.test()``. + +num_predict_batches +******************* + +The number of prediction batches that will be used during ``trainer.predict()``. + +train_dataloader +**************** + +The training dataloader(s) used during ``trainer.fit()``. + +val_dataloaders +*************** + +The validation dataloader(s) used during ``trainer.fit()`` or ``trainer.validate()``. + +test_dataloaders +**************** + +The test dataloader(s) used during ``trainer.test()``. + +predict_dataloaders +******************* + +The prediction dataloader(s) used during ``trainer.predict()``. diff --git a/source/common_usecases.rst b/docs/source-pytorch/common_usecases.rst similarity index 94% rename from source/common_usecases.rst rename to docs/source-pytorch/common_usecases.rst index 606eea9..263fb34 100644 --- a/source/common_usecases.rst +++ b/docs/source-pytorch/common_usecases.rst @@ -6,10 +6,6 @@ Common Workflows Customize and extend Lightning for things like custom hardware or distributed strategies. -.. join_slack:: - :align: left - ----- .. raw:: html @@ -116,13 +112,6 @@ Customize and extend Lightning for things like custom hardware or distributed st :button_link: advanced/model_parallel.html :height: 100 -.. displayitem:: - :header: Train on the cloud - :description: Run models on the cloud. - :col_css: col-md-12 - :button_link: clouds/cloud_training.html - :height: 100 - .. displayitem:: :header: Train on single or multiple GPUs :description: Train models faster with GPUs. @@ -134,7 +123,7 @@ Customize and extend Lightning for things like custom hardware or distributed st :header: Train on single or multiple HPUs :description: Train models faster with HPUs. :col_css: col-md-12 - :button_link: accelerators/hpu.html + :button_link: integrations/hpu/index.html :height: 100 .. displayitem:: diff --git a/docs/source-pytorch/community/governance.rst b/docs/source-pytorch/community/governance.rst new file mode 100644 index 0000000..3530664 --- /dev/null +++ b/docs/source-pytorch/community/governance.rst @@ -0,0 +1,71 @@ +.. _governance: + +Lightning Governance +#################### + +This document describes governance processes we follow in developing PyTorch Lightning. + +Persons of Interest +******************* + +.. _governance_bdfl: + +BDFL +---- +Role: All final decisions related to Lightning. + +- William Falcon (`williamFalcon `_) (Lightning founder) + +Maintainers +----------- +- Adrian Wälchli (`awaelchli `_) +- Carlos Mocholí (`carmocca `_) +- Jirka Borovec (`Borda `_) +- Justus Schock (`justusschock `_) + + +Emeritus Maintainers +-------------------- +- Ethan Harris (`ethanwharris `_) (Torchbearer founder) +- Nicki Skafte (`SkafteNicki `_) +- Thomas Chaton (`tchaton `_) + + +Alumni +------ +- Akihiro Nitta (`akihironitta `_) +- Ananth Subramaniam (`ananthsub `_) +- Danielle Pintz (`daniellepintz `_) +- Jeff Ling (`jeffling `_) +- Jeff Yang (`ydcjeff `_) +- Jeremy Jordan (`jeremyjordan `_) +- Kaushik Bokka (`kaushikb11 `_) +- Kushashwa Ravi Shrimali (`krshrimali `_) +- Lezwon Castelino (`lezwon `_) +- Matthew Painter (`MattPainter01 `_) (Torchbearer founder) +- Nate Raw (`nateraw `_) +- Nic Eggert (`neggert `_) +- Ota Jasek (`otaj `_) +- Peter Yu (`yukw777 `_) +- Roger Shieh (`s-rog `_) +- Rohit Gupta (`rohitgr7 `_) +- Sean Narenthiran (`SeanNaren `_) +- Siyu Wang (`four4fish `_) +- Teddy Koker (`teddykoker `_) +- Tullie Murrell (`tullie `_) + + +Project Management and Decision Making +************************************** + +The decision what goes into a release is governed by the maintainers of ``lightning.pytorch``. +Whenever possible, discussion happens publicly on GitHub and includes the whole community. +For controversial changes, it is mandatory to seek consultation from :ref:`governance_bdfl` for a final decision. +When a consensus is reached, maintainers assign milestones and labels to the issue and/or pull request +and start tracking the development. It is possible that priorities change over time. + +Commits to the project are exclusively to be added by pull requests on GitHub and anyone in the community is welcome to +review them. However, reviews submitted by +`code owners `_ +have higher weight and it is necessary to get the approval of code owners before a pull request can be merged. +Additional requirements may apply case by case. diff --git a/docs/source-pytorch/community/index.rst b/docs/source-pytorch/community/index.rst new file mode 100644 index 0000000..95f30a6 --- /dev/null +++ b/docs/source-pytorch/community/index.rst @@ -0,0 +1,75 @@ + +.. toctree:: + :maxdepth: 1 + :hidden: + + ../generated/CODE_OF_CONDUCT.md + ../generated/CONTRIBUTING.md + ../generated/BECOMING_A_CORE_CONTRIBUTOR.md + governance + ../versioning + ../past_versions + ../generated/CHANGELOG.md + +######### +Community +######### + +.. raw:: html + +
+
+ +.. displayitem:: + :header: Code of conduct + :description: Contributor Covenant Code of Conduct + :col_css: col-md-12 + :button_link: ../generated/CODE_OF_CONDUCT.html + :height: 100 + +.. displayitem:: + :header: Contribution guide + :description: How to contribute to PyTorch Lightning + :col_css: col-md-12 + :button_link: ../generated/CONTRIBUTING.html + :height: 100 + +.. displayitem:: + :header: How to Become a core contributor + :description: Steps to be a core contributor + :col_css: col-md-12 + :button_link: ../generated/BECOMING_A_CORE_CONTRIBUTOR.html + :height: 100 + +.. displayitem:: + :header: Lightning Governance + :description: The governance processes we follow + :col_css: col-md-12 + :button_link: governance.html + :height: 100 + +.. displayitem:: + :header: Versioning + :description: PyTorch Lightning's versioning policy + :col_css: col-md-12 + :button_link: ../versioning.html + :height: 100 + +.. displayitem:: + :header: Past PyTorch Lightning versions + :description: Docs and upgrade guide for past versions + :col_css: col-md-12 + :button_link: ../past_versions.html + :height: 100 + +.. displayitem:: + :header: Changelog + :description: All notable changes to PyTorch Lightning + :col_css: col-md-12 + :button_link: ../generated/CHANGELOG.html + :height: 100 + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/conf.py b/docs/source-pytorch/conf.py new file mode 100644 index 0000000..4a65e2e --- /dev/null +++ b/docs/source-pytorch/conf.py @@ -0,0 +1,451 @@ +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +import glob +import os +import shutil +import warnings +from importlib.util import module_from_spec, spec_from_file_location +from types import ModuleType + +import pt_lightning_sphinx_theme +from lightning_utilities.docs import fetch_external_assets +from lightning_utilities.docs.formatting import _transform_changelog + +import lightning + +# ----------------------- +# VARIABLES WHEN WORKING ON DOCS... MAKE THIS TRUE TO BUILD FASTER +# ----------------------- +_PL_FAST_DOCS_DEV = bool(int(os.getenv("PL_FAST_DOCS_DEV", 0))) + +# ----------------------- +# BUILD stuff +# ----------------------- +_PATH_HERE = os.path.abspath(os.path.dirname(__file__)) +_PATH_ROOT = os.path.join(_PATH_HERE, "..", "..") +_PATH_RAW_NB = os.path.join(_PATH_ROOT, "_notebooks") +_SHOULD_COPY_NOTEBOOKS = True + + +def _load_py_module(name: str, location: str) -> ModuleType: + spec = spec_from_file_location(name, location) + py = module_from_spec(spec) + spec.loader.exec_module(py) + return py + + +assist_local = _load_py_module("assistant", os.path.join(_PATH_ROOT, ".actions", "assistant.py")) + +if os.path.isdir(os.path.join(_PATH_RAW_NB, ".actions")): + assist_nb = _load_py_module("assistant", os.path.join(_PATH_RAW_NB, ".actions", "assistant.py")) +else: + _SHOULD_COPY_NOTEBOOKS = False + warnings.warn("To build the code, please run: `git submodule update --init --recursive`", stacklevel=2) + +FOLDER_GENERATED = "generated" +SPHINX_MOCK_REQUIREMENTS = int(os.environ.get("SPHINX_MOCK_REQUIREMENTS", True)) + +# -- Project documents ------------------------------------------------------- + +if _SHOULD_COPY_NOTEBOOKS: + assist_nb.AssistantCLI.copy_notebooks( + _PATH_RAW_NB, + _PATH_HERE, + "notebooks", + patterns=[".", "course_UvA-DL", "lightning_examples"], + ) + # TODO: Complete converting the missing items and add them back + ignore = [ + "course_UvA-DL/13-contrastive-learning", + "lightning_examples/augmentation_kornia", + "lightning_examples/finetuning-scheduler", + "lightning_examples/reinforce-learning-DQN", + "lightning_examples/text-transformers", + "lightning_examples/warp-drive", + ] + for file in ignore: + file = os.path.join(_PATH_HERE, "notebooks", file) + if os.path.exists(file): + os.remove(file) + + +os.makedirs(os.path.join(_PATH_HERE, FOLDER_GENERATED), exist_ok=True) +# copy all documents from GH templates like contribution guide +for md in glob.glob(os.path.join(_PATH_ROOT, ".github", "*.md")): + shutil.copy(md, os.path.join(_PATH_HERE, FOLDER_GENERATED, os.path.basename(md))) +# DISABLE CHANGELOG for KR-site +# copy also the changelog +# _transform_changelog( + # os.path.join(_PATH_ROOT, "src", "lightning", "fabric", "CHANGELOG.md"), + # os.path.join(_PATH_HERE, FOLDER_GENERATED, "CHANGELOG.md"), +# ) + + +assist_local.AssistantCLI.pull_docs_files( + gh_user_repo="Lightning-AI/lightning-Habana", + target_dir="docs/source-pytorch/integrations/hpu", + checkout="tags/1.0.0", +) + +if not _PL_FAST_DOCS_DEV: + fetch_external_assets( + docs_folder=_PATH_HERE, + assets_folder="_static/fetched-s3-assets", + retrieve_pattern=r"https?://[-a-zA-Z0-9_]+\.s3\.[-a-zA-Z0-9()_\\+.\\/=]+", + ) + + +# -- Project information ----------------------------------------------------- + +project = "PyTorch Lightning & PyTorch Korea User Group" +copyright = lightning.__copyright__ +author = lightning.__author__ + +# The short X.Y version +version = lightning.__version__ +# The full version, including alpha/beta/rc tags +release = lightning.__version__ + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. + +needs_sphinx = "4.5" + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx_toolbox.collapse", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx.ext.imgmath", + "sphinx.ext.autosectionlabel", + # 'sphinxcontrib.mockautodoc', # raises error: directive 'automodule' is already registered ... + # 'sphinxcontrib.fulltoc', # breaks pytorch-theme with unexpected kw argument 'titles_only' + "sphinxcontrib.video", + "myst_parser", + "nbsphinx", + "sphinx_autodoc_typehints", + "sphinx_copybutton", + "sphinx_paramlinks", + "sphinx_togglebutton", + "pt_lightning_sphinx_theme.extensions.lightning", +] + +# Suppress warnings about duplicate labels (needed for PL tutorials) +suppress_warnings = [ + "autosectionlabel.*", +] + +copybutton_prompt_text = ">>> " +copybutton_prompt_text1 = "... " +copybutton_exclude = ".linenos" + +copybutton_only_copy_prompt_lines = True + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# https://berkeley-stat159-f17.github.io/stat159-f17/lectures/14-sphinx..html#conf.py-(cont.) +# https://stackoverflow.com/questions/38526888/embed-ipython-notebook-in-sphinx-document +# I execute the notebooks manually in advance. If notebooks test the code, +# they should be run at build time. +nbsphinx_execute = "never" +nbsphinx_allow_errors = True +nbsphinx_requirejs_path = "" + +# myst-parser, forcing to parse all html pages with mathjax +# https://github.com/executablebooks/MyST-Parser/issues/394 +myst_update_mathjax = False +# https://myst-parser.readthedocs.io/en/latest/syntax/optional.html?highlight=anchor#auto-generated-header-anchors +myst_heading_anchors = 3 + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_parsers = {".rst": "restructuredtext", ".txt": "markdown", ".md": "markdown", ".ipynb": "nbsphinx"} + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = "ko" + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + f"{FOLDER_GENERATED}/PULL_REQUEST_TEMPLATE.md", + "notebooks/sample-template*", +] + +if _PL_FAST_DOCS_DEV: + exclude_patterns.append("notebooks/*") + exclude_patterns.append("tutorials.rst") + + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# http://www.sphinx-doc.org/en/master/usage/theming.html#builtin-themes +# html_theme = 'bizstyle' +# https://sphinx-themes.org +html_theme = "pt_lightning_sphinx_theme" +html_theme_path = [os.environ.get("LIT_SPHINX_PATH", pt_lightning_sphinx_theme.get_html_theme_path())] + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. + +html_theme_options = { + "pytorch_project": "https://lightning.ai", + "canonical_url": lightning.__docs_url__, + "collapse_navigation": False, + "display_version": True, + "logo_only": False, +} + +html_logo = "_static/images/logo.svg" + +html_favicon = "_static/images/icon.svg" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_templates", "_static"] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = project + "-doc" + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', + # Latex figure (float) alignment + "figure_align": "htbp" +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [(master_doc, project + ".tex", project + " Documentation", author, "manual")] + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [(master_doc, project, project + " Documentation", [author], 1)] + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + project, + project + " Documentation", + author, + project, + "One line description of project.", + "Miscellaneous", + ) +] + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ["search.html"] + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "torch": ("https://pytorch.org/docs/stable/", None), + "numpy": ("https://numpy.org/doc/stable/", None), + "PIL": ("https://pillow.readthedocs.io/en/stable/", None), + "torchmetrics": ("https://torchmetrics.readthedocs.io/en/stable/", None), + "graphcore": ("https://docs.graphcore.ai/en/latest/", None), + "habana": ("https://lightning-ai.github.io/lightning-Habana/", None), +} + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +# todo_include_todos = True +todo_include_todos = False + + +def setup(app): + # this is for hiding doctest decoration, + # see: http://z4r.github.io/python/2011/12/02/hides-the-prompts-and-output/ + app.add_js_file("copybutton.js") + app.add_css_file("main.css") + + +# copy all notebooks to local folder +# path_nbs = os.path.join(PATH_HERE, 'notebooks') +# if not os.path.isdir(path_nbs): +# os.mkdir(path_nbs) +# for path_ipynb in glob.glob(os.path.join(PATH_ROOT, 'notebooks', '*.ipynb')): +# path_ipynb2 = os.path.join(path_nbs, os.path.basename(path_ipynb)) +# shutil.copy(path_ipynb, path_ipynb2) + + +# Ignoring Third-party packages +# https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule +def package_list_from_file(file): + """List up package name (not containing version and extras) from a package list file.""" + mocked_packages = [] + with open(file) as fp: + for ln in fp.readlines(): + # Example: `tqdm>=4.41.0` => `tqdm` + # `[` is for package with extras + found = [ln.index(ch) for ch in list(",=<>#[") if ch in ln] + pkg = ln[: min(found)] if found else ln + if pkg.rstrip(): + mocked_packages.append(pkg.rstrip()) + return mocked_packages + + +# define mapping from PyPI names to python imports +PACKAGE_MAPPING = { + "Pillow": "PIL", + "opencv-python": "cv2", + "PyYAML": "yaml", + "hydra-core": "hydra", +} +MOCK_PACKAGES = [] +if SPHINX_MOCK_REQUIREMENTS: + _path_require = lambda fname: os.path.join(_PATH_ROOT, "requirements", "pytorch", fname) + # mock also base packages when we are on RTD since we don't install them there + MOCK_PACKAGES += package_list_from_file(_path_require("base.txt")) + MOCK_PACKAGES += package_list_from_file(_path_require("extra.txt")) + MOCK_PACKAGES += package_list_from_file(_path_require("strategies.txt")) +MOCK_PACKAGES = [PACKAGE_MAPPING.get(pkg, pkg) for pkg in MOCK_PACKAGES] + +autodoc_mock_imports = MOCK_PACKAGES + +autosummary_generate = True + +autodoc_member_order = "groupwise" + +autoclass_content = "both" + +autodoc_default_options = { + "members": True, + "methods": True, + "special-members": "__call__", + "exclude-members": "_abc_impl", + "show-inheritance": True, +} + +# Sphinx will add “permalinks” for each heading and description environment as paragraph signs that +# become visible when the mouse hovers over them. +# This value determines the text for the permalink; it defaults to "¶". Set it to None or the empty +# string to disable permalinks. +# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_add_permalinks +html_permalinks = True +html_permalinks_icon = "¶" + +# True to prefix each section label with the name of the document it is in, followed by a colon. +# For example, index:Introduction for a section called Introduction that appears in document index.rst. +# Useful for avoiding ambiguity when the same section heading appears in different documents. +# http://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html +autosectionlabel_prefix_document = True + +# only run doctests marked with a ".. doctest::" directive +doctest_test_doctest_blocks = "" +doctest_global_setup = """ +import importlib +import os +import sys +from typing import Optional + +import torch +import lightning.pytorch as pl +from torch import nn +from torch.utils.data import IterableDataset, DataLoader, Dataset +from lightning.pytorch import LightningDataModule, LightningModule, Trainer, seed_everything +from lightning.pytorch.callbacks import Callback +from lightning.pytorch.cli import _JSONARGPARSE_SIGNATURES_AVAILABLE as _JSONARGPARSE_AVAILABLE +from lightning.pytorch.utilities import _TORCHVISION_AVAILABLE +from lightning.fabric.loggers.tensorboard import _TENSORBOARD_AVAILABLE, _TENSORBOARDX_AVAILABLE +from lightning.pytorch.loggers.neptune import _NEPTUNE_AVAILABLE +from lightning.pytorch.loggers.comet import _COMET_AVAILABLE +from lightning.pytorch.loggers.mlflow import _MLFLOW_AVAILABLE +from lightning.pytorch.loggers.wandb import _WANDB_AVAILABLE +""" +coverage_skip_undoc_in_source = True + +# skip false positive linkcheck errors from anchors +linkcheck_anchors = False + +# ignore all links in any CHANGELOG file +linkcheck_exclude_documents = [r"^(.*\/)*CHANGELOG.*$"] + +# ignore the following relative links (false positive errors during linkcheck) +linkcheck_ignore = [ + r"^starter/installation.html$", + r"^installation.html$", + r"^../cli/lightning_cli.html$", + r"^../common/trainer.html#trainer-flags$", +] diff --git a/source/debug/debugging.rst b/docs/source-pytorch/debug/debugging.rst similarity index 100% rename from source/debug/debugging.rst rename to docs/source-pytorch/debug/debugging.rst diff --git a/source/debug/debugging_advanced.rst b/docs/source-pytorch/debug/debugging_advanced.rst similarity index 99% rename from source/debug/debugging_advanced.rst rename to docs/source-pytorch/debug/debugging_advanced.rst index 0c1685a..444e947 100644 --- a/source/debug/debugging_advanced.rst +++ b/docs/source-pytorch/debug/debugging_advanced.rst @@ -25,7 +25,6 @@ or use regular print statements. class LitModel(LightningModule): def training_step(self, batch, batch_idx): - debugging_message = ... print(f"RANK - {self.trainer.global_rank}: {debugging_message}") diff --git a/source/debug/debugging_basic.rst b/docs/source-pytorch/debug/debugging_basic.rst similarity index 82% rename from source/debug/debugging_basic.rst rename to docs/source-pytorch/debug/debugging_basic.rst index 147285f..09075c1 100644 --- a/source/debug/debugging_basic.rst +++ b/docs/source-pytorch/debug/debugging_basic.rst @@ -5,13 +5,13 @@ ######################## Debug your model (basic) ######################## -**Audience**: Users who want to learn the basics of debugging models. -.. raw:: html +**Audience**: Users who want to learn the basics of debugging models. - +.. video:: ../_static/fetched-s3-assets/Trainer+flags+7-+debugging_1.mp4 + :poster: ../_static/fetched-s3-assets/thumb_debugging.png + :width: 400 + :muted: ---- @@ -36,7 +36,7 @@ A breakpoint stops your code execution so you can inspect variables, etc... and import pdb pdb.set_trace() - y = x ** 2 + y = x**2 In this example, the code will stop before executing the ``y = x**2`` line. @@ -47,7 +47,7 @@ Run all your model code once quickly ************************************ If you've ever trained a model for days only to crash during validation or testing then this trainer argument is about to become your best friend. -The :paramref:`~pytorch_lightning.trainer.trainer.Trainer.fast_dev_run` argument in the trainer runs 5 batch of training, validation, test and prediction data through your trainer to see if there are any bugs: +The :paramref:`~lightning.pytorch.trainer.trainer.Trainer.fast_dev_run` argument in the trainer runs 5 batch of training, validation, test and prediction data through your trainer to see if there are any bugs: .. code:: python @@ -63,8 +63,8 @@ To change how many batches to use, change the argument to an integer. Here we ru .. note:: This argument will disable tuner, checkpoint callbacks, early stopping callbacks, - loggers and logger callbacks like :class:`~pytorch_lightning.callbacks.lr_monitor.LearningRateMonitor` and - :class:`~pytorch_lightning.callbacks.device_stats_monitor.DeviceStatsMonitor`. + loggers and logger callbacks like :class:`~lightning.pytorch.callbacks.lr_monitor.LearningRateMonitor` and + :class:`~lightning.pytorch.callbacks.device_stats_monitor.DeviceStatsMonitor`. ---- @@ -92,8 +92,8 @@ Run a Sanity Check Lightning runs **2** steps of validation in the beginning of training. This avoids crashing in the validation loop sometime deep into a lengthy training loop. -(See: :paramref:`~pytorch_lightning.trainer.trainer.Trainer.num_sanity_val_steps` -argument of :class:`~pytorch_lightning.trainer.trainer.Trainer`) +(See: :paramref:`~lightning.pytorch.trainer.trainer.Trainer.num_sanity_val_steps` +argument of :class:`~lightning.pytorch.trainer.trainer.Trainer`) .. testcode:: @@ -120,14 +120,24 @@ this generate a table like: 1 | net.0 | Linear | 131 K 2 | net.1 | BatchNorm1d | 1.0 K -To add the child modules to the summary add a :class:`~pytorch_lightning.callbacks.model_summary.ModelSummary`: +To add the child modules to the summary add a :class:`~lightning.pytorch.callbacks.model_summary.ModelSummary`: .. testcode:: - from pytorch_lightning.callbacks import ModelSummary + from lightning.pytorch.callbacks import ModelSummary trainer = Trainer(callbacks=[ModelSummary(max_depth=-1)]) +To print the model summary if ``.fit()`` is not called: + +.. code-block:: python + + from lightning.pytorch.utilities.model_summary import ModelSummary + + model = LitModel() + summary = ModelSummary(model, max_depth=-1) + print(summary) + To turn off the autosummary use: .. code:: python diff --git a/docs/source-pytorch/debug/debugging_intermediate.rst b/docs/source-pytorch/debug/debugging_intermediate.rst new file mode 100644 index 0000000..9895223 --- /dev/null +++ b/docs/source-pytorch/debug/debugging_intermediate.rst @@ -0,0 +1,90 @@ +:orphan: + +.. _debugging_intermediate: + + +############################### +Debug your model (intermediate) +############################### +**Audience**: Users who want to debug their ML code + +---- + +*************************** +Why should I debug ML code? +*************************** +Machine learning code requires debugging mathematical correctness, which is not something non-ML code has to deal with. Lightning implements a few best-practice techniques to give all users, expert level ML debugging abilities. + +---- + +************************************** +Overfit your model on a Subset of Data +************************************** +A good debugging technique is to take a tiny portion of your data (say 2 samples per class), +and try to get your model to overfit. If it can't, it's a sign it won't work with large datasets. + +(See: :paramref:`~lightning.pytorch.trainer.trainer.Trainer.overfit_batches` +argument of :class:`~lightning.pytorch.trainer.trainer.Trainer`) + +.. testcode:: + + # use only 1% of training data (and turn off validation) + trainer = Trainer(overfit_batches=0.01) + + # similar, but with a fixed 10 batches + trainer = Trainer(overfit_batches=10) + +When using this argument, the validation loop will be disabled. We will also replace the sampler +in the training set to turn off shuffle for you. + +---- + +******************************** +Look-out for exploding gradients +******************************** +One major problem that plagues models is exploding gradients. +Gradient clipping is one technique that can help keep gradients from exploding. + +You can keep an eye on the gradient norm by logging it in your LightningModule: + +.. code-block:: python + + from lightning.pytorch.utilities import grad_norm + + + def on_before_optimizer_step(self, optimizer): + # Compute the 2-norm for each layer + # If using mixed precision, the gradients are already unscaled here + norms = grad_norm(self.layer, norm_type=2) + self.log_dict(norms) + + +This will plot the 2-norm of each layer to your experiment manager. +If you notice the norm is going up, there's a good chance your gradients will explode. + +One technique to stop exploding gradients is to clip the gradient when the norm is above a certain threashold: + +.. testcode:: + + # DEFAULT (ie: don't clip) + trainer = Trainer(gradient_clip_val=0) + + # clip gradients' global norm to <=0.5 using gradient_clip_algorithm='norm' by default + trainer = Trainer(gradient_clip_val=0.5) + + # clip gradients' maximum magnitude to <=0.5 + trainer = Trainer(gradient_clip_val=0.5, gradient_clip_algorithm="value") + +---- + +************************* +Detect autograd anomalies +************************* +Lightning helps you detect anomalies in the PyTorh autograd engine via PyTorch's built-in +`Anomaly Detection Context-manager `_. + +Enable it via the **detect_anomaly** trainer argument: + +.. testcode:: + + trainer = Trainer(detect_anomaly=True) diff --git a/source/deploy/production.rst b/docs/source-pytorch/deploy/production.rst similarity index 100% rename from source/deploy/production.rst rename to docs/source-pytorch/deploy/production.rst diff --git a/docs/source-pytorch/deploy/production_advanced.rst b/docs/source-pytorch/deploy/production_advanced.rst new file mode 100644 index 0000000..5e02423 --- /dev/null +++ b/docs/source-pytorch/deploy/production_advanced.rst @@ -0,0 +1,78 @@ +######################################## +Deploy models into production (advanced) +######################################## +**Audience**: Machine learning engineers optimizing models for enterprise-scale production environments. + +---- + +************************** +Compile your model to ONNX +************************** +`ONNX `_ is a package developed by Microsoft to optimize inference. ONNX allows the model to be independent of PyTorch and run on any ONNX Runtime. + +To export your model to ONNX format call the :meth:`~lightning.pytorch.core.module.LightningModule.to_onnx` function on your :class:`~lightning.pytorch.core.module.LightningModule` with the ``filepath`` and ``input_sample``. + +.. code-block:: python + + class SimpleModel(LightningModule): + def __init__(self): + super().__init__() + self.l1 = torch.nn.Linear(in_features=64, out_features=4) + + def forward(self, x): + return torch.relu(self.l1(x.view(x.size(0), -1))) + + + # create the model + model = SimpleModel() + filepath = "model.onnx" + input_sample = torch.randn((1, 64)) + model.to_onnx(filepath, input_sample, export_params=True) + +You can also skip passing the input sample if the ``example_input_array`` property is specified in your :class:`~lightning.pytorch.core.module.LightningModule`. + +.. code-block:: python + + class SimpleModel(LightningModule): + def __init__(self): + super().__init__() + self.l1 = torch.nn.Linear(in_features=64, out_features=4) + self.example_input_array = torch.randn(7, 64) + + def forward(self, x): + return torch.relu(self.l1(x.view(x.size(0), -1))) + + + # create the model + model = SimpleModel() + filepath = "model.onnx" + model.to_onnx(filepath, export_params=True) + +Once you have the exported model, you can run it on your ONNX runtime in the following way: + +.. code-block:: python + + import onnxruntime + + ort_session = onnxruntime.InferenceSession(filepath) + input_name = ort_session.get_inputs()[0].name + ort_inputs = {input_name: np.random.randn(1, 64)} + ort_outs = ort_session.run(None, ort_inputs) + +---- + +**************************** +Validate a Model Is Servable +**************************** + +.. warning:: This is an :ref:`experimental ` feature. + +Production ML Engineers would argue that a model shouldn't be trained if it can't be deployed reliably and in a fully automated manner. + +In order to ease transition from training to production, PyTorch Lightning provides a way for you to validate a model can be served even before starting training. + +In order to do so, your LightningModule needs to subclass the :class:`~lightning.pytorch.serve.servable_module.ServableModule`, implements its hooks and pass a :class:`~lightning.pytorch.serve.servable_module_validator.ServableModuleValidator` callback to the Trainer. + +Below you can find an example of how the serving of a resnet18 can be validated. + +.. literalinclude:: ../../../examples/pytorch/servable_module/production.py diff --git a/source/deploy/production_advanced_2.rst b/docs/source-pytorch/deploy/production_advanced_2.rst similarity index 90% rename from source/deploy/production_advanced_2.rst rename to docs/source-pytorch/deploy/production_advanced_2.rst index e86aee8..09014d2 100644 --- a/source/deploy/production_advanced_2.rst +++ b/docs/source-pytorch/deploy/production_advanced_2.rst @@ -11,7 +11,7 @@ Deploy models into production (advanced) Compile your model to TorchScript ********************************* `TorchScript `_ allows you to serialize your models in a way that it can be loaded in non-Python environments. -The ``LightningModule`` has a handy method :meth:`~pytorch_lightning.core.lightning.LightningModule.to_torchscript` that returns a scripted module which you +The ``LightningModule`` has a handy method :meth:`~lightning.pytorch.core.module.LightningModule.to_torchscript` that returns a scripted module which you can save or directly use. .. testcode:: python @@ -34,7 +34,7 @@ can save or directly use. It is recommended that you install the latest supported version of PyTorch to use this feature without limitations. -Once you have the exported model, you can run it in Pytorch or C++ runtime: +Once you have the exported model, you can run it in PyTorch or C++ runtime: .. code-block:: python diff --git a/docs/source-pytorch/deploy/production_basic.rst b/docs/source-pytorch/deploy/production_basic.rst new file mode 100644 index 0000000..e03a2b5 --- /dev/null +++ b/docs/source-pytorch/deploy/production_basic.rst @@ -0,0 +1,102 @@ +##################################### +Deploy models into production (basic) +##################################### +**Audience**: All users. + +---- + +***************************** +Load a checkpoint and predict +***************************** +The easiest way to use a model for predictions is to load the weights using **load_from_checkpoint** found in the LightningModule. + +.. code-block:: python + + model = LitModel.load_from_checkpoint("best_model.ckpt") + model.eval() + x = torch.randn(1, 64) + + with torch.no_grad(): + y_hat = model(x) + +---- + +************************************** +Predict step with your LightningModule +************************************** +Loading a checkpoint and predicting still leaves you with a lot of boilerplate around the predict epoch. The **predict step** in the LightningModule removes this boilerplate. + +.. code-block:: python + + class MyModel(LightningModule): + def predict_step(self, batch, batch_idx, dataloader_idx=0): + return self(batch) + +And pass in any dataloader to the Lightning Trainer: + +.. code-block:: python + + data_loader = DataLoader(...) + model = MyModel() + trainer = Trainer() + predictions = trainer.predict(model, data_loader) + +---- + +******************************** +Enable complicated predict logic +******************************** +When you need to add complicated pre-processing or post-processing logic to your data use the predict step. For example here we do `Monte Carlo Dropout `_ for predictions: + +.. code-block:: python + + class LitMCdropoutModel(pl.LightningModule): + def __init__(self, model, mc_iteration): + super().__init__() + self.model = model + self.dropout = nn.Dropout() + self.mc_iteration = mc_iteration + + def predict_step(self, batch, batch_idx): + # enable Monte Carlo Dropout + self.dropout.train() + + # take average of `self.mc_iteration` iterations + pred = [self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)] + pred = torch.vstack(pred).mean(dim=0) + return pred + +---- + +**************************** +Enable distributed inference +**************************** +By using the predict step in Lightning you get free distributed inference using :class:`~lightning.pytorch.callbacks.prediction_writer.BasePredictionWriter`. + +.. code-block:: python + + import torch + from lightning.pytorch.callbacks import BasePredictionWriter + + + class CustomWriter(BasePredictionWriter): + def __init__(self, output_dir, write_interval): + super().__init__(write_interval) + self.output_dir = output_dir + + def write_on_epoch_end(self, trainer, pl_module, predictions, batch_indices): + # this will create N (num processes) files in `output_dir` each containing + # the predictions of it's respective rank + torch.save(predictions, os.path.join(self.output_dir, f"predictions_{trainer.global_rank}.pt")) + + # optionally, you can also save `batch_indices` to get the information about the data index + # from your prediction data + torch.save(batch_indices, os.path.join(self.output_dir, f"batch_indices_{trainer.global_rank}.pt")) + + + # or you can set `writer_interval="batch"` and override `write_on_batch_end` to save + # predictions at batch level + pred_writer = CustomWriter(output_dir="pred_path", write_interval="epoch") + trainer = Trainer(accelerator="gpu", strategy="ddp", devices=8, callbacks=[pred_writer]) + model = BoringModel() + trainer.predict(model, return_predictions=False) diff --git a/source/deploy/production_intermediate.rst b/docs/source-pytorch/deploy/production_intermediate.rst similarity index 89% rename from source/deploy/production_intermediate.rst rename to docs/source-pytorch/deploy/production_intermediate.rst index eacb03d..61034b7 100644 --- a/source/deploy/production_intermediate.rst +++ b/docs/source-pytorch/deploy/production_intermediate.rst @@ -14,16 +14,15 @@ If you prefer to use PyTorch directly, feel free to use any Lightning checkpoint import torch - model = torch.load("path/to/lightning/checkpoint.ckpt") - model.eval() -You can also pull out the specific modules you want out of the checkpoint: + class MyModel(nn.Module): + ... -.. code-block:: python - model = torch.load("path/to/lightning/checkpoint.ckpt") - encoder = model["encoder"] - encoder.eval() + model = MyModel() + checkpoint = torch.load("path/to/lightning/checkpoint.ckpt") + model.load_state_dict(checkpoint["state_dict"]) + model.eval() ---- @@ -85,7 +84,7 @@ from your LightningModule ``init`` and ``forward`` method. # if you want to restore any hyperparameters, you can pass them too model = AutoEncoderProd(**hyper_parameters) - state_dict = checkpoint["state_dict"] + model_weights = checkpoint["state_dict"] # update keys by dropping `auto_encoder.` for key in list(model_weights): diff --git a/source/ecosystem/asr_nlp_tts.rst b/docs/source-pytorch/ecosystem/asr_nlp_tts.rst similarity index 99% rename from source/ecosystem/asr_nlp_tts.rst rename to docs/source-pytorch/ecosystem/asr_nlp_tts.rst index b624696..09d5bb0 100644 --- a/source/ecosystem/asr_nlp_tts.rst +++ b/docs/source-pytorch/ecosystem/asr_nlp_tts.rst @@ -48,7 +48,7 @@ so that each can be configured from .yaml or the Hydra CLI. .. note:: Every NeMo model has an example configuration file and a corresponding script that contains all configurations needed for training. -The end result of using NeMo, Pytorch Lightning, and Hydra is that +The end result of using NeMo, PyTorch Lightning, and Hydra is that NeMo models all have the same look and feel. This makes it easy to do Conversational AI research across multiple domains. NeMo models are also fully compatible with the PyTorch ecosystem. @@ -719,6 +719,7 @@ be customized with PyTorch Lightning since every NeMo model is a LightningModule """ ... + # NeMo models come with neural type checking @typecheck( input_types={ diff --git a/source/ecosystem/bolts.rst b/docs/source-pytorch/ecosystem/bolts.rst similarity index 98% rename from source/ecosystem/bolts.rst rename to docs/source-pytorch/ecosystem/bolts.rst index 56c7768..a82184d 100644 --- a/source/ecosystem/bolts.rst +++ b/docs/source-pytorch/ecosystem/bolts.rst @@ -83,7 +83,7 @@ We also have a collection of callbacks. .. code-block:: python from pl_bolts.callbacks import PrintTableMetricsCallback - import pytorch_lightning as pl + import lightning.pytorch as pl trainer = pl.Trainer(callbacks=[PrintTableMetricsCallback()]) diff --git a/source/ecosystem/community_examples.rst b/docs/source-pytorch/ecosystem/community_examples.rst similarity index 93% rename from source/ecosystem/community_examples.rst rename to docs/source-pytorch/ecosystem/community_examples.rst index f535857..bd95f21 100644 --- a/source/ecosystem/community_examples.rst +++ b/docs/source-pytorch/ecosystem/community_examples.rst @@ -4,8 +4,8 @@ Community Examples ================== -- `Lightning Bolts: Deep Learning components for extending PyTorch Lightning `_. -- `Lightning Flash: Your PyTorch AI Factory - Flash enables you to easily configure and run complex AI recipes `_. +- `Lightning Bolts: Deep Learning components for extending PyTorch Lightning `_. +- `Lightning Flash: Your PyTorch AI Factory - Flash enables you to easily configure and run complex AI recipes `_. - `Contextual Emotion Detection (DoubleDistilBert) `_ - `Cotatron: Transcription-Guided Speech Encoder `_ - `FasterRCNN object detection + Hydra `_ @@ -33,4 +33,4 @@ PyTorch Ecosystem Examples ========================== - `PyTorch Geometric: Deep learning on graphs and other irregular structures `_. -- `TorchIO, MONAI and Lightning for 3D medical image segmentation `_. +- `TorchIO, MONAI and Lightning for 3D medical image segmentation `_. diff --git a/source/ecosystem/ecosystem-ci.rst b/docs/source-pytorch/ecosystem/ecosystem-ci.rst similarity index 81% rename from source/ecosystem/ecosystem-ci.rst rename to docs/source-pytorch/ecosystem/ecosystem-ci.rst index 04ff342..5a4d7b0 100644 --- a/source/ecosystem/ecosystem-ci.rst +++ b/docs/source-pytorch/ecosystem/ecosystem-ci.rst @@ -3,7 +3,7 @@ Ecosystem CI ============ -`Ecosystem CI `_ automates issue discovery for your projects against Lightning nightly and releases. +`Ecosystem CI `_ automates issue discovery for your projects against Lightning nightly and releases. It is a lightweight repository that provides easy configuration of Continues Integration running on CPUs and GPUs. Any user who wants to keep their project aligned with current and future Lightning releases can use the EcoSystem CI to configure their integrations. Read more: `Stay Ahead of Breaking Changes with the New Lightning Ecosystem CI `_ @@ -23,8 +23,7 @@ Follow the instructions below to add a new project to the PyTorch Lightning ecos For GPU integrations, you only add the path to the config (OS/Linux and Python version is fixed) to be running with Azure pipelines. 4. Add a Contact to the ``.github/CODEOWNERS`` list for your organization folder or just a single project. 5. Create a Draft PR with all mentioned requirements. -6. Join our `Slack `_ (Optional) channel ``#alerts-ecosystem-ci`` to be notified if your project is breaking. +6. Join our `Discord `_ (Optional) channel ``#alerts-ecosystem-ci`` to be notified if your project is breaking. - -To learn more about Ecosystem CI, please refer to the `Ecosystem CI repo `_. +To learn more about Ecosystem CI, please refer to the `Ecosystem CI repo `_. Also, note that some particular implementation details described above may evolve over time. diff --git a/source/ecosystem/flash.rst b/docs/source-pytorch/ecosystem/flash.rst similarity index 100% rename from source/ecosystem/flash.rst rename to docs/source-pytorch/ecosystem/flash.rst diff --git a/source/ecosystem/metrics.rst b/docs/source-pytorch/ecosystem/metrics.rst similarity index 100% rename from source/ecosystem/metrics.rst rename to docs/source-pytorch/ecosystem/metrics.rst diff --git a/source/expertise_levels.rst b/docs/source-pytorch/expertise_levels.rst similarity index 82% rename from source/expertise_levels.rst rename to docs/source-pytorch/expertise_levels.rst index 9b563f7..3f98c8e 100644 --- a/source/expertise_levels.rst +++ b/docs/source-pytorch/expertise_levels.rst @@ -4,11 +4,6 @@ Level up ======== Learn enough Lightning to match the level of expertise required by your research or job. -.. join_slack:: - :align: left - :margin: 30 - ----- Basic skills ------------ @@ -97,15 +92,7 @@ Learn to scale up your models and enable collaborative model development at acad :tag: intermediate .. displayitem:: - :header: Level 8: Train in the background on the cloud - :description: Learn how to run models on the cloud in the background. - :button_link: levels/intermediate_level_8.html - :col_css: col-md-6 - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 9: Modularize your projects + :header: Level 8: Modularize your projects :description: Create DataModules to enable dataset reusability. :col_css: col-md-6 :button_link: levels/intermediate_level_9.html @@ -113,7 +100,7 @@ Learn to scale up your models and enable collaborative model development at acad :tag: intermediate .. displayitem:: - :header: Level 10: Understand your model + :header: Level 9: Understand your model :description: Use advanced visuals to find the best performing model. :col_css: col-md-6 :button_link: levels/intermediate_level_10.html @@ -121,7 +108,7 @@ Learn to scale up your models and enable collaborative model development at acad :tag: intermediate .. displayitem:: - :header: Level 11: Explore SOTA scaling techniques + :header: Level 10: Explore SOTA scaling techniques :description: Explore SOTA techniques to help convergence, stability and scalability. :col_css: col-md-6 :button_link: levels/intermediate_level_11.html @@ -129,7 +116,7 @@ Learn to scale up your models and enable collaborative model development at acad :tag: intermediate .. displayitem:: - :header: Level 12: Deploy your models + :header: Level 11: Deploy your models :description: Learn how to deploy your models with optimizations like ONNX and torchscript. :col_css: col-md-6 :button_link: levels/intermediate_level_12.html @@ -137,7 +124,7 @@ Learn to scale up your models and enable collaborative model development at acad :tag: intermediate .. displayitem:: - :header: Level 13: Optimize training speed + :header: Level 12: Optimize training speed :description: Use advanced profilers to mixed precision to train bigger models, faster. :col_css: col-md-6 :button_link: levels/intermediate_level_13.html @@ -145,7 +132,7 @@ Learn to scale up your models and enable collaborative model development at acad :tag: intermediate .. displayitem:: - :header: Level 14: Run on on-prem clusters + :header: Level 13: Run on on-prem clusters :description: Run on a custom on-prem cluster or SLURM cluster. :col_css: col-md-6 :button_link: levels/intermediate_level_14.html @@ -171,7 +158,7 @@ Configure all aspects of Lightning for advanced usecases. .. Add callout items below this line .. displayitem:: - :header: Level 15: Customize configs to run in production + :header: Level 14: Customize configs to run in production :description: Enable composable YAMLs :col_css: col-md-6 :button_link: levels/advanced_level_15.html @@ -179,7 +166,7 @@ Configure all aspects of Lightning for advanced usecases. :tag: advanced .. displayitem:: - :header: Level 16: Customize the trainer + :header: Level 15: Customize the trainer :description: Inject custom code into the trainer and modify the progress bar. :col_css: col-md-6 :button_link: levels/advanced_level_16.html @@ -187,7 +174,7 @@ Configure all aspects of Lightning for advanced usecases. :tag: advanced .. displayitem:: - :header: Level 17: Own the training loop + :header: Level 16: Own the training loop :description: Learn all the ways of owning your raw PyTorch loops with Lighting. :col_css: col-md-6 :button_link: levels/advanced_level_17.html @@ -195,7 +182,7 @@ Configure all aspects of Lightning for advanced usecases. :tag: advanced .. displayitem:: - :header: Level 18: Enable advanced checkpointing + :header: Level 17: Enable advanced checkpointing :description: Enable composable or cloud based checkpoints. :col_css: col-md-6 :button_link: levels/advanced_level_18.html @@ -203,7 +190,7 @@ Configure all aspects of Lightning for advanced usecases. :tag: advanced .. displayitem:: - :header: Level 19: Explore IPUs + :header: Level 18: Explore IPUs :description: Explore Intelligence Processing Unit (IPU) for model scaling. :col_css: col-md-6 :button_link: levels/advanced_level_19.html @@ -211,7 +198,7 @@ Configure all aspects of Lightning for advanced usecases. :tag: advanced .. displayitem:: - :header: Level 20: Explore HPUs + :header: Level 19: Explore HPUs :description: Explore Havana Gaudi Processing Unit (HPU) for model scaling. :col_css: col-md-6 :button_link: levels/advanced_level_20.html @@ -219,7 +206,7 @@ Configure all aspects of Lightning for advanced usecases. :tag: advanced .. displayitem:: - :header: Level 21: Master TPUs + :header: Level 20: Master TPUs :description: Master TPUs and run on cloud TPUs. :col_css: col-md-6 :button_link: levels/advanced_level_21.html @@ -227,7 +214,7 @@ Configure all aspects of Lightning for advanced usecases. :tag: advanced .. displayitem:: - :header: Level 22: Reach 1 trillion parameters on GPUs + :header: Level 21: Reach 1 trillion parameters on GPUs :description: Scale to 1 trillion params on GPUs. :col_css: col-md-6 :button_link: levels/advanced_level_22.html @@ -253,7 +240,7 @@ Customize and extend Lightning for things like custom hardware or distributed st .. Add callout items below this line .. displayitem:: - :header: Level 23: Extend the Lightning CLI + :header: Level 22: Extend the Lightning CLI :description: Extend the functionality of the Lightning CLI. :col_css: col-md-6 :button_link: levels/expert_level_23.html @@ -261,7 +248,7 @@ Customize and extend Lightning for things like custom hardware or distributed st :tag: expert .. displayitem:: - :header: Level 24: Integrate a custom cluster + :header: Level 23: Integrate a custom cluster :description: Integrate a custom cluster into Lightning. :col_css: col-md-6 :button_link: levels/expert_level_24.html @@ -269,15 +256,7 @@ Customize and extend Lightning for things like custom hardware or distributed st :tag: expert .. displayitem:: - :header: Level 25: Explore fault-tolerance in-depth - :description: Understand the details of fault-tolerance. - :col_css: col-md-6 - :button_link: clouds/fault_tolerant_training_faq.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 26: Make your own profiler + :header: Level 24: Make your own profiler :description: Make your own profiler. :col_css: col-md-6 :button_link: tuning/profiler_expert.html @@ -285,7 +264,7 @@ Customize and extend Lightning for things like custom hardware or distributed st :tag: expert .. displayitem:: - :header: Level 27: Add a new accelerator or Strategy + :header: Level 25: Add a new accelerator or Strategy :description: Integrate a new accelerator or distributed strategy. :col_css: col-md-6 :button_link: levels/expert_level_27.html diff --git a/source/extensions/accelerator.rst b/docs/source-pytorch/extensions/accelerator.rst similarity index 85% rename from source/extensions/accelerator.rst rename to docs/source-pytorch/extensions/accelerator.rst index dd5a067..fb71d37 100644 --- a/source/extensions/accelerator.rst +++ b/docs/source-pytorch/extensions/accelerator.rst @@ -4,19 +4,20 @@ Accelerator ########### -The Accelerator connects a Lightning Trainer to arbitrary hardware (CPUs, GPUs, TPUs, IPUs, ...). +The Accelerator connects a Lightning Trainer to arbitrary hardware (CPUs, GPUs, TPUs, IPUs, MPS, ...). Currently there are accelerators for: - CPU - :doc:`GPU <../accelerators/gpu>` - :doc:`TPU <../accelerators/tpu>` - :doc:`IPU <../accelerators/ipu>` -- :doc:`HPU <../accelerators/hpu>` +- :doc:`HPU <../integrations/hpu/index>` +- :doc:`MPS <../accelerators/mps>` The Accelerator is part of the Strategy which manages communication across multiple devices (distributed communication). Whenever the Trainer, the loops or any other component in Lightning needs to talk to hardware, it calls into the Strategy and the Strategy calls into the Accelerator. -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/strategies/overview.jpeg +.. image:: ../_static/fetched-s3-assets/overview.jpeg :alt: Illustration of the Strategy as a composition of the Accelerator and several plugins We expose Accelerators and Strategies mainly for expert users who want to extend Lightning to work with new @@ -28,6 +29,8 @@ hardware and distributed training or clusters. Create a Custom Accelerator --------------------------- +.. warning:: This is an :ref:`experimental ` feature. + Here is how you create a new Accelerator. Let's pretend we want to integrate the fictional XPU accelerator and we have access to its hardware through a library ``xpulib``. @@ -38,7 +41,7 @@ Let's pretend we want to integrate the fictional XPU accelerator and we have acc class XPUAccelerator(Accelerator): - """Experimental support for XPU, optimized for large-scale machine learning.""" + """Support for a hypothetical XPU, optimized for large-scale machine learning.""" @staticmethod def parse_devices(devices: Any) -> Any: @@ -69,7 +72,7 @@ Finally, add the XPUAccelerator to the Trainer: .. code-block:: python - from pytorch_lightning import Trainer + from lightning.pytorch import Trainer accelerator = XPUAccelerator() trainer = Trainer(accelerator=accelerator, devices=2) @@ -83,7 +86,7 @@ Finally, add the XPUAccelerator to the Trainer: Registering Accelerators ------------------------ -If you wish to switch to a custom accelerator from the CLI without code changes, you can implement the :meth:`~pytorch_lightning.accelerators.accelerator.Accelerator.register_accelerators` class method to register your new accelerator under a shorthand name like so: +If you wish to switch to a custom accelerator from the CLI without code changes, you can implement the :meth:`~lightning.pytorch.accelerators.accelerator.Accelerator.register_accelerators` class method to register your new accelerator under a shorthand name like so: .. code-block:: python @@ -116,7 +119,7 @@ Or if you are using the Lightning CLI, for example: Accelerator API --------------- -.. currentmodule:: pytorch_lightning.accelerators +.. currentmodule:: lightning.pytorch.accelerators .. autosummary:: :nosignatures: @@ -124,7 +127,6 @@ Accelerator API Accelerator CPUAccelerator - GPUAccelerator - HPUAccelerator - IPUAccelerator - TPUAccelerator + CUDAAccelerator + MPSAccelerator + XLAAccelerator diff --git a/docs/source-pytorch/extensions/callbacks.rst b/docs/source-pytorch/extensions/callbacks.rst new file mode 100644 index 0000000..0e43c23 --- /dev/null +++ b/docs/source-pytorch/extensions/callbacks.rst @@ -0,0 +1,368 @@ +.. role:: hidden + :class: hidden-section + +.. _callbacks: + +######## +Callback +######## + +Callbacks allow you to add arbitrary self-contained programs to your training. +At specific points during the flow of execution (hooks), the Callback interface allows you to design programs that encapsulate a full set of functionality. +It de-couples functionality that does not need to be in the :doc:`lightning module <../common/lightning_module>` and can be shared across projects. + +Lightning has a callback system to execute them when needed. Callbacks should capture NON-ESSENTIAL +logic that is NOT required for your :doc:`lightning module <../common/lightning_module>` to run. + +A complete list of Callback hooks can be found in :class:`~lightning.pytorch.callbacks.callback.Callback`. + +An overall Lightning system should have: + +1. Trainer for all engineering +2. LightningModule for all research code. +3. Callbacks for non-essential code. + +| + +Example: + +.. testcode:: + + from lightning.pytorch.callbacks import Callback + + + class MyPrintingCallback(Callback): + def on_train_start(self, trainer, pl_module): + print("Training is starting") + + def on_train_end(self, trainer, pl_module): + print("Training is ending") + + + trainer = Trainer(callbacks=[MyPrintingCallback()]) + +We successfully extended functionality without polluting our super clean +:doc:`lightning module <../common/lightning_module>` research code. + +You can do pretty much anything with callbacks. + +-------------- + +****************** +Built-in Callbacks +****************** +Lightning has a few built-in callbacks. + +.. note:: + For a richer collection of callbacks, check out our + `bolts library `_. + +.. currentmodule:: lightning.pytorch.callbacks + +.. autosummary:: + :nosignatures: + :template: classtemplate.rst + + BackboneFinetuning + BaseFinetuning + BasePredictionWriter + BatchSizeFinder + Callback + DeviceStatsMonitor + EarlyStopping + GradientAccumulationScheduler + LambdaCallback + LearningRateFinder + LearningRateMonitor + ModelCheckpoint + ModelPruning + ModelSummary + ProgressBar + RichModelSummary + RichProgressBar + StochasticWeightAveraging + Timer + TQDMProgressBar + +---------- + +.. include:: callbacks_state.rst + +---------- + + +************** +Best Practices +************** +The following are best practices when using/designing callbacks. + +1. Callbacks should be isolated in their functionality. +2. Your callback should not rely on the behavior of other callbacks in order to work properly. +3. Do not manually call methods from the callback. +4. Directly calling methods (eg. `on_validation_end`) is strongly discouraged. +5. Whenever possible, your callbacks should not depend on the order in which they are executed. + + +----------- + +.. include:: entry_points.rst + +----------- + +.. _callback_hooks: + +************ +Callback API +************ +Here is the full API of methods available in the Callback base class. + +The :class:`~lightning.pytorch.callbacks.Callback` class is the base for all the callbacks in Lightning just like the :class:`~lightning.pytorch.core.module.LightningModule` is the base for all models. +It defines a public interface that each callback implementation must follow, the key ones are: + +Properties +========== + +state_key +^^^^^^^^^ + +.. autoattribute:: lightning.pytorch.callbacks.Callback.state_key + :noindex: + + +Hooks +===== + +setup +^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.setup + :noindex: + +teardown +^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.teardown + :noindex: + +on_fit_start +^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_fit_start + :noindex: + +on_fit_end +^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_fit_end + :noindex: + +on_sanity_check_start +^^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_sanity_check_start + :noindex: + +on_sanity_check_end +^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_sanity_check_end + :noindex: + +on_train_batch_start +^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_train_batch_start + :noindex: + +on_train_batch_end +^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_train_batch_end + :noindex: + +on_train_epoch_start +^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_train_epoch_start + :noindex: + +on_train_epoch_end +^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_train_epoch_end + :noindex: + +on_validation_epoch_start +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_validation_epoch_start + :noindex: + +on_validation_epoch_end +^^^^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_validation_epoch_end + :noindex: + +on_test_epoch_start +^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_test_epoch_start + :noindex: + +on_test_epoch_end +^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_test_epoch_end + :noindex: + +on_predict_epoch_start +^^^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_predict_epoch_start + :noindex: + +on_predict_epoch_end +^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_predict_epoch_end + :noindex: + +on_validation_batch_start +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_validation_batch_start + :noindex: + +on_validation_batch_end +^^^^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_validation_batch_end + :noindex: + +on_test_batch_start +^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_test_batch_start + :noindex: + +on_test_batch_end +^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_test_batch_end + :noindex: + +on_predict_batch_start +^^^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_predict_batch_start + :noindex: + +on_predict_batch_end +^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_predict_batch_end + :noindex: + +on_train_start +^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_train_start + :noindex: + +on_train_end +^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_train_end + :noindex: + +on_validation_start +^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_validation_start + :noindex: + +on_validation_end +^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_validation_end + :noindex: + +on_test_start +^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_test_start + :noindex: + +on_test_end +^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_test_end + :noindex: + +on_predict_start +^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_predict_start + :noindex: + +on_predict_end +^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_predict_end + :noindex: + + +on_exception +^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_exception + :noindex: + +state_dict +^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.state_dict + :noindex: + +on_save_checkpoint +^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_save_checkpoint + :noindex: + +load_state_dict +^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.load_state_dict + :noindex: + +on_load_checkpoint +^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_load_checkpoint + :noindex: + +on_before_backward +^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_before_backward + :noindex: + +on_after_backward +^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_after_backward + :noindex: + +on_before_optimizer_step +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_before_optimizer_step + :noindex: + +on_before_zero_grad +^^^^^^^^^^^^^^^^^^^ + +.. automethod:: lightning.pytorch.callbacks.Callback.on_before_zero_grad + :noindex: diff --git a/source/extensions/callbacks_state.rst b/docs/source-pytorch/extensions/callbacks_state.rst similarity index 84% rename from source/extensions/callbacks_state.rst rename to docs/source-pytorch/extensions/callbacks_state.rst index 0a104ca..c83f373 100644 --- a/source/extensions/callbacks_state.rst +++ b/docs/source-pytorch/extensions/callbacks_state.rst @@ -4,12 +4,12 @@ Save Callback state Some callbacks require internal state in order to function properly. You can optionally choose to persist your callback's state as part of model checkpoint files using -:meth:`~pytorch_lightning.callbacks.Callback.state_dict` and :meth:`~pytorch_lightning.callbacks.Callback.load_state_dict`. +:meth:`~lightning.pytorch.callbacks.Callback.state_dict` and :meth:`~lightning.pytorch.callbacks.Callback.load_state_dict`. Note that the returned state must be able to be pickled. When your callback is meant to be used only as a singleton callback then implementing the above two hooks is enough to persist state effectively. However, if passing multiple instances of the callback to the Trainer is supported, then -the callback must define a :attr:`~pytorch_lightning.callbacks.Callback.state_key` property in order for Lightning +the callback must define a :attr:`~lightning.pytorch.callbacks.Callback.state_key` property in order for Lightning to be able to distinguish the different states when loading the callback state. This concept is best illustrated by the following example. @@ -22,9 +22,9 @@ the following example. self.state = {"epochs": 0, "batches": 0} @property - def state_key(self): + def state_key(self) -> str: # note: we do not include `verbose` here on purpose - return self._generate_state_key(what=self.what) + return f"Counter[what={self.what}]" def on_train_epoch_end(self, *args, **kwargs): if self.what == "epochs": @@ -57,6 +57,6 @@ A Lightning checkpoint from this Trainer with the two stateful callbacks will in } } -The implementation of a :attr:`~pytorch_lightning.callbacks.Callback.state_key` is essential here. If it were missing, -Lightning would not be able to disambiguate the state for these two callbacks, and :attr:`~pytorch_lightning.callbacks.Callback.state_key` +The implementation of a :attr:`~lightning.pytorch.callbacks.Callback.state_key` is essential here. If it were missing, +Lightning would not be able to disambiguate the state for these two callbacks, and :attr:`~lightning.pytorch.callbacks.Callback.state_key` by default only defines the class name as the key, e.g., here ``Counter``. diff --git a/source/extensions/datamodules_state.rst b/docs/source-pytorch/extensions/datamodules_state.rst similarity index 100% rename from source/extensions/datamodules_state.rst rename to docs/source-pytorch/extensions/datamodules_state.rst diff --git a/docs/source-pytorch/extensions/entry_points.rst b/docs/source-pytorch/extensions/entry_points.rst new file mode 100644 index 0000000..bf1279a --- /dev/null +++ b/docs/source-pytorch/extensions/entry_points.rst @@ -0,0 +1,45 @@ +************ +Entry Points +************ + +Lightning supports registering Trainer callbacks directly through +`Entry Points `_. Entry points allow an arbitrary +package to include callbacks that the Lightning Trainer can automatically use, without you having to add them +to the Trainer manually. This is useful in production environments where it is common to provide specialized monitoring +and logging callbacks globally for every application. + +Here is a callback factory function that returns two special callbacks: + +.. code-block:: python + :caption: factories.py + + def my_custom_callbacks_factory(): + return [MyCallback1(), MyCallback2()] + +If we make this `factories.py` file into an installable package, we can define an **entry point** for this factory function. +Here is a minimal example of the `setup.py` file for the package `my-package`: + +.. code-block:: python + :caption: setup.py + + from setuptools import setup + + setup( + name="my-package", + version="0.0.1", + install_requires=["lightning"], + entry_points={ + "lightning.pytorch.callbacks_factory": [ + # The format here must be [any name]=[module path]:[function name] + "monitor_callbacks=factories:my_custom_callbacks_factory" + ] + }, + ) + +The group name for the entry points is ``lightning.pytorch.callbacks_factory`` and it contains a list of strings that +specify where to find the function within the package. + +Now, if you `pip install -e .` this package, it will register the ``my_custom_callbacks_factory`` function and Lightning +will automatically call it to collect the callbacks whenever you run the Trainer! + +To unregister the factory, simply uninstall the package with `pip uninstall "my-package"`. diff --git a/docs/source-pytorch/extensions/logging.rst b/docs/source-pytorch/extensions/logging.rst new file mode 100644 index 0000000..a96e332 --- /dev/null +++ b/docs/source-pytorch/extensions/logging.rst @@ -0,0 +1,408 @@ +:orphan: + +.. testsetup:: * + + from lightning.pytorch import loggers as pl_loggers + +.. role:: hidden + :class: hidden-section + +.. _logging: + + +####### +Logging +####### + +***************** +Supported Loggers +***************** + +The following are loggers we support: + +.. currentmodule:: lightning.pytorch.loggers + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: classtemplate.rst + + CometLogger + CSVLogger + MLFlowLogger + NeptuneLogger + TensorBoardLogger + WandbLogger + + +The above loggers will normally plot an additional chart (**global_step VS epoch**). Depending on the loggers you use, there might be some additional charts too. + +By default, Lightning uses ``TensorBoard`` logger under the hood, and stores the logs to a directory (by default in ``lightning_logs/``). + +.. testcode:: + + from lightning.pytorch import Trainer + + # Automatically logs to a directory (by default ``lightning_logs/``) + trainer = Trainer() + +To see your logs: + +.. code-block:: bash + + tensorboard --logdir=lightning_logs/ + +To visualize tensorboard in a jupyter notebook environment, run the following command in a jupyter cell: + +.. code-block:: bash + + %reload_ext tensorboard + %tensorboard --logdir=lightning_logs/ + +You can also pass a custom Logger to the :class:`~lightning.pytorch.trainer.trainer.Trainer`. + +.. testcode:: + :skipif: not _TENSORBOARD_AVAILABLE and not _TENSORBOARDX_AVAILABLE + + from lightning.pytorch import loggers as pl_loggers + + tb_logger = pl_loggers.TensorBoardLogger(save_dir="logs/") + trainer = Trainer(logger=tb_logger) + +Choose from any of the others such as MLflow, Comet, Neptune, WandB, etc. + +.. testcode:: + :skipif: not _COMET_AVAILABLE + + comet_logger = pl_loggers.CometLogger(save_dir="logs/") + trainer = Trainer(logger=comet_logger) + +To use multiple loggers, simply pass in a ``list`` or ``tuple`` of loggers. + +.. testcode:: + :skipif: (not _TENSORBOARD_AVAILABLE and not _TENSORBOARDX_AVAILABLE) or not _COMET_AVAILABLE + + tb_logger = pl_loggers.TensorBoardLogger(save_dir="logs/") + comet_logger = pl_loggers.CometLogger(save_dir="logs/") + trainer = Trainer(logger=[tb_logger, comet_logger]) + +.. note:: + + By default, Lightning logs every 50 steps. Use Trainer flags to :ref:`logging_frequency`. + +.. note:: + + By default, all loggers log to ``os.getcwd()``. You can change the logging path using + ``Trainer(default_root_dir="/your/path/to/save/checkpoints")`` without instantiating a logger. + +---------- + +****************************** +Logging from a LightningModule +****************************** + +Lightning offers automatic log functionalities for logging scalars, or manual logging for anything else. + +Automatic Logging +================= + +Use the :meth:`~lightning.pytorch.core.module.LightningModule.log` or :meth:`~lightning.pytorch.core.module.LightningModule.log_dict` +methods to log from anywhere in a :doc:`LightningModule <../common/lightning_module>` and :doc:`callbacks <../extensions/callbacks>`. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + self.log("my_metric", x) + + + # or a dict to log all metrics at once with individual plots + def training_step(self, batch, batch_idx): + self.log_dict({"acc": acc, "recall": recall}) + +.. note:: + Everything explained below applies to both :meth:`~lightning.pytorch.core.module.LightningModule.log` or :meth:`~lightning.pytorch.core.module.LightningModule.log_dict` methods. + +Depending on where the :meth:`~lightning.pytorch.core.module.LightningModule.log` method is called, Lightning auto-determines +the correct logging mode for you. Of course you can override the default behavior by manually setting the +:meth:`~lightning.pytorch.core.module.LightningModule.log` parameters. + +.. code-block:: python + + def training_step(self, batch, batch_idx): + self.log("my_loss", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True) + +The :meth:`~lightning.pytorch.core.module.LightningModule.log` method has a few options: + +* ``on_step``: Logs the metric at the current step. +* ``on_epoch``: Automatically accumulates and logs at the end of the epoch. +* ``prog_bar``: Logs to the progress bar (Default: ``False``). +* ``logger``: Logs to the logger like ``Tensorboard``, or any other custom logger passed to the :class:`~lightning.pytorch.trainer.trainer.Trainer` (Default: ``True``). +* ``reduce_fx``: Reduction function over step values for end of epoch. Uses :meth:`torch.mean` by default and is not applied when a :class:`torchmetrics.Metric` is logged. +* ``enable_graph``: If True, will not auto detach the graph. +* ``sync_dist``: If True, reduces the metric across devices. Use with care as this may lead to a significant communication overhead. +* ``sync_dist_group``: The DDP group to sync across. +* ``add_dataloader_idx``: If True, appends the index of the current dataloader to the name (when using multiple dataloaders). If False, user needs to give unique names for each dataloader to not mix the values. +* ``batch_size``: Current batch size used for accumulating logs logged with ``on_epoch=True``. This will be directly inferred from the loaded batch, but for some data structures you might need to explicitly provide it. +* ``rank_zero_only``: Whether the value will be logged only on rank 0. This will prevent synchronization which would produce a deadlock as not all processes would perform this log call. + +.. list-table:: Default behavior of logging in Callback or LightningModule + :widths: 50 25 25 + :header-rows: 1 + + * - Hook + - on_step + - on_epoch + * - on_train_start, on_train_epoch_start, on_train_epoch_end + - False + - True + * - on_before_backward, on_after_backward, on_before_optimizer_step, on_before_zero_grad + - True + - False + * - on_train_batch_start, on_train_batch_end, training_step + - True + - False + * - on_validation_start, on_validation_epoch_start, on_validation_epoch_end + - False + - True + * - on_validation_batch_start, on_validation_batch_end, validation_step + - False + - True + + +.. note:: + + While logging tensor metrics with ``on_epoch=True`` inside step-level hooks and using mean-reduction (default) to accumulate the metrics across the current epoch, Lightning tries to extract the + batch size from the current batch. If multiple possible batch sizes are found, a warning is logged and if it fails to extract the batch size from the current batch, which is possible if + the batch is a custom structure/collection, then an error is raised. To avoid this, you can specify the ``batch_size`` inside the ``self.log(... batch_size=batch_size)`` call. + + .. code-block:: python + + def training_step(self, batch, batch_idx): + # extracts the batch size from `batch` + self.log("train_loss", loss, on_epoch=True) + + + def validation_step(self, batch, batch_idx): + # uses `batch_size=10` + self.log("val_loss", loss, batch_size=10) + +.. note:: + + - The above config for ``validation`` applies for ``test`` hooks as well. + + - Setting ``on_epoch=True`` will cache all your logged values during the full training epoch and perform a + reduction in ``on_train_epoch_end``. We recommend using `TorchMetrics `_, when working with custom reduction. + + - Setting both ``on_step=True`` and ``on_epoch=True`` will create two keys per metric you log with + suffix ``_step`` and ``_epoch`` respectively. You can refer to these keys e.g. in the `monitor` + argument of :class:`~lightning.pytorch.callbacks.model_checkpoint.ModelCheckpoint` or in the graphs plotted to the logger of your choice. + + +If your work requires to log in an unsupported method, please open an issue with a clear description of why it is blocking you. + + +Manual Logging Non-Scalar Artifacts +=================================== + +If you want to log anything that is not a scalar, like histograms, text, images, etc., you may need to use the logger object directly. + +.. code-block:: python + + def training_step(self): + ... + # the logger you used (in this case tensorboard) + tensorboard = self.logger.experiment + tensorboard.add_image() + tensorboard.add_histogram(...) + tensorboard.add_figure(...) + + +---------- + +******************** +Make a Custom Logger +******************** + +You can implement your own logger by writing a class that inherits from :class:`~lightning.pytorch.loggers.logger.Logger`. +Use the :func:`~lightning.pytorch.loggers.logger.rank_zero_experiment` and :func:`~lightning.pytorch.utilities.rank_zero.rank_zero_only` decorators to make sure that only the first process in DDP training creates the experiment and logs the data respectively. + +.. testcode:: + + from lightning.pytorch.loggers.logger import Logger, rank_zero_experiment + from lightning.pytorch.utilities import rank_zero_only + + + class MyLogger(Logger): + @property + def name(self): + return "MyLogger" + + @property + def version(self): + # Return the experiment version, int or str. + return "0.1" + + @rank_zero_only + def log_hyperparams(self, params): + # params is an argparse.Namespace + # your code to record hyperparameters goes here + pass + + @rank_zero_only + def log_metrics(self, metrics, step): + # metrics is a dictionary of metric names and values + # your code to record metrics goes here + pass + + @rank_zero_only + def save(self): + # Optional. Any code necessary to save logger data goes here + pass + + @rank_zero_only + def finalize(self, status): + # Optional. Any code that needs to be run after training + # finishes goes here + pass + +If you write a logger that may be useful to others, please send +a pull request to add it to Lightning! + +---------- + +.. _logging_frequency: + + +************************* +Control Logging Frequency +************************* + +Logging frequency +================= + +It may slow down training to log on every single batch. By default, Lightning logs every 50 rows, or 50 training steps. +To change this behaviour, set the ``log_every_n_steps`` :class:`~lightning.pytorch.trainer.trainer.Trainer` flag. + +.. testcode:: + + k = 10 + trainer = Trainer(log_every_n_steps=k) + + +Log Writing Frequency +===================== + +Individual logger implementations determine their flushing frequency. For example, on the +:class:`~lightning.pytorch.loggers.csv_logs.CSVLogger` you can set the flag ``flush_logs_every_n_steps``. + +---------- + +************ +Progress Bar +************ + +You can add any metric to the progress bar using :meth:`~lightning.pytorch.core.module.LightningModule.log` +method, setting ``prog_bar=True``. + + +.. code-block:: python + + def training_step(self, batch, batch_idx): + self.log("my_loss", loss, prog_bar=True) + + +You could learn more about progress bars supported by Lightning :doc:`here <../common/progress_bar>`. + +Modifying the Progress Bar +========================== + +The progress bar by default already includes the training loss and version number of the experiment +if you are using a logger. These defaults can be customized by overriding the +:meth:`~lightning.pytorch.callbacks.progress.progress_bar.ProgressBar.get_metrics` hook in your logger. + +.. code-block:: python + + from lightning.pytorch.callbacks.progress import TQDMProgressBar + + + class CustomProgressBar(TQDMProgressBar): + def get_metrics(self, *args, **kwargs): + # don't show the version number + items = super().get_metrics() + items.pop("v_num", None) + return items + + +---------- + + +************************* +Configure Console Logging +************************* + +Lightning logs useful information about the training process and user warnings to the console. +You can retrieve the Lightning console logger and change it to your liking. For example, adjust the logging level +or redirect output for certain modules to log files: + +.. testcode:: + + import logging + + # configure logging at the root level of Lightning + logging.getLogger("lightning.pytorch").setLevel(logging.ERROR) + + # configure logging on module level, redirect to file + logger = logging.getLogger("lightning.pytorch.core") + logger.addHandler(logging.FileHandler("core.log")) + +Read more about custom Python logging `here `_. + + +---------- + +*********************** +Logging Hyperparameters +*********************** + +When training a model, it is useful to know what hyperparams went into that model. +When Lightning creates a checkpoint, it stores a key ``"hyper_parameters"`` with the hyperparams. + +.. code-block:: python + + lightning_checkpoint = torch.load(filepath, map_location=lambda storage, loc: storage) + hyperparams = lightning_checkpoint["hyper_parameters"] + +Some loggers also allow logging the hyperparams used in the experiment. For instance, +when using the ``TensorBoardLogger``, all hyperparams will show +in the hparams tab at :meth:`torch.utils.tensorboard.writer.SummaryWriter.add_hparams`. + +.. note:: + If you want to track a metric in the tensorboard hparams tab, log scalars to the key ``hp_metric``. If tracking multiple metrics, initialize ``TensorBoardLogger`` with ``default_hp_metric=False`` and call ``log_hyperparams`` only once with your metric keys and initial values. Subsequent updates can simply be logged to the metric keys. Refer to the examples below for setting up proper hyperparams metrics tracking within the :doc:`LightningModule <../common/lightning_module>`. + + .. code-block:: python + + # Using default_hp_metric + def validation_step(self, batch, batch_idx): + self.log("hp_metric", some_scalar) + + + # Using custom or multiple metrics (default_hp_metric=False) + def on_train_start(self): + self.logger.log_hyperparams(self.hparams, {"hp/metric_1": 0, "hp/metric_2": 0}) + + + def validation_step(self, batch, batch_idx): + self.log("hp/metric_1", some_scalar_1) + self.log("hp/metric_2", some_scalar_2) + + In the example, using ``"hp/"`` as a prefix allows for the metrics to be grouped under "hp" in the tensorboard scalar tab where you can collapse them. + +----------- + +*************************** +Managing Remote Filesystems +*************************** + +Lightning supports saving logs to a variety of filesystems, including local filesystems and several cloud storage providers. + +Check out the :doc:`Remote Filesystems <../common/remote_fs>` doc for more info. diff --git a/source/extensions/plugins.rst b/docs/source-pytorch/extensions/plugins.rst similarity index 82% rename from source/extensions/plugins.rst rename to docs/source-pytorch/extensions/plugins.rst index 392a072..83d9f66 100644 --- a/source/extensions/plugins.rst +++ b/docs/source-pytorch/extensions/plugins.rst @@ -46,24 +46,19 @@ We provide precision plugins for you to benefit from numerical representations w The full list of built-in precision plugins is listed below. -.. currentmodule:: pytorch_lightning.plugins.precision +.. currentmodule:: lightning.pytorch.plugins.precision .. autosummary:: :nosignatures: :template: classtemplate.rst - ApexMixedPrecisionPlugin DeepSpeedPrecisionPlugin DoublePrecisionPlugin - FullyShardedNativeMixedPrecisionPlugin - HPUPrecisionPlugin - IPUPrecisionPlugin + FSDPMixedPrecisionPlugin MixedPrecisionPlugin - NativeMixedPrecisionPlugin PrecisionPlugin - ShardedNativeMixedPrecisionPlugin - TPUBf16PrecisionPlugin - TPUPrecisionPlugin + XLABf16PrecisionPlugin + XLAPrecisionPlugin More information regarding precision with Lightning can be found :ref:`here ` @@ -76,19 +71,19 @@ More information regarding precision with Lightning can be found :ref:`here
`. It can be controlled by passing different
+strategy with aliases (``"ddp"``, ``"ddp_spawn"``, ``"deepspeed"`` and so on) as well as a custom strategy to the ``strategy`` parameter for Trainer.
+
+The Strategy in PyTorch Lightning handles the following responsibilities:
+
+* Launch and teardown of training processes (if applicable).
+* Setup communication between processes (NCCL, GLOO, MPI, and so on).
+* Provide a unified communication interface for reduction, broadcast, and so on.
+* Owns the :class:`~lightning.pytorch.core.module.LightningModule`
+* Handles/owns optimizers and schedulers.
+
+
+Strategy is a composition of one :doc:`Accelerator <../extensions/accelerator>`, one :ref:`Precision Plugin `, a :ref:`CheckpointIO `
+plugin and other optional plugins such as the :ref:`ClusterEnvironment `.
+
+.. image:: ../_static/fetched-s3-assets/overview.jpeg
+    :alt: Illustration of the Strategy as a composition of the Accelerator and several plugins
+
+We expose Strategies mainly for expert users that want to extend Lightning for new hardware support or new distributed backends (e.g. a backend not yet supported by `PyTorch `_ itself).
+
+
+----
+
+*****************************
+Selecting a Built-in Strategy
+*****************************
+
+Built-in strategies can be selected in two ways.
+
+1. Pass the shorthand name to the ``strategy`` Trainer argument
+2. Import a Strategy from :mod:`lightning.pytorch.strategies`, instantiate it and pass it to the ``strategy`` Trainer argument
+
+The latter allows you to configure further options on the specifc strategy.
+Here are some examples:
+
+.. code-block:: python
+
+    # Training with the DistributedDataParallel strategy on 4 GPUs
+    trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4)
+
+    # Training with the DistributedDataParallel strategy on 4 GPUs, with options configured
+    trainer = Trainer(strategy=DDPStrategy(static_graph=True), accelerator="gpu", devices=4)
+
+    # Training with the DDP Spawn strategy using auto accelerator selection
+    trainer = Trainer(strategy="ddp_spawn", accelerator="auto", devices=4)
+
+    # Training with the DeepSpeed strategy on available GPUs
+    trainer = Trainer(strategy="deepspeed", accelerator="gpu", devices="auto")
+
+    # Training with the DDP strategy using 3 CPU processes
+    trainer = Trainer(strategy="ddp", accelerator="cpu", devices=3)
+
+    # Training with the DDP Spawn strategy on 8 TPU cores
+    trainer = Trainer(strategy="ddp_spawn", accelerator="tpu", devices=8)
+
+    # Training with the default IPU strategy on 8 IPUs
+    trainer = Trainer(accelerator="ipu", devices=8)
+
+The below table lists all relevant strategies available in Lightning with their corresponding short-hand name:
+
+.. list-table:: Strategy Classes and Nicknames
+   :widths: 20 20 20
+   :header-rows: 1
+
+   * - Name
+     - Class
+     - Description
+   * - fsdp
+     - :class:`~lightning.pytorch.strategies.FSDPStrategy`
+     - Strategy for Fully Sharded Data Parallel training. :ref:`Learn more. `
+   * - ddp
+     - :class:`~lightning.pytorch.strategies.DDPStrategy`
+     - Strategy for multi-process single-device training on one or multiple nodes. :ref:`Learn more. `
+   * - ddp_spawn
+     - :class:`~lightning.pytorch.strategies.DDPStrategy`
+     - Same as "ddp" but launches processes using :func:`torch.multiprocessing.spawn` method and joins processes after training finishes. :ref:`Learn more. `
+   * - deepspeed
+     - :class:`~lightning.pytorch.strategies.DeepSpeedStrategy`
+     - Provides capabilities to run training using the DeepSpeed library, with training optimizations for large billion parameter models. :ref:`Learn more. `
+   * - hpu_parallel
+     - ``HPUParallelStrategy``
+     - Strategy for distributed training on multiple HPU devices. :doc:`Learn more. <../integrations/hpu/index>`
+   * - hpu_single
+     - ``SingleHPUStrategy``
+     - Strategy for training on a single HPU device. :doc:`Learn more. <../integrations/hpu/index>`
+   * - ipu_strategy
+     - ``IPUStrategy``
+     - Plugin for training on IPU devices. :doc:`Learn more. <../accelerators/ipu>`
+   * - xla
+     - :class:`~lightning.pytorch.strategies.XLAStrategy`
+     - Strategy for training on multiple TPU devices using the :func:`torch_xla.distributed.xla_multiprocessing.spawn` method. :doc:`Learn more. <../accelerators/tpu>`
+   * - single_xla
+     - :class:`~lightning.pytorch.strategies.SingleXLAStrategy`
+     - Strategy for training on a single XLA device, like TPUs. :doc:`Learn more. <../accelerators/tpu>`
+
+----
+
+
+**********************
+Third-party Strategies
+**********************
+
+There are powerful third-party strategies that integrate well with Lightning but aren't maintained as part of the ``lightning`` package.
+
+.. list-table:: List of third-party strategy implementations
+   :widths: 20 20 20
+   :header-rows: 1
+
+   * - Name
+     - Package
+     - Description
+   * - ColossalAI
+     - `Lightning-AI/lightning-colossalai `_
+     - Colossal-AI provides a collection of parallel components for you. It aims to support you to write your distributed deep learning models just like how you write your model on your laptop. `Learn more. `__
+   * - Bagua
+     - `Lightning-AI/lightning-Bagua `_
+     - Bagua is a deep learning training acceleration framework for PyTorch, with advanced distributed training algorithms and system optimizations. `Learn more. `__
+   * - hivemind
+     - `Lightning-AI/lightning-hivemind `_
+     - Hivemind is a PyTorch library for decentralized deep learning across the Internet. Its intended usage is training one large model on hundreds of computers from different universities, companies, and volunteers. `Learn more. `__
+
+
+----
+
+
+************************
+Create a Custom Strategy
+************************
+
+Every strategy in Lightning is a subclass of one of the main base classes: :class:`~lightning.pytorch.strategies.Strategy`, :class:`~lightning.pytorch.strategies.SingleDeviceStrategy` or :class:`~lightning.pytorch.strategies.ParallelStrategy`.
+
+.. image:: ../_static/fetched-s3-assets/hierarchy.jpeg
+    :alt: Strategy base classes
+
+As an expert user, you may choose to extend either an existing built-in Strategy or create a completely new one by
+subclassing the base classes.
+
+.. code-block:: python
+
+    from lightning.pytorch.strategies import DDPStrategy
+
+
+    class CustomDDPStrategy(DDPStrategy):
+        def configure_ddp(self):
+            self.model = MyCustomDistributedDataParallel(
+                self.model,
+                device_ids=...,
+            )
+
+        def setup(self, trainer):
+            # you can access the accelerator and plugins directly
+            self.accelerator.setup()
+            self.precision_plugin.connect(...)
+
+
+The custom strategy can then be passed into the ``Trainer`` directly via the ``strategy`` parameter.
+
+.. code-block:: python
+
+    # custom strategy
+    trainer = Trainer(strategy=CustomDDPStrategy())
+
+
+Since the strategy also hosts the Accelerator and various plugins, you can customize all of them to work together as you like:
+
+.. code-block:: python
+
+    # custom strategy, with new accelerator and plugins
+    accelerator = MyAccelerator()
+    precision_plugin = MyPrecisionPlugin()
+    strategy = CustomDDPStrategy(accelerator=accelerator, precision_plugin=precision_plugin)
+    trainer = Trainer(strategy=strategy)
diff --git a/docs/source-pytorch/generated/CHANGELOG.md b/docs/source-pytorch/generated/CHANGELOG.md
new file mode 100644
index 0000000..0d01829
--- /dev/null
+++ b/docs/source-pytorch/generated/CHANGELOG.md
@@ -0,0 +1,7 @@
+# Changelog
+
+이 프로젝트의 모든 주요한 변경 사항은 이 파일에 문서화됩니다.
+하지만, 한국어 번역에서는 최신 버전을 반영하지 못할 수 있으므로, [원본 문서](https://lightning.ai/docs/pytorch/stable/generated/CHANGELOG.html)를 참고해주세요.
+
+> All notable changes to this project will be documented in this file.
+> However, the Korean site may not reflect the latest version, so please refer to the [original documentation](https://lightning.ai/docs/pytorch/stable/generated/CHANGELOG.html).
\ No newline at end of file
diff --git a/docs/source-pytorch/glossary/index.rst b/docs/source-pytorch/glossary/index.rst
new file mode 100644
index 0000000..94a042b
--- /dev/null
+++ b/docs/source-pytorch/glossary/index.rst
@@ -0,0 +1,331 @@
+
+.. toctree::
+   :maxdepth: 1
+   :hidden:
+
+   Accelerators <../extensions/accelerator>
+   Callback <../extensions/callbacks>
+   Checkpointing <../common/checkpointing>
+   Cluster <../clouds/cluster>
+   Cloud checkpoint <../common/checkpointing_advanced>
+   Console Logging <../common/console_logs>
+   Debugging <../debug/debugging>
+   Early stopping <../common/early_stopping>
+   Experiment manager (Logger) <../visualize/experiment_managers>
+   Finetuning <../advanced/finetuning>
+   GPU <../accelerators/gpu>
+   Half precision <../common/precision>
+   HPU <../integrations/hpu/index>
+   Inference <../deploy/production_intermediate>
+   IPU <../accelerators/ipu>
+   Lightning CLI <../cli/lightning_cli>
+   LightningDataModule <../data/datamodule>
+   LightningModule <../common/lightning_module>
+   Log <../visualize/loggers>
+   TPU <../accelerators/tpu>
+   Metrics 
+   Model <../model/build_model.rst>
+   Model Parallel <../advanced/model_parallel>
+   Plugins <../extensions/plugins>
+   Progress bar <../common/progress_bar>
+   Production <../deploy/production_advanced>
+   Predict <../deploy/production_basic>
+   Pretrained models <../advanced/pretrained>
+   Profiler <../tuning/profiler>
+   Pruning and Quantization <../advanced/pruning_quantization>
+   Remote filesystem and FSSPEC <../common/remote_fs>
+   Strategy <../extensions/strategy>
+   Strategy registry <../advanced/strategy_registry>
+   Style guide <../starter/style_guide>
+   SWA <../advanced/training_tricks>
+   SLURM <../clouds/cluster_advanced>
+   Transfer learning <../advanced/transfer_learning>
+   Trainer <../common/trainer>
+   Torch distributed <../clouds/cluster_intermediate_2>
+
+########
+Glossary
+########
+
+.. raw:: html
+
+    
+
+ +.. displayitem:: + :header: Accelerators + :description: Accelerators connect the Trainer to hardware to train faster + :col_css: col-md-12 + :button_link: ../extensions/accelerator.html + :height: 100 + +.. displayitem:: + :header: Callback + :description: Add self-contained extra functionality during training execution + :col_css: col-md-12 + :button_link: ../extensions/callbacks.html + :height: 100 + +.. displayitem:: + :header: Checkpointing + :description: Save and load progress with checkpoints + :col_css: col-md-12 + :button_link: ../common/checkpointing.html + :height: 100 + +.. displayitem:: + :header: Cluster + :description: Run on your own group of servers + :col_css: col-md-12 + :button_link: ../clouds/cluster.html + :height: 100 + +.. displayitem:: + :header: Cloud checkpoint + :description: Save your models to cloud filesystems + :col_css: col-md-12 + :button_link: ../common/checkpointing_advanced.html + :height: 100 + +.. displayitem:: + :header: Console Logging + :description: Capture more visible logs + :col_css: col-md-12 + :button_link: ../common/console_logs.html + :height: 100 + +.. displayitem:: + :header: Debugging + :description: Fix errors in your code + :col_css: col-md-12 + :button_link: ../debug/debugging.html + :height: 100 + +.. displayitem:: + :header: Early stopping + :description: Stop the training when no improvement is observed + :col_css: col-md-12 + :button_link: ../common/early_stopping.html + :height: 100 + +.. displayitem:: + :header: Experiment manager (Logger) + :description: Tools for tracking and visualizing artifacts and logs + :col_css: col-md-12 + :button_link: ../visualize/experiment_managers.html + :height: 100 + +.. displayitem:: + :header: Finetuning + :description: Technique for training pretrained models + :col_css: col-md-12 + :button_link: ../advanced/finetuning.html + :height: 100 + +.. displayitem:: + :header: GPU + :description: Graphics Processing Unit for faster training + :col_css: col-md-12 + :button_link: ../accelerators/gpu.html + :height: 100 + +.. displayitem:: + :header: Half precision + :description: Using different numerical formats to save memory and run fatser + :col_css: col-md-12 + :button_link: ../common/precision.html + :height: 100 + +.. displayitem:: + :header: HPU + :description: Habana Gaudi AI Processor Unit for faster training + :col_css: col-md-12 + :button_link: ../integrations/hpu/index.html + :height: 100 + +.. displayitem:: + :header: Inference + :description: Making predictions by applying a trained model to unlabeled examples + :col_css: col-md-12 + :button_link: ../deploy/production_intermediate.html + :height: 100 + +.. displayitem:: + :header: IPU + :description: Graphcore Intelligence Processing Unit for faster training + :col_css: col-md-12 + :button_link: ../accelerators/ipu.html + :height: 100 + +.. displayitem:: + :header: Lightning CLI + :description: A Command-line Interface (CLI) to interact with Lightning code via a terminal + :col_css: col-md-12 + :button_link: ../cli/lightning_cli.html + :height: 100 + +.. displayitem:: + :header: LightningDataModule + :description: A shareable, reusable class that encapsulates all the steps needed to process data + :col_css: col-md-12 + :button_link: ../data/datamodule.html + :height: 100 + +.. displayitem:: + :header: LightningModule + :description: A base class organizug your neural network module + :col_css: col-md-12 + :button_link: ../common/lightning_module.html + :height: 100 + +.. displayitem:: + :header: Log + :description: Outpus or results used for visualization and tracking + :col_css: col-md-12 + :button_link: ../visualize/loggers.html + :height: 100 + +.. displayitem:: + :header: Metrics + :description: A statistic used to measure performance or other objectives we want to optimize + :col_css: col-md-12 + :button_link: https://torchmetrics.readthedocs.io/en/stable/ + :height: 100 + +.. displayitem:: + :header: Model + :description: The set of parameters and structure for a system to make predictions + :col_css: col-md-12 + :button_link: ../model/build_model.html + :height: 100 + +.. displayitem:: + :header: Model Parallelism + :description: A way to scale training that splits a model between multiple devices. + :col_css: col-md-12 + :button_link: ../advanced/model_parallel.html + :height: 100 + +.. displayitem:: + :header: Plugins + :description: Custom trainer integrations such as custom precision, checkpointing or cluster environment implementation + :col_css: col-md-12 + :button_link: ../extensions/plugins.html + :height: 100 + +.. displayitem:: + :header: Progress bar + :description: Output printed to the terminal to visualize the progression of training + :col_css: col-md-12 + :button_link: ../common/progress_bar.html + :height: 100 + +.. displayitem:: + :header: Production + :description: Using ML models in real world systems + :col_css: col-md-12 + :button_link: ../deploy/production_advanced.html + :height: 100 + +.. displayitem:: + :header: Prediction + :description: Computing a model's output + :col_css: col-md-12 + :button_link: ../deploy/production_basic.html + :height: 100 + +.. displayitem:: + :header: Pretrained models + :description: Models that have already been trained for a particular task + :col_css: col-md-12 + :button_link: ../advanced/pretrained.html + :height: 100 + +.. displayitem:: + :header: Profiler + :description: Tool to identify bottlenecks and performance of different parts of a model + :col_css: col-md-12 + :button_link: ../tuning/profiler.html + :height: 100 + +.. displayitem:: + :header: Pruning + :description: A technique to eliminae some of the model weights to reduce the model size and decrease inference requirements + :col_css: col-md-12 + :button_link: ../advanced/pruning_quantization.html + :height: 100 + +.. displayitem:: + :header: Quantization + :description: A technique to accelerate the model inference speed and decrease the memory load while still maintaining the model accuracy + :col_css: col-md-12 + :button_link: ../advanced/post_training_quantization.html + :height: 100 + +.. displayitem:: + :header: Remote filesystem and FSSPEC + :description: Accessing files from cloud storage providers + :col_css: col-md-12 + :button_link: ../common/remote_fs.html + :height: 100 + +.. displayitem:: + :header: Strategy + :description: Ways the trainer controls the model distribution across training, evaluation, and prediction + :col_css: col-md-12 + :button_link: ../extensions/strategy.html + :height: 100 + +.. displayitem:: + :header: Strategy registry + :description: A class that holds information about training strategies and allows adding new custom strategies + :col_css: col-md-12 + :button_link: ../advanced/strategy_registry.html + :height: 100 + +.. displayitem:: + :header: Style guide + :description: Best practices to improve readability and reproducability + :col_css: col-md-12 + :button_link: ../starter/style_guide.html + :height: 100 + +.. displayitem:: + :header: SWA + :description: Stochastic Weight Averaging (SWA) can make your models generalize better + :col_css: col-md-12 + :button_link: ../advanced/training_tricks.html#stochastic-weight-averaging + :height: 100 + +.. displayitem:: + :header: SLURM + :description: Simple Linux Utility for Resource Management, or simply Slurm, is a free and open-source job scheduler for Linux clusters + :col_css: col-md-12 + :button_link: ../clouds/cluster_advanced.html + :height: 100 + +.. displayitem:: + :header: Transfer learning + :description: Using pre-trained models to improve learning + :col_css: col-md-12 + :button_link: ../advanced/transfer_learning.html + :height: 100 + +.. displayitem:: + :header: Trainer + :description: The class that automates and customizes model training + :col_css: col-md-12 + :button_link: ../common/trainer.html + :height: 100 + +.. displayitem:: + :header: Torch distributed + :description: Setup for running on distributed environments + :col_css: col-md-12 + :button_link: ../clouds/cluster_intermediate_2.html + :height: 100 + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/index.rst b/docs/source-pytorch/index.rst new file mode 100644 index 0000000..e2b6501 --- /dev/null +++ b/docs/source-pytorch/index.rst @@ -0,0 +1,200 @@ +⚡ PyTorch Lightning에 오신 것을 환영합니다! +======================================== + +.. twocolumns:: + :left: + .. image:: _static/fetched-s3-assets/mov.gif + :alt: Animation showing how to convert standard training code to Lightning + :right: + PyTorch Lightning(파이토치 라이트닝))은 대규모에서 성능을 포기하지 않으면서 최대한의 유연성을 필요로 하는 전문적인 AI 연구자들과 머신러닝 엔지니어들을 위한 딥러닝 프레임워크입니다. + Lightning(라이트닝)은 프로젝트가 생각으로부터 문서 / 제품화에 이르는 동안 함께 발전합니다. + +.. raw:: html + +
+
+
+
+ +.. raw:: html + +
+
+ + +.. raw:: html + +
+ + +Lightning 설치하기 +---------------------- + + +.. raw:: html + +
+
+ +Pip 사용자라면, + +.. code-block:: bash + + pip install lightning + +.. raw:: html + +
+
+ +Conda 사용자라면, + +.. code-block:: bash + + conda install lightning -c conda-forge + +.. raw:: html + +
+
+ +또는 `advanced install guide `_ 참조하세요. + +지원하는 PyTorch 버전은 :ref:`compatibility matrix ` 에서 확인할 수 있습니다. + +.. raw:: html + +
+ +처음이신가요? +----------- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. customcalloutitem:: + :description: Learn the 7 key steps of a typical Lightning workflow. + :header: Lightning in 15 minutes + :button_link: starter/introduction.html + +.. customcalloutitem:: + :description: Learn how to benchmark PyTorch Lightning. + :header: Benchmarking + :button_link: benchmarking/benchmarks.html + +.. raw:: html + +
+
+ +.. End of callout item section + +.. raw:: html + +
+ +이미 Lightning 사용자라면? +----------------------- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. customcalloutitem:: + :description: Learn Lightning in small bites at 4 levels of expertise: Introductory, intermediate, advanced and expert. + :header: Level Up! + :button_link: expertise_levels.html + +.. customcalloutitem:: + :description: Detailed description of API each package. Assumes you already have basic Lightning knowledge. + :header: API Reference + :button_link: api_references.html + +.. customcalloutitem:: + :description: From NLP, Computer vision to RL and meta learning - see how to use Lightning in ALL research areas. + :header: Hands-on Examples + :button_link: tutorials.html + +.. customcalloutitem:: + :description: Learn how to do everything from hyper-parameters sweeps to cloud training to Pruning and Quantization with Lightning. + :header: Common Workflows + :button_link: common_usecases.html + +.. customcalloutitem:: + :description: Convert your current code to Lightning + :header: Convert code to PyTorch Lightning + :button_link: starter/converting.html + + +.. raw:: html + +
+
+ +.. End of callout item section + +.. raw:: html + +
+ +.. toctree:: + :maxdepth: 1 + :name: start + :caption: Home + + starter/introduction + Install + upgrade/migration_guide + + +.. toctree:: + :maxdepth: 2 + :name: levels + :caption: Level Up + + levels/core_skills + levels/intermediate + levels/advanced + levels/expert + +.. toctree:: + :maxdepth: 1 + :name: pl_docs + :caption: Core API + + common/lightning_module + common/trainer + +.. toctree:: + :maxdepth: 1 + :name: api + :caption: Optional API + + api_references + +.. toctree:: + :maxdepth: 1 + :name: More + :caption: More + + Community + Examples + Glossary + How to + + +.. raw:: html + +
+ +.. PyTorch-Lightning documentation master file, created by + sphinx-quickstart on Fri Nov 15 07:48:22 2019. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. diff --git a/docs/source-pytorch/integrations/hpu/advanced.rst b/docs/source-pytorch/integrations/hpu/advanced.rst new file mode 100644 index 0000000..a3b34c4 --- /dev/null +++ b/docs/source-pytorch/integrations/hpu/advanced.rst @@ -0,0 +1,140 @@ +:orphan: + +.. _hpu_advanced: + +Accelerator: HPU Training +========================= +This document offers instructions to Gaudi chip users who want to use advanced strategies and profiling HPUs. + +---- + +Using HPUProfiler +----------------- + +HPUProfiler is a Lightning implementation of PyTorch profiler for HPU. It aids in obtaining profiling summary of PyTorch functions. +It subclasses PyTorch Lightning's `PyTorch profiler `_. + +Default Profiling +^^^^^^^^^^^^^^^^^^ +For auto profiling, create an ``HPUProfiler`` instance and pass it to the trainer. +At the end of ``profiler.fit()``, it will generate a JSON trace for the run. +In case ``accelerator= HPUAccelerator()`` is not used with ``HPUProfiler``, it will dump only CPU traces, similar to ``PyTorchProfiler``. + +.. code-block:: python + + from lightning import Trainer + from lightning_habana.pytorch.accelerator import HPUAccelerator + from lightning_habana.pytorch.profiler.profiler import HPUProfiler + + trainer = Trainer(accelerator=HPUAccelerator(), profiler=HPUProfiler()) + +Distributed Profiling +^^^^^^^^^^^^^^^^^^^^^^ + +To profile a distributed model, use ``HPUProfiler`` with the filename argument which will save a report per rank. + +.. code-block:: python + + from pytorch_lightning import Trainer + from lightning_habana.pytorch.accelerator import HPUAccelerator + from lightning_habana.pytorch.profiler.profiler import HPUProfiler + + profiler = HPUProfiler(filename="perf-logs") + trainer = Trainer(profiler=profiler, accelerator=HPUAccelerator()) + +Custom Profiling +^^^^^^^^^^^^^^^^^ + +To `profile custom actions of interest `_, +reference a profiler in the ``LightningModule``. + +.. code-block:: python + + from pytorch_lightning import Trainer + from lightning_habana.pytorch.accelerator import HPUAccelerator + from lightning_habana.pytorch.profiler.profiler import HPUProfiler + + # Reference profiler in LightningModule + class MyModel(LightningModule): + def __init__(self, profiler=None): + self.profiler = profiler + + # To profile in any part of your code, use the self.profiler.profile() function + def custom_processing_step_basic(self, data): + with self.profiler.profile("my_custom_action"): + print("do somthing") + return data + + # Alternatively, use self.profiler.start("my_custom_action") + # and self.profiler.stop("my_custom_action") functions + # to enclose the part of code to be profiled. + def custom_processing_step_granular(self, data): + self.profiler.start("my_custom_action") + print("do somthing") + self.profiler.stop("my_custom_action") + return data + + # Pass profiler instance to LightningModule + profiler = HPUProfiler() + model = MyModel(profiler) + trainer = Trainer(accelerator=HPUAccelerator(), profiler=profiler) + +For more details on Profiler, refer to `PyTorchProfiler `_ + +Visualizing Profiled Operations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Profiler dumps traces in JSON format. The traces can be visualized in 2 ways as described below. + +Using PyTorch TensorBoard Profiler +"""""""""""""""""""""""""""""""""" + +For further instructions see, https://github.com/pytorch/kineto/tree/master/tb_plugin. + +1. Install tensorboard + +.. code-block:: bash + + python -um pip install tensorboard torch-tb-profiler + +2. Start the TensorBoard server (default at port 6006) + +.. code-block:: bash + + tensorboard --logdir ./tensorboard --port 6006 + +3. Open the following URL in your browser: `http://localhost:6006/#profile`. + +Using Chrome +""""""""""""" + + 1. Open Chrome and paste this URL: `chrome://tracing/`. + 2. Once tracing opens, click on `Load` at the top-right and load one of the generated traces. + +Limitations +^^^^^^^^^^^^ + +- When using ``HPUProfiler``, wall clock time will not be representative of the true wall clock time. This is due to forcing profiled operations to be measured synchronously, when many HPU ops happen asynchronously. + It is recommended to use this Profiler to find bottlenecks/breakdowns, however for end to end wall clock time use the ``SimpleProfiler``. + +- ``HPUProfiler.summary()`` is not supported. + +- Passing the Profiler name as a string "hpu" to the trainer is not supported. + +---- + +Using DeepSpeed +------------------------ + +HPU supports advanced strategies like ``deepspeed``. By default, HPU training uses 32-bit precision. +To enable mixed precision, set the ``precision`` flag. + +.. code-block:: python + + from lightning.pytorch.plugins import DeepSpeedPrecisionPlugin + from lightning_habana.pytorch.accelerator import HPUAccelerator + from lightning_habana.pytorch.strategies import HPUDeepSpeedStrategy + + trainer = Trainer(devices=8, accelerator=HPUAccelerator(), strategy=HPUDeepSpeedStrategy(), plugins=[DeepSpeedPrecisionPlugin(precision="bf16-mixed")]) + +For further details on the supported DeepSpeed features and functionalities, refer to `Using Deepspeed with HPU `_. diff --git a/docs/source-pytorch/integrations/hpu/basic.rst b/docs/source-pytorch/integrations/hpu/basic.rst new file mode 100644 index 0000000..58f6c8d --- /dev/null +++ b/docs/source-pytorch/integrations/hpu/basic.rst @@ -0,0 +1,112 @@ +:orphan: + +.. _hpu_basics: + +Accelerator: HPU training +========================= +**Audience:** Users looking to save money and run large models faster using single or multiple Gaudi devices. + +---- + +What is an HPU? +--------------- + +`Habana® Gaudi® AI Processor (HPU) `__ training processors are built on a heterogeneous architecture with a cluster of fully programmable Tensor Processing Cores (TPC) along with its associated development tools and libraries, and a configurable Matrix Math engine. + +The TPC core is a VLIW SIMD processor with an instruction set and hardware tailored to serve training workloads efficiently. +The Gaudi memory architecture includes on-die SRAM and local memories in each TPC and, +Gaudi is the first DL training processor that has integrated RDMA over Converged Ethernet (RoCE v2) engines on-chip. + +On the software side, the PyTorch Habana bridge interfaces between the framework and SynapseAI software stack to enable the execution of deep learning models on the Habana Gaudi device. + +Gaudi offers a substantial price/performance advantage -- so you get to do more deep learning training while spending less. + +For more information, check out `Gaudi Architecture `__ and `Gaudi Developer Docs `__. + +---- + +Run on Gaudi +------------ + +To enable PyTorch Lightning to utilize the HPU accelerator, simply provide ``accelerator=HPUAccelerator()"`` parameter to the Trainer class. + +.. code-block:: python + + from lightning_habana.pytorch.accelerator import HPUAccelerator + + # run on as many Gaudi devices as available by default + trainer = Trainer(accelerator="auto", devices="auto", strategy="auto") + # equivalent to + trainer = Trainer() + + # run on one Gaudi device + trainer = Trainer(accelerator=HPUAccelerator(), devices=1) + # run on multiple Gaudi devices + trainer = Trainer(accelerator=HPUAccelerator(), devices=8) + # choose the number of devices automatically + trainer = Trainer(accelerator=HPUAccelerator(), devices="auto") + + +The ``devices=1`` parameter with HPUs enables the Habana accelerator for single card training. +It uses :class:`~lightning_habana.pytorch.strategies.SingleHPUStrategy`. + +The ``devices>1`` parameter with HPUs enables the Habana accelerator for distributed training. +It uses :class:`~lightning_habana.pytorch.strategies.HPUParallelStrategy` which is based on DDP +strategy with the addition of Habana's collective communication library (HCCL) to support scale-up within a node and +scale-out across multiple nodes. + +.. note:: + accelerator="auto" or accelerator="hpu" is not yet enabled with lightning>2.0.0 and lightning-habana. + However passing class object :class:`HPUAccelerator()` is supported. + +---- + +Scale-out on Gaudis +------------------- + +To train a Lightning model using multiple HPU nodes, set the ``num_nodes`` parameter with the available nodes in the ``Trainer`` class. + +.. code-block:: python + + from lightning_habana.pytorch.accelerator import HPUAccelerator + from lightning_habana.pytorch.strategies import HPUParallelStrategy + + hpus = 8 + parallel_hpus = [torch.device("hpu")] * hpus + trainer = Trainer(accelerator=HPUAccelerator(), devices=hpus, strategy=HPUParallelStrategy(parallel_devices=parallel_hpus), num_nodes=2) + +In addition to this, the following environment variables need to be set to establish communication across nodes. + +- *MASTER_PORT* - required; has to be a free port on machine with NODE_RANK 0 +- *MASTER_ADDR* - required (except for NODE_RANK 0); address of NODE_RANK 0 node +- *WORLD_SIZE* - required; how many workers are in the cluster +- *NODE_RANK* - required; id of the node in the cluster + +The trainer needs to be instantiated on every node participating in the training. + +On Node 1: + +.. code-block:: bash + + MASTER_ADDR= MASTER_PORT= NODE_RANK=0 WORLD_SIZE=16 + python -m some_model_trainer.py (--arg1 ... train script args...) + +On Node 2: + +.. code-block:: bash + + MASTER_ADDR= MASTER_PORT= NODE_RANK=1 WORLD_SIZE=16 + python -m some_model_trainer.py (--arg1 ... train script args...) + +---- + +How to access HPUs +------------------ + +To use HPUs, you must have access to a system with HPU devices. + +AWS +^^^ +You can either use `Gaudi-based AWS EC2 DL1 instances `__ or `Supermicro X12 Gaudi server `__ to get access to HPUs. + +Check out the `PyTorch Model on AWS DL1 Instance Quick Start `__. diff --git a/docs/source-pytorch/integrations/hpu/index.rst b/docs/source-pytorch/integrations/hpu/index.rst new file mode 100644 index 0000000..2f8607d --- /dev/null +++ b/docs/source-pytorch/integrations/hpu/index.rst @@ -0,0 +1,40 @@ +.. _hpu: + +Accelerator: HPU training +========================= + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Basic + :description: Learn the basics of single and multi-HPU core training. + :col_css: col-md-4 + :button_link: basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: Intermediate + :description: Enable state-of-the-art scaling with advanced mix-precision settings. + :col_css: col-md-4 + :button_link: intermediate.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Advanced + :description: Explore state-of-the-art scaling with additional advanced configurations. + :col_css: col-md-4 + :button_link: advanced.html + :height: 150 + :tag: advanced + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/integrations/hpu/intermediate.rst b/docs/source-pytorch/integrations/hpu/intermediate.rst new file mode 100644 index 0000000..b5b1565 --- /dev/null +++ b/docs/source-pytorch/integrations/hpu/intermediate.rst @@ -0,0 +1,116 @@ +:orphan: + +.. _hpu_intermediate: + +Accelerator: HPU Training +========================= +This document offers instructions to Gaudi chip users who want to conserve memory and scale models using mixed-precision training. + +---- + +Enable Mixed Precision +---------------------- + +With Lightning, you can leverage mixed precision training on HPUs. By default, HPU training +uses 32-bit precision. To enable mixed precision, set the ``precision`` flag. + +.. code-block:: python + + from lightning_habana.pytorch.accelerator import HPUAccelerator + + trainer = Trainer(devices=1, accelerator=HPUAccelerator(), precision="bf16-mixed") + +---- + +Customize Mixed Precision +------------------------- + +Internally, :class:`~lightning_habana.pytorch.plugins.precision.HPUPrecisionPlugin` uses the Habana Mixed Precision (HMP) package to enable mixed precision training. + +You can execute the ops in FP32 or BF16 precision. The HMP package modifies the Python operators to add the appropriate cast operations for the arguments before execution. +With the default settings, you can easily enable mixed precision training with minimal code. + +In addition to the default settings in HMP, you can choose to override these defaults and provide your own BF16 and FP32 operator lists by passing them as parameters +to :class:`~lightning_habana.pytorch.plugins.precision.HPUPrecisionPlugin`. + +The following is an excerpt from an MNIST example implemented on a single HPU. + +.. code-block:: python + + import pytorch_lightning as pl + from lightning_habana.pytorch.accelerator import HPUAccelerator + from lightning_habana.pytorch.plugins.precision import HPUPrecisionPlugin + + # Initialize a trainer with HPU accelerator for HPU strategy for single device, + # with mixed precision using overidden HMP settings + trainer = pl.Trainer( + accelerator=HPUAccelerator(), + devices=1, + # Optional Habana mixed precision params to be set + # Checkout `examples/pl_hpu/ops_bf16_mnist.txt` for the format + plugins=[ + HPUPrecisionPlugin( + precision="bf16-mixed", + opt_level="O1", + verbose=False, + bf16_file_path="ops_bf16_mnist.txt", + fp32_file_path="ops_fp32_mnist.txt", + ) + ], + ) + + # Init our model + model = LitClassifier() + # Init the data + dm = MNISTDataModule(batch_size=batch_size) + + # Train the model ⚡ + trainer.fit(model, datamodule=dm) + +For more details, please refer to `PyTorch Mixed Precision Training on Gaudi `__. + +---- + +Enabling DeviceStatsMonitor with HPUs +---------------------------------------- + +:class:`~pytorch_lightning.callbacks.device_stats_monitor.DeviceStatsMonitor` is a callback that automatically monitors and logs device stats during the training stage. +This callback can be passed for training with HPUs. It returns a map of the following metrics with their values in bytes of type uint64: + ++-------------------+---------------------------------------------+ +| Metric | Value | ++===================+=============================================+ +| Limit | Amount of total memory on HPU. | ++-------------------+---------------------------------------------+ +| InUse | Amount of allocated memory at any instance. | ++-------------------+---------------------------------------------+ +| MaxInUse | Amount of total active memory allocated. | ++-------------------+---------------------------------------------+ +| NumAllocs | Number of allocations. | ++-------------------+---------------------------------------------+ +| NumFrees | Number of freed chunks. | ++-------------------+---------------------------------------------+ +| ActiveAllocs | Number of active allocations. | ++-------------------+---------------------------------------------+ +| MaxAllocSize | Maximum allocated size. | ++-------------------+---------------------------------------------+ +| TotalSystemAllocs | Total number of system allocations. | ++-------------------+---------------------------------------------+ +| TotalSystemFrees | Total number of system frees. | ++-------------------+---------------------------------------------+ +| TotalActiveAllocs | Total number of active allocations. | ++-------------------+---------------------------------------------+ + + +The below shows how ``DeviceStatsMonitor`` can be enabled. + +.. code-block:: python + + from pytorch_lightning import Trainer + from pytorch_lightning.callbacks import DeviceStatsMonitor + from lightning_habana.pytorch.accelerator import HPUAccelerator + + device_stats = DeviceStatsMonitor() + trainer = Trainer(accelerator=HPUAccelerator(), callbacks=[device_stats]) + +For more details, please refer to `Memory Stats APIs `__. diff --git a/docs/source-pytorch/integrations/strategies/bagua.rst b/docs/source-pytorch/integrations/strategies/bagua.rst new file mode 100644 index 0000000..60214f8 --- /dev/null +++ b/docs/source-pytorch/integrations/strategies/bagua.rst @@ -0,0 +1,53 @@ +:orphan: + +##### +Bagua +##### + +The `Bagua strategy `_ speeds up PyTorch training from a single node to large scale. +Bagua is a deep learning training acceleration framework for PyTorch, with advanced distributed training algorithms and system optimizations. +Bagua currently supports: + +- **Advanced Distributed Training Algorithms**: Users can extend the training on a single GPU to multi-GPUs (may across multiple machines) by simply adding a few lines of code (optionally in `elastic mode `_). One prominent feature of Bagua is to provide a flexible system abstraction that supports state-of-the-art system relaxation techniques of distributed training. So far, Bagua has integrated communication primitives including + + - Centralized Synchronous Communication (e.g. `Gradient AllReduce `_) + + - Decentralized Synchronous Communication (e.g. `Decentralized SGD `_) + + - Low Precision Communication (e.g. `ByteGrad `_) + + - Asynchronous Communication (e.g. `Async Model Average `_) +- `Cached Dataset `_: When samples in a dataset need tedious preprocessing, or reading the dataset itself is slow, they could become a major bottleneck of the whole training process. Bagua provides cached dataset to speedup this process by caching data samples in memory, so that reading these samples after the first time can be much faster. +- `TCP Communication Acceleration (Bagua-Net) `_: Bagua-Net is a low level communication acceleration feature provided by Bagua. It can greatly improve the throughput of AllReduce on TCP network. You can enable Bagua-Net optimization on any distributed training job that uses NCCL to do GPU communication (this includes PyTorch-DDP, Horovod, DeepSpeed, and more). +- `Performance Autotuning `_: Bagua can automatically tune system parameters to achieve the highest throughput. +- `Generic Fused Optimizer `_: Bagua provides generic fused optimizer which improves the performance of optimizers, by fusing the optimizer `.step()` operation on multiple layers. It can be applied to arbitrary PyTorch optimizer, in contrast to `NVIDIA Apex `_'s approach, where only some specific optimizers are implemented. +- `Load Balanced Data Loader `_: When the computation complexity of samples in training data are different, for example in NLP and speech tasks, where each sample have different lengths, distributed training throughput can be greatly improved by using Bagua's load balanced data loader, which distributes samples in a way that each worker's workload are similar. + +You can install the Bagua integration by running + +.. code-block:: bash + + pip install lightning-bagua + +This will install both the `bagua `_ package as well as the ``BaguaStrategy`` for the Lightning Trainer: + +.. code-block:: python + + trainer = Trainer(strategy="bagua", accelerator="gpu", devices=...) + + +You can tune several settings by instantiating the strategy objects and pass options in: + +.. code-block:: python + + from lightning_bagua import BaguaStrategy + + strategy = BaguaStrategy(algorithm="bytegrad") + trainer = Trainer(strategy=strategy, accelerator="gpu", devices=...) + + +.. note:: + + * Bagua is only supported on Linux systems with GPU(s). + +See `Bagua Tutorials `_ for more details on installation and advanced features. diff --git a/docs/source-pytorch/integrations/strategies/colossalai.rst b/docs/source-pytorch/integrations/strategies/colossalai.rst new file mode 100644 index 0000000..2165673 --- /dev/null +++ b/docs/source-pytorch/integrations/strategies/colossalai.rst @@ -0,0 +1,112 @@ +:orphan: + +########### +Colossal-AI +########### + +The `Colossal-AI strategy `_ implements ZeRO-DP with chunk-based memory management. +With this chunk mechanism, really large models can be trained with a small number of GPUs. +It supports larger trainable model size and batch size than usual heterogeneous training by reducing CUDA memory fragments and CPU memory consumption. +Also, it speeds up this kind of heterogeneous training by fully utilizing all kinds of resources. + +.. warning:: This is an :ref:`experimental ` feature. + +When enabling chunk mechanism, a set of consecutive parameters are stored in a chunk, and then the chunk is sharded across different processes. +This can reduce communication and data transmission frequency and fully utilize communication and PCI-E bandwidth, which makes training faster. + +Unlike traditional implementations, which adopt static memory partition, we implemented a dynamic heterogeneous memory management system named Gemini. +During the first training step, the warmup phase will sample the maximum non-model data memory (memory usage expect parameters, gradients, and optimizer states). +In later training, it will use the collected memory usage information to evict chunks dynamically. +Gemini allows you to fit much larger models with limited GPU memory. + +According to our benchmark results, we can train models with up to 24 billion parameters in 1 GPU. + +You can install the Colossal-AI integration by running + +.. code-block:: bash + + pip install lightning-colossalai + +This will install both the `colossalai `_ package as well as the ``ColossalAIStrategy`` for the Lightning Trainer: + +.. code-block:: python + + trainer = Trainer(strategy="colossalai", precision=16, devices=...) + + +You can tune several settings by instantiating the strategy objects and pass options in: + +.. code-block:: python + + from lightning_colossalai import ColossalAIStrategy + + strategy = ColossalAIStrategy(...) + trainer = Trainer(strategy=strategy, precision=16, devices=...) + + +See a full example of a benchmark with the a `GPT-2 model `_ of up to 24 billion parameters + +.. note:: + + * The only accelerator which ColossalAI supports is ``"gpu"``. But CPU resources will be used when the placement policy is set to "auto" or "cpu". + + * The only precision which ColossalAI allows is 16-bit mixed precision (FP16). + + * It only supports a single optimizer, which must be ``colossalai.nn.optimizer.CPUAdam`` or ``colossalai.nn.optimizer. + HybridAdam`` now. You can set ``adamw_mode`` to False to use normal Adam. Noticing that ``HybridAdam`` is highly optimized, it uses fused CUDA kernel and parallel CPU kernel. + It is recomended to use ``HybridAdam``, since it updates parameters in GPU and CPU both. + + * Your model must be created using the :meth:`~lightning.pytorch.core.module.LightningModule.configure_model` method. + + * ``ColossalaiStrategy`` doesn't support gradient accumulation as of now. + +.. _colossal_placement_policy: + +Model Definition +================ + +ColossalAI requires the layers of your model to be created in the special :meth:`~lightning.pytorch.core.module.LightningModule.configure_model` hook. +This allows the strategy to efficiently shard your model before materializing the weight tensors. + +.. code-block:: python + + class MyModel(LightningModule): + def __init__(self): + super().__init__() + # don't instantiate layers here + # move the creation of layers to `configure_model` + + def configure_model(self): + # create all your layers here + self.layers = nn.Sequential(...) + + +Placement Policy +================ + +Placement policies can help users fully exploit their GPU-CPU heterogeneous memory space for better training efficiency. +There are three options for the placement policy. +They are "cpu", "cuda" and "auto" respectively. + +When the placement policy is set to "cpu", all participated parameters will be offloaded into CPU memory immediately at the end of every auto-grad operation. +In this way, "cpu" placement policy uses the least CUDA memory. +It is the best choice for users who want to exceptionally enlarge their model size or training batch size. + +When using "cuda" option, all parameters are placed in the CUDA memory, no CPU resources will be used during the training. +It is for users who get plenty of CUDA memory. + +The third option, "auto", enables Gemini. +It monitors the consumption of CUDA memory during the warmup phase and collects CUDA memory usage of all auto-grad operations. +In later training steps, Gemini automatically manages the data transmission between GPU and CPU according to collected CUDA memory usage information. +It is the fastest option when CUDA memory is enough. + +Here's an example of changing the placement policy to "cpu". + +.. code-block:: python + + from lightning_colossalai import ColossalAIStrategy + + model = MyModel() + my_strategy = ColossalAIStrategy(placement_policy="cpu") + trainer = Trainer(accelerator="gpu", devices=4, precision=16, strategy=my_strategy) + trainer.fit(model) diff --git a/docs/source-pytorch/integrations/strategies/hivemind.rst b/docs/source-pytorch/integrations/strategies/hivemind.rst new file mode 100644 index 0000000..32079b8 --- /dev/null +++ b/docs/source-pytorch/integrations/strategies/hivemind.rst @@ -0,0 +1,114 @@ +:orphan: + +################################################################ +Hivemind - training on unreliable mixed GPUs across the internet +################################################################ + +Collaborative Training tries to solve the need for top-tier multi-GPU servers by allowing you to train across unreliable machines, +such as local machines or even preemptible cloud compute across the internet. + +Under the hood, we use `Hivemind `__ which provides de-centralized training across the internet. + +.. warning:: This is an :ref:`experimental ` feature. + + +To use Collaborative Training, you need to first this extension. + +.. code-block:: bash + + pip install lightning-hivemind + +This will install both the `Hivemind `__ package as well as the ``HivemindStrategy`` for the Lightning Trainer: + +Reducing Communication By Overlapping Communication +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We can reduce the impact of communication across all machines by overlapping communication with our training iterations. In short, we enable communication to happen +in the background of training. + +Overlap Gradient and State Averaging +"""""""""""""""""""""""""""""""""""" + +When the target batch size is reached, all processes that are included in the step send gradients and model states to each other. By enabling some flags through +the strategy, communication can happen in the background. This allows training to continue (with slightly outdated weights) but provides us the means +to overlap communication with computation. + +.. warning:: + Enabling overlapping communication means convergence will slightly be affected. + +.. note:: + Enabling these flags means that you must pass in a ``scheduler_fn`` to the ``HivemindStrategy`` instead of relying on a scheduler from ``configure_optimizers``. + The optimizer is re-created by Hivemind, and as a result, the scheduler has to be re-created. + +.. code-block:: python + + import torch + from functools import partial + from lightning import Trainer + from lightning_hivemind.strategy import HivemindStrategy + + trainer = Trainer( + strategy=HivemindStrategy( + target_batch_size=8192, + delay_state_averaging=True, + delay_grad_averaging=True, + delay_optimizer_step=True, + offload_optimizer=True, # required to delay averaging + scheduler_fn=partial(torch.optim.lr_scheduler.ExponentialLR, gamma=...), + ), + accelerator="gpu", + devices=1, + ) + + +Reducing GPU Memory requirements by re-using buffers & CPU offloading +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We can also offload the optimizer state to the CPU whilst re-using gradient buffers to reduce the memory requirement for machines. + +Offloading Optimizer State to the CPU +""""""""""""""""""""""""""""""""""""" + +Offloading the Optimizer state to the CPU works the same as Deepspeed Zero-stage-2-offload, where we save GPU memory by keeping all optimizer states on the CPU. + +.. note:: + Enabling these flags means that you must pass in a ``scheduler_fn`` to the ``HivemindStrategy`` instead of relying on a scheduler from ``configure_optimizers``. + The optimizer is re-created by Hivemind, and as a result, the scheduler has to be re-created. + + We suggest enabling offloading and overlapping communication to hide the additional overhead from having to communicate with the CPU. + +.. code-block:: python + + import torch + from functools import partial + from lightning import Trainer + from lightning_hivemind.strategy import HivemindStrategy + + trainer = Trainer( + strategy=HivemindStrategy( + target_batch_size=8192, + offload_optimizer=True, + scheduler_fn=partial(torch.optim.lr_scheduler.ExponentialLR, gamma=...), + ), + accelerator="gpu", + devices=1, + ) + + +Re-using Gradient Buffers +""""""""""""""""""""""""" + +By default, Hivemind accumulates gradients in a separate buffer. This means additional GPU memory is required to store gradients. You can enable re-using the model parameter gradient buffers by passing ``reuse_grad_buffers=True`` to the ``HivemindStrategy``. + +.. warning:: + The ``HivemindStrategy`` will override ``zero_grad`` in your ``LightningModule`` to have no effect. This is because gradients are accumulated in the model + and Hivemind manages when they need to be cleared. + +.. code-block:: python + + from pytorch_lightning import Trainer + from lightning_hivemind.strategy import HivemindStrategy + + trainer = Trainer( + strategy=HivemindStrategy(target_batch_size=8192, reuse_grad_buffers=True), accelerator="gpu", devices=1 + ) diff --git a/docs/source-pytorch/levels/advanced.rst b/docs/source-pytorch/levels/advanced.rst new file mode 100644 index 0000000..f04f562 --- /dev/null +++ b/docs/source-pytorch/levels/advanced.rst @@ -0,0 +1,83 @@ + +############### +Advanced skills +############### + +Configure all aspects of Lightning for advanced usecases. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 14: Customize configs to run in production + :description: Enable composable YAMLs + :col_css: col-md-6 + :button_link: advanced_level_15.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 15: Customize the trainer + :description: Inject custom code into the trainer and modify the progress bar. + :col_css: col-md-6 + :button_link: advanced_level_16.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 16: Own the training loop + :description: Learn all the ways of owning your raw PyTorch loops with Lighting. + :col_css: col-md-6 + :button_link: advanced_level_17.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 17: Enable advanced checkpointing + :description: Enable composable or cloud based checkpoints. + :col_css: col-md-6 + :button_link: advanced_level_18.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 18: Explore IPUs + :description: Explore Intelligence Processing Unit (IPU) for model scaling. + :col_css: col-md-6 + :button_link: advanced_level_19.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 19: Explore HPUs + :description: Explore Habana Gaudi Processing Unit (HPU) for model scaling. + :col_css: col-md-6 + :button_link: advanced_level_20.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 20: Master TPUs + :description: Master TPUs and run on cloud TPUs. + :col_css: col-md-6 + :button_link: advanced_level_21.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Level 21: Reach 1 trillion parameters on GPUs + :description: Scale to 1 trillion params on GPUs. + :col_css: col-md-6 + :button_link: advanced_level_22.html + :height: 150 + :tag: advanced + +.. raw:: html + +
+
diff --git a/source/levels/advanced_level_15.rst b/docs/source-pytorch/levels/advanced_level_15.rst similarity index 94% rename from source/levels/advanced_level_15.rst rename to docs/source-pytorch/levels/advanced_level_15.rst index 761dbd3..fbbb4ef 100644 --- a/source/levels/advanced_level_15.rst +++ b/docs/source-pytorch/levels/advanced_level_15.rst @@ -1,7 +1,7 @@ :orphan: ################################################ -Level 15: Customize configs to run in production +Level 14: Customize configs to run in production ################################################ This level goes over advanced YAML use for running models in production. diff --git a/source/levels/advanced_level_16.rst b/docs/source-pytorch/levels/advanced_level_16.rst similarity index 87% rename from source/levels/advanced_level_16.rst rename to docs/source-pytorch/levels/advanced_level_16.rst index fd41df1..2e70c4b 100644 --- a/source/levels/advanced_level_16.rst +++ b/docs/source-pytorch/levels/advanced_level_16.rst @@ -1,10 +1,10 @@ :orphan: ############################### -Level 16: Customize the trainer +Level 15: Customize the trainer ############################### -In this level, you'll learn to modify the Trainer behavior. +In this level you'll learn to modify the behavior of the Trainer. ---- diff --git a/docs/source-pytorch/levels/advanced_level_17.rst b/docs/source-pytorch/levels/advanced_level_17.rst new file mode 100644 index 0000000..cc375dd --- /dev/null +++ b/docs/source-pytorch/levels/advanced_level_17.rst @@ -0,0 +1,37 @@ +:orphan: + +############################### +Level 16: Own the training loop +############################### + +Learn all the ways of owning your raw PyTorch loops with Lightning. + +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Enable manual optimization + :description: Gain control of the training loop with manual optimization and LightningModule methods. + :col_css: col-md-4 + :button_link: ../model/build_model_advanced.html + :height: 150 + :tag: advanced + +.. displayitem:: + :header: Use Lightning Fabric + :description: Write your own training loops using lightning Fabric. + :col_css: col-md-4 + :button_link: https://lightning.ai/docs/fabric/stable/ + :height: 150 + :tag: advanced + +.. raw:: html + +
+
diff --git a/source/levels/advanced_level_18.rst b/docs/source-pytorch/levels/advanced_level_18.rst similarity index 87% rename from source/levels/advanced_level_18.rst rename to docs/source-pytorch/levels/advanced_level_18.rst index a7d7966..b34861d 100644 --- a/source/levels/advanced_level_18.rst +++ b/docs/source-pytorch/levels/advanced_level_18.rst @@ -1,10 +1,10 @@ :orphan: ####################################### -Level 18: Enable advanced checkpointing +Level 17: Enable advanced checkpointing ####################################### -This level shows you how to enable composable and/or cloud based checkpoints. +This level shows you how to enable composable and/or cloud-based checkpoints. ---- diff --git a/source/levels/advanced_level_19.rst b/docs/source-pytorch/levels/advanced_level_19.rst similarity index 97% rename from source/levels/advanced_level_19.rst rename to docs/source-pytorch/levels/advanced_level_19.rst index c7b6697..4265b03 100644 --- a/source/levels/advanced_level_19.rst +++ b/docs/source-pytorch/levels/advanced_level_19.rst @@ -1,7 +1,7 @@ :orphan: ###################### -Level 19: Explore IPUs +Level 18: Explore IPUs ###################### Explore Intelligence Processing Unit (IPU) for model scaling. diff --git a/source/levels/advanced_level_20.rst b/docs/source-pytorch/levels/advanced_level_20.rst similarity index 79% rename from source/levels/advanced_level_20.rst rename to docs/source-pytorch/levels/advanced_level_20.rst index 7e9d562..f17ebdf 100644 --- a/source/levels/advanced_level_20.rst +++ b/docs/source-pytorch/levels/advanced_level_20.rst @@ -4,7 +4,7 @@ Level 19: Explore HPUs ###################### -Explore Intelligence Processing Unit (IPU) for model scaling. +Explore Intel Habana Processing Unit (HPU) for model scaling. ---- @@ -19,7 +19,7 @@ Explore Intelligence Processing Unit (IPU) for model scaling. :header: Train models on HPUs :description: Learn the basics of single and multi-HPU core training. :col_css: col-md-6 - :button_link: ../accelerators/hpu_basic.html + :button_link: ../integrations/hpu/basic.html :height: 150 :tag: basic @@ -27,7 +27,7 @@ Explore Intelligence Processing Unit (IPU) for model scaling. :header: Optimize models training on HPUs :description: Enable state-of-the-art scaling with advanced mix-precision settings. :col_css: col-md-6 - :button_link: ../accelerators/hpu_intermediate.html + :button_link: ../integrations/hpu/intermediate.html :height: 150 :tag: intermediate diff --git a/source/levels/advanced_level_21.rst b/docs/source-pytorch/levels/advanced_level_21.rst similarity index 97% rename from source/levels/advanced_level_21.rst rename to docs/source-pytorch/levels/advanced_level_21.rst index 5252a1c..92358c0 100644 --- a/source/levels/advanced_level_21.rst +++ b/docs/source-pytorch/levels/advanced_level_21.rst @@ -1,7 +1,7 @@ :orphan: ##################### -Level 21: Master TPUs +Level 20: Master TPUs ##################### Master cloud TPU training with profiling and scaling techniques. diff --git a/source/levels/advanced_level_22.rst b/docs/source-pytorch/levels/advanced_level_22.rst similarity index 94% rename from source/levels/advanced_level_22.rst rename to docs/source-pytorch/levels/advanced_level_22.rst index a90a482..1e4dc39 100644 --- a/source/levels/advanced_level_22.rst +++ b/docs/source-pytorch/levels/advanced_level_22.rst @@ -1,7 +1,7 @@ :orphan: ############################################# -Level 22: Reach 1 trillion parameters on GPUs +Level 21: Reach 1 trillion parameters on GPUs ############################################# Scale to 1 trillion+ parameters with multiple distributed strategies. diff --git a/source/levels/basic_level_2.rst b/docs/source-pytorch/levels/basic_level_2.rst similarity index 100% rename from source/levels/basic_level_2.rst rename to docs/source-pytorch/levels/basic_level_2.rst diff --git a/source/levels/basic_level_5.rst b/docs/source-pytorch/levels/basic_level_5.rst similarity index 100% rename from source/levels/basic_level_5.rst rename to docs/source-pytorch/levels/basic_level_5.rst diff --git a/source/levels/core_level_3.rst b/docs/source-pytorch/levels/core_level_3.rst similarity index 100% rename from source/levels/core_level_3.rst rename to docs/source-pytorch/levels/core_level_3.rst diff --git a/source/levels/core_level_6.rst b/docs/source-pytorch/levels/core_level_6.rst similarity index 100% rename from source/levels/core_level_6.rst rename to docs/source-pytorch/levels/core_level_6.rst diff --git a/source/levels/core_skills.rst b/docs/source-pytorch/levels/core_skills.rst similarity index 95% rename from source/levels/core_skills.rst rename to docs/source-pytorch/levels/core_skills.rst index 19cab69..75c4002 100644 --- a/source/levels/core_skills.rst +++ b/docs/source-pytorch/levels/core_skills.rst @@ -4,10 +4,6 @@ Basic skills ############ Learn the basics of model development with Lightning. Researchers and machine learning engineers should start here. -.. join_slack:: - :align: left - ----- .. raw:: html @@ -27,7 +23,7 @@ Learn the basics of model development with Lightning. Researchers and machine le .. displayitem:: :header: Level 2: Add a validation and test set :description: Add validation and test sets to avoid over/underfitting. - :button_link: /levels/basic_level_2.html + :button_link: ../levels/basic_level_2.html :col_css: col-md-6 :height: 150 :tag: basic diff --git a/docs/source-pytorch/levels/expert.rst b/docs/source-pytorch/levels/expert.rst new file mode 100644 index 0000000..c734142 --- /dev/null +++ b/docs/source-pytorch/levels/expert.rst @@ -0,0 +1,51 @@ + +############# +Expert skills +############# + +Customize and extend Lightning for things like custom hardware or distributed strategies. + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 22: Extend the Lightning CLI + :description: Extend the functionality of the Lightning CLI. + :col_css: col-md-6 + :button_link: expert_level_23.html + :height: 150 + :tag: expert + +.. displayitem:: + :header: Level 23: Integrate a custom cluster + :description: Integrate a custom cluster into Lightning. + :col_css: col-md-6 + :button_link: expert_level_24.html + :height: 150 + :tag: expert + +.. displayitem:: + :header: Level 24: Make your own profiler + :description: Make your own profiler. + :col_css: col-md-6 + :button_link: ../tuning/profiler_expert.html + :height: 150 + :tag: expert + +.. displayitem:: + :header: Level 25: Add a new accelerator or Strategy + :description: Integrate a new accelerator or distributed strategy. + :col_css: col-md-6 + :button_link: expert_level_27.html + :height: 150 + :tag: expert + +.. raw:: html + +
+
diff --git a/source/levels/expert_level_23.rst b/docs/source-pytorch/levels/expert_level_23.rst similarity index 95% rename from source/levels/expert_level_23.rst rename to docs/source-pytorch/levels/expert_level_23.rst index 9b143a0..5d1ba67 100644 --- a/source/levels/expert_level_23.rst +++ b/docs/source-pytorch/levels/expert_level_23.rst @@ -1,7 +1,7 @@ :orphan: ################################## -Level 23: Extend the Lightning CLI +Level 22: Extend the Lightning CLI ################################## Extend the functionality of the Lightning CLI. diff --git a/docs/source-pytorch/levels/expert_level_24.rst b/docs/source-pytorch/levels/expert_level_24.rst new file mode 100644 index 0000000..54c544e --- /dev/null +++ b/docs/source-pytorch/levels/expert_level_24.rst @@ -0,0 +1,29 @@ +:orphan: + +#################################### +Level 23: Integrate a custom cluster +#################################### + +Extend the functionality of the Lightning CLI. + +---- + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Integrate your own cluster + :description: Learn how to integrate your own cluster + :col_css: col-md-6 + :button_link: ../clouds/cluster_expert.html + :height: 150 + :tag: expert + +.. raw:: html + +
+
diff --git a/source/levels/expert_level_27.rst b/docs/source-pytorch/levels/expert_level_27.rst similarity index 96% rename from source/levels/expert_level_27.rst rename to docs/source-pytorch/levels/expert_level_27.rst index c2d682b..9b06b10 100644 --- a/source/levels/expert_level_27.rst +++ b/docs/source-pytorch/levels/expert_level_27.rst @@ -1,7 +1,7 @@ :orphan: ########################################### -Level 27: Add a new accelerator or Strategy +Level 25: Add a new accelerator or Strategy ########################################### Integrate a new accelerator or distributed strategy. diff --git a/docs/source-pytorch/levels/intermediate.rst b/docs/source-pytorch/levels/intermediate.rst new file mode 100644 index 0000000..f7beb29 --- /dev/null +++ b/docs/source-pytorch/levels/intermediate.rst @@ -0,0 +1,77 @@ + +################### +Intermediate skills +################### + +Learn to scale up your models and enable collaborative model development at academic or industry research labs. + + +.. include:: ../links.rst + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 7: Interactive cloud development + :description: Learn how to access GPUs and TPUs on the cloud. + :button_link: intermediate_level_7.html + :col_css: col-md-6 + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level 8: Modularize your projects + :description: Create DataModules to enable dataset reusability. + :col_css: col-md-6 + :button_link: intermediate_level_9.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level 9: Understand your model + :description: Use advanced visuals to find the best performing model. + :col_css: col-md-6 + :button_link: intermediate_level_10.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level 10: Explore SOTA scaling techniques + :description: Explore SOTA techniques to help convergence, stability and scalability. + :col_css: col-md-6 + :button_link: intermediate_level_11.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level 11: Deploy your models + :description: Learn how to deploy your models with optimizations like ONNX and torchscript. + :col_css: col-md-6 + :button_link: intermediate_level_12.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level 12: Optimize training speed + :description: Use advanced profilers to mixed precision to train bigger models, faster. + :col_css: col-md-6 + :button_link: intermediate_level_13.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: Level 13: Run on on-prem clusters + :description: Run on a custom on-prem cluster or SLURM cluster. + :col_css: col-md-6 + :button_link: intermediate_level_14.html + :height: 150 + :tag: intermediate + +.. raw:: html + +
+
diff --git a/source/levels/intermediate_level_10.rst b/docs/source-pytorch/levels/intermediate_level_10.rst similarity index 97% rename from source/levels/intermediate_level_10.rst rename to docs/source-pytorch/levels/intermediate_level_10.rst index d7f5dc5..1c9b646 100644 --- a/source/levels/intermediate_level_10.rst +++ b/docs/source-pytorch/levels/intermediate_level_10.rst @@ -1,7 +1,7 @@ :orphan: ############################### -Level 10: Understand your model +Level 9: Understand your model ############################### Find the best model using advanced visualizations for deeper insights. diff --git a/source/levels/intermediate_level_11.rst b/docs/source-pytorch/levels/intermediate_level_11.rst similarity index 95% rename from source/levels/intermediate_level_11.rst rename to docs/source-pytorch/levels/intermediate_level_11.rst index 4c7ed06..77dbbf9 100644 --- a/source/levels/intermediate_level_11.rst +++ b/docs/source-pytorch/levels/intermediate_level_11.rst @@ -1,7 +1,7 @@ :orphan: ######################################### -Level 11: Explore SOTA scaling techniques +Level 10: Explore SOTA scaling techniques ######################################### In this level you'll explore SOTA techniques to help convergence, stability and scalability. diff --git a/source/levels/intermediate_level_12.rst b/docs/source-pytorch/levels/intermediate_level_12.rst similarity index 97% rename from source/levels/intermediate_level_12.rst rename to docs/source-pytorch/levels/intermediate_level_12.rst index fe1c076..b929355 100644 --- a/source/levels/intermediate_level_12.rst +++ b/docs/source-pytorch/levels/intermediate_level_12.rst @@ -1,7 +1,7 @@ :orphan: ############################ -Level 12: Deploy your models +Level 11: Deploy your models ############################ In this level you'll learn a few options for deploying models into production. diff --git a/source/levels/intermediate_level_13.rst b/docs/source-pytorch/levels/intermediate_level_13.rst similarity index 96% rename from source/levels/intermediate_level_13.rst rename to docs/source-pytorch/levels/intermediate_level_13.rst index 38ac7aa..345e6d6 100644 --- a/source/levels/intermediate_level_13.rst +++ b/docs/source-pytorch/levels/intermediate_level_13.rst @@ -1,7 +1,7 @@ :orphan: ################################# -Level 13: Optimize training speed +Level 12: Optimize training speed ################################# In this level you'll use advanced profilers and mixed precision techniques to train bigger models faster. diff --git a/source/levels/intermediate_level_14.rst b/docs/source-pytorch/levels/intermediate_level_14.rst similarity index 91% rename from source/levels/intermediate_level_14.rst rename to docs/source-pytorch/levels/intermediate_level_14.rst index a779ede..b73cce2 100644 --- a/source/levels/intermediate_level_14.rst +++ b/docs/source-pytorch/levels/intermediate_level_14.rst @@ -1,10 +1,10 @@ :orphan: ################################# -Level 14: Run on on-prem clusters +Level 13: Run on on-prem clusters ################################# -In this level you'll learn to run on onprem clusters. +In this level you'll learn to run on on-prem clusters. ---- diff --git a/source/levels/intermediate_level_7.rst b/docs/source-pytorch/levels/intermediate_level_7.rst similarity index 79% rename from source/levels/intermediate_level_7.rst rename to docs/source-pytorch/levels/intermediate_level_7.rst index cc55fd7..ef4122d 100644 --- a/source/levels/intermediate_level_7.rst +++ b/docs/source-pytorch/levels/intermediate_level_7.rst @@ -21,14 +21,6 @@ Learn to develop models on cloud GPUs and TPUs. :height: 180 :tag: basic -.. displayitem:: - :header: Access a cloud machine with GPUs - :description: Learn how to get a cloud machine with single or multiple GPUs. - :col_css: col-md-3 - :button_link: ../clouds/session_basic.html - :height: 180 - :tag: basic - .. displayitem:: :header: GPU Training :description: Learn the basics on single and multi-GPU training. diff --git a/source/levels/intermediate_level_9.rst b/docs/source-pytorch/levels/intermediate_level_9.rst similarity index 96% rename from source/levels/intermediate_level_9.rst rename to docs/source-pytorch/levels/intermediate_level_9.rst index 8c537d7..b09db07 100644 --- a/source/levels/intermediate_level_9.rst +++ b/docs/source-pytorch/levels/intermediate_level_9.rst @@ -1,7 +1,7 @@ :orphan: ################################# -Level 9: Modularize your projects +Level 8: Modularize your projects ################################# This module teaches you how to setup complex projects that can be controlled via the CLI. diff --git a/source/links.rst b/docs/source-pytorch/links.rst similarity index 100% rename from source/links.rst rename to docs/source-pytorch/links.rst diff --git a/docs/source-pytorch/make.bat b/docs/source-pytorch/make.bat new file mode 100644 index 0000000..9b56514 --- /dev/null +++ b/docs/source-pytorch/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=../build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/source-pytorch/model/build_model.rst b/docs/source-pytorch/model/build_model.rst new file mode 100644 index 0000000..c480a90 --- /dev/null +++ b/docs/source-pytorch/model/build_model.rst @@ -0,0 +1,55 @@ +:orphan: + +############# +Build a Model +############# + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1: Train a model + :description: Build a model to learn the basic ideas of Lightning + :col_css: col-md-4 + :button_link: train_model_basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: 2: Validate and test a model + :description: Add a validation and test data split to avoid overfitting. + :col_css: col-md-4 + :button_link: ../common/evaluation_basic.html + :height: 150 + :tag: basic + +.. displayitem:: + :header: 3: Supercharge training + :description: Enable state-of-the-art training techniques with the Trainer features. + :col_css: col-md-4 + :button_link: build_model_intermediate.html + :height: 150 + :tag: intermediate + +.. displayitem:: + :header: LightningModule API + :description: Dig into LightningModule API in depth + :col_css: col-md-4 + :button_link: ../common/lightning_module.html#lightningmodule-api + :height: 150 + +.. displayitem:: + :header: Trainer API + :description: Dig into Trainer API in depth + :col_css: col-md-4 + :button_link: ../common/trainer.html#trainer-class-api + :height: 150 + +.. raw:: html + +
+
diff --git a/source/model/build_model_advanced.rst b/docs/source-pytorch/model/build_model_advanced.rst similarity index 77% rename from source/model/build_model_advanced.rst rename to docs/source-pytorch/model/build_model_advanced.rst index 33be842..fb06cdf 100644 --- a/source/model/build_model_advanced.rst +++ b/docs/source-pytorch/model/build_model_advanced.rst @@ -8,7 +8,7 @@ Own your loop (advanced) Customize training loop *********************** -.. image:: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/custom_loop.png +.. image:: ../_static/fetched-s3-assets/custom_loop.png :width: 600 :alt: Injecting custom code in a training loop @@ -17,7 +17,7 @@ Inject custom code anywhere in the Training loop using any of the 20+ methods (: .. testcode:: class LitModel(pl.LightningModule): - def backward(self, loss, optimizer, optimizer_idx): + def backward(self, loss): loss.backward() ---- diff --git a/source/model/build_model_intermediate.rst b/docs/source-pytorch/model/build_model_intermediate.rst similarity index 86% rename from source/model/build_model_intermediate.rst rename to docs/source-pytorch/model/build_model_intermediate.rst index 55f1247..1ecf1d4 100644 --- a/source/model/build_model_intermediate.rst +++ b/docs/source-pytorch/model/build_model_intermediate.rst @@ -35,10 +35,11 @@ Enable advanced training features using Trainer arguments. These are SOTA techni Extend the Trainer ****************** -.. raw:: html - - +.. video:: ../_static/fetched-s3-assets/cb.mp4 + :width: 600 + :autoplay: + :loop: + :muted: If you have multiple lines of code with similar functionalities, you can use *callbacks* to easily group them together and toggle all of those lines on or off at the same time. diff --git a/docs/source-pytorch/model/manual_optimization.rst b/docs/source-pytorch/model/manual_optimization.rst new file mode 100644 index 0000000..964f095 --- /dev/null +++ b/docs/source-pytorch/model/manual_optimization.rst @@ -0,0 +1,348 @@ +******************* +Manual Optimization +******************* + +For advanced research topics like reinforcement learning, sparse coding, or GAN research, it may be desirable to +manually manage the optimization process, especially when dealing with multiple optimizers at the same time. + +In this mode, Lightning will handle only accelerator, precision and strategy logic. +The users are left with ``optimizer.zero_grad()``, gradient accumulation, optimizer toggling, etc.. + +To manually optimize, do the following: + +* Set ``self.automatic_optimization=False`` in your ``LightningModule``'s ``__init__``. +* Use the following functions and call them manually: + + * ``self.optimizers()`` to access your optimizers (one or multiple) + * ``optimizer.zero_grad()`` to clear the gradients from the previous training step + * ``self.manual_backward(loss)`` instead of ``loss.backward()`` + * ``optimizer.step()`` to update your model parameters + * ``self.toggle_optimizer()`` and ``self.untoggle_optimizer()`` if needed + +Here is a minimal example of manual optimization. + +.. testcode:: python + + from lightning.pytorch import LightningModule + + + class MyModel(LightningModule): + def __init__(self): + super().__init__() + # Important: This property activates manual optimization. + self.automatic_optimization = False + + def training_step(self, batch, batch_idx): + opt = self.optimizers() + opt.zero_grad() + loss = self.compute_loss(batch) + self.manual_backward(loss) + opt.step() + +.. tip:: + Be careful where you call ``optimizer.zero_grad()``, or your model won't converge. + It is good practice to call ``optimizer.zero_grad()`` before ``self.manual_backward(loss)``. + + +Access your Own Optimizer +========================= + +The provided ``optimizer`` is a :class:`~lightning.pytorch.core.optimizer.LightningOptimizer` object wrapping your own optimizer +configured in your :meth:`~lightning.pytorch.core.module.LightningModule.configure_optimizers`. You can access your own optimizer +with ``optimizer.optimizer``. However, if you use your own optimizer to perform a step, Lightning won't be able to +support accelerators, precision and profiling for you. + +.. testcode:: python + + class Model(LightningModule): + def __init__(self): + super().__init__() + self.automatic_optimization = False + ... + + def training_step(self, batch, batch_idx): + optimizer = self.optimizers() + + # `optimizer` is a `LightningOptimizer` wrapping the optimizer. + # To access it, do the following. + # However, it won't work on TPU, AMP, etc... + optimizer = optimizer.optimizer + ... + +Gradient Accumulation +===================== + +You can accumulate gradients over batches similarly to ``accumulate_grad_batches`` argument in +:ref:`Trainer ` for automatic optimization. To perform gradient accumulation with one optimizer +after every ``N`` steps, you can do as such. + +.. testcode:: python + + def __init__(self): + super().__init__() + self.automatic_optimization = False + + + def training_step(self, batch, batch_idx): + opt = self.optimizers() + + # scale losses by 1/N (for N batches of gradient accumulation) + loss = self.compute_loss(batch) / N + self.manual_backward(loss) + + # accumulate gradients of N batches + if (batch_idx + 1) % N == 0: + opt.step() + opt.zero_grad() + +Gradient Clipping +================= + +You can clip optimizer gradients during manual optimization similar to passing the ``gradient_clip_val`` and +``gradient_clip_algorithm`` argument in :ref:`Trainer ` during automatic optimization. +To perform gradient clipping with one optimizer with manual optimization, you can do as such. + +.. testcode:: python + + from lightning.pytorch import LightningModule + + + class SimpleModel(LightningModule): + def __init__(self): + super().__init__() + self.automatic_optimization = False + + def training_step(self, batch, batch_idx): + opt = self.optimizers() + + # compute loss + loss = self.compute_loss(batch) + + opt.zero_grad() + self.manual_backward(loss) + + # clip gradients + self.clip_gradients(opt, gradient_clip_val=0.5, gradient_clip_algorithm="norm") + + opt.step() + +.. warning:: + * Note that ``configure_gradient_clipping()`` won't be called in Manual Optimization. Instead consider using ``self. clip_gradients()`` manually like in the example above. + + +Use Multiple Optimizers (like GANs) +=================================== + +Here is an example training a simple GAN with multiple optimizers using manual optimization. + +.. testcode:: python + + import torch + from torch import Tensor + from lightning.pytorch import LightningModule + + + class SimpleGAN(LightningModule): + def __init__(self): + super().__init__() + self.G = Generator() + self.D = Discriminator() + + # Important: This property activates manual optimization. + self.automatic_optimization = False + + def sample_z(self, n) -> Tensor: + sample = self._Z.sample((n,)) + return sample + + def sample_G(self, n) -> Tensor: + z = self.sample_z(n) + return self.G(z) + + def training_step(self, batch, batch_idx): + # Implementation follows the PyTorch tutorial: + # https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html + g_opt, d_opt = self.optimizers() + + X, _ = batch + batch_size = X.shape[0] + + real_label = torch.ones((batch_size, 1), device=self.device) + fake_label = torch.zeros((batch_size, 1), device=self.device) + + g_X = self.sample_G(batch_size) + + ########################## + # Optimize Discriminator # + ########################## + d_x = self.D(X) + errD_real = self.criterion(d_x, real_label) + + d_z = self.D(g_X.detach()) + errD_fake = self.criterion(d_z, fake_label) + + errD = errD_real + errD_fake + + d_opt.zero_grad() + self.manual_backward(errD) + d_opt.step() + + ###################### + # Optimize Generator # + ###################### + d_z = self.D(g_X) + errG = self.criterion(d_z, real_label) + + g_opt.zero_grad() + self.manual_backward(errG) + g_opt.step() + + self.log_dict({"g_loss": errG, "d_loss": errD}, prog_bar=True) + + def configure_optimizers(self): + g_opt = torch.optim.Adam(self.G.parameters(), lr=1e-5) + d_opt = torch.optim.Adam(self.D.parameters(), lr=1e-5) + return g_opt, d_opt + + +Learning Rate Scheduling +======================== + +Every optimizer you use can be paired with any +`Learning Rate Scheduler `_. Please see the +documentation of :meth:`~lightning.pytorch.core.module.LightningModule.configure_optimizers` for all the available options + +You can call ``lr_scheduler.step()`` at arbitrary intervals. +Use ``self.lr_schedulers()`` in your :class:`~lightning.pytorch.core.module.LightningModule` to access any learning rate schedulers +defined in your :meth:`~lightning.pytorch.core.module.LightningModule.configure_optimizers`. + +.. warning:: + * ``lr_scheduler.step()`` can be called at arbitrary intervals by the user in case of manual optimization, or by Lightning if ``"interval"`` is defined in :meth:`~lightning.pytorch.core.module.LightningModule.configure_optimizers` in case of automatic optimization. + * Note that the ``lr_scheduler_config`` keys, such as ``"frequency"`` and ``"interval"``, will be ignored even if they are provided in + your :meth:`~lightning.pytorch.core.module.LightningModule.configure_optimizers` during manual optimization. + +Here is an example calling ``lr_scheduler.step()`` every step. + +.. testcode:: python + + # step every batch + def __init__(self): + super().__init__() + self.automatic_optimization = False + + + def training_step(self, batch, batch_idx): + # do forward, backward, and optimization + ... + + # single scheduler + sch = self.lr_schedulers() + sch.step() + + # multiple schedulers + sch1, sch2 = self.lr_schedulers() + sch1.step() + sch2.step() + +If you want to call ``lr_scheduler.step()`` every ``N`` steps/epochs, do the following. + +.. testcode:: python + + def __init__(self): + super().__init__() + self.automatic_optimization = False + + + def training_step(self, batch, batch_idx): + # do forward, backward, and optimization + ... + + sch = self.lr_schedulers() + + # step every N batches + if (batch_idx + 1) % N == 0: + sch.step() + + # step every N epochs + if self.trainer.is_last_batch and (self.trainer.current_epoch + 1) % N == 0: + sch.step() + +If you want to call schedulers that require a metric value after each epoch, consider doing the following: + +.. testcode:: + + def __init__(self): + super().__init__() + self.automatic_optimization = False + + + def on_train_epoch_end(self): + sch = self.lr_schedulers() + + # If the selected scheduler is a ReduceLROnPlateau scheduler. + if isinstance(sch, torch.optim.lr_scheduler.ReduceLROnPlateau): + sch.step(self.trainer.callback_metrics["loss"]) + + +Optimizer Steps at Different Frequencies +======================================== + +In manual optimization, you are free to ``step()`` one optimizer more often than another one. +For example, here we step the optimizer for the *discriminator* weights twice as often as the optimizer for the *generator*. + +.. testcode:: python + + # Alternating schedule for optimizer steps (e.g. GANs) + def training_step(self, batch, batch_idx): + g_opt, d_opt = self.optimizers() + ... + + # update discriminator every other step + d_opt.zero_grad() + self.manual_backward(errD) + if (batch_idx + 1) % 2 == 0: + d_opt.step() + + ... + + # update generator every step + g_opt.zero_grad() + self.manual_backward(errG) + g_opt.step() + + +Use Closure for LBFGS-like Optimizers +===================================== + +It is a good practice to provide the optimizer with a closure function that performs a ``forward``, ``zero_grad`` and +``backward`` of your model. It is optional for most optimizers, but makes your code compatible if you switch to an +optimizer which requires a closure, such as :class:`~torch.optim.LBFGS`. + +See `the PyTorch docs `_ for more about the closure. + +Here is an example using a closure function. + +.. testcode:: python + + def __init__(self): + super().__init__() + self.automatic_optimization = False + + + def configure_optimizers(self): + return torch.optim.LBFGS(...) + + + def training_step(self, batch, batch_idx): + opt = self.optimizers() + + def closure(): + loss = self.compute_loss(batch) + opt.zero_grad() + self.manual_backward(loss) + return loss + + opt.step(closure=closure) + +.. warning:: + The :class:`~torch.optim.LBFGS` optimizer is not supported for AMP, IPUs, or DeepSpeed. diff --git a/docs/source-pytorch/model/own_your_loop.rst b/docs/source-pytorch/model/own_your_loop.rst new file mode 100644 index 0000000..ad0c889 --- /dev/null +++ b/docs/source-pytorch/model/own_your_loop.rst @@ -0,0 +1,25 @@ +:orphan: + +################################ +Use a pure PyTorch training loop +################################ + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Enable manual optimization + :description: Gain control of the training loop with manual optimization and LightningModule methods. + :col_css: col-md-4 + :button_link: build_model_advanced.html + :height: 150 + :tag: advanced + +.. raw:: html + +
+
diff --git a/source/model/train_model_basic.rst b/docs/source-pytorch/model/train_model_basic.rst similarity index 91% rename from source/model/train_model_basic.rst rename to docs/source-pytorch/model/train_model_basic.rst index 24bdab8..028734e 100644 --- a/source/model/train_model_basic.rst +++ b/docs/source-pytorch/model/train_model_basic.rst @@ -20,8 +20,8 @@ Add the relevant imports at the top of the file import torch.nn.functional as F from torchvision import transforms from torchvision.datasets import MNIST - from torch.utils.data import DataLoader, random_split - import pytorch_lightning as pl + from torch.utils.data import DataLoader + import lightning.pytorch as pl ---- @@ -33,6 +33,7 @@ Define the PyTorch nn.Modules class Encoder(nn.Module): def __init__(self): + super().__init__() self.l1 = nn.Sequential(nn.Linear(28 * 28, 64), nn.ReLU(), nn.Linear(64, 3)) def forward(self, x): @@ -41,6 +42,7 @@ Define the PyTorch nn.Modules class Decoder(nn.Module): def __init__(self): + super().__init__() self.l1 = nn.Sequential(nn.Linear(3, 64), nn.ReLU(), nn.Linear(64, 28 * 28)) def forward(self, x): @@ -114,11 +116,11 @@ Under the hood, the Lightning Trainer runs the following training loop on your b .. code:: python - autoencoder = LitAutoEncoder(encoder, decoder) + autoencoder = LitAutoEncoder(Encoder(), Decoder()) optimizer = autoencoder.configure_optimizers() - for batch, batch_idx in enumerate(train_loader): - loss = autoencoder(batch, batch_idx) + for batch_idx, batch in enumerate(train_loader): + loss = autoencoder.training_step(batch, batch_idx) loss.backward() optimizer.step() diff --git a/docs/source-pytorch/notebooks.rst b/docs/source-pytorch/notebooks.rst new file mode 100644 index 0000000..1fcafec --- /dev/null +++ b/docs/source-pytorch/notebooks.rst @@ -0,0 +1,12 @@ +:orphan: + +PyTorch Lightning Tutorials +=========================== + +.. toctree:: + :maxdepth: 1 + :name: Notebooks + :caption: Notebooks + :glob: + + notebooks/**/* diff --git a/docs/source-pytorch/past_versions.rst b/docs/source-pytorch/past_versions.rst new file mode 100644 index 0000000..597e2c1 --- /dev/null +++ b/docs/source-pytorch/past_versions.rst @@ -0,0 +1,141 @@ +Past PyTorch Lightning versions +=============================== + +PyTorch Lightning :doc:`evolved over time `. Here's the history of versions with links to their respective docs. + +To help you with keeping up to spead, check :doc:`Migration guide `. + +.. list-table:: Past versions + :widths: 5 50 30 15 + :header-rows: 1 + + * - Version + - Title + - Bug-fix versions + - Upgrade guide + + * - `1.9 `_ + - `Stability and additional improvements `_ + - `1.9.0 `_, + `1.9.1 `_, + `1.9.2 `_, + `1.9.3 `_, + `1.9.4 `_, + `1.9.5 `_ + - :doc:`from 1.9 to 2.0 ` + + * - `1.8 `_ + - `Colossal-AI Strategy, Commands and Secrets for Apps, FSDP Improvements and More! `_ + - `1.8.0 `_, + `1.8.1 `_, + `1.8.2 `_, + `1.8.3 `_, + `1.8.4 `_, + `1.8.5 `_, + `1.8.6 `_ + - :doc:`from 1.8 to 2.0 ` + + * - `1.7 `_ + - `Apple Silicon support, Native FSDP, Collaborative training, and multi-GPU support with Jupyter notebooks `_ + - `1.7.0 `_, + `1.7.1 `_, + `1.7.2 `_, + `1.7.3 `_, + `1.7.4 `_, + `1.7.5 `_, + `1.7.6 `_, + `1.7.7 `_ + - :doc:`from 1.7 to 2.0 ` + + * - `1.6 `_ + - `Support Intel's Habana Accelerator, New efficient DDP strategy (Bagua), Manual Fault-tolerance, Stability and Reliability `_ + - `1.6.0 `_, + `1.6.1 `_, + `1.6.2 `_, + `1.6.3 `_, + `1.6.4 `_, + `1.6.5 `_ + - :doc:`from 1.6 to 2.0 ` + + * - `1.5 `_ + - `LightningLite, Fault-Tolerant Training, Loop Customization, Lightning Tutorials, LightningCLI v2, RichProgressBar, CheckpointIO Plugin, and Trainer Strategy Flag `_ + - `1.5.0 `_, + `1.5.1 `_, + `1.5.2 `_, + `1.5.3 `_, + `1.5.4 `_, + `1.5.5 `_, + `1.5.6 `_, + `1.5.7 `_, + `1.5.8 `_, + `1.5.9 `_, + `1.5.10 `_ + - :doc:`from 1.5 to 2.0 ` + + * - `1.4 `_ + - `TPU Pod Training, IPU Accelerator, DeepSpeed Infinity, Fully Sharded Data Parallel `_ + - `1.4.0 `_, + `1.4.1 `_, + `1.4.2 `_, + `1.4.3 `_, + `1.4.4 `_, + `1.4.5 `_, + `1.4.6 `_, + `1.4.7 `_, + `1.4.8 `_, + `1.4.9 `_ + - :doc:`from 1.4 to 2.0 ` + + * - `1.3 `_ + - `Lightning CLI, PyTorch Profiler, Improved Early Stopping `_ + - `1.3.0 `_, + `1.3.1 `_, + `1.3.2 `_, + `1.3.3 `_, + `1.3.4 `_, + `1.3.5 `_, + `1.3.6 `_, + `1.3.7 `_, + `1.3.8 `_ + - + + * - `1.2 `_ + - `Pruning & Quantization & SWA `_ + - `1.2.0 `_, + `1.2.1 `_, + `1.2.2 `_, + `1.2.3 `_, + `1.2.4 `_, + `1.2.5 `_, + `1.2.6 `_, + `1.2.7 `_, + `1.2.8 `_, + `1.2.9 `_, + `1.2.10 `_ + - + + * - `1.1 `_ + - `Model Parallelism Training and More Logging Options `_ + - `1.1.0 `_, + `1.1.1 `_, + `1.1.2 `_, + `1.1.3 `_, + `1.1.4 `_, + `1.1.5 `_, + `1.1.6 `_, + `1.1.7 `_, + `1.1.8 `_ + - + + * - `1.0 `_ + - `General availability `_ + - `1.0.0 `_, + `1.0.1 `_, + `1.0.2 `_, + `1.0.3 `_, + `1.0.4 `_, + `1.0.5 `_, + `1.0.6 `_, + `1.0.7 `_, + `1.0.8 `_ + - diff --git a/source/starter/converting.rst b/docs/source-pytorch/starter/converting.rst similarity index 92% rename from source/starter/converting.rst rename to docs/source-pytorch/starter/converting.rst index 9e664d6..1e0d179 100644 --- a/source/starter/converting.rst +++ b/docs/source-pytorch/starter/converting.rst @@ -8,15 +8,15 @@ PyTorch를 Lightning으로 구성하기 -------- -****************************** +******************************* 1. 연산 코드 가져오기 -****************************** +******************************* 일반적인 nn.Module 구조를 가져옵니다 .. testcode:: - import pytorch_lightning as pl + import lightning.pytorch as pl import torch import torch.nn as nn import torch.nn.functional as F @@ -37,9 +37,9 @@ PyTorch를 Lightning으로 구성하기 -------- -*************************** +******************************** 2. 학습 로직 구성하기 -*************************** +******************************** LightningModule의 training_step에 학습 데이터를 묶음(batch)으로 가져와 학습하는 과정을 구성합니다: .. testcode:: @@ -59,9 +59,9 @@ LightningModule의 training_step에 학습 데이터를 묶음(batch)으로 가 ---- -**************************************** -3. 옵티마이저와 LR스케줄러 이동하기 -**************************************** +************************************************** +3. Move Optimizer(s) and LR Scheduler(s) +************************************************** 옵티마이저(들)를 :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers` 훅(hook)으로 이동합니다. .. testcode:: @@ -92,9 +92,9 @@ LightningModule의 training_step에 학습 데이터를 묶음(batch)으로 가 -------- -************************************ +***************************************** 5. (선택사항) 테스트 로직 구성하기 -************************************ +***************************************** 테스트(test) 루프가 필요하면, 테스트 데이터를 묶음(batch)으로 가져와 테스트하는 과정을 구성합니다: .. testcode:: @@ -127,8 +127,6 @@ LightningModule의 training_step에 학습 데이터를 묶음(batch)으로 가 7. .cuda() 또는 .to(device) 호출 제거하기 ****************************************** -:doc:`LightningModule <../common/lightning_module>` 은 어떠한 하드웨어에서도 자동으로 실행됩니다! - ``LightningModule.__init__`` 내에서 초기화된 :class:`~torch.nn.Module` 인스턴스들과 :class:`~torch.utils.data.DataLoader` 에서 가져온 데이터는 Lightning이 자동으로 해당 장치로 이동해서 실행하므로, 기존에 명시적으로 ``.cuda()`` 또는 ``.to(device)`` 을 호출하는 부분은 제거해도 됩니다. @@ -154,17 +152,16 @@ Hint: ``LightningModule.__init__`` 메소드 내에서 :class:`~torch.Tensor` -------- -************************* +****************************** 8. 기존 데이터 사용하기 -************************* +****************************** 일반적인 PyTorch DataLoader는 Lightning에서 동작합니다. 더 모듈화되고 확장 가능한 데이터셋들은 :doc:`LightningDataModule <../data/datamodule>` 를 참고하세요. - ---- -************ +********************** 더 알아두기 -************ +********************** 추가로, :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate` 메소드를 사용하면 검증(validation) 루프만 실행할 수 있습니다. @@ -175,7 +172,6 @@ Hint: ``LightningModule.__init__`` 메소드 내에서 :class:`~torch.Tensor` .. note:: ``model.eval()`` 와 ``torch.no_grad()`` 는 검증 시에 자동으로 호출됩니다. - 테스트 루프(test loop)는 :meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` 에서 사용되지 않으므로, 필요 시 명시적으로 :meth:`~pytorch_lightning.trainer.trainer.Trainer.test` 을 호출해야 합니다. diff --git a/docs/source-pytorch/starter/installation.rst b/docs/source-pytorch/starter/installation.rst new file mode 100644 index 0000000..4d34f87 --- /dev/null +++ b/docs/source-pytorch/starter/installation.rst @@ -0,0 +1,91 @@ +:orphan: + +.. _installation: + +############ +설치하기 +############ + +********************* +pip를 사용하여 설치 +********************* + +라이트닝(lightning)을 가상환경이나 conda 환경에서 pip로 설치할 수 있습니다 + +.. code-block:: bash + + python -m pip install lightning + +-------------- + +*********************** +Conda를 사용하여 설치 +*********************** + +만약 conda를 아직 설치하지 않았다면, `Conda 설치 가이드 `_ 를 참고하세요. +Lightning은 아래 명령어로 `conda `_ 를 사용하여 설치할 수 있습니다: + +.. code-block:: bash + + conda install lightning -c conda-forge + +`Conda 가상환경(Environments) `_ 을 사용할 수도 있습니다: + +.. code-block:: bash + + conda activate my_env + conda install lightning -c conda-forge + +---- + +GRPC 패키지를 가져오는 데 어려움이 있는 경우 `이 글 `_ 을 따라해보세요. + + + +---- + +********************** +소스 코드에서 설치 +********************** + +소스 코드로 최신 버전(nightly)을 설치합니다. 아직 배포되지 않은 버그 수정(bug fix)과 새롭게 출시할 기능들이 +포함되어 있습니다. 미검증·불안정 최신 기능(bleeding edge)이므로, 신중하게 사용하세요. + +.. code-block:: bash + + pip install https://github.com/Lightning-AI/lightning/archive/refs/heads/master.zip -U + +향후 공개될 개선 버전(patch release)를 소스 코드로부터 설치합니다. 개선 버전은 가장 최근의 주요 버전(major release)에 대한 버그 수정만 +포함되어 있습니다. + +.. code-block:: bash + + pip install https://github.com/Lightning-AI/lightning/archive/refs/heads/release/stable.zip -U + +---- + +******************************* +모델 개발에 최적화된 버전 설치 +******************************* +이미 Lightning으로 개발한 모델을 배포하기 위해 최소한의 의존성만을 필요로 하는 경우, 최적화된 `lightning[pytorch]` 패키지를 설치하세요: + +.. code-block:: bash + + pip install 'lightning[pytorch]' + +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +PyTorch 버전 지정하기 +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +특정한 PyTorch 버전을 사용하려면 `PyTorch 설치 페이지 `_ 를 참고하세요. + +---- + + +***************************************************** +ML 워크플로우에 최적화된 버전 설치하기 (lightning Apps) +***************************************************** +이미 Lightning으로 개발한 워크플로우를 배포하기 위해 최소한의 의존성만을 필요로 하는 경우, 최적화된 `lightning[apps]` 패키지를 설치하세요: + +.. code-block:: bash + + pip install lightning-app diff --git a/docs/source-pytorch/starter/introduction.rst b/docs/source-pytorch/starter/introduction.rst new file mode 100644 index 0000000..e6d5440 --- /dev/null +++ b/docs/source-pytorch/starter/introduction.rst @@ -0,0 +1,371 @@ +:orphan: + +####################### +Lightning in 15 minutes +####################### +**Required background:** None + +**Goal:** In this guide, we'll walk you through the 7 key steps of a typical Lightning workflow. + +PyTorch Lightning is the deep learning framework with "batteries included" for professional AI researchers and machine learning engineers who need maximal flexibility while super-charging performance at scale. + +Lightning organizes PyTorch code to remove boilerplate and unlock scalability. + +.. video:: ../_static/fetched-s3-assets/pl_readme_gif_2_0.mp4 + :width: 800 + :autoplay: + :loop: + :muted: + +By organizing PyTorch code, lightning enables: + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Full flexibility + :description: Try any ideas using raw PyTorch without the boilerplate. + :col_css: col-md-3 + :image_center: ../_static/fetched-s3-assets/card_full_control.png + :height: 290 + +.. displayitem:: + :description: Decoupled research and engineering code enable reproducibility and better readability. + :header: Reproducible + Readable + :col_css: col-md-3 + :image_center: ../_static/fetched-s3-assets/card_no_boilerplate.png + :height: 290 + +.. displayitem:: + :description: Use multiple GPUs/TPUs/HPUs etc... without code changes. + :header: Simple multi-GPU training + :col_css: col-md-3 + :image_center: ../_static/fetched-s3-assets/card_hardware.png + :height: 290 + +.. displayitem:: + :description: We've done all the testing so you don't have to. + :header: Built-in testing + :col_css: col-md-3 + :image_center: ../_static/fetched-s3-assets/card_testing.png + :height: 290 + +.. raw:: html + +
+
+ +.. End of callout item section + +---- + +**************************** +1: Install PyTorch Lightning +**************************** +.. raw:: html + +
+
+ +For `pip `_ users + +.. code-block:: bash + + pip install lightning + +.. raw:: html + +
+
+ +For `conda `_ users + +.. code-block:: bash + + conda install lightning -c conda-forge + +.. raw:: html + +
+
+ +Or read the `advanced install guide `_ + +---- + +.. _new_project: + +*************************** +2: Define a LightningModule +*************************** + +A LightningModule enables your PyTorch nn.Module to play together in complex ways inside the training_step (there is also an optional validation_step and test_step). + +.. testcode:: + :skipif: not _TORCHVISION_AVAILABLE + + import os + from torch import optim, nn, utils, Tensor + from torchvision.datasets import MNIST + from torchvision.transforms import ToTensor + import lightning.pytorch as pl + + # define any number of nn.Modules (or use your current ones) + encoder = nn.Sequential(nn.Linear(28 * 28, 64), nn.ReLU(), nn.Linear(64, 3)) + decoder = nn.Sequential(nn.Linear(3, 64), nn.ReLU(), nn.Linear(64, 28 * 28)) + + + # define the LightningModule + class LitAutoEncoder(pl.LightningModule): + def __init__(self, encoder, decoder): + super().__init__() + self.encoder = encoder + self.decoder = decoder + + def training_step(self, batch, batch_idx): + # training_step defines the train loop. + # it is independent of forward + x, y = batch + x = x.view(x.size(0), -1) + z = self.encoder(x) + x_hat = self.decoder(z) + loss = nn.functional.mse_loss(x_hat, x) + # Logging to TensorBoard (if installed) by default + self.log("train_loss", loss) + return loss + + def configure_optimizers(self): + optimizer = optim.Adam(self.parameters(), lr=1e-3) + return optimizer + + + # init the autoencoder + autoencoder = LitAutoEncoder(encoder, decoder) + +---- + +******************* +3: Define a dataset +******************* + +Lightning supports ANY iterable (:class:`~torch.utils.data.DataLoader`, numpy, etc...) for the train/val/test/predict splits. + +.. code-block:: python + + # setup data + dataset = MNIST(os.getcwd(), download=True, transform=ToTensor()) + train_loader = utils.data.DataLoader(dataset) + +---- + +****************** +4: Train the model +****************** + +The Lightning :doc:`Trainer <../common/trainer>` "mixes" any :doc:`LightningModule <../common/lightning_module>` with any dataset and abstracts away all the engineering complexity needed for scale. + +.. code-block:: python + + # train the model (hint: here are some helpful Trainer arguments for rapid idea iteration) + trainer = pl.Trainer(limit_train_batches=100, max_epochs=1) + trainer.fit(model=autoencoder, train_dataloaders=train_loader) + +The Lightning :doc:`Trainer <../common/trainer>` automates `40+ tricks <../common/trainer.html#trainer-flags>`_ including: + +* Epoch and batch iteration +* ``optimizer.step()``, ``loss.backward()``, ``optimizer.zero_grad()`` calls +* Calling of ``model.eval()``, enabling/disabling grads during evaluation +* :doc:`Checkpoint Saving and Loading <../common/checkpointing>` +* Tensorboard (see :doc:`loggers <../visualize/loggers>` options) +* :doc:`Multi-GPU <../accelerators/gpu>` support +* :doc:`TPU <../accelerators/tpu>` +* :ref:`16-bit precision AMP ` support + +---- + + +**************** +5: Use the model +**************** +Once you've trained the model you can export to onnx, torchscript and put it into production or simply load the weights and run predictions. + +.. code:: python + + # load checkpoint + checkpoint = "./lightning_logs/version_0/checkpoints/epoch=0-step=100.ckpt" + autoencoder = LitAutoEncoder.load_from_checkpoint(checkpoint, encoder=encoder, decoder=decoder) + + # choose your trained nn.Module + encoder = autoencoder.encoder + encoder.eval() + + # embed 4 fake images! + fake_image_batch = torch.rand(4, 28 * 28, device=autoencoder.device) + embeddings = encoder(fake_image_batch) + print("⚡" * 20, "\nPredictions (4 image embeddings):\n", embeddings, "\n", "⚡" * 20) + +---- + +********************* +6: Visualize training +********************* +If you have tensorboard installed, you can use it for visualizing experiments. + +Run this on your commandline and open your browser to **http://localhost:6006/** + +.. code:: bash + + tensorboard --logdir . + +---- + +*********************** +7: Supercharge training +*********************** +Enable advanced training features using Trainer arguments. These are state-of-the-art techniques that are automatically integrated into your training loop without changes to your code. + +.. code:: + + # train on 4 GPUs + trainer = Trainer( + devices=4, + accelerator="gpu", + ) + + # train 1TB+ parameter models with Deepspeed/fsdp + trainer = Trainer( + devices=4, + accelerator="gpu", + strategy="deepspeed_stage_2", + precision=16 + ) + + # 20+ helpful flags for rapid idea iteration + trainer = Trainer( + max_epochs=10, + min_epochs=5, + overfit_batches=1 + ) + + # access the latest state of the art techniques + trainer = Trainer(callbacks=[StochasticWeightAveraging(...)]) + +---- + +******************** +Maximize flexibility +******************** +Lightning's core guiding principle is to always provide maximal flexibility **without ever hiding any of the PyTorch**. + +Lightning offers 5 *added* degrees of flexibility depending on your project's complexity. + +---- + +Customize training loop +======================= + +.. image:: ../_static/fetched-s3-assets/custom_loop.png + :width: 600 + :alt: Injecting custom code in a training loop + +Inject custom code anywhere in the Training loop using any of the 20+ methods (:ref:`lightning_hooks`) available in the LightningModule. + +.. testcode:: + + class LitAutoEncoder(pl.LightningModule): + def backward(self, loss): + loss.backward() + +---- + +Extend the Trainer +================== + +.. video:: ../_static/fetched-s3-assets/cb.mp4 + :width: 600 + :autoplay: + :loop: + :muted: + +If you have multiple lines of code with similar functionalities, you can use callbacks to easily group them together and toggle all of those lines on or off at the same time. + +.. code:: + + trainer = Trainer(callbacks=[AWSCheckpoints()]) + +---- + +Use a raw PyTorch loop +====================== + +For certain types of work at the bleeding-edge of research, Lightning offers experts full control of optimization or the training loop in various ways. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Manual optimization + :description: Automated training loop, but you own the optimization steps. + :col_css: col-md-4 + :image_center: ../_static/fetched-s3-assets/manual_opt.png + :button_link: ../model/build_model_advanced.html#manual-optimization + :image_height: 220px + :height: 320 + +.. raw:: html + +
+
+ +.. End of callout item section + +---- + +********** +Next steps +********** +Depending on your use case, you might want to check one of these out next. + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: Level 2: Add a validation and test set + :description: Add validation and test sets to avoid over/underfitting. + :button_link: ../levels/basic_level_2.html + :col_css: col-md-3 + :height: 180 + :tag: basic + +.. displayitem:: + :header: See more examples + :description: See examples across computer vision, NLP, RL, etc... + :col_css: col-md-3 + :button_link: ../tutorials.html + :height: 180 + :tag: basic + +.. displayitem:: + :header: Deploy your model + :description: Learn how to predict or put your model into production + :col_css: col-md-3 + :button_link: ../deploy/production.html + :height: 180 + :tag: basic + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/starter/style_guide.rst b/docs/source-pytorch/starter/style_guide.rst new file mode 100644 index 0000000..7bfc959 --- /dev/null +++ b/docs/source-pytorch/starter/style_guide.rst @@ -0,0 +1,223 @@ +########### +Style Guide +########### +The main goal of PyTorch Lightning is to improve readability and reproducibility. Imagine looking into any GitHub repo or a research project, +finding a :class:`~lightning.pytorch.core.module.LightningModule`, and knowing exactly where to look to find the things you care about. + +The goal of this style guide is to encourage Lightning code to be structured similarly. + +-------------- + +*************** +LightningModule +*************** + +These are best practices for structuring your :class:`~lightning.pytorch.core.module.LightningModule` class: + +Systems vs Models +================= + +.. figure:: ../_static/fetched-s3-assets/model_system.png + :width: 400 + +The main principle behind a LightningModule is that a full system should be self-contained. +In Lightning, we differentiate between a system and a model. + +A model is something like a resnet18, RNN, and so on. + +A system defines how a collection of models interact with each other with user-defined training/evaluation logic. Examples of this are: + +* GANs +* Seq2Seq +* BERT +* etc. + +A LightningModule can define both a system and a model: + +Here's a LightningModule that defines a system. This structure is what we recommend as a best practice. Keeping the model separate from the system improves +modularity, which eventually helps in better testing, reduces dependencies on the system and makes it easier to refactor. + +.. testcode:: + + class Encoder(nn.Module): + ... + + + class Decoder(nn.Module): + ... + + + class AutoEncoder(nn.Module): + def __init__(self): + super().__init__() + self.encoder = Encoder() + self.decoder = Decoder() + + def forward(self, x): + return self.encoder(x) + + + class AutoEncoderSystem(LightningModule): + def __init__(self): + super().__init__() + self.auto_encoder = AutoEncoder() + + +For fast prototyping, it's often useful to define all the computations in a LightningModule. For reusability +and scalability, it might be better to pass in the relevant backbones. + +Here's a LightningModule that defines a model. Although, we do not recommend to define a model like in the example. + +.. testcode:: + + class LitModel(LightningModule): + def __init__(self): + super().__init__() + self.layer_1 = nn.Linear() + self.layer_2 = nn.Linear() + self.layer_3 = nn.Linear() + + +Self-contained +============== + +A Lightning module should be self-contained. To see how self-contained your model is, a good test is to ask +yourself this question: + +"Can someone drop this file into a Trainer without knowing anything about the internals?" + +For example, we couple the optimizer with a model because the majority of models require a specific optimizer with +a specific learning rate scheduler to work well. + +Init +==== +The first place where LightningModules tend to stop being self-contained is in the init. Try to define all the relevant +sensible defaults in the init so that the user doesn't have to guess. + +Here's an example where a user will have to go hunt through files to figure out how to init this LightningModule. + +.. testcode:: + + class LitModel(LightningModule): + def __init__(self, params): + self.lr = params.lr + self.coef_x = params.coef_x + +Models defined as such leave you with many questions, such as what is ``coef_x``? Is it a string? A float? What is the range? +Instead, be explicit in your init + +.. testcode:: + + class LitModel(LightningModule): + def __init__(self, encoder: nn.Module, coef_x: float = 0.2, lr: float = 1e-3): + ... + +Now the user doesn't have to guess. Instead, they know the value type, and the model has a sensible default where the +user can see the value immediately. + + +Method Order +============ +The only required methods in the LightningModule are: + +* init +* training_step +* configure_optimizers + +However, if you decide to implement the rest of the optional methods, the recommended order is: + +* model/system definition (init) +* if doing inference, define forward +* training hooks +* validation hooks +* test hooks +* predict hooks +* configure_optimizers +* any other hooks + +In practice, the code looks like this: + +.. code-block:: + + class LitModel(pl.LightningModule): + + def __init__(...): + + def forward(...): + + def training_step(...): + + def on_train_epoch_end(...): + + def validation_step(...): + + def on_validation_epoch_end(...): + + def test_step(...): + + def on_test_epoch_end(...): + + def configure_optimizers(...): + + def any_extra_hook(...): + + +Forward vs training_step +======================== + +We recommend using :meth:`~lightning.pytorch.core.module.LightningModule.forward` for inference/predictions and keeping +:meth:`~lightning.pytorch.core.module.LightningModule.training_step` independent. + +.. code-block:: python + + def forward(self, x): + embeddings = self.encoder(x) + return embeddings + + + def training_step(self, batch, batch_idx): + x, _ = batch + z = self.encoder(x) + pred = self.decoder(z) + ... + + +-------------- + +**** +Data +**** + +These are best practices for handling data. + +DataLoaders +=========== + +Lightning uses :class:`~torch.utils.data.DataLoader` to handle all the data flow through the system. Whenever you structure dataloaders, +make sure to tune the number of workers for maximum efficiency. + + +DataModules +=========== + +The :class:`~lightning.pytorch.core.datamodule.LightningDataModule` is designed as a way of decoupling data-related +hooks from the :class:`~lightning.pytorch.core.module.LightningModule` so you can develop dataset agnostic models. It makes it easy to hot swap different +datasets with your model, so you can test it and benchmark it across domains. It also makes sharing and reusing the exact data splits and transforms across projects possible. + +Check out :ref:`data` document to understand data management within Lightning and its best practices. + +* What dataset splits were used? +* How many samples does this dataset have overall and within each split? +* Which transforms were used? + +It's for this reason that we recommend you use datamodules. This is especially important when collaborating because +it will save your team a lot of time as well. + +All they need to do is drop a datamodule into the Trainer and not worry about what was done to the data. + +This is true for both academic and corporate settings where data cleaning and ad-hoc instructions slow down the progress +of iterating through ideas. + +- Checkout the live examples to get your hands dirty: +- `Introduction to PyTorch Lightning `_ +- `Introduction to DataModules `_ diff --git a/source/tuning/profiler.rst b/docs/source-pytorch/tuning/profiler.rst similarity index 100% rename from source/tuning/profiler.rst rename to docs/source-pytorch/tuning/profiler.rst diff --git a/source/tuning/profiler_advanced.rst b/docs/source-pytorch/tuning/profiler_advanced.rst similarity index 93% rename from source/tuning/profiler_advanced.rst rename to docs/source-pytorch/tuning/profiler_advanced.rst index ad2ab9e..63a0013 100644 --- a/source/tuning/profiler_advanced.rst +++ b/docs/source-pytorch/tuning/profiler_advanced.rst @@ -12,11 +12,11 @@ Find bottlenecks in your code (advanced) ************************ Profile cloud TPU models ************************ -To profile TPU models use the :class:`~pytorch_lightning.profiler.xla.XLAProfiler` +To profile TPU models use the :class:`~lightning.pytorch.profilers.xla.XLAProfiler` .. code-block:: python - from pytorch_lightning.profiler import XLAProfiler + from lightning.pytorch.profilers import XLAProfiler profiler = XLAProfiler(port=9001) trainer = Trainer(profiler=profiler) diff --git a/source/tuning/profiler_basic.rst b/docs/source-pytorch/tuning/profiler_basic.rst similarity index 88% rename from source/tuning/profiler_basic.rst rename to docs/source-pytorch/tuning/profiler_basic.rst index 899e657..d248cc6 100644 --- a/source/tuning/profiler_basic.rst +++ b/docs/source-pytorch/tuning/profiler_basic.rst @@ -59,7 +59,6 @@ The simple profiler measures all the standard methods used in the training loop - on_after_backward - optimizer_step - on_train_batch_end -- training_step_end - on_training_end - etc... @@ -68,7 +67,7 @@ The simple profiler measures all the standard methods used in the training loop ************************************** Profile the time within every function ************************************** -To profile the time within every function, use the :class:`~pytorch_lightning.profiler.advanced.AdvancedProfiler` built on top of Python's `cProfiler `_. +To profile the time within every function, use the :class:`~lightning.pytorch.profilers.advanced.AdvancedProfiler` built on top of Python's `cProfiler `_. .. code-block:: python @@ -101,7 +100,7 @@ If the profiler report becomes too long, you can stream the report to a file: .. code-block:: python - from pytorch_lightning.profiler import AdvancedProfiler + from lightning.pytorch.profilers import AdvancedProfiler profiler = AdvancedProfiler(dirpath=".", filename="perf_logs") trainer = Trainer(profiler=profiler) @@ -112,10 +111,13 @@ If the profiler report becomes too long, you can stream the report to a file: Measure accelerator usage ************************* Another helpful technique to detect bottlenecks is to ensure that you're using the full capacity of your accelerator (GPU/TPU/IPU/HPU). -This can be measured with the :class:`~pytorch_lightning.callbacks.device_stats_monitor.DeviceStatsMonitor`: +This can be measured with the :class:`~lightning.pytorch.callbacks.device_stats_monitor.DeviceStatsMonitor`: .. testcode:: - from pytorch_lightning.callbacks import DeviceStatsMonitor + from lightning.pytorch.callbacks import DeviceStatsMonitor trainer = Trainer(callbacks=[DeviceStatsMonitor()]) + +CPU metrics will be tracked by default on the CPU accelerator. To enable it for other accelerators set ``DeviceStatsMonitor(cpu_stats=True)``. To disable logging +CPU metrics, you can specify ``DeviceStatsMonitor(cpu_stats=False)``. diff --git a/source/tuning/profiler_expert.rst b/docs/source-pytorch/tuning/profiler_expert.rst similarity index 90% rename from source/tuning/profiler_expert.rst rename to docs/source-pytorch/tuning/profiler_expert.rst index 64ff784..a64c0fa 100644 --- a/source/tuning/profiler_expert.rst +++ b/docs/source-pytorch/tuning/profiler_expert.rst @@ -12,12 +12,12 @@ Find bottlenecks in your code (expert) *********************** Build your own profiler *********************** -To build your own profiler, subclass :class:`~pytorch_lightning.profiler.base.Profiler` +To build your own profiler, subclass :class:`~lightning.pytorch.profilers.profiler.Profiler` and override some of its methods. Here is a simple example that profiles the first occurrence and total calls of each action: .. code-block:: python - from pytorch_lightning.profiler import Profiler + from lightning.pytorch.profilers import Profiler from collections import defaultdict import time @@ -69,7 +69,7 @@ To profile a specific action of interest, reference a profiler in the LightningM .. code-block:: python - from pytorch_lightning.profiler import SimpleProfiler, PassThroughProfiler + from lightning.pytorch.profilers import SimpleProfiler, PassThroughProfiler class MyModel(LightningModule): @@ -90,7 +90,7 @@ Here's the full code: .. code-block:: python - from pytorch_lightning.profiler import SimpleProfiler, PassThroughProfiler + from lightning.pytorch.profilers import SimpleProfiler, PassThroughProfiler class MyModel(LightningModule): diff --git a/source/tuning/profiler_intermediate.rst b/docs/source-pytorch/tuning/profiler_intermediate.rst similarity index 92% rename from source/tuning/profiler_intermediate.rst rename to docs/source-pytorch/tuning/profiler_intermediate.rst index d2b64b5..802bfc5 100644 --- a/source/tuning/profiler_intermediate.rst +++ b/docs/source-pytorch/tuning/profiler_intermediate.rst @@ -12,11 +12,11 @@ Find bottlenecks in your code (intermediate) ************************** Profile pytorch operations ************************** -To understand the cost of each PyTorch operation, use the :class:`~pytorch_lightning.profiler.pytorch.PyTorchProfiler` built on top of the `PyTorch profiler `__. +To understand the cost of each PyTorch operation, use the :class:`~lightning.pytorch.profilers.pytorch.PyTorchProfiler` built on top of the `PyTorch profiler `__. .. code-block:: python - from pytorch_lightning.profiler import PyTorchProfiler + from lightning.pytorch.profilers import PyTorchProfiler profiler = PyTorchProfiler() trainer = Trainer(profiler=profiler) @@ -65,11 +65,11 @@ The profiler will generate an output like this: *************************** Profile a distributed model *************************** -To profile a distributed model, use the :class:`~pytorch_lightning.profiler.pytorch.PyTorchProfiler` with the *filename* argument which will save a report per rank. +To profile a distributed model, use the :class:`~lightning.pytorch.profilers.pytorch.PyTorchProfiler` with the *filename* argument which will save a report per rank. .. code-block:: python - from pytorch_lightning.profiler import PyTorchProfiler + from lightning.pytorch.profilers import PyTorchProfiler profiler = PyTorchProfiler(filename="perf-logs") trainer = Trainer(profiler=profiler) @@ -138,9 +138,8 @@ With two ranks, it will generate a report like so: --------------------- --------------- --------------- --------------- --------------- --------------- Self CPU time total: 1.681ms -This profiler will record ``training_step``, ``backward``, ``validation_step``, ``test_step``, and ``predict_step`` by default. -The output below shows the profiling for the action ``training_step``. The user can provide ``PyTorchProfiler(record_functions={...})`` -to extend the scope of profiled functions. +This profiler will record ``training_step``, ``validation_step``, ``test_step``, and ``predict_step``. +The output above shows the profiling for the action ``training_step``. .. note:: When using the PyTorch Profiler, wall clock time will not not be representative of the true wall clock time. @@ -153,11 +152,11 @@ to extend the scope of profiled functions. ***************************** Visualize profiled operations ***************************** -To visualize the profiled operations, enable **emit_nvtx** in the :class:`~pytorch_lightning.profiler.pytorch.PyTorchProfiler`. +To visualize the profiled operations, enable **emit_nvtx** in the :class:`~lightning.pytorch.profilers.pytorch.PyTorchProfiler`. .. code-block:: python - from pytorch_lightning.profiler import PyTorchProfiler + from lightning.pytorch.profilers import PyTorchProfiler profiler = PyTorchProfiler(emit_nvtx=True) trainer = Trainer(profiler=profiler) diff --git a/source/tutorials.rst b/docs/source-pytorch/tutorials.rst similarity index 88% rename from source/tutorials.rst rename to docs/source-pytorch/tutorials.rst index 67b1abb..1d69b07 100644 --- a/source/tutorials.rst +++ b/docs/source-pytorch/tutorials.rst @@ -1,5 +1,3 @@ -:orphan: - PyTorch Lightning Tutorials =========================== diff --git a/docs/source-pytorch/upgrade/from_1_4.rst b/docs/source-pytorch/upgrade/from_1_4.rst new file mode 100644 index 0000000..d0117be --- /dev/null +++ b/docs/source-pytorch/upgrade/from_1_4.rst @@ -0,0 +1,34 @@ +:orphan: + +Upgrade from 1.4 to the 2.0 +########################### + +Regular User +************ + +.. include:: sections/1_4_regular.rst +.. include:: sections/1_5_regular.rst +.. include:: sections/1_6_regular.rst +.. include:: sections/1_7_regular.rst +.. include:: sections/1_8_regular.rst +.. include:: sections/1_9_regular.rst + +Advanced User +************* + +.. include:: sections/1_4_advanced.rst +.. include:: sections/1_5_advanced.rst +.. include:: sections/1_6_advanced.rst +.. include:: sections/1_7_advanced.rst +.. include:: sections/1_8_advanced.rst +.. include:: sections/1_9_advanced.rst + +Developer +********* + +.. include:: sections/1_4_devel.rst +.. include:: sections/1_5_devel.rst +.. include:: sections/1_6_devel.rst +.. include:: sections/1_7_devel.rst +.. include:: sections/1_8_devel.rst +.. include:: sections/1_9_devel.rst diff --git a/docs/source-pytorch/upgrade/from_1_5.rst b/docs/source-pytorch/upgrade/from_1_5.rst new file mode 100644 index 0000000..16d13e6 --- /dev/null +++ b/docs/source-pytorch/upgrade/from_1_5.rst @@ -0,0 +1,31 @@ +:orphan: + +Upgrade from 1.5 to the 2.0 +########################### + +Regular User +************ + +.. include:: sections/1_5_regular.rst +.. include:: sections/1_6_regular.rst +.. include:: sections/1_7_regular.rst +.. include:: sections/1_8_regular.rst +.. include:: sections/1_9_regular.rst + +Advanced User +************* + +.. include:: sections/1_5_advanced.rst +.. include:: sections/1_6_advanced.rst +.. include:: sections/1_7_advanced.rst +.. include:: sections/1_8_advanced.rst +.. include:: sections/1_9_advanced.rst + +Developer +********* + +.. include:: sections/1_5_devel.rst +.. include:: sections/1_6_devel.rst +.. include:: sections/1_7_devel.rst +.. include:: sections/1_8_devel.rst +.. include:: sections/1_9_devel.rst diff --git a/docs/source-pytorch/upgrade/from_1_6.rst b/docs/source-pytorch/upgrade/from_1_6.rst new file mode 100644 index 0000000..c0d5155 --- /dev/null +++ b/docs/source-pytorch/upgrade/from_1_6.rst @@ -0,0 +1,28 @@ +:orphan: + +Upgrade from 1.6 to the 2.0 +########################### + +Regular User +************ + +.. include:: sections/1_6_regular.rst +.. include:: sections/1_7_regular.rst +.. include:: sections/1_8_regular.rst +.. include:: sections/1_9_regular.rst + +Advanced User +************* + +.. include:: sections/1_6_advanced.rst +.. include:: sections/1_7_advanced.rst +.. include:: sections/1_8_advanced.rst +.. include:: sections/1_9_advanced.rst + +Developer +********* + +.. include:: sections/1_6_devel.rst +.. include:: sections/1_7_devel.rst +.. include:: sections/1_8_devel.rst +.. include:: sections/1_9_devel.rst diff --git a/docs/source-pytorch/upgrade/from_1_7.rst b/docs/source-pytorch/upgrade/from_1_7.rst new file mode 100644 index 0000000..b1413b9 --- /dev/null +++ b/docs/source-pytorch/upgrade/from_1_7.rst @@ -0,0 +1,25 @@ +:orphan: + +Upgrade from 1.7 to the 2.0 +########################### + +Regular User +************ + +.. include:: sections/1_7_regular.rst +.. include:: sections/1_8_regular.rst +.. include:: sections/1_9_regular.rst + +Advanced User +************* + +.. include:: sections/1_7_advanced.rst +.. include:: sections/1_8_advanced.rst +.. include:: sections/1_9_advanced.rst + +Developer +********* + +.. include:: sections/1_7_devel.rst +.. include:: sections/1_8_devel.rst +.. include:: sections/1_9_devel.rst diff --git a/docs/source-pytorch/upgrade/from_1_8.rst b/docs/source-pytorch/upgrade/from_1_8.rst new file mode 100644 index 0000000..c844d3f --- /dev/null +++ b/docs/source-pytorch/upgrade/from_1_8.rst @@ -0,0 +1,22 @@ +:orphan: + +Upgrade from 1.8 to the 2.0 +########################### + +Regular User +************ + +.. include:: sections/1_8_regular.rst +.. include:: sections/1_9_regular.rst + +Advanced User +************* + +.. include:: sections/1_8_advanced.rst +.. include:: sections/1_9_advanced.rst + +Developer +********* + +.. include:: sections/1_8_devel.rst +.. include:: sections/1_9_devel.rst diff --git a/docs/source-pytorch/upgrade/from_1_9.rst b/docs/source-pytorch/upgrade/from_1_9.rst new file mode 100644 index 0000000..e1233d9 --- /dev/null +++ b/docs/source-pytorch/upgrade/from_1_9.rst @@ -0,0 +1,16 @@ +:orphan: + +Regular User +************ + +.. include:: sections/1_9_regular.rst + +Advanced User +************* + +.. include:: sections/1_9_advanced.rst + +Developer +********* + +.. include:: sections/1_9_devel.rst diff --git a/docs/source-pytorch/upgrade/migration_guide.rst b/docs/source-pytorch/upgrade/migration_guide.rst new file mode 100644 index 0000000..8e2d451 --- /dev/null +++ b/docs/source-pytorch/upgrade/migration_guide.rst @@ -0,0 +1,63 @@ +Guide how to upgrade to the 2.0 version +####################################### + +The following section will guide you through updating to the 2.0 release. + +Particular versions +******************* + + + +.. raw:: html + +
+
+ +.. Add callout items below this line + +.. displayitem:: + :header: 1.9.x + :description: Upgrade from 1.9.x series to the 2.0. + :col_css: col-md-12 + :button_link: from_1_9.html + :height: 100 + +.. displayitem:: + :header: 1.8.x + :description: Upgrade from 1.8.x series to the 2.0. + :col_css: col-md-12 + :button_link: from_1_8.html + :height: 100 + +.. displayitem:: + :header: 1.7.x + :description: Upgrade from 1.7.x series to the 2.0. + :col_css: col-md-12 + :button_link: from_1_7.html + :height: 100 + +.. displayitem:: + :header: 1.6.x + :description: Upgrade from 1.6.x series to the 2.0. + :col_css: col-md-12 + :button_link: from_1_6.html + :height: 100 + +.. displayitem:: + :header: 1.5.x + :description: Upgrade from 1.5.x series to the 2.0. + :col_css: col-md-12 + :button_link: from_1_5.html + :height: 100 + +.. displayitem:: + :header: 1.4.x + :description: Upgrade from 1.4.x series to the 2.0. + :col_css: col-md-12 + :button_link: from_1_4.html + :height: 100 + +.. raw:: html + +
+
diff --git a/docs/source-pytorch/upgrade/sections/1_4_advanced.rst b/docs/source-pytorch/upgrade/sections/1_4_advanced.rst new file mode 100644 index 0000000..b43a316 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_4_advanced.rst @@ -0,0 +1,43 @@ +.. list-table:: adv. user 1.4 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - called ``ModelCheckpoint.save_function`` + - now call ``Trainer.save_checkpoint`` + - `PR7201`_ + + * - accessed the ``Trainer.running_sanity_check`` property + - now access the ``Trainer.sanity_checking`` property + - `PR4945`_ + + * - used ``LightningModule.grad_norm`` + - now use the ``pl.utilities.grad_norm`` utility function instead + - `PR7292`_ + + * - used ``TrainerTrainingTricksMixin.detect_nan_tensors`` + - now use ``pl.utilities.grads.grad_norm`` + - `PR6834`_ + + * - used ``TrainerTrainingTricksMixin.print_nan_gradients`` + - now use ``pl.utilities.finite_checks.print_nan_gradients`` + - `PR6834`_ + + * - If you relied on ``TrainerLoggingMixin.metrics_to_scalars`` + - now use ``pl.utilities.metrics.metrics_to_scalars`` + - `PR7180`_ + + * - selected the i-th GPU with ``Trainer(gpus="i,j")`` + - now this will set the number of GPUs, just like passing ``Trainer(devices=i)``, you can still select the specific GPU by setting the ``CUDA_VISIBLE_DEVICES=i,j`` environment variable + - `PR6388`_ + + +.. _pr7201: https://github.com/Lightning-AI/lightning/pull/7201 +.. _pr4945: https://github.com/Lightning-AI/lightning/pull/4945 +.. _pr7292: https://github.com/Lightning-AI/lightning/pull/7292 +.. _pr6834: https://github.com/Lightning-AI/lightning/pull/6834 +.. _pr7180: https://github.com/Lightning-AI/lightning/pull/7180 +.. _pr6388: https://github.com/Lightning-AI/lightning/pull/6388 diff --git a/docs/source-pytorch/upgrade/sections/1_4_devel.rst b/docs/source-pytorch/upgrade/sections/1_4_devel.rst new file mode 100644 index 0000000..e69de29 diff --git a/docs/source-pytorch/upgrade/sections/1_4_regular.rst b/docs/source-pytorch/upgrade/sections/1_4_regular.rst new file mode 100644 index 0000000..866bef5 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_4_regular.rst @@ -0,0 +1,59 @@ +.. list-table:: reg. user 1.4 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - relied on the ``outputs`` in your ``LightningModule.on_train_epoch_end`` or ``Callback.on_train_epoch_end`` hooks + - rely on either ``on_train_epoch_end`` or set outputs as attributes in your ``LightningModule`` instances and access them from the hook + - `PR7339`_ + + * - accessed ``Trainer.truncated_bptt_steps`` + - swicth to manual optimization + - `PR7323`_ + + * - called ``LightningModule.write_predictions`` and ``LightningModule.write_predictions_dict`` + - rely on ``predict_step`` and ``Trainer.predict`` + callbacks to write out predictions + - `PR7066`_ + + * - passed the ``period`` argument to the ``ModelCheckpoint`` callback + - pass the ``every_n_epochs`` argument to the ``ModelCheckpoint`` callback + - `PR6146`_ + + * - passed the ``output_filename`` argument to ``Profiler`` + - now pass ``dirpath`` and ``filename``, that is ``Profiler(dirpath=...., filename=...)`` + - `PR6621`_ + + * - passed the ``profiled_functions`` argument in ``PytorchProfiler`` + - now pass the ``record_functions`` argument + - `PR6349`_ + + * - relied on the ``@auto_move_data`` decorator to use the ``LightningModule`` outside of the ``Trainer`` for inference + - use ``Trainer.predict`` + - `PR6993`_ + + * - implemented ``on_load_checkpoint`` with a ``checkpoint`` only argument, as in ``Callback.on_load_checkpoint(checkpoint)`` + - now update the signature to include ``pl_module`` and ``trainer``, as in ``Callback.on_load_checkpoint(trainer, pl_module, checkpoint)`` + - `PR7253`_ + + * - relied on ``pl.metrics`` + - now import separate package ``torchmetrics`` + - `torchmetrics`_ + + * - accessed ``datamodule`` attribute of ``LightningModule``, that is ``model.datamodule`` + - now access ``Trainer.datamodule``, that is ``model.trainer.datamodule`` + - `PR7168`_ + + +.. _torchmetrics: https://torchmetrics.readthedocs.io/en/stable +.. _pr7339: https://github.com/Lightning-AI/lightning/pull/7339 +.. _pr7323: https://github.com/Lightning-AI/lightning/pull/7323 +.. _pr7066: https://github.com/Lightning-AI/lightning/pull/7066 +.. _pr6146: https://github.com/Lightning-AI/lightning/pull/6146 +.. _pr6621: https://github.com/Lightning-AI/lightning/pull/6621 +.. _pr6349: https://github.com/Lightning-AI/lightning/pull/6349 +.. _pr6993: https://github.com/Lightning-AI/lightning/pull/6993 +.. _pr7253: https://github.com/Lightning-AI/lightning/pull/7253 +.. _pr7168: https://github.com/Lightning-AI/lightning/pull/7168 diff --git a/docs/source-pytorch/upgrade/sections/1_5_advanced.rst b/docs/source-pytorch/upgrade/sections/1_5_advanced.rst new file mode 100644 index 0000000..22ba000 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_5_advanced.rst @@ -0,0 +1,75 @@ +.. list-table:: adv. user 1.5 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - used ``self.log(sync_dist_op=...)`` + - use ``self.log(reduce_fx=...)`` instead. Passing ``"mean"`` will still work, but it also takes a callable + - `PR7891`_ + + * - used the argument ``model`` from ``pytorch_lightning.utilities.model_helper.is_overridden`` + - use ``instance`` instead + - `PR7918`_ + + * - returned values from ``training_step`` that had ``.grad`` defined (e.g., a loss) and expected ``.detach()`` to be called for you + - call ``.detach()`` manually + - `PR7994`_ + + * - imported ``pl.utilities.distributed.rank_zero_warn`` + - import ``pl.utilities.rank_zero.rank_zero_warn`` + - + + * - relied on ``DataModule.has_prepared_data`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - relied on ``DataModule.has_setup_fit`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - relied on ``DataModule.has_setup_validate`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - relied on ``DataModule.has_setup_test`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - relied on ``DataModule.has_setup_predict`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - relied on ``DataModule.has_teardown_fit`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - relied on ``DataModule.has_teardown_validate`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - relied on ``DataModule.has_teardown_test`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - relied on ``DataModule.has_teardown_predict`` attribute + - manage data lifecycle in customer methods + - `PR7657`_ + + * - used ``DDPPlugin.task_idx`` + - use ``DDPStrategy.local_rank`` + - `PR8203`_ + + * - used ``Trainer.disable_validation`` + - use the condition ``not Trainer.enable_validation`` + - `PR8291`_ + + +.. _pr7891: https://github.com/Lightning-AI/lightning/pull/7891 +.. _pr7918: https://github.com/Lightning-AI/lightning/pull/7918 +.. _pr7994: https://github.com/Lightning-AI/lightning/pull/7994 +.. _pr7657: https://github.com/Lightning-AI/lightning/pull/7657 +.. _pr8203: https://github.com/Lightning-AI/lightning/pull/8203 +.. _pr8291: https://github.com/Lightning-AI/lightning/pull/8291 diff --git a/docs/source-pytorch/upgrade/sections/1_5_devel.rst b/docs/source-pytorch/upgrade/sections/1_5_devel.rst new file mode 100644 index 0000000..dcaefe4 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_5_devel.rst @@ -0,0 +1,30 @@ +.. list-table:: devel 1.5 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + + * - called ``CheckpointConnector.hpc_load()`` + - just call ``CheckpointConnector.restore()`` + - `PR7652`_ + + * - used ``TrainerModelHooksMixin`` + - now rely on the corresponding utility functions in ``pytorch_lightning.utilities.signature_utils`` + - `PR7422`_ + + * - assigned the ``Trainer.train_loop`` property + - now assign the equivalent ``Trainer.fit_loop`` property + - `PR8025`_ + + * - accessed ``LightningModule.loaded_optimizer_states_dict`` + - the property has been removed + - `PR8229`_ + + +.. _pr7652: https://github.com/Lightning-AI/lightning/pull/7652 +.. _pr7422: https://github.com/Lightning-AI/lightning/pull/7422 +.. _pr8025: https://github.com/Lightning-AI/lightning/pull/8025 +.. _pr8229: https://github.com/Lightning-AI/lightning/pull/8229 diff --git a/docs/source-pytorch/upgrade/sections/1_5_regular.rst b/docs/source-pytorch/upgrade/sections/1_5_regular.rst new file mode 100644 index 0000000..994cfae --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_5_regular.rst @@ -0,0 +1,47 @@ +.. list-table:: reg. user 1.5 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - used ``trainer.fit(train_dataloaders=...)`` + - use ``trainer.fit(dataloaders=...)`` + - `PR7431`_ + + * - used ``trainer.validate(val_dataloaders...)`` + - use ``trainer.validate(dataloaders=...)`` + - `PR7431`_ + + * - passed ``num_nodes`` to ``DDPPlugin`` and ``DDPSpawnPlugin`` + - remove them since these parameters are now passed from the ``Trainer`` + - `PR7026`_ + + * - passed ``sync_batchnorm`` to ``DDPPlugin`` and ``DDPSpawnPlugin`` + - remove them since these parameters are now passed from the ``Trainer`` + - `PR7026`_ + + * - didn’t provide a ``monitor`` argument to the ``EarlyStopping`` callback and just relied on the default value + - pass ``monitor`` as it is now a required argument + - `PR7907`_ + + * - used ``every_n_val_epochs`` in ``ModelCheckpoint`` + - change the argument to ``every_n_epochs`` + - `PR8383`_ + + * - used Trainer’s flag ``reload_dataloaders_every_epoch`` + - use pass ``reload_dataloaders_every_n_epochs`` + - `PR5043`_ + + * - used Trainer’s flag ``distributed_backend`` + - use ``strategy`` + - `PR8575`_ + + +.. _pr7431: https://github.com/Lightning-AI/lightning/pull/7431 +.. _pr7026: https://github.com/Lightning-AI/lightning/pull/7026 +.. _pr7907: https://github.com/Lightning-AI/lightning/pull/7907 +.. _pr8383: https://github.com/Lightning-AI/lightning/pull/8383 +.. _pr5043: https://github.com/Lightning-AI/lightning/pull/5043 +.. _pr8575: https://github.com/Lightning-AI/lightning/pull/8575 diff --git a/docs/source-pytorch/upgrade/sections/1_6_advanced.rst b/docs/source-pytorch/upgrade/sections/1_6_advanced.rst new file mode 100644 index 0000000..c17be4a --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_6_advanced.rst @@ -0,0 +1,61 @@ +.. list-table:: adv. user 1.6 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - passed ``prepare_data_per_node`` to the ``Trainer`` + - set it as a property of ``DataHooks``, accessible in the ``LightningModule`` and ``LightningDataModule`` instead + - `PR8958`_ + + * - used ``process_position`` flag + - specify your ``ProgressBar`` callback and set it as ``process_position`` directly + - `PR9222`_ + + * - used distributed training attributes ``add_to_queue`` and ``get_from_queue`` in ``LightningModule`` + - user the same methods in ``DDPStrategy(start_method='spawn')`` + - `PR9118`_ + + * - called ``LightningModule.get_progress_bar_dict`` + - use the utility function ``pl.callbacks.progress.base.get_standard_metrics(module.trainer)`` + - `PR9118`_ + + * - used ``LightningModule.on_post_move_to_device`` + - remove it as parameters tying happens automatically without the need of implementing your own logic + - `PR9525`_ + + * - relied on ``Trainer.progress_bar_dict`` + - use ``ProgressBarBase.get_metrics`` + - `PR9118`_ + + * - used ``LightningDistributed`` + - rely on the logic in ``DDPStrategy(start_method='...')`` + - `PR9691`_ + + * - used the Accelerator collective API ``Accelerator.barrier``, ``Accelerator.broadcast``, and ``Accelerator.all_gather`` + - call ``Strategy`` collectives API directly, without going through ``Accelerator`` + - `PR9677`_ + + * - used ``pytorch_lightning.core.decorators.parameter_validation`` + - rely on automatic parameters tying with ``pytorch_lightning.utilities.params_tying.set_shared_parameters`` + - `PR9525`_ + + * - used ``LearningRateMonitor.lr_sch_names`` + - access them using ``LearningRateMonitor.lrs.keys()`` which will return the names of all the optimizers, even those without a scheduler. + - `PR10066`_ + + * - implemented ``DataModule`` ``train_transforms``, ``val_transforms``, ``test_transforms``, ``size``, ``dims`` + - switch to ``LightningDataModule`` + - `PR8851`_ + + +.. _pr8958: https://github.com/Lightning-AI/lightning/pull/8958 +.. _pr9222: https://github.com/Lightning-AI/lightning/pull/9222 +.. _pr9118: https://github.com/Lightning-AI/lightning/pull/9118 +.. _pr9525: https://github.com/Lightning-AI/lightning/pull/9525 +.. _pr9691: https://github.com/Lightning-AI/lightning/pull/9691 +.. _pr9677: https://github.com/Lightning-AI/lightning/pull/9677 +.. _pr10066: https://github.com/Lightning-AI/lightning/pull/10066 +.. _pr8851: https://github.com/Lightning-AI/lightning/pull/8851 diff --git a/docs/source-pytorch/upgrade/sections/1_6_devel.rst b/docs/source-pytorch/upgrade/sections/1_6_devel.rst new file mode 100644 index 0000000..9369de0 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_6_devel.rst @@ -0,0 +1,37 @@ +.. list-table:: devel 1.6 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - called ``LightningLoggerBase.close`` + - switch to ``LightningLoggerBase.finalize``. + - `PR9422`_ + + * - called ``LoggerCollection.close`` + - switch to ``LoggerCollection.finalize``. + - `PR9422`_ + + * - used ``AcceleratorConnector.is_slurm_managing_tasks`` attribute + - it is set not as protected and discouraged from direct use + - `PR10101`_ + + * - used ``AcceleratorConnector.configure_slurm_ddp`` attributes + - it is set not as protected and discouraged from direct use + - `PR10101`_ + + * - used ``ClusterEnvironment.creates_children()`` method + - change it to ``ClusterEnvironment.creates_processes_externally`` which is property now. + - `PR10106`_ + + * - called ``PrecisionPlugin.master_params()`` + - update it ``PrecisionPlugin.main_params()`` + - `PR10105`_ + + +.. _pr9422: https://github.com/Lightning-AI/lightning/pull/9422 +.. _pr10101: https://github.com/Lightning-AI/lightning/pull/10101 +.. _pr10105: https://github.com/Lightning-AI/lightning/pull/10105 +.. _pr10106: https://github.com/Lightning-AI/lightning/pull/10106 diff --git a/docs/source-pytorch/upgrade/sections/1_6_regular.rst b/docs/source-pytorch/upgrade/sections/1_6_regular.rst new file mode 100644 index 0000000..e016084 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_6_regular.rst @@ -0,0 +1,105 @@ +.. list-table:: reg. user 1.6 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - used Trainer’s flag ``terminate_on_nan`` + - set ``detect_anomaly`` instead, which enables detecting anomalies in the autograd engine + - `PR9175`_ + + * - used Trainer’s flag ``weights_summary`` + - pass a ``ModelSummary`` callback with ``max_depth`` instead + - `PR9699`_ + + * - used Trainer’s flag ``checkpoint_callback`` + - set ``enable_checkpointing``. If you set ``enable_checkpointing=True``, it configures a default ``ModelCheckpoint`` callback if none is provided ``lightning_pytorch.trainer.trainer.Trainer.callbacks.ModelCheckpoint`` + - `PR9754`_ + + * - used Trainer’s flag ``stochastic_weight_avg`` + - add the ``StochasticWeightAveraging`` callback directly to the list of callbacks, so for example, ``Trainer(..., callbacks=[StochasticWeightAveraging(), ...])`` + - `PR8989`_ + + * - used Trainer’s flag ``flush_logs_every_n_steps`` + - pass it to the logger init if it is supported for the particular logger + - `PR9366`_ + + * - used Trainer’s flag ``max_steps`` to the ``Trainer``, ``max_steps=None`` won't have any effect + - turn off the limit by passing ``Trainer(max_steps=-1)`` which is the default + - `PR9460`_ + + * - used Trainer’s flag ``resume_from_checkpoint="..."`` + - pass the same path to the fit function instead, ``trainer.fit(ckpt_path="...")`` + - `PR9693`_ + + * - used Trainer’s flag ``log_gpu_memory``, ``gpu_metrics`` + - use the ``DeviceStatsMonitor`` callback instead + - `PR9921`_ + + * - used Trainer’s flag ``progress_bar_refresh_rate`` + - set the ``ProgressBar`` callback and set ``refresh_rate`` there, or pass ``enable_progress_bar=False`` to disable the progress bar + - `PR9616`_ + + * - called ``LightningModule.summarize()`` + - use the utility function ``pl.utilities.model_summary.summarize(model)`` + - `PR8513`_ + + * - used the ``LightningModule.model_size`` property + - use the utility function ``pl.utilities.memory.get_model_size_mb(model)`` + - `PR8495`_ + + * - relied on the ``on_train_dataloader()`` hooks in ``LightningModule`` and ``LightningDataModule`` + - use ``train_dataloader`` + - `PR9098`_ + + * - relied on the ``on_val_dataloader()`` hooks in ``LightningModule`` and ``LightningDataModule`` + - use ``val_dataloader`` + - `PR9098`_ + + * - relied on the ``on_test_dataloader()`` hooks in ``LightningModule`` and ``LightningDataModule`` + - use ``test_dataloader`` + - `PR9098`_ + + * - relied on the ``on_predict_dataloader()`` hooks in ``LightningModule`` and ``LightningDataModule`` + - use ``predict_dataloader`` + - `PR9098`_ + + * - implemented the ``on_keyboard_interrupt`` callback hook + - implement the ``on_exception`` hook, and specify the exception type + - `PR9260`_ + + * - relied on the ``TestTubeLogger`` + - Use another logger like ``TensorBoardLogger`` + - `PR9065`_ + + * - used the basic progress bar ``ProgressBar`` callback + - use the ``TQDMProgressBar`` callback instead with the same arguments + - `PR10134`_ + + * - were using ``GPUStatsMonitor`` callbacks + - use ``DeviceStatsMonitor`` callback instead + - `PR9924`_ + + * - were using ``XLAStatsMonitor`` callbacks + - use ``DeviceStatsMonitor`` callback instead + - `PR9924`_ + + +.. _pr9175: https://github.com/Lightning-AI/lightning/pull/9175 +.. _pr9699: https://github.com/Lightning-AI/lightning/pull/9699 +.. _pr9754: https://github.com/Lightning-AI/lightning/pull/9754 +.. _pr8989: https://github.com/Lightning-AI/lightning/pull/8989 +.. _pr9366: https://github.com/Lightning-AI/lightning/pull/9366 +.. _pr9460: https://github.com/Lightning-AI/lightning/pull/9460 +.. _pr9693: https://github.com/Lightning-AI/lightning/pull/9693 +.. _pr9921: https://github.com/Lightning-AI/lightning/pull/9921 +.. _pr9616: https://github.com/Lightning-AI/lightning/pull/9616 +.. _pr8513: https://github.com/Lightning-AI/lightning/pull/8513 +.. _pr8495: https://github.com/Lightning-AI/lightning/pull/8495 +.. _pr9098: https://github.com/Lightning-AI/lightning/pull/9098 +.. _pr9260: https://github.com/Lightning-AI/lightning/pull/9260 +.. _pr9065: https://github.com/Lightning-AI/lightning/pull/9065 +.. _pr10134: https://github.com/Lightning-AI/lightning/pull/10134 +.. _pr9924: https://github.com/Lightning-AI/lightning/pull/9924 diff --git a/docs/source-pytorch/upgrade/sections/1_7_advanced.rst b/docs/source-pytorch/upgrade/sections/1_7_advanced.rst new file mode 100644 index 0000000..8b92044 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_7_advanced.rst @@ -0,0 +1,139 @@ +.. list-table:: adv. user 1.7 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - used ``DDP2Strategy`` + - switch to ``DDPStrategy`` + - `PR14026`_ + + * - used ``Trainer.training_type_plugin`` property + - now use ``Trainer.strategy`` and update the references + - `PR11141`_ + + * - used any ``TrainingTypePluginsn`` + - rename them to ``Strategy`` + - `PR11120`_ + + * - used ``DistributedType`` + - rely on protected ``_StrategyType`` + - `PR10505`_ + + * - used ``DeviceType`` + - rely on protected ``_AcceleratorType`` + - `PR10503`_ + + * - used ``pl.utiltiies.meta`` functions + - switch to built-in https://github.com/pytorch/torchdistx support + - `PR13868`_ + + * - have implemented ``Callback.on_configure_sharded_model`` hook + - move your implementation to ``Callback.setup`` + - `PR14834`_ + + * - have implemented the ``Callback.on_before_accelerator_backend_setup`` hook + - move your implementation to ``Callback.setup`` + - `PR14834`_ + + * - have implemented the ``Callback.on_batch_start`` hook + - move your implementation to ``Callback.on_train_batch_start`` + - `PR14834`_ + + * - have implemented the ``Callback.on_batch_end`` hook + - move your implementation to ``Callback.on_train_batch_end`` + - `PR14834`_ + + * - have implemented the ``Callback.on_epoch_start`` hook + - move your implementation to ``Callback.on_train_epoch_start`` , to ``Callback.on_validation_epoch_start`` , to ``Callback.on_test_epoch_start`` + - `PR14834`_ + + * - have implemented the ``Callback.on_pretrain_routine_{start,end}`` hook + - move your implementation to ``Callback.on_fit_start`` + - `PR14834`_ + + * - used ``Callback.on_init_start`` hook + - use ``Callback.on_train_start`` instead + - `PR10940`_ + + * - used ``Callback.on_init_end`` hook + - use ``Callback.on_train_start`` instead + - `PR10940`_ + + * - used Trainer’s attribute ``Trainer.num_processes`` + - it was replaced by ``Trainer.num_devices`` + - `PR12388`_ + + * - used Trainer’s attribute ``Trainer.gpus`` + - it was replaced by ``Trainer.num_devices`` + - `PR12436`_ + + * - used Trainer’s attribute ``Trainer.num_gpus`` + - use ``Trainer.num_devices`` instead + - `PR12384`_ + + * - used Trainer’s attribute ``Trainer.ipus`` + - use ``Trainer.num_devices`` instead + - `PR12386`_ + + * - used Trainer’s attribute ``Trainer.tpu_cores`` + - use ``Trainer.num_devices`` instead + - `PR12437`_ + + * - used ``Trainer.num_processes`` attribute + - switch to using ``Trainer.num_devices`` + - `PR12388`_ + + * - used ``LightningIPUModule`` + - it was removed + - `PR14830`_ + + * - logged with ``LightningLoggerBase.agg_and_log_metrics`` + - switch to ``LightningLoggerBase.log_metrics`` + - `PR11832`_ + + * - used ``agg_key_funcs`` parameter from ``LightningLoggerBase`` + - log metrics explicitly + - `PR11871`_ + + * - used ``agg_default_func`` parameters in ``LightningLoggerBase`` + - log metrics explicitly + - `PR11871`_ + + * - used ``Trainer.validated_ckpt_path`` attribute + - rely on generic read-only property ``Trainer.ckpt_path`` which is set when checkpoints are loaded via ``Trainer.validate(````ckpt_path=...)`` + - `PR11696`_ + + * - used ``Trainer.tested_ckpt_path`` attribute + - rely on generic read-only property ``Trainer.ckpt_path`` which is set when checkpoints are loaded via ``Trainer.test(````ckpt_path=...)`` + - `PR11696`_ + + * - used ``Trainer.predicted_ckpt_path`` attribute + - rely on generic read-only property ``Trainer.ckpt_path``, which is set when checkpoints are loaded via ``Trainer.predict(````ckpt_path=...)`` + - `PR11696`_ + + * - rely on the returned dictionary from ``Callback.on_save_checkpoint`` + - call directly ``Callback.state_dict`` instead + - `PR11887`_ + + +.. _pr14026: https://github.com/Lightning-AI/lightning/pull/14026 +.. _pr11141: https://github.com/Lightning-AI/lightning/pull/11141 +.. _pr11120: https://github.com/Lightning-AI/lightning/pull/11120 +.. _pr10505: https://github.com/Lightning-AI/lightning/pull/10505 +.. _pr10503: https://github.com/Lightning-AI/lightning/pull/10503 +.. _pr13868: https://github.com/Lightning-AI/lightning/pull/13868 +.. _pr14834: https://github.com/Lightning-AI/lightning/pull/14834 +.. _pr10940: https://github.com/Lightning-AI/lightning/pull/10940 +.. _pr12388: https://github.com/Lightning-AI/lightning/pull/12388 +.. _pr12436: https://github.com/Lightning-AI/lightning/pull/12436 +.. _pr12384: https://github.com/Lightning-AI/lightning/pull/12384 +.. _pr12386: https://github.com/Lightning-AI/lightning/pull/12386 +.. _pr12437: https://github.com/Lightning-AI/lightning/pull/12437 +.. _pr14830: https://github.com/Lightning-AI/lightning/pull/14830 +.. _pr11832: https://github.com/Lightning-AI/lightning/pull/11832 +.. _pr11871: https://github.com/Lightning-AI/lightning/pull/11871 +.. _pr11696: https://github.com/Lightning-AI/lightning/pull/11696 +.. _pr11887: https://github.com/Lightning-AI/lightning/pull/11887 diff --git a/docs/source-pytorch/upgrade/sections/1_7_devel.rst b/docs/source-pytorch/upgrade/sections/1_7_devel.rst new file mode 100644 index 0000000..11fab55 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_7_devel.rst @@ -0,0 +1,145 @@ +.. list-table:: devel 1.7 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - Removed the legacy ``Trainer.get_deprecated_arg_names()`` + - + - `PR14415`_ + + * - used the generic method ``Trainer.run_stage`` + - switch to a specific one depending on your purpose ``Trainer.{fit,validate,test,predict}`` . + - `PR11000`_ + + * - used ``rank_zero_only`` from ``pl.utilities.distributed`` + - import it from ``pl.utilities.rank_zero`` + - `PR11747`_ + + * - used ``rank_zero_debug`` from ``pl.utilities.distributed`` + - import it from ``pl.utilities.rank_zero`` + - `PR11747`_ + + * - used ``rank_zero_info`` from ``pl.utilities.distributed`` + - import it from ``pl.utilities.rank_zero`` + - `PR11747`_ + + * - used ``rank_zero_warn`` from ``pl.utilities.warnings`` + - import it from ``pl.utilities.rank_zero`` + - `PR11747`_ + + * - used ``rank_zero_deprecation`` from ``pl.utilities.warnings`` + - import it from ``pl.utilities.rank_zero`` + - `PR11747`_ + + * - used ``LightningDeprecationWarning`` from ``pl.utilities.warnings`` + - import it from ``pl.utilities.rank_zero`` + - `PR11747`_ + + * - used ``LightningDeprecationWarning`` from ``pl.utilities.warnings`` + - import it from ``pl.utilities.rank_zero`` + - `PR11747`_ + + * - used ``Trainer.data_parallel_device_ids`` attribute + - switch it to ``Trainer.device_ids`` + - `PR12072`_ + + * - derived it from ``TrainerCallbackHookMixin`` + - use Trainer base class + - `PR14401`_ + + * - used base class ``pytorch_lightning.profiler.BaseProfilerto`` + - switch to use ``pytorch_lightning.profiler.Profiler`` instead + - `PR12150`_ + + * - set distributed backend via the environment variable ``PL_TORCH_DISTRIBUTED_BACKEND`` + - use ``process_group_backend`` in the strategy constructor + - `PR11745`_ + + * - used ``PrecisionPlugin.on_load_checkpoint`` hooks + - switch to ``PrecisionPlugin.load_state_dict`` + - `PR11978`_ + + * - used ``PrecisionPlugin.on_save_checkpoint`` hooks + - switch to ``PrecisionPlugin.load_state_dict`` + - `PR11978`_ + + * - used ``Trainer.root_gpu`` attribute + - use ``Trainer.strategy.root_device.index`` when GPU is used + - `PR12262`_ + + * - used ``Trainer.use_amp`` attribute + - rely on Torch native AMP + - `PR12312`_ + + * - used ``LightingModule.use_amp`` attribute + - rely on Torch native AMP + - `PR12315`_ + + * - used Trainer’s attribute ``Trainer.verbose_evaluate`` + - rely on loop constructor ``EvaluationLoop(verbose=...)`` + - `PR10931`_ + + * - used Trainer’s attribute ``Trainer.should_rank_save_checkpoint`` + - it was removed + - `PR11068`_ + + * - derived from ``TrainerOptimizersMixin`` + - rely on ``core/optimizer.py`` + - `PR11155`_ + + * - derived from ``TrainerDataLoadingMixin`` + - rely on methods from ``Trainer`` and ``DataConnector`` + - `PR11282`_ + + * - used Trainer’s attribute ``Trainer.lightning_optimizers`` + - switch to the ``Strategy`` and its attributes. + - `PR11444`_ + + * - used ``Trainer.call_hook`` + - it was set as a protected method ``Trainer._call_callback_hooks``, ``Trainer._call_lightning_module_hook``, ``Trainer._call_ttp_hook``, ``Trainer._call_accelerator_hook`` and shall not be used. + - `PR10979`_ + + * - used Profiler’s attribute ``SimpleProfiler.profile_iterable`` + - it was removed + - `PR12102`_ + + * - used Profiler’s attribute ``AdvancedProfiler.profile_iterable`` + - it was removed + - `PR12102`_ + + * - used the ``device_stats_monitor.prefix_metric_keys`` + - + - `PR11254`_ + + * - used ``on_train_batch_end(outputs, ...)`` with 2d list with sizes (n_optimizers, tbptt_steps) + - chang it to (tbptt_steps, n_optimizers). You can update your code by adding the following parameter to your hook signature: ``on_train_batch_end(outputs, ..., new_format=True)``. + - `PR12182`_ + + * - used ``training_epoch_end(outputs)`` with a 3d list with sizes (n_optimizers, n_batches, tbptt_steps) + - change it to (n_batches, tbptt_steps, n_optimizers). You can update your code by adding the following parameter to your hook signature: ``training_epoch_end(outputs, new_format=True)``. + - `PR12182`_ + + +.. _pr14415: https://github.com/Lightning-AI/lightning/pull/14415 +.. _pr11000: https://github.com/Lightning-AI/lightning/pull/11000 +.. _pr11747: https://github.com/Lightning-AI/lightning/pull/11747 +.. _pr12072: https://github.com/Lightning-AI/lightning/pull/12072 +.. _pr14401: https://github.com/Lightning-AI/lightning/pull/14401 +.. _pr12150: https://github.com/Lightning-AI/lightning/pull/12150 +.. _pr11745: https://github.com/Lightning-AI/lightning/pull/11745 +.. _pr11978: https://github.com/Lightning-AI/lightning/pull/11978 +.. _pr12262: https://github.com/Lightning-AI/lightning/pull/12262 +.. _pr12312: https://github.com/Lightning-AI/lightning/pull/12312 +.. _pr12315: https://github.com/Lightning-AI/lightning/pull/12315 +.. _pr10931: https://github.com/Lightning-AI/lightning/pull/10931 +.. _pr11068: https://github.com/Lightning-AI/lightning/pull/11068 +.. _pr11155: https://github.com/Lightning-AI/lightning/pull/11155 +.. _pr11282: https://github.com/Lightning-AI/lightning/pull/11282 +.. _pr11444: https://github.com/Lightning-AI/lightning/pull/11444 +.. _pr10979: https://github.com/Lightning-AI/lightning/pull/10979 +.. _pr12102: https://github.com/Lightning-AI/lightning/pull/12102 +.. _pr11254: https://github.com/Lightning-AI/lightning/pull/11254 +.. _pr12182: https://github.com/Lightning-AI/lightning/pull/12182 diff --git a/docs/source-pytorch/upgrade/sections/1_7_regular.rst b/docs/source-pytorch/upgrade/sections/1_7_regular.rst new file mode 100644 index 0000000..acfb741 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_7_regular.rst @@ -0,0 +1,51 @@ +.. list-table:: reg. user 1.7 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - have wrapped your loggers with ``LoggerCollection`` + - directly pass a list of loggers to the Trainer and access the list via the ``trainer.loggers`` attribute. + - `PR12147`_ + + * - used ``Trainer.lr_schedulers`` + - access ``trainer.lr_scheduler_configs`` instead, which contains dataclasses instead of dictionaries. + - `PR11443`_ + + * - used ``neptune-client`` API in the ``NeptuneLogger`` + - upgrade to the latest API + - `PR14727`_ + + * - used ``LightningDataModule.on_save`` hook + - use ``LightningDataModule.on_save_checkpoint`` instead + - `PR11887`_ + + * - used ``LightningDataModule.on_load_checkpoint`` hook + - use ``LightningDataModule.on_load_checkpoint`` hook instead + - `PR11887`_ + + * - used ``LightningModule.on_hpc_load`` hook + - switch to general purpose hook ``LightningModule.on_load_checkpoint`` + - `PR14315`_ + + * - used ``LightningModule.on_hpc_save`` hook + - switch to general purpose hook ``LightningModule.on_save_checkpoint`` + - `PR14315`_ + + * - used Trainer’s flag ``weights_save_path`` + - use directly ``dirpath`` argument in the ``ModelCheckpoint`` callback. + - `PR14424`_ + + * - used Trainer’s property ``Trainer.weights_save_path`` is dropped + - + - `PR14424`_ + + +.. _pr12147: https://github.com/Lightning-AI/lightning/pull/12147 +.. _pr11443: https://github.com/Lightning-AI/lightning/pull/11443 +.. _pr14727: https://github.com/Lightning-AI/lightning/pull/14727 +.. _pr11887: https://github.com/Lightning-AI/lightning/pull/11887 +.. _pr14315: https://github.com/Lightning-AI/lightning/pull/14315 +.. _pr14424: https://github.com/Lightning-AI/lightning/pull/14424 diff --git a/docs/source-pytorch/upgrade/sections/1_8_advanced.rst b/docs/source-pytorch/upgrade/sections/1_8_advanced.rst new file mode 100644 index 0000000..2d4a45e --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_8_advanced.rst @@ -0,0 +1,52 @@ +.. list-table:: adv. user 1.8 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - imported ``pl.callbacks.base`` + - import ``pl.callbacks.callback`` + - `PR13031`_ + + * - imported ``pl.loops.base`` + - import ``pl.loops.loop`` instead + - `PR13043`_ + + * - imported ``pl.utilities.cli`` + - import ``pl.cli`` instead + - `PR13767`_ + + * - imported profiler classes from ``pl.profiler.*`` + - import ``pl.profilers`` instead + - `PR12308`_ + + * - used ``pl.accelerators.GPUAccelerator`` + - use ``pl.accelerators.CUDAAccelerator`` + - `PR13636`_ + + * - used ``LightningDeepSpeedModule`` + - use ``strategy="deepspeed"`` or ``strategy=DeepSpeedStrategy(...)`` + - :class:`~lightning.pytorch.strategies.DeepSpeedStrategy` + + * - used the ``with init_meta_context()`` context manager from ``import pl.utilities.meta`` + - switch to ``deepspeed-zero-stage-3`` + - :ref:`deepspeed-zero-stage-3` + + * - used the Lightning Hydra multi-run integration + - removed support for it as it caused issues with processes hanging + - `PR15689`_ + + * - used ``pl.utilities.memory.get_gpu_memory_map`` + - use ``pl.accelerators.cuda.get_nvidia_gpu_stats`` + - `PR9921`_ + + +.. _pr13031: https://github.com/Lightning-AI/lightning/pull/13031 +.. _pr13043: https://github.com/Lightning-AI/lightning/pull/13043 +.. _pr13767: https://github.com/Lightning-AI/lightning/pull/13767 +.. _pr12308: https://github.com/Lightning-AI/lightning/pull/12308 +.. _pr13636: https://github.com/Lightning-AI/lightning/pull/13636 +.. _pr15689: https://github.com/Lightning-AI/lightning/pull/15689 +.. _pr9921: https://github.com/Lightning-AI/lightning/pull/9921 diff --git a/docs/source-pytorch/upgrade/sections/1_8_devel.rst b/docs/source-pytorch/upgrade/sections/1_8_devel.rst new file mode 100644 index 0000000..8c69736 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_8_devel.rst @@ -0,0 +1,24 @@ +.. list-table:: devel 1.8 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - derived from ``pytorch_lightning.loggers.base.LightningLoggerBase`` + - derive from ``pytorch_lightning.loggers.logger.Logger`` + - `PR12014`_ + + * - derived from ``pytorch_lightning.profiler.base.BaseProfiler`` + - derive from ``pytorch_lightning.profilers.profiler.Profiler`` + - `PR12150`_ + + * - derived from ``pytorch_lightning.profiler.base.AbstractProfiler`` + - derive from ``pytorch_lightning.profilers.profiler.Profiler`` + - `PR12106`_ + + +.. _pr12014: https://github.com/Lightning-AI/lightning/pull/12014 +.. _pr12150: https://github.com/Lightning-AI/lightning/pull/12150 +.. _pr12106: https://github.com/Lightning-AI/lightning/pull/12106 diff --git a/docs/source-pytorch/upgrade/sections/1_8_regular.rst b/docs/source-pytorch/upgrade/sections/1_8_regular.rst new file mode 100644 index 0000000..5f4fac2 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_8_regular.rst @@ -0,0 +1,24 @@ +.. list-table:: reg. user 1.8 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - used ``seed_everything_default=None`` in ``LightningCLI`` + - set ``seed_everything_default=False`` instead + - `PR12804`_ + + * - used ``Trainer.reset_train_val_dataloaders()`` + - call ``Trainer.reset_train_dataloaders()`` and ``Trainer.reset_val_dataloaders()`` separately + - `PR12184`_ + + * - imported ``pl.core.lightning`` + - import ``pl.core.module`` instead + - `PR12740`_ + + +.. _pr12804: https://github.com/Lightning-AI/lightning/pull/12804 +.. _pr12184: https://github.com/Lightning-AI/lightning/pull/12184 +.. _pr12740: https://github.com/Lightning-AI/lightning/pull/12740 diff --git a/docs/source-pytorch/upgrade/sections/1_9_advanced.rst b/docs/source-pytorch/upgrade/sections/1_9_advanced.rst new file mode 100644 index 0000000..f9ef1b8 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_9_advanced.rst @@ -0,0 +1,285 @@ +.. list-table:: adv. user 1.9 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - used the ``pl.lite`` module + - switch to ``lightning_fabric`` + - `PR15953`_ + + * - used Trainer’s flag ``strategy='dp'`` + - use DDP with ``strategy='ddp'`` or DeepSpeed instead + - `PR16748`_ + + * - implemented ``LightningModule.training_epoch_end`` hooks + - port your logic to ``LightningModule.on_train_epoch_end`` hook + - `PR16520`_ + + * - implemented ``LightningModule.validation_epoch_end`` hook + - port your logic to ``LightningModule.on_validation_epoch_end`` hook + - `PR16520`_ + + * - implemented ``LightningModule.test_epoch_end`` hooks + - port your logic to ``LightningModule.on_test_epoch_end`` hook + - `PR16520`_ + + * - used Trainer’s flag ``multiple_trainloader_mode`` + - switch to ``CombinedLoader(..., mode=...)`` and set mode directly now + - `PR16800`_ + + * - used Trainer’s flag ``move_metrics_to_cpu`` + - implement particular offload logic in your custom metric or turn it on in ``torchmetrics`` + - `PR16358`_ + + * - used Trainer’s flag ``track_grad_norm`` + - overwrite ``on_before_optimizer_step`` hook and pass the argument directly and ``LightningModule.log_grad_norm()`` hook + - `PR16745`_ `PR16745`_ + + * - used Trainer’s flag ``replace_sampler_ddp`` + - use ``use_distributed_sampler``; the sampler gets created not only for the DDP strategies + - + + * - relied on the ``on_tpu`` argument in ``LightningModule.optimizer_step`` hook + - switch to manual optimization + - `PR16537`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - relied on the ``using_lbfgs`` argument in ``LightningModule.optimizer_step`` hook + - switch to manual optimization + - `PR16538`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - were using ``nvidia/apex`` in any form + - switch to PyTorch native mixed precision ``torch.amp`` instead + - `PR16039`_ :doc:`Precision <../../common/precision>` + + * - used Trainer’s flag ``using_native_amp`` + - use PyTorch native mixed precision + - `PR16039`_ :doc:`Precision <../../common/precision>` + + * - used Trainer’s flag ``amp_backend`` + - use PyTorch native mixed precision + - `PR16039`_ :doc:`Precision <../../common/precision>` + + * - used Trainer’s flag ``amp_level`` + - use PyTorch native mixed precision + - `PR16039`_ :doc:`Precision <../../common/precision>` + + * - used Trainer’s attribute ``using_native_amp`` + - use PyTorch native mixed precision + - `PR16039`_ :doc:`Precision <../../common/precision>` + + * - used Trainer’s attribute ``amp_backend`` + - use PyTorch native mixed precision + - `PR16039`_ :doc:`Precision <../../common/precision>` + + * - used Trainer’s attribute ``amp_level`` + - use PyTorch native mixed precision + - `PR16039`_ :doc:`Precision <../../common/precision>` + + * - use the ``FairScale`` integration + - consider using PyTorch's native FSDP implementation or outsourced implementation into own project + - `lightning-Fairscale`_ + + * - used ``pl.overrides.fairscale.LightningShardedDataParallel`` + - use native FSDP instead + - `PR16400`_ :doc:`FSDP <../../accelerators/gpu_expert>` + + * - used ``pl.plugins.precision.fully_sharded_native_amp.FullyShardedNativeMixedPrecisionPlugin`` + - use native FSDP instead + - `PR16400`_ :doc:`FSDP <../../accelerators/gpu_expert>` + + * - used ``pl.plugins.precision.sharded_native_amp.ShardedNativeMixedPrecisionPlugin`` + - use native FSDP instead + - `PR16400`_ :doc:`FSDP <../../accelerators/gpu_expert>` + + * - used ``pl.strategies.fully_sharded.DDPFullyShardedStrategy`` + - use native FSDP instead + - `PR16400`_ :doc:`FSDP <../../accelerators/gpu_expert>` + + * - used ``pl.strategies.sharded.DDPShardedStrategy`` + - use native FSDP instead + - `PR16400`_ :doc:`FSDP <../../accelerators/gpu_expert>` + + * - used ``pl.strategies.sharded_spawn.DDPSpawnShardedStrategy`` + - use native FSDP instead + - `PR16400`_ :doc:`FSDP <../../accelerators/gpu_expert>` + + * - used ``save_config_overwrite`` parameters in ``LightningCLI`` + - pass this option and via dictionary of ``save_config_kwargs`` parameter + - `PR14998`_ + + * - used ``save_config_multifile`` parameters in ``LightningCLI`` + - pass this option and via dictionary of ``save_config_kwargs`` parameter + - `PR14998`_ + + * - have customized loops ``Loop.replace()`` + - implement your training loop with Fabric. + - `PR14998`_ `Fabric`_ + + * - have customized loops ``Loop.run()`` + - implement your training loop with Fabric. + - `PR14998`_ `Fabric`_ + + * - have customized loops ``Loop.connect()`` + - implement your training loop with Fabric. + - `PR14998`_ `Fabric`_ + + * - used the Trainer’s ``trainer.fit_loop`` property + - implement your training loop with Fabric + - `PR14998`_ `Fabric`_ + + * - used the Trainer’s ``trainer.validate_loop`` property + - implement your training loop with Fabric + - `PR14998`_ `Fabric`_ + + * - used the Trainer’s ``trainer.test_loop`` property + - implement your training loop with Fabric + - `PR14998`_ `Fabric`_ + + * - used the Trainer’s ``trainer.predict_loop`` property + - implement your training loop with Fabric + - `PR14998`_ `Fabric`_ + + * - used the ``Trainer.loop`` and fetching classes + - being marked as protected + - + + * - used ``opt_idx`` argument in ``BaseFinetuning.finetune_function`` + - use manual optimization + - `PR16539`_ + + * - used ``opt_idx`` argument in ``Callback.on_before_optimizer_step`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` as an optional argument in ``LightningModule.training_step`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``LightningModule.on_before_optimizer_step`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``LightningModule.configure_gradient_clipping`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``LightningModule.optimizer_step`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``LightningModule.optimizer_zero_grad`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``LightningModule.lr_scheduler_step`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used declaring optimizer frequencies in the dictionary returned from ``LightningModule.configure_optimizers`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer`` argument in ``LightningModule.backward`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``LightningModule.backward`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``PrecisionPlugin.optimizer_step`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``PrecisionPlugin.,backward`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``PrecisionPlugin.optimizer_step`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``Strategy.backward`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``optimizer_idx`` argument in ``Strategy.optimizer_step`` + - use manual optimization + - `PR16539`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used Trainer’s ``Trainer.optimizer_frequencies`` attribute + - use manual optimization + - :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``PL_INTER_BATCH_PARALLELISM`` environment flag + - + - `PR16355`_ + + * - used training integration with Horovod + - install standalone package/project + - `lightning-Horovod`_ + + * - used training integration with ColossalAI + - install standalone package/project + - `lightning-ColossalAI`_ + + * - used ``QuantizationAwareTraining`` callback + - use Torch’s Quantization directly + - `PR16750`_ + + * - had any logic except reducing the DP outputs in ``LightningModule.training_step_end`` hook + - port it to ``LightningModule.on_train_batch_end`` hook + - `PR16791`_ + + * - had any logic except reducing the DP outputs in ``LightningModule.validation_step_end`` hook + - port it to ``LightningModule.on_validation_batch_end`` hook + - `PR16791`_ + + * - had any logic except reducing the DP outputs in ``LightningModule.test_step_end`` hook + - port it to ``LightningModule.on_test_batch_end`` hook + - `PR16791`_ + + * - used ``pl.strategies.DDPSpawnStrategy`` + - switch to general ``DDPStrategy(start_method='spawn')`` with proper starting method + - `PR16809`_ + + * - used the automatic addition of a moving average of the ``training_step`` loss in the progress bar + - use ``self.log("loss", ..., prog_bar=True)`` instead. + - `PR16192`_ + + * - rely on the ``outputs`` argument from the ``on_predict_epoch_end`` hook + - access them via ``trainer.predict_loop.predictions`` + - `PR16655`_ + + * - need to pass a dictionary to ``self.log()`` + - pass them independently. + - `PR16389`_ + + +.. _Fabric: https://lightning.ai/docs/fabric/ +.. _lightning-Horovod: https://github.com/Lightning-AI/lightning-Horovod +.. _lightning-ColossalAI: https://lightning.ai/docs/pytorch/latest/integrations/strategies/colossalai.html +.. _lightning-Fairscale: https://github.com/Lightning-Sandbox/lightning-Fairscale + +.. _pr15953: https://github.com/Lightning-AI/lightning/pull/15953 +.. _pr16748: https://github.com/Lightning-AI/lightning/pull/16748 +.. _pr16520: https://github.com/Lightning-AI/lightning/pull/16520 +.. _pr16800: https://github.com/Lightning-AI/lightning/pull/16800 +.. _pr16358: https://github.com/Lightning-AI/lightning/pull/16358 +.. _pr16745: https://github.com/Lightning-AI/lightning/pull/16745 +.. _pr16537: https://github.com/Lightning-AI/lightning/pull/16537 +.. _pr16538: https://github.com/Lightning-AI/lightning/pull/16538 +.. _pr16039: https://github.com/Lightning-AI/lightning/pull/16039 +.. _pr16400: https://github.com/Lightning-AI/lightning/pull/16400 +.. _pr14998: https://github.com/Lightning-AI/lightning/pull/14998 +.. _pr16539: https://github.com/Lightning-AI/lightning/pull/16539 +.. _pr16355: https://github.com/Lightning-AI/lightning/pull/16355 +.. _pr16750: https://github.com/Lightning-AI/lightning/pull/16750 +.. _pr16791: https://github.com/Lightning-AI/lightning/pull/16791 +.. _pr16809: https://github.com/Lightning-AI/lightning/pull/16809 +.. _pr16192: https://github.com/Lightning-AI/lightning/pull/16192 +.. _pr16655: https://github.com/Lightning-AI/lightning/pull/16655 +.. _pr16389: https://github.com/Lightning-AI/lightning/pull/16389 diff --git a/docs/source-pytorch/upgrade/sections/1_9_devel.rst b/docs/source-pytorch/upgrade/sections/1_9_devel.rst new file mode 100644 index 0000000..c7179cb --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_9_devel.rst @@ -0,0 +1,307 @@ +.. list-table:: devel 1.9 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - passed the ``pl_module`` argument to distributed module wrappers + - passed the (required) ``forward_module`` argument + - `PR16386`_ + + * - used ``DataParallel`` and the ``LightningParallelModule`` wrapper + - use DDP or DeepSpeed instead + - `PR16748`_ :doc:`DDP <../../accelerators/gpu_expert>` + + * - used ``pl_module`` argument from the distributed module wrappers + - use DDP or DeepSpeed instead + - `PR16386`_ :doc:`DDP <../../accelerators/gpu_expert>` + + * - called ``pl.overrides.base.unwrap_lightning_module`` function + - use DDP or DeepSpeed instead + - `PR16386`_ :doc:`DDP <../../accelerators/gpu_expert>` + + * - used or derived from ``pl.overrides.distributed.LightningDistributedModule`` class + - use DDP instead + - `PR16386`_ :doc:`DDP <../../accelerators/gpu_expert>` + + * - used the pl.plugins.ApexMixedPrecisionPlugin`` plugin + - use PyTorch native mixed precision + - `PR16039`_ + + * - used the ``pl.plugins.NativeMixedPrecisionPlugin`` plugin + - switch to the ``pl.plugins.MixedPrecisionPlugin`` plugin + - `PR16039`_ + + * - used the ``fit_loop.min_steps`` setters + - implement your training loop with Fabric + - `PR16803`_ + + * - used the ``fit_loop.max_steps`` setters + - implement your training loop with Fabric + - `PR16803`_ + + * - used the ``data_parallel`` attribute in ``Trainer`` + - check the same using ``isinstance(trainer.strategy, ParallelStrategy)`` + - `PR16703`_ + + * - used any function from ``pl.utilities.xla_device`` + - switch to ``pl.accelerators.XLAAccelerator.is_available()`` + - `PR14514`_ `PR14550`_ + + * - imported functions from ``pl.utilities.device_parser.*`` + - import them from ``lightning_fabric.utilities.device_parser.*`` + - `PR14492`_ `PR14753`_ + + * - imported functions from ``pl.utilities.cloud_io.*`` + - import them from ``lightning_fabric.utilities.cloud_io.*`` + - `PR14515`_ + + * - imported functions from ``pl.utilities.apply_func.*`` + - import them from ``lightning_utilities.core.apply_func.*`` + - `PR14516`_ `PR14537`_ + + * - used any code from ``pl.core.mixins`` + - use the base classes + - `PR16424`_ + + * - used any code from ``pl.utilities.distributed`` + - rely on Pytorch's native functions + - `PR16390`_ + + * - used any code from ``pl.utilities.data`` + - it was removed + - `PR16440`_ + + * - used any code from ``pl.utilities.optimizer`` + - it was removed + - `PR16439`_ + + * - used any code from ``pl.utilities.seed`` + - it was removed + - `PR16422`_ + + * - were using truncated backpropagation through time (TBPTT) with ``LightningModule.truncated_bptt_steps`` + - use manual optimization + - `PR16172`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - were using truncated backpropagation through time (TBPTT) with ``LightningModule.tbptt_split_batch`` + - use manual optimization + - `PR16172`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - were using truncated backpropagation through time (TBPTT) and passing ``hidden`` to ``LightningModule.training_step`` + - use manual optimization + - `PR16172`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``pl.utilities.finite_checks.print_nan_gradients`` function + - it was removed + - + + * - used ``pl.utilities.finite_checks.detect_nan_parameters`` function + - it was removed + - + + * - used ``pl.utilities.parsing.flatten_dict`` function + - it was removed + - + + * - used ``pl.utilities.metrics.metrics_to_scalars`` function + - it was removed + - + + * - used ``pl.utilities.memory.get_model_size_mb`` function + - it was removed + - + + * - used ``pl.strategies.utils.on_colab_kaggle`` function + - it was removed + - `PR16437`_ + + * - used ``LightningDataModule.add_argparse_args()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``LightningDataModule.parse_argparser()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``LightningDataModule.from_argparse_args()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``LightningDataModule.get_init_arguments_and_types()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``Trainer.default_attributes()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``Trainer.from_argparse_args()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``Trainer.parse_argparser()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``Trainer.match_env_arguments()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``Trainer.add_argparse_args()`` method + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``pl.utilities.argparse.from_argparse_args()`` function + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``pl.utilities.argparse.parse_argparser()`` function + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``pl.utilities.argparseparse_env_variables()`` function + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``get_init_arguments_and_types()`` function + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``pl.utilities.argparse.add_argparse_args()`` function + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``pl.utilities.parsing.str_to_bool()`` function + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``pl.utilities.parsing.str_to_bool_or_int()`` function + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - used ``pl.utilities.parsing.str_to_bool_or_str()`` function + - switch to using ``LightningCLI`` + - `PR16708`_ + + * - derived from ``pl.utilities.distributed.AllGatherGrad`` class + - switch to PyTorch native equivalent + - `PR15364`_ + + * - used ``PL_RECONCILE_PROCESS=1`` env. variable + - customize your logger + - `PR16204`_ + + * - if you derived from mixin’s method ``pl.core.saving.ModelIO.load_from_checkpoint`` + - rely on ``pl.core.module.LightningModule`` + - `PR16999`_ + + * - used ``Accelerator.setup_environment`` method + - switch to ``Accelerator.setup_device`` + - `PR16436`_ + + * - used ``PL_FAULT_TOLERANT_TRAINING`` env. variable + - implement own logic with Fabric + - `PR16516`_ `PR16533`_ + + * - used or derived from public ``pl.overrides.distributed.IndexBatchSamplerWrapper`` class + - it is set as protected + - `PR16826`_ + + * - used the ``DataLoaderLoop`` class + - use manual optimization + - `PR16726`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used the ``EvaluationEpochLoop`` class + - use manual optimization + - `PR16726`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used the ``PredictionEpochLoop`` class + - use manual optimization + - `PR16726`_ :doc:`Manual Optimization <../../model/manual_optimization>` + + * - used ``trainer.reset_*_dataloader()`` methods + - use ``Loop.setup_data()`` for the top-level loops + - `PR16726`_ + + * - used ``LightningModule.precision`` attribute + - rely on Trainer precision attribute + - `PR16203`_ + + * - used ``Trainer.model`` setter + - you shall pass the ``model`` in fit/test/predict method + - `PR16462`_ + + * - relied on ``pl.utilities.supporters.CombinedLoaderIterator`` class + - pass dataloders directly + - `PR16714`_ + + * - relied on ``pl.utilities.supporters.CombinedLoaderIterator`` class + - pass dataloders directly + - `PR16714`_ + + * - accessed ``ProgressBarBase.train_batch_idx`` property + - rely on Trainer internal loops’ properties + - `PR16760`_ + + * - accessed ``ProgressBarBase.val_batch_idx`` property + - rely on Trainer internal loops’ properties + - `PR16760`_ + + * - accessed ``ProgressBarBase.test_batch_idx`` property + - rely on Trainer internal loops’ properties + - `PR16760`_ + + * - accessed ``ProgressBarBase.predict_batch_idx`` property + - rely on Trainer internal loops’ properties + - `PR16760`_ + + * - used ``Trainer.prediction_writer_callbacks`` property + - rely on precision plugin + - `PR16759`_ + + * - used ``PrecisionPlugin.dispatch`` + - it was removed + - `PR16618`_ + + * - used ``Strategy.dispatch`` + - it was removed + - `PR16618`_ + + +.. _pr16386: https://github.com/Lightning-AI/lightning/pull/16386 +.. _pr16748: https://github.com/Lightning-AI/lightning/pull/16748 +.. _pr16039: https://github.com/Lightning-AI/lightning/pull/16039 +.. _pr16803: https://github.com/Lightning-AI/lightning/pull/16803 +.. _pr16703: https://github.com/Lightning-AI/lightning/pull/16703 +.. _pr14514: https://github.com/Lightning-AI/lightning/pull/14514 +.. _pr14550: https://github.com/Lightning-AI/lightning/pull/14550 +.. _pr14492: https://github.com/Lightning-AI/lightning/pull/14492 +.. _pr14753: https://github.com/Lightning-AI/lightning/pull/14753 +.. _pr14515: https://github.com/Lightning-AI/lightning/pull/14515 +.. _pr14516: https://github.com/Lightning-AI/lightning/pull/14516 +.. _pr14537: https://github.com/Lightning-AI/lightning/pull/14537 +.. _pr16424: https://github.com/Lightning-AI/lightning/pull/16424 +.. _pr16390: https://github.com/Lightning-AI/lightning/pull/16390 +.. _pr16440: https://github.com/Lightning-AI/lightning/pull/16440 +.. _pr16439: https://github.com/Lightning-AI/lightning/pull/16439 +.. _pr16422: https://github.com/Lightning-AI/lightning/pull/16422 +.. _pr16172: https://github.com/Lightning-AI/lightning/pull/16172 +.. _pr16437: https://github.com/Lightning-AI/lightning/pull/16437 +.. _pr16708: https://github.com/Lightning-AI/lightning/pull/16708 +.. _pr15364: https://github.com/Lightning-AI/lightning/pull/15364 +.. _pr16204: https://github.com/Lightning-AI/lightning/pull/16204 +.. _pr16999: https://github.com/Lightning-AI/lightning/pull/16999 +.. _pr16436: https://github.com/Lightning-AI/lightning/pull/16436 +.. _pr16516: https://github.com/Lightning-AI/lightning/pull/16516 +.. _pr16533: https://github.com/Lightning-AI/lightning/pull/16533 +.. _pr16826: https://github.com/Lightning-AI/lightning/pull/16826 +.. _pr16726: https://github.com/Lightning-AI/lightning/pull/16726 +.. _pr16203: https://github.com/Lightning-AI/lightning/pull/16203 +.. _pr16462: https://github.com/Lightning-AI/lightning/pull/16462 +.. _pr16714: https://github.com/Lightning-AI/lightning/pull/16714 +.. _pr16760: https://github.com/Lightning-AI/lightning/pull/16760 +.. _pr16759: https://github.com/Lightning-AI/lightning/pull/16759 +.. _pr16618: https://github.com/Lightning-AI/lightning/pull/16618 diff --git a/docs/source-pytorch/upgrade/sections/1_9_regular.rst b/docs/source-pytorch/upgrade/sections/1_9_regular.rst new file mode 100644 index 0000000..0aa0108 --- /dev/null +++ b/docs/source-pytorch/upgrade/sections/1_9_regular.rst @@ -0,0 +1,75 @@ +.. list-table:: reg. user 1.9 + :widths: 40 40 20 + :header-rows: 1 + + * - If + - Then + - Ref + + * - used Python 3.7 + - upgrade to Python 3.8 or higher + - `PR16579`_ + + * - used PyTorch 1.10 + - upgrade to PyTorch 1.11 or higher + - `PR16492`_ + + * - used Trainer’s flag ``gpus`` + - use ``devices`` with the same number + - `PR16171`_ + + * - used Trainer’s flag ``tpu_cores`` + - use ``devices`` with the same number + - `PR16171`_ + + * - used Trainer’s flag ``ipus`` + - use ``devices`` with the same number + - `PR16171`_ + + * - used Trainer’s flag ``num_processes`` + - use ``devices`` with the same number + - `PR16171`_ + + * - used Trainer’s flag ``resume_from_checkpoint`` + - pass the path to the ``Trainer.fit(ckpt_path="...")`` method, + - `PR10061`_ + + * - used Trainer’s flag ``auto_select_gpus`` + - use ``devices="auto"`` + - `PR16184`_ + + * - called the ``pl.tuner.auto_gpu_select.pick_single_gpu`` function + - use Trainer’s flag``devices="auto"`` + - `PR16184`_ + + * - called the ``pl.tuner.auto_gpu_select.pick_multiple_gpus`` functions + - use Trainer’s flag``devices="auto"`` + - `PR16184`_ + + * - used Trainer’s flag ``accumulate_grad_batches`` with a scheduling dictionary value + - use the ``GradientAccumulationScheduler`` callback and configure it + - `PR16729`_ + + * - imported profiles from ``pl.profiler`` + - import from ``pl.profilers`` + - `PR16359`_ + + * - used ``Tuner`` as part of ``Trainer`` in any form + - move to a standalone ``Tuner`` object or use particular callbacks ``LearningRateFinder`` and ``BatchSizeFinder`` + - :ref:`batch_size_finder` :ref:`learning_rate_finder` + + * - used Trainer’s flag ``auto_scale_batch_size`` + - use ``BatchSizeFinder`` callback instead and the ``Trainer.tune()`` method was removed + - + + * - used Trainer’s flag ``auto_lr_find`` + - use callbacks ``LearningRateFinder`` callback instead and the ``Trainer.tune()`` method was removed + - + +.. _pr16579: https://github.com/Lightning-AI/lightning/pull/16579 +.. _pr16492: https://github.com/Lightning-AI/lightning/pull/16492 +.. _pr10061: https://github.com/Lightning-AI/lightning/pull/10061 +.. _pr16171: https://github.com/Lightning-AI/lightning/pull/16171 +.. _pr16184: https://github.com/Lightning-AI/lightning/pull/16184 +.. _pr16729: https://github.com/Lightning-AI/lightning/pull/16729 +.. _pr16359: https://github.com/Lightning-AI/lightning/pull/16359 diff --git a/docs/source-pytorch/versioning.rst b/docs/source-pytorch/versioning.rst new file mode 100644 index 0000000..0b8a801 --- /dev/null +++ b/docs/source-pytorch/versioning.rst @@ -0,0 +1,155 @@ +.. _versioning: + +Versioning Policy +################# + +PyTorch Lightning follows its own versioning policy which differs from `semantic versioning (SemVer) `_. + +Versioning +********** + +A Lightning release number is in the format of ``MAJOR.MINOR.PATCH``. + +- A patch release contains only bug fixes. Since it introduces no breaking changes, we recommend users always update the package to the latest version within the minor version whenever possible. +- A minor release may contain backwards-incompatible changes **with deprecations** (unlike SemVer), such as API changes and removals, as well as new features and bugfixes since last release. +- A major release may contain backwards-incompatible changes **without deprecations**, as well as new features, and bugfixes since last release. + +With every release, we publish a changelog where we list additions, removals, deprecations, changed functionality and fixes. + +The ``lightning.app`` package is an exception to this rule, as it may contain any change with or without deprecations in any of the releases. + +API Stability +************* + +In Lightning, all public APIs are considered stable unless explicitly marked as experimental in their documentation or docstrings. +Modules, functions, classes, and methods that are protected (have a leading underscore, see https://peps.python.org/pep-0008/ for more information) may be changed or removed at any time. + +Stable API +---------- + +Everything not specifically labelled as experimental is stable. + +For stable APIs, all of the following are true: + +- The API is not expected to change. +- If anything does change, we show a deprecation warning before applying the breaking change following the policy described in the "API Evolution" section below. + +Experimental API +---------------- + +Experimental APIs are labelled as experimental in their documentation or docstrings. +For experimental features, any of the following may be true: + +- The feature uses dependencies that are under active development and may change outside our control. +- The API may change without notice in future versions. +- The performance of the feature has not been verified. +- The feature has not been battle tested by the core team in production scenarios. +- The feature is under active development. + +While we may still issue deprecation warnings for experimental API changes, this is not guaranteed. +Therefore, it is important to be cautious when using experimental features and be prepared to modify your code if the +API changes in a future release. In this case, you might want to pin your dependencies to avoid unexpected issues. + +API Evolution +************* + +Lightning's development is driven by research and best practices in a rapidly developing field of AI and machine learning. Change is inevitable and when it happens, the Lightning team is committed to minimizing user friction and maximizing ease of transition from one version to the next. We take backwards compatibility and reproducibility very seriously. + +For API removal, renaming or other forms of backwards-incompatible changes, the procedure is: + +#. A deprecation process is initiated at a minor version ``MAJOR.MINOR.PATCH`` (e.g. ``1.5.0``), producing a deprecation warning at runtime and removing it from the documentation. +#. The deprecated API remains unchanged during the deprecation phase for two minor versions or the next major update, whichever comes first. +#. The breaking change is done in version ``MAJOR.(MINOR+2).0`` (e.g. ``1.7.0``), or ``(MAJOR+1).0.0`` (e.g. ``2.0.0``), whichever comes first. +#. From that version onward, the deprecation warning gets converted into a helpful error, which will remain until next major release. + +This policy is not strict. Shorter or longer deprecation cycles may apply to some cases. +For example, in the past DDP2 was removed without a deprecation process because the feature was broken and unusable beyond fixing as discussed in `#12584 `_. +Also, `#10410 `_ is an example that a longer deprecation applied to. We deprecated the accelerator arguments, such as ``Trainer(gpus=...)``, in 1.7, however, because the APIs were so core that they would impact almost all use cases, we decided not to introduce the breaking change until 2.0. + +Compatibility matrix +******************** + +PyTorch Lightning follows `NEP 29 `_ which PyTorch also follows (`#74203 `_). +The table below indicates the coverage of tested versions in our CI. Versions outside the ranges may unofficially work in some cases. + +.. list-table:: + :header-rows: 1 + + * - ``lightning.pytorch`` + - ``pytorch_lightning`` + - ``lightning.fabric`` + - ``torch`` + - ``torchmetrics`` + - Python + * - 2.0 + - 2.0 + - 2.0 (GA) + - ≥1.11, ≤2.0 + - ≥0.7.0 + - ≥3.8, ≤3.10 + * - 1.9 + - 1.9 + - 1.9 (experimental) + - ≥1.10, ≤1.13 + - ≥0.7.0 + - ≥3.7, ≤3.10 + * - 1.8** + - 1.8 + - n/a*** + - ≥1.10, ≤1.13 + - ≥0.7.0 + - ≥3.7, ≤3.10 + * - n/a + - 1.7 + - n/a*** + - ≥1.9, ≤1.12 + - ≥0.7.0 + - ≥3.7, ≤3.10 + * - n/a + - 1.6 + - n/a*** + - ≥1.8, ≤1.11 + - ≥0.4.1 + - ≥3.7, ≤3.9 + * - n/a + - 1.5 + - n/a*** + - ≥1.7, ≤1.10 + - ≥0.4.1 + - ≥3.6, ≤3.9 + * - n/a + - 1.4 + - n/a + - ≥1.6, ≤1.9 + - ≥0.4.0 + - ≥3.6, ≤3.9 + * - n/a + - 1.3 + - n/a + - ≥1.4, ≤1.8 + - ≥0.2.0 + - ≥3.6, ≤3.9 + * - n/a + - 1.2 + - n/a + - ≥1.4, ≤1.8 + - n/a* + - ≥3.6, ≤3.8 + * - n/a + - 1.1 + - n/a + - ≥1.3, ≤1.8 + - n/a* + - ≥3.6, ≤3.8 + * - n/a + - 1.0 + - n/a + - ≥1.3, ≤1.7 + - n/a* + - ≥3.6, ≤3.8 + +\* ``torchmetrics`` was part of ``pytorch_lightning`` at the time and was decoupled to a separate package in v1.3. + +\*\* The joint ``lightning`` package was first published in version 1.8 + +\*\*\* Fabric is the evolution of ``LightningLite`` which was released inside ``pytorch_lightning`` 1.5 and was decoupled to a separate package in v1.9 diff --git a/source/visualize/experiment_managers.rst b/docs/source-pytorch/visualize/experiment_managers.rst similarity index 91% rename from source/visualize/experiment_managers.rst rename to docs/source-pytorch/visualize/experiment_managers.rst index 30fada9..e40b741 100644 --- a/source/visualize/experiment_managers.rst +++ b/docs/source-pytorch/visualize/experiment_managers.rst @@ -5,7 +5,7 @@ To track other artifacts, such as histograms or model topology graphs first sele .. code-block:: python - from pytorch_lightning import loggers as pl_loggers + from lightning.pytorch import loggers as pl_loggers tensorboard = pl_loggers.TensorBoardLogger() trainer = Trainer(logger=tensorboard) diff --git a/source/visualize/loggers.rst b/docs/source-pytorch/visualize/loggers.rst similarity index 100% rename from source/visualize/loggers.rst rename to docs/source-pytorch/visualize/loggers.rst diff --git a/source/visualize/logging_advanced.rst b/docs/source-pytorch/visualize/logging_advanced.rst similarity index 87% rename from source/visualize/logging_advanced.rst rename to docs/source-pytorch/visualize/logging_advanced.rst index ca11e39..fd9153b 100644 --- a/source/visualize/logging_advanced.rst +++ b/docs/source-pytorch/visualize/logging_advanced.rst @@ -12,11 +12,11 @@ Track and Visualize Experiments (advanced) **************************** Change progress bar defaults **************************** -To change the default values (ie: version number) shown in the progress bar, override the :meth:`~pytorch_lightning.callbacks.progress.base.ProgressBarBase.get_metrics` method in your logger. +To change the default values (ie: version number) shown in the progress bar, override the :meth:`~lightning.pytorch.callbacks.progress.progress_bar.ProgressBar.get_metrics` method in your logger. .. code-block:: python - from pytorch_lightning.callbacks.progress import Tqdm + from lightning.pytorch.callbacks.progress import Tqdm class CustomProgressBar(Tqdm): @@ -37,7 +37,7 @@ Modify logging frequency ======================== Logging a metric on every single batch can slow down training. By default, Lightning logs every 50 rows, or 50 training steps. -To change this behaviour, set the *log_every_n_steps* :class:`~pytorch_lightning.trainer.trainer.Trainer` flag. +To change this behaviour, set the *log_every_n_steps* :class:`~lightning.pytorch.trainer.trainer.Trainer` flag. .. testcode:: @@ -49,20 +49,19 @@ To change this behaviour, set the *log_every_n_steps* :class:`~pytorch_lightning Modify flushing frequency ========================= -Metrics are kept in memory for N steps to improve training efficiency. Every N steps, metrics flush to disk. To change the frequency of this flushing, use the *flush_logs_every_n_steps* Trainer argument. +Some loggers keep logged metrics in memory for N steps and only periodically flush them to disk to improve training efficiency. +Every logger handles this a bit differently. For example, here is how to fine-tune flushing for the TensorBoard logger: .. code-block:: python - # faster training, high memory - Trainer(flush_logs_every_n_steps=500) + # Default used by TensorBoard: Write to disk after 10 logging events or every two minutes + logger = TensorBoardLogger(..., max_queue=10, flush_secs=120) - # slower training, low memory - Trainer(flush_logs_every_n_steps=500) + # Faster training, more memory used + logger = TensorBoardLogger(..., max_queue=100) -The higher *flush_logs_every_n_steps* is, the faster the model will train but the memory will build up until the next flush. -The smaller *flush_logs_every_n_steps* is, the slower the model will train but memory will be kept to a minimum. - -TODO: chart + # Slower training, less memory used + logger = TensorBoardLogger(..., max_queue=1) ---- @@ -114,7 +113,7 @@ logger ====== **Default:** True -Send logs to the logger like ``Tensorboard``, or any other custom logger passed to the :class:`~pytorch_lightning.trainer.trainer.Trainer` (Default: ``True``). +Send logs to the logger like ``Tensorboard``, or any other custom logger passed to the :class:`~lightning.pytorch.trainer.trainer.Trainer` (Default: ``True``). .. code-block:: python @@ -211,11 +210,11 @@ reduce_fx ========= **Default:** :meth:`torch.mean` -Reduction function over step values for end of epoch. Uses :meth:`torch.mean` by default. +Reduction function over step values for end of epoch. Uses :meth:`torch.mean` by default and is not applied when a :class:`torchmetrics.Metric` is logged. .. code-block:: python - self.log(reduce_fx=torch.mean) + self.log(..., reduce_fx=torch.mean) ---- @@ -315,7 +314,7 @@ To save logs to a remote filesystem, prepend a protocol like "s3:/" to the root_ .. code-block:: python - from pytorch_lightning.loggers import TensorBoardLogger + from lightning.pytorch.loggers import TensorBoardLogger logger = TensorBoardLogger(save_dir="s3://my_bucket/logs/") @@ -356,10 +355,10 @@ In LightningModule * - Method - on_step - on_epoch - * - on_after_backward, on_before_backward, on_before_optimizer_step, on_before_zero_grad, training_step, training_step_end + * - on_after_backward, on_before_backward, on_before_optimizer_step, optimizer_step, configure_gradient_clipping, on_before_zero_grad, training_step - True - False - * - training_epoch_end, test_epoch_end, test_step, test_step_end, validation_epoch_end, validation_step, validation_step_end + * - test_step, validation_step - False - True diff --git a/source/visualize/logging_basic.rst b/docs/source-pytorch/visualize/logging_basic.rst similarity index 76% rename from source/visualize/logging_basic.rst rename to docs/source-pytorch/visualize/logging_basic.rst index 8732305..61de5a9 100644 --- a/source/visualize/logging_basic.rst +++ b/docs/source-pytorch/visualize/logging_basic.rst @@ -29,8 +29,8 @@ To track a metric, simply use the *self.log* method available inside the *Lightn class LitModel(pl.LightningModule): def training_step(self, batch, batch_idx): - value = self.global_step - self.log("some_value", self.global_step) + value = ... + self.log("some_value", value) To log multiple metrics at once, use *self.log_dict* @@ -50,7 +50,7 @@ To view metrics in the commandline progress bar, set the *prog_bar* argument to .. code-block:: python - self.log(prog_bar=True) + self.log(..., prog_bar=True) TODO: need progress bar here @@ -58,13 +58,13 @@ TODO: need progress bar here View in the browser =================== -To view metrics in the browser you need to use an *experiment manager* with these capabilities. By Default, Lightning uses Tensorboard which is free and opensource. +To view metrics in the browser you need to use an *experiment manager* with these capabilities. -Tensorboard is already enabled by default +By Default, Lightning uses Tensorboard (if available) and a simple CSV logger otherwise. .. code-block:: python - # every trainer already has tensorboard enabled by default + # every trainer already has tensorboard enabled by default (if the dependency is available) trainer = Trainer() To launch the tensorboard dashboard run the following command on the commandline. @@ -100,39 +100,14 @@ When you call self.log inside the *validation_step* and *test_step*, Lightning a TODO: show single point plotted -If you don't want to average, add your own function in the *reduce_fx* argument. +If you don't want to average you can also choose from ``{min,max,sum}`` by passing the *reduce_fx* argument. .. code-block:: python # default function - self.log(reduce_fx=torch.mean) + self.log(..., reduce_fx="mean") ----- - -************ -Track images -************ -If your *experiment manager* supports image visualization, simply *log* the image with *self.log* - -.. code-block:: python - - # (32 batch samples, 3 channels, 32 width, 32 height) - image = torch.Tensor(32, 3, 28, 28) - self.log("an_image", image) - ----- - -********** -Track text -********** -If your *experiment manager* supports text visualization, simply *log* the text with *self.log* - -.. code-block:: python - - text = "hello world" - self.log("some_text", text) - -# TODO: show screenshot +For other reductions, we recommend logging a :class:`torchmetrics.Metric` instance instead. ---- diff --git a/source/visualize/logging_expert.rst b/docs/source-pytorch/visualize/logging_expert.rst similarity index 77% rename from source/visualize/logging_expert.rst rename to docs/source-pytorch/visualize/logging_expert.rst index 3b44ee9..febb662 100644 --- a/source/visualize/logging_expert.rst +++ b/docs/source-pytorch/visualize/logging_expert.rst @@ -19,11 +19,11 @@ If you'd like to change the way the progress bar displays information you can us Use the TQDMProgressBar ======================= -To use the TQDMProgressBar pass it into the *callbacks* :class:`~pytorch_lightning.trainer.trainer.Trainer` argument. +To use the TQDMProgressBar pass it into the *callbacks* :class:`~lightning.pytorch.trainer.trainer.Trainer` argument. .. code-block:: python - from pytorch_lightning.callbacks import TQDMProgressBar + from lightning.pytorch.callbacks import TQDMProgressBar trainer = Trainer(callbacks=[TQDMProgressBar()]) @@ -37,11 +37,11 @@ The RichProgressBar can add custom colors and beautiful formatting for your prog pip install rich -Then pass the callback into the callbacks :class:`~pytorch_lightning.trainer.trainer.Trainer` argument: +Then pass the callback into the callbacks :class:`~lightning.pytorch.trainer.trainer.Trainer` argument: .. code-block:: python - from pytorch_lightning.callbacks import RichProgressBar + from lightning.pytorch.callbacks import RichProgressBar trainer = Trainer(callbacks=[RichProgressBar()]) @@ -49,8 +49,8 @@ The rich progress bar can also have custom themes .. code-block:: python - from pytorch_lightning.callbacks import RichProgressBar - from pytorch_lightning.callbacks.progress.rich_progress import RichProgressBarTheme + from lightning.pytorch.callbacks import RichProgressBar + from lightning.pytorch.callbacks.progress.rich_progress import RichProgressBarTheme # create your own theme! theme = RichProgressBarTheme(description="green_yellow", progress_bar="green1") @@ -64,11 +64,11 @@ The rich progress bar can also have custom themes ************************ Customize a progress bar ************************ -To customize either the :class:`~pytorch_lightning.callbacks.TQDMProgressBar` or the :class:`~pytorch_lightning.callbacks.RichProgressBar`, subclass it and override any of its methods. +To customize either the :class:`~lightning.pytorch.callbacks.TQDMProgressBar` or the :class:`~lightning.pytorch.callbacks.RichProgressBar`, subclass it and override any of its methods. .. code-block:: python - from pytorch_lightning.callbacks import TQDMProgressBar + from lightning.pytorch.callbacks import TQDMProgressBar class LitProgressBar(TQDMProgressBar): @@ -82,14 +82,14 @@ To customize either the :class:`~pytorch_lightning.callbacks.TQDMProgressBar` o *************************** Build your own progress bar *************************** -To build your own progress bar, subclass :class:`~pytorch_lightning.callbacks.ProgressBarBase` +To build your own progress bar, subclass :class:`~lightning.pytorch.callbacks.ProgressBar` .. code-block:: python - from pytorch_lightning.callbacks import ProgressBarBase + from lightning.pytorch.callbacks import ProgressBar - class LitProgressBar(ProgressBarBase): + class LitProgressBar(ProgressBar): def __init__(self): super().__init__() # don't forget this :) self.enable = True @@ -112,11 +112,11 @@ To build your own progress bar, subclass :class:`~pytorch_lightning.callbacks.Pr ******************************* Integrate an experiment manager ******************************* -To create an integration between a custom logger and Lightning, subclass :class:`~pytorch_lightning.loggers.base.LightningLoggerBase` +To create an integration between a custom logger and Lightning, subclass :class:`~lightning.pytorch.loggers.base.LightningLoggerBase` .. code-block:: python - from pytorch_lightning.loggers import Logger + from lightning.pytorch.loggers import Logger class LitLogger(Logger): diff --git a/source/visualize/logging_intermediate.rst b/docs/source-pytorch/visualize/logging_intermediate.rst similarity index 81% rename from source/visualize/logging_intermediate.rst rename to docs/source-pytorch/visualize/logging_intermediate.rst index 1b0dd6b..24c3760 100644 --- a/source/visualize/logging_intermediate.rst +++ b/docs/source-pytorch/visualize/logging_intermediate.rst @@ -14,9 +14,9 @@ To track other artifacts, such as histograms or model topology graphs first sele .. code-block:: python - from pytorch_lightning import loggers as pl_loggers + from lightning.pytorch import loggers as pl_loggers - tensorboard = pl_loggers.TensorBoardLogger() + tensorboard = pl_loggers.TensorBoardLogger(save_dir="") trainer = Trainer(logger=tensorboard) then access the logger's API directly @@ -35,17 +35,6 @@ then access the logger's API directly ---- -**************************************** -Track multiple metrics in the same chart -**************************************** -If your logger supports plotting multiple metrics on the same chart, pass in a dictionary to *self.log*. - -.. code-block:: python - - self.log("performance", {"acc": acc, "recall": recall}) - ----- - ********************* Track hyperparameters ********************* diff --git a/docs/source-pytorch/visualize/supported_exp_managers.rst b/docs/source-pytorch/visualize/supported_exp_managers.rst new file mode 100644 index 0000000..49ab10c --- /dev/null +++ b/docs/source-pytorch/visualize/supported_exp_managers.rst @@ -0,0 +1,204 @@ +Comet.ml +======== +To use `Comet.ml `_ first install the comet package: + +.. code-block:: bash + + pip install comet-ml + +Configure the logger and pass it to the :class:`~lightning.pytorch.trainer.trainer.Trainer`: + +.. testcode:: + :skipif: not _COMET_AVAILABLE + + from lightning.pytorch.loggers import CometLogger + + comet_logger = CometLogger(api_key="YOUR_COMET_API_KEY") + trainer = Trainer(logger=comet_logger) + +Access the comet logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts + +.. code-block:: python + + class LitModel(LightningModule): + def any_lightning_module_function_or_hook(self): + comet = self.logger.experiment + fake_images = torch.Tensor(32, 3, 28, 28) + comet.add_image("generated_images", fake_images, 0) + +Here's the full documentation for the :class:`~lightning.pytorch.loggers.CometLogger`. + +---- + +MLflow +====== +To use `MLflow `_ first install the MLflow package: + +.. code-block:: bash + + pip install mlflow + +Configure the logger and pass it to the :class:`~lightning.pytorch.trainer.trainer.Trainer`: + +.. testcode:: + :skipif: not _MLFLOW_AVAILABLE + + from lightning.pytorch.loggers import MLFlowLogger + + mlf_logger = MLFlowLogger(experiment_name="lightning_logs", tracking_uri="file:./ml-runs") + trainer = Trainer(logger=mlf_logger) + +Access the mlflow logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts + +.. code-block:: python + + class LitModel(LightningModule): + def any_lightning_module_function_or_hook(self): + mlf_logger = self.logger.experiment + fake_images = torch.Tensor(32, 3, 28, 28) + mlf_logger.add_image("generated_images", fake_images, 0) + +Here's the full documentation for the :class:`~lightning.pytorch.loggers.MLFlowLogger`. + +---- + +Neptune.ai +========== +To use `Neptune.ai `_ first install the neptune package: + +.. code-block:: bash + + pip install neptune + +or with conda: + +.. code-block:: bash + + conda install -c conda-forge neptune + +Configure the logger and pass it to the :class:`~lightning.pytorch.trainer.trainer.Trainer`: + +.. testcode:: + :skipif: not _NEPTUNE_AVAILABLE + + import neptune + from lightning.pytorch.loggers import NeptuneLogger + + neptune_logger = NeptuneLogger( + api_key=neptune.ANONYMOUS_API_TOKEN, # replace with your own + project="common/pytorch-lightning-integration", # format "" + ) + trainer = Trainer(logger=neptune_logger) + +Access the neptune logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts + +.. code-block:: python + + class LitModel(LightningModule): + def any_lightning_module_function_or_hook(self): + neptune_logger = self.logger.experiment["your/metadata/structure"] + neptune_logger.append(metadata) + +Here's the full documentation for the :class:`~lightning.pytorch.loggers.NeptuneLogger`. + +---- + +Tensorboard +=========== +`TensorBoard `_ can be installed with: + +.. code-block:: bash + + pip install tensorboard + +Configure the logger and pass it to the :class:`~lightning.pytorch.trainer.trainer.Trainer`: + +.. code-block:: python + + from lightning.pytorch.loggers import TensorBoardLogger + + logger = TensorBoardLogger() + trainer = Trainer(logger=logger) + +Access the tensorboard logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts + +.. code-block:: python + + class LitModel(LightningModule): + def any_lightning_module_function_or_hook(self): + tensorboard_logger = self.logger.experiment + fake_images = torch.Tensor(32, 3, 28, 28) + tensorboard_logger.add_image("generated_images", fake_images, 0) + +Here's the full documentation for the :class:`~lightning.pytorch.loggers.TensorBoardLogger`. + +---- + +Weights and Biases +================== +To use `Weights and Biases `_ (wandb) first install the wandb package: + +.. code-block:: bash + + pip install wandb + +Configure the logger and pass it to the :class:`~lightning.pytorch.trainer.trainer.Trainer`: + +.. testcode:: + :skipif: not _WANDB_AVAILABLE + + from lightning.pytorch.loggers import WandbLogger + + wandb_logger = WandbLogger(project="MNIST", log_model="all") + trainer = Trainer(logger=wandb_logger) + + # log gradients and model topology + wandb_logger.watch(model) + +Access the wandb logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts + +.. code-block:: python + + class MyModule(LightningModule): + def any_lightning_module_function_or_hook(self): + wandb_logger = self.logger.experiment + fake_images = torch.Tensor(32, 3, 28, 28) + + # Option 1 + wandb_logger.log({"generated_images": [wandb.Image(fake_images, caption="...")]}) + + # Option 2 for specifically logging images + wandb_logger.log_image(key="generated_images", images=[fake_images]) + +Here's the full documentation for the :class:`~lightning.pytorch.loggers.WandbLogger`. +`Demo in Google Colab `__ with hyperparameter search and model logging. + +---- + +Use multiple exp managers +========================= +To use multiple experiment managers at the same time, pass a list to the *logger* :class:`~lightning.pytorch.trainer.trainer.Trainer` argument. + +.. testcode:: + :skipif: (not _TENSORBOARD_AVAILABLE and not _TENSORBOARDX_AVAILABLE) or not _WANDB_AVAILABLE + + from lightning.pytorch.loggers import TensorBoardLogger, WandbLogger + + logger1 = TensorBoardLogger() + logger2 = WandbLogger() + trainer = Trainer(logger=[logger1, logger2]) + + +Access all loggers from any function (except the LightningModule *init*) to use their APIs for tracking advanced artifacts + +.. code-block:: python + + class MyModule(LightningModule): + def any_lightning_module_function_or_hook(self): + tensorboard_logger = self.loggers.experiment[0] + wandb_logger = self.loggers.experiment[1] + + fake_images = torch.Tensor(32, 3, 28, 28) + + tensorboard_logger.add_image("generated_images", fake_images, 0) + wandb_logger.add_image("generated_images", fake_images, 0) diff --git a/docs/starter/converting.html b/docs/starter/converting.html deleted file mode 100644 index 0581990..0000000 --- a/docs/starter/converting.html +++ /dev/null @@ -1,877 +0,0 @@ - - - - - - - - - - - - - - How to Organize PyTorch Into Lightning — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • How to Organize PyTorch Into Lightning
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

How to Organize PyTorch Into Lightning

-

To enable your code to work with Lightning, perform the following to organize PyTorch into Lightning.

-
-
-

1. Keep you Computational Code

-

Keep your regular nn.Module architecture

-
import pytorch_lightning as pl
-import torch
-import torch.nn as nn
-import torch.nn.functional as F
-
-
-class LitModel(nn.Module):
-    def __init__(self):
-        super().__init__()
-        self.layer_1 = nn.Linear(28 * 28, 128)
-        self.layer_2 = nn.Linear(128, 10)
-
-    def forward(self, x):
-        x = x.view(x.size(0), -1)
-        x = self.layer_1(x)
-        x = F.relu(x)
-        x = self.layer_2(x)
-        return x
-
-
-
-
-
-

2. Configure Training Logic

-

In the training_step of the LightningModule configure how your training routine behaves with a batch of training data:

-
class LitModel(pl.LightningModule):
-    def __init__(self, encoder):
-        super().__init__()
-        self.encoder = encoder
-
-    def training_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.encoder(x)
-        loss = F.cross_entropy(y_hat, y)
-        return loss
-
-
-
-

Note

-

If you need to fully own the training loop for complicated legacy projects, check out Own your loop.

-
-
-
-
-

3. Move Optimizer(s) and LR Scheduler(s)

-

Move your optimizers to the configure_optimizers() hook.

-
class LitModel(pl.LightningModule):
-    def configure_optimizers(self):
-        optimizer = torch.optim.Adam(self.encoder.parameters(), lr=1e-3)
-        lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1)
-        return [optimizer], [lr_scheduler]
-
-
-
-
-
-

4. Organize Validation Logic (optional)

-

If you need a validation loop, configure how your validation routine behaves with a batch of validation data:

-
class LitModel(pl.LightningModule):
-    def validation_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.encoder(x)
-        val_loss = F.cross_entropy(y_hat, y)
-        self.log("val_loss", val_loss)
-
-
-
-

Tip

-

trainer.validate() loads the best checkpoint automatically by default if checkpointing was enabled during fitting.

-
-
-
-
-

5. Organize Testing Logic (optional)

-

If you need a test loop, configure how your testing routine behaves with a batch of test data:

-
class LitModel(pl.LightningModule):
-    def test_step(self, batch, batch_idx):
-        x, y = batch
-        y_hat = self.encoder(x)
-        test_loss = F.cross_entropy(y_hat, y)
-        self.log("test_loss", test_loss)
-
-
-
-
-
-

6. Configure Prediction Logic (optional)

-

If you need a prediction loop, configure how your prediction routine behaves with a batch of test data:

-
class LitModel(LightningModule):
-    def predict_step(self, batch, batch_idx):
-        x, y = batch
-        pred = self.encoder(x)
-        return pred
-
-
-
-
-
-

7. Remove any .cuda() or .to(device) Calls

-

Your LightningModule can automatically run on any hardware!

-

If you have any explicit calls to .cuda() or .to(device), you can remove them since Lightning makes sure that the data coming from DataLoader -and all the Module instances initialized inside LightningModule.__init__ are moved to the respective devices automatically. -If you still need to access the current device, you can use self.device anywhere in your LightningModule except in the __init__ and setup methods.

-
class LitModel(LightningModule):
-    def training_step(self, batch, batch_idx):
-        z = torch.randn(4, 5, device=self.device)
-        ...
-
-
-

Hint: If you are initializing a Tensor within the LightningModule.__init__ method and want it to be moved to the device automatically you should call -register_buffer() to register it as a parameter.

-
class LitModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.register_buffer("running_mean", torch.zeros(num_features))
-
-
-
-
-
-

8. Use your own data

-

Regular PyTorch DataLoaders work with Lightning. For more modular and scalable datasets, check out LightningDataModule.

-
-
-
-

Good to know

-

Additionally, you can run only the validation loop using validate() method.

-
model = LitModel()
-trainer.validate(model)
-
-
-
-

Note

-

model.eval() and torch.no_grad() are called automatically for validation.

-
-

The test loop isn’t used within fit(), therefore, you would need to explicitly call test().

-
model = LitModel()
-trainer.test(model)
-
-
-
-

Note

-

model.eval() and torch.no_grad() are called automatically for testing.

-
-
-

Tip

-

trainer.test() loads the best checkpoint automatically by default if checkpointing is enabled.

-
-

The predict loop will not be used until you call predict().

-
model = LitModel()
-trainer.predict(model)
-
-
-
-

Note

-

model.eval() and torch.no_grad() are called automatically for testing.

-
-
-

Tip

-

trainer.predict() loads the best checkpoint automatically by default if checkpointing is enabled.

-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/starter/installation.html b/docs/starter/installation.html deleted file mode 100644 index 908f4b9..0000000 --- a/docs/starter/installation.html +++ /dev/null @@ -1,738 +0,0 @@ - - - - - - - - - - - - - - Installation — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Installation

-
-
-

Installation with pip

-

Install any supported version of PyTorch if you want from PyTorch Installation Page. -Now you can install using pip using the following command:

-
pip install pytorch-lightning
-
-
-
-
-
-

Installation with Conda

-

If you don’t have conda installed, follow the Conda Installation Guide. -Lightning can be installed with conda using the following command:

-
conda install pytorch-lightning -c conda-forge
-
-
-

You can also use Conda Environments:

-
conda activate my_env
-conda install pytorch-lightning -c conda-forge
-
-
-
-
-
-

Installation from Source

-

Install nightly from the source. Note that it contains all the bug fixes and newly released features that -are not published yet. This is the bleeding edge, so use it at your own discretion.

-
pip install https://github.com/PyTorchLightning/pytorch-lightning/archive/master.zip
-
-
-

Install future patch releases from the source. Note that the patch release contains only the bug fixes for the recent major release.

-
pip install https://github.com/PyTorchLightning/pytorch-lightning/archive/refs/heads/release/1.5.x.zip
-
-
-
-
-
-

Lightning Coverage

-

PyTorch Lightning is maintained and tested on different Python and PyTorch versions.

-

Check out the CI Coverage for more info.

-

It is rigorously tested across multiple GPUs, TPUs, CPUs and IPUs. GPU tests run on two NVIDIA P100. TPU tests run on Google GKE TPUv2/3. -TPU py3.7 means we support Colab and Kaggle env. IPU tests run on MK1 IPU boxes.

-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/starter/introduction.html b/docs/starter/introduction.html deleted file mode 100644 index 1b6c6c8..0000000 --- a/docs/starter/introduction.html +++ /dev/null @@ -1,1038 +0,0 @@ - - - - - - - - - - - - - - Lightning 15분 만에 배워보기 — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Lightning 15분 만에 배워보기
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Lightning 15분 만에 배워보기

-

필요한 배경지식: 없음

-

목표: 이 문서에서는 일반적인 Lightning 워크플로우의 주요한 7단계를 안내합니다.

-

PyTorch Lightning(파이토치 라이트닝)은 대규모로 엄청 빠른 성능을 요구하면서 최대한의 유연성을 필요로 하는 -전문적인 AI 연구자들과 머신러닝 엔지니어들을 위한 “배터리가 포함된(batteries included)” 딥러닝 프레임워크입니다.

-

-

Lightning(라이트닝)은 반복적으로 사용하는 코드(boilerplate)를 제거하고 확장성(scalability)을 확보하도록 PyTorch 코드를 재구성합니다.

-
-

-
-

PyTorch 코드를 재구성함으로써, Lightning에서는 이런 것들이 가능해집니다:

-
-

- -
-
-

완전한 유연성

-

반복되는 코드 없이 PyTorch를 그대로 사용하여 아이디어를 구현합니다.

-
- - -

-

- -
-
-

재현성 + 가독성

-

연구용 코드와 엔지니어링 코드를 분리하여 재현성을 갖추고 더 나은 가독성을 제공합니다.

-
- - -

-

- -
-
-

간단한 다중 GPU 학습

-

코드 변경 없이 여러개의 GPU/TPU/HPU 등을 사용합니다.

-
- - -

-

- -
-
-

테스트 완료

-

이미 모든 테스트를 완료하여 직접 테스트 할 필요없습니다.

-
- - -

-
-

-
-

1: PyTorch Lightning 설치하기

-
-

pip 사용자라면,

-
pip install pytorch-lightning
-
-
-
-

conda 사용자라면,

-
conda install pytorch-lightning -c conda-forge
-
-
-
-

또는 advanced install guide 를 참조하세요.

-
-
-
-

2: LightningModule 정의하기

-

LightningModule을 사용하여 PyTorch nn.Module이 training_step (뿐만 아니라 validation_step이나 test_step) 내에서 복잡한 방식으로 함께 동작할 수 있도록 합니다.

-
import os
-from torch import optim, nn, utils, Tensor
-from tests.helpers.datasets import MNIST
-import pytorch_lightning as pl
-
-# 원하는만큼의 nn.Module (또는 기존 모델)을 정의합니다.
-encoder = nn.Sequential(nn.Linear(28 * 28, 64), nn.ReLU(), nn.Linear(64, 3))
-decoder = nn.Sequential(nn.Linear(3, 64), nn.ReLU(), nn.Linear(64, 28 * 28))
-
-# LightningModule을 정의합니다.
-class LitAutoEncoder(pl.LightningModule):
-    def __init__(self, encoder, decoder):
-        super().__init__()
-        self.encoder = encoder
-        self.decoder = decoder
-
-    def training_step(self, batch, batch_idx):
-        # training_step defines the train loop.
-        # it is independent of forward
-        x, y = batch
-        x = x.view(x.size(0), -1)
-        z = self.encoder(x)
-        x_hat = self.decoder(z)
-        loss = nn.functional.mse_loss(x_hat, x)
-        # Logging to TensorBoard by default
-        self.log("train_loss", loss)
-        return loss
-
-    def configure_optimizers(self):
-        optimizer = optim.Adam(self.parameters(), lr=1e-3)
-        return optimizer
-
-
-# 오토인코더(autoencoder)를 초기화합니다.
-autoencoder = LitAutoEncoder(encoder, decoder)
-
-
-
-
-
-

3: 데이터셋 정의하기

-

Lightning은 어떠한 순회 가능한 객체(iterable; DataLoader, numpy 등…)도 학습/검증/테스트/예측용으로 나누어 사용할 수 있습니다.

-
# 데이터를 설정합니다.
-dataset = MNIST(os.getcwd(), download=True)
-train_loader = utils.data.DataLoader(dataset)
-
-
-
-
-
-

4: 모델 학습하기

-

Lightning Trainer 는 모든 LightningModule 과 데이터셋을 “함께(mix)” 학습할 수 있으며, -확장에 필요한 모든 엔지니어링적 복잡성들을 추상화(abstract)합니다.

-
# 모델을 학습합니다 (힌트: 빠른 아이디어 반복에 도움이 되는 Trainer의 인자들을 참고하세요)
-trainer = pl.Trainer(limit_train_batches=100, max_epochs=1)
-trainer.fit(model=autoencoder, train_dataloaders=train_loader)
-
-
-

Lightning Trainer 는 아래 예시들을 포함하여 40종류 이상의 기법들 을 자동화합니다:

- -
-
-
-

5: 모델 사용하기

-

모델을 학습한 뒤에는 ONNX, TorchScript로 내보내기(export)하여 상용 환경에 포함하거나 단순히 가중치를 불러오고 예측을 실행할 수 있습니다.

-
# 체크포인트(checkpoint)를 불러옵니다.
-checkpoint = "./lightning_logs/version_0/checkpoints/epoch=0-step=100.ckpt"
-autoencoder = LitAutoEncoder.load_from_checkpoint(checkpoint, encoder=encoder, decoder=decoder)
-
-# 학습한 nn.Module을 선택합니다.
-encoder = autoencoder.encoder
-encoder.eval()
-
-# 4개의 가짜 이미지로 예측(embed)합니다!
-fake_image_batch = Tensor(4, 28 * 28)
-embeddings = encoder(fake_image_batch)
-print("⚡" * 20, "\nPredictions (4 image embeddings):\n", embeddings, "\n", "⚡" * 20)
-
-
-
-
-
-

6: 학습 시각화하기

-

Lightning에는 많은 배터리가 포함되어 있습니다. 실험을 시각화하는데 사용하는 텐서보드(Tensorboard)도 유용한 도구 중 하나입니다.

-

명령줄(commandline)에서 아래를 실행하고 브라우저에서 http://localhost:6006/ 을 열어보세요.

-
tensorboard --logdir .
-
-
-
-
-
-

7: 엄청 빠르게 학습하기

-

Trainer에 인자(argument)를 사용하여 고급 학습 기능을 사용할 수 있습니다. 이는 다른 코드를 변경하지 않으면서 학습 단계(train loop)에 자동으로 통합할 수 있도록 하는 최신(state-of-the-art)의 기술입니다.

-
# 4개의 GPU에서 학습
-trainer = Trainer(
-    devices=4,
-    accelerator="gpu",
- )
-
-# Deepspeed/FSDP를 사용하여 1TB 이상의 매개변수를 갖는 모델 학습
-trainer = Trainer(
-    devices=4,
-    accelerator="gpu",
-    strategy="deepspeed_stage_2",
-    precision=16
- )
-
-# 빠른 아이디어 반복을 위한 20개 이상의 유용한 플래그(flag)
-trainer = Trainer(
-    max_epochs=10,
-    min_epochs=5,
-    overfit_batches=1
- )
-
-# 최신 기술을 사용
-trainer = Trainer(callbacks=[StochasticWeightAveraging(...)])
-
-
-
-
-
-

유연성 극대화하기

-

Lightning의 핵심 원칙은 PyTorch의 어떠한 부분도 숨기지 않으면서 언제나 최대한의 유연성을 제공하는 것입니다.

-

Lightning은 프로젝트의 복잡도에 따라 추가적인 5단계의 유연성을 제공합니다.

-
-
-

학습 단계(loop) 사용자 정의하기

-Injecting custom code in a training loop -

LightningModule에서 사용할 수 있는 20개 이상의 메소드 (Hooks) 중 일부를 사용하여 훈련 단계 어디에든 사용자 정의 코드를 삽입할 수 있습니다.

-
class LitAutoEncoder(pl.LightningModule):
-    def backward(self, loss, optimizer, optimizer_idx):
-        loss.backward()
-
-
-
-
-
-

Trainer 확장하기

-

유사한 기능을 하는 여러줄의 코드가 있는 경우, 콜백(callback)을 사용하여 손쉽게 그룹으로 묶어서 해당하는 코드들을 동시에 켜거나 끌 수 있습니다.

-
trainer = Trainer(callbacks=[AWSCheckpoints()])
-
-
-
-
-
-

PyTorch 자체의 반복(loop) 사용하기

-

최첨단 연구 시 특정 유형의 작업들을 위해, Lightning은 전문가들이 다양한 방식으로 학습 단계를 완전히 제어할 수 있는 기능을 제공합니다.

-
-
-
-
-

다음 단계

-

사용 사례에 따라, 아래 내용들 중 하나를 다음 단계로 살펴보세요.

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/starter/lightning_lite.html b/docs/starter/lightning_lite.html deleted file mode 100644 index 3eed1ab..0000000 --- a/docs/starter/lightning_lite.html +++ /dev/null @@ -1,1340 +0,0 @@ - - - - - - - - - - - - - - LightningLite (Stepping Stone to Lightning) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • LightningLite (Stepping Stone to Lightning)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

LightningLite (Stepping Stone to Lightning)

-

LightningLite enables pure PyTorch users to scale their existing code -on any kind of device while retaining full control over their own loops and optimization logic.

-Animation showing how to convert your PyTorch code to LightningLite. -
-

-
-

LightningLite is the right tool for you if you match one of the two following descriptions:

-
    -
  • I want to quickly scale my existing code to multiple devices with minimal code changes.

  • -
  • I would like to convert my existing code to the Lightning API, but a full path to Lightning transition might be too complex. I am looking for a stepping stone to ensure reproducibility during the transition.

  • -
-
-

Warning

-

LightningLite is currently a beta feature. Its API is subject to change based on your feedback.

-
-
-
-

Learn by example

-
-

My Existing PyTorch Code

-

The run function contains custom training loop used to train MyModel on MyDataset for num_epochs epochs.

-
import torch
-from torch import nn
-from torch.utils.data import DataLoader, Dataset
-
-
-class MyModel(nn.Module):
-    ...
-
-
-class MyDataset(Dataset):
-    ...
-
-
-def run(args):
-    device = "cuda" if torch.cuda.is_available() else "cpu"
-
-    model = MyModel(...).to(device)
-    optimizer = torch.optim.SGD(model.parameters(), ...)
-
-    dataloader = DataLoader(MyDataset(...), ...)
-
-    model.train()
-    for epoch in range(args.num_epochs):
-        for batch in dataloader:
-            batch = batch.to(device)
-            optimizer.zero_grad()
-            loss = model(batch)
-            loss.backward()
-            optimizer.step()
-
-
-run(args)
-
-
-
-
-
-

Convert to LightningLite

-

Here are five required steps to convert to LightningLite.

-
    -
  1. Subclass LightningLite and override its run() method.

  2. -
  3. Move the body of your existing run function into LightningLite run method.

  4. -
  5. Remove all .to(...), .cuda() etc calls since LightningLite will take care of it.

  6. -
  7. Apply setup() over each model and optimizers pair and setup_dataloaders() on all your dataloaders and replace loss.backward() by self.backward(loss).

  8. -
  9. Instantiate your LightningLite subclass and call its run() method.

  10. -
-
-

-
-
import torch
-from torch import nn
-from torch.utils.data import DataLoader, Dataset
-from pytorch_lightning.lite import LightningLite
-
-
-class MyModel(nn.Module):
-    ...
-
-
-class MyDataset(Dataset):
-    ...
-
-
-class Lite(LightningLite):
-    def run(self, args):
-
-        model = MyModel(...)
-        optimizer = torch.optim.SGD(model.parameters(), ...)
-        model, optimizer = self.setup(model, optimizer)  # Scale your model / optimizers
-
-        dataloader = DataLoader(MyDataset(...), ...)
-        dataloader = self.setup_dataloaders(dataloader)  # Scale your dataloaders
-
-        model.train()
-        for epoch in range(args.num_epochs):
-            for batch in dataloader:
-                optimizer.zero_grad()
-                loss = model(batch)
-                self.backward(loss)  # instead of loss.backward()
-                optimizer.step()
-
-
-Lite(...).run(args)
-
-
-

That’s all. You can now train on any kind of device and scale your training. Check out this full MNIST training example with LightningLite.

-

LightningLite takes care of device management, so you don’t have to. -You should remove any device-specific logic within your code.

-

Here is how to train on eight GPUs with torch.bfloat16 precision:

-
Lite(strategy="ddp", devices=8, accelerator="gpu", precision="bf16").run(10)
-
-
-

Here is how to use DeepSpeed Zero3 with eight GPUs and precision 16:

-
Lite(strategy="deepspeed", devices=8, accelerator="gpu", precision=16).run(10)
-
-
-

LightningLite can also figure it out automatically for you!

-
Lite(devices="auto", accelerator="auto", precision=16).run(10)
-
-
-

You can also easily use distributed collectives if required. -Here is an example while running on 256 GPUs (eight GPUs times 32 nodes).

-
class Lite(LightningLite):
-    def run(self):
-
-        # Transfer and concatenate tensors across processes
-        self.all_gather(...)
-
-        # Transfer an object from one process to all the others
-        self.broadcast(..., src=...)
-
-        # The total number of processes running across all devices and nodes.
-        self.world_size
-
-        # The global index of the current process across all devices and nodes.
-        self.global_rank
-
-        # The index of the current process among the processes running on the local node.
-        self.local_rank
-
-        # The index of the current node.
-        self.node_rank
-
-        # Wether this global rank is rank zero.
-        if self.is_global_zero:
-            # do something on rank 0
-            ...
-
-        # Wait for all processes to enter this call.
-        self.barrier()
-
-
-Lite(strategy="ddp", devices=8, num_nodes=32, accelerator="gpu").run()
-
-
-

If you require custom data or model device placement, you can deactivate -LightningLite automatic placement by doing -self.setup_dataloaders(..., move_to_device=False) for the data and -self.setup(..., move_to_device=False) for the model. -Furthermore, you can access the current device from self.device or -rely on to_device() -utility to move an object to the current device.

-
-

Note

-

We recommend instantiating the models within the run() method as large models would cause an out-of-memory error otherwise.

-
-
-

Tip

-

If you have hundreds or thousands of lines within your run() function -and you are feeling unsure about them, then that is the correct feeling. -In 2019, our LightningModule was getting larger -and we got the same feeling, so we started to organize our code for simplicity, interoperability and standardization. -This is definitely a good sign that you should consider refactoring your code and / or switching to -LightningModule ultimately.

-
-
-
-
-

Distributed Training Pitfalls

-

The LightningLite provides you with the tools to scale your training, -but there are several major challenges ahead of you now:

- ---- - - - - - - - - - - - - - - - - - - - - -

Processes divergence

This happens when processes execute a different section of the code due to different if/else conditions, race conditions on existing files and so on, resulting in hanging.

Cross processes reduction

Miscalculated metrics or gradients due to errors in their reduction.

Large sharded models

Instantiation, materialization and state management of large models.

Rank 0 only actions

Logging, profiling, and so on.

Checkpointing / Early stopping / Callbacks / Logging

Ability to customize your training behavior easily and make it stateful.

Fault-tolerant training

Ability to resume from a failure as if it never happened.

-

If you are facing one of those challenges, then you are already meeting the limit of LightningLite. -We recommend you to convert to Lightning, so you never have to worry about those.

-
-
-
-

Convert to Lightning

-

LightningLite is a stepping stone to transition fully to the Lightning API and benefit -from its hundreds of features.

-

You can see our LightningLite class as a -future LightningModule, and slowly refactor your code into its API. -Below, the training_step(), forward(), -configure_optimizers(), train_dataloader() methods -are implemented.

-
class Lite(LightningLite):
-
-    # 1. This would become the LightningModule `__init__` function.
-    def run(self, args):
-        self.args = args
-
-        self.model = MyModel(...)
-
-        self.fit()  # This would be automated by the Lightning Trainer.
-
-    # 2. This can be fully removed as Lightning creates its own fitting loop,
-    # and sets up the model, optimizer, dataloader, etc for you.
-    def fit(self):
-        # setup everything
-        optimizer = self.configure_optimizers()
-        self.model, optimizer = self.setup(self.model, optimizer)
-        dataloader = self.setup_dataloaders(self.train_dataloader())
-
-        # start fitting
-        self.model.train()
-        for epoch in range(num_epochs):
-            for batch in enumerate(dataloader):
-                optimizer.zero_grad()
-                loss = self.training_step(batch, batch_idx)
-                self.backward(loss)
-                optimizer.step()
-
-    # 3. This stays here as it belongs to the LightningModule.
-    def forward(self, x):
-        return self.model(x)
-
-    def training_step(self, batch, batch_idx):
-        return self.forward(batch)
-
-    def configure_optimizers(self):
-        return torch.optim.SGD(self.model.parameters(), ...)
-
-    # 4. [Optionally] This can stay here or be extracted to the LightningDataModule to enable higher composability.
-    def train_dataloader(self):
-        return DataLoader(MyDataset(...), ...)
-
-
-Lite(...).run(args)
-
-
-

Finally, change the run() into a -__init__() and drop the fit call from inside.

-
from pytorch_lightning import LightningDataModule, LightningModule, Trainer
-
-
-class LightningModel(LightningModule):
-    def __init__(self, args):
-        super().__init__()
-        self.model = MyModel(...)
-
-    def forward(self, x):
-        return self.model(x)
-
-    def training_step(self, batch, batch_idx):
-        loss = self(batch)
-        self.log("train_loss", loss)
-        return loss
-
-    def configure_optimizers(self):
-        return torch.optim.SGD(self.model.parameters(), lr=0.001)
-
-
-class BoringDataModule(LightningDataModule):
-    def train_dataloader(self):
-        return DataLoader(MyDataset(...), ...)
-
-
-trainer = Trainer(max_epochs=10)
-trainer.fit(LightningModel(), datamodule=BoringDataModule())
-
-
-

You have successfully converted to PyTorch Lightning, and can now benefit from its hundred of features!

-
-
-
-
-

Lightning Lite Flags

-

Lite is specialized in accelerated distributed training and inference. It offers you convenient ways to configure -your device and communication strategy and to switch seamlessly from one to the other. The terminology and usage are -identical to Lightning, which means minimum effort for you to convert when you decide to do so.

-
-

accelerator

-

Choose one of "cpu", "gpu", "tpu", "auto" (IPU support is coming soon).

-
# CPU accelerator
-lite = Lite(accelerator="cpu")
-
-# Running with GPU Accelerator using 2 GPUs
-lite = Lite(devices=2, accelerator="gpu")
-
-# Running with TPU Accelerator using 8 tpu cores
-lite = Lite(devices=8, accelerator="tpu")
-
-# Running with GPU Accelerator using the DistributedDataParallel strategy
-lite = Lite(devices=4, accelerator="gpu", strategy="ddp")
-
-
-

The "auto" option recognizes the machine you are on and selects the available accelerator.

-
# If your machine has GPUs, it will use the GPU Accelerator
-lite = Lite(devices=2, accelerator="auto")
-
-
-
-
-

strategy

-

Choose a training strategy: "dp", "ddp", "ddp_spawn", "tpu_spawn", "deepspeed", "ddp_sharded", or "ddp_sharded_spawn".

-
# Running with the DistributedDataParallel strategy on 4 GPUs
-lite = Lite(strategy="ddp", accelerator="gpu", devices=4)
-
-# Running with the DDP Spawn strategy using 4 cpu processes
-lite = Lite(strategy="ddp_spawn", accelerator="cpu", devices=4)
-
-
-

Additionally, you can pass in your custom strategy by configuring additional parameters.

-
from pytorch_lightning.strategies import DeepSpeedStrategy
-
-lite = Lite(strategy=DeepSpeedStrategy(stage=2), accelerator="gpu", devices=2)
-
-
-

Support for Horovod and Fully Sharded training strategies are coming soon.

-
-
-

devices

-

Configure the devices to run on. Can be of type:

-
    -
  • int: the number of devices (e.g., GPUs) to train on

  • -
  • list of int: which device index (e.g., GPU ID) to train on (0-indexed)

  • -
  • str: a string representation of one of the above

  • -
-
# default used by Lite, i.e., use the CPU
-lite = Lite(devices=None)
-
-# equivalent
-lite = Lite(devices=0)
-
-# int: run on two GPUs
-lite = Lite(devices=2, accelerator="gpu")
-
-# list: run on GPUs 1, 4 (by bus ordering)
-lite = Lite(devices=[1, 4], accelerator="gpu")
-lite = Lite(devices="1, 4", accelerator="gpu")  # equivalent
-
-# -1: run on all GPUs
-lite = Lite(devices=-1, accelerator="gpu")
-lite = Lite(devices="-1", accelerator="gpu")  # equivalent
-
-
-
-
-

gpus

-
-

Warning

-

gpus=x has been deprecated in v1.7 and will be removed in v2.0. -Please use accelerator='gpu' and devices=x instead.

-
-

Shorthand for setting devices=X and accelerator="gpu".

-
# Run on two GPUs
-lite = Lite(accelerator="gpu", devices=2)
-
-# Equivalent
-lite = Lite(devices=2, accelerator="gpu")
-
-
-
-
-

tpu_cores

-
-

Warning

-

tpu_cores=x has been deprecated in v1.7 and will be removed in v2.0. -Please use accelerator='tpu' and devices=x instead.

-
-

Shorthand for devices=X and accelerator="tpu".

-
# Run on eight TPUs
-lite = Lite(accelerator="tpu", devices=8)
-
-# Equivalent
-lite = Lite(devices=8, accelerator="tpu")
-
-
-
-
-

num_nodes

-

Number of cluster nodes for distributed operation.

-
# Default used by Lite
-lite = Lite(num_nodes=1)
-
-# Run on 8 nodes
-lite = Lite(num_nodes=8)
-
-
-

Learn more about distributed multi-node training on clusters here.

-
-
-

precision

-

Lightning Lite supports double precision (64), full precision (32), or half precision (16) operation (including bfloat16). -Half precision, or mixed precision, is the combined use of 32 and 16-bit floating points to reduce the memory footprint during model training. -This can result in improved performance, achieving significant speedups on modern GPUs.

-
# Default used by the Lite
-lite = Lite(precision=32, devices=1)
-
-# 16-bit (mixed) precision
-lite = Lite(precision=16, devices=1)
-
-# 16-bit bfloat precision
-lite = Lite(precision="bf16", devices=1)
-
-# 64-bit (double) precision
-lite = Lite(precision=64, devices=1)
-
-
-
-
-

plugins

-

Plugins allow you to connect arbitrary backends, precision libraries, clusters etc. For example: -To define your own behavior, subclass the relevant class and pass it in. Here’s an example linking up your own -ClusterEnvironment.

-
from pytorch_lightning.plugins.environments import ClusterEnvironment
-
-
-class MyCluster(ClusterEnvironment):
-    @property
-    def main_address(self):
-        return your_main_address
-
-    @property
-    def main_port(self):
-        return your_main_port
-
-    def world_size(self):
-        return the_world_size
-
-
-lite = Lite(plugins=[MyCluster()], ...)
-
-
-
-
-
-
-

Lightning Lite Methods

-
-

run

-

The run method serves two purposes:

-
    -
  1. Override this method from the LightningLite class and put your -training (or inference) code inside.

  2. -
  3. Launch the training procedure by calling the run method. Lite will take care of setting up the distributed backend.

  4. -
-

You can optionally pass arguments to the run method. For example, the hyperparameters or a backbone for the model.

-
from pytorch_lightning.lite import LightningLite
-
-
-class Lite(LightningLite):
-
-    # Input arguments are optional; put whatever you need
-    def run(self, learning_rate, num_layers):
-        """Here goes your training loop"""
-
-
-lite = Lite(accelerator="gpu", devices=2)
-lite.run(learning_rate=0.01, num_layers=12)
-
-
-
-
-

setup

-

Set up a model and corresponding optimizer(s). If you need to set up multiple models, call setup() on each of them. -Moves the model and optimizer to the correct device automatically.

-
model = nn.Linear(32, 64)
-optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
-
-# Set up model and optimizer for accelerated training
-model, optimizer = self.setup(model, optimizer)
-
-# If you don't want Lite to set the device
-model, optimizer = self.setup(model, optimizer, move_to_device=False)
-
-
-

The setup method also prepares the model for the selected precision choice so that operations during forward() get -cast automatically.

-
-
-

setup_dataloaders

-

Set up one or multiple dataloaders for accelerated operation. If you are running a distributed strategy (e.g., DDP), Lite -replaces the sampler automatically for you. In addition, the dataloader will be configured to move the returned -data tensors to the correct device automatically.

-
train_data = torch.utils.DataLoader(train_dataset, ...)
-test_data = torch.utils.DataLoader(test_dataset, ...)
-
-train_data, test_data = self.setup_dataloaders(train_data, test_data)
-
-# If you don't want Lite to move the data to the device
-train_data, test_data = self.setup_dataloaders(train_data, test_data, move_to_device=False)
-
-# If you don't want Lite to replace the sampler in the context of distributed training
-train_data, test_data = self.setup_dataloaders(train_data, test_data, replace_sampler=False)
-
-
-
-
-

backward

-

This replaces any occurrences of loss.backward() and makes your code accelerator and precision agnostic.

-
output = model(input)
-loss = loss_fn(output, target)
-
-# loss.backward()
-self.backward(loss)
-
-
-
-
-

to_device

-

Use to_device() to move models, tensors or collections of tensors to -the current device. By default setup() and -setup_dataloaders() already move the model and data to the correct -device, so calling this method is only necessary for manual operation when needed.

-
data = torch.load("dataset.pt")
-data = self.to_device(data)
-
-
-
-
-

seed_everything

-

Make your code reproducible by calling this method at the beginning of your run.

-
# Instead of `torch.manual_seed(...)`, call:
-self.seed_everything(1234)
-
-
-

This covers PyTorch, NumPy and Python random number generators. In addition, Lite takes care of properly initializing -the seed of dataloader worker processes (can be turned off by passing workers=False).

-
-
-

autocast

-

Let the precision backend autocast the block of code under this context manager. This is optional and already done by -Lite for the model’s forward method (once the model was setup()). -You need this only if you wish to autocast more operations outside the ones in model forward:

-
model, optimizer = self.setup(model, optimizer)
-
-# Lite handles precision automatically for the model
-output = model(inputs)
-
-with self.autocast():  # optional
-    loss = loss_function(output, target)
-
-self.backward(loss)
-...
-
-
-
-
-

print

-

Print to the console via the built-in print function, but only on the main process. -This avoids excessive printing and logs when running on multiple devices/nodes.

-
# Print only on the main process
-self.print(f"{epoch}/{num_epochs}| Train Epoch Loss: {loss}")
-
-
-
-
-

save

-

Save contents to a checkpoint. Replaces all occurrences of torch.save(...) in your code. Lite will take care of -handling the saving part correctly, no matter if you are running a single device, multi-devices or multi-nodes.

-
# Instead of `torch.save(...)`, call:
-self.save(model.state_dict(), "path/to/checkpoint.ckpt")
-
-
-
-
-

load

-

Load checkpoint contents from a file. Replaces all occurrences of torch.load(...) in your code. Lite will take care of -handling the loading part correctly, no matter if you are running a single device, multi-device, or multi-node.

-
# Instead of `torch.load(...)`, call:
-self.load("path/to/checkpoint.ckpt")
-
-
-
-
-

barrier

-

Call this if you want all processes to wait and synchronize. Once all processes have entered this call, -execution continues. Useful for example when you want to download data on one process and make all others wait until -the data is written to disk.

-
# Download data only on one process
-if self.global_rank == 0:
-    download_data("http://...")
-
-# Wait until all processes meet up here
-self.barrier()
-
-# All processes are allowed to read the data now
-
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/starter/style_guide.html b/docs/starter/style_guide.html deleted file mode 100644 index bcc555d..0000000 --- a/docs/starter/style_guide.html +++ /dev/null @@ -1,908 +0,0 @@ - - - - - - - - - - - - - - 스타일 가이드 — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • 스타일 가이드
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

스타일 가이드

-

파이토치 라이트닝(PyTorch Lightning)의 주요한 목표는 가독성과 재현성을 개선하는 것입니다. GitHub 저장소나 연구 프로젝트에서 -LightningModule 을 발견하고, 관심있는 부분을 찾기 위해 정확히 어디를 봐야할지 정확히 알고 있다고 상상해보세요.

-

이 스타일 가이드의 목표는 Lightning의 코드가 유사하게 구성되도록 권장하는데 있습니다.

-
-
-

LightningModule

-

These are best practices for structuring your LightningModule class:

-
-

Systems vs Models

-
-https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/pl_docs/model_system.png -
-

The main principle behind a LightningModule is that a full system should be self-contained. -In Lightning, we differentiate between a system and a model.

-

A model is something like a resnet18, RNN, and so on.

-

A system defines how a collection of models interact with each other with user-defined training/evaluation logic. Examples of this are:

-
    -
  • GANs

  • -
  • Seq2Seq

  • -
  • BERT

  • -
  • etc.

  • -
-

A LightningModule can define both a system and a model:

-

Here’s a LightningModule that defines a system. This structure is what we recommend as a best practice. Keeping the model separate from the system improves -modularity, which eventually helps in better testing, reduces dependencies on the system and makes it easier to refactor.

-
class Encoder(nn.Module):
-    ...
-
-
-class Decoder(nn.Module):
-    ...
-
-
-class AutoEncoder(nn.Module):
-    def __init__(self):
-        super().__init__()
-        self.encoder = Encoder()
-        self.decoder = Decoder()
-
-    def forward(self, x):
-        return self.encoder(x)
-
-
-class AutoEncoderSystem(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.auto_encoder = AutoEncoder()
-
-
-

For fast prototyping, it’s often useful to define all the computations in a LightningModule. For reusability -and scalability, it might be better to pass in the relevant backbones.

-

Here’s a LightningModule that defines a model. Although, we do not recommend to define a model like in the example.

-
class LitModel(LightningModule):
-    def __init__(self):
-        super().__init__()
-        self.layer_1 = nn.Linear()
-        self.layer_2 = nn.Linear()
-        self.layer_3 = nn.Linear()
-
-
-
-
-

Self-contained

-

A Lightning module should be self-contained. To see how self-contained your model is, a good test is to ask -yourself this question:

-

“Can someone drop this file into a Trainer without knowing anything about the internals?”

-

For example, we couple the optimizer with a model because the majority of models require a specific optimizer with -a specific learning rate scheduler to work well.

-
-
-

Init

-

The first place where LightningModules tend to stop being self-contained is in the init. Try to define all the relevant -sensible defaults in the init so that the user doesn’t have to guess.

-

Here’s an example where a user will have to go hunt through files to figure out how to init this LightningModule.

-
class LitModel(LightningModule):
-    def __init__(self, params):
-        self.lr = params.lr
-        self.coef_x = params.coef_x
-
-
-

Models defined as such leave you with many questions, such as what is coef_x? Is it a string? A float? What is the range? -Instead, be explicit in your init

-
class LitModel(LightningModule):
-    def __init__(self, encoder: nn.Module, coef_x: float = 0.2, lr: float = 1e-3):
-        ...
-
-
-

Now the user doesn’t have to guess. Instead, they know the value type, and the model has a sensible default where the -user can see the value immediately.

-
-
-

Method Order

-

The only required methods in the LightningModule are:

-
    -
  • init

  • -
  • training_step

  • -
  • configure_optimizers

  • -
-

However, if you decide to implement the rest of the optional methods, the recommended order is:

-
    -
  • model/system definition (init)

  • -
  • if doing inference, define forward

  • -
  • training hooks

  • -
  • validation hooks

  • -
  • test hooks

  • -
  • predict hooks

  • -
  • configure_optimizers

  • -
  • any other hooks

  • -
-

In practice, the code looks like this:

-
class LitModel(pl.LightningModule):
-
-    def __init__(...):
-
-    def forward(...):
-
-    def training_step(...):
-
-    def training_step_end(...):
-
-    def training_epoch_end(...):
-
-    def validation_step(...):
-
-    def validation_step_end(...):
-
-    def validation_epoch_end(...):
-
-    def test_step(...):
-
-    def test_step_end(...):
-
-    def test_epoch_end(...):
-
-    def configure_optimizers(...):
-
-    def any_extra_hook(...):
-
-
-
-
-

Forward vs training_step

-

We recommend using forward() for inference/predictions and keeping -training_step() independent.

-
def forward(self, x):
-    embeddings = self.encoder(x)
-    return embeddings
-
-
-def training_step(self, batch, batch_idx):
-    x, _ = batch
-    z = self.encoder(x)
-    pred = self.decoder(z)
-    ...
-
-
-
-
-
-
-

Data

-

These are best practices for handling data.

-
-

DataLoaders

-

Lightning uses DataLoader to handle all the data flow through the system. Whenever you structure dataloaders, -make sure to tune the number of workers for maximum efficiency.

-
-

Warning

-

Make sure not to use Trainer(strategy="ddp_spawn") with num_workers>0 in the DataLoader or you will bottleneck you code.

-
-
-
-

DataModules

-

The LightningDataModule is designed as a way of decoupling data-related -hooks from the LightningModule so you can develop dataset agnostic models. It makes it easy to hot swap different -datasets with your model, so you can test it and benchmark it across domains. It also makes sharing and reusing the exact data splits and transforms across projects possible.

-

Check out Managing Data document to understand data management within Lightning and its best practices.

-
    -
  • What dataset splits were used?

  • -
  • How many samples does this dataset have overall and within each split?

  • -
  • Which transforms were used?

  • -
-

It’s for this reason that we recommend you use datamodules. This is especially important when collaborating because -it will save your team a lot of time as well.

-

All they need to do is drop a datamodule into the Trainer and not worry about what was done to the data.

-

This is true for both academic and corporate settings where data cleaning and ad-hoc instructions slow down the progress -of iterating through ideas.

- -
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/tuning/profiler.html b/docs/tuning/profiler.html deleted file mode 100644 index 1bb3f0b..0000000 --- a/docs/tuning/profiler.html +++ /dev/null @@ -1,741 +0,0 @@ - - - - - - - - - - - - - - Find bottlenecks in your code — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Find bottlenecks in your code
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
- - -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/tuning/profiler_advanced.html b/docs/tuning/profiler_advanced.html deleted file mode 100644 index 13cf507..0000000 --- a/docs/tuning/profiler_advanced.html +++ /dev/null @@ -1,754 +0,0 @@ - - - - - - - - - - - - - - Find bottlenecks in your code (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Find bottlenecks in your code (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Find bottlenecks in your code (advanced)

-

Audience: Users who want to profile their TPU models to find bottlenecks and improve performance.

-
-
-

Profile cloud TPU models

-

To profile TPU models use the XLAProfiler

-
from pytorch_lightning.profiler import XLAProfiler
-
-profiler = XLAProfiler(port=9001)
-trainer = Trainer(profiler=profiler)
-
-
-
-
-
-

Capture profiling logs in Tensorboard

-

To capture profile logs in Tensorboard, follow these instructions:

-
-
-

0: Setup the required installs

-

Use this guide to help you with the Cloud TPU required installations.

-
-
-
-

1: Start Tensorboard

-

Start the TensorBoard server:

-
tensorboard --logdir ./tensorboard --port 9001
-
-
-

Now open the following url on your browser

-
http://localhost:9001/#profile
-
-
-
-
-
-

2: Capture the profile

-

Once the code you want to profile is running:

-
    -
  1. click on the CAPTURE PROFILE button.

  2. -
  3. Enter localhost:9001 (default port for XLA Profiler) as the Profile Service URL.

  4. -
  5. Enter the number of milliseconds for the profiling duration

  6. -
  7. Click CAPTURE

  8. -
-
-
-
-

3: Don’t stop your code

-

Make sure the code is running while you are trying to capture the traces. It will lead to better performance insights if the profiling duration is longer than the step time.

-
-
-
-

4: View the profiling logs

-

Once the capture is finished, the page will refresh and you can browse through the insights using the Tools dropdown at the top left

-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/tuning/profiler_basic.html b/docs/tuning/profiler_basic.html deleted file mode 100644 index 835cbfd..0000000 --- a/docs/tuning/profiler_basic.html +++ /dev/null @@ -1,789 +0,0 @@ - - - - - - - - - - - - - - Find bottlenecks in your code (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Find bottlenecks in your code (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Find bottlenecks in your code (basic)

-

Audience: Users who want to learn the basics of removing bottlenecks from their code

-
-
-

Why do I need profiling?

-

Profiling helps you find bottlenecks in your code by capturing analytics such as how long a function takes or how much memory is used.

-
-
-
-

Find training loop bottlenecks

-

The most basic profile measures all the key methods across Callbacks, DataModules and the LightningModule in the training loop.

-
trainer = Trainer(profiler="simple")
-
-
-

Once the .fit() function has completed, you’ll see an output like this:

-
FIT Profiler Report
-
------------------------------------------------------------------------------------------------
-|  Action                                          |  Mean duration (s)     |  Total time (s) |
------------------------------------------------------------------------------------------------
-|  [LightningModule]BoringModel.prepare_data       |  10.0001               |  20.00          |
-|  run_training_epoch                              |  6.1558                |  6.1558         |
-|  run_training_batch                              |  0.0022506             |  0.015754       |
-|  [LightningModule]BoringModel.optimizer_step     |  0.0017477             |  0.012234       |
-|  [LightningModule]BoringModel.val_dataloader     |  0.00024388            |  0.00024388     |
-|  on_train_batch_start                            |  0.00014637            |  0.0010246      |
-|  [LightningModule]BoringModel.teardown           |  2.15e-06              |  2.15e-06       |
-|  [LightningModule]BoringModel.on_train_start     |  1.644e-06             |  1.644e-06      |
-|  [LightningModule]BoringModel.on_train_end       |  1.516e-06             |  1.516e-06      |
-|  [LightningModule]BoringModel.on_fit_end         |  1.426e-06             |  1.426e-06      |
-|  [LightningModule]BoringModel.setup              |  1.403e-06             |  1.403e-06      |
-|  [LightningModule]BoringModel.on_fit_start       |  1.226e-06             |  1.226e-06      |
------------------------------------------------------------------------------------------------
-
-
-

In this report we can see that the slowest function is prepare_data. Now you can figure out why data preparation is slowing down your training.

-

The simple profiler measures all the standard methods used in the training loop automatically, including:

-
    -
  • on_train_epoch_start

  • -
  • on_train_epoch_end

  • -
  • on_train_batch_start

  • -
  • model_backward

  • -
  • on_after_backward

  • -
  • optimizer_step

  • -
  • on_train_batch_end

  • -
  • training_step_end

  • -
  • on_training_end

  • -
  • etc…

  • -
-
-
-
-

Profile the time within every function

-

To profile the time within every function, use the AdvancedProfiler built on top of Python’s cProfiler.

-
trainer = Trainer(profiler="advanced")
-
-
-

Once the .fit() function has completed, you’ll see an output like this:

-
Profiler Report
-
-Profile stats for: get_train_batch
-        4869394 function calls (4863767 primitive calls) in 18.893 seconds
-Ordered by: cumulative time
-List reduced from 76 to 10 due to restriction <10>
-ncalls  tottime  percall  cumtime  percall filename:lineno(function)
-3752/1876    0.011    0.000   18.887    0.010 {built-in method builtins.next}
-    1876     0.008    0.000   18.877    0.010 dataloader.py:344(__next__)
-    1876     0.074    0.000   18.869    0.010 dataloader.py:383(_next_data)
-    1875     0.012    0.000   18.721    0.010 fetch.py:42(fetch)
-    1875     0.084    0.000   18.290    0.010 fetch.py:44(<listcomp>)
-    60000    1.759    0.000   18.206    0.000 mnist.py:80(__getitem__)
-    60000    0.267    0.000   13.022    0.000 transforms.py:68(__call__)
-    60000    0.182    0.000    7.020    0.000 transforms.py:93(__call__)
-    60000    1.651    0.000    6.839    0.000 functional.py:42(to_tensor)
-    60000    0.260    0.000    5.734    0.000 transforms.py:167(__call__)
-
-
-

If the profiler report becomes too long, you can stream the report to a file:

-
from pytorch_lightning.profiler import AdvancedProfiler
-
-profiler = AdvancedProfiler(dirpath=".", filename="perf_logs")
-trainer = Trainer(profiler=profiler)
-
-
-
-
-
-

Measure accelerator usage

-

Another helpful technique to detect bottlenecks is to ensure that you’re using the full capacity of your accelerator (GPU/TPU/IPU/HPU). -This can be measured with the DeviceStatsMonitor:

-
from pytorch_lightning.callbacks import DeviceStatsMonitor
-
-trainer = Trainer(callbacks=[DeviceStatsMonitor()])
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/tuning/profiler_expert.html b/docs/tuning/profiler_expert.html deleted file mode 100644 index 49b19e5..0000000 --- a/docs/tuning/profiler_expert.html +++ /dev/null @@ -1,782 +0,0 @@ - - - - - - - - - - - - - - Find bottlenecks in your code (expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Find bottlenecks in your code (expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Find bottlenecks in your code (expert)

-

Audience: Users who want to build their own profilers.

-
-
-

Build your own profiler

-

To build your own profiler, subclass Profiler -and override some of its methods. Here is a simple example that profiles the first occurrence and total calls of each action:

-
from pytorch_lightning.profiler import Profiler
-from collections import defaultdict
-import time
-
-
-class ActionCountProfiler(Profiler):
-    def __init__(self, dirpath=None, filename=None):
-        super().__init__(dirpath=dirpath, filename=filename)
-        self._action_count = defaultdict(int)
-        self._action_first_occurrence = {}
-
-    def start(self, action_name):
-        if action_name not in self._action_first_occurrence:
-            self._action_first_occurrence[action_name] = time.strftime("%m/%d/%Y, %H:%M:%S")
-
-    def stop(self, action_name):
-        self._action_count[action_name] += 1
-
-    def summary(self):
-        res = f"\nProfile Summary: \n"
-        max_len = max(len(x) for x in self._action_count)
-
-        for action_name in self._action_count:
-            # generate summary for actions called more than once
-            if self._action_count[action_name] > 1:
-                res += (
-                    f"{action_name:<{max_len}s} \t "
-                    + "self._action_first_occurrence[action_name]} \t "
-                    + "{self._action_count[action_name]} \n"
-                )
-
-        return res
-
-    def teardown(self, stage):
-        self._action_count = {}
-        self._action_first_occurrence = {}
-        super().teardown(stage=stage)
-
-
-
trainer = Trainer(profiler=ActionCountProfiler())
-trainer.fit(...)
-
-
-
-
-
-

Profile custom actions of interest

-

To profile a specific action of interest, reference a profiler in the LightningModule.

-
from pytorch_lightning.profiler import SimpleProfiler, PassThroughProfiler
-
-
-class MyModel(LightningModule):
-    def __init__(self, profiler=None):
-        self.profiler = profiler or PassThroughProfiler()
-
-
-

To profile in any part of your code, use the self.profiler.profile() function

-
class MyModel(LightningModule):
-    def custom_processing_step(self, data):
-        with self.profiler.profile("my_custom_action"):
-            ...
-        return data
-
-
-

Here’s the full code:

-
from pytorch_lightning.profiler import SimpleProfiler, PassThroughProfiler
-
-
-class MyModel(LightningModule):
-    def __init__(self, profiler=None):
-        self.profiler = profiler or PassThroughProfiler()
-
-    def custom_processing_step(self, data):
-        with self.profiler.profile("my_custom_action"):
-            ...
-        return data
-
-
-profiler = SimpleProfiler()
-model = MyModel(profiler)
-trainer = Trainer(profiler=profiler, max_epochs=1)
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/tuning/profiler_intermediate.html b/docs/tuning/profiler_intermediate.html deleted file mode 100644 index 9c09f2e..0000000 --- a/docs/tuning/profiler_intermediate.html +++ /dev/null @@ -1,848 +0,0 @@ - - - - - - - - - - - - - - Find bottlenecks in your code (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Find bottlenecks in your code (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Find bottlenecks in your code (intermediate)

-

Audience: Users who want to see more granular profiling information

-
-
-

Profile pytorch operations

-

To understand the cost of each PyTorch operation, use the PyTorchProfiler built on top of the PyTorch profiler.

-
from pytorch_lightning.profiler import PyTorchProfiler
-
-profiler = PyTorchProfiler()
-trainer = Trainer(profiler=profiler)
-
-
-

The profiler will generate an output like this:

-
Profiler Report
-
-Profile stats for: training_step
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-Name                   Self CPU total %  Self CPU total   CPU total %      CPU total        CPU time avg
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-t                      62.10%           1.044ms          62.77%           1.055ms          1.055ms
-addmm                  32.32%           543.135us        32.69%           549.362us        549.362us
-mse_loss               1.35%            22.657us         3.58%            60.105us         60.105us
-mean                   0.22%            3.694us          2.05%            34.523us         34.523us
-div_                   0.64%            10.756us         1.90%            32.001us         16.000us
-ones_like              0.21%            3.461us          0.81%            13.669us         13.669us
-sum_out                0.45%            7.638us          0.74%            12.432us         12.432us
-transpose              0.23%            3.786us          0.68%            11.393us         11.393us
-as_strided             0.60%            10.060us         0.60%            10.060us         3.353us
-to                     0.18%            3.059us          0.44%            7.464us          7.464us
-empty_like             0.14%            2.387us          0.41%            6.859us          6.859us
-empty_strided          0.38%            6.351us          0.38%            6.351us          3.175us
-fill_                  0.28%            4.782us          0.33%            5.566us          2.783us
-expand                 0.20%            3.336us          0.28%            4.743us          4.743us
-empty                  0.27%            4.456us          0.27%            4.456us          2.228us
-copy_                  0.15%            2.526us          0.15%            2.526us          2.526us
-broadcast_tensors      0.15%            2.492us          0.15%            2.492us          2.492us
-size                   0.06%            0.967us          0.06%            0.967us          0.484us
-is_complex             0.06%            0.961us          0.06%            0.961us          0.481us
-stride                 0.03%            0.517us          0.03%            0.517us          0.517us
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-Self CPU time total: 1.681ms
-
-
-
-

Note

-

When using the PyTorch Profiler, wall clock time will not not be representative of the true wall clock time. -This is due to forcing profiled operations to be measured synchronously, when many CUDA ops happen asynchronously. -It is recommended to use this Profiler to find bottlenecks/breakdowns, however for end to end wall clock time use -the SimpleProfiler.

-
-
-
-
-

Profile a distributed model

-

To profile a distributed model, use the PyTorchProfiler with the filename argument which will save a report per rank.

-
from pytorch_lightning.profiler import PyTorchProfiler
-
-profiler = PyTorchProfiler(filename="perf-logs")
-trainer = Trainer(profiler=profiler)
-
-
-

With two ranks, it will generate a report like so:

-
Profiler Report: rank 0
-
-Profile stats for: training_step
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-Name                   Self CPU total %  Self CPU total   CPU total %      CPU total        CPU time avg
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-t                      62.10%           1.044ms          62.77%           1.055ms          1.055ms
-addmm                  32.32%           543.135us        32.69%           549.362us        549.362us
-mse_loss               1.35%            22.657us         3.58%            60.105us         60.105us
-mean                   0.22%            3.694us          2.05%            34.523us         34.523us
-div_                   0.64%            10.756us         1.90%            32.001us         16.000us
-ones_like              0.21%            3.461us          0.81%            13.669us         13.669us
-sum_out                0.45%            7.638us          0.74%            12.432us         12.432us
-transpose              0.23%            3.786us          0.68%            11.393us         11.393us
-as_strided             0.60%            10.060us         0.60%            10.060us         3.353us
-to                     0.18%            3.059us          0.44%            7.464us          7.464us
-empty_like             0.14%            2.387us          0.41%            6.859us          6.859us
-empty_strided          0.38%            6.351us          0.38%            6.351us          3.175us
-fill_                  0.28%            4.782us          0.33%            5.566us          2.783us
-expand                 0.20%            3.336us          0.28%            4.743us          4.743us
-empty                  0.27%            4.456us          0.27%            4.456us          2.228us
-copy_                  0.15%            2.526us          0.15%            2.526us          2.526us
-broadcast_tensors      0.15%            2.492us          0.15%            2.492us          2.492us
-size                   0.06%            0.967us          0.06%            0.967us          0.484us
-is_complex             0.06%            0.961us          0.06%            0.961us          0.481us
-stride                 0.03%            0.517us          0.03%            0.517us          0.517us
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-Self CPU time total: 1.681ms
-
-
-
Profiler Report: rank 1
-
-Profile stats for: training_step
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-Name                   Self CPU total %  Self CPU total   CPU total %      CPU total        CPU time avg
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-t                      42.10%           1.044ms          62.77%           1.055ms          1.055ms
-addmm                  32.32%           543.135us        32.69%           549.362us        549.362us
-mse_loss               1.35%            22.657us         3.58%            60.105us         60.105us
-mean                   0.22%            3.694us          2.05%            34.523us         34.523us
-div_                   0.64%            10.756us         1.90%            32.001us         16.000us
-ones_like              0.21%            3.461us          0.81%            13.669us         13.669us
-sum_out                0.45%            7.638us          0.74%            12.432us         12.432us
-transpose              0.23%            3.786us          0.68%            11.393us         11.393us
-as_strided             0.60%            10.060us         0.60%            10.060us         3.353us
-to                     0.18%            3.059us          0.44%            7.464us          7.464us
-empty_like             0.14%            2.387us          0.41%            6.859us          6.859us
-empty_strided          0.38%            6.351us          0.38%            6.351us          3.175us
-fill_                  0.28%            4.782us          0.33%            5.566us          2.783us
-expand                 0.20%            3.336us          0.28%            4.743us          4.743us
-empty                  0.27%            4.456us          0.27%            4.456us          2.228us
-copy_                  0.15%            2.526us          0.15%            2.526us          2.526us
-broadcast_tensors      0.15%            2.492us          0.15%            2.492us          2.492us
-size                   0.06%            0.967us          0.06%            0.967us          0.484us
-is_complex             0.06%            0.961us          0.06%            0.961us          0.481us
-stride                 0.03%            0.517us          0.03%            0.517us          0.517us
----------------------  ---------------  ---------------  ---------------  ---------------  ---------------
-Self CPU time total: 1.681ms
-
-
-

This profiler will record training_step, backward, validation_step, test_step, and predict_step by default. -The output below shows the profiling for the action training_step. The user can provide PyTorchProfiler(record_functions={...}) -to extend the scope of profiled functions.

-
-

Note

-

When using the PyTorch Profiler, wall clock time will not not be representative of the true wall clock time. -This is due to forcing profiled operations to be measured synchronously, when many CUDA ops happen asynchronously. -It is recommended to use this Profiler to find bottlenecks/breakdowns, however for end to end wall clock time use -the SimpleProfiler.

-
-
-
-
-

Visualize profiled operations

-

To visualize the profiled operations, enable emit_nvtx in the PyTorchProfiler.

-
from pytorch_lightning.profiler import PyTorchProfiler
-
-profiler = PyTorchProfiler(emit_nvtx=True)
-trainer = Trainer(profiler=profiler)
-
-
-

Then run as following:

-
nvprof --profile-from-start off -o trace_name.prof -- <regular command here>
-
-
-

To visualize the profiled operation, you can either use nvvp:

-
nvvp trace_name.prof
-
-
-

or python:

-
python -c 'import torch; print(torch.autograd.profiler.load_nvprof("trace_name.prof"))'
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/visualize/experiment_managers.html b/docs/visualize/experiment_managers.html deleted file mode 100644 index 37ad539..0000000 --- a/docs/visualize/experiment_managers.html +++ /dev/null @@ -1,881 +0,0 @@ - - - - - - - - - - - - - - Manage Experiments — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Manage Experiments
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Manage Experiments

-

To track other artifacts, such as histograms or model topology graphs first select one of the many experiment managers (loggers) supported by Lightning

-
from pytorch_lightning import loggers as pl_loggers
-
-tensorboard = pl_loggers.TensorBoardLogger()
-trainer = Trainer(logger=tensorboard)
-
-
-

then access the logger’s API directly

-
def training_step(self):
-    tensorboard = self.logger.experiment
-    tensorboard.add_image()
-    tensorboard.add_histogram(...)
-    tensorboard.add_figure(...)
-
-
-
-
-

Comet.ml

-

To use Comet.ml first install the comet package:

-
pip install comet-ml
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import CometLogger
-
-comet_logger = CometLogger(api_key="YOUR_COMET_API_KEY")
-trainer = Trainer(logger=comet_logger)
-
-
-

Access the comet logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        comet = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        comet.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the CometLogger.

-
-
-
-

MLflow

-

To use MLflow first install the MLflow package:

-
pip install mlflow
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import MLFlowLogger
-
-mlf_logger = MLFlowLogger(experiment_name="lightning_logs", tracking_uri="file:./ml-runs")
-trainer = Trainer(logger=mlf_logger)
-
-
-

Access the comet logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        mlf_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        mlf_logger.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the MLFlowLogger.

-
-
-
-

Neptune.ai

-

To use Neptune.ai first install the neptune package:

-
pip install neptune-client
-
-
-

or with conda:

-
conda install -c conda-forge neptune-client
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import NeptuneLogger
-
-neptune_logger = NeptuneLogger(
-    api_key="ANONYMOUS",  # replace with your own
-    project="common/pytorch-lightning-integration",  # format "<WORKSPACE/PROJECT>"
-)
-trainer = Trainer(logger=neptune_logger)
-
-
-

Access the neptune logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        neptune_logger = self.logger.experiment["your/metadata/structure"]
-        neptune_logger.log(metadata)
-
-
-

Here’s the full documentation for the NeptuneLogger.

-
-
-
-

Tensorboard

-

TensorBoard already comes installed with Lightning. If you removed the install install the following package.

-
pip install tensorboard
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import TensorBoardLogger
-
-logger = TensorBoardLogger()
-trainer = Trainer(logger=logger)
-
-
-

Access the tensorboard logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        tensorboard_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        tensorboard_logger.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the TensorBoardLogger.

-
-
-
-

Weights and Biases

-

To use Weights and Biases (wandb) first install the wandb package:

-
pip install wandb
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import WandbLogger
-
-wandb_logger = WandbLogger(project="MNIST", log_model="all")
-trainer = Trainer(logger=wandb_logger)
-
-# log gradients and model topology
-wandb_logger.watch(model)
-
-
-

Access the wandb logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class MyModule(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        wandb_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-
-        # Option 1
-        wandb_logger.log({"generated_images": [wandb.Image(fake_images, caption="...")]})
-
-        # Option 2 for specifically logging images
-        wandb_logger.log_image(key="generated_images", images=[fake_images])
-
-
-

Here’s the full documentation for the WandbLogger. -Demo in Google Colab with hyperparameter search and model logging.

-
-
-
-

Use multiple exp managers

-

To use multiple experiment managers at the same time, pass a list to the logger Trainer argument.

-
from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger
-
-logger1 = TensorBoardLogger()
-logger2 = WandbLogger()
-trainer = Trainer(logger=[logger1, logger2])
-
-
-

Access all loggers from any function (except the LightningModule init) to use their APIs for tracking advanced artifacts

-
class MyModule(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        tensorboard_logger = self.logger.experiment[0]
-        wandb_logger = self.logger.experiment[1]
-
-        fake_images = torch.Tensor(32, 3, 28, 28)
-
-        tensorboard_logger.add_image("generated_images", fake_images, 0)
-        wandb_logger.add_image("generated_images", fake_images, 0)
-
-
-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/visualize/loggers.html b/docs/visualize/loggers.html deleted file mode 100644 index 5fa0bc1..0000000 --- a/docs/visualize/loggers.html +++ /dev/null @@ -1,751 +0,0 @@ - - - - - - - - - - - - - - Track and Visualize Experiments — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Track and Visualize Experiments
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/visualize/logging_advanced.html b/docs/visualize/logging_advanced.html deleted file mode 100644 index d6800b2..0000000 --- a/docs/visualize/logging_advanced.html +++ /dev/null @@ -1,1044 +0,0 @@ - - - - - - - - - - - - - - Track and Visualize Experiments (advanced) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Track and Visualize Experiments (advanced)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Track and Visualize Experiments (advanced)

-

Audience: Users who want to do advanced speed optimizations by customizing the logging behavior.

-
-
-

Change progress bar defaults

-

To change the default values (ie: version number) shown in the progress bar, override the get_metrics() method in your logger.

-
from pytorch_lightning.callbacks.progress import Tqdm
-
-
-class CustomProgressBar(Tqdm):
-    def get_metrics(self, *args, **kwargs):
-        # don't show the version number
-        items = super().get_metrics()
-        items.pop("v_num", None)
-        return items
-
-
-
-
-
-

Customize tracking to speed up model

-
-

Modify logging frequency

-

Logging a metric on every single batch can slow down training. By default, Lightning logs every 50 rows, or 50 training steps. -To change this behaviour, set the log_every_n_steps Trainer flag.

-
k = 10
-trainer = Trainer(log_every_n_steps=k)
-
-
-
-
-
-

Modify flushing frequency

-

Metrics are kept in memory for N steps to improve training efficiency. Every N steps, metrics flush to disk. To change the frequency of this flushing, use the flush_logs_every_n_steps Trainer argument.

-
# faster training, high memory
-Trainer(flush_logs_every_n_steps=500)
-
-# slower training, low memory
-Trainer(flush_logs_every_n_steps=500)
-
-
-

The higher flush_logs_every_n_steps is, the faster the model will train but the memory will build up until the next flush. -The smaller flush_logs_every_n_steps is, the slower the model will train but memory will be kept to a minimum.

-

TODO: chart

-
-
-
-
-

Customize self.log

-

The LightningModule self.log method offers many configurations to customize its behavior.

-
-
-

add_dataloader_idx

-

Default: True

-

If True, appends the index of the current dataloader to the name (when using multiple dataloaders). If False, user needs to give unique names for each dataloader to not mix the values.

-
self.log(add_dataloader_idx=True)
-
-
-
-
-
-

batch_size

-

Default: None

-

Current batch size used for accumulating logs logged with on_epoch=True. This will be directly inferred from the loaded batch, but for some data structures you might need to explicitly provide it.

-
self.log(batch_size=32)
-
-
-
-
-
-

enable_graph

-

Default: True

-

If True, will not auto detach the graph.

-
self.log(enable_graph=True)
-
-
-
-
-
-

logger

-

Default: True

-

Send logs to the logger like Tensorboard, or any other custom logger passed to the Trainer (Default: True).

-
self.log(logger=True)
-
-
-
-
-
-

on_epoch

-

Default: It varies

-

If this is True, that specific self.log call accumulates and reduces all metrics to the end of the epoch.

-
self.log(on_epoch=True)
-
-
-

The default value depends in which function this is called

-
def training_step(self, batch, batch_idx):
-    # Default: False
-    self.log(on_epoch=False)
-
-
-def validation_step(self, batch, batch_idx):
-    # Default: True
-    self.log(on_epoch=True)
-
-
-def test_step(self, batch, batch_idx):
-    # Default: True
-    self.log(on_epoch=True)
-
-
-
-
-
-

on_step

-

Default: It varies

-

If this is True, that specific self.log call will NOT accumulate metrics. Instead it will generate a timeseries across steps.

-
self.log(on_step=True)
-
-
-

The default value depends in which function this is called

-
def training_step(self, batch, batch_idx):
-    # Default: True
-    self.log(on_step=True)
-
-
-def validation_step(self, batch, batch_idx):
-    # Default: False
-    self.log(on_step=False)
-
-
-def test_step(self, batch, batch_idx):
-    # Default: False
-    self.log(on_step=False)
-
-
-
-
-
-

prog_bar

-

Default: False

-

If set to True, logs will be sent to the progress bar.

-
self.log(prog_bar=True)
-
-
-
-
-
-

rank_zero_only

-

Default: True

-

Whether the value will be logged only on rank 0. This will prevent synchronization which would produce a deadlock as not all processes would perform this log call.

-
self.log(rank_zero_only=True)
-
-
-
-
-
-

reduce_fx

-

Default: torch.mean()

-

Reduction function over step values for end of epoch. Uses torch.mean() by default.

-
self.log(reduce_fx=torch.mean)
-
-
-
-
-
-

sync_dist

-

Default: False

-

If True, reduces the metric across devices. Use with care as this may lead to a significant communication overhead.

-
self.log(sync_dist=False)
-
-
-
-
-
-

sync_dist_group

-

Default: None

-

The DDP group to sync across.

-
import torch.distributed as dist
-
-group = dist.init_process_group("nccl", rank=self.global_rank, world_size=self.world_size)
-self.log(sync_dist_group=group)
-
-
-
-
-
-
-

Enable metrics for distributed training

-

For certain types of metrics that need complex aggregation, we recommended to build your metric using torchmetric which ensures all the complexities of metric aggregation in distributed environments is handled.

-

First, implement your metric:

-
import torch
-import torchmetrics
-
-
-class MyAccuracy(Metric):
-    def __init__(self, dist_sync_on_step=False):
-        # call `self.add_state`for every internal state that is needed for the metrics computations
-        # dist_reduce_fx indicates the function that should be used to reduce
-        # state from multiple processes
-        super().__init__(dist_sync_on_step=dist_sync_on_step)
-
-        self.add_state("correct", default=torch.tensor(0), dist_reduce_fx="sum")
-        self.add_state("total", default=torch.tensor(0), dist_reduce_fx="sum")
-
-    def update(self, preds: torch.Tensor, target: torch.Tensor):
-        # update metric states
-        preds, target = self._input_format(preds, target)
-        assert preds.shape == target.shape
-
-        self.correct += torch.sum(preds == target)
-        self.total += target.numel()
-
-    def compute(self):
-        # compute final result
-        return self.correct.float() / self.total
-
-
-

To use the metric inside Lightning, 1) initialize it in the init, 2) compute the metric, 3) pass it into self.log

-
class LitModel(LightningModule):
-    def __init__(self):
-        # 1. initialize the metric
-        self.accuracy = MyAccuracy()
-
-    def training_step(self, batch, batch_idx):
-        x, y = batch
-        preds = self(x)
-
-        # 2. compute the metric
-        self.accuracy(preds, y)
-
-        # 3. log it
-        self.log("train_acc_step", self.accuracy)
-
-
-
-
-
-

Log to a custom cloud filesystem

-

Lightning is integrated with the major remote file systems including local filesystems and several cloud storage providers such as -S3 on AWS, GCS on Google Cloud, -or ADL on Azure.

-

PyTorch Lightning uses fsspec internally to handle all filesystem operations.

-

To save logs to a remote filesystem, prepend a protocol like “s3:/” to the root_dir used for writing and reading model data.

-
from pytorch_lightning.loggers import TensorBoardLogger
-
-logger = TensorBoardLogger(save_dir="s3://my_bucket/logs/")
-
-trainer = Trainer(logger=logger)
-trainer.fit(model)
-
-
-
-
-
-

Track both step and epoch metrics

-

To track the timeseries over steps (on_step) as well as the accumulated epoch metric (on_epoch), set both to True

-
self.log(on_step=True, on_epoch=True)
-
-
-

Setting both to True will generate two graphs with _step for the timeseries over steps and _epoch for the epoch metric.

-

# TODO: show images of both

-
-
-
-

Understand self.log automatic behavior

-

This table shows the default values of on_step and on_epoch depending on the LightningModule or Callback method.

-
-
-

In LightningModule

- - ----- - - - - - - - - - - - - - - - - -
Default behavior of logging in ightningModule

Method

on_step

on_epoch

on_after_backward, on_before_backward, on_before_optimizer_step, on_before_zero_grad, training_step, training_step_end

True

False

training_epoch_end, test_epoch_end, test_step, test_step_end, validation_epoch_end, validation_step, validation_step_end

False

True

-
-
-
-

In Callback

- - ----- - - - - - - - - - - - - - - - - -
Default behavior of logging in Callback

Method

on_step

on_epoch

on_after_backward, on_before_backward, on_before_optimizer_step, on_before_zero_grad, on_train_batch_start, on_train_batch_end

True

False

on_train_epoch_start, on_train_epoch_end, on_train_start, on_validation_batch_start, on_validation_batch_end, on_validation_start, on_validation_epoch_start, on_validation_epoch_end

False

True

-
-

Note

-

To add logging to an unsupported method, please open an issue with a clear description of why it is blocking you.

-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/visualize/logging_basic.html b/docs/visualize/logging_basic.html deleted file mode 100644 index de65446..0000000 --- a/docs/visualize/logging_basic.html +++ /dev/null @@ -1,800 +0,0 @@ - - - - - - - - - - - - - - Track and Visualize Experiments (basic) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Track and Visualize Experiments (basic)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Track and Visualize Experiments (basic)

-

Audience: Users who want to visualize and monitor their model development

-
-
-

Why do I need to track metrics?

-

In model development, we track values of interest such as the validation_loss to visualize the learning process for our models. Model development is like driving a car without windows, charts and logs provide the windows to know where to drive the car.

-

With Lightning, you can visualize virtually anything you can think of: numbers, text, images, audio. Your creativity and imagination are the only limiting factor.

-
-
-
-

Track metrics

-

Metric visualization is the most basic but powerful way of understanding how your model is doing throughout the model development process.

-

To track a metric, simply use the self.log method available inside the LightningModule

-
class LitModel(pl.LightningModule):
-    def training_step(self, batch, batch_idx):
-        value = self.global_step
-        self.log("some_value", self.global_step)
-
-
-

To log multiple metrics at once, use self.log_dict

-
values = {"loss": loss, "acc": acc, "metric_n": metric_n}  # add more items if needed
-self.log_dict(values)
-
-
-

TODO: show plot of metric changing over time

-
-
-

View in the commandline

-

To view metrics in the commandline progress bar, set the prog_bar argument to True.

-
self.log(prog_bar=True)
-
-
-

TODO: need progress bar here

-
-
-
-

View in the browser

-

To view metrics in the browser you need to use an experiment manager with these capabilities. By Default, Lightning uses Tensorboard which is free and opensource.

-

Tensorboard is already enabled by default

-
# every trainer already has tensorboard enabled by default
-trainer = Trainer()
-
-
-

To launch the tensorboard dashboard run the following command on the commandline.

-
tensorboard --logdir=lightning_logs/
-
-
-

If you’re using a notebook environment such as colab or kaggle or jupyter, launch Tensorboard with this command

-
%reload_ext tensorboard
-%tensorboard --logdir=lightning_logs/
-
-
-
-
-
-

Accumulate a metric

-

When self.log is called inside the training_step, it generates a timeseries showing how the metric behaves over time.

-

TODO: show chart

-

However, For the validation and test sets we are not generally interested in plotting the metric values per batch of data. Instead, we want to compute a summary statistic (such as average, min or max) across the full split of data.

-

When you call self.log inside the validation_step and test_step, Lightning automatically accumulates the metric and averages it once it’s gone through the whole split (epoch).

-
def validation_step(self, batch, batch_idx):
-    value = batch_idx + 1
-    self.log("average_value", value)
-
-
-

TODO: show single point plotted

-

If you don’t want to average, add your own function in the reduce_fx argument.

-
# default function
-self.log(reduce_fx=torch.mean)
-
-
-
-
-
-
-

Track images

-

If your experiment manager supports image visualization, simply log the image with self.log

-
# (32 batch samples, 3 channels, 32 width, 32 height)
-image = torch.Tensor(32, 3, 28, 28)
-self.log("an_image", image)
-
-
-
-
-
-

Track text

-

If your experiment manager supports text visualization, simply log the text with self.log

-
text = "hello world"
-self.log("some_text", text)
-
-
-

# TODO: show screenshot

-
-
-
-

Configure the saving directory

-

By default, anything that is logged is saved to the current working directory. To use a different directory, set the default_root_dir argument in the Trainer.

-
Trainer(default_root_dir="/your/custom/path")
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/visualize/logging_expert.html b/docs/visualize/logging_expert.html deleted file mode 100644 index 8a6c436..0000000 --- a/docs/visualize/logging_expert.html +++ /dev/null @@ -1,806 +0,0 @@ - - - - - - - - - - - - - - Track and Visualize Experiments (expert) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Track and Visualize Experiments (expert)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Track and Visualize Experiments (expert)

-

Audience: Users who want to make their own progress bars or integrate new experiment managers.

-
-
-

Change the progress bar

-

If you’d like to change the way the progress bar displays information you can use some of our built-in progress bard or build your own.

-
-
-

Use the TQDMProgressBar

-

To use the TQDMProgressBar pass it into the callbacks Trainer argument.

-
from pytorch_lightning.callbacks import TQDMProgressBar
-
-trainer = Trainer(callbacks=[TQDMProgressBar()])
-
-
-
-
-
-

Use the RichProgressBar

-

The RichProgressBar can add custom colors and beautiful formatting for your progress bars. First, install the `rich <https://github.com/Textualize/rich>`_ library

-
pip install rich
-
-
-

Then pass the callback into the callbacks Trainer argument:

-
from pytorch_lightning.callbacks import RichProgressBar
-
-trainer = Trainer(callbacks=[RichProgressBar()])
-
-
-

The rich progress bar can also have custom themes

-
from pytorch_lightning.callbacks import RichProgressBar
-from pytorch_lightning.callbacks.progress.rich_progress import RichProgressBarTheme
-
-# create your own theme!
-theme = RichProgressBarTheme(description="green_yellow", progress_bar="green1")
-
-# init as normal
-progress_bar = RichProgressBar(theme=theme)
-trainer = Trainer(callbacks=progress_bar)
-
-
-
-
-
-
-

Customize a progress bar

-

To customize either the TQDMProgressBar or the RichProgressBar, subclass it and override any of its methods.

-
from pytorch_lightning.callbacks import TQDMProgressBar
-
-
-class LitProgressBar(TQDMProgressBar):
-    def init_validation_tqdm(self):
-        bar = super().init_validation_tqdm()
-        bar.set_description("running validation...")
-        return bar
-
-
-
-
-
-

Build your own progress bar

-

To build your own progress bar, subclass ProgressBarBase

-
from pytorch_lightning.callbacks import ProgressBarBase
-
-
-class LitProgressBar(ProgressBarBase):
-    def __init__(self):
-        super().__init__()  # don't forget this :)
-        self.enable = True
-
-    def disable(self):
-        self.enable = False
-
-    def on_train_batch_end(self, trainer, pl_module, outputs, batch_idx):
-        super().on_train_batch_end(trainer, pl_module, outputs, batch_idx)  # don't forget this :)
-        percent = (self.train_batch_idx / self.total_train_batches) * 100
-        sys.stdout.flush()
-        sys.stdout.write(f"{percent:.01f} percent complete \r")
-
-
-bar = LitProgressBar()
-trainer = Trainer(callbacks=[bar])
-
-
-
-
-
-

Integrate an experiment manager

-

To create an integration between a custom logger and Lightning, subclass LightningLoggerBase

-
from pytorch_lightning.loggers import Logger
-
-
-class LitLogger(Logger):
-    @property
-    def name(self) -> str:
-        return "my-experiment"
-
-    @property
-    def version(self):
-        return "version_0"
-
-    def log_metrics(self, metrics, step=None):
-        print("my logged metrics", metrics)
-
-    def log_hyperparams(self, params, *args, **kwargs):
-        print("my logged hyperparameters", params)
-
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/visualize/logging_intermediate.html b/docs/visualize/logging_intermediate.html deleted file mode 100644 index 5f8e554..0000000 --- a/docs/visualize/logging_intermediate.html +++ /dev/null @@ -1,926 +0,0 @@ - - - - - - - - - - - - - - Track and Visualize Experiments (intermediate) — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- -
    - -
  • - - - Docs - - > -
  • - - -
  • Track and Visualize Experiments (intermediate)
  • - - -
  • - - - - - -
  • - -
- - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Track and Visualize Experiments (intermediate)

-

Audience: Users who want to track more complex outputs and use third-party experiment managers.

-
-
-

Track audio and other artifacts

-

To track other artifacts, such as histograms or model topology graphs first select one of the many loggers supported by Lightning

-
from pytorch_lightning import loggers as pl_loggers
-
-tensorboard = pl_loggers.TensorBoardLogger()
-trainer = Trainer(logger=tensorboard)
-
-
-

then access the logger’s API directly

-
def training_step(self):
-    tensorboard = self.logger.experiment
-    tensorboard.add_image()
-    tensorboard.add_histogram(...)
-    tensorboard.add_figure(...)
-
-
-
-
-

Comet.ml

-

To use Comet.ml first install the comet package:

-
pip install comet-ml
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import CometLogger
-
-comet_logger = CometLogger(api_key="YOUR_COMET_API_KEY")
-trainer = Trainer(logger=comet_logger)
-
-
-

Access the comet logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        comet = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        comet.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the CometLogger.

-
-
-
-

MLflow

-

To use MLflow first install the MLflow package:

-
pip install mlflow
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import MLFlowLogger
-
-mlf_logger = MLFlowLogger(experiment_name="lightning_logs", tracking_uri="file:./ml-runs")
-trainer = Trainer(logger=mlf_logger)
-
-
-

Access the comet logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        mlf_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        mlf_logger.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the MLFlowLogger.

-
-
-
-

Neptune.ai

-

To use Neptune.ai first install the neptune package:

-
pip install neptune-client
-
-
-

or with conda:

-
conda install -c conda-forge neptune-client
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import NeptuneLogger
-
-neptune_logger = NeptuneLogger(
-    api_key="ANONYMOUS",  # replace with your own
-    project="common/pytorch-lightning-integration",  # format "<WORKSPACE/PROJECT>"
-)
-trainer = Trainer(logger=neptune_logger)
-
-
-

Access the neptune logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        neptune_logger = self.logger.experiment["your/metadata/structure"]
-        neptune_logger.log(metadata)
-
-
-

Here’s the full documentation for the NeptuneLogger.

-
-
-
-

Tensorboard

-

TensorBoard already comes installed with Lightning. If you removed the install install the following package.

-
pip install tensorboard
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import TensorBoardLogger
-
-logger = TensorBoardLogger()
-trainer = Trainer(logger=logger)
-
-
-

Access the tensorboard logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        tensorboard_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        tensorboard_logger.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the TensorBoardLogger.

-
-
-
-

Weights and Biases

-

To use Weights and Biases (wandb) first install the wandb package:

-
pip install wandb
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import WandbLogger
-
-wandb_logger = WandbLogger(project="MNIST", log_model="all")
-trainer = Trainer(logger=wandb_logger)
-
-# log gradients and model topology
-wandb_logger.watch(model)
-
-
-

Access the wandb logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class MyModule(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        wandb_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-
-        # Option 1
-        wandb_logger.log({"generated_images": [wandb.Image(fake_images, caption="...")]})
-
-        # Option 2 for specifically logging images
-        wandb_logger.log_image(key="generated_images", images=[fake_images])
-
-
-

Here’s the full documentation for the WandbLogger. -Demo in Google Colab with hyperparameter search and model logging.

-
-
-
-

Use multiple exp managers

-

To use multiple experiment managers at the same time, pass a list to the logger Trainer argument.

-
from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger
-
-logger1 = TensorBoardLogger()
-logger2 = WandbLogger()
-trainer = Trainer(logger=[logger1, logger2])
-
-
-

Access all loggers from any function (except the LightningModule init) to use their APIs for tracking advanced artifacts

-
class MyModule(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        tensorboard_logger = self.logger.experiment[0]
-        wandb_logger = self.logger.experiment[1]
-
-        fake_images = torch.Tensor(32, 3, 28, 28)
-
-        tensorboard_logger.add_image("generated_images", fake_images, 0)
-        wandb_logger.add_image("generated_images", fake_images, 0)
-
-
-
-
-
-
-

Track multiple metrics in the same chart

-

If your logger supports plotting multiple metrics on the same chart, pass in a dictionary to self.log.

-
self.log("performance", {"acc": acc, "recall": recall})
-
-
-
-
-
-

Track hyperparameters

-

To track hyperparameters, first call save_hyperparameters from the LightningModule init:

-
class MyLightningModule(LightningModule):
-    def __init__(self, learning_rate, another_parameter, *args, **kwargs):
-        super().__init__()
-        self.save_hyperparameters()
-
-
-

If your logger supports tracked hyperparameters, the hyperparameters will automatically show up on the logger dashboard.

-

TODO: show tracked hyperparameters.

-
-
-
-

Track model topology

-

Multiple loggers support visualizing the model topology. Here’s an example that tracks the model topology using Tensorboard.

-
def any_lightning_module_function_or_hook(self):
-    tensorboard_logger = self.logger.experiment
-
-    prototype_array = torch.Tensor(32, 1, 28, 27)
-    tensorboard_logger.log_graph(model=self, input_array=prototype_array)
-
-
-

TODO: show tensorboard topology.

-
-
- - -
- -
-
- - - - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/docs/visualize/supported_exp_managers.html b/docs/visualize/supported_exp_managers.html deleted file mode 100644 index daab22f..0000000 --- a/docs/visualize/supported_exp_managers.html +++ /dev/null @@ -1,848 +0,0 @@ - - - - - - - - - - - - - - Comet.ml — PyTorch Lightning 1.7.0dev documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - - - - -
-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - -
- - - - -
-
- -
- Shortcuts -
-
- -
-
- - - -
- -
-
- -
-

Comet.ml

-

To use Comet.ml first install the comet package:

-
pip install comet-ml
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import CometLogger
-
-comet_logger = CometLogger(api_key="YOUR_COMET_API_KEY")
-trainer = Trainer(logger=comet_logger)
-
-
-

Access the comet logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        comet = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        comet.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the CometLogger.

-
-
-
-

MLflow

-

To use MLflow first install the MLflow package:

-
pip install mlflow
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import MLFlowLogger
-
-mlf_logger = MLFlowLogger(experiment_name="lightning_logs", tracking_uri="file:./ml-runs")
-trainer = Trainer(logger=mlf_logger)
-
-
-

Access the comet logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        mlf_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        mlf_logger.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the MLFlowLogger.

-
-
-
-

Neptune.ai

-

To use Neptune.ai first install the neptune package:

-
pip install neptune-client
-
-
-

or with conda:

-
conda install -c conda-forge neptune-client
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import NeptuneLogger
-
-neptune_logger = NeptuneLogger(
-    api_key="ANONYMOUS",  # replace with your own
-    project="common/pytorch-lightning-integration",  # format "<WORKSPACE/PROJECT>"
-)
-trainer = Trainer(logger=neptune_logger)
-
-
-

Access the neptune logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        neptune_logger = self.logger.experiment["your/metadata/structure"]
-        neptune_logger.log(metadata)
-
-
-

Here’s the full documentation for the NeptuneLogger.

-
-
-
-

Tensorboard

-

TensorBoard already comes installed with Lightning. If you removed the install install the following package.

-
pip install tensorboard
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import TensorBoardLogger
-
-logger = TensorBoardLogger()
-trainer = Trainer(logger=logger)
-
-
-

Access the tensorboard logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class LitModel(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        tensorboard_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-        tensorboard_logger.add_image("generated_images", fake_images, 0)
-
-
-

Here’s the full documentation for the TensorBoardLogger.

-
-
-
-

Weights and Biases

-

To use Weights and Biases (wandb) first install the wandb package:

-
pip install wandb
-
-
-

Configure the logger and pass it to the Trainer:

-
from pytorch_lightning.loggers import WandbLogger
-
-wandb_logger = WandbLogger(project="MNIST", log_model="all")
-trainer = Trainer(logger=wandb_logger)
-
-# log gradients and model topology
-wandb_logger.watch(model)
-
-
-

Access the wandb logger from any function (except the LightningModule init) to use its API for tracking advanced artifacts

-
class MyModule(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        wandb_logger = self.logger.experiment
-        fake_images = torch.Tensor(32, 3, 28, 28)
-
-        # Option 1
-        wandb_logger.log({"generated_images": [wandb.Image(fake_images, caption="...")]})
-
-        # Option 2 for specifically logging images
-        wandb_logger.log_image(key="generated_images", images=[fake_images])
-
-
-

Here’s the full documentation for the WandbLogger. -Demo in Google Colab with hyperparameter search and model logging.

-
-
-
-

Use multiple exp managers

-

To use multiple experiment managers at the same time, pass a list to the logger Trainer argument.

-
from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger
-
-logger1 = TensorBoardLogger()
-logger2 = WandbLogger()
-trainer = Trainer(logger=[logger1, logger2])
-
-
-

Access all loggers from any function (except the LightningModule init) to use their APIs for tracking advanced artifacts

-
class MyModule(LightningModule):
-    def any_lightning_module_function_or_hook(self):
-        tensorboard_logger = self.logger.experiment[0]
-        wandb_logger = self.logger.experiment[1]
-
-        fake_images = torch.Tensor(32, 3, 28, 28)
-
-        tensorboard_logger.add_image("generated_images", fake_images, 0)
-        wandb_logger.add_image("generated_images", fake_images, 0)
-
-
-
- - -
- -
-
- - - - -
- - - -
-

- © Copyright Copyright (c) 2018-2022, William Falcon et al... - -

-
- -
- Built with Sphinx using a theme provided by Read the Docs. -
- - -
- -
-
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
- - - - - - - - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - - \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..243e172 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,37 @@ +# Examples + +Our most robust examples showing all sorts of implementations +can be found in our sister library [Lightning Bolts](https://lightning.ai/docs/pytorch/latest/ecosystem/bolts.html). + +______________________________________________________________________ + +*Note that some examples may rely on new features that are only available in the development branch and may be incompatible with any releases.* +*If you see any errors, you might want to consider switching to a version tag you would like to run examples with.* +*For example, if you're using `pytorch-lightning==1.6.4` in your environment and seeing issues, run examples of the tag [1.6.4](https://github.com/Lightning-AI/lightning/tree/1.6.4/pl_examples).* + +______________________________________________________________________ + +## Lightning Fabric Examples + +We show how to accelerate your PyTorch code with [Lightning Fabric](https://lightning.ai/docs/fabric) with minimal code changes. +You stay in full control of the training loop. + +- [MNIST: Vanilla PyTorch vs. Fabric](fabric/image_classifier/README.md) +- [DCGAN: Vanilla PyTorch vs. Fabric](fabric/dcgan/README.md) + +______________________________________________________________________ + +## Lightning Trainer Examples + +In this folder, we have 2 simple examples that showcase the power of the Lightning Trainer. + +- [Image Classifier](pytorch/basics/backbone_image_classifier.py) (trains arbitrary datasets with arbitrary backbones). +- [Autoencoder](pytorch/basics/autoencoder.py) + +______________________________________________________________________ + +## Domain Examples + +This folder contains older examples. You should instead use the examples +in [Lightning Bolts](https://lightning.ai/docs/pytorch/latest/ecosystem/bolts.html) +for advanced use cases. diff --git a/examples/app/argparse/app.py b/examples/app/argparse/app.py new file mode 100644 index 0000000..794e2fc --- /dev/null +++ b/examples/app/argparse/app.py @@ -0,0 +1,28 @@ +import argparse + +import lightning as L + + +class Work(L.LightningWork): + def __init__(self, cloud_compute): + super().__init__(cloud_compute=cloud_compute) + + def run(self): + pass + + +class Flow(L.LightningFlow): + def __init__(self, cloud_compute): + super().__init__() + self.work = Work(cloud_compute) + + def run(self): + assert self.work.cloud_compute.name == "gpu", self.work.cloud_compute.name + self.stop() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--use_gpu", action="store_true", default=False, help="Whether to use GPU in the cloud") + hparams = parser.parse_args() + app = L.LightningApp(Flow(L.CloudCompute("gpu" if hparams.use_gpu else "cpu"))) diff --git a/examples/app/boring/.gitignore b/examples/app/boring/.gitignore new file mode 100644 index 0000000..9401870 --- /dev/null +++ b/examples/app/boring/.gitignore @@ -0,0 +1,10 @@ +lightning_logs +*.pt +.storage/ +.shared/ +data +*.ckpt +redis-stable +node_modules +*.rdb +boring_file.txt diff --git a/examples/app/boring/app.py b/examples/app/boring/app.py new file mode 100644 index 0000000..bf77786 --- /dev/null +++ b/examples/app/boring/app.py @@ -0,0 +1,61 @@ +import os + +import lightning as L +from lightning.app.components import TracerPythonScript +from lightning.app.storage import Path + +FILE_CONTENT = """ +Hello there! +This tab is currently an IFrame of the FastAPI Server running in `DestinationFileAndServeWork`. +Also, the content of this file was created in `SourceFileWork` and then transferred to `DestinationFileAndServeWork`. +Are you already 🤯 ? Stick with us, this is only the beginning. Lightning is 🚀. +""" + + +class SourceFileWork(L.LightningWork): + def __init__(self, cloud_compute: L.CloudCompute = L.CloudCompute(), **kwargs): + super().__init__(parallel=True, **kwargs, cloud_compute=cloud_compute) + self.boring_path = None + + def run(self): + # This should be used as a REFERENCE to the file. + self.boring_path = "lit://boring_file.txt" + with open(self.boring_path, "w", encoding="utf-8") as f: + f.write(FILE_CONTENT) + + +class DestinationFileAndServeWork(TracerPythonScript): + def run(self, path: Path): + assert path.exists() + self.script_args += [f"--filepath={path}", f"--host={self.host}", f"--port={self.port}"] + super().run() + + +class BoringApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.source_work = SourceFileWork() + self.dest_work = DestinationFileAndServeWork( + script_path=os.path.join(os.path.dirname(__file__), "scripts/serve.py"), + port=1111, + parallel=False, # runs until killed. + cloud_compute=L.CloudCompute(), + raise_exception=True, + ) + + @property + def ready(self) -> bool: + return self.dest_work.is_running + + def run(self): + self.source_work.run() + if self.source_work.has_succeeded: + # the flow passes the file from one work to another. + self.dest_work.run(self.source_work.boring_path) + self.stop("Boring App End") + + def configure_layout(self): + return {"name": "Boring Tab", "content": self.dest_work.url + "/file"} + + +app = L.LightningApp(BoringApp()) diff --git a/examples/app/boring/app_dynamic.py b/examples/app/boring/app_dynamic.py new file mode 100644 index 0000000..ea66cc2 --- /dev/null +++ b/examples/app/boring/app_dynamic.py @@ -0,0 +1,72 @@ +import os + +import lightning as L +from lightning.app.components import TracerPythonScript +from lightning.app.storage import Path +from lightning.app.structures import Dict + +FILE_CONTENT = """ +Hello there! +This tab is currently an IFrame of the FastAPI Server running in `DestinationFileAndServeWork`. +Also, the content of this file was created in `SourceFileWork` and then transferred to `DestinationFileAndServeWork`. +Are you already 🤯 ? Stick with us, this is only the beginning. Lightning is 🚀. +""" + + +class SourceFileWork(L.LightningWork): + def __init__(self, cloud_compute: L.CloudCompute = L.CloudCompute(), **kwargs): + super().__init__(parallel=True, **kwargs, cloud_compute=cloud_compute) + self.boring_path = None + + def run(self): + # This should be used as a REFERENCE to the file. + self.boring_path = "lit://boring_file.txt" + with open(self.boring_path, "w") as f: + f.write(FILE_CONTENT) + + +class DestinationFileAndServeWork(TracerPythonScript): + def run(self, path: Path): + assert path.exists() + self.script_args += [f"--filepath={path}", f"--host={self.host}", f"--port={self.port}"] + super().run() + + +class BoringApp(L.LightningFlow): + def __init__(self): + super().__init__() + self.dict = Dict() + + @property + def ready(self) -> bool: + if "dst_w" in self.dict: + return self.dict["dst_w"].url != "" + return False + + def run(self): + # create dynamically the source_work at runtime + if "src_w" not in self.dict: + self.dict["src_w"] = SourceFileWork() + + self.dict["src_w"].run() + + if self.dict["src_w"].has_succeeded: + # create dynamically the dst_w at runtime + if "dst_w" not in self.dict: + self.dict["dst_w"] = DestinationFileAndServeWork( + script_path=os.path.join(os.path.dirname(__file__), "scripts/serve.py"), + port=1111, + parallel=False, # runs until killed. + cloud_compute=L.CloudCompute(), + raise_exception=True, + ) + + # the flow passes the file from one work to another. + self.dict["dst_w"].run(self.dict["src_w"].boring_path) + self.stop("Boring App End") + + def configure_layout(self): + return {"name": "Boring Tab", "content": self.dict["dst_w"].url + "/file" if "dst_w" in self.dict else ""} + + +app = L.LightningApp(BoringApp(), log_level="debug") diff --git a/examples/app/boring/scripts/__init__.py b/examples/app/boring/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/app/boring/scripts/serve.py b/examples/app/boring/scripts/serve.py new file mode 100644 index 0000000..dedd601 --- /dev/null +++ b/examples/app/boring/scripts/serve.py @@ -0,0 +1,29 @@ +import argparse +import os + +import uvicorn +from fastapi import FastAPI +from fastapi.requests import Request +from fastapi.responses import HTMLResponse + +if __name__ == "__main__": + parser = argparse.ArgumentParser("Server Parser") + parser.add_argument("--filepath", type=str, help="Where to find the `filepath`") + parser.add_argument("--host", type=str, default="0.0.0.0", help="Server host`") + parser.add_argument("--port", type=int, default="8888", help="Server port`") + hparams = parser.parse_args() + + fastapi_service = FastAPI() + + if not os.path.exists(str(hparams.filepath)): + content = ["The file wasn't transferred"] + else: + with open(hparams.filepath) as fo: + content = fo.readlines() # read the file received from SourceWork. + + @fastapi_service.get("/file") + async def get_file_content(request: Request, response_class=HTMLResponse): + lines = "\n".join(["

" + line + "

" for line in content]) + return HTMLResponse(f"
    {lines}
") + + uvicorn.run(app=fastapi_service, host=hparams.host, port=hparams.port) diff --git a/examples/app/commands_and_api/.lightningignore b/examples/app/commands_and_api/.lightningignore new file mode 100644 index 0000000..f7275bb --- /dev/null +++ b/examples/app/commands_and_api/.lightningignore @@ -0,0 +1 @@ +venv/ diff --git a/examples/app/commands_and_api/app.py b/examples/app/commands_and_api/app.py new file mode 100644 index 0000000..a661663 --- /dev/null +++ b/examples/app/commands_and_api/app.py @@ -0,0 +1,53 @@ +from command import CustomCommand, CustomConfig + +from lightning import LightningFlow +from lightning.app.api import Get, Post +from lightning.app.core.app import LightningApp + + +async def handler(): + print("Has been called") + return "Hello World !" + + +class ChildFlow(LightningFlow): + def nested_command(self, name: str): + """A nested command.""" + print(f"Hello {name}") + + def configure_commands(self): + return [{"nested_command": self.nested_command}] + + +class FlowCommands(LightningFlow): + def __init__(self): + super().__init__() + self.names = [] + self.child_flow = ChildFlow() + + def run(self): + if self.names: + print(self.names) + + def command_without_client(self, name: str): + """A command without a client.""" + self.names.append(name) + + def command_with_client(self, config: CustomConfig): + self.names.append(config.name) + + def configure_commands(self): + commands = [ + {"command_without_client": self.command_without_client}, + {"command_with_client": CustomCommand(self.command_with_client)}, + ] + return commands + self.child_flow.configure_commands() + + def configure_api(self): + return [ + Post("/user/command_without_client", self.command_without_client), + Get("/pure_function", handler), + ] + + +app = LightningApp(FlowCommands(), log_level="debug") diff --git a/examples/app/commands_and_api/command.py b/examples/app/commands_and_api/command.py new file mode 100644 index 0000000..96d3f67 --- /dev/null +++ b/examples/app/commands_and_api/command.py @@ -0,0 +1,19 @@ +from argparse import ArgumentParser + +from pydantic import BaseModel + +from lightning.app.utilities.commands import ClientCommand + + +class CustomConfig(BaseModel): + name: str + + +class CustomCommand(ClientCommand): + description = "A command with a client." + + def run(self): + parser = ArgumentParser() + parser.add_argument("--name", type=str) + args = parser.parse_args() + self.invoke_handler(config=CustomConfig(name=args.name)) diff --git a/examples/app/components/python/__init__.py b/examples/app/components/python/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/app/components/python/app.py b/examples/app/components/python/app.py new file mode 100644 index 0000000..467caa2 --- /dev/null +++ b/examples/app/components/python/app.py @@ -0,0 +1,24 @@ +import os +from pathlib import Path + +import lightning as L +from examples.components.python.component_tracer import PLTracerPythonScript + + +class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + script_path = Path(__file__).parent / "pl_script.py" + self.tracer_python_script = PLTracerPythonScript(script_path) + + def run(self): + assert os.getenv("GLOBAL_RANK", "0") == "0" + if not self.tracer_python_script.has_started: + self.tracer_python_script.run() + if self.tracer_python_script.has_succeeded: + self.stop("tracer script succeed") + if self.tracer_python_script.has_failed: + self.stop("tracer script failed") + + +app = L.LightningApp(RootFlow()) diff --git a/examples/app/components/python/component_popen.py b/examples/app/components/python/component_popen.py new file mode 100644 index 0000000..bc70b9f --- /dev/null +++ b/examples/app/components/python/component_popen.py @@ -0,0 +1,7 @@ +from pathlib import Path + +from lightning.app.components import PopenPythonScript + +if __name__ == "__main__": + comp = PopenPythonScript(Path(__file__).parent / "pl_script.py") + comp.run() diff --git a/examples/app/components/python/component_tracer.py b/examples/app/components/python/component_tracer.py new file mode 100644 index 0000000..27ff653 --- /dev/null +++ b/examples/app/components/python/component_tracer.py @@ -0,0 +1,52 @@ +from lightning.app.components import TracerPythonScript +from lightning.app.storage import Path +from lightning.app.utilities.tracer import Tracer +from lightning.pytorch import Trainer + + +class PLTracerPythonScript(TracerPythonScript): + """This component can be used for ANY PyTorch Lightning script to track its progress and extract its best model + path.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Define the component state. + self.global_step = None + self.best_model_path = None + + def configure_tracer(self) -> Tracer: + from lightning.pytorch.callbacks import Callback + + class MyInjectedCallback(Callback): + def __init__(self, lightning_work): + self.lightning_work = lightning_work + + def on_train_start(self, trainer, pl_module) -> None: + print("This code doesn't belong to the script but was injected.") + print("Even the Lightning Work is available and state transfer works !") + print(self.lightning_work) + + def on_batch_train_end(self, trainer, *_) -> None: + # On every batch end, collects some information. + # This is communicated automatically to the rest of the app, + # so you can track your training in real time in the Lightning App UI. + self.lightning_work.global_step = trainer.global_step + best_model_path = trainer.checkpoint_callback.best_model_path + if best_model_path: + self.lightning_work.best_model_path = Path(best_model_path) + + # This hook would be called every time + # before a Trainer `__init__` method is called. + + def trainer_pre_fn(trainer, *args, **kwargs): + kwargs["callbacks"] = kwargs.get("callbacks", []) + [MyInjectedCallback(self)] + return {}, args, kwargs + + tracer = super().configure_tracer() + tracer.add_traced(Trainer, "__init__", pre_fn=trainer_pre_fn) + return tracer + + +if __name__ == "__main__": + comp = PLTracerPythonScript(Path(__file__).parent / "pl_script.py") + res = comp.run() diff --git a/examples/app/components/python/pl_script.py b/examples/app/components/python/pl_script.py new file mode 100644 index 0000000..75538da --- /dev/null +++ b/examples/app/components/python/pl_script.py @@ -0,0 +1,10 @@ +from lightning.pytorch import Trainer +from lightning.pytorch.demos.boring_classes import BoringModel + +if __name__ == "__main__": + model = BoringModel() + trainer = Trainer(max_epochs=1, accelerator="cpu", devices=2, strategy="ddp") + trainer.fit(model) + trainer.validate(model) + trainer.test(model) + trainer.predict(model) diff --git a/examples/app/components/serve/gradio/app.py b/examples/app/components/serve/gradio/app.py new file mode 100644 index 0000000..eef7000 --- /dev/null +++ b/examples/app/components/serve/gradio/app.py @@ -0,0 +1,52 @@ +from functools import partial + +import gradio as gr +import requests +import torch +from PIL import Image + +import lightning as L +from lightning.app.components import ServeGradio + + +# Credit to @akhaliq for his inspiring work. +# Find his original code there: https://huggingface.co/spaces/akhaliq/AnimeGANv2/blob/main/app.py +class AnimeGANv2UI(ServeGradio): + inputs = gr.inputs.Image(type="pil") + outputs = gr.outputs.Image(type="pil") + elon = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/34/Elon_Musk_Royal_Society_%28crop2%29.jpg/330px-Elon_Musk_Royal_Society_%28crop2%29.jpg" + img = Image.open(requests.get(elon, stream=True).raw) + img.save("elon.jpg") + examples = [["elon.jpg"]] + + def __init__(self): + super().__init__() + self.ready = False + + def predict(self, img): + return self.model(img=img) + + def build_model(self): + repo = "AK391/animegan2-pytorch:main" + model = torch.hub.load(repo, "generator", device="cpu") + face2paint = torch.hub.load(repo, "face2paint", size=512, device="cpu") + self.ready = True + return partial(face2paint, model=model) + + +class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.demo = AnimeGANv2UI() + + def run(self): + self.demo.run() + + def configure_layout(self): + tabs = [] + if self.demo.ready: + tabs.append({"name": "Home", "content": self.demo}) + return tabs + + +app = L.LightningApp(RootFlow()) diff --git a/examples/app/components/serve/gradio/beyonce.jpg b/examples/app/components/serve/gradio/beyonce.jpg new file mode 100644 index 0000000..68b6084 Binary files /dev/null and b/examples/app/components/serve/gradio/beyonce.jpg differ diff --git a/examples/app/components/serve/gradio/requirements.txt b/examples/app/components/serve/gradio/requirements.txt new file mode 100644 index 0000000..25acedd --- /dev/null +++ b/examples/app/components/serve/gradio/requirements.txt @@ -0,0 +1 @@ +gradio diff --git a/examples/app/dag/.gitignore b/examples/app/dag/.gitignore new file mode 100644 index 0000000..fcb9fa9 --- /dev/null +++ b/examples/app/dag/.gitignore @@ -0,0 +1,6 @@ +df_data +df_target +X_train +X_test +y_train +y_test diff --git a/examples/app/dag/.lightningignore b/examples/app/dag/.lightningignore new file mode 100644 index 0000000..78ae490 --- /dev/null +++ b/examples/app/dag/.lightningignore @@ -0,0 +1,8 @@ +*df_data* +*df_target* +*X_train* +*X_test* +*y_train* +*y_test* +*.shared* +*.storage* diff --git a/examples/app/dag/app.py b/examples/app/dag/app.py new file mode 100644 index 0000000..f344242 --- /dev/null +++ b/examples/app/dag/app.py @@ -0,0 +1,131 @@ +import os +from importlib import import_module + +import numpy as np +import pandas as pd +from sklearn import datasets +from sklearn.metrics import mean_squared_error + +import lightning as L +from lightning.app.components import TracerPythonScript +from lightning.app.storage import Payload +from lightning.app.structures import Dict, List + + +def get_path(path): + return os.path.join(os.path.dirname(__file__), path) + + +class GetDataWork(L.LightningWork): + """This component is responsible to download some data and store them with a PayLoad.""" + + def __init__(self): + super().__init__() + self.df_data = None + self.df_target = None + + def run(self): + print("Starting data collection...") + data = datasets.fetch_california_housing(data_home=get_path("data")) + self.df_data = Payload(pd.DataFrame(data["data"], columns=data["feature_names"])) + self.df_target = Payload(pd.DataFrame(data["target"], columns=["MedHouseVal"])) + print("Finished data collection.") + + +class ModelWork(L.LightningWork): + """This component is receiving some data and train a sklearn model.""" + + def __init__(self, model_path: str, parallel: bool): + super().__init__(parallel=parallel) + self.model_path, self.model_name = model_path.split(".") + self.test_rmse = None + + def run(self, X_train: Payload, X_test: Payload, y_train: Payload, y_test: Payload): + print(f"Starting training and evaluating {self.model_name}...") + module = import_module(f"sklearn.{self.model_path}") + model = getattr(module, self.model_name)() + model.fit(X_train.value, y_train.value.ravel()) + y_test_prediction = model.predict(X_test.value) + self.test_rmse = np.sqrt(mean_squared_error(y_test.value, y_test_prediction)) + print(f"Finished training and evaluating {self.model_name}.") + + +class DAG(L.LightningFlow): + """This component is a DAG.""" + + def __init__(self, models_paths: list): + super().__init__() + # Step 1: Create a work to get the data. + self.data_collector = GetDataWork() + + # Step 2: Create a tracer component. This is used to execute python script + # and collect any outputs from its globals as Payloads. + self.processing = TracerPythonScript( + get_path("processing.py"), + outputs=["X_train", "X_test", "y_train", "y_test"], + ) + + # Step 3: Create the work to train the models_paths in parallel. + self.dict = Dict( + **{model_path.split(".")[-1]: ModelWork(model_path, parallel=True) for model_path in models_paths} + ) + + # Step 4: Some element to track components progress. + self.has_completed = False + self.metrics = {} + + def run(self): + # Step 1 and 2: Download and process the data. + self.data_collector.run() + self.processing.run( + df_data=self.data_collector.df_data, + df_target=self.data_collector.df_target, + ) + + # Step 3: Launch n models training in parallel. + for model, work in self.dict.items(): + work.run( + X_train=self.processing.X_train, + X_test=self.processing.X_test, + y_train=self.processing.y_train, + y_test=self.processing.y_test, + ) + if work.test_rmse: # Use the state to control when to collect and stop. + self.metrics[model] = work.test_rmse + work.stop() # Stop the model work to reduce cost + + # Step 4: Print the score of each model when they are all finished. + if len(self.metrics) == len(self.dict): + print(self.metrics) + self.has_completed = True + + +class ScheduledDAG(L.LightningFlow): + def __init__(self, dag_cls, **dag_kwargs): + super().__init__() + self.dags = List() + self._dag_cls = dag_cls + self.dag_kwargs = dag_kwargs + + def run(self): + """Example of scheduling an infinite number of DAG runs continuously.""" + # Step 1: Every minute, create and launch a new DAG. + if self.schedule("* * * * *"): + print("Launching a new DAG") + self.dags.append(self._dag_cls(**self.dag_kwargs)) + + for dag in self.dags: + if not dag.has_completed: + dag.run() + + +app = L.LightningApp( + ScheduledDAG( + DAG, + models_paths=[ + "svm.SVR", + "linear_model.LinearRegression", + "tree.DecisionTreeRegressor", + ], + ), +) diff --git a/examples/app/dag/processing.py b/examples/app/dag/processing.py new file mode 100644 index 0000000..245377f --- /dev/null +++ b/examples/app/dag/processing.py @@ -0,0 +1,14 @@ +import random + +from sklearn.model_selection import train_test_split +from sklearn.preprocessing import MinMaxScaler + +print("Starting processing ...") +scaler = MinMaxScaler() + +X_train, X_test, y_train, y_test = train_test_split( + df_data.values, df_target.values, test_size=0.20, random_state=random.randint(0, 42) +) +X_train = scaler.fit_transform(X_train) +X_test = scaler.transform(X_test) +print("Finished processing.") diff --git a/examples/app/dag/requirements.txt b/examples/app/dag/requirements.txt new file mode 100644 index 0000000..f669f51 --- /dev/null +++ b/examples/app/dag/requirements.txt @@ -0,0 +1,2 @@ +scikit-learn +pandas diff --git a/examples/app/display_name/.lightningignore b/examples/app/display_name/.lightningignore new file mode 100644 index 0000000..f7275bb --- /dev/null +++ b/examples/app/display_name/.lightningignore @@ -0,0 +1 @@ +venv/ diff --git a/examples/app/display_name/app.py b/examples/app/display_name/app.py new file mode 100644 index 0000000..f06d8ee --- /dev/null +++ b/examples/app/display_name/app.py @@ -0,0 +1,25 @@ +import lightning as L + + +class Work(L.LightningWork): + def __init__(self, start_with_flow=True): + super().__init__(start_with_flow=start_with_flow) + + def run(self): + pass + + +class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.w = Work() + self.w1 = Work(start_with_flow=False) + self.w.display_name = "My Custom Name" # Not supported yet + self.w1.display_name = "My Custom Name 1" + + def run(self): + self.w.run() + self.w1.run() + + +app = L.LightningApp(Flow()) diff --git a/examples/app/drive/.gitignore b/examples/app/drive/.gitignore new file mode 100644 index 0000000..eaa5fa8 --- /dev/null +++ b/examples/app/drive/.gitignore @@ -0,0 +1 @@ +a.txt diff --git a/examples/app/drive/app.py b/examples/app/drive/app.py new file mode 100644 index 0000000..1a24f5c --- /dev/null +++ b/examples/app/drive/app.py @@ -0,0 +1,51 @@ +import os + +import lightning as L +from lightning.app.storage import Drive + + +class Work_1(L.LightningWork): + def run(self, drive: Drive): + # 1. Create a file. + with open("a.txt", "w") as f: + f.write("Hello World !") + + # 2. Put the file into the drive. + drive.put("a.txt") + + # 3. Delete the locally. + os.remove("a.txt") + + +class Work_2(L.LightningWork): + def __init__(self): + super().__init__() + + def run(self, drive: Drive): + print(drive.list(".")) # Prints ["a.txt"] + + print(os.path.exists("a.txt")) # Prints False + + drive.get("a.txt") # Transfer the file from this drive to the local filesystem. + + print(os.path.exists("a.txt")) # Prints True + + with open("a.txt") as f: + print(f.readlines()[0]) # Prints Hello World ! + + +class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.drive_1 = Drive("lit://drive_1") + self.work_1 = Work_1() + self.work_2 = Work_2() + + def run(self): + # Pass the drive to both works. + self.work_1.run(self.drive_1) + self.work_2.run(self.drive_1) + self.stop("Application End!") + + +app = L.LightningApp(Flow()) diff --git a/examples/app/hpo/README.md b/examples/app/hpo/README.md new file mode 100644 index 0000000..b9b648b --- /dev/null +++ b/examples/app/hpo/README.md @@ -0,0 +1,64 @@ +# Build a Lightning Hyperparameter Optimization (HPO) App + +## A bit of background + +Traditionally, developing machine learning (ML) products requires choosing among a large space of +hyperparameters while creating and training the ML models. Hyperparameter optimization +(HPO) aims to find a well-performing hyperparameter configuration for a given ML model +on a dataset at hand, including the ML model, +its hyperparameters, and other data processing steps. + +HPOs free the human expert from a tedious and error-prone, manual hyperparameter tuning process. + +As an example, in the famous [scikit-learn](https://scikit-learn.org/stable/) library, +hyperparameters are passed as arguments to the constructor of +the estimator classes such as `C` kernel for +[Support Vector Classifier](https://scikit-learn.org/stable/modules/classes.html?highlight=svm#module-sklearn.svm), etc. + +It is possible and recommended to search the hyperparameter space for the best validation score. + +An HPO search consists of: + +- an objective method +- a defined parameter space +- a method for searching or sampling candidates + +A naive method for sampling candidates is grid search, which exhaustively considers all +hyperparameter combinations from a user-specified grid. + +Fortunately, HPO is an active area of research, and many methods have been developed to +optimize the time required to get strong candidates. + +In the following tutorial, you will learn how to use Lightning together with [Optuna](https://optuna.org/). + +[Optuna](https://optuna.org/) is an open source HPO framework to automate hyperparameter search. +Out-of-the-box, it provides efficient algorithms to search large spaces and prune unpromising trials for faster results. + +First, you will learn about the best practices on how to implement HPO without the Lightning Framework. +Secondly, we will dive into a working HPO application with Lightning, and finally create a neat +[HiPlot UI](https://facebookresearch.github.io/hiplot/_static/demo/demo_basic_usage.html?hip.filters=%5B%5D&hip.color_by=%22dropout%22&hip.PARALLEL_PLOT.order=%5B%22uid%22%2C%22dropout%22%2C%22lr%22%2C%22loss%22%2C%22optimizer%22%5D) +for our application. + +## Getting started + +### Step 1: Download the data + +```bash +python download_data.py +``` + +### Step 2: Run the HPO Lightning App without an UI + +```bash +lightning run app app_wo_ui.py +``` + +### Step 3: Run the HPO Lightning App with HiPlot UI in Streamlit. + +```bash +lightning run app app_wi_ui.py +``` + +## Learn More + +In the documentation, search for `Build a Sweep App`. diff --git a/examples/app/hpo/app_wi_ui.py b/examples/app/hpo/app_wi_ui.py new file mode 100644 index 0000000..ec0ce25 --- /dev/null +++ b/examples/app/hpo/app_wi_ui.py @@ -0,0 +1,61 @@ +from pathlib import Path + +import optuna +from hyperplot import HiPlotFlow +from objective import ObjectiveWork + +import lightning as L +from lightning.app.structures import Dict + + +class RootHPOFlow(L.LightningFlow): + def __init__(self, script_path, data_dir, total_trials, simultaneous_trials): + super().__init__() + self.script_path = script_path + self.data_dir = data_dir + self.total_trials = total_trials + self.simultaneous_trials = simultaneous_trials + self.num_trials = simultaneous_trials + self._study = optuna.create_study() + self.ws = Dict() + self.hi_plot = HiPlotFlow() + + def run(self): + if self.num_trials >= self.total_trials: + self.stop() + + has_told_study = [] + + for trial_idx in range(self.num_trials): + work_name = f"objective_work_{trial_idx}" + if work_name not in self.ws: + objective_work = ObjectiveWork( + script_path=self.script_path, + data_dir=self.data_dir, + cloud_compute=L.CloudCompute("cpu"), + ) + self.ws[work_name] = objective_work + if not self.ws[work_name].has_started: + trial = self._study.ask(ObjectiveWork.distributions()) + self.ws[work_name].run(trial_id=trial._trial_id, **trial.params) + + if self.ws[work_name].metric and not self.ws[work_name].has_told_study: + self.hi_plot.data.append({"x": -1 * self.ws[work_name].metric, **self.ws[work_name].params}) + self._study.tell(self.ws[work_name].trial_id, self.ws[work_name].metric) + self.ws[work_name].has_told_study = True + + has_told_study.append(self.ws[work_name].has_told_study) + + if all(has_told_study): + self.num_trials += self.simultaneous_trials + + +if __name__ == "__main__": + app = L.LightningApp( + RootHPOFlow( + script_path=str(Path(__file__).parent / "pl_script.py"), + data_dir="data/hymenoptera_data_version_0", + total_trials=6, + simultaneous_trials=2, + ) + ) diff --git a/examples/app/hpo/app_wo_ui.py b/examples/app/hpo/app_wo_ui.py new file mode 100644 index 0000000..9ae6c58 --- /dev/null +++ b/examples/app/hpo/app_wo_ui.py @@ -0,0 +1,58 @@ +from pathlib import Path + +import optuna +from objective import ObjectiveWork + +import lightning as L +from lightning.app.structures import Dict + + +class RootHPOFlow(L.LightningFlow): + def __init__(self, script_path, data_dir, total_trials, simultaneous_trials): + super().__init__() + self.script_path = script_path + self.data_dir = data_dir + self.total_trials = total_trials + self.simultaneous_trials = simultaneous_trials + self.num_trials = simultaneous_trials + self._study = optuna.create_study() + self.ws = Dict() + + def run(self): + if self.num_trials >= self.total_trials: + self.stop() + + has_told_study = [] + + for trial_idx in range(self.num_trials): + work_name = f"objective_work_{trial_idx}" + if work_name not in self.ws: + objective_work = ObjectiveWork( + script_path=self.script_path, + data_dir=self.data_dir, + cloud_compute=L.CloudCompute("cpu"), + ) + self.ws[work_name] = objective_work + if not self.ws[work_name].has_started: + trial = self._study.ask(ObjectiveWork.distributions()) + self.ws[work_name].run(trial_id=trial._trial_id, **trial.params) + + if self.ws[work_name].metric and not self.ws[work_name].has_told_study: + self._study.tell(self.ws[work_name].trial_id, self.ws[work_name].metric) + self.ws[work_name].has_told_study = True + + has_told_study.append(self.ws[work_name].has_told_study) + + if all(has_told_study): + self.num_trials += self.simultaneous_trials + + +if __name__ == "__main__": + app = L.LightningApp( + RootHPOFlow( + script_path=str(Path(__file__).parent / "pl_script.py"), + data_dir="data/hymenoptera_data_version_0", + total_trials=6, + simultaneous_trials=2, + ) + ) diff --git a/examples/app/hpo/download_data.py b/examples/app/hpo/download_data.py new file mode 100644 index 0000000..d82b86a --- /dev/null +++ b/examples/app/hpo/download_data.py @@ -0,0 +1,5 @@ +from utils import download_data + +data_dir = "hymenoptera_data_version_0" +download_url = f"https://pl-flash-data.s3.amazonaws.com/{data_dir}.zip" +download_data(download_url, "./data") diff --git a/examples/app/hpo/hyperplot.py b/examples/app/hpo/hyperplot.py new file mode 100644 index 0000000..3d82378 --- /dev/null +++ b/examples/app/hpo/hyperplot.py @@ -0,0 +1,34 @@ +import lightning as L +from lightning.app.frontend import StreamlitFrontend +from lightning.app.utilities.state import AppState + + +class HiPlotFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.data = [] + + def run(self): + pass + + def configure_layout(self): + return StreamlitFrontend(render_fn=render_fn) + + +def render_fn(state: AppState): + import json + + import hiplot as hip + import streamlit as st + from streamlit_autorefresh import st_autorefresh + + st.set_page_config(layout="wide") + st_autorefresh(interval=1000, limit=None, key="refresh") + + if not state.data: + st.write("No data available yet ! Stay tuned") + return + + xp = hip.Experiment.from_iterable(state.data) + ret_val = xp.to_streamlit(ret="selected_uids", key="hip").display() + st.markdown("hiplot returned " + json.dumps(ret_val)) diff --git a/examples/app/hpo/objective.py b/examples/app/hpo/objective.py new file mode 100644 index 0000000..a9dae08 --- /dev/null +++ b/examples/app/hpo/objective.py @@ -0,0 +1,63 @@ +import os +import tempfile +from datetime import datetime +from typing import Optional + +import pandas as pd +import torch +from optuna.distributions import CategoricalDistribution, LogUniformDistribution +from torchmetrics import Accuracy + +import lightning as L +from lightning.app.components import TracerPythonScript + + +class ObjectiveWork(TracerPythonScript): + def __init__(self, script_path: str, data_dir: str, cloud_compute: Optional[L.CloudCompute]): + timestamp = datetime.now().strftime("%H:%M:%S") + tmpdir = tempfile.TemporaryDirectory().name + submission_path = os.path.join(tmpdir, f"{timestamp}.csv") + best_model_path = os.path.join(tmpdir, f"{timestamp}.model.pt") + super().__init__( + script_path, + script_args=[ + f"--train_data_path={data_dir}/train", + f"--test_data_path={data_dir}/test", + f"--submission_path={submission_path}", + f"--best_model_path={best_model_path}", + ], + cloud_compute=cloud_compute, + ) + self.data_dir = data_dir + self.best_model_path = best_model_path + self.submission_path = submission_path + self.metric = None + self.trial_id = None + self.metric = None + self.params = None + self.has_told_study = False + + def run(self, trial_id: int, **params): + self.trial_id = trial_id + self.params = params + self.script_args.extend([f"--{k}={v}" for k, v in params.items()]) + super().run() + self.compute_metric() + + def _to_labels(self, path: str): + return torch.from_numpy(pd.read_csv(path).label.values) + + def compute_metric(self): + self.metric = -1 * float( + Accuracy(task="binary")( + self._to_labels(self.submission_path), + self._to_labels(f"{self.data_dir}/ground_truth.csv"), + ) + ) + + @staticmethod + def distributions(): + return { + "backbone": CategoricalDistribution(["resnet18", "resnet34"]), + "learning_rate": LogUniformDistribution(0.0001, 0.1), + } diff --git a/examples/app/hpo/pl_script.py b/examples/app/hpo/pl_script.py new file mode 100644 index 0000000..bbc4537 --- /dev/null +++ b/examples/app/hpo/pl_script.py @@ -0,0 +1,43 @@ +import argparse +import os + +import pandas as pd +import torch +from flash import Trainer +from flash.image import ImageClassificationData, ImageClassifier + +# Parse arguments provided by the Work. +parser = argparse.ArgumentParser() +parser.add_argument("--train_data_path", type=str, required=True) +parser.add_argument("--submission_path", type=str, required=True) +parser.add_argument("--test_data_path", type=str, required=True) +parser.add_argument("--best_model_path", type=str, required=True) +# Optional +parser.add_argument("--backbone", type=str, default="resnet18") +parser.add_argument("--learning_rate", type=float, default=0.01) +args = parser.parse_args() + + +datamodule = ImageClassificationData.from_folders( + train_folder=args.train_data_path, + batch_size=8, +) + +model = ImageClassifier(datamodule.num_classes, backbone=args.backbone) +trainer = Trainer(fast_dev_run=True) +trainer.fit(model, datamodule=datamodule) +trainer.save_checkpoint(args.best_model_path) + +datamodule = ImageClassificationData.from_folders( + predict_folder=args.test_data_path, + batch_size=8, +) + +predictions = Trainer().predict(model, datamodule=datamodule) +submission_data = [ + {"filename": os.path.basename(p["metadata"]["filepath"]), "label": torch.argmax(p["preds"]).item()} + for batch in predictions + for p in batch +] +df = pd.DataFrame(submission_data) +df.to_csv(args.submission_path, index=False) diff --git a/examples/app/hpo/requirements.txt b/examples/app/hpo/requirements.txt new file mode 100644 index 0000000..bd85880 --- /dev/null +++ b/examples/app/hpo/requirements.txt @@ -0,0 +1,3 @@ +optuna +lightning-flash[image,serve] == 0.7.0 +hiplot diff --git a/examples/app/hpo/utils.py b/examples/app/hpo/utils.py new file mode 100644 index 0000000..a08fda2 --- /dev/null +++ b/examples/app/hpo/utils.py @@ -0,0 +1,54 @@ +import os +import os.path +import tarfile +import zipfile + +import requests + + +def download_data(url: str, path: str = "data/", verbose: bool = False) -> None: + """Download file with progressbar. + + # Code taken from: https://gist.github.com/ruxi/5d6803c116ec1130d484a4ab8c00c603 + # __author__ = "github.com/ruxi" + # __license__ = "MIT" + + Usage: + download_file('http://web4host.net/5MB.zip') + """ + if url == "NEED_TO_BE_CREATED": + raise NotImplementedError + + if not os.path.exists(path): + os.makedirs(path) + local_filename = os.path.join(path, url.split("/")[-1]) + r = requests.get(url, stream=True, verify=False) + file_size = int(r.headers["Content-Length"]) if "Content-Length" in r.headers else 0 + chunk_size = 1024 + num_bars = int(file_size / chunk_size) + if verbose: + print({"file_size": file_size}) + print({"num_bars": num_bars}) + + if not os.path.exists(local_filename): + with open(local_filename, "wb") as fp: + for chunk in r.iter_content(chunk_size=chunk_size): + fp.write(chunk) # type: ignore + + def extract_tarfile(file_path: str, extract_path: str, mode: str): + if os.path.exists(file_path): + with tarfile.open(file_path, mode=mode) as tar_ref: + for member in tar_ref.getmembers(): + try: + tar_ref.extract(member, path=extract_path, set_attrs=False) + except PermissionError: + raise PermissionError(f"Could not extract tar file {file_path}") + + if ".zip" in local_filename: + if os.path.exists(local_filename): + with zipfile.ZipFile(local_filename, "r") as zip_ref: + zip_ref.extractall(path) + elif local_filename.endswith(".tar.gz") or local_filename.endswith(".tgz"): + extract_tarfile(local_filename, path, "r:gz") + elif local_filename.endswith(".tar.bz2") or local_filename.endswith(".tbz"): + extract_tarfile(local_filename, path, "r:bz2") diff --git a/examples/app/installation_commands/app.py b/examples/app/installation_commands/app.py new file mode 100644 index 0000000..b03ce63 --- /dev/null +++ b/examples/app/installation_commands/app.py @@ -0,0 +1,32 @@ +# EXAMPLE COMPONENT: RUN A SCRIPT +# app.py +# !echo "I am installing a dependency not declared in a requirements file" +# !pip install lmdb +import lmdb + +import lightning as L + + +class YourComponent(L.LightningWork): + def run(self): + print(lmdb.version()) + print("lmdb successfully installed") + print("Accessing a module in a Work or Flow body works!") + + +class RootFlow(L.LightningFlow): + def __init__(self, work): + super().__init__() + self.work = work + + def run(self): + self.work.run() + + +print(f"Accessing an object in main code body works!: version = {lmdb.version()}") + + +# run on a cloud machine +compute = L.CloudCompute("cpu") +worker = YourComponent(cloud_compute=compute) +app = L.LightningApp(RootFlow(worker)) diff --git a/examples/app/interruptible/app.py b/examples/app/interruptible/app.py new file mode 100644 index 0000000..60077ec --- /dev/null +++ b/examples/app/interruptible/app.py @@ -0,0 +1,32 @@ +from time import sleep + +import lightning as L + + +class Work(L.LightningWork): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.counter = 0 + + def run(self): + while True: + print(self.counter) + self.counter += 1 + sleep(1) + + +class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.w = Work( + cloud_compute=L.CloudCompute("gpu", interruptible=True), + start_with_flow=False, + parallel=True, + ) + + def run(self): + self.w.run() + print(self.w.counter) + + +app = L.LightningApp(Flow()) diff --git a/examples/app/justpy/app.py b/examples/app/justpy/app.py new file mode 100644 index 0000000..a4c9abc --- /dev/null +++ b/examples/app/justpy/app.py @@ -0,0 +1,42 @@ +from typing import Callable + +from lightning import LightningApp, LightningFlow +from lightning.app.frontend import JustPyFrontend + + +class Flow(LightningFlow): + def __init__(self): + super().__init__() + self.counter = 0 + + def run(self): + print(self.counter) + + def configure_layout(self): + return JustPyFrontend(render_fn=render_fn) + + +def render_fn(get_state: Callable) -> Callable: + import justpy as jp + + def webpage(): + wp = jp.QuasarPage(dark=True) + d = jp.Div(classes="q-pa-md q-gutter-sm", a=wp) + container = jp.QBtn(color="primary", text="Counter: 0") + + async def click(*_): + state = get_state() + state.counter += 1 + container.text = f"Counter: {state.counter}" + + button = jp.QBtn(color="primary", text="Click Me!", click=click) + + d.add(button) + d.add(container) + + return wp + + return webpage + + +app = LightningApp(Flow()) diff --git a/examples/app/justpy/requirements.txt b/examples/app/justpy/requirements.txt new file mode 100644 index 0000000..5f69409 --- /dev/null +++ b/examples/app/justpy/requirements.txt @@ -0,0 +1 @@ +justpy diff --git a/examples/app/layout/app.py b/examples/app/layout/app.py new file mode 100644 index 0000000..7048f62 --- /dev/null +++ b/examples/app/layout/app.py @@ -0,0 +1,100 @@ +"""An example showcasing how `configure_layout` can be used to nest user interfaces of different flows. + +Run the app: + +lightning run app examples/layout/demo.py + +This starts one server for each flow that returns a UI. Access the UI at the link printed in the terminal. +""" + +import os +from time import sleep + +import lightning as L +from lightning.app.frontend import StaticWebFrontend, StreamlitFrontend + + +class C11(L.LightningFlow): + def __init__(self): + super().__init__() + self.message = "Hello Streamlit!" + + def run(self): + pass + + def configure_layout(self): + return StreamlitFrontend(render_fn=render_c11) + + +def render_c11(state): + import streamlit as st + + st.write(state.message) + + +class C21(L.LightningFlow): + def __init__(self): + super().__init__() + + def run(self): + pass + + def configure_layout(self): + return StaticWebFrontend(os.path.join(os.path.dirname(__file__), "ui1")) + + +class C22(L.LightningFlow): + def __init__(self): + super().__init__() + + def run(self): + pass + + def configure_layout(self): + return StaticWebFrontend(os.path.join(os.path.dirname(__file__), "ui2")) + + +class C1(L.LightningFlow): + def __init__(self): + super().__init__() + self.c11 = C11() + + def run(self): + pass + + +class C2(L.LightningFlow): + def __init__(self): + super().__init__() + self.c21 = C21() + self.c22 = C22() + + def run(self): + pass + + def configure_layout(self): + return [ + {"name": "one", "content": self.c21}, + {"name": "two", "content": self.c22}, + ] + + +class Root(L.LightningFlow): + def __init__(self): + super().__init__() + self.c1 = C1() + self.c2 = C2() + + def run(self): + sleep(10) + self.stop("Layout End") + + def configure_layout(self): + return [ + {"name": "one", "content": self.c1.c11}, + {"name": "two", "content": self.c2}, + {"name": "three", "content": "https://lightning.ai"}, + ] + + +app = L.LightningApp(Root()) diff --git a/examples/app/layout/requirements.txt b/examples/app/layout/requirements.txt new file mode 100644 index 0000000..12a4706 --- /dev/null +++ b/examples/app/layout/requirements.txt @@ -0,0 +1 @@ +streamlit diff --git a/examples/app/layout/ui1/index.html b/examples/app/layout/ui1/index.html new file mode 100644 index 0000000..7019634 --- /dev/null +++ b/examples/app/layout/ui1/index.html @@ -0,0 +1,10 @@ + + + + + One + + +One + + diff --git a/examples/app/layout/ui2/index.html b/examples/app/layout/ui2/index.html new file mode 100644 index 0000000..f9b6432 --- /dev/null +++ b/examples/app/layout/ui2/index.html @@ -0,0 +1,10 @@ + + + + + Two + + +Two + + diff --git a/examples/app/mount/app.py b/examples/app/mount/app.py new file mode 100644 index 0000000..d0d2adf --- /dev/null +++ b/examples/app/mount/app.py @@ -0,0 +1,35 @@ +import os + +import lightning as L +from lightning_app import CloudCompute +from lightning_app.storage import Mount + + +class Work(L.LightningWork): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def run(self): + files = os.listdir("/content/esRedditJson/") + for file in files: + print(file) + assert "esRedditJson1" in files + + +class Flow(L.LightningFlow): + def __init__(self): + super().__init__() + self.work_1 = Work( + cloud_compute=CloudCompute( + mounts=Mount( + source="s3://ryft-public-sample-data/esRedditJson/", + mount_path="/content/esRedditJson/", + ), + ) + ) + + def run(self): + self.work_1.run() + + +app = L.LightningApp(Flow()) diff --git a/examples/app/multi_node/README.md b/examples/app/multi_node/README.md new file mode 100644 index 0000000..aef1524 --- /dev/null +++ b/examples/app/multi_node/README.md @@ -0,0 +1,51 @@ +# Lightning & Multi Node Training + +Lightning supports makes multi-node training simple by providing a simple interface to orchestrate compute and data. + +## Multi Node with raw PyTorch + +You can run the multi-node raw PyTorch by running the following commands. + +Here is an example where you spawn your processes yourself. + +```bash +lightning run app train_pytorch.py +``` + +or you can use the built-in component for it. + +```bash +lightning run app train_pytorch_spawn.py +``` + +## Multi Node with raw PyTorch + Fabric + +You can run the multi-node raw PyTorch and Fabric by running the following commands. + +```bash +lightning run app train_fabric.py +``` + +Using Fabric, you retain control over your loops while accessing in a minimal way all Lightning distributed strategies. + +## Multi Node with Lightning Trainer + +Lightning supports running Lightning Trainer from a script or within a Lightning Work. + +You can either run a script directly + +```bash +lightning run app train_pl_script.py +``` + +or run your code within as a work. + +```bash +lightning run app train_pl.py +``` + +## Multi Node with any frameworks + +```bash +lightning run app train_any.py +``` diff --git a/examples/app/multi_node/pl_boring_script.py b/examples/app/multi_node/pl_boring_script.py new file mode 100644 index 0000000..bdced94 --- /dev/null +++ b/examples/app/multi_node/pl_boring_script.py @@ -0,0 +1,7 @@ +import lightning as L +from lightning.pytorch.demos.boring_classes import BoringModel + +if __name__ == "__main__": + model = BoringModel() + trainer = L.Trainer(max_epochs=1) + trainer.fit(model) diff --git a/examples/app/multi_node/requirements.txt b/examples/app/multi_node/requirements.txt new file mode 100644 index 0000000..12c6d5d --- /dev/null +++ b/examples/app/multi_node/requirements.txt @@ -0,0 +1 @@ +torch diff --git a/examples/app/multi_node/train_any.py b/examples/app/multi_node/train_any.py new file mode 100644 index 0000000..5dfb947 --- /dev/null +++ b/examples/app/multi_node/train_any.py @@ -0,0 +1,22 @@ +import lightning as L +from lightning.app.components import MultiNode + + +class AnyDistributedComponent(L.LightningWork): + def run( + self, + main_address: str, + main_port: int, + num_nodes: int, + node_rank: int, + ): + print(f"ADD YOUR DISTRIBUTED CODE: {main_address} {main_port} {num_nodes} {node_rank}.") + + +app = L.LightningApp( + MultiNode( + AnyDistributedComponent, + num_nodes=2, + cloud_compute=L.CloudCompute("gpu"), + ) +) diff --git a/examples/app/multi_node/train_fabric.py b/examples/app/multi_node/train_fabric.py new file mode 100644 index 0000000..335e1e7 --- /dev/null +++ b/examples/app/multi_node/train_fabric.py @@ -0,0 +1,41 @@ +import torch + +import lightning as L +from lightning.app.components import FabricMultiNode +from lightning.fabric import Fabric + + +class FabricPyTorchDistributed(L.LightningWork): + def run(self): + # 1. Prepare the model + model = torch.nn.Sequential( + torch.nn.Linear(1, 1), + torch.nn.ReLU(), + torch.nn.Linear(1, 1), + ) + + # 2. Create Fabric. + fabric = Fabric(strategy="ddp", precision="16-mixed") + model, optimizer = fabric.setup(model, torch.optim.SGD(model.parameters(), lr=0.01)) + criterion = torch.nn.MSELoss() + + # 3. Train the model for 1000 steps. + for step in range(1000): + model.zero_grad() + x = torch.tensor([0.8]).to(fabric.device) + target = torch.tensor([1.0]).to(fabric.device) + output = model(x) + loss = criterion(output, target) + print(f"global_rank: {fabric.global_rank} step: {step} loss: {loss}") + fabric.backward(loss) + optimizer.step() + + +# 8 GPUs: (2 nodes of 4 x v100) +app = L.LightningApp( + FabricMultiNode( + FabricPyTorchDistributed, + cloud_compute=L.CloudCompute("gpu-fast-multi"), # 4 x V100 + num_nodes=2, + ) +) diff --git a/examples/app/multi_node/train_lt.py b/examples/app/multi_node/train_lt.py new file mode 100644 index 0000000..8ed62a1 --- /dev/null +++ b/examples/app/multi_node/train_lt.py @@ -0,0 +1,20 @@ +# app.py +import lightning as L +from lightning.app.components import LightningTrainerMultiNode +from lightning.pytorch.demos.boring_classes import BoringModel + + +class LightningTrainerDistributed(L.LightningWork): + def run(self): + model = BoringModel() + trainer = L.Trainer(max_epochs=10, strategy="ddp") + trainer.fit(model) + + +# 8 GPUs: (2 nodes of 4 x v100) +component = LightningTrainerMultiNode( + LightningTrainerDistributed, + num_nodes=2, + cloud_compute=L.CloudCompute("gpu-fast-multi"), # 4 x v100 +) +app = L.LightningApp(component) diff --git a/examples/app/multi_node/train_lt_script.py b/examples/app/multi_node/train_lt_script.py new file mode 100644 index 0000000..58f8473 --- /dev/null +++ b/examples/app/multi_node/train_lt_script.py @@ -0,0 +1,12 @@ +import lightning as L +from lightning.app.components import LightningTrainerScript +from lightning.app.utilities.packaging.cloud_compute import CloudCompute + +# 8 GPUs: (2 nodes of 4 x v100) +app = L.LightningApp( + LightningTrainerScript( + "pl_boring_script.py", + num_nodes=2, + cloud_compute=CloudCompute("gpu-fast-multi"), # 4 x v100 + ), +) diff --git a/examples/app/multi_node/train_pytorch.py b/examples/app/multi_node/train_pytorch.py new file mode 100644 index 0000000..e5a9a1f --- /dev/null +++ b/examples/app/multi_node/train_pytorch.py @@ -0,0 +1,61 @@ +# app.py +# ! pip install torch +import torch +from torch.nn.parallel.distributed import DistributedDataParallel + +import lightning as L +from lightning.app.components import MultiNode + + +def distributed_train(local_rank: int, main_address: str, main_port: int, num_nodes: int, node_rank: int, nprocs: int): + # 1. SET UP DISTRIBUTED ENVIRONMENT + global_rank = local_rank + node_rank * nprocs + world_size = num_nodes * nprocs + + if torch.distributed.is_available() and not torch.distributed.is_initialized(): + torch.distributed.init_process_group( + "nccl" if torch.cuda.is_available() else "gloo", + rank=global_rank, + world_size=world_size, + init_method=f"tcp://{main_address}:{main_port}", + ) + + # 2. PREPARE DISTRIBUTED MODEL + model = torch.nn.Linear(32, 2) + device = torch.device(f"cuda:{local_rank}") if torch.cuda.is_available() else torch.device("cpu") + model = DistributedDataParallel(model, device_ids=[local_rank] if torch.cuda.is_available() else None).to(device) + + # 3. SETUP LOSS AND OPTIMIZER + criterion = torch.nn.MSELoss() + optimizer = torch.optim.SGD(model.parameters(), lr=0.01) + + # 4.TRAIN THE MODEL FOR 50 STEPS + for step in range(50): + model.zero_grad() + x = torch.randn(64, 32).to(device) + output = model(x) + loss = criterion(output, torch.ones_like(output)) + print(f"global_rank: {global_rank} step: {step} loss: {loss}") + loss.backward() + optimizer.step() + + # 5. VERIFY ALL COPIES OF THE MODEL HAVE THE SAME WEIGTHS AT END OF TRAINING + weight = model.module.weight.clone() + torch.distributed.all_reduce(weight) + assert torch.equal(model.module.weight, weight / world_size) + + print("Multi Node Distributed Training Done!") + + +class PyTorchDistributed(L.LightningWork): + def run(self, main_address: str, main_port: int, num_nodes: int, node_rank: int): + nprocs = torch.cuda.device_count() if torch.cuda.is_available() else 1 + torch.multiprocessing.spawn( + distributed_train, args=(main_address, main_port, num_nodes, node_rank, nprocs), nprocs=nprocs + ) + + +# 8 GPUs: (2 nodes x 4 v 100) +compute = L.CloudCompute("gpu-fast-multi") # 4 x v100 +component = MultiNode(PyTorchDistributed, num_nodes=2, cloud_compute=compute) +app = L.LightningApp(component) diff --git a/examples/app/multi_node/train_pytorch_spawn.py b/examples/app/multi_node/train_pytorch_spawn.py new file mode 100644 index 0000000..165a0c7 --- /dev/null +++ b/examples/app/multi_node/train_pytorch_spawn.py @@ -0,0 +1,52 @@ +import torch +from torch.nn.parallel.distributed import DistributedDataParallel + +import lightning as L +from lightning.app.components import PyTorchSpawnMultiNode + + +class PyTorchDistributed(L.LightningWork): + def run( + self, + world_size: int, + node_rank: int, + global_rank: str, + local_rank: int, + ): + # 1. Prepare the model + model = torch.nn.Sequential( + torch.nn.Linear(1, 1), + torch.nn.ReLU(), + torch.nn.Linear(1, 1), + ) + + # 2. Setup distributed training + device = torch.device(f"cuda:{local_rank}") if torch.cuda.is_available() else torch.device("cpu") + model = DistributedDataParallel( + model.to(device), device_ids=[local_rank] if torch.cuda.is_available() else None + ) + + # 3. Prepare loss and optimizer + criterion = torch.nn.MSELoss() + optimizer = torch.optim.SGD(model.parameters(), lr=0.01) + + # 4. Train the model for 1000 steps. + for step in range(1000): + model.zero_grad() + x = torch.tensor([0.8]).to(device) + target = torch.tensor([1.0]).to(device) + output = model(x) + loss = criterion(output, target) + print(f"global_rank: {global_rank} step: {step} loss: {loss}") + loss.backward() + optimizer.step() + + +# 8 GPUs: (2 nodes x 4 v 100) +app = L.LightningApp( + PyTorchSpawnMultiNode( + PyTorchDistributed, + num_nodes=2, + cloud_compute=L.CloudCompute("gpu-fast-multi"), # 4 x v100 + ) +) diff --git a/examples/app/payload/app.py b/examples/app/payload/app.py new file mode 100644 index 0000000..3da3a3c --- /dev/null +++ b/examples/app/payload/app.py @@ -0,0 +1,31 @@ +import lightning as L +from lightning.app.storage import Payload + + +class SourceFileWriterWork(L.LightningWork): + def __init__(self): + super().__init__() + self.value = None + + def run(self): + self.value = Payload(42) + + +class DestinationWork(L.LightningWork): + def run(self, payload): + assert payload.value == 42 + + +class RootFlow(L.LightningFlow): + def __init__(self): + super().__init__() + self.src = SourceFileWriterWork() + self.dst = DestinationWork() + + def run(self): + self.src.run() + self.dst.run(self.src.value) + self.stop("Application End!") + + +app = L.LightningApp(RootFlow()) diff --git a/examples/app/pickle_or_not/app.py b/examples/app/pickle_or_not/app.py new file mode 100644 index 0000000..25b3abf --- /dev/null +++ b/examples/app/pickle_or_not/app.py @@ -0,0 +1,54 @@ +import logging + +import lightning as L + +logger = logging.getLogger(__name__) + + +class PickleChecker(L.LightningWork): + def run(self, pickle_image: bytes): + parsed = self.parse_image(pickle_image) + if parsed == b"it is a pickle": + return True + if parsed == b"it is not a pickle": + return False + raise Exception("Couldn't parse the image") + + @staticmethod + def parse_image(image_str: bytes): + return image_str + + +class Slack(L.LightningFlow): + def __init__(self): + super().__init__() + + @staticmethod + def send_message(message): + logger.info(f"Sending message: {message}") + + def run(self): + pass + + +class RootComponent(L.LightningFlow): + def __init__(self): + super().__init__() + self.pickle_checker = PickleChecker() + self.slack = Slack() + self.counter = 3 + + def run(self): + if self.counter > 0: + logger.info(f"Running the app {self.counter}") + image_str = b"it is not a pickle" + if self.pickle_checker.run(image_str): + self.slack.send_message("It's a pickle!") + else: + self.slack.send_message("It's not a pickle!") + self.counter -= 1 + else: + self.stop("Pickle or Not End") + + +app = L.LightningApp(RootComponent()) diff --git a/examples/app/pickle_or_not/requirements.txt b/examples/app/pickle_or_not/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/examples/app/server/app.py b/examples/app/server/app.py new file mode 100644 index 0000000..6cd2397 --- /dev/null +++ b/examples/app/server/app.py @@ -0,0 +1,42 @@ +# !pip install torchvision pydantic +import base64 +import io + +import torch +import torchvision +from PIL import Image +from pydantic import BaseModel + +import lightning as L +from lightning.app.components.serve import Image as InputImage +from lightning.app.components.serve import PythonServer + + +class PyTorchServer(PythonServer): + def setup(self): + self._model = torchvision.models.resnet18(pretrained=True) + self._device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + self._model.to(self._device) + + def predict(self, request): + image = base64.b64decode(request.image.encode("utf-8")) + image = Image.open(io.BytesIO(image)) + transforms = torchvision.transforms.Compose( + [ + torchvision.transforms.Resize(224), + torchvision.transforms.ToTensor(), + torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), + ] + ) + image = transforms(image) + image = image.to(self._device) + prediction = self._model(image.unsqueeze(0)) + return {"prediction": prediction.argmax().item()} + + +class OutputData(BaseModel): + prediction: int + + +component = PyTorchServer(input_type=InputImage, output_type=OutputData, cloud_compute=L.CloudCompute("gpu")) +app = L.LightningApp(component) diff --git a/examples/app/server_with_auto_scaler/app.py b/examples/app/server_with_auto_scaler/app.py new file mode 100644 index 0000000..8e0907b --- /dev/null +++ b/examples/app/server_with_auto_scaler/app.py @@ -0,0 +1,91 @@ +# ! pip install torch torchvision +from typing import List + +import torch +import torchvision +from pydantic import BaseModel + +import lightning as L + + +class BatchRequestModel(BaseModel): + inputs: List[L.app.components.Image] + + +class BatchResponse(BaseModel): + outputs: List[L.app.components.Number] + + +class PyTorchServer(L.app.components.PythonServer): + def __init__(self, *args, **kwargs): + super().__init__( + input_type=BatchRequestModel, + output_type=BatchResponse, + *args, + **kwargs, + ) + + def setup(self): + self._device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + self._model = torchvision.models.resnet18(pretrained=True).to(self._device) + + def predict(self, requests: BatchRequestModel): + transforms = torchvision.transforms.Compose( + [ + torchvision.transforms.Resize(224), + torchvision.transforms.ToTensor(), + torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), + ] + ) + images = [] + for request in requests.inputs: + image = L.app.components.serve.types.image.Image.deserialize(request.image) + image = transforms(image).unsqueeze(0) + images.append(image) + images = torch.cat(images) + images = images.to(self._device) + predictions = self._model(images) + results = predictions.argmax(1).cpu().numpy().tolist() + return BatchResponse(outputs=[{"prediction": pred} for pred in results]) + + +class MyAutoScaler(L.app.components.AutoScaler): + def scale(self, replicas: int, metrics: dict) -> int: + pending_requests = metrics["pending_requests"] + active_or_pending_works = replicas + metrics["pending_works"] + + if active_or_pending_works == 0: + return 1 if pending_requests > 0 else 0 + + pending_requests_per_running_or_pending_work = pending_requests / active_or_pending_works + + # scale out if the number of pending requests exceeds max batch size. + max_requests_per_work = self.max_batch_size + if pending_requests_per_running_or_pending_work >= max_requests_per_work: + return replicas + 1 + + # scale in if the number of pending requests is below 25% of max_requests_per_work + min_requests_per_work = max_requests_per_work * 0.25 + if pending_requests_per_running_or_pending_work < min_requests_per_work: + return replicas - 1 + + return replicas + + +app = L.LightningApp( + MyAutoScaler( + # work class and args + PyTorchServer, + cloud_compute=L.CloudCompute("gpu"), + # autoscaler specific args + min_replicas=1, + max_replicas=4, + scale_out_interval=10, + scale_in_interval=10, + endpoint="predict", + input_type=L.app.components.Image, + output_type=L.app.components.Number, + timeout_batching=1, + max_batch_size=8, + ) +) diff --git a/examples/app/template_streamlit_ui/app.py b/examples/app/template_streamlit_ui/app.py new file mode 100644 index 0000000..21a1303 --- /dev/null +++ b/examples/app/template_streamlit_ui/app.py @@ -0,0 +1,44 @@ +from lightning.app import LightningApp, LightningFlow +from lightning.app.frontend import StreamlitFrontend +from lightning.app.utilities.state import AppState + + +class StreamlitUI(LightningFlow): + def __init__(self): + super().__init__() + self.message_to_print = "Hello World!" + self.should_print = False + + def configure_layout(self): + return StreamlitFrontend(render_fn=render_fn) + + +def render_fn(state: AppState): + import streamlit as st + + should_print = st.button("Should print to the terminal ?") + + if should_print: + state.should_print = not state.should_print + + st.write("Currently printing." if state.should_print else "Currently waiting to print.") + + +class HelloWorld(LightningFlow): + def __init__(self): + super().__init__() + self.counter = 0 + self.streamlit_ui = StreamlitUI() + + def run(self): + self.streamlit_ui.run() + if self.streamlit_ui.should_print: + print(f"{self.counter}: {self.streamlit_ui.message_to_print}") + self.counter += 1 + self.streamlit_ui.should_print = False + + def configure_layout(self): + return [{"name": "StreamLitUI", "content": self.streamlit_ui}] + + +app = LightningApp(HelloWorld()) diff --git a/examples/app/template_streamlit_ui/requirements.txt b/examples/app/template_streamlit_ui/requirements.txt new file mode 100644 index 0000000..12a4706 --- /dev/null +++ b/examples/app/template_streamlit_ui/requirements.txt @@ -0,0 +1 @@ +streamlit diff --git a/examples/app/v0/.gitignore b/examples/app/v0/.gitignore new file mode 100644 index 0000000..186149f --- /dev/null +++ b/examples/app/v0/.gitignore @@ -0,0 +1,2 @@ +.storage +.lightning diff --git a/examples/app/v0/README.md b/examples/app/v0/README.md new file mode 100644 index 0000000..516283a --- /dev/null +++ b/examples/app/v0/README.md @@ -0,0 +1,18 @@ +# v0 app + +This app is a flow-only app with nothing fancy. +This is meant to present the basic functionalities of the lightning framework. + +## Starting it + +Local + +```bash +lightning run app app.py +``` + +Cloud + +```bash +lightning run app app.py --cloud +``` diff --git a/examples/app/v0/app.py b/examples/app/v0/app.py new file mode 100644 index 0000000..bf8803f --- /dev/null +++ b/examples/app/v0/app.py @@ -0,0 +1,49 @@ +# v0_app.py +import os +from datetime import datetime +from time import sleep + +import lightning as L +from lightning.app.frontend import StaticWebFrontend + + +class Word(L.LightningFlow): + def __init__(self, letter): + super().__init__() + self.letter = letter + self.repeats = letter + + def run(self): + self.repeats += self.letter + + def configure_layout(self): + return StaticWebFrontend(os.path.join(os.path.dirname(__file__), f"ui/{self.letter}")) + + +class V0App(L.LightningFlow): + def __init__(self): + super().__init__() + self.aas = Word("a") + self.bbs = Word("b") + self.counter = 0 + + def run(self): + now = datetime.now() + now = now.strftime("%H:%M:%S") + log = {"time": now, "a": self.aas.repeats, "b": self.bbs.repeats} + print(log) + self.aas.run() + self.bbs.run() + + sleep(2.0) + self.counter += 1 + + def configure_layout(self): + tab1 = {"name": "Tab_1", "content": self.aas} + tab2 = {"name": "Tab_2", "content": self.bbs} + tab3 = {"name": "Tab_3", "content": "https://tensorboard.dev/experiment/8m1aX0gcQ7aEmH0J7kbBtg/#scalars"} + + return [tab1, tab2, tab3] + + +app = L.LightningApp(V0App(), log_level="debug") diff --git a/examples/app/v0/emulate_ui.py b/examples/app/v0/emulate_ui.py new file mode 100644 index 0000000..8a5b45c --- /dev/null +++ b/examples/app/v0/emulate_ui.py @@ -0,0 +1,19 @@ +from time import sleep + +import requests + +from lightning.app.utilities.state import headers_for + +headers = headers_for({}) +headers["X-Lightning-Type"] = "DEFAULT" + +res = requests.get("http://127.0.0.1:7501/state", headers=headers) + + +res = requests.post("http://127.0.0.1:7501/state", json={"stage": "running"}, headers=headers) +print(res) + +sleep(10) + +res = requests.post("http://127.0.0.1:7501/state", json={"stage": "stopping"}, headers=headers) +print(res) diff --git a/examples/app/v0/requirements.txt b/examples/app/v0/requirements.txt new file mode 100644 index 0000000..edfce78 --- /dev/null +++ b/examples/app/v0/requirements.txt @@ -0,0 +1 @@ +py diff --git a/examples/app/v0/ui/a/index.html b/examples/app/v0/ui/a/index.html new file mode 100644 index 0000000..6ddb9a5 --- /dev/null +++ b/examples/app/v0/ui/a/index.html @@ -0,0 +1 @@ +
Hello from component A
diff --git a/examples/app/v0/ui/b/index.html b/examples/app/v0/ui/b/index.html new file mode 100644 index 0000000..3bfd9e2 --- /dev/null +++ b/examples/app/v0/ui/b/index.html @@ -0,0 +1 @@ +
Hello from component B
diff --git a/examples/app/works_on_default_machine/app_v2.py b/examples/app/works_on_default_machine/app_v2.py new file mode 100644 index 0000000..ee60e77 --- /dev/null +++ b/examples/app/works_on_default_machine/app_v2.py @@ -0,0 +1,53 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from uvicorn import run + +from lightning import CloudCompute, LightningApp, LightningFlow, LightningWork + + +class Work(LightningWork): + def __init__(self, **kwargs): + super().__init__(parallel=True, **kwargs) + + def run(self): + fastapi_service = FastAPI() + + fastapi_service.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + @fastapi_service.get("/") + def get_root(): + return {"Hello Word!"} + + run(fastapi_service, host=self.host, port=self.port) + + +class Flow(LightningFlow): + def __init__(self): + super().__init__() + # In the Cloud: All the works defined without passing explicitly a CloudCompute object + # are running on the default machine. + # This would apply to `work_a`, `work_b` and the dynamically created `work_d`. + + self.work_a = Work() + self.work_b = Work() + + self.work_c = Work(cloud_compute=CloudCompute(name="cpu-small")) + + def run(self): + if not hasattr(self, "work_d"): + self.work_d = Work() + + for work in self.works(): + work.run() + + def configure_layout(self): + return [{"name": w.name, "content": w} for i, w in enumerate(self.works())] + + +app = LightningApp(Flow(), log_level="debug") diff --git a/examples/app/works_on_default_machine/requirements.txt b/examples/app/works_on_default_machine/requirements.txt new file mode 100644 index 0000000..12a4706 --- /dev/null +++ b/examples/app/works_on_default_machine/requirements.txt @@ -0,0 +1 @@ +streamlit diff --git a/examples/data/image/imagenet.py b/examples/data/image/imagenet.py new file mode 100644 index 0000000..c9cd50f --- /dev/null +++ b/examples/data/image/imagenet.py @@ -0,0 +1,190 @@ +import os +import traceback +from argparse import ArgumentParser +from typing import Callable, Literal, Optional + +import torch +import torch.nn.functional as F +import torch.optim as optim +import torch.optim.lr_scheduler as lr_scheduler + +import lightning as L +from lightning.pytorch.utilities.model_helpers import get_torchvision_model + +parser = ArgumentParser() +parser.add_argument("--workers", default=4, type=int) +parser.add_argument("--batchsize", default=56, type=int) +parser.add_argument("-e", "--evaluate", dest="evaluate", action="store_true", help="evaluate model on validation set") +args = parser.parse_args() + +# -------------------------------- +# Step 1: Define a LightningModule +# -------------------------------- + + +class ImageNetLightningModel(L.LightningModule): + """ + >>> ImageNetLightningModel(data_path='missing') # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + ImageNetLightningModel( + (model): ResNet(...) + ) + """ + + from torchvision.models.resnet import ResNet18_Weights + + def __init__( + self, + data_path: str, + index_file_path: str = None, + arch: str = "resnet18", + weights=ResNet18_Weights.IMAGENET1K_V1, + lr: float = 1e-4, + momentum: float = 0.9, + weight_decay: float = 1e-4, + batch_size: int = 256, + workers: int = 4, + ): + super().__init__() + self.arch = arch + self.weights = weights + self.lr = lr + self.momentum = momentum + self.weight_decay = weight_decay + self.batch_size = batch_size + self.workers = workers + self.data_path = data_path + self.index_file_path = index_file_path + self.model = get_torchvision_model(self.arch, weights=self.weights) + self.train_dataset: Optional[Dataset] = None + self.eval_dataset: Optional[Dataset] = None + + def forward(self, x): + return self.model(x) + + def training_step(self, batch, batch_idx): + images, target = batch + output = self.model(images) + loss_train = F.cross_entropy(output, target) + self.log("train_loss", loss_train) + return loss_train + + def eval_step(self, batch, batch_idx, prefix: str): + images, target = batch + output = self.model(images) + loss_val = F.cross_entropy(output, target) + self.log(f"{prefix}_loss", loss_val) + return loss_val + + def validation_step(self, batch, batch_idx): + return self.eval_step(batch, batch_idx, "val") + + def test_step(self, batch, batch_idx): + return self.eval_step(batch, batch_idx, "test") + + def configure_optimizers(self): + optimizer = optim.SGD(self.parameters(), lr=self.lr, momentum=self.momentum, weight_decay=self.weight_decay) + scheduler = lr_scheduler.LambdaLR(optimizer, lambda epoch: 0.1 ** (epoch // 30)) + return [optimizer], [scheduler] + + def train_dataloader(self): + import torchvision as tv + + transforms = tv.transforms.Compose([tv.transforms.RandomResizedCrop(224), tv.transforms.ToTensor()]) + + train_dataset = S3LightningImagenetDataset( + data_source=self.data_path, split="train", transforms=transforms, path_to_index_file=self.index_file_path + ) + + return torch.utils.data.DataLoader( + dataset=train_dataset, batch_size=self.batch_size, shuffle=True, num_workers=self.workers + ) + + def val_dataloader(self): + import torchvision as tv + + transforms = tv.transforms.Compose([tv.transforms.RandomResizedCrop(224), tv.transforms.ToTensor()]) + + val_dataset = S3LightningImagenetDataset( + data_source=self.data_path, split="val", transforms=transforms, path_to_index_file=self.index_file_path + ) + + return torch.utils.data.DataLoader( + dataset=val_dataset, batch_size=self.batch_size, shuffle=True, num_workers=self.workers + ) + + def test_dataloader(self): + return self.val_dataloader() + + +# ------------------- +# Step 2: Define data +# ------------------- + + +class S3LightningImagenetDataset(L.LightningDataset): + def __init__( + self, + data_source: str, + split: Literal["train", "val"], + transforms: Optional[Callable] = None, + path_to_index_file: Optional[str] = None, + ): + from torchvision.models._meta import _IMAGENET_CATEGORIES + + super().__init__(data_source=data_source, backend="s3", path_to_index_file=path_to_index_file) + + # only get files for the split + self.files = tuple([x for x in self.files if split in x]) + + # get unique classes + self.classes = _IMAGENET_CATEGORIES + + self.transforms = transforms + + def load_sample(self, file_path, stream): + from PIL import Image + + try: + img = Image.open(stream) + + if self.transforms is not None: + img = self.transforms(img) + + # Converting grey scale images to RGB + if img.shape[0] == 1: + img = img.repeat((3, 1, 1)) + + curr_cls = os.path.basename(os.path.dirname(file_path)).replace("_", " ") + cls_idx = self.classes.index(curr_cls) + return img, cls_idx + except Exception: + print(file_path, traceback.print_exc()) + pass + + +if __name__ == "__main__": + # os.environ["AWS_ACCESS_KEY"] = + # os.environ["AWS_SECRET_ACCESS_KEY"] = + + data_path = "s3://imagenet-tiny" + index_file_path = "imagenet/imagenet-index.txt" + + # ------------------- + # Step 3: Train + # ------------------- + + print("Instantiate Model") + model = ImageNetLightningModel( + weights=None, + data_path=data_path, + index_file_path=index_file_path, + batch_size=args.batchsize, + workers=args.workers, + ) + trainer = L.Trainer() + + print("Train Model") + if args.evaluate: + trainer.test(model) + else: + trainer.fit(model) diff --git a/examples/fabric/build_your_own_trainer/README.md b/examples/fabric/build_your_own_trainer/README.md new file mode 100644 index 0000000..b795ad3 --- /dev/null +++ b/examples/fabric/build_your_own_trainer/README.md @@ -0,0 +1,27 @@ +## Build Your Own Trainer (BYOT) + +This example demonstrates how easy it is to build a fully customizable trainer for your `LightningModule` using `Fabric`. +It is built upon `lightning.fabric` for hardware and training orchestration and consists of two files: + +- trainer.py contains the actual `MyCustomTrainer` implementation +- run.py contains a script utilizing this trainer for training a very simple MNIST module. + +### Run + +To run this example, call `python run.py` + +### Requirements + +This example has the following requirements which need to be installed on your python environment: + +- `lightning` +- `torchmetrics` +- `torch` +- `torchvision` +- `tqdm` + +to install them with the appropriate versions run: + +```bash +pip install "lightning>=2.0" "torchmetrics>=0.11" "torchvision>=0.14" "torch>=1.13" tqdm +``` diff --git a/examples/fabric/build_your_own_trainer/run.py b/examples/fabric/build_your_own_trainer/run.py new file mode 100644 index 0000000..6902374 --- /dev/null +++ b/examples/fabric/build_your_own_trainer/run.py @@ -0,0 +1,81 @@ +import torch +from torchmetrics.functional.classification.accuracy import accuracy +from trainer import MyCustomTrainer + +import lightning as L + + +class MNISTModule(L.LightningModule): + def __init__(self) -> None: + super().__init__() + self.model = torch.nn.Sequential( + torch.nn.Conv2d( + in_channels=1, + out_channels=16, + kernel_size=5, + stride=1, + padding=2, + ), + torch.nn.ReLU(), + torch.nn.MaxPool2d(kernel_size=2), + torch.nn.Conv2d(16, 32, 5, 1, 2), + torch.nn.ReLU(), + torch.nn.MaxPool2d(2), + torch.nn.Flatten(), + # fully connected layer, output 10 classes + torch.nn.Linear(32 * 7 * 7, 10), + ) + self.loss_fn = torch.nn.CrossEntropyLoss() + + def forward(self, x: torch.Tensor): + return self.model(x) + + def training_step(self, batch, batch_idx: int): + x, y = batch + + logits = self(x) + + loss = self.loss_fn(logits, y) + accuracy_train = accuracy(logits.argmax(-1), y, num_classes=10, task="multiclass", top_k=1) + + return {"loss": loss, "accuracy": accuracy_train} + + def configure_optimizers(self): + optim = torch.optim.Adam(self.parameters(), lr=1e-4) + return optim, { + "scheduler": torch.optim.lr_scheduler.ReduceLROnPlateau(optim, mode="max", verbose=True), + "monitor": "val_accuracy", + "interval": "epoch", + "frequency": 1, + } + + def validation_step(self, *args, **kwargs): + return self.training_step(*args, **kwargs) + + +def train(model): + from torchvision.datasets import MNIST + from torchvision.transforms import ToTensor + + train_set = MNIST(root="/tmp/data/MNIST", train=True, transform=ToTensor(), download=True) + val_set = MNIST(root="/tmp/data/MNIST", train=False, transform=ToTensor(), download=False) + + train_loader = torch.utils.data.DataLoader( + train_set, batch_size=64, shuffle=True, pin_memory=torch.cuda.is_available(), num_workers=4 + ) + val_loader = torch.utils.data.DataLoader( + val_set, batch_size=64, shuffle=False, pin_memory=torch.cuda.is_available(), num_workers=4 + ) + + # MPS backend currently does not support all operations used in this example. + # If you want to use MPS, set accelerator='auto' and also set PYTORCH_ENABLE_MPS_FALLBACK=1 + accelerator = "cpu" if torch.backends.mps.is_available() else "auto" + + trainer = MyCustomTrainer( + accelerator=accelerator, devices="auto", limit_train_batches=10, limit_val_batches=20, max_epochs=3 + ) + trainer.fit(model, train_loader, val_loader) + + +if __name__ == "__main__": + train(MNISTModule()) diff --git a/examples/fabric/build_your_own_trainer/trainer.py b/examples/fabric/build_your_own_trainer/trainer.py new file mode 100644 index 0000000..69895b6 --- /dev/null +++ b/examples/fabric/build_your_own_trainer/trainer.py @@ -0,0 +1,525 @@ +import os +from collections.abc import Mapping +from functools import partial +from typing import Any, cast, Iterable, List, Literal, Optional, Tuple, Union + +import torch +from lightning_utilities import apply_to_collection +from tqdm import tqdm + +import lightning as L +from lightning.fabric.accelerators import Accelerator +from lightning.fabric.loggers import Logger +from lightning.fabric.strategies import Strategy +from lightning.fabric.wrappers import _unwrap_objects +from lightning.pytorch.utilities.model_helpers import is_overridden + + +class MyCustomTrainer: + def __init__( + self, + accelerator: Union[str, Accelerator] = "auto", + strategy: Union[str, Strategy] = "auto", + devices: Union[List[int], str, int] = "auto", + precision: Union[str, int] = "32-true", + plugins: Optional[Union[str, Any]] = None, + callbacks: Optional[Union[List[Any], Any]] = None, + loggers: Optional[Union[Logger, List[Logger]]] = None, + max_epochs: Optional[int] = 1000, + max_steps: Optional[int] = None, + grad_accum_steps: int = 1, + limit_train_batches: Union[int, float] = float("inf"), + limit_val_batches: Union[int, float] = float("inf"), + validation_frequency: int = 1, + use_distributed_sampler: bool = True, + checkpoint_dir: str = "./checkpoints", + checkpoint_frequency: int = 1, + ) -> None: + """Exemplary Trainer with Fabric. This is a very simple trainer focused on readablity but with reduced + featureset. As a trainer with more included features, we recommend using the + :class:`lightning.pytorch.Trainer`. + + Args: + accelerator: The hardware to run on. Possible choices are: + ``"cpu"``, ``"cuda"``, ``"mps"``, ``"gpu"``, ``"tpu"``, ``"auto"``. + strategy: Strategy for how to run across multiple devices. Possible choices are: + ``"dp"``, ``"ddp"``, ``"ddp_spawn"``, ``"deepspeed"``, ``"fsdp"``. + devices: Number of devices to train on (``int``), + which GPUs to train on (``list`` or ``str``), or ``"auto"``. + The value applies per node. + precision: Double precision (``"64"``), full precision (``"32"``), half precision AMP (``"16-mixed"``), + or bfloat16 precision AMP (``"bf16-mixed"``). + plugins: One or several custom plugins + callbacks: A single callback or a list of callbacks. The following hooks are supported: + - on_train_epoch_start + - on train_epoch_end + - on_train_batch_start + - on_train_batch_end + - on_before_backward + - on_after_backward + - on_before_zero_grad + - on_before_optimizer_step + - on_validation_model_eval + - on_validation_model_train + - on_validation_epoch_start + - on_validation_epoch_end + - on_validation_batch_start + - on_validation_batch_end + + loggers: A single logger or a list of loggers. See :meth:`~lightning.fabric.fabric.Fabric.log` for more + information. + + max_epochs: The maximum number of epochs to train + max_steps: The maximum number of (optimizer) steps to train + grad_accum_steps: How many batches to process before each optimizer step + limit_train_batches: Limits the number of train batches per epoch + If greater than number of batches in the dataloader, this has no effect. + limit_val_batches: Limits the number of validation batches per epoch. + If greater than number of batches in the dataloader, this has no effect. + validation_frequency: How many epochs to run before each validation epoch. + use_distributed_sampler: Wraps the sampler of each dataloader with a respective distributed-aware sampler + in case of distributed training. + checkpoint_dir: Directory to store checkpoints to. + checkpoint_frequency: How many epochs to run before each checkpoint is written. + + Warning: + callbacks written for the lightning trainer (especially making assumptions on the trainer), won't work! + """ + + self.fabric = L.Fabric( + accelerator=accelerator, + strategy=strategy, + devices=devices, + precision=precision, + plugins=plugins, + callbacks=callbacks, + loggers=loggers, + ) + self.global_step = 0 + self.grad_accum_steps: int = grad_accum_steps + self.current_epoch = 0 + + self.max_epochs = max_epochs + self.max_steps = max_steps + self.should_stop = False + + # ensures limit_X_batches is either int or inf + if not isinstance(limit_train_batches, int): + assert limit_train_batches == float("inf") + + if not isinstance(limit_val_batches, int): + assert limit_val_batches == float("inf") + + self.limit_train_batches = limit_train_batches + self.limit_val_batches = limit_val_batches + self.validation_frequency = validation_frequency + self.use_distributed_sampler = use_distributed_sampler + self._current_train_return: Union[torch.Tensor, Mapping[str, Any]] = {} + self._current_val_return: Optional[Union[torch.Tensor, Mapping[str, Any]]] = {} + + self.checkpoint_dir = checkpoint_dir + self.checkpoint_frequency = checkpoint_frequency + + def fit( + self, + model: L.LightningModule, + train_loader: torch.utils.data.DataLoader, + val_loader: torch.utils.data.DataLoader, + ckpt_path: Optional[str] = None, + ): + """The main entrypoint of the trainer, triggering the actual training. + + Args: + model: the LightningModule to train. + Can have the same hooks as :attr:`callbacks` (see :meth:`MyCustomTrainer.__init__`). + train_loader: the training dataloader. Has to be an iterable returning batches. + val_loader: the validation dataloader. Has to be an iterable returning batches. + If not specified, no validation will run. + ckpt_path: Path to previous checkpoints to resume training from. + If specified, will always look for the latest checkpoint within the given directory. + """ + self.fabric.launch() + + # setup dataloaders + train_loader = self.fabric.setup_dataloaders(train_loader, use_distributed_sampler=self.use_distributed_sampler) + if val_loader is not None: + val_loader = self.fabric.setup_dataloaders(val_loader, use_distributed_sampler=self.use_distributed_sampler) + + # setup model and optimizer + if isinstance(self.fabric.strategy, L.fabric.strategies.fsdp.FSDPStrategy): + # currently, there is no way to support fsdp with model.configure_optimizers in fabric + # as it would require fabric to hold a reference to the model, which we don't want to. + raise NotImplementedError("BYOT currently does not support FSDP") + + optimizer, scheduler_cfg = self._parse_optimizers_schedulers(model.configure_optimizers()) + assert optimizer is not None + model, optimizer = self.fabric.setup(model, optimizer) + + # assemble state (current epoch and global step will be added in save) + state = {"model": model, "optim": optimizer, "scheduler": scheduler_cfg} + + # load last checkpoint if available + if ckpt_path is not None and os.path.isdir(ckpt_path): + latest_checkpoint_path = self.get_latest_checkpoint(self.checkpoint_dir) + if latest_checkpoint_path is not None: + self.load(state, latest_checkpoint_path) + + # check if we even need to train here + if self.max_epochs is not None and self.current_epoch >= self.max_epochs: + self.should_stop = True + + while not self.should_stop: + self.train_loop( + model, optimizer, train_loader, limit_batches=self.limit_train_batches, scheduler_cfg=scheduler_cfg + ) + + if self.should_validate: + self.val_loop(model, val_loader, limit_batches=self.limit_val_batches) + + self.step_scheduler(model, scheduler_cfg, level="epoch", current_value=self.current_epoch) + + self.current_epoch += 1 + + # stopping condition on epoch level + if self.max_epochs is not None and self.current_epoch >= self.max_epochs: + self.should_stop = True + + self.save(state) + + # reset for next fit call + self.should_stop = False + + def train_loop( + self, + model: L.LightningModule, + optimizer: torch.optim.Optimizer, + train_loader: torch.utils.data.DataLoader, + limit_batches: Union[int, float] = float("inf"), + scheduler_cfg: Optional[Mapping[str, Union[L.fabric.utilities.types.LRScheduler, bool, str, int]]] = None, + ): + """The training loop running a single training epoch. + + Args: + model: the LightningModule to train + optimizer: the optimizer, optimizing the LightningModule. + train_loader: The dataloader yielding the training batches. + limit_batches: Limits the batches during this training epoch. + If greater then the number of batches in the ``train_loader``, this has no effect. + scheduler_cfg: The learning rate scheduler configuration. + Have a look at :meth:`lightning.pytorch.LightninModule.configure_optimizers` for supported values. + """ + self.fabric.call("on_train_epoch_start") + iterable = self.progbar_wrapper( + train_loader, total=min(len(train_loader), limit_batches), desc=f"Epoch {self.current_epoch}" + ) + + for batch_idx, batch in enumerate(iterable): + # end epoch if stopping training completely or max batches for this epoch reached + if self.should_stop or batch_idx >= limit_batches: + break + + self.fabric.call("on_train_batch_start", batch, batch_idx) + + # check if optimizer should step in gradient accumulation + should_optim_step = self.global_step % self.grad_accum_steps == 0 + if should_optim_step: + # currently only supports a single optimizer + self.fabric.call("on_before_optimizer_step", optimizer, 0) + + # optimizer step runs train step internally through closure + optimizer.step(partial(self.training_step, model=model, batch=batch, batch_idx=batch_idx)) + self.fabric.call("on_before_zero_grad", optimizer) + + optimizer.zero_grad() + + else: + # gradient accumulation -> no optimizer step + self.training_step(model=model, batch=batch, batch_idx=batch_idx) + + self.fabric.call("on_train_batch_end", self._current_train_return, batch, batch_idx) + + # this guard ensures, we only step the scheduler once per global step + if should_optim_step: + self.step_scheduler(model, scheduler_cfg, level="step", current_value=self.global_step) + + # add output values to progress bar + self._format_iterable(iterable, self._current_train_return, "train") + + # only increase global step if optimizer stepped + self.global_step += int(should_optim_step) + + # stopping criterion on step level + if self.max_steps is not None and self.global_step >= self.max_steps: + self.should_stop = True + break + + self.fabric.call("on_train_epoch_end") + + def val_loop( + self, + model: L.LightningModule, + val_loader: Optional[torch.utils.data.DataLoader], + limit_batches: Union[int, float] = float("inf"), + ): + """The validation loop ruunning a single validation epoch. + + Args: + model: the LightningModule to evaluate + val_loader: The dataloader yielding the validation batches. + limit_batches: Limits the batches during this validation epoch. + If greater then the number of batches in the ``val_loader``, this has no effect. + """ + # no validation if val_loader wasn't passed + if val_loader is None: + return + + # no validation but warning if val_loader was passed, but validation_step not implemented + if val_loader is not None and not is_overridden("validation_step", _unwrap_objects(model)): + L.fabric.utilities.rank_zero_warn( + "Your LightningModule does not have a validation_step implemented, " + "but you passed a validation dataloder. Skipping Validation." + ) + return + + self.fabric.call("on_validation_model_eval") # calls `model.eval()` + + torch.set_grad_enabled(False) + + self.fabric.call("on_validation_epoch_start") + + iterable = self.progbar_wrapper(val_loader, total=min(len(val_loader), limit_batches), desc="Validation") + + for batch_idx, batch in enumerate(iterable): + # end epoch if stopping training completely or max batches for this epoch reached + if self.should_stop or batch_idx >= limit_batches: + break + + self.fabric.call("on_validation_batch_start", batch, batch_idx) + + out = model.validation_step(batch, batch_idx) + # avoid gradients in stored/accumulated values -> prevents potential OOM + out = apply_to_collection(out, torch.Tensor, lambda x: x.detach()) + + self.fabric.call("on_validation_batch_end", out, batch, batch_idx) + self._current_val_return = out + + self._format_iterable(iterable, self._current_val_return, "val") + + self.fabric.call("on_validation_epoch_end") + + self.fabric.call("on_validation_model_train") + torch.set_grad_enabled(True) + + def training_step(self, model: L.LightningModule, batch: Any, batch_idx: int) -> torch.Tensor: + """A single training step, running forward and backward. The optimizer step is called separately, as this + is given as a closure to the optimizer step. + + Args: + model: the lightning module to train + batch: the batch to run the forward on + batch_idx: index of the current batch w.r.t the current epoch + """ + outputs: Union[torch.Tensor, Mapping[str, Any]] = model.training_step(batch, batch_idx=batch_idx) + + loss = outputs if isinstance(outputs, torch.Tensor) else outputs["loss"] + + self.fabric.call("on_before_backward", loss) + self.fabric.backward(loss) + self.fabric.call("on_after_backward") + + # avoid gradients in stored/accumulated values -> prevents potential OOM + self._current_train_return = apply_to_collection(outputs, dtype=torch.Tensor, function=lambda x: x.detach()) + + return loss + + def step_scheduler( + self, + model: L.LightningModule, + scheduler_cfg: Optional[Mapping[str, Union[L.fabric.utilities.types.LRScheduler, bool, str, int]]], + level: Literal["step", "epoch"], + current_value: int, + ) -> None: + """Steps the learning rate scheduler if necessary. + + Args: + model: The LightningModule to train + scheduler_cfg: The learning rate scheduler configuration. + Have a look at :meth:`lightning.pytorch.LightningModule.configure_optimizers` for supported values. + level: whether we are trying to step on epoch- or step-level + current_value: Holds the current_epoch if ``level==epoch``, else holds the ``global_step`` + """ + + # no scheduler + if scheduler_cfg is None: + return + + # wrong interval (step vs. epoch) + if scheduler_cfg["interval"] != level: + return + + # right interval, but wrong step wrt frequency + if current_value % cast(int, scheduler_cfg["frequency"]) != 0: + return + + # assemble potential monitored values + possible_monitor_vals = {None: None} + if isinstance(self._current_train_return, torch.Tensor): + possible_monitor_vals.update("train_loss", self._current_train_return) + elif isinstance(self._current_train_return, Mapping): + possible_monitor_vals.update({"train_" + k: v for k, v in self._current_train_return.items()}) + + if isinstance(self._current_val_return, torch.Tensor): + possible_monitor_vals.update("val_loss", self._current_val_return) + elif isinstance(self._current_val_return, Mapping): + possible_monitor_vals.update({"val_" + k: v for k, v in self._current_val_return.items()}) + + try: + monitor = possible_monitor_vals[cast(Optional[str], scheduler_cfg["monitor"])] + except KeyError as ex: + possible_keys = list(possible_monitor_vals.keys()) + raise KeyError( + f"monitor {scheduler_cfg['monitor']} is invalid. Possible values are {possible_keys}." + ) from ex + + # rely on model hook for actual step + model.lr_scheduler_step(scheduler_cfg["scheduler"], monitor) + + @property + def should_validate(self) -> bool: + """Whether to currently run validation.""" + return self.current_epoch % self.validation_frequency == 0 + + def progbar_wrapper(self, iterable: Iterable, total: int, **kwargs: Any): + """Wraps the iterable with tqdm for global rank zero. + + Args: + iterable: the iterable to wrap with tqdm + total: the total length of the iterable, necessary in case the number of batches was limited. + """ + if self.fabric.is_global_zero: + return tqdm(iterable, total=total, **kwargs) + return iterable + + def load(self, state: Optional[Mapping], path: str) -> None: + """Loads a checkpoint from a given file into state. + + Args: + state: a mapping contaning model, optimizer and lr scheduler + path: the path to load the checkpoint from + """ + if state is None: + state = {} + + remainder = self.fabric.load(path, state) + self.global_step = remainder.pop("global_step") + self.current_epoch = remainder.pop("current_epoch") + + if remainder: + raise RuntimeError(f"Unused Checkpoint Values: {remainder}") + + def save(self, state: Optional[Mapping]) -> None: + """Saves a checkpoint to the ``checkpoint_dir`` + + Args: + state: A mapping containing model, optimizer and lr scheduler. + """ + if state is None: + state = {} + + state.update(global_step=self.global_step, current_epoch=self.current_epoch) + + self.fabric.save(os.path.join(self.checkpoint_dir, f"epoch-{self.current_epoch:04d}.ckpt"), state) + + @staticmethod + def get_latest_checkpoint(checkpoint_dir: str) -> Optional[str]: + """Returns the latest checkpoint from the ``checkpoint_dir`` + + Args: + checkpoint_dir: the directory to search for checkpoints + """ + if not os.path.isdir(checkpoint_dir): + return None + + items = sorted(os.listdir(checkpoint_dir)) + + if not items: + return None + + return os.path.join(checkpoint_dir, items[-1]) + + def _parse_optimizers_schedulers( + self, configure_optim_output + ) -> Tuple[ + Optional[L.fabric.utilities.types.Optimizable], + Optional[Mapping[str, Union[L.fabric.utilities.types.LRScheduler, bool, str, int]]], + ]: + """Recursively parses the output of :meth:`lightning.pytorch.LightningModule.configure_optimizers`. + + Args: + configure_optim_output: The output of ``configure_optimizers``. + For supported values, please refer to :meth:`lightning.pytorch.LightningModule.configure_optimizers`. + """ + _lr_sched_defaults = {"interval": "epoch", "frequency": 1, "monitor": "val_loss"} + + # single optimizer + if isinstance(configure_optim_output, L.fabric.utilities.types.Optimizable): + return configure_optim_output, None + + # single lr scheduler + if isinstance(configure_optim_output, L.fabric.utilities.types.LRScheduler): + return None, _lr_sched_defaults.update(scheduler=configure_optim_output) + + # single lr scheduler config + if isinstance(configure_optim_output, Mapping): + _lr_sched_defaults.update(configure_optim_output) + return None, _lr_sched_defaults + + # list or tuple + if isinstance(configure_optim_output, (list, tuple)): + if all(isinstance(_opt_cand, L.fabric.utilities.types.Optimizable) for _opt_cand in configure_optim_output): + # single optimizer in list + if len(configure_optim_output) == 1: + return configure_optim_output[0][0], None + + raise NotImplementedError("BYOT only supports a single optimizer") + + if all( + isinstance(_lr_cand, (L.fabric.utilities.types.LRScheduler, Mapping)) + for _lr_cand in configure_optim_output + ): + # single scheduler in list + if len(configure_optim_output) == 1: + return None, self._parse_optimizers_schedulers(configure_optim_output[0])[1] + + # optimizer and lr scheduler + elif len(configure_optim_output) == 2: + opt_cands, lr_cands = ( + self._parse_optimizers_schedulers(configure_optim_output[0])[0], + self._parse_optimizers_schedulers(configure_optim_output[1])[1], + ) + return opt_cands, lr_cands + + return None, None + + @staticmethod + def _format_iterable( + prog_bar, candidates: Optional[Union[torch.Tensor, Mapping[str, Union[torch.Tensor, float, int]]]], prefix: str + ): + """Adds values as postfix string to progressbar. + + Args: + prog_bar: a progressbar (on global rank zero) or an iterable (every other rank). + candidates: the values to add as postfix strings to the progressbar. + prefix: the prefix to add to each of these values. + """ + if isinstance(prog_bar, tqdm) and candidates is not None: + postfix_str = "" + float_candidates = apply_to_collection(candidates, torch.Tensor, lambda x: x.item()) + if isinstance(candidates, torch.Tensor): + postfix_str += f" {prefix}_loss: {float_candidates:.3f}" + elif isinstance(candidates, Mapping): + for k, v in float_candidates.items(): + postfix_str += f" {prefix}_{k}: {v:.3f}" + + if postfix_str: + prog_bar.set_postfix_str(postfix_str) diff --git a/examples/fabric/dcgan/README.md b/examples/fabric/dcgan/README.md new file mode 100644 index 0000000..669e282 --- /dev/null +++ b/examples/fabric/dcgan/README.md @@ -0,0 +1,45 @@ +## DCGAN + +This is an example of a GAN (Generative Adversarial Network) that learns to generate realistic images of faces. +We show two code versions: +The first one is implemented in raw PyTorch, but isn't easy to scale. +The second one is using [Lightning Fabric](https://lightning.ai/docs/fabric) to accelerate and scale the model. + +Tip: You can easily inspect the difference between the two files with: + +```bash +sdiff train_torch.py train_fabric.py +``` + +| Real | Generated | +| :------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------: | +| ![sample-data](https://user-images.githubusercontent.com/5495193/206484557-2e9e3810-a9c8-4ae0-bc6e-126866fef4f0.png) | ![fake-7914](https://user-images.githubusercontent.com/5495193/206484621-5dc4a9a6-c782-4c71-8e80-27580cdcc7e6.png) | + +### Run + +**Raw PyTorch:** + +```bash +python train_torch.py +``` + +**Accelerated using Lightning Fabric:** + +```bash +python train_fabric.py +``` + +Generated images get saved to the _outputs_ folder. + +### Notes + +The CelebA dataset is hosted through a Google Drive link by the authors, but the downloads are limited. +You may get a message saying that the daily quota was reached. In this case, +[manually download the data](https://drive.google.com/drive/folders/0B7EVK8r0v71pWEZsZE9oNnFzTm8?resourcekey=0-5BR16BdXnb8hVj6CNHKzLg) +through your browser. + +### References + +- [DCGAN Tutorial](https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html) +- [Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks](https://arxiv.org/abs/1511.06434) +- [Large-scale CelebFaces Attributes (CelebA) Dataset](https://mmlab.ie.cuhk.edu.hk/projects/CelebA.html) diff --git a/examples/fabric/dcgan/train_fabric.py b/examples/fabric/dcgan/train_fabric.py new file mode 100644 index 0000000..2eb3998 --- /dev/null +++ b/examples/fabric/dcgan/train_fabric.py @@ -0,0 +1,268 @@ +""" +DCGAN - Accelerated with Lightning Fabric + +Code adapted from the official PyTorch DCGAN tutorial: +https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html +""" +import os +import time +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.parallel +import torch.optim as optim +import torch.utils.data +import torchvision.transforms as transforms +import torchvision.utils +from torchvision.datasets import CelebA + +from lightning.fabric import Fabric, seed_everything + +# Root directory for dataset +dataroot = "data/" +# Number of workers for dataloader +workers = os.cpu_count() +# Batch size during training +batch_size = 128 +# Spatial size of training images +image_size = 64 +# Number of channels in the training images +nc = 3 +# Size of z latent vector (i.e. size of generator input) +nz = 100 +# Size of feature maps in generator +ngf = 64 +# Size of feature maps in discriminator +ndf = 64 +# Number of training epochs +num_epochs = 5 +# Learning rate for optimizers +lr = 0.0002 +# Beta1 hyperparameter for Adam optimizers +beta1 = 0.5 +# Number of GPUs to use +num_gpus = 1 + + +def main(): + # Set random seed for reproducibility + seed_everything(999) + + fabric = Fabric(accelerator="auto", devices=1) + fabric.launch() + + dataset = CelebA( + root=dataroot, + split="all", + download=True, + transform=transforms.Compose( + [ + transforms.Resize(image_size), + transforms.CenterCrop(image_size), + transforms.ToTensor(), + transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), + ] + ), + ) + + # Create the dataloader + dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=workers) + + output_dir = Path("outputs-fabric", time.strftime("%Y%m%d-%H%M%S")) + output_dir.mkdir(parents=True, exist_ok=True) + + # Plot some training images + real_batch = next(iter(dataloader)) + torchvision.utils.save_image( + real_batch[0][:64], + output_dir / "sample-data.png", + padding=2, + normalize=True, + ) + + # Create the generator + generator = Generator() + + # Apply the weights_init function to randomly initialize all weights + generator.apply(weights_init) + + # Create the Discriminator + discriminator = Discriminator() + + # Apply the weights_init function to randomly initialize all weights + discriminator.apply(weights_init) + + # Initialize BCELoss function + criterion = nn.BCELoss() + + # Create batch of latent vectors that we will use to visualize + # the progression of the generator + fixed_noise = torch.randn(64, nz, 1, 1, device=fabric.device) + + # Establish convention for real and fake labels during training + real_label = 1.0 + fake_label = 0.0 + + # Set up Adam optimizers for both G and D + optimizer_d = optim.Adam(discriminator.parameters(), lr=lr, betas=(beta1, 0.999)) + optimizer_g = optim.Adam(generator.parameters(), lr=lr, betas=(beta1, 0.999)) + + discriminator, optimizer_d = fabric.setup(discriminator, optimizer_d) + generator, optimizer_g = fabric.setup(generator, optimizer_g) + dataloader = fabric.setup_dataloaders(dataloader) + + # Lists to keep track of progress + losses_g = [] + losses_d = [] + iteration = 0 + + # Training loop + for epoch in range(num_epochs): + for i, data in enumerate(dataloader, 0): + # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z))) + # (a) Train with all-real batch + discriminator.zero_grad() + real = data[0] + b_size = real.size(0) + label = torch.full((b_size,), real_label, dtype=torch.float, device=fabric.device) + # Forward pass real batch through D + output = discriminator(real).view(-1) + # Calculate loss on all-real batch + err_d_real = criterion(output, label) + # Calculate gradients for D in backward pass + fabric.backward(err_d_real) + d_x = output.mean().item() + + # (b) Train with all-fake batch + # Generate batch of latent vectors + noise = torch.randn(b_size, nz, 1, 1, device=fabric.device) + # Generate fake image batch with G + fake = generator(noise) + label.fill_(fake_label) + # Classify all fake batch with D + output = discriminator(fake.detach()).view(-1) + # Calculate D's loss on the all-fake batch + err_d_fake = criterion(output, label) + # Calculate the gradients for this batch, accumulated (summed) with previous gradients + fabric.backward(err_d_fake) + d_g_z1 = output.mean().item() + # Compute error of D as sum over the fake and the real batches + err_d = err_d_real + err_d_fake + # Update D + optimizer_d.step() + + # (2) Update G network: maximize log(D(G(z))) + generator.zero_grad() + label.fill_(real_label) # fake labels are real for generator cost + # Since we just updated D, perform another forward pass of all-fake batch through D + output = discriminator(fake).view(-1) + # Calculate G's loss based on this output + err_g = criterion(output, label) + # Calculate gradients for G + fabric.backward(err_g) + d_g_z2 = output.mean().item() + # Update G + optimizer_g.step() + + # Output training stats + if i % 50 == 0: + fabric.print( + f"[{epoch}/{num_epochs}][{i}/{len(dataloader)}]\t" + f"Loss_D: {err_d.item():.4f}\t" + f"Loss_G: {err_g.item():.4f}\t" + f"D(x): {d_x:.4f}\t" + f"D(G(z)): {d_g_z1:.4f} / {d_g_z2:.4f}" + ) + + # Save Losses for plotting later + losses_g.append(err_g.item()) + losses_d.append(err_d.item()) + + # Check how the generator is doing by saving G's output on fixed_noise + if (iteration % 500 == 0) or ((epoch == num_epochs - 1) and (i == len(dataloader) - 1)): + with torch.no_grad(): + fake = generator(fixed_noise).detach().cpu() + + if fabric.is_global_zero: + torchvision.utils.save_image( + fake, + output_dir / f"fake-{iteration:04d}.png", + padding=2, + normalize=True, + ) + fabric.barrier() + + iteration += 1 + + +def weights_init(m): + # custom weights initialization called on netG and netD + classname = m.__class__.__name__ + if classname.find("Conv") != -1: + nn.init.normal_(m.weight.data, 0.0, 0.02) + elif classname.find("BatchNorm") != -1: + nn.init.normal_(m.weight.data, 1.0, 0.02) + nn.init.constant_(m.bias.data, 0) + + +class Generator(nn.Module): + def __init__(self): + super().__init__() + self.main = nn.Sequential( + # input is Z, going into a convolution + nn.ConvTranspose2d(nz, ngf * 8, 4, 1, 0, bias=False), + nn.BatchNorm2d(ngf * 8), + nn.ReLU(True), + # state size. (ngf*8) x 4 x 4 + nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False), + nn.BatchNorm2d(ngf * 4), + nn.ReLU(True), + # state size. (ngf*4) x 8 x 8 + nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False), + nn.BatchNorm2d(ngf * 2), + nn.ReLU(True), + # state size. (ngf*2) x 16 x 16 + nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, bias=False), + nn.BatchNorm2d(ngf), + nn.ReLU(True), + # state size. (ngf) x 32 x 32 + nn.ConvTranspose2d(ngf, nc, 4, 2, 1, bias=False), + nn.Tanh() + # state size. (nc) x 64 x 64 + ) + + def forward(self, input): + return self.main(input) + + +class Discriminator(nn.Module): + def __init__(self): + super().__init__() + self.main = nn.Sequential( + # input is (nc) x 64 x 64 + nn.Conv2d(nc, ndf, 4, 2, 1, bias=False), + nn.LeakyReLU(0.2, inplace=True), + # state size. (ndf) x 32 x 32 + nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False), + nn.BatchNorm2d(ndf * 2), + nn.LeakyReLU(0.2, inplace=True), + # state size. (ndf*2) x 16 x 16 + nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False), + nn.BatchNorm2d(ndf * 4), + nn.LeakyReLU(0.2, inplace=True), + # state size. (ndf*4) x 8 x 8 + nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False), + nn.BatchNorm2d(ndf * 8), + nn.LeakyReLU(0.2, inplace=True), + # state size. (ndf*8) x 4 x 4 + nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False), + nn.Sigmoid(), + ) + + def forward(self, input): + return self.main(input) + + +if __name__ == "__main__": + main() diff --git a/examples/fabric/dcgan/train_torch.py b/examples/fabric/dcgan/train_torch.py new file mode 100644 index 0000000..6362736 --- /dev/null +++ b/examples/fabric/dcgan/train_torch.py @@ -0,0 +1,271 @@ +""" +DCGAN - Raw PyTorch Implementation + +Code adapted from the official PyTorch DCGAN tutorial: +https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html +""" +import os +import random +import time +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.parallel +import torch.optim as optim +import torch.utils.data +import torchvision.transforms as transforms +import torchvision.utils +from torchvision.datasets import CelebA + +# Root directory for dataset +dataroot = "data/" +# Number of workers for dataloader +workers = os.cpu_count() +# Batch size during training +batch_size = 128 +# Spatial size of training images +image_size = 64 +# Number of channels in the training images +nc = 3 +# Size of z latent vector (i.e. size of generator input) +nz = 100 +# Size of feature maps in generator +ngf = 64 +# Size of feature maps in discriminator +ndf = 64 +# Number of training epochs +num_epochs = 5 +# Learning rate for optimizers +lr = 0.0002 +# Beta1 hyperparameter for Adam optimizers +beta1 = 0.5 +# Number of GPUs to use +num_gpus = 1 + + +def main(): + # Set random seed for reproducibility + seed = 999 + print("Random Seed: ", seed) + random.seed(seed) + torch.manual_seed(seed) + + dataset = CelebA( + root=dataroot, + split="all", + download=True, + transform=transforms.Compose( + [ + transforms.Resize(image_size), + transforms.CenterCrop(image_size), + transforms.ToTensor(), + transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), + ] + ), + ) + + # Create the dataloader + dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=workers) + + # Decide which device we want to run on + device = torch.device("cuda:0" if (torch.cuda.is_available() and num_gpus > 0) else "cpu") + + output_dir = Path("outputs-torch", time.strftime("%Y%m%d-%H%M%S")) + output_dir.mkdir(parents=True, exist_ok=True) + + # Plot some training images + real_batch = next(iter(dataloader)) + torchvision.utils.save_image( + real_batch[0][:64], + output_dir / "sample-data.png", + padding=2, + normalize=True, + ) + + # Create the generator + generator = Generator().to(device) + + # Handle multi-gpu if desired + if (device.type == "cuda") and (num_gpus > 1): + generator = nn.DataParallel(generator, list(range(num_gpus))) + + # Apply the weights_init function to randomly initialize all weights + generator.apply(weights_init) + + # Create the Discriminator + discriminator = Discriminator().to(device) + + # Handle multi-gpu if desired + if (device.type == "cuda") and (num_gpus > 1): + discriminator = nn.DataParallel(discriminator, list(range(num_gpus))) + + # Apply the weights_init function to randomly initialize all weights + discriminator.apply(weights_init) + + # Initialize BCELoss function + criterion = nn.BCELoss() + + # Create batch of latent vectors that we will use to visualize + # the progression of the generator + fixed_noise = torch.randn(64, nz, 1, 1, device=device) + + # Establish convention for real and fake labels during training + real_label = 1.0 + fake_label = 0.0 + + # Set up Adam optimizers for both G and D + optimizer_d = optim.Adam(discriminator.parameters(), lr=lr, betas=(beta1, 0.999)) + optimizer_g = optim.Adam(generator.parameters(), lr=lr, betas=(beta1, 0.999)) + + # Lists to keep track of progress + losses_g = [] + losses_d = [] + iteration = 0 + + # Training loop + for epoch in range(num_epochs): + for i, data in enumerate(dataloader, 0): + # (1) Update D network: maximize log(D(x)) + log(1 - D(G(z))) + # (a) Train with all-real batch + discriminator.zero_grad() + real_cpu = data[0].to(device) + b_size = real_cpu.size(0) + label = torch.full((b_size,), real_label, dtype=torch.float, device=device) + # Forward pass real batch through D + output = discriminator(real_cpu).view(-1) + # Calculate loss on all-real batch + err_d_real = criterion(output, label) + # Calculate gradients for D in backward pass + err_d_real.backward() + d_x = output.mean().item() + + # (b) Train with all-fake batch + # Generate batch of latent vectors + noise = torch.randn(b_size, nz, 1, 1, device=device) + # Generate fake image batch with G + fake = generator(noise) + label.fill_(fake_label) + # Classify all fake batch with D + output = discriminator(fake.detach()).view(-1) + # Calculate D's loss on the all-fake batch + err_d_fake = criterion(output, label) + # Calculate the gradients for this batch, accumulated (summed) with previous gradients + err_d_fake.backward() + d_g_z1 = output.mean().item() + # Compute error of D as sum over the fake and the real batches + err_d = err_d_real + err_d_fake + # Update D + optimizer_d.step() + + # (2) Update G network: maximize log(D(G(z))) + generator.zero_grad() + label.fill_(real_label) # fake labels are real for generator cost + # Since we just updated D, perform another forward pass of all-fake batch through D + output = discriminator(fake).view(-1) + # Calculate G's loss based on this output + err_g = criterion(output, label) + # Calculate gradients for G + err_g.backward() + d_g_z2 = output.mean().item() + # Update G + optimizer_g.step() + + # Output training stats + if i % 50 == 0: + print( + f"[{epoch}/{num_epochs}][{i}/{len(dataloader)}]\t" + f"Loss_D: {err_d.item():.4f}\t" + f"Loss_G: {err_g.item():.4f}\t" + f"D(x): {d_x:.4f}\t" + f"D(G(z)): {d_g_z1:.4f} / {d_g_z2:.4f}" + ) + + # Save Losses for plotting later + losses_g.append(err_g.item()) + losses_d.append(err_d.item()) + + # Check how the generator is doing by saving G's output on fixed_noise + if (iteration % 500 == 0) or ((epoch == num_epochs - 1) and (i == len(dataloader) - 1)): + with torch.no_grad(): + fake = generator(fixed_noise).detach().cpu() + torchvision.utils.save_image( + fake, + output_dir / f"fake-{iteration:04d}.png", + padding=2, + normalize=True, + ) + + iteration += 1 + + +def weights_init(m): + # custom weights initialization called on netG and netD + classname = m.__class__.__name__ + if classname.find("Conv") != -1: + nn.init.normal_(m.weight.data, 0.0, 0.02) + elif classname.find("BatchNorm") != -1: + nn.init.normal_(m.weight.data, 1.0, 0.02) + nn.init.constant_(m.bias.data, 0) + + +class Generator(nn.Module): + def __init__(self): + super().__init__() + self.main = nn.Sequential( + # input is Z, going into a convolution + nn.ConvTranspose2d(nz, ngf * 8, 4, 1, 0, bias=False), + nn.BatchNorm2d(ngf * 8), + nn.ReLU(True), + # state size. (ngf*8) x 4 x 4 + nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, bias=False), + nn.BatchNorm2d(ngf * 4), + nn.ReLU(True), + # state size. (ngf*4) x 8 x 8 + nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, bias=False), + nn.BatchNorm2d(ngf * 2), + nn.ReLU(True), + # state size. (ngf*2) x 16 x 16 + nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, bias=False), + nn.BatchNorm2d(ngf), + nn.ReLU(True), + # state size. (ngf) x 32 x 32 + nn.ConvTranspose2d(ngf, nc, 4, 2, 1, bias=False), + nn.Tanh() + # state size. (nc) x 64 x 64 + ) + + def forward(self, input): + return self.main(input) + + +class Discriminator(nn.Module): + def __init__(self): + super().__init__() + self.main = nn.Sequential( + # input is (nc) x 64 x 64 + nn.Conv2d(nc, ndf, 4, 2, 1, bias=False), + nn.LeakyReLU(0.2, inplace=True), + # state size. (ndf) x 32 x 32 + nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False), + nn.BatchNorm2d(ndf * 2), + nn.LeakyReLU(0.2, inplace=True), + # state size. (ndf*2) x 16 x 16 + nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False), + nn.BatchNorm2d(ndf * 4), + nn.LeakyReLU(0.2, inplace=True), + # state size. (ndf*4) x 8 x 8 + nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False), + nn.BatchNorm2d(ndf * 8), + nn.LeakyReLU(0.2, inplace=True), + # state size. (ndf*8) x 4 x 4 + nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False), + nn.Sigmoid(), + ) + + def forward(self, input): + return self.main(input) + + +if __name__ == "__main__": + main() diff --git a/examples/fabric/image_classifier/README.md b/examples/fabric/image_classifier/README.md new file mode 100644 index 0000000..65332ac --- /dev/null +++ b/examples/fabric/image_classifier/README.md @@ -0,0 +1,37 @@ +## MNIST Examples + +Here are two MNIST classifiers implemented in PyTorch. +The first one is implemented in pure PyTorch, but isn't easy to scale. +The second one is using [Lightning Fabric](https://lightning.ai/docs/fabric) to accelerate and scale the model. + +Tip: You can easily inspect the difference between the two files with: + +```bash +sdiff train_torch.py train_fabric.py +``` + +#### 1. Image Classifier with Vanilla PyTorch + +Trains a simple CNN over MNIST using vanilla PyTorch. It only supports singe GPU training. + +```bash +# CPU +python train_torch.py +``` + +______________________________________________________________________ + +#### 2. Image Classifier with Lightning Fabric + +This script shows you how to scale the pure PyTorch code to enable GPU and multi-GPU training using [Lightning Fabric](https://lightning.ai/docs/fabric). + +```bash +# CPU +lightning run model train_fabric.py + +# GPU (CUDA or M1 Mac) +lightning run model train_fabric.py --accelerator=gpu + +# Multiple GPUs +lightning run model train_fabric.py --accelerator=gpu --devices=4 +``` diff --git a/examples/fabric/image_classifier/train_fabric.py b/examples/fabric/image_classifier/train_fabric.py new file mode 100644 index 0000000..5f4d931 --- /dev/null +++ b/examples/fabric/image_classifier/train_fabric.py @@ -0,0 +1,192 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Here are 4 easy steps to use Fabric in your PyTorch code. + +1. Create the Lightning Fabric object at the beginning of your script. + +2. Remove all ``.to`` and ``.cuda`` calls since Fabric will take care of it. + +3. Apply ``setup`` over each model and optimizers pair, ``setup_dataloaders`` on all your dataloaders, +and replace ``loss.backward()`` with ``self.backward(loss)``. + +4. Run the script from the terminal using ``lightning run model path/to/train.py`` + +Accelerate your training loop by setting the ``--accelerator``, ``--strategy``, ``--devices`` options directly from +the command line. See ``lightning run model --help`` or learn more from the documentation: +https://lightning.ai/docs/fabric. +""" + +import argparse +from os import path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torchvision.transforms as T +from torch.optim.lr_scheduler import StepLR +from torchmetrics.classification import Accuracy +from torchvision.datasets import MNIST + +from lightning.fabric import Fabric # import Fabric +from lightning.fabric import seed_everything + +DATASETS_PATH = path.join(path.dirname(__file__), "..", "..", "..", "Datasets") + + +class Net(nn.Module): + def __init__(self) -> None: + super().__init__() + self.conv1 = nn.Conv2d(1, 32, 3, 1) + self.conv2 = nn.Conv2d(32, 64, 3, 1) + self.dropout1 = nn.Dropout(0.25) + self.dropout2 = nn.Dropout(0.5) + self.fc1 = nn.Linear(9216, 128) + self.fc2 = nn.Linear(128, 10) + + def forward(self, x): + x = self.conv1(x) + x = F.relu(x) + x = self.conv2(x) + x = F.relu(x) + x = F.max_pool2d(x, 2) + x = self.dropout1(x) + x = torch.flatten(x, 1) + x = self.fc1(x) + x = F.relu(x) + x = self.dropout2(x) + x = self.fc2(x) + return F.log_softmax(x, dim=1) + + +def run(hparams): + # Create the Lightning Fabric object. The parameters like accelerator, strategy, devices etc. will be proided + # by the command line. See all options: `lightning run model --help` + fabric = Fabric() + + seed_everything(hparams.seed) # instead of torch.manual_seed(...) + + transform = T.Compose([T.ToTensor(), T.Normalize((0.1307,), (0.3081,))]) + + # Let rank 0 download the data first, then everyone will load MNIST + with fabric.rank_zero_first(): + train_dataset = MNIST(DATASETS_PATH, download=fabric.is_global_zero, train=True, transform=transform) + test_dataset = MNIST(DATASETS_PATH, download=fabric.is_global_zero, train=False, transform=transform) + + train_loader = torch.utils.data.DataLoader( + train_dataset, + batch_size=hparams.batch_size, + ) + test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=hparams.batch_size) + + # don't forget to call `setup_dataloaders` to prepare for dataloaders for distributed training. + train_loader, test_loader = fabric.setup_dataloaders(train_loader, test_loader) + + model = Net() # remove call to .to(device) + optimizer = optim.Adadelta(model.parameters(), lr=hparams.lr) + + # don't forget to call `setup` to prepare for model / optimizer for distributed training. + # the model is moved automatically to the right device. + model, optimizer = fabric.setup(model, optimizer) + + scheduler = StepLR(optimizer, step_size=1, gamma=hparams.gamma) + + # use torchmetrics instead of manually computing the accuracy + test_acc = Accuracy(task="multiclass", num_classes=10).to(fabric.device) + + # EPOCH LOOP + for epoch in range(1, hparams.epochs + 1): + # TRAINING LOOP + model.train() + for batch_idx, (data, target) in enumerate(train_loader): + # NOTE: no need to call `.to(device)` on the data, target + optimizer.zero_grad() + output = model(data) + loss = F.nll_loss(output, target) + fabric.backward(loss) # instead of loss.backward() + + optimizer.step() + if (batch_idx == 0) or ((batch_idx + 1) % hparams.log_interval == 0): + print( + "Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}".format( + epoch, + batch_idx * len(data), + len(train_loader.dataset), + 100.0 * batch_idx / len(train_loader), + loss.item(), + ) + ) + if hparams.dry_run: + break + + scheduler.step() + + # TESTING LOOP + model.eval() + test_loss = 0 + with torch.no_grad(): + for data, target in test_loader: + # NOTE: no need to call `.to(device)` on the data, target + output = model(data) + test_loss += F.nll_loss(output, target, reduction="sum").item() + + # WITHOUT TorchMetrics + # pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability + # correct += pred.eq(target.view_as(pred)).sum().item() + + # WITH TorchMetrics + test_acc(output, target) + + if hparams.dry_run: + break + + # all_gather is used to aggregated the value across processes + test_loss = fabric.all_gather(test_loss).sum() / len(test_loader.dataset) + + print(f"\nTest set: Average loss: {test_loss:.4f}, Accuracy: ({100 * test_acc.compute():.0f}%)\n") + test_acc.reset() + + if hparams.dry_run: + break + + # When using distributed training, use `fabric.save` + # to ensure the current process is allowed to save a checkpoint + if hparams.save_model: + fabric.save(model.state_dict(), "mnist_cnn.pt") + + +if __name__ == "__main__": + # Arguments can be passed in through the CLI as normal and will be parsed here + # Example: + # lightning run model image_classifier.py accelerator=cuda --epochs=3 + parser = argparse.ArgumentParser(description="Fabric MNIST Example") + parser.add_argument( + "--batch-size", type=int, default=64, metavar="N", help="input batch size for training (default: 64)" + ) + parser.add_argument("--epochs", type=int, default=14, metavar="N", help="number of epochs to train (default: 14)") + parser.add_argument("--lr", type=float, default=1.0, metavar="LR", help="learning rate (default: 1.0)") + parser.add_argument("--gamma", type=float, default=0.7, metavar="M", help="Learning rate step gamma (default: 0.7)") + parser.add_argument("--dry-run", action="store_true", default=False, help="quickly check a single pass") + parser.add_argument("--seed", type=int, default=1, metavar="S", help="random seed (default: 1)") + parser.add_argument( + "--log-interval", + type=int, + default=10, + metavar="N", + help="how many batches to wait before logging training status", + ) + parser.add_argument("--save-model", action="store_true", default=False, help="For Saving the current Model") + hparams = parser.parse_args() + + run(hparams) diff --git a/examples/fabric/image_classifier/train_torch.py b/examples/fabric/image_classifier/train_torch.py new file mode 100644 index 0000000..e2bfd75 --- /dev/null +++ b/examples/fabric/image_classifier/train_torch.py @@ -0,0 +1,152 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import argparse +from os import path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torchvision.transforms as T +from torch.optim.lr_scheduler import StepLR +from torchvision.datasets import MNIST + +DATASETS_PATH = path.join(path.dirname(__file__), "..", "..", "..", "Datasets") + + +# Credit to the PyTorch team +# Taken from https://github.com/pytorch/examples/blob/master/mnist/main.py and slightly adapted. +class Net(nn.Module): + def __init__(self) -> None: + super().__init__() + self.conv1 = nn.Conv2d(1, 32, 3, 1) + self.conv2 = nn.Conv2d(32, 64, 3, 1) + self.dropout1 = nn.Dropout(0.25) + self.dropout2 = nn.Dropout(0.5) + self.fc1 = nn.Linear(9216, 128) + self.fc2 = nn.Linear(128, 10) + + def forward(self, x): + x = self.conv1(x) + x = F.relu(x) + x = self.conv2(x) + x = F.relu(x) + x = F.max_pool2d(x, 2) + x = self.dropout1(x) + x = torch.flatten(x, 1) + x = self.fc1(x) + x = F.relu(x) + x = self.dropout2(x) + x = self.fc2(x) + return F.log_softmax(x, dim=1) + + +def run(hparams): + torch.manual_seed(hparams.seed) + + use_cuda = torch.cuda.is_available() + device = torch.device("cuda" if use_cuda else "cpu") + + transform = T.Compose([T.ToTensor(), T.Normalize((0.1307,), (0.3081,))]) + train_dataset = MNIST(DATASETS_PATH, train=True, download=True, transform=transform) + test_dataset = MNIST(DATASETS_PATH, train=False, transform=transform) + train_loader = torch.utils.data.DataLoader( + train_dataset, + batch_size=hparams.batch_size, + ) + test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=hparams.batch_size) + + model = Net().to(device) + optimizer = optim.Adadelta(model.parameters(), lr=hparams.lr) + + scheduler = StepLR(optimizer, step_size=1, gamma=hparams.gamma) + + # EPOCH LOOP + for epoch in range(1, hparams.epochs + 1): + # TRAINING LOOP + model.train() + for batch_idx, (data, target) in enumerate(train_loader): + data, target = data.to(device), target.to(device) + optimizer.zero_grad() + output = model(data) + loss = F.nll_loss(output, target) + loss.backward() + optimizer.step() + if (batch_idx == 0) or ((batch_idx + 1) % hparams.log_interval == 0): + print( + "Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}".format( + epoch, + batch_idx * len(data), + len(train_loader.dataset), + 100.0 * batch_idx / len(train_loader), + loss.item(), + ) + ) + if hparams.dry_run: + break + scheduler.step() + + # TESTING LOOP + model.eval() + test_loss = 0 + correct = 0 + with torch.no_grad(): + for data, target in test_loader: + data, target = data.to(device), target.to(device) + output = model(data) + test_loss += F.nll_loss(output, target, reduction="sum").item() # sum up batch loss + pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability + correct += pred.eq(target.view_as(pred)).sum().item() + if hparams.dry_run: + break + + test_loss /= len(test_loader.dataset) + + print( + "\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n".format( + test_loss, correct, len(test_loader.dataset), 100.0 * correct / len(test_loader.dataset) + ) + ) + + if hparams.dry_run: + break + + if hparams.save_model: + torch.save(model.state_dict(), "mnist_cnn.pt") + + +def main(): + parser = argparse.ArgumentParser(description="PyTorch MNIST Example") + parser.add_argument( + "--batch-size", type=int, default=64, metavar="N", help="input batch size for training (default: 64)" + ) + parser.add_argument("--epochs", type=int, default=14, metavar="N", help="number of epochs to train (default: 14)") + parser.add_argument("--lr", type=float, default=1.0, metavar="LR", help="learning rate (default: 1.0)") + parser.add_argument("--gamma", type=float, default=0.7, metavar="M", help="Learning rate step gamma (default: 0.7)") + parser.add_argument("--dry-run", action="store_true", default=False, help="quickly check a single pass") + parser.add_argument("--seed", type=int, default=1, metavar="S", help="random seed (default: 1)") + parser.add_argument( + "--log-interval", + type=int, + default=10, + metavar="N", + help="how many batches to wait before logging training status", + ) + parser.add_argument("--save-model", action="store_true", default=False, help="For Saving the current Model") + hparams = parser.parse_args() + run(hparams) + + +if __name__ == "__main__": + main() diff --git a/examples/fabric/kfold_cv/README.md b/examples/fabric/kfold_cv/README.md new file mode 100644 index 0000000..a98aac9 --- /dev/null +++ b/examples/fabric/kfold_cv/README.md @@ -0,0 +1,30 @@ +## K-Fold Cross Validation + +This is an example of performing K-Fold cross validation supported with [Lightning Fabric](https://lightning.ai/docs/fabric). To learn more about cross validation, check out [this article](https://sebastianraschka.com/blog/2016/model-evaluation-selection-part3.html#introduction-to-k-fold-cross-validation). + +We use the MNIST dataset to train a simple CNN model. We create the k-fold cross validation splits using the `ModelSelection.KFold` [class](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.KFold.html) in the `scikit-learn` library. Ensure that you have the `scikit-learn` library installed; + +```bash +pip install scikit-learn +``` + +#### Run K-Fold Image Classification with Lightning Fabric + +This script shows you how to scale the pure PyTorch code to enable GPU and multi-GPU training using [Lightning Fabric](https://lightning.ai/docs/fabric). + +```bash +# CPU +lightning run model train_fabric.py + +# GPU (CUDA or M1 Mac) +lightning run model train_fabric.py --accelerator=gpu + +# Multiple GPUs +lightning run model train_fabric.py --accelerator=gpu --devices=4 +``` + +### References + +- [KFold Model Selection](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.KFold.html) +- [K-Fold Cross Validation by Sebastian Rashcka](https://sebastianraschka.com/blog/2016/model-evaluation-selection-part3.html#introduction-to-k-fold-cross-validation) +- [Cross Validation Wiki]() diff --git a/examples/fabric/kfold_cv/train_fabric.py b/examples/fabric/kfold_cv/train_fabric.py new file mode 100644 index 0000000..ffaa11f --- /dev/null +++ b/examples/fabric/kfold_cv/train_fabric.py @@ -0,0 +1,194 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +from os import path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.optim as optim +import torchvision.transforms as T +from sklearn import model_selection +from torch.utils.data import DataLoader, SubsetRandomSampler +from torchmetrics.classification import Accuracy +from torchvision.datasets import MNIST + +from lightning.fabric import Fabric # import Fabric +from lightning.fabric import seed_everything + +DATASETS_PATH = path.join(path.dirname(__file__), "..", "..", "..", "Datasets") + + +class Net(nn.Module): + def __init__(self) -> None: + super().__init__() + self.conv1 = nn.Conv2d(1, 32, 3, 1) + self.conv2 = nn.Conv2d(32, 64, 3, 1) + self.dropout1 = nn.Dropout(0.25) + self.dropout2 = nn.Dropout(0.5) + self.fc1 = nn.Linear(9216, 128) + self.fc2 = nn.Linear(128, 10) + + def forward(self, x): + x = self.conv1(x) + x = F.relu(x) + x = self.conv2(x) + x = F.relu(x) + x = F.max_pool2d(x, 2) + x = self.dropout1(x) + x = torch.flatten(x, 1) + x = self.fc1(x) + x = F.relu(x) + x = self.dropout2(x) + x = self.fc2(x) + return F.log_softmax(x, dim=1) + + +def train_dataloader(model, data_loader, optimizer, fabric, epoch, hparams, fold): + # TRAINING LOOP + model.train() + for batch_idx, (data, target) in enumerate(data_loader): + # NOTE: no need to call `.to(device)` on the data, target + optimizer.zero_grad() + output = model(data) + loss = F.nll_loss(output, target) + fabric.backward(loss) # instead of loss.backward() + + optimizer.step() + if (batch_idx == 0) or ((batch_idx + 1) % hparams.log_interval == 0): + print( + "Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}".format( + epoch, + batch_idx * len(data), + len(data_loader.dataset), + 100.0 * batch_idx / len(data_loader), + loss.item(), + ) + ) + + if hparams.dry_run: + break + + +def validate_dataloader(model, data_loader, fabric, hparams, fold, acc_metric): + model.eval() + loss = 0 + with torch.no_grad(): + for data, target in data_loader: + # NOTE: no need to call `.to(device)` on the data, target + output = model(data) + loss += F.nll_loss(output, target, reduction="sum").item() + + # Accuracy with torchmetrics + acc_metric.update(output, target) + + if hparams.dry_run: + break + + # all_gather is used to aggregated the value across processes + loss = fabric.all_gather(loss).sum() / len(data_loader.dataset) + + # compute acc + acc = acc_metric.compute() * 100 + + print(f"\nFor fold: {fold} Validation set: Average loss: {loss:.4f}, Accuracy: ({acc:.0f}%)\n") + return acc + + +def run(hparams): + # Create the Lightning Fabric object. The parameters like accelerator, strategy, devices etc. will be proided + # by the command line. See all options: `lightning run model --help` + fabric = Fabric() + + seed_everything(hparams.seed) # instead of torch.manual_seed(...) + + transform = T.Compose([T.ToTensor(), T.Normalize((0.1307,), (0.3081,))]) + + # Let rank 0 download the data first, then everyone will load MNIST + with fabric.rank_zero_first(): + dataset = MNIST(DATASETS_PATH, train=True, transform=transform) + + # Loop over different folds (shuffle = False by default so reproducible) + folds = hparams.folds + kfold = model_selection.KFold(n_splits=folds) + + # initialize n_splits models and optimizers + models = [Net() for _ in range(kfold.n_splits)] + optimizers = [optim.Adadelta(model.parameters(), lr=hparams.lr) for model in models] + + # fabric setup for models and optimizers + for i in range(kfold.n_splits): + models[i], optimizers[i] = fabric.setup(models[i], optimizers[i]) + + # Accuracy using torchmetrics + acc_metric = Accuracy(task="multiclass", num_classes=10).to(fabric.device) + + # loop over epochs + for epoch in range(1, hparams.epochs + 1): + # loop over folds + epoch_acc = 0 + for fold, (train_ids, val_ids) in enumerate(kfold.split(dataset)): + print(f"Working on fold {fold}") + + # initialize dataloaders based on folds + batch_size = hparams.batch_size + train_loader = DataLoader(dataset, batch_size=batch_size, sampler=SubsetRandomSampler(train_ids)) + val_loader = DataLoader(dataset, batch_size=batch_size, sampler=SubsetRandomSampler(val_ids)) + + # get model and optimizer for the current fold + model, optimizer = models[fold], optimizers[fold] + + # train and validate + train_dataloader(model, train_loader, optimizer, fabric, epoch, hparams, fold) + epoch_acc += validate_dataloader(model, val_loader, fabric, hparams, fold, acc_metric) + acc_metric.reset() + + # log epoch metrics + print(f"Epoch {epoch} - Average acc: {epoch_acc / kfold.n_splits}") + + if hparams.dry_run: + break + + # When using distributed training, use `fabric.save` + # to ensure the current process is allowed to save a checkpoint + if hparams.save_model: + fabric.save(model.state_dict(), "mnist_cnn.pt") + + +if __name__ == "__main__": + # Arguments can be passed in through the CLI as normal and will be parsed here + # Example: + # lightning run model image_classifier.py accelerator=cuda --epochs=3 + parser = argparse.ArgumentParser(description="Fabric MNIST K-Fold Cross Validation Example") + parser.add_argument( + "--batch-size", type=int, default=64, metavar="N", help="input batch size for training (default: 64)" + ) + parser.add_argument("--epochs", type=int, default=14, metavar="N", help="number of epochs to train (default: 14)") + parser.add_argument("--lr", type=float, default=1.0, metavar="LR", help="learning rate (default: 1.0)") + parser.add_argument("--gamma", type=float, default=0.7, metavar="M", help="Learning rate step gamma (default: 0.7)") + parser.add_argument("--dry-run", action="store_true", default=False, help="quickly check a single pass") + parser.add_argument("--seed", type=int, default=1, metavar="S", help="random seed (default: 1)") + parser.add_argument( + "--log-interval", + type=int, + default=10, + metavar="N", + help="how many batches to wait before logging training status", + ) + parser.add_argument("--folds", type=int, default=5, help="number of folds for k-fold cross validation") + parser.add_argument("--save-model", action="store_true", default=False, help="For Saving the current Model") + hparams = parser.parse_args() + + run(hparams) diff --git a/examples/fabric/language_model/README.md b/examples/fabric/language_model/README.md new file mode 100644 index 0000000..013b5d8 --- /dev/null +++ b/examples/fabric/language_model/README.md @@ -0,0 +1,17 @@ +## Transformers + +This example contains a simple training loop for next-word prediction with a [Transformer model](https://arxiv.org/abs/1706.03762) on a subset of the [WikiText2](https://www.salesforce.com/products/einstein/ai-research/the-wikitext-dependency-language-modeling-dataset/) dataset. +It is a simplified version of the [official PyTorch example](https://github.com/pytorch/examples/tree/main/word_language_model). + +### Train with Fabric + +```bash +# CPU +lightning run model --accelerator=cpu train.py + +# GPU (CUDA or M1 Mac) +lightning run model --accelerator=gpu train.py + +# Multiple GPUs +lightning run model --accelerator=gpu --devices=4 train.py +``` diff --git a/examples/fabric/language_model/train.py b/examples/fabric/language_model/train.py new file mode 100644 index 0000000..64af73f --- /dev/null +++ b/examples/fabric/language_model/train.py @@ -0,0 +1,75 @@ +import torch +import torch.nn.functional as F +from torch.utils.data import DataLoader, random_split + +import lightning as L +from lightning.pytorch.demos import Transformer, WikiText2 + + +def main(): + L.seed_everything(42) + + fabric = L.Fabric() + + # Data + dataset = WikiText2() + train_dataloader, val_dataloader, _ = get_dataloaders(dataset) + + # Model + model = Transformer(vocab_size=dataset.vocab_size) + + # Optimizer + optimizer = torch.optim.SGD(model.parameters(), lr=0.1) + + model, optimizer = fabric.setup(model, optimizer) + train_dataloader, val_dataloader = fabric.setup_dataloaders(train_dataloader, val_dataloader) + train(fabric, model, optimizer, train_dataloader, val_dataloader) + + +def train(fabric, model, optimizer, train_dataloader, val_dataloader, max_epochs=20): + for epoch in range(max_epochs): + train_epoch(fabric, model, optimizer, train_dataloader, epoch) + val_loss = validate(fabric, model, val_dataloader) + fabric.print(f"val loss {val_loss.item():.4f}") + + +def train_epoch(fabric, model, optimizer, train_dataloader, epoch): + for batch_idx, batch in enumerate(train_dataloader): + input, target = batch + output = model(input, target) + loss = F.nll_loss(output, target.view(-1)) + fabric.backward(loss) + fabric.clip_gradients(model, optimizer, clip_val=0.25) + optimizer.step() + optimizer.zero_grad() + + if batch_idx % 200 == 0: + fabric.print(f"epoch: {epoch} - iteration: {batch_idx} - loss {loss.item():.4f}") + + +@torch.no_grad() +def validate(fabric, model, val_dataloader): + fabric.print("Validating ...") + model.eval() + losses = torch.zeros(len(val_dataloader)) + for k, batch in enumerate(val_dataloader): + input, target = batch + output = model(input, target) + loss = F.nll_loss(output, target.view(-1)) + losses[k] = loss.item() + out = losses.mean() + model.train() + return out + + +def get_dataloaders(dataset): + n = len(dataset) + train_dataset, val_dataset, test_dataset = random_split(dataset, [n - 4000, 2000, 2000]) + train_dataloader = DataLoader(train_dataset, batch_size=20, shuffle=True) + val_dataloader = DataLoader(val_dataset, batch_size=20, shuffle=False) + test_dataloader = DataLoader(test_dataset, batch_size=20, shuffle=False) + return train_dataloader, val_dataloader, test_dataloader + + +if __name__ == "__main__": + main() diff --git a/examples/fabric/meta_learning/README.md b/examples/fabric/meta_learning/README.md new file mode 100644 index 0000000..c31b9c7 --- /dev/null +++ b/examples/fabric/meta_learning/README.md @@ -0,0 +1,43 @@ +## Meta-Learning - MAML + +This is an example of a meta-learning algorithm called [MAML](https://arxiv.org/abs/1703.03400), trained on the +[Omniglot dataset](https://paperswithcode.com/dataset/omniglot-1) of handwritten characters from different alphabets. + +The goal of meta-learning in this context is to learn a 'meta'-model trained on many different tasks, such that it can quickly adapt to a new task when trained with very few samples (few-shot learning). +If you are new to meta-learning, have a look at this short [introduction video](https://www.youtube.com/watch?v=ItPEBdD6VMk). + +We show two code versions: +The first one is implemented in raw PyTorch, but it contains quite a bit of boilerplate code for distributed training. +The second one is using [Lightning Fabric](https://lightning.ai/docs/fabric) to accelerate and scale the model. + +Tip: You can easily inspect the difference between the two files with: + +```bash +sdiff train_torch.py train_fabric.py +``` + +### Requirements + +```bash +pip install lightning learn2learn cherry-rl 'gym<=0.22' +``` + +### Run + +**Raw PyTorch:** + +```bash +torchrun --nproc_per_node=2 --standalone train_torch.py +``` + +**Accelerated using Lightning Fabric:** + +```bash +lightning run model train_fabric.py --devices 2 --strategy ddp --accelerator cpu +``` + +### References + +- [MAML explained in 7 minutes](https://www.youtube.com/watch?v=ItPEBdD6VMk) +- [Learn2Learn Resources](http://learn2learn.net/examples/vision/#maml) +- [MAML Paper](https://arxiv.org/abs/1703.03400) diff --git a/examples/fabric/meta_learning/train_fabric.py b/examples/fabric/meta_learning/train_fabric.py new file mode 100644 index 0000000..d0806fa --- /dev/null +++ b/examples/fabric/meta_learning/train_fabric.py @@ -0,0 +1,163 @@ +""" +MAML - Accelerated with Lightning Fabric + +Adapted from https://github.com/learnables/learn2learn/blob/master/examples/vision/distributed_maml.py +Original code author: Séb Arnold - learnables.net +Based on the paper: https://arxiv.org/abs/1703.03400 + +Requirements: +- lightning>=1.9.0 +- learn2learn +- cherry-rl +- gym<=0.22 + +Run it with: + lightning run model train_fabric.py --accelerator=cuda --devices=2 --strategy=ddp +""" +import cherry +import learn2learn as l2l +import torch + +from lightning.fabric import Fabric, seed_everything + + +def accuracy(predictions, targets): + predictions = predictions.argmax(dim=1).view(targets.shape) + return (predictions == targets).sum().float() / targets.size(0) + + +def fast_adapt(batch, learner, loss, adaptation_steps, shots, ways): + data, labels = batch + + # Separate data into adaptation/evalutation sets + adaptation_indices = torch.zeros(data.size(0), dtype=bool) + adaptation_indices[torch.arange(shots * ways) * 2] = True + evaluation_indices = ~adaptation_indices + adaptation_data, adaptation_labels = data[adaptation_indices], labels[adaptation_indices] + evaluation_data, evaluation_labels = data[evaluation_indices], labels[evaluation_indices] + + # Adapt the model + for step in range(adaptation_steps): + train_error = loss(learner(adaptation_data), adaptation_labels) + learner.adapt(train_error) + + # Evaluate the adapted model + predictions = learner(evaluation_data) + valid_error = loss(predictions, evaluation_labels) + valid_accuracy = accuracy(predictions, evaluation_labels) + return valid_error, valid_accuracy + + +def main( + ways=5, + shots=5, + meta_lr=0.003, + fast_lr=0.5, + meta_batch_size=32, + adaptation_steps=1, + num_iterations=60000, + seed=42, +): + # Create the Fabric object + # Arguments get parsed from the command line, see `lightning run model --help` + fabric = Fabric() + + meta_batch_size = meta_batch_size // fabric.world_size + seed_everything(seed + fabric.global_rank) + + # Create Tasksets using the benchmark interface + tasksets = l2l.vision.benchmarks.get_tasksets( + # 'mini-imagenet' works too, but you need to download it manually due to license restrictions of ImageNet + "omniglot", + train_ways=ways, + train_samples=2 * shots, + test_ways=ways, + test_samples=2 * shots, + num_tasks=20000, + root="data", + ) + + # Create model + # model = l2l.vision.models.MiniImagenetCNN(ways) + model = l2l.vision.models.OmniglotFC(28**2, ways) + model = fabric.to_device(model) + maml = l2l.algorithms.MAML(model, lr=fast_lr, first_order=False) + optimizer = torch.optim.Adam(maml.parameters(), meta_lr) + optimizer = cherry.optim.Distributed(maml.parameters(), opt=optimizer, sync=1) + + # model, optimizer = fabric.setup(model, optimizer) + + optimizer.sync_parameters() + loss = torch.nn.CrossEntropyLoss(reduction="mean") + + for iteration in range(num_iterations): + optimizer.zero_grad() + meta_train_error = 0.0 + meta_train_accuracy = 0.0 + meta_valid_error = 0.0 + meta_valid_accuracy = 0.0 + for task in range(meta_batch_size): + # Compute meta-training loss + learner = maml.clone() + batch = fabric.to_device(tasksets.train.sample()) + evaluation_error, evaluation_accuracy = fast_adapt( + batch, + learner, + loss, + adaptation_steps, + shots, + ways, + ) + fabric.backward(evaluation_error) + meta_train_error += evaluation_error.item() + meta_train_accuracy += evaluation_accuracy.item() + + # Compute meta-validation loss + learner = maml.clone() + batch = fabric.to_device(tasksets.validation.sample()) + evaluation_error, evaluation_accuracy = fast_adapt( + batch, + learner, + loss, + adaptation_steps, + shots, + ways, + ) + meta_valid_error += evaluation_error.item() + meta_valid_accuracy += evaluation_accuracy.item() + + # Print some metrics + fabric.print("\n") + fabric.print("Iteration", iteration) + fabric.print("Meta Train Error", meta_train_error / meta_batch_size) + fabric.print("Meta Train Accuracy", meta_train_accuracy / meta_batch_size) + fabric.print("Meta Valid Error", meta_valid_error / meta_batch_size) + fabric.print("Meta Valid Accuracy", meta_valid_accuracy / meta_batch_size) + + # Average the accumulated gradients and optimize + for p in maml.parameters(): + p.grad.data.mul_(1.0 / meta_batch_size) + optimizer.step() # averages gradients across all workers + + meta_test_error = 0.0 + meta_test_accuracy = 0.0 + for task in range(meta_batch_size): + # Compute meta-testing loss + learner = maml.clone() + batch = fabric.to_device(tasksets.test.sample()) + evaluation_error, evaluation_accuracy = fast_adapt( + batch, + learner, + loss, + adaptation_steps, + shots, + ways, + ) + meta_test_error += evaluation_error.item() + meta_test_accuracy += evaluation_accuracy.item() + fabric.print("Meta Test Error", meta_test_error / meta_batch_size) + fabric.print("Meta Test Accuracy", meta_test_accuracy / meta_batch_size) + + +if __name__ == "__main__": + main() diff --git a/examples/fabric/meta_learning/train_torch.py b/examples/fabric/meta_learning/train_torch.py new file mode 100644 index 0000000..365d01d --- /dev/null +++ b/examples/fabric/meta_learning/train_torch.py @@ -0,0 +1,180 @@ +""" +MAML - Raw PyTorch implementation using the Learn2Learn library + +Adapted from https://github.com/learnables/learn2learn/blob/master/examples/vision/distributed_maml.py +Original code author: Séb Arnold - learnables.net +Based on the paper: https://arxiv.org/abs/1703.03400 + +Requirements: +- learn2learn +- cherry-rl +- gym<=0.22 + +This code is written for distributed training. + +Run it with: + torchrun --nproc_per_node=2 --standalone train_torch.py +""" +import os +import random + +import cherry +import learn2learn as l2l +import torch +import torch.distributed as dist + + +def accuracy(predictions, targets): + predictions = predictions.argmax(dim=1).view(targets.shape) + return (predictions == targets).sum().float() / targets.size(0) + + +def fast_adapt(batch, learner, loss, adaptation_steps, shots, ways, device): + data, labels = batch + data, labels = data.to(device), labels.to(device) + + # Separate data into adaptation/evalutation sets + adaptation_indices = torch.zeros(data.size(0), dtype=bool) + adaptation_indices[torch.arange(shots * ways) * 2] = True + evaluation_indices = ~adaptation_indices + adaptation_data, adaptation_labels = data[adaptation_indices], labels[adaptation_indices] + evaluation_data, evaluation_labels = data[evaluation_indices], labels[evaluation_indices] + + # Adapt the model + for step in range(adaptation_steps): + train_error = loss(learner(adaptation_data), adaptation_labels) + learner.adapt(train_error) + + # Evaluate the adapted model + predictions = learner(evaluation_data) + valid_error = loss(predictions, evaluation_labels) + valid_accuracy = accuracy(predictions, evaluation_labels) + return valid_error, valid_accuracy + + +def main( + ways=5, + shots=5, + meta_lr=0.003, + fast_lr=0.5, + meta_batch_size=32, + adaptation_steps=1, + num_iterations=60000, + cuda=True, + seed=42, +): + local_rank = int(os.environ["LOCAL_RANK"]) + world_size = int(os.environ["WORLD_SIZE"]) + os.environ["MASTER_ADDR"] = "127.0.0.1" + os.environ["MASTER_PORT"] = "12345" + dist.init_process_group("gloo", rank=local_rank, world_size=world_size) + rank = dist.get_rank() + + meta_batch_size = meta_batch_size // world_size + seed = seed + rank + + random.seed(seed) + torch.manual_seed(seed) + device = torch.device("cpu") + if cuda and torch.cuda.device_count(): + torch.cuda.manual_seed(seed) + device_id = rank % torch.cuda.device_count() + device = torch.device("cuda:" + str(device_id)) + + # Create Tasksets using the benchmark interface + tasksets = l2l.vision.benchmarks.get_tasksets( + # 'mini-imagenet' works too, but you need to download it manually due to license restrictions of ImageNet + "omniglot", + train_ways=ways, + train_samples=2 * shots, + test_ways=ways, + test_samples=2 * shots, + num_tasks=20000, + root="data", + ) + + # Create model + # model = l2l.vision.models.MiniImagenetCNN(ways) + model = l2l.vision.models.OmniglotFC(28**2, ways) + model.to(device) + maml = l2l.algorithms.MAML(model, lr=fast_lr, first_order=False) + optimizer = torch.optim.Adam(maml.parameters(), meta_lr) + optimizer = cherry.optim.Distributed(maml.parameters(), opt=optimizer, sync=1) + optimizer.sync_parameters() + loss = torch.nn.CrossEntropyLoss(reduction="mean") + + for iteration in range(num_iterations): + optimizer.zero_grad() + meta_train_error = 0.0 + meta_train_accuracy = 0.0 + meta_valid_error = 0.0 + meta_valid_accuracy = 0.0 + for task in range(meta_batch_size): + # Compute meta-training loss + learner = maml.clone() + batch = tasksets.train.sample() + evaluation_error, evaluation_accuracy = fast_adapt( + batch, + learner, + loss, + adaptation_steps, + shots, + ways, + device, + ) + evaluation_error.backward() + meta_train_error += evaluation_error.item() + meta_train_accuracy += evaluation_accuracy.item() + + # Compute meta-validation loss + learner = maml.clone() + batch = tasksets.validation.sample() + evaluation_error, evaluation_accuracy = fast_adapt( + batch, + learner, + loss, + adaptation_steps, + shots, + ways, + device, + ) + meta_valid_error += evaluation_error.item() + meta_valid_accuracy += evaluation_accuracy.item() + + # Print some metrics + if rank == 0: + print("\n") + print("Iteration", iteration) + print("Meta Train Error", meta_train_error / meta_batch_size) + print("Meta Train Accuracy", meta_train_accuracy / meta_batch_size) + print("Meta Valid Error", meta_valid_error / meta_batch_size) + print("Meta Valid Accuracy", meta_valid_accuracy / meta_batch_size) + + # Average the accumulated gradients and optimize + for p in maml.parameters(): + p.grad.data.mul_(1.0 / meta_batch_size) + optimizer.step() # averages gradients across all workers + + meta_test_error = 0.0 + meta_test_accuracy = 0.0 + for task in range(meta_batch_size): + # Compute meta-testing loss + learner = maml.clone() + batch = tasksets.test.sample() + evaluation_error, evaluation_accuracy = fast_adapt( + batch, + learner, + loss, + adaptation_steps, + shots, + ways, + device, + ) + meta_test_error += evaluation_error.item() + meta_test_accuracy += evaluation_accuracy.item() + print("Meta Test Error", meta_test_error / meta_batch_size) + print("Meta Test Accuracy", meta_test_accuracy / meta_batch_size) + + +if __name__ == "__main__": + main() diff --git a/examples/fabric/reinforcement_learning/README.md b/examples/fabric/reinforcement_learning/README.md new file mode 100644 index 0000000..73b6e91 --- /dev/null +++ b/examples/fabric/reinforcement_learning/README.md @@ -0,0 +1,129 @@ +# Proximal Policy Optimization - PPO implementation powered by Lightning Fabric + +This is an example of a Reinforcement Learning algorithm called [Proximal Policy Optimization (PPO)](https://arxiv.org/abs/1707.06347) implemented in PyTorch and accelerated by [Lightning Fabric](https://lightning.ai/docs/fabric). + +The goal of Reinforcement Learning is to train agents to act in their surrounding environment maximizing the cumulative reward received from it. This can be depicted in the following figure: + +

+ +

+ +PPO is one of such algorithms, which alternates between sampling data through interaction with the environment, and optimizing a +“surrogate” objective function using stochastic gradient ascent. + +## Requirements + +Install requirements by running + +```bash +pip install -r requirements.txt +``` + +## Example 1 - Environment coupled with the agent + +In this example we present two code versions: the first one is implemented in raw PyTorch, but it contains quite a bit of boilerplate code for distributed training. The second one is using Lightning Fabric to accelerate and scale the model. + +The main architecture is the following: + +

+ +

+ +where `N+1` processes (labelled *rank-0*, ..., *rank-N* in the figure above) will be spawned by Fabric/PyTorch, each of them running `M+1` independent copies of the environment (*Env-0*, ..., *Env-M*). Every rank has its own copy of the agent, represented by a [LightningModule](https://lightning.ai/docs/pytorch/stable/common/lightning_module.html)/[Pytorch Module](https://pytorch.org/docs/stable/generated/torch.nn.Module.html), which will be updated through distributed training. + +### Raw PyTorch: + +```bash +torchrun --nproc_per_node=2 --standalone train_torch.py +``` + +### Lightning Fabric: + +```bash +lightning run model --accelerator=cpu --strategy=ddp --devices=2 train_fabric.py +``` + +### Visualizing logs + +You can visualize training and test logs by running: + +```bash +tensorboard --logdir logs +``` + +Under the `logs` folder you should find two folders: + +- `logs/torch_logs` +- `logs/fabric_logs` + +If you have run the experiment with the `--capture-video` you should find the `train_videos` and `test_videos` folders under the specific experiment folder. + +## Results + +The following video shows a trained agent on the [LunarLander-v2 environment](https://gymnasium.farama.org/environments/box2d/lunar_lander/). + +

+ +

+ +The agent was trained with the following: + +```bash +lightning run model \ + --accelerator=cpu \ + --strategy=ddp \ + --devices=2 \ + train_fabric.py \ + --capture-video \ + --env-id LunarLander-v2 \ + --total-timesteps 500000 \ + --ortho-init \ + --num-envs 2 \ + --num-steps 2048 \ + --seed 1 +``` + +## Example 2 - Environment decoupled from the agent + +In this example we have gone even further leveraging the flexibility offered by [Fabric](https://lightning.ai/docs/fabric). +The architecture is depicted in the following figure: + +

+ +

+ +where, differently from the previous example, we have completely decoupled the environment from the agents: the *rank-0* process will be regarded as the *Player*, which runs `M+1` independent copies of the environment (*Env-0*, ..., *Env-M*); the *rank-1*, ..., *rank-N* are the *Trainers*, which contain the agent to be optimized. Player and Trainer share data through [collectives](https://lightning.ai/docs/fabric/stable/api/generated/lightning.fabric.plugins.collectives.TorchCollective.html#lightning.fabric.plugins.collectives.TorchCollective) and thanks to Fabric's flexibility we can run Player and Trainers on different devices. +So for example: + +```bash +lightning run model --devices=3 train_fabric_decoupled.py --num-envs 4 +``` + +will spawn 3 processes, one is the Player and the others the Trainers, with the Player running 4 independent environments, where every process runs on the CPU; + +```bash +lightning run model --devices=3 train_fabric_decoupled.py --num-envs 4 --cuda +``` + +will instead run only the Trainers on the GPU. +If one wants to run both the Player and the Trainers on the GPU, then both the flags `--cuda` and `--player-on-gpu` must be provided: + +```bash +lightning run model --devices=3 train_fabric_decoupled.py --num-envs 4 --cuda --player-on-gpu +``` + +> **Warning** +> +> With this second example, there is no need for the user to provide the `accellerator` and the `strategy` to the `lightning run model` script. + +## Number of updates, environment steps and share data + +In every one of the examples above, one has that: + +- The number of total updates will be given by `args.total_timesteps / args.num_steps` +- `args.num_steps` is the number of environment interactions before the agent training step, i.e. the agent gathers `args.num_steps` experiences and uses them to update itself during the training step +- `args.share_data` controls how the data is shared between processes. In particular: + - In the first example, **if `args.share_data` is set** then every process will have access at the data gathered by all the other processes, effectively calling the [all_gather](https://pytorch.org/docs/stable/distributed.html#torch.distributed.all_gather) distributed function. In this way, during the training step, the agents can employ the standard [PyTorch distributed training receipe](https://pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html#torch.nn.parallel.DistributedDataParallel), where one can assume that before the training starts every process sees the same data, and trains the model on a disjoint subset (from process to process) of it. Otherwise, **if `args.share_data` is not set** (the default), then every process will update the model with its own local data + - In the second example, when **`args.share_data` is set** then one has the same behaviour of the first example. Instead, when **`args.share_data` is not set** then the player scatters an almost-equal-sized subset of the collected experiences to the trainers, effectively calling the [scatter](https://pytorch.org/docs/stable/distributed.html#torch.distributed.scatter) distributed function diff --git a/examples/fabric/reinforcement_learning/requirements.txt b/examples/fabric/reinforcement_learning/requirements.txt new file mode 100644 index 0000000..83bfd42 --- /dev/null +++ b/examples/fabric/reinforcement_learning/requirements.txt @@ -0,0 +1,5 @@ +gymnasium[box2d]>=0.27.1 +moviepy +lightning>=1.9.0 +torchmetrics +tensorboard diff --git a/examples/fabric/reinforcement_learning/rl/__init__.py b/examples/fabric/reinforcement_learning/rl/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/fabric/reinforcement_learning/rl/agent.py b/examples/fabric/reinforcement_learning/rl/agent.py new file mode 100644 index 0000000..6f34265 --- /dev/null +++ b/examples/fabric/reinforcement_learning/rl/agent.py @@ -0,0 +1,248 @@ +import math +from typing import Dict, Tuple + +import gymnasium as gym +import torch +import torch.nn.functional as F +from rl.loss import entropy_loss, policy_loss, value_loss +from rl.utils import layer_init +from torch import Tensor +from torch.distributions import Categorical +from torchmetrics import MeanMetric + +from lightning.pytorch import LightningModule + + +class PPOAgent(torch.nn.Module): + def __init__(self, envs: gym.vector.SyncVectorEnv, act_fun: str = "relu", ortho_init: bool = False) -> None: + super().__init__() + if act_fun.lower() == "relu": + act_fun = torch.nn.ReLU() + elif act_fun.lower() == "tanh": + act_fun = torch.nn.Tanh() + else: + raise ValueError("Unrecognized activation function: `act_fun` must be either `relu` or `tanh`") + self.critic = torch.nn.Sequential( + layer_init( + torch.nn.Linear(math.prod(envs.single_observation_space.shape), 64), + ortho_init=ortho_init, + ), + act_fun, + layer_init(torch.nn.Linear(64, 64), ortho_init=ortho_init), + act_fun, + layer_init(torch.nn.Linear(64, 1), std=1.0, ortho_init=ortho_init), + ) + self.actor = torch.nn.Sequential( + layer_init( + torch.nn.Linear(math.prod(envs.single_observation_space.shape), 64), + ortho_init=ortho_init, + ), + act_fun, + layer_init(torch.nn.Linear(64, 64), ortho_init=ortho_init), + act_fun, + layer_init(torch.nn.Linear(64, envs.single_action_space.n), std=0.01, ortho_init=ortho_init), + ) + + def get_action(self, x: Tensor, action: Tensor = None) -> Tuple[Tensor, Tensor, Tensor]: + logits = self.actor(x) + distribution = Categorical(logits=logits) + if action is None: + action = distribution.sample() + return action, distribution.log_prob(action), distribution.entropy() + + def get_greedy_action(self, x: Tensor) -> Tensor: + logits = self.actor(x) + probs = F.softmax(logits, dim=-1) + return torch.argmax(probs, dim=-1) + + def get_value(self, x: Tensor) -> Tensor: + return self.critic(x) + + def get_action_and_value(self, x: Tensor, action: Tensor = None) -> Tuple[Tensor, Tensor, Tensor, Tensor]: + action, log_prob, entropy = self.get_action(x, action) + value = self.get_value(x) + return action, log_prob, entropy, value + + def forward(self, x: Tensor, action: Tensor = None) -> Tuple[Tensor, Tensor, Tensor, Tensor]: + return self.get_action_and_value(x, action) + + @torch.no_grad() + def estimate_returns_and_advantages( + self, + rewards: Tensor, + values: Tensor, + dones: Tensor, + next_obs: Tensor, + next_done: Tensor, + num_steps: int, + gamma: float, + gae_lambda: float, + ) -> Tuple[Tensor, Tensor]: + next_value = self.get_value(next_obs).reshape(1, -1) + advantages = torch.zeros_like(rewards) + lastgaelam = 0 + for t in reversed(range(num_steps)): + if t == num_steps - 1: + nextnonterminal = torch.logical_not(next_done) + nextvalues = next_value + else: + nextnonterminal = torch.logical_not(dones[t + 1]) + nextvalues = values[t + 1] + delta = rewards[t] + gamma * nextvalues * nextnonterminal - values[t] + advantages[t] = lastgaelam = delta + gamma * gae_lambda * nextnonterminal * lastgaelam + returns = advantages + values + return returns, advantages + + +class PPOLightningAgent(LightningModule): + def __init__( + self, + envs: gym.vector.SyncVectorEnv, + act_fun: str = "relu", + ortho_init: bool = False, + vf_coef: float = 1.0, + ent_coef: float = 0.0, + clip_coef: float = 0.2, + clip_vloss: bool = False, + normalize_advantages: bool = False, + **torchmetrics_kwargs, + ): + super().__init__() + if act_fun.lower() == "relu": + act_fun = torch.nn.ReLU() + elif act_fun.lower() == "tanh": + act_fun = torch.nn.Tanh() + else: + raise ValueError("Unrecognized activation function: `act_fun` must be either `relu` or `tanh`") + self.vf_coef = vf_coef + self.ent_coef = ent_coef + self.clip_coef = clip_coef + self.clip_vloss = clip_vloss + self.normalize_advantages = normalize_advantages + self.critic = torch.nn.Sequential( + layer_init( + torch.nn.Linear(math.prod(envs.single_observation_space.shape), 64), + ortho_init=ortho_init, + ), + act_fun, + layer_init(torch.nn.Linear(64, 64), ortho_init=ortho_init), + act_fun, + layer_init(torch.nn.Linear(64, 1), std=1.0, ortho_init=ortho_init), + ) + self.actor = torch.nn.Sequential( + layer_init( + torch.nn.Linear(math.prod(envs.single_observation_space.shape), 64), + ortho_init=ortho_init, + ), + act_fun, + layer_init(torch.nn.Linear(64, 64), ortho_init=ortho_init), + act_fun, + layer_init(torch.nn.Linear(64, envs.single_action_space.n), std=0.01, ortho_init=ortho_init), + ) + self.avg_pg_loss = MeanMetric(**torchmetrics_kwargs) + self.avg_value_loss = MeanMetric(**torchmetrics_kwargs) + self.avg_ent_loss = MeanMetric(**torchmetrics_kwargs) + + def get_action(self, x: Tensor, action: Tensor = None) -> Tuple[Tensor, Tensor, Tensor]: + logits = self.actor(x) + distribution = Categorical(logits=logits) + if action is None: + action = distribution.sample() + return action, distribution.log_prob(action), distribution.entropy() + + def get_greedy_action(self, x: Tensor) -> Tensor: + logits = self.actor(x) + probs = F.softmax(logits, dim=-1) + return torch.argmax(probs, dim=-1) + + def get_value(self, x: Tensor) -> Tensor: + return self.critic(x) + + def get_action_and_value(self, x: Tensor, action: Tensor = None) -> Tuple[Tensor, Tensor, Tensor, Tensor]: + action, log_prob, entropy = self.get_action(x, action) + value = self.get_value(x) + return action, log_prob, entropy, value + + def forward(self, x: Tensor, action: Tensor = None) -> Tuple[Tensor, Tensor, Tensor, Tensor]: + return self.get_action_and_value(x, action) + + @torch.no_grad() + def estimate_returns_and_advantages( + self, + rewards: Tensor, + values: Tensor, + dones: Tensor, + next_obs: Tensor, + next_done: Tensor, + num_steps: int, + gamma: float, + gae_lambda: float, + ) -> Tuple[Tensor, Tensor]: + next_value = self.get_value(next_obs).reshape(1, -1) + advantages = torch.zeros_like(rewards) + lastgaelam = 0 + for t in reversed(range(num_steps)): + if t == num_steps - 1: + nextnonterminal = torch.logical_not(next_done) + nextvalues = next_value + else: + nextnonterminal = torch.logical_not(dones[t + 1]) + nextvalues = values[t + 1] + delta = rewards[t] + gamma * nextvalues * nextnonterminal - values[t] + advantages[t] = lastgaelam = delta + gamma * gae_lambda * nextnonterminal * lastgaelam + returns = advantages + values + return returns, advantages + + def training_step(self, batch: Dict[str, Tensor]): + # Get actions and values given the current observations + _, newlogprob, entropy, newvalue = self(batch["obs"], batch["actions"].long()) + logratio = newlogprob - batch["logprobs"] + ratio = logratio.exp() + + # Policy loss + advantages = batch["advantages"] + if self.normalize_advantages: + advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8) + + pg_loss = policy_loss(batch["advantages"], ratio, self.clip_coef) + + # Value loss + v_loss = value_loss( + newvalue, + batch["values"], + batch["returns"], + self.clip_coef, + self.clip_vloss, + self.vf_coef, + ) + + # Entropy loss + ent_loss = entropy_loss(entropy, self.ent_coef) + + # Update metrics + self.avg_pg_loss(pg_loss) + self.avg_value_loss(v_loss) + self.avg_ent_loss(ent_loss) + + # Overall loss + return pg_loss + ent_loss + v_loss + + def on_train_epoch_end(self, global_step: int) -> None: + # Log metrics and reset their internal state + self.logger.log_metrics( + { + "Loss/policy_loss": self.avg_pg_loss.compute(), + "Loss/value_loss": self.avg_value_loss.compute(), + "Loss/entropy_loss": self.avg_ent_loss.compute(), + }, + global_step, + ) + self.reset_metrics() + + def reset_metrics(self): + self.avg_pg_loss.reset() + self.avg_value_loss.reset() + self.avg_ent_loss.reset() + + def configure_optimizers(self, lr: float): + return torch.optim.Adam(self.parameters(), lr=lr, eps=1e-4) diff --git a/examples/fabric/reinforcement_learning/rl/loss.py b/examples/fabric/reinforcement_learning/rl/loss.py new file mode 100644 index 0000000..0ab74b3 --- /dev/null +++ b/examples/fabric/reinforcement_learning/rl/loss.py @@ -0,0 +1,29 @@ +import torch +import torch.nn.functional as F +from torch import Tensor + + +def policy_loss(advantages: torch.Tensor, ratio: torch.Tensor, clip_coef: float) -> torch.Tensor: + pg_loss1 = -advantages * ratio + pg_loss2 = -advantages * torch.clamp(ratio, 1 - clip_coef, 1 + clip_coef) + return torch.max(pg_loss1, pg_loss2).mean() + + +def value_loss( + new_values: Tensor, + old_values: Tensor, + returns: Tensor, + clip_coef: float, + clip_vloss: bool, + vf_coef: float, +) -> Tensor: + new_values = new_values.view(-1) + if not clip_vloss: + values_pred = new_values + else: + values_pred = old_values + torch.clamp(new_values - old_values, -clip_coef, clip_coef) + return vf_coef * F.mse_loss(values_pred, returns) + + +def entropy_loss(entropy: Tensor, ent_coef: float) -> Tensor: + return -entropy.mean() * ent_coef diff --git a/examples/fabric/reinforcement_learning/rl/utils.py b/examples/fabric/reinforcement_learning/rl/utils.py new file mode 100644 index 0000000..a990337 --- /dev/null +++ b/examples/fabric/reinforcement_learning/rl/utils.py @@ -0,0 +1,182 @@ +import argparse +import math +import os +from distutils.util import strtobool +from typing import Optional, TYPE_CHECKING, Union + +import gymnasium as gym +import torch +from torch.utils.tensorboard import SummaryWriter + +if TYPE_CHECKING: + from rl.agent import PPOAgent, PPOLightningAgent + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--exp-name", type=str, default="default", help="the name of this experiment") + + # PyTorch arguments + parser.add_argument("--seed", type=int, default=42, help="seed of the experiment") + parser.add_argument( + "--cuda", + type=lambda x: bool(strtobool(x)), + default=False, + nargs="?", + const=True, + help="If toggled, GPU training will be used. " + "This affects also the distributed backend used (NCCL (gpu) vs GLOO (cpu))", + ) + parser.add_argument( + "--player-on-gpu", + type=lambda x: bool(strtobool(x)), + default=False, + nargs="?", + const=True, + help="If toggled, player will run on GPU (used only by `train_fabric_decoupled.py` script). " + "This affects also the distributed backend used (NCCL (gpu) vs GLOO (cpu))", + ) + parser.add_argument( + "--torch-deterministic", + type=lambda x: bool(strtobool(x)), + default=True, + nargs="?", + const=True, + help="if toggled, `torch.backends.cudnn.deterministic=False`", + ) + + # Distributed arguments + parser.add_argument("--num-envs", type=int, default=2, help="the number of parallel game environments") + parser.add_argument( + "--share-data", + type=lambda x: bool(strtobool(x)), + default=False, + nargs="?", + const=True, + help="Toggle sharing data between processes", + ) + parser.add_argument("--per-rank-batch-size", type=int, default=64, help="the batch size for each rank") + + # Environment arguments + parser.add_argument("--env-id", type=str, default="CartPole-v1", help="the id of the environment") + parser.add_argument( + "--num-steps", type=int, default=128, help="the number of steps to run in each environment per policy rollout" + ) + parser.add_argument( + "--capture-video", + type=lambda x: bool(strtobool(x)), + default=False, + nargs="?", + const=True, + help="whether to capture videos of the agent performances (check out `videos` folder)", + ) + + # PPO arguments + parser.add_argument("--total-timesteps", type=int, default=2**16, help="total timesteps of the experiments") + parser.add_argument("--learning-rate", type=float, default=1e-3, help="the learning rate of the optimizer") + parser.add_argument( + "--anneal-lr", + type=lambda x: bool(strtobool(x)), + default=False, + nargs="?", + const=True, + help="Toggle learning rate annealing for policy and value networks", + ) + parser.add_argument("--gamma", type=float, default=0.99, help="the discount factor gamma") + parser.add_argument( + "--gae-lambda", type=float, default=0.95, help="the lambda for the general advantage estimation" + ) + parser.add_argument("--update-epochs", type=int, default=10, help="the K epochs to update the policy") + parser.add_argument( + "--activation-function", + type=str, + default="relu", + choices=["relu", "tanh"], + help="The activation function of the model", + ) + parser.add_argument( + "--ortho-init", + type=lambda x: bool(strtobool(x)), + default=False, + nargs="?", + const=True, + help="Toggles the orthogonal initialization of the model", + ) + parser.add_argument( + "--normalize-advantages", + type=lambda x: bool(strtobool(x)), + default=False, + nargs="?", + const=True, + help="Toggles advantages normalization", + ) + parser.add_argument("--clip-coef", type=float, default=0.2, help="the surrogate clipping coefficient") + parser.add_argument( + "--clip-vloss", + type=lambda x: bool(strtobool(x)), + default=False, + nargs="?", + const=True, + help="Toggles whether or not to use a clipped loss for the value function, as per the paper.", + ) + parser.add_argument("--ent-coef", type=float, default=0.0, help="coefficient of the entropy") + parser.add_argument("--vf-coef", type=float, default=1.0, help="coefficient of the value function") + parser.add_argument("--max-grad-norm", type=float, default=0.5, help="the maximum norm for the gradient clipping") + return parser.parse_args() + + +def layer_init( + layer: torch.nn.Module, + std: float = math.sqrt(2), + bias_const: float = 0.0, + ortho_init: bool = True, +): + if ortho_init: + torch.nn.init.orthogonal_(layer.weight, std) + torch.nn.init.constant_(layer.bias, bias_const) + return layer + + +def linear_annealing(optimizer: torch.optim.Optimizer, update: int, num_updates: int, initial_lr: float): + frac = 1.0 - (update - 1.0) / num_updates + lrnow = frac * initial_lr + for pg in optimizer.param_groups: + pg["lr"] = lrnow + + +def make_env(env_id: str, seed: int, idx: int, capture_video: bool, run_name: Optional[str] = None, prefix: str = ""): + def thunk(): + env = gym.make(env_id, render_mode="rgb_array") + env = gym.wrappers.RecordEpisodeStatistics(env) + if capture_video and idx == 0 and run_name is not None: + env = gym.wrappers.RecordVideo( + env, os.path.join(run_name, prefix + "_videos" if prefix else "videos"), disable_logger=True + ) + env.action_space.seed(seed) + env.observation_space.seed(seed) + return env + + return thunk + + +@torch.no_grad() +def test( + agent: Union["PPOLightningAgent", "PPOAgent"], device: torch.device, logger: SummaryWriter, args: argparse.Namespace +): + env = make_env(args.env_id, args.seed, 0, args.capture_video, logger.log_dir, "test")() + step = 0 + done = False + cumulative_rew = 0 + next_obs = torch.tensor(env.reset(seed=args.seed)[0], device=device) + while not done: + # Act greedly through the environment + action = agent.get_greedy_action(next_obs) + + # Single environment step + next_obs, reward, done, truncated, info = env.step(action.cpu().numpy()) + done = done or truncated + cumulative_rew += reward + next_obs = torch.tensor(next_obs, device=device) + step += 1 + logger.add_scalar("Test/cumulative_reward", cumulative_rew, 0) + env.close() diff --git a/examples/fabric/reinforcement_learning/train_fabric.py b/examples/fabric/reinforcement_learning/train_fabric.py new file mode 100644 index 0000000..5b142fc --- /dev/null +++ b/examples/fabric/reinforcement_learning/train_fabric.py @@ -0,0 +1,215 @@ +""" +Proximal Policy Optimization (PPO) - Accelerated with Lightning Fabric + +Author: Federico Belotti @belerico +Adapted from https://github.com/vwxyzjn/cleanrl/blob/master/cleanrl/ppo.py +Based on the paper: https://arxiv.org/abs/1707.06347 + +Requirements: +- gymnasium[box2d]>=0.27.1 +- moviepy +- lightning +- torchmetrics +- tensorboard + + +Run it with: + lightning run model --accelerator=cpu --strategy=ddp --devices=2 train_fabric.py +""" + +import argparse +import os +import time +from datetime import datetime +from typing import Dict + +import gymnasium as gym +import torch +import torchmetrics +from rl.agent import PPOLightningAgent +from rl.utils import linear_annealing, make_env, parse_args, test +from torch import Tensor +from torch.utils.data import BatchSampler, DistributedSampler, RandomSampler + +from lightning.fabric import Fabric +from lightning.fabric.loggers import TensorBoardLogger + + +def train( + fabric: Fabric, + agent: PPOLightningAgent, + optimizer: torch.optim.Optimizer, + data: Dict[str, Tensor], + global_step: int, + args: argparse.Namespace, +): + indexes = list(range(data["obs"].shape[0])) + if args.share_data: + sampler = DistributedSampler( + indexes, num_replicas=fabric.world_size, rank=fabric.global_rank, shuffle=True, seed=args.seed + ) + else: + sampler = RandomSampler(indexes) + sampler = BatchSampler(sampler, batch_size=args.per_rank_batch_size, drop_last=False) + + for epoch in range(args.update_epochs): + if args.share_data: + sampler.sampler.set_epoch(epoch) + for batch_idxes in sampler: + loss = agent.training_step({k: v[batch_idxes] for k, v in data.items()}) + optimizer.zero_grad(set_to_none=True) + fabric.backward(loss) + fabric.clip_gradients(agent, optimizer, max_norm=args.max_grad_norm) + optimizer.step() + agent.on_train_epoch_end(global_step) + + +def main(args: argparse.Namespace): + run_name = f"{args.env_id}_{args.exp_name}_{args.seed}_{int(time.time())}" + logger = TensorBoardLogger( + root_dir=os.path.join("logs", "fabric_logs", datetime.today().strftime("%Y-%m-%d_%H-%M-%S")), name=run_name + ) + + # Initialize Fabric + fabric = Fabric(loggers=logger) + rank = fabric.global_rank + world_size = fabric.world_size + device = fabric.device + fabric.seed_everything(args.seed) + torch.backends.cudnn.deterministic = args.torch_deterministic + + # Log hyperparameters + fabric.logger.experiment.add_text( + "hyperparameters", + "|param|value|\n|-|-|\n%s" % ("\n".join([f"|{key}|{value}|" for key, value in vars(args).items()])), + ) + + # Environment setup + envs = gym.vector.SyncVectorEnv( + [ + make_env( + args.env_id, args.seed + rank * args.num_envs + i, rank, args.capture_video, logger.log_dir, "train" + ) + for i in range(args.num_envs) + ] + ) + assert isinstance(envs.single_action_space, gym.spaces.Discrete), "only discrete action space is supported" + + # Define the agent and the optimizer and setup them with Fabric + agent: PPOLightningAgent = PPOLightningAgent( + envs, + act_fun=args.activation_function, + vf_coef=args.vf_coef, + ent_coef=args.ent_coef, + clip_coef=args.clip_coef, + clip_vloss=args.clip_vloss, + ortho_init=args.ortho_init, + normalize_advantages=args.normalize_advantages, + ) + optimizer = agent.configure_optimizers(args.learning_rate) + agent, optimizer = fabric.setup(agent, optimizer) + + # Player metrics + rew_avg = torchmetrics.MeanMetric().to(device) + ep_len_avg = torchmetrics.MeanMetric().to(device) + + # Local data + obs = torch.zeros((args.num_steps, args.num_envs) + envs.single_observation_space.shape, device=device) + actions = torch.zeros((args.num_steps, args.num_envs) + envs.single_action_space.shape, device=device) + logprobs = torch.zeros((args.num_steps, args.num_envs), device=device) + rewards = torch.zeros((args.num_steps, args.num_envs), device=device) + dones = torch.zeros((args.num_steps, args.num_envs), device=device) + values = torch.zeros((args.num_steps, args.num_envs), device=device) + + # Global variables + global_step = 0 + start_time = time.time() + single_global_rollout = int(args.num_envs * args.num_steps * world_size) + num_updates = args.total_timesteps // single_global_rollout + + # Get the first environment observation and start the optimization + next_obs = torch.tensor(envs.reset(seed=args.seed)[0], device=device) + next_done = torch.zeros(args.num_envs, device=device) + for update in range(1, num_updates + 1): + # Learning rate annealing + if args.anneal_lr: + linear_annealing(optimizer, update, num_updates, args.learning_rate) + fabric.log("Info/learning_rate", optimizer.param_groups[0]["lr"], global_step) + + for step in range(0, args.num_steps): + global_step += args.num_envs * world_size + obs[step] = next_obs + dones[step] = next_done + + # Sample an action given the observation received by the environment + with torch.no_grad(): + action, logprob, _, value = agent.get_action_and_value(next_obs) + values[step] = value.flatten() + actions[step] = action + logprobs[step] = logprob + + # Single environment step + next_obs, reward, done, truncated, info = envs.step(action.cpu().numpy()) + done = torch.logical_or(torch.tensor(done), torch.tensor(truncated)) + rewards[step] = torch.tensor(reward, device=device).view(-1) + next_obs, next_done = torch.tensor(next_obs, device=device), done.to(device) + + if "final_info" in info: + for i, agent_final_info in enumerate(info["final_info"]): + if agent_final_info is not None and "episode" in agent_final_info: + fabric.print( + f"Rank-0: global_step={global_step}, reward_env_{i}={agent_final_info['episode']['r'][0]}" + ) + rew_avg(agent_final_info["episode"]["r"][0]) + ep_len_avg(agent_final_info["episode"]["l"][0]) + + # Sync the metrics + rew_avg_reduced = rew_avg.compute() + if not rew_avg_reduced.isnan(): + fabric.log("Rewards/rew_avg", rew_avg_reduced, global_step) + ep_len_avg_reduced = ep_len_avg.compute() + if not ep_len_avg_reduced.isnan(): + fabric.log("Game/ep_len_avg", ep_len_avg_reduced, global_step) + rew_avg.reset() + ep_len_avg.reset() + + # Estimate returns with GAE (https://arxiv.org/abs/1506.02438) + returns, advantages = agent.estimate_returns_and_advantages( + rewards, values, dones, next_obs, next_done, args.num_steps, args.gamma, args.gae_lambda + ) + + # Flatten the batch + local_data = { + "obs": obs.reshape((-1,) + envs.single_observation_space.shape), + "logprobs": logprobs.reshape(-1), + "actions": actions.reshape((-1,) + envs.single_action_space.shape), + "advantages": advantages.reshape(-1), + "returns": returns.reshape(-1), + "values": values.reshape(-1), + } + + if args.share_data: + # Gather all the tensors from all the world and reshape them + gathered_data = fabric.all_gather(local_data) + for k, v in gathered_data.items(): + if k == "obs": + gathered_data[k] = v.reshape((-1,) + envs.single_observation_space.shape) + elif k == "actions": + gathered_data[k] = v.reshape((-1,) + envs.single_action_space.shape) + else: + gathered_data[k] = v.reshape(-1) + else: + gathered_data = local_data + + # Train the agent + train(fabric, agent, optimizer, gathered_data, global_step, args) + fabric.log("Time/step_per_second", int(global_step / (time.time() - start_time)), global_step) + + envs.close() + if fabric.is_global_zero: + test(agent.module, device, fabric.logger.experiment, args) + + +if __name__ == "__main__": + args = parse_args() + main(args) diff --git a/examples/fabric/reinforcement_learning/train_fabric_decoupled.py b/examples/fabric/reinforcement_learning/train_fabric_decoupled.py new file mode 100644 index 0000000..da43a75 --- /dev/null +++ b/examples/fabric/reinforcement_learning/train_fabric_decoupled.py @@ -0,0 +1,352 @@ +""" +Proximal Policy Optimization (PPO) - Accelerated with Lightning Fabric + +Author: Federico Belotti @belerico +Adapted from https://github.com/vwxyzjn/cleanrl/blob/master/cleanrl/ppo.py +Based on the paper: https://arxiv.org/abs/1707.06347 + +Requirements: +- gymnasium[box2d]>=0.27.1 +- moviepy +- lightning +- torchmetrics +- tensorboard + + +Run it with: + lightning run model --devices=2 train_fabric_decoupled.py +""" + +import argparse +import os +import time +from contextlib import nullcontext +from datetime import datetime + +import gymnasium as gym +import torch +from rl.agent import PPOLightningAgent +from rl.utils import linear_annealing, make_env, parse_args, test +from torch.distributed.algorithms.join import Join +from torch.utils.data import BatchSampler, DistributedSampler, RandomSampler +from torchmetrics import MeanMetric + +from lightning.fabric import Fabric +from lightning.fabric.loggers import TensorBoardLogger +from lightning.fabric.plugins.collectives import TorchCollective +from lightning.fabric.plugins.collectives.collective import CollectibleGroup +from lightning.fabric.strategies import DDPStrategy + + +@torch.no_grad() +def player(args, world_collective: TorchCollective, player_trainer_collective: TorchCollective): + run_name = f"{args.env_id}_{args.exp_name}_{args.seed}" + logger = TensorBoardLogger( + root_dir=os.path.join("logs", "fabric_decoupled_logs", datetime.today().strftime("%Y-%m-%d_%H-%M-%S")), + name=run_name, + ) + log_dir = logger.log_dir + + # Initialize Fabric object + fabric = Fabric(loggers=logger, accelerator="cuda" if args.player_on_gpu else "cpu") + device = fabric.device + fabric.seed_everything(args.seed) + torch.backends.cudnn.deterministic = args.torch_deterministic + + # Log hyperparameters + logger.experiment.add_text( + "hyperparameters", + "|param|value|\n|-|-|\n%s" % ("\n".join([f"|{key}|{value}|" for key, value in vars(args).items()])), + ) + + # Environment setup + envs = gym.vector.SyncVectorEnv( + [make_env(args.env_id, args.seed + i, 0, args.capture_video, log_dir, "train") for i in range(args.num_envs)] + ) + assert isinstance(envs.single_action_space, gym.spaces.Discrete), "only discrete action space is supported" + + # Define the agent + agent: PPOLightningAgent = PPOLightningAgent( + envs, + act_fun=args.activation_function, + vf_coef=args.vf_coef, + ent_coef=args.ent_coef, + clip_coef=args.clip_coef, + clip_vloss=args.clip_vloss, + ortho_init=args.ortho_init, + normalize_advantages=args.normalize_advantages, + ).to(device) + flattened_parameters = torch.empty_like( + torch.nn.utils.convert_parameters.parameters_to_vector(agent.parameters()), device=device + ) + + # Receive the first weights from the rank-1, a.k.a. the first of the trainers + # In this way we are sure that before the first iteration everyone starts with the same parameters + player_trainer_collective.broadcast(flattened_parameters, src=1) + torch.nn.utils.convert_parameters.vector_to_parameters(flattened_parameters, agent.parameters()) + + # Player metrics + rew_avg = MeanMetric(sync_on_compute=False).to(device) + ep_len_avg = MeanMetric(sync_on_compute=False).to(device) + + # Local data + obs = torch.zeros((args.num_steps, args.num_envs) + envs.single_observation_space.shape).to(device) + actions = torch.zeros((args.num_steps, args.num_envs) + envs.single_action_space.shape).to(device) + logprobs = torch.zeros((args.num_steps, args.num_envs)).to(device) + rewards = torch.zeros((args.num_steps, args.num_envs)).to(device) + dones = torch.zeros((args.num_steps, args.num_envs)).to(device) + values = torch.zeros((args.num_steps, args.num_envs)).to(device) + + # Global variables + global_step = 0 + start_time = time.time() + single_global_step = int(args.num_envs * args.num_steps) + num_updates = args.total_timesteps // single_global_step + if not args.share_data: + if single_global_step < world_collective.world_size - 1: + raise RuntimeError( + "The number of trainers ({}) is greater than the available collected data ({}). ".format( + world_collective.world_size - 1, single_global_step + ) + + "Consider to lower the number of trainers at least to the size of available collected data" + ) + chunks_sizes = [ + len(chunk) + for chunk in torch.tensor_split(torch.arange(single_global_step), world_collective.world_size - 1) + ] + + # Broadcast num_updates to all the world + update_t = torch.tensor([num_updates], device=device, dtype=torch.float32) + world_collective.broadcast(update_t, src=0) + + # Get the first environment observation and start the optimization + next_obs = torch.tensor(envs.reset(seed=args.seed)[0], device=device) + next_done = torch.zeros(args.num_envs).to(device) + for _ in range(1, num_updates + 1): + for step in range(0, args.num_steps): + global_step += args.num_envs + obs[step] = next_obs + dones[step] = next_done + + # Sample an action given the observation received by the environment + action, logprob, _, value = agent.get_action_and_value(next_obs) + values[step] = value.flatten() + actions[step] = action + logprobs[step] = logprob + + # Single environment step + next_obs, reward, done, truncated, info = envs.step(action.cpu().numpy()) + done = torch.logical_or(torch.tensor(done), torch.tensor(truncated)) + rewards[step] = torch.tensor(reward, device=device).view(-1) + next_obs, next_done = torch.tensor(next_obs, device=device), done.to(device) + + if "final_info" in info: + for i, agent_final_info in enumerate(info["final_info"]): + if agent_final_info is not None and "episode" in agent_final_info: + fabric.print( + f"Rank-0: global_step={global_step}, reward_env_{i}={agent_final_info['episode']['r'][0]}" + ) + rew_avg(agent_final_info["episode"]["r"][0]) + ep_len_avg(agent_final_info["episode"]["l"][0]) + + # Sync the metrics + rew_avg_reduced = rew_avg.compute() + if not rew_avg_reduced.isnan(): + fabric.log("Rewards/rew_avg", rew_avg_reduced, global_step) + ep_len_avg_reduced = ep_len_avg.compute() + if not ep_len_avg_reduced.isnan(): + fabric.log("Game/ep_len_avg", ep_len_avg_reduced, global_step) + rew_avg.reset() + ep_len_avg.reset() + + # Estimate returns with GAE (https://arxiv.org/abs/1506.02438) + returns, advantages = agent.estimate_returns_and_advantages( + rewards, values, dones, next_obs, next_done, args.num_steps, args.gamma, args.gae_lambda + ) + + # Flatten the batch + local_data = { + "obs": obs.reshape((-1,) + envs.single_observation_space.shape), + "logprobs": logprobs.reshape(-1), + "actions": actions.reshape((-1,) + envs.single_action_space.shape), + "advantages": advantages.reshape(-1), + "returns": returns.reshape(-1), + "values": values.reshape(-1), + } + if not args.player_on_gpu and args.cuda: + for v in local_data.values(): + v = v.pin_memory() + + # Send data to the training agents + if args.share_data: + world_collective.broadcast_object_list([local_data], src=0) + else: + # Split data in an even way, when possible + perm = torch.randperm(single_global_step, device=device) + chunks = [{} for _ in range(world_collective.world_size - 1)] + for k, v in local_data.items(): + chunked_local_data = v[perm].split(chunks_sizes) + for i in range(len(chunks)): + chunks[i][k] = chunked_local_data[i] + + world_collective.scatter_object_list([None], [None] + chunks, src=0) + + # Gather metrics from the trainers to be plotted + metrics = [None] + player_trainer_collective.broadcast_object_list(metrics, src=1) + + # Wait the trainers to finish + player_trainer_collective.broadcast(flattened_parameters, src=1) + + # Convert back the parameters + torch.nn.utils.convert_parameters.vector_to_parameters(flattened_parameters, agent.parameters()) + + fabric.log_dict(metrics[0], global_step) + fabric.log_dict({"Time/step_per_second": int(global_step / (time.time() - start_time))}, global_step) + + if args.share_data: + world_collective.broadcast_object_list([-1], src=0) + else: + world_collective.scatter_object_list([None], [None] + [-1] * (world_collective.world_size - 1), src=0) + envs.close() + test(agent, device, fabric.logger.experiment, args) + + +def trainer( + args, + world_collective: TorchCollective, + player_trainer_collective: TorchCollective, + optimization_pg: CollectibleGroup, +): + global_rank = world_collective.rank + group_rank = global_rank - 1 + group_world_size = world_collective.world_size - 1 + + # Initialize Fabric + fabric = Fabric(strategy=DDPStrategy(process_group=optimization_pg), accelerator="cuda" if args.cuda else "cpu") + device = fabric.device + fabric.seed_everything(args.seed) + torch.backends.cudnn.deterministic = args.torch_deterministic + + # Environment setup + envs = gym.vector.SyncVectorEnv([make_env(args.env_id, 0, 0, False, None)]) + assert isinstance(envs.single_action_space, gym.spaces.Discrete), "only discrete action space is supported" + + # Define the agent and the optimizer and setup them with Fabric + agent: PPOLightningAgent = PPOLightningAgent( + envs, + act_fun=args.activation_function, + vf_coef=args.vf_coef, + ent_coef=args.ent_coef, + clip_coef=args.clip_coef, + clip_vloss=args.clip_vloss, + ortho_init=args.ortho_init, + normalize_advantages=args.normalize_advantages, + process_group=optimization_pg, + ) + optimizer = agent.configure_optimizers(args.learning_rate) + agent, optimizer = fabric.setup(agent, optimizer) + + # Send weights to rank-0, a.k.a. the player + if global_rank == 1: + player_trainer_collective.broadcast( + torch.nn.utils.convert_parameters.parameters_to_vector(agent.parameters()), src=1 + ) + + # Receive maximum number of updates from the player + update = 0 + num_updates = torch.zeros(1, device=device) + world_collective.broadcast(num_updates, src=0) + num_updates = num_updates.item() + + # Start training + while True: + # Wait for data + data = [None] + if args.share_data: + world_collective.broadcast_object_list(data, src=0) + else: + world_collective.scatter_object_list(data, [None for _ in range(world_collective.world_size)], src=0) + data = data[0] + if data == -1: + return + + # Metrics dict to be sent to the player + if group_rank == 0: + metrics = {} + + # Lerning rate annealing + if args.anneal_lr: + linear_annealing(optimizer, update, num_updates, args.learning_rate) + if group_rank == 0: + metrics["Info/learning_rate"] = optimizer.param_groups[0]["lr"] + update += 1 + + indexes = list(range(data["obs"].shape[0])) + if args.share_data: + sampler = DistributedSampler( + indexes, num_replicas=group_world_size, rank=group_rank, shuffle=True, seed=args.seed, drop_last=False + ) + else: + sampler = RandomSampler(indexes) + sampler = BatchSampler(sampler, batch_size=args.per_rank_batch_size, drop_last=False) + + # The Join context is needed because there can be the possibility + # that some ranks receive less data + with Join([agent._forward_module]) if not args.share_data else nullcontext(): + for epoch in range(args.update_epochs): + if args.share_data: + sampler.sampler.set_epoch(epoch) + for batch_idxes in sampler: + loss = agent.training_step({k: v[batch_idxes].to(device) for k, v in data.items()}) + optimizer.zero_grad(set_to_none=True) + fabric.backward(loss) + fabric.clip_gradients(agent, optimizer, max_norm=args.max_grad_norm) + optimizer.step() + + # Sync metrics + avg_pg_loss = agent.avg_pg_loss.compute() + avg_value_loss = agent.avg_value_loss.compute() + avg_ent_loss = agent.avg_ent_loss.compute() + agent.reset_metrics() + + # Send updated weights to the player + if global_rank == 1: + metrics["Loss/policy_loss"] = avg_pg_loss + metrics["Loss/value_loss"] = avg_value_loss + metrics["Loss/entropy_loss"] = avg_ent_loss + player_trainer_collective.broadcast_object_list( + [metrics], src=1 + ) # Broadcast metrics: fake send with object list between rank-0 and rank-1 + player_trainer_collective.broadcast( + torch.nn.utils.convert_parameters.parameters_to_vector(agent.parameters()), src=1 + ) + + +def main(args: argparse.Namespace): + world_collective = TorchCollective() + player_trainer_collective = TorchCollective() + world_collective.setup(backend="nccl" if args.player_on_gpu and args.cuda else "gloo") + + # Create a global group, assigning it to the collective: used by the player to exchange + # collected experiences with the trainers + world_collective.create_group() + global_rank = world_collective.rank + + # Create a group between rank-0 (player) and rank-1 (trainer), assigning it to the collective: + # used by rank-1 to send metrics to be tracked by the rank-0 at the end of a training episode + player_trainer_collective.create_group(ranks=[0, 1]) + + # Create a new group, without assigning it to the collective: in this way the trainers can + # still communicate with the player through the global group, but they can optimize the agent + # between themselves + optimization_pg = world_collective.new_group(ranks=list(range(1, world_collective.world_size))) + if global_rank == 0: + player(args, world_collective, player_trainer_collective) + else: + trainer(args, world_collective, player_trainer_collective, optimization_pg) + + +if __name__ == "__main__": + args = parse_args() + main(args) diff --git a/examples/fabric/reinforcement_learning/train_torch.py b/examples/fabric/reinforcement_learning/train_torch.py new file mode 100644 index 0000000..8974d30 --- /dev/null +++ b/examples/fabric/reinforcement_learning/train_torch.py @@ -0,0 +1,278 @@ +""" +Proximal Policy Optimization (PPO) - Accelerated with Lightning Fabric + +Author: Federico Belotti @belerico +Adapted from https://github.com/vwxyzjn/cleanrl/blob/master/cleanrl/ppo.py +Based on the paper: https://arxiv.org/abs/1707.06347 + +Requirements: +- gymnasium[box2d]>=0.27.1 +- moviepy +- lightning +- torchmetrics +- tensorboard + + +Run it with: + torchrun --nproc_per_node=2 --standalone train_torch.py +""" + +import argparse +import os +import random +import time +from datetime import datetime +from typing import Dict + +import gymnasium as gym +import torch +import torch.distributed as distributed +import torch.nn as nn +import torch.optim as optim +from rl.agent import PPOAgent +from rl.loss import entropy_loss, policy_loss, value_loss +from rl.utils import linear_annealing, make_env, parse_args, test +from torch import Tensor +from torch.nn.parallel import DistributedDataParallel +from torch.utils.data import BatchSampler, DistributedSampler, RandomSampler +from torch.utils.tensorboard import SummaryWriter + + +def train( + agent: PPOAgent, + optimizer: torch.optim.Optimizer, + data: Dict[str, Tensor], + logger: SummaryWriter, + global_step: int, + args: argparse.Namespace, +): + indexes = list(range(data["obs"].shape[0])) + if args.share_data: + sampler = DistributedSampler( + indexes, + num_replicas=distributed.get_world_size(), + rank=distributed.get_rank(), + shuffle=True, + seed=args.seed, + ) + else: + sampler = RandomSampler(indexes) + sampler = BatchSampler(sampler, batch_size=args.per_rank_batch_size, drop_last=False) + per_epoch_losses = torch.tensor([0.0, 0.0, 0.0], device=data["obs"].device) + for epoch in range(args.update_epochs): + if args.share_data: + sampler.sampler.set_epoch(epoch) + for batch_idxes in sampler: + _, newlogprob, entropy, newvalue = agent(data["obs"][batch_idxes], data["actions"].long()[batch_idxes]) + logratio = newlogprob - data["logprobs"][batch_idxes] + ratio = logratio.exp() + + advantages = data["advantages"][batch_idxes] + if args.normalize_advantages: + advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8) + + # Policy loss + pg_loss = policy_loss(advantages, ratio, args.clip_coef) + per_epoch_losses[0] += pg_loss.detach() + + # Value loss + v_loss = value_loss( + newvalue, + data["values"][batch_idxes], + data["returns"][batch_idxes], + args.clip_coef, + args.clip_vloss, + args.vf_coef, + ) + per_epoch_losses[1] += v_loss.detach() + + # Entropy loss + ent_loss = entropy_loss(entropy, args.ent_coef) + per_epoch_losses[2] += ent_loss.detach() + + # Overall loss + loss = pg_loss + ent_loss + v_loss + + optimizer.zero_grad(set_to_none=True) + loss.backward() + nn.utils.clip_grad_norm_(agent.parameters(), args.max_grad_norm) + optimizer.step() + + # Log + distributed.reduce(per_epoch_losses, dst=0) + if logger is not None: + per_epoch_losses = per_epoch_losses / (len(sampler) * distributed.get_world_size()) + logger.add_scalar("Loss/policy_loss", per_epoch_losses[0], global_step) + logger.add_scalar("Loss/value_loss", per_epoch_losses[1], global_step) + logger.add_scalar("Loss/entropy_loss", per_epoch_losses[2], global_step) + per_epoch_losses.fill_(0) + + +def main(args: argparse.Namespace): + # Init distributed environment + is_cuda_available = torch.cuda.is_available() and args.cuda + backend = "nccl" if is_cuda_available else "gloo" + local_rank = int(os.environ["LOCAL_RANK"]) + global_rank = int(os.environ["RANK"]) + world_size = int(os.environ["WORLD_SIZE"]) + if is_cuda_available: + torch.cuda.set_device(local_rank) + device = torch.device(f"cuda:{local_rank}" if is_cuda_available else "cpu") + distributed.init_process_group(backend=backend) + + # Seed everything + random.seed(args.seed) + torch.manual_seed(args.seed) + torch.cuda.manual_seed_all(args.seed) + torch.backends.cudnn.deterministic = args.torch_deterministic + + # Logger + log_dir = None + logger = None + run_name = f"{args.env_id}_{args.exp_name}_{args.seed}_{int(time.time())}" + if global_rank == 0: + log_dir = os.path.join("logs", "torch_logs", datetime.today().strftime("%Y-%m-%d_%H-%M-%S"), run_name) + logger = SummaryWriter(log_dir=log_dir) + + # Log hyperparameters + if global_rank == 0: + logger.add_text( + "hyperparameters", + "|param|value|\n|-|-|\n%s" % ("\n".join([f"|{key}|{value}|" for key, value in vars(args).items()])), + ) + + # Environment setup + envs = gym.vector.SyncVectorEnv( + [ + make_env( + args.env_id, + args.seed + global_rank * args.num_envs + i, + global_rank, + args.capture_video, + logger.log_dir if global_rank == 0 else None, + "train", + ) + for i in range(args.num_envs) + ] + ) + assert isinstance(envs.single_action_space, gym.spaces.Discrete), "only discrete action space is supported" + + # Define the agent and the optimizer and setup them with DistributedDataParallel + agent: PPOAgent = PPOAgent(envs, act_fun=args.activation_function, ortho_init=args.ortho_init).to(device) + agent = DistributedDataParallel( + agent, + device_ids=[local_rank] if is_cuda_available else None, + output_device=local_rank if is_cuda_available else None, + ) + optimizer = optim.Adam(agent.parameters(), lr=args.learning_rate, eps=1e-4) + + # Local data + obs = torch.zeros((args.num_steps, args.num_envs) + envs.single_observation_space.shape, device=device) + actions = torch.zeros((args.num_steps, args.num_envs) + envs.single_action_space.shape, device=device) + logprobs = torch.zeros((args.num_steps, args.num_envs), device=device) + rewards = torch.zeros((args.num_steps, args.num_envs), device=device) + dones = torch.zeros((args.num_steps, args.num_envs), device=device) + values = torch.zeros((args.num_steps, args.num_envs), device=device) + local_rew = 0.0 + local_ep_len = 0.0 + local_num_episodes = 0.0 + + # Global variables + global_step = 0 + start_time = time.time() + single_global_step = int(args.num_envs * args.num_steps * world_size) + num_updates = args.total_timesteps // single_global_step + + # Get the first environment observation and start the optimization + next_obs = torch.tensor(envs.reset(seed=args.seed)[0], device=device) + next_done = torch.zeros(args.num_envs, device=device) + for update in range(1, num_updates + 1): + # Learning rate annealing + if args.anneal_lr: + linear_annealing(optimizer, update, num_updates, args.learning_rate) + if global_rank == 0: + logger.add_scalar("Info/learning_rate", optimizer.param_groups[0]["lr"], global_step) + + for step in range(0, args.num_steps): + global_step += args.num_envs * world_size + obs[step] = next_obs + dones[step] = next_done + + # Sample an action given the observation received by the environment + with torch.no_grad(): + action, logprob, _, value = agent(next_obs) + values[step] = value.flatten() + actions[step] = action + logprobs[step] = logprob + + # Single environment step + next_obs, reward, done, truncated, info = envs.step(action.cpu().numpy()) + done = torch.logical_or(torch.tensor(done), torch.tensor(truncated)) + rewards[step] = torch.tensor(reward, device=device).view(-1) + next_obs, next_done = torch.tensor(next_obs, device=device), done.to(device) + + if "final_info" in info: + for i, agent_final_info in enumerate(info["final_info"]): + if agent_final_info is not None and "episode" in agent_final_info: + if global_rank == 0: + print( + f"Rank-0: global_step={global_step}, " + f"reward_env_{i}={agent_final_info['episode']['r'][0]}" + ) + local_num_episodes += 1 + local_rew += agent_final_info["episode"]["r"][0] + local_ep_len += agent_final_info["episode"]["l"][0] + + # Sync the metrics + global_stats = torch.tensor([local_rew, local_ep_len, local_num_episodes], device=device, dtype=torch.float32) + distributed.reduce(global_stats, dst=0) + if global_rank == 0 and global_stats[2] != 0.0: + logger.add_scalar("Rewards/rew_avg", global_stats[0] / global_stats[2], global_step) + logger.add_scalar("Game/ep_len_avg", global_stats[1] / global_stats[2], global_step) + + # Reset metrics + local_rew = 0 + local_ep_len = 0 + local_num_episodes = 0 + + # Estimate returns with GAE (https://arxiv.org/abs/1506.02438) + returns, advantages = agent.module.estimate_returns_and_advantages( + rewards, values, dones, next_obs, next_done, args.num_steps, args.gamma, args.gae_lambda + ) + + # Flatten the batch + local_data = { + "obs": obs.reshape((-1,) + envs.single_observation_space.shape), + "logprobs": logprobs.reshape(-1), + "actions": actions.reshape((-1,) + envs.single_action_space.shape), + "advantages": advantages.reshape(-1), + "returns": returns.reshape(-1), + "values": values.reshape(-1), + } + + if args.share_data: + # Gather all the tensors from all the world, concat and reshape them + gathered_data = [None for _ in range(world_size)] + distributed.all_gather_object(gathered_data, local_data) + processed_gathered_data = gathered_data[0] + for i in range(1, len(gathered_data)): + for k in processed_gathered_data: + processed_gathered_data[k] = torch.cat( + (processed_gathered_data[k].to(device), gathered_data[i][k].to(device)), dim=0 + ) + else: + processed_gathered_data = local_data + + # Train the agent + train(agent, optimizer, processed_gathered_data, logger, global_step, args) + if global_rank == 0: + logger.add_scalar("Time/step_per_second", int(global_step / (time.time() - start_time)), global_step) + + envs.close() + if global_rank == 0: + test(agent.module, device, logger, args) + + +if __name__ == "__main__": + args = parse_args() + main(args) diff --git a/examples/pytorch/basics/README.md b/examples/pytorch/basics/README.md new file mode 100644 index 0000000..0ef1933 --- /dev/null +++ b/examples/pytorch/basics/README.md @@ -0,0 +1,56 @@ +## Basic Examples + +Use these examples to test how Lightning works. + +### AutoEncoder + +This script shows you how to implement a CNN auto-encoder. + +```bash +# CPU +python autoencoder.py + +# GPUs (any number) +python autoencoder.py --trainer.accelerator 'gpu' --trainer.devices 2 + +# Distributed Data Parallel (DDP) +python autoencoder.py --trainer.accelerator 'gpu' --trainer.devices 2 --trainer.strategy 'ddp' +``` + +______________________________________________________________________ + +### Backbone Image Classifier + +This script shows you how to implement a `LightningModule` as a system. +A system describes a `LightningModule` which takes a single `torch.nn.Module` which makes exporting to producion simpler. + +```bash +# CPU +python backbone_image_classifier.py + +# GPUs (any number) +python backbone_image_classifier.py --trainer.accelerator 'gpu' --trainer.devices 2 + +# Distributed Data Parallel (DDP) +python backbone_image_classifier.py --trainer.accelerator 'gpu' --trainer.devices 2 --trainer.strategy 'ddp' +``` + +______________________________________________________________________ + +### Transformers + +This example contains a simple training loop for next-word prediction with a [Transformer model](https://arxiv.org/abs/1706.03762) on a subset of the [WikiText2](https://www.salesforce.com/products/einstein/ai-research/the-wikitext-dependency-language-modeling-dataset/) dataset. + +```bash +python transformer.py +``` + +______________________________________________________________________ + +### PyTorch Profiler + +This script shows you how to activate the [PyTorch Profiler](https://github.com/pytorch/kineto) with Lightning. + +```bash +python profiler_example.py +``` diff --git a/examples/pytorch/basics/autoencoder.py b/examples/pytorch/basics/autoencoder.py new file mode 100644 index 0000000..006397f --- /dev/null +++ b/examples/pytorch/basics/autoencoder.py @@ -0,0 +1,192 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""MNIST autoencoder example. + +To run: python autoencoder.py --trainer.max_epochs=50 +""" +from os import path +from typing import Optional, Tuple + +import torch +import torch.nn.functional as F +from torch import nn +from torch.utils.data import DataLoader, random_split + +from lightning.pytorch import callbacks, cli_lightning_logo, LightningDataModule, LightningModule, Trainer +from lightning.pytorch.cli import LightningCLI +from lightning.pytorch.demos.mnist_datamodule import MNIST +from lightning.pytorch.utilities import rank_zero_only +from lightning.pytorch.utilities.imports import _TORCHVISION_AVAILABLE + +if _TORCHVISION_AVAILABLE: + import torchvision + from torchvision import transforms + from torchvision.utils import save_image + +DATASETS_PATH = path.join(path.dirname(__file__), "..", "..", "Datasets") + + +class ImageSampler(callbacks.Callback): + def __init__( + self, + num_samples: int = 3, + nrow: int = 8, + padding: int = 2, + normalize: bool = True, + norm_range: Optional[Tuple[int, int]] = None, + scale_each: bool = False, + pad_value: int = 0, + ) -> None: + """ + Args: + num_samples: Number of images displayed in the grid. Default: ``3``. + nrow: Number of images displayed in each row of the grid. + The final grid size is ``(B / nrow, nrow)``. Default: ``8``. + padding: Amount of padding. Default: ``2``. + normalize: If ``True``, shift the image to the range (0, 1), + by the min and max values specified by :attr:`range`. Default: ``False``. + norm_range: Tuple (min, max) where min and max are numbers, + then these numbers are used to normalize the image. By default, min and max + are computed from the tensor. + scale_each: If ``True``, scale each image in the batch of + images separately rather than the (min, max) over all images. Default: ``False``. + pad_value: Value for the padded pixels. Default: ``0``. + """ + if not _TORCHVISION_AVAILABLE: # pragma: no cover + raise ModuleNotFoundError("You want to use `torchvision` which is not installed yet.") + + super().__init__() + self.num_samples = num_samples + self.nrow = nrow + self.padding = padding + self.normalize = normalize + self.norm_range = norm_range + self.scale_each = scale_each + self.pad_value = pad_value + + def _to_grid(self, images): + return torchvision.utils.make_grid( + tensor=images, + nrow=self.nrow, + padding=self.padding, + normalize=self.normalize, + value_range=self.norm_range, + scale_each=self.scale_each, + pad_value=self.pad_value, + ) + + @rank_zero_only + def on_train_epoch_end(self, trainer: Trainer, pl_module: LightningModule) -> None: + if not _TORCHVISION_AVAILABLE: + return + + images, _ = next(iter(DataLoader(trainer.datamodule.mnist_val, batch_size=self.num_samples))) + images_flattened = images.view(images.size(0), -1) + + # generate images + with torch.no_grad(): + pl_module.eval() + images_generated = pl_module(images_flattened.to(pl_module.device)) + pl_module.train() + + if trainer.current_epoch == 0: + save_image(self._to_grid(images), f"grid_ori_{trainer.current_epoch}.png") + save_image(self._to_grid(images_generated.reshape(images.shape)), f"grid_generated_{trainer.current_epoch}.png") + + +class LitAutoEncoder(LightningModule): + """ + >>> LitAutoEncoder() # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + LitAutoEncoder( + (encoder): ... + (decoder): ... + ) + """ + + def __init__(self, hidden_dim: int = 64, learning_rate=10e-3): + super().__init__() + self.save_hyperparameters() + self.encoder = nn.Sequential(nn.Linear(28 * 28, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, 3)) + self.decoder = nn.Sequential(nn.Linear(3, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, 28 * 28)) + + def forward(self, x): + z = self.encoder(x) + return self.decoder(z) + + def training_step(self, batch, batch_idx): + return self._common_step(batch, batch_idx, "train") + + def validation_step(self, batch, batch_idx): + self._common_step(batch, batch_idx, "val") + + def test_step(self, batch, batch_idx): + self._common_step(batch, batch_idx, "test") + + def predict_step(self, batch, batch_idx, dataloader_idx=None): + x = self._prepare_batch(batch) + return self(x) + + def configure_optimizers(self): + return torch.optim.Adam(self.parameters(), lr=self.hparams.learning_rate) + + def _prepare_batch(self, batch): + x, _ = batch + return x.view(x.size(0), -1) + + def _common_step(self, batch, batch_idx, stage: str): + x = self._prepare_batch(batch) + loss = F.mse_loss(x, self(x)) + self.log(f"{stage}_loss", loss, on_step=True) + return loss + + +class MyDataModule(LightningDataModule): + def __init__(self, batch_size: int = 32): + super().__init__() + dataset = MNIST(DATASETS_PATH, train=True, download=True, transform=transforms.ToTensor()) + self.mnist_test = MNIST(DATASETS_PATH, train=False, download=True, transform=transforms.ToTensor()) + self.mnist_train, self.mnist_val = random_split(dataset, [55000, 5000]) + self.batch_size = batch_size + + def train_dataloader(self): + return DataLoader(self.mnist_train, batch_size=self.batch_size) + + def val_dataloader(self): + return DataLoader(self.mnist_val, batch_size=self.batch_size) + + def test_dataloader(self): + return DataLoader(self.mnist_test, batch_size=self.batch_size) + + def predict_dataloader(self): + return DataLoader(self.mnist_test, batch_size=self.batch_size) + + +def cli_main(): + cli = LightningCLI( + LitAutoEncoder, + MyDataModule, + seed_everything_default=1234, + run=False, # used to de-activate automatic fitting. + trainer_defaults={"callbacks": ImageSampler(), "max_epochs": 10}, + save_config_kwargs={"overwrite": True}, + ) + cli.trainer.fit(cli.model, datamodule=cli.datamodule) + cli.trainer.test(ckpt_path="best", datamodule=cli.datamodule) + predictions = cli.trainer.predict(ckpt_path="best", datamodule=cli.datamodule) + print(predictions[0]) + + +if __name__ == "__main__": + cli_lightning_logo() + cli_main() diff --git a/examples/pytorch/basics/backbone_image_classifier.py b/examples/pytorch/basics/backbone_image_classifier.py new file mode 100644 index 0000000..f293a0a --- /dev/null +++ b/examples/pytorch/basics/backbone_image_classifier.py @@ -0,0 +1,137 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""MNIST backbone image classifier example. + +To run: python backbone_image_classifier.py --trainer.max_epochs=50 +""" +from os import path +from typing import Optional + +import torch +from torch.nn import functional as F +from torch.utils.data import DataLoader, random_split + +from lightning.pytorch import cli_lightning_logo, LightningDataModule, LightningModule +from lightning.pytorch.cli import LightningCLI +from lightning.pytorch.demos.mnist_datamodule import MNIST +from lightning.pytorch.utilities.imports import _TORCHVISION_AVAILABLE + +if _TORCHVISION_AVAILABLE: + from torchvision import transforms + +DATASETS_PATH = path.join(path.dirname(__file__), "..", "..", "Datasets") + + +class Backbone(torch.nn.Module): + """ + >>> Backbone() # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Backbone( + (l1): Linear(...) + (l2): Linear(...) + ) + """ + + def __init__(self, hidden_dim=128): + super().__init__() + self.l1 = torch.nn.Linear(28 * 28, hidden_dim) + self.l2 = torch.nn.Linear(hidden_dim, 10) + + def forward(self, x): + x = x.view(x.size(0), -1) + x = torch.relu(self.l1(x)) + x = torch.relu(self.l2(x)) + return x + + +class LitClassifier(LightningModule): + """ + >>> LitClassifier(Backbone()) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + LitClassifier( + (backbone): ... + ) + """ + + def __init__(self, backbone: Optional[Backbone] = None, learning_rate: float = 0.0001): + super().__init__() + self.save_hyperparameters(ignore=["backbone"]) + if backbone is None: + backbone = Backbone() + self.backbone = backbone + + def forward(self, x): + # use forward for inference/predictions + return self.backbone(x) + + def training_step(self, batch, batch_idx): + x, y = batch + y_hat = self(x) + loss = F.cross_entropy(y_hat, y) + self.log("train_loss", loss, on_epoch=True) + return loss + + def validation_step(self, batch, batch_idx): + x, y = batch + y_hat = self(x) + loss = F.cross_entropy(y_hat, y) + self.log("valid_loss", loss, on_step=True) + + def test_step(self, batch, batch_idx): + x, y = batch + y_hat = self(x) + loss = F.cross_entropy(y_hat, y) + self.log("test_loss", loss) + + def predict_step(self, batch, batch_idx, dataloader_idx=None): + x, y = batch + return self(x) + + def configure_optimizers(self): + # self.hparams available because we called self.save_hyperparameters() + return torch.optim.Adam(self.parameters(), lr=self.hparams.learning_rate) + + +class MyDataModule(LightningDataModule): + def __init__(self, batch_size: int = 32): + super().__init__() + dataset = MNIST(DATASETS_PATH, train=True, download=True, transform=transforms.ToTensor()) + self.mnist_test = MNIST(DATASETS_PATH, train=False, download=True, transform=transforms.ToTensor()) + self.mnist_train, self.mnist_val = random_split(dataset, [55000, 5000]) + self.batch_size = batch_size + + def train_dataloader(self): + return DataLoader(self.mnist_train, batch_size=self.batch_size) + + def val_dataloader(self): + return DataLoader(self.mnist_val, batch_size=self.batch_size) + + def test_dataloader(self): + return DataLoader(self.mnist_test, batch_size=self.batch_size) + + def predict_dataloader(self): + return DataLoader(self.mnist_test, batch_size=self.batch_size) + + +def cli_main(): + cli = LightningCLI( + LitClassifier, MyDataModule, seed_everything_default=1234, save_config_kwargs={"overwrite": True}, run=False + ) + cli.trainer.fit(cli.model, datamodule=cli.datamodule) + cli.trainer.test(ckpt_path="best", datamodule=cli.datamodule) + predictions = cli.trainer.predict(ckpt_path="best", datamodule=cli.datamodule) + print(predictions[0]) + + +if __name__ == "__main__": + cli_lightning_logo() + cli_main() diff --git a/examples/pytorch/basics/profiler_example.py b/examples/pytorch/basics/profiler_example.py new file mode 100644 index 0000000..0c429d2 --- /dev/null +++ b/examples/pytorch/basics/profiler_example.py @@ -0,0 +1,113 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""This script will generate 2 traces: one for `training_step` and one for `validation_step`. The traces can be +visualized in 2 ways: + +* With Chrome: + 1. Open Chrome and copy/paste this url: `chrome://tracing/`. + 2. Once tracing opens, click on `Load` at the top-right and load one of the generated traces. +* With PyTorch Tensorboard Profiler (Instructions are here: https://github.com/pytorch/kineto/tree/master/tb_plugin) + 1. pip install tensorboard torch-tb-profiler + 2. tensorboard --logdir={FOLDER} +""" + +from os import path + +import torch +import torchvision +import torchvision.transforms as T + +from lightning.pytorch import cli_lightning_logo, LightningDataModule, LightningModule +from lightning.pytorch.cli import LightningCLI +from lightning.pytorch.profilers.pytorch import PyTorchProfiler +from lightning.pytorch.utilities.model_helpers import get_torchvision_model + +DATASETS_PATH = path.join(path.dirname(__file__), "..", "..", "Datasets") + + +class ModelToProfile(LightningModule): + def __init__(self, name: str = "resnet18", automatic_optimization: bool = True): + super().__init__() + self.model = get_torchvision_model(name, weights="DEFAULT") + self.criterion = torch.nn.CrossEntropyLoss() + self.automatic_optimization = automatic_optimization + self.training_step = ( + self.automatic_optimization_training_step + if automatic_optimization + else self.manual_optimization_training_step + ) + + def automatic_optimization_training_step(self, batch, batch_idx): + inputs, labels = batch + outputs = self.model(inputs) + loss = self.criterion(outputs, labels) + self.log("train_loss", loss) + return loss + + def manual_optimization_training_step(self, batch, batch_idx): + opt = self.optimizers() + opt.zero_grad() + inputs, labels = batch + outputs = self.model(inputs) + loss = self.criterion(outputs, labels) + self.log("train_loss", loss) + self.manual_backward(loss) + opt.step() + + def validation_step(self, batch, batch_idx): + inputs, labels = batch + outputs = self.model(inputs) + loss = self.criterion(outputs, labels) + self.log("val_loss", loss) + + def predict_step(self, batch, batch_idx, dataloader_idx: int = None): + inputs = batch[0] + return self.model(inputs) + + def configure_optimizers(self): + return torch.optim.SGD(self.parameters(), lr=0.001, momentum=0.9) + + +class CIFAR10DataModule(LightningDataModule): + transform = T.Compose([T.Resize(256), T.CenterCrop(224), T.ToTensor()]) + + def train_dataloader(self, *args, **kwargs): + trainset = torchvision.datasets.CIFAR10(root=DATASETS_PATH, train=True, download=True, transform=self.transform) + return torch.utils.data.DataLoader(trainset, batch_size=2, shuffle=True, num_workers=0) + + def val_dataloader(self, *args, **kwargs): + valset = torchvision.datasets.CIFAR10(root=DATASETS_PATH, train=False, download=True, transform=self.transform) + return torch.utils.data.DataLoader(valset, batch_size=2, shuffle=True, num_workers=0) + + +def cli_main(): + cli = LightningCLI( + ModelToProfile, + CIFAR10DataModule, + save_config_kwargs={"overwrite": True}, + trainer_defaults={ + "profiler": PyTorchProfiler(), + "max_epochs": 1, + "limit_train_batches": 15, + "limit_val_batches": 15, + "accelerator": "gpu", + }, + run=False, + ) + cli.trainer.fit(cli.model, datamodule=cli.datamodule) + + +if __name__ == "__main__": + cli_lightning_logo() + cli_main() diff --git a/examples/pytorch/basics/transformer.py b/examples/pytorch/basics/transformer.py new file mode 100644 index 0000000..dbd990d --- /dev/null +++ b/examples/pytorch/basics/transformer.py @@ -0,0 +1,62 @@ +import torch +import torch.nn.functional as F +from torch.utils.data import DataLoader, random_split + +import lightning as L +from lightning.pytorch.demos import Transformer, WikiText2 + + +class LanguageModel(L.LightningModule): + def __init__(self, vocab_size): + super().__init__() + self.model = Transformer(vocab_size=vocab_size) + + def training_step(self, batch, batch_idx): + input, target = batch + output = self.model(input, target) + loss = F.nll_loss(output, target.view(-1)) + self.log("train_loss", loss, prog_bar=True) + return loss + + def validation_step(self, batch, batch_idx): + input, target = batch + output = self.model(input, target) + loss = F.nll_loss(output, target.view(-1)) + self.log("val_loss", loss, prog_bar=True) + return loss + + def test_step(self, batch, batch_idx): + input, target = batch + output = self.model(input, target) + loss = F.nll_loss(output, target.view(-1)) + self.log("test_loss", loss, prog_bar=True) + return loss + + def configure_optimizers(self): + return torch.optim.SGD(self.parameters(), lr=0.1) + + +def main(): + L.seed_everything(42) + + # Data + dataset = WikiText2() + + # Split data in to train, val, test + n = len(dataset) + train_dataset, val_dataset, test_dataset = random_split(dataset, [n - 4000, 2000, 2000]) + train_dataloader = DataLoader(train_dataset, batch_size=20, shuffle=True) + val_dataloader = DataLoader(val_dataset, batch_size=20, shuffle=False) + test_dataloader = DataLoader(test_dataset, batch_size=20, shuffle=False) + + # Model + model = LanguageModel(vocab_size=dataset.vocab_size) + + # Trainer + trainer = L.Trainer(gradient_clip_val=0.25, max_epochs=20) + trainer.fit(model, train_dataloader, val_dataloader) + trainer.test(model, test_dataloader) + + +if __name__ == "__main__": + main() diff --git a/examples/pytorch/bug_report/bug_report_model.ipynb b/examples/pytorch/bug_report/bug_report_model.ipynb new file mode 100644 index 0000000..dababd0 --- /dev/null +++ b/examples/pytorch/bug_report/bug_report_model.ipynb @@ -0,0 +1,267 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "bug_report_model.ipynb", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "rR4_BAUYs3Mb" + }, + "source": [ + "![image.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQYAAABSCAYAAAC2XXppAAAaWElEQVR4Ae2dh3dU1RbG3x/i0mXvoighgKgI2BtW7KKiSAcFFVBQmgqKil2UJzYUQXl2ERSRpoiIIJhMOiEQkpCQRup+63eSHS73zkxmkmFC2Wetm5m55ZTv7P3tcs5M/iNWDAFDwBDwIfAf32f7aAgYAoaAGDGYEBgChkAAASOGACR2whAwBIwYTAYMAUMggIARQwASO2EIGAJGDCYDhoAhEEDAiCEAiZ0wBAwBIwaTAUPAEAggYMQQgMROGAKGgBGDyYAhYAgEEDBiCEBiJwwBQ8CIwWTAEDAEAggYMQQgsROGgCFgxGAyYAgYAgEEjBgCkNgJQ8AQMGIwGTAEDIEAAkYMAUjshCFgCBgxmAwYAoZAAAEjhgAkdsIQMASMGEwGDAFDIIBAhxJDXW2j7NldL6WFdVJb0xjonJ0wBAyBjkGgQ4ihoUGkvLhONi6vlLlPFMg7j++Qv5aVS0VZvdTXG0F0jChYq4bAPgSSSwyNIpV7GiTzrypZMKNIJvXLlhHdQzKye0ie6pcjC57bKRkbqqW6vF4ajR/2zZK9MwSSjEDSiGFvVaMUZNbI93NL5Lk7cmRUz5AMS0mX4Rxdm47RF2TIs3fmynfv7pYdmTWyt7ohyXBYc4aAIQACB5wYyCMUFdTJmsVlMntIvoy9OFOGdQ3JsNR0GdEttO9IDbUQxNjemfLqsHxZ9UWZ7C6slTrLP5i0GgJJReCAEQOhQNmuOvn7l0r574QCGX95lgyHELqmywg/KXgJovme4d1CMv6KbJk7YYdsWlHhkpQN9UnFxhozBI5YBBJPDJpH2FgtC2cVydM3ZMuIbhkyrEu6DI9GCF5y6NbkPUAio3pkyuQbc2TRrELJ3FQtVXuMHY5YabWBJw2BhBJDTVWjyw38MK9EZt6dJ6MvyGzyEuIghHDhBbmIhy/MlJn35MmS9y3/kDTpsIaOWAQSQgyN9SK7ttXK2q/L5PWR22Vsb2/Y4Mkj+LyC/Ugg2rXUphBkeGpIHuuT5dqgLfIP9bW2fHHESq8N/IAh0C5iII+wp7hetv5WKe9P2ikTroIQMppWG+L1ElD+1FZIpDn/MKpbSJ68Ols+eHqnbF5VKRWl9cLeCCuGgCGQGATaRQx7iurkqzdKZHr/HBnZo42EgKfQTApjLsqQUT0z9q1UhPMimlcvyD+M7pkh027JlR/nlUhZUV1iELFaDAFDoO3LlY0NIqEN1fJwrwjLj+GUOtw5FyaE5ImrsmXuuO0yplemEDK0GmZAECnpbpVjWv8c2fp7pU2nIWAIJAiBNnsMuO5b11bK0C4sP8agyFFIYcLVWfLd3BJZvqC0dULw15MSkonX5sjmXysSBIlVYwgYAu0jht8qY7PufmXmc2pIhqaky8TrcmTVwlIp3VUvC2YUykPn+DY+hXvWey61aTv15pWxEUNjY6Ps2bNH/v77b9mwYUPg4HxmZqaUlpbGLR01NTWSnZ0dqDNcO3ruzz//lFAoJBUVsfU/7k7F8ABj3bx5s6xfv162b98udXVtD8tyc3Pl999/d0dxcbGAd7RC27T722+/SX5+vjS0I1mUk5Pj6vnjjz86FE/w++uvv1xftm7dGm34B+21jiEGwocu6TKpX46sXFQqe6sbHTHMGrhNhpx3YImhvr5e1q1bJ7fddptce+217rj66qvlqquucsf1118vDz30kLz55pvyzz//SG1tbcyTh1LNmDFDrrzySqFOPWjnuuuuc8c111zTcp7rl19+uYwdO1Y2bdoUczuJvnHt2rUycOBAueKKK+T9999vEylqn+bMmeNwvOyyy2TFihWt4sdcgHmvXr1c21VVVVpV3K+vv/660C54o5CtkZK3AQhq586dcT3jfd77HsODfF188cXyyCOPeC8dMu+TTwyQQkq6TLo+R1YvLnOkQL4i79+9Mu7SrLg2Qbk8RJweA2yOwJ5//vnSqVMnOeecc+Tcc8+V8847z71y7owzzpAuXbrI4MGDBaWJ1Yrl5eXJ448/LmeffbZ07ty55aANDs7z6r125plnyr333uusZkdJzc8//+wU6rTTTpNXXnlFsPRtLS+88IIb31lnnSVLliwRvKhoZdWqVXLBBRfIKaecIm+88YZUVrY9VzR9+nShXeYSzy/WecO6z549W1577bVW+xttLHqtrKxMLrnkEjn11FPd3Or5Q+k1ucTQHD48dX2OrFpcJjXVTW4m34X44/ty912JuPMVbSQGhBEBuvHGG2XmzJnueO655+SJJ56Q/v37O+GGMEaMGCHbtm2LaU53794tixcvFgT02WeflWeeecYdDzzwgHTr1s21d8cdd8iUKVNark2dOlU++OADwQXvqAIx4OWc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)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "i7XbLCXGkll9" + }, + "source": [ + "# The Boring Model\n", + "Replicate a bug you experience, using this model.\n", + "\n", + "[Remember! we're always available for support on Slack](https://www.pytorchlightning.ai/community)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2LODD6w9ixlT" + }, + "source": [ + "---\n", + "## Setup env" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "zK7-Gg69kMnG" + }, + "source": [ + "%%capture\n", + "! pip install -qU pytorch-lightning" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WvuSN5jEbY8P" + }, + "source": [ + "---\n", + "## Deps" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "w4_TYnt_keJi" + }, + "source": [ + "import os\n", + "\n", + "import torch\n", + "from torch.utils.data import DataLoader, Dataset\n", + "\n", + "from pytorch_lightning import LightningModule, Trainer" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XrJDukwPtUnS" + }, + "source": [ + "---\n", + "## Data\n", + "Random data is best for debugging. If you needs special tensor shapes or batch compositions or dataloaders, modify as needed" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "hvgTiaZpkvwS" + }, + "source": [ + "class RandomDataset(Dataset):\n", + " def __init__(self, size, num_samples):\n", + " self.len = num_samples\n", + " self.data = torch.randn(num_samples, size)\n", + "\n", + " def __getitem__(self, index):\n", + " return self.data[index]\n", + "\n", + " def __len__(self):\n", + " return self.len" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "sxVlWjGhl02D" + }, + "source": [ + "num_samples = 10000" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "V7ELesz1kVQo" + }, + "source": [ + "class BoringModel(LightningModule):\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.layer = torch.nn.Linear(32, 2)\n", + "\n", + " def forward(self, x):\n", + " return self.layer(x)\n", + "\n", + " def training_step(self, batch, batch_idx):\n", + " loss = self(batch).sum()\n", + " self.log(\"train_loss\", loss)\n", + " return {\"loss\": loss}\n", + "\n", + " def validation_step(self, batch, batch_idx):\n", + " loss = self(batch).sum()\n", + " self.log(\"valid_loss\", loss)\n", + "\n", + " def test_step(self, batch, batch_idx):\n", + " loss = self(batch).sum()\n", + " self.log(\"test_loss\", loss)\n", + "\n", + " def configure_optimizers(self):\n", + " return torch.optim.SGD(self.layer.parameters(), lr=0.1)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ubvW3LGSupmt" + }, + "source": [ + "---\n", + "## Define the test" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "4Dk6Ykv8lI7X" + }, + "source": [ + "def run():\n", + " train_data = DataLoader(RandomDataset(32, 64), batch_size=2)\n", + " val_data = DataLoader(RandomDataset(32, 64), batch_size=2)\n", + " test_data = DataLoader(RandomDataset(32, 64), batch_size=2)\n", + "\n", + " model = BoringModel()\n", + " trainer = Trainer(\n", + " default_root_dir=os.getcwd(),\n", + " limit_train_batches=1,\n", + " limit_val_batches=1,\n", + " limit_test_batches=1,\n", + " num_sanity_val_steps=0,\n", + " max_epochs=1,\n", + " enable_model_summary=False,\n", + " )\n", + " trainer.fit(model, train_dataloaders=train_data, val_dataloaders=val_data)\n", + " trainer.test(model, dataloaders=test_data)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4dPfTZVgmgxz" + }, + "source": [ + "---\n", + "## Run Test" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "AAtq1hwSmjKe" + }, + "source": [ + "run()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Flyi--SpvsJN" + }, + "source": [ + "---\n", + "## Environment\n", + "Run this to get the environment details" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "0-yvGFRoaDSi" + }, + "source": [ + "%%capture\n", + "! wget https://raw.githubusercontent.com/Lightning-AI/lightning/master/requirements/collect_env_details.py" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "quj4LUDgmFvj" + }, + "source": [ + "! python collect_env_details.py" + ], + "execution_count": null, + "outputs": [] + } + ] +} diff --git a/examples/pytorch/bug_report/bug_report_model.py b/examples/pytorch/bug_report/bug_report_model.py new file mode 100644 index 0000000..551ea21 --- /dev/null +++ b/examples/pytorch/bug_report/bug_report_model.py @@ -0,0 +1,66 @@ +import os + +import torch +from torch.utils.data import DataLoader, Dataset + +from lightning.pytorch import LightningModule, Trainer + + +class RandomDataset(Dataset): + def __init__(self, size, length): + self.len = length + self.data = torch.randn(length, size) + + def __getitem__(self, index): + return self.data[index] + + def __len__(self): + return self.len + + +class BoringModel(LightningModule): + def __init__(self): + super().__init__() + self.layer = torch.nn.Linear(32, 2) + + def forward(self, x): + return self.layer(x) + + def training_step(self, batch, batch_idx): + loss = self(batch).sum() + self.log("train_loss", loss) + return {"loss": loss} + + def validation_step(self, batch, batch_idx): + loss = self(batch).sum() + self.log("valid_loss", loss) + + def test_step(self, batch, batch_idx): + loss = self(batch).sum() + self.log("test_loss", loss) + + def configure_optimizers(self): + return torch.optim.SGD(self.layer.parameters(), lr=0.1) + + +def run(): + train_data = DataLoader(RandomDataset(32, 64), batch_size=2) + val_data = DataLoader(RandomDataset(32, 64), batch_size=2) + test_data = DataLoader(RandomDataset(32, 64), batch_size=2) + + model = BoringModel() + trainer = Trainer( + default_root_dir=os.getcwd(), + limit_train_batches=1, + limit_val_batches=1, + limit_test_batches=1, + num_sanity_val_steps=0, + max_epochs=1, + enable_model_summary=False, + ) + trainer.fit(model, train_dataloaders=train_data, val_dataloaders=val_data) + trainer.test(model, dataloaders=test_data) + + +if __name__ == "__main__": + run() diff --git a/examples/pytorch/domain_templates/computer_vision_fine_tuning.py b/examples/pytorch/domain_templates/computer_vision_fine_tuning.py new file mode 100644 index 0000000..c7e492e --- /dev/null +++ b/examples/pytorch/domain_templates/computer_vision_fine_tuning.py @@ -0,0 +1,286 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Computer vision example on Transfer Learning. This computer vision example illustrates how one could fine-tune a +pre-trained network (by default, a ResNet50 is used) using pytorch-lightning. For the sake of this example, the +'cats and dogs dataset' (~60MB, see `DATA_URL` below) and the proposed network (denoted by `TransferLearningModel`, +see below) is trained for 15 epochs. + +The training consists of three stages. + +From epoch 0 to 4, the feature extractor (the pre-trained network) is frozen except +maybe for the BatchNorm layers (depending on whether `train_bn = True`). The BatchNorm +layers (if `train_bn = True`) and the parameters of the classifier are trained as a +single parameters group with lr = 1e-2. + +From epoch 5 to 9, the last two layer groups of the pre-trained network are unfrozen +and added to the optimizer as a new parameter group with lr = 1e-4 (while lr = 1e-3 +for the first parameter group in the optimizer). + +Eventually, from epoch 10, all the remaining layer groups of the pre-trained network +are unfrozen and added to the optimizer as a third parameter group. From epoch 10, +the parameters of the pre-trained network are trained with lr = 1e-5 while those of +the classifier is trained with lr = 1e-4. + +Note: + See: https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html + +To run: + python computer_vision_fine_tuning.py fit +""" + +import logging +from pathlib import Path +from typing import Union + +import torch +import torch.nn.functional as F +from torch import nn, optim +from torch.optim.lr_scheduler import MultiStepLR +from torch.optim.optimizer import Optimizer +from torch.utils.data import DataLoader +from torchmetrics import Accuracy +from torchvision import transforms +from torchvision.datasets import ImageFolder +from torchvision.datasets.utils import download_and_extract_archive + +from lightning.pytorch import cli_lightning_logo, LightningDataModule, LightningModule +from lightning.pytorch.callbacks.finetuning import BaseFinetuning +from lightning.pytorch.cli import LightningCLI +from lightning.pytorch.utilities import rank_zero_info +from lightning.pytorch.utilities.model_helpers import get_torchvision_model + +log = logging.getLogger(__name__) +DATA_URL = "https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip" + +# --- Finetuning Callback --- + + +class MilestonesFinetuning(BaseFinetuning): + def __init__(self, milestones: tuple = (5, 10), train_bn: bool = False): + super().__init__() + self.milestones = milestones + self.train_bn = train_bn + + def freeze_before_training(self, pl_module: LightningModule): + self.freeze(modules=pl_module.feature_extractor, train_bn=self.train_bn) + + def finetune_function(self, pl_module: LightningModule, epoch: int, optimizer: Optimizer): + if epoch == self.milestones[0]: + # unfreeze 5 last layers + self.unfreeze_and_add_param_group( + modules=pl_module.feature_extractor[-5:], optimizer=optimizer, train_bn=self.train_bn + ) + + elif epoch == self.milestones[1]: + # unfreeze remaining layers + self.unfreeze_and_add_param_group( + modules=pl_module.feature_extractor[:-5], optimizer=optimizer, train_bn=self.train_bn + ) + + +class CatDogImageDataModule(LightningDataModule): + def __init__(self, dl_path: Union[str, Path] = "data", num_workers: int = 0, batch_size: int = 8): + """CatDogImageDataModule. + + Args: + dl_path: root directory where to download the data + num_workers: number of CPU workers + batch_size: number of sample in a batch + """ + super().__init__() + + self._dl_path = dl_path + self._num_workers = num_workers + self._batch_size = batch_size + + def prepare_data(self): + """Download images and prepare images datasets.""" + download_and_extract_archive(url=DATA_URL, download_root=self._dl_path, remove_finished=True) + + @property + def data_path(self): + return Path(self._dl_path).joinpath("cats_and_dogs_filtered") + + @property + def normalize_transform(self): + return transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + + @property + def train_transform(self): + return transforms.Compose( + [ + transforms.Resize((224, 224)), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + self.normalize_transform, + ] + ) + + @property + def valid_transform(self): + return transforms.Compose([transforms.Resize((224, 224)), transforms.ToTensor(), self.normalize_transform]) + + def create_dataset(self, root, transform): + return ImageFolder(root=root, transform=transform) + + def __dataloader(self, train: bool): + """Train/validation loaders.""" + if train: + dataset = self.create_dataset(self.data_path.joinpath("train"), self.train_transform) + else: + dataset = self.create_dataset(self.data_path.joinpath("validation"), self.valid_transform) + return DataLoader(dataset=dataset, batch_size=self._batch_size, num_workers=self._num_workers, shuffle=train) + + def train_dataloader(self): + log.info("Training data loaded.") + return self.__dataloader(train=True) + + def val_dataloader(self): + log.info("Validation data loaded.") + return self.__dataloader(train=False) + + +# --- PyTorch Lightning module --- + + +class TransferLearningModel(LightningModule): + def __init__( + self, + backbone: str = "resnet50", + train_bn: bool = False, + milestones: tuple = (2, 4), + batch_size: int = 32, + lr: float = 1e-3, + lr_scheduler_gamma: float = 1e-1, + num_workers: int = 6, + **kwargs, + ) -> None: + """TransferLearningModel. + + Args: + backbone: Name (as in ``torchvision.models``) of the feature extractor + train_bn: Whether the BatchNorm layers should be trainable + milestones: List of two epochs milestones + lr: Initial learning rate + lr_scheduler_gamma: Factor by which the learning rate is reduced at each milestone + """ + super().__init__() + self.backbone = backbone + self.train_bn = train_bn + self.milestones = milestones + self.batch_size = batch_size + self.lr = lr + self.lr_scheduler_gamma = lr_scheduler_gamma + self.num_workers = num_workers + + self.__build_model() + + self.train_acc = Accuracy(task="binary") + self.valid_acc = Accuracy(task="binary") + self.save_hyperparameters() + + def __build_model(self): + """Define model layers & loss.""" + # 1. Load pre-trained network: + backbone = get_torchvision_model(self.backbone, weights="DEFAULT") + + _layers = list(backbone.children())[:-1] + self.feature_extractor = nn.Sequential(*_layers) + + # 2. Classifier: + _fc_layers = [nn.Linear(2048, 256), nn.ReLU(), nn.Linear(256, 32), nn.Linear(32, 1)] + self.fc = nn.Sequential(*_fc_layers) + + # 3. Loss: + self.loss_func = F.binary_cross_entropy_with_logits + + def forward(self, x): + """Forward pass. + + Returns logits. + """ + # 1. Feature extraction: + x = self.feature_extractor(x) + x = x.squeeze(-1).squeeze(-1) + + # 2. Classifier (returns logits): + x = self.fc(x) + + return x + + def loss(self, logits, labels): + return self.loss_func(input=logits, target=labels) + + def training_step(self, batch, batch_idx): + # 1. Forward pass: + x, y = batch + y_logits = self.forward(x) + y_scores = torch.sigmoid(y_logits) + y_true = y.view((-1, 1)).type_as(x) + + # 2. Compute loss + train_loss = self.loss(y_logits, y_true) + + # 3. Compute accuracy: + self.log("train_acc", self.train_acc(y_scores, y_true.int()), prog_bar=True) + + return train_loss + + def validation_step(self, batch, batch_idx): + # 1. Forward pass: + x, y = batch + y_logits = self.forward(x) + y_scores = torch.sigmoid(y_logits) + y_true = y.view((-1, 1)).type_as(x) + + # 2. Compute loss + self.log("val_loss", self.loss(y_logits, y_true), prog_bar=True) + + # 3. Compute accuracy: + self.log("val_acc", self.valid_acc(y_scores, y_true.int()), prog_bar=True) + + def configure_optimizers(self): + parameters = list(self.parameters()) + trainable_parameters = list(filter(lambda p: p.requires_grad, parameters)) + rank_zero_info( + f"The model will start training with only {len(trainable_parameters)} " + f"trainable parameters out of {len(parameters)}." + ) + optimizer = optim.Adam(trainable_parameters, lr=self.lr) + scheduler = MultiStepLR(optimizer, milestones=self.milestones, gamma=self.lr_scheduler_gamma) + return [optimizer], [scheduler] + + +class MyLightningCLI(LightningCLI): + def add_arguments_to_parser(self, parser): + parser.add_lightning_class_args(MilestonesFinetuning, "finetuning") + parser.link_arguments("data.batch_size", "model.batch_size") + parser.link_arguments("finetuning.milestones", "model.milestones") + parser.link_arguments("finetuning.train_bn", "model.train_bn") + parser.set_defaults( + { + "trainer.max_epochs": 15, + "trainer.enable_model_summary": False, + "trainer.num_sanity_val_steps": 0, + } + ) + + +def cli_main(): + MyLightningCLI(TransferLearningModel, CatDogImageDataModule, seed_everything_default=1234) + + +if __name__ == "__main__": + cli_lightning_logo() + cli_main() diff --git a/examples/pytorch/domain_templates/generative_adversarial_net.py b/examples/pytorch/domain_templates/generative_adversarial_net.py new file mode 100644 index 0000000..7837f6a --- /dev/null +++ b/examples/pytorch/domain_templates/generative_adversarial_net.py @@ -0,0 +1,233 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""To run this template just do: python generative_adversarial_net.py. + +After a few epochs, launch TensorBoard to see the images being generated at every batch: + +tensorboard --logdir default +""" +from argparse import ArgumentParser, Namespace + +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F + +from lightning.pytorch import cli_lightning_logo +from lightning.pytorch.core import LightningModule +from lightning.pytorch.demos.mnist_datamodule import MNISTDataModule +from lightning.pytorch.trainer import Trainer +from lightning.pytorch.utilities.imports import _TORCHVISION_AVAILABLE + +if _TORCHVISION_AVAILABLE: + import torchvision + + +class Generator(nn.Module): + """ + >>> Generator(img_shape=(1, 8, 8)) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Generator( + (model): Sequential(...) + ) + """ + + def __init__(self, latent_dim: int = 100, img_shape: tuple = (1, 28, 28)): + super().__init__() + self.img_shape = img_shape + + def block(in_feat, out_feat, normalize=True): + layers = [nn.Linear(in_feat, out_feat)] + if normalize: + layers.append(nn.BatchNorm1d(out_feat, 0.8)) + layers.append(nn.LeakyReLU(0.2, inplace=True)) + return layers + + self.model = nn.Sequential( + *block(latent_dim, 128, normalize=False), + *block(128, 256), + *block(256, 512), + *block(512, 1024), + nn.Linear(1024, int(np.prod(img_shape))), + nn.Tanh(), + ) + + def forward(self, z): + img = self.model(z) + img = img.view(img.size(0), *self.img_shape) + return img + + +class Discriminator(nn.Module): + """ + >>> Discriminator(img_shape=(1, 28, 28)) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Discriminator( + (model): Sequential(...) + ) + """ + + def __init__(self, img_shape): + super().__init__() + + self.model = nn.Sequential( + nn.Linear(int(np.prod(img_shape)), 512), + nn.LeakyReLU(0.2, inplace=True), + nn.Linear(512, 256), + nn.LeakyReLU(0.2, inplace=True), + nn.Linear(256, 1), + ) + + def forward(self, img): + img_flat = img.view(img.size(0), -1) + return self.model(img_flat) + + +class GAN(LightningModule): + """ + >>> GAN(img_shape=(1, 8, 8)) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + GAN( + (generator): Generator( + (model): Sequential(...) + ) + (discriminator): Discriminator( + (model): Sequential(...) + ) + ) + """ + + def __init__( + self, + img_shape: tuple = (1, 28, 28), + lr: float = 0.0002, + b1: float = 0.5, + b2: float = 0.999, + latent_dim: int = 100, + ): + super().__init__() + self.save_hyperparameters() + self.automatic_optimization = False + + # networks + self.generator = Generator(latent_dim=self.hparams.latent_dim, img_shape=img_shape) + self.discriminator = Discriminator(img_shape=img_shape) + + self.validation_z = torch.randn(8, self.hparams.latent_dim) + + self.example_input_array = torch.zeros(2, self.hparams.latent_dim) + + def forward(self, z): + return self.generator(z) + + @staticmethod + def adversarial_loss(y_hat, y): + return F.binary_cross_entropy_with_logits(y_hat, y) + + def training_step(self, batch): + imgs, _ = batch + + opt_g, opt_d = self.optimizers() + + # sample noise + z = torch.randn(imgs.shape[0], self.hparams.latent_dim) + z = z.type_as(imgs) + + # Train generator + # ground truth result (ie: all fake) + # put on GPU because we created this tensor inside training_loop + valid = torch.ones(imgs.size(0), 1) + valid = valid.type_as(imgs) + + self.toggle_optimizer(opt_g) + # adversarial loss is binary cross-entropy + g_loss = self.adversarial_loss(self.discriminator(self(z)), valid) + opt_g.zero_grad() + self.manual_backward(g_loss) + opt_g.step() + self.untoggle_optimizer(opt_g) + + # Train discriminator + # Measure discriminator's ability to classify real from generated samples + # how well can it label as real? + valid = torch.ones(imgs.size(0), 1) + valid = valid.type_as(imgs) + + self.toggle_optimizer(opt_d) + real_loss = self.adversarial_loss(self.discriminator(imgs), valid) + + # how well can it label as fake? + fake = torch.zeros(imgs.size(0), 1) + fake = fake.type_as(imgs) + + fake_loss = self.adversarial_loss(self.discriminator(self(z).detach()), fake) + + # discriminator loss is the average of these + d_loss = (real_loss + fake_loss) / 2 + + opt_d.zero_grad() + self.manual_backward(d_loss) + opt_d.step() + self.untoggle_optimizer(opt_d) + + self.log_dict({"d_loss": d_loss, "g_loss": g_loss}) + + def configure_optimizers(self): + lr = self.hparams.lr + b1 = self.hparams.b1 + b2 = self.hparams.b2 + + opt_g = torch.optim.Adam(self.generator.parameters(), lr=lr, betas=(b1, b2)) + opt_d = torch.optim.Adam(self.discriminator.parameters(), lr=lr, betas=(b1, b2)) + return opt_g, opt_d + + def on_train_epoch_end(self): + z = self.validation_z.type_as(self.generator.model[0].weight) + + # log sampled images + sample_imgs = self(z) + grid = torchvision.utils.make_grid(sample_imgs) + for logger in self.loggers: + logger.experiment.add_image("generated_images", grid, self.current_epoch) + + +def main(args: Namespace) -> None: + # ------------------------ + # 1 INIT LIGHTNING MODEL + # ------------------------ + model = GAN(lr=args.lr, b1=args.b1, b2=args.b2, latent_dim=args.latent_dim) + + # ------------------------ + # 2 INIT TRAINER + # ------------------------ + # If use distributed training PyTorch recommends to use DistributedDataParallel. + # See: https://pytorch.org/docs/stable/nn.html#torch.nn.DataParallel + dm = MNISTDataModule() + trainer = Trainer(accelerator="gpu", devices=1) + + # ------------------------ + # 3 START TRAINING + # ------------------------ + trainer.fit(model, dm) + + +if __name__ == "__main__": + cli_lightning_logo() + parser = ArgumentParser() + + # Hyperparameters + parser.add_argument("--lr", type=float, default=0.0002, help="adam: learning rate") + parser.add_argument("--b1", type=float, default=0.5, help="adam: decay of first order momentum of gradient") + parser.add_argument("--b2", type=float, default=0.999, help="adam: decay of second order momentum of gradient") + parser.add_argument("--latent_dim", type=int, default=100, help="dimensionality of the latent space") + args = parser.parse_args() + + main(args) diff --git a/examples/pytorch/domain_templates/imagenet.py b/examples/pytorch/domain_templates/imagenet.py new file mode 100644 index 0000000..0d7275f --- /dev/null +++ b/examples/pytorch/domain_templates/imagenet.py @@ -0,0 +1,195 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""This example is largely adapted from https://github.com/pytorch/examples/blob/master/imagenet/main.py. + +Before you can run this example, you will need to download the ImageNet dataset manually from the +`official website `_ and place it into a folder `path/to/imagenet`. + +Train on ImageNet with default parameters: + +.. code-block: bash + + python imagenet.py fit --model.data_path /path/to/imagenet + +or show all options you can change: + +.. code-block: bash + + python imagenet.py --help + python imagenet.py fit --help +""" +import os +from typing import Optional + +import torch +import torch.nn.functional as F +import torch.nn.parallel +import torch.optim as optim +import torch.optim.lr_scheduler as lr_scheduler +import torch.utils.data +import torch.utils.data.distributed +import torchvision.datasets as datasets +import torchvision.transforms as transforms +from torch.utils.data import Dataset +from torchmetrics import Accuracy + +from lightning.pytorch import LightningModule +from lightning.pytorch.callbacks import ModelCheckpoint, TQDMProgressBar +from lightning.pytorch.cli import LightningCLI +from lightning.pytorch.strategies import ParallelStrategy +from lightning.pytorch.utilities.model_helpers import get_torchvision_model + + +class ImageNetLightningModel(LightningModule): + """ + >>> ImageNetLightningModel(data_path='missing') # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + ImageNetLightningModel( + (model): ResNet(...) + ) + """ + + def __init__( + self, + data_path: str, + arch: str = "resnet18", + weights: Optional[str] = None, + lr: float = 0.1, + momentum: float = 0.9, + weight_decay: float = 1e-4, + batch_size: int = 256, + workers: int = 4, + ): + super().__init__() + self.arch = arch + self.weights = weights + self.lr = lr + self.momentum = momentum + self.weight_decay = weight_decay + self.data_path = data_path + self.batch_size = batch_size + self.workers = workers + self.model = get_torchvision_model(self.arch, weights=self.weights) + self.train_dataset: Optional[Dataset] = None + self.eval_dataset: Optional[Dataset] = None + # ToDo: this number of classes hall be parsed when the dataset is loaded from folder + self.train_acc1 = Accuracy(task="multiclass", num_classes=1000, top_k=1) + self.train_acc5 = Accuracy(task="multiclass", num_classes=1000, top_k=5) + self.eval_acc1 = Accuracy(task="multiclass", num_classes=1000, top_k=1) + self.eval_acc5 = Accuracy(task="multiclass", num_classes=1000, top_k=5) + + def forward(self, x): + return self.model(x) + + def training_step(self, batch, batch_idx): + images, target = batch + output = self.model(images) + loss_train = F.cross_entropy(output, target) + self.log("train_loss", loss_train) + # update metrics + self.train_acc1(output, target) + self.train_acc5(output, target) + self.log("train_acc1", self.train_acc1, prog_bar=True) + self.log("train_acc5", self.train_acc5, prog_bar=True) + return loss_train + + def eval_step(self, batch, batch_idx, prefix: str): + images, target = batch + output = self.model(images) + loss_val = F.cross_entropy(output, target) + self.log(f"{prefix}_loss", loss_val) + # update metrics + self.eval_acc1(output, target) + self.eval_acc5(output, target) + self.log(f"{prefix}_acc1", self.eval_acc1, prog_bar=True) + self.log(f"{prefix}_acc5", self.eval_acc5, prog_bar=True) + return loss_val + + def validation_step(self, batch, batch_idx): + return self.eval_step(batch, batch_idx, "val") + + def test_step(self, batch, batch_idx): + return self.eval_step(batch, batch_idx, "test") + + def configure_optimizers(self): + optimizer = optim.SGD(self.parameters(), lr=self.lr, momentum=self.momentum, weight_decay=self.weight_decay) + scheduler = lr_scheduler.LambdaLR(optimizer, lambda epoch: 0.1 ** (epoch // 30)) + return [optimizer], [scheduler] + + def setup(self, stage: str): + if isinstance(self.trainer.strategy, ParallelStrategy): + # When using a single GPU per process and per `DistributedDataParallel`, we need to divide the batch size + # ourselves based on the total number of GPUs we have + num_processes = max(1, self.trainer.strategy.num_processes) + self.batch_size = int(self.batch_size / num_processes) + self.workers = int(self.workers / num_processes) + + if stage == "fit": + normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + train_dir = os.path.join(self.data_path, "train") + self.train_dataset = datasets.ImageFolder( + train_dir, + transforms.Compose( + [ + transforms.RandomResizedCrop(224), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + normalize, + ] + ), + ) + # all stages will use the eval dataset + normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) + val_dir = os.path.join(self.data_path, "val") + self.eval_dataset = datasets.ImageFolder( + val_dir, + transforms.Compose([transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), normalize]), + ) + + def train_dataloader(self): + return torch.utils.data.DataLoader( + dataset=self.train_dataset, + batch_size=self.batch_size, + shuffle=True, + num_workers=self.workers, + pin_memory=True, + ) + + def val_dataloader(self): + return torch.utils.data.DataLoader( + self.eval_dataset, batch_size=self.batch_size, num_workers=self.workers, pin_memory=True + ) + + def test_dataloader(self): + return self.val_dataloader() + + +if __name__ == "__main__": + LightningCLI( + ImageNetLightningModel, + trainer_defaults={ + "max_epochs": 90, + "accelerator": "auto", + "devices": 1, + "logger": False, + "benchmark": True, + "callbacks": [ + # the PyTorch example refreshes every 10 batches + TQDMProgressBar(refresh_rate=10), + # save when the validation top1 accuracy improves + ModelCheckpoint(monitor="val_acc1", mode="max"), + ], + }, + seed_everything_default=42, + save_config_kwargs={"overwrite": True}, + ) diff --git a/examples/pytorch/domain_templates/reinforce_learn_Qnet.py b/examples/pytorch/domain_templates/reinforce_learn_Qnet.py new file mode 100644 index 0000000..0f3e455 --- /dev/null +++ b/examples/pytorch/domain_templates/reinforce_learn_Qnet.py @@ -0,0 +1,395 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Deep Reinforcement Learning: Deep Q-network (DQN) + +The template illustrates using Lightning for Reinforcement Learning. The example builds a basic DQN using the +classic CartPole environment. + +To run the template, just run: +`python reinforce_learn_Qnet.py` + +After ~1500 steps, you will see the total_reward hitting the max score of 475+. +Open up TensorBoard to see the metrics: + +`tensorboard --logdir default` + +References +---------- + +[1] https://github.com/PacktPublishing/Deep-Reinforcement-Learning-Hands-On- +Second-Edition/blob/master/Chapter06/02_dqn_pong.py +""" + +import argparse +from collections import deque, namedtuple, OrderedDict +from typing import Iterator, List, Tuple + +import gym +import numpy as np +import torch +import torch.nn as nn +import torch.optim as optim +from torch.optim.optimizer import Optimizer +from torch.utils.data import DataLoader +from torch.utils.data.dataset import IterableDataset + +from lightning.pytorch import cli_lightning_logo, LightningModule, seed_everything, Trainer + + +class DQN(nn.Module): + """Simple MLP network. + + >>> DQN(10, 5) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + DQN( + (net): Sequential(...) + ) + """ + + def __init__(self, obs_size: int, n_actions: int, hidden_size: int = 128): + """ + Args: + obs_size: observation/state size of the environment + n_actions: number of discrete actions available in the environment + hidden_size: size of hidden layers + """ + super().__init__() + self.net = nn.Sequential(nn.Linear(obs_size, hidden_size), nn.ReLU(), nn.Linear(hidden_size, n_actions)) + + def forward(self, x): + return self.net(x.float()) + + +# Named tuple for storing experience steps gathered in training +Experience = namedtuple("Experience", field_names=["state", "action", "reward", "done", "new_state"]) + + +class ReplayBuffer: + """Replay Buffer for storing past experiences allowing the agent to learn from them. + + >>> ReplayBuffer(5) # doctest: +ELLIPSIS + <...reinforce_learn_Qnet.ReplayBuffer object at ...> + """ + + def __init__(self, capacity: int) -> None: + """ + Args: + capacity: size of the buffer + """ + self.buffer = deque(maxlen=capacity) + + def __len__(self) -> int: + return len(self.buffer) + + def append(self, experience: Experience) -> None: + """Add experience to the buffer. + + Args: + experience: tuple (state, action, reward, done, new_state) + """ + self.buffer.append(experience) + + def sample(self, batch_size: int) -> Tuple: + indices = np.random.choice(len(self.buffer), batch_size, replace=False) + states, actions, rewards, dones, next_states = zip(*(self.buffer[idx] for idx in indices)) + + return ( + np.array(states), + np.array(actions), + np.array(rewards, dtype=np.float32), + np.array(dones, dtype=np.bool), + np.array(next_states), + ) + + +class RLDataset(IterableDataset): + """Iterable Dataset containing the ExperienceBuffer which will be updated with new experiences during training. + + >>> RLDataset(ReplayBuffer(5)) # doctest: +ELLIPSIS + <...reinforce_learn_Qnet.RLDataset object at ...> + """ + + def __init__(self, buffer: ReplayBuffer, sample_size: int = 200) -> None: + """ + Args: + buffer: replay buffer + sample_size: number of experiences to sample at a time + """ + self.buffer = buffer + self.sample_size = sample_size + + def __iter__(self) -> Iterator: + states, actions, rewards, dones, new_states = self.buffer.sample(self.sample_size) + for i in range(len(dones)): + yield states[i], actions[i], rewards[i], dones[i], new_states[i] + + +class Agent: + """Base Agent class handling the interaction with the environment. + + >>> env = gym.make("CartPole-v1") + >>> buffer = ReplayBuffer(10) + >>> Agent(env, buffer) # doctest: +ELLIPSIS + <...reinforce_learn_Qnet.Agent object at ...> + """ + + def __init__(self, env: gym.Env, replay_buffer: ReplayBuffer) -> None: + """ + Args: + env: training environment + replay_buffer: replay buffer storing experiences + """ + self.env = env + self.replay_buffer = replay_buffer + self.reset() + self.state = self.env.reset() + + def reset(self) -> None: + """Resets the environment and updates the state.""" + self.state = self.env.reset() + + def get_action(self, net: nn.Module, epsilon: float, device: str) -> int: + """Using the given network, decide what action to carry out using an epsilon-greedy policy. + + Args: + net: DQN network + epsilon: value to determine likelihood of taking a random action + device: current device + + Returns: + action + """ + if np.random.random() < epsilon: + action = self.env.action_space.sample() + else: + state = torch.tensor([self.state]) + + if device not in ["cpu"]: + state = state.cuda(device) + + q_values = net(state) + _, action = torch.max(q_values, dim=1) + action = int(action.item()) + + return action + + @torch.no_grad() + def play_step(self, net: nn.Module, epsilon: float = 0.0, device: str = "cpu") -> Tuple[float, bool]: + """Carries out a single interaction step between the agent and the environment. + + Args: + net: DQN network + epsilon: value to determine likelihood of taking a random action + device: current device + + Returns: + reward, done + """ + action = self.get_action(net, epsilon, device) + + # do step in the environment + new_state, reward, done, _ = self.env.step(action) + + exp = Experience(self.state, action, reward, done, new_state) + + self.replay_buffer.append(exp) + + self.state = new_state + if done: + self.reset() + return reward, done + + +class DQNLightning(LightningModule): + """Basic DQN Model. + + >>> DQNLightning(env="CartPole-v1") # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + DQNLightning( + (net): DQN( + (net): Sequential(...) + ) + (target_net): DQN( + (net): Sequential(...) + ) + ) + """ + + def __init__( + self, + env: str, + replay_size: int = 200, + warm_start_steps: int = 200, + gamma: float = 0.99, + eps_start: float = 1.0, + eps_end: float = 0.01, + eps_last_frame: int = 200, + sync_rate: int = 10, + lr: float = 1e-2, + episode_length: int = 50, + batch_size: int = 4, + **kwargs, + ) -> None: + super().__init__(**kwargs) + self.replay_size = replay_size + self.warm_start_steps = warm_start_steps + self.gamma = gamma + self.eps_start = eps_start + self.eps_end = eps_end + self.eps_last_frame = eps_last_frame + self.sync_rate = sync_rate + self.lr = lr + self.episode_length = episode_length + self.batch_size = batch_size + + self.env = gym.make(env) + obs_size = self.env.observation_space.shape[0] + n_actions = self.env.action_space.n + + self.net = DQN(obs_size, n_actions) + self.target_net = DQN(obs_size, n_actions) + + self.buffer = ReplayBuffer(self.replay_size) + self.agent = Agent(self.env, self.buffer) + self.total_reward = 0 + self.episode_reward = 0 + self.populate(self.warm_start_steps) + + def populate(self, steps: int = 1000) -> None: + """Carries out several random steps through the environment to initially fill up the replay buffer with + experiences. + + Args: + steps: number of random steps to populate the buffer with + """ + for i in range(steps): + self.agent.play_step(self.net, epsilon=1.0) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + """Passes in a state `x` through the network and gets the `q_values` of each action as an output. + + Args: + x: environment state + + Returns: + q values + """ + return self.net(x) + + def dqn_mse_loss(self, batch: Tuple[torch.Tensor, torch.Tensor]) -> torch.Tensor: + """Calculates the mse loss using a mini batch from the replay buffer. + + Args: + batch: current mini batch of replay data + + Returns: + loss + """ + states, actions, rewards, dones, next_states = batch + + state_action_values = self.net(states).gather(1, actions.unsqueeze(-1)).squeeze(-1) + + with torch.no_grad(): + next_state_values = self.target_net(next_states).max(1)[0] + next_state_values[dones] = 0.0 + next_state_values = next_state_values.detach() + + expected_state_action_values = next_state_values * self.gamma + rewards + + return nn.MSELoss()(state_action_values, expected_state_action_values) + + def training_step(self, batch: Tuple[torch.Tensor, torch.Tensor], nb_batch) -> OrderedDict: + """Carries out a single step through the environment to update the replay buffer. Then calculates loss + based on the minibatch received. + + Args: + batch: current mini batch of replay data + nb_batch: batch number + + Returns: + Training loss and log metrics + """ + device = self.get_device(batch) + epsilon = max(self.eps_end, self.eps_start - (self.global_step + 1) / self.eps_last_frame) + + # step through environment with agent + reward, done = self.agent.play_step(self.net, epsilon, device) + self.episode_reward += reward + + # calculates training loss + loss = self.dqn_mse_loss(batch) + + if done: + self.total_reward = self.episode_reward + self.episode_reward = 0 + + # Soft update of target network + if self.global_step % self.sync_rate == 0: + self.target_net.load_state_dict(self.net.state_dict()) + + log = { + "total_reward": torch.tensor(self.total_reward).to(device), + "reward": torch.tensor(reward).to(device), + "steps": torch.tensor(self.global_step).to(device), + } + + return OrderedDict({"loss": loss, "log": log, "progress_bar": log}) + + def configure_optimizers(self) -> List[Optimizer]: + """Initialize Adam optimizer.""" + optimizer = optim.Adam(self.net.parameters(), lr=self.lr) + return [optimizer] + + def __dataloader(self) -> DataLoader: + """Initialize the Replay Buffer dataset used for retrieving experiences.""" + dataset = RLDataset(self.buffer, self.episode_length) + return DataLoader(dataset=dataset, batch_size=self.batch_size, sampler=None) + + def train_dataloader(self) -> DataLoader: + """Get train loader.""" + return self.__dataloader() + + def get_device(self, batch) -> str: + """Retrieve device currently being used by minibatch.""" + return batch[0].device.index if self.on_gpu else "cpu" + + +def main(args) -> None: + model = DQNLightning(**vars(args)) + trainer = Trainer(accelerator="cpu", devices=1, val_check_interval=100) + trainer.fit(model) + + +if __name__ == "__main__": + cli_lightning_logo() + seed_everything(0) + + parser = argparse.ArgumentParser() + parser.add_argument("--batch_size", type=int, default=16, help="size of the batches") + parser.add_argument("--lr", type=float, default=1e-2, help="learning rate") + parser.add_argument("--env", type=str, default="CartPole-v1", help="gym environment tag") + parser.add_argument("--gamma", type=float, default=0.99, help="discount factor") + parser.add_argument("--sync_rate", type=int, default=10, help="how many frames do we update the target network") + parser.add_argument("--replay_size", type=int, default=1000, help="capacity of the replay buffer") + parser.add_argument( + "--warm_start_steps", + type=int, + default=1000, + help="how many samples do we use to fill our buffer at the start of training", + ) + parser.add_argument("--eps_last_frame", type=int, default=1000, help="what frame should epsilon stop decaying") + parser.add_argument("--eps_start", type=float, default=1.0, help="starting value of epsilon") + parser.add_argument("--eps_end", type=float, default=0.01, help="final value of epsilon") + parser.add_argument("--episode_length", type=int, default=200, help="max length of an episode") + args = parser.parse_args() + + main(args) diff --git a/examples/pytorch/domain_templates/reinforce_learn_ppo.py b/examples/pytorch/domain_templates/reinforce_learn_ppo.py new file mode 100644 index 0000000..b68fcf7 --- /dev/null +++ b/examples/pytorch/domain_templates/reinforce_learn_ppo.py @@ -0,0 +1,455 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""PyTorch Lightning implementation of Proximal Policy Optimization (PPO) + + +Paper authors: John Schulman, Filip Wolski, Prafulla Dhariwal, Alec Radford, Oleg Klimov + +The example implements PPO compatible to work with any continuous or discrete action-space environments via OpenAI Gym. + +To run the template, just run: +`python reinforce_learn_ppo.py` + +References +---------- +[1] https://github.com/openai/baselines/blob/master/baselines/ppo2/ppo2.py +[2] https://github.com/openai/spinningup +[3] https://github.com/sid-sundrani/ppo_lightning +""" +import argparse +from typing import Callable, Iterator, List, Tuple + +import gym +import torch +from torch import nn +from torch.distributions import Categorical, Normal +from torch.optim.optimizer import Optimizer +from torch.utils.data import DataLoader, IterableDataset + +from lightning.pytorch import cli_lightning_logo, LightningModule, seed_everything, Trainer + + +def create_mlp(input_shape: Tuple[int], n_actions: int, hidden_size: int = 128): + """Simple Multi-Layer Perceptron network.""" + return nn.Sequential( + nn.Linear(input_shape[0], hidden_size), + nn.ReLU(), + nn.Linear(hidden_size, hidden_size), + nn.ReLU(), + nn.Linear(hidden_size, n_actions), + ) + + +class ActorCategorical(nn.Module): + """Policy network, for discrete action spaces, which returns a distribution and an action given an + observation.""" + + def __init__(self, actor_net): + """ + Args: + input_shape: observation shape of the environment + n_actions: number of discrete actions available in the environment + """ + super().__init__() + + self.actor_net = actor_net + + def forward(self, states): + logits = self.actor_net(states) + pi = Categorical(logits=logits) + actions = pi.sample() + + return pi, actions + + def get_log_prob(self, pi: Categorical, actions: torch.Tensor): + """Takes in a distribution and actions and returns log prob of actions under the distribution. + + Args: + pi: torch distribution + actions: actions taken by distribution + + Returns: + log probability of the action under pi + """ + return pi.log_prob(actions) + + +class ActorContinuous(nn.Module): + """Policy network, for continuous action spaces, which returns a distribution and an action given an + observation.""" + + def __init__(self, actor_net, act_dim): + """ + Args: + input_shape: observation shape of the environment + n_actions: number of discrete actions available in the environment + """ + super().__init__() + self.actor_net = actor_net + log_std = -0.5 * torch.ones(act_dim, dtype=torch.float) + self.log_std = nn.Parameter(log_std) + + def forward(self, states): + mu = self.actor_net(states) + std = torch.exp(self.log_std) + pi = Normal(loc=mu, scale=std) + actions = pi.sample() + + return pi, actions + + def get_log_prob(self, pi: Normal, actions: torch.Tensor): + """Takes in a distribution and actions and returns log prob of actions under the distribution. + + Args: + pi: torch distribution + actions: actions taken by distribution + + Returns: + log probability of the action under pi + """ + return pi.log_prob(actions).sum(axis=-1) + + +class ExperienceSourceDataset(IterableDataset): + """Implementation from PyTorch Lightning Bolts: https://github.com/Lightning-AI/lightning- + bolts/blob/master/pl_bolts/datamodules/experience_source.py. + + Basic experience source dataset. Takes a generate_batch function that returns an iterator. The logic for the + experience source and how the batch is generated is defined the Lightning model itself + """ + + def __init__(self, generate_batch: Callable): + self.generate_batch = generate_batch + + def __iter__(self) -> Iterator: + return self.generate_batch() + + +class PPOLightning(LightningModule): + """PyTorch Lightning implementation of PPO. + + Example: + model = PPOLightning("CartPole-v0") + Train: + trainer = Trainer() + trainer.fit(model) + """ + + def __init__( + self, + env: str, + gamma: float = 0.99, + lam: float = 0.95, + lr_actor: float = 3e-4, + lr_critic: float = 1e-3, + max_episode_len: float = 200, + batch_size: int = 512, + steps_per_epoch: int = 2048, + nb_optim_iters: int = 4, + clip_ratio: float = 0.2, + **kwargs, + ) -> None: + """ + Args: + env: gym environment tag + gamma: discount factor + lam: advantage discount factor (lambda in the paper) + lr_actor: learning rate of actor network + lr_critic: learning rate of critic network + max_episode_len: maximum number interactions (actions) in an episode + batch_size: batch_size when training network- can simulate number of policy updates performed per epoch + steps_per_epoch: how many action-state pairs to rollout for trajectory collection per epoch + nb_optim_iters: how many steps of gradient descent to perform on each batch + clip_ratio: hyperparameter for clipping in the policy objective + """ + super().__init__() + + # Hyperparameters + self.lr_actor = lr_actor + self.lr_critic = lr_critic + self.steps_per_epoch = steps_per_epoch + self.nb_optim_iters = nb_optim_iters + self.batch_size = batch_size + self.gamma = gamma + self.lam = lam + self.max_episode_len = max_episode_len + self.clip_ratio = clip_ratio + self.save_hyperparameters() + + self.automatic_optimization = False + + self.env = gym.make(env) + # value network + self.critic = create_mlp(self.env.observation_space.shape, 1) + # policy network (agent) + if isinstance(self.env.action_space, gym.spaces.box.Box): + act_dim = self.env.action_space.shape[0] + actor_mlp = create_mlp(self.env.observation_space.shape, act_dim) + self.actor = ActorContinuous(actor_mlp, act_dim) + elif isinstance(self.env.action_space, gym.spaces.discrete.Discrete): + actor_mlp = create_mlp(self.env.observation_space.shape, self.env.action_space.n) + self.actor = ActorCategorical(actor_mlp) + else: + raise NotImplementedError( + "Env action space should be of type Box (continuous) or Discrete (categorical)." + f" Got type: {type(self.env.action_space)}" + ) + + self.batch_states = [] + self.batch_actions = [] + self.batch_adv = [] + self.batch_qvals = [] + self.batch_logp = [] + + self.ep_rewards = [] + self.ep_values = [] + self.epoch_rewards = [] + + self.episode_step = 0 + self.avg_ep_reward = 0 + self.avg_ep_len = 0 + self.avg_reward = 0 + + self.state = torch.FloatTensor(self.env.reset()) + + def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: + """Passes in a state x through the network and returns the policy and a sampled action. + + Args: + x: environment state + + Returns: + Tuple of policy and action + """ + pi, action = self.actor(x) + value = self.critic(x) + + return pi, action, value + + def discount_rewards(self, rewards: List[float], discount: float) -> List[float]: + """Calculate the discounted rewards of all rewards in list. + + Args: + rewards: list of rewards/advantages + + Returns: + list of discounted rewards/advantages + """ + assert isinstance(rewards[0], float) + + cumul_reward = [] + sum_r = 0.0 + + for r in reversed(rewards): + sum_r = (sum_r * discount) + r + cumul_reward.append(sum_r) + + return list(reversed(cumul_reward)) + + def calc_advantage(self, rewards: List[float], values: List[float], last_value: float) -> List[float]: + """Calculate the advantage given rewards, state values, and the last value of episode. + + Args: + rewards: list of episode rewards + values: list of state values from critic + last_value: value of last state of episode + + Returns: + list of advantages + """ + rews = rewards + [last_value] + vals = values + [last_value] + # GAE + delta = [rews[i] + self.gamma * vals[i + 1] - vals[i] for i in range(len(rews) - 1)] + return self.discount_rewards(delta, self.gamma * self.lam) + + def generate_trajectory_samples(self) -> Tuple[List[torch.Tensor], List[torch.Tensor], List[torch.Tensor]]: + """ + Contains the logic for generating trajectory data to train policy and value network + Yield: + Tuple of Lists containing tensors for states, actions, log probs, qvals and advantage + """ + for step in range(self.steps_per_epoch): + self.state = self.state.to(device=self.device) + + with torch.no_grad(): + pi, action, value = self(self.state) + log_prob = self.actor.get_log_prob(pi, action) + + next_state, reward, done, _ = self.env.step(action.cpu().numpy()) + + self.episode_step += 1 + + self.batch_states.append(self.state) + self.batch_actions.append(action) + self.batch_logp.append(log_prob) + + self.ep_rewards.append(reward) + self.ep_values.append(value.item()) + + self.state = torch.FloatTensor(next_state) + + epoch_end = step == (self.steps_per_epoch - 1) + terminal = len(self.ep_rewards) == self.max_episode_len + + if epoch_end or done or terminal: + # if trajectory ends abtruptly, bootstrap value of next state + if (terminal or epoch_end) and not done: + self.state = self.state.to(device=self.device) + with torch.no_grad(): + _, _, value = self(self.state) + last_value = value.item() + steps_before_cutoff = self.episode_step + else: + last_value = 0 + steps_before_cutoff = 0 + + # discounted cumulative reward + self.batch_qvals += self.discount_rewards(self.ep_rewards + [last_value], self.gamma)[:-1] + # advantage + self.batch_adv += self.calc_advantage(self.ep_rewards, self.ep_values, last_value) + # logs + self.epoch_rewards.append(sum(self.ep_rewards)) + # reset params + self.ep_rewards = [] + self.ep_values = [] + self.episode_step = 0 + self.state = torch.FloatTensor(self.env.reset()) + + if epoch_end: + train_data = zip( + self.batch_states, self.batch_actions, self.batch_logp, self.batch_qvals, self.batch_adv + ) + + for state, action, logp_old, qval, adv in train_data: + yield state, action, logp_old, qval, adv + + self.batch_states.clear() + self.batch_actions.clear() + self.batch_adv.clear() + self.batch_logp.clear() + self.batch_qvals.clear() + + # logging + self.avg_reward = sum(self.epoch_rewards) / self.steps_per_epoch + + # if epoch ended abruptly, exlude last cut-short episode to prevent stats skewness + epoch_rewards = self.epoch_rewards + if not done: + epoch_rewards = epoch_rewards[:-1] + + total_epoch_reward = sum(epoch_rewards) + nb_episodes = len(epoch_rewards) + + self.avg_ep_reward = total_epoch_reward / nb_episodes + self.avg_ep_len = (self.steps_per_epoch - steps_before_cutoff) / nb_episodes + + self.epoch_rewards.clear() + + def actor_loss(self, state, action, logp_old, qval, adv) -> torch.Tensor: + pi, _ = self.actor(state) + logp = self.actor.get_log_prob(pi, action) + ratio = torch.exp(logp - logp_old) + clip_adv = torch.clamp(ratio, 1 - self.clip_ratio, 1 + self.clip_ratio) * adv + return -(torch.min(ratio * adv, clip_adv)).mean() + + def critic_loss(self, state, action, logp_old, qval, adv) -> torch.Tensor: + value = self.critic(state) + return (qval - value).pow(2).mean() + + def training_step(self, batch: Tuple[torch.Tensor, torch.Tensor]): + """Carries out a single update to actor and critic network from a batch of replay buffer. + + Args: + batch: batch of replay buffer/trajectory data + """ + state, action, old_logp, qval, adv = batch + + # normalize advantages + adv = (adv - adv.mean()) / adv.std() + + self.log("avg_ep_len", self.avg_ep_len, prog_bar=True, on_step=False, on_epoch=True) + self.log("avg_ep_reward", self.avg_ep_reward, prog_bar=True, on_step=False, on_epoch=True) + self.log("avg_reward", self.avg_reward, prog_bar=True, on_step=False, on_epoch=True) + + optimizer_actor, optimizer_critic = self.optimizers() + + loss_actor = self.actor_loss(state, action, old_logp, qval, adv) + self.manual_backward(loss_actor) + optimizer_actor.step() + optimizer_actor.zero_grad() + + loss_critic = self.critic_loss(state, action, old_logp, qval, adv) + self.manual_backward(loss_critic) + optimizer_critic.step() + optimizer_critic.zero_grad() + + self.log("loss_critic", loss_critic, on_step=False, on_epoch=True, prog_bar=False, logger=True) + self.log("loss_actor", loss_actor, on_step=False, on_epoch=True, prog_bar=True, logger=True) + + def configure_optimizers(self) -> List[Optimizer]: + """Initialize Adam optimizer.""" + optimizer_actor = torch.optim.Adam(self.actor.parameters(), lr=self.lr_actor) + optimizer_critic = torch.optim.Adam(self.critic.parameters(), lr=self.lr_critic) + return optimizer_actor, optimizer_critic + + def optimizer_step(self, *args, **kwargs): + """Run 'nb_optim_iters' number of iterations of gradient descent on actor and critic for each data + sample.""" + for _ in range(self.nb_optim_iters): + super().optimizer_step(*args, **kwargs) + + def _dataloader(self) -> DataLoader: + """Initialize the Replay Buffer dataset used for retrieving experiences.""" + dataset = ExperienceSourceDataset(self.generate_trajectory_samples) + return DataLoader(dataset=dataset, batch_size=self.batch_size) + + def train_dataloader(self) -> DataLoader: + """Get train loader.""" + return self._dataloader() + + +def main(args) -> None: + model = PPOLightning(**vars(args)) + trainer = Trainer(accelerator="cpu", devices=1, val_check_interval=100) + trainer.fit(model) + + +if __name__ == "__main__": + cli_lightning_logo() + seed_everything(0) + + parser = argparse.ArgumentParser() + parser.add_argument("--env", type=str, default="CartPole-v0") + parser.add_argument("--gamma", type=float, default=0.99, help="discount factor") + parser.add_argument("--lam", type=float, default=0.95, help="advantage discount factor") + parser.add_argument("--lr_actor", type=float, default=3e-4, help="learning rate of actor network") + parser.add_argument("--lr_critic", type=float, default=1e-3, help="learning rate of critic network") + parser.add_argument("--max_episode_len", type=int, default=1000, help="capacity of the replay buffer") + parser.add_argument("--batch_size", type=int, default=512, help="batch_size when training network") + parser.add_argument( + "--steps_per_epoch", + type=int, + default=2048, + help="how many action-state pairs to rollout for trajectory collection per epoch", + ) + parser.add_argument( + "--nb_optim_iters", type=int, default=4, help="how many steps of gradient descent to perform on each batch" + ) + parser.add_argument( + "--clip_ratio", type=float, default=0.2, help="hyperparameter for clipping in the policy objective" + ) + args = parser.parse_args() + + main(args) diff --git a/examples/pytorch/domain_templates/semantic_segmentation.py b/examples/pytorch/domain_templates/semantic_segmentation.py new file mode 100644 index 0000000..eb81675 --- /dev/null +++ b/examples/pytorch/domain_templates/semantic_segmentation.py @@ -0,0 +1,406 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import random +from argparse import ArgumentParser, Namespace + +import numpy as np +import torch +import torch.nn.functional as F +import torchvision.transforms as transforms +from PIL import Image +from torch import nn +from torch.utils.data import DataLoader, Dataset + +from lightning.pytorch import cli_lightning_logo, LightningModule, Trainer + +DEFAULT_VOID_LABELS = (0, 1, 2, 3, 4, 5, 6, 9, 10, 14, 15, 16, 18, 29, 30, -1) +DEFAULT_VALID_LABELS = (7, 8, 11, 12, 13, 17, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 31, 32, 33) + + +def _create_synth_kitti_dataset(path_dir: str, image_dims: tuple = (1024, 512)): + """Create synthetic dataset with random images, just to simulate that the dataset have been already + downloaded.""" + path_dir_images = os.path.join(path_dir, KITTI.IMAGE_PATH) + path_dir_masks = os.path.join(path_dir, KITTI.MASK_PATH) + for p_dir in (path_dir_images, path_dir_masks): + os.makedirs(p_dir, exist_ok=True) + for i in range(3): + path_img = os.path.join(path_dir_images, f"dummy_kitti_{i}.png") + Image.new("RGB", image_dims).save(path_img) + path_mask = os.path.join(path_dir_masks, f"dummy_kitti_{i}.png") + Image.new("L", image_dims).save(path_mask) + + +class KITTI(Dataset): + """Class for KITTI Semantic Segmentation Benchmark dataset. + + Dataset link - http://www.cvlibs.net/datasets/kitti/eval_semseg.php?benchmark=semantics2015 + + There are 34 classes in the given labels. However, not all of them are useful for training + (like railings on highways, road dividers, etc.). + So, these useless classes (the pixel values of these classes) are stored in the `void_labels`. + The useful classes are stored in the `valid_labels`. + + The `encode_segmap` function sets all pixels with any of the `void_labels` to `ignore_index` + (250 by default). It also sets all of the valid pixels to the appropriate value between 0 and + `len(valid_labels)` (since that is the number of valid classes), so it can be used properly by + the loss function when comparing with the output. + + The `get_filenames` function retrieves the filenames of all images in the given `path` and + saves the absolute path in a list. + + In the `get_item` function, images and masks are resized to the given `img_size`, masks are + encoded using `encode_segmap`, and given `transform` (if any) are applied to the image only + (mask does not usually require transforms, but they can be implemented in a similar way). + """ + + IMAGE_PATH = os.path.join("training", "image_2") + MASK_PATH = os.path.join("training", "semantic") + + def __init__( + self, + data_path: str, + split: str, + img_size: tuple = (1242, 376), + void_labels: list = DEFAULT_VOID_LABELS, + valid_labels: list = DEFAULT_VALID_LABELS, + transform=None, + ): + self.img_size = img_size + self.void_labels = void_labels + self.valid_labels = valid_labels + self.ignore_index = 250 + self.class_map = dict(zip(self.valid_labels, range(len(self.valid_labels)))) + self.transform = transform + + self.split = split + self.data_path = data_path + self.img_path = os.path.join(self.data_path, self.IMAGE_PATH) + self.mask_path = os.path.join(self.data_path, self.MASK_PATH) + self.img_list = self.get_filenames(self.img_path) + self.mask_list = self.get_filenames(self.mask_path) + + # Split between train and valid set (80/20) + random_inst = random.Random(12345) # for repeatability + n_items = len(self.img_list) + idxs = random_inst.sample(range(n_items), n_items // 5) + if self.split == "train": + idxs = [idx for idx in range(n_items) if idx not in idxs] + self.img_list = [self.img_list[i] for i in idxs] + self.mask_list = [self.mask_list[i] for i in idxs] + + def __len__(self): + return len(self.img_list) + + def __getitem__(self, idx): + img = Image.open(self.img_list[idx]) + img = img.resize(self.img_size) + img = np.array(img) + + mask = Image.open(self.mask_list[idx]).convert("L") + mask = mask.resize(self.img_size) + mask = np.array(mask) + mask = self.encode_segmap(mask) + + if self.transform: + img = self.transform(img) + + return img, mask + + def encode_segmap(self, mask): + """Sets void classes to zero so they won't be considered for training.""" + for voidc in self.void_labels: + mask[mask == voidc] = self.ignore_index + for validc in self.valid_labels: + mask[mask == validc] = self.class_map[validc] + # remove extra idxs from updated dataset + mask[mask > 18] = self.ignore_index + return mask + + def get_filenames(self, path): + """Returns a list of absolute paths to images inside given `path`""" + files_list = [] + for filename in os.listdir(path): + files_list.append(os.path.join(path, filename)) + return files_list + + +class UNet(nn.Module): + """Architecture based on U-Net: Convolutional Networks for Biomedical Image Segmentation. + + Link - https://arxiv.org/abs/1505.04597 + + >>> UNet(num_classes=2, num_layers=3) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + UNet( + (layers): ModuleList( + (0): DoubleConv(...) + (1): Down(...) + (2): Down(...) + (3): Up(...) + (4): Up(...) + (5): Conv2d(64, 2, kernel_size=(1, 1), stride=(1, 1)) + ) + ) + """ + + def __init__(self, num_classes: int = 19, num_layers: int = 5, features_start: int = 64, bilinear: bool = False): + """ + Args: + num_classes: Number of output classes required (default 19 for KITTI dataset) + num_layers: Number of layers in each side of U-net + features_start: Number of features in first layer + bilinear: Whether to use bilinear interpolation or transposed convolutions for upsampling. + """ + super().__init__() + self.num_layers = num_layers + + layers = [DoubleConv(3, features_start)] + + feats = features_start + for _ in range(num_layers - 1): + layers.append(Down(feats, feats * 2)) + feats *= 2 + + for _ in range(num_layers - 1): + layers.append(Up(feats, feats // 2, bilinear)) + feats //= 2 + + layers.append(nn.Conv2d(feats, num_classes, kernel_size=1)) + + self.layers = nn.ModuleList(layers) + + def forward(self, x): + xi = [self.layers[0](x)] + # Down path + for layer in self.layers[1 : self.num_layers]: + xi.append(layer(xi[-1])) + # Up path + for i, layer in enumerate(self.layers[self.num_layers : -1]): + xi[-1] = layer(xi[-1], xi[-2 - i]) + return self.layers[-1](xi[-1]) + + +class DoubleConv(nn.Module): + """Double Convolution and BN and ReLU (3x3 conv -> BN -> ReLU) ** 2. + + >>> DoubleConv(4, 4) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + DoubleConv( + (net): Sequential(...) + ) + """ + + def __init__(self, in_ch: int, out_ch: int): + super().__init__() + self.net = nn.Sequential( + nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1), + nn.BatchNorm2d(out_ch), + nn.ReLU(inplace=True), + nn.Conv2d(out_ch, out_ch, kernel_size=3, padding=1), + nn.BatchNorm2d(out_ch), + nn.ReLU(inplace=True), + ) + + def forward(self, x): + return self.net(x) + + +class Down(nn.Module): + """Combination of MaxPool2d and DoubleConv in series. + + >>> Down(4, 8) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Down( + (net): Sequential( + (0): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) + (1): DoubleConv( + (net): Sequential(...) + ) + ) + ) + """ + + def __init__(self, in_ch: int, out_ch: int): + super().__init__() + self.net = nn.Sequential(nn.MaxPool2d(kernel_size=2, stride=2), DoubleConv(in_ch, out_ch)) + + def forward(self, x): + return self.net(x) + + +class Up(nn.Module): + """Upsampling (by either bilinear interpolation or transpose convolutions) followed by concatenation of feature + map from contracting path, followed by double 3x3 convolution. + + >>> Up(8, 4) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + Up( + (upsample): ConvTranspose2d(8, 4, kernel_size=(2, 2), stride=(2, 2)) + (conv): DoubleConv( + (net): Sequential(...) + ) + ) + """ + + def __init__(self, in_ch: int, out_ch: int, bilinear: bool = False): + super().__init__() + self.upsample = None + if bilinear: + self.upsample = nn.Sequential( + nn.Upsample(scale_factor=2, mode="bilinear", align_corners=True), + nn.Conv2d(in_ch, in_ch // 2, kernel_size=1), + ) + else: + self.upsample = nn.ConvTranspose2d(in_ch, in_ch // 2, kernel_size=2, stride=2) + + self.conv = DoubleConv(in_ch, out_ch) + + def forward(self, x1, x2): + x1 = self.upsample(x1) + + # Pad x1 to the size of x2 + diff_h = x2.shape[2] - x1.shape[2] + diff_w = x2.shape[3] - x1.shape[3] + + x1 = F.pad(x1, [diff_w // 2, diff_w - diff_w // 2, diff_h // 2, diff_h - diff_h // 2]) + + # Concatenate along the channels axis + x = torch.cat([x2, x1], dim=1) + return self.conv(x) + + +class SegModel(LightningModule): + """Semantic Segmentation Module. + + This is a basic semantic segmentation module implemented with Lightning. + It uses CrossEntropyLoss as the default loss function. May be replaced with + other loss functions as required. + It is specific to KITTI dataset i.e. dataloaders are for KITTI + and Normalize transform uses the mean and standard deviation of this dataset. + It uses the FCN ResNet50 model as an example. + + Adam optimizer is used along with Cosine Annealing learning rate scheduler. + + >>> dataset_path = os.path.join(".", "Kitti") + >>> _create_synth_kitti_dataset(dataset_path, image_dims=(1024, 512)) + >>> SegModel(dataset_path) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE + SegModel( + (net): UNet( + (layers): ModuleList( + (0): DoubleConv(...) + (1): Down(...) + (2): Down(...) + (3): Up(...) + (4): Up(...) + (5): Conv2d(64, 19, kernel_size=(1, 1), stride=(1, 1)) + ) + ) + ) + """ + + def __init__( + self, + data_path: str, + batch_size: int = 4, + lr: float = 1e-3, + num_layers: int = 3, + features_start: int = 64, + bilinear: bool = False, + **kwargs, + ): + super().__init__(**kwargs) + self.data_path = data_path + self.batch_size = batch_size + self.lr = lr + self.num_layers = num_layers + self.features_start = features_start + self.bilinear = bilinear + + self.net = UNet( + num_classes=19, num_layers=self.num_layers, features_start=self.features_start, bilinear=self.bilinear + ) + self.transform = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize( + mean=[0.35675976, 0.37380189, 0.3764753], std=[0.32064945, 0.32098866, 0.32325324] + ), + ] + ) + self.trainset = KITTI(self.data_path, split="train", transform=self.transform) + self.validset = KITTI(self.data_path, split="valid", transform=self.transform) + + def forward(self, x): + return self.net(x) + + def training_step(self, batch, batch_nb): + img, mask = batch + img = img.float() + mask = mask.long() + out = self(img) + loss = F.cross_entropy(out, mask, ignore_index=250) + log_dict = {"train_loss": loss} + return {"loss": loss, "log": log_dict, "progress_bar": log_dict} + + def validation_step(self, batch, batch_idx): + img, mask = batch + img = img.float() + mask = mask.long() + out = self(img) + val_loss = F.cross_entropy(out, mask, ignore_index=250) + self.log("val_loss", val_loss, prog_bar=True) + + def configure_optimizers(self): + opt = torch.optim.Adam(self.net.parameters(), lr=self.learning_rate) + sch = torch.optim.lr_scheduler.CosineAnnealingLR(opt, T_max=10) + return [opt], [sch] + + def train_dataloader(self): + return DataLoader(self.trainset, batch_size=self.batch_size, shuffle=True) + + def val_dataloader(self): + return DataLoader(self.validset, batch_size=self.batch_size, shuffle=False) + + +def main(hparams: Namespace): + # ------------------------ + # 1 INIT LIGHTNING MODEL + # ------------------------ + model = SegModel(**vars(hparams)) + + # ------------------------ + # 2 INIT TRAINER + # ------------------------ + trainer = Trainer() + + # ------------------------ + # 3 START TRAINING + # ------------------------ + trainer.fit(model) + + +if __name__ == "__main__": + cli_lightning_logo() + + parser = ArgumentParser() + parser.add_argument("--data_path", type=str, help="path where dataset is stored") + parser.add_argument("--batch_size", type=int, default=16, help="size of the batches") + parser.add_argument("--lr", type=float, default=0.001, help="adam: learning rate") + parser.add_argument("--num_layers", type=int, default=5, help="number of layers on u-net") + parser.add_argument("--features_start", type=float, default=64, help="number of features in first layer") + parser.add_argument( + "--bilinear", action="store_true", default=False, help="whether to use bilinear interpolation or transposed" + ) + hparams = parser.parse_args() + + main(hparams) diff --git a/examples/pytorch/hpu/mnist_sample.py b/examples/pytorch/hpu/mnist_sample.py new file mode 100644 index 0000000..0d04074 --- /dev/null +++ b/examples/pytorch/hpu/mnist_sample.py @@ -0,0 +1,73 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import torch +from jsonargparse import lazy_instance +from lightning_habana import HPUPrecisionPlugin +from torch.nn import functional as F + +from lightning.pytorch import LightningModule +from lightning.pytorch.cli import LightningCLI +from lightning.pytorch.demos.mnist_datamodule import MNISTDataModule + + +class LitClassifier(LightningModule): + def __init__(self): + super().__init__() + self.l1 = torch.nn.Linear(28 * 28, 10) + + def forward(self, x): + return torch.relu(self.l1(x.view(x.size(0), -1))) + + def training_step(self, batch, batch_idx): + x, y = batch + return F.cross_entropy(self(x), y) + + def validation_step(self, batch, batch_idx): + x, y = batch + probs = self(x) + acc = self.accuracy(probs, y) + self.log("val_acc", acc) + + def test_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + acc = self.accuracy(logits, y) + self.log("test_acc", acc) + + @staticmethod + def accuracy(logits, y): + return torch.sum(torch.eq(torch.argmax(logits, -1), y).to(torch.float32)) / len(y) + + def configure_optimizers(self): + return torch.optim.Adam(self.parameters(), lr=0.02) + + +if __name__ == "__main__": + cli = LightningCLI( + LitClassifier, + MNISTDataModule, + trainer_defaults={ + "accelerator": "hpu", + "devices": 1, + "max_epochs": 1, + "plugins": lazy_instance(HPUPrecisionPlugin, precision="16-mixed"), + }, + run=False, + save_config_kwargs={"overwrite": True}, + ) + + # Run the model ⚡ + cli.trainer.fit(cli.model, datamodule=cli.datamodule) + cli.trainer.validate(cli.model, datamodule=cli.datamodule) + cli.trainer.test(cli.model, datamodule=cli.datamodule) diff --git a/examples/pytorch/hpu/ops_bf16_mnist.txt b/examples/pytorch/hpu/ops_bf16_mnist.txt new file mode 100644 index 0000000..53ec99c --- /dev/null +++ b/examples/pytorch/hpu/ops_bf16_mnist.txt @@ -0,0 +1,2 @@ +linear +relu diff --git a/examples/pytorch/hpu/ops_fp32_mnist.txt b/examples/pytorch/hpu/ops_fp32_mnist.txt new file mode 100644 index 0000000..4509b7e --- /dev/null +++ b/examples/pytorch/hpu/ops_fp32_mnist.txt @@ -0,0 +1 @@ +cross_entropy diff --git a/examples/pytorch/ipu/mnist_sample.py b/examples/pytorch/ipu/mnist_sample.py new file mode 100644 index 0000000..8cef121 --- /dev/null +++ b/examples/pytorch/ipu/mnist_sample.py @@ -0,0 +1,84 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +from torch.nn import functional as F + +from lightning.pytorch import LightningModule, Trainer +from lightning.pytorch.demos.mnist_datamodule import MNISTDataModule + + +class LitClassifier(LightningModule): + def __init__(self, hidden_dim: int = 128, learning_rate: float = 0.0001): + super().__init__() + self.save_hyperparameters() + + self.l1 = torch.nn.Linear(28 * 28, self.hparams.hidden_dim) + self.l2 = torch.nn.Linear(self.hparams.hidden_dim, 10) + + self.val_outptus = [] + self.test_outputs = [] + + def forward(self, x): + x = x.view(x.size(0), -1) + x = torch.relu(self.l1(x)) + x = torch.relu(self.l2(x)) + return x + + def training_step(self, batch, batch_idx): + x, y = batch + y_hat = self(x) + return F.cross_entropy(y_hat, y) + + def validation_step(self, batch, batch_idx): + x, y = batch + probs = self(x) + acc = self.accuracy(probs, y) + self.val_outputs.append(acc) + return acc + + def test_step(self, batch, batch_idx): + x, y = batch + logits = self(x) + acc = self.accuracy(logits, y) + self.test_outputs.append(acc) + return acc + + def accuracy(self, logits, y): + # currently IPU poptorch doesn't implicit convert bools to tensor + # hence we use an explicit calculation for accuracy here. Once fixed in poptorch + # we can use the accuracy metric. + return torch.sum(torch.eq(torch.argmax(logits, -1), y).to(torch.float32)) / len(y) + + def on_validation_epoch_end(self) -> None: + # since the training step/validation step and test step are run on the IPU device + # we must log the average loss outside the step functions. + self.log("val_acc", torch.stack(self.val_outptus).mean(), prog_bar=True) + self.val_outptus.clear() + + def on_test_epoch_end(self) -> None: + self.log("test_acc", torch.stack(self.test_outputs).mean()) + self.test_outputs.clear() + + def configure_optimizers(self): + return torch.optim.Adam(self.parameters(), lr=self.hparams.learning_rate) + + +if __name__ == "__main__": + dm = MNISTDataModule(batch_size=32) + model = LitClassifier() + trainer = Trainer(max_epochs=2, accelerator="ipu", devices=8) + + trainer.fit(model, datamodule=dm) + trainer.test(model, datamodule=dm) diff --git a/examples/pytorch/servable_module/production.py b/examples/pytorch/servable_module/production.py new file mode 100644 index 0000000..c13b1bf --- /dev/null +++ b/examples/pytorch/servable_module/production.py @@ -0,0 +1,124 @@ +import base64 +from dataclasses import dataclass +from io import BytesIO +from os import path +from typing import Dict, Optional + +import numpy as np +import torch +import torchvision +import torchvision.transforms as T +from PIL import Image as PILImage + +from lightning.pytorch import cli_lightning_logo, LightningDataModule, LightningModule +from lightning.pytorch.cli import LightningCLI +from lightning.pytorch.serve import ServableModule, ServableModuleValidator +from lightning.pytorch.utilities.model_helpers import get_torchvision_model + +DATASETS_PATH = path.join(path.dirname(__file__), "..", "..", "Datasets") + + +class LitModule(LightningModule): + def __init__(self, name: str = "resnet18"): + super().__init__() + self.model = get_torchvision_model(name, weights="DEFAULT") + self.model.fc = torch.nn.Linear(self.model.fc.in_features, 10) + self.criterion = torch.nn.CrossEntropyLoss() + + def training_step(self, batch, batch_idx): + inputs, labels = batch + outputs = self.model(inputs) + loss = self.criterion(outputs, labels) + self.log("train_loss", loss) + return loss + + def validation_step(self, batch, batch_idx): + inputs, labels = batch + outputs = self.model(inputs) + loss = self.criterion(outputs, labels) + self.log("val_loss", loss) + + def configure_optimizers(self): + return torch.optim.SGD(self.parameters(), lr=0.001, momentum=0.9) + + +class CIFAR10DataModule(LightningDataModule): + transform = T.Compose([T.Resize(256), T.CenterCrop(224), T.ToTensor()]) + + def train_dataloader(self, *args, **kwargs): + trainset = torchvision.datasets.CIFAR10(root=DATASETS_PATH, train=True, download=True, transform=self.transform) + return torch.utils.data.DataLoader(trainset, batch_size=2, shuffle=True, num_workers=0) + + def val_dataloader(self, *args, **kwargs): + valset = torchvision.datasets.CIFAR10(root=DATASETS_PATH, train=False, download=True, transform=self.transform) + return torch.utils.data.DataLoader(valset, batch_size=2, shuffle=True, num_workers=0) + + +@dataclass(unsafe_hash=True) +class Image: + height: Optional[int] = None + width: Optional[int] = None + extension: str = "JPEG" + mode: str = "RGB" + channel_first: bool = False + + def deserialize(self, data: str) -> torch.Tensor: + encoded_with_padding = (data + "===").encode("UTF-8") + img = base64.b64decode(encoded_with_padding) + buffer = BytesIO(img) + img = PILImage.open(buffer, mode="r") + if self.height and self.width: + img = img.resize((self.width, self.height)) + arr = np.array(img) + return T.ToTensor()(arr).unsqueeze(0) + + +class Top1: + def serialize(self, tensor: torch.Tensor) -> int: + return torch.nn.functional.softmax(tensor).argmax().item() + + +class ProductionReadyModel(LitModule, ServableModule): + def configure_payload(self): + # 1: Access the train dataloader and load a single sample. + image, _ = self.trainer.train_dataloader.dataset[0] + + # 2: Convert the image into a PIL Image to bytes and encode it with base64 + pil_image = T.ToPILImage()(image) + buffered = BytesIO() + pil_image.save(buffered, format="JPEG") + img_str = base64.b64encode(buffered.getvalue()).decode("UTF-8") + + return {"body": {"x": img_str}} + + def configure_serialization(self): + return {"x": Image(224, 224).deserialize}, {"output": Top1().serialize} + + def serve_step(self, x: torch.Tensor) -> Dict[str, torch.Tensor]: + return {"output": self.model(x)} + + def configure_response(self): + return {"output": 7} + + +def cli_main(): + cli = LightningCLI( + ProductionReadyModel, + CIFAR10DataModule, + seed_everything_default=42, + save_config_kwargs={"overwrite": True}, + run=False, + trainer_defaults={ + "accelerator": "cpu", + "callbacks": [ServableModuleValidator()], + "max_epochs": 1, + "limit_train_batches": 5, + "limit_val_batches": 5, + }, + ) + cli.trainer.fit(cli.model, cli.datamodule) + + +if __name__ == "__main__": + cli_lightning_logo() + cli_main() diff --git a/examples/run_fabric_examples.sh b/examples/run_fabric_examples.sh new file mode 100644 index 0000000..e03c8ca --- /dev/null +++ b/examples/run_fabric_examples.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set -ex + +export PYTHONPATH="${PYTHONPATH}:$(pwd)" +dir_path=$(dirname "${BASH_SOURCE[0]}") + +args="--epochs=1" +python -m lightning_fabric.cli "${dir_path}/fabric/image_classifier/train_fabric.py" ${args} "$@" diff --git a/examples/run_pl_examples.sh b/examples/run_pl_examples.sh new file mode 100644 index 0000000..0d5410d --- /dev/null +++ b/examples/run_pl_examples.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set -ex + +export PYTHONPATH="${PYTHONPATH}:$(pwd)" +dir_path=$(dirname "${BASH_SOURCE[0]}") +args=" + --data.batch_size=32 + --trainer.max_epochs=1 + --trainer.limit_train_batches=2 + --trainer.limit_val_batches=2 + --trainer.limit_test_batches=2 + --trainer.limit_predict_batches=2 + --optimizer=Adam +" + +python "${dir_path}/pytorch/basics/backbone_image_classifier.py" ${args} "$@" +python "${dir_path}/pytorch/basics/autoencoder.py" ${args} "$@" + + +# test that a user can manually launch individual processes +args="--trainer.devices 2 --trainer.strategy ddp --trainer.max_epochs=1 --trainer.limit_train_batches=1 --trainer.limit_val_batches=1 --trainer.limit_test_batches=1" +MASTER_ADDR="localhost" MASTER_PORT=1234 LOCAL_RANK=1 python "${dir_path}/pytorch/basics/autoencoder.py" ${args} & +MASTER_ADDR="localhost" MASTER_PORT=1234 LOCAL_RANK=0 python "${dir_path}/pytorch/basics/autoencoder.py" ${args} diff --git a/requirements.txt b/requirements.txt index 52c1fc7..4cdb001 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,24 +1,7 @@ -# docs requirements -# from pytorch-lightning/requirements/docs.txt -sphinx>=4.0,<5.0 -myst-parser>=0.15,<0.17 -nbsphinx>=0.8.5 -ipython[notebook] -pandoc>=1.0 -docutils>=0.16 -sphinxcontrib-fulltoc>=1.0 -sphinxcontrib-mockautodoc -sphinx-autodoc-typehints>=1.11,<1.15 # v1.15 failing on master (#11405) -sphinx-paramlinks>=0.5.1 -sphinx-togglebutton>=0.2 -sphinx-copybutton>=0.3 -typing-extensions # already in `requirements.txt` but the docs CI job does not install it -jinja2>=3.0.0,<3.1.0 +# instead of install from source, use the pre-built wheel +lightning -# -r ../_notebooks/.actions/requirements.txt - -# switch sphinx theme to PyTorchKorea's -pt-lightning-sphinx-theme @ https://github.com/PyTorchKorea/lightning_sphinx_theme/archive/master.zip - -# PyTorch Lightning -pytorch-lightning==1.6.4 +# the default package dependencies +-r ./requirements/app/base.txt +-r ./requirements/pytorch/base.txt +-r ./requirements/pytorch/docs.txt \ No newline at end of file diff --git a/requirements/README.md b/requirements/README.md new file mode 100644 index 0000000..5b408f3 --- /dev/null +++ b/requirements/README.md @@ -0,0 +1,12 @@ +# Project Requirements + +This root requirements folder branches into sub-folders depending on the python package. +Within the folder, we have grouped requirements files/lists per focus, which shall closely match package extra +So, for example, when you install PL as `pip install pytorch-lightning[extra]`, this list is stored in `requirements/pytorch/extra.txt`. + +## CI/CD upper bounds + +For Ci stability, we have set for all package versions upper bounds (the latest version), so with any sudden release, we won't put our development on fire. +The continues updated of these upper bounds are managed by dependabot. +Note that these upper bounds are lifters when installing a package from the source or as a package. +If you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment. diff --git a/requirements/_integrations/accelerators.txt b/requirements/_integrations/accelerators.txt new file mode 100644 index 0000000..cfdb7d6 --- /dev/null +++ b/requirements/_integrations/accelerators.txt @@ -0,0 +1,3 @@ +# validation HPU connectors +lightning-habana >=0.1.0 +lightning-graphcore >=0.1.0.rc3 diff --git a/requirements/_integrations/strategies.txt b/requirements/_integrations/strategies.txt new file mode 100644 index 0000000..3227549 --- /dev/null +++ b/requirements/_integrations/strategies.txt @@ -0,0 +1,5 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment + +lightning-colossalai >=0.1.0 +lightning-bagua >=0.1.0 diff --git a/requirements/app/base.txt b/requirements/app/base.txt new file mode 100644 index 0000000..edeb5ad --- /dev/null +++ b/requirements/app/base.txt @@ -0,0 +1,30 @@ +lightning-cloud >=0.5.37 +packaging +typing-extensions >=4.0.0, <=4.7.1 +deepdiff >=5.7.0, <6.3.2 +starsessions >=1.2.1, <2.0 # strict +fsspec >=2022.5.0, <=2023.6.0 +croniter >=1.3.0, <1.5.0 # strict; TODO: for now until we find something more robust. +traitlets >=5.3.0, <5.10.0 +arrow >=1.2.0, <1.2.4 +lightning-utilities >=0.8.0, <0.10.0 +beautifulsoup4 >=4.8.0, <4.12.3 +inquirer >=2.10.0, <=3.1.3 +psutil <5.9.5 +click <=8.1.3 +python-multipart>=0.0.5, <=0.0.6 +backoff >=2.2.1, <2.3.0 + +fastapi >=0.92.0, <0.100.0 +starlette # https://fastapi.tiangolo.com/deployment/versions/#about-starlette +pydantic >=1.7.4, <2.1.0 # strict # https://fastapi.tiangolo.com/deployment/versions/#about-pydantic + +dateutils <=0.6.12 +Jinja2 <=3.1.2 +PyYAML <=6.0 +requests <2.31.1 +rich >=12.3.0, <=13.4.2 +urllib3 <=2.0.2 +uvicorn <=0.22.0 +websocket-client <1.6.2 +websockets <=11.0.3 diff --git a/requirements/app/cloud.txt b/requirements/app/cloud.txt new file mode 100644 index 0000000..d7a9d2a --- /dev/null +++ b/requirements/app/cloud.txt @@ -0,0 +1,4 @@ +redis >=4.0.1, <=4.6.0 +docker >=5.0.0, <6.1.4 +s3fs >=2022.5.0, <2023.6.1 +# setuptools==59.5.0 diff --git a/requirements/app/components.txt b/requirements/app/components.txt new file mode 100644 index 0000000..b821f7a --- /dev/null +++ b/requirements/app/components.txt @@ -0,0 +1,5 @@ +# deps required by components in the lightning app repository (src/lightning/app/components) +lightning_api_access >=0.0.3 # serve +aiohttp >=3.8.0, <=3.8.4 # auto_scaler +lightning-fabric >=1.9.0 # multinode +pytorch-lightning >=1.9.0 # multinode diff --git a/requirements/app/docs.txt b/requirements/app/docs.txt new file mode 100644 index 0000000..4f5c18a --- /dev/null +++ b/requirements/app/docs.txt @@ -0,0 +1,5 @@ +-r ../docs.txt + +# lai-sphinx-theme is not installable +# refer: https://github.com/Lightning-AI/lightning/issues/16158 +# lai-sphinx-theme diff --git a/requirements/app/test.txt b/requirements/app/test.txt new file mode 100644 index 0000000..f9cd551 --- /dev/null +++ b/requirements/app/test.txt @@ -0,0 +1,16 @@ +coverage ==7.2.7 +pytest ==7.3.1 +pytest-timeout ==2.1.0 +pytest-cov ==4.0.0 +pytest-doctestplus >=0.9.0 +pytest-asyncio ==0.21.0 +# pytest-random-order ==1.1.0 +pytest-rerunfailures <=11.1.2 + +playwright ==1.35.0 +httpx ==0.24.1 +trio <0.22.0 # strict https://github.com/python-trio/trio/pull/2213 +pympler +psutil <=5.9.5 +setuptools <67.7.0 +requests-mock <=1.10.0 diff --git a/requirements/app/ui.txt b/requirements/app/ui.txt new file mode 100644 index 0000000..6ba6bbc --- /dev/null +++ b/requirements/app/ui.txt @@ -0,0 +1,2 @@ +streamlit >=1.13.0, <1.22.1 +panel >=1.0.0, <=1.1.1 diff --git a/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..fe77f77 --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,36 @@ +Jinja2<4.0 +PyYAML<7.0 +PyYAML<7.0,>=5.4 +arrow<2.0,>=1.2.0 +backoff<3.0,>=2.2.1 +beautifulsoup4<5.0,>=4.8.0 +click<9.0 +croniter<1.5.0,>=1.3.0 # strict +dateutils<1.0 +deepdiff<7.0,>=5.7.0 +fastapi<1.0,>=0.92.0 +fsspec<2024.0,>=2022.5.0 +fsspec[http]<2024.0,>2021.06.0 +inquirer<4.0,>=2.10.0 +lightning-cloud>=0.5.37 +lightning-utilities<1.0,>=0.8.0 +numpy<2.0,>=1.17.2 +packaging +packaging<24.0,>=20.0 +psutil<6.0 +pydantic<2.1.0,>=1.7.4 # strict +python-multipart<1.0,>=0.0.5 +requests<3.0 +rich<14.0,>=12.3.0 +starlette +starsessions<2.0,>=1.2.1 # strict +torch<3.0,>=1.11.0 +torchmetrics<2.0,>=0.7.0 +tqdm<5.0,>=4.57.0 +traitlets<6.0,>=5.3.0 +typing-extensions<5.0,>=4.0.0 +urllib3<3.0 +uvicorn<1.0 +websocket-client<2.0 +websockets<12.0 + diff --git a/requirements/collect_env_details.py b/requirements/collect_env_details.py new file mode 100644 index 0000000..3dd2b8d --- /dev/null +++ b/requirements/collect_env_details.py @@ -0,0 +1,87 @@ +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Diagnose your system and show basic information. + +This server mainly to get detail info for better bug reporting. +""" + +import os +import platform +import sys + +import pkg_resources +import torch + +sys.path += [os.path.abspath(".."), os.path.abspath("")] + + +LEVEL_OFFSET = "\t" +KEY_PADDING = 20 + + +def info_system() -> dict: + return { + "OS": platform.system(), + "architecture": platform.architecture(), + "version": platform.version(), + "release": platform.release(), + "processor": platform.processor(), + "python": platform.python_version(), + } + + +def info_cuda() -> dict: + return { + "GPU": [torch.cuda.get_device_name(i) for i in range(torch.cuda.device_count())] or None, + "available": torch.cuda.is_available(), + "version": torch.version.cuda, + } + + +def info_packages() -> dict: + """Get name and version of all installed packages.""" + packages = {} + for dist in pkg_resources.working_set: + package = dist.as_requirement() + packages[package.key] = package.specs[0][1] + return packages + + +def nice_print(details: dict, level: int = 0) -> list: + lines = [] + for k in sorted(details): + key = f"* {k}:" if level == 0 else f"- {k}:" + if isinstance(details[k], dict): + lines += [level * LEVEL_OFFSET + key] + lines += nice_print(details[k], level + 1) + elif isinstance(details[k], (set, list, tuple)): + lines += [level * LEVEL_OFFSET + key] + lines += [(level + 1) * LEVEL_OFFSET + "- " + v for v in details[k]] + else: + template = "{:%is} {}" % KEY_PADDING + key_val = template.format(key, details[k]) + lines += [(level * LEVEL_OFFSET) + key_val] + return lines + + +def main() -> None: + details = {"System": info_system(), "CUDA": info_cuda(), "Packages": info_packages()} + details["Lightning"] = {k: v for k, v in details["Packages"].items() if "torch" in k or "lightning" in k} + lines = nice_print(details) + text = os.linesep.join(lines) + print(f"
\n Current environment\n\n{text}\n\n
") + + +if __name__ == "__main__": + main() diff --git a/requirements/data/cloud.txt b/requirements/data/cloud.txt new file mode 100644 index 0000000..950fb4b --- /dev/null +++ b/requirements/data/cloud.txt @@ -0,0 +1,5 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment + +fsspec[http] >2021.06.0, <2023.5.0 +s3fs >=2022.5.0, <=2023.6.0 diff --git a/requirements/data/data.txt b/requirements/data/data.txt new file mode 100644 index 0000000..4fa81bd --- /dev/null +++ b/requirements/data/data.txt @@ -0,0 +1,8 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment + +lightning-utilities >=0.8.0, <0.10.0 +# to be able to include also 0.6 and preserve `>` needed for CI min version bypass +torchdata >0.5.9, <0.7.0 +# to be able to include also PL 2.0 and preserve `>` needed for CI min version bypass +torch >0.14.0, <2.1.0 diff --git a/requirements/data/examples.txt b/requirements/data/examples.txt new file mode 100644 index 0000000..4daff66 --- /dev/null +++ b/requirements/data/examples.txt @@ -0,0 +1,3 @@ +Pillow >= 9.5.0 +# min version to match torch >= 2.0.1 +torchvision >=0.15.2, <=0.16 diff --git a/requirements/data/test.txt b/requirements/data/test.txt new file mode 100644 index 0000000..55459ea --- /dev/null +++ b/requirements/data/test.txt @@ -0,0 +1,5 @@ +coverage ==7.2.7 +pytest ==7.3.1 +pytest-cov ==4.0.0 +pytest-rerunfailures ==10.3 +pytest-random-order ==1.1.0 diff --git a/requirements/docs.txt b/requirements/docs.txt new file mode 100644 index 0000000..b4bd535 --- /dev/null +++ b/requirements/docs.txt @@ -0,0 +1,19 @@ +sphinx >=4.0, <5.0 +myst-parser >=0.18.1, <1.0.0 +nbsphinx >=0.8.5, <=0.8.9 +pandoc >=1.0, <=2.3 +docutils >=0.16, <0.21 +sphinxcontrib-fulltoc >=1.0, <=1.2.0 +sphinxcontrib-mockautodoc +sphinx-autobuild +sphinx-autodoc-typehints >=1.16 +sphinx-paramlinks >=0.5.1, <=0.5.4 +sphinx-togglebutton >=0.2, <=0.3.2 +sphinx-copybutton >=0.3, <=0.5.2 +sphinx-multiproject +sphinx-toolbox ==3.4.0 +sphinx-rtd-dark-mode +sphinxcontrib-video ==0.2.0 +jinja2 <3.2.0 + +lightning-utilities >=0.9.0 diff --git a/requirements/fabric/base.txt b/requirements/fabric/base.txt new file mode 100644 index 0000000..6f479b8 --- /dev/null +++ b/requirements/fabric/base.txt @@ -0,0 +1,9 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment + +numpy >=1.17.2, <1.25.1 +torch >=1.11.0, <2.1.0 +fsspec[http]>2021.06.0, <2023.5.0 +packaging >=20.0, <=23.0 +typing-extensions >=4.0.0, <=4.7.1 +lightning-utilities >=0.8.0, <0.10.0 diff --git a/requirements/fabric/docs.txt b/requirements/fabric/docs.txt new file mode 100644 index 0000000..3d55fb8 --- /dev/null +++ b/requirements/fabric/docs.txt @@ -0,0 +1,5 @@ +-r ../docs.txt + +# pt-lightning-sphinx-theme @ https://github.com/Lightning-AI/lightning_sphinx_theme/archive/master.zip +pt-lightning-sphinx-theme @ https://github.com/PyTorchKorea/lightning_sphinx_theme/archive/master.zip +tensorboard diff --git a/requirements/fabric/examples.txt b/requirements/fabric/examples.txt new file mode 100644 index 0000000..b609512 --- /dev/null +++ b/requirements/fabric/examples.txt @@ -0,0 +1,5 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment +torchvision >=0.12.0, <=0.15.2 +torchmetrics >=0.10.0, <0.12.0 +lightning-utilities >=0.8.0, <0.10.0 diff --git a/requirements/fabric/strategies.txt b/requirements/fabric/strategies.txt new file mode 100644 index 0000000..dba2b79 --- /dev/null +++ b/requirements/fabric/strategies.txt @@ -0,0 +1,3 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment +deepspeed >=0.8.2, <=0.9.3; platform_system != "Windows" diff --git a/requirements/fabric/test.txt b/requirements/fabric/test.txt new file mode 100644 index 0000000..a26e7e0 --- /dev/null +++ b/requirements/fabric/test.txt @@ -0,0 +1,8 @@ +coverage ==7.2.7 +pytest ==7.3.1 +pytest-cov ==4.0.0 +pytest-rerunfailures ==10.3 +pytest-random-order ==1.1.0 +click ==8.1.3 +tensorboardX >=2.2, <=2.6.1 # min version is set by torch.onnx missing attribute +torchmetrics >=0.7.0, <1.1.0 # needed for using fixed compare_version diff --git a/requirements/pytorch/base.txt b/requirements/pytorch/base.txt new file mode 100644 index 0000000..6f685aa --- /dev/null +++ b/requirements/pytorch/base.txt @@ -0,0 +1,12 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment + +numpy >=1.17.2, <1.25.1 +torch >=1.11.0, <2.1.0 +tqdm >=4.57.0, <4.66.0 +PyYAML >=5.4, <=6.0 +fsspec[http] >2021.06.0, <2023.5.0 +torchmetrics >=0.7.0, <1.1.0 # needed for using fixed compare_version +packaging >=20.0, <=23.0 +typing-extensions >=4.0.0, <=4.7.1 +lightning-utilities >=0.8.0, <0.10.0 diff --git a/requirements/pytorch/check-avail-extras.py b/requirements/pytorch/check-avail-extras.py new file mode 100644 index 0000000..3ab8d28 --- /dev/null +++ b/requirements/pytorch/check-avail-extras.py @@ -0,0 +1,6 @@ +if __name__ == "__main__": + import hydra # noqa: F401 + import jsonargparse # noqa: F401 + import matplotlib # noqa: F401 + import omegaconf # noqa: F401 + import rich # noqa: F401 diff --git a/requirements/pytorch/docs.txt b/requirements/pytorch/docs.txt new file mode 100644 index 0000000..ff76cdc --- /dev/null +++ b/requirements/pytorch/docs.txt @@ -0,0 +1,11 @@ +-r ../docs.txt + +# ipython[notebook] <8.7.0 +ipython[notebook]==8.10 +# setuptools<58.0 # workaround for `error in ipython setup command: use_2to3 is invalid.` +setuptools==65.5.1 + +# pt-lightning-sphinx-theme @ https://github.com/Lightning-AI/lightning_sphinx_theme/archive/master.zip +pt-lightning-sphinx-theme@https://github.com/PyTorchKorea/lightning_sphinx_theme/archive/master.zip + +-r ../../_notebooks/.actions/requires.txt diff --git a/requirements/pytorch/examples.txt b/requirements/pytorch/examples.txt new file mode 100644 index 0000000..65ed18b --- /dev/null +++ b/requirements/pytorch/examples.txt @@ -0,0 +1,7 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment +torchvision >=0.12.0, <=0.15.2 +gym[classic_control] >=0.17.0, <0.26.3 +ipython[all] <8.14.1 +torchmetrics >=0.10.0, <0.12.0 +lightning-utilities >=0.7.0, <0.10.0 diff --git a/requirements/pytorch/extra.txt b/requirements/pytorch/extra.txt new file mode 100644 index 0000000..414d9bf --- /dev/null +++ b/requirements/pytorch/extra.txt @@ -0,0 +1,10 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment + +# extended list of package dependencies to reach full functionality +matplotlib>3.1, <3.6.2 +omegaconf >=2.0.5, <2.4.0 +hydra-core >=1.0.5, <1.4.0 +jsonargparse[signatures] >=4.18.0, <4.23.0 +rich >=12.3.0, <=13.4.2 +tensorboardX >=2.2, <=2.6.1 # min version is set by torch.onnx missing attribute diff --git a/requirements/pytorch/loggers.info b/requirements/pytorch/loggers.info new file mode 100644 index 0000000..ad8c6a8 --- /dev/null +++ b/requirements/pytorch/loggers.info @@ -0,0 +1,6 @@ +# all supported loggers. this list is here as a reference, but they are not installed in CI +neptune +comet-ml +mlflow >=1.0.0 +wandb >=0.12.0 +tensorboard >=2.9.1 diff --git a/requirements/pytorch/strategies.txt b/requirements/pytorch/strategies.txt new file mode 100644 index 0000000..dba2b79 --- /dev/null +++ b/requirements/pytorch/strategies.txt @@ -0,0 +1,3 @@ +# NOTE: the upper bound for the package version is only set for CI stability, and it is dropped while installing this package +# in case you want to preserve/enforce restrictions on the latest compatible version, add "strict" as an in-line comment +deepspeed >=0.8.2, <=0.9.3; platform_system != "Windows" diff --git a/requirements/pytorch/test.txt b/requirements/pytorch/test.txt new file mode 100644 index 0000000..12ce033 --- /dev/null +++ b/requirements/pytorch/test.txt @@ -0,0 +1,19 @@ +coverage ==7.2.7 +pytest ==7.3.1 +pytest-cov ==4.0.0 +pytest-forked ==1.4.0 +pytest-rerunfailures ==10.3 +pytest-random-order ==1.1.0 + +# needed in tests +cloudpickle >=1.3, <2.3.0 +scikit-learn >0.22.1, <1.3.1 +onnx <1.15.0 +onnxruntime <1.16.0 +psutil <5.9.5 # for `DeviceStatsMonitor` +pandas >1.0, <2.0.3 # needed in benchmarks +fastapi <0.100.0 # for `ServableModuleValidator` +uvicorn <0.22.1 # for `ServableModuleValidator` + +tensorboard >=2.9.1, <2.14.0 # for `TensorBoardLogger` +protobuf <=3.20.1 # strict # an extra is updating protobuf, this pin prevents TensorBoard failure diff --git a/requirements/typing.txt b/requirements/typing.txt new file mode 100644 index 0000000..ba7adf0 --- /dev/null +++ b/requirements/typing.txt @@ -0,0 +1,20 @@ +mypy==1.4.1 +torch==2.0.1 + +types-Markdown +types-PyYAML +types-bleach +types-cachetools +types-croniter +types-paramiko +types-protobuf +types-python-dateutil +types-redis +types-requests +types-setuptools +types-six +types-tabulate +types-toml +types-tzlocal +types-ujson +types-decorator diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..043308e --- /dev/null +++ b/setup.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# Copyright The Lightning AI team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""This is the main and only one setup entry point for installing each package as stand-alone as well as joint +installation for all packages. + +There are considered three main scenarios for installing this project: + +1. Using PyPI registry when you can install `pytorch-lightning`, `lightning-app`, etc. or `lightning` for all. + +2. Installation from source code after cloning repository. + In such case we recommend to use command `pip install .` or `pip install -e .` for development version + (development ver. do not copy python files to your pip file system, just create links, so you can edit here) + In case you want to install just one package you need to export env. variable before calling `pip` + + - for `pytorch-lightning` use `export PACKAGE_NAME=pytorch ; pip install .` + - for `lightning-fabric` use `export PACKAGE_NAME=fabric ; pip install .` + - for `lightning-app` use `export PACKAGE_NAME=app ; pip install .` + +3. Building packages as sdist or binary wheel and installing or publish to PyPI afterwords you use command + `python setup.py sdist` or `python setup.py bdist_wheel` accordingly. + In case you want to build just a particular package you want to set an environment variable: + `PACKAGE_NAME=lightning|pytorch|app|fabric python setup.py sdist|bdist_wheel` + +4. Automated releasing with GitHub action is natural extension of 3) is composed of three consecutive steps: + a) determine which packages shall be released based on version increment in `__version__.py` and eventually + compared against PyPI registry + b) with a parameterization build desired packages in to standard `dist/` folder + c) validate packages and publish to PyPI +""" +import contextlib +import glob +import logging +import os +import tempfile +from importlib.util import module_from_spec, spec_from_file_location +from types import ModuleType +from typing import Generator, Mapping, Optional + +import setuptools +import setuptools.command.egg_info + +_PACKAGE_NAME = os.environ.get("PACKAGE_NAME") +_PACKAGE_MAPPING = { + "lightning": "lightning", + "pytorch": "pytorch_lightning", + "app": "lightning_app", + "fabric": "lightning_fabric", +} +# https://packaging.python.org/guides/single-sourcing-package-version/ +# http://blog.ionelmc.ro/2014/05/25/python-packaging/ +_PATH_ROOT = os.path.dirname(__file__) +_PATH_SRC = os.path.join(_PATH_ROOT, "src") +_PATH_REQUIRE = os.path.join(_PATH_ROOT, "requirements") +_FREEZE_REQUIREMENTS = os.environ.get("FREEZE_REQUIREMENTS", "0").lower() in ("1", "true") + + +def _load_py_module(name: str, location: str) -> ModuleType: + spec = spec_from_file_location(name, location) + assert spec, f"Failed to load module {name} from {location}" + py = module_from_spec(spec) + assert spec.loader, f"ModuleSpec.loader is None for {name} from {location}" + spec.loader.exec_module(py) + return py + + +def _named_temporary_file(directory: Optional[str] = None) -> str: + # `tempfile.NamedTemporaryFile` has issues in Windows + # https://github.com/deepchem/deepchem/issues/707#issuecomment-556002823 + if directory is None: + directory = tempfile.gettempdir() + return os.path.join(directory, os.urandom(24).hex()) + + +@contextlib.contextmanager +def _set_manifest_path(manifest_dir: str, aggregate: bool = False, mapping: Mapping = _PACKAGE_MAPPING) -> Generator: + if aggregate: + # aggregate all MANIFEST.in contents into a single temporary file + manifest_path = _named_temporary_file(manifest_dir) + lines = [] + # load manifest and aggregated all manifests + for pkg in mapping.values(): + pkg_manifest = os.path.join(_PATH_SRC, pkg, "MANIFEST.in") + if os.path.isfile(pkg_manifest): + with open(pkg_manifest) as fh: + lines.extend(fh.readlines()) + # convert lightning_foo to lightning/foo + for new, old in mapping.items(): + if old == "lightning": + continue # avoid `lightning` -> `lightning/lightning` + lines = [ln.replace(old, f"lightning/{new}") for ln in lines] + lines = sorted(set(filter(lambda ln: not ln.strip().startswith("#"), lines))) + logging.debug(f"aggregated manifest consists of: {lines}") + with open(manifest_path, mode="w") as fp: + fp.writelines(lines) + else: + manifest_path = os.path.join(manifest_dir, "MANIFEST.in") + assert os.path.exists(manifest_path) + # avoid error: setup script specifies an absolute path + manifest_path = os.path.relpath(manifest_path, _PATH_ROOT) + logging.info("Set manifest path to", manifest_path) + setuptools.command.egg_info.manifest_maker.template = manifest_path + yield + # cleanup + setuptools.command.egg_info.manifest_maker.template = "MANIFEST.in" + if aggregate: + os.remove(manifest_path) + + +if __name__ == "__main__": + assistant = _load_py_module(name="assistant", location=os.path.join(_PATH_ROOT, ".actions", "assistant.py")) + + if os.path.isdir(_PATH_SRC): + # copy the version information to all packages + assistant.distribute_version(_PATH_SRC) + print(f"Requested package: '{_PACKAGE_NAME}'") # requires `-v` to appear + + local_pkgs = [ + os.path.basename(p) + for p in glob.glob(os.path.join(_PATH_SRC, "*")) + if os.path.isdir(p) and not p.endswith(".egg-info") + ] + print(f"Local package candidates: {local_pkgs}") + is_source_install = len(local_pkgs) > 2 + print(f"Installing from source: {is_source_install}") + if is_source_install: + if _PACKAGE_NAME is not None and _PACKAGE_NAME not in _PACKAGE_MAPPING: + raise ValueError( + f"Unexpected package name: {_PACKAGE_NAME}. Possible choices are: {list(_PACKAGE_MAPPING)}" + ) + package_to_install = _PACKAGE_MAPPING.get(_PACKAGE_NAME, "lightning") + if package_to_install == "lightning": + # merge all requirements files + assistant._load_aggregate_requirements(_PATH_REQUIRE, _FREEZE_REQUIREMENTS) + else: + # replace imports and copy the code + assistant.create_mirror_package(_PATH_SRC, _PACKAGE_MAPPING) + else: + assert len(local_pkgs) > 0 + # PL as a package is distributed together with Fabric, so in such case there are more than one candidate + package_to_install = "pytorch_lightning" if "pytorch_lightning" in local_pkgs else local_pkgs[0] + print(f"Installing package: {package_to_install}") + + # going to install with `setuptools.setup` + pkg_path = os.path.join(_PATH_SRC, package_to_install) + pkg_setup = os.path.join(pkg_path, "__setup__.py") + if not os.path.exists(pkg_setup): + raise RuntimeError(f"Something's wrong, no package was installed. Package name: {_PACKAGE_NAME}") + setup_module = _load_py_module(name=f"{package_to_install}_setup", location=pkg_setup) + setup_args = setup_module._setup_args() + is_main_pkg = package_to_install == "lightning" + print(f"Installing as the main package: {is_main_pkg}") + if is_source_install: + # we are installing from source, set the correct manifest path + with _set_manifest_path(pkg_path, aggregate=is_main_pkg): + setuptools.setup(**setup_args) + else: + setuptools.setup(**setup_args) + print("Finished setup configuration.") diff --git a/source/_static/images/accelerator/ipus/profiler.png b/source/_static/images/accelerator/ipus/profiler.png deleted file mode 100644 index cbed276..0000000 Binary files a/source/_static/images/accelerator/ipus/profiler.png and /dev/null differ diff --git a/source/_static/images/benchmarks/figure-parity-times.png b/source/_static/images/benchmarks/figure-parity-times.png deleted file mode 100644 index 2e8c589..0000000 Binary files a/source/_static/images/benchmarks/figure-parity-times.png and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/01-introduction-to-pytorch.jpg b/source/_static/images/course_UvA-DL/01-introduction-to-pytorch.jpg deleted file mode 100644 index a56ca66..0000000 Binary files a/source/_static/images/course_UvA-DL/01-introduction-to-pytorch.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/02-activation-functions.jpg b/source/_static/images/course_UvA-DL/02-activation-functions.jpg deleted file mode 100644 index 1b21f50..0000000 Binary files a/source/_static/images/course_UvA-DL/02-activation-functions.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/03-initialization-and-optimization.jpg b/source/_static/images/course_UvA-DL/03-initialization-and-optimization.jpg deleted file mode 100644 index e8d42d4..0000000 Binary files a/source/_static/images/course_UvA-DL/03-initialization-and-optimization.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/04-inception-resnet-densenet.jpg b/source/_static/images/course_UvA-DL/04-inception-resnet-densenet.jpg deleted file mode 100644 index a7e0205..0000000 Binary files a/source/_static/images/course_UvA-DL/04-inception-resnet-densenet.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/05-transformers-and-MH-attention.jpg b/source/_static/images/course_UvA-DL/05-transformers-and-MH-attention.jpg deleted file mode 100644 index e644f9a..0000000 Binary files a/source/_static/images/course_UvA-DL/05-transformers-and-MH-attention.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/06-graph-neural-networks.jpg b/source/_static/images/course_UvA-DL/06-graph-neural-networks.jpg deleted file mode 100644 index 0cda6bd..0000000 Binary files a/source/_static/images/course_UvA-DL/06-graph-neural-networks.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/07-deep-energy-based-generative-models.jpg b/source/_static/images/course_UvA-DL/07-deep-energy-based-generative-models.jpg deleted file mode 100644 index 32cd948..0000000 Binary files a/source/_static/images/course_UvA-DL/07-deep-energy-based-generative-models.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/08-deep-autoencoders.jpg b/source/_static/images/course_UvA-DL/08-deep-autoencoders.jpg deleted file mode 100644 index 1b07169..0000000 Binary files a/source/_static/images/course_UvA-DL/08-deep-autoencoders.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/09-normalizing-flows.jpg b/source/_static/images/course_UvA-DL/09-normalizing-flows.jpg deleted file mode 100644 index 9654f8a..0000000 Binary files a/source/_static/images/course_UvA-DL/09-normalizing-flows.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/10-autoregressive-image-modeling.jpg b/source/_static/images/course_UvA-DL/10-autoregressive-image-modeling.jpg deleted file mode 100644 index 1ad5d61..0000000 Binary files a/source/_static/images/course_UvA-DL/10-autoregressive-image-modeling.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/11-vision-transformer.jpg b/source/_static/images/course_UvA-DL/11-vision-transformer.jpg deleted file mode 100644 index c129c4b..0000000 Binary files a/source/_static/images/course_UvA-DL/11-vision-transformer.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/12-meta-learning.jpg b/source/_static/images/course_UvA-DL/12-meta-learning.jpg deleted file mode 100644 index 4f8f6d9..0000000 Binary files a/source/_static/images/course_UvA-DL/12-meta-learning.jpg and /dev/null differ diff --git a/source/_static/images/course_UvA-DL/13-contrastive-learning.jpg b/source/_static/images/course_UvA-DL/13-contrastive-learning.jpg deleted file mode 100644 index 6e05cce..0000000 Binary files a/source/_static/images/course_UvA-DL/13-contrastive-learning.jpg and /dev/null differ diff --git a/source/_static/images/general/PTL101_youtube_thumbnail.jpg b/source/_static/images/general/PTL101_youtube_thumbnail.jpg deleted file mode 100644 index a09dc43..0000000 Binary files a/source/_static/images/general/PTL101_youtube_thumbnail.jpg and /dev/null differ diff --git a/source/_static/images/general/fast_2.gif b/source/_static/images/general/fast_2.gif deleted file mode 100644 index 77c6f85..0000000 Binary files a/source/_static/images/general/fast_2.gif and /dev/null differ diff --git a/source/_static/images/general/pl_overview.gif b/source/_static/images/general/pl_overview.gif deleted file mode 100644 index 1aa6419..0000000 Binary files a/source/_static/images/general/pl_overview.gif and /dev/null differ diff --git a/source/_static/images/general/pl_overview_flat.jpg b/source/_static/images/general/pl_overview_flat.jpg deleted file mode 100644 index c7c6480..0000000 Binary files a/source/_static/images/general/pl_overview_flat.jpg and /dev/null differ diff --git a/source/_static/images/general/pl_quick_start_full_compressed.gif b/source/_static/images/general/pl_quick_start_full_compressed.gif deleted file mode 100644 index f7136d0..0000000 Binary files a/source/_static/images/general/pl_quick_start_full_compressed.gif and /dev/null differ diff --git a/source/_static/images/general/tf_loss.jpg b/source/_static/images/general/tf_loss.jpg deleted file mode 100644 index 869947f..0000000 Binary files a/source/_static/images/general/tf_loss.jpg and /dev/null differ diff --git a/source/_static/images/general/tf_tags.jpg b/source/_static/images/general/tf_tags.jpg deleted file mode 100644 index 40918ec..0000000 Binary files a/source/_static/images/general/tf_tags.jpg and /dev/null differ diff --git a/source/_static/images/general/tutorial_cover.jpg b/source/_static/images/general/tutorial_cover.jpg deleted file mode 100644 index 1c0e7f3..0000000 Binary files a/source/_static/images/general/tutorial_cover.jpg and /dev/null differ diff --git a/source/_static/images/icon.svg b/source/_static/images/icon.svg deleted file mode 100644 index 481762a..0000000 --- a/source/_static/images/icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/source/_static/images/lightning_examples/augmentation_kornia.svg b/source/_static/images/lightning_examples/augmentation_kornia.svg deleted file mode 100644 index 481762a..0000000 --- a/source/_static/images/lightning_examples/augmentation_kornia.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/source/_static/images/lightning_lite/lite.gif b/source/_static/images/lightning_lite/lite.gif deleted file mode 100644 index 8413b2d..0000000 Binary files a/source/_static/images/lightning_lite/lite.gif and /dev/null differ diff --git a/source/_static/images/lightning_module/pt_to_pl.png b/source/_static/images/lightning_module/pt_to_pl.png deleted file mode 100644 index 5135ec2..0000000 Binary files a/source/_static/images/lightning_module/pt_to_pl.png and /dev/null differ diff --git a/source/_static/images/lightning_module/pt_trainer.png b/source/_static/images/lightning_module/pt_trainer.png deleted file mode 100644 index f465d43..0000000 Binary files a/source/_static/images/lightning_module/pt_trainer.png and /dev/null differ diff --git a/source/_static/images/logo.png b/source/_static/images/logo.png deleted file mode 100644 index 331f201..0000000 Binary files a/source/_static/images/logo.png and /dev/null differ diff --git a/source/_static/images/logo.svg b/source/_static/images/logo.svg deleted file mode 100644 index dca54b3..0000000 --- a/source/_static/images/logo.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/source/_static/images/logo_light.svg b/source/_static/images/logo_light.svg deleted file mode 100644 index 4695557..0000000 --- a/source/_static/images/logo_light.svg +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - diff --git a/source/_static/images/mnist_imgs/mnist_cpu_bar.png b/source/_static/images/mnist_imgs/mnist_cpu_bar.png deleted file mode 100644 index fa896ea..0000000 Binary files a/source/_static/images/mnist_imgs/mnist_cpu_bar.png and /dev/null differ diff --git a/source/_static/images/mnist_imgs/mnist_gpu.png b/source/_static/images/mnist_imgs/mnist_gpu.png deleted file mode 100644 index 75021ce..0000000 Binary files a/source/_static/images/mnist_imgs/mnist_gpu.png and /dev/null differ diff --git a/source/_static/images/mnist_imgs/mnist_tb.png b/source/_static/images/mnist_imgs/mnist_tb.png deleted file mode 100644 index a8cf719..0000000 Binary files a/source/_static/images/mnist_imgs/mnist_tb.png and /dev/null differ diff --git a/source/_static/images/mnist_imgs/pt_to_pl.jpg b/source/_static/images/mnist_imgs/pt_to_pl.jpg deleted file mode 100644 index 4bad788..0000000 Binary files a/source/_static/images/mnist_imgs/pt_to_pl.jpg and /dev/null differ diff --git a/source/_static/images/mnist_imgs/restart_runtime.png b/source/_static/images/mnist_imgs/restart_runtime.png deleted file mode 100644 index 84ccae4..0000000 Binary files a/source/_static/images/mnist_imgs/restart_runtime.png and /dev/null differ diff --git a/source/_static/images/mnist_imgs/runtime_tpu.png b/source/_static/images/mnist_imgs/runtime_tpu.png deleted file mode 100644 index 9dc069a..0000000 Binary files a/source/_static/images/mnist_imgs/runtime_tpu.png and /dev/null differ diff --git a/source/_static/images/mnist_imgs/tpu_fast.png b/source/_static/images/mnist_imgs/tpu_fast.png deleted file mode 100644 index 08d9f9a..0000000 Binary files a/source/_static/images/mnist_imgs/tpu_fast.png and /dev/null differ diff --git a/source/_static/images/mnist_imgs/tpu_start.png b/source/_static/images/mnist_imgs/tpu_start.png deleted file mode 100644 index 3474f68..0000000 Binary files a/source/_static/images/mnist_imgs/tpu_start.png and /dev/null differ diff --git a/source/_static/images/trainer/lr_finder.png b/source/_static/images/trainer/lr_finder.png deleted file mode 100644 index bd1667b..0000000 Binary files a/source/_static/images/trainer/lr_finder.png and /dev/null differ diff --git a/source/_templates/classtemplate.rst b/source/_templates/classtemplate.rst deleted file mode 100644 index 398a0ec..0000000 --- a/source/_templates/classtemplate.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: {{ module }} - - -{{ name | underline }} - -.. autoclass:: {{ name }} - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: diff --git a/source/_templates/theme_variables.jinja b/source/_templates/theme_variables.jinja deleted file mode 100644 index 5073b7a..0000000 --- a/source/_templates/theme_variables.jinja +++ /dev/null @@ -1,20 +0,0 @@ -{%- set external_urls = { - 'github': 'https://github.com/PyTorchLightning/pytorch-lightning', - 'github_issues': 'https://github.com/PyTorchLightning/pytorch-lightning/issues', - 'contributing': 'https://github.com/PyTorchLightning/pytorch-lightning/blob/master/CONTRIBUTING.md', - 'governance': 'https://pytorch-lightning.readthedocs.io/en/latest/governance.html', - 'docs': 'https://pytorch-lightning.rtfd.io/en/latest', - 'twitter': 'https://twitter.com/PyTorchLightnin', - 'discuss': 'https://www.pytorchlightning.ai/community', - 'tutorials': 'https://pytorch-lightning.readthedocs.io/en/latest/#tutorials', - 'previous_pytorch_versions': 'https://pytorch-lightning.rtfd.io/en/latest/', - 'home': 'https://pytorch-lightning.rtfd.io/en/latest/', - 'get_started': 'https://pytorch-lightning.readthedocs.io/en/latest/starter/introduction.html', - 'features': 'https://pytorch-lightning.rtfd.io/en/latest/', - 'blog': 'https://www.pytorchlightning.ai/blog', - 'resources': 'https://pytorch-lightning.readthedocs.io/en/latest/#community-examples', - 'support': 'https://pytorch-lightning.rtfd.io/en/latest/', - 'community': 'https://www.pytorchlightning.ai/community', - 'forums': 'https://github.com/PyTorchLightning/pytorch-lightning/discussions', -} --%} diff --git a/source/accelerators/gpu_advanced.rst b/source/accelerators/gpu_advanced.rst deleted file mode 100644 index eadeb03..0000000 --- a/source/accelerators/gpu_advanced.rst +++ /dev/null @@ -1,16 +0,0 @@ -:orphan: - -.. _gpu_advanced: - -GPU training (Advanced) -======================= -**Audience:** Users looking to scale massive models (ie: 1 Trillion parameters). - ----- - -For experts pushing the state-of-the-art in model development, Lightning offers various techniques to enable Trillion+ parameter-scale models. - ----- - -.. - .. include:: ../advanced/model_parallel.rst diff --git a/source/accelerators/gpu_basic.rst b/source/accelerators/gpu_basic.rst deleted file mode 100644 index 43be718..0000000 --- a/source/accelerators/gpu_basic.rst +++ /dev/null @@ -1,97 +0,0 @@ -:orphan: - -.. _gpu_basic: - -GPU training (Basic) -==================== -**Audience:** Users looking to save money and run large models faster using single or multiple - ----- - -What is a GPU? --------------- -A Graphics Processing Unit (GPU), is a specialized hardware accelerator designed to speed up mathematical computations used in gaming and deep learning. - ----- - -Train on 1 GPU --------------- - -Make sure you're running on a machine with at least one GPU. There's no need to specify any NVIDIA flags -as Lightning will do it for you. - -.. testcode:: - :skipif: torch.cuda.device_count() < 1 - - trainer = Trainer(accelerator="gpu", devices=1) - ----------------- - - -.. _multi_gpu: - -Train on multiple GPUs ----------------------- - -To use multiple GPUs, set the number of devices in the Trainer or the index of the GPUs. - -.. code:: - - trainer = Trainer(accelerator="gpu", devices=4) - -Choosing GPU devices -^^^^^^^^^^^^^^^^^^^^ - -You can select the GPU devices using ranges, a list of indices or a string containing -a comma separated list of GPU ids: - -.. testsetup:: - - k = 1 - -.. testcode:: - :skipif: torch.cuda.device_count() < 2 - - # DEFAULT (int) specifies how many GPUs to use per node - Trainer(accelerator="gpu", devices=k) - - # Above is equivalent to - Trainer(accelerator="gpu", devices=list(range(k))) - - # Specify which GPUs to use (don't use when running on cluster) - Trainer(accelerator="gpu", devices=[0, 1]) - - # Equivalent using a string - Trainer(accelerator="gpu", devices="0, 1") - - # To use all available GPUs put -1 or '-1' - # equivalent to list(range(torch.cuda.device_count())) - Trainer(accelerator="gpu", devices=-1) - -The table below lists examples of possible input formats and how they are interpreted by Lightning. - -+------------------+-----------+---------------------+---------------------------------+ -| `devices` | Type | Parsed | Meaning | -+==================+===========+=====================+=================================+ -| 3 | int | [0, 1, 2] | first 3 GPUs | -+------------------+-----------+---------------------+---------------------------------+ -| -1 | int | [0, 1, 2, ...] | all available GPUs | -+------------------+-----------+---------------------+---------------------------------+ -| [0] | list | [0] | GPU 0 | -+------------------+-----------+---------------------+---------------------------------+ -| [1, 3] | list | [1, 3] | GPUs 1 and 3 | -+------------------+-----------+---------------------+---------------------------------+ -| "3" | str | [0, 1, 2] | first 3 GPUs | -+------------------+-----------+---------------------+---------------------------------+ -| "1, 3" | str | [1, 3] | GPUs 1 and 3 | -+------------------+-----------+---------------------+---------------------------------+ -| "-1" | str | [0, 1, 2, ...] | all available GPUs | -+------------------+-----------+---------------------+---------------------------------+ - -.. note:: - - When specifying number of ``devices`` as an integer ``devices=k``, setting the trainer flag - ``auto_select_gpus=True`` will automatically help you find ``k`` GPUs that are not - occupied by other processes. This is especially useful when GPUs are configured - to be in "exclusive mode", such that only one process at a time can access them. - For more details see the :doc:`trainer guide <../common/trainer>`. diff --git a/source/accelerators/gpu_faq.rst b/source/accelerators/gpu_faq.rst deleted file mode 100644 index c697b2c..0000000 --- a/source/accelerators/gpu_faq.rst +++ /dev/null @@ -1,97 +0,0 @@ -:orphan: - -.. _gpu_faq: - -GPU training (FAQ) -================== - -****************************************************************** -How should I adjust the learning rate when using multiple devices? -****************************************************************** - -When using distributed training make sure to modify your learning rate according to your effective -batch size. - -Let's say you have a batch size of 7 in your dataloader. - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - return Dataset(..., batch_size=7) - -In DDP, DDP_SPAWN, Deepspeed, DDP_SHARDED, or Horovod your effective batch size will be 7 * devices * num_nodes. - -.. code-block:: python - - # effective batch size = 7 * 8 - Trainer(accelerator="gpu", devices=8, strategy="ddp") - Trainer(accelerator="gpu", devices=8, strategy="ddp_spawn") - Trainer(accelerator="gpu", devices=8, strategy="ddp_sharded") - Trainer(accelerator="gpu", devices=8, strategy="horovod") - - # effective batch size = 7 * 8 * 10 - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp") - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp_spawn") - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp_sharded") - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="horovod") - -In DDP2 or DP, your effective batch size will be 7 * num_nodes. -The reason is that the full batch is visible to all GPUs on the node when using DDP2. - -.. code-block:: python - - # effective batch size = 7 - Trainer(accelerator="gpu", devices=8, strategy="ddp2") - Trainer(accelerator="gpu", devices=8, strategy="dp") - - # effective batch size = 7 * 10 - Trainer(accelerator="gpu", devices=8, num_nodes=10, strategy="ddp2") - Trainer(accelerator="gpu", devices=8, strategy="dp") - - -.. note:: Huge batch sizes are actually really bad for convergence. Check out: - `Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour `_ - ----- - -********************************************************* -How do I use multiple GPUs on Jupyter or Colab notebooks? -********************************************************* - -To use multiple GPUs on notebooks, use the *DP* mode. - -.. code-block:: python - - Trainer(accelerator="gpu", devices=4, strategy="dp") - -If you want to use other models, please launch your training via the command-shell. - -.. note:: Learn how to :ref:`access a cloud machine with multiple GPUs ` in this guide. - ----- - -***************************************************** -I'm getting errors related to Pickling. What do I do? -***************************************************** - -Pickle is Python's mechanism for serializing and unserializing data. A majority of distributed modes require that your code is fully pickle compliant. If you run into an issue with pickling try the following to figure out the issue - -.. code-block:: python - - import pickle - - model = YourModel() - pickle.dumps(model) - -If you `ddp` your code doesn't need to be pickled. - -.. code-block:: python - - Trainer(accelerator="gpu", devices=4, strategy="ddp") - -If you use `ddp_spawn` the pickling requirement remains. This is a limitation of Python. - -.. code-block:: python - - Trainer(accelerator="gpu", devices=4, strategy="ddp_spawn") diff --git a/source/accelerators/gpu_intermediate.rst b/source/accelerators/gpu_intermediate.rst deleted file mode 100644 index c4d9ad8..0000000 --- a/source/accelerators/gpu_intermediate.rst +++ /dev/null @@ -1,533 +0,0 @@ -:orphan: - -.. _gpu_intermediate: - -GPU training (Intermediate) -=========================== -**Audience:** Users looking to train across machines or experiment with different scaling techniques. - ----- - -Distributed Training strategies -------------------------------- -Lightning supports multiple ways of doing distributed training. - -.. raw:: html - - - -| - -- Data Parallel (``strategy='dp'``) (multiple-gpus, 1 machine) -- DistributedDataParallel (``strategy='ddp'``) (multiple-gpus across many machines (python script based)). -- DistributedDataParallel (``strategy='ddp_spawn'``) (multiple-gpus across many machines (spawn based)). -- DistributedDataParallel 2 (``strategy='ddp2'``) (DP in a machine, DDP across machines). -- Horovod (``strategy='horovod'``) (multi-machine, multi-gpu, configured at runtime) -- Bagua (``strategy='bagua'``) (multiple-gpus across many machines with advanced training algorithms) - -.. note:: - If you request multiple GPUs or nodes without setting a mode, DDP Spawn will be automatically used. - -For a deeper understanding of what Lightning is doing, feel free to read this -`guide `_. - - -Data Parallel -^^^^^^^^^^^^^ -:class:`~torch.nn.DataParallel` (DP) splits a batch across k GPUs. -That is, if you have a batch of 32 and use DP with 2 GPUs, each GPU will process 16 samples, -after which the root node will aggregate the results. - -.. warning:: DP use is discouraged by PyTorch and Lightning. State is not maintained on the replicas created by the - :class:`~torch.nn.DataParallel` wrapper and you may see errors or misbehavior if you assign state to the module - in the ``forward()`` or ``*_step()`` methods. For the same reason we cannot fully support - :doc:`Manual Optimization <../model/manual_optimization>` with DP. Use DDP which is more stable and at least 3x faster. - -.. warning:: DP only supports scattering and gathering primitive collections of tensors like lists, dicts, etc. - Therefore the :meth:`~pytorch_lightning.core.hooks.ModelHooks.transfer_batch_to_device` hook does not apply in - this mode and if you have overridden it, it will not be called. - -.. testcode:: - :skipif: torch.cuda.device_count() < 2 - - # train on 2 GPUs (using DP mode) - trainer = Trainer(accelerator="gpu", devices=2, strategy="dp") - -Distributed Data Parallel -^^^^^^^^^^^^^^^^^^^^^^^^^ -:class:`~torch.nn.parallel.DistributedDataParallel` (DDP) works as follows: - -1. Each GPU across each node gets its own process. - -2. Each GPU gets visibility into a subset of the overall dataset. It will only ever see that subset. - -3. Each process inits the model. - -4. Each process performs a full forward and backward pass in parallel. - -5. The gradients are synced and averaged across all processes. - -6. Each process updates its optimizer. - -.. code-block:: python - - # train on 8 GPUs (same machine (ie: node)) - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp") - - # train on 32 GPUs (4 nodes) - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp", num_nodes=4) - -This Lightning implementation of DDP calls your script under the hood multiple times with the correct environment -variables: - -.. code-block:: bash - - # example for 3 GPUs DDP - MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=0 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc - MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=1 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc - MASTER_ADDR=localhost MASTER_PORT=random() WORLD_SIZE=3 NODE_RANK=2 LOCAL_RANK=0 python my_file.py --accelerator 'gpu' --devices 3 --etc - -We use DDP this way because `ddp_spawn` has a few limitations (due to Python and PyTorch): - -1. Since `.spawn()` trains the model in subprocesses, the model on the main process does not get updated. -2. Dataloader(num_workers=N), where N is large, bottlenecks training with DDP... ie: it will be VERY slow or won't work at all. This is a PyTorch limitation. -3. Forces everything to be picklable. - -There are cases in which it is NOT possible to use DDP. Examples are: - -- Jupyter Notebook, Google COLAB, Kaggle, etc. -- You have a nested script without a root package - -In these situations you should use `dp` or `ddp_spawn` instead. - -Distributed Data Parallel 2 -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -In certain cases, it's advantageous to use all batches on the same machine instead of a subset. -For instance, you might want to compute a NCE loss where it pays to have more negative samples. - -In this case, we can use DDP2 which behaves like DP in a machine and DDP across nodes. DDP2 does the following: - -1. Copies a subset of the data to each node. - -2. Inits a model on each node. - -3. Runs a forward and backward pass using DP. - -4. Syncs gradients across nodes. - -5. Applies the optimizer updates. - -.. code-block:: python - - # train on 32 GPUs (4 nodes) - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp2", num_nodes=4) - -Distributed Data Parallel Spawn -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -`ddp_spawn` is exactly like `ddp` except that it uses .spawn to start the training processes. - -.. warning:: It is STRONGLY recommended to use `DDP` for speed and performance. - -.. code-block:: python - - mp.spawn(self.ddp_train, nprocs=self.num_processes, args=(model,)) - -If your script does not support being called from the command line (ie: it is nested without a root -project module) you can use the following method: - -.. code-block:: python - - # train on 8 GPUs (same machine (ie: node)) - trainer = Trainer(accelerator="gpu", devices=8, strategy="ddp_spawn") - -We STRONGLY discourage this use because it has limitations (due to Python and PyTorch): - -1. The model you pass in will not update. Please save a checkpoint and restore from there. -2. Set Dataloader(num_workers=0) or it will bottleneck training. - -`ddp` is MUCH faster than `ddp_spawn`. We recommend you - -1. Install a top-level module for your project using setup.py - -.. code-block:: python - - # setup.py - #!/usr/bin/env python - - from setuptools import setup, find_packages - - setup( - name="src", - version="0.0.1", - description="Describe Your Cool Project", - author="", - author_email="", - url="https://github.com/YourSeed", # REPLACE WITH YOUR OWN GITHUB PROJECT LINK - install_requires=["pytorch-lightning"], - packages=find_packages(), - ) - -2. Setup your project like so: - -.. code-block:: bash - - /project - /src - some_file.py - /or_a_folder - setup.py - -3. Install as a root-level package - -.. code-block:: bash - - cd /project - pip install -e . - -You can then call your scripts anywhere - -.. code-block:: bash - - cd /project/src - python some_file.py --accelerator 'gpu' --devices 8 --strategy 'ddp' - - -Horovod -^^^^^^^ -`Horovod `_ allows the same training script to be used for single-GPU, -multi-GPU, and multi-node training. - -Like Distributed Data Parallel, every process in Horovod operates on a single GPU with a fixed -subset of the data. Gradients are averaged across all GPUs in parallel during the backward pass, -then synchronously applied before beginning the next step. - -The number of worker processes is configured by a driver application (`horovodrun` or `mpirun`). In -the training script, Horovod will detect the number of workers from the environment, and automatically -scale the learning rate to compensate for the increased total batch size. - -Horovod can be configured in the training script to run with any number of GPUs / processes as follows: - -.. code-block:: python - - # train Horovod on GPU (number of GPUs / machines provided on command-line) - trainer = Trainer(strategy="horovod", accelerator="gpu", devices=1) - - # train Horovod on CPU (number of processes / machines provided on command-line) - trainer = Trainer(strategy="horovod") - -When starting the training job, the driver application will then be used to specify the total -number of worker processes: - -.. code-block:: bash - - # run training with 4 GPUs on a single machine - horovodrun -np 4 python train.py - - # run training with 8 GPUs on two machines (4 GPUs each) - horovodrun -np 8 -H hostname1:4,hostname2:4 python train.py - -See the official `Horovod documentation `_ for details -on installation and performance tuning. - - -Bagua -^^^^^ -`Bagua `_ is a deep learning training acceleration framework which supports -multiple advanced distributed training algorithms including: - -- `Gradient AllReduce `_ for centralized synchronous communication, where gradients are averaged among all workers. -- `Decentralized SGD `_ for decentralized synchronous communication, where each worker exchanges data with one or a few specific workers. -- `ByteGrad `_ and `QAdam `_ for low precision communication, where data is compressed into low precision before communication. -- `Asynchronous Model Average `_ for asynchronous communication, where workers are not required to be synchronized in the same iteration in a lock-step style. - -By default, Bagua uses *Gradient AllReduce* algorithm, which is also the algorithm implemented in Distributed Data Parallel and Horovod, -but Bagua can usually produce a higher training throughput due to its backend written in Rust. - -.. code-block:: python - - # train on 4 GPUs (using Bagua mode) - trainer = Trainer(strategy="bagua", accelerator="gpu", devices=4) - - -By specifying the ``algorithm`` in the ``BaguaStrategy``, you can select more advanced training algorithms featured by Bagua: - - -.. code-block:: python - - # train on 4 GPUs, using Bagua Gradient AllReduce algorithm - trainer = Trainer( - strategy=BaguaStrategy(algorithm="gradient_allreduce"), - accelerator="gpu", - devices=4, - ) - - # train on 4 GPUs, using Bagua ByteGrad algorithm - trainer = Trainer( - strategy=BaguaStrategy(algorithm="bytegrad"), - accelerator="gpu", - devices=4, - ) - - # train on 4 GPUs, using Bagua Decentralized SGD - trainer = Trainer( - strategy=BaguaStrategy(algorithm="decentralized"), - accelerator="gpu", - devices=4, - ) - - # train on 4 GPUs, using Bagua Low Precision Decentralized SGD - trainer = Trainer( - strategy=BaguaStrategy(algorithm="low_precision_decentralized"), - accelerator="gpu", - devices=4, - ) - - # train on 4 GPUs, using Asynchronous Model Average algorithm, with a synchronization interval of 100ms - trainer = Trainer( - strategy=BaguaStrategy(algorithm="async", sync_interval_ms=100), - accelerator="gpu", - devices=4, - ) - -To use *QAdam*, we need to initialize -`QAdamOptimizer `_ first: - -.. code-block:: python - - from pytorch_lightning.strategies import BaguaStrategy - from bagua.torch_api.algorithms.q_adam import QAdamOptimizer - - - class MyModel(pl.LightningModule): - ... - - def configure_optimizers(self): - # initialize QAdam Optimizer - return QAdamOptimizer(self.parameters(), lr=0.05, warmup_steps=100) - - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=BaguaStrategy(algorithm="qadam"), - ) - trainer.fit(model) - -Bagua relies on its own `launcher `_ to schedule jobs. -Below, find examples using ``bagua.distributed.launch`` which follows ``torch.distributed.launch`` API: - -.. code-block:: bash - - # start training with 8 GPUs on a single node - python -m bagua.distributed.launch --nproc_per_node=8 train.py - -If the ssh service is available with passwordless login on each node, you can launch the distributed job on a -single node with ``baguarun`` which has a similar syntax as ``mpirun``. When staring the job, ``baguarun`` will -automatically spawn new processes on each of your training node provided by ``--host_list`` option and each node in it -is described as an ip address followed by a ssh port. - -.. code-block:: bash - - # Run on node1 (or node2) to start training on two nodes (node1 and node2), 8 GPUs per node - baguarun --host_list hostname1:ssh_port1,hostname2:ssh_port2 --nproc_per_node=8 --master_port=port1 train.py - - -.. note:: You can also start training in the same way as Distributed Data Parallel. However, system optimizations like - `Bagua-Net `_ and - `Performance autotuning `_ can only be enabled through bagua - launcher. It is worth noting that with ``Bagua-Net``, Distributed Data Parallel can also achieve - better performance without modifying the training script. - - -See `Bagua Tutorials `_ for more details on installation and advanced features. - - -DP/DDP2 caveats -^^^^^^^^^^^^^^^ -In DP and DDP2 each GPU within a machine sees a portion of a batch. -DP and ddp2 roughly do the following: - -.. testcode:: - - def distributed_forward(batch, model): - batch = torch.Tensor(32, 8) - gpu_0_batch = batch[:8] - gpu_1_batch = batch[8:16] - gpu_2_batch = batch[16:24] - gpu_3_batch = batch[24:] - - y_0 = model_copy_gpu_0(gpu_0_batch) - y_1 = model_copy_gpu_1(gpu_1_batch) - y_2 = model_copy_gpu_2(gpu_2_batch) - y_3 = model_copy_gpu_3(gpu_3_batch) - - return [y_0, y_1, y_2, y_3] - -So, when Lightning calls any of the `training_step`, `validation_step`, `test_step` -you will only be operating on one of those pieces. - -.. testcode:: - - # the batch here is a portion of the FULL batch - def training_step(self, batch, batch_idx): - y_0 = batch - -For most metrics, this doesn't really matter. However, if you want -to add something to your computational graph (like softmax) -using all batch parts you can use the `training_step_end` step. - -.. testcode:: - - def training_step_end(self, outputs): - # only use when on dp - outputs = torch.cat(outputs, dim=1) - softmax = softmax(outputs, dim=1) - out = softmax.mean() - return out - -In pseudocode, the full sequence is: - -.. code-block:: python - - # get data - batch = next(dataloader) - - # copy model and data to each gpu - batch_splits = split_batch(batch, num_gpus) - models = copy_model_to_gpus(model) - - # in parallel, operate on each batch chunk - all_results = [] - for gpu_num in gpus: - batch_split = batch_splits[gpu_num] - gpu_model = models[gpu_num] - out = gpu_model(batch_split) - all_results.append(out) - - # use the full batch for something like softmax - full_out = model.training_step_end(all_results) - -To illustrate why this is needed, let's look at DataParallel - -.. testcode:: - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self(batch) - - # on dp or ddp2 if we did softmax now it would be wrong - # because batch is actually a piece of the full batch - return y_hat - - - def training_step_end(self, step_output): - # step_output has outputs of each part of the batch - - # do softmax here - outputs = torch.cat(outputs, dim=1) - softmax = softmax(outputs, dim=1) - out = softmax.mean() - - return out - -If `training_step_end` is defined it will be called regardless of TPU, DP, DDP, etc... which means -it will behave the same regardless of the backend. - -Validation and test step have the same option when using DP. - -.. testcode:: - - def validation_step_end(self, step_output): - ... - - - def test_step_end(self, step_output): - ... - - -Distributed and 16-bit precision -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Due to an issue with Apex and DataParallel (PyTorch and NVIDIA issue), Lightning does -not allow 16-bit and DP training. We tried to get this to work, but it's an issue on their end. - -Below are the possible configurations we support. - -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| 1 GPU | 1+ GPUs | DP | DDP | 16-bit | command | -+=======+=========+=====+=====+========+=======================================================================+ -| Y | | | | | `Trainer(accelerator="gpu", devices=1)` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| Y | | | | Y | `Trainer(accelerator="gpu", devices=1, precision=16)` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| | Y | Y | | | `Trainer(accelerator="gpu", devices=k, strategy='dp')` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| | Y | | Y | | `Trainer(accelerator="gpu", devices=k, strategy='ddp')` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ -| | Y | | Y | Y | `Trainer(accelerator="gpu", devices=k, strategy='ddp', precision=16)` | -+-------+---------+-----+-----+--------+-----------------------------------------------------------------------+ - - -Implement Your Own Distributed (DDP) training -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If you need your own way to init PyTorch DDP you can override :meth:`pytorch_lightning.strategies.ddp.DDPStrategy.init_dist_connection`. - -If you also need to use your own DDP implementation, override :meth:`pytorch_lightning.strategies.ddp.DDPStrategy.configure_ddp`. - ----------- - -Torch Distributed Elastic -------------------------- -Lightning supports the use of Torch Distributed Elastic to enable fault-tolerant and elastic distributed job scheduling. To use it, specify the 'ddp' or 'ddp2' backend and the number of GPUs you want to use in the trainer. - -.. code-block:: python - - Trainer(accelerator="gpu", devices=8, strategy="ddp") - -To launch a fault-tolerant job, run the following on all nodes. - -.. code-block:: bash - - python -m torch.distributed.run - --nnodes=NUM_NODES - --nproc_per_node=TRAINERS_PER_NODE - --rdzv_id=JOB_ID - --rdzv_backend=c10d - --rdzv_endpoint=HOST_NODE_ADDR - YOUR_LIGHTNING_TRAINING_SCRIPT.py (--arg1 ... train script args...) - -To launch an elastic job, run the following on at least ``MIN_SIZE`` nodes and at most ``MAX_SIZE`` nodes. - -.. code-block:: bash - - python -m torch.distributed.run - --nnodes=MIN_SIZE:MAX_SIZE - --nproc_per_node=TRAINERS_PER_NODE - --rdzv_id=JOB_ID - --rdzv_backend=c10d - --rdzv_endpoint=HOST_NODE_ADDR - YOUR_LIGHTNING_TRAINING_SCRIPT.py (--arg1 ... train script args...) - -See the official `Torch Distributed Elastic documentation `_ for details -on installation and more use cases. - -Optimize multi-machine communication ------------------------------------- - -By default, Lightning will select the ``nccl`` backend over ``gloo`` when running on GPUs. -Find more information about PyTorch's supported backends `here `__. - -Lightning allows explicitly specifying the backend via the `process_group_backend` constructor argument on the relevant Strategy classes. By default, Lightning will select the appropriate process group backend based on the hardware used. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy - - # Explicitly specify the process group backend if you choose to - ddp = DDPStrategy(process_group_backend="nccl") - - # Configure the strategy on the Trainer - trainer = Trainer(strategy=ddp, accelerator="gpu", devices=8) diff --git a/source/accelerators/hpu.rst b/source/accelerators/hpu.rst deleted file mode 100644 index 13eeab8..0000000 --- a/source/accelerators/hpu.rst +++ /dev/null @@ -1,40 +0,0 @@ -.. _hpu: - -Accelerator: HPU training -========================= - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Prepare your code (Optional) - :description: Prepare your code to run on any hardware - :col_css: col-md-4 - :button_link: accelerator_prepare.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Basic - :description: Learn the basics of single and multi-HPU core training. - :col_css: col-md-4 - :button_link: hpu_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Enable state-of-the-art scaling with advanced mix-precision settings. - :col_css: col-md-4 - :button_link: hpu_intermediate.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/source/accelerators/hpu_basic.rst b/source/accelerators/hpu_basic.rst deleted file mode 100644 index e07e153..0000000 --- a/source/accelerators/hpu_basic.rst +++ /dev/null @@ -1,83 +0,0 @@ -:orphan: - -.. _hpu_basics: - -Accelerator: HPU training -========================= -**Audience:** Users looking to save money and run large models faster using single or multiple Gaudi devices. - ----- - -What is an HPU? ---------------- - -`Habana® Gaudi® AI Processor (HPU) `__ training processors are built on a heterogeneous architecture with a cluster of fully programmable Tensor Processing Cores (TPC) along with its associated development tools and libraries, and a configurable Matrix Math engine. - -The TPC core is a VLIW SIMD processor with an instruction set and hardware tailored to serve training workloads efficiently. -The Gaudi memory architecture includes on-die SRAM and local memories in each TPC and, -Gaudi is the first DL training processor that has integrated RDMA over Converged Ethernet (RoCE v2) engines on-chip. - -On the software side, the PyTorch Habana bridge interfaces between the framework and SynapseAI software stack to enable the execution of deep learning models on the Habana Gaudi device. - -Gaudi offers a substantial price/performance advantage -- so you get to do more deep learning training while spending less. - -For more information, check out `Gaudi Architecture `__ and `Gaudi Developer Docs `__. - ----- - -Run on 1 Gaudi --------------- - -To enable PyTorch Lightning to utilize the HPU accelerator, simply provide ``accelerator="hpu"`` parameter to the Trainer class. - -.. code-block:: python - - trainer = Trainer(accelerator="hpu", devices=1) - ----- - -Run on multiple Gaudis ----------------------- -The ``devices=8`` and ``accelerator="hpu"`` parameters to the Trainer class enables the Habana accelerator for distributed training with 8 Gaudis. -It uses :class:`~pytorch_lightning.strategies.hpu_parallel.HPUParallelStrategy` internally which is based on DDP strategy with the addition of Habana's collective communication library (HCCL) to support scale-up within a node and scale-out across multiple nodes. - -.. code-block:: python - - trainer = Trainer(devices=8, accelerator="hpu") - ----- - -Select Gaudis automatically ---------------------------- - -Lightning can automatically detect the number of Gaudi devices to run on. This setting is enabled by default if the devices argument is missing. - -.. code-block:: python - - # equivalent - trainer = Trainer(accelerator="hpu") - trainer = Trainer(accelerator="hpu", devices="auto") - ----- - -How to access HPUs ------------------- - -To use HPUs, you must have access to a system with HPU devices. - -AWS -^^^ -You can either use `Gaudi-based AWS EC2 DL1 instances `__ or `Supermicro X12 Gaudi server `__ to get access to HPUs. - -Check out the `Get Started Guide with AWS and Habana `__. - ----- - -.. _known-limitations_hpu: - -Known limitations ------------------ - -* Multiple optimizers are not supported. -* `Habana dataloader `__ is not supported. -* :class:`~pytorch_lightning.callbacks.device_stats_monitor.DeviceStatsMonitor` is not supported. diff --git a/source/accelerators/hpu_intermediate.rst b/source/accelerators/hpu_intermediate.rst deleted file mode 100644 index 65dca85..0000000 --- a/source/accelerators/hpu_intermediate.rst +++ /dev/null @@ -1,68 +0,0 @@ -:orphan: - -.. _hpu_intermediate: - -Accelerator: HPU training -========================= -**Audience:** Gaudi chip users looking to save memory and scale models with mixed-precision training. - ----- - -Enable Mixed Precision ----------------------- - -Lightning also allows mixed precision training with HPUs. -By default, HPU training will use 32-bit precision. To enable mixed precision, set the ``precision`` flag. - -.. code-block:: python - - trainer = Trainer(devices=1, accelerator="hpu", precision=16) - ----- - -Customize Mixed Precision -------------------------- - -Internally, :class:`~pytorch_lightning.plugins.precision.hpu.HPUPrecisionPlugin` uses the Habana Mixed Precision (HMP) package to enable mixed precision training. - -You can execute the ops in FP32 or BF16 precision. The HMP package modifies the Python operators to add the appropriate cast operations for the arguments before execution. -The default settings enable users to enable mixed precision training with minimal code easily. - -In addition to the default settings in HMP, users also have the option of overriding these defaults and providing their -BF16 and FP32 operator lists by passing them as parameter to :class:`~pytorch_lightning.plugins.precision.hpu.HPUPrecisionPlugin`. - -The below snippet shows an example model using MNIST with a single Habana Gaudi device and making use of HMP by overriding the default parameters. -This enables advanced users to provide their own BF16 and FP32 operator list instead of using the HMP defaults. - -.. code-block:: python - - import pytorch_lightning as pl - from pytorch_lightning.plugins import HPUPrecisionPlugin - - # Initialize a trainer with HPU accelerator for HPU strategy for single device, - # with mixed precision using overidden HMP settings - trainer = pl.Trainer( - accelerator="hpu", - devices=1, - # Optional Habana mixed precision params to be set - # Checkout `pl_examples/hpu_examples/simple_mnist/ops_bf16_mnist.txt` for the format - plugins=[ - HPUPrecisionPlugin( - precision=16, - opt_level="O1", - verbose=False, - bf16_file_path="ops_bf16_mnist.txt", - fp32_file_path="ops_fp32_mnist.txt", - ) - ], - ) - - # Init our model - model = LitClassifier() - # Init the data - dm = MNISTDataModule(batch_size=batch_size) - - # Train the model ⚡ - trainer.fit(model, datamodule=dm) - -For more details, please refer to `PyTorch Mixed Precision Training on Gaudi `__. diff --git a/source/accelerators/ipu_basic.rst b/source/accelerators/ipu_basic.rst deleted file mode 100644 index 492c7bf..0000000 --- a/source/accelerators/ipu_basic.rst +++ /dev/null @@ -1,68 +0,0 @@ -:orphan: - -.. _ipu_basic: - -Accelerator: IPU training -========================= -**Audience:** Users looking to save money and run large models faster using single or multiple IPU devices. - ----- - -What is an IPU? ---------------- - -The Graphcore `Intelligence Processing Unit (IPU) `__, built for Artificial Intelligence and Machine Learning, consists of many individual cores, called *tiles*, allowing highly parallel computation. Due to the high bandwidth between tiles, IPUs facilitate machine learning loads where parallelization is essential. Because computation is heavily parallelized, - -IPUs operate in a different way to conventional accelerators such as CPU/GPUs. IPUs do not require large batch sizes for maximum parallelization, can provide optimizations across the compiled graph and rely on model parallelism to fully utilize tiles for larger models. - -IPUs are used to build IPU-PODs, rack-based systems of IPU-Machines for larger workloads. See the `IPU Architecture `__ for more information. - -See the `Graphcore Glossary `__ for the definitions of other IPU-specific terminology. - -.. note:: - IPU support is experimental and a work in progress (see :ref:`known-limitations`). If you run into any problems, please leave an issue. - ----- - -Run on 1 IPU ------------- -To use a single IPU, set the accelerator and devices argument. - -.. code-block:: python - - trainer = pl.Trainer(accelerator="ipu", devices=1) - ----- - -Run on multiple IPUs --------------------- -To use multiple IPUs set the devices to a number that is a power of 2 (i.e: 2, 4, 8, 16, ...) - -.. code-block:: python - - trainer = pl.Trainer(accelerator="ipu", devices=8) - ----- - -How to access IPUs ------------------- - -To use IPUs you must have access to a system with IPU devices. To get access see `get started `__. - -You must ensure that the IPU system has enabled the PopART and Poplar packages from the SDK. Instructions are in the Get Started guide for your IPU system, on the Graphcore `documents portal `__. - ----- - -.. _known-limitations: - -Known limitations ------------------ - -Currently there are some known limitations that are being addressed in the near future to make the experience seamless when moving from different devices. - -Please see the `MNIST example `__ which displays most of the limitations and how to overcome them till they are resolved. - -* ``self.log`` is not supported in the ``training_step``, ``validation_step``, ``test_step`` or ``predict_step``. This is due to the step function being traced and sent to the IPU devices. We're actively working on fixing this -* Multiple optimizers are not supported. ``training_step`` only supports returning one loss from the ``training_step`` function as a result -* Since the step functions are traced, branching logic or any form of primitive values are traced into constants. Be mindful as this could lead to errors in your custom code -* Clipping gradients is not supported diff --git a/source/accelerators/tpu_advanced.rst b/source/accelerators/tpu_advanced.rst deleted file mode 100644 index 0aa490e..0000000 --- a/source/accelerators/tpu_advanced.rst +++ /dev/null @@ -1,68 +0,0 @@ -:orphan: - -TPU training (Advanced) -======================= -**Audience:** Users looking to apply advanced performance techniques to TPU training. - ----- - -Weight Sharing/Tying --------------------- -Weight Tying/Sharing is a technique where in the module weights are shared among two or more layers. -This is a common method to reduce memory consumption and is utilized in many State of the Art -architectures today. - -PyTorch XLA requires these weights to be tied/shared after moving the model -to the TPU device. To support this requirement Lightning provides a model hook which is -called after the model is moved to the device. Any weights that require to be tied should -be done in the `on_post_move_to_device` model hook. This will ensure that the weights -among the modules are shared and not copied. - -PyTorch Lightning has an inbuilt check which verifies that the model parameter lengths -match once the model is moved to the device. If the lengths do not match Lightning -throws a warning message. - -Example: - -.. code-block:: python - - from pytorch_lightning.core.lightning import LightningModule - from torch import nn - from pytorch_lightning.trainer.trainer import Trainer - - - class WeightSharingModule(LightningModule): - def __init__(self): - super().__init__() - self.layer_1 = nn.Linear(32, 10, bias=False) - self.layer_2 = nn.Linear(10, 32, bias=False) - self.layer_3 = nn.Linear(32, 10, bias=False) - # TPU shared weights are copied independently - # on the XLA device and this line won't have any effect. - # However, it works fine for CPU and GPU. - self.layer_3.weight = self.layer_1.weight - - def forward(self, x): - x = self.layer_1(x) - x = self.layer_2(x) - x = self.layer_3(x) - return x - - def on_post_move_to_device(self): - # Weights shared after the model has been moved to TPU Device - self.layer_3.weight = self.layer_1.weight - - - model = WeightSharingModule() - trainer = Trainer(max_epochs=1, accelerator="tpu", devices=8) - -See `XLA Documentation `_ - ----- - -XLA ---- -XLA is the library that interfaces PyTorch with the TPUs. -For more information check out `XLA `_. - -Guide for `troubleshooting XLA `_ diff --git a/source/accelerators/tpu_basic.rst b/source/accelerators/tpu_basic.rst deleted file mode 100644 index af7c45f..0000000 --- a/source/accelerators/tpu_basic.rst +++ /dev/null @@ -1,255 +0,0 @@ -:orphan: - -TPU training (Basic) -==================== -**Audience:** Users looking to train on single or multiple TPU cores. - ----- - -.. raw:: html - - - -| - -Lightning supports running on TPUs. At this moment, TPUs are available -on Google Cloud (GCP), Google Colab and Kaggle Environments. For more information on TPUs -`watch this video `_. - ----------------- - -What is a TPU? --------------- -Tensor Processing Unit (TPU) is an AI accelerator application-specific integrated circuit (ASIC) developed by Google specifically for neural networks. - -A TPU has 8 cores where each core is optimized for 128x128 matrix multiplies. In general, a single TPU is about as fast as 5 V100 GPUs! - -A TPU pod hosts many TPUs on it. Currently, TPU v3 Pod has up to 2048 TPU cores and 32 TiB of memory! -You can request a full pod from Google cloud or a "slice" which gives you -some subset of those 2048 cores. - ----- - -Run on 1 TPU core ------------------ -Enable the following Trainer arguments to run on 1 TPU. - -.. code:: - - trainer = Trainer(accelerator="tpu", devices=1) - ----- - -Run on multiple TPU cores -------------------------- -For multiple TPU cores, change the value of the devices flag. - -.. code:: - - trainer = Trainer(accelerator="tpu", devices=8) - ----- - -Run on a specific TPU core --------------------------- - -To run on a specific core, specify the index of the TPU core. - -.. code-block:: python - - trainer = pl.Trainer(accelerator="tpu", devices=[5]) - -This example runs on the 5th core, not on five cores. - ----- - -How to access TPUs ------------------- -To access TPUs, there are three main ways. - -Google Colab -^^^^^^^^^^^^ -Colab is like a jupyter notebook with a free GPU or TPU -hosted on GCP. - -To get a TPU on colab, follow these steps: - -1. Go to `https://colab.research.google.com/ `_. - -2. Click "new notebook" (bottom right of pop-up). - -3. Click runtime > change runtime settings. Select Python 3, and hardware accelerator "TPU". - This will give you a TPU with 8 cores. - -4. Next, insert this code into the first cell and execute. - This will install the xla library that interfaces between PyTorch and the TPU. - - .. code-block:: - - !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.9-cp37-cp37m-linux_x86_64.whl - -5. Once the above is done, install PyTorch Lightning. - - .. code-block:: - - !pip install pytorch-lightning - -6. Then set up your LightningModule as normal. - -Google Cloud (GCP) -^^^^^^^^^^^^^^^^^^ -? - -Kaggle -^^^^^^ -For starting Kaggle projects with TPUs, refer to this `kernel `_. - ----- - -Optimize Performance --------------------- - -The TPU was designed for specific workloads and operations to carry out large volumes of matrix multiplication, -convolution operations and other commonly used ops in applied deep learning. -The specialization makes it a strong choice for NLP tasks, sequential convolutional networks, and under low precision operation. -There are cases in which training on TPUs is slower when compared with GPUs, for possible reasons listed: - -- Too small batch size. -- Explicit evaluation of tensors during training, e.g. ``tensor.item()`` -- Tensor shapes (e.g. model inputs) change often during training. -- Limited resources when using TPU's with PyTorch `Link `_ -- XLA Graph compilation during the initial steps `Reference `_ -- Some tensor ops are not fully supported on TPU, or not supported at all. These operations will be performed on CPU (context switch). -- PyTorch integration is still experimental. Some performance bottlenecks may simply be the result of unfinished implementation. - -The official PyTorch XLA `performance guide `_ -has more detailed information on how PyTorch code can be optimized for TPU. In particular, the -`metrics report `_ allows -one to identify operations that lead to context switching. - ----- - -FAQ ---- - -**XLA configuration is missing** - -.. code-block:: - - File "/usr/local/lib/python3.8/dist-packages/torch_xla/core/xla_model.py", line 18, in - _DEVICES = xu.LazyProperty(lambda: torch_xla._XLAC._xla_get_devices()) - RuntimeError: tensorflow/compiler/xla/xla_client/computation_client.cc:273 : Missing XLA configuration - Traceback (most recent call last): - ... - File "/home/kaushikbokka/pytorch-lightning/pytorch_lightning/utilities/device_parser.py", line 125, in parse_tpu_cores - raise MisconfigurationException('No TPU devices were found.') - pytorch_lightning.utilities.exceptions.MisconfigurationException: No TPU devices were found. - -This means the system is missing XLA configuration. You would need to set up XRT TPU device configuration. - -For TPUVM architecture, you could set it in your terminal by: - -.. code-block:: bash - - export XRT_TPU_CONFIG="localservice;0;localhost:51011" - -And for the old TPU + 2VM architecture, you could set it by: - -.. code-block:: bash - - export TPU_IP_ADDRESS=10.39.209.42 # You could get the IP Address in the GCP TPUs section - export XRT_TPU_CONFIG="tpu_worker;0;$TPU_IP_ADDRESS:8470" - ----- - -**How to clear up the programs using TPUs in the background** - -.. code-block:: bash - - lsof -w /lib/libtpu.so | grep "python" | awk '{print $2}' | xargs -r kill -9 - -Sometimes, there can still be old programs running on the TPUs, which would make the TPUs unavailable to use. You could use the above command in the terminal to kill the running processes. - ----- - -**Replication issue** - -.. code-block:: - - File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 200, in set_replication - replication_devices = xla_replication_devices(devices) - File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 187, in xla_replication_devices - .format(len(local_devices), len(kind_devices))) - RuntimeError: Cannot replicate if number of devices (1) is different from 8 - -This error is raised when the XLA device is called outside the spawn process. Internally in `TPUSpawn` Strategy for training on multiple tpu cores, we use XLA's `xmp.spawn`. -Don't use ``xm.xla_device()`` while working on Lightning + TPUs! - ----- - -**Unsupported datatype transfer to TPU** - -.. code-block:: - - File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 205, in _for_each_instance_rewrite - v = _for_each_instance_rewrite(result.__dict__[k], select_fn, fn, rwmap) - File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 206, in _for_each_instance_rewrite - result.__dict__[k] = v - TypeError: 'mappingproxy' object does not support item assignment - -PyTorch XLA only supports Tensor objects for CPU to TPU data transfer. Might cause issues if the User is trying to send some non-tensor objects through the DataLoader or during saving states. - ----- - -**Using `tpu_spawn_debug` Strategy alias** - -.. code-block:: python - - import pytorch_lightning as pl - - my_model = MyLightningModule() - trainer = pl.Trainer(accelerator="tpu", devices=8, strategy="tpu_spawn_debug") - trainer.fit(my_model) - -Example Metrics report: - -.. code-block:: - - Metric: CompileTime - TotalSamples: 202 - Counter: 06m09s401ms746.001us - ValueRate: 778ms572.062us / second - Rate: 0.425201 / second - Percentiles: 1%=001ms32.778us; 5%=001ms61.283us; 10%=001ms79.236us; 20%=001ms110.973us; 50%=001ms228.773us; 80%=001ms339.183us; 90%=001ms434.305us; 95%=002ms921.063us; 99%=21s102ms853.173us - - -A lot of PyTorch operations aren't lowered to XLA, which could lead to significant slowdown of the training process. -These operations are moved to the CPU memory and evaluated, and then the results are transferred back to the XLA device(s). -By using the `tpu_spawn_debug` Strategy, users could create a metrics report to diagnose issues. - -The report includes things like (`XLA Reference `_): - -* how many times we issue XLA compilations and time spent on issuing. -* how many times we execute and time spent on execution -* how many device data handles we create/destroy etc. - ----- - -**TPU Pod Training Startup script** - -All TPU VMs in a Pod setup are required to access the model code and data. -One easy way to achieve this is to use the following startup script when creating the TPU VM pod. -It will perform the data downloading on all TPU VMs. Note that you need to export the corresponding environment variables following the instruction in Create TPU Node. - -.. code-block:: bash - - gcloud alpha compute tpus tpu-vm create ${TPU_NAME} --zone ${ZONE} --project ${PROJECT_ID} --accelerator-type v3-32 --version ${RUNTIME_VERSION} --metadata startup-script=setup.py - -Then users could ssh to any TPU worker, e.g. worker 0, check if data/model downloading is finished and -start the training after generating the ssh-keys to ssh between VM workers on a pod: - -.. code-block:: bash - - python3 -m torch_xla.distributed.xla_dist --tpu=$TPU_NAME -- python3 train.py --max_epochs=5 --batch_size=32 diff --git a/source/accelerators/tpu_faq.rst b/source/accelerators/tpu_faq.rst deleted file mode 100644 index af7c45f..0000000 --- a/source/accelerators/tpu_faq.rst +++ /dev/null @@ -1,255 +0,0 @@ -:orphan: - -TPU training (Basic) -==================== -**Audience:** Users looking to train on single or multiple TPU cores. - ----- - -.. raw:: html - - - -| - -Lightning supports running on TPUs. At this moment, TPUs are available -on Google Cloud (GCP), Google Colab and Kaggle Environments. For more information on TPUs -`watch this video `_. - ----------------- - -What is a TPU? --------------- -Tensor Processing Unit (TPU) is an AI accelerator application-specific integrated circuit (ASIC) developed by Google specifically for neural networks. - -A TPU has 8 cores where each core is optimized for 128x128 matrix multiplies. In general, a single TPU is about as fast as 5 V100 GPUs! - -A TPU pod hosts many TPUs on it. Currently, TPU v3 Pod has up to 2048 TPU cores and 32 TiB of memory! -You can request a full pod from Google cloud or a "slice" which gives you -some subset of those 2048 cores. - ----- - -Run on 1 TPU core ------------------ -Enable the following Trainer arguments to run on 1 TPU. - -.. code:: - - trainer = Trainer(accelerator="tpu", devices=1) - ----- - -Run on multiple TPU cores -------------------------- -For multiple TPU cores, change the value of the devices flag. - -.. code:: - - trainer = Trainer(accelerator="tpu", devices=8) - ----- - -Run on a specific TPU core --------------------------- - -To run on a specific core, specify the index of the TPU core. - -.. code-block:: python - - trainer = pl.Trainer(accelerator="tpu", devices=[5]) - -This example runs on the 5th core, not on five cores. - ----- - -How to access TPUs ------------------- -To access TPUs, there are three main ways. - -Google Colab -^^^^^^^^^^^^ -Colab is like a jupyter notebook with a free GPU or TPU -hosted on GCP. - -To get a TPU on colab, follow these steps: - -1. Go to `https://colab.research.google.com/ `_. - -2. Click "new notebook" (bottom right of pop-up). - -3. Click runtime > change runtime settings. Select Python 3, and hardware accelerator "TPU". - This will give you a TPU with 8 cores. - -4. Next, insert this code into the first cell and execute. - This will install the xla library that interfaces between PyTorch and the TPU. - - .. code-block:: - - !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.9-cp37-cp37m-linux_x86_64.whl - -5. Once the above is done, install PyTorch Lightning. - - .. code-block:: - - !pip install pytorch-lightning - -6. Then set up your LightningModule as normal. - -Google Cloud (GCP) -^^^^^^^^^^^^^^^^^^ -? - -Kaggle -^^^^^^ -For starting Kaggle projects with TPUs, refer to this `kernel `_. - ----- - -Optimize Performance --------------------- - -The TPU was designed for specific workloads and operations to carry out large volumes of matrix multiplication, -convolution operations and other commonly used ops in applied deep learning. -The specialization makes it a strong choice for NLP tasks, sequential convolutional networks, and under low precision operation. -There are cases in which training on TPUs is slower when compared with GPUs, for possible reasons listed: - -- Too small batch size. -- Explicit evaluation of tensors during training, e.g. ``tensor.item()`` -- Tensor shapes (e.g. model inputs) change often during training. -- Limited resources when using TPU's with PyTorch `Link `_ -- XLA Graph compilation during the initial steps `Reference `_ -- Some tensor ops are not fully supported on TPU, or not supported at all. These operations will be performed on CPU (context switch). -- PyTorch integration is still experimental. Some performance bottlenecks may simply be the result of unfinished implementation. - -The official PyTorch XLA `performance guide `_ -has more detailed information on how PyTorch code can be optimized for TPU. In particular, the -`metrics report `_ allows -one to identify operations that lead to context switching. - ----- - -FAQ ---- - -**XLA configuration is missing** - -.. code-block:: - - File "/usr/local/lib/python3.8/dist-packages/torch_xla/core/xla_model.py", line 18, in - _DEVICES = xu.LazyProperty(lambda: torch_xla._XLAC._xla_get_devices()) - RuntimeError: tensorflow/compiler/xla/xla_client/computation_client.cc:273 : Missing XLA configuration - Traceback (most recent call last): - ... - File "/home/kaushikbokka/pytorch-lightning/pytorch_lightning/utilities/device_parser.py", line 125, in parse_tpu_cores - raise MisconfigurationException('No TPU devices were found.') - pytorch_lightning.utilities.exceptions.MisconfigurationException: No TPU devices were found. - -This means the system is missing XLA configuration. You would need to set up XRT TPU device configuration. - -For TPUVM architecture, you could set it in your terminal by: - -.. code-block:: bash - - export XRT_TPU_CONFIG="localservice;0;localhost:51011" - -And for the old TPU + 2VM architecture, you could set it by: - -.. code-block:: bash - - export TPU_IP_ADDRESS=10.39.209.42 # You could get the IP Address in the GCP TPUs section - export XRT_TPU_CONFIG="tpu_worker;0;$TPU_IP_ADDRESS:8470" - ----- - -**How to clear up the programs using TPUs in the background** - -.. code-block:: bash - - lsof -w /lib/libtpu.so | grep "python" | awk '{print $2}' | xargs -r kill -9 - -Sometimes, there can still be old programs running on the TPUs, which would make the TPUs unavailable to use. You could use the above command in the terminal to kill the running processes. - ----- - -**Replication issue** - -.. code-block:: - - File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 200, in set_replication - replication_devices = xla_replication_devices(devices) - File "/usr/local/lib/python3.6/dist-packages/torch_xla/core/xla_model.py", line 187, in xla_replication_devices - .format(len(local_devices), len(kind_devices))) - RuntimeError: Cannot replicate if number of devices (1) is different from 8 - -This error is raised when the XLA device is called outside the spawn process. Internally in `TPUSpawn` Strategy for training on multiple tpu cores, we use XLA's `xmp.spawn`. -Don't use ``xm.xla_device()`` while working on Lightning + TPUs! - ----- - -**Unsupported datatype transfer to TPU** - -.. code-block:: - - File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 205, in _for_each_instance_rewrite - v = _for_each_instance_rewrite(result.__dict__[k], select_fn, fn, rwmap) - File "/usr/local/lib/python3.8/dist-packages/torch_xla/utils/utils.py", line 206, in _for_each_instance_rewrite - result.__dict__[k] = v - TypeError: 'mappingproxy' object does not support item assignment - -PyTorch XLA only supports Tensor objects for CPU to TPU data transfer. Might cause issues if the User is trying to send some non-tensor objects through the DataLoader or during saving states. - ----- - -**Using `tpu_spawn_debug` Strategy alias** - -.. code-block:: python - - import pytorch_lightning as pl - - my_model = MyLightningModule() - trainer = pl.Trainer(accelerator="tpu", devices=8, strategy="tpu_spawn_debug") - trainer.fit(my_model) - -Example Metrics report: - -.. code-block:: - - Metric: CompileTime - TotalSamples: 202 - Counter: 06m09s401ms746.001us - ValueRate: 778ms572.062us / second - Rate: 0.425201 / second - Percentiles: 1%=001ms32.778us; 5%=001ms61.283us; 10%=001ms79.236us; 20%=001ms110.973us; 50%=001ms228.773us; 80%=001ms339.183us; 90%=001ms434.305us; 95%=002ms921.063us; 99%=21s102ms853.173us - - -A lot of PyTorch operations aren't lowered to XLA, which could lead to significant slowdown of the training process. -These operations are moved to the CPU memory and evaluated, and then the results are transferred back to the XLA device(s). -By using the `tpu_spawn_debug` Strategy, users could create a metrics report to diagnose issues. - -The report includes things like (`XLA Reference `_): - -* how many times we issue XLA compilations and time spent on issuing. -* how many times we execute and time spent on execution -* how many device data handles we create/destroy etc. - ----- - -**TPU Pod Training Startup script** - -All TPU VMs in a Pod setup are required to access the model code and data. -One easy way to achieve this is to use the following startup script when creating the TPU VM pod. -It will perform the data downloading on all TPU VMs. Note that you need to export the corresponding environment variables following the instruction in Create TPU Node. - -.. code-block:: bash - - gcloud alpha compute tpus tpu-vm create ${TPU_NAME} --zone ${ZONE} --project ${PROJECT_ID} --accelerator-type v3-32 --version ${RUNTIME_VERSION} --metadata startup-script=setup.py - -Then users could ssh to any TPU worker, e.g. worker 0, check if data/model downloading is finished and -start the training after generating the ssh-keys to ssh between VM workers on a pod: - -.. code-block:: bash - - python3 -m torch_xla.distributed.xla_dist --tpu=$TPU_NAME -- python3 train.py --max_epochs=5 --batch_size=32 diff --git a/source/accelerators/tpu_intermediate.rst b/source/accelerators/tpu_intermediate.rst deleted file mode 100644 index 826f568..0000000 --- a/source/accelerators/tpu_intermediate.rst +++ /dev/null @@ -1,113 +0,0 @@ -:orphan: - -TPU training (Intermediate) -=========================== -**Audience:** Users looking to use cloud TPUs. - ----- - -DistributedSamplers -------------------- -Lightning automatically inserts the correct samplers - no need to do this yourself! - -Usually, with TPUs (and DDP), you would need to define a DistributedSampler to move the right -chunk of data to the appropriate TPU. As mentioned, this is not needed in Lightning - -.. note:: Don't add distributedSamplers. Lightning does this automatically - -If for some reason you still need to, this is how to construct the sampler -for TPU use - -.. code-block:: python - - import torch_xla.core.xla_model as xm - - - def train_dataloader(self): - dataset = MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()) - - # required for TPU support - sampler = None - if use_tpu: - sampler = torch.utils.data.distributed.DistributedSampler( - dataset, num_replicas=xm.xrt_world_size(), rank=xm.get_ordinal(), shuffle=True - ) - - loader = DataLoader(dataset, sampler=sampler, batch_size=32) - - return loader - -Configure the number of TPU cores in the trainer. You can only choose 1 or 8. -To use a full TPU pod skip to the TPU pod section. - -.. code-block:: python - - import pytorch_lightning as pl - - my_model = MyLightningModule() - trainer = pl.Trainer(accelerator="tpu", devices=8) - trainer.fit(my_model) - -That's it! Your model will train on all 8 TPU cores. - ----------------- - -Distributed Backend with TPU ----------------------------- -The ``accelerator`` option used for GPUs does not apply to TPUs. -TPUs work in DDP mode by default (distributing over each core) - ----------------- - -TPU VM ------- -Lightning supports training on the new Cloud TPU VMs. -Previously, we needed separate VMs to connect to the TPU machines, but as -Cloud TPU VMs run on the TPU Host machines, it allows direct SSH access -for the users. Hence, this architecture upgrade leads to cheaper and significantly -better performance and usability while working with TPUs. - -The TPUVMs come pre-installed with latest versions of PyTorch and PyTorch XLA. -After connecting to the VM and before running your Lightning code, you would need -to set the XRT TPU device configuration. - -.. code-block:: bash - - $ export XRT_TPU_CONFIG="localservice;0;localhost:51011" - -You could learn more about the Cloud TPU VM architecture `here `_ - ----------------- - -TPU Pod -------- -To train on more than 8 cores, your code actually doesn't change! -All you need to do is submit the following command: - -.. code-block:: bash - - $ python -m torch_xla.distributed.xla_dist - --tpu=$TPU_POD_NAME - --conda-env=torch-xla-nightly - -- python /usr/share/torch-xla-1.8.1/pytorch/xla/test/test_train_imagenet.py --fake_data - -See `this guide `_ -on how to set up the instance groups and VMs needed to run TPU Pods. - ----------------- - -16 bit precision ----------------- -Lightning also supports training in 16-bit precision with TPUs. -By default, TPU training will use 32-bit precision. To enable 16-bit, -set the 16-bit flag. - -.. code-block:: python - - import pytorch_lightning as pl - - my_model = MyLightningModule() - trainer = pl.Trainer(accelerator="tpu", devices=8, precision=16) - trainer.fit(my_model) - -Under the hood the xla library will use the `bfloat16 type `_. diff --git a/source/advanced/model_parallel.rst b/source/advanced/model_parallel.rst deleted file mode 100644 index 811bc57..0000000 --- a/source/advanced/model_parallel.rst +++ /dev/null @@ -1,905 +0,0 @@ -.. _model-parallel: - -Train 1 trillion+ parameter models -================================== - -When training large models, fitting larger batch sizes, or trying to increase throughput using multi-GPU compute, Lightning provides advanced optimized distributed training strategies to support these cases and offer substantial improvements in memory usage. - -In many cases these strategies are some flavour of model parallelism however we only introduce concepts at a high level to get you started. Refer to the `FairScale documentation `_ for more information about model parallelism. - -Note that some of the extreme memory saving configurations will affect the speed of training. This Speed/Memory trade-off in most cases can be adjusted. - -Some of these memory-efficient strategies rely on offloading onto other forms of memory, such as CPU RAM or NVMe. This means you can even see memory benefits on a **single GPU**, using a strategy such as :ref:`deepspeed-zero-stage-3-offload`. - -Check out this amazing video explaining model parallelism and how it works behind the scenes: - -.. raw:: html - - - - -Choosing an Advanced Distributed GPU Strategy -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you would like to stick with PyTorch DDP, see :ref:`ddp-optimizations`. - -Unlike :class:`~torch.nn.parallel.DistributedDataParallel` (DDP) where the maximum trainable model size and batch size do not change with respect to the number of GPUs, memory-optimized strategies can accommodate bigger models and larger batches as more GPUs are used. This means as you scale up the number of GPUs, you can reach the number of model parameters you'd like to train. - -There are many considerations when choosing a strategy as described below. In addition, check out the visualization of various strategy benchmarks using `minGPT `__ `here `__. - -Pre-training vs Fine-tuning -""""""""""""""""""""""""""" - -When fine-tuning, we often use a magnitude less data compared to pre-training a model. This is important when choosing a distributed strategy as usually for pre-training, **we are compute-bound**. -This means we cannot sacrifice throughput as much as if we were fine-tuning, because in fine-tuning the data requirement is smaller. - -Overall: - -* When **fine-tuning** a model, use advanced memory efficient strategies such as :ref:`deepspeed-zero-stage-3` or :ref:`deepspeed-zero-stage-3-offload`, allowing you to fine-tune larger models if you are limited on compute -* When **pre-training** a model, use simpler optimizations such :ref:`sharded-training`, :ref:`deepspeed-zero-stage-2` or :ref:`fully-sharded-training`, scaling the number of GPUs to reach larger parameter sizes -* For both fine-tuning and pre-training, use :ref:`deepspeed-activation-checkpointing` or :ref:`fairscale-activation-checkpointing` as the throughput degradation is not significant - -For example when using 128 GPUs, you can **pre-train** large 10 to 20 Billion parameter models using :ref:`deepspeed-zero-stage-2` without having to take a performance hit with more advanced optimized multi-gpu strategy. - -But for **fine-tuning** a model, you can reach 10 to 20 Billion parameter models using :ref:`deepspeed-zero-stage-3-offload` on a **single GPU**. This does come with a significant throughput hit, which needs to be weighed accordingly. - -When Shouldn't I use an Optimized Distributed Strategy? -""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -Sharding techniques help when model sizes are fairly large; roughly 500M+ parameters is where we've seen benefits. However, in the following cases, we recommend sticking to ordinary distributed strategies -* When your model is small (ResNet50 of around 80M Parameters), unless you are using unusually large batch sizes or inputs. -* Due to high distributed communication between devices, if running on a slow network/interconnect, the training might be much slower than expected and then it's up to you to determince the tradeoff here. - ----------- - -.. _sharded-training: - -Sharded Training -^^^^^^^^^^^^^^^^ -Lightning integration of optimizer sharded training provided by `FairScale `_. -The technique can be found within `DeepSpeed ZeRO `_ and -`ZeRO-2 `_, -however the implementation is built from the ground up to be PyTorch compatible and standalone. -Sharded Training allows you to maintain GPU scaling efficiency, whilst reducing memory overhead drastically. In short, expect near-normal linear scaling (if your network allows), and significantly reduced memory usage when training large models. - -Sharded Training still utilizes Data Parallel Training under the hood, except optimizer states and gradients are sharded across GPUs. -This means the memory overhead per GPU is lower, as each GPU only has to maintain a partition of your optimizer state and gradients. - -The benefits vary by model and parameter sizes, but we've recorded up to a 63% memory reduction per GPU allowing us to double our model sizes. Because of efficient communication, -these benefits in multi-GPU setups are almost free and throughput scales well with multi-node setups. - -It is highly recommended to use Sharded Training in multi-GPU environments where memory is limited, or where training larger models are beneficial (500M+ parameter models). -A technical note: as batch size scales, storing activations for the backwards pass becomes the bottleneck in training. As a result, sharding optimizer state and gradients becomes less impactful. -Use :ref:`fairscale-activation-checkpointing` to see even more benefit at the cost of some throughput. - -To use Sharded Training, you need to first install FairScale using the command below. - -.. code-block:: bash - - pip install fairscale - - -.. code-block:: python - - # train using Sharded DDP - trainer = Trainer(strategy="ddp_sharded") - -Sharded Training can work across all DDP variants by adding the additional ``--strategy ddp_sharded`` flag via command line using a PyTorch Lightning script. - -Internally we re-initialize your optimizers and shard them across your machines and processes. We handle all communication using PyTorch distributed, so no code changes are required. - ----------- - -.. _fully-sharded-training: - -Fully Sharded Training -^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: - Fully Sharded Training is in beta and the API is subject to change. Please create an `issue `_ if you run into any issues. - -`Fully Sharded `__ shards optimizer state, gradients and parameters across data parallel workers. This allows you to fit much larger models onto multiple GPUs into memory. - -Fully Sharded Training alleviates the need to worry about balancing layers onto specific devices using some form of pipe parallelism, and optimizes for distributed communication with minimal effort. - -Shard Parameters to Reach 10+ Billion Parameters -"""""""""""""""""""""""""""""""""""""""""""""""" - -To reach larger parameter sizes and be memory efficient, we have to shard parameters. There are various ways to enable this. - -.. note:: - Currently Fully Sharded Training relies on the user to wrap the model with Fully Sharded within the ``LightningModule``. - This means you must create a single model that is treated as a ``torch.nn.Module`` within the ``LightningModule``. - This is a limitation of Fully Sharded Training that will be resolved in the future. - -Enabling Module Sharding for Maximum Memory Efficiency -"""""""""""""""""""""""""""""""""""""""""""""""""""""" - -To activate parameter sharding, you must wrap your model using provided ``wrap`` or ``auto_wrap`` functions as described below. Internally in Lightning, we enable a context manager around the ``configure_sharded_model`` function to make sure the ``wrap`` and ``auto_wrap`` parameters are passed correctly. - -When not using Fully Sharded these wrap functions are a no-op. This means once the changes have been made, there is no need to remove the changes for other strategies. - -``auto_wrap`` will recursively wrap :class:`~torch.nn.Module` within the ``LightningModule`` with nested Fully Sharded Wrappers, -signalling that we'd like to partition these modules across data parallel devices, discarding the full weights when not required (information :class:`here `). - -``auto_wrap`` can have varying level of success based on the complexity of your model. **Auto Wrap does not support models with shared parameters**. - -``wrap`` will simply wrap the module with a Fully Sharded Parallel class with the correct parameters from the Lightning context manager. - -Below is an example of using both ``wrap`` and ``auto_wrap`` to create your model. - -.. code-block:: python - - import torch - import torch.nn as nn - import pytorch_lightning as pl - from pytorch_lightning import Trainer - from fairscale.nn import checkpoint_wrapper, auto_wrap, wrap - - - class MyModel(pl.LightningModule): - def __init__(self): - super().__init__() - self.linear_layer = nn.Linear(32, 32) - self.block = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - self.final_block = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - - def configure_sharded_model(self): - # modules are sharded across processes - # as soon as they are wrapped with ``wrap`` or ``auto_wrap``. - # During the forward/backward passes, weights get synced across processes - # and de-allocated once computation is complete, saving memory. - - # Wraps the layer in a Fully Sharded Wrapper automatically - linear_layer = wrap(self.linear_layer) - - # Wraps the module recursively - # based on a minimum number of parameters (default 100M parameters) - block = auto_wrap(self.block) - - # For best memory efficiency, - # add FairScale activation checkpointing - final_block = auto_wrap(checkpoint_wrapper(self.final_block)) - self.model = nn.Sequential(linear_layer, nn.ReLU(), block, final_block) - - def configure_optimizers(self): - return torch.optim.AdamW(self.model.parameters()) - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="fsdp", precision=16) - trainer.fit(model) - - trainer.test() - trainer.predict() - - ----------- - -.. _fairscale-activation-checkpointing: - -FairScale Activation Checkpointing -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Activation checkpointing frees activations from memory as soon as they are not needed during the forward pass. They are then re-computed for the backwards pass as needed. Activation checkpointing is very useful when you have intermediate layers that produce large activations. - -FairScales' checkpointing wrapper also handles batch norm layers correctly unlike the PyTorch implementation, ensuring stats are tracked correctly due to the multiple forward passes. - -This saves memory when training larger models however requires wrapping modules you'd like to use activation checkpointing on. See :class:`here ` for more information. - -.. warning:: - - Ensure to not wrap the entire model with activation checkpointing. This is not the intended usage of activation checkpointing, and will lead to failures as seen in `this discussion `__. - -.. code-block:: python - - from pytorch_lightning import Trainer - from fairscale.nn import checkpoint_wrapper - - - class MyModel(pl.LightningModule): - def __init__(self): - super().__init__() - # Wrap layers using checkpoint_wrapper - self.block_1 = checkpoint_wrapper(nn.Sequential(nn.Linear(32, 32), nn.ReLU())) - self.block_2 = nn.Linear(32, 2) - - -.. _deepspeed_advanced: - -DeepSpeed -^^^^^^^^^ - -.. note:: - The DeepSpeed strategy is in beta and the API is subject to change. Please create an `issue `_ if you run into any issues. - -`DeepSpeed `__ is a deep learning training optimization library, providing the means to train massive billion parameter models at scale. -Using the DeepSpeed strategy, we were able to **train model sizes of 10 Billion parameters and above**, with a lot of useful information in this `benchmark `_ and the `DeepSpeed docs `__. -DeepSpeed also offers lower level training optimizations, and efficient optimizers such as `1-bit Adam `_. We recommend using DeepSpeed in environments where speed and memory optimizations are important (such as training large billion parameter models). - -Below is a summary of all the configurations of DeepSpeed. - -* :ref:`deepspeed-zero-stage-1` - **Shard optimizer states**, remains at speed parity with DDP whilst providing memory improvement - -* :ref:`deepspeed-zero-stage-2` - **Shard optimizer states and gradients**, remains at speed parity with DDP whilst providing even more memory improvement - -* :ref:`deepspeed-zero-stage-2-offload` - **Offload optimizer states and gradients to CPU**. Increases distributed communication volume and GPU-CPU device transfer, but provides significant memory improvement - -* :ref:`deepspeed-zero-stage-3` - **Shard optimizer states, gradients, parameters and optionally activations**. Increases distributed communication volume, but provides even more memory improvement - -* :ref:`deepspeed-zero-stage-3-offload` - **Offload optimizer states, gradients, parameters and optionally activations to CPU**. Increases distributed communication volume and GPU-CPU device transfer, but even more significant memory improvement. - -* :ref:`deepspeed-activation-checkpointing` - **Free activations after forward pass**. Increases computation, but provides memory improvement for all stages. - -To use DeepSpeed, you first need to install DeepSpeed using the commands below. - -.. code-block:: bash - - pip install deepspeed - -If you run into an issue with the install or later in training, ensure that the CUDA version of the PyTorch you've installed matches your locally installed CUDA (you can see which one has been recognized by running ``nvcc --version``). - -.. note:: - - DeepSpeed currently only supports single optimizer, single scheduler within the training loop. - - When saving a checkpoint we rely on DeepSpeed which saves a directory containing the model and various components. - - -.. _deepspeed-zero-stage-1: - -DeepSpeed ZeRO Stage 1 -"""""""""""""""""""""" - -`DeepSpeed ZeRO Stage 1 `_ partitions your optimizer states (Stage 1) across your GPUs to reduce memory. - -It is recommended to skip Stage 1 and use Stage 2, which comes with larger memory improvements and still remains efficient. Stage 1 is useful to pair with certain optimizations such as `Torch ORT `__. - -.. code-block:: python - - from pytorch_lightning import Trainer - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_1", precision=16) - trainer.fit(model) - - -.. _deepspeed-zero-stage-2: - -DeepSpeed ZeRO Stage 2 -"""""""""""""""""""""" - -`DeepSpeed ZeRO Stage 2 `_ partitions your optimizer states (Stage 1) and your gradients (Stage 2) across your GPUs to reduce memory. In most cases, this is more efficient or at parity with DDP, primarily due to the optimized custom communications written by the DeepSpeed team. -As a result, benefits can also be seen on a single GPU. Do note that the default bucket sizes allocate around ``3.6GB`` of VRAM to use during distributed communications, which can be tweaked when instantiating the strategy described in a few sections below. - -.. code-block:: python - - from pytorch_lightning import Trainer - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2", precision=16) - trainer.fit(model) - -.. code-block:: bash - - python train.py --strategy deepspeed_stage_2 --precision 16 --accelerator 'gpu' --devices 4 - - -.. _deepspeed-zero-stage-2-offload: - -DeepSpeed ZeRO Stage 2 Offload -"""""""""""""""""""""""""""""" - -Below we show an example of running `ZeRO-Offload `_. ZeRO-Offload leverages the host CPU to offload optimizer memory/computation, reducing the overall memory consumption. - -.. code-block:: python - - from pytorch_lightning import Trainer - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2_offload", precision=16) - trainer.fit(model) - - -This can also be done via the command line using a PyTorch Lightning script: - -.. code-block:: bash - - python train.py --strategy deepspeed_stage_2_offload --precision 16 --accelerator 'gpu' --devices 4 - - -You can also modify the ZeRO-Offload parameters via the strategy as below. - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DeepSpeedStrategy(offload_optimizer=True, allgather_bucket_size=5e8, reduce_bucket_size=5e8), - precision=16, - ) - trainer.fit(model) - - -.. note:: - We suggest tuning the ``allgather_bucket_size`` parameter and ``reduce_bucket_size`` parameter to find optimum parameters based on your model size. - These control how large a buffer we limit the model to using when reducing gradients/gathering updated parameters. Smaller values will result in less memory, but tradeoff with speed. - - DeepSpeed allocates a reduce buffer size `multiplied by 1.5x `_ so take that into consideration when tweaking the parameters. - - The strategy sets a reasonable default of ``2e8``, which should work for most low VRAM GPUs (less than ``7GB``), allocating roughly ``3.6GB`` of VRAM as buffer. Higher VRAM GPUs should aim for values around ``5e8``. - -For even more speed benefit, DeepSpeed offers an optimized CPU version of ADAM called `DeepSpeedCPUAdam `_ to run the offloaded computation, which is faster than the standard PyTorch implementation. - -.. code-block:: python - - import pytorch_lightning - from pytorch_lightning import Trainer - from deepspeed.ops.adam import DeepSpeedCPUAdam - - - class MyModel(pl.LightningModule): - ... - - def configure_optimizers(self): - # DeepSpeedCPUAdam provides 5x to 7x speedup over torch.optim.adam(w) - return DeepSpeedCPUAdam(self.parameters()) - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_2_offload", precision=16) - trainer.fit(model) - - -.. _deepspeed-zero-stage-3: - -DeepSpeed ZeRO Stage 3 -"""""""""""""""""""""" - -DeepSpeed ZeRO Stage 3 shards the optimizer states, gradients and the model parameters (also optionally activations). Sharding model parameters and activations comes with an increase in distributed communication, however allows you to scale your models massively from one GPU to multiple GPUs. -**The DeepSpeed team report the ability to fine-tune models with over 40B parameters on a single GPU and over 2 Trillion parameters on 512 GPUs.** For more information we suggest checking the `DeepSpeed ZeRO-3 Offload documentation `__. - -We've ran benchmarks for all these features and given a simple example of how all these features work in Lightning, which you can see at `minGPT `_. - -To reach the highest memory efficiency or model size, you must: - -1. Use the DeepSpeed strategy with the stage 3 parameter -2. Use CPU Offloading to offload weights to CPU, plus have a reasonable amount of CPU RAM to offload onto -3. Use DeepSpeed Activation Checkpointing to shard activations - -Below we describe how to enable all of these to see benefit. **With all these improvements we reached 45 Billion parameters training a GPT model on 8 GPUs with ~1TB of CPU RAM available**. - -Also please have a look at our :ref:`deepspeed-zero-stage-3-tips` which contains a lot of helpful information when configuring your own models. - -.. note:: - - When saving a model using DeepSpeed and Stage 3, model states and optimizer states will be saved in separate sharded states (based on the world size). See :ref:`deepspeed-zero-stage-3-single-file` to obtain a single checkpoint file. - -.. code-block:: python - - from pytorch_lightning import Trainer - from deepspeed.ops.adam import FusedAdam - - - class MyModel(pl.LightningModule): - ... - - def configure_optimizers(self): - return FusedAdam(self.parameters()) - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) - trainer.fit(model) - - trainer.test() - trainer.predict() - - -You can also use the Lightning Trainer to run predict or evaluate with DeepSpeed once the model has been trained. - -.. code-block:: python - - from pytorch_lightning import Trainer - - - class MyModel(pl.LightningModule): - ... - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) - trainer.test(ckpt_path="my_saved_deepspeed_checkpoint.ckpt") - - -Shard Model Instantly to Reduce Initialization Time/Memory -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -When instantiating really large models, it is sometimes necessary to shard the model layers instantly. - -This is the case if layers may not fit on one single machines CPU or GPU memory, but would fit once sharded across multiple machines. -We expose a hook that layers initialized within the hook will be sharded instantly on a per layer basis, allowing you to instantly shard models. - -This reduces the time taken to initialize very large models, as well as ensure we do not run out of memory when instantiating larger models. For more information you can refer to the DeepSpeed docs for `Constructing Massive Models `_. - -.. code-block:: python - - import torch.nn as nn - from pytorch_lightning import Trainer - from deepspeed.ops.adam import FusedAdam - - - class MyModel(pl.LightningModule): - ... - - def configure_sharded_model(self): - # Created within sharded model context, modules are instantly sharded across processes - # as soon as they are made. - self.block = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - - def configure_optimizers(self): - return FusedAdam(self.parameters()) - - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3", precision=16) - trainer.fit(model) - - trainer.test() - trainer.predict() - - -.. _deepspeed-zero-stage-3-offload: - -DeepSpeed ZeRO Stage 3 Offload -"""""""""""""""""""""""""""""" - -DeepSpeed ZeRO Stage 3 Offloads optimizer state, gradients to the host CPU to reduce memory usage as ZeRO Stage 2 does, however additionally allows you to offload the parameters as well for even more memory saving. - -.. note:: - - When saving a model using DeepSpeed and Stage 3, model states and optimizer states will be saved in separate sharded states (based on the world size). See :ref:`deepspeed-zero-stage-3-single-file` to obtain a single checkpoint file. - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - # Enable CPU Offloading - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) - trainer.fit(model) - - # Enable CPU Offloading, and offload parameters to CPU - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DeepSpeedStrategy( - stage=3, - offload_optimizer=True, - offload_parameters=True, - ), - precision=16, - ) - trainer.fit(model) - - -DeepSpeed Infinity (NVMe Offloading) -"""""""""""""""""""""""""""""""""""" - -Additionally, DeepSpeed supports offloading to NVMe drives for even larger models, utilizing the large memory space found in NVMes. DeepSpeed `reports `__ the ability to fine-tune 1 Trillion+ parameters using NVMe Offloading on one 8 GPU machine. Below shows how to enable this, assuming the NVMe drive is mounted in a directory called ``/local_nvme``. - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - # Enable CPU Offloading - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) - trainer.fit(model) - - # Enable CPU Offloading, and offload parameters to CPU - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DeepSpeedStrategy( - stage=3, - offload_optimizer=True, - offload_parameters=True, - remote_device="nvme", - offload_params_device="nvme", - offload_optimizer_device="nvme", - nvme_path="/local_nvme", - ), - precision=16, - ) - trainer.fit(model) - -When offloading to NVMe you may notice that the speed is slow. There are parameters that need to be tuned based on the drives that you are using. Running the `aio_bench_perf_sweep.py `__ script can help you to find optimum parameters. See the `issue `__ for more information on how to parse the information. - -.. _deepspeed-activation-checkpointing: - -DeepSpeed Activation Checkpointing -"""""""""""""""""""""""""""""""""" - -Activation checkpointing frees activations from memory as soon as they are not needed during the forward pass. -They are then re-computed for the backwards pass as needed. - -Activation checkpointing is very useful when you have intermediate layers that produce large activations. - -This saves memory when training larger models, however requires using a checkpoint function to run modules as shown below. - -.. warning:: - - Ensure to not wrap the entire model with activation checkpointing. This is not the intended usage of activation checkpointing, and will lead to failures as seen in `this discussion `__. - -.. code-block:: python - - from pytorch_lightning import Trainer - import deepspeed - - - class MyModel(LightningModule): - ... - - def __init__(self): - super().__init__() - self.block_1 = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - self.block_2 = torch.nn.Linear(32, 2) - - def forward(self, x): - # Use the DeepSpeed checkpointing function instead of calling the module directly - # checkpointing self.block_1 means the activations are deleted after use, - # and re-calculated during the backward passes - x = deepspeed.checkpointing.checkpoint(self.block_1, x) - return self.block_2(x) - - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - import deepspeed - - - class MyModel(pl.LightningModule): - ... - - def configure_sharded_model(self): - self.block_1 = nn.Sequential(nn.Linear(32, 32), nn.ReLU()) - self.block_2 = torch.nn.Linear(32, 2) - - def forward(self, x): - # Use the DeepSpeed checkpointing function instead of calling the module directly - x = deepspeed.checkpointing.checkpoint(self.block_1, x) - return self.block_2(x) - - - model = MyModel() - - trainer = Trainer(accelerator="gpu", devices=4, strategy="deepspeed_stage_3_offload", precision=16) - - # Enable CPU Activation Checkpointing - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DeepSpeedStrategy( - stage=3, - offload_optimizer=True, # Enable CPU Offloading - cpu_checkpointing=True, # (Optional) offload activations to CPU - ), - precision=16, - ) - trainer.fit(model) - - -.. _deepspeed-zero-stage-3-tips: - -DeepSpeed ZeRO Stage 3 Tips -""""""""""""""""""""""""""" - -Here is some helpful information when setting up DeepSpeed ZeRO Stage 3 with Lightning. - -* If you're using Adam or AdamW, ensure to use FusedAdam or DeepSpeedCPUAdam (for CPU Offloading) rather than the default torch optimizers as they come with large speed benefits -* Treat your GPU/CPU memory as one large pool. In some cases, you may not want to offload certain things (like activations) to provide even more space to offload model parameters -* When offloading to the CPU, make sure to bump up the batch size as GPU memory will be freed -* We also support sharded checkpointing. By passing ``save_full_weights=False`` to the ``DeepSpeedStrategy``, we'll save shards of the model which allows you to save extremely large models. However to load the model and run test/validation/predict you must use the Trainer object. - -.. _deepspeed-zero-stage-3-single-file: - -Collating Single File Checkpoint for DeepSpeed ZeRO Stage 3 -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -After training using ZeRO Stage 3, you'll notice that your checkpoints are a directory of sharded model and optimizer states. If you'd like to collate a single file from the checkpoint directory please use the below command, which handles all the Lightning states additionally when collating the file. - -.. code-block:: python - - from pytorch_lightning.utilities.deepspeed import convert_zero_checkpoint_to_fp32_state_dict - - # lightning deepspeed has saved a directory instead of a file - save_path = "lightning_logs/version_0/checkpoints/epoch=0-step=0.ckpt/" - output_path = "lightning_model.pt" - convert_zero_checkpoint_to_fp32_state_dict(save_path, output_path) - - -.. warning:: - - This single file checkpoint does not include the optimizer/lr-scheduler states. This means we cannot restore training via the ``trainer.fit(ckpt_path=)`` call. Ensure to keep the sharded checkpoint directory if this is required. - -Custom DeepSpeed Config -""""""""""""""""""""""" - -In some cases you may want to define your own DeepSpeed Config, to access all parameters defined. We've exposed most of the important parameters, however, there may be debugging parameters to enable. Also, DeepSpeed allows the use of custom DeepSpeed optimizers and schedulers defined within a config file that is supported. - -.. note:: - All strategy default parameters will be ignored when a config object is passed. - All compatible arguments can be seen in the `DeepSpeed docs `_. - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - deepspeed_config = { - "zero_allow_untested_optimizer": True, - "optimizer": { - "type": "OneBitAdam", - "params": { - "lr": 3e-5, - "betas": [0.998, 0.999], - "eps": 1e-5, - "weight_decay": 1e-9, - "cuda_aware": True, - }, - }, - "scheduler": { - "type": "WarmupLR", - "params": { - "last_batch_iteration": -1, - "warmup_min_lr": 0, - "warmup_max_lr": 3e-5, - "warmup_num_steps": 100, - }, - }, - "zero_optimization": { - "stage": 2, # Enable Stage 2 ZeRO (Optimizer/Gradient state partitioning) - "offload_optimizer": True, # Enable Offloading optimizer state/calculation to the host CPU - "contiguous_gradients": True, # Reduce gradient fragmentation. - "overlap_comm": True, # Overlap reduce/backward operation of gradients for speed. - "allgather_bucket_size": 2e8, # Number of elements to all gather at once. - "reduce_bucket_size": 2e8, # Number of elements we reduce/allreduce at once. - }, - } - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy=DeepSpeedStrategy(config=deepspeed_config), precision=16) - trainer.fit(model) - - -We support taking the config as a json formatted file: - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DeepSpeedStrategy - - model = MyModel() - trainer = Trainer( - accelerator="gpu", devices=4, strategy=DeepSpeedStrategy(config="/path/to/deepspeed_config.json"), precision=16 - ) - trainer.fit(model) - - -You can use also use an environment variable via your PyTorch Lightning script: - -.. code-block:: bash - - PL_DEEPSPEED_CONFIG_PATH=/path/to/deepspeed_config.json python train.py --strategy deepspeed - ----------- - -.. _ddp-optimizations: - -DDP Optimizations -^^^^^^^^^^^^^^^^^ - - -When Using DDP Strategies, Set find_unused_parameters=False -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -By default, we have set ``find_unused_parameters=True`` for compatibility reasons that have been observed in the past (refer to the `discussion `_ for more details). -When enabled, it can result in a performance hit and can be disabled in most cases. Read more about it `here `_. - -.. tip:: - It applies to all DDP strategies that support ``find_unused_parameters`` as input. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy - - trainer = pl.Trainer( - accelerator="gpu", - devices=2, - strategy=DDPStrategy(find_unused_parameters=False), - ) - -.. code-block:: python - - from pytorch_lightning.strategies import DDPSpawnStrategy - - trainer = pl.Trainer( - accelerator="gpu", - devices=2, - strategy=DDPSpawnStrategy(find_unused_parameters=False), - ) - - -DDP Static Graph -"""""""""""""""" - -`DDP static graph `__ assumes that your model -employs the same set of used/unused parameters in every iteration, so that it can deterministically know the flow of -training and apply special optimizations during runtime. - -.. note:: - DDP static graph support requires PyTorch>=1.11.0 - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - - trainer = Trainer(devices=4, strategy=DDPStrategy(static_graph=True)) - - -When Using DDP on a Multi-node Cluster, Set NCCL Parameters -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -`NCCL `__ is the NVIDIA Collective Communications Library that is used by PyTorch to handle communication across nodes and GPUs. There are reported benefits in terms of speedups when adjusting NCCL parameters as seen in this `issue `__. In the issue, we see a 30% speed improvement when training the Transformer XLM-RoBERTa and a 15% improvement in training with Detectron2. - -NCCL parameters can be adjusted via environment variables. - -.. note:: - - AWS and GCP already set default values for these on their clusters. This is typically useful for custom cluster setups. - -* `NCCL_NSOCKS_PERTHREAD `__ -* `NCCL_SOCKET_NTHREADS `__ -* `NCCL_MIN_NCHANNELS `__ - -.. code-block:: bash - - export NCCL_NSOCKS_PERTHREAD=4 - export NCCL_SOCKET_NTHREADS=2 - - -Gradients as Bucket View -"""""""""""""""""""""""" - -Enabling ``gradient_as_bucket_view=True`` in the ``DDPStrategy`` will make gradients views point to different offsets of the ``allreduce`` communication buckets. See :class:`~torch.nn.parallel.DistributedDataParallel` for more information. - -This can reduce peak memory usage and throughput as saved memory will be equal to the total gradient memory + removes the need to copy gradients to the ``allreduce`` communication buckets. - -.. note:: - - When ``gradient_as_bucket_view=True`` you cannot call ``detach_()`` on gradients. If hitting such errors, please fix it by referring to the :meth:`~torch.optim.Optimizer.zero_grad` function in ``torch/optim/optimizer.py`` as a solution (`source `__). - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy=DDPStrategy(gradient_as_bucket_view=True)) - trainer.fit(model) - -DDP Communication Hooks -""""""""""""""""""""""" - -DDP Communication hooks is an interface to control how gradients are communicated across workers, overriding the standard allreduce in DistributedDataParallel. This allows you to enable performance improving communication hooks when using multiple nodes. - -Enable `FP16 Compress Hook for multi-node throughput improvement `__: - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - from torch.distributed.algorithms.ddp_comm_hooks import default_hooks as default - - model = MyModel() - trainer = Trainer(accelerator="gpu", devices=4, strategy=DDPStrategy(ddp_comm_hook=default.fp16_compress_hook)) - trainer.fit(model) - -Enable `PowerSGD for multi-node throughput improvement `__: - -.. note:: - - PowerSGD typically requires extra memory of the same size as the model’s gradients to enable error feedback, which can compensate for biased compressed communication and improve accuracy (`source `__). - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - from torch.distributed.algorithms.ddp_comm_hooks import powerSGD_hook as powerSGD - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DDPStrategy( - ddp_comm_state=powerSGD.PowerSGDState( - process_group=None, - matrix_approximation_rank=1, - start_powerSGD_iter=5000, - ), - ddp_comm_hook=powerSGD.powerSGD_hook, - ), - ) - trainer.fit(model) - - -Combine hooks for accumulated benefit: - -.. note:: - DDP communication wrappers support requires PyTorch>=1.9.0 - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - from torch.distributed.algorithms.ddp_comm_hooks import ( - default_hooks as default, - powerSGD_hook as powerSGD, - ) - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DDPStrategy( - ddp_comm_state=powerSGD.PowerSGDState( - process_group=None, - matrix_approximation_rank=1, - start_powerSGD_iter=5000, - ), - ddp_comm_hook=powerSGD.powerSGD_hook, - ddp_comm_wrapper=default.fp16_compress_wrapper, - ), - ) - trainer.fit(model) - - -When using Post-localSGD, you must also pass ``model_averaging_period`` to allow for model parameter averaging: - -.. note:: - Post-localSGD support requires PyTorch>=1.10.0 - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.strategies import DDPStrategy - from torch.distributed.algorithms.ddp_comm_hooks import post_localSGD_hook as post_localSGD - - model = MyModel() - trainer = Trainer( - accelerator="gpu", - devices=4, - strategy=DDPStrategy( - ddp_comm_state=post_localSGD.PostLocalSGDState( - process_group=None, - subgroup=None, - start_localSGD_iter=8, - ), - ddp_comm_hook=post_localSGD.post_localSGD_hook, - model_averaging_period=4, - ), - ) - trainer.fit(model) diff --git a/source/advanced/pruning_quantization.rst b/source/advanced/pruning_quantization.rst deleted file mode 100644 index 552a96d..0000000 --- a/source/advanced/pruning_quantization.rst +++ /dev/null @@ -1,115 +0,0 @@ -.. _pruning_quantization: - -######################## -Pruning and Quantization -######################## - -Pruning and Quantization are techniques to compress model size for deployment, allowing inference speed up and energy saving without significant accuracy losses. - -******* -Pruning -******* - -.. warning:: - - Pruning is in beta and subject to change. - -Pruning is a technique which focuses on eliminating some of the model weights to reduce the model size and decrease inference requirements. - -Pruning has been shown to achieve significant efficiency improvements while minimizing the drop in model performance (prediction quality). Model pruning is recommended for cloud endpoints, deploying models on edge devices, or mobile inference (among others). - -To enable pruning during training in Lightning, simply pass in the :class:`~pytorch_lightning.callbacks.ModelPruning` callback to the Lightning Trainer. PyTorch's native pruning implementation is used under the hood. - -This callback supports multiple pruning functions: pass any `torch.nn.utils.prune `_ function as a string to select which weights to prune (`random_unstructured `_, `RandomStructured `_, etc) or implement your own by subclassing `BasePruningMethod `_. - -.. code-block:: python - - from pytorch_lightning.callbacks import ModelPruning - - # set the amount to be the fraction of parameters to prune - trainer = Trainer(callbacks=[ModelPruning("l1_unstructured", amount=0.5)]) - -You can also perform iterative pruning, apply the `lottery ticket hypothesis `__, and more! - -.. code-block:: python - - def compute_amount(epoch): - # the sum of all returned values need to be smaller than 1 - if epoch == 10: - return 0.5 - - elif epoch == 50: - return 0.25 - - elif 75 < epoch < 99: - return 0.01 - - - # the amount can be also be a callable - trainer = Trainer(callbacks=[ModelPruning("l1_unstructured", amount=compute_amount)]) - - -************ -Quantization -************ - -.. warning :: - Quantization is in beta and subject to change. - -Model quantization is another performance optimization technique that allows speeding up inference and decreasing memory requirements by performing computations and storing tensors at lower bitwidths (such as INT8 or FLOAT16) than floating-point precision. This is particularly beneficial during model deployment. - -Quantization Aware Training (QAT) mimics the effects of quantization during training: The computations are carried-out in floating-point precision but the subsequent quantization effect is taken into account. The weights and activations are quantized into lower precision only for inference, when training is completed. - -Quantization is useful when it is required to serve large models on machines with limited memory, or when there's a need to switch between models and reducing the I/O time is important. For example, switching between monolingual speech recognition models across multiple languages. - -Lightning includes :class:`~pytorch_lightning.callbacks.QuantizationAwareTraining` callback (using PyTorch's native quantization, read more `here `__), which allows creating fully quantized models (compatible with torchscript). - -.. code-block:: python - - from pytorch_lightning.callbacks import QuantizationAwareTraining - - - class RegressionModel(LightningModule): - def __init__(self): - super().__init__() - self.layer_0 = nn.Linear(16, 64) - self.layer_0a = torch.nn.ReLU() - self.layer_1 = nn.Linear(64, 64) - self.layer_1a = torch.nn.ReLU() - self.layer_end = nn.Linear(64, 1) - - def forward(self, x): - x = self.layer_0(x) - x = self.layer_0a(x) - x = self.layer_1(x) - x = self.layer_1a(x) - x = self.layer_end(x) - return x - - - trainer = Trainer(callbacks=[QuantizationAwareTraining()]) - qmodel = RegressionModel() - trainer.fit(qmodel, ...) - - batch = iter(my_dataloader()).next() - qmodel(qmodel.quant(batch[0])) - - tsmodel = qmodel.to_torchscript() - tsmodel(tsmodel.quant(batch[0])) - -You can further customize the callback: - -.. code-block:: python - - - qcb = QuantizationAwareTraining( - # specification of quant estimation quality - observer_type="histogram", - # specify which layers shall be merged together to increase efficiency - modules_to_fuse=[(f"layer_{i}", f"layer_{i}a") for i in range(2)], - # make your model compatible with all original input/outputs, in such case the model is wrapped in a shell with entry/exit layers. - input_compatible=True, - ) - - batch = iter(my_dataloader()).next() - qmodel(batch[0]) diff --git a/source/advanced/training_tricks.rst b/source/advanced/training_tricks.rst deleted file mode 100644 index a8d5c2d..0000000 --- a/source/advanced/training_tricks.rst +++ /dev/null @@ -1,356 +0,0 @@ -.. testsetup:: * - - from pytorch_lightning.callbacks import StochasticWeightAveraging - -.. _training_tricks: - -############################# -Effective Training Techniques -############################# - -Lightning implements various techniques to help during training that can help make the training smoother. - ----------- - -******************** -Accumulate Gradients -******************** - -.. include:: ../common/gradient_accumulation.rst - ----------- - -***************** -Gradient Clipping -***************** - -Gradient clipping can be enabled to avoid exploding gradients. By default, this will clip the gradient norm by calling -:func:`torch.nn.utils.clip_grad_norm_` computed over all model parameters together. -If the Trainer's ``gradient_clip_algorithm`` is set to ``'value'`` (``'norm'`` by default), this will use instead -:func:`torch.nn.utils.clip_grad_value_` for each parameter instead. - -.. note:: - If using mixed precision, the ``gradient_clip_val`` does not need to be changed as the gradients are unscaled - before applying the clipping function. - -.. seealso:: :class:`~pytorch_lightning.trainer.trainer.Trainer` - -.. testcode:: - - # DEFAULT (ie: don't clip) - trainer = Trainer(gradient_clip_val=0) - - # clip gradients' global norm to <=0.5 using gradient_clip_algorithm='norm' by default - trainer = Trainer(gradient_clip_val=0.5) - - # clip gradients' maximum magnitude to <=0.5 - trainer = Trainer(gradient_clip_val=0.5, gradient_clip_algorithm="value") - -Read more about :ref:`Configuring Gradient Clipping ` for advanced use-cases. - ----------- - -*************************** -Stochastic Weight Averaging -*************************** - -Stochastic Weight Averaging (SWA) can make your models generalize better at virtually no additional cost. -This can be used with both non-trained and trained models. The SWA procedure smooths the loss landscape thus making -it harder to end up in a local minimum during optimization. - -For a more detailed explanation of SWA and how it works, -read `this post `__ by the PyTorch team. - -.. seealso:: The :class:`~pytorch_lightning.callbacks.StochasticWeightAveraging` callback - -.. testcode:: - - # Enable Stochastic Weight Averaging using the callback - trainer = Trainer(callbacks=[StochasticWeightAveraging(swa_lrs=1e-2)]) - ----------- - -***************** -Batch Size Finder -***************** - -Auto-scaling of batch size can be enabled to find the largest batch size that fits into -memory. Large batch size often yields a better estimation of the gradients, but may also result in -longer training time. Inspired by https://github.com/BlackHC/toma. - -.. seealso:: :class:`~pytorch_lightning.trainer.trainer.Trainer` - -.. code-block:: python - - # DEFAULT (ie: don't scale batch size automatically) - trainer = Trainer(auto_scale_batch_size=None) - - # Autoscale batch size - trainer = Trainer(auto_scale_batch_size=None | "power" | "binsearch") - - # Find the batch size - trainer.tune(model) - -Currently, this feature supports two modes ``'power'`` scaling and ``'binsearch'`` -scaling. In ``'power'`` scaling, starting from a batch size of 1 keeps doubling -the batch size until an out-of-memory (OOM) error is encountered. Setting the -argument to ``'binsearch'`` will initially also try doubling the batch size until -it encounters an OOM, after which it will do a binary search that will finetune the -batch size. Additionally, it should be noted that the batch size scaler cannot -search for batch sizes larger than the size of the training dataset. - - -.. note:: - - This feature expects that a ``batch_size`` field is either located as a model attribute - i.e. ``model.batch_size`` or as a field in your ``hparams`` i.e. ``model.hparams.batch_size``. - Similarly it can work with datamodules too. The field should exist and will be updated by - the results of this algorithm. Additionally, your ``train_dataloader()`` method should depend - on this field for this feature to work i.e. - - .. code-block:: python - - # using LightningModule - class LitModel(LightningModule): - def __init__(self, batch_size): - super().__init__() - self.save_hyperparameters() - # or - self.batch_size = batch_size - - def train_dataloader(self): - return DataLoader(train_dataset, batch_size=self.batch_size | self.hparams.batch_size) - - - trainer = Trainer(...) - model = LitModel(batch_size=32) - trainer.tune(model) - - # using LightningDataModule - class LitDataModule(LightningDataModule): - def __init__(self, batch_size): - super().__init__() - self.save_hyperparameters() - # or - self.batch_size = batch_size - - def train_dataloader(self): - return DataLoader(train_dataset, batch_size=self.batch_size | self.hparams.batch_size) - - - trainer = Trainer(...) - model = MyModel() - datamodule = LitDataModule(batch_size=32) - trainer.tune(model, datamodule=datamodule) - -.. warning:: - - Due to the constraints listed above, this features does *NOT* work when passing dataloaders directly - to ``.fit()``. - -The scaling algorithm has a number of parameters that the user can control by -invoking the :meth:`~pytorch_lightning.tuner.tuning.Tuner.scale_batch_size` method: - -.. code-block:: python - - # Use default in trainer construction - trainer = Trainer() - tuner = Tuner(trainer) - - # Invoke method - new_batch_size = tuner.scale_batch_size(model, *extra_parameters_here) - - # Override old batch size (this is done automatically) - model.hparams.batch_size = new_batch_size - - # Fit as normal - trainer.fit(model) - -The algorithm in short works by: - 1. Dumping the current state of the model and trainer - 2. Iteratively until convergence or maximum number of tries ``max_trials`` (default 25) has been reached: - - Call ``fit()`` method of trainer. This evaluates ``steps_per_trial`` (default 3) number of - optimization steps. Each training step can trigger an OOM error if the tensors - (training batch, weights, gradients, etc.) allocated during the steps have a - too large memory footprint. - - If an OOM error is encountered, decrease batch size else increase it. - How much the batch size is increased/decreased is determined by the chosen - strategy. - 3. The found batch size is saved to either ``model.batch_size`` or ``model.hparams.batch_size`` - 4. Restore the initial state of model and trainer - -.. warning:: Batch size finder is not yet supported for DDP or any of its variations, it is coming soon. - ----------- - -.. _learning_rate_finder: - -******************** -Learning Rate Finder -******************** - -.. raw:: html - - - -| - -For training deep neural networks, selecting a good learning rate is essential -for both better performance and faster convergence. Even optimizers such as -:class:`~torch.optim.Adam` that are self-adjusting the learning rate can benefit from more optimal -choices. - -To reduce the amount of guesswork concerning choosing a good initial learning -rate, a `learning rate finder` can be used. As described in `this paper `_ -a learning rate finder does a small run where the learning rate is increased -after each processed batch and the corresponding loss is logged. The result of -this is a ``lr`` vs. ``loss`` plot that can be used as guidance for choosing an optimal -initial learning rate. - -.. warning:: - - For the moment, this feature only works with models having a single optimizer. - LR Finder support for DDP and any of its variations is not implemented yet. It is coming soon. - - -Using Lightning's built-in LR finder -==================================== - -To enable the learning rate finder, your :doc:`lightning module <../common/lightning_module>` needs to -have a ``learning_rate`` or ``lr`` attribute (or as a field in your ``hparams`` i.e. -``hparams.learning_rate`` or ``hparams.lr``). Then, set ``Trainer(auto_lr_find=True)`` -during trainer construction, and then call ``trainer.tune(model)`` to run the LR finder. -The suggested ``learning_rate`` will be written to the console and will be automatically -set to your :doc:`lightning module <../common/lightning_module>`, which can be accessed -via ``self.learning_rate`` or ``self.lr``. - -.. seealso:: :ref:`trainer.tune `. - -.. code-block:: python - - class LitModel(LightningModule): - def __init__(self, learning_rate): - super().__init__() - self.learning_rate = learning_rate - self.model = Model(...) - - def configure_optimizers(self): - return Adam(self.parameters(), lr=(self.lr or self.learning_rate)) - - - model = LitModel() - - # finds learning rate automatically - # sets hparams.lr or hparams.learning_rate to that learning rate - trainer = Trainer(auto_lr_find=True) - - trainer.tune(model) - -If your model is using an arbitrary value instead of ``self.lr`` or ``self.learning_rate``, set that value as ``auto_lr_find``: - -.. code-block:: python - - model = LitModel() - - # to set to your own hparams.my_value - trainer = Trainer(auto_lr_find="my_value") - - trainer.tune(model) - -You can also inspect the results of the learning rate finder or just play around -with the parameters of the algorithm. This can be done by invoking the -:meth:`~pytorch_lightning.tuner.tuning.Tuner.lr_find` method. A typical example of this would look like: - -.. code-block:: python - - model = MyModelClass(hparams) - trainer = Trainer() - - # Run learning rate finder - lr_finder = trainer.tuner.lr_find(model) - - # Results can be found in - print(lr_finder.results) - - # Plot with - fig = lr_finder.plot(suggest=True) - fig.show() - - # Pick point based on plot, or get suggestion - new_lr = lr_finder.suggestion() - - # update hparams of the model - model.hparams.lr = new_lr - - # Fit model - trainer.fit(model) - -The figure produced by ``lr_finder.plot()`` should look something like the figure -below. It is recommended to not pick the learning rate that achieves the lowest -loss, but instead something in the middle of the sharpest downward slope (red point). -This is the point returned py ``lr_finder.suggestion()``. - -.. figure:: ../_static/images/trainer/lr_finder.png - ----------- - -************************** -Advanced GPU Optimizations -************************** - -When training on single or multiple GPU machines, Lightning offers a host of advanced optimizations to improve throughput, memory efficiency, and model scaling. -Refer to :doc:`Advanced GPU Optimized Training <../advanced/model_parallel>` for more details. - ----------- - - -.. _ddp_spawn_shared_memory: - -****************************************** -Sharing Datasets Across Process Boundaries -****************************************** - -The :class:`~pytorch_lightning.core.datamodule.LightningDataModule` class provides an organized way to decouple data loading from training logic, with :meth:`~pytorch_lightning.core.hooks.DataHooks.prepare_data` being used for downloading and pre-processing the dataset on a single process, and :meth:`~pytorch_lightning.core.hooks.DataHooks.setup` loading the pre-processed data for each process individually: - -.. code-block:: python - - class MNISTDataModule(pl.LightningDataModule): - def prepare_data(self): - MNIST(self.data_dir, download=True) - - def setup(self, stage: Optional[str] = None): - self.mnist = MNIST(self.data_dir) - - def train_loader(self): - return DataLoader(self.mnist, batch_size=128) - -However, for in-memory datasets, that means that each process will hold a (redundant) replica of the dataset in memory, which may be impractical when using many processes while utilizing datasets that nearly fit into CPU memory, as the memory consumption will scale up linearly with the number of processes. -For example, when training Graph Neural Networks, a common strategy is to load the entire graph into CPU memory for fast access to the entire graph structure and its features, and to then perform neighbor sampling to obtain mini-batches that fit onto the GPU. - -A simple way to prevent redundant dataset replicas is to rely on :obj:`torch.multiprocessing` to share the `data automatically between spawned processes via shared memory `_. -For this, all data pre-loading should be done on the main process inside :meth:`DataModule.__init__`. As a result, all tensor-data will get automatically shared when using the :class:`~pytorch_lightning.plugins.strategies.ddp_spawn.DDPSpawnStrategy` strategy. - -.. warning:: - - :obj:`torch.multiprocessing` will send a handle of each individual tensor to other processes. - In order to prevent any errors due to too many open file handles, try to reduce the number of tensors to share, *e.g.*, by stacking your data into a single tensor. - -.. code-block:: python - - class MNISTDataModule(pl.LightningDataModule): - def __init__(self, data_dir: str): - self.mnist = MNIST(data_dir, download=True, transform=T.ToTensor()) - - def train_loader(self): - return DataLoader(self.mnist, batch_size=128) - - - model = Model(...) - datamodule = MNISTDataModule("data/MNIST") - - trainer = Trainer(accelerator="gpu", devices=2, strategy="ddp_spawn") - trainer.fit(model, datamodule) - -See the `graph-level `_ and `node-level `_ prediction examples in PyTorch Geometric for practical use-cases. diff --git a/source/benchmarking/benchmarks.rst b/source/benchmarking/benchmarks.rst deleted file mode 100644 index af9715f..0000000 --- a/source/benchmarking/benchmarks.rst +++ /dev/null @@ -1,19 +0,0 @@ -:orphan: - -Benchmark with vanilla PyTorch -============================== - -In this section we set grounds for comparison between vanilla PyTorch and PT Lightning for most common scenarios. - -Time comparison ---------------- - -We have set regular benchmarking against PyTorch vanilla training loop on with RNN and simple MNIST classifier as per of out CI. -In average for simple MNIST CNN classifier we are only about 0.06s slower per epoch, see detail chart bellow. - -.. figure:: ../_static/images/benchmarks/figure-parity-times.png - :alt: Speed parity to vanilla PT, created on 2020-12-16 - :width: 500 - - -Learn more about reproducible benchmarking from the `PyTorch Reproducibility Guide `__. diff --git a/source/cli/lightning_cli.rst b/source/cli/lightning_cli.rst deleted file mode 100644 index 76f3f12..0000000 --- a/source/cli/lightning_cli.rst +++ /dev/null @@ -1,94 +0,0 @@ -:orphan: - -.. _lightning-cli: - -############################ -Eliminate config boilerplate -############################ - -********* -Basic use -********* - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Control it all from the CLI - :description: Learn to control a LightningModule and LightningDataModule from the CLI - :col_css: col-md-4 - :button_link: lightning_cli_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 2: Mix models and datasets - :description: Register models, datasets, optimizers and learning rate schedulers - :col_css: col-md-4 - :button_link: lightning_cli_intermediate_2.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 3: Control it all via YAML - :description: Enable composable YAMLs - :col_css: col-md-4 - :button_link: lightning_cli_advanced.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
- ----- - -************ -Advanced use -************ - -.. raw:: html - -
-
- -.. displayitem:: - :header: YAML for production - :description: Use the Lightning CLI with YAMLs for production environments - :col_css: col-md-6 - :button_link: lightning_cli_advanced_2.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Customize configs for complex projects - :description: Learn how to connect complex projects with each Registry. - :col_css: col-md-6 - :button_link: lightning_cli_advanced_3.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Extend the Lightning CLI - :description: Customize the Lightning CLI - :col_css: col-md-6 - :button_link: lightning_cli_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: FAQ - :description: Frequently asked questions about working with the Lightning CLI and YAML files - :col_css: col-md-6 - :button_link: lightning_cli_faq.html - :height: 150 - -.. raw:: html - -
-
diff --git a/source/cli/lightning_cli_advanced.rst b/source/cli/lightning_cli_advanced.rst deleted file mode 100644 index 2d4f330..0000000 --- a/source/cli/lightning_cli_advanced.rst +++ /dev/null @@ -1,113 +0,0 @@ -:orphan: - -####################################### -Eliminate config boilerplate (Advanced) -####################################### -**Audience:** Users looking to modularize their code for a professional project. - -**Pre-reqs:** You must have read :doc:`(Control it all from the CLI) `. - ----- - -*************************** -What is a yaml config file? -*************************** -A yaml is a standard configuration file that describes parameters for sections of a program. It is a common tool in engineering, and it has recently started to gain popularity in machine learning. - -.. code:: yaml - - # file.yaml - car: - max_speed:100 - max_passengers:2 - plane: - fuel_capacity: 50 - class_3: - option_1: 'x' - option_2: 'y' - ----- - - -********************* -Print the config used -********************* -Before or after you run a training routine, you can print the full training spec in yaml format using ``--print_config``: - -.. code:: bash - - python main.py fit --print_config - -which generates the following config: - -.. code:: bash - - seed_everything: null - trainer: - logger: true - ... - terminate_on_nan: null - model: - out_dim: 10 - learning_rate: 0.02 - data: - data_dir: ./ - ckpt_path: null - ----- - -******************************** -Write a config yaml from the CLI -******************************** -To have a copy of the configuration that produced this model, save a *yaml* file from the *--print_config* outputs: - -.. code:: bash - - python main.py fit --model.learning_rate 0.001 --print_config > config.yaml - ----- - -********************** -Run from a single yaml -********************** -To run from a yaml, pass a yaml produced with ``--print_config`` to the ``--config`` argument: - -.. code:: bash - - python main.py fit --config config.yaml - -when using a yaml to run, you can still pass in inline arguments - -.. code:: bash - - python main.py fit --config config.yaml --trainer.max_epochs 100 - ----- - -****************** -Compose yaml files -****************** -For production or complex research projects it's advisable to have each object in its own config file. To compose all the configs, pass them all inline: - -.. code-block:: bash - - $ python trainer.py fit --config trainer.yaml --config datamodules.yaml --config models.yaml ... - -The configs will be parsed sequentially. Let's say we have two configs with the same args: - -.. code:: yaml - - # trainer.yaml - trainer: - num_epochs: 10 - - - # trainer_2.yaml - trainer: - num_epochs: 20 - -the ones from the last config will be used (num_epochs = 20) in this case: - -.. code-block:: bash - - $ python trainer.py fit --config trainer.yaml --config trainer_2.yaml diff --git a/source/cli/lightning_cli_advanced_2.rst b/source/cli/lightning_cli_advanced_2.rst deleted file mode 100644 index 0474699..0000000 --- a/source/cli/lightning_cli_advanced_2.rst +++ /dev/null @@ -1,207 +0,0 @@ -:orphan: - -.. testsetup:: * - :skipif: not _JSONARGPARSE_AVAILABLE - - import torch - from unittest import mock - from typing import List - import pytorch_lightning as pl - from pytorch_lightning import LightningModule, LightningDataModule, Trainer, Callback - - - class NoFitTrainer(Trainer): - def fit(self, *_, **__): - pass - - - class LightningCLI(pl.utilities.cli.LightningCLI): - def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): - super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) - - - class MyModel(LightningModule): - def __init__( - self, - encoder_layers: int = 12, - decoder_layers: List[int] = [2, 4], - batch_size: int = 8, - ): - pass - - - class MyDataModule(LightningDataModule): - def __init__(self, batch_size: int = 8): - self.num_classes = 5 - - - mock_argv = mock.patch("sys.argv", ["any.py"]) - mock_argv.start() - -.. testcleanup:: * - - mock_argv.stop() - -####################################### -Eliminate config boilerplate (Advanced) -####################################### - -****************************** -Customize arguments by command -****************************** -To customize arguments by subcommand, pass the config *before* the subcommand: - -.. code-block:: bash - - $ python main.py [before] [subcommand] [after] - $ python main.py ... fit ... - -For example, here we set the Trainer argument [max_steps = 100] for the full training routine and [max_steps = 10] for testing: - -.. code-block:: bash - - # config1.yaml - fit: - trainer: - max_steps: 100 - test: - trainer: - max_epochs: 10 - -now you can toggle this behavior by subcommand: - -.. code-block:: bash - - # full routine with max_steps = 100 - $ python main.py --config config1.yaml fit - - # test only with max_epochs = 10 - $ python main.py --config config1.yaml test - ----- - -********************* -Use groups of options -********************* -Groups of options can also be given as independent config files: - -.. code-block:: bash - - $ python trainer.py fit --trainer trainer.yaml --model model.yaml --data data.yaml [...] - ----- - -*************************** -Run from cloud yaml configs -*************************** -For certain enterprise workloads, Lightning CLI supports running from hosted configs: - -.. code-block:: bash - - $ python trainer.py [subcommand] --config s3://bucket/config.yaml - -For more options, refer to :doc:`Remote filesystems <../common/remote_fs>`. - ----- - -************************************** -Use a config via environment variables -************************************** -For certain CI/CD systems, it's useful to pass in config files as environment variables: - -.. code-block:: bash - - $ python trainer.py fit --trainer "$TRAINER_CONFIG" --model "$MODEL_CONFIG" [...] - ----- - -*************************************** -Run from environment variables directly -*************************************** -The Lightning CLI can convert every possible CLI flag into an environment variable. To enable this, set the *env_parse* argument: - -.. code:: python - - LightningCLI(env_parse=True) - -now use the ``--help`` CLI flag with any subcommand: - -.. code:: bash - - $ python main.py fit --help - -which will show you ALL possible environment variables you can now set: - -.. code:: bash - - usage: main.py [options] fit [-h] [-c CONFIG] - [--trainer.max_epochs MAX_EPOCHS] [--trainer.min_epochs MIN_EPOCHS] - [--trainer.max_steps MAX_STEPS] [--trainer.min_steps MIN_STEPS] - ... - [--ckpt_path CKPT_PATH] - - optional arguments: - ... - --model CONFIG Path to a configuration file. - --model.out_dim OUT_DIM - (type: int, default: 10) - --model.learning_rate LEARNING_RATE - (type: float, default: 0.02) - -now you can customize the behavior via environment variables: - -.. code:: bash - - # set the options via env vars - $ export LEARNING_RATE=0.01 - $ export OUT_DIM=5 - - $ python main.py fit - ----- - -************************ -Set default config files -************************ -To set a path to a config file of defaults, use the ``default_config_files`` argument: - -.. testcode:: - - cli = LightningCLI(MyModel, MyDataModule, parser_kwargs={"default_config_files": ["my_cli_defaults.yaml"]}) - -or if you want defaults per subcommand: - -.. testcode:: - - cli = LightningCLI(MyModel, MyDataModule, parser_kwargs={"fit": {"default_config_files": ["my_fit_defaults.yaml"]}}) - -For more configuration options, refer to the `ArgumentParser API -`_ documentation. - ----- - -***************************** -Enable variable interpolation -***************************** -In certain cases where multiple configs need to share variables, consider using variable interpolation. Variable interpolation -allows you to add variables to your yaml configs like so: - -.. code-block:: yaml - - model: - encoder_layers: 12 - decoder_layers: - - ${model.encoder_layers} - - 4 - -To enable variable interpolation, first install omegaconf: - -.. code:: bash - - pip install omegaconf - -Once this is installed, the Lightning CLI will automatically handle variables in yaml files: - -.. code bash: - - python main.py --model.encoder_layers=12 diff --git a/source/cli/lightning_cli_advanced_3.rst b/source/cli/lightning_cli_advanced_3.rst deleted file mode 100644 index 2eeae17..0000000 --- a/source/cli/lightning_cli_advanced_3.rst +++ /dev/null @@ -1,415 +0,0 @@ -:orphan: - -.. testsetup:: * - :skipif: not _JSONARGPARSE_AVAILABLE - - import torch - from unittest import mock - from typing import List - import pytorch_lightning as pl - from pytorch_lightning import LightningModule, LightningDataModule, Trainer, Callback - - - class NoFitTrainer(Trainer): - def fit(self, *_, **__): - pass - - - class LightningCLI(pl.utilities.cli.LightningCLI): - def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): - super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) - - - class MyModel(LightningModule): - def __init__( - self, - encoder_layers: int = 12, - decoder_layers: List[int] = [2, 4], - batch_size: int = 8, - ): - pass - - - class MyDataModule(LightningDataModule): - def __init__(self, batch_size: int = 8): - self.num_classes = 5 - - - MyModelBaseClass = MyModel - MyDataModuleBaseClass = MyDataModule - - mock_argv = mock.patch("sys.argv", ["any.py"]) - mock_argv.start() - -.. testcleanup:: * - - mock_argv.stop() - -Instantiation only mode -^^^^^^^^^^^^^^^^^^^^^^^ - -The CLI is designed to start fitting with minimal code changes. On class instantiation, the CLI will automatically -call the trainer function associated to the subcommand provided so you don't have to do it. -To avoid this, you can set the following argument: - -.. testcode:: - - cli = LightningCLI(MyModel, run=False) # True by default - # you'll have to call fit yourself: - cli.trainer.fit(cli.model) - -In this mode, there are subcommands added to the parser. -This can be useful to implement custom logic without having to subclass the CLI, but still using the CLI's instantiation -and argument parsing capabilities. - - -Subclass registration -^^^^^^^^^^^^^^^^^^^^^ - -To use shorthand notation, the options need to be registered beforehand. This can be easily done with: - -.. code-block:: - - LightningCLI(auto_registry=True) # False by default - -which will register all subclasses of :class:`torch.optim.Optimizer`, :class:`torch.optim.lr_scheduler._LRScheduler`, -:class:`~pytorch_lightning.core.lightning.LightningModule`, -:class:`~pytorch_lightning.core.datamodule.LightningDataModule`, :class:`~pytorch_lightning.callbacks.Callback`, and -:class:`~pytorch_lightning.loggers.LightningLoggerBase` across all imported modules. This includes those in your own -code. - -Alternatively, if this is left unset, only the subclasses defined in PyTorch's :class:`torch.optim.Optimizer`, -:class:`torch.optim.lr_scheduler._LRScheduler` and Lightning's :class:`~pytorch_lightning.callbacks.Callback` and -:class:`~pytorch_lightning.loggers.LightningLoggerBase` subclassess will be registered. - -In subsequent sections, we will go over adding specific classes to specific registries as well as how to use -shorthand notation. - - -Trainer Callbacks and arguments with class type -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -A very important argument of the :class:`~pytorch_lightning.trainer.trainer.Trainer` class is the :code:`callbacks`. In -contrast to other more simple arguments which just require numbers or strings, :code:`callbacks` expects a list of -instances of subclasses of :class:`~pytorch_lightning.callbacks.Callback`. To specify this kind of argument in a config -file, each callback must be given as a dictionary including a :code:`class_path` entry with an import path of the class, -and optionally an :code:`init_args` entry with arguments required to instantiate it. Therefore, a simple configuration -file example that defines a couple of callbacks is the following: - -.. code-block:: yaml - - trainer: - callbacks: - - class_path: pytorch_lightning.callbacks.EarlyStopping - init_args: - patience: 5 - - class_path: pytorch_lightning.callbacks.LearningRateMonitor - init_args: - ... - -Similar to the callbacks, any arguments in :class:`~pytorch_lightning.trainer.trainer.Trainer` and user extended -:class:`~pytorch_lightning.core.lightning.LightningModule` and -:class:`~pytorch_lightning.core.datamodule.LightningDataModule` classes that have as type hint a class can be configured -the same way using :code:`class_path` and :code:`init_args`. - -For callbacks in particular, Lightning simplifies the command line so that only -the :class:`~pytorch_lightning.callbacks.Callback` name is required. -The argument's order matters and the user needs to pass the arguments in the following way. - -.. code-block:: bash - - $ python ... \ - --trainer.callbacks={CALLBACK_1_NAME} \ - --trainer.callbacks.{CALLBACK_1_ARGS_1}=... \ - --trainer.callbacks.{CALLBACK_1_ARGS_2}=... \ - ... - --trainer.callbacks={CALLBACK_N_NAME} \ - --trainer.callbacks.{CALLBACK_N_ARGS_1}=... \ - ... - -Here is an example: - -.. code-block:: bash - - $ python ... \ - --trainer.callbacks=EarlyStopping \ - --trainer.callbacks.patience=5 \ - --trainer.callbacks=LearningRateMonitor \ - --trainer.callbacks.logging_interval=epoch - -Lightning provides a mechanism for you to add your own callbacks and benefit from the command line simplification -as described above: - -.. code-block:: python - - from pytorch_lightning.utilities.cli import CALLBACK_REGISTRY - - - @CALLBACK_REGISTRY - class CustomCallback(Callback): - ... - - - cli = LightningCLI(...) - -.. code-block:: bash - - $ python ... --trainer.callbacks=CustomCallback ... - -.. note:: - - This shorthand notation is only supported in the shell and not inside a configuration file. The configuration file - generated by calling the previous command with ``--print_config`` will have the ``class_path`` notation. - - .. code-block:: yaml - - trainer: - callbacks: - - class_path: your_class_path.CustomCallback - init_args: - ... - - -.. tip:: - - ``--trainer.logger`` also supports shorthand notation and a ``LOGGER_REGISTRY`` is available to register custom - Loggers. - - -Multiple models and/or datasets -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Additionally, the tool can be configured such that a model and/or a datamodule is -specified by an import path and init arguments. For example, with a tool implemented as: - -.. code-block:: python - - cli = LightningCLI(MyModelBaseClass, MyDataModuleBaseClass, subclass_mode_model=True, subclass_mode_data=True) - -A possible config file could be as follows: - -.. code-block:: yaml - - model: - class_path: mycode.mymodels.MyModel - init_args: - decoder_layers: - - 2 - - 4 - encoder_layers: 12 - data: - class_path: mycode.mydatamodules.MyDataModule - init_args: - ... - trainer: - callbacks: - - class_path: pytorch_lightning.callbacks.EarlyStopping - init_args: - patience: 5 - ... - -Only model classes that are a subclass of :code:`MyModelBaseClass` would be allowed, and similarly only subclasses of -:code:`MyDataModuleBaseClass`. If as base classes :class:`~pytorch_lightning.core.lightning.LightningModule` and -:class:`~pytorch_lightning.core.datamodule.LightningDataModule` are given, then the tool would allow any lightning -module and data module. - -.. tip:: - - Note that with the subclass modes the :code:`--help` option does not show information for a specific subclass. To - get help for a subclass the options :code:`--model.help` and :code:`--data.help` can be used, followed by the - desired class path. Similarly :code:`--print_config` does not include the settings for a particular subclass. To - include them the class path should be given before the :code:`--print_config` option. Examples for both help and - print config are: - - .. code-block:: bash - - $ python trainer.py fit --model.help mycode.mymodels.MyModel - $ python trainer.py fit --model mycode.mymodels.MyModel --print_config - - -Models with multiple submodules -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Many use cases require to have several modules each with its own configurable options. One possible way to handle this -with LightningCLI is to implement a single module having as init parameters each of the submodules. Since the init -parameters have as type a class, then in the configuration these would be specified with :code:`class_path` and -:code:`init_args` entries. For instance a model could be implemented as: - -.. testcode:: - - class MyMainModel(LightningModule): - def __init__(self, encoder: nn.Module, decoder: nn.Module): - """Example encoder-decoder submodules model - - Args: - encoder: Instance of a module for encoding - decoder: Instance of a module for decoding - """ - super().__init__() - self.encoder = encoder - self.decoder = decoder - -If the CLI is implemented as :code:`LightningCLI(MyMainModel)` the configuration would be as follows: - -.. code-block:: yaml - - model: - encoder: - class_path: mycode.myencoders.MyEncoder - init_args: - ... - decoder: - class_path: mycode.mydecoders.MyDecoder - init_args: - ... - -It is also possible to combine :code:`subclass_mode_model=True` and submodules, thereby having two levels of -:code:`class_path`. - - -Class type defaults -^^^^^^^^^^^^^^^^^^^ - -The support for classes as type hints allows to try many possibilities with the same CLI. This is a useful feature, but -it can make it tempting to use an instance of a class as a default. For example: - -.. code-block:: - - class MyMainModel(LightningModule): - def __init__( - self, - backbone: torch.nn.Module = MyModel(encoder_layers=24), # BAD PRACTICE! - ): - super().__init__() - self.backbone = backbone - -Normally classes are mutable as it is in this case. The instance of :code:`MyModel` would be created the moment that the -module that defines :code:`MyMainModel` is first imported. This means that the default of :code:`backbone` will be -initialized before the CLI class runs :code:`seed_everything` making it non-reproducible. Furthermore, if -:code:`MyMainModel` is used more than once in the same Python process and the :code:`backbone` parameter is not -overridden, the same instance would be used in multiple places which very likely is not what the developer intended. -Having an instance as default also makes it impossible to generate the complete config file since for arbitrary classes -it is not known which arguments were used to instantiate it. - -A good solution to these problems is to not have a default or set the default to a special value (e.g. a -string) which would be checked in the init and instantiated accordingly. If a class parameter has no default and the CLI -is subclassed then a default can be set as follows: - -.. testcode:: - - default_backbone = { - "class_path": "import.path.of.MyModel", - "init_args": { - "encoder_layers": 24, - }, - } - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.set_defaults({"model.backbone": default_backbone}) - -A more compact version that avoids writing a dictionary would be: - -.. testcode:: - - from jsonargparse import lazy_instance - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.set_defaults({"model.backbone": lazy_instance(MyModel, encoder_layers=24)}) - -Optimizers -^^^^^^^^^^ - -If you will not be changing the class, you can manually add the arguments for specific optimizers and/or -learning rate schedulers by subclassing the CLI. This has the advantage of providing the proper help message for those -classes. The following code snippet shows how to implement it: - -.. testcode:: - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.add_optimizer_args(torch.optim.Adam) - parser.add_lr_scheduler_args(torch.optim.lr_scheduler.ExponentialLR) - -With this, in the config the :code:`optimizer` and :code:`lr_scheduler` groups would accept all of the options for the -given classes, in this example :code:`Adam` and :code:`ExponentialLR`. -Therefore, the config file would be structured like: - -.. code-block:: yaml - - optimizer: - lr: 0.01 - lr_scheduler: - gamma: 0.2 - model: - ... - trainer: - ... - -Where the arguments can be passed directly through command line without specifying the class. For example: - -.. code-block:: bash - - $ python trainer.py fit --optimizer.lr=0.01 --lr_scheduler.gamma=0.2 - -The automatic implementation of :code:`configure_optimizers` can be disabled by linking the configuration group. An -example can be when one wants to add support for multiple optimizers: - -.. code-block:: python - - from pytorch_lightning.utilities.cli import instantiate_class - - - class MyModel(LightningModule): - def __init__(self, optimizer1_init: dict, optimizer2_init: dict): - super().__init__() - self.optimizer1_init = optimizer1_init - self.optimizer2_init = optimizer2_init - - def configure_optimizers(self): - optimizer1 = instantiate_class(self.parameters(), self.optimizer1_init) - optimizer2 = instantiate_class(self.parameters(), self.optimizer2_init) - return [optimizer1, optimizer2] - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.add_optimizer_args( - OPTIMIZER_REGISTRY.classes, nested_key="gen_optimizer", link_to="model.optimizer1_init" - ) - parser.add_optimizer_args( - OPTIMIZER_REGISTRY.classes, nested_key="gen_discriminator", link_to="model.optimizer2_init" - ) - - - cli = MyLightningCLI(MyModel) - -The value given to :code:`optimizer*_init` will always be a dictionary including :code:`class_path` and -:code:`init_args` entries. The function :func:`~pytorch_lightning.utilities.cli.instantiate_class` -takes care of importing the class defined in :code:`class_path` and instantiating it using some positional arguments, -in this case :code:`self.parameters()`, and the :code:`init_args`. -Any number of optimizers and learning rate schedulers can be added when using :code:`link_to`. - -With shorthand notation: - -.. code-block:: bash - - $ python trainer.py fit \ - --gen_optimizer=Adam \ - --gen_optimizer.lr=0.01 \ - --gen_discriminator=AdamW \ - --gen_discriminator.lr=0.0001 - -You can also pass the class path directly, for example, if the optimizer hasn't been registered to the -``OPTIMIZER_REGISTRY``: - -.. code-block:: bash - - $ python trainer.py fit \ - --gen_optimizer.class_path=torch.optim.Adam \ - --gen_optimizer.init_args.lr=0.01 \ - --gen_discriminator.class_path=torch.optim.AdamW \ - --gen_discriminator.init_args.lr=0.0001 diff --git a/source/cli/lightning_cli_expert.rst b/source/cli/lightning_cli_expert.rst deleted file mode 100644 index dbd6061..0000000 --- a/source/cli/lightning_cli_expert.rst +++ /dev/null @@ -1,266 +0,0 @@ -:orphan: - -.. testsetup:: * - :skipif: not _JSONARGPARSE_AVAILABLE - - import torch - from unittest import mock - from typing import List - import pytorch_lightning as pl - from pytorch_lightning import LightningModule, LightningDataModule, Trainer, Callback - - - class NoFitTrainer(Trainer): - def fit(self, *_, **__): - pass - - - class LightningCLI(pl.utilities.cli.LightningCLI): - def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): - super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) - - - class MyModel(LightningModule): - def __init__( - self, - encoder_layers: int = 12, - decoder_layers: List[int] = [2, 4], - batch_size: int = 8, - ): - pass - - - class MyClassModel(LightningModule): - def __init__(self, num_classes: int): - pass - - - class MyDataModule(LightningDataModule): - def __init__(self, batch_size: int = 8): - self.num_classes = 5 - - - def send_email(address, message): - pass - - - mock_argv = mock.patch("sys.argv", ["any.py"]) - mock_argv.start() - -.. testcleanup:: * - - mock_argv.stop() - -####################################### -Eliminate config boilerplate (Advanced) -####################################### -**Audience:** Users who already understand the LightningCLI and want to customize it. - ----- - -************************** -Customize the LightningCLI -************************** - -The init parameters of the :class:`~pytorch_lightning.utilities.cli.LightningCLI` class can be used to customize some -things, namely: the description of the tool, enabling parsing of environment variables and additional arguments to -instantiate the trainer and configuration parser. - -Nevertheless the init arguments are not enough for many use cases. For this reason the class is designed so that can be -extended to customize different parts of the command line tool. The argument parser class used by -:class:`~pytorch_lightning.utilities.cli.LightningCLI` is -:class:`~pytorch_lightning.utilities.cli.LightningArgumentParser` which is an extension of python's argparse, thus -adding arguments can be done using the :func:`add_argument` method. In contrast to argparse it has additional methods to -add arguments, for example :func:`add_class_arguments` adds all arguments from the init of a class, though requiring -parameters to have type hints. For more details about this please refer to the `respective documentation -`_. - -The :class:`~pytorch_lightning.utilities.cli.LightningCLI` class has the -:meth:`~pytorch_lightning.utilities.cli.LightningCLI.add_arguments_to_parser` method which can be implemented to include -more arguments. After parsing, the configuration is stored in the :code:`config` attribute of the class instance. The -:class:`~pytorch_lightning.utilities.cli.LightningCLI` class also has two methods that can be used to run code before -and after the trainer runs: :code:`before_` and :code:`after_`. -A realistic example for these would be to send an email before and after the execution. -The code for the :code:`fit` subcommand would be something like: - -.. testcode:: - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.add_argument("--notification_email", default="will@email.com") - - def before_fit(self): - send_email(address=self.config["notification_email"], message="trainer.fit starting") - - def after_fit(self): - send_email(address=self.config["notification_email"], message="trainer.fit finished") - - - cli = MyLightningCLI(MyModel) - -Note that the config object :code:`self.config` is a dictionary whose keys are global options or groups of options. It -has the same structure as the yaml format described previously. This means for instance that the parameters used for -instantiating the trainer class can be found in :code:`self.config['fit']['trainer']`. - -.. tip:: - - Have a look at the :class:`~pytorch_lightning.utilities.cli.LightningCLI` class API reference to learn about other - methods that can be extended to customize a CLI. - ----- - -************************** -Configure forced callbacks -************************** -As explained previously, any Lightning callback can be added by passing it through command line or -including it in the config via :code:`class_path` and :code:`init_args` entries. - -However, certain callbacks MUST be coupled with a model so they are always present and configurable. -This can be implemented as follows: - -.. testcode:: - - from pytorch_lightning.callbacks import EarlyStopping - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.add_lightning_class_args(EarlyStopping, "my_early_stopping") - parser.set_defaults({"my_early_stopping.monitor": "val_loss", "my_early_stopping.patience": 5}) - - - cli = MyLightningCLI(MyModel) - -To change the configuration of the :code:`EarlyStopping` in the config it would be: - -.. code-block:: yaml - - model: - ... - trainer: - ... - my_early_stopping: - patience: 5 - -.. note:: - - The example above overrides a default in :code:`add_arguments_to_parser`. This is included to show that defaults can - be changed if needed. However, note that overriding of defaults in the source code is not intended to be used to - store the best hyperparameters for a task after experimentation. To ease reproducibility the source code should be - stable. It is better practice to store the best hyperparameters for a task in a configuration file independent from - the source code. - ----- - -******************* -Class type defaults -******************* - -The support for classes as type hints allows to try many possibilities with the same CLI. This is a useful feature, but -it can make it tempting to use an instance of a class as a default. For example: - -.. testcode:: - - class MyMainModel(LightningModule): - def __init__( - self, - backbone: torch.nn.Module = MyModel(encoder_layers=24), # BAD PRACTICE! - ): - super().__init__() - self.backbone = backbone - -Normally classes are mutable as it is in this case. The instance of :code:`MyModel` would be created the moment that the -module that defines :code:`MyMainModel` is first imported. This means that the default of :code:`backbone` will be -initialized before the CLI class runs :code:`seed_everything` making it non-reproducible. Furthermore, if -:code:`MyMainModel` is used more than once in the same Python process and the :code:`backbone` parameter is not -overridden, the same instance would be used in multiple places which very likely is not what the developer intended. -Having an instance as default also makes it impossible to generate the complete config file since for arbitrary classes -it is not known which arguments were used to instantiate it. - -A good solution to these problems is to not have a default or set the default to a special value (e.g. a -string) which would be checked in the init and instantiated accordingly. If a class parameter has no default and the CLI -is subclassed then a default can be set as follows: - -.. testcode:: - - default_backbone = { - "class_path": "import.path.of.MyModel", - "init_args": { - "encoder_layers": 24, - }, - } - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.set_defaults({"model.backbone": default_backbone}) - -A more compact version that avoids writing a dictionary would be: - -.. testcode:: - - from jsonargparse import lazy_instance - - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.set_defaults({"model.backbone": lazy_instance(MyModel, encoder_layers=24)}) - ----- - -************************ -Connect two config files -************************ -Another case in which it might be desired to extend :class:`~pytorch_lightning.utilities.cli.LightningCLI` is that the -model and data module depend on a common parameter. For example in some cases both classes require to know the -:code:`batch_size`. It is a burden and error prone giving the same value twice in a config file. To avoid this the -parser can be configured so that a value is only given once and then propagated accordingly. With a tool implemented -like shown below, the :code:`batch_size` only has to be provided in the :code:`data` section of the config. - -.. testcode:: - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.link_arguments("data.batch_size", "model.batch_size") - - - cli = MyLightningCLI(MyModel, MyDataModule) - -The linking of arguments is observed in the help of the tool, which for this example would look like: - -.. code-block:: bash - - $ python trainer.py fit --help - ... - --data.batch_size BATCH_SIZE - Number of samples in a batch (type: int, default: 8) - - Linked arguments: - model.batch_size <-- data.batch_size - Number of samples in a batch (type: int) - -Sometimes a parameter value is only available after class instantiation. An example could be that your model requires -the number of classes to instantiate its fully connected layer (for a classification task) but the value is not -available until the data module has been instantiated. The code below illustrates how to address this. - -.. testcode:: - - class MyLightningCLI(LightningCLI): - def add_arguments_to_parser(self, parser): - parser.link_arguments("data.num_classes", "model.num_classes", apply_on="instantiate") - - - cli = MyLightningCLI(MyClassModel, MyDataModule) - -Instantiation links are used to automatically determine the order of instantiation, in this case data first. - -.. tip:: - - The linking of arguments can be used for more complex cases. For example to derive a value via a function that takes - multiple settings as input. For more details have a look at the API of `link_arguments - `_. - - -The linking of arguments is intended for things that are meant to be non-configurable. This improves the CLI user -experience since it avoids the need for providing more parameters. A related concept is -variable interpolation which in contrast keeps things being configurable. diff --git a/source/cli/lightning_cli_faq.rst b/source/cli/lightning_cli_faq.rst deleted file mode 100644 index ca1be71..0000000 --- a/source/cli/lightning_cli_faq.rst +++ /dev/null @@ -1,136 +0,0 @@ -:orphan: - -.. testsetup:: * - :skipif: not _JSONARGPARSE_AVAILABLE - - import torch - from unittest import mock - from typing import List - import pytorch_lightning as pl - from pytorch_lightning import LightningModule, LightningDataModule, Trainer, Callback - - - class NoFitTrainer(Trainer): - def fit(self, *_, **__): - pass - - - class LightningCLI(pl.utilities.cli.LightningCLI): - def __init__(self, *args, trainer_class=NoFitTrainer, run=False, **kwargs): - super().__init__(*args, trainer_class=trainer_class, run=run, **kwargs) - - - class MyModel(LightningModule): - def __init__( - self, - encoder_layers: int = 12, - decoder_layers: List[int] = [2, 4], - batch_size: int = 8, - ): - pass - - - mock_argv = mock.patch("sys.argv", ["any.py"]) - mock_argv.start() - -.. testcleanup:: * - - mock_argv.stop() - -##################################### -Eliminate config boilerplate (expert) -##################################### - -*************** -Troubleshooting -*************** -The standard behavior for CLIs, when they fail, is to terminate the process with a non-zero exit code and a short message -to hint the user about the cause. This is problematic while developing the CLI since there is no information to track -down the root of the problem. A simple change in the instantiation of the ``LightningCLI`` can be used such that when -there is a failure an exception is raised and the full stack trace printed. - -.. testcode:: - - cli = LightningCLI(MyModel, parser_kwargs={"error_handler": None}) - -.. note:: - - When asking about problems and reporting issues please set the ``error_handler`` to ``None`` and include the stack - trace in your description. With this, it is more likely for people to help out identifying the cause without needing - to create a reproducible script. - ----- - -************************************* -Reproducibility with the LightningCLI -************************************* -The topic of reproducibility is complex and it is impossible to guarantee reproducibility by just providing a class that -people can use in unexpected ways. Nevertheless, the :class:`~pytorch_lightning.utilities.cli.LightningCLI` tries to -give a framework and recommendations to make reproducibility simpler. - -When an experiment is run, it is good practice to use a stable version of the source code, either being a released -package or at least a commit of some version controlled repository. For each run of a CLI the config file is -automatically saved including all settings. This is useful to figure out what was done for a particular run without -requiring to look at the source code. If by mistake the exact version of the source code is lost or some defaults -changed, having the full config means that most of the information is preserved. - -The class is targeted at implementing CLIs because running a command from a shell provides a separation with the Python -source code. Ideally the CLI would be placed in your path as part of the installation of a stable package, instead of -running from a clone of a repository that could have uncommitted local modifications. Creating installable packages that -include CLIs is out of the scope of this document. This is mentioned only as a teaser for people who would strive for -the best practices possible. - - -For every CLI implemented, users are encouraged to learn how to run it by reading the documentation printed with the -:code:`--help` option and use the :code:`--print_config` option to guide the writing of config files. A few more details -that might not be clear by only reading the help are the following. - -:class:`~pytorch_lightning.utilities.cli.LightningCLI` is based on argparse and as such follows the same arguments style -as many POSIX command line tools. Long options are prefixed with two dashes and its corresponding values should be -provided with an empty space or an equal sign, as :code:`--option value` or :code:`--option=value`. Command line options -are parsed from left to right, therefore if a setting appears multiple times the value most to the right will override -the previous ones. If a class has an init parameter that is required (i.e. no default value), it is given as -:code:`--option` which makes it explicit and more readable instead of relying on positional arguments. - ----- - -********************* -What is a subcommand? -********************* -A subcommand is what is the action the LightningCLI applies to the script: - -.. code:: bash - - python main.py [subcommand] - -See the Potential subcommands with: - -.. code:: bash - - python main.py --help - -which prints: - -.. code:: bash - - ... - - fit Runs the full optimization routine. - validate Perform one evaluation epoch over the validation set. - test Perform one evaluation epoch over the test set. - predict Run inference on your data. - tune Runs routines to tune hyperparameters before training. - -use a subcommand as follows: - -.. code:: bash - - python main.py fit - python main.py test - ----- - -**************** -What is the CLI? -**************** -CLI is short for commandline interface. Use your terminal to enter these commands. diff --git a/source/cli/lightning_cli_intermediate.rst b/source/cli/lightning_cli_intermediate.rst deleted file mode 100644 index 36c6adb..0000000 --- a/source/cli/lightning_cli_intermediate.rst +++ /dev/null @@ -1,204 +0,0 @@ -:orphan: - -########################################### -Eliminate config boilerplate (Intermediate) -########################################### -**Audience:** Users who want advanced modularity via the commandline interface (CLI). - -**Pre-reqs:** You must already understand how to use a commandline and :doc:`LightningDataModule <../data/datamodule>`. - ----- - -*************************** -What is config boilerplate? -*************************** -As Lightning projects grow in complexity it becomes desirable to enable full customizability from the commandline (CLI) so you can -change any hyperparameters without changing your code: - -.. code:: bash - - # Mix and match anything - $ python main.py --command fit --model.learning_rate 0.02 - $ python main.py --command fit --model.learning_rate 0.01 --trainer.fast_dev_run True - -This is what the Lightning CLI enables. Without the Lightning CLI, you usually end up with a TON of boilerplate that looks like this: - -.. code:: python - - from argparse import ArgumentParser - - if __name__ == "__main__": - parser = ArgumentParser() - parser.add_argument("--learning_rate_1", default=0.02) - parser.add_argument("--learning_rate_2", default=0.03) - parser.add_argument("--model", default="cnn") - parser.add_argument("--command", default="fit") - parser.add_argument("--run_fast", default=True) - ... - # add 100 more of these - ... - - args = parser.parse_args() - - if args.model == "cnn": - model = ConvNet(learning_rate=args.learning_rate_1) - elif args.model == "transformer": - model = Transformer(learning_rate=args.learning_rate_2) - trainer = Trainer(fast_dev_run=args.run_fast) - ... - - if args.command == "fit": - trainer.fit() - elif args.command == "test": - ... - -This kind of boilerplate is unsustainable as projects grow in complexity. - ----- - -************************ -Enable the Lightning CLI -************************ -To enable the Lightning CLI install the extras: - -.. code:: bash - - pip install pytorch-lightning[extra] - -if the above fails, only install jsonargparse: - -.. code:: bash - - pip install -U jsonargparse[signatures] - ----- - -************************** -Connect a model to the CLI -************************** -The simplest way to control a model with the CLI is to wrap it in the LightningCLI object: - -.. code:: python - - # main.py - - import torch - from pytorch_lightning.utilities.cli import LightningCLI - from pytorch_lightning import LightningModule, demos - - - class DemoModel(LightningModule): - def __init__(self, out_dim: int = 10, learning_rate: float = 0.02): - super().__init__() - self.l1 = torch.nn.Linear(32, out_dim) - self.learning_rate = learning_rate - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - def training_step(self, batch, batch_nb): - x = batch - x = self(x) - loss = x.sum() - return loss - - def configure_optimizers(self): - return torch.optim.Adam(self.parameters(), lr=self.learning_rate) - - - cli = LightningCLI(DemoModel, demos.BoringDataModule) - # don't call fit!! - -Now your model can be managed via the CLI. To see the available commands type: - -.. code:: bash - - $ python main.py --help - -Which prints out: - -.. code:: bash - - usage: a.py [-h] [-c CONFIG] [--print_config [={comments,skip_null,skip_default}+]] - {fit,validate,test,predict,tune} ... - - pytorch-lightning trainer command line tool - - optional arguments: - -h, --help Show this help message and exit. - -c CONFIG, --config CONFIG - Path to a configuration file in json or yaml format. - --print_config [={comments,skip_null,skip_default}+] - Print configuration and exit. - - subcommands: - For more details of each subcommand add it as argument followed by --help. - - {fit,validate,test,predict,tune} - fit Runs the full optimization routine. - validate Perform one evaluation epoch over the validation set. - test Perform one evaluation epoch over the test set. - predict Run inference on your data. - tune Runs routines to tune hyperparameters before training. - - -the message tells us that we have a few available subcommands: - -.. code:: bash - - python main.py [subcommand] - -which you can use depending on your use case: - -.. code:: bash - - $ python main.py fit - $ python main.py validate - $ python main.py test - $ python main.py predict - $ python main.py tune - ----- - -************************** -Train a model with the CLI -************************** -To run the full training routine (train, val, test), use the subcommand ``fit``: - -.. code:: bash - - python main.py fit - -View all available options with the ``--help`` command: - -.. code:: bash - - usage: main.py [options] fit [-h] [-c CONFIG] - [--seed_everything SEED_EVERYTHING] [--trainer CONFIG] - ... - [--ckpt_path CKPT_PATH] - --trainer.logger LOGGER - - optional arguments: - : - --model.out_dim OUT_DIM - (type: int, default: 10) - --model.learning_rate LEARNING_RATE - (type: float, default: 0.02) - : - --data CONFIG Path to a configuration file. - --data.data_dir DATA_DIR - (type: str, default: ./) - -With the Lightning CLI enabled, you can now change the parameters without touching your code: - -.. code:: bash - - # change the learning_rate - python main.py fit --model.out_dim 30 - - # change the out dimensions also - python main.py fit --model.out_dim 10 --model.learning_rate 0.1 - - # change trainer and data arguments too - python main.py fit --model.out_dim 2 --model.learning_rate 0.1 --data.data_dir '~/' --trainer.logger False diff --git a/source/cli/lightning_cli_intermediate_2.rst b/source/cli/lightning_cli_intermediate_2.rst deleted file mode 100644 index 493d536..0000000 --- a/source/cli/lightning_cli_intermediate_2.rst +++ /dev/null @@ -1,251 +0,0 @@ -:orphan: - -########################################### -Eliminate config boilerplate (intermediate) -########################################### -**Audience:** Users who have multiple models and datasets per project. - -**Pre-reqs:** You must have read :doc:`(Control it all from the CLI) `. - ----- - -**************************************** -Why do I want to mix models and datasets -**************************************** -Lightning projects usually begin with one model and one dataset. As the project grows in complexity and you introduce more models and more datasets, it becomes desirable -to mix any model with any dataset directly from the commandline without changing your code. - - -.. code:: bash - - # Mix and match anything - $ python main.py fit --model=GAN --data=MNIST - $ python main.py fit --model=Transformer --data=MNIST - -This is what the Lightning CLI enables. Otherwise, this kind of configuration requires a significant amount of boilerplate that often looks like this: - -.. code:: python - - # choose model - if args.model == "gan": - model = GAN(args.feat_dim) - elif args.model == "transformer": - model = Transformer(args.feat_dim) - ... - - # choose datamodule - if args.data == "MNIST": - datamodule = MNIST() - elif args.data == "imagenet": - datamodule = Imagenet() - ... - - # mix them! - trainer.fit(model, datamodule) - ----- - -************************* -Register LightningModules -************************* -Connect models across different files with the ``MODEL_REGISTRY`` to make them available from the CLI: - -.. code:: python - - # main.py - - from pytorch_lightning import demos - from pytorch_lightning.utilities import cli as pl_cli - - - @pl_cli.MODEL_REGISTRY - class Model1(demos.DemoModel): - def configure_optimizers(self): - print("⚡", "using Model1", "⚡") - return super().configure_optimizers() - - - @pl_cli.MODEL_REGISTRY - class Model2(demos.DemoModel): - def configure_optimizers(self): - print("⚡", "using Model2", "⚡") - return super().configure_optimizers() - - - cli = pl_cli.LightningCLI(datamodule_class=demos.BoringDataModule) - -Now you can choose between any model from the CLI: - -.. code:: bash - - # use Model1 - python main.py fit --model Model1 - - # use Model2 - python main.py fit --model Model2 - ----- - -******************** -Register DataModules -******************** -Connect DataModules across different files with the ``DATAMODULE_REGISTRY`` to make them available from the CLI: - -.. code:: python - - # main.py - import torch - from pytorch_lightning.utilities import cli as pl_cli - from pytorch_lightning import demos - - - @pl_cli.DATAMODULE_REGISTRY - class FakeDataset1(demos.BoringDataModule): - def train_dataloader(self): - print("⚡", "using FakeDataset1", "⚡") - return torch.utils.data.DataLoader(self.random_train) - - - @pl_cli.DATAMODULE_REGISTRY - class FakeDataset2(demos.BoringDataModule): - def train_dataloader(self): - print("⚡", "using FakeDataset2", "⚡") - return torch.utils.data.DataLoader(self.random_train) - - - cli = pl_cli.LightningCLI(demos.DemoModel) - -Now you can choose between any dataset at runtime: - -.. code:: bash - - # use Model1 - python main.py fit --data FakeDataset1 - - # use Model2 - python main.py fit --data FakeDataset2 - ----- - -******************* -Register optimizers -******************* -Connect optimizers with the ``OPTIMIZER_REGISTRY`` to make them available from the CLI: - -.. code:: python - - # main.py - import torch - from pytorch_lightning.utilities import cli as pl_cli - from pytorch_lightning import demos - - - @pl_cli.OPTIMIZER_REGISTRY - class LitAdam(torch.optim.Adam): - def step(self, closure): - print("⚡", "using LitAdam", "⚡") - super().step(closure) - - - @pl_cli.OPTIMIZER_REGISTRY - class FancyAdam(torch.optim.Adam): - def step(self, closure): - print("⚡", "using FancyAdam", "⚡") - super().step(closure) - - - cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule) - -Now you can choose between any optimizer at runtime: - -.. code:: bash - - # use LitAdam - python main.py fit --optimizer LitAdam - - # use FancyAdam - python main.py fit --optimizer FancyAdam - -Bonus: If you need only 1 optimizer, the Lightning CLI already works out of the box with any Optimizer from ``torch.optim.optim``: - -.. code:: bash - - python main.py fit --optimizer AdamW - -If the optimizer you want needs other arguments, add them via the CLI (no need to change your code)! - -.. code:: bash - - python main.py fit --optimizer SGD --optimizer.lr=0.01 - ----- - -********************** -Register LR schedulers -********************** -Connect learning rate schedulers with the ``LR_SCHEDULER_REGISTRY`` to make them available from the CLI: - -.. code:: python - - # main.py - import torch - from pytorch_lightning.utilities import cli as pl_cli - from pytorch_lightning import demos - - - @pl_cli.LR_SCHEDULER_REGISTRY - class LitLRScheduler(torch.optim.lr_scheduler.CosineAnnealingLR): - def step(self): - print("⚡", "using LitLRScheduler", "⚡") - super().step() - - - cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule) - -Now you can choose between any learning rate scheduler at runtime: - -.. code:: bash - - # LitLRScheduler - python main.py fit --lr_scheduler LitLRScheduler - - -Bonus: If you need only 1 LRScheduler, the Lightning CLI already works out of the box with any LRScheduler from ``torch.optim``: - -.. code:: bash - - python main.py fit --lr_scheduler CosineAnnealingLR - python main.py fit --lr_scheduler LinearLR - ... - -If the scheduler you want needs other arguments, add them via the CLI (no need to change your code)! - -.. code:: bash - - python main.py fit --lr_scheduler=ReduceLROnPlateau --lr_scheduler.monitor=epoch - ----- - -************************* -Register from any package -************************* -A shortcut to register many classes from a package is to use the ``register_classes`` method. Here we register all optimizers from the ``torch.optim`` library: - -.. code:: python - - import torch - from pytorch_lightning.utilities import cli as pl_cli - from pytorch_lightning import demos - - # add all PyTorch optimizers! - pl_cli.OPTIMIZER_REGISTRY.register_classes(module=torch.optim, base_cls=torch.optim.Optimizer) - - cli = pl_cli.LightningCLI(demos.DemoModel, demos.BoringDataModule) - -Now use any of the optimizers in the ``torch.optim`` library: - -.. code:: bash - - python main.py fit --optimizer AdamW - -This method is supported by all the registry classes. diff --git a/source/clouds/cloud_training.rst b/source/clouds/cloud_training.rst deleted file mode 100644 index 1bd57b1..0000000 --- a/source/clouds/cloud_training.rst +++ /dev/null @@ -1,86 +0,0 @@ -.. _grid: - -################## -Train on the cloud -################## -**Audience:** Users who want to develop and train models on the cloud (public cloud, private cloud or onprem clusters). - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Access a GPU machine on the cloud - :description: Learn to train models using an interactive cloud machine. - :col_css: col-md-4 - :button_link: session_basic.html - :height: 200 - :tag: basic - -.. displayitem:: - :header: 2: Run a model in the background on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-4 - :button_link: run_basic.html - :height: 200 - :tag: intermediate - -.. displayitem:: - :header: 3: Save up to 80% on cloud costs with fault-tolerant training - :description: Run on the cloud for 1/10th the price with fault-tolerant training. - :col_css: col-md-4 - :button_link: fault_tolerant_training_basic.html - :height: 200 - :tag: intermediate - -.. displayitem:: - :header: 4: Run many models at once - :description: Run many models at once (sweep) to find the best performing model. - :col_css: col-md-4 - :button_link: run_intermediate.html - :height: 200 - :tag: intermediate - -.. displayitem:: - :header: 5: Run on your own cloud - :description: Learn how to run on your Company or University private clouds. - :col_css: col-md-4 - :button_link: run_expert.html - :height: 200 - :tag: expert - -.. raw:: html - -
-
- ----- - -.. raw:: html - -
-
- -.. raw:: html - - - - -.. raw:: html - -
-
- -`Grid.ai `_ is the official cloud training solution for PyTorch Lightning. Grid is designed to support researcher workloads at both academic labs and major companies. - -.. raw:: html - -
-
diff --git a/source/clouds/cloud_training_intermediate.rst b/source/clouds/cloud_training_intermediate.rst deleted file mode 100644 index c5a65d7..0000000 --- a/source/clouds/cloud_training_intermediate.rst +++ /dev/null @@ -1,7 +0,0 @@ -:orphan: - -.. _grid_cloud_intermediate: - -################################# -Train on the cloud (intermediate) -################################# diff --git a/source/clouds/cluster_advanced.rst b/source/clouds/cluster_advanced.rst deleted file mode 100644 index 918bf06..0000000 --- a/source/clouds/cluster_advanced.rst +++ /dev/null @@ -1,213 +0,0 @@ -#################################### -Run on an on-prem cluster (advanced) -#################################### - -.. _slurm: - ----- - -****************************** -Run on a SLRUM managed cluster -****************************** -Lightning automates the details behind training on a SLURM-powered cluster. In contrast to the general purpose -cluster above, the user does not start the jobs manually on each node and instead submits it to SLURM which -schedules the resources and time for which the job is allowed to run. - ----- - -*************************** -Design your training script -*************************** - -To train a model using multiple nodes, do the following: - -1. Design your :ref:`lightning_module` (no need to add anything specific here). - -2. Enable DDP in the trainer - - .. code-block:: python - - # train on 32 GPUs across 4 nodes - trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp") - -3. It's a good idea to structure your training script like this: - - .. testcode:: - - # train.py - def main(hparams): - model = LightningTemplateModel(hparams) - - trainer = Trainer(accelerator="gpu", devices=8, num_nodes=4, strategy="ddp") - - trainer.fit(model) - - - if __name__ == "__main__": - root_dir = os.path.dirname(os.path.realpath(__file__)) - parent_parser = ArgumentParser(add_help=False) - hyperparams = parser.parse_args() - - # TRAIN - main(hyperparams) - -4. Create the appropriate SLURM job: - - .. code-block:: bash - - # (submit.sh) - #!/bin/bash -l - - # SLURM SUBMIT SCRIPT - #SBATCH --nodes=4 - #SBATCH --gres=gpu:8 - #SBATCH --ntasks-per-node=8 - #SBATCH --mem=0 - #SBATCH --time=0-02:00:00 - - # activate conda env - source activate $1 - - # debugging flags (optional) - export NCCL_DEBUG=INFO - export PYTHONFAULTHANDLER=1 - - # on your cluster you might need these: - # set the network interface - # export NCCL_SOCKET_IFNAME=^docker0,lo - - # might need the latest CUDA - # module load NCCL/2.4.7-1-cuda.10.0 - - # run script from above - srun python3 train.py - -5. If you want auto-resubmit (read below), add this line to the submit.sh script - - .. code-block:: bash - - #SBATCH --signal=SIGUSR1@90 - -6. Submit the SLURM job - - .. code-block:: bash - - sbatch submit.sh - ----- - -********************************** -Enable auto wall-time resubmitions -********************************** -When you use Lightning in a SLURM cluster, it automatically detects when it is about -to run into the wall time and does the following: - -1. Saves a temporary checkpoint. -2. Requeues the job. -3. When the job starts, it loads the temporary checkpoint. - -To get this behavior make sure to add the correct signal to your SLURM script - -.. code-block:: bash - - # 90 seconds before training ends - SBATCH --signal=SIGUSR1@90 - -If auto-resubmit is not desired, it can be turned off in the :class:`~pytorch_lightning.plugins.environments.slurm_environment.SLURMEnvironment` plugin: - -.. code-block:: python - - from pytorch_lightning.plugins.environments import SLURMEnvironment - - trainer = Trainer(plugins=[SLURMEnvironment(auto_requeue=False)]) - ----- - -*********************** -Build your SLURM script -*********************** -Instead of manually building SLURM scripts, you can use the -`SlurmCluster object `_ -to do this for you. The SlurmCluster can also run a grid search if you pass -in a `HyperOptArgumentParser -`_. - -Here is an example where you run a grid search of 9 combinations of hyperparameters. -See also the multi-node examples -`here `__. - -.. code-block:: python - - # grid search 3 values of learning rate and 3 values of number of layers for your net - # this generates 9 experiments (lr=1e-3, layers=16), (lr=1e-3, layers=32), - # (lr=1e-3, layers=64), ... (lr=1e-1, layers=64) - parser = HyperOptArgumentParser(strategy="grid_search", add_help=False) - parser.opt_list("--learning_rate", default=0.001, type=float, options=[1e-3, 1e-2, 1e-1], tunable=True) - parser.opt_list("--layers", default=1, type=float, options=[16, 32, 64], tunable=True) - hyperparams = parser.parse_args() - - # Slurm cluster submits 9 jobs, each with a set of hyperparams - cluster = SlurmCluster( - hyperparam_optimizer=hyperparams, - log_path="/some/path/to/save", - ) - - # OPTIONAL FLAGS WHICH MAY BE CLUSTER DEPENDENT - # which interface your nodes use for communication - cluster.add_command("export NCCL_SOCKET_IFNAME=^docker0,lo") - - # see the output of the NCCL connection process - # NCCL is how the nodes talk to each other - cluster.add_command("export NCCL_DEBUG=INFO") - - # setting a main port here is a good idea. - cluster.add_command("export MASTER_PORT=%r" % PORT) - - # ************** DON'T FORGET THIS *************** - # MUST load the latest NCCL version - cluster.load_modules(["NCCL/2.4.7-1-cuda.10.0"]) - - # configure cluster - cluster.per_experiment_nb_nodes = 12 - cluster.per_experiment_nb_gpus = 8 - - cluster.add_slurm_cmd(cmd="ntasks-per-node", value=8, comment="1 task per gpu") - - # submit a script with 9 combinations of hyper params - # (lr=1e-3, layers=16), (lr=1e-3, layers=32), (lr=1e-3, layers=64), ... (lr=1e-1, layers=64) - cluster.optimize_parallel_cluster_gpu( - main, nb_trials=9, job_name="name_for_squeue" # how many permutations of the grid search to run - ) - - -The other option is that you generate scripts on your own via a bash command or use our -:doc:`native solution <../clouds/cloud_training>`. - ----- - -******** -Get help -******** -Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI. - -Try it out for free today: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: cloud_training.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
bool: - """Return True if the cluster is managed (you don't launch processes yourself)""" - return True - - def world_size(self) -> int: - return int(os.environ["WORLD_SIZE"]) - - def global_rank(self) -> int: - return int(os.environ["RANK"]) - - def local_rank(self) -> int: - return int(os.environ["LOCAL_RANK"]) - - def node_rank(self) -> int: - return int(os.environ["NODE_RANK"]) - - def main_address(self) -> str: - return os.environ["MASTER_ADDRESS"] - - def main_port(self) -> int: - return int(os.environ["MASTER_PORT"]) - - - trainer = Trainer(plugins=[MyClusterEnvironment()]) - ----- - -******** -Get help -******** -Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI. - -Try it out for free today: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: cloud_training.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
`__ provides helper functions to setup distributed environment variables from the `PyTorch distributed communication package `__ that need to be defined on each node. - -Once the script is setup like described in :ref:` Training Script Setup`, you can run the below command across your nodes to start multi-node training. - -Like a custom cluster, you have to ensure that there is network connectivity between the nodes with firewall rules that allow traffic flow on a specified *MASTER_PORT*. - -Finally, you'll need to decide which node you'd like to be the main node (*MASTER_ADDR*), and the ranks of each node (*NODE_RANK*). - -For example: - -* *MASTER_ADDR* 10.10.10.16 -* *MASTER_PORT* 29500 -* *NODE_RANK* 0 for the first node, 1 for the second node - -Run the below command with the appropriate variables set on each node. - -.. code-block:: bash - - python -m torch.distributed.run - --nnodes=2 # number of nodes you'd like to run with - --master_addr - --master_port - --node_rank - train.py (--arg1 ... train script args...) - -.. note:: - - ``torch.distributed.run`` assumes that you'd like to spawn a process per GPU if GPU devices are found on the node. This can be adjusted with ``-nproc_per_node``. - ----- - -******** -Get help -******** -Setting up a cluster for distributed training is not trivial. Lightning offers lightning-grid which allows you to configure a cluster easily and run experiments via the CLI and web UI. - -Try it out for free today: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Train models on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: cloud_training.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
`. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Save money with fault-tolerant training on the cloud - :col_css: col-md-4 - :button_link: fault_tolerant_training_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Expert - :description: Learn how to enable fault tolerance on any cloud or cluster environment - :col_css: col-md-4 - :button_link: fault_tolerant_training_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: FAQ - :description: Frequently asked questions about fault-tolerant training. - :col_css: col-md-4 - :button_link: fault_tolerant_training_faq.html - :height: 150 - -.. raw:: html - -
-
diff --git a/source/clouds/fault_tolerant_training_basic.rst b/source/clouds/fault_tolerant_training_basic.rst deleted file mode 100644 index d5af9ed..0000000 --- a/source/clouds/fault_tolerant_training_basic.rst +++ /dev/null @@ -1,43 +0,0 @@ -:orphan: - -############################### -Fault-tolerant Training (basic) -############################### -**Audience:** User who want to run on the cloud or a cluster environment. - -**Pre-requisites**: Users must have first read :doc:`Run on the cloud (basic) ` - ----- - -******************************** -What is fault-tolerant training? -******************************** -When developing models on the cloud or cluster environments, you may be forced to restart from scratch in the event of a software or hardware failure (ie: a *fault*). Lightning models can run fault-proof. - -With Fault Tolerant Training, when ``Trainer.fit()`` fails in the middle of an epoch during training or validation, -Lightning will restart exactly where it failed, and everything will be restored (down to the batch it was on even if the dataset was shuffled). - -.. warning:: Fault-tolerant Training is currently an experimental feature within Lightning. - ----- - -*************************************************** -Use fault-tolerance to save money on cloud training -*************************************************** -Cloud providers offer pre-emptible machines which can be priced as low as 1/10th the cost but can be shut-down automatically at any time. -Because fault-tolerant training can automatically recover from an interruption, you can train models for many weeks/months at a time for the pre-emptible prices. - -To easily run on the cloud with fault-tolerance with lightning-grid, use the following arguments: - -.. code-block:: bash - - grid run --use_spot --auto_resume lightning_script.py - -The ``--use_spot`` argument enables cheap preemptible pricing (but the machines that can be interrupted). -If the machine is interrupted, the ``--auto_resume`` argument automatically restarts the machine. - -As long as you are running a script that runs a lightning model, the model will restore itself and handle all the details of fault tolerance. - ----- - -.. include:: grid_costs.rst diff --git a/source/clouds/fault_tolerant_training_expert.rst b/source/clouds/fault_tolerant_training_expert.rst deleted file mode 100644 index f0051f7..0000000 --- a/source/clouds/fault_tolerant_training_expert.rst +++ /dev/null @@ -1,34 +0,0 @@ -:orphan: - -################################ -Fault-tolerant Training (expert) -################################ -**Audience**: Experts looking to enable and handle their own fault-tolerance. - -**Pre-requisites**: Users must have first read :doc:`Fault-tolrance Training (basic) ` - ----- - -*************************************** -Enable fault-tolerant behavior anywhere -*************************************** -To enable fault tolerance on your own cloud or cluster environment enable the *PL_FAULT_TOLERANT_TRAINING* environment variable: - -.. code-block:: bash - - PL_FAULT_TOLERANT_TRAINING=1 python script.py - -Although Lighting will now be fault-tolerant, you'll have to handle all the nuances of making sure the models are automatically restarted. - -.. note:: This complexity is already handled for you if you use **lightning-grid**. - ----- - -************************************************** -Enable fault-tolerant behavior on your own cluster -************************************************** -The simplest way to enable fault-tolerant behavior is to enable lightning-grid to work on your on-prem cluster or cloud environment which will handle all the nuances of fault-tolerant training at scale. - -Email us to connect with your own cloud account: - -``_ diff --git a/source/clouds/fault_tolerant_training_faq.rst b/source/clouds/fault_tolerant_training_faq.rst deleted file mode 100644 index 4f2bdf4..0000000 --- a/source/clouds/fault_tolerant_training_faq.rst +++ /dev/null @@ -1,144 +0,0 @@ -:orphan: - -############################# -Fault-tolerant Training (FAQ) -############################# - -******************************* -How do I use iterable datasets? -******************************* -To support fault-tolerance, you will need to use and expose a sampler within your dataset. - -For example, the following implementation for an iterable dataset sub-classing :class:`~torch.utils.data.IterableDataset` won't be supported. - -.. code-block:: python - - from torch.utils.data import IterableDataset, DataLoader - - - # does not support fault tolerance training! - class RandomIterableDataset(IterableDataset): - def __init__(self, size: int, count: int): - self.count = count - self.size = size - - def __iter__(self): - for _ in range(self.count): - yield torch.randn(self.size) - - -There are two primary reasons why Lightning can't support the previous implementation. - -* Lightning cannot infer what you are iterating over, making it difficult to restart training. Lightning Fault Tolerant Training requires a :class:`~torch.utils.data.distributed.Sampler` to be used to encapsulate the fetching logic, requiring both the sampler and an iterator to be made available as attributes within the dataset, so Lightning can access them to track progress. -* Implementing the `__next__` method is required as it separates iterator creation from its consumption, which is essential for Lightning to wrap the iterator before their consumption. - -If your iterable dataset are implemented in the following way, everything should works as expected. - -.. code-block:: python - - import torch - from torch.utils.data import IterableDataset, DataLoader - - - class RandomIterableDataset(IterableDataset): - def __init__(self, size: int, length: int): - self.data = torch.randn(length, size) - - # expose the sampler as an attribute - self.sampler = RandomSampler(range(length)) - - def __iter__(self) -> "RandomIterableDataset": - # expose the generator from the sampler as an attribute - # the ``sampler_iter`` will be wrapped by Lightning to ensure - # we can capture random seeds and iteration count for fast-forward samplers - # while restarting. - self.sampler_iter = iter(self.sampler) - return self - - def __next__(self) -> torch.Tensor: - # call next on the iterator and get the associated data. - # the logic here can become more complex but the sampler - # should be the central piece for fetching the next sample - index = next(self.sampler_iter) - return self.data[index] - ----- - -********************************** -How do I use multiple dataloaders? -********************************** -If you are using multiple training dataloaders, Lightning won't be able to restore the random state properly. - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - loader_a = torch.utils.data.DataLoader(range(8), batch_size=4) - loader_b = torch.utils.data.DataLoader(range(16), batch_size=4) - return {"loader_a": loader_a, "loader_b": loader_b} - - def training_step(self, batch, batch_idx): - # access the data in the same format as the collection of dataloaders. - # dict, list are supported. - loader_a = batch["loader_a"] - loader_b = batch["loader_b"] - - -If you believe this to be useful, please open a `feature request `_. - - ----- - -********************************* -What are the performance impacts? -********************************* -Fault-tolerant Training was tested on common and worst-case scenarios in order to measure the impact of the internal state tracking on the total training time. -On tiny models like the `BoringModel and RandomDataset `_ -which has virtually no data loading and processing overhead, we noticed up to 50% longer training time with fault tolerance enabled. -In this worst-case scenario, fault-tolerant adds an overhead that is noticeable in comparison to the compute time for dataloading itself. -However, for more realistic training workloads where data loading and preprocessing is more expensive, the constant overhead that fault tolerance adds becomes less noticeable or not noticeable at all. -For example, when training with ResNet50 on CIFAR 10 we have observed a 0.5% to 1% increase in training time depending on ``batch size`` or ``number of workers``. - -More detailed benchmarks will be shared in the future. - -.. note:: - - The extra time is coming from several parts: - - - Capturing the iteration count + random states for each sample within each DataLoader workers and pass it through the data_queue - - Extra logic to handle / store the dataloader's states from each batch. - ----- - -************************************ -What happens to my shuffled dataset? -************************************ -If you are using a single map-based dataset by sub-classing :class:`~torch.utils.data.Dataset`, everything should work as expected. - -.. code-block:: python - - from torch.utils.data import Dataset, DataLoader - - - class RandomDataset(Dataset): - def __init__(self, size: int, length: int): - self.len = length - self.data = torch.randn(length, size) - - def __getitem__(self, index): - return self.data[index] - - def __len__(self): - return self.len - ----- - -****************************** -What parts are fault-tolerant? -****************************** -Lightning keeps track of the following state updates during training: - -* Samplers indices and random states across multiple processes and workers: This enables restoring random transforms and batch fetching to the exact state as it was right before the failure. -* Optimizers, learning rate schedulers, callbacks, etc.. -* Loop progression -* Logging internal states such that metric reductions on epoch end are not getting affected by the failure and model selection can continue as expected. diff --git a/source/clouds/grid_costs.rst b/source/clouds/grid_costs.rst deleted file mode 100644 index 04b1864..0000000 --- a/source/clouds/grid_costs.rst +++ /dev/null @@ -1,6 +0,0 @@ -**** -Cost -**** -Lightning (via `lightning-grid `_) provides access to cloud machines to the community for free. However, you must buy credits on `lightning-grid `_ which are used to pay the cloud providers on your behalf. - -If you want to run on your own AWS account and pay the cloud provider directly, please contact our onprem team: ``_ diff --git a/source/clouds/run_advanced.rst b/source/clouds/run_advanced.rst deleted file mode 100644 index 3418dee..0000000 --- a/source/clouds/run_advanced.rst +++ /dev/null @@ -1,130 +0,0 @@ -:orphan: - -.. _grid_cloud_advanced: - -############################# -Train on the cloud (advanced) -############################# -**Audience**: Anyone looking to train a model on the cloud in the background - ----- - -**************************** -What is background training? -**************************** -Background training lets you train models in the background without you needing to interact with the machine. As the model trains you can monitor its progress via Tensorboard or an experiment manager of your choice. - ----- - -************************* -0: Install lightning-grid -************************* -First Navigate to https://platform.grid.ai to create a free account. - -Next, install lightning-grid and login - -.. code:: bash - - pip install lightning-grid - grid login - ----- - -******************* -1: Create a dataset -******************* -Create a datastore which optimizes your datasets for training at scale on the cloud. - -First, let's download a dummy dataset we created. - -.. code:: bash - - # download - curl https://pl-flash-data.s3.amazonaws.com/cifar5.zip -o cifar5.zip - - # unzip - unzip cifar5.zip - -Now create the datastore - -.. code:: bash - - grid datastore create cifar5/ --name cifar5 - -Now your dataset is ready to be used for training on the cloud! - -.. note:: In some *research* workflows, your model script ALSO downloads the dataset. If the dataset is only a few GBs this is fine. Otherwise we recommend you create a Datastore. - ----- - -************************** -2: Choose the model to run -************************** -You can run any python script in the background. For this example, we'll use a simple classifier: - -Clone the code to your machine: - -.. code bash - - git clone https://github.com/williamFalcon/cifar5-simple.git - - -.. note:: Code repositories can be as complicated as needed. This is just a simple demo. - ----- - -******************* -3: Run on the cloud -******************* -To run this model on the cloud, use the **grid run** command which has two parts: - -.. code:: bash - - grid run [run args] file.py [file args] - -To attach the datastore **cifar5** to the **cifar5.py** file use the following command: - -.. code:: bash - - # command | the datastore to use | the model | argument to the model - grid run --datastore_name cifar5 cifar5.py.py --data_dir /datastores/cifar5 - ----- - -********************* -4: Monitor and manage -********************* -Now that your model is running in the background you can monitor and manage it `here `_. - -You can also monitor its progress on the commandline: - -.. code:: bash - - grid status - ----- - -********** -Next Steps -********** -Here are the recommended next steps depending on your workflow. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run many models at once - :description: Learn how to run many models at once using sweeps. - :col_css: col-md-12 - :button_link: session_intermediate.html - :height: 150 - :tag: basic - -.. raw:: html - -
-
`_. - -You can also monitor its progress on the commandline: - -.. code:: bash - - grid status - ----- - -.. include:: grid_costs.rst - ----- - -********** -Next Steps -********** -Here are the recommended next steps depending on your workflow. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run many models at once - :description: Learn how to find the best performaning model by running multiple models at once using a sweep. - :col_css: col-md-4 - :button_link: run_intermediate.html - :height: 150 - :tag: basic - -.. raw:: html - -
-
` for more information. - ----- - -*********************************** -Run on your own cloud (hassle free) -*********************************** -Cluster training can get complicated once you start doing multi-node training, fault-tolerant training or sweeps. -If you'd prefer to not deal with any of the hassles of running on your own cloud environments, lightning-grid enables University and Enterprise customers to run on the cloud with their own credentials or even onprem. - -These are some of the benefits of running via lightning-grid: - -- create datasets optimized for scale -- fully configurable on-prem deployment -- SOC-2 compliance (in-progress) (ETA Q3 2022) -- micro cost optimizations everywhere (which add up) -- built-in fault tolerance -- enabled collaboration for teams and enterprises - -Contact our sales support engineering team so we can help you set up Grid with your own cloud credentials. - -Email us to connect with your own cloud account: - -``_. diff --git a/source/clouds/run_intermediate.rst b/source/clouds/run_intermediate.rst deleted file mode 100644 index dad2edf..0000000 --- a/source/clouds/run_intermediate.rst +++ /dev/null @@ -1,229 +0,0 @@ -:orphan: - -.. _grid_cloud_run_intermediate: - -################################# -Train on the cloud (intermediate) -################################# -**Audience**: User looking to run many models at once - ----- - -**************** -What is a sweep? -**************** -A sweep is the term giving to running the same model multiple times with different hyperparameters to find the one that performs the best (according to your definition of performance). - -Let's say I have a python script that trains a Lighting model to classify images. We run this file like so: - -.. code:: bash - - grid run file.py --batch_size 8 - -with such a model, I would be interested in knowing how it performs with different batch size. In this case, I'm going to train many versions of this model. - -.. code:: bash - - # run 4 models in parallel - grid run file.py --batch_size 8 - grid run file.py --batch_size 16 - grid run file.py --batch_size 32 - grid run file.py --batch_size 64 - -Now I can see how my model performs according to the layers and based on time and cost I can pick my "best" model: - -.. list-table:: Training speed vs cost - :widths: 10 40 15 15 - :header-rows: 1 - - * - Batch size - - classification accuracy (%) - - training time - - cost - * - 8 - - 0.80 - - 5 minutes - - $0.15 - * - 16 - - 0.85 - - 10 minutes - - $0.30 - * - 32 - - 0.90 - - 30 minutes - - $0.50 - * - 64 - - 0.95 - - 60 minutes - - $1.01 - ----- - -************* -Start a Sweep -************* -First, recall that in the `previous tutorial `_ we ran a single model using this command: - -.. code:: bash - - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 - -Now we're going to run that same model 4 different times each with a different number of layers: - -.. code:: bash - - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 8 - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 16 - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 32 - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size 64 - -Grid has a special syntax based on python that gives you shortcuts for sweeps. The shortcut for the above commands is: - -.. code:: bash - - grid run --datastore_name cifar5 cifar5.py --data_dir /datastores/cifar5 --batch_size "[8, 16, 32, 64]" - ----- - -**************** -Syntax Shortcuts -**************** - -List -==== - -.. code:: bash - - grid run file.py --batch_size "[8, 16, 32, 64]" - -equivalent to: - -.. code:: bash - - grid run file.py --batch_size 8 - grid run file.py --batch_size 16 - grid run file.py --batch_size 32 - grid run file.py --batch_size 64 - ----- - -Range -===== - -.. code:: bash - - grid run file.py --batch_size "range(1, 10, 2)" - -equivalent to: - -.. code:: bash - - grid run main.py --batch_size 1 - grid run main.py --batch_size 3 - grid run main.py --batch_size 5 - grid run main.py --batch_size 7 - grid run main.py --batch_size 9 - ---- - -String list -=========== - -.. code:: bash - - grid run file.py --model_backbone "['resnet18' 'transformer', 'resnet50']" - -equivalent to: - -.. code:: bash - - grid run file.py --model_backbone 'resnet18' - grid run file.py --model_backbone 'transformer' - grid run file.py --model_backbone 'resnet50' - ----- - -Sampling -======== - -.. code:: bash - - grid run file.py --learning_rate "uniform(1e-5, 1e-1, 3)" - -equivalent to: - -.. code:: bash - - grid run file.py --learning_rate 0.03977392 - grid run file.py --learning_rate 0.04835479 - grid run file.py --learning_rate 0.05200016 - ----- - -**************** -Sweep strategies -**************** -Models often have dozens of hyperparameters. We usually don't run all combinations because it would be too prohibitive. Grid supports two strategies: - ----- - -Grid search -=========== -Grid search is a common approach that tries all combinations of hyperparamaters. Grid will automatically compute combinations when it detects special syntax: - -.. code:: bash - - grid run file.py --batch_size "[1, 2]" --layers "[3, 5]" - -is equivalent to: - -.. code:: bash - - grid run file.py --batch_size 1 --layers 3 - grid run file.py --batch_size 2 --layers 3 - grid run file.py --batch_size 1 --layers 5 - grid run file.py --batch_size 2 --layers 5 - ----- - -Random search -============= -With random search, we choose only a subset of hyperparamaters. The larger the number of trials (*num_trials*) the more probable we'll find a great performing model without needing to try all possible combinations. - -.. code:: bash - - grid run --strategy random_search --num_trials 2 file.py --batch_size "[1, 2]" --layers "[3, 5]" - -the above command generates the 4 combinations and runs only 2 at random - -.. code:: bash - - grid run file.py --batch_size 2 --layers 3 - grid run file.py --batch_size 1 --layers 5 - ----- - -********** -Next Steps -********** -Here are the recommended next steps depending on your workflow. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run with your own cloud credentials - :description: Learn how to use Grid products with your Company or University cloud account. - :col_css: col-md-4 - :button_link: run_expert.html - :height: 180 - :tag: expert - -.. raw:: html - -
-
`_ to create a free account, then start a new Grid Session. - -A Grid Session is an interactive machine with 1-16 GPUs per machine. - -.. image:: https://docs.grid.ai/assets/images/new-session-3c58be3fd64ffabcdeb7b52516e0782e.gif - :alt: Start a Grid Session in a few seconds - ----- - -************************* -Open the Jupyter Notebook -************************* -Once the Session starts, open a Jupyter notebook. - -.. raw:: html - - - ----- - -************************ -Clone and run your model -************************ -On the Jupyter page you can use a Notebook, or to clone your code and run via the CLI. - -.. raw:: html - - - ----- - -.. include:: grid_costs.rst - ----- - -********** -Next Steps -********** -Here are the recommended next steps depending on your workflow. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Run a model in the background - :description: Learn to run a model in the background - :col_css: col-md-6 - :button_link: run_basic.html - :height: 180 - :tag: basic - -.. displayitem:: - :header: Run with your own cloud credentials - :description: Learn how to use Grid products on your Company or University private cloud account. - :col_css: col-md-6 - :button_link: run_expert.html - :height: 180 - :tag: expert - -.. raw:: html - -
-
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Basic - :description: Learn to save and load checkpoints - :col_css: col-md-3 - :button_link: checkpointing_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: Intermediate - :description: Customize checkpointing behavior - :col_css: col-md-3 - :button_link: checkpointing_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Advanced - :description: Enable cloud-based checkpointing and composable checkpoints. - :col_css: col-md-3 - :button_link: checkpointing_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Expert - :description: Customize checkpointing for custom distributed strategies and accelerators. - :col_css: col-md-3 - :button_link: checkpointing_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
- ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: ModelCheckpoint API - :description: Dig into the ModelCheckpoint API - :col_css: col-md-4 - :button_link: ../api/pytorch_lightning.callbacks.ModelCheckpoint.html - :height: 150 - -.. raw:: html - -
-
diff --git a/source/common/checkpointing_expert.rst b/source/common/checkpointing_expert.rst deleted file mode 100644 index c1859d6..0000000 --- a/source/common/checkpointing_expert.rst +++ /dev/null @@ -1,89 +0,0 @@ -:orphan: - -.. _checkpointing_expert: - -###################### -Checkpointing (expert) -###################### - -TODO: I don't understand this... - -*********************** -Customize Checkpointing -*********************** - -.. warning:: - - The Checkpoint IO API is experimental and subject to change. - - -Lightning supports modifying the checkpointing save/load functionality through the ``CheckpointIO``. This encapsulates the save/load logic -that is managed by the ``Strategy``. ``CheckpointIO`` is different from :meth:`~pytorch_lightning.core.hooks.CheckpointHooks.on_save_checkpoint` -and :meth:`~pytorch_lightning.core.hooks.CheckpointHooks.on_load_checkpoint` methods as it determines how the checkpoint is saved/loaded to storage rather than -what's saved in the checkpoint. - - -****************************** -Built-in Checkpoint IO Plugins -****************************** - -.. list-table:: Built-in Checkpoint IO Plugins - :widths: 25 75 - :header-rows: 1 - - * - Plugin - - Description - * - :class:`~pytorch_lightning.plugins.io.TorchCheckpointIO` - - CheckpointIO that utilizes :func:`torch.save` and :func:`torch.load` to save and load checkpoints - respectively, common for most use cases. - * - :class:`~pytorch_lightning.plugins.io.XLACheckpointIO` - - CheckpointIO that utilizes :func:`xm.save` to save checkpoints for TPU training strategies. - - -*************************** -Custom Checkpoint IO Plugin -*************************** - -``CheckpointIO`` can be extended to include your custom save/load functionality to and from a path. The ``CheckpointIO`` object can be passed to either a ``Trainer`` directly or a ``Strategy`` as shown below: - -.. code-block:: python - - from pytorch_lightning import Trainer - from pytorch_lightning.callbacks import ModelCheckpoint - from pytorch_lightning.plugins import CheckpointIO - from pytorch_lightning.strategies import SingleDeviceStrategy - - - class CustomCheckpointIO(CheckpointIO): - def save_checkpoint(self, checkpoint, path, storage_options=None): - ... - - def load_checkpoint(self, path, storage_options=None): - ... - - def remove_checkpoint(self, path): - ... - - - custom_checkpoint_io = CustomCheckpointIO() - - # Either pass into the Trainer object - model = MyModel() - trainer = Trainer( - plugins=[custom_checkpoint_io], - callbacks=ModelCheckpoint(save_last=True), - ) - trainer.fit(model) - - # or pass into Strategy - model = MyModel() - device = torch.device("cpu") - trainer = Trainer( - strategy=SingleDeviceStrategy(device, checkpoint_io=custom_checkpoint_io), - callbacks=ModelCheckpoint(save_last=True), - ) - trainer.fit(model) - -.. note:: - - Some ``TrainingTypePlugins`` like ``DeepSpeedStrategy`` do not support custom ``CheckpointIO`` as checkpointing logic is not modifiable. diff --git a/source/common/early_stopping.rst b/source/common/early_stopping.rst deleted file mode 100644 index 593106f..0000000 --- a/source/common/early_stopping.rst +++ /dev/null @@ -1,99 +0,0 @@ -.. testsetup:: * - - from pytorch_lightning.callbacks.early_stopping import EarlyStopping - -.. _early_stopping: - - -############## -Early Stopping -############## - -.. raw:: html - - - - -*********************** -Stopping an Epoch Early -*********************** - -You can stop and skip the rest of the current epoch early by overriding :meth:`~pytorch_lightning.core.hooks.ModelHooks.on_train_batch_start` to return ``-1`` when some condition is met. - -If you do this repeatedly, for every epoch you had originally requested, then this will stop your entire training. - - -********************** -EarlyStopping Callback -********************** - -The :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback can be used to monitor a metric and stop the training when no improvement is observed. - -To enable it: - -- Import :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback. -- Log the metric you want to monitor using :meth:`~pytorch_lightning.core.lightning.LightningModule.log` method. -- Init the callback, and set ``monitor`` to the logged metric of your choice. -- Set the ``mode`` based on the metric needs to be monitored. -- Pass the :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback to the :class:`~pytorch_lightning.trainer.trainer.Trainer` callbacks flag. - -.. code-block:: python - - from pytorch_lightning.callbacks.early_stopping import EarlyStopping - - - class LitModel(LightningModule): - def validation_step(self, batch, batch_idx): - loss = ... - self.log("val_loss", loss) - - - model = LitModel() - trainer = Trainer(callbacks=[EarlyStopping(monitor="val_loss", mode="min")]) - trainer.fit(model) - -You can customize the callbacks behaviour by changing its parameters. - -.. testcode:: - - early_stop_callback = EarlyStopping(monitor="val_accuracy", min_delta=0.00, patience=3, verbose=False, mode="max") - trainer = Trainer(callbacks=[early_stop_callback]) - - -Additional parameters that stop training at extreme points: - -- ``stopping_threshold``: Stops training immediately once the monitored quantity reaches this threshold. - It is useful when we know that going beyond a certain optimal value does not further benefit us. -- ``divergence_threshold``: Stops training as soon as the monitored quantity becomes worse than this threshold. - When reaching a value this bad, we believes the model cannot recover anymore and it is better to stop early and run with different initial conditions. -- ``check_finite``: When turned on, it stops training if the monitored metric becomes NaN or infinite. -- ``check_on_train_epoch_end``: When turned on, it checks the metric at the end of a training epoch. Use this only when you are monitoring any metric logged within - training-specific hooks on epoch-level. - - -In case you need early stopping in a different part of training, subclass :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` -and change where it is called: - -.. testcode:: - - class MyEarlyStopping(EarlyStopping): - def on_validation_end(self, trainer, pl_module): - # override this to disable early stopping at the end of val loop - pass - - def on_train_end(self, trainer, pl_module): - # instead, do it at the end of training loop - self._run_early_stopping_check(trainer) - -.. note:: - The :class:`~pytorch_lightning.callbacks.early_stopping.EarlyStopping` callback runs - at the end of every validation epoch by default. However, the frequency of validation - can be modified by setting various parameters in the :class:`~pytorch_lightning.trainer.trainer.Trainer`, - for example :paramref:`~pytorch_lightning.trainer.trainer.Trainer.check_val_every_n_epoch` - and :paramref:`~pytorch_lightning.trainer.trainer.Trainer.val_check_interval`. - It must be noted that the ``patience`` parameter counts the number of - validation checks with no improvement, and not the number of training epochs. - Therefore, with parameters ``check_val_every_n_epoch=10`` and ``patience=3``, the trainer - will perform at least 40 training epochs before being stopped. diff --git a/source/common/gradient_accumulation.rst b/source/common/gradient_accumulation.rst deleted file mode 100644 index c65e75e..0000000 --- a/source/common/gradient_accumulation.rst +++ /dev/null @@ -1,43 +0,0 @@ -Accumulated gradients run K small batches of size ``N`` before doing a backward pass. The effect is a large effective batch size of size ``KxN``, where ``N`` is the batch size. -Internally it doesn't stack up the batches and do a forward pass rather it accumulates the gradients for K batches and then do an ``optimizer.step`` to make sure the -effective batch size is increased but there is no memory overhead. - -.. warning:: - - When using distributed training for eg. DDP, with let's say with ``P`` devices, each device accumulates independently i.e. it stores the gradients - after each ``loss.backward()`` and doesn't sync the gradients across the devices until we call ``optimizer.step()``. So for each accumulation - step, the effective batch size on each device will remain ``N*K`` but right before the ``optimizer.step()``, the gradient sync will make the effective - batch size as ``P*N*K``. For DP, since the batch is split across devices, the final effective batch size will be ``N*K``. - -.. seealso:: :class:`~pytorch_lightning.trainer.trainer.Trainer` - -.. testcode:: - - # DEFAULT (ie: no accumulated grads) - trainer = Trainer(accumulate_grad_batches=1) - - # Accumulate gradients for 7 batches - trainer = Trainer(accumulate_grad_batches=7) - -You can set different values for it at different epochs by passing a dictionary, where the key represents the epoch at which the value for gradient accumulation -should be updated. - -.. testcode:: - - # till 5th epoch, it will accumulate every 8 batches. From 5th epoch - # till 9th epoch it will accumulate every 4 batches and after that no accumulation - # will happen. Note that you need to use zero-indexed epoch keys here - trainer = Trainer(accumulate_grad_batches={0: 8, 4: 4, 8: 1}) - -Or, you can create custom :class:`~pytorch_lightning.callbacks.gradient_accumulation_scheduler.GradientAccumulationScheduler` - -.. testcode:: - - from pytorch_lightning.callbacks import GradientAccumulationScheduler - - - # till 5th epoch, it will accumulate every 8 batches. From 5th epoch - # till 9th epoch it will accumulate every 4 batches and after that no accumulation - # will happen. Note that you need to use zero-indexed epoch keys here - accumulator = GradientAccumulationScheduler(scheduling={0: 8, 4: 4, 8: 1}) - trainer = Trainer(callbacks=accumulator) diff --git a/source/common/hyperparameters.rst b/source/common/hyperparameters.rst deleted file mode 100644 index 9103100..0000000 --- a/source/common/hyperparameters.rst +++ /dev/null @@ -1,279 +0,0 @@ -.. testsetup:: * - - from argparse import ArgumentParser, Namespace - - sys.argv = ["foo"] - -Configure hyperparameters from the CLI --------------------------------------- - -Lightning has utilities to interact seamlessly with the command line ``ArgumentParser`` -and plays well with the hyperparameter optimization framework of your choice. - ----------- - -ArgumentParser -^^^^^^^^^^^^^^ -Lightning is designed to augment a lot of the functionality of the built-in Python ArgumentParser - -.. testcode:: - - from argparse import ArgumentParser - - parser = ArgumentParser() - parser.add_argument("--layer_1_dim", type=int, default=128) - args = parser.parse_args() - -This allows you to call your program like so: - -.. code-block:: bash - - python trainer.py --layer_1_dim 64 - ----------- - -Argparser Best Practices -^^^^^^^^^^^^^^^^^^^^^^^^ -It is best practice to layer your arguments in three sections. - -1. Trainer args (``accelerator``, ``devices``, ``num_nodes``, etc...) -2. Model specific arguments (``layer_dim``, ``num_layers``, ``learning_rate``, etc...) -3. Program arguments (``data_path``, ``cluster_email``, etc...) - -| - -We can do this as follows. First, in your ``LightningModule``, define the arguments -specific to that module. Remember that data splits or data paths may also be specific to -a module (i.e.: if your project has a model that trains on Imagenet and another on CIFAR-10). - -.. testcode:: - - class LitModel(LightningModule): - @staticmethod - def add_model_specific_args(parent_parser): - parser = parent_parser.add_argument_group("LitModel") - parser.add_argument("--encoder_layers", type=int, default=12) - parser.add_argument("--data_path", type=str, default="/some/path") - return parent_parser - -Now in your main trainer file, add the ``Trainer`` args, the program args, and add the model args - -.. testcode:: - - # ---------------- - # trainer_main.py - # ---------------- - from argparse import ArgumentParser - - parser = ArgumentParser() - - # add PROGRAM level args - parser.add_argument("--conda_env", type=str, default="some_name") - parser.add_argument("--notification_email", type=str, default="will@email.com") - - # add model specific args - parser = LitModel.add_model_specific_args(parser) - - # add all the available trainer options to argparse - # ie: now --accelerator --devices --num_nodes ... --fast_dev_run all work in the cli - parser = Trainer.add_argparse_args(parser) - - args = parser.parse_args() - -Now you can call run your program like so: - -.. code-block:: bash - - python trainer_main.py --accelerator 'gpu' --devices 2 --num_nodes 2 --conda_env 'my_env' --encoder_layers 12 - -Finally, make sure to start the training like so: - -.. code-block:: python - - # init the trainer like this - trainer = Trainer.from_argparse_args(args, early_stopping_callback=...) - - # NOT like this - trainer = Trainer(accelerator=hparams.accelerator, devices=hparams.devices, ...) - - # init the model with Namespace directly - model = LitModel(args) - - # or init the model with all the key-value pairs - dict_args = vars(args) - model = LitModel(**dict_args) - ----------- - -LightningModule hyperparameters -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Often times we train many versions of a model. You might share that model or come back to it a few months later -at which point it is very useful to know how that model was trained (i.e.: what learning rate, neural network, etc...). - -Lightning has a standardized way of saving the information for you in checkpoints and YAML files. The goal here is to -improve readability and reproducibility. - -save_hyperparameters -"""""""""""""""""""" - -Use :meth:`~pytorch_lightning.core.lightning.LightningModule.save_hyperparameters` within your -:class:`~pytorch_lightning.core.lightning.LightningModule`'s ``__init__`` method. -It will enable Lightning to store all the provided arguments under the ``self.hparams`` attribute. -These hyperparameters will also be stored within the model checkpoint, which simplifies model re-instantiation after training. - -.. code-block:: python - - class LitMNIST(LightningModule): - def __init__(self, layer_1_dim=128, learning_rate=1e-2): - super().__init__() - # call this to save (layer_1_dim=128, learning_rate=1e-4) to the checkpoint - self.save_hyperparameters() - - # equivalent - self.save_hyperparameters("layer_1_dim", "learning_rate") - - # Now possible to access layer_1_dim from hparams - self.hparams.layer_1_dim - - -In addition, loggers that support it will automatically log the contents of ``self.hparams``. - -Excluding hyperparameters -""""""""""""""""""""""""" - -By default, every parameter of the ``__init__`` method will be considered a hyperparameter to the LightningModule. -However, sometimes some parameters need to be excluded from saving, for example when they are not serializable. -Those parameters should be provided back when reloading the LightningModule. -In this case, exclude them explicitly: - -.. code-block:: python - - class LitMNIST(LightningModule): - def __init__(self, loss_fx, generator_network, layer_1_dim=128): - super().__init__() - self.layer_1_dim = layer_1_dim - self.loss_fx = loss_fx - - # call this to save only (layer_1_dim=128) to the checkpoint - self.save_hyperparameters("layer_1_dim") - - # equivalent - self.save_hyperparameters(ignore=["loss_fx", "generator_network"]) - - -load_from_checkpoint -"""""""""""""""""""" - -LightningModules that have hyperparameters automatically saved with :meth:`~pytorch_lightning.core.lightning.LightningModule.save_hyperparameters` -can conveniently be loaded and instantiated directly from a checkpoint with :meth:`~pytorch_lightning.core.lightning.LightningModule.load_from_checkpoint`: - -.. code-block:: python - - # to load specify the other args - model = LitMNIST.load_from_checkpoint(PATH, loss_fx=torch.nn.SomeOtherLoss, generator_network=MyGenerator()) - - -If parameters were excluded, they need to be provided at the time of loading: - -.. code-block:: python - - # the excluded parameters were `loss_fx` and `generator_network` - model = LitMNIST.load_from_checkpoint(PATH, loss_fx=torch.nn.SomeOtherLoss, generator_network=MyGenerator()) - - ----------- - -Trainer args -^^^^^^^^^^^^ -To recap, add ALL possible trainer flags to the argparser and init the ``Trainer`` this way - -.. code-block:: python - - parser = ArgumentParser() - parser = Trainer.add_argparse_args(parser) - hparams = parser.parse_args() - - trainer = Trainer.from_argparse_args(hparams) - - # or if you need to pass in callbacks - trainer = Trainer.from_argparse_args(hparams, enable_checkpointing=..., callbacks=[...]) - ----------- - -Multiple Lightning Modules -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -We often have multiple Lightning Modules where each one has different arguments. Instead of -polluting the ``main.py`` file, the ``LightningModule`` lets you define arguments for each one. - -.. testcode:: - - class LitMNIST(LightningModule): - def __init__(self, layer_1_dim, **kwargs): - super().__init__() - self.layer_1 = nn.Linear(28 * 28, layer_1_dim) - - @staticmethod - def add_model_specific_args(parent_parser): - parser = parent_parser.add_argument_group("LitMNIST") - parser.add_argument("--layer_1_dim", type=int, default=128) - return parent_parser - -.. testcode:: - - class GoodGAN(LightningModule): - def __init__(self, encoder_layers, **kwargs): - super().__init__() - self.encoder = Encoder(layers=encoder_layers) - - @staticmethod - def add_model_specific_args(parent_parser): - parser = parent_parser.add_argument_group("GoodGAN") - parser.add_argument("--encoder_layers", type=int, default=12) - return parent_parser - - -Now we can allow each model to inject the arguments it needs in the ``main.py`` - -.. code-block:: python - - def main(args): - dict_args = vars(args) - - # pick model - if args.model_name == "gan": - model = GoodGAN(**dict_args) - elif args.model_name == "mnist": - model = LitMNIST(**dict_args) - - trainer = Trainer.from_argparse_args(args) - trainer.fit(model) - - - if __name__ == "__main__": - parser = ArgumentParser() - parser = Trainer.add_argparse_args(parser) - - # figure out which model to use - parser.add_argument("--model_name", type=str, default="gan", help="gan or mnist") - - # THIS LINE IS KEY TO PULL THE MODEL NAME - temp_args, _ = parser.parse_known_args() - - # let the model add what it wants - if temp_args.model_name == "gan": - parser = GoodGAN.add_model_specific_args(parser) - elif temp_args.model_name == "mnist": - parser = LitMNIST.add_model_specific_args(parser) - - args = parser.parse_args() - - # train - main(args) - -and now we can train MNIST or the GAN using the command line interface! - -.. code-block:: bash - - $ python main.py --model_name gan --encoder_layers 24 - $ python main.py --model_name mnist --layer_1_dim 128 diff --git a/source/common/lightning_module.rst b/source/common/lightning_module.rst deleted file mode 100644 index 19bb9b0..0000000 --- a/source/common/lightning_module.rst +++ /dev/null @@ -1,1664 +0,0 @@ -.. role:: hidden - :class: hidden-section - -.. _lightning_module: - -############### -LightningModule -############### - -A :class:`~LightningModule` organizes your PyTorch code into 6 sections: - -- Computations (init). -- Train Loop (training_step) -- Validation Loop (validation_step) -- Test Loop (test_step) -- Prediction Loop (predict_step) -- Optimizers and LR Schedulers (configure_optimizers) - -| - -.. raw:: html - - - -| - -Notice a few things. - -1. It is the SAME code. -2. The PyTorch code IS NOT abstracted - just organized. -3. All the other code that's not in the :class:`~LightningModule` - has been automated for you by the Trainer. - -| - - .. code-block:: python - - net = Net() - trainer = Trainer() - trainer.fit(net) - -4. There are no ``.cuda()`` or ``.to(device)`` calls required. Lightning does these for you. - -| - - .. code-block:: python - - # don't do in Lightning - x = torch.Tensor(2, 3) - x = x.cuda() - x = x.to(device) - - # do this instead - x = x # leave it alone! - - # or to init a new tensor - new_x = torch.Tensor(2, 3) - new_x = new_x.type_as(x) - -5. When running under a distributed strategy, Lightning handles the distributed sampler for you by default. - -| - - .. code-block:: python - - # Don't do in Lightning... - data = MNIST(...) - sampler = DistributedSampler(data) - DataLoader(data, sampler=sampler) - - # do this instead - data = MNIST(...) - DataLoader(data) - -6. A :class:`~LightningModule` is a :class:`torch.nn.Module` but with added functionality. Use it as such! - -| - - .. code-block:: python - - net = Net.load_from_checkpoint(PATH) - net.freeze() - out = net(x) - -Thus, to use Lightning, you just need to organize your code which takes about 30 minutes, -(and let's be real, you probably should do anyway). - ------------- - -*************** -Starter Example -*************** - -Here are the only required methods. - -.. code-block:: python - - import pytorch_lightning as pl - import torch.nn as nn - import torch.nn.functional as F - - - class LitModel(pl.LightningModule): - def __init__(self): - super().__init__() - self.l1 = nn.Linear(28 * 28, 10) - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self(x) - loss = F.cross_entropy(y_hat, y) - return loss - - def configure_optimizers(self): - return torch.optim.Adam(self.parameters(), lr=0.02) - -Which you can train by doing: - -.. code-block:: python - - train_loader = DataLoader(MNIST(os.getcwd(), download=True, transform=transforms.ToTensor())) - trainer = pl.Trainer(max_epochs=1) - model = LitModel() - - trainer.fit(model, train_dataloaders=train_loader) - -The LightningModule has many convenience methods, but the core ones you need to know about are: - -.. list-table:: - :widths: 50 50 - :header-rows: 1 - - * - Name - - Description - * - init - - Define computations here - * - forward - - Use for inference only (separate from training_step) - * - training_step - - the complete training loop - * - validation_step - - the complete validation loop - * - test_step - - the complete test loop - * - predict_step - - the complete prediction loop - * - configure_optimizers - - define optimizers and LR schedulers - ----------- - -******** -Training -******** - -Training Loop -============= - -To activate the training loop, override the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` method. - -.. code-block:: python - - class LitClassifier(pl.LightningModule): - def __init__(self, model): - super().__init__() - self.model = model - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - return loss - -Under the hood, Lightning does the following (pseudocode): - -.. code-block:: python - - # put model in train mode and enable gradient calculation - model.train() - torch.set_grad_enabled(True) - - outs = [] - for batch_idx, batch in enumerate(train_dataloader): - loss = training_step(batch, batch_idx) - outs.append(loss.detach()) - - # clear gradients - optimizer.zero_grad() - - # backward - loss.backward() - - # update parameters - optimizer.step() - - -Train Epoch-level Metrics -========================= - -If you want to calculate epoch-level metrics and log them, use :meth:`~pytorch_lightning.core.lightning.LightningModule.log`. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - - # logs metrics for each training_step, - # and the average across the epoch, to the progress bar and logger - self.log("train_loss", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True) - return loss - -The :meth:`~pytorch_lightning.core.lightning.LightningModule.log` object automatically reduces the -requested metrics across a complete epoch and devices. Here's the pseudocode of what it does under the hood: - -.. code-block:: python - - outs = [] - for batch_idx, batch in enumerate(train_dataloader): - # forward - loss = training_step(batch, batch_idx) - outs.append(loss) - - # clear gradients - optimizer.zero_grad() - - # backward - loss.backward() - - # update parameters - optimizer.step() - - epoch_metric = torch.mean(torch.stack([x for x in outs])) - -Train Epoch-level Operations -============================ - -If you need to do something with all the outputs of each :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step`, -override the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_epoch_end` method. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - preds = ... - return {"loss": loss, "other_stuff": preds} - - - def training_epoch_end(self, training_step_outputs): - all_preds = torch.stack(training_step_outputs) - ... - -The matching pseudocode is: - -.. code-block:: python - - outs = [] - for batch_idx, batch in enumerate(train_dataloader): - # forward - loss = training_step(batch, batch_idx) - outs.append(loss) - - # clear gradients - optimizer.zero_grad() - - # backward - loss.backward() - - # update parameters - optimizer.step() - - training_epoch_end(outs) - -Training with DataParallel -========================== - -When training using a ``strategy`` that splits data from each batch across GPUs, sometimes you might -need to aggregate them on the main GPU for processing (DP, or DDP2). - -In this case, implement the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step_end` -method which will have outputs from all the devices and you can accumulate to get the effective results. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - pred = ... - return {"loss": loss, "pred": pred} - - - def training_step_end(self, batch_parts): - # predictions from each GPU - predictions = batch_parts["pred"] - # losses from each GPU - losses = batch_parts["loss"] - - gpu_0_prediction = predictions[0] - gpu_1_prediction = predictions[1] - - # do something with both outputs - return (losses[0] + losses[1]) / 2 - - - def training_epoch_end(self, training_step_outputs): - for out in training_step_outputs: - ... - -Here is the Lightning training pseudo-code for DP: - -.. code-block:: python - - outs = [] - for batch_idx, train_batch in enumerate(train_dataloader): - batches = split_batch(train_batch) - dp_outs = [] - for sub_batch in batches: - # 1 - dp_out = training_step(sub_batch, batch_idx) - dp_outs.append(dp_out) - - # 2 - out = training_step_end(dp_outs) - outs.append(out) - - # do something with the outputs for all batches - # 3 - training_epoch_end(outs) - ------------------- - -********** -Validation -********** - -Validation Loop -=============== - -To activate the validation loop while training, override the :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step` method. - -.. code-block:: python - - class LitModel(pl.LightningModule): - def validation_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - self.log("val_loss", loss) - -Under the hood, Lightning does the following (pseudocode): - -.. code-block:: python - - # ... - for batch_idx, batch in enumerate(train_dataloader): - loss = model.training_step(batch, batch_idx) - loss.backward() - # ... - - if validate_at_some_point: - # disable grads + batchnorm + dropout - torch.set_grad_enabled(False) - model.eval() - - # ----------------- VAL LOOP --------------- - for val_batch_idx, val_batch in enumerate(val_dataloader): - val_out = model.validation_step(val_batch, val_batch_idx) - # ----------------- VAL LOOP --------------- - - # enable grads + batchnorm + dropout - torch.set_grad_enabled(True) - model.train() - -You can also run just the validation loop on your validation dataloaders by overriding :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step` -and calling :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate`. - -.. code-block:: python - - model = Model() - trainer = Trainer() - trainer.validate(model) - -.. note:: - - It is recommended to validate on single device to ensure each sample/batch gets evaluated exactly once. - This is helpful to make sure benchmarking for research papers is done the right way. Otherwise, in a - multi-device setting, samples could occur duplicated when :class:`~torch.utils.data.distributed.DistributedSampler` - is used, for eg. with ``strategy="ddp"``. It replicates some samples on some devices to make sure all devices have - same batch size in case of uneven inputs. - - -Validation Epoch-level Metrics -============================== - -If you need to do something with all the outputs of each :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step`, -override the :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_epoch_end` method. Note that this method is called before :meth:`~pytorch_lightning.core.lightning.LightningModule.training_epoch_end`. - -.. code-block:: python - - def validation_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - pred = ... - return pred - - - def validation_epoch_end(self, validation_step_outputs): - all_preds = torch.stack(validation_step_outputs) - ... - -Validating with DataParallel -============================ - -When training using a ``strategy`` that splits data from each batch across GPUs, sometimes you might -need to aggregate them on the main GPU for processing (DP, or DDP2). - -In this case, implement the :meth:`~pytorch_lightning.core.lightning.LightningModule.validation_step_end` -method which will have outputs from all the devices and you can accumulate to get the effective results. - -.. code-block:: python - - def validation_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - pred = ... - return {"loss": loss, "pred": pred} - - - def validation_step_end(self, batch_parts): - # predictions from each GPU - predictions = batch_parts["pred"] - # losses from each GPU - losses = batch_parts["loss"] - - gpu_0_prediction = predictions[0] - gpu_1_prediction = predictions[1] - - # do something with both outputs - return (losses[0] + losses[1]) / 2 - - - def validation_epoch_end(self, validation_step_outputs): - for out in validation_step_outputs: - ... - -Here is the Lightning validation pseudo-code for DP: - -.. code-block:: python - - outs = [] - for batch in dataloader: - batches = split_batch(batch) - dp_outs = [] - for sub_batch in batches: - # 1 - dp_out = validation_step(sub_batch) - dp_outs.append(dp_out) - - # 2 - out = validation_step_end(dp_outs) - outs.append(out) - - # do something with the outputs for all batches - # 3 - validation_epoch_end(outs) - ----------------- - -******* -Testing -******* - -Test Loop -========= - -The process for enabling a test loop is the same as the process for enabling a validation loop. Please refer to -the section above for details. For this you need to override the :meth:`~pytorch_lightning.core.lightning.LightningModule.test_step` method. - -The only difference is that the test loop is only called when :meth:`~pytorch_lightning.trainer.trainer.Trainer.test` is used. - -.. code-block:: python - - model = Model() - trainer = Trainer() - trainer.fit(model) - - # automatically loads the best weights for you - trainer.test(model) - -There are two ways to call ``test()``: - -.. code-block:: python - - # call after training - trainer = Trainer() - trainer.fit(model) - - # automatically auto-loads the best weights from the previous run - trainer.test(dataloaders=test_dataloader) - - # or call with pretrained model - model = MyLightningModule.load_from_checkpoint(PATH) - trainer = Trainer() - trainer.test(model, dataloaders=test_dataloader) - -.. note:: - - It is recommended to validate on single device to ensure each sample/batch gets evaluated exactly once. - This is helpful to make sure benchmarking for research papers is done the right way. Otherwise, in a - multi-device setting, samples could occur duplicated when :class:`~torch.utils.data.distributed.DistributedSampler` - is used, for eg. with ``strategy="ddp"``. It replicates some samples on some devices to make sure all devices have - same batch size in case of uneven inputs. - - ----------- - -********* -Inference -********* - -Prediction Loop -=============== - -By default, the :meth:`~pytorch_lightning.core.lightning.LightningModule.predict_step` method runs the -:meth:`~pytorch_lightning.core.lightning.LightningModule.forward` method. In order to customize this behaviour, -simply override the :meth:`~pytorch_lightning.core.lightning.LightningModule.predict_step` method. - -For the example let's override ``predict_step`` and try out `Monte Carlo Dropout `_: - -.. code-block:: python - - class LitMCdropoutModel(pl.LightningModule): - def __init__(self, model, mc_iteration): - super().__init__() - self.model = model - self.dropout = nn.Dropout() - self.mc_iteration = mc_iteration - - def predict_step(self, batch, batch_idx): - # enable Monte Carlo Dropout - self.dropout.train() - - # take average of `self.mc_iteration` iterations - pred = torch.vstack([self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)]).mean(dim=0) - return pred - -Under the hood, Lightning does the following (pseudocode): - -.. code-block:: python - - # disable grads + batchnorm + dropout - torch.set_grad_enabled(False) - model.eval() - all_preds = [] - - for batch_idx, batch in enumerate(predict_dataloader): - pred = model.predict_step(batch, batch_idx) - all_preds.append(pred) - -There are two ways to call ``predict()``: - -.. code-block:: python - - # call after training - trainer = Trainer() - trainer.fit(model) - - # automatically auto-loads the best weights from the previous run - predictions = trainer.predict(dataloaders=predict_dataloader) - - # or call with pretrained model - model = MyLightningModule.load_from_checkpoint(PATH) - trainer = Trainer() - predictions = trainer.predict(model, dataloaders=test_dataloader) - -Inference in Research -===================== - -If you want to perform inference with the system, you can add a ``forward`` method to the LightningModule. - -.. note:: When using forward, you are responsible to call :func:`~torch.nn.Module.eval` and use the :func:`~torch.no_grad` context manager. - -.. code-block:: python - - class Autoencoder(pl.LightningModule): - def forward(self, x): - return self.decoder(x) - - - model = Autoencoder() - model.eval() - with torch.no_grad(): - reconstruction = model(embedding) - -The advantage of adding a forward is that in complex systems, you can do a much more involved inference procedure, -such as text generation: - -.. code-block:: python - - class Seq2Seq(pl.LightningModule): - def forward(self, x): - embeddings = self(x) - hidden_states = self.encoder(embeddings) - for h in hidden_states: - # decode - ... - return decoded - -In the case where you want to scale your inference, you should be using -:meth:`~pytorch_lightning.core.lightning.LightningModule.predict_step`. - -.. code-block:: python - - class Autoencoder(pl.LightningModule): - def forward(self, x): - return self.decoder(x) - - def predict_step(self, batch, batch_idx, dataloader_idx=0): - # this calls forward - return self(batch) - - - data_module = ... - model = Autoencoder() - trainer = Trainer(accelerator="gpu", devices=2) - trainer.predict(model, data_module) - -Inference in Production -======================= - -For cases like production, you might want to iterate different models inside a LightningModule. - -.. code-block:: python - - from torchmetrics.functional import accuracy - - - class ClassificationTask(pl.LightningModule): - def __init__(self, model): - super().__init__() - self.model = model - - def training_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - return loss - - def validation_step(self, batch, batch_idx): - loss, acc = self._shared_eval_step(batch, batch_idx) - metrics = {"val_acc": acc, "val_loss": loss} - self.log_dict(metrics) - return metrics - - def test_step(self, batch, batch_idx): - loss, acc = self._shared_eval_step(batch, batch_idx) - metrics = {"test_acc": acc, "test_loss": loss} - self.log_dict(metrics) - return metrics - - def _shared_eval_step(self, batch, batch_idx): - x, y = batch - y_hat = self.model(x) - loss = F.cross_entropy(y_hat, y) - acc = accuracy(y_hat, y) - return loss, acc - - def predict_step(self, batch, batch_idx, dataloader_idx=0): - x, y = batch - y_hat = self.model(x) - return y_hat - - def configure_optimizers(self): - return torch.optim.Adam(self.model.parameters(), lr=0.02) - -Then pass in any arbitrary model to be fit with this task - -.. code-block:: python - - for model in [resnet50(), vgg16(), BidirectionalRNN()]: - task = ClassificationTask(model) - - trainer = Trainer(accelerator="gpu", devices=2) - trainer.fit(task, train_dataloaders=train_dataloader, val_dataloaders=val_dataloader) - -Tasks can be arbitrarily complex such as implementing GAN training, self-supervised or even RL. - -.. code-block:: python - - class GANTask(pl.LightningModule): - def __init__(self, generator, discriminator): - super().__init__() - self.generator = generator - self.discriminator = discriminator - - ... - -When used like this, the model can be separated from the Task and thus used in production without needing to keep it in -a ``LightningModule``. - -The following example shows how you can run inference in the Python runtime: - -.. code-block:: python - - task = ClassificationTask(model) - trainer = Trainer(accelerator="gpu", devices=2) - trainer.fit(task, train_dataloader, val_dataloader) - trainer.save_checkpoint("best_model.ckpt") - - # use model after training or load weights and drop into the production system - model = ClassificationTask.load_from_checkpoint("best_model.ckpt") - x = ... - model.eval() - with torch.no_grad(): - y_hat = model(x) - -Check out :ref:`Inference in Production ` guide to learn about the possible ways to perform inference in production. - - ------------ - - -************* -Child Modules -************* - -.. include:: ../common/child_modules.rst - ------------ - -******************* -LightningModule API -******************* - - -Methods -======= - -all_gather -~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.all_gather - :noindex: - -configure_callbacks -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.configure_callbacks - :noindex: - -configure_optimizers -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.configure_optimizers - :noindex: - -forward -~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.forward - :noindex: - -freeze -~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.freeze - :noindex: - -.. _lm-log: - -log -~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.log - :noindex: - -log_dict -~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.log_dict - :noindex: - -lr_schedulers -~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.lr_schedulers - :noindex: - -manual_backward -~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.manual_backward - :noindex: - -optimizers -~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.optimizers - :noindex: - -print -~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.print - :noindex: - -predict_step -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.predict_step - :noindex: - -save_hyperparameters -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.save_hyperparameters - :noindex: - -toggle_optimizer -~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.toggle_optimizer - :noindex: - -test_step -~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.test_step - :noindex: - -test_step_end -~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.test_step_end - :noindex: - -test_epoch_end -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.test_epoch_end - :noindex: - -to_onnx -~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.to_onnx - :noindex: - -to_torchscript -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.to_torchscript - :noindex: - -training_step -~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.training_step - :noindex: - -training_step_end -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.training_step_end - :noindex: - -training_epoch_end -~~~~~~~~~~~~~~~~~~ -.. automethod:: pytorch_lightning.core.lightning.LightningModule.training_epoch_end - :noindex: - -unfreeze -~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.unfreeze - :noindex: - -untoggle_optimizer -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.untoggle_optimizer - :noindex: - -validation_step -~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.validation_step - :noindex: - -validation_step_end -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.validation_step_end - :noindex: - -validation_epoch_end -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.validation_epoch_end - :noindex: - ------------ - -Properties -========== - -These are properties available in a LightningModule. - -current_epoch -~~~~~~~~~~~~~ - -The number of epochs run. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.current_epoch == 0: - ... - -device -~~~~~~ - -The device the module is on. Use it to keep your code device agnostic. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - z = torch.rand(2, 3, device=self.device) - -global_rank -~~~~~~~~~~~ - -The ``global_rank`` is the index of the current process across all nodes and devices. -Lightning will perform some operations such as logging, weight checkpointing only when ``global_rank=0``. You -usually do not need to use this property, but it is useful to know how to access it if needed. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.global_rank == 0: - # do something only once across all the nodes - ... - -global_step -~~~~~~~~~~~ - -The number of optimizer steps taken (does not reset each epoch). -This includes multiple optimizers and TBPTT steps (if enabled). - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.logger.experiment.log_image(..., step=self.global_step) - -hparams -~~~~~~~ - -The arguments passed through ``LightningModule.__init__()`` and saved by calling -:meth:`~pytorch_lightning.core.mixins.hparams_mixin.HyperparametersMixin.save_hyperparameters` could be accessed by the ``hparams`` attribute. - -.. code-block:: python - - def __init__(self, learning_rate): - self.save_hyperparameters() - - - def configure_optimizers(self): - return Adam(self.parameters(), lr=self.hparams.learning_rate) - -logger -~~~~~~ - -The current logger being used (tensorboard or other supported logger) - -.. code-block:: python - - def training_step(self, batch, batch_idx): - # the generic logger (same no matter if tensorboard or other supported logger) - self.logger - - # the particular logger - tensorboard_logger = self.logger.experiment - -loggers -~~~~~~~ - -The list of loggers currently being used by the Trainer. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - # List of Logger objects - loggers = self.loggers - for logger in loggers: - logger.log_metrics({"foo": 1.0}) - -local_rank -~~~~~~~~~~~ - -The ``local_rank`` is the index of the current process across all the devices for the current node. -You usually do not need to use this property, but it is useful to know how to access it if needed. -For example, if using 10 machines (or nodes), the GPU at index 0 on each machine has local_rank = 0. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.local_rank == 0: - # do something only once across each node - ... - -precision -~~~~~~~~~ - -The type of precision used: - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.precision == 16: - ... - -trainer -~~~~~~~ - -Pointer to the trainer - -.. code-block:: python - - def training_step(self, batch, batch_idx): - max_steps = self.trainer.max_steps - any_flag = self.trainer.any_flag - -prepare_data_per_node -~~~~~~~~~~~~~~~~~~~~~ - -If set to ``True`` will call ``prepare_data()`` on LOCAL_RANK=0 for every node. -If set to ``False`` will only call from NODE_RANK=0, LOCAL_RANK=0. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self): - super().__init__() - self.prepare_data_per_node = True - -automatic_optimization -~~~~~~~~~~~~~~~~~~~~~~ - -When set to ``False``, Lightning does not automate the optimization process. This means you are responsible for handling -your optimizers. However, we do take care of precision and any accelerators used. - -See :ref:`manual optimization ` for details. - -.. code-block:: python - - def __init__(self): - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - opt = self.optimizers(use_pl_optimizer=True) - - loss = ... - opt.zero_grad() - self.manual_backward(loss) - opt.step() - -This is recommended only if using 2+ optimizers AND if you know how to perform the optimization procedure properly. Note -that automatic optimization can still be used with multiple optimizers by relying on the ``optimizer_idx`` parameter. -Manual optimization is most useful for research topics like reinforcement learning, sparse coding, and GAN research. - -.. code-block:: python - - def __init__(self): - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - # access your optimizers with use_pl_optimizer=False. Default is True - opt_a, opt_b = self.optimizers(use_pl_optimizer=True) - - gen_loss = ... - opt_a.zero_grad() - self.manual_backward(gen_loss) - opt_a.step() - - disc_loss = ... - opt_b.zero_grad() - self.manual_backward(disc_loss) - opt_b.step() - -example_input_array -~~~~~~~~~~~~~~~~~~~ - -Set and access example_input_array, which basically represents a single batch. - -.. code-block:: python - - def __init__(self): - self.example_input_array = ... - self.generator = ... - - - def on_train_epoch_end(self): - # generate some images using the example_input_array - gen_images = self.generator(self.example_input_array) - -truncated_bptt_steps -~~~~~~~~~~~~~~~~~~~~ - -Truncated Backpropagation Through Time (TBPTT) performs perform backpropogation every k steps of -a much longer sequence. This is made possible by passing training batches -split along the time-dimensions into splits of size k to the -``training_step``. In order to keep the same forward propagation behavior, all -hidden states should be kept in-between each time-dimension split. - - -If this is enabled, your batches will automatically get truncated -and the Trainer will apply Truncated Backprop to it. - -(`Williams et al. "An efficient gradient-based algorithm for on-line training of -recurrent network trajectories." -`_) - -`Tutorial `_ - -.. testcode:: python - - from pytorch_lightning import LightningModule - - - class MyModel(LightningModule): - def __init__(self, input_size, hidden_size, num_layers): - super().__init__() - # batch_first has to be set to True - self.lstm = nn.LSTM( - input_size=input_size, - hidden_size=hidden_size, - num_layers=num_layers, - batch_first=True, - ) - - ... - - # Important: This property activates truncated backpropagation through time - # Setting this value to 2 splits the batch into sequences of size 2 - self.truncated_bptt_steps = 2 - - # Truncated back-propagation through time - def training_step(self, batch, batch_idx, hiddens): - x, y = batch - - # the training step must be updated to accept a ``hiddens`` argument - # hiddens are the hiddens from the previous truncated backprop step - out, hiddens = self.lstm(x, hiddens) - - ... - - return {"loss": ..., "hiddens": hiddens} - -Lightning takes care of splitting your batch along the time-dimension. It is -assumed to be the second dimension of your batches. Therefore, in the -example above, we have set ``batch_first=True``. - -.. code-block:: python - - # we use the second as the time dimension - # (batch, time, ...) - sub_batch = batch[0, 0:t, ...] - -To modify how the batch is split, -override the :meth:`pytorch_lightning.core.lightning.LightningModule.tbptt_split_batch` method: - -.. testcode:: python - - class LitMNIST(LightningModule): - def tbptt_split_batch(self, batch, split_size): - # do your own splitting on the batch - return splits - --------------- - -.. _lightning_hooks: - -Hooks -===== - -This is the pseudocode to describe the structure of :meth:`~pytorch_lightning.trainer.Trainer.fit`. -The inputs and outputs of each function are not represented for simplicity. Please check each function's API reference -for more information. - -.. code-block:: python - - def fit(self): - if global_rank == 0: - # prepare data is called on GLOBAL_ZERO only - prepare_data() - - configure_callbacks() - - with parallel(devices): - # devices can be GPUs, TPUs, ... - train_on_device(model) - - - def train_on_device(model): - # called PER DEVICE - on_fit_start() - setup("fit") - configure_optimizers() - - # the sanity check runs here - - on_train_start() - for epoch in epochs: - fit_loop() - on_train_end() - - on_fit_end() - teardown("fit") - - - def fit_loop(): - on_train_epoch_start() - - for batch in train_dataloader(): - on_train_batch_start() - - on_before_batch_transfer() - transfer_batch_to_device() - on_after_batch_transfer() - - training_step() - - on_before_zero_grad() - optimizer_zero_grad() - - on_before_backward() - backward() - on_after_backward() - - on_before_optimizer_step() - configure_gradient_clipping() - optimizer_step() - - on_train_batch_end() - - if should_check_val: - val_loop() - # end training epoch - training_epoch_end() - - on_train_epoch_end() - - - def val_loop(): - on_validation_model_eval() # calls `model.eval()` - torch.set_grad_enabled(False) - - on_validation_start() - on_validation_epoch_start() - - val_outs = [] - for batch_idx, batch in enumerate(val_dataloader()): - on_validation_batch_start(batch, batch_idx) - - batch = on_before_batch_transfer(batch) - batch = transfer_batch_to_device(batch) - batch = on_after_batch_transfer(batch) - - out = validation_step(batch, batch_idx) - - on_validation_batch_end(batch, batch_idx) - val_outs.append(out) - - validation_epoch_end(val_outs) - - on_validation_epoch_end() - on_validation_end() - - # set up for train - on_validation_model_train() # calls `model.train()` - torch.set_grad_enabled(True) - -backward -~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.backward - :noindex: - -on_before_backward -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_before_backward - :noindex: - -on_after_backward -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_after_backward - :noindex: - -on_before_zero_grad -~~~~~~~~~~~~~~~~~~~ -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_before_zero_grad - :noindex: - -on_fit_start -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_fit_start - :noindex: - -on_fit_end -~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_fit_end - :noindex: - - -on_load_checkpoint -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_load_checkpoint - :noindex: - -on_save_checkpoint -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_save_checkpoint - :noindex: - -load_from_checkpoint -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.load_from_checkpoint - :noindex: - -on_hpc_save -~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_hpc_save - :noindex: - -on_hpc_load -~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_hpc_load - :noindex: - -on_train_start -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_start - :noindex: - -on_train_end -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_end - :noindex: - -on_validation_start -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_start - :noindex: - -on_validation_end -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_end - :noindex: - -on_test_batch_start -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_batch_start - :noindex: - -on_test_batch_end -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_batch_end - :noindex: - -on_test_epoch_start -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_epoch_start - :noindex: - -on_test_epoch_end -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_epoch_end - :noindex: - -on_test_start -~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_start - :noindex: - -on_test_end -~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_end - :noindex: - -on_predict_batch_start -~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_batch_start - :noindex: - -on_predict_batch_end -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_batch_end - :noindex: - -on_predict_epoch_start -~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_epoch_start - :noindex: - -on_predict_epoch_end -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_epoch_end - :noindex: - -on_predict_start -~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_start - :noindex: - -on_predict_end -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_end - :noindex: - -on_train_batch_start -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_batch_start - :noindex: - -on_train_batch_end -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_batch_end - :noindex: - -on_train_epoch_start -~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_epoch_start - :noindex: - -on_train_epoch_end -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_epoch_end - :noindex: - -on_validation_batch_start -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_batch_start - :noindex: - -on_validation_batch_end -~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_batch_end - :noindex: - -on_validation_epoch_start -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_epoch_start - :noindex: - -on_validation_epoch_end -~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_epoch_end - :noindex: - -on_post_move_to_device -~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_post_move_to_device - :noindex: - -configure_sharded_model -~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.configure_sharded_model - :noindex: - -on_validation_model_eval -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_model_eval - :noindex: - -on_validation_model_train -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_validation_model_train - :noindex: - -on_test_model_eval -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_model_eval - :noindex: - -on_test_model_train -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_model_train - :noindex: - -on_before_optimizer_step -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_before_optimizer_step - :noindex: - -configure_gradient_clipping -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.configure_gradient_clipping - :noindex: - -optimizer_step -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.optimizer_step - :noindex: - -optimizer_zero_grad -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.optimizer_zero_grad - :noindex: - -prepare_data -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.prepare_data - :noindex: - -setup -~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.setup - :noindex: - -tbptt_split_batch -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.tbptt_split_batch - :noindex: - -teardown -~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.teardown - :noindex: - -train_dataloader -~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.train_dataloader - :noindex: - -val_dataloader -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.val_dataloader - :noindex: - -test_dataloader -~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.test_dataloader - :noindex: - -predict_dataloader -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.predict_dataloader - :noindex: - -on_train_dataloader -~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_train_dataloader - :noindex: - -on_val_dataloader -~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_val_dataloader - :noindex: - -on_test_dataloader -~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_test_dataloader - :noindex: - -on_predict_dataloader -~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_predict_dataloader - :noindex: - -transfer_batch_to_device -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.transfer_batch_to_device - :noindex: - -on_before_batch_transfer -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_before_batch_transfer - :noindex: - -on_after_batch_transfer -~~~~~~~~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.on_after_batch_transfer - :noindex: - -add_to_queue -~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.add_to_queue - :noindex: - -get_from_queue -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.core.lightning.LightningModule.get_from_queue - :noindex: diff --git a/source/common/optimization.rst b/source/common/optimization.rst deleted file mode 100644 index e7e9e12..0000000 --- a/source/common/optimization.rst +++ /dev/null @@ -1,327 +0,0 @@ -:orphan: - -.. _optimization: - -############ -Optimization -############ - -Lightning offers two modes for managing the optimization process: - -- Manual Optimization -- Automatic Optimization - -For the majority of research cases, **automatic optimization** will do the right thing for you and it is what most -users should use. - -For advanced/expert users who want to do esoteric optimization schedules or techniques, use **manual optimization**. - -.. _manual_optimization: - ----- - -.. include:: ../model/manual_optimization.rst - ------ - -********************** -Automatic Optimization -********************** - -With Lightning, most users don't have to think about when to call ``.zero_grad()``, ``.backward()`` and ``.step()`` -since Lightning automates that for you. - -Under the hood, Lightning does the following: - -.. code-block:: python - - for epoch in epochs: - for batch in data: - - def closure(): - loss = model.training_step(batch, batch_idx, ...) - optimizer.zero_grad() - loss.backward() - return loss - - optimizer.step(closure) - - lr_scheduler.step() - -In the case of multiple optimizers, Lightning does the following: - -.. code-block:: python - - for epoch in epochs: - for batch in data: - for opt in optimizers: - - def closure(): - loss = model.training_step(batch, batch_idx, optimizer_idx) - opt.zero_grad() - loss.backward() - return loss - - opt.step(closure) - - for lr_scheduler in lr_schedulers: - lr_scheduler.step() - -As can be seen in the code snippet above, Lightning defines a closure with ``training_step()``, ``optimizer.zero_grad()`` -and ``loss.backward()`` for the optimization. This mechanism is in place to support optimizers which operate on the -output of the closure (e.g. the loss) or need to call the closure several times (e.g. :class:`~torch.optim.LBFGS`). - -.. warning:: - - Before v1.2.2, Lightning internally calls ``backward``, ``step`` and ``zero_grad`` in the order. - From v1.2.2, the order is changed to ``zero_grad``, ``backward`` and ``step``. - - -Gradient Accumulation -===================== - -.. include:: ../common/gradient_accumulation.rst - - -Use Multiple Optimizers (like GANs) -=================================== - -To use multiple optimizers (optionally with learning rate schedulers), return two or more optimizers from -:meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`. - -.. testcode:: python - - # two optimizers, no schedulers - def configure_optimizers(self): - return Adam(...), SGD(...) - - - # two optimizers, one scheduler for adam only - def configure_optimizers(self): - opt1 = Adam(...) - opt2 = SGD(...) - optimizers = [opt1, opt2] - lr_schedulers = {"scheduler": ReduceLROnPlateau(opt1, ...), "monitor": "metric_to_track"} - return optimizers, lr_schedulers - - - # two optimizers, two schedulers - def configure_optimizers(self): - opt1 = Adam(...) - opt2 = SGD(...) - return [opt1, opt2], [StepLR(opt1, ...), OneCycleLR(opt2, ...)] - -Under the hood, Lightning will call each optimizer sequentially: - -.. code-block:: python - - for epoch in epochs: - for batch in data: - for opt in optimizers: - loss = train_step(batch, batch_idx, optimizer_idx) - opt.zero_grad() - loss.backward() - opt.step() - - for lr_scheduler in lr_schedulers: - lr_scheduler.step() - - -Step Optimizeres at Arbitrary Intervals -======================================= - -To do more interesting things with your optimizers such as learning rate warm-up or odd scheduling, -override the :meth:`~pytorch_lightning.core.lightning.LightningModule.optimizer_step` function. - -.. warning:: - If you are overriding this method, make sure that you pass the ``optimizer_closure`` parameter to - ``optimizer.step()`` function as shown in the examples because ``training_step()``, ``optimizer.zero_grad()``, - ``loss.backward()`` are called in the closure function. - -For example, here step optimizer A every batch and optimizer B every 2 batches. - -.. testcode:: python - - # Alternating schedule for optimizer steps (e.g. GANs) - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu=False, - using_native_amp=False, - using_lbfgs=False, - ): - # update generator every step - if optimizer_idx == 0: - optimizer.step(closure=optimizer_closure) - - # update discriminator every 2 steps - if optimizer_idx == 1: - if (batch_idx + 1) % 2 == 0: - # the closure (which includes the `training_step`) will be executed by `optimizer.step` - optimizer.step(closure=optimizer_closure) - else: - # call the closure by itself to run `training_step` + `backward` without an optimizer step - optimizer_closure() - - # ... - # add as many optimizers as you want - -Here we add a manual learning rate warm-up without an lr scheduler. - -.. testcode:: python - - # learning rate warm-up - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu=False, - using_native_amp=False, - using_lbfgs=False, - ): - # update params - optimizer.step(closure=optimizer_closure) - - # skip the first 500 steps - if self.trainer.global_step < 500: - lr_scale = min(1.0, float(self.trainer.global_step + 1) / 500.0) - for pg in optimizer.param_groups: - pg["lr"] = lr_scale * self.hparams.learning_rate - - -Access your Own Optimizer -========================= - -The provided ``optimizer`` is a :class:`~pytorch_lightning.core.optimizer.LightningOptimizer` object wrapping your own optimizer -configured in your :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`. -You can access your own optimizer with ``optimizer.optimizer``. However, if you use your own optimizer -to perform a step, Lightning won't be able to support accelerators, precision and profiling for you. - -.. testcode:: python - - # function hook in LightningModule - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu=False, - using_native_amp=False, - using_lbfgs=False, - ): - optimizer.step(closure=optimizer_closure) - - - # `optimizer` is a `LightningOptimizer` wrapping the optimizer. - # To access it, do the following. - # However, it won't work on TPU, AMP, etc... - def optimizer_step( - self, - epoch, - batch_idx, - optimizer, - optimizer_idx, - optimizer_closure, - on_tpu=False, - using_native_amp=False, - using_lbfgs=False, - ): - optimizer = optimizer.optimizer - optimizer.step(closure=optimizer_closure) - ------ - - -Bring your own Custom Learning Rate Schedulers -============================================== - -Lightning allows using custom learning rate schedulers that aren't available in `PyTorch natively `_. -One good example is `Timm Schedulers `_. When using custom learning rate schedulers -relying on a different API from Native PyTorch ones, you should override the :meth:`~pytorch_lightning.core.lightning.LightningModule.lr_scheduler_step` with your desired logic. -If you are using native PyTorch schedulers, there is no need to override this hook since Lightning will handle it automatically by default. - -.. code-block:: python - - from timm.scheduler import TanhLRScheduler - - - def configure_optimizers(self): - optimizer = ... - scheduler = TanhLRScheduler(optimizer, ...) - return [optimizer], [{"scheduler": scheduler, "interval": "epoch"}] - - - def lr_scheduler_step(self, scheduler, optimizer_idx, metric): - scheduler.step(epoch=self.current_epoch) # timm's scheduler need the epoch value - - -.. _configure_gradient_clipping: - -Configure Gradient Clipping -=========================== - -To configure custom gradient clipping, consider overriding -the :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_gradient_clipping` method. -Attributes ``gradient_clip_val`` and ``gradient_clip_algorithm`` from Trainer will be passed in the -respective arguments here and Lightning will handle gradient clipping for you. In case you want to set -different values for your arguments of your choice and let Lightning handle the gradient clipping, you can -use the inbuilt :meth:`~pytorch_lightning.core.lightning.LightningModule.clip_gradients` method and pass -the arguments along with your optimizer. - -.. warning:: - Make sure to not override :meth:`~pytorch_lightning.core.lightning.LightningModule.clip_gradients` - method. If you want to customize gradient clipping, consider using - :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_gradient_clipping` method. - -For example, here we will apply gradient clipping only to the gradients associated with optimizer A. - -.. testcode:: python - - def configure_gradient_clipping(self, optimizer, optimizer_idx, gradient_clip_val, gradient_clip_algorithm): - if optimizer_idx == 0: - # Lightning will handle the gradient clipping - self.clip_gradients( - optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm - ) - -Here we configure gradient clipping differently for optimizer B. - -.. testcode:: python - - def configure_gradient_clipping(self, optimizer, optimizer_idx, gradient_clip_val, gradient_clip_algorithm): - if optimizer_idx == 0: - # Lightning will handle the gradient clipping - self.clip_gradients( - optimizer, gradient_clip_val=gradient_clip_val, gradient_clip_algorithm=gradient_clip_algorithm - ) - elif optimizer_idx == 1: - self.clip_gradients( - optimizer, gradient_clip_val=gradient_clip_val * 2, gradient_clip_algorithm=gradient_clip_algorithm - ) - - -Total Stepping Batches -====================== - -You can use built-in trainer property :paramref:`~pytorch_lightning.trainer.trainer.Trainer.estimated_stepping_batches` to compute -total number of stepping batches for the complete training. The property is computed considering gradient accumulation factor and -distributed setting into consideration so you don't have to derive it manually. One good example where this can be helpful is while using -:class:`~torch.optim.lr_scheduler.OneCycleLR` scheduler, which requires pre-computed ``total_steps`` during initialization. - -.. code-block:: python - - def configure_optimizers(self): - optimizer = ... - scheduler = torch.optim.lr_scheduler.OneCycleLR( - optimizer, max_lr=1e-3, total_steps=self.trainer.estimated_stepping_batches - ) - return [optimizer], [scheduler] diff --git a/source/common/precision_basic.rst b/source/common/precision_basic.rst deleted file mode 100644 index 3cc0b3a..0000000 --- a/source/common/precision_basic.rst +++ /dev/null @@ -1,92 +0,0 @@ -:orphan: - -.. _precision_basic: - -####################### -N-Bit Precision (Basic) -####################### -**Audience:** Users looking to train models faster and consume less memory. - ----- - -If you're looking to run models faster or consume less memory, consider tweaking the precision settings of your models. - -Lower precision, such as 16-bit floating-point, requires less memory and enables training and deploying larger models. -Higher precision, such as the 64-bit floating-point, can be used for highly sensitive use-cases. - ----- - -**************** -16-bit Precision -**************** - -Use 16-bit precision to cut your memory consumption in half so that you can train and deploy larger models. If your GPUs are [`Tensor Core `_] GPUs, you can also get a ~3x speed improvement. Half precision can sometimes lead to unstable training. - -.. code:: - - Trainer(precision=16) - ----- - -**************** -32-bit Precision -**************** - -32-bit precision is the default used across all models and research. This precision is known to be stable in contrast to lower precision settings. - -.. testcode:: - - Trainer(precision=32) - ----- - -**************** -64-bit Precision -**************** - -For certain scientific computations, 64-bit precision enables more accurate models. However, doubling the precision from 32 to 64 bit also doubles the memory requirements. - -.. testcode:: - - Trainer(precision=64) - -.. note:: - - Since in deep learning, memory is always a bottleneck, especially when dealing with a large volume of data and with limited resources. - It is recommended using single precision for better speed. Although you can still use it if you want for your particular use-case. - ----- - -******************************** -Precision support by accelerator -******************************** - -.. list-table:: Precision with Accelerators - :widths: 20 20 20 20 20 - :header-rows: 1 - - * - Precision - - CPU - - GPU - - TPU - - IPU - * - 16 - - No - - Yes - - No - - Yes - * - BFloat16 - - Yes - - Yes - - Yes - - No - * - 32 - - Yes - - Yes - - Yes - - Yes - * - 64 - - Yes - - Yes - - No - - No diff --git a/source/common/precision_intermediate.rst b/source/common/precision_intermediate.rst deleted file mode 100644 index 9ed4c75..0000000 --- a/source/common/precision_intermediate.rst +++ /dev/null @@ -1,143 +0,0 @@ -:orphan: - -.. _precision_intermediate: - -############################## -N-Bit Precision (Intermediate) -############################## -**Audience:** Users looking to scale larger models or take advantage of optimized accelerators. - ----- - -************************ -What is Mixed Precision? -************************ - -PyTorch, like most deep learning frameworks, trains on 32-bit floating-point (FP32) arithmetic by default. However, many deep learning models do not require this to reach complete accuracy. By conducting -operations in half-precision format while keeping minimum information in single-precision to maintain as much information as possible in crucial areas of the network, mixed precision training delivers -significant computational speedup. Switching to mixed precision has resulted in considerable training speedups since the introduction of Tensor Cores in the Volta and Turing architectures. It combines -FP32 and lower-bit floating-points (such as FP16) to reduce memory footprint and increase performance during model training and evaluation. It accomplishes this by recognizing the steps that require -complete accuracy and employing a 32-bit floating-point for those steps only, while using a 16-bit floating-point for the rest. When compared to complete precision training, mixed precision training -delivers all of these benefits while ensuring that no task-specific accuracy is lost. [`2 `_]. - -.. note:: - - In some cases, it is essential to remain in FP32 for numerical stability, so keep this in mind when using mixed precision. - For example, when running scatter operations during the forward (such as torchpoint3d), computation must remain in FP32. - -.. warning:: - - Do not cast anything to other dtypes manually using ``torch.autocast`` or ``tensor.half()`` when using native precision because - this can bring instability. - - .. code-block:: python - - class LitModel(LightningModule): - def training_step(self, batch, batch_idx): - outs = self(batch) - - a_float32 = torch.rand((8, 8), device=self.device, dtype=self.dtype) - b_float32 = torch.rand((8, 4), device=self.device, dtype=self.dtype) - - # casting to float16 manually - with torch.autocast(device_type=self.device.type): - c_float16 = torch.mm(a_float32, b_float32) - target = self.layer(c_float16.flatten()[None]) - - # here outs is of type float32 and target is of type float16 - loss = torch.mm(target @ outs).float() - return loss - - - trainer = Trainer(accelerator="gpu", devices=1, precision=32) - ----- - -******************** -FP16 Mixed Precision -******************** - -In most cases, mixed precision uses FP16. Supported `PyTorch operations `__ automatically run in FP16, saving memory and improving throughput on the supported accelerators. - - -.. note:: - - When using TPUs, setting ``precision=16`` will enable bfloat16, the only supported half precision type on TPUs. - -.. testcode:: - :skipif: not torch.cuda.is_available() - - Trainer(accelerator="gpu", devices=1, precision=16) - - -PyTorch Native --------------- - -PyTorch 1.6 release introduced mixed precision functionality into their core as the AMP package, `torch.cuda.amp `__. It is more flexible and intuitive compared to `NVIDIA APEX `__. -Since computation happens in FP16, there is a chance of numerical instability during training. This is handled internally by a dynamic grad scaler which skips invalid steps and adjusts the scaler to ensure subsequent steps fall within a finite range. For more information `see the autocast docs `__. -Lightning uses native amp by default with ``precision=16|"bf16"``. You can also set it using: - -.. testcode:: - - Trainer(precision=16, amp_backend="native") - - -NVIDIA APEX ------------ - -.. warning:: - - We strongly recommend using the above native mixed precision rather than NVIDIA APEX unless you require more refined control. - -`NVIDIA APEX `__ offers additional flexibility in setting mixed precision. This can be useful when trying out different precision configurations, such as keeping most of your weights in FP16 and running computation in FP16. - -.. testcode:: - :skipif: not _APEX_AVAILABLE or not torch.cuda.is_available() - - Trainer(accelerator="gpu", devices=1, amp_backend="apex", precision=16) - -Set the `NVIDIA optimization level `__ via the trainer. - -.. testcode:: - :skipif: not _APEX_AVAILABLE or not torch.cuda.is_available() - - Trainer(accelerator="gpu", devices=1, amp_backend="apex", amp_level="O2", precision=16) - ----- - -************************ -BFloat16 Mixed Precision -************************ - -.. warning:: - - BFloat16 requires PyTorch 1.10 or later and is only supported with PyTorch Native AMP. - - BFloat16 is also experimental and may not provide significant speedups or memory improvements, offering better numerical stability. - - Do note for GPUs, the most significant benefits require `Ampere `__ based GPUs, such as A100s or 3090s. - -BFloat16 Mixed precision is similar to FP16 mixed precision, however, it maintains more of the "dynamic range" that FP32 offers. This means it is able to improve numerical stability than FP16 mixed precision. For more information, see `this TPU performance blogpost `__. - -Under the hood, we use `torch.autocast `__ with the dtype set to ``bfloat16``, with no gradient scaling. - -.. testcode:: - :skipif: not _TORCH_GREATER_EQUAL_1_10 or not torch.cuda.is_available() - - Trainer(accelerator="gpu", devices=1, precision="bf16") - -It is also possible to use BFloat16 mixed precision on the CPU, relying on MKLDNN under the hood. - -.. testcode:: - :skipif: not _TORCH_GREATER_EQUAL_1_10 - - Trainer(precision="bf16") - ----- - -*************** -8-bit Optimizer -*************** - -It is possible to further reduce the precision using third-party libraries like `bitsandbytes `_. Although, -Lightning doesn't support it out of the box yet but you can still use it by configuring it in your LightningModule and setting ``Trainer(precision=32)``. diff --git a/source/common/progress_bar.rst b/source/common/progress_bar.rst deleted file mode 100644 index d00c716..0000000 --- a/source/common/progress_bar.rst +++ /dev/null @@ -1,138 +0,0 @@ -.. testsetup:: * - - from pytorch_lightning.trainer.trainer import Trainer - -.. _progress_bar: - - -Customize the progress bar -========================== - -Lightning supports two different types of progress bars (`tqdm `_ and `rich `_). :class:`~pytorch_lightning.callbacks.TQDMProgressBar` is used by default, -but you can override it by passing a custom :class:`~pytorch_lightning.callbacks.TQDMProgressBar` or :class:`~pytorch_lightning.callbacks.RichProgressBar` to the ``callbacks`` argument of the :class:`~pytorch_lightning.trainer.trainer.Trainer`. - -You could also use the :class:`~pytorch_lightning.callbacks.ProgressBarBase` class to implement your own progress bar. - -------------- - -TQDMProgressBar ---------------- - -The :class:`~pytorch_lightning.callbacks.TQDMProgressBar` uses the `tqdm `_ library internally and is the default progress bar used by Lightning. -It prints to ``stdout`` and shows up to four different bars: - -- **sanity check progress:** the progress during the sanity check run -- **main progress:** shows training + validation progress combined. It also accounts for multiple validation runs during training when :paramref:`~pytorch_lightning.trainer.trainer.Trainer.val_check_interval` is used. -- **validation progress:** only visible during validation; shows total progress over all validation datasets. -- **test progress:** only active when testing; shows total progress over all test datasets. - -For infinite datasets, the progress bar never ends. - -You can update ``refresh_rate`` (rate (number of batches) at which the progress bar get updated) for :class:`~pytorch_lightning.callbacks.TQDMProgressBar` by: - -.. code-block:: python - - from pytorch_lightning.callbacks import TQDMProgressBar - - trainer = Trainer(callbacks=[TQDMProgressBar(refresh_rate=10)]) - -If you want to customize the default :class:`~pytorch_lightning.callbacks.TQDMProgressBar` used by Lightning, you can override -specific methods of the callback class and pass your custom implementation to the :class:`~pytorch_lightning.trainer.trainer.Trainer`. - -.. code-block:: python - - class LitProgressBar(TQDMProgressBar): - def init_validation_tqdm(self): - bar = super().init_validation_tqdm() - bar.set_description("running validation...") - return bar - - - trainer = Trainer(callbacks=[LitProgressBar()]) - -.. seealso:: - - :class:`~pytorch_lightning.callbacks.TQDMProgressBar` docs. - - `tqdm library `__ - ----------------- - -RichProgressBar ---------------- - -`Rich `_ is a Python library for rich text and beautiful formatting in the terminal. -To use the :class:`~pytorch_lightning.callbacks.RichProgressBar` as your progress bar, first install the package: - -.. code-block:: bash - - pip install rich - -Then configure the callback and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.callbacks import RichProgressBar - - trainer = Trainer(callbacks=[RichProgressBar()]) - -Customize the theme for your :class:`~pytorch_lightning.callbacks.RichProgressBar` like this: - -.. code-block:: python - - from pytorch_lightning.callbacks import RichProgressBar - from pytorch_lightning.callbacks.progress.rich_progress import RichProgressBarTheme - - # create your own theme! - progress_bar = RichProgressBar( - theme=RichProgressBarTheme( - description="green_yellow", - progress_bar="green1", - progress_bar_finished="green1", - progress_bar_pulse="#6206E0", - batch_progress="green_yellow", - time="grey82", - processing_speed="grey82", - metrics="grey82", - ) - ) - - trainer = Trainer(callbacks=progress_bar) - -You can customize the components used within :class:`~pytorch_lightning.callbacks.RichProgressBar` with ease by overriding the -:func:`~pytorch_lightning.callbacks.RichProgressBar.configure_columns` method. - -.. code-block:: python - - from rich.progress import TextColumn - - custom_column = TextColumn("[progress.description]Custom Rich Progress Bar!") - - - class CustomRichProgressBar(RichProgressBar): - def configure_columns(self, trainer): - return [custom_column] - - - progress_bar = CustomRichProgressBar() - -If you wish for a new progress bar to be displayed at the end of every epoch, you should enable -:paramref:`RichProgressBar.leave ` by passing ``True`` - -.. code-block:: python - - from pytorch_lightning.callbacks import RichProgressBar - - trainer = Trainer(callbacks=[RichProgressBar(leave=True)]) - -.. seealso:: - - :class:`~pytorch_lightning.callbacks.RichProgressBar` docs. - - :class:`~pytorch_lightning.callbacks.RichModelSummary` docs to customize the model summary table. - - `Rich library `__. - - -.. note:: - - Progress bar is automatically enabled with the Trainer, and to disable it, one should do this: - - .. code-block:: python - - trainer = Trainer(enable_progress_bar=False) diff --git a/source/common/trainer.rst b/source/common/trainer.rst deleted file mode 100644 index 848ac8a..0000000 --- a/source/common/trainer.rst +++ /dev/null @@ -1,1832 +0,0 @@ -.. role:: hidden - :class: hidden-section - -.. testsetup:: * - - import os - from pytorch_lightning.trainer.trainer import Trainer - from pytorch_lightning.core.lightning import LightningModule - from pytorch_lightning.utilities.seed import seed_everything - -.. _trainer: - -Trainer -======= - -Once you've organized your PyTorch code into a LightningModule, -the Trainer automates everything else. - -.. raw:: html - - - -| - -This abstraction achieves the following: - -1. You maintain control over all aspects via PyTorch code without an added abstraction. - -2. The trainer uses best practices embedded by contributors and users - from top AI labs such as Facebook AI Research, NYU, MIT, Stanford, etc... - -3. The trainer allows overriding any key part that you don't want automated. - -| - ------------ - -Basic use ---------- - -This is the basic use of the trainer: - -.. code-block:: python - - model = MyLightningModule() - - trainer = Trainer() - trainer.fit(model, train_dataloader, val_dataloader) - --------- - -Under the hood --------------- -Under the hood, the Lightning Trainer handles the training loop details for you, some examples include: - -- Automatically enabling/disabling grads -- Running the training, validation and test dataloaders -- Calling the Callbacks at the appropriate times -- Putting batches and computations on the correct devices - -Here's the pseudocode for what the trainer does under the hood (showing the train loop only) - -.. code-block:: python - - # put model in train mode - model.train() - torch.set_grad_enabled(True) - - losses = [] - for batch in train_dataloader: - # calls hooks like this one - on_train_batch_start() - - # train step - loss = training_step(batch) - - # clear gradients - optimizer.zero_grad() - - # backward - loss.backward() - - # update parameters - optimizer.step() - - losses.append(loss) - - --------- - -Trainer in Python scripts -------------------------- -In Python scripts, it's recommended you use a main function to call the Trainer. - -.. code-block:: python - - from argparse import ArgumentParser - - - def main(hparams): - model = LightningModule() - trainer = Trainer(accelerator=hparams.accelerator, devices=hparams.devices) - trainer.fit(model) - - - if __name__ == "__main__": - parser = ArgumentParser() - parser.add_argument("--accelerator", default=None) - parser.add_argument("--devices", default=None) - args = parser.parse_args() - - main(args) - -So you can run it like so: - -.. code-block:: bash - - python main.py --accelerator 'gpu' --devices 2 - -.. note:: - - Pro-tip: You don't need to define all flags manually. Lightning can add them automatically - -.. code-block:: python - - from argparse import ArgumentParser - - - def main(args): - model = LightningModule() - trainer = Trainer.from_argparse_args(args) - trainer.fit(model) - - - if __name__ == "__main__": - parser = ArgumentParser() - parser = Trainer.add_argparse_args(parser) - args = parser.parse_args() - - main(args) - -So you can run it like so: - -.. code-block:: bash - - python main.py --accelerator 'gpu' --devices 2 --max_steps 10 --limit_train_batches 10 --any_trainer_arg x - -.. note:: - If you want to stop a training run early, you can press "Ctrl + C" on your keyboard. - The trainer will catch the ``KeyboardInterrupt`` and attempt a graceful shutdown, including - running accelerator callback ``on_train_end`` to clean up memory. The trainer object will also set - an attribute ``interrupted`` to ``True`` in such cases. If you have a callback which shuts down compute - resources, for example, you can conditionally run the shutdown logic for only uninterrupted runs. - ------------- - -Validation ----------- -You can perform an evaluation epoch over the validation set, outside of the training loop, -using :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate`. This might be -useful if you want to collect new metrics from a model right at its initialization -or after it has already been trained. - -.. code-block:: python - - trainer.validate(dataloaders=val_dataloaders) - ------------- - -Testing -------- -Once you're done training, feel free to run the test set! -(Only right before publishing your paper or pushing to production) - -.. code-block:: python - - trainer.test(dataloaders=test_dataloaders) - ------------- - -Reproducibility ---------------- - -To ensure full reproducibility from run to run you need to set seeds for pseudo-random generators, -and set ``deterministic`` flag in ``Trainer``. - -Example:: - - from pytorch_lightning import Trainer, seed_everything - - seed_everything(42, workers=True) - # sets seeds for numpy, torch and python.random. - model = Model() - trainer = Trainer(deterministic=True) - - -By setting ``workers=True`` in :func:`~pytorch_lightning.utilities.seed.seed_everything`, Lightning derives -unique seeds across all dataloader workers and processes for :mod:`torch`, :mod:`numpy` and stdlib -:mod:`random` number generators. When turned on, it ensures that e.g. data augmentations are not repeated across workers. - -------- - -.. _trainer_flags: - -Trainer flags -------------- - -accelerator -^^^^^^^^^^^ - -Supports passing different accelerator types (``"cpu", "gpu", "tpu", "ipu", "auto"``) -as well as custom accelerator instances. - -.. code-block:: python - - # CPU accelerator - trainer = Trainer(accelerator="cpu") - - # Training with GPU Accelerator using 2 GPUs - trainer = Trainer(devices=2, accelerator="gpu") - - # Training with TPU Accelerator using 8 tpu cores - trainer = Trainer(devices=8, accelerator="tpu") - - # Training with GPU Accelerator using the DistributedDataParallel strategy - trainer = Trainer(devices=4, accelerator="gpu", strategy="ddp") - -.. note:: The ``"auto"`` option recognizes the machine you are on, and selects the respective ``Accelerator``. - -.. code-block:: python - - # If your machine has GPUs, it will use the GPU Accelerator for training - trainer = Trainer(devices=2, accelerator="auto") - -You can also modify hardware behavior by subclassing an existing accelerator to adjust for your needs. - -Example:: - - class MyOwnAcc(CPUAccelerator): - ... - - Trainer(accelerator=MyOwnAcc()) - -.. note:: - - If the ``devices`` flag is not defined, it will assume ``devices`` to be ``"auto"`` and fetch the ``auto_device_count`` - from the accelerator. - - .. code-block:: python - - # This is part of the built-in `GPUAccelerator` - class GPUAccelerator(Accelerator): - """Accelerator for GPU devices.""" - - @staticmethod - def auto_device_count() -> int: - """Get the devices when set to auto.""" - return torch.cuda.device_count() - - - # Training with GPU Accelerator using total number of gpus available on the system - Trainer(accelerator="gpu") - -.. warning:: Passing training strategies (e.g., ``"ddp"``) to ``accelerator`` has been deprecated in v1.5.0 - and will be removed in v1.7.0. Please use the ``strategy`` argument instead. - -accumulate_grad_batches -^^^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Accumulates grads every k batches or as set up in the dict. -Trainer also calls ``optimizer.step()`` for the last indivisible step number. - -.. testcode:: - - # default used by the Trainer (no accumulation) - trainer = Trainer(accumulate_grad_batches=1) - -Example:: - - # accumulate every 4 batches (effective batch size is batch*4) - trainer = Trainer(accumulate_grad_batches=4) - - # no accumulation for epochs 1-4. accumulate 3 for epochs 5-10. accumulate 20 after that - trainer = Trainer(accumulate_grad_batches={5: 3, 10: 20}) - -amp_backend -^^^^^^^^^^^ - -.. raw:: html - - - -| - -Use PyTorch AMP ('native'), or NVIDIA apex ('apex'). - -.. testcode:: - - # using PyTorch built-in AMP, default used by the Trainer - trainer = Trainer(amp_backend="native") - - # using NVIDIA Apex - trainer = Trainer(amp_backend="apex") - -amp_level -^^^^^^^^^ - -.. raw:: html - - - -| - -The optimization level to use (O1, O2, etc...) -for 16-bit GPU precision (using NVIDIA apex under the hood). - -Check `NVIDIA apex docs `_ for level - -Example:: - - # default used by the Trainer - trainer = Trainer(amp_level='O2') - -auto_scale_batch_size -^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Automatically tries to find the largest batch size that fits into memory, -before any training. - -.. code-block:: python - - # default used by the Trainer (no scaling of batch size) - trainer = Trainer(auto_scale_batch_size=None) - - # run batch size scaling, result overrides hparams.batch_size - trainer = Trainer(auto_scale_batch_size="binsearch") - - # call tune to find the batch size - trainer.tune(model) - -auto_select_gpus -^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -If enabled and ``devices`` is an integer, pick available GPUs automatically. -This is especially useful when GPUs are configured to be in "exclusive mode", -such that only one process at a time can access them. - -Example:: - - # no auto selection (picks first 2 GPUs on system, may fail if other process is occupying) - trainer = Trainer(accelerator="gpu", devices=2, auto_select_gpus=False) - - # enable auto selection (will find two available GPUs on system) - trainer = Trainer(accelerator="gpu", devices=2, auto_select_gpus=True) - - # specifies all GPUs regardless of its availability - Trainer(accelerator="gpu", devices=-1, auto_select_gpus=False) - - # specifies all available GPUs (if only one GPU is not occupied, uses one gpu) - Trainer(accelerator="gpu", devices=-1, auto_select_gpus=True) - -auto_lr_find -^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Runs a learning rate finder algorithm (see this `paper `_) -when calling trainer.tune(), to find optimal initial learning rate. - -.. code-block:: python - - # default used by the Trainer (no learning rate finder) - trainer = Trainer(auto_lr_find=False) - -Example:: - - # run learning rate finder, results override hparams.learning_rate - trainer = Trainer(auto_lr_find=True) - - # call tune to find the lr - trainer.tune(model) - -Example:: - - # run learning rate finder, results override hparams.my_lr_arg - trainer = Trainer(auto_lr_find='my_lr_arg') - - # call tune to find the lr - trainer.tune(model) - -.. note:: - See the :ref:`learning rate finder guide `. - -benchmark -^^^^^^^^^ - -.. raw:: html - - - -| - -Defaults to ``True`` if :paramref:`~pytorch_lightning.trainer.Trainer.deterministic` is not set. -This flag sets the ``torch.backends.cudnn.benchmark`` flag. You can read more about its impact -`here `__ - -This is likely to increase the speed of your system if your input sizes don't change. However, if they do, then it -might make your system slower. The CUDNN auto-tuner will try to find the best algorithm for the hardware when a new -input size is encountered. Read more about it `here `__. - -Example:: - - # defaults to True if not deterministic (which is False by default) - trainer = Trainer() - - # you can overwrite the value - trainer = Trainer(benchmark=False) - -deterministic -^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -This flag sets the ``torch.backends.cudnn.deterministic`` flag. -Might make your system slower, but ensures reproducibility. -Also sets ``$HOROVOD_FUSION_THRESHOLD=0``. - -For more info check `PyTorch docs `_. - -Example:: - - # default used by the Trainer - trainer = Trainer(deterministic=False) - -callbacks -^^^^^^^^^ - -.. raw:: html - - - -| - -Add a list of :class:`~pytorch_lightning.callbacks.Callback`. Callbacks run sequentially in the order defined here -with the exception of :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` callbacks which run -after all others to ensure all states are saved to the checkpoints. - -.. code-block:: python - - # a list of callbacks - callbacks = [PrintCallback()] - trainer = Trainer(callbacks=callbacks) - -Example:: - - from pytorch_lightning.callbacks import Callback - - class PrintCallback(Callback): - def on_train_start(self, trainer, pl_module): - print("Training is started!") - def on_train_end(self, trainer, pl_module): - print("Training is done.") - - -Model-specific callbacks can also be added inside the ``LightningModule`` through -:meth:`~pytorch_lightning.core.lightning.LightningModule.configure_callbacks`. -Callbacks returned in this hook will extend the list initially given to the ``Trainer`` argument, and replace -the trainer callbacks should there be two or more of the same type. -:class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` callbacks always run last. - - -check_val_every_n_epoch -^^^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Check val every n train epochs. - -Example:: - - # default used by the Trainer - trainer = Trainer(check_val_every_n_epoch=1) - - # run val loop every 10 training epochs - trainer = Trainer(check_val_every_n_epoch=10) - -checkpoint_callback -^^^^^^^^^^^^^^^^^^^ - -.. warning:: `checkpoint_callback` has been deprecated in v1.5 and will be removed in v1.7. - To disable checkpointing, pass ``enable_checkpointing = False`` to the Trainer instead. - - -default_root_dir -^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Default path for logs and weights when no logger or -:class:`pytorch_lightning.callbacks.ModelCheckpoint` callback passed. On -certain clusters you might want to separate where logs and checkpoints are -stored. If you don't then use this argument for convenience. Paths can be local -paths or remote paths such as `s3://bucket/path` or 'hdfs://path/'. Credentials -will need to be set up to use remote filepaths. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(default_root_dir=os.getcwd()) - -devices -^^^^^^^ - -Number of devices to train on (``int``), which devices to train on (``list`` or ``str``), or ``"auto"``. -It will be mapped to either ``gpus``, ``tpu_cores``, ``num_processes`` or ``ipus``, -based on the accelerator type (``"cpu", "gpu", "tpu", "ipu", "auto"``). - -.. code-block:: python - - # Training with CPU Accelerator using 2 processes - trainer = Trainer(devices=2, accelerator="cpu") - - # Training with GPU Accelerator using GPUs 1 and 3 - trainer = Trainer(devices=[1, 3], accelerator="gpu") - - # Training with TPU Accelerator using 8 tpu cores - trainer = Trainer(devices=8, accelerator="tpu") - -.. tip:: The ``"auto"`` option recognizes the devices to train on, depending on the ``Accelerator`` being used. - -.. code-block:: python - - # If your machine has GPUs, it will use all the available GPUs for training - trainer = Trainer(devices="auto", accelerator="auto") - - # Training with CPU Accelerator using 1 process - trainer = Trainer(devices="auto", accelerator="cpu") - - # Training with TPU Accelerator using 8 tpu cores - trainer = Trainer(devices="auto", accelerator="tpu") - - # Training with IPU Accelerator using 4 ipus - trainer = Trainer(devices="auto", accelerator="ipu") - -.. note:: - - If the ``devices`` flag is not defined, it will assume ``devices`` to be ``"auto"`` and fetch the ``auto_device_count`` - from the accelerator. - - .. code-block:: python - - # This is part of the built-in `GPUAccelerator` - class GPUAccelerator(Accelerator): - """Accelerator for GPU devices.""" - - @staticmethod - def auto_device_count() -> int: - """Get the devices when set to auto.""" - return torch.cuda.device_count() - - - # Training with GPU Accelerator using total number of gpus available on the system - Trainer(accelerator="gpu") - -enable_checkpointing -^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -By default Lightning saves a checkpoint for you in your current working directory, with the state of your last training epoch, -Checkpoints capture the exact value of all parameters used by a model. -To disable automatic checkpointing, set this to `False`. - -.. code-block:: python - - # default used by Trainer, saves the most recent model to a single checkpoint after each epoch - trainer = Trainer(enable_checkpointing=True) - - # turn off automatic checkpointing - trainer = Trainer(enable_checkpointing=False) - - -You can override the default behavior by initializing the :class:`~pytorch_lightning.callbacks.ModelCheckpoint` -callback, and adding it to the :paramref:`~pytorch_lightning.trainer.trainer.Trainer.callbacks` list. -See :doc:`Saving and Loading Checkpoints <../common/checkpointing>` for how to customize checkpointing. - -.. testcode:: - - from pytorch_lightning.callbacks import ModelCheckpoint - - # Init ModelCheckpoint callback, monitoring 'val_loss' - checkpoint_callback = ModelCheckpoint(monitor="val_loss") - - # Add your callback to the callbacks list - trainer = Trainer(callbacks=[checkpoint_callback]) - -fast_dev_run -^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Runs n if set to ``n`` (int) else 1 if set to ``True`` batch(es) of train, val and test -to find any bugs (ie: a sort of unit test). - -Under the hood the pseudocode looks like this when running *fast_dev_run* with a single batch: - -.. code-block:: python - - # loading - __init__() - prepare_data - - # test training step - training_batch = next(train_dataloader) - training_step(training_batch) - - # test val step - val_batch = next(val_dataloader) - out = validation_step(val_batch) - validation_epoch_end([out]) - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(fast_dev_run=False) - - # runs 1 train, val, test batch and program ends - trainer = Trainer(fast_dev_run=True) - - # runs 7 train, val, test batches and program ends - trainer = Trainer(fast_dev_run=7) - -.. note:: - - This argument is a bit different from ``limit_train/val/test_batches``. Setting this argument will - disable tuner, checkpoint callbacks, early stopping callbacks, loggers and logger callbacks like - ``LearningRateLogger`` and runs for only 1 epoch. This must be used only for debugging purposes. - ``limit_train/val/test_batches`` only limits the number of batches and won't disable anything. - -flush_logs_every_n_steps -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: ``flush_logs_every_n_steps`` has been deprecated in v1.5 and will be removed in v1.7. - Please configure flushing directly in the logger instead. - -.. raw:: html - - - -| - -Writes logs to disk this often. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(flush_logs_every_n_steps=100) - -See Also: - - :doc:`logging <../extensions/logging>` - -.. _gpus: - -gpus -^^^^ - -.. warning:: ``gpus=x`` has been deprecated in v1.7 and will be removed in v2.0. - Please use ``accelerator='gpu'`` and ``devices=x`` instead. - -.. raw:: html - - - -| - -- Number of GPUs to train on (int) -- or which GPUs to train on (list) -- can handle strings - -.. testcode:: - - # default used by the Trainer (ie: train on CPU) - trainer = Trainer(gpus=None) - - # equivalent - trainer = Trainer(gpus=0) - -Example:: - - # int: train on 2 gpus - trainer = Trainer(gpus=2) - - # list: train on GPUs 1, 4 (by bus ordering) - trainer = Trainer(gpus=[1, 4]) - trainer = Trainer(gpus='1, 4') # equivalent - - # -1: train on all gpus - trainer = Trainer(gpus=-1) - trainer = Trainer(gpus='-1') # equivalent - - # combine with num_nodes to train on multiple GPUs across nodes - # uses 8 gpus in total - trainer = Trainer(gpus=2, num_nodes=4) - - # train only on GPUs 1 and 4 across nodes - trainer = Trainer(gpus=[1, 4], num_nodes=4) - -See Also: - - :ref:`Multi GPU Training ` - -gradient_clip_val -^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Gradient clipping value - -- 0 means don't clip. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(gradient_clip_val=0.0) - -limit_train_batches -^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -How much of training dataset to check. -Useful when debugging or testing something that happens at the end of an epoch. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(limit_train_batches=1.0) - -Example:: - - # default used by the Trainer - trainer = Trainer(limit_train_batches=1.0) - - # run through only 25% of the training set each epoch - trainer = Trainer(limit_train_batches=0.25) - - # run through only 10 batches of the training set each epoch - trainer = Trainer(limit_train_batches=10) - -limit_test_batches -^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -How much of test dataset to check. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(limit_test_batches=1.0) - - # run through only 25% of the test set each epoch - trainer = Trainer(limit_test_batches=0.25) - - # run for only 10 batches - trainer = Trainer(limit_test_batches=10) - -In the case of multiple test dataloaders, the limit applies to each dataloader individually. - -limit_val_batches -^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -How much of validation dataset to check. -Useful when debugging or testing something that happens at the end of an epoch. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(limit_val_batches=1.0) - - # run through only 25% of the validation set each epoch - trainer = Trainer(limit_val_batches=0.25) - - # run for only 10 batches - trainer = Trainer(limit_val_batches=10) - -In the case of multiple validation dataloaders, the limit applies to each dataloader individually. - -log_every_n_steps -^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - - -How often to add logging rows (does not write to disk) - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(log_every_n_steps=50) - -See Also: - - :doc:`logging <../extensions/logging>` - -logger -^^^^^^ - -.. raw:: html - - - -| - -:doc:`Logger <../visualize/loggers>` (or iterable collection of loggers) for experiment tracking. A ``True`` value uses the default ``TensorBoardLogger`` shown below. ``False`` will disable logging. - -.. testcode:: - - from pytorch_lightning.loggers import TensorBoardLogger - - # default logger used by trainer - logger = TensorBoardLogger(save_dir=os.getcwd(), version=1, name="lightning_logs") - Trainer(logger=logger) - -max_epochs -^^^^^^^^^^ - -.. raw:: html - - - -| - -Stop training once this number of epochs is reached - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(max_epochs=1000) - -If both ``max_epochs`` and ``max_steps`` aren't specified, ``max_epochs`` will default to ``1000``. -To enable infinite training, set ``max_epochs = -1``. - -min_epochs -^^^^^^^^^^ - -.. raw:: html - - - -| - -Force training for at least these many epochs - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(min_epochs=1) - -max_steps -^^^^^^^^^ - -.. raw:: html - - - -| - -Stop training after this number of :ref:`global steps `. -Training will stop if max_steps or max_epochs have reached (earliest). - -.. testcode:: - - # Default (disabled) - trainer = Trainer(max_steps=None) - - # Stop after 100 steps - trainer = Trainer(max_steps=100) - -If ``max_steps`` is not specified, ``max_epochs`` will be used instead (and ``max_epochs`` defaults to -``1000`` if ``max_epochs`` is not specified). To disable this default, set ``max_steps = -1``. - -min_steps -^^^^^^^^^ - -.. raw:: html - - - -| - -Force training for at least this number of :ref:`global steps `. -Trainer will train model for at least min_steps or min_epochs (latest). - -.. testcode:: - - # Default (disabled) - trainer = Trainer(min_steps=None) - - # Run at least for 100 steps (disable min_epochs) - trainer = Trainer(min_steps=100, min_epochs=0) - -max_time -^^^^^^^^ - -Set the maximum amount of time for training. Training will get interrupted mid-epoch. -For customizable options use the :class:`~pytorch_lightning.callbacks.timer.Timer` callback. - -.. testcode:: - - # Default (disabled) - trainer = Trainer(max_time=None) - - # Stop after 12 hours of training or when reaching 10 epochs (string) - trainer = Trainer(max_time="00:12:00:00", max_epochs=10) - - # Stop after 1 day and 5 hours (dict) - trainer = Trainer(max_time={"days": 1, "hours": 5}) - -In case ``max_time`` is used together with ``min_steps`` or ``min_epochs``, the ``min_*`` requirement -always has precedence. - -num_nodes -^^^^^^^^^ - -.. raw:: html - - - -| - -Number of GPU nodes for distributed training. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(num_nodes=1) - - # to train on 8 nodes - trainer = Trainer(num_nodes=8) - -num_processes -^^^^^^^^^^^^^ - -.. warning:: ``num_processes=x`` has been deprecated in v1.7 and will be removed in v2.0. - Please use ``accelerator='cpu'`` and ``devices=x`` instead. - -.. raw:: html - - - -| - -Number of processes to train with. Automatically set to the number of GPUs -when using ``strategy="ddp"``. Set to a number greater than 1 when -using ``accelerator="cpu"`` and ``strategy="ddp"`` to mimic distributed training on a -machine without GPUs. This is useful for debugging, but **will not** provide -any speedup, since single-process Torch already makes efficient use of multiple -CPUs. While it would typically spawns subprocesses for training, setting -``num_nodes > 1`` and keeping ``num_processes = 1`` runs training in the main -process. - -.. testcode:: - - # Simulate DDP for debugging on your GPU-less laptop - trainer = Trainer(accelerator="cpu", strategy="ddp", num_processes=2) - -num_sanity_val_steps -^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Sanity check runs n batches of val before starting the training routine. -This catches any bugs in your validation without having to wait for the first validation check. -The Trainer uses 2 steps by default. Turn it off or modify it here. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(num_sanity_val_steps=2) - - # turn it off - trainer = Trainer(num_sanity_val_steps=0) - - # check all validation data - trainer = Trainer(num_sanity_val_steps=-1) - - -This option will reset the validation dataloader unless ``num_sanity_val_steps=0``. - -overfit_batches -^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Uses this much data of the training & validation set. -If the training & validation dataloaders have ``shuffle=True``, Lightning will automatically disable it. - -Useful for quickly debugging or trying to overfit on purpose. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(overfit_batches=0.0) - - # use only 1% of the train & val set - trainer = Trainer(overfit_batches=0.01) - - # overfit on 10 of the same batches - trainer = Trainer(overfit_batches=10) - -plugins -^^^^^^^ - -.. raw:: html - - - -| - -:ref:`Plugins` allow you to connect arbitrary backends, precision libraries, clusters etc. For example: - -- :ref:`Checkpoint IO ` -- `TorchElastic `_ -- :ref:`Precision Plugins ` - -To define your own behavior, subclass the relevant class and pass it in. Here's an example linking up your own -:class:`~pytorch_lightning.plugins.environments.ClusterEnvironment`. - -.. code-block:: python - - from pytorch_lightning.plugins.environments import ClusterEnvironment - - - class MyCluster(ClusterEnvironment): - def main_address(self): - return your_main_address - - def main_port(self): - return your_main_port - - def world_size(self): - return the_world_size - - - trainer = Trainer(plugins=[MyCluster()], ...) - -precision -^^^^^^^^^ - -.. raw:: html - - - -| - -Lightning supports either double (64), float (32), bfloat16 (bf16), or half (16) precision training. - -Half precision, or mixed precision, is the combined use of 32 and 16 bit floating points to reduce memory footprint during model training. This can result in improved performance, achieving +3X speedups on modern GPUs. - -.. testcode:: - :skipif: not torch.cuda.is_available() - - # default used by the Trainer - trainer = Trainer(precision=32) - - # 16-bit precision - trainer = Trainer(precision=16, accelerator="gpu", devices=1) # works only on CUDA - - # bfloat16 precision - trainer = Trainer(precision="bf16") - - # 64-bit precision - trainer = Trainer(precision=64) - - -.. note:: When running on TPUs, torch.bfloat16 will be used but tensor printing will still show torch.float32. - -.. admonition:: If you are interested in using Apex 16-bit training: - :class: dropdown - - NVIDIA Apex and DDP have instability problems. We recommend using the native AMP for 16-bit precision with multiple GPUs. - To use Apex 16-bit training: - - 1. `Install apex. `__ - - 2. Set the ``precision`` trainer flag to 16. You can customize the `Apex optimization level `_ by setting the `amp_level` flag. - - .. testcode:: - :skipif: not _APEX_AVAILABLE or not torch.cuda.is_available() - - # turn on 16-bit - trainer = Trainer(amp_backend="apex", amp_level="O2", precision=16, accelerator="gpu", devices=1) - - -process_position -^^^^^^^^^^^^^^^^ - -.. warning:: ``process_position`` has been deprecated in v1.5 and will be removed in v1.7. - Please pass :class:`~pytorch_lightning.callbacks.progress.TQDMProgressBar` with ``process_position`` - directly to the Trainer's ``callbacks`` argument instead. - -.. raw:: html - - - -| - -Orders the progress bar. Useful when running multiple trainers on the same node. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(process_position=0) - -.. note:: This argument is ignored if a custom callback is passed to :paramref:`~Trainer.callbacks`. - -profiler -^^^^^^^^ - -.. raw:: html - - - -| - -To profile individual steps during training and assist in identifying bottlenecks. - -See the :doc:`profiler documentation <../tuning/profiler>`. for more details. - -.. testcode:: - - from pytorch_lightning.profiler import SimpleProfiler, AdvancedProfiler - - # default used by the Trainer - trainer = Trainer(profiler=None) - - # to profile standard training events, equivalent to `profiler=SimpleProfiler()` - trainer = Trainer(profiler="simple") - - # advanced profiler for function-level stats, equivalent to `profiler=AdvancedProfiler()` - trainer = Trainer(profiler="advanced") - -enable_progress_bar -^^^^^^^^^^^^^^^^^^^ - -Whether to enable or disable the progress bar. Defaults to True. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(enable_progress_bar=True) - - # disable progress bar - trainer = Trainer(enable_progress_bar=False) - -reload_dataloaders_every_n_epochs -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Set to a positive integer to reload dataloaders every n epochs. - -.. code-block:: python - - # if 0 (default) - train_loader = model.train_dataloader() - for epoch in epochs: - for batch in train_loader: - ... - - # if a positive integer - for epoch in epochs: - if not epoch % reload_dataloaders_every_n_epochs: - train_loader = model.train_dataloader() - for batch in train_loader: - ... - -.. _replace-sampler-ddp: - -replace_sampler_ddp -^^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Enables auto adding of :class:`~torch.utils.data.distributed.DistributedSampler`. In PyTorch, you must use it in -distributed settings such as TPUs or multi-node. The sampler makes sure each GPU sees the appropriate part of your data. -By default it will add ``shuffle=True`` for train sampler and ``shuffle=False`` for val/test sampler. -If you want to customize it, you can set ``replace_sampler_ddp=False`` and add your own distributed sampler. -If ``replace_sampler_ddp=True`` and a distributed sampler was already added, -Lightning will not replace the existing one. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(replace_sampler_ddp=True) - -By setting to False, you have to add your own distributed sampler: - -.. code-block:: python - - # in your LightningModule or LightningDataModule - def train_dataloader(self): - # default used by the Trainer - sampler = torch.utils.data.distributed.DistributedSampler(dataset, shuffle=True) - dataloader = DataLoader(dataset, batch_size=32, sampler=sampler) - return dataloader - -.. note:: For iterable datasets, we don't do this automatically. - -resume_from_checkpoint -^^^^^^^^^^^^^^^^^^^^^^ - -.. warning:: ``resume_from_checkpoint`` is deprecated in v1.5 and will be removed in v2.0. - Please pass ``trainer.fit(ckpt_path="some/path/to/my_checkpoint.ckpt")`` instead. - - -.. raw:: html - - - -| - -To resume training from a specific checkpoint pass in the path here. If resuming from a mid-epoch -checkpoint, training will start from the beginning of the next epoch. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(resume_from_checkpoint=None) - - # resume from a specific checkpoint - trainer = Trainer(resume_from_checkpoint="some/path/to/my_checkpoint.ckpt") - -strategy -^^^^^^^^ - -Supports passing different training strategies with aliases (ddp, ddp_spawn, etc) as well as custom strategies. - -.. code-block:: python - - # Training with the DistributedDataParallel strategy on 4 GPUs - trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4) - - # Training with the DDP Spawn strategy using 4 cpu processes - trainer = Trainer(strategy="ddp_spawn", accelerator="cpu", devices=4) - -.. note:: Additionally, you can pass your custom strategy to the ``strategy`` argument. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy - - - class CustomDDPStrategy(DDPStrategy): - def configure_ddp(self): - self._model = MyCustomDistributedDataParallel( - self.model, - device_ids=..., - ) - - - trainer = Trainer(strategy=CustomDDPStrategy(), accelerator="gpu", devices=2) - -See Also: - - :ref:`Multi GPU Training `. - - :doc:`Model Parallel GPU training guide <../advanced/model_parallel>`. - - :doc:`TPU training guide <../accelerators/tpu>`. - -sync_batchnorm -^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -Enable synchronization between batchnorm layers across all GPUs. - -.. testcode:: - - trainer = Trainer(sync_batchnorm=True) - -track_grad_norm -^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -- no tracking (-1) -- Otherwise tracks that norm (2 for 2-norm) - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(track_grad_norm=-1) - - # track the 2-norm - trainer = Trainer(track_grad_norm=2) - -.. _tpu_cores: - -tpu_cores -^^^^^^^^^ - -.. warning:: ``tpu_cores=x`` has been deprecated in v1.7 and will be removed in v2.0. - Please use ``accelerator='tpu'`` and ``devices=x`` instead. - -.. raw:: html - - - -| - -- How many TPU cores to train on (1 or 8). -- Which TPU core to train on [1-8] - -A single TPU v2 or v3 has 8 cores. A TPU pod has -up to 2048 cores. A slice of a POD means you get as many cores -as you request. - -Your effective batch size is batch_size * total tpu cores. - -This parameter can be either 1 or 8. - -Example:: - - # your_trainer_file.py - - # default used by the Trainer (ie: train on CPU) - trainer = Trainer(tpu_cores=None) - - # int: train on a single core - trainer = Trainer(tpu_cores=1) - - # list: train on a single selected core - trainer = Trainer(tpu_cores=[2]) - - # int: train on all cores few cores - trainer = Trainer(tpu_cores=8) - - # for 8+ cores must submit via xla script with - # a max of 8 cores specified. The XLA script - # will duplicate script onto each TPU in the POD - trainer = Trainer(tpu_cores=8) - -To train on more than 8 cores (ie: a POD), -submit this script using the xla_dist script. - -Example:: - - python -m torch_xla.distributed.xla_dist - --tpu=$TPU_POD_NAME - --conda-env=torch-xla-nightly - --env=XLA_USE_BF16=1 - -- python your_trainer_file.py - - -val_check_interval -^^^^^^^^^^^^^^^^^^ - -.. raw:: html - - - -| - -How often within one training epoch to check the validation set. -Can specify as float or int. - -- pass a ``float`` in the range [0.0, 1.0] to check after a fraction of the training epoch. -- pass an ``int`` to check after a fixed number of training batches. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(val_check_interval=1.0) - - # check validation set 4 times during a training epoch - trainer = Trainer(val_check_interval=0.25) - - # check validation set every 1000 training batches - # use this when using iterableDataset and your dataset has no length - # (ie: production cases with streaming data) - trainer = Trainer(val_check_interval=1000) - - -.. code-block:: python - - # Here is the computation to estimate the total number of batches seen within an epoch. - - # Find the total number of train batches - total_train_batches = total_train_samples // (train_batch_size * world_size) - - # Compute how many times we will call validation during the training loop - val_check_batch = max(1, int(total_train_batches * val_check_interval)) - val_checks_per_epoch = total_train_batches / val_check_batch - - # Find the total number of validation batches - total_val_batches = total_val_samples // (val_batch_size * world_size) - - # Total number of batches run - total_fit_batches = total_train_batches + total_val_batches - - -weights_save_path -^^^^^^^^^^^^^^^^^ - - -.. warning:: `weights_save_path` has been deprecated in v1.6 and will be removed in v1.8. Please pass - ``dirpath`` directly to the :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` - callback. - - -.. raw:: html - - - -| - -Directory of where to save weights if specified. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(weights_save_path=os.getcwd()) - - # save to your custom path - trainer = Trainer(weights_save_path="my/path") - -Example:: - - # if checkpoint callback used, then overrides the weights path - # **NOTE: this saves weights to some/path NOT my/path - checkpoint = ModelCheckpoint(dirpath='some/path') - trainer = Trainer( - callbacks=[checkpoint], - weights_save_path='my/path' - ) - -weights_summary -^^^^^^^^^^^^^^^ - -.. warning:: `weights_summary` is deprecated in v1.5 and will be removed in v1.7. Please pass :class:`~pytorch_lightning.callbacks.model_summary.ModelSummary` - directly to the Trainer's ``callbacks`` argument instead. To disable the model summary, - pass ``enable_model_summary = False`` to the Trainer. - - -.. raw:: html - - - -| - -Prints a summary of the weights when training begins. -Options: 'full', 'top', None. - -.. testcode:: - - # default used by the Trainer (ie: print summary of top level modules) - trainer = Trainer(weights_summary="top") - - # print full summary of all modules and submodules - trainer = Trainer(weights_summary="full") - - # don't print a summary - trainer = Trainer(weights_summary=None) - - -enable_model_summary -^^^^^^^^^^^^^^^^^^^^ - -Whether to enable or disable the model summarization. Defaults to True. - -.. testcode:: - - # default used by the Trainer - trainer = Trainer(enable_model_summary=True) - - # disable summarization - trainer = Trainer(enable_model_summary=False) - - # enable custom summarization - from pytorch_lightning.callbacks import ModelSummary - - trainer = Trainer(enable_model_summary=True, callbacks=[ModelSummary(max_depth=-1)]) - ------ - -Trainer class API ------------------ - -Methods -^^^^^^^ - -init -**** - -.. automethod:: pytorch_lightning.trainer.Trainer.__init__ - :noindex: - -fit -**** - -.. automethod:: pytorch_lightning.trainer.Trainer.fit - :noindex: - -validate -******** - -.. automethod:: pytorch_lightning.trainer.Trainer.validate - :noindex: - -test -**** - -.. automethod:: pytorch_lightning.trainer.Trainer.test - :noindex: - -predict -******* - -.. automethod:: pytorch_lightning.trainer.Trainer.predict - :noindex: - -tune -**** - -.. automethod:: pytorch_lightning.trainer.Trainer.tune - :noindex: - - -Properties -^^^^^^^^^^ - -callback_metrics -**************** - -The metrics available to callbacks. These are automatically set when you log via `self.log` - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("a_val", 2) - - - callback_metrics = trainer.callback_metrics - assert callback_metrics["a_val"] == 2 - -current_epoch -************* - -The number of epochs run. - -.. code-block:: python - - if trainer.current_epoch >= 10: - ... - -global_step -*********** - -The number of optimizer steps taken (does not reset each epoch). -This includes multiple optimizers and TBPTT steps (if enabled). - -.. code-block:: python - - if trainer.global_step >= 100: - ... - -logger -******* - -The current logger being used. Here's an example using tensorboard - -.. code-block:: python - - logger = trainer.logger - tensorboard = logger.experiment - - -loggers -******** - -The list of loggers currently being used by the Trainer. - -.. code-block:: python - - # List of Logger objects - loggers = trainer.loggers - for logger in loggers: - logger.log_metrics({"foo": 1.0}) - - -logged_metrics -************** - -The metrics sent to the logger (visualizer). - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("a_val", 2, logger=True) - - - logged_metrics = trainer.logged_metrics - assert logged_metrics["a_val"] == 2 - -log_dir -******* -The directory for the current experiment. Use this to save images to, etc... - -.. code-block:: python - - def training_step(self, batch, batch_idx): - img = ... - save_img(img, self.trainer.log_dir) - - - -is_global_zero -************** - -Whether this process is the global zero in multi-node training - -.. code-block:: python - - def training_step(self, batch, batch_idx): - if self.trainer.is_global_zero: - print("in node 0, accelerator 0") - -progress_bar_metrics -******************** - -The metrics sent to the progress bar. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("a_val", 2, prog_bar=True) - - - progress_bar_metrics = trainer.progress_bar_metrics - assert progress_bar_metrics["a_val"] == 2 - - -estimated_stepping_batches -************************** - -Check out :meth:`~pytorch_lightning.trainer.trainer.Trainer.estimated_stepping_batches`. - -state -***** - -The current state of the Trainer, including the current function that is running, the stage of -execution within that function, and the status of the Trainer. - -.. code-block:: python - - # fn in ("fit", "validate", "test", "predict", "tune") - trainer.state.fn - # status in ("initializing", "running", "finished", "interrupted") - trainer.state.status - # stage in ("train", "sanity_check", "validate", "test", "predict", "tune") - trainer.state.stage diff --git a/source/conf.py b/source/conf.py deleted file mode 100644 index 2a5cdad..0000000 --- a/source/conf.py +++ /dev/null @@ -1,408 +0,0 @@ -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. - -# import m2r -import glob -import os -import shutil -import sys -import warnings -from importlib.util import module_from_spec, spec_from_file_location - -import pt_lightning_sphinx_theme - -# ----------------------- -# VARIABLES WHEN WORKING ON DOCS... MAKE THIS TRUE TO BUILD FASTER -# ----------------------- -_PL_FAST_DOCS_DEV = bool(int(os.getenv("PL_FAST_DOCS_DEV", 1))) - -# ----------------------- -# BUILD stuff -# ----------------------- -PATH_HERE = os.path.abspath(os.path.dirname(__file__)) -PATH_ROOT = os.path.join(PATH_HERE, "..") -PATH_RAW_NB = os.path.join(PATH_ROOT, "_notebooks") -sys.path.insert(0, os.path.abspath(PATH_ROOT)) -sys.path.append(os.path.join(PATH_RAW_NB, ".actions")) - -_SHOULD_COPY_NOTEBOOKS = False - - -try: - from assistant import AssistantCLI -except ImportError: - _SHOULD_COPY_NOTEBOOKS = False - warnings.warn("To build the code, please run: `git submodule update --init --recursive`", stacklevel=2) - -FOLDER_GENERATED = "generated" -SPHINX_MOCK_REQUIREMENTS = int(os.environ.get("SPHINX_MOCK_REQUIREMENTS", True)) - -spec = spec_from_file_location( - "pytorch_lightning/__about__.py", os.path.join(PATH_ROOT, "__about__.py") -) -about = module_from_spec(spec) -spec.loader.exec_module(about) - -# -- Project documents ------------------------------------------------------- -if _SHOULD_COPY_NOTEBOOKS: - AssistantCLI.copy_notebooks( - PATH_RAW_NB, PATH_HERE, "notebooks", patterns=[".", "course_UvA-DL", "lightning_examples"] - ) - - -def _transform_changelog(path_in: str, path_out: str) -> None: - with open(path_in) as fp: - chlog_lines = fp.readlines() - # enrich short subsub-titles to be unique - chlog_ver = "" - for i, ln in enumerate(chlog_lines): - if ln.startswith("## "): - chlog_ver = ln[2:].split("-")[0].strip() - elif ln.startswith("### "): - ln = ln.replace("###", f"### {chlog_ver} -") - chlog_lines[i] = ln - with open(path_out, "w") as fp: - fp.writelines(chlog_lines) - - -os.makedirs(os.path.join(PATH_HERE, FOLDER_GENERATED), exist_ok=True) -# # copy all documents from GH templates like contribution guide -# for md in glob.glob(os.path.join(PATH_ROOT, ".github", "*.md")): -# shutil.copy(md, os.path.join(PATH_HERE, FOLDER_GENERATED, os.path.basename(md))) -# # copy also the changelog -# _transform_changelog(os.path.join(PATH_ROOT, "CHANGELOG.md"), os.path.join(PATH_HERE, FOLDER_GENERATED, "CHANGELOG.md")) - -# -- Project information ----------------------------------------------------- - -project = "PyTorch Lightning" -copyright = about.__copyright__ -author = about.__author__ - -# The short X.Y version -version = about.__version__ -# The full version, including alpha/beta/rc tags -release = about.__version__ - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. - -needs_sphinx = "4.0" - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.autodoc", - # 'sphinxcontrib.mockautodoc', # raises error: directive 'automodule' is already registered ... - # 'sphinxcontrib.fulltoc', # breaks pytorch-theme with unexpected kw argument 'titles_only' - "sphinx.ext.doctest", - "sphinx.ext.intersphinx", - "sphinx.ext.todo", - "sphinx.ext.coverage", - "sphinx.ext.viewcode", - "sphinx.ext.autosummary", - "sphinx.ext.napoleon", - "sphinx.ext.imgmath", - "sphinx.ext.autosectionlabel", - "myst_parser", - "nbsphinx", - "sphinx_autodoc_typehints", - "sphinx_copybutton", - "sphinx_paramlinks", - "sphinx_togglebutton", - "pt_lightning_sphinx_theme.extensions.lightning_tutorials", -] - -# Suppress warnings about duplicate labels (needed for PL tutorials) -suppress_warnings = [ - "autosectionlabel.*", -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ["_templates"] - -# https://berkeley-stat159-f17.github.io/stat159-f17/lectures/14-sphinx..html#conf.py-(cont.) -# https://stackoverflow.com/questions/38526888/embed-ipython-notebook-in-sphinx-document -# I execute the notebooks manually in advance. If notebooks test the code, -# they should be run at build time. -nbsphinx_execute = "never" -nbsphinx_allow_errors = True -nbsphinx_requirejs_path = "" - -# myst-parser, forcing to parse all html pages with mathjax -# https://github.com/executablebooks/MyST-Parser/issues/394 -myst_update_mathjax = False - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -source_parsers = {".rst": "restructuredtext", ".txt": "markdown", ".md": "markdown", ".ipynb": "nbsphinx"} - -# The master toctree document. -master_doc = "index" - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [ - f"{FOLDER_GENERATED}/PULL_REQUEST_TEMPLATE.md", - "notebooks/sample-template*", -] - -if _PL_FAST_DOCS_DEV: - exclude_patterns.append("notebooks/*") - exclude_patterns.append("tutorials.rst") - - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# http://www.sphinx-doc.org/en/master/usage/theming.html#builtin-themes -# html_theme = 'bizstyle' -# https://sphinx-themes.org -html_theme = "pt_lightning_sphinx_theme" -html_theme_path = [pt_lightning_sphinx_theme.get_html_theme_path()] -# html_theme_path = ["/Users/williamfalcon/Developer/opensource/lightning_sphinx_theme"] - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. - -html_theme_options = { - "pytorch_project": "https://pytorchlightning.ai", - "canonical_url": about.__docs_url__, - "collapse_navigation": False, - "display_version": True, - "logo_only": False, -} - -html_logo = "_static/images/logo.svg" - -html_favicon = "_static/images/icon.svg" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_templates", "_static"] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = project + "-doc" - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - # 'preamble': '', - # Latex figure (float) alignment - "figure_align": "htbp" -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [(master_doc, project + ".tex", project + " Documentation", author, "manual")] - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [(master_doc, project, project + " Documentation", [author], 1)] - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - project, - project + " Documentation", - author, - project, - "One line description of project.", - "Miscellaneous", - ) -] - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ["search.html"] - -# -- Extension configuration ------------------------------------------------- - -# -- Options for intersphinx extension --------------------------------------- - -# intersphinx_mapping = { -# "python": ("https://docs.python.org/3", None), -# "torch": ("https://pytorch.org/docs/stable/", None), -# "numpy": ("https://numpy.org/doc/stable/", None), -# "PIL": ("https://pillow.readthedocs.io/en/stable/", None), -# "torchmetrics": ("https://torchmetrics.readthedocs.io/en/stable/", None), -# "fairscale": ("https://fairscale.readthedocs.io/en/latest/", None), -# "graphcore": ("https://docs.graphcore.ai/en/latest/", None), -# } - -# -- Options for todo extension ---------------------------------------------- - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True - - -def setup(app): - # this is for hiding doctest decoration, - # see: http://z4r.github.io/python/2011/12/02/hides-the-prompts-and-output/ - app.add_js_file("copybutton.js") - app.add_css_file("main.css") - - -# copy all notebooks to local folder -# path_nbs = os.path.join(PATH_HERE, 'notebooks') -# if not os.path.isdir(path_nbs): -# os.mkdir(path_nbs) -# for path_ipynb in glob.glob(os.path.join(PATH_ROOT, 'notebooks', '*.ipynb')): -# path_ipynb2 = os.path.join(path_nbs, os.path.basename(path_ipynb)) -# shutil.copy(path_ipynb, path_ipynb2) - - -# Ignoring Third-party packages -# https://stackoverflow.com/questions/15889621/sphinx-how-to-exclude-imports-in-automodule -def package_list_from_file(file): - """List up package name (not containing version and extras) from a package list file.""" - mocked_packages = [] - with open(file) as fp: - for ln in fp.readlines(): - # Example: `tqdm>=4.41.0` => `tqdm` - # `[` is for package with extras - found = [ln.index(ch) for ch in list(",=<>#[") if ch in ln] - pkg = ln[: min(found)] if found else ln - if pkg.rstrip(): - mocked_packages.append(pkg.rstrip()) - return mocked_packages - - -# define mapping from PyPI names to python imports -PACKAGE_MAPPING = { - "Pillow": "PIL", - "opencv-python": "cv2", - "PyYAML": "yaml", - "comet-ml": "comet_ml", - "neptune-client": "neptune", - "hydra-core": "hydra", - "pyDeprecate": "deprecate", -} -MOCK_PACKAGES = [] -if SPHINX_MOCK_REQUIREMENTS: - MOCK_PACKAGES += ["fairscale"] - # mock also base packages when we are on RTD since we don't install them there - MOCK_PACKAGES += package_list_from_file(os.path.join(PATH_ROOT, "requirements.txt")) - # MOCK_PACKAGES += package_list_from_file(os.path.join(PATH_ROOT, "requirements", "extra.txt")) - # MOCK_PACKAGES += package_list_from_file(os.path.join(PATH_ROOT, "requirements", "loggers.txt")) -MOCK_PACKAGES = [PACKAGE_MAPPING.get(pkg, pkg) for pkg in MOCK_PACKAGES] - -autodoc_mock_imports = MOCK_PACKAGES - -autosummary_generate = True - -autodoc_member_order = "groupwise" - -autoclass_content = "both" - -autodoc_default_options = { - "members": True, - "methods": True, - "special-members": "__call__", - "exclude-members": "_abc_impl", - "show-inheritance": True, -} - -# Sphinx will add “permalinks” for each heading and description environment as paragraph signs that -# become visible when the mouse hovers over them. -# This value determines the text for the permalink; it defaults to "¶". Set it to None or the empty -# string to disable permalinks. -# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_add_permalinks -html_permalinks = True -html_permalinks_icon = "¶" - -# True to prefix each section label with the name of the document it is in, followed by a colon. -# For example, index:Introduction for a section called Introduction that appears in document index.rst. -# Useful for avoiding ambiguity when the same section heading appears in different documents. -# http://www.sphinx-doc.org/en/master/usage/extensions/autosectionlabel.html -autosectionlabel_prefix_document = True - -# only run doctests marked with a ".. doctest::" directive -doctest_test_doctest_blocks = "" -doctest_global_setup = """ -import importlib -import os -import sys -from typing import Optional - -import torch -import pytorch_lightning as pl -from torch import nn -from torch.utils.data import IterableDataset, DataLoader, Dataset -from pytorch_lightning import LightningDataModule, LightningModule, Trainer, seed_everything -from pytorch_lightning.callbacks import Callback -from pytorch_lightning.utilities import ( - _APEX_AVAILABLE, - _XLA_AVAILABLE, - _TPU_AVAILABLE, - _TORCHVISION_AVAILABLE, - _TORCH_GREATER_EQUAL_1_10, - _module_available, -) -_JSONARGPARSE_AVAILABLE = _module_available("jsonargparse") -""" -coverage_skip_undoc_in_source = True diff --git a/source/data/datamodule.rst b/source/data/datamodule.rst deleted file mode 100644 index bce1877..0000000 --- a/source/data/datamodule.rst +++ /dev/null @@ -1,501 +0,0 @@ -.. _datamodules: - -################### -LightningDataModule -################### -A datamodule is a shareable, reusable class that encapsulates all the steps needed to process data: - -.. raw:: html - - - -| - -A datamodule encapsulates the five steps involved in data processing in PyTorch: - -1. Download / tokenize / process. -2. Clean and (maybe) save to disk. -3. Load inside :class:`~torch.utils.data.Dataset`. -4. Apply transforms (rotate, tokenize, etc...). -5. Wrap inside a :class:`~torch.utils.data.DataLoader`. - -| - -This class can then be shared and used anywhere: - -.. code-block:: python - - from pl_bolts.datamodules import CIFAR10DataModule, ImagenetDataModule - - model = LitClassifier() - trainer = Trainer() - - imagenet = ImagenetDataModule() - trainer.fit(model, datamodule=imagenet) - - cifar10 = CIFAR10DataModule() - trainer.fit(model, datamodule=cifar10) - ---------------- - -*************************** -Why do I need a DataModule? -*************************** -In normal PyTorch code, the data cleaning/preparation is usually scattered across many files. This makes -sharing and reusing the exact splits and transforms across projects impossible. - -Datamodules are for you if you ever asked the questions: - -- what splits did you use? -- what transforms did you use? -- what normalization did you use? -- how did you prepare/tokenize the data? - --------------- - -********************* -What is a DataModule? -********************* -A DataModule is simply a collection of a train_dataloader(s), val_dataloader(s), test_dataloader(s) and -predict_dataloader(s) along with the matching transforms and data processing/downloads steps required. - -Here's a simple PyTorch example: - -.. code-block:: python - - # regular PyTorch - test_data = MNIST(my_path, train=False, download=True) - predict_data = MNIST(my_path, train=False, download=True) - train_data = MNIST(my_path, train=True, download=True) - train_data, val_data = random_split(train_data, [55000, 5000]) - - train_loader = DataLoader(train_data, batch_size=32) - val_loader = DataLoader(val_data, batch_size=32) - test_loader = DataLoader(test_data, batch_size=32) - predict_loader = DataLoader(predict_data, batch_size=32) - -The equivalent DataModule just organizes the same exact code, but makes it reusable across projects. - -.. code-block:: python - - class MNISTDataModule(pl.LightningDataModule): - def __init__(self, data_dir: str = "path/to/dir", batch_size: int = 32): - super().__init__() - self.data_dir = data_dir - self.batch_size = batch_size - - def setup(self, stage: Optional[str] = None): - self.mnist_test = MNIST(self.data_dir, train=False) - self.mnist_predict = MNIST(self.data_dir, train=False) - mnist_full = MNIST(self.data_dir, train=True) - self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) - - def train_dataloader(self): - return DataLoader(self.mnist_train, batch_size=self.batch_size) - - def val_dataloader(self): - return DataLoader(self.mnist_val, batch_size=self.batch_size) - - def test_dataloader(self): - return DataLoader(self.mnist_test, batch_size=self.batch_size) - - def predict_dataloader(self): - return DataLoader(self.mnist_predict, batch_size=self.batch_size) - - def teardown(self, stage: Optional[str] = None): - # Used to clean-up when the run is finished - ... - -But now, as the complexity of your processing grows (transforms, multiple-GPU training), you can -let Lightning handle those details for you while making this dataset reusable so you can share with -colleagues or use in different projects. - -.. code-block:: python - - mnist = MNISTDataModule(my_path) - model = LitClassifier() - - trainer = Trainer() - trainer.fit(model, mnist) - -Here's a more realistic, complex DataModule that shows how much more reusable the datamodule is. - -.. code-block:: python - - import pytorch_lightning as pl - from torch.utils.data import random_split, DataLoader - - # Note - you must have torchvision installed for this example - from torchvision.datasets import MNIST - from torchvision import transforms - - - class MNISTDataModule(pl.LightningDataModule): - def __init__(self, data_dir: str = "./"): - super().__init__() - self.data_dir = data_dir - self.transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]) - - def prepare_data(self): - # download - MNIST(self.data_dir, train=True, download=True) - MNIST(self.data_dir, train=False, download=True) - - def setup(self, stage: Optional[str] = None): - - # Assign train/val datasets for use in dataloaders - if stage == "fit" or stage is None: - mnist_full = MNIST(self.data_dir, train=True, transform=self.transform) - self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) - - # Assign test dataset for use in dataloader(s) - if stage == "test" or stage is None: - self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform) - - if stage == "predict" or stage is None: - self.mnist_predict = MNIST(self.data_dir, train=False, transform=self.transform) - - def train_dataloader(self): - return DataLoader(self.mnist_train, batch_size=32) - - def val_dataloader(self): - return DataLoader(self.mnist_val, batch_size=32) - - def test_dataloader(self): - return DataLoader(self.mnist_test, batch_size=32) - - def predict_dataloader(self): - return DataLoader(self.mnist_predict, batch_size=32) - ---------------- - -*********************** -LightningDataModule API -*********************** -To define a DataModule the following methods are used to create train/val/test/predict dataloaders: - -- :ref:`prepare_data` (how to download, tokenize, etc...) -- :ref:`setup` (how to split, define dataset, etc...) -- :ref:`train_dataloader` -- :ref:`val_dataloader` -- :ref:`test_dataloader` -- :ref:`predict_dataloader` - - -prepare_data -============ -Downloading and saving data with multiple processes (distributed settings) will result in corrupted data. Lightning -ensures the :meth:`~pytorch_lightning.core.hooks.DataHooks.prepare_data` is called only within a single process on CPU, -so you can safely add your downloading logic within. In case of multi-node training, the execution of this hook -depends upon :ref:`prepare_data_per_node`. :meth:`~pytorch_lightning.core.hooks.DataHooks.setup` is called after -``prepare_data`` and there is a barrier in between which ensures that all the processes proceed to ``setup`` once the data is prepared and available for use. - -- download, i.e. download data only once on the disk from a single process -- tokenize. Since it's a one time process, it is not recommended to do it on all processes -- etc... - -.. code-block:: python - - class MNISTDataModule(pl.LightningDataModule): - def prepare_data(self): - # download - MNIST(os.getcwd(), train=True, download=True, transform=transforms.ToTensor()) - MNIST(os.getcwd(), train=False, download=True, transform=transforms.ToTensor()) - - -.. warning:: - - ``prepare_data`` is called from the main process. It is not recommended to assign state here (e.g. ``self.x = y``) since it is called on a single process and if you assign - states here then they won't be available for other processes. - - -setup -===== -There are also data operations you might want to perform on every GPU. Use :meth:`~pytorch_lightning.core.hooks.DataHooks.setup` to do things like: - -- count number of classes -- build vocabulary -- perform train/val/test splits -- create datasets -- apply transforms (defined explicitly in your datamodule) -- etc... - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def setup(self, stage: Optional[str] = None): - - # Assign Train/val split(s) for use in Dataloaders - if stage in (None, "fit"): - mnist_full = MNIST(self.data_dir, train=True, download=True, transform=self.transform) - self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000]) - - # Assign Test split(s) for use in Dataloaders - if stage in (None, "test"): - self.mnist_test = MNIST(self.data_dir, train=False, download=True, transform=self.transform) - - -For eg., if you are working with NLP task where you need to tokenize the text and use it, then you can do something like as follows: - -.. code-block:: python - - class LitDataModule(LightningDataModule): - def prepare_data(self): - dataset = load_Dataset(...) - train_dataset = ... - val_dataset = ... - # tokenize - # save it to disk - - def setup(self, stage): - # load it back here - dataset = load_dataset_from_disk(...) - - -This method expects a ``stage`` argument. -It is used to separate setup logic for ``trainer.{fit,validate,test,predict}``. If ``setup`` is called with ``stage=None``, -we assume all stages have been set-up. - -.. note:: :ref:`setup` is called from every process across all the nodes. Setting state here is recommended. -.. note:: :ref:`teardown` can be used to clean up the state. It is also called from every process across all the nodes. - - -train_dataloader -================ -Use the :meth:`~pytorch_lightning.core.hooks.DataHooks.train_dataloader` method to generate the training dataloader(s). -Usually you just wrap the dataset you defined in :ref:`setup`. This is the dataloader that the Trainer -:meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` method uses. - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def train_dataloader(self): - return DataLoader(self.mnist_train, batch_size=64) - -.. _datamodule_val_dataloader_label: - -val_dataloader -============== -Use the :meth:`~pytorch_lightning.core.hooks.DataHooks.val_dataloader` method to generate the validation dataloader(s). -Usually you just wrap the dataset you defined in :ref:`setup`. This is the dataloader that the Trainer -:meth:`~pytorch_lightning.trainer.trainer.Trainer.fit` and :meth:`~pytorch_lightning.trainer.trainer.Trainer.validate` methods uses. - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def val_dataloader(self): - return DataLoader(self.mnist_val, batch_size=64) - - -.. _datamodule_test_dataloader_label: - -test_dataloader -=============== -Use the :meth:`~pytorch_lightning.core.hooks.DataHooks.test_dataloader` method to generate the test dataloader(s). -Usually you just wrap the dataset you defined in :ref:`setup`. This is the dataloader that the Trainer -:meth:`~pytorch_lightning.trainer.trainer.Trainer.test` method uses. - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def test_dataloader(self): - return DataLoader(self.mnist_test, batch_size=64) - - -predict_dataloader -================== -Use the :meth:`~pytorch_lightning.core.hooks.DataHooks.predict_dataloader` method to generate the prediction dataloader(s). -Usually you just wrap the dataset you defined in :ref:`setup`. This is the dataloader that the Trainer -:meth:`~pytorch_lightning.trainer.trainer.Trainer.predict` method uses. - -.. code-block:: python - - import pytorch_lightning as pl - - - class MNISTDataModule(pl.LightningDataModule): - def predict_dataloader(self): - return DataLoader(self.mnist_predict, batch_size=64) - - -transfer_batch_to_device -======================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.transfer_batch_to_device - :noindex: - -on_before_batch_transfer -======================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_before_batch_transfer - :noindex: - -on_after_batch_transfer -======================= - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_after_batch_transfer - :noindex: - -load_state_dict -=============== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.load_state_dict - :noindex: - -state_dict -========== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.state_dict - :noindex: - -on_train_dataloader -=================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_train_dataloader - :noindex: - -on_val_dataloader -================= - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_val_dataloader - :noindex: - -on_test_dataloader -================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_test_dataloader - :noindex: - -on_predict_dataloader -===================== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.on_predict_dataloader - :noindex: - -teardown -======== - -.. automethod:: pytorch_lightning.core.datamodule.LightningDataModule.teardown - :noindex: - -prepare_data_per_node -===================== -If set to ``True`` will call ``prepare_data()`` on LOCAL_RANK=0 for every node. -If set to ``False`` will only call from NODE_RANK=0, LOCAL_RANK=0. - -.. testcode:: - - class LitDataModule(LightningDataModule): - def __init__(self): - super().__init__() - self.prepare_data_per_node = True - - ------------------- - -****************** -Using a DataModule -****************** - -The recommended way to use a DataModule is simply: - -.. code-block:: python - - dm = MNISTDataModule() - model = Model() - trainer.fit(model, datamodule=dm) - trainer.test(datamodule=dm) - trainer.validate(datamodule=dm) - trainer.predict(datamodule=dm) - -If you need information from the dataset to build your model, then run -:ref:`prepare_data` and -:ref:`setup` manually (Lightning ensures -the method runs on the correct devices). - -.. code-block:: python - - dm = MNISTDataModule() - dm.prepare_data() - dm.setup(stage="fit") - - model = Model(num_classes=dm.num_classes, width=dm.width, vocab=dm.vocab) - trainer.fit(model, dm) - - dm.setup(stage="test") - trainer.test(datamodule=dm) - ----------------- - -***************************** -DataModules without Lightning -***************************** -You can of course use DataModules in plain PyTorch code as well. - -.. code-block:: python - - # download, etc... - dm = MNISTDataModule() - dm.prepare_data() - - # splits/transforms - dm.setup(stage="fit") - - # use data - for batch in dm.train_dataloader(): - ... - - for batch in dm.val_dataloader(): - ... - - dm.teardown(stage="fit") - - # lazy load test data - dm.setup(stage="test") - for batch in dm.test_dataloader(): - ... - - dm.teardown(stage="test") - -But overall, DataModules encourage reproducibility by allowing all details of a dataset to be specified in a unified -structure. - ----------------- - -****************************** -Hyperparameters in DataModules -****************************** -Like LightningModules, DataModules support hyperparameters with the same API. - -.. code-block:: python - - import pytorch_lightning as pl - - - class CustomDataModule(pl.LightningDataModule): - def __init__(self, *args, **kwargs): - super().__init__() - self.save_hyperparameters() - - def configure_optimizers(self): - # access the saved hyperparameters - opt = optim.Adam(self.parameters(), lr=self.hparams.lr) - -Refer to ``save_hyperparameters`` in :doc:`lightning module <../common/lightning_module>` for more details. - - ----- - -.. include:: ../extensions/datamodules_state.rst diff --git a/source/debug/debugging_intermediate.rst b/source/debug/debugging_intermediate.rst deleted file mode 100644 index da8eb59..0000000 --- a/source/debug/debugging_intermediate.rst +++ /dev/null @@ -1,79 +0,0 @@ -:orphan: - -.. _debugging_intermediate: - - -############################### -Debug your model (intermediate) -############################### -**Audience**: Users who want to debug their ML code - ----- - -*************************** -Why should I debug ML code? -*************************** -Machine learning code requires debugging mathematical correctness, which is not something non-ML code has to deal with. Lightning implements a few best-practice techniques to give all users, expert level ML debugging abilities. - ----- - -************************************** -Overfit your model on a Subset of Data -************************************** -A good debugging technique is to take a tiny portion of your data (say 2 samples per class), -and try to get your model to overfit. If it can't, it's a sign it won't work with large datasets. - -(See: :paramref:`~pytorch_lightning.trainer.trainer.Trainer.overfit_batches` -argument of :class:`~pytorch_lightning.trainer.trainer.Trainer`) - -.. testcode:: - - # use only 1% of training data (and turn off validation) - trainer = Trainer(overfit_batches=0.01) - - # similar, but with a fixed 10 batches - trainer = Trainer(overfit_batches=10) - -When using this argument, the validation loop will be disabled. We will also replace the sampler -in the training set to turn off shuffle for you. - ----- - -******************************** -Look-out for exploding gradients -******************************** -One major problem that plagues models is exploding gradients. Gradient norm is one technique that can help keep gradients from exploding. - -.. testcode:: - - # the 2-norm - trainer = Trainer(track_grad_norm=2) - -This will plot the 2-norm to your experiment manager. If you notice the norm is going up, there's a good chance your gradients are/will explode. - -One technique to stop exploding gradients is to clip the gradient - -.. testcode:: - - # DEFAULT (ie: don't clip) - trainer = Trainer(gradient_clip_val=0) - - # clip gradients' global norm to <=0.5 using gradient_clip_algorithm='norm' by default - trainer = Trainer(gradient_clip_val=0.5) - - # clip gradients' maximum magnitude to <=0.5 - trainer = Trainer(gradient_clip_val=0.5, gradient_clip_algorithm="value") - ----- - -************************* -Detect autograd anomalies -************************* -Lightning helps you detect anomalies in the PyTorh autograd engine via PyTorch's built-in -`Anomaly Detection Context-manager `_. - -Enable it via the **detect_anomaly** trainer argument: - -.. testcode:: - - trainer = Trainer(detect_anomaly=True) diff --git a/source/deploy/production_advanced.rst b/source/deploy/production_advanced.rst deleted file mode 100644 index 750355d..0000000 --- a/source/deploy/production_advanced.rst +++ /dev/null @@ -1,60 +0,0 @@ -######################################## -Deploy models into production (advanced) -######################################## -**Audience**: Machine learning engineers optimizing models for enterprise-scale production environments. - ----- - -************************** -Compile your model to ONNX -************************** -`ONNX `_ is a package developed by Microsoft to optimize inference. ONNX allows the model to be independent of PyTorch and run on any ONNX Runtime. - -To export your model to ONNX format call the :meth:`~pytorch_lightning.core.lightning.LightningModule.to_onnx` function on your :class:`~pytorch_lightning.core.lightning.LightningModule` with the ``filepath`` and ``input_sample``. - -.. code-block:: python - - class SimpleModel(LightningModule): - def __init__(self): - super().__init__() - self.l1 = torch.nn.Linear(in_features=64, out_features=4) - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - - # create the model - model = SimpleModel() - filepath = "model.onnx" - input_sample = torch.randn((1, 64)) - model.to_onnx(filepath, input_sample, export_params=True) - -You can also skip passing the input sample if the ``example_input_array`` property is specified in your :class:`~pytorch_lightning.core.lightning.LightningModule`. - -.. code-block:: python - - class SimpleModel(LightningModule): - def __init__(self): - super().__init__() - self.l1 = torch.nn.Linear(in_features=64, out_features=4) - self.example_input_array = torch.randn(7, 64) - - def forward(self, x): - return torch.relu(self.l1(x.view(x.size(0), -1))) - - - # create the model - model = SimpleModel() - filepath = "model.onnx" - model.to_onnx(filepath, export_params=True) - -Once you have the exported model, you can run it on your ONNX runtime in the following way: - -.. code-block:: python - - import onnxruntime - - ort_session = onnxruntime.InferenceSession(filepath) - input_name = ort_session.get_inputs()[0].name - ort_inputs = {input_name: np.random.randn(1, 64)} - ort_outs = ort_session.run(None, ort_inputs) diff --git a/source/deploy/production_basic.rst b/source/deploy/production_basic.rst deleted file mode 100644 index 00e9caa..0000000 --- a/source/deploy/production_basic.rst +++ /dev/null @@ -1,80 +0,0 @@ -##################################### -Deploy models into production (basic) -##################################### -**Audience**: All users. - ----- - -***************************** -Load a checkpoint and predict -***************************** -The easiest way to use a model for predictions is to load the weights using **load_from_checkpoint** found in the LightningModule. - -.. code-block:: python - - model = LitModel.load_from_checkpoint("best_model.ckpt") - model.eval() - x = torch.randn(1, 64) - - with torch.no_grad(): - y_hat = model(x) - ----- - -************************************** -Predict step with your LightningModule -************************************** -Loading a checkpoint and predicting still leaves you with a lot of boilerplate around the predict epoch. The **predict step** in the LightningModule removes this boilerplate. - -.. code-block:: python - - class MyModel(LightningModule): - def predict_step(self, batch, batch_idx, dataloader_idx=0): - return self(batch) - -And pass in any dataloader to the Lightning Trainer: - -.. code-block:: python - - data_loader = DataLoader(...) - model = MyModel() - trainer = Trainer() - predictions = trainer.predict(model, data_loader) - ----- - -******************************** -Enable complicated predict logic -******************************** -When you need to add complicated pre-processing or post-processing logic to your data use the predict step. For example here we do `Monte Carlo Dropout `_ for predictions: - -.. code-block:: python - - class LitMCdropoutModel(pl.LightningModule): - def __init__(self, model, mc_iteration): - super().__init__() - self.model = model - self.dropout = nn.Dropout() - self.mc_iteration = mc_iteration - - def predict_step(self, batch, batch_idx): - # enable Monte Carlo Dropout - self.dropout.train() - - # take average of `self.mc_iteration` iterations - pred = [self.dropout(self.model(x)).unsqueeze(0) for _ in range(self.mc_iteration)] - pred = torch.vstack(pred).mean(dim=0) - return pred - ----- - -**************************** -Enable distributed inference -**************************** -By using the predict step in Lightning you get free distributed inference - - -.. code-block:: python - - trainer = Trainer(devices=8, accelerator="gpu") - predictions = trainer.predict(model, data_loader) diff --git a/source/ecosystem/transformers.rst b/source/ecosystem/transformers.rst deleted file mode 100644 index b20402a..0000000 --- a/source/ecosystem/transformers.rst +++ /dev/null @@ -1,47 +0,0 @@ -:orphan: - -Lightning Transformers -====================== - -`Lightning Transformers `_ offers a flexible interface for training and fine-tuning SOTA Transformer models -using the :doc:`PyTorch Lightning Trainer <../common/trainer>`. - -.. code-block:: bash - - pip install lightning-transformers - -In Lightning Transformers, we offer the following benefits: - -- Powered by `PyTorch Lightning `_ - Accelerators, custom Callbacks, Loggers, and high performance scaling with minimal changes. -- Backed by `HuggingFace Transformers `_ models and datasets, spanning multiple modalities and tasks within NLP/Audio and Vision. -- Task Abstraction for Rapid Research & Experimentation - Build your own custom transformer tasks across all modalities with little friction. -- Powerful config composition backed by `Hydra `_ - simply swap out models, optimizers, schedulers task, and many more configurations without touching the code. -- Seamless Memory and Speed Optimizations - Out-of-the-box training optimizations such as `DeepSpeed ZeRO `_ or `FairScale Sharded Training `_ with no code changes. - ------------------ - -Using Lightning-Transformers ----------------------------- - -Lightning Transformers has a collection of tasks for common NLP problems such as `language_modeling `_, -`translation `_ and more. To use, simply: - -1. Pick a task to train (passed to ``train.py`` as ``task=``) - -2. Pick a dataset (passed to ``train.py`` as ``dataset=``) - -3. Customize the backbone, optimizer, or any component within the config - -4. Add any :doc:`Lightning supported parameters and optimizations <../common/trainer>` - -.. code-block:: bash - - python train.py \ - task= \ - dataset= - backbone.pretrained_model_name_or_path= # Optionally change the HF backbone - optimizer= # Optionally specify optimizer (Default AdamW) - trainer. # Optionally specify Lightning trainer arguments - - -To learn more about Lightning Transformers, please refer to the `Lightning Transformers documentation `_. diff --git a/source/extensions/callbacks.rst b/source/extensions/callbacks.rst deleted file mode 100644 index 6def5ee..0000000 --- a/source/extensions/callbacks.rst +++ /dev/null @@ -1,408 +0,0 @@ -.. role:: hidden - :class: hidden-section - -.. _callbacks: - -######## -Callback -######## - -.. raw:: html - - - -| - -A callback is a self-contained program that can be reused across projects. - -Lightning has a callback system to execute them when needed. Callbacks should capture NON-ESSENTIAL -logic that is NOT required for your :doc:`lightning module <../common/lightning_module>` to run. - -Here's the flow of how the callback hooks are executed: - -.. raw:: html - - - -An overall Lightning system should have: - -1. Trainer for all engineering -2. LightningModule for all research code. -3. Callbacks for non-essential code. - -| - -Example: - -.. testcode:: - - from pytorch_lightning.callbacks import Callback - - - class MyPrintingCallback(Callback): - def on_train_start(self, trainer, pl_module): - print("Training is starting") - - def on_train_end(self, trainer, pl_module): - print("Training is ending") - - - trainer = Trainer(callbacks=[MyPrintingCallback()]) - -We successfully extended functionality without polluting our super clean -:doc:`lightning module <../common/lightning_module>` research code. - ------------ - -******** -Examples -******** -You can do pretty much anything with callbacks. - -- `Add a MLP to fine-tune self-supervised networks `_. -- `Find how to modify an image input to trick the classification result `_. -- `Interpolate the latent space of any variational model `_. -- `Log images to Tensorboard for any model `_. - - --------------- - -****************** -Built-in Callbacks -****************** -Lightning has a few built-in callbacks. - -.. note:: - For a richer collection of callbacks, check out our - `bolts library `_. - -.. currentmodule:: pytorch_lightning.callbacks - -.. autosummary:: - :nosignatures: - :template: classtemplate.rst - - BackboneFinetuning - BaseFinetuning - BasePredictionWriter - Callback - DeviceStatsMonitor - EarlyStopping - GradientAccumulationScheduler - LambdaCallback - LearningRateMonitor - ModelCheckpoint - ModelPruning - ModelSummary - ProgressBarBase - QuantizationAwareTraining - RichModelSummary - RichProgressBar - StochasticWeightAveraging - Timer - TQDMProgressBar - ----------- - -.. include:: callbacks_state.rst - ----------- - -************** -Best Practices -************** -The following are best practices when using/designing callbacks. - -1. Callbacks should be isolated in their functionality. -2. Your callback should not rely on the behavior of other callbacks in order to work properly. -3. Do not manually call methods from the callback. -4. Directly calling methods (eg. `on_validation_end`) is strongly discouraged. -5. Whenever possible, your callbacks should not depend on the order in which they are executed. - ------------ - -.. _callback_hooks: - -************ -Callback API -************ -Here is the full API of methods available in the Callback base class. - -The :class:`~pytorch_lightning.callbacks.Callback` class is the base for all the callbacks in Lightning just like the :class:`~pytorch_lightning.core.lightning.LightningModule` is the base for all models. -It defines a public interface that each callback implementation must follow, the key ones are: - -Properties -========== - -state_key -^^^^^^^^^ - -.. autoattribute:: pytorch_lightning.callbacks.Callback.state_key - :noindex: - - -Hooks -===== - -on_configure_sharded_model -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_configure_sharded_model - :noindex: - -setup -^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.setup - :noindex: - -teardown -^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.teardown - :noindex: - -on_init_start -^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_init_start - :noindex: - -on_init_end -^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_init_end - :noindex: - -on_fit_start -^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_fit_start - :noindex: - -on_fit_end -^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_fit_end - :noindex: - -on_sanity_check_start -^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_sanity_check_start - :noindex: - -on_sanity_check_end -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_sanity_check_end - :noindex: - -on_train_batch_start -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_batch_start - :noindex: - -on_train_batch_end -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_batch_end - :noindex: - -on_train_epoch_start -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_epoch_start - :noindex: - -on_train_epoch_end -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_epoch_end - :noindex: - -on_validation_epoch_start -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_epoch_start - :noindex: - -on_validation_epoch_end -^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_epoch_end - :noindex: - -on_test_epoch_start -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_epoch_start - :noindex: - -on_test_epoch_end -^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_epoch_end - :noindex: - -on_predict_epoch_start -^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_epoch_start - :noindex: - -on_predict_epoch_end -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_epoch_end - :noindex: - -.. automethod:: pytorch_lightning.callbacks.Callback.on_epoch_end - :noindex: - -on_validation_batch_start -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_batch_start - :noindex: - -on_validation_batch_end -^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_batch_end - :noindex: - -on_test_batch_start -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_batch_start - :noindex: - -on_test_batch_end -^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_batch_end - :noindex: - -on_predict_batch_start -^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_batch_start - :noindex: - -on_predict_batch_end -^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_batch_end - :noindex: - -on_train_start -^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_start - :noindex: - -on_train_end -^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_train_end - :noindex: - -on_validation_start -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_start - :noindex: - -on_validation_end -^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_validation_end - :noindex: - -on_test_start -^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_start - :noindex: - -on_test_end -^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_test_end - :noindex: - -on_predict_start -^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_start - :noindex: - -on_predict_end -^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_predict_end - :noindex: - -on_keyboard_interrupt -^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_keyboard_interrupt - :noindex: - -on_exception -^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_exception - :noindex: - -state_dict -^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.state_dict - :noindex: - -on_save_checkpoint -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_save_checkpoint - :noindex: - -load_state_dict -^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.load_state_dict - :noindex: - -on_load_checkpoint -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_load_checkpoint - :noindex: - -on_before_backward -^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_before_backward - :noindex: - -on_after_backward -^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_after_backward - :noindex: - -on_before_optimizer_step -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_before_optimizer_step - :noindex: - -on_before_zero_grad -^^^^^^^^^^^^^^^^^^^ - -.. automethod:: pytorch_lightning.callbacks.Callback.on_before_zero_grad - :noindex: diff --git a/source/extensions/generated/pytorch_lightning.loggers.CSVLogger.rst b/source/extensions/generated/pytorch_lightning.loggers.CSVLogger.rst deleted file mode 100644 index bd8ccbf..0000000 --- a/source/extensions/generated/pytorch_lightning.loggers.CSVLogger.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -CSVLogger -========= - -.. autoclass:: CSVLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/source/extensions/generated/pytorch_lightning.loggers.CometLogger.rst b/source/extensions/generated/pytorch_lightning.loggers.CometLogger.rst deleted file mode 100644 index 324d77c..0000000 --- a/source/extensions/generated/pytorch_lightning.loggers.CometLogger.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -CometLogger -=========== - -.. autoclass:: CometLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/source/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.rst b/source/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.rst deleted file mode 100644 index 2eaf478..0000000 --- a/source/extensions/generated/pytorch_lightning.loggers.MLFlowLogger.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -MLFlowLogger -============ - -.. autoclass:: MLFlowLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/source/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.rst b/source/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.rst deleted file mode 100644 index e9da513..0000000 --- a/source/extensions/generated/pytorch_lightning.loggers.NeptuneLogger.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -NeptuneLogger -============= - -.. autoclass:: NeptuneLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/source/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.rst b/source/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.rst deleted file mode 100644 index 6bcd4a2..0000000 --- a/source/extensions/generated/pytorch_lightning.loggers.TensorBoardLogger.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -TensorBoardLogger -================= - -.. autoclass:: TensorBoardLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/source/extensions/generated/pytorch_lightning.loggers.WandbLogger.rst b/source/extensions/generated/pytorch_lightning.loggers.WandbLogger.rst deleted file mode 100644 index 3dcb424..0000000 --- a/source/extensions/generated/pytorch_lightning.loggers.WandbLogger.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. role:: hidden - :class: hidden-section -.. currentmodule:: pytorch_lightning.loggers - - -WandbLogger -=========== - -.. autoclass:: WandbLogger - :members: - - -.. - autogenerated from source/_templates/classtemplate.rst - note it does not have :inherited-members: \ No newline at end of file diff --git a/source/extensions/logging.rst b/source/extensions/logging.rst deleted file mode 100644 index 8bad452..0000000 --- a/source/extensions/logging.rst +++ /dev/null @@ -1,410 +0,0 @@ -:orphan: - -.. testsetup:: * - - from pytorch_lightning import loggers as pl_loggers - -.. role:: hidden - :class: hidden-section - -.. _logging: - - -####### -Logging -####### - -***************** -Supported Loggers -***************** - -The following are loggers we support: - -.. currentmodule:: pytorch_lightning.loggers - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: classtemplate.rst - - CometLogger - CSVLogger - MLFlowLogger - NeptuneLogger - TensorBoardLogger - WandbLogger - - -The above loggers will normally plot an additional chart (**global_step VS epoch**). Depending on the loggers you use, there might be some additional charts too. - -By default, Lightning uses ``TensorBoard`` logger under the hood, and stores the logs to a directory (by default in ``lightning_logs/``). - -.. testcode:: - - from pytorch_lightning import Trainer - - # Automatically logs to a directory (by default ``lightning_logs/``) - trainer = Trainer() - -To see your logs: - -.. code-block:: bash - - tensorboard --logdir=lightning_logs/ - -To visualize tensorboard in a jupyter notebook environment, run the following command in a jupyter cell: - -.. code-block:: bash - - %reload_ext tensorboard - %tensorboard --logdir=lightning_logs/ - -You can also pass a custom Logger to the :class:`~pytorch_lightning.trainer.trainer.Trainer`. - -.. testcode:: - - from pytorch_lightning import loggers as pl_loggers - - tb_logger = pl_loggers.TensorBoardLogger(save_dir="logs/") - trainer = Trainer(logger=tb_logger) - -Choose from any of the others such as MLflow, Comet, Neptune, WandB, etc. - -.. testcode:: - - comet_logger = pl_loggers.CometLogger(save_dir="logs/") - trainer = Trainer(logger=comet_logger) - -To use multiple loggers, simply pass in a ``list`` or ``tuple`` of loggers. - -.. testcode:: - - tb_logger = pl_loggers.TensorBoardLogger(save_dir="logs/") - comet_logger = pl_loggers.CometLogger(save_dir="logs/") - trainer = Trainer(logger=[tb_logger, comet_logger]) - -.. note:: - - By default, Lightning logs every 50 steps. Use Trainer flags to :ref:`logging_frequency`. - -.. note:: - - By default, all loggers log to ``os.getcwd()``. You can change the logging path using - ``Trainer(default_root_dir="/your/path/to/save/checkpoints")`` without instantiating a logger. - ----------- - -****************************** -Logging from a LightningModule -****************************** - -Lightning offers automatic log functionalities for logging scalars, or manual logging for anything else. - -Automatic Logging -================= - -Use the :meth:`~pytorch_lightning.core.lightning.LightningModule.log` or :meth:`~pytorch_lightning.core.lightning.LightningModule.log_dict` -methods to log from anywhere in a :doc:`LightningModule <../common/lightning_module>` and :doc:`callbacks <../extensions/callbacks>`. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("my_metric", x) - - - # or a dict to get multiple metrics on the same plot if the logger supports it - def training_step(self, batch, batch_idx): - self.log("performance", {"acc": acc, "recall": recall}) - - - # or a dict to log all metrics at once with individual plots - def training_step(self, batch, batch_idx): - self.log_dict({"acc": acc, "recall": recall}) - -.. note:: - Everything explained below applies to both :meth:`~pytorch_lightning.core.lightning.LightningModule.log` or :meth:`~pytorch_lightning.core.lightning.LightningModule.log_dict` methods. - -Depending on where the :meth:`~pytorch_lightning.core.lightning.LightningModule.log` method is called, Lightning auto-determines -the correct logging mode for you. Of course you can override the default behavior by manually setting the -:meth:`~pytorch_lightning.core.lightning.LightningModule.log` parameters. - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("my_loss", loss, on_step=True, on_epoch=True, prog_bar=True, logger=True) - -The :meth:`~pytorch_lightning.core.lightning.LightningModule.log` method has a few options: - -* ``on_step``: Logs the metric at the current step. -* ``on_epoch``: Automatically accumulates and logs at the end of the epoch. -* ``prog_bar``: Logs to the progress bar (Default: ``False``). -* ``logger``: Logs to the logger like ``Tensorboard``, or any other custom logger passed to the :class:`~pytorch_lightning.trainer.trainer.Trainer` (Default: ``True``). -* ``reduce_fx``: Reduction function over step values for end of epoch. Uses :meth:`torch.mean` by default. -* ``enable_graph``: If True, will not auto detach the graph. -* ``sync_dist``: If True, reduces the metric across devices. Use with care as this may lead to a significant communication overhead. -* ``sync_dist_group``: The DDP group to sync across. -* ``add_dataloader_idx``: If True, appends the index of the current dataloader to the name (when using multiple dataloaders). If False, user needs to give unique names for each dataloader to not mix the values. -* ``batch_size``: Current batch size used for accumulating logs logged with ``on_epoch=True``. This will be directly inferred from the loaded batch, but for some data structures you might need to explicitly provide it. -* ``rank_zero_only``: Whether the value will be logged only on rank 0. This will prevent synchronization which would produce a deadlock as not all processes would perform this log call. - -.. list-table:: Default behavior of logging in Callback or LightningModule - :widths: 50 25 25 - :header-rows: 1 - - * - Hook - - on_step - - on_epoch - * - on_train_start, on_train_epoch_start, on_train_epoch_end, training_epoch_end - - False - - True - * - on_before_backward, on_after_backward, on_before_optimizer_step, on_before_zero_grad - - True - - False - * - on_train_batch_start, on_train_batch_end, training_step, training_step_end - - True - - False - * - on_validation_start, on_validation_epoch_start, on_validation_epoch_end, validation_epoch_end - - False - - True - * - on_validation_batch_start, on_validation_batch_end, validation_step, validation_step_end - - False - - True - - -.. note:: - - While logging tensor metrics with ``on_epoch=True`` inside step-level hooks and using mean-reduction (default) to accumulate the metrics across the current epoch, Lightning tries to extract the - batch size from the current batch. If multiple possible batch sizes are found, a warning is logged and if it fails to extract the batch size from the current batch, which is possible if - the batch is a custom structure/collection, then an error is raised. To avoid this, you can specify the ``batch_size`` inside the ``self.log(... batch_size=batch_size)`` call. - - .. code-block:: python - - def training_step(self, batch, batch_idx): - # extracts the batch size from `batch` - self.log("train_loss", loss, on_epoch=True) - - - def validation_step(self, batch, batch_idx): - # uses `batch_size=10` - self.log("val_loss", loss, batch_size=10) - -.. note:: - - - The above config for ``validation`` applies for ``test`` hooks as well. - - - Setting ``on_epoch=True`` will cache all your logged values during the full training epoch and perform a - reduction in ``on_train_epoch_end``. We recommend using `TorchMetrics `_, when working with custom reduction. - - - Setting both ``on_step=True`` and ``on_epoch=True`` will create two keys per metric you log with - suffix ``_step`` and ``_epoch`` respectively. You can refer to these keys e.g. in the `monitor` - argument of :class:`~pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint` or in the graphs plotted to the logger of your choice. - - -If your work requires to log in an unsupported method, please open an issue with a clear description of why it is blocking you. - - -Manual Logging Non-Scalar Artifacts -=================================== - -If you want to log anything that is not a scalar, like histograms, text, images, etc., you may need to use the logger object directly. - -.. code-block:: python - - def training_step(self): - ... - # the logger you used (in this case tensorboard) - tensorboard = self.logger.experiment - tensorboard.add_image() - tensorboard.add_histogram(...) - tensorboard.add_figure(...) - - ----------- - -******************** -Make a Custom Logger -******************** - -You can implement your own logger by writing a class that inherits from :class:`~pytorch_lightning.loggers.logger.Logger`. -Use the :func:`~pytorch_lightning.loggers.logger.rank_zero_experiment` and :func:`~pytorch_lightning.utilities.rank_zero.rank_zero_only` decorators to make sure that only the first process in DDP training creates the experiment and logs the data respectively. - -.. testcode:: - - from pytorch_lightning.loggers.logger import Logger, rank_zero_experiment - from pytorch_lightning.utilities.distributed import rank_zero_only - - - class MyLogger(Logger): - @property - def name(self): - return "MyLogger" - - @property - def version(self): - # Return the experiment version, int or str. - return "0.1" - - @rank_zero_only - def log_hyperparams(self, params): - # params is an argparse.Namespace - # your code to record hyperparameters goes here - pass - - @rank_zero_only - def log_metrics(self, metrics, step): - # metrics is a dictionary of metric names and values - # your code to record metrics goes here - pass - - @rank_zero_only - def save(self): - # Optional. Any code necessary to save logger data goes here - pass - - @rank_zero_only - def finalize(self, status): - # Optional. Any code that needs to be run after training - # finishes goes here - pass - -If you write a logger that may be useful to others, please send -a pull request to add it to Lightning! - ----------- - -.. _logging_frequency: - - -************************* -Control Logging Frequency -************************* - -Logging frequency -================= - -It may slow down training to log on every single batch. By default, Lightning logs every 50 rows, or 50 training steps. -To change this behaviour, set the ``log_every_n_steps`` :class:`~pytorch_lightning.trainer.trainer.Trainer` flag. - -.. testcode:: - - k = 10 - trainer = Trainer(log_every_n_steps=k) - - -Log Writing Frequency -===================== - -Individual logger implementations determine their flushing frequency. For example, on the -:class:`~pytorch_lightning.loggers.csv_logs.CSVLogger` you can set the flag ``flush_logs_every_n_steps``. - ----------- - -************ -Progress Bar -************ - -You can add any metric to the progress bar using :meth:`~pytorch_lightning.core.lightning.LightningModule.log` -method, setting ``prog_bar=True``. - - -.. code-block:: python - - def training_step(self, batch, batch_idx): - self.log("my_loss", loss, prog_bar=True) - - -You could learn more about progress bars supported by Lightning :doc:`here <../common/progress_bar>`. - -Modifying the Progress Bar -========================== - -The progress bar by default already includes the training loss and version number of the experiment -if you are using a logger. These defaults can be customized by overriding the -:meth:`~pytorch_lightning.callbacks.progress.base.ProgressBarBase.get_metrics` hook in your logger. - -.. code-block:: python - - from pytorch_lightning.callbacks.progress import Tqdm - - - class CustomProgressBar(Tqdm): - def get_metrics(self, *args, **kwargs): - # don't show the version number - items = super().get_metrics() - items.pop("v_num", None) - return items - - ----------- - - -************************* -Configure Console Logging -************************* - -Lightning logs useful information about the training process and user warnings to the console. -You can retrieve the Lightning console logger and change it to your liking. For example, adjust the logging level -or redirect output for certain modules to log files: - -.. testcode:: - - import logging - - # configure logging at the root level of Lightning - logging.getLogger("pytorch_lightning").setLevel(logging.ERROR) - - # configure logging on module level, redirect to file - logger = logging.getLogger("pytorch_lightning.core") - logger.addHandler(logging.FileHandler("core.log")) - -Read more about custom Python logging `here `_. - - ----------- - -*********************** -Logging Hyperparameters -*********************** - -When training a model, it is useful to know what hyperparams went into that model. -When Lightning creates a checkpoint, it stores a key ``"hyper_parameters"`` with the hyperparams. - -.. code-block:: python - - lightning_checkpoint = torch.load(filepath, map_location=lambda storage, loc: storage) - hyperparams = lightning_checkpoint["hyper_parameters"] - -Some loggers also allow logging the hyperparams used in the experiment. For instance, -when using the ``TensorBoardLogger``, all hyperparams will show -in the `hparams tab `_. - -.. note:: - If you want to track a metric in the tensorboard hparams tab, log scalars to the key ``hp_metric``. If tracking multiple metrics, initialize ``TensorBoardLogger`` with ``default_hp_metric=False`` and call ``log_hyperparams`` only once with your metric keys and initial values. Subsequent updates can simply be logged to the metric keys. Refer to the examples below for setting up proper hyperparams metrics tracking within the :doc:`LightningModule <../common/lightning_module>`. - - .. code-block:: python - - # Using default_hp_metric - def validation_step(self, batch, batch_idx): - self.log("hp_metric", some_scalar) - - - # Using custom or multiple metrics (default_hp_metric=False) - def on_train_start(self): - self.logger.log_hyperparams(self.hparams, {"hp/metric_1": 0, "hp/metric_2": 0}) - - - def validation_step(self, batch, batch_idx): - self.log("hp/metric_1", some_scalar_1) - self.log("hp/metric_2", some_scalar_2) - - In the example, using ``"hp/"`` as a prefix allows for the metrics to be grouped under "hp" in the tensorboard scalar tab where you can collapse them. - ------------ - -*************************** -Managing Remote Filesystems -*************************** - -Lightning supports saving logs to a variety of filesystems, including local filesystems and several cloud storage providers. - -Check out the :doc:`Remote Filesystems <../common/remote_fs>` doc for more info. diff --git a/source/extensions/loops.rst b/source/extensions/loops.rst deleted file mode 100644 index c24d4ce..0000000 --- a/source/extensions/loops.rst +++ /dev/null @@ -1,461 +0,0 @@ -.. _loop-customization-extensions: - - -Loops -===== - -Loops let advanced users swap out the default gradient descent optimization loop at the core of Lightning with a different optimization paradigm. - -The Lightning Trainer is built on top of the standard gradient descent optimization loop which works for 90%+ of machine learning use cases: - -.. code-block:: python - - for i, batch in enumerate(dataloader): - x, y = batch - y_hat = model(x) - loss = loss_function(y_hat, y) - optimizer.zero_grad() - loss.backward() - optimizer.step() - -However, some new research use cases such as meta-learning, active learning, recommendation systems, etc., require a different loop structure. -For example here is a simple loop that guides the weight updates with a loss from a special validation split: - -.. code-block:: python - - for i, batch in enumerate(train_dataloader): - x, y = batch - y_hat = model(x) - loss = loss_function(y_hat, y) - optimizer.zero_grad() - loss.backward() - - val_loss = 0 - for i, val_batch in enumerate(val_dataloader): - x, y = val_batch - y_hat = model(x) - val_loss += loss_function(y_hat, y) - - scale_gradients(model, 1 / val_loss) - optimizer.step() - - -With Lightning Loops, you can customize to non-standard gradient descent optimizations to get the same loop above: - -.. code-block:: python - - trainer = Trainer() - trainer.fit_loop.epoch_loop = MyGradientDescentLoop() - -Think of this as swapping out the engine in a car! - ----------- - -Understanding the Default Trainer Loop --------------------------------------- - -The Lightning :class:`~pytorch_lightning.trainer.trainer.Trainer` automates the standard optimization loop which every PyTorch user is familiar with: - -.. code-block:: python - - for i, batch in enumerate(dataloader): - x, y = batch - y_hat = model(x) - loss = loss_function(y_hat, y) - optimizer.zero_grad() - loss.backward() - optimizer.step() - -The core research logic is simply shifted to the :class:`~pytorch_lightning.core.lightning.LightningModule`: - -.. code-block:: python - - for i, batch in enumerate(dataloader): - # x, y = batch moved to training_step - # y_hat = model(x) moved to training_step - # loss = loss_function(y_hat, y) moved to training_step - loss = lightning_module.training_step(batch, i) - - # Lightning handles automatically: - optimizer.zero_grad() - loss.backward() - optimizer.step() - -Under the hood, the above loop is implemented using the :class:`~pytorch_lightning.loops.base.Loop` API like so: - -.. code-block:: python - - class DefaultLoop(Loop): - def advance(self, batch, i): - loss = lightning_module.training_step(batch, i) - optimizer.zero_grad() - loss.backward() - optimizer.step() - - def run(self, dataloader): - for i, batch in enumerate(dataloader): - self.advance(batch, i) - -Defining a loop within a class interface instead of hard-coding a raw Python for/while loop has several benefits: - -1. You can have full control over the data flow through loops. -2. You can add new loops and nest as many of them as you want. -3. If needed, the state of a loop can be :ref:`saved and resumed `. -4. New hooks can be injected at any point. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/loops/epoch-loop-steps.gif - :alt: Animation showing how to convert a standard training loop to a Lightning loop - ----------- - -.. _override-default-loops-extensions: - -Overriding the default Loops ----------------------------- - -The fastest way to get started with loops, is to override functionality of an existing loop. -Lightning has 4 main loops which relies on : :class:`~pytorch_lightning.loops.fit_loop.FitLoop` for fitting (training and validating), -:class:`~pytorch_lightning.loops.dataloader.evaluation_loop.EvaluationLoop` for validating or testing, -:class:`~pytorch_lightning.loops.dataloader.prediction_loop.PredictionLoop` for predicting. - -For simple changes that don't require a custom loop, you can modify each of these loops. - -Each loop has a series of methods that can be modified. -For example with the :class:`~pytorch_lightning.loops.fit_loop.FitLoop`: - -.. code-block:: python - - from pytorch_lightning.loops import FitLoop - - - class MyLoop(FitLoop): - def advance(self): - """Advance from one iteration to the next.""" - - def on_advance_end(self): - """Do something at the end of an iteration.""" - - def on_run_end(self): - """Do something when the loop ends.""" - -A full list with all built-in loops and subloops can be found :ref:`here `. - -To add your own modifications to a loop, simply subclass an existing loop class and override what you need. -Here is a simple example how to add a new hook: - -.. code-block:: python - - from pytorch_lightning.loops import FitLoop - - - class CustomFitLoop(FitLoop): - def advance(self): - # ... whatever code before - - # pass anything you want to the hook - self.trainer.call_hook("my_new_hook", *args, **kwargs) - - # ... whatever code after - -Now simply attach the correct loop in the trainer directly: - -.. code-block:: python - - trainer = Trainer(...) - trainer.fit_loop = CustomFitLoop() - - # fit() now uses the new FitLoop! - trainer.fit(...) - - # the equivalent for validate() - val_loop = CustomValLoop() - trainer = Trainer() - trainer.validate_loop = val_loop - trainer.validate(...) - -Now your code is FULLY flexible and you can still leverage ALL the best parts of Lightning! - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/loops/replace-fit-loop.gif - :alt: Animation showing how to replace a loop on the Trainer - ----------- - -Creating a New Loop From Scratch --------------------------------- - -You can also go wild and implement a full loop from scratch by sub-classing the :class:`~pytorch_lightning.loops.base.Loop` base class. -You will need to override a minimum of two things: - -.. code-block:: python - - from pytorch_lightning.loop import Loop - - - class MyFancyLoop(Loop): - @property - def done(self): - """Provide a condition to stop the loop.""" - - def advance(self): - """ - Access your dataloader/s in whatever way you want. - Do your fancy optimization things. - Call the LightningModule methods at your leisure. - """ - -Finally, attach it into the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - trainer = Trainer(...) - trainer.fit_loop = MyFancyLoop() - - # fit() now uses your fancy loop! - trainer.fit(...) - -But beware: Loop customization gives you more power and full control over the Trainer and with great power comes great responsibility. -We recommend that you familiarize yourself with :ref:`overriding the default loops ` first before you start building a new loop from the ground up. - ----------- - -Loop API --------- -Here is the full API of methods available in the Loop base class. - -The :class:`~pytorch_lightning.loops.base.Loop` class is the base of all loops in the same way as the :class:`~pytorch_lightning.core.lightning.LightningModule` is the base of all models. -It defines a public interface that each loop implementation must follow, the key ones are: - -Properties -^^^^^^^^^^ - -done -~~~~ - -.. autoattribute:: pytorch_lightning.loops.base.Loop.done - :noindex: - -skip (optional) -~~~~~~~~~~~~~~~ - -.. autoattribute:: pytorch_lightning.loops.base.Loop.skip - :noindex: - -Methods -^^^^^^^ - -reset (optional) -~~~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.loops.base.Loop.reset - :noindex: - -advance -~~~~~~~ - -.. automethod:: pytorch_lightning.loops.base.Loop.advance - :noindex: - -run (optional) -~~~~~~~~~~~~~~ - -.. automethod:: pytorch_lightning.loops.base.Loop.run - :noindex: - - ----------- - -Subloops --------- - -When you want to customize nested loops within loops, use the :meth:`~pytorch_lightning.loops.base.Loop.replace` method: - -.. code-block:: python - - # This takes care of properly instantiating the new Loop and setting all references - trainer.fit_loop.replace(epoch_loop=MyEpochLoop) - # Trainer runs the fit loop with your new epoch loop! - trainer.fit(model) - -Alternatively, for more fine-grained control, use the :meth:`~pytorch_lightning.loops.base.Loop.connect` method: - -.. code-block:: python - - # Optional: stitch back the trainer arguments - epoch_loop = MyEpochLoop(trainer.fit_loop.epoch_loop.min_steps, trainer.fit_loop.epoch_loop.max_steps) - # Optional: connect children loops as they might have existing state - epoch_loop.connect(trainer.fit_loop.epoch_loop.batch_loop, trainer.fit_loop.epoch_loop.val_loop) - # Instantiate and connect the loop. - trainer.fit_loop.connect(epoch_loop=epoch_loop) - trainer.fit(model) - -More about the built-in loops and how they are composed is explained in the next section. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/loops/connect-epoch-loop.gif - :alt: Animation showing how to connect a custom subloop - ----------- - -Built-in Loops --------------- - -.. _loop-structure-extensions: - -The training loop in Lightning is called *fit loop* and is actually a combination of several loops. -Here is what the structure would look like in plain Python: - -.. code-block:: python - - # FitLoop - for epoch in range(max_epochs): - - # TrainingEpochLoop - for batch_idx, batch in enumerate(train_dataloader): - - # TrainingBatchLoop - for split_batch in tbptt_split(batch): - - # OptimizerLoop - for optimizer_idx, opt in enumerate(optimizers): - - loss = lightning_module.training_step(batch, batch_idx, optimizer_idx) - ... - - # ValidationEpochLoop - for batch_idx, batch in enumerate(val_dataloader): - lightning_module.validation_step(batch, batch_idx, optimizer_idx) - ... - - -Each of these :code:`for`-loops represents a class implementing the :class:`~pytorch_lightning.loops.base.Loop` interface. - - -.. list-table:: Trainer entry points and associated loops - :widths: 25 75 - :header-rows: 1 - - * - Built-in loop - - Description - * - :class:`~pytorch_lightning.loops.fit_loop.FitLoop` - - The :class:`~pytorch_lightning.loops.fit_loop.FitLoop` is the top-level loop where training starts. - It simply counts the epochs and iterates from one to the next by calling :code:`TrainingEpochLoop.run()` in its :code:`advance()` method. - * - :class:`~pytorch_lightning.loops.epoch.training_epoch_loop.TrainingEpochLoop` - - The :class:`~pytorch_lightning.loops.epoch.training_epoch_loop.TrainingEpochLoop` is the one that iterates over the dataloader that the user returns in their :meth:`~pytorch_lightning.core.lightning.LightningModule.train_dataloader` method. - Its main responsibilities are calling the :code:`*_epoch_start` and :code:`*_epoch_end` hooks, accumulating outputs if the user request them in one of these hooks, and running validation at the requested interval. - The validation is carried out by yet another loop, :class:`~pytorch_lightning.loops.epoch.validation_epoch_loop.ValidationEpochLoop`. - - In the :code:`run()` method, the training epoch loop could in theory simply call the :code:`LightningModule.training_step` already and perform the optimization. - However, Lightning has built-in support for automatic optimization with multiple optimizers and on top of that also supports :ref:`TBPTT `. - For this reason there are actually two more loops nested under :class:`~pytorch_lightning.loops.epoch.training_epoch_loop.TrainingEpochLoop`. - * - :class:`~pytorch_lightning.loops.batch.training_batch_loop.TrainingBatchLoop` - - The responsibility of the :class:`~pytorch_lightning.loops.batch.training_batch_loop.TrainingBatchLoop` is to split a batch given by the :class:`~pytorch_lightning.loops.epoch.training_epoch_loop.TrainingEpochLoop` along the time-dimension and iterate over the list of splits. - It also keeps track of the hidden state *hiddens* returned by the training step. - By default, when truncated back-propagation through time (TBPTT) is turned off, this loop does not do anything except redirect the call to the :class:`~pytorch_lightning.loops.optimization.optimizer_loop.OptimizerLoop`. - Read more about :ref:`TBPTT `. - * - :class:`~pytorch_lightning.loops.optimization.optimizer_loop.OptimizerLoop` - - The :class:`~pytorch_lightning.loops.optimization.optimizer_loop.OptimizerLoop` iterates over one or multiple optimizers and for each one it calls the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` method with the batch, the current batch index and the optimizer index if multiple optimizers are requested. - It is the leaf node in the tree of loops and performs the actual optimization (forward, zero grad, backward, optimizer step). - * - :class:`~pytorch_lightning.loops.optimization.manual_loop.ManualOptimization` - - Substitutes the :class:`~pytorch_lightning.loops.optimization.optimizer_loop.OptimizerLoop` in case of :doc:`manual optimization <../model/manual_optimization>` and implements the manual optimization step. - * - :class:`~pytorch_lightning.loops.dataloader.evaluation_loop.EvaluationLoop` - - The :class:`~pytorch_lightning.loops.dataloader.evaluation_loop.EvaluationLoop` is the top-level loop where validation/testing starts. - It simply iterates over each evaluation dataloader from one to the next by calling :code:`EvaluationEpochLoop.run()` in its :code:`advance()` method. - * - :class:`~pytorch_lightning.loops.dataloader.prediction_loop.PredictionLoop` - - The :class:`~pytorch_lightning.loops.dataloader.prediction_loop.PredictionLoop` is the top-level loop where prediction starts. - It simply iterates over each prediction dataloader from one to the next by calling :code:`PredictionEpochLoop.run()` in its :code:`advance()` method. - - ----------- - -Available Loops in Lightning Flash ----------------------------------- - -`Active Learning `__ is a machine learning practice in which the user interacts with the learner in order to provide new labels when required. - -You can find a real use case in `Lightning Flash `_. - -Flash implements the :code:`ActiveLearningLoop` that you can use together with the :code:`ActiveLearningDataModule` to label new data on the fly. -To run the following demo, install Flash and `BaaL `__ first: - -.. code-block:: bash - - pip install lightning-flash baal - -.. code-block:: python - - import torch - - import flash - from flash.core.classification import Probabilities - from flash.core.data.utils import download_data - from flash.image import ImageClassificationData, ImageClassifier - from flash.image.classification.integrations.baal import ActiveLearningDataModule, ActiveLearningLoop - - # 1. Create the DataModule - download_data("https://pl-flash-data.s3.amazonaws.com/hymenoptera_data.zip", "./data") - - # Implement the research use-case where we mask labels from labelled dataset. - datamodule = ActiveLearningDataModule( - ImageClassificationData.from_folders(train_folder="data/hymenoptera_data/train/", batch_size=2), - initial_num_labels=5, - val_split=0.1, - ) - - # 2. Build the task - head = torch.nn.Sequential( - torch.nn.Dropout(p=0.1), - torch.nn.Linear(512, datamodule.num_classes), - ) - model = ImageClassifier(backbone="resnet18", head=head, num_classes=datamodule.num_classes, output=Probabilities()) - - - # 3.1 Create the trainer - trainer = flash.Trainer(max_epochs=3) - - # 3.2 Create the active learning loop and connect it to the trainer - active_learning_loop = ActiveLearningLoop(label_epoch_frequency=1) - active_learning_loop.connect(trainer.fit_loop) - trainer.fit_loop = active_learning_loop - - # 3.3 Finetune - trainer.finetune(model, datamodule=datamodule, strategy="freeze") - - # 4. Predict what's on a few images! ants or bees? - predictions = model.predict("data/hymenoptera_data/val/bees/65038344_52a45d090d.jpg") - print(predictions) - - # 5. Save the model! - trainer.save_checkpoint("image_classification_model.pt") - -Here is the `Active Learning Loop example `_ and the `code for the active learning loop `_. - - ----------- - -Advanced Examples ------------------ - - -.. list-table:: Ready-to-run loop examples and tutorials - :widths: 25 75 - :header-rows: 1 - - * - Link to Example - - Description - * - `K-fold Cross Validation `_ - - `KFold / Cross Validation `__ is a machine learning practice in which the training dataset is being partitioned into ``num_folds`` complementary subsets. - One cross validation round will perform fitting where one fold is left out for validation and the other folds are used for training. - To reduce variability, once all rounds are performed using the different folds, the trained models are ensembled and their predictions are - averaged when estimating the model's predictive performance on the test dataset. - * - `Yielding Training Step `_ - - This loop enables you to write the :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` hook - as a Python Generator for automatic optimization with multiple optimizers, i.e., you can :code:`yield` loss - values from it instead of returning them. This can enable more elegant and expressive implementations, as shown - shown with a GAN in this example. - - ----------- - -Advanced Features ------------------ - -Next: :doc:`Advanced loop features <../extensions/loops_advanced>` diff --git a/source/extensions/loops_advanced.rst b/source/extensions/loops_advanced.rst deleted file mode 100644 index e71c827..0000000 --- a/source/extensions/loops_advanced.rst +++ /dev/null @@ -1,41 +0,0 @@ -:orphan: - -Loops (Advanced) -================ - -.. _persisting loop state: - -Persisting the State of Loops ------------------------------ - -.. note:: - - This is an experimental feature and is not activated by default. - Set the environment variable `PL_FAULT_TOLERANT_TRAINING = 1` to enable saving the progress of loops. - Read more about :doc:`fault-tolerant training <../clouds/fault_tolerant_training>`. - -A powerful property of the class-based loop interface is that it can own an internal state. -Loop instances can save their state to the checkpoint through corresponding hooks and if implemented accordingly, resume the state of execution at the appropriate place. -This design is particularly interesting for fault-tolerant training which is an experimental feature released in Lightning v1.5. - -The two hooks :meth:`~pytorch_lightning.loops.base.Loop.on_save_checkpoint` and :meth:`~pytorch_lightning.loops.base.Loop.on_load_checkpoint` function very similarly to how LightningModules and Callbacks save and load state. - -.. code-block:: python - - def on_save_checkpoint(self): - state_dict["iteration"] = self.iteration - return state_dict - - - def on_load_checkpoint(self, state_dict): - self.iteration = state_dict["iteration"] - -When the Trainer is restarting from a checkpoint (e.g., through :code:`trainer.fit(ckpt_path=...)`), the loop exposes a boolean attribute :attr:`~pytorch_lightning.loops.base.Loop.restarting`. -Based around the value of this variable, the user can write the loop in such a way that it can restart from an arbitrary point given the state loaded from the checkpoint. -For example, the implementation of the :meth:`~pytorch_lightning.loops.base.Loop.reset` method could look like this given our previous example: - -.. code-block:: python - - def reset(self): - if not self.restarting: - self.iteration = 0 diff --git a/source/extensions/strategy.rst b/source/extensions/strategy.rst deleted file mode 100644 index ad9d799..0000000 --- a/source/extensions/strategy.rst +++ /dev/null @@ -1,122 +0,0 @@ -:orphan: - -################### -What is a Strategy? -################### - -Strategy controls the model distribution across training, evaluation, and prediction to be used by the :doc:`Trainer <../common/trainer>`. It can be controlled by passing different -strategy with aliases (``"ddp"``, ``"ddp_spawn"``, ``"deepspeed"`` and so on) as well as a custom strategy to the ``strategy`` parameter for Trainer. - -The Strategy in PyTorch Lightning handles the following responsibilities: - -* Launch and teardown of training processes (if applicable). -* Setup communication between processes (NCCL, GLOO, MPI, and so on). -* Provide a unified communication interface for reduction, broadcast, and so on. -* Owns the :class:`~pytorch_lightning.core.lightning.LightningModule` -* Handles/owns optimizers and schedulers. - - -:class:`~pytorch_lightning.strategies.strategy.Strategy` also manages the accelerator, precision, and checkpointing plugins. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/strategies/overview.jpeg - :alt: Illustration of the Strategy as a composition of the Accelerator and several plugins - -We expose Strategies mainly for expert users that want to extend Lightning for new hardware support or new distributed backends (e.g. a backend not yet supported by `PyTorch `_ itself). - - ----- - -########################### -Enable Different Strategies -########################### - -.. code-block:: python - - # Training with the DistributedDataParallel strategy on 4 GPUs - trainer = Trainer(strategy="ddp", accelerator="gpu", devices=4) - - # Training with the custom DistributedDataParallel strategy on 4 GPUs - trainer = Trainer(strategy=DDPStrategy(...), accelerator="gpu", devices=4) - - # Training with the DDP Spawn strategy using auto accelerator selection - trainer = Trainer(strategy="ddp_spawn", accelerator="auto", devices=4) - - # Training with the DeepSpeed strategy on available GPUs - trainer = Trainer(strategy="deepspeed", accelerator="gpu", devices="auto") - - # Training with the DDP strategy using 3 CPU processes - trainer = Trainer(strategy="ddp", accelerator="cpu", devices=3) - - # Training with the DDP Spawn strategy on 8 TPU cores - trainer = Trainer(strategy="ddp_spawn", accelerator="tpu", devices=8) - - # Training with the default IPU strategy on 8 IPUs - trainer = Trainer(accelerator="ipu", devices=8) - ----- - -######################## -Create a Custom Strategy -######################## - -Expert users may choose to extend an existing strategy by overriding its methods. - -.. code-block:: python - - from pytorch_lightning.strategies import DDPStrategy - - - class CustomDDPStrategy(DDPStrategy): - def configure_ddp(self): - self.model = MyCustomDistributedDataParallel( - self.model, - device_ids=..., - ) - -or by subclassing the base class :class:`~pytorch_lightning.strategies.Strategy` to create new ones. These custom strategies -can then be passed into the ``Trainer`` directly via the ``strategy`` parameter. - -.. code-block:: python - - # custom plugins - trainer = Trainer(strategy=CustomDDPStrategy()) - - # fully custom accelerator and plugins - accelerator = MyAccelerator() - precision_plugin = MyPrecisionPlugin() - training_strategy = CustomDDPStrategy(accelerator=accelerator, precision_plugin=precision_plugin) - trainer = Trainer(strategy=training_strategy) - - -The complete list of built-in strategies is listed below. - ----- - -############################# -Available Training Strategies -############################# - -.. currentmodule:: pytorch_lightning.strategies - -.. autosummary:: - :nosignatures: - :template: classtemplate.rst - - BaguaStrategy - DDP2Strategy - DDPFullyShardedStrategy - DDPShardedStrategy - DDPSpawnShardedStrategy - DDPSpawnStrategy - DDPStrategy - DataParallelStrategy - DeepSpeedStrategy - HorovodStrategy - HPUParallelStrategy - IPUStrategy - ParallelStrategy - SingleDeviceStrategy - SingleHPUStrategy - SingleTPUStrategy - Strategy - TPUSpawnStrategy diff --git a/source/guides/data.rst b/source/guides/data.rst deleted file mode 100644 index 72dba27..0000000 --- a/source/guides/data.rst +++ /dev/null @@ -1,423 +0,0 @@ -:orphan: - -.. _data: - -############# -Managing Data -############# - -**************************** -Data Containers in Lightning -**************************** - -There are a few different data containers used in Lightning: - -.. list-table:: Data objects - :widths: 20 80 - :header-rows: 1 - - * - Object - - Definition - * - :class:`~torch.utils.data.Dataset` - - The PyTorch :class:`~torch.utils.data.Dataset` represents a map from keys to data samples. - * - :class:`~torch.utils.data.IterableDataset` - - The PyTorch :class:`~torch.utils.data.IterableDataset` represents a stream of data. - * - :class:`~torch.utils.data.DataLoader` - - The PyTorch :class:`~torch.utils.data.DataLoader` represents a Python iterable over a Dataset. - * - :class:`~pytorch_lightning.core.datamodule.LightningDataModule` - - A :class:`~pytorch_lightning.core.datamodule.LightningDataModule` is simply a collection of: training DataLoader(s), validation DataLoader(s), test DataLoader(s) and predict DataLoader(s), along with the matching transforms and data processing/downloads steps required. - - -Why Use LightningDataModule? -============================ - -The :class:`~pytorch_lightning.core.datamodule.LightningDataModule` was designed as a way of decoupling data-related hooks from the :class:`~pytorch_lightning.core.lightning.LightningModule` so you can develop dataset agnostic models. The :class:`~pytorch_lightning.core.datamodule.LightningDataModule` makes it easy to hot swap different Datasets with your model, so you can test it and benchmark it across domains. It also makes sharing and reusing the exact data splits and transforms across projects possible. - -Read :ref:`this ` for more details on LightningDataModule. - ---------- - -.. _multiple-dataloaders: - -***************** -Multiple Datasets -***************** - -There are a few ways to pass multiple Datasets to Lightning: - -1. Create a DataLoader that iterates over multiple Datasets under the hood. -2. In the training loop, you can pass multiple DataLoaders as a dict or list/tuple, and Lightning will - automatically combine the batches from different DataLoaders. -3. In the validation, test, or prediction, you have the option to return multiple DataLoaders as list/tuple, which Lightning will call sequentially - or combine the DataLoaders using :class:`~pytorch_lightning.trainer.supporters.CombinedLoader`, which Lightning will - automatically combine the batches from different DataLoaders. - - -Using LightningDataModule -========================= - -You can set more than one :class:`~torch.utils.data.DataLoader` in your :class:`~pytorch_lightning.core.datamodule.LightningDataModule` using its DataLoader hooks -and Lightning will use the correct one. - -.. testcode:: - - class DataModule(LightningDataModule): - - ... - - def train_dataloader(self): - return DataLoader(self.train_dataset) - - def val_dataloader(self): - return [DataLoader(self.val_dataset_1), DataLoader(self.val_dataset_2)] - - def test_dataloader(self): - return DataLoader(self.test_dataset) - - def predict_dataloader(self): - return DataLoader(self.predict_dataset) - - -Using LightningModule Hooks -=========================== - -Concatenated Dataset --------------------- - -For training with multiple Datasets, you can create a :class:`~torch.utils.data.DataLoader` class -which wraps your multiple Datasets using :class:`~torch.utils.data.ConcatDataset`. This, of course, -also works for testing, validation, and prediction Datasets. - -.. testcode:: - - from torch.utils.data import ConcatDataset - - - class LitModel(LightningModule): - def train_dataloader(self): - concat_dataset = ConcatDataset(datasets.ImageFolder(traindir_A), datasets.ImageFolder(traindir_B)) - - loader = DataLoader( - concat_dataset, batch_size=args.batch_size, shuffle=True, num_workers=args.workers, pin_memory=True - ) - return loader - - def val_dataloader(self): - # SAME - ... - - def test_dataloader(self): - # SAME - ... - - -Return Multiple DataLoaders ---------------------------- - -You can set multiple DataLoaders in your :class:`~pytorch_lightning.core.lightning.LightningModule`, and Lightning will take care of batch combination. - -For more details, refer to :paramref:`~pytorch_lightning.trainer.trainer.Trainer.multiple_trainloader_mode` - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - - loader_a = DataLoader(range(6), batch_size=4) - loader_b = DataLoader(range(15), batch_size=5) - - # pass loaders as a dict. This will create batches like this: - # {'a': batch from loader_a, 'b': batch from loader_b} - loaders = {"a": loader_a, "b": loader_b} - - # OR: - # pass loaders as sequence. This will create batches like this: - # [batch from loader_a, batch from loader_b] - loaders = [loader_a, loader_b] - - return loaders - -Furthermore, Lightning also supports nested lists and dicts (or a combination). - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - - loader_a = DataLoader(range(8), batch_size=4) - loader_b = DataLoader(range(16), batch_size=2) - - return {"a": loader_a, "b": loader_b} - - def training_step(self, batch, batch_idx): - # access a dictionary with a batch from each DataLoader - batch_a = batch["a"] - batch_b = batch["b"] - - -.. testcode:: - - class LitModel(LightningModule): - def train_dataloader(self): - - loader_a = DataLoader(range(8), batch_size=4) - loader_b = DataLoader(range(16), batch_size=4) - loader_c = DataLoader(range(32), batch_size=4) - loader_c = DataLoader(range(64), batch_size=4) - - # pass loaders as a nested dict. This will create batches like this: - loaders = {"loaders_a_b": [loader_a, loader_b], "loaders_c_d": {"c": loader_c, "d": loader_d}} - return loaders - - def training_step(self, batch, batch_idx): - # access the data - batch_a_b = batch["loaders_a_b"] - batch_c_d = batch["loaders_c_d"] - - batch_a = batch_a_b[0] - batch_b = batch_a_b[1] - - batch_c = batch_c_d["c"] - batch_d = batch_c_d["d"] - -Alternatively, you can also pass in a :class:`~pytorch_lightning.trainer.supporters.CombinedLoader` containing multiple DataLoaders. - -.. testcode:: - - from pytorch_lightning.trainer.supporters import CombinedLoader - - - def train_dataloader(self): - loader_a = DataLoader() - loader_b = DataLoader() - loaders = {"a": loader_a, "b": loader_b} - combined_loader = CombinedLoader(loaders, mode="max_size_cycle") - return combined_loader - - - def training_step(self, batch, batch_idx): - batch_a = batch["a"] - batch_b = batch["b"] - - -Multiple Validation/Test/Predict DataLoaders -============================================ - -For validation, test and predict DataLoaders, you can pass a single DataLoader or a list of them. This optional named -parameter can be used in conjunction with any of the above use cases. You can choose to pass -the batches sequentially or simultaneously, as is done for the training step. -The default mode for these DataLoaders is sequential. Note that when using a sequence of DataLoaders you need -to add an additional argument ``dataloader_idx`` in their corresponding step specific hook. The corresponding loop will process -the DataLoaders in sequential order; that is, the first DataLoader will be processed completely, then the second one, and so on. - -Refer to the following for more details for the default sequential option: - -- :meth:`~pytorch_lightning.core.hooks.DataHooks.val_dataloader` -- :meth:`~pytorch_lightning.core.hooks.DataHooks.test_dataloader` -- :meth:`~pytorch_lightning.core.hooks.DataHooks.predict_dataloader` - -.. testcode:: - - def val_dataloader(self): - loader_1 = DataLoader() - loader_2 = DataLoader() - return [loader_1, loader_2] - - - def validation_step(self, batch, batch_idx, dataloader_idx): - ... - - -Evaluation DataLoaders are iterated over sequentially. If you want to iterate over them in parallel, PyTorch Lightning provides a :class:`~pytorch_lightning.trainer.supporters.CombinedLoader` object which supports collections of DataLoaders such as list, tuple, or dictionary. The DataLoaders can be accessed using in the same way as the provided structure: - -.. testcode:: - - from pytorch_lightning.trainer.supporters import CombinedLoader - - - def val_dataloader(self): - loader_a = DataLoader() - loader_b = DataLoader() - loaders = {"a": loader_a, "b": loader_b} - combined_loaders = CombinedLoader(loaders, mode="max_size_cycle") - return combined_loaders - - - def validation_step(self, batch, batch_idx): - batch_a = batch["a"] - batch_b = batch["b"] - - -Evaluate with Additional DataLoaders -==================================== - -You can evaluate your models using additional DataLoaders even if the DataLoader specific hooks haven't been defined within your -:class:`~pytorch_lightning.core.lightning.LightningModule`. For example, this would be the case if your test data -set is not available at the time your model was declared. Simply pass the test set to the :meth:`~pytorch_lightning.trainer.trainer.Trainer.test` method: - -.. code-block:: python - - # setup your DataLoader - test = DataLoader(...) - - # test (pass in the loader) - trainer.test(dataloaders=test) - --------------- - -******************************************** -Accessing DataLoaders within LightningModule -******************************************** - -In the case that you require access to the DataLoader or Dataset objects, DataLoaders for each step can be accessed using the ``Trainer`` object: - -.. testcode:: - - from pytorch_lightning import LightningModule - - - class Model(LightningModule): - def test_step(self, batch, batch_idx, dataloader_idx): - test_dl = self.trainer.test_dataloaders[dataloader_idx] - test_dataset = test_dl.dataset - test_sampler = test_dl.sampler - ... - # extract metadata, etc. from the dataset: - ... - -If you are using a :class:`~pytorch_lightning.trainer.supporters.CombinedLoader` object which allows you to fetch batches from a collection of DataLoaders -simultaneously which supports collections of DataLoader such as list, tuple, or dictionary. The DataLoaders can be accessed using the same collection structure: - -.. code-block:: python - - from pytorch_lightning.trainer.supporters import CombinedLoader - - test_dl1 = ... - test_dl2 = ... - - # If you provided a list of DataLoaders: - - combined_loader = CombinedLoader([test_dl1, test_dl2]) - list_of_loaders = combined_loader.loaders - test_dl1 = list_of_loaders.loaders[0] - - - # If you provided dictionary of DataLoaders: - - combined_loader = CombinedLoader({"dl1": test_dl1, "dl2": test_dl2}) - dictionary_of_loaders = combined_loader.loaders - test_dl1 = dictionary_of_loaders["dl1"] - --------------- - -.. _sequential-data: - -*************** -Sequential Data -*************** - -Lightning has built in support for dealing with sequential data. - - -Packed Sequences as Inputs -========================== - -When using :class:`~torch.nn.utils.rnn.PackedSequence`, do two things: - -1. Return either a padded tensor in dataset or a list of variable length tensors in the DataLoader's `collate_fn `_ (example shows the list implementation). -2. Pack the sequence in forward or training and validation steps depending on use case. - -| - -.. testcode:: - - # For use in DataLoader - def collate_fn(batch): - x = [item[0] for item in batch] - y = [item[1] for item in batch] - return x, y - - - # In LightningModule - def training_step(self, batch, batch_idx): - x = rnn.pack_sequence(batch[0], enforce_sorted=False) - y = rnn.pack_sequence(batch[1], enforce_sorted=False) - - -Truncated Backpropagation Through Time (TBPTT) -============================================== - -There are times when multiple backwards passes are needed for each batch. -For example, it may save memory to use **Truncated Backpropagation Through Time** when training RNNs. - -Lightning can handle TBPTT automatically via this flag. - -.. testcode:: - - from pytorch_lightning import LightningModule - - - class MyModel(LightningModule): - def __init__(self): - super().__init__() - # Important: This property activates truncated backpropagation through time - # Setting this value to 2 splits the batch into sequences of size 2 - self.truncated_bptt_steps = 2 - - # Truncated back-propagation through time - def training_step(self, batch, batch_idx, hiddens): - # the training step must be updated to accept a ``hiddens`` argument - # hiddens are the hiddens from the previous truncated backprop step - out, hiddens = self.lstm(data, hiddens) - return {"loss": ..., "hiddens": hiddens} - -.. note:: If you need to modify how the batch is split, - override :func:`~pytorch_lightning.core.lightning.LightningModule.tbptt_split_batch`. - - -Iterable Datasets -================= -Lightning supports using :class:`~torch.utils.data.IterableDataset` as well as map-style Datasets. IterableDatasets provide a more natural -option when using sequential data. - -.. note:: When using an :class:`~torch.utils.data.IterableDataset` you must set the ``val_check_interval`` to 1.0 (the default) or an int - (specifying the number of training batches to run before each validation loop) when initializing the Trainer. This is - because the IterableDataset does not have a ``__len__`` and Lightning requires this to calculate the validation - interval when ``val_check_interval`` is less than one. Similarly, you can set ``limit_{mode}_batches`` to a float or - an int. If it is set to 0.0 or 0, it will set ``num_{mode}_batches`` to 0, if it is an int, it will set ``num_{mode}_batches`` - to ``limit_{mode}_batches``, if it is set to 1.0 it will run for the whole dataset, otherwise it will throw an exception. - Here ``mode`` can be train/val/test/predict. - -When iterable datasets are used, Lightning will pre-fetch 1 batch (in addition to the current batch) so it can detect -when the training will stop and run validation if necessary. - -.. testcode:: - - # IterableDataset - class CustomDataset(IterableDataset): - def __init__(self, data): - self.data_source = data - - def __iter__(self): - return iter(self.data_source) - - - # Setup DataLoader - def train_dataloader(self): - seq_data = ["A", "long", "time", "ago", "in", "a", "galaxy", "far", "far", "away"] - iterable_dataset = CustomDataset(seq_data) - - dataloader = DataLoader(dataset=iterable_dataset, batch_size=5) - return dataloader - - -.. testcode:: - - # Set val_check_interval - trainer = Trainer(val_check_interval=100) - - # Set limit_val_batches to 0.0 or 0 - trainer = Trainer(limit_val_batches=0.0) - - # Set limit_val_batches as an int - trainer = Trainer(limit_val_batches=100) diff --git a/source/index.rst b/source/index.rst deleted file mode 100644 index d646beb..0000000 --- a/source/index.rst +++ /dev/null @@ -1,275 +0,0 @@ -.. PyTorch-Lightning documentation master file, created by - sphinx-quickstart on Fri Nov 15 07:48:22 2019. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -⚡ PyTorch Lightning에 오신 것을 환영합니다! -============================================== - -.. twocolumns:: - :left: - .. image:: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/mov.gif - :alt: Animation showing how to convert a standard training loop to a Lightning loop - :right: - PyTorch Lightning(파이토치 라이트닝))은 대규모에서 성능을 포기하지 않으면서 최대한의 유연성을 필요로 하는 전문적인 AI 연구자들과 머신러닝 엔지니어들을 위한 딥러닝 프레임워크입니다. - Lightning(라이트닝)은 프로젝트가 생각으로부터 문서 / 제품화에 이르는 동안 함께 발전합니다. - -.. raw:: html - -
-
-
-
- -.. join_slack:: - :align: center - :margin: 0 - -.. raw:: html - -
-
- - -.. raw:: html - -
- - -Lightning 설치하기 ----------------------- - - -.. raw:: html - -
-
- -Pip 사용자라면, - -.. code-block:: bash - - pip install pytorch-lightning - -.. raw:: html - -
-
- -Conda 사용자라면, - -.. code-block:: bash - - conda install pytorch-lightning -c conda-forge - -.. raw:: html - -
-
- -또는 `advanced install guide `_ 참조하세요. - -.. raw:: html - -
- -처음이신가요? ------------------ - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. customcalloutitem:: - :header: LIGHTNING 15분 만에 배워보기 - :description: 일반적인 Lightning 워크플로우의 주요한 7단계를 배웁니다. - :button_link: starter/introduction.html - -.. customcalloutitem:: - :header: Benchmarking - :description: Learn how to benchmark PyTorch Lightning. - :button_link: benchmarking/benchmarks.html - -.. raw:: html - -
-
- -.. End of callout item section - -.. raw:: html - -
- -이미 Lightning 사용자라면? ---------------------------- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. customcalloutitem:: - :description: Learn Lightning in small bites at 4 levels of expertise: Introductory, intermediate, advanced and expert. - :header: Level Up! - :button_link: expertise_levels.html - -.. customcalloutitem:: - :description: Detailed description of API each package. Assumes you already have basic Lightning knowledge. - :header: API Reference - :button_link: api_references.html - -.. customcalloutitem:: - :description: From NLP, Computer vision to RL and meta learning - see how to use Lightning in ALL research areas. - :header: Hands-on Examples - :button_link: tutorials.html - -.. customcalloutitem:: - :description: Learn how to do everything from hyperparameters sweeps to cloud training to Pruning and Quantization with Lightning. - :header: Common Workflows - :button_link: common_usecases.html - -.. customcalloutitem:: - :description: Convert your current code to Lightning - :header: Convert code to PyTorch Lightning - :button_link: starter/converting.html - - -.. raw:: html - -
-
- -.. End of callout item section - -.. raw:: html - -
- -.. toctree:: - :maxdepth: 1 - :name: start - :caption: Get Started - - starter/introduction - Organize existing PyTorch into Lightning - - -.. toctree:: - :maxdepth: 2 - :name: levels - :caption: Level Up - - levels/core_skills - levels/intermediate - levels/advanced - levels/expert - -.. toctree:: - :maxdepth: 2 - :name: pl_docs - :caption: Core API - - common/lightning_module - common/trainer - -.. toctree:: - :maxdepth: 1 - :name: Common Workflows - :caption: Common Workflows - - Avoid overfitting - model/build_model.rst - common/hyperparameters - common/progress_bar - deploy/production - advanced/training_tricks - cli/lightning_cli - tuning/profiler - Finetune a model - Manage experiments - clouds/cluster - advanced/model_parallel - clouds/cloud_training - Save and load model progress - Save memory with half-precision - Train on single or multiple GPUs - Train on single or multiple HPUs - Train on single or multiple IPUs - Train on single or multiple TPUs - model/own_your_loop - -.. toctree:: - :maxdepth: 1 - :name: Glossary - :caption: Glossary - - Accelerators - Callback - Checkpointing - Cluster - Cloud checkpoint - Console Logging - Debugging - Early stopping - Experiment manager (Logger) - Fault tolerant training - Flash - Grid AI - GPU - Half precision - HPU - Inference - IPU - Lightning CLI - Lightning Lite - LightningDataModule - LightningModule - Lightning Transformers - Log - Loops - TPU - Metrics - Model - Model Parallel - Plugins - Progress bar - Production - Predict - Profiler - Pruning and Quantization - Remote filesystem and FSSPEC - Strategy registry - Style guide - Sweep - SWA - SLURM - Transfer learning - Trainer - Torch distributed - -.. toctree:: - :maxdepth: 1 - :name: Hands-on Examples - :caption: Hands-on Examples - :glob: - - PyTorch Lightning 101 class - From PyTorch to PyTorch Lightning [Blog] - From PyTorch to PyTorch Lightning [Video] - - -.. raw:: html - -
- -색인 및 검색 ------------------- - -* :ref:`genindex` -* :ref:`search` diff --git a/source/levels/advanced.rst b/source/levels/advanced.rst deleted file mode 100644 index 4ffe090..0000000 --- a/source/levels/advanced.rst +++ /dev/null @@ -1,87 +0,0 @@ - -############### -Advanced skills -############### - -Configure all aspects of Lightning for advanced usecases. - -.. join_slack:: - :align: left - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 15: Customize configs to run in production - :description: Enable composable YAMLs - :col_css: col-md-6 - :button_link: advanced_level_15.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 16: Customize the trainer - :description: Inject custom code into the trainer and modify the progress bar. - :col_css: col-md-6 - :button_link: advanced_level_16.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 17: Own the training loop - :description: Learn all the ways of owning your raw PyTorch loops with Lighting. - :col_css: col-md-6 - :button_link: advanced_level_17.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 18: Enable advanced checkpointing - :description: Enable composable or cloud based checkpoints. - :col_css: col-md-6 - :button_link: advanced_level_18.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 19: Explore IPUs - :description: Explore Intelligence Processing Unit (IPU) for model scaling. - :col_css: col-md-6 - :button_link: advanced_level_19.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 20: Explore HPUs - :description: Explore Havana Gaudi Processing Unit (HPU) for model scaling. - :col_css: col-md-6 - :button_link: advanced_level_20.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 21: Master TPUs - :description: Master TPUs and run on cloud TPUs. - :col_css: col-md-6 - :button_link: advanced_level_21.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Level 22: Reach 1 trillion parameters on GPUs - :description: Scale to 1 trillion params on GPUs. - :col_css: col-md-6 - :button_link: advanced_level_22.html - :height: 150 - :tag: advanced - -.. raw:: html - -
-
diff --git a/source/levels/advanced_level_17.rst b/source/levels/advanced_level_17.rst deleted file mode 100644 index c05c8c9..0000000 --- a/source/levels/advanced_level_17.rst +++ /dev/null @@ -1,45 +0,0 @@ -:orphan: - -############################### -Level 17: Own the training loop -############################### - -Learn all the ways of owning your raw PyTorch loops with Lighting. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Enable manual optimization - :description: Gain control of the training loop with manual optimization and LightningModule methods. - :col_css: col-md-4 - :button_link: ../model/build_model_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Use a Raw PyTorch Loop - :description: Migrate complex PyTorch projects to Lightning and push bleeding-edge research with the raw PyTorch loop. - :col_css: col-md-4 - :button_link: ../model/build_model_expert.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Make a custom Lightning Loop - :description: Conduct bleeding-edge research like meta-learning and RL with a custom Loop. - :col_css: col-md-4 - :button_link: ../extensions/loops.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/source/levels/expert.rst b/source/levels/expert.rst deleted file mode 100644 index d41680b..0000000 --- a/source/levels/expert.rst +++ /dev/null @@ -1,63 +0,0 @@ - -############# -Expert skills -############# - -Customize and extend Lightning for things like custom hardware or distributed strategies. - -.. join_slack:: - :align: left - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 23: Extend the Lightning CLI - :description: Extend the functionality of the Lightning CLI. - :col_css: col-md-6 - :button_link: expert_level_23.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 24: Integrate a custom cluster - :description: Integrate a custom cluster into Lightning. - :col_css: col-md-6 - :button_link: expert_level_24.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 25: Explore fault-tolerance in-depth - :description: Understand the details of fault-tolerance. - :col_css: col-md-6 - :button_link: ../clouds/fault_tolerant_training_faq.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 26: Make your own profiler - :description: Make your own profiler. - :col_css: col-md-6 - :button_link: ../tuning/profiler_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Level 27: Add a new accelerator or Strategy - :description: Integrate a new accelerator or distributed strategy. - :col_css: col-md-6 - :button_link: expert_level_27.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/source/levels/expert_level_24.rst b/source/levels/expert_level_24.rst deleted file mode 100644 index b32a8ac..0000000 --- a/source/levels/expert_level_24.rst +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -#################################### -Level 24: Integrate a custom cluster -#################################### - -Extend the functionality of the Lightning CLI. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Integrate your own cluster - :description: Learn how to integrate your own cluster - :col_css: col-md-6 - :button_link: ../clouds/cluster_expert.html - :height: 150 - :tag: expert - -.. displayitem:: - :header: Run on your own cloud - :description: Learn how to run on your Company or University private clouds. - :col_css: col-md-6 - :button_link: ../clouds/run_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/source/levels/intermediate.rst b/source/levels/intermediate.rst deleted file mode 100644 index 331e477..0000000 --- a/source/levels/intermediate.rst +++ /dev/null @@ -1,89 +0,0 @@ - -################### -Intermediate skills -################### - -Learn to scale up your models and enable collaborative model development at academic or industry research labs. - -.. join_slack:: - :align: left - ----- - -.. include:: ../links.rst - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 7: Interactive cloud development - :description: Learn how to access GPUs and TPUs on the cloud. - :button_link: intermediate_level_7.html - :col_css: col-md-6 - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 8: Train in the background on the cloud - :description: Learn how to run models on the cloud in the background. - :button_link: intermediate_level_8.html - :col_css: col-md-6 - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 9: Modularize your projects - :description: Create DataModules to enable dataset reusability. - :col_css: col-md-6 - :button_link: intermediate_level_9.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 10: Understand your model - :description: Use advanced visuals to find the best performing model. - :col_css: col-md-6 - :button_link: intermediate_level_10.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 11: Explore SOTA scaling techniques - :description: Explore SOTA techniques to help convergence, stability and scalability. - :col_css: col-md-6 - :button_link: intermediate_level_11.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 12: Deploy your models - :description: Learn how to deploy your models with optimizations like ONNX and torchscript. - :col_css: col-md-6 - :button_link: intermediate_level_12.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 13: Optimize training speed - :description: Use advanced profilers to mixed precision to train bigger models, faster. - :col_css: col-md-6 - :button_link: intermediate_level_13.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: Level 14: Run on on-prem clusters - :description: Run on a custom on-prem cluster or SLURM cluster. - :col_css: col-md-6 - :button_link: intermediate_level_14.html - :height: 150 - :tag: intermediate - -.. raw:: html - -
-
diff --git a/source/levels/intermediate_level_8.rst b/source/levels/intermediate_level_8.rst deleted file mode 100644 index 190364a..0000000 --- a/source/levels/intermediate_level_8.rst +++ /dev/null @@ -1,53 +0,0 @@ -:orphan: - -################################ -Level 8: Run models on the cloud -################################ - -Learn to run models on the cloud in the background asynchroneously. - ----- - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Run a model in the background on the cloud - :description: Learn to run a model in the background on a cloud machine. - :col_css: col-md-6 - :button_link: ../clouds/run_basic.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 2: Save up to 80% on cloud costs with fault-tolerant training - :description: Run on the cloud for 1/10th the price with fault-tolerant training. - :col_css: col-md-6 - :button_link: ../clouds/fault_tolerant_training_basic.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 3: Run many models at once - :description: Run many models at once (sweep) to find the best performing model. - :col_css: col-md-6 - :button_link: ../clouds/run_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: 4: Run on your own cloud - :description: Learn how to run on your Company or University private clouds. - :col_css: col-md-6 - :button_link: ../clouds/run_expert.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/source/model/build_model.rst b/source/model/build_model.rst deleted file mode 100644 index 8d12110..0000000 --- a/source/model/build_model.rst +++ /dev/null @@ -1,55 +0,0 @@ -:orphan: - -############# -Build a Model -############# - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 1: Train a model - :description: Build a model to learn the basic ideas of Lightning - :col_css: col-md-4 - :button_link: train_model_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: 2: Validate and test a model - :description: Add a validation and test data split to avoid overfitting. - :col_css: col-md-4 - :button_link: validate_model_basic.html - :height: 150 - :tag: basic - -.. displayitem:: - :header: 3: Supercharge training - :description: Enable state-of-the-art training techniques with the Trainer features. - :col_css: col-md-4 - :button_link: build_model_intermediate.html - :height: 150 - :tag: intermediate - -.. displayitem:: - :header: LightningModule API - :description: Dig into LightningModule API in depth - :col_css: col-md-4 - :button_link: ../common/lightning_module.html#lightningmodule-api - :height: 150 - -.. displayitem:: - :header: Trainer API - :description: Dig into Trainer API in depth - :col_css: col-md-4 - :button_link: ../common/trainer.html#trainer-class-api - :height: 150 - -.. raw:: html - -
-
diff --git a/source/model/build_model_expert.rst b/source/model/build_model_expert.rst deleted file mode 100644 index f321e90..0000000 --- a/source/model/build_model_expert.rst +++ /dev/null @@ -1,7 +0,0 @@ -:orphan: - -######################### -Raw PyTorch loop (expert) -######################### - -.. include:: ../starter/lightning_lite.rst diff --git a/source/model/manual_optimization.rst b/source/model/manual_optimization.rst deleted file mode 100644 index e4a31dd..0000000 --- a/source/model/manual_optimization.rst +++ /dev/null @@ -1,290 +0,0 @@ -******************* -Manual Optimization -******************* - -For advanced research topics like reinforcement learning, sparse coding, or GAN research, it may be desirable to -manually manage the optimization process. - -This is only recommended for experts who need ultimate flexibility. -Lightning will handle only accelerator, precision and strategy logic. -The users are left with ``optimizer.zero_grad()``, gradient accumulation, model toggling, etc.. - -To manually optimize, do the following: - -* Set ``self.automatic_optimization=False`` in your ``LightningModule``'s ``__init__``. -* Use the following functions and call them manually: - - * ``self.optimizers()`` to access your optimizers (one or multiple) - * ``optimizer.zero_grad()`` to clear the gradients from the previous training step - * ``self.manual_backward(loss)`` instead of ``loss.backward()`` - * ``optimizer.step()`` to update your model parameters - -Here is a minimal example of manual optimization. - -.. testcode:: python - - from pytorch_lightning import LightningModule - - - class MyModel(LightningModule): - def __init__(self): - super().__init__() - # Important: This property activates manual optimization. - self.automatic_optimization = False - - def training_step(self, batch, batch_idx): - opt = self.optimizers() - opt.zero_grad() - loss = self.compute_loss(batch) - self.manual_backward(loss) - opt.step() - -.. warning:: - Before 1.2, ``optimizer.step()`` was calling ``optimizer.zero_grad()`` internally. - From 1.2, it is left to the user's expertise. - -.. tip:: - Be careful where you call ``optimizer.zero_grad()``, or your model won't converge. - It is good practice to call ``optimizer.zero_grad()`` before ``self.manual_backward(loss)``. - - -Access your Own Optimizer -========================= - -The provided ``optimizer`` is a :class:`~pytorch_lightning.core.optimizer.LightningOptimizer` object wrapping your own optimizer -configured in your :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`. You can access your own optimizer -with ``optimizer.optimizer``. However, if you use your own optimizer to perform a step, Lightning won't be able to -support accelerators, precision and profiling for you. - -.. testcode:: python - - class Model(LightningModule): - def __init__(self): - super().__init__() - self.automatic_optimization = False - ... - - def training_step(self, batch, batch_idx): - optimizer = self.optimizers() - - # `optimizer` is a `LightningOptimizer` wrapping the optimizer. - # To access it, do the following. - # However, it won't work on TPU, AMP, etc... - optimizer = optimizer.optimizer - ... - -Gradient Accumulation -===================== - -You can accumulate gradients over batches similarly to ``accumulate_grad_batches`` argument in -:ref:`Trainer ` for automatic optimization. To perform gradient accumulation with one optimizer -after every ``N`` steps, you can do as such. - -.. testcode:: python - - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - opt = self.optimizers() - - loss = self.compute_loss(batch) - self.manual_backward(loss) - - # accumulate gradients of N batches - if (batch_idx + 1) % N == 0: - opt.step() - opt.zero_grad() - - -Use Multiple Optimizers (like GANs) -=================================== - -Here is an example training a simple GAN with multiple optimizers using manual optimization. - -.. testcode:: python - - import torch - from torch import Tensor - from pytorch_lightning import LightningModule - - - class SimpleGAN(LightningModule): - def __init__(self): - super().__init__() - self.G = Generator() - self.D = Discriminator() - - # Important: This property activates manual optimization. - self.automatic_optimization = False - - def sample_z(self, n) -> Tensor: - sample = self._Z.sample((n,)) - return sample - - def sample_G(self, n) -> Tensor: - z = self.sample_z(n) - return self.G(z) - - def training_step(self, batch, batch_idx): - # Implementation follows the PyTorch tutorial: - # https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html - g_opt, d_opt = self.optimizers() - - X, _ = batch - batch_size = X.shape[0] - - real_label = torch.ones((batch_size, 1), device=self.device) - fake_label = torch.zeros((batch_size, 1), device=self.device) - - g_X = self.sample_G(batch_size) - - ########################## - # Optimize Discriminator # - ########################## - d_x = self.D(X) - errD_real = self.criterion(d_x, real_label) - - d_z = self.D(g_X.detach()) - errD_fake = self.criterion(d_z, fake_label) - - errD = errD_real + errD_fake - - d_opt.zero_grad() - self.manual_backward(errD) - d_opt.step() - - ###################### - # Optimize Generator # - ###################### - d_z = self.D(g_X) - errG = self.criterion(d_z, real_label) - - g_opt.zero_grad() - self.manual_backward(errG) - g_opt.step() - - self.log_dict({"g_loss": errG, "d_loss": errD}, prog_bar=True) - - def configure_optimizers(self): - g_opt = torch.optim.Adam(self.G.parameters(), lr=1e-5) - d_opt = torch.optim.Adam(self.D.parameters(), lr=1e-5) - return g_opt, d_opt - - -Learning Rate Scheduling -======================== - -Every optimizer you use can be paired with any -`Learning Rate Scheduler `_. Please see the -documentation of :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers` for all the available options - -You can call ``lr_scheduler.step()`` at arbitrary intervals. -Use ``self.lr_schedulers()`` in your :class:`~pytorch_lightning.core.lightning.LightningModule` to access any learning rate schedulers -defined in your :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`. - -.. warning:: - * Before v1.3, Lightning automatically called ``lr_scheduler.step()`` in both automatic and manual optimization. From - 1.3, ``lr_scheduler.step()`` is now for the user to call at arbitrary intervals. - * Note that the ``lr_scheduler_config`` keys, such as ``"frequency"`` and ``"interval"``, will be ignored even if they are provided in - your :meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers` during manual optimization. - -Here is an example calling ``lr_scheduler.step()`` every step. - -.. testcode:: python - - # step every batch - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - # do forward, backward, and optimization - ... - - # single scheduler - sch = self.lr_schedulers() - sch.step() - - # multiple schedulers - sch1, sch2 = self.lr_schedulers() - sch1.step() - sch2.step() - -If you want to call ``lr_scheduler.step()`` every ``N`` steps/epochs, do the following. - -.. testcode:: python - - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def training_step(self, batch, batch_idx): - # do forward, backward, and optimization - ... - - sch = self.lr_schedulers() - - # step every N batches - if (batch_idx + 1) % N == 0: - sch.step() - - # step every N epochs - if self.trainer.is_last_batch and (self.trainer.current_epoch + 1) % N == 0: - sch.step() - -If you want to call schedulers that require a metric value after each epoch, consider doing the following: - -.. testcode:: - - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def training_epoch_end(self, outputs): - sch = self.lr_schedulers() - - # If the selected scheduler is a ReduceLROnPlateau scheduler. - if isinstance(sch, torch.optim.lr_scheduler.ReduceLROnPlateau): - sch.step(self.trainer.callback_metrics["loss"]) - -Use Closure for LBFGS-like Optimizers -===================================== - -It is a good practice to provide the optimizer with a closure function that performs a ``forward``, ``zero_grad`` and -``backward`` of your model. It is optional for most optimizers, but makes your code compatible if you switch to an -optimizer which requires a closure, such as :class:`~torch.optim.LBFGS`. - -See `the PyTorch docs `_ for more about the closure. - -Here is an example using a closure function. - -.. testcode:: python - - def __init__(self): - super().__init__() - self.automatic_optimization = False - - - def configure_optimizers(self): - return torch.optim.LBFGS(...) - - - def training_step(self, batch, batch_idx): - opt = self.optimizers() - - def closure(): - loss = self.compute_loss(batch) - opt.zero_grad() - self.manual_backward(loss) - return loss - - opt.step(closure=closure) - -.. warning:: - The :class:`~torch.optim.LBFGS` optimizer is not supported for apex AMP, native AMP, IPUs, or DeepSpeed. diff --git a/source/model/own_your_loop.rst b/source/model/own_your_loop.rst deleted file mode 100644 index 5982b0a..0000000 --- a/source/model/own_your_loop.rst +++ /dev/null @@ -1,41 +0,0 @@ -:orphan: - -################################ -Use a pure PyTorch training loop -################################ - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Enable manual optimization - :description: Gain control of the training loop with manual optimization and LightningModule methods. - :col_css: col-md-4 - :button_link: build_model_advanced.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Use a Raw PyTorch Loop - :description: Migrate complex PyTorch projects to Lightning and push bleeding-edge research with the raw PyTorch loop. - :col_css: col-md-4 - :button_link: build_model_expert.html - :height: 150 - :tag: advanced - -.. displayitem:: - :header: Make a custom Lightning Loop - :description: Conduct bleeding-edge research like meta-learning and RL with a custom Loop. - :col_css: col-md-4 - :button_link: loops.html - :height: 150 - :tag: expert - -.. raw:: html - -
-
diff --git a/source/notebooks/course_UvA-DL/01-introduction-to-pytorch.ipynb b/source/notebooks/course_UvA-DL/01-introduction-to-pytorch.ipynb deleted file mode 100644 index cdf4ec3..0000000 --- a/source/notebooks/course_UvA-DL/01-introduction-to-pytorch.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "c1ca2a69", "metadata": {"papermill": {"duration": 0.179789, "end_time": "2021-12-04T15:53:22.277549", "exception": false, "start_time": "2021-12-04T15:53:22.097760", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 1: Introduction to PyTorch\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:52:44.026092\n", "\n", "This tutorial will give a short introduction to PyTorch basics, and get you setup for writing your own neural networks.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/01-introduction-to-pytorch.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "8387d057", "metadata": {"papermill": {"duration": 0.177673, "end_time": "2021-12-04T15:53:22.631831", "exception": false, "start_time": "2021-12-04T15:53:22.454158", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "9d8201a6", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T15:53:22.994731Z", "iopub.status.busy": "2021-12-04T15:53:22.994231Z", "iopub.status.idle": "2021-12-04T15:53:25.459580Z", "shell.execute_reply": "2021-12-04T15:53:25.459996Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.651811, "end_time": "2021-12-04T15:53:25.460282", "exception": false, "start_time": "2021-12-04T15:53:22.808471", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"torchmetrics>=0.3\" \"matplotlib\" \"torch>=1.6, <1.9\" \"pytorch-lightning>=1.3\""]}, {"cell_type": "markdown", "id": "3ef762bf", "metadata": {"papermill": {"duration": 0.180076, "end_time": "2021-12-04T15:53:25.821045", "exception": false, "start_time": "2021-12-04T15:53:25.640969", "status": "completed"}, "tags": []}, "source": ["
\n", "Welcome to our PyTorch tutorial for the Deep Learning course 2020 at the University of Amsterdam!\n", "The following notebook is meant to give a short introduction to PyTorch basics, and get you setup for writing your own neural networks.\n", "PyTorch is an open source machine learning framework that allows you to write your own neural networks and optimize them efficiently.\n", "However, PyTorch is not the only framework of its kind.\n", "Alternatives to PyTorch include [TensorFlow](https://www.tensorflow.org/), [JAX](https://github.com/google/jax) and [Caffe](http://caffe.berkeleyvision.org/).\n", "We choose to teach PyTorch at the University of Amsterdam because it is well established, has a huge developer community (originally developed by Facebook), is very flexible and especially used in research.\n", "Many current papers publish their code in PyTorch, and thus it is good to be familiar with PyTorch as well.\n", "Meanwhile, TensorFlow (developed by Google) is usually known for being a production-grade deep learning library.\n", "Still, if you know one machine learning framework in depth, it is very easy to learn another one because many of them use the same concepts and ideas.\n", "For instance, TensorFlow's version 2 was heavily inspired by the most popular features of PyTorch, making the frameworks even more similar.\n", "If you are already familiar with PyTorch and have created your own neural network projects, feel free to just skim this notebook.\n", "\n", "We are of course not the first ones to create a PyTorch tutorial.\n", "There are many great tutorials online, including the [\"60-min blitz\"](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html) on the official [PyTorch website](https://pytorch.org/tutorials/).\n", "Yet, we choose to create our own tutorial which is designed to give you the basics particularly necessary for the practicals, but still understand how PyTorch works under the hood.\n", "Over the next few weeks, we will also keep exploring new PyTorch features in the series of Jupyter notebook tutorials about deep learning.\n", "\n", "We will use a set of standard libraries that are often used in machine learning projects.\n", "If you are running this notebook on Google Colab, all libraries should be pre-installed.\n", "If you are running this notebook locally, make sure you have installed our `dl2020` environment ([link](https://github.com/uvadlc/uvadlc_practicals_2020/blob/master/environment.yml)) and have activated it."]}, {"cell_type": "code", "execution_count": 2, "id": "fd4e9d73", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:26.189594Z", "iopub.status.busy": "2021-12-04T15:53:26.189116Z", "iopub.status.idle": "2021-12-04T15:53:27.107430Z", "shell.execute_reply": "2021-12-04T15:53:27.107817Z"}, "papermill": {"duration": 1.106304, "end_time": "2021-12-04T15:53:27.107981", "exception": false, "start_time": "2021-12-04T15:53:26.001677", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_739/4100430984.py:14: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\")\n"]}], "source": ["import time\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import torch\n", "import torch.nn as nn\n", "import torch.utils.data as data\n", "\n", "%matplotlib inline\n", "from IPython.display import set_matplotlib_formats\n", "from matplotlib.colors import to_rgba\n", "from tqdm.notebook import tqdm # Progress bar\n", "\n", "set_matplotlib_formats(\"svg\", \"pdf\")"]}, {"cell_type": "markdown", "id": "e6ab2b50", "metadata": {"papermill": {"duration": 0.177291, "end_time": "2021-12-04T15:53:27.467274", "exception": false, "start_time": "2021-12-04T15:53:27.289983", "status": "completed"}, "tags": []}, "source": ["## The Basics of PyTorch\n", "\n", "We will start with reviewing the very basic concepts of PyTorch.\n", "As a prerequisite, we recommend to be familiar with the `numpy` package as most machine learning frameworks are based on very similar concepts.\n", "If you are not familiar with numpy yet, don't worry: here is a [tutorial](https://numpy.org/devdocs/user/quickstart.html) to go through.\n", "\n", "So, let's start with importing PyTorch.\n", "The package is called `torch`, based on its original framework [Torch](http://torch.ch/).\n", "As a first step, we can check its version:"]}, {"cell_type": "code", "execution_count": 3, "id": "11531bd1", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:27.828703Z", "iopub.status.busy": "2021-12-04T15:53:27.828236Z", "iopub.status.idle": "2021-12-04T15:53:27.830237Z", "shell.execute_reply": "2021-12-04T15:53:27.830613Z"}, "papermill": {"duration": 0.186067, "end_time": "2021-12-04T15:53:27.830761", "exception": false, "start_time": "2021-12-04T15:53:27.644694", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Using torch 1.8.1+cu102\n"]}], "source": ["print(\"Using torch\", torch.__version__)"]}, {"cell_type": "markdown", "id": "09c6bc8c", "metadata": {"papermill": {"duration": 0.180813, "end_time": "2021-12-04T15:53:28.192377", "exception": false, "start_time": "2021-12-04T15:53:28.011564", "status": "completed"}, "tags": []}, "source": ["At the time of writing this tutorial (mid of August 2021), the current stable version is 1.9.\n", "You should therefore see the output `Using torch 1.9.0`, eventually with some extension for the CUDA version on Colab.\n", "In case you use the `dl2020` environment, you should see `Using torch 1.6.0` since the environment was provided in October 2020.\n", "It is recommended to update the PyTorch version to the newest one.\n", "If you see a lower version number than 1.6, make sure you have installed the correct the environment, or ask one of your TAs.\n", "In case PyTorch 1.10 or newer will be published during the time of the course, don't worry.\n", "The interface between PyTorch versions doesn't change too much, and hence all code should also be runnable with newer versions.\n", "\n", "As in every machine learning framework, PyTorch provides functions that are stochastic like generating random numbers.\n", "However, a very good practice is to setup your code to be reproducible with the exact same random numbers.\n", "This is why we set a seed below."]}, {"cell_type": "code", "execution_count": 4, "id": "541190c5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:28.555309Z", "iopub.status.busy": "2021-12-04T15:53:28.554820Z", "iopub.status.idle": "2021-12-04T15:53:28.559934Z", "shell.execute_reply": "2021-12-04T15:53:28.560320Z"}, "papermill": {"duration": 0.188101, "end_time": "2021-12-04T15:53:28.560451", "exception": false, "start_time": "2021-12-04T15:53:28.372350", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/plain": [""]}, "execution_count": 4, "metadata": {}, "output_type": "execute_result"}], "source": ["torch.manual_seed(42) # Setting the seed"]}, {"cell_type": "markdown", "id": "07432939", "metadata": {"papermill": {"duration": 0.179782, "end_time": "2021-12-04T15:53:28.920061", "exception": false, "start_time": "2021-12-04T15:53:28.740279", "status": "completed"}, "tags": []}, "source": ["### Tensors\n", "\n", "Tensors are the PyTorch equivalent to Numpy arrays, with the addition to also have support for GPU acceleration (more on that later).\n", "The name \"tensor\" is a generalization of concepts you already know.\n", "For instance, a vector is a 1-D tensor, and a matrix a 2-D tensor.\n", "When working with neural networks, we will use tensors of various shapes and number of dimensions.\n", "\n", "Most common functions you know from numpy can be used on tensors as well.\n", "Actually, since numpy arrays are so similar to tensors, we can convert most tensors to numpy arrays (and back) but we don't need it too often.\n", "\n", "#### Initialization\n", "\n", "Let's first start by looking at different ways of creating a tensor.\n", "There are many possible options, the most simple one is to call\n", "`torch.Tensor` passing the desired shape as input argument:"]}, {"cell_type": "code", "execution_count": 5, "id": "9a7c7abb", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:29.290240Z", "iopub.status.busy": "2021-12-04T15:53:29.289779Z", "iopub.status.idle": "2021-12-04T15:53:29.293533Z", "shell.execute_reply": "2021-12-04T15:53:29.293096Z"}, "papermill": {"duration": 0.186675, "end_time": "2021-12-04T15:53:29.293645", "exception": false, "start_time": "2021-12-04T15:53:29.106970", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["tensor([[[-8.4362e+34, 4.5652e-41, -2.7143e-34, -4.6629e+13],\n", " [-1.2059e+35, 4.5652e-41, -8.4184e+34, 4.5652e-41],\n", " [ 9.5052e-38, 2.5041e-19, -1.1549e+35, 4.5652e-41]],\n", "\n", " [[-8.4185e+34, 4.5652e-41, 1.1811e+06, 6.3794e+25],\n", " [-1.2059e+35, 4.5652e-41, -8.4186e+34, 4.5652e-41],\n", " [ 1.1561e+02, 3.9652e+37, -1.2049e+35, 4.5652e-41]]])\n"]}], "source": ["x = torch.Tensor(2, 3, 4)\n", "print(x)"]}, {"cell_type": "markdown", "id": "c3259cdd", "metadata": {"papermill": {"duration": 0.182347, "end_time": "2021-12-04T15:53:29.658142", "exception": false, "start_time": "2021-12-04T15:53:29.475795", "status": "completed"}, "tags": []}, "source": ["The function `torch.Tensor` allocates memory for the desired tensor, but reuses any values that have already been in the memory.\n", "To directly assign values to the tensor during initialization, there are many alternatives including:\n", "\n", "* `torch.zeros`: Creates a tensor filled with zeros\n", "* `torch.ones`: Creates a tensor filled with ones\n", "* `torch.rand`: Creates a tensor with random values uniformly sampled between 0 and 1\n", "* `torch.randn`: Creates a tensor with random values sampled from a normal distribution with mean 0 and variance 1\n", "* `torch.arange`: Creates a tensor containing the values $N,N+1,N+2,...,M$\n", "* `torch.Tensor` (input list): Creates a tensor from the list elements you provide"]}, {"cell_type": "code", "execution_count": 6, "id": "d7d674fc", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:30.029266Z", "iopub.status.busy": "2021-12-04T15:53:30.028795Z", "iopub.status.idle": "2021-12-04T15:53:30.031289Z", "shell.execute_reply": "2021-12-04T15:53:30.031670Z"}, "papermill": {"duration": 0.18988, "end_time": "2021-12-04T15:53:30.031801", "exception": false, "start_time": "2021-12-04T15:53:29.841921", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["tensor([[1., 2.],\n", " [3., 4.]])\n"]}], "source": ["# Create a tensor from a (nested) list\n", "x = torch.Tensor([[1, 2], [3, 4]])\n", "print(x)"]}, {"cell_type": "code", "execution_count": 7, "id": "2267c81e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:30.409461Z", "iopub.status.busy": "2021-12-04T15:53:30.408988Z", "iopub.status.idle": "2021-12-04T15:53:30.411852Z", "shell.execute_reply": "2021-12-04T15:53:30.412236Z"}, "papermill": {"duration": 0.192911, "end_time": "2021-12-04T15:53:30.412364", "exception": false, "start_time": "2021-12-04T15:53:30.219453", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["tensor([[[0.8823, 0.9150, 0.3829, 0.9593],\n", " [0.3904, 0.6009, 0.2566, 0.7936],\n", " [0.9408, 0.1332, 0.9346, 0.5936]],\n", "\n", " [[0.8694, 0.5677, 0.7411, 0.4294],\n", " [0.8854, 0.5739, 0.2666, 0.6274],\n", " [0.2696, 0.4414, 0.2969, 0.8317]]])\n"]}], "source": ["# Create a tensor with random values between 0 and 1 with the shape [2, 3, 4]\n", "x = torch.rand(2, 3, 4)\n", "print(x)"]}, {"cell_type": "markdown", "id": "f11f5cfd", "metadata": {"papermill": {"duration": 0.184187, "end_time": "2021-12-04T15:53:30.778847", "exception": false, "start_time": "2021-12-04T15:53:30.594660", "status": "completed"}, "tags": []}, "source": ["You can obtain the shape of a tensor in the same way as in numpy (`x.shape`), or using the `.size` method:"]}, {"cell_type": "code", "execution_count": 8, "id": "217f784e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:31.151326Z", "iopub.status.busy": "2021-12-04T15:53:31.150840Z", "iopub.status.idle": "2021-12-04T15:53:31.153313Z", "shell.execute_reply": "2021-12-04T15:53:31.153693Z"}, "papermill": {"duration": 0.191823, "end_time": "2021-12-04T15:53:31.153829", "exception": false, "start_time": "2021-12-04T15:53:30.962006", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Shape: torch.Size([2, 3, 4])\n", "Size: torch.Size([2, 3, 4])\n", "Size: 2 3 4\n"]}], "source": ["shape = x.shape\n", "print(\"Shape:\", x.shape)\n", "\n", "size = x.size()\n", "print(\"Size:\", size)\n", "\n", "dim1, dim2, dim3 = x.size()\n", "print(\"Size:\", dim1, dim2, dim3)"]}, {"cell_type": "markdown", "id": "692ea692", "metadata": {"papermill": {"duration": 0.18425, "end_time": "2021-12-04T15:53:31.521916", "exception": false, "start_time": "2021-12-04T15:53:31.337666", "status": "completed"}, "tags": []}, "source": ["#### Tensor to Numpy, and Numpy to Tensor\n", "\n", "Tensors can be converted to numpy arrays, and numpy arrays back to tensors.\n", "To transform a numpy array into a tensor, we can use the function `torch.from_numpy`:"]}, {"cell_type": "code", "execution_count": 9, "id": "52c87663", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:31.897321Z", "iopub.status.busy": "2021-12-04T15:53:31.896848Z", "iopub.status.idle": "2021-12-04T15:53:31.899783Z", "shell.execute_reply": "2021-12-04T15:53:31.899380Z"}, "papermill": {"duration": 0.193907, "end_time": "2021-12-04T15:53:31.899893", "exception": false, "start_time": "2021-12-04T15:53:31.705986", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Numpy array: [[1 2]\n", " [3 4]]\n", "PyTorch tensor: tensor([[1, 2],\n", " [3, 4]])\n"]}], "source": ["np_arr = np.array([[1, 2], [3, 4]])\n", "tensor = torch.from_numpy(np_arr)\n", "\n", "print(\"Numpy array:\", np_arr)\n", "print(\"PyTorch tensor:\", tensor)"]}, {"cell_type": "markdown", "id": "fe3837cb", "metadata": {"papermill": {"duration": 0.183323, "end_time": "2021-12-04T15:53:32.270124", "exception": false, "start_time": "2021-12-04T15:53:32.086801", "status": "completed"}, "tags": []}, "source": ["To transform a PyTorch tensor back to a numpy array, we can use the function `.numpy()` on tensors:"]}, {"cell_type": "code", "execution_count": 10, "id": "4a036f09", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:32.645038Z", "iopub.status.busy": "2021-12-04T15:53:32.644564Z", "iopub.status.idle": "2021-12-04T15:53:32.647718Z", "shell.execute_reply": "2021-12-04T15:53:32.647247Z"}, "papermill": {"duration": 0.193847, "end_time": "2021-12-04T15:53:32.647829", "exception": false, "start_time": "2021-12-04T15:53:32.453982", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["PyTorch tensor: tensor([0, 1, 2, 3])\n", "Numpy array: [0 1 2 3]\n"]}], "source": ["tensor = torch.arange(4)\n", "np_arr = tensor.numpy()\n", "\n", "print(\"PyTorch tensor:\", tensor)\n", "print(\"Numpy array:\", np_arr)"]}, {"cell_type": "markdown", "id": "7ed5a7da", "metadata": {"papermill": {"duration": 0.184638, "end_time": "2021-12-04T15:53:33.016987", "exception": false, "start_time": "2021-12-04T15:53:32.832349", "status": "completed"}, "tags": []}, "source": ["The conversion of tensors to numpy require the tensor to be on the CPU, and not the GPU (more on GPU support in a later section).\n", "In case you have a tensor on GPU, you need to call `.cpu()` on the tensor beforehand.\n", "Hence, you get a line like `np_arr = tensor.cpu().numpy()`."]}, {"cell_type": "markdown", "id": "e8e882b6", "metadata": {"papermill": {"duration": 0.183883, "end_time": "2021-12-04T15:53:33.386141", "exception": false, "start_time": "2021-12-04T15:53:33.202258", "status": "completed"}, "tags": []}, "source": ["#### Operations\n", "\n", "Most operations that exist in numpy, also exist in PyTorch.\n", "A full list of operations can be found in the [PyTorch documentation](https://pytorch.org/docs/stable/tensors.html#), but we will review the most important ones here.\n", "\n", "The simplest operation is to add two tensors:"]}, {"cell_type": "code", "execution_count": 11, "id": "a66afde2", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:33.763575Z", "iopub.status.busy": "2021-12-04T15:53:33.763098Z", "iopub.status.idle": "2021-12-04T15:53:33.768208Z", "shell.execute_reply": "2021-12-04T15:53:33.767800Z"}, "papermill": {"duration": 0.195513, "end_time": "2021-12-04T15:53:33.768317", "exception": false, "start_time": "2021-12-04T15:53:33.572804", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X1 tensor([[0.1053, 0.2695, 0.3588],\n", " [0.1994, 0.5472, 0.0062]])\n", "X2 tensor([[0.9516, 0.0753, 0.8860],\n", " [0.5832, 0.3376, 0.8090]])\n", "Y tensor([[1.0569, 0.3448, 1.2448],\n", " [0.7826, 0.8848, 0.8151]])\n"]}], "source": ["x1 = torch.rand(2, 3)\n", "x2 = torch.rand(2, 3)\n", "y = x1 + x2\n", "\n", "print(\"X1\", x1)\n", "print(\"X2\", x2)\n", "print(\"Y\", y)"]}, {"cell_type": "markdown", "id": "3510743a", "metadata": {"papermill": {"duration": 0.188609, "end_time": "2021-12-04T15:53:34.145802", "exception": false, "start_time": "2021-12-04T15:53:33.957193", "status": "completed"}, "tags": []}, "source": ["Calling `x1 + x2` creates a new tensor containing the sum of the two inputs.\n", "However, we can also use in-place operations that are applied directly on the memory of a tensor.\n", "We therefore change the values of `x2` without the chance to re-accessing the values of `x2` before the operation.\n", "An example is shown below:"]}, {"cell_type": "code", "execution_count": 12, "id": "93e2d0e2", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:34.523641Z", "iopub.status.busy": "2021-12-04T15:53:34.523169Z", "iopub.status.idle": "2021-12-04T15:53:34.528393Z", "shell.execute_reply": "2021-12-04T15:53:34.528008Z"}, "papermill": {"duration": 0.195416, "end_time": "2021-12-04T15:53:34.528506", "exception": false, "start_time": "2021-12-04T15:53:34.333090", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X1 (before) tensor([[0.5779, 0.9040, 0.5547],\n", " [0.3423, 0.6343, 0.3644]])\n", "X2 (before) tensor([[0.7104, 0.9464, 0.7890],\n", " [0.2814, 0.7886, 0.5895]])\n", "X1 (after) tensor([[0.5779, 0.9040, 0.5547],\n", " [0.3423, 0.6343, 0.3644]])\n", "X2 (after) tensor([[1.2884, 1.8504, 1.3437],\n", " [0.6237, 1.4230, 0.9539]])\n"]}], "source": ["x1 = torch.rand(2, 3)\n", "x2 = torch.rand(2, 3)\n", "print(\"X1 (before)\", x1)\n", "print(\"X2 (before)\", x2)\n", "\n", "x2.add_(x1)\n", "print(\"X1 (after)\", x1)\n", "print(\"X2 (after)\", x2)"]}, {"cell_type": "markdown", "id": "acae6209", "metadata": {"papermill": {"duration": 0.189916, "end_time": "2021-12-04T15:53:34.905098", "exception": false, "start_time": "2021-12-04T15:53:34.715182", "status": "completed"}, "tags": []}, "source": ["In-place operations are usually marked with a underscore postfix (e.g. \"add_\" instead of \"add\").\n", "\n", "Another common operation aims at changing the shape of a tensor.\n", "A tensor of size (2,3) can be re-organized to any other shape with the same number of elements (e.g. a tensor of size (6), or (3,2), ...).\n", "In PyTorch, this operation is called `view`:"]}, {"cell_type": "code", "execution_count": 13, "id": "12e785e2", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:35.283833Z", "iopub.status.busy": "2021-12-04T15:53:35.283366Z", "iopub.status.idle": "2021-12-04T15:53:35.285936Z", "shell.execute_reply": "2021-12-04T15:53:35.290804Z"}, "papermill": {"duration": 0.19887, "end_time": "2021-12-04T15:53:35.290937", "exception": false, "start_time": "2021-12-04T15:53:35.092067", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X tensor([0, 1, 2, 3, 4, 5])\n"]}], "source": ["x = torch.arange(6)\n", "print(\"X\", x)"]}, {"cell_type": "code", "execution_count": 14, "id": "68681255", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:35.669862Z", "iopub.status.busy": "2021-12-04T15:53:35.669397Z", "iopub.status.idle": "2021-12-04T15:53:35.672118Z", "shell.execute_reply": "2021-12-04T15:53:35.671721Z"}, "papermill": {"duration": 0.193577, "end_time": "2021-12-04T15:53:35.672223", "exception": false, "start_time": "2021-12-04T15:53:35.478646", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X tensor([[0, 1, 2],\n", " [3, 4, 5]])\n"]}], "source": ["x = x.view(2, 3)\n", "print(\"X\", x)"]}, {"cell_type": "code", "execution_count": 15, "id": "b87c271d", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:36.053012Z", "iopub.status.busy": "2021-12-04T15:53:36.052549Z", "iopub.status.idle": "2021-12-04T15:53:36.055288Z", "shell.execute_reply": "2021-12-04T15:53:36.054885Z"}, "papermill": {"duration": 0.195401, "end_time": "2021-12-04T15:53:36.055396", "exception": false, "start_time": "2021-12-04T15:53:35.859995", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X tensor([[0, 3],\n", " [1, 4],\n", " [2, 5]])\n"]}], "source": ["x = x.permute(1, 0) # Swapping dimension 0 and 1\n", "print(\"X\", x)"]}, {"cell_type": "markdown", "id": "b45990ab", "metadata": {"papermill": {"duration": 0.191632, "end_time": "2021-12-04T15:53:36.435435", "exception": false, "start_time": "2021-12-04T15:53:36.243803", "status": "completed"}, "tags": []}, "source": ["Other commonly used operations include matrix multiplications, which are essential for neural networks.\n", "Quite often, we have an input vector $\\mathbf{x}$, which is transformed using a learned weight matrix $\\mathbf{W}$.\n", "There are multiple ways and functions to perform matrix multiplication, some of which we list below:\n", "\n", "* `torch.matmul`: Performs the matrix product over two tensors, where the specific behavior depends on the dimensions.\n", "If both inputs are matrices (2-dimensional tensors), it performs the standard matrix product.\n", "For higher dimensional inputs, the function supports broadcasting (for details see the [documentation](https://pytorch.org/docs/stable/generated/torch.matmul.html?highlight=matmul#torch.matmul)).\n", "Can also be written as `a @ b`, similar to numpy.\n", "* `torch.mm`: Performs the matrix product over two matrices, but doesn't support broadcasting (see [documentation](https://pytorch.org/docs/stable/generated/torch.mm.html?highlight=torch%20mm#torch.mm))\n", "* `torch.bmm`: Performs the matrix product with a support batch dimension.\n", "If the first tensor $T$ is of shape ($b\\times n\\times m$), and the second tensor $R$ ($b\\times m\\times p$), the output $O$ is of shape ($b\\times n\\times p$), and has been calculated by performing $b$ matrix multiplications of the submatrices of $T$ and $R$: $O_i = T_i @ R_i$\n", "* `torch.einsum`: Performs matrix multiplications and more (i.e. sums of products) using the Einstein summation convention.\n", "Explanation of the Einstein sum can be found in assignment 1.\n", "\n", "Usually, we use `torch.matmul` or `torch.bmm`. We can try a matrix multiplication with `torch.matmul` below."]}, {"cell_type": "code", "execution_count": 16, "id": "913092b1", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:36.816103Z", "iopub.status.busy": "2021-12-04T15:53:36.815637Z", "iopub.status.idle": "2021-12-04T15:53:36.818033Z", "shell.execute_reply": "2021-12-04T15:53:36.818409Z"}, "papermill": {"duration": 0.195078, "end_time": "2021-12-04T15:53:36.818538", "exception": false, "start_time": "2021-12-04T15:53:36.623460", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X tensor([[0, 1, 2],\n", " [3, 4, 5]])\n"]}], "source": ["x = torch.arange(6)\n", "x = x.view(2, 3)\n", "print(\"X\", x)"]}, {"cell_type": "code", "execution_count": 17, "id": "aed345cb", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:37.208721Z", "iopub.status.busy": "2021-12-04T15:53:37.208252Z", "iopub.status.idle": "2021-12-04T15:53:37.212000Z", "shell.execute_reply": "2021-12-04T15:53:37.211600Z"}, "papermill": {"duration": 0.199386, "end_time": "2021-12-04T15:53:37.212109", "exception": false, "start_time": "2021-12-04T15:53:37.012723", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["W tensor([[0, 1, 2],\n", " [3, 4, 5],\n", " [6, 7, 8]])\n"]}], "source": ["W = torch.arange(9).view(3, 3) # We can also stack multiple operations in a single line\n", "print(\"W\", W)"]}, {"cell_type": "code", "execution_count": 18, "id": "ba15bb65", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:37.596703Z", "iopub.status.busy": "2021-12-04T15:53:37.596239Z", "iopub.status.idle": "2021-12-04T15:53:37.599133Z", "shell.execute_reply": "2021-12-04T15:53:37.598757Z"}, "papermill": {"duration": 0.197023, "end_time": "2021-12-04T15:53:37.599245", "exception": false, "start_time": "2021-12-04T15:53:37.402222", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["h tensor([[15, 18, 21],\n", " [42, 54, 66]])\n"]}], "source": ["h = torch.matmul(x, W) # Verify the result by calculating it by hand too!\n", "print(\"h\", h)"]}, {"cell_type": "markdown", "id": "e2969a0e", "metadata": {"papermill": {"duration": 0.191191, "end_time": "2021-12-04T15:53:37.983302", "exception": false, "start_time": "2021-12-04T15:53:37.792111", "status": "completed"}, "tags": []}, "source": ["#### Indexing\n", "\n", "We often have the situation where we need to select a part of a tensor.\n", "Indexing works just like in numpy, so let's try it:"]}, {"cell_type": "code", "execution_count": 19, "id": "9eabb311", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:38.371801Z", "iopub.status.busy": "2021-12-04T15:53:38.371341Z", "iopub.status.idle": "2021-12-04T15:53:38.373967Z", "shell.execute_reply": "2021-12-04T15:53:38.374339Z"}, "papermill": {"duration": 0.198334, "end_time": "2021-12-04T15:53:38.374469", "exception": false, "start_time": "2021-12-04T15:53:38.176135", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X tensor([[ 0, 1, 2, 3],\n", " [ 4, 5, 6, 7],\n", " [ 8, 9, 10, 11]])\n"]}], "source": ["x = torch.arange(12).view(3, 4)\n", "print(\"X\", x)"]}, {"cell_type": "code", "execution_count": 20, "id": "9e961958", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:38.762589Z", "iopub.status.busy": "2021-12-04T15:53:38.762126Z", "iopub.status.idle": "2021-12-04T15:53:38.764774Z", "shell.execute_reply": "2021-12-04T15:53:38.764396Z"}, "papermill": {"duration": 0.199401, "end_time": "2021-12-04T15:53:38.764881", "exception": false, "start_time": "2021-12-04T15:53:38.565480", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["tensor([1, 5, 9])\n"]}], "source": ["print(x[:, 1]) # Second column"]}, {"cell_type": "code", "execution_count": 21, "id": "8df1b8d5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:39.158549Z", "iopub.status.busy": "2021-12-04T15:53:39.158081Z", "iopub.status.idle": "2021-12-04T15:53:39.160695Z", "shell.execute_reply": "2021-12-04T15:53:39.160291Z"}, "papermill": {"duration": 0.199565, "end_time": "2021-12-04T15:53:39.160802", "exception": false, "start_time": "2021-12-04T15:53:38.961237", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["tensor([0, 1, 2, 3])\n"]}], "source": ["print(x[0]) # First row"]}, {"cell_type": "code", "execution_count": 22, "id": "91fda3e0", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:39.553851Z", "iopub.status.busy": "2021-12-04T15:53:39.553387Z", "iopub.status.idle": "2021-12-04T15:53:39.555977Z", "shell.execute_reply": "2021-12-04T15:53:39.555511Z"}, "papermill": {"duration": 0.201859, "end_time": "2021-12-04T15:53:39.556086", "exception": false, "start_time": "2021-12-04T15:53:39.354227", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["tensor([3, 7])\n"]}], "source": ["print(x[:2, -1]) # First two rows, last column"]}, {"cell_type": "code", "execution_count": 23, "id": "a9cf47ae", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:39.949800Z", "iopub.status.busy": "2021-12-04T15:53:39.949338Z", "iopub.status.idle": "2021-12-04T15:53:39.951698Z", "shell.execute_reply": "2021-12-04T15:53:39.952071Z"}, "papermill": {"duration": 0.201325, "end_time": "2021-12-04T15:53:39.952202", "exception": false, "start_time": "2021-12-04T15:53:39.750877", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["tensor([[ 4, 5, 6, 7],\n", " [ 8, 9, 10, 11]])\n"]}], "source": ["print(x[1:3, :]) # Middle two rows"]}, {"cell_type": "markdown", "id": "9fea92c8", "metadata": {"papermill": {"duration": 0.198501, "end_time": "2021-12-04T15:53:40.352499", "exception": false, "start_time": "2021-12-04T15:53:40.153998", "status": "completed"}, "tags": []}, "source": ["### Dynamic Computation Graph and Backpropagation\n", "\n", "One of the main reasons for using PyTorch in Deep Learning projects is that we can automatically get **gradients/derivatives** of functions that we define.\n", "We will mainly use PyTorch for implementing neural networks, and they are just fancy functions.\n", "If we use weight matrices in our function that we want to learn, then those are called the **parameters** or simply the **weights**.\n", "\n", "If our neural network would output a single scalar value, we would talk about taking the **derivative**, but you will see that quite often we will have **multiple** output variables (\"values\"); in that case we talk about **gradients**.\n", "It's a more general term.\n", "\n", "Given an input $\\mathbf{x}$, we define our function by **manipulating** that input, usually by matrix-multiplications with weight matrices and additions with so-called bias vectors.\n", "As we manipulate our input, we are automatically creating a **computational graph**.\n", "This graph shows how to arrive at our output from our input.\n", "PyTorch is a **define-by-run** framework; this means that we can just do our manipulations, and PyTorch will keep track of that graph for us.\n", "Thus, we create a dynamic computation graph along the way.\n", "\n", "So, to recap: the only thing we have to do is to compute the **output**, and then we can ask PyTorch to automatically get the **gradients**.\n", "\n", "> **Note: Why do we want gradients?\n", "** Consider that we have defined a function, a neural net, that is supposed to compute a certain output $y$ for an input vector $\\mathbf{x}$.\n", "We then define an **error measure** that tells us how wrong our network is; how bad it is in predicting output $y$ from input $\\mathbf{x}$.\n", "Based on this error measure, we can use the gradients to **update** the weights $\\mathbf{W}$ that were responsible for the output, so that the next time we present input $\\mathbf{x}$ to our network, the output will be closer to what we want.\n", "\n", "The first thing we have to do is to specify which tensors require gradients.\n", "By default, when we create a tensor, it does not require gradients."]}, {"cell_type": "code", "execution_count": 24, "id": "c2cc2ed7", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:40.750883Z", "iopub.status.busy": "2021-12-04T15:53:40.750396Z", "iopub.status.idle": "2021-12-04T15:53:40.752681Z", "shell.execute_reply": "2021-12-04T15:53:40.752209Z"}, "papermill": {"duration": 0.203425, "end_time": "2021-12-04T15:53:40.752790", "exception": false, "start_time": "2021-12-04T15:53:40.549365", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["False\n"]}], "source": ["x = torch.ones((3,))\n", "print(x.requires_grad)"]}, {"cell_type": "markdown", "id": "95fb29d4", "metadata": {"papermill": {"duration": 0.202898, "end_time": "2021-12-04T15:53:41.153550", "exception": false, "start_time": "2021-12-04T15:53:40.950652", "status": "completed"}, "tags": []}, "source": ["We can change this for an existing tensor using the function `requires_grad_()` (underscore indicating that this is a in-place operation).\n", "Alternatively, when creating a tensor, you can pass the argument\n", "`requires_grad=True` to most initializers we have seen above."]}, {"cell_type": "code", "execution_count": 25, "id": "7c78f25f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:41.555039Z", "iopub.status.busy": "2021-12-04T15:53:41.554541Z", "iopub.status.idle": "2021-12-04T15:53:41.556872Z", "shell.execute_reply": "2021-12-04T15:53:41.556397Z"}, "papermill": {"duration": 0.20518, "end_time": "2021-12-04T15:53:41.556986", "exception": false, "start_time": "2021-12-04T15:53:41.351806", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["True\n"]}], "source": ["x.requires_grad_(True)\n", "print(x.requires_grad)"]}, {"cell_type": "markdown", "id": "20803bcc", "metadata": {"papermill": {"duration": 0.196387, "end_time": "2021-12-04T15:53:41.950177", "exception": false, "start_time": "2021-12-04T15:53:41.753790", "status": "completed"}, "tags": []}, "source": ["In order to get familiar with the concept of a computation graph, we will create one for the following function:\n", "\n", "$$y = \\frac{1}{|x|}\\sum_i \\left[(x_i + 2)^2 + 3\\right]$$\n", "\n", "You could imagine that $x$ are our parameters, and we want to optimize (either maximize or minimize) the output $y$.\n", "For this, we want to obtain the gradients $\\partial y / \\partial \\mathbf{x}$.\n", "For our example, we'll use $\\mathbf{x}=[0,1,2]$ as our input."]}, {"cell_type": "code", "execution_count": 26, "id": "1736b0d5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:42.353546Z", "iopub.status.busy": "2021-12-04T15:53:42.353077Z", "iopub.status.idle": "2021-12-04T15:53:42.355812Z", "shell.execute_reply": "2021-12-04T15:53:42.356193Z"}, "papermill": {"duration": 0.206211, "end_time": "2021-12-04T15:53:42.356325", "exception": false, "start_time": "2021-12-04T15:53:42.150114", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X tensor([0., 1., 2.], requires_grad=True)\n"]}], "source": ["x = torch.arange(3, dtype=torch.float32, requires_grad=True) # Only float tensors can have gradients\n", "print(\"X\", x)"]}, {"cell_type": "markdown", "id": "cf511f52", "metadata": {"papermill": {"duration": 0.200601, "end_time": "2021-12-04T15:53:42.755512", "exception": false, "start_time": "2021-12-04T15:53:42.554911", "status": "completed"}, "tags": []}, "source": ["Now let's build the computation graph step by step.\n", "You can combine multiple operations in a single line, but we will\n", "separate them here to get a better understanding of how each operation\n", "is added to the computation graph."]}, {"cell_type": "code", "execution_count": 27, "id": "fbcee41c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:43.160150Z", "iopub.status.busy": "2021-12-04T15:53:43.159591Z", "iopub.status.idle": "2021-12-04T15:53:43.163509Z", "shell.execute_reply": "2021-12-04T15:53:43.163027Z"}, "papermill": {"duration": 0.209982, "end_time": "2021-12-04T15:53:43.163620", "exception": false, "start_time": "2021-12-04T15:53:42.953638", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Y tensor(12.6667, grad_fn=)\n"]}], "source": ["a = x + 2\n", "b = a ** 2\n", "c = b + 3\n", "y = c.mean()\n", "print(\"Y\", y)"]}, {"cell_type": "markdown", "id": "c012a822", "metadata": {"papermill": {"duration": 0.201708, "end_time": "2021-12-04T15:53:43.567654", "exception": false, "start_time": "2021-12-04T15:53:43.365946", "status": "completed"}, "tags": []}, "source": ["Using the statements above, we have created a computation graph that looks similar to the figure below:\n", "\n", "
\n", "\n", "We calculate $a$ based on the inputs $x$ and the constant $2$, $b$ is $a$ squared, and so on.\n", "The visualization is an abstraction of the dependencies between inputs and outputs of the operations we have applied.\n", "Each node of the computation graph has automatically defined a function for calculating the gradients with respect to its inputs, `grad_fn`.\n", "You can see this when we printed the output tensor $y$.\n", "This is why the computation graph is usually visualized in the reverse direction (arrows point from the result to the inputs).\n", "We can perform backpropagation on the computation graph by calling the\n", "function `backward()` on the last output, which effectively calculates\n", "the gradients for each tensor that has the property\n", "`requires_grad=True`:"]}, {"cell_type": "code", "execution_count": 28, "id": "3afeb787", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:43.969958Z", "iopub.status.busy": "2021-12-04T15:53:43.969494Z", "iopub.status.idle": "2021-12-04T15:53:44.203064Z", "shell.execute_reply": "2021-12-04T15:53:44.203439Z"}, "papermill": {"duration": 0.436371, "end_time": "2021-12-04T15:53:44.203621", "exception": false, "start_time": "2021-12-04T15:53:43.767250", "status": "completed"}, "tags": []}, "outputs": [], "source": ["y.backward()"]}, {"cell_type": "markdown", "id": "6b048147", "metadata": {"papermill": {"duration": 0.198366, "end_time": "2021-12-04T15:53:44.603913", "exception": false, "start_time": "2021-12-04T15:53:44.405547", "status": "completed"}, "tags": []}, "source": ["`x.grad` will now contain the gradient $\\partial y/ \\partial \\mathcal{x}$, and this gradient indicates how a change in $\\mathbf{x}$ will affect output $y$ given the current input $\\mathbf{x}=[0,1,2]$:"]}, {"cell_type": "code", "execution_count": 29, "id": "2a36cc4c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:45.011258Z", "iopub.status.busy": "2021-12-04T15:53:45.010791Z", "iopub.status.idle": "2021-12-04T15:53:45.013643Z", "shell.execute_reply": "2021-12-04T15:53:45.013258Z"}, "papermill": {"duration": 0.210613, "end_time": "2021-12-04T15:53:45.013754", "exception": false, "start_time": "2021-12-04T15:53:44.803141", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["tensor([1.3333, 2.0000, 2.6667])\n"]}], "source": ["print(x.grad)"]}, {"cell_type": "markdown", "id": "01673131", "metadata": {"papermill": {"duration": 0.200204, "end_time": "2021-12-04T15:53:45.415831", "exception": false, "start_time": "2021-12-04T15:53:45.215627", "status": "completed"}, "tags": []}, "source": ["We can also verify these gradients by hand.\n", "We will calculate the gradients using the chain rule, in the same way as PyTorch did it:\n", "\n", "$$\\frac{\\partial y}{\\partial x_i} = \\frac{\\partial y}{\\partial c_i}\\frac{\\partial c_i}{\\partial b_i}\\frac{\\partial b_i}{\\partial a_i}\\frac{\\partial a_i}{\\partial x_i}$$\n", "\n", "Note that we have simplified this equation to index notation, and by using the fact that all operation besides the mean do not combine the elements in the tensor.\n", "The partial derivatives are:\n", "\n", "$$\n", "\\frac{\\partial a_i}{\\partial x_i} = 1,\\hspace{1cm}\n", "\\frac{\\partial b_i}{\\partial a_i} = 2\\cdot a_i\\hspace{1cm}\n", "\\frac{\\partial c_i}{\\partial b_i} = 1\\hspace{1cm}\n", "\\frac{\\partial y}{\\partial c_i} = \\frac{1}{3}\n", "$$\n", "\n", "Hence, with the input being $\\mathbf{x}=[0,1,2]$, our gradients are $\\partial y/\\partial \\mathbf{x}=[4/3,2,8/3]$.\n", "The previous code cell should have printed the same result."]}, {"cell_type": "markdown", "id": "7d826bdb", "metadata": {"papermill": {"duration": 0.198481, "end_time": "2021-12-04T15:53:45.813968", "exception": false, "start_time": "2021-12-04T15:53:45.615487", "status": "completed"}, "tags": []}, "source": ["### GPU support\n", "\n", "A crucial feature of PyTorch is the support of GPUs, short for Graphics Processing Unit.\n", "A GPU can perform many thousands of small operations in parallel, making it very well suitable for performing large matrix operations in neural networks.\n", "When comparing GPUs to CPUs, we can list the following main differences (credit: [Kevin Krewell, 2009](https://blogs.nvidia.com/blog/2009/12/16/whats-the-difference-between-a-cpu-and-a-gpu/))\n", "\n", "
\n", "\n", "CPUs and GPUs have both different advantages and disadvantages, which is why many computers contain both components and use them for different tasks.\n", "In case you are not familiar with GPUs, you can read up more details in this [NVIDIA blog post](https://blogs.nvidia.com/blog/2009/12/16/whats-the-difference-between-a-cpu-and-a-gpu/) or [here](https://www.intel.com/content/www/us/en/products/docs/processors/what-is-a-gpu.html).\n", "\n", "GPUs can accelerate the training of your network up to a factor of $100$ which is essential for large neural networks.\n", "PyTorch implements a lot of functionality for supporting GPUs (mostly those of NVIDIA due to the libraries [CUDA](https://developer.nvidia.com/cuda-zone) and [cuDNN](https://developer.nvidia.com/cudnn)).\n", "First, let's check whether you have a GPU available:"]}, {"cell_type": "code", "execution_count": 30, "id": "8fe50089", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:46.222636Z", "iopub.status.busy": "2021-12-04T15:53:46.222150Z", "iopub.status.idle": "2021-12-04T15:53:46.224494Z", "shell.execute_reply": "2021-12-04T15:53:46.224024Z"}, "papermill": {"duration": 0.209045, "end_time": "2021-12-04T15:53:46.224603", "exception": false, "start_time": "2021-12-04T15:53:46.015558", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Is the GPU available? True\n"]}], "source": ["gpu_avail = torch.cuda.is_available()\n", "print(f\"Is the GPU available? {gpu_avail}\")"]}, {"cell_type": "markdown", "id": "cc6dd1a9", "metadata": {"papermill": {"duration": 0.201657, "end_time": "2021-12-04T15:53:46.632585", "exception": false, "start_time": "2021-12-04T15:53:46.430928", "status": "completed"}, "tags": []}, "source": ["If you have a GPU on your computer but the command above returns False, make sure you have the correct CUDA-version installed.\n", "The `dl2020` environment comes with the CUDA-toolkit 10.1, which is selected for the Lisa supercomputer.\n", "Please change it if necessary (CUDA 10.2 is currently common).\n", "On Google Colab, make sure that you have selected a GPU in your runtime setup (in the menu, check under `Runtime -> Change runtime type`).\n", "\n", "By default, all tensors you create are stored on the CPU.\n", "We can push a tensor to the GPU by using the function `.to(...)`, or `.cuda()`.\n", "However, it is often a good practice to define a `device` object in your code which points to the GPU if you have one, and otherwise to the CPU.\n", "Then, you can write your code with respect to this device object, and it allows you to run the same code on both a CPU-only system, and one with a GPU.\n", "Let's try it below.\n", "We can specify the device as follows:"]}, {"cell_type": "code", "execution_count": 31, "id": "5a3d8e38", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:47.042732Z", "iopub.status.busy": "2021-12-04T15:53:47.042239Z", "iopub.status.idle": "2021-12-04T15:53:47.044024Z", "shell.execute_reply": "2021-12-04T15:53:47.044398Z"}, "papermill": {"duration": 0.208184, "end_time": "2021-12-04T15:53:47.044529", "exception": false, "start_time": "2021-12-04T15:53:46.836345", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Device cuda\n"]}], "source": ["device = torch.device(\"cuda\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", "print(\"Device\", device)"]}, {"cell_type": "markdown", "id": "83f280ae", "metadata": {"papermill": {"duration": 0.203434, "end_time": "2021-12-04T15:53:47.452136", "exception": false, "start_time": "2021-12-04T15:53:47.248702", "status": "completed"}, "tags": []}, "source": ["Now let's create a tensor and push it to the device:"]}, {"cell_type": "code", "execution_count": 32, "id": "31cbb86a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:47.862514Z", "iopub.status.busy": "2021-12-04T15:53:47.862045Z", "iopub.status.idle": "2021-12-04T15:53:51.511287Z", "shell.execute_reply": "2021-12-04T15:53:51.510793Z"}, "papermill": {"duration": 3.856813, "end_time": "2021-12-04T15:53:51.511416", "exception": false, "start_time": "2021-12-04T15:53:47.654603", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["X tensor([[0., 0., 0.],\n", " [0., 0., 0.]], device='cuda:0')\n"]}], "source": ["x = torch.zeros(2, 3)\n", "x = x.to(device)\n", "print(\"X\", x)"]}, {"cell_type": "markdown", "id": "de20fb36", "metadata": {"papermill": {"duration": 0.205639, "end_time": "2021-12-04T15:53:51.921267", "exception": false, "start_time": "2021-12-04T15:53:51.715628", "status": "completed"}, "tags": []}, "source": ["In case you have a GPU, you should now see the attribute `device='cuda:0'` being printed next to your tensor.\n", "The zero next to cuda indicates that this is the zero-th GPU device on your computer.\n", "PyTorch also supports multi-GPU systems, but this you will only need once you have very big networks to train (if interested, see the [PyTorch documentation](https://pytorch.org/docs/stable/distributed.html#distributed-basics)).\n", "We can also compare the runtime of a large matrix multiplication on the CPU with a operation on the GPU:"]}, {"cell_type": "code", "execution_count": 33, "id": "bbf73c6d", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:52.333482Z", "iopub.status.busy": "2021-12-04T15:53:52.333010Z", "iopub.status.idle": "2021-12-04T15:53:52.904067Z", "shell.execute_reply": "2021-12-04T15:53:52.904461Z"}, "papermill": {"duration": 0.778998, "end_time": "2021-12-04T15:53:52.904626", "exception": false, "start_time": "2021-12-04T15:53:52.125628", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["CPU time: 0.27197s\n", "GPU time: 0.02201s\n"]}], "source": ["x = torch.randn(5000, 5000)\n", "\n", "# CPU version\n", "start_time = time.time()\n", "_ = torch.matmul(x, x)\n", "end_time = time.time()\n", "print(f\"CPU time: {(end_time - start_time):6.5f}s\")\n", "\n", "# GPU version\n", "if torch.cuda.is_available():\n", " x = x.to(device)\n", " # CUDA is asynchronous, so we need to use different timing functions\n", " start = torch.cuda.Event(enable_timing=True)\n", " end = torch.cuda.Event(enable_timing=True)\n", " start.record()\n", " _ = torch.matmul(x, x)\n", " end.record()\n", " torch.cuda.synchronize() # Waits for everything to finish running on the GPU\n", " print(f\"GPU time: {0.001 * start.elapsed_time(end):6.5f}s\") # Milliseconds to seconds"]}, {"cell_type": "markdown", "id": "b6483a54", "metadata": {"papermill": {"duration": 0.208145, "end_time": "2021-12-04T15:53:53.320523", "exception": false, "start_time": "2021-12-04T15:53:53.112378", "status": "completed"}, "tags": []}, "source": ["Depending on the size of the operation and the CPU/GPU in your system, the speedup of this operation can be >50x.\n", "As `matmul` operations are very common in neural networks, we can already see the great benefit of training a NN on a GPU.\n", "The time estimate can be relatively noisy here because we haven't run it for multiple times.\n", "Feel free to extend this, but it also takes longer to run.\n", "\n", "When generating random numbers, the seed between CPU and GPU is not synchronized.\n", "Hence, we need to set the seed on the GPU separately to ensure a reproducible code.\n", "Note that due to different GPU architectures, running the same code on different GPUs does not guarantee the same random numbers.\n", "Still, we don't want that our code gives us a different output every time we run it on the exact same hardware.\n", "Hence, we also set the seed on the GPU:"]}, {"cell_type": "code", "execution_count": 34, "id": "119c0085", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:53.736717Z", "iopub.status.busy": "2021-12-04T15:53:53.736247Z", "iopub.status.idle": "2021-12-04T15:53:53.737755Z", "shell.execute_reply": "2021-12-04T15:53:53.738129Z"}, "papermill": {"duration": 0.215091, "end_time": "2021-12-04T15:53:53.738260", "exception": false, "start_time": "2021-12-04T15:53:53.523169", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# GPU operations have a separate seed we also want to set\n", "if torch.cuda.is_available():\n", " torch.cuda.manual_seed(42)\n", " torch.cuda.manual_seed_all(42)\n", "\n", "# Additionally, some operations on a GPU are implemented stochastic for efficiency\n", "# We want to ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False"]}, {"cell_type": "markdown", "id": "d3e51727", "metadata": {"papermill": {"duration": 0.201794, "end_time": "2021-12-04T15:53:54.143745", "exception": false, "start_time": "2021-12-04T15:53:53.941951", "status": "completed"}, "tags": []}, "source": ["## Learning by example: Continuous XOR\n", "
\n", "\n", "If we want to build a neural network in PyTorch, we could specify all our parameters (weight matrices, bias vectors) using `Tensors` (with `requires_grad=True`), ask PyTorch to calculate the gradients and then adjust the parameters.\n", "But things can quickly get cumbersome if we have a lot of parameters.\n", "In PyTorch, there is a package called `torch.nn` that makes building neural networks more convenient.\n", "\n", "We will introduce the libraries and all additional parts you might need to train a neural network in PyTorch, using a simple example classifier on a simple yet well known example: XOR.\n", "Given two binary inputs $x_1$ and $x_2$, the label to predict is $1$ if either $x_1$ or $x_2$ is $1$ while the other is $0$, or the label is $0$ in all other cases.\n", "The example became famous by the fact that a single neuron, i.e. a linear classifier, cannot learn this simple function.\n", "Hence, we will learn how to build a small neural network that can learn this function.\n", "To make it a little bit more interesting, we move the XOR into continuous space and introduce some gaussian noise on the binary inputs.\n", "Our desired separation of an XOR dataset could look as follows:\n", "\n", "
"]}, {"cell_type": "markdown", "id": "be79359e", "metadata": {"papermill": {"duration": 0.205221, "end_time": "2021-12-04T15:53:54.552887", "exception": false, "start_time": "2021-12-04T15:53:54.347666", "status": "completed"}, "tags": []}, "source": ["### The model\n", "\n", "The package `torch.nn` defines a series of useful classes like linear networks layers, activation functions, loss functions etc.\n", "A full list can be found [here](https://pytorch.org/docs/stable/nn.html).\n", "In case you need a certain network layer, check the documentation of the package first before writing the layer yourself as the package likely contains the code for it already.\n", "We import it below:"]}, {"cell_type": "code", "execution_count": null, "id": "43daa017", "metadata": {"lines_to_next_cell": 0, "papermill": {"duration": 0.203631, "end_time": "2021-12-04T15:53:54.958970", "exception": false, "start_time": "2021-12-04T15:53:54.755339", "status": "completed"}, "tags": []}, "outputs": [], "source": []}, {"cell_type": "code", "execution_count": null, "id": "961da892", "metadata": {"papermill": {"duration": 0.204014, "end_time": "2021-12-04T15:53:55.368098", "exception": false, "start_time": "2021-12-04T15:53:55.164084", "status": "completed"}, "tags": []}, "outputs": [], "source": []}, {"cell_type": "markdown", "id": "5b961e6a", "metadata": {"papermill": {"duration": 0.203803, "end_time": "2021-12-04T15:53:55.774983", "exception": false, "start_time": "2021-12-04T15:53:55.571180", "status": "completed"}, "tags": []}, "source": ["Additionally to `torch.nn`, there is also `torch.nn.functional`.\n", "It contains functions that are used in network layers.\n", "This is in contrast to `torch.nn` which defines them as `nn.Modules` (more on it below), and `torch.nn` actually uses a lot of functionalities from `torch.nn.functional`.\n", "Hence, the functional package is useful in many situations, and so we import it as well here."]}, {"cell_type": "markdown", "id": "e60690c4", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.205786, "end_time": "2021-12-04T15:53:56.185307", "exception": false, "start_time": "2021-12-04T15:53:55.979521", "status": "completed"}, "tags": []}, "source": ["#### nn.Module\n", "\n", "In PyTorch, a neural network is built up out of modules.\n", "Modules can contain other modules, and a neural network is considered to be a module itself as well.\n", "The basic template of a module is as follows:"]}, {"cell_type": "code", "execution_count": 35, "id": "71eaf6ae", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:56.605744Z", "iopub.status.busy": "2021-12-04T15:53:56.605272Z", "iopub.status.idle": "2021-12-04T15:53:56.606804Z", "shell.execute_reply": "2021-12-04T15:53:56.607182Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.216416, "end_time": "2021-12-04T15:53:56.607313", "exception": false, "start_time": "2021-12-04T15:53:56.390897", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class MyModule(nn.Module):\n", " def __init__(self):\n", " super().__init__()\n", " # Some init for my module\n", "\n", " def forward(self, x):\n", " # Function for performing the calculation of the module.\n", " pass"]}, {"cell_type": "markdown", "id": "63463f01", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.229323, "end_time": "2021-12-04T15:53:57.045874", "exception": false, "start_time": "2021-12-04T15:53:56.816551", "status": "completed"}, "tags": []}, "source": ["The forward function is where the computation of the module is taken place, and is executed when you call the module (`nn = MyModule(); nn(x)`).\n", "In the init function, we usually create the parameters of the module, using `nn.Parameter`, or defining other modules that are used in the forward function.\n", "The backward calculation is done automatically, but could be overwritten as well if wanted.\n", "\n", "#### Simple classifier\n", "We can now make use of the pre-defined modules in the `torch.nn` package, and define our own small neural network.\n", "We will use a minimal network with a input layer, one hidden layer with tanh as activation function, and a output layer.\n", "In other words, our networks should look something like this:\n", "\n", "
\n", "\n", "The input neurons are shown in blue, which represent the coordinates $x_1$ and $x_2$ of a data point.\n", "The hidden neurons including a tanh activation are shown in white, and the output neuron in red.\n", "In PyTorch, we can define this as follows:"]}, {"cell_type": "code", "execution_count": 36, "id": "6c0cc064", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:57.460540Z", "iopub.status.busy": "2021-12-04T15:53:57.460070Z", "iopub.status.idle": "2021-12-04T15:53:57.461979Z", "shell.execute_reply": "2021-12-04T15:53:57.461520Z"}, "papermill": {"duration": 0.213388, "end_time": "2021-12-04T15:53:57.462086", "exception": false, "start_time": "2021-12-04T15:53:57.248698", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class SimpleClassifier(nn.Module):\n", " def __init__(self, num_inputs, num_hidden, num_outputs):\n", " super().__init__()\n", " # Initialize the modules we need to build the network\n", " self.linear1 = nn.Linear(num_inputs, num_hidden)\n", " self.act_fn = nn.Tanh()\n", " self.linear2 = nn.Linear(num_hidden, num_outputs)\n", "\n", " def forward(self, x):\n", " # Perform the calculation of the model to determine the prediction\n", " x = self.linear1(x)\n", " x = self.act_fn(x)\n", " x = self.linear2(x)\n", " return x"]}, {"cell_type": "markdown", "id": "84553ec2", "metadata": {"papermill": {"duration": 0.204402, "end_time": "2021-12-04T15:53:57.868446", "exception": false, "start_time": "2021-12-04T15:53:57.664044", "status": "completed"}, "tags": []}, "source": ["For the examples in this notebook, we will use a tiny neural network with two input neurons and four hidden neurons.\n", "As we perform binary classification, we will use a single output neuron.\n", "Note that we do not apply a sigmoid on the output yet.\n", "This is because other functions, especially the loss, are more efficient and precise to calculate on the original outputs instead of the sigmoid output.\n", "We will discuss the detailed reason later."]}, {"cell_type": "code", "execution_count": 37, "id": "4f9b83e1", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:58.283224Z", "iopub.status.busy": "2021-12-04T15:53:58.282754Z", "iopub.status.idle": "2021-12-04T15:53:58.285141Z", "shell.execute_reply": "2021-12-04T15:53:58.285538Z"}, "papermill": {"duration": 0.213062, "end_time": "2021-12-04T15:53:58.285669", "exception": false, "start_time": "2021-12-04T15:53:58.072607", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["SimpleClassifier(\n", " (linear1): Linear(in_features=2, out_features=4, bias=True)\n", " (act_fn): Tanh()\n", " (linear2): Linear(in_features=4, out_features=1, bias=True)\n", ")\n"]}], "source": ["model = SimpleClassifier(num_inputs=2, num_hidden=4, num_outputs=1)\n", "# Printing a module shows all its submodules\n", "print(model)"]}, {"cell_type": "markdown", "id": "360961ed", "metadata": {"papermill": {"duration": 0.202451, "end_time": "2021-12-04T15:53:58.691398", "exception": false, "start_time": "2021-12-04T15:53:58.488947", "status": "completed"}, "tags": []}, "source": ["Printing the model lists all submodules it contains.\n", "The parameters of a module can be obtained by using its `parameters()` functions, or `named_parameters()` to get a name to each parameter object.\n", "For our small neural network, we have the following parameters:"]}, {"cell_type": "code", "execution_count": 38, "id": "780fcc13", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:53:59.102900Z", "iopub.status.busy": "2021-12-04T15:53:59.102421Z", "iopub.status.idle": "2021-12-04T15:53:59.104603Z", "shell.execute_reply": "2021-12-04T15:53:59.104977Z"}, "papermill": {"duration": 0.211481, "end_time": "2021-12-04T15:53:59.105106", "exception": false, "start_time": "2021-12-04T15:53:58.893625", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Parameter linear1.weight, shape torch.Size([4, 2])\n", "Parameter linear1.bias, shape torch.Size([4])\n", "Parameter linear2.weight, shape torch.Size([1, 4])\n", "Parameter linear2.bias, shape torch.Size([1])\n"]}], "source": ["for name, param in model.named_parameters():\n", " print(f\"Parameter {name}, shape {param.shape}\")"]}, {"cell_type": "markdown", "id": "bc034c95", "metadata": {"papermill": {"duration": 0.203191, "end_time": "2021-12-04T15:53:59.512934", "exception": false, "start_time": "2021-12-04T15:53:59.309743", "status": "completed"}, "tags": []}, "source": ["Each linear layer has a weight matrix of the shape `[output, input]`, and a bias of the shape `[output]`.\n", "The tanh activation function does not have any parameters.\n", "Note that parameters are only registered for `nn.Module` objects that are direct object attributes, i.e. `self.a = ...`.\n", "If you define a list of modules, the parameters of those are not registered for the outer module and can cause some issues when you try to optimize your module.\n", "There are alternatives, like `nn.ModuleList`, `nn.ModuleDict` and `nn.Sequential`, that allow you to have different data structures of modules.\n", "We will use them in a few later tutorials and explain them there."]}, {"cell_type": "markdown", "id": "136035b4", "metadata": {"papermill": {"duration": 0.204985, "end_time": "2021-12-04T15:53:59.921347", "exception": false, "start_time": "2021-12-04T15:53:59.716362", "status": "completed"}, "tags": []}, "source": ["### The data\n", "\n", "PyTorch also provides a few functionalities to load the training and\n", "test data efficiently, summarized in the package `torch.utils.data`."]}, {"cell_type": "code", "execution_count": null, "id": "1cf8b77a", "metadata": {"papermill": {"duration": 0.204911, "end_time": "2021-12-04T15:54:00.331565", "exception": false, "start_time": "2021-12-04T15:54:00.126654", "status": "completed"}, "tags": []}, "outputs": [], "source": []}, {"cell_type": "markdown", "id": "056c9971", "metadata": {"papermill": {"duration": 0.203757, "end_time": "2021-12-04T15:54:00.743646", "exception": false, "start_time": "2021-12-04T15:54:00.539889", "status": "completed"}, "tags": []}, "source": ["The data package defines two classes which are the standard interface for handling data in PyTorch: `data.Dataset`, and `data.DataLoader`.\n", "The dataset class provides an uniform interface to access the\n", "training/test data, while the data loader makes sure to efficiently load\n", "and stack the data points from the dataset into batches during training."]}, {"cell_type": "markdown", "id": "92ffaea5", "metadata": {"papermill": {"duration": 0.20609, "end_time": "2021-12-04T15:54:01.153489", "exception": false, "start_time": "2021-12-04T15:54:00.947399", "status": "completed"}, "tags": []}, "source": ["#### The dataset class\n", "\n", "The dataset class summarizes the basic functionality of a dataset in a natural way.\n", "To define a dataset in PyTorch, we simply specify two functions: `__getitem__`, and `__len__`.\n", "The get-item function has to return the $i$-th data point in the dataset, while the len function returns the size of the dataset.\n", "For the XOR dataset, we can define the dataset class as follows:"]}, {"cell_type": "code", "execution_count": 39, "id": "9eadd4ba", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:01.569750Z", "iopub.status.busy": "2021-12-04T15:54:01.569274Z", "iopub.status.idle": "2021-12-04T15:54:01.571170Z", "shell.execute_reply": "2021-12-04T15:54:01.570790Z"}, "papermill": {"duration": 0.213645, "end_time": "2021-12-04T15:54:01.571284", "exception": false, "start_time": "2021-12-04T15:54:01.357639", "status": "completed"}, "tags": []}, "outputs": [], "source": ["\n", "\n", "class XORDataset(data.Dataset):\n", " def __init__(self, size, std=0.1):\n", " \"\"\"\n", " Inputs:\n", " size - Number of data points we want to generate\n", " std - Standard deviation of the noise (see generate_continuous_xor function)\n", " \"\"\"\n", " super().__init__()\n", " self.size = size\n", " self.std = std\n", " self.generate_continuous_xor()\n", "\n", " def generate_continuous_xor(self):\n", " # Each data point in the XOR dataset has two variables, x and y, that can be either 0 or 1\n", " # The label is their XOR combination, i.e. 1 if only x or only y is 1 while the other is 0.\n", " # If x=y, the label is 0.\n", " data = torch.randint(low=0, high=2, size=(self.size, 2), dtype=torch.float32)\n", " label = (data.sum(dim=1) == 1).to(torch.long)\n", " # To make it slightly more challenging, we add a bit of gaussian noise to the data points.\n", " data += self.std * torch.randn(data.shape)\n", "\n", " self.data = data\n", " self.label = label\n", "\n", " def __len__(self):\n", " # Number of data point we have. Alternatively self.data.shape[0], or self.label.shape[0]\n", " return self.size\n", "\n", " def __getitem__(self, idx):\n", " # Return the idx-th data point of the dataset\n", " # If we have multiple things to return (data point and label), we can return them as tuple\n", " data_point = self.data[idx]\n", " data_label = self.label[idx]\n", " return data_point, data_label"]}, {"cell_type": "markdown", "id": "170ab9ba", "metadata": {"papermill": {"duration": 0.207351, "end_time": "2021-12-04T15:54:01.984743", "exception": false, "start_time": "2021-12-04T15:54:01.777392", "status": "completed"}, "tags": []}, "source": ["Let's try to create such a dataset and inspect it:"]}, {"cell_type": "code", "execution_count": 40, "id": "be2c0ec7", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:02.398061Z", "iopub.status.busy": "2021-12-04T15:54:02.397602Z", "iopub.status.idle": "2021-12-04T15:54:02.401563Z", "shell.execute_reply": "2021-12-04T15:54:02.401095Z"}, "papermill": {"duration": 0.211018, "end_time": "2021-12-04T15:54:02.401672", "exception": false, "start_time": "2021-12-04T15:54:02.190654", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Size of dataset: 200\n", "Data point 0: (tensor([0.9632, 0.1117]), tensor(1))\n"]}], "source": ["dataset = XORDataset(size=200)\n", "print(\"Size of dataset:\", len(dataset))\n", "print(\"Data point 0:\", dataset[0])"]}, {"cell_type": "markdown", "id": "86828a5c", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.206005, "end_time": "2021-12-04T15:54:02.812653", "exception": false, "start_time": "2021-12-04T15:54:02.606648", "status": "completed"}, "tags": []}, "source": ["To better relate to the dataset, we visualize the samples below."]}, {"cell_type": "code", "execution_count": 41, "id": "84457292", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:03.229327Z", "iopub.status.busy": "2021-12-04T15:54:03.228861Z", "iopub.status.idle": "2021-12-04T15:54:03.230785Z", "shell.execute_reply": "2021-12-04T15:54:03.230390Z"}, "papermill": {"duration": 0.213933, "end_time": "2021-12-04T15:54:03.230894", "exception": false, "start_time": "2021-12-04T15:54:03.016961", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def visualize_samples(data, label):\n", " if isinstance(data, torch.Tensor):\n", " data = data.cpu().numpy()\n", " if isinstance(label, torch.Tensor):\n", " label = label.cpu().numpy()\n", " data_0 = data[label == 0]\n", " data_1 = data[label == 1]\n", "\n", " plt.figure(figsize=(4, 4))\n", " plt.scatter(data_0[:, 0], data_0[:, 1], edgecolor=\"#333\", label=\"Class 0\")\n", " plt.scatter(data_1[:, 0], data_1[:, 1], edgecolor=\"#333\", label=\"Class 1\")\n", " plt.title(\"Dataset samples\")\n", " plt.ylabel(r\"$x_2$\")\n", " plt.xlabel(r\"$x_1$\")\n", " plt.legend()"]}, {"cell_type": "code", "execution_count": 42, "id": "7b382126", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:03.657738Z", "iopub.status.busy": "2021-12-04T15:54:03.645550Z", "iopub.status.idle": "2021-12-04T15:54:04.134432Z", "shell.execute_reply": "2021-12-04T15:54:04.134847Z"}, "papermill": {"duration": 0.697237, "end_time": "2021-12-04T15:54:04.135014", "exception": false, "start_time": "2021-12-04T15:54:03.437777", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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MzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzUwIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjggNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjE3IDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTcgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwOAo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTk1IDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAveCAxOSAwIFIgPj4KZW5kb2JqCjI0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjM1ID4+CnN0cmVhbQp4nDVR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MTUgL3MgL3QgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDIxIDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDIwIDAgUiA+PgplbmRvYmoKMjEgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoyMCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoyMyAwIG9iago8PCAvQyAyNCAwIFIgL0QgMjUgMCBSIC9hIDI2IDAgUiAvZSAyNyAwIFIgL2VpZ2h0IDI4IDAgUiAvZml2ZSAyOSAwIFIKL2ZvdXIgMzAgMCBSIC9sIDMxIDAgUiAvbSAzMiAwIFIgL29uZSAzNCAwIFIgL3AgMzUgMCBSIC9wZXJpb2QgMzYgMCBSCi9zIDM3IDAgUiAvc2V2ZW4gMzggMCBSIC9zaXggMzkgMCBSIC9zcGFjZSA0MCAwIFIgL3QgNDEgMCBSIC90d28gNDIgMCBSCi96ZXJvIDQzIDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMjIgMCBSIC9GMiAxNyAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAwLjggL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC44ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMS1EZWphVnVTYW5zLW1pbnVzIDMzIDAgUiAvTTAgMTMgMCBSIC9NMSAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CQm94IFsgLTggLTggOCA4IF0gL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMzEgL1N1YnR5cGUgL0Zvcm0KL1R5cGUgL1hPYmplY3QgPj4Kc3RyZWFtCnicbZBBDoQgDEX3PUUv8ElLRWXr0mu4mUzi/bcDcUBM3TTQvjx+Uf6S8E6lwPgkCUtOs+R605DSukyMGObVsijHoFEt1s51OKjP0HBjdIuxFKbU1uh4o5vpNt6TP/qwWSFGPxwOr4R7FkMmXCkxBoffCy/bw/8Rnl7UwB+ijX5jWkP9CmVuZHN0cmVhbQplbmRvYmoKMTQgMCBvYmoKPDwgL0JCb3ggWyAtOCAtOCA4IDggXSAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEzMSAvU3VidHlwZSAvRm9ybQovVHlwZSAvWE9iamVjdCA+PgpzdHJlYW0KeJxtkEEOhCAMRfc9RS/wSUtFZevSa7iZTOL9twNxQEzdNNC+PH5R/pLwTqXA+CQJS06z5HrTkNK6TIwY5tWyKMegUS3WznU4qM/QcGN0i7EUptTW6Hijm+k23pM/+rBZIUY/HA6vhHsWQyZcKTEGh98LL9vD/xGeXtTAH6KNfmNaQ/0KZW5kc3RyZWFtCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0NCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1NDA0KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQ1CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDE1MTI1IDAwMDAwIG4gCjAwMDAwMTQzMTkgMDAwMDAgbiAKMDAwMDAxNDM2MiAwMDAwMCBuIAowMDAwMDE0NTA0IDAwMDAwIG4gCjAwMDAwMTQ1MjUgMDAwMDAgbiAKMDAwMDAxNDU0NiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDUgMDAwMDAgbiAKMDAwMDAwNTM0NSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDUzMjQgMDAwMDAgbiAKMDAwMDAxNDYxNyAwMDAwMCBuIAowMDAwMDE0ODcxIDAwMDAwIG4gCjAwMDAwMDYwNTYgMDAwMDAgbiAKMDAwMDAwNTg0OCAwMDAwMCBuIAowMDAwMDA1NTMyIDAwMDAwIG4gCjAwMDAwMDcxMDkgMDAwMDAgbiAKMDAwMDAwNTM2NSAwMDAwMCBuIAowMDAwMDEzMDIyIDAwMDAwIG4gCjAwMDAwMTI4MjIgMDAwMDAgbiAKMDAwMDAxMjQwNiAwMDAwMCBuIAowMDAwMDE0MDc1IDAwMDAwIG4gCjAwMDAwMDcxNDEgMDAwMDAgbiAKMDAwMDAwNzQ0OSAwMDAwMCBuIAowMDAwMDA3Njg2IDAwMDAwIG4gCjAwMDAwMDgwNjYgMDAwMDAgbiAKMDAwMDAwODM4OCAwMDAwMCBuIAowMDAwMDA4ODU2IDAwMDAwIG4gCjAwMDAwMDkxNzggMDAwMDAgbiAKMDAwMDAwOTM0NCAwMDAwMCBuIAowMDAwMDA5NDYzIDAwMDAwIG4gCjAwMDAwMDk3OTQgMDAwMDAgbiAKMDAwMDAwOTk2NiAwMDAwMCBuIAowMDAwMDEwMTIxIDAwMDAwIG4gCjAwMDAwMTA0MzMgMDAwMDAgbiAKMDAwMDAxMDU1NiAwMDAwMCBuIAowMDAwMDEwOTYzIDAwMDAwIG4gCjAwMDAwMTExMDUgMDAwMDAgbiAKMDAwMDAxMTQ5OCAwMDAwMCBuIAowMDAwMDExNTg4IDAwMDAwIG4gCjAwMDAwMTE3OTQgMDAwMDAgbiAKMDAwMDAxMjExOCAwMDAwMCBuIAowMDAwMDE1MTg1IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDQgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ1ID4+CnN0YXJ0eHJlZgoxNTM0MgolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:54:03.873238\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["visualize_samples(dataset.data, dataset.label)\n", "plt.show()"]}, {"cell_type": "markdown", "id": "c4ff362d", "metadata": {"papermill": {"duration": 0.209873, "end_time": "2021-12-04T15:54:04.554262", "exception": false, "start_time": "2021-12-04T15:54:04.344389", "status": "completed"}, "tags": []}, "source": ["#### The data loader class\n", "\n", "The class `torch.utils.data.DataLoader` represents a Python iterable over a dataset with support for automatic batching, multi-process data loading and many more features.\n", "The data loader communicates with the dataset using the function `__getitem__`, and stacks its outputs as tensors over the first dimension to form a batch.\n", "In contrast to the dataset class, we usually don't have to define our own data loader class, but can just create an object of it with the dataset as input.\n", "Additionally, we can configure our data loader with the following input arguments (only a selection, see full list [here](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader)):\n", "\n", "* `batch_size`: Number of samples to stack per batch\n", "* `shuffle`: If True, the data is returned in a random order.\n", "This is important during training for introducing stochasticity.\n", "* `num_workers`: Number of subprocesses to use for data loading.\n", "The default, 0, means that the data will be loaded in the main process which can slow down training for datasets where loading a data point takes a considerable amount of time (e.g. large images).\n", "More workers are recommended for those, but can cause issues on Windows computers.\n", "For tiny datasets as ours, 0 workers are usually faster.\n", "* `pin_memory`: If True, the data loader will copy Tensors into CUDA pinned memory before returning them.\n", "This can save some time for large data points on GPUs.\n", "Usually a good practice to use for a training set, but not necessarily for validation and test to save memory on the GPU.\n", "* `drop_last`: If True, the last batch is dropped in case it is smaller than the specified batch size.\n", "This occurs when the dataset size is not a multiple of the batch size.\n", "Only potentially helpful during training to keep a consistent batch size.\n", "\n", "Let's create a simple data loader below:"]}, {"cell_type": "code", "execution_count": 43, "id": "93c89ea5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:04.976648Z", "iopub.status.busy": "2021-12-04T15:54:04.976186Z", "iopub.status.idle": "2021-12-04T15:54:04.977746Z", "shell.execute_reply": "2021-12-04T15:54:04.978119Z"}, "papermill": {"duration": 0.215753, "end_time": "2021-12-04T15:54:04.978250", "exception": false, "start_time": "2021-12-04T15:54:04.762497", "status": "completed"}, "tags": []}, "outputs": [], "source": ["data_loader = data.DataLoader(dataset, batch_size=8, shuffle=True)"]}, {"cell_type": "code", "execution_count": 44, "id": "b7a50dd3", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:05.404638Z", "iopub.status.busy": "2021-12-04T15:54:05.404165Z", "iopub.status.idle": "2021-12-04T15:54:05.408587Z", "shell.execute_reply": "2021-12-04T15:54:05.408963Z"}, "papermill": {"duration": 0.219528, "end_time": "2021-12-04T15:54:05.409092", "exception": false, "start_time": "2021-12-04T15:54:05.189564", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Data inputs torch.Size([8, 2]) \n", " tensor([[ 1.2108, -0.1180],\n", " [-0.1895, 0.0415],\n", " [ 1.1542, -0.0989],\n", " [ 1.1135, 0.1228],\n", " [-0.0280, 0.0046],\n", " [-0.0378, 1.0500],\n", " [-0.0636, 0.9167],\n", " [-0.0392, 0.8611]])\n", "Data labels torch.Size([8]) \n", " tensor([1, 0, 1, 1, 0, 1, 1, 1])\n"]}], "source": ["# next(iter(...)) catches the first batch of the data loader\n", "# If shuffle is True, this will return a different batch every time we run this cell\n", "# For iterating over the whole dataset, we can simple use \"for batch in data_loader: ...\"\n", "data_inputs, data_labels = next(iter(data_loader))\n", "\n", "# The shape of the outputs are [batch_size, d_1,...,d_N] where d_1,...,d_N are the\n", "# dimensions of the data point returned from the dataset class\n", "print(\"Data inputs\", data_inputs.shape, \"\\n\", data_inputs)\n", "print(\"Data labels\", data_labels.shape, \"\\n\", data_labels)"]}, {"cell_type": "markdown", "id": "40090168", "metadata": {"papermill": {"duration": 0.213288, "end_time": "2021-12-04T15:54:05.833809", "exception": false, "start_time": "2021-12-04T15:54:05.620521", "status": "completed"}, "tags": []}, "source": ["### Optimization\n", "\n", "After defining the model and the dataset, it is time to prepare the optimization of the model.\n", "During training, we will perform the following steps:\n", "\n", "1. Get a batch from the data loader\n", "2. Obtain the predictions from the model for the batch\n", "3. Calculate the loss based on the difference between predictions and labels\n", "4. Backpropagation: calculate the gradients for every parameter with respect to the loss\n", "5. Update the parameters of the model in the direction of the gradients\n", "\n", "We have seen how we can do step 1, 2 and 4 in PyTorch. Now, we will look at step 3 and 5."]}, {"cell_type": "markdown", "id": "202634de", "metadata": {"papermill": {"duration": 0.216115, "end_time": "2021-12-04T15:54:06.271200", "exception": false, "start_time": "2021-12-04T15:54:06.055085", "status": "completed"}, "tags": []}, "source": ["#### Loss modules\n", "\n", "We can calculate the loss for a batch by simply performing a few tensor operations as those are automatically added to the computation graph.\n", "For instance, for binary classification, we can use Binary Cross Entropy (BCE) which is defined as follows:\n", "\n", "$$\\mathcal{L}_{BCE} = -\\sum_i \\left[ y_i \\log x_i + (1 - y_i) \\log (1 - x_i) \\right]$$\n", "\n", "where $y$ are our labels, and $x$ our predictions, both in the range of $[0,1]$.\n", "However, PyTorch already provides a list of predefined loss functions which we can use (see [here](https://pytorch.org/docs/stable/nn.html#loss-functions) for a full list).\n", "For instance, for BCE, PyTorch has two modules: `nn.BCELoss()`, `nn.BCEWithLogitsLoss()`.\n", "While `nn.BCELoss` expects the inputs $x$ to be in the range $[0,1]$, i.e. the output of a sigmoid, `nn.BCEWithLogitsLoss` combines a sigmoid layer and the BCE loss in a single class.\n", "This version is numerically more stable than using a plain Sigmoid followed by a BCE loss because of the logarithms applied in the loss function.\n", "Hence, it is adviced to use loss functions applied on \"logits\" where possible (remember to not apply a sigmoid on the output of the model in this case!).\n", "For our model defined above, we therefore use the module `nn.BCEWithLogitsLoss`."]}, {"cell_type": "code", "execution_count": 45, "id": "5427b133", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:06.708663Z", "iopub.status.busy": "2021-12-04T15:54:06.708193Z", "iopub.status.idle": "2021-12-04T15:54:06.710106Z", "shell.execute_reply": "2021-12-04T15:54:06.709723Z"}, "papermill": {"duration": 0.221156, "end_time": "2021-12-04T15:54:06.710216", "exception": false, "start_time": "2021-12-04T15:54:06.489060", "status": "completed"}, "tags": []}, "outputs": [], "source": ["loss_module = nn.BCEWithLogitsLoss()"]}, {"cell_type": "markdown", "id": "af76ed16", "metadata": {"papermill": {"duration": 0.21844, "end_time": "2021-12-04T15:54:07.146700", "exception": false, "start_time": "2021-12-04T15:54:06.928260", "status": "completed"}, "tags": []}, "source": ["#### Stochastic Gradient Descent\n", "\n", "For updating the parameters, PyTorch provides the package `torch.optim` that has most popular optimizers implemented.\n", "We will discuss the specific optimizers and their differences later in the course, but will for now use the simplest of them: `torch.optim.SGD`.\n", "Stochastic Gradient Descent updates parameters by multiplying the gradients with a small constant, called learning rate, and subtracting those from the parameters (hence minimizing the loss).\n", "Therefore, we slowly move towards the direction of minimizing the loss.\n", "A good default value of the learning rate for a small network as ours is 0.1."]}, {"cell_type": "code", "execution_count": 46, "id": "60eae78b", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:07.623913Z", "iopub.status.busy": "2021-12-04T15:54:07.623439Z", "iopub.status.idle": "2021-12-04T15:54:07.625388Z", "shell.execute_reply": "2021-12-04T15:54:07.625006Z"}, "papermill": {"duration": 0.22472, "end_time": "2021-12-04T15:54:07.625501", "exception": false, "start_time": "2021-12-04T15:54:07.400781", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Input to the optimizer are the parameters of the model: model.parameters()\n", "optimizer = torch.optim.SGD(model.parameters(), lr=0.1)"]}, {"cell_type": "markdown", "id": "cab67e22", "metadata": {"papermill": {"duration": 0.214969, "end_time": "2021-12-04T15:54:08.054953", "exception": false, "start_time": "2021-12-04T15:54:07.839984", "status": "completed"}, "tags": []}, "source": ["The optimizer provides two useful functions: `optimizer.step()`, and `optimizer.zero_grad()`.\n", "The step function updates the parameters based on the gradients as explained above.\n", "The function `optimizer.zero_grad()` sets the gradients of all parameters to zero.\n", "While this function seems less relevant at first, it is a crucial pre-step before performing backpropagation.\n", "If we would call the `backward` function on the loss while the parameter gradients are non-zero from the previous batch, the new gradients would actually be added to the previous ones instead of overwriting them.\n", "This is done because a parameter might occur multiple times in a computation graph, and we need to sum the gradients in this case instead of replacing them.\n", "Hence, remember to call `optimizer.zero_grad()` before calculating the gradients of a batch."]}, {"cell_type": "markdown", "id": "815da7f3", "metadata": {"papermill": {"duration": 0.214692, "end_time": "2021-12-04T15:54:08.483699", "exception": false, "start_time": "2021-12-04T15:54:08.269007", "status": "completed"}, "tags": []}, "source": ["### Training\n", "\n", "Finally, we are ready to train our model.\n", "As a first step, we create a slightly larger dataset and specify a data loader with a larger batch size."]}, {"cell_type": "code", "execution_count": 47, "id": "be9ac2d0", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:08.929206Z", "iopub.status.busy": "2021-12-04T15:54:08.928716Z", "iopub.status.idle": "2021-12-04T15:54:08.931250Z", "shell.execute_reply": "2021-12-04T15:54:08.930773Z"}, "papermill": {"duration": 0.229194, "end_time": "2021-12-04T15:54:08.931361", "exception": false, "start_time": "2021-12-04T15:54:08.702167", "status": "completed"}, "tags": []}, "outputs": [], "source": ["train_dataset = XORDataset(size=1000)\n", "train_data_loader = data.DataLoader(train_dataset, batch_size=128, shuffle=True)"]}, {"cell_type": "markdown", "id": "0271a836", "metadata": {"papermill": {"duration": 0.214622, "end_time": "2021-12-04T15:54:09.363363", "exception": false, "start_time": "2021-12-04T15:54:09.148741", "status": "completed"}, "tags": []}, "source": ["Now, we can write a small training function.\n", "Remember our five steps: load a batch, obtain the predictions, calculate the loss, backpropagate, and update.\n", "Additionally, we have to push all data and model parameters to the device of our choice (GPU if available).\n", "For the tiny neural network we have, communicating the data to the GPU actually takes much more time than we could save from running the operation on GPU.\n", "For large networks, the communication time is significantly smaller than the actual runtime making a GPU crucial in these cases.\n", "Still, to practice, we will push the data to GPU here."]}, {"cell_type": "code", "execution_count": 48, "id": "c7b13460", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:09.806935Z", "iopub.status.busy": "2021-12-04T15:54:09.806433Z", "iopub.status.idle": "2021-12-04T15:54:09.810018Z", "shell.execute_reply": "2021-12-04T15:54:09.809609Z"}, "papermill": {"duration": 0.222561, "end_time": "2021-12-04T15:54:09.810128", "exception": false, "start_time": "2021-12-04T15:54:09.587567", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/plain": ["SimpleClassifier(\n", " (linear1): Linear(in_features=2, out_features=4, bias=True)\n", " (act_fn): Tanh()\n", " (linear2): Linear(in_features=4, out_features=1, bias=True)\n", ")"]}, "execution_count": 48, "metadata": {}, "output_type": "execute_result"}], "source": ["# Push model to device. Has to be only done once\n", "model.to(device)"]}, {"cell_type": "markdown", "id": "cadf5955", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.214526, "end_time": "2021-12-04T15:54:10.247009", "exception": false, "start_time": "2021-12-04T15:54:10.032483", "status": "completed"}, "tags": []}, "source": ["In addition, we set our model to training mode.\n", "This is done by calling `model.train()`.\n", "There exist certain modules that need to perform a different forward\n", "step during training than during testing (e.g. BatchNorm and Dropout),\n", "and we can switch between them using `model.train()` and `model.eval()`."]}, {"cell_type": "code", "execution_count": 49, "id": "b2e9a50f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:10.679237Z", "iopub.status.busy": "2021-12-04T15:54:10.678761Z", "iopub.status.idle": "2021-12-04T15:54:10.680363Z", "shell.execute_reply": "2021-12-04T15:54:10.680739Z"}, "papermill": {"duration": 0.219695, "end_time": "2021-12-04T15:54:10.680870", "exception": false, "start_time": "2021-12-04T15:54:10.461175", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_model(model, optimizer, data_loader, loss_module, num_epochs=100):\n", " # Set model to train mode\n", " model.train()\n", "\n", " # Training loop\n", " for epoch in tqdm(range(num_epochs)):\n", " for data_inputs, data_labels in data_loader:\n", "\n", " # Step 1: Move input data to device (only strictly necessary if we use GPU)\n", " data_inputs = data_inputs.to(device)\n", " data_labels = data_labels.to(device)\n", "\n", " # Step 2: Run the model on the input data\n", " preds = model(data_inputs)\n", " preds = preds.squeeze(dim=1) # Output is [Batch size, 1], but we want [Batch size]\n", "\n", " # Step 3: Calculate the loss\n", " loss = loss_module(preds, data_labels.float())\n", "\n", " # Step 4: Perform backpropagation\n", " # Before calculating the gradients, we need to ensure that they are all zero.\n", " # The gradients would not be overwritten, but actually added to the existing ones.\n", " optimizer.zero_grad()\n", " # Perform backpropagation\n", " loss.backward()\n", "\n", " # Step 5: Update the parameters\n", " optimizer.step()"]}, {"cell_type": "code", "execution_count": 50, "id": "6857c620", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:11.110005Z", "iopub.status.busy": "2021-12-04T15:54:11.109541Z", "iopub.status.idle": "2021-12-04T15:54:12.322199Z", "shell.execute_reply": "2021-12-04T15:54:12.322585Z"}, "papermill": {"duration": 1.429741, "end_time": "2021-12-04T15:54:12.322764", "exception": false, "start_time": "2021-12-04T15:54:10.893023", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "8222db0c1ce847c18b82712853539f5e", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/100 [00:00 Don't drop the last batch although it is smaller than 128\n", "test_data_loader = data.DataLoader(test_dataset, batch_size=128, shuffle=False, drop_last=False)"]}, {"cell_type": "markdown", "id": "9b0c0259", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.223015, "end_time": "2021-12-04T15:54:16.659749", "exception": false, "start_time": "2021-12-04T15:54:16.436734", "status": "completed"}, "tags": []}, "source": ["As metric, we will use accuracy which is calculated as follows:\n", "\n", "$$acc = \\frac{\\#\\text{correct predictions}}{\\#\\text{all predictions}} = \\frac{TP+TN}{TP+TN+FP+FN}$$\n", "\n", "where TP are the true positives, TN true negatives, FP false positives, and FN the fale negatives.\n", "\n", "When evaluating the model, we don't need to keep track of the computation graph as we don't intend to calculate the gradients.\n", "This reduces the required memory and speed up the model.\n", "In PyTorch, we can deactivate the computation graph using `with torch.no_grad(): ...`.\n", "Remember to additionally set the model to eval mode."]}, {"cell_type": "code", "execution_count": 55, "id": "81d5e627", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:17.100737Z", "iopub.status.busy": "2021-12-04T15:54:17.100266Z", "iopub.status.idle": "2021-12-04T15:54:17.102233Z", "shell.execute_reply": "2021-12-04T15:54:17.101833Z"}, "papermill": {"duration": 0.228058, "end_time": "2021-12-04T15:54:17.102341", "exception": false, "start_time": "2021-12-04T15:54:16.874283", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def eval_model(model, data_loader):\n", " model.eval() # Set model to eval mode\n", " true_preds, num_preds = 0.0, 0.0\n", "\n", " with torch.no_grad(): # Deactivate gradients for the following code\n", " for data_inputs, data_labels in data_loader:\n", "\n", " # Determine prediction of model on dev set\n", " data_inputs, data_labels = data_inputs.to(device), data_labels.to(device)\n", " preds = model(data_inputs)\n", " preds = preds.squeeze(dim=1)\n", " preds = torch.sigmoid(preds) # Sigmoid to map predictions between 0 and 1\n", " pred_labels = (preds >= 0.5).long() # Binarize predictions to 0 and 1\n", "\n", " # Keep records of predictions for the accuracy metric (true_preds=TP+TN, num_preds=TP+TN+FP+FN)\n", " true_preds += (pred_labels == data_labels).sum()\n", " num_preds += data_labels.shape[0]\n", "\n", " acc = true_preds / num_preds\n", " print(f\"Accuracy of the model: {100.0*acc:4.2f}%\")"]}, {"cell_type": "code", "execution_count": 56, "id": "71db7937", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:17.540450Z", "iopub.status.busy": "2021-12-04T15:54:17.539985Z", "iopub.status.idle": "2021-12-04T15:54:17.546678Z", "shell.execute_reply": "2021-12-04T15:54:17.547057Z"}, "papermill": {"duration": 0.228671, "end_time": "2021-12-04T15:54:17.547187", "exception": false, "start_time": "2021-12-04T15:54:17.318516", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Accuracy of the model: 100.00%\n"]}], "source": ["eval_model(model, test_data_loader)"]}, {"cell_type": "markdown", "id": "0b763086", "metadata": {"papermill": {"duration": 0.216167, "end_time": "2021-12-04T15:54:17.978220", "exception": false, "start_time": "2021-12-04T15:54:17.762053", "status": "completed"}, "tags": []}, "source": ["If we trained our model correctly, we should see a score close to 100% accuracy.\n", "However, this is only possible because of our simple task, and\n", "unfortunately, we usually don't get such high scores on test sets of\n", "more complex tasks."]}, {"cell_type": "markdown", "id": "5fc4b4e1", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.215766, "end_time": "2021-12-04T15:54:18.415334", "exception": false, "start_time": "2021-12-04T15:54:18.199568", "status": "completed"}, "tags": []}, "source": ["#### Visualizing classification boundaries\n", "\n", "To visualize what our model has learned, we can perform a prediction for every data point in a range of $[-0.5, 1.5]$, and visualize the predicted class as in the sample figure at the beginning of this section.\n", "This shows where the model has created decision boundaries, and which points would be classified as $0$, and which as $1$.\n", "We therefore get a background image out of blue (class 0) and orange (class 1).\n", "The spots where the model is uncertain we will see a blurry overlap.\n", "The specific code is less relevant compared to the output figure which\n", "should hopefully show us a clear separation of classes:"]}, {"cell_type": "code", "execution_count": 57, "id": "9b2cdd8a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:18.862084Z", "iopub.status.busy": "2021-12-04T15:54:18.861126Z", "iopub.status.idle": "2021-12-04T15:54:19.252452Z", "shell.execute_reply": "2021-12-04T15:54:19.252841Z"}, "papermill": {"duration": 0.618628, "end_time": "2021-12-04T15:54:19.253007", "exception": false, "start_time": "2021-12-04T15:54:18.634379", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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bGlxdWUgL0NoYXJQcm9jcyAxOSAwIFIKL0VuY29kaW5nIDw8IC9EaWZmZXJlbmNlcyBbIDEyMCAveCBdIC9UeXBlIC9FbmNvZGluZyA+PiAvRmlyc3RDaGFyIDAKL0ZvbnRCQm94IFsgLTEwMTYgLTM1MSAxNjYwIDEwNjggXSAvRm9udERlc2NyaXB0b3IgMTcgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMtT2JsaXF1ZQovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxNiAwIFIgPj4KZW5kb2JqCjE3IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyA5NgovRm9udEJCb3ggWyAtMTAxNiAtMzUxIDE2NjAgMTA2OCBdIC9Gb250TmFtZSAvRGVqYVZ1U2Fucy1PYmxpcXVlCi9JdGFsaWNBbmdsZSAwIC9NYXhXaWR0aCAxMzUwIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNiAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAg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IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjIxIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjI0IDAgb2JqCjw8IC9DIDI1IDAgUiAvRCAyNiAwIFIgL2EgMjcgMCBSIC9lIDI4IDAgUiAvZml2ZSAyOSAwIFIgL2wgMzAgMCBSCi9tIDMxIDAgUiAvb25lIDMzIDAgUiAvcCAzNCAwIFIgL3BlcmlvZCAzNSAwIFIgL3MgMzYgMCBSIC9zZXZlbiAzNyAwIFIKL3NwYWNlIDM4IDAgUiAvdCAzOSAwIFIgL3R3byA0MCAwIFIgL3plcm8gNDEgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAyMyAwIFIgL0YyIDE4IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDAuOCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjggPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzIgMCBSIC9JMSAxMyAwIFIgL00wIDE0IDAgUiAvTTEgMTUgMCBSID4+CmVuZG9iagoxMyAwIG9iago8PCAvQml0c1BlckNvbXBvbmVudCA4IC9Db2xvclNwYWNlIC9EZXZpY2VSR0IKL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMyAvQ29sdW1ucyAyMTggL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9G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MDAwMDAgbiAKMDAwMDAxMzM3OSAwMDAwMCBuIAowMDAwMDMyMzE0IDAwMDAwIG4gCjAwMDAwMzI1NjggMDAwMDAgbiAKMDAwMDAwNTg4OCAwMDAwMCBuIAowMDAwMDA1NjgwIDAwMDAwIG4gCjAwMDAwMDUzNjQgMDAwMDAgbiAKMDAwMDAwNjk0MSAwMDAwMCBuIAowMDAwMDA1MTk3IDAwMDAwIG4gCjAwMDAwMTE4MTIgMDAwMDAgbiAKMDAwMDAxMTYxMiAwMDAwMCBuIAowMDAwMDExMjExIDAwMDAwIG4gCjAwMDAwMTI4NjUgMDAwMDAgbiAKMDAwMDAwNjk3MyAwMDAwMCBuIAowMDAwMDA3MjgxIDAwMDAwIG4gCjAwMDAwMDc1MTggMDAwMDAgbiAKMDAwMDAwNzg5OCAwMDAwMCBuIAowMDAwMDA4MjIwIDAwMDAwIG4gCjAwMDAwMDg1NDIgMDAwMDAgbiAKMDAwMDAwODY2MSAwMDAwMCBuIAowMDAwMDA4OTkyIDAwMDAwIG4gCjAwMDAwMDkxNjQgMDAwMDAgbiAKMDAwMDAwOTMxOSAwMDAwMCBuIAowMDAwMDA5NjMxIDAwMDAwIG4gCjAwMDAwMDk3NTQgMDAwMDAgbiAKMDAwMDAxMDE2MSAwMDAwMCBuIAowMDAwMDEwMzAzIDAwMDAwIG4gCjAwMDAwMTAzOTMgMDAwMDAgbiAKMDAwMDAxMDU5OSAwMDAwMCBuIAowMDAwMDEwOTIzIDAwMDAwIG4gCjAwMDAwMzIyOTIgMDAwMDAgbiAKMDAwMDAzMjg4MiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQzIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0NCA+PgpzdGFydHhyZWYKMzMwMzkKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:54:18.983966\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["@torch.no_grad() # Decorator, same effect as \"with torch.no_grad(): ...\" over the whole function.\n", "def visualize_classification(model, data, label):\n", " if isinstance(data, torch.Tensor):\n", " data = data.cpu().numpy()\n", " if isinstance(label, torch.Tensor):\n", " label = label.cpu().numpy()\n", " data_0 = data[label == 0]\n", " data_1 = data[label == 1]\n", "\n", " plt.figure(figsize=(4, 4))\n", " plt.scatter(data_0[:, 0], data_0[:, 1], edgecolor=\"#333\", label=\"Class 0\")\n", " plt.scatter(data_1[:, 0], data_1[:, 1], edgecolor=\"#333\", label=\"Class 1\")\n", " plt.title(\"Dataset samples\")\n", " plt.ylabel(r\"$x_2$\")\n", " plt.xlabel(r\"$x_1$\")\n", " plt.legend()\n", "\n", " # Let's make use of a lot of operations we have learned above\n", " model.to(device)\n", " c0 = torch.Tensor(to_rgba(\"C0\")).to(device)\n", " c1 = torch.Tensor(to_rgba(\"C1\")).to(device)\n", " x1 = torch.arange(-0.5, 1.5, step=0.01, device=device)\n", " x2 = torch.arange(-0.5, 1.5, step=0.01, device=device)\n", " xx1, xx2 = torch.meshgrid(x1, x2) # Meshgrid function as in numpy\n", " model_inputs = torch.stack([xx1, xx2], dim=-1)\n", " preds = model(model_inputs)\n", " preds = torch.sigmoid(preds)\n", " # Specifying \"None\" in a dimension creates a new one\n", " output_image = (1 - preds) * c0[None, None] + preds * c1[None, None]\n", " output_image = (\n", " output_image.cpu().numpy()\n", " ) # Convert to numpy array. This only works for tensors on CPU, hence first push to CPU\n", " plt.imshow(output_image, origin=\"lower\", extent=(-0.5, 1.5, -0.5, 1.5))\n", " plt.grid(False)\n", "\n", "\n", "visualize_classification(model, dataset.data, dataset.label)\n", "plt.show()"]}, {"cell_type": "markdown", "id": "34a5111d", "metadata": {"papermill": {"duration": 0.21845, "end_time": "2021-12-04T15:54:19.694395", "exception": false, "start_time": "2021-12-04T15:54:19.475945", "status": "completed"}, "tags": []}, "source": ["The decision boundaries might not look exactly as in the figure in the preamble of this section which can be caused by running it on CPU or a different GPU architecture.\n", "Nevertheless, the result on the accuracy metric should be the approximately the same."]}, {"cell_type": "markdown", "id": "a501a3ba", "metadata": {"papermill": {"duration": 0.219567, "end_time": "2021-12-04T15:54:20.134648", "exception": false, "start_time": "2021-12-04T15:54:19.915081", "status": "completed"}, "tags": []}, "source": ["## Additional features we didn't get to discuss yet\n", "\n", "Finally, you are all set to start with your own PyTorch project!\n", "In summary, we have looked at how we can build neural networks in PyTorch, and train and test them on data.\n", "However, there is still much more to PyTorch we haven't discussed yet.\n", "In the comming series of Jupyter notebooks, we will discover more and more functionalities of PyTorch, so that you also get familiar to PyTorch concepts beyond the basics.\n", "If you are already interested in learning more of PyTorch, we recommend the official [tutorial website](https://pytorch.org/tutorials/) that contains many tutorials on various topics.\n", "Especially logging with Tensorboard ([tutorial\n", "here](https://pytorch.org/tutorials/intermediate/tensorboard_tutorial.html))\n", "is a good practice that we will explore from Tutorial 5 on."]}, {"cell_type": "markdown", "id": "96f1e6ba", "metadata": {"papermill": {"duration": 0.227432, "end_time": "2021-12-04T15:54:20.580697", "exception": false, "start_time": "2021-12-04T15:54:20.353265", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAByMAAAE8CAYAAABjFR0gAAAACXBIWXMAAAsSAAALEgHS3X78AAAgAElEQVR4nOzdT2ydV3rn+eP3FQLYoVJsZFEgPU4pjYDCAO5QobxLai6dypKKlFQ2Mroj2inImwnEkrwZIl2kqgOtJJWcrGTUlKikoZpFqkr+s0zGZLuzk1xiV23kDiZyKkVNLYKRIkWFALqvB7/X5zBH9+Wfe8lz3r/fD0BI5qXFe+/73vc95zzneZ7nPvvsMwNsZ3F6Y9YYM2sfvmeMWb2wPnlvmx8HAAAAAAAAAAAANhGMRMHi9Ma4MWbBfn2h8APGvGuMWb6wPnmn8AgAAAAAAAAAAABgEYzEMxanN+YVaDTGfGmId+a6ApYX1icfFB4BAAAAAAAAAABA5xGMRG5xeuOIMeaKMaY34jvyUP/fhfXJ5cIjAAAAAAAAAAAA6DSCkR1nS7IqCHlqn+/Ep8aY+Qvrk6uFRwAAAAAAAAAAANBJBCM7bHF6Y3mHvpB7tWaDkve6/v4CAAAAAAAAAAB0HcHIDlqc3pg1xqwM2Rdyr95W70n6SQIAAAAAAAAAAHQXwcgOWZzeOGSDkKP2hdwr9ZNcuLA+udL19x4AAAAAAAAAAKCLCEZ2gO0LqZKsZyp6tes2KEk/SQAAAAAAAAAAgA4hGNlyi9MbCzYQGbIv5F69a4OS9JMEAAAAAAAAAADoAIKRLWX7Ql4xxkzX7BU+tM/rCv0kAQAAAAAAAAAA2o1gZMvYvpAK9h2v+Sv7VBmb9JMEAAAAAAAAAABoL4KRLWH7Qqok61LDXtGaLd16p/AIAAAAAAAAAAAAGo1gZAssTm/M276QX2rwq7lug5KUbgUAAAAAAAAAAGgJgpENZvtCKgjZa8lLemh7SS4XHgEAAAAAAAAAAEDjEIxsIFuSVX0hT7X0Jaqf5PyF9cnVwiMAAAAAAAAAAABoDIKRDbM4vbFse0N+oQMvd80GJe8VHgEAAAAAAAAAAEDtEYxsiMXpjRM2G7LJfSH36m2Vo6WfJAAAAAAAAAAAQLMQjKy5xemNIzYI2Za+kHv10AYkrzTz6QMAAAAAAAAAAHQPwciasn0hVZL1TNffiwHrKlNLP0kAAAAAAAAAAID6IxhZQ4vTGws2ENmFvpB79a4NStJPEgAAAAAAAAAAoKYIRtbI4vTGrDFmpaN9IffqvMrY0k8SAAAAAAAAAACgfghG1sDi9MYh2xfyeNffiz361PaTXGnkswcAAAAAAAAAAGgpgpEVsn0hVZJ1qbNvQlhrNihJP0kAAAAAAAAAAIAaIBhZkcXpjXmbDUlfyPCu236SlG4FAAAAAAAAAACoEMHIktm+kApCTnfqhZfvoe0ludy1Fw4AAAAAAAAAAFAXBCNLYvtCKjB2qhMvuD4+tVmSN7v+RgAAAAAAAAAAAJSNYGRkXl/IBUqyVmrNBiXvdPg9AAAAAAAAAAAAKBXByIhsX0hlQ36ptS+yed7WMaGfJAAAAAAAAAAAQHwEIyNYnN44YvtC9lr34trhoQ1IXun6GwEAAAAAAAAAABATwciAbEnWK/SFbAz1k5y/sD652vU3AgAAAAAAAAAAIAaCkYEsTm8s0xeysd61/STvdf2NAAAAAAAAAAAACIlg5D4tTm+csNmQ9IVsvvM6lvSTBAAAAAAAAAAACINg5B7RF7K1HtosyZWuvxEAAAAAAAAAAAD7RTByRLYvpEqynmnUE8eo1m1Qkn6SAAAAAAAAAAAAe0QwcgSL0xsLNhBJX8juuK5jTj9JAAAAAAAAAACA0RGMHMLi9MasMWaFvpCd9dCW5KWfJAAAAAAAAAAAwAgIRu5gcXrjkA1C0hcS8qnNkqSfJAAAAAAAAAAAwBAIRm6BvpDYxZrtJ3ln5x8DAAAAAAAAAADoNoKRAxanN+ZtSU76QmI3121QktKtAAAAAAAAAAAAWyAYadm+kApCThceBLb30PaSXN72JwAAAAAAAAAAADqq88FI2xdSQcjjhQeB4X1qsyRv8p4BAAAAAAAAAAB8rrPBSNsXcsEYs1R4ENg79ZOcv7A+eY/3EAAAAAAAAAAAdF0ng5H0hUQJ3jbGLNNPEgAAAAAAAAAAdFmngpG2L6R6+/UKDwLhPbQBySu8twAAAAAAAAAAoIs6EYy0fSEVhDxVeBCI71NbunWV9xoAAAAAAAAAAHRJq4ORXl/IBUqyogbe1blIP0kAAAAAAAAAANAVrQ1G2r6Qyob8UuFBoFrn1bOUfpIAAAAAAAAAAKDtWheMpC8kGuKhzZJc4YABAAAAAAAAAIC2ak0wkr6QaKh1G5SknyQAAAAAAAAAAGidVgQjF6c3lukLiYajnyQAAAAAAAAAAGidRgcjF6c3Tqj3Hn0h0RIP7flMP0kAAAAAAAAAANAKjQxGLk5vHLFBG/pCoo0+Vclh+kkCAAAAAAAAAICma1QwcnF6Y9wGIekLiS5Ys0FJ+kkCAAAAAAAAAIBGakwwkr6Q6LDrtp8kpVsBAAAAAAAAAECj1D4YSV9IIPfQ9pJc5u0AAAAAAAAAAABNUdtgJH0hgS19arMkb271IAAAAAAAAAAAQJ3ULhhJX0hgKGs2KHmHtwsAAAAAAAAAANRVrYKRi9Mb6gm5TF9IYGj0kwQAAAAAAAAAALVVi2Dk4vTGrDFmhb6QwJ6on+TyhfXJK7x9AAAAAAAAAACgTioNRi5ObxyyQUj6QgL7p36S8xfWJ1d5LwEAAAAAAAAAQB1UEoy0fSFVjvVM4UEA+7Vmg5L3eCcBAAAAAAAAAECVSg9G0hcSKM3btnwr/SQBAAAAAAAAAEAlSgtG0hcSqAT9JAEAAAAAAAAAQGWiByPpCwnUAv0kAQAAAAAAAABA6aIFI+kLCdTSu8aYBfpJAgAAAAAAAACAMkQJRtIXEqi988aYK/STBAAAAAAAAAAAMQUNRtIXEmiUhzZLcoXDBgAAAAAAAAAAYggSjKQvJNBo6zYoST9JAAAAAAAAAAAQ1L6CkfSFBFqFfpIAAAAAAAAAACCoPQcjF6c35tVzjr6QQOvQTxIAAAAAAAAAAAQxcjDS9oVUEHK68CCAtvhUWc/0kwQAAAAAAAAAAPsxdDDS9oVUEPJ44UEAbbVmg5L0kwQAAAAAAAAAACPbNRhp+0IuGGOWCg8C6IrrNihJP0kAAAAAAAAAADC0HYOR9IUE4Hlorwf0kwQAAAAAAAAAAEPZMhhJX0gAO6CfJAAAAAAAAAAAGMozwUj6QgIYgfpJLlxYn7zDmwYAAAAAAAAAALayGYxcnN44YYxZoSQrgBG9TpYkAAAAAAAAAADYSh6MtGVZP9zicQAYBgFJAAAAAAAAAABQ8Nz/8es/HTfGqMzilwqPAuVYpz9p4z00xhy6sD75oOtvBAAAAAAAAAAA+DeJMeYEgUhU6PqF9ckjxphPOQiN9gV7LQEAAAAAAAAAANiUEEBAhRSInF+c3jhEQLwVZrv+BgAAAAAAAAAAgGcpGDle+C4QXx6ItL/lCO93Kxzq+hsAAAAAAAAAAACepWBkr/BdIC4/EGnIqAMAAAAAAAAAAGinhOOKkg0GIg3BSAAAAAAAAAAAgHYiGIkybRWIlOnCdwAAAAAAAAAAANB4BCNRli0DkYvTG2RFAgAAAAAAAAAAtBTBSJRhy0CkRTASAAAAAAAAAACgpQhGIradApFypPAdAAAAAAAAAAAAtALBSMS0WyDSkBkJAAAAAAAAAADQXgQjEcuugcjF6Q1lRX6h8AAAAAAAAAAAAABagWAkYhgmI9JQohUAAAAAAAAAAKDdCEYitPUhA5GGEq0AAAAAAAAAAADtRjASIa2PGGAkGAkAAAAAAAAAANBiBCMRSh6IvLA++WCYf29xemPcGPOlwgMAAAAAAAAAAABoDYKRCGGkQKRFViQAAAAAAAAAAEDLEYzEfu0lEClHCt8BAAAAAAAAAABAqxCMxH7sNRBpyIwEAAAAAAAAAABoP4KR2Kv9BCKlV/gOAAAAAAAAAAAAWoVgJPZiX4HIxekNsiIBAAAAAAAAAAA6gGAkRrXfjEhDv0gAAAAAAAAAAIBuUDByjWONIYUIRBr6RQIAAAAAAAAAAHTDAY4zhhQqEGkIRgIAAAAAdnHIfsWyygEY2nKkf3fFGHOv8F10gSpmjUd6nXeMMSHWrgA0R8wxA9cUIBCCkRhGsEDk4vSGBpxfKDwAYC/0eTphA/y9hr6DO2XnuwWiB3bwd4/Fik6Yt19ongX7WQXQzWt3rAUgghXdpfNqKeKrf67wHWwn1nFY5fPdWVcizmFfZbMB0DkxxwxcU4BACEZiN58GzIg09IsEgjrhBlv379/Pv5pkbGzMTE1N7TQBLTyWZdnjJElu24HgKgPCVjq01bFHI8Ta3Q6g/uYjXrsJVnTcBx98YDY2NoK9CceOHTMTExOF72Nnn3zyibl06dKOPzPKMZibmyt8H93zzjvvBHvNr7zyipmZmSl8H0B3hBwzcE0BwiMYiZ08VLAjYCDSUKIVCO/NN980t2/fbtU7e/DgQQUq878fPnw4D1xqIDgxMTE2MTHRswuebtfbu8aYm/aL0hkt0cbzuq1Onz6dfwGArt2hEKyA8/777wcdE9gxZeH72Nnjx4+DHYejR48WvoduChmMFAIHQLeFHjNwTQHCIhiJ7Ty0GZGhy60RjASwq0ePHm0OIN2fbqI6OTmZL2C4r4mJiePGGH1dM8Zct+XcyJgEAKBkIRd/CFYAAAAAQHsQjMRWogQiF6c3VHrvS4UHAGAEKrmhL+14E2VQKntCX2NjY6eMMaf6/f6P0zS9ZAOTAAAAAAAAAICKJLzxGBArI9LQLxIILv9MNa1XZGiuf83s7Kw5f/68+fjjj02api8rUzLLsn+wvTUBAAAAAAAAABUgGAlfzECkoUQrENy4sZmC+JyyJdW7Tj2rFJRMkuQlY8wPbNlWNkQAAAAAAAAAQMkIRsKJHYg0BCMBlEU9q/ygpDGmZ4z5oTFmmYMAAAAAAAAAAOUhGAlTRiBycXpDGVzThQcAICIXlLx8+bJ5/PixftFSv9//EVmSAAAAAABUapy2KgDQHQQjUUZGpGHhH0CVbty4YY4dO2bW1tbyfpJZln1kjJnnoAAAAAAAUCrNxW8aY/4/Y8wCbz0AdAPByG4rKxBpKNEKoGqPHj0y586dy7MkkyQZM8ZcM8ascGAAAAAAAIhKGZArWZY9snPx47zdANAtBCO7q8xApCEYCaAulCX52muvubKtp+yOzHEOEAAAAAAAwahK2pV+v/+PxpgfaP795MmTsQ8++CCfkwMAuuUAx7uTyg5ESq/wHQCoyCeffJKXbb169aqZmpo63u/3P0rT9MvGmAccEwAAAAAA9uSQsiCzLDubJMlL+gfSNDUKQK6uruZfAIBuIhjZPaUHIhenN8iKBFA7Ktv65ptvuoDkywQkAQAAAADYk3lbijUvv5okiVlbW9sMQGr+DQDoNoKR3bNQckaksWUZAKB2BgOSxphVrlkAAAAAAOxKWZDzNgvyoH74/v375rvf/W4egNzY2OAdBABsIhjZLa9fWJ9cqeAVkxkJoLYGApLTaqpvd3UCAAAAAIBnjasXpHpAGpsFqTKs77//vrl9+3bhhwEAMAQjO6WqQKQhGAmg7gYCkppQ3TPGLHPgAAAAAADYNG5bnLzssiAVhKQMKwBgN8kuj6MdKgtELk5vqNzhFwoPAEDNaPK0vLxsHj9+rCe2xEYKAAAAAACesaBApPpBHjt2zNy4cYNAJABgKAQj26/KjEjDYj6AJvnkk0/ygKRkWfaeLT8DAAAAAEDnqT+kNvC6eTMAAMMiGNluVQci5UjhOwBQY2q0r1IztgF/1ddQAAAAAADqYFbzZM2ZyYYEAIyKnpHtVYdApCEzEkATXbp0yRw9elT9I48bY04YY25yIFGmqakpc/DgwUa855OTk4XvAQAAAGidPOHg9u3bHFnU0uHDh4M9Lea5QHgEI9upFoHIxemNQ8aYLxUeAFB76v0wMTGx7dNUkGS3QZ7+/+3+jY8//rjwvbt3727urnSTmyonOQpIXr16VWVo/iJJkl8xxjwo/BCiOXfunOvfWXs6d3W+hPTWW2+ZmZkZTjAAAFALGpfcunWLgwF0W97GZGNjo+vvA2rq7NmzHBqgxghGtk9dMiINWZFAcykYGSgQsjb4jX6//8szMzMvD35/u9+nPo4KSupL5WDKot+ncq0nT55UetqCMYamGCVSZiBy5xv0NtwrfAcAALRBYUwfCJv9AAAhxFwsYp4LBEIwsl3qFIg0BCOBVnh1hxexp8FemqaF71numjFuy78ou/rQ1NRUT4GpkydPmvv375t33nnHvP/++4X/OQb9LgVmn3/++bfSNF1hEFqK5YYFfj8rfCcsguAAAKBqzO0BAHW2GjkgCSCAhDexNeoWiDRMWIBWWN3hKzT3u27aAMy8vY78O2PM7xlj3lXZ16WlpTwYOTsb/xKjsrE3btxQAPUXCQoBAAAAAAAAwOgIRrZD7QKR9IsEENADG6A8YYz5VWPMdQUlL168aJaXl/P+lTGpVKt6F/b7/T9wPTIAAAAAAAAAAMMhGNl852uYEWnIigQQyT2bMflqlmU/mZubM1evXo0akBzIjlwo/AAAAAAAAAAAYFsEI5vt+oX1ybqWDSQYCSCm1SRJft0Ys6Z+krEDkh988EH+Z7/f/1rhQQAAAAAAAADAtghGNpcCkfM1fvYEIwHE9sBea667gGQsGxsbZm1tTdmRL9pysQAAAAAAAACAIRCMbKZaByLpFwmgZPMuQ/LcuXPRfvP777/v/kowEgAAAAAAAACGRDCyeeqeEWnIigRQgRPqIXny5Elz9OjRKL99dXXVPH782GRZ9vuFBwEAAAAAAAAAWyIY2SxNCEQagpEAKvAgSZI/1K9dXo7XSlcBySRJDnKdAwAAAAAAAIDhEIxsjqYEIg2L9AAqsqpr5cTEhDl9+nSUZ3D79m33V65zAAAAAAAAADAEgpHN0KRApKFfJIAK5WmRr732WpRnQDASAAAAAAAAAEZDMLL+mhaIBIAq3dN1c2xszBw7diz409jY2DD379/XX3uFBwEAAAAAAAAABQQj641AJACM7or+j9nZOMmLn3zyifvrkcKDAAAAAAAAAIBnEIysryYHIh8WvgMA5bmTZdlPer2eOXjwYPBfevfuXffX8cKDAAAAAIA2GbdtOga/2JwKAMAIDvBm1VLTMyJvGmNOFb4LACVJkuT7xpgzyo58//33g/5SLzNSE9DVwg8AKItLfx5Mg/b/e/AzurrN94FQZu2ipVugHN9msfKOMebBwN85L+M5Yr8OuWtEv9//5TRNXx74jev2WDywx8V93WvRewEgnnHvejPuXW9+LU3TF7f5re66c89+rQ7cI1AuF3A8kmXZTJIkL+3227Mse5wkye2B+8adwg8CALC73eaS/pyxceMFgpH1824LSrMuE4wEUDFtijgzNTUV/Fk8evSo8D0AUbkBeL44tMuC3qDB/q5L7i/9fv+naZresgP41ZYEgma9YOxWE5f9WNjHwpoLArnnc8h+bWdwUvXAXtfrFhDyz8tXRjgvzRbnZs47L1e9BWmMTufXCfu15Xudpqn5+OOPN/9b/aanpqamvR857v6SZdkju9Fp1Z6LBAnQJaHvJ757DQ/2H/LuA7+z3X3g5z//ub+hcdPAdcddq/KxSr/f/3Gapn/jXXdGeU473WP3o+nHaztagzuRZdlXkiQZcz+TJMnmfeLWrVuF//Xw4cN5JZ6JiYmxiYmJnn+/8e4bN0c8fjGEPCdinFtHShiHx+nhEs7KHj5bux3X7TZltnWzQ8xrX9Pes+XCd3Z3ZIfqX1td+1d3eAzDc+MIt5Fpy3nLgKXCd4xZs8fhjre+sVezEa+Z9whG1su6HQQ12oX1yXuL0xtvKxDQ9QMKoDL5jVcTxNAGMiMHaSDx94Xv7oNdmP47+y/cswGBNkwe9F4tZ1n2+0mShK+na8yvMihutG0DCW5BTyWTtTlAf/c3Cbj/1uKQvyFhcnJSi0X5n/qamZnRguGLNuDgBvTveot+TTx/ZreZnISw3eR0K7P22OnP6S0e381Wk7DxPU6sQzthFyyfuXa5wNbGxkb+5Z+Xg+eoHD16NP/Tnaf6U/esgfPSZVt8jyDYUHSOzPf7/T/yMx7v379vbt++vXnd0H/rGO3EXS90nHRcjh49enBsbOyU3XB5zV4rrpDNio7Q4tiHkV7q+Zpc20eRj1EGrzXuPqDrjK43g/eC3eh64+4Jr7zyiu4H+rf1dcYGtr5jrzu7jU8WIq7FNPF4bUfHcSHLsjfc/fxnP/tZfr9wX7vdKwZ594xn7hvevXylovvGfMTxYQhf2Gbs1yUxEjq23ZTpZfOueoGLpo8xY57nrzZszBf6fdjq8zn4O/xgGBsqd6Zx1bydTz6Tfa95ir78DTC6Hzn+GoebP9qNTW5DjH8tWbPzx1GPx4mI44g1gpH1oUDk7IX1yVYsMFxYn1xYnN4YJ0MSQFXUN3JmZmbXsjqj2mVB4Z5doDyuxYfHjx8XfmBUU1NTL46Njbld1r1+v380TdMvN3yyoLJHH2nn8ZMnT7bcKb4XWjjWlzcQRrNsG0hYXV3dDCYMuzCkz6o/cPf/7ijgYBeM8q+JiYnjNgj0LTs2u9LEANDly5fzxdCtXvOoTp8+nX8NobCoZ2wgbqtF2Z2emwvUGbup5OzZs4WfKdkR+9q+6jImdO1yi5WaLI56HfNfv85vnyaYbjFzdnZ2zFvM/POKFzPr6pBfmUUBAR2PDz74IH9vR11MFhdU9o+TjsuxY8d0TDavFRprJEnyDXtMgFZToG2r7LC90P13bm6uaW/XCRvoyxdl3bXGjVF2uq8Nw/3/+vfeeeedfJHR3gf0pcDWGbs4uGavedvdB/IsVv0boTT0eG3nmXuG7ud6z2/cuLHvOYk7D/RviT12z9zLbcbrpSruG7ov7uWeuBWNz0NQ4D0mfQ5mZmai/o4A8s/s2tra5rkzDLcpcyuDmzIHN79tlc3rBS5WmrzWEPI8txtDCt9vAp0bly5dGvqZ7rRZz507jn9+6T3aKhhmA95/42WHd31D5bjdyHTOrXW47HuNrUYZRwzOHR23IUbHxq5tbH7G7Zzl+/bzvVtgMr8mvfnmm4UH9uPq1av5/00wsh5aFYh0VG52cTq/kBGQBFC6JEn+H2NM8GDkEG66YOTyctiNw/r35ubmXrYDiBOFH2iGQy4QqclhyIUS+/4YG0BCcxyxi3vBAgnDcgEH11vWZSPoPLJl0655AaDlpgS5QwUizbOLTdv1yd1yUc8tzu6ygWNLoZ57APP+hNF/bdtNAkNwAVz/vFQQ7NixY35gkiDYwLmnDUBaxAu5COVzizr60uLya6+9ZuymJ10nvpkkyR9u8xkBWkGLZaHGbVoka1Bwa95+xvN5he6L3/3ud6OPUXT/dPcbLfzquqPNQXZx8UMbONi2hHrIMXbDjtd2XIWFPNvDHUfda2O14fDHC/Y+7jJer/X7/T9N0/RPyryP67XWaIyFf5NXHwk5fh/clGm2CGC4DQ9u04MXuNCmzOtN3fwW+jxvajBS4+JQ78NWQW93Pvn3GjeX1p92E4bb6HutyefUPo3bTa1ntWHXVVHQear3MOT9ZzCgqY1E+mzrM97r9V6y978zdlPM/7nbxoNY9wuCkYiKgCSACilo0NONt+RJ10qWZX+mHcwa4IccXGgBVAO7qamp4zsEBupuOUYg0u3YtmVtq+7LguHM2kWhfLeeFoU0KI8VSBiGCwIpoLFVAMhOohoTlAxhh2NRWNTTZzr0pKoimwvP/oTRBQfLtlMQrIrFzBrY8twr8/i4BWbde7QRxh6PD211hPmdJvYAGkMluf9M9wJlL7ispSqCObqvuvuQ5jbKVpiZmdH46YfGmLftNZHrzvZ0LP9CC8FaoNc9Y5QMtBDc8dP4Uvfxubm5F9nMgir5Gx40xnTzac1/JiYm3Nxnt0xsYJObSzsDVUUqzw6vwLILQrpNrbr/7DC/Dkq/R/c6fblNTd6mmG95Gw+u+Bub+v3+r6k1TixJyw5yU023+UOogKQdIANAmSoLFqini0pV6GYfkiYMXqmNJvZrURbLKbdwHJLbrZ2m6cVqXyKGcMhOaLVw39P5cP78+XxgXObgfDcuAKTnpednMwRP2b6wKyP2UGwblVr+HwoGaVHPHb+Y2QUlme33+z/S4qAWnxUY14KvMlGqCkQO0iRWz+d3f/d388B9mqZuMfMfGpwxP4oT9rUWzr0q6Hql46HzxC6+5KVbO3IsgLZy45QfuHuBrrnnzp2rRVaZnoOuO97Y5Iy9LrqJR2+rLJaOGrdVa3QsDyoTUveMsgORPlc9R+eUAtw24/ZD+zy7PLZExTSm0VzMzX20Gc9uGv3QXhOPcIwwCn8+/dZbb+XnlK04c83OucIumNXHrL0vL+ne4z5XuvZXtdbhNjW5eaSek20rdcpubFp1x0Pzy5jjCIKR9XF8cXqjzQFJlQ95vfAAALRTfj0/efJk8Bfn+ubZicGhwg/Umzan5CWRQtMOY4u+XfU1bnfd/f1gELIugZ6tuIH7YFDSBhwWtvhfWsULLrrrjT7HP3QLtHU/fkMat9eODzVB1kRZ1xRNGOtazkwT2S0WM39gJ5JNuzcMY8sF5bqcezpPdM5oYm/7iv6AkuFAIy27cYruBdpoUOXi4U50/dN1R9dD26v5Q3fdCdG33uctSjZp4XjWbpw6rrGbjqUWxeuyaUrnlALcel52bHncPl8CPqicC1p4m61cJnbXN2Rij9yGSp1TXlCybRsx3HrHh5qbaY7mAn912rDrNh4oeULrG95n/MOnT5/+98L/EBjByHo51fKA5AoBSQAdoRIHa65xdGheMK9R2ZH9fv9rxk5uQtKCtDJRbYkJSlTV0+x2mXRN4hb+NHh/8uSJTrpv2V2drV048hYgD9kFiGvuGGqBtgUlWV2mXZ61rV27mqg9p3AAACAASURBVCg3JavEX8x0E8ksy37UskC5O0a1XFD26dqg52cDAWfIdAEa45BdgFvS5/fy5cv5vaDu/fVc1ZSB606U39MwK24xWBunNHar67F0m1n0PO3Goh+6DZzojNpuInPn58CGzK5U40AELrt/YCPGP7QgS/JIv9//yK13aE6pOVodNzP53PqGCxIfOHDgN83OrVr2jWBk/RCQBIB2yK/lCriEpl1lGuBkWfbVBi1ynlC5B020Qy9oeO8xWSj1dMVfEGp6Jp3OXwUcNGj3dnX+sANZktoteUrXnjqVLd2nK36mnY6prq9NtEVm3rdaFAg74x+jugcH9Px0nXNlW+3CBAFJoL60YepHWoBzmfFVlvHci4HrTu0XP0vwJbfBqAkbp/T89DwV8LFB5WsNbcmBvcmDkXUe32yRif0DsiSxH27uMpDd39Tr3nyWZR9pXUDZkLofN21O6QeJNRYiGNk9BCQBYP/ynVUVZres9Pv9n6qXoZrBh+SazdsF56bsSsx3+IYOYCj7dGZmRlmXP/abbqMWDtmswXx3oCt11oJMupzrFafsCbtw1Kbgz5ZcILIFvajG7fVi89ysa6bdqFygfCAQ1ujMXRccaNIx0vN02apamCAgCdTWvN0wNabrp+5xTQ3k6brjMuy6HIz02wA0bTHYlca048ol2k+gTvxMbJcl2YZxJqqzRXb/UgPn0wqgXtM4QusCyoZs8pzSBSVjtFZyCEbWV1cCkg8LDwJAQKEHAl7Z1XuFBwekafptfUcBydC0YGI+L316rgHnyyFXXi/0jk/XKzJN00uFB1GlPMvA3x1Y92ymvVL2hBega0XwZ5BeW4sCkUdsyeBpvZY2npt6XZrU67Onz6B26jbxnNQ9w5VKbOJ5NxiQtIsrAOrjmdLjbmzddNr4pYBkF7WhAofuGV5A8hQlW1E3LqOt6eNM1Ic7pxpYVWTFlXfXmL9pVRV2EjOgSjCy3roQkJwlIAkgkp7dsRfLrsFIVzbUBcxC0o5nr0Rk3Qf/+SQ69O6qgwcP5oHeLMsesXO4Vp7JMmj67sBhuOCPFsHaOCnXtbRFgciP/B5SbT039br02bOlj8aaeE5qMbnpk3o/IGlLHXOvAuphpYWlxzd1NTOyLa97ICB5jf58qBs3zvTaA9DrFPui63fDqoo8M45o68brGAhG1l/bA5J3CEgCiCBfcI2xcH748GH312GCkQ+MMdfHxsai9I70Fk5q3auu3+9/zUQo0ere0yRJLhceRFXm7aJJq7IMhuH6/bhJuQ3+NL0Rf06vq0WByDEFIvWaukClj/RZbGpAsg1cQNKVNGOxDqjcMwuILbi/oYV0XirYI1mW/aXrKwjUieY9Gmda1xjjYD/cmNlt8K1xQDIfR+g67WV0YkgEI5uBgCQAjCYPAMTYnTQxMeH+Okww0rjsyJMnTxYe2C8F97wSPnXdNTafpumLGlCGzkDy3lMyTeph3pU706C8bVkGw3KTcrtL+MM2TMpbkD24GYjUselKINLRZ5GAZLVcBoH5fFH5z1lUBipDIHIPRmlTgXA0l3Wb3J4+ffpfeWtbK1+7aOr1yI0zrWtt2YyJargNvq4McA0DkpuBSAVOu9yjea8IRjYHAUkAGF4+AL5161bwt8zLjLxTeHBr+rm1qakpfyIfjBfwqWvAI39eoQNTs7OzLjD8LgsjtbAZiGRx79lJuQ08EPypzqEsy/6bFvLUf7CrQXK9br1+G5B8ryF9WFpF10WvnBmbaIDyzROI3DfG3CXTfUOtOQ4cOPCbda+Gg/1p8ua/gbnP+8x9sF+uMo9tS3SlJm/oghtHvPXWW61vRRMLwchmISAJALtTtsFxlUOLscigoGK/3/+pLcE6rPzaHaNUq+vDmGXZ2cKD1dOx6Ok4hM5S9fpw1mVg2mUEIrdANloYk5OT7t8Z5ZrrjPf7/feTJDmo7Oym9x/cL71+vQ/qmWmMudnsV9NMWlT2+kdSygwozwnGKmgqV9Ehy7L/wmYi1NXA3IeNb9iXgb7rqgRWdWkbjSO+5cYRZETuHcHI5iEgCQA7yxf3XJAuJAUi1f8xTdNRUy5XFMCcm5vzF9aD0CBIJSzs4nLdSqLku3dDHwu9hzMzM5qQ/8QYs1r4AZTpiOsRyeJekZ+N9tlnn60xKR+dVxp72Gx034p203apR+RuvP6fvRpM6jvJnYv9fv9PuSYApThke+61pQcyOkbzPS+znns3aktzH2/jG1UgsC+uZKttTbRU4XrXETeOUNsFxhGj89ZB7xGMbCYFJO8sTm+0cvJKQBLAPowrQ1CDlRil+F555RX315EDYGmaXtSfCkiGVtNSrToWb+hYrK6GjRe6XpFJknyj8CDKlJe/1O/TLlgG5Vtz2WjPPffcL9meFyiHFuuO67y8dOkSb7lHZYVqMKnvLJ2TuiaonzIl94D4bIb8mII5ocekQFm0udPeu8+wkQV15m36OM44B/ulc8nLDv+LCq5/qrTzl67lR+iKX13hbTAmGNlg01oMJyAJAM9YUDk+Lf7HqN/u9Xzcy0qGsiP/xSsvGowWVlSW1pavqMt94YSOhZ5byGNx8ODBvNxtlmWPKDNYLVf+UosjXe3DN6ya9ryoPX3WvWvmKGVaNYZcop/H1pRhUfGkvvMUFDGfX0e/1vX3Aojsiu696rnnPndAE2ks45WbJ8DTIv1+/9dsoLk1BkoLH+r6Mcb+aE1Jaw4247bs7PBljSNUjazrLT9COdCOl9FZLiA5e2F9ci99dGpNAUm9Nrvo/4WuH2wAuzriFp9jlGg1Nhipcqtpmu6lXOCDNE3/amxs7JQW2EMHb/TvqUymnZxWXr6n3++fS9M0+MLP7OxsXirXGPOdPfaQQxjLbnGvTllnKqWsgLWjIH1d+jmo54U+p2NjY2dsIJ30jG3oWqvPusuCNsa8O0KZVmVl/0WSJPlCSB2O/+B56VOQuopgqSb1mlT3er2X6nLfiEVlgbzduPn7XXUmt85LZUfOzc29aKsaUMoMCE9rCWc0N1BZtaro+q/7gNvUqP8+fPhw/nddj+7evZv/XdclfdGHqho6Rjouul/oz8H79q1bt/LMRB2vqjJjNMfVfE+VgJIkGfm+rTmot7l2aK7qUMzxiu7V3jwvGP/+X1eqlNC2CjN6PVoHOH36tCstTJ9s7IvOJ10jJiYmypxLb44jqmz54cYRujfpGtn0cQTByOYjIAkAtnSCgl8aJMSYKHmTo78uPDg8jWBOaRIZOhipRU39u8qySNO06kXlWReoCj0QsgFXQ3ZZpTYD/1Ut7mkArs+kG5Srh+hO9Fw1MNdCkv6sokybrkt6v65evarP6X+1WZKtDqhrYUmLXu44ub67I3p7xEDZsnbN6ppYxXHWa1RJb/3pvnaj81MLm3q+obPJd6L7pQ2QL9lg2L0dfrwR3GKmO+92WoT0rwt6/8teXNbualu6fYFgJBCevzGl7E0fuv64wM9u94Fer/fMf7sWB7omUXkiLh0fHadhgmCDY03Nc3R8yrxv6/fYjSyKlJ4YtUrMftqFaA4Wqz+8jsHS0lLh+x3R2uoUCh7p2E5MTJyy4xw2YpajleeUrn/aBH3x4kXz9OnTPz1w4MBvFX4osKaPI9xX3RCMbIeuBCRX7GsFgEErWtiPufisSaq1n9KgWuhdm5iY6GlQEXLhcyDLYuTJaWD5zsfQCyh6z+zC8lobFs2bKnbgfyduwWhwwG0zlv/OBvf8DLr8g/vCCy8cnZmZGfMXkpQV5haRyqLPvHa1nzx58kUbYGtdmS1XSlkLXttMnNYK3ylyx/HmCBmRxt+9WmbGrgJgyuK0u3UHH17b4rx0tFhwROdnr9cb03mtxThdy3Vuxg6O6fOrhaKzZ88au8HjROGHGkLnnI7BFuecO9/8D7o2VIy/8MILMzMzMwfddUHnjQKE+oyWcW1zu5enpqambQkz7mtAOPnGFN3ry7zP61qkgM3AvWDN3gPueJ9z96SO2HvBuP277glfmZubG9N9VJuYdD/QdYmMyXA0p1DFCnf9z7JMNTL/xh4Xd5z8a7I7PrpWz2ZZ9tszMzMv6f/XMSrz3qHz2QYVR5nv7TcQNKuNSy64FPJ1agylsY+OQZIk/3mb8VKbHWnzi9M5YwPNy/QpL01rzyld/7QRZGZm5jdLqCxSp3HE+sD9yXjX9EP2a8txhMtsr9M4gmBke3QpQ5KAJACfBiDHtaAXa/FZi+u6kduAx36DfFrw7akPWuiFZg0y7OR0vsJgpAZCp1QeM3Qw0usdR1ZkdTZ7JpQ1KNfnT0EGHX+3az3Lsp8kSfJ9V6JFwdGdaEejHaBrLHGi3+//Tq/Xe1HBH52rmiiXlX1QUYmZ6LSYpInTwK77de813omdCdrv9/+8zED54GKm9a59vavDLqjZ81OTx/l+v/8H2lSi91FBSd3XYr4WLaDq8zUxMXHcfj4adT5uMWFf9xZdd3z/7ft+yL5uLbTmlQt0rdH7UkZ/OR1jGww+wb0NCOZQv99/6+c//3lpG1O2uBat2WvRzV3uff51Kh+722uTrkvzL7zwwldPnjw5puu0FhJ1XaIP8t5pTKkxgrepLT9OSZLstqD9wLs/rvj37eeff/5rp0+f/kWdA/q3Y28k0vhbi8s6N5IkGbb05WBwdeRfq3G0Wg1oDBtyzOxK8idJ8scNqRLwWeE7AWgsqUoNo9AcRl+DXNnGOpRsdK1ktBmbjVdb0rX+w60e2K+9nFMK9G1F55POqyoqiQxSlSF9ZVn2zSGu3Xulth/nnjx50oRxhH99b8Q4gmBku7Q9IPmAgCQAz7i92fY0yNaCcKybqgYG5vN+Dn9VeHB0NxVIUZ8uLd6HnBxoYKgJiV1UrmqwHyUrUu+VFg5sEKrKrM8uq2RQrh3nXums61qwT5JkxyDDNh7Ya8ZNG7zMB+gqHaQduxr8l7GIVEWJmdgUvNH7p+OUZdmjJEkuV1D2c96Vh44dKNdips5LF3i116VvDDFZ3InO6YU0TZUtO68J9tzc3Eta9NN5GfM1NXHXuo6BPkdeIPi6ff6jnnP37Lm6YjOVF1544YVz6nHk3vuYfZx0XG0wcp5gJBCMNk79ojYVxF6E1/hU14kA16JB+aaWJEny65J6BJ48efKgC3jVsexa3SlzXvcNLfTa+/Yf7nMDjrtv63hf0XhSC+S6p8bezGKzI8dK3kSkMc6ZLSpA7Ivre1ZxVZ+RuDLvIWjsbKs6ZMaY/2mM+X+H/We/+MUvHp2YmNiyvrBfRUbP1VVnqSIwOTDOpHfkFrYLLO/FwDn1o2HnJlmW/XtlfBce2KZMta5DsXvJbkXnss2OfCniNVDrDWOMI+IgGNk+LiB54sL6ZOt2nBCQBGDN2p5rL8YORBpvx2aohUK7UP8t/buhgzqaZHgLm6X3jrQ9K/PnEZJb9LfvHapR9aB8IXBmnctec4tIx7WIpKzP2Jl1JZeYicbPMOj3+/9ijLmYJMmVKnph2t2xeZA3JmVD6rptA695EDLCrtwVu+liQWXR9JouX76cZ+vFMLBrvfbZkf6Ccr/f/3Gapv8pUFm3B7Ykk87hlampqeNaQIvVI8vYEuteqdbxtveRBUqQV+hQsCD0WHSQ27Cg+8HTp0//9sCBA/97hBKT/nVp855QRuZ8m+i+oeu53dx23S7OhrrePnBVabIs+0ttZnHj2Fi0GG/nRo2raLCDxtz/dN/W2CAEjSs1/zDGfDTqhjCb/bQVV1JYf57QGEOfAa09aP6h31dmdpvGmdrEZ7N5Q8/nWkHvUahNDN459T9HKdm6w/m0bZlqrTvpfqTnXmag22VH2vWB0NfA0sYR/sbrksYRy8pwd+OImPepnWx7pqHRNJlVWdNW1om2WZ+ztgwTgG45ZBftP1QgUoMDZeTEnIjbpuvGBkNCbfJYUV8M/dtazA/JZSQqKBj0Hx6OMpNe1MAm9DHRcbbBjkYGbVqgtEG5JlAKumiCo0CDMeY37CJPrInrPVsi8VWbtZz//i16zwVlJ1B5EC3qL4pE1y69BhuIVEDoZRvYrWKBYV49PXTtiZnFpmu2XrNdzHw7SZJfj3hNemDfT52XjzXZjzlh9BZAat3H1C0ou/tymqb/IdKkXdeEr+tY6/fFvB54C4L0UgL2L79Q6j4ec36gcakW8+z94LytchCz1527J/yGxioKROl+FHoe0UZ6j7xA5PmIY8qbSZJ8+bPPPvtnHZ9QwaqtcN9opZABFdd3fdkGkf6dMeZ1XTs0v3IZvAqal0VrFNrU2uT+5A30fwV6yq5M9Yqdc/2KMeZXdT1VRRxd79577708qFbWPclVBFO7JhssDSmfC8UeR+geoYxhe2/6eknjiAV/HKHXWMU4gmBke33BZkgSkATQBrN28PP3rh+hsiHLKBXpTSRDLjg/SJLkexp4aFd1SBowaUFeQcEKyqDkvy909o4CAHqvbJlcdlJWo5TFvYFgz/lIgYbtrNrg0nUFOsoIQGh3soJoTStZ5AKR9v1RQOjLVfaAcQHdmGXRdG5qwqjAoDHm9yJk6m5H5+WX9Xs1aXRlw0PTIpE3qT9Uwusa2TYLyjFpB/HrLiAZa7LuLSq3ct4GlEjl5L8ae+OUNoZog4i9H7xaciWSO3assqZ7MAHJ3blqBl7pu5juPPfccz2dG5pDhp7nOcpA0nmeZdnRwoNA0QNbdUNBpFe1iVBBSc3rYo0rB3ktXAhGtsM9m233SzbQ/UiZt2Vs6HW8cyrkfEDjiDfKGEfoHuGNI8ps1VD5OIJgZLsRkATQVEdc6cJ+v/+Ptql3HoQ8f/58Pmguo7SIlxW5FqH8Qz4ZjrFrNtLAbDdHXP/O0JlJocvkYmTjZWRFDgR7Xq+izLBXaivPiNKEKtZCkvGyI5sWjNQEygUiI2etDuOEAroqrxurPJB/biowWEFfozsuIKnnEeuc9D7ftcyOLHlB2dFGpLf1e2NtgPLumWS4APujjI2xmD2slBGpjSHe/aCKEpluHeQ6Acmd6f7tVdooa6yle3Ye4dF4Kdax0b3DZpqFzgpCu63azZ75XEfjyjJKNep8tQH0rxQeRNO5QPfbWjsrK8itDfhWyPGz5pUHY44jtP7X9XEEwcj2cwHJVu4+UUDywvqkFsDfLjwIIITVIb+W9/B1c/Df1o4qY8xnxpgfGmOuKQCiDD8tMr/11lv5oMYLtEWlm7FKTVgxJq/aTbamAZvKUobkla3olZjhki9ehw5W6b2xQY+1EjPk8KzNjNdYg/Itgj1Vl+O9YgOifuAtOH1WbSCi15SsKC3EutKsNQlabZbSiUHXoIFzs6rrkBY31Rsx2uKm7q92oeiNwoMVq2hB2VlwWQQxAsEuiN7v93+58CCAofX7/T8yEcaijj7/LiOy4vuBM+8vJOJZ/lzO9hYu06rbyBKrXOutW7fcX8mqx15orvMbrqxwGQFJ9cy3AXQ2X7WPKwH6uts8GTsgqfGzV9UliH6/n980Yo0j9J64jMi6jSPKqDrnEIzsBgUkf7A4vdGoXfejuLA+uWBTmwGE1Rvya2kPX8cH/+2f/exnYypbqOCjSqKpFOsrr7ySTyQ1eC2TBgmuL1jE8oP5qF+L+6GVnOGymTkXOljsDWLpFVmRLMvOmmd3Hwalwa8mLObzxvl1GJQ7K36Jxlh9VeqejebTwp6ujerfmqbpsRqUTdZmi54mojGy5fV63cQsSZI/rsG5edMtbsZYNNJmA7tQdLBu2bpuMbeCBWX3e3X8/U1KQWnsY/uuAtibI/oM6bMUI0teYwB33bVZb3UZq8xrs4TGUrGuT02lyipeNn0Vx2vZlS6M3JuvlqXV0QgqK/wlXUPKCEhSlr4TVvxqLrEDkt4aYYgA9yGNI7RRONY4wt2n6ziO0KbLmL2OfQcK30GbXVuc3lDgrnULuovTG+OU7wPCGeUmpMXavWQNafE4Vkm9/VImjCaOmkAmSRJzVK5s0J/0er2XNDgJ+X4oKKjj+MILL7yRJEnsIEeUzCSdW5oY9fv9n6ZpSjCyGrMxS2C6HnDW6zXMftV5d2RsbOzMxYsXo2wc0GdVE5MXXnjhq0mS1HrjmJ6nXdi7WGWPSE9e+SPW7lXv9b5dow0RytL7Sq/Xe1nnjj4/ITeB6Dqu6659b2vxmr2S6VUtKBub5fLuxMTEcWVHlb1BCsCu8vtnrAoqWqT3+tXW6gKgvs1Zlv3DyZMnD+raVEYriybwFsGrKPsvD5IkuawNu5pXhs468Y4zwUjsxwNdQz777LNP5+bmfknnVazr6N27d91fZ1m/bTXXXuKjpaWlMR330G18nIFWB/u9N+fzylgbsL1xxNfrOo44ffr0QV0DYo8jCEZ2T+sCkjYQqQ/ydOFBANEpk6JNk14FR7ydz38YO/MnSZJv6NocepLqMlzm5uZchku0636/3/9amqbBB26uV2Sapt8uPIiy5It7sRbevUF5nYI9gxRsPzI1NdVTgN8LngZjP6tjdhJUdj/CobjNAXaTRi0WEFSST9eeGOenNqXY1/uTyJtSRmazA29OTEx8SbuOXWZxYMdt1nvV2a9+3+Cqj4PO++Na4A59zmmhRjuSAy2mAJ2TZdnvJ0kS5X7gl4lO07RW9wPrgZ2z/EDjqjJ6ddWdNo14m1iq3Dyl+8aSjkmZJfCAET147rnnVKnqh9qIp7WdGJtQvcARAfT2u2OrylzTtU8bemO0e/EC3PvOto05r3TjCNt6qI6B+FLHEZRp7aZrbSnZSiASQGi6+XqT1zKCAje1uK8bfugeYF6mYsxr/rz6eioQGXrSQonW6vX7/d8xkTINtFBUs96DO5lXuRlNpGKU2vImPbXt8e0+j3aXf+UBqtildFQm3PzbhpE6vF7fHTvp1s7ad+3ENsZX5T199Hmz1RfWa5CN66oZBL9fx+rHC3TEIVVxUInWGJ+lqstED+mmzd6OUsWhaWo0h9D44bo23oXuP+6d65S8RAgaW349VisAxwYkWb/thhV3X/I2FgblBbjH9/nvRptXujYnVp3XPDbHEQQjEcsVG8hrLAKRAELT5L2C4Ih2IX1HA//QN30NpuwArRdxB2KUslgDZfnqUA6yi9R/6UWVaI3B9Uxwvdhq7l6SJJf0OY3RS0HBSPVcVWZH4cGasKU7TY02B0QrpaOsSJcFU+PNEA/sztoTNmgY46vyLF1tWrBqcRySJPm+secIgNrILxSxshlqUCZ6WPncpayeT3Vm53M/rUmmef4cvPtZEAEX4QHniusdF2uco/kOOkUbeh/pvhSrd6633rUf0cYRA5n6nR9HGIKRnfaFyJkyURGIBBCaFhvOnj2rYMBju/O5zEyYvFRDjB1jXi+1GMFVBTh7GgCGLtVLVmQt5MGeyIt77zaoJOGysqIUlIsxmdJnKEmSg3Xc4V6z7DQnnzTeunWr8MB+uetPmqbUVKuYl0lSl+tE/jxCZ7gM9LwBMJr8c+OVawvGW5CrY3nWQfdcFl6XS7W6IEqapn9deLAa+X3j8OHDNXk6wPbcJlFXISS0gb6RaD/XOzdagCtQgDs/H2O0n2riOCJ2diTByG6rbSmwnRCIBBCaFhVdllaSJMcq2LF0z5VECL1r1su4eiPCztl8QOUFPIPQ8fCykuidVZ0yBuV1L8/6DFuyM1p2pFW7ybm3O7o2/SyzLPuKrm1eECcI1xvTZlSwGaJiXuC/LjuJ8+fxyiuvFB7YD8q0AnuXZdlvmwjjFY1H7captQZV6cjH5rFK4jVBDccs+bljzyWg7vKS9JqLx9h8yXink64oO1LrXKHbHAzYcyWwfr+f3zhCjyN0P/I2YDOOsAhGdlvjassTiAQQmhYa3nnnHaNdxMaY1ysMfuXZkaH7vGjAryCHzbgKuQllPMuyryoYELpEq3sPyEqqVpZlR+/fvx+8b4I3KG/S4p6zEmsyVeedwt4CWl0CQkeSJBmLESh3G0LSNP2rwoMonbcxpS5YVAZqxvWLDK2hVTp0jVrX/CZWSby68zaL1KYcnu5joTPqgVhcJluXNzUgqM22RKE33ptnq+TsORjp+kWGxjhiawQj0RgEIgGEpsHQQCCyykFCtF2IN27cyP/s9/vnCg/u3byCAaEDkQrw6Lgo4ENWUqUO6fhGHpRfKTzYALEmU3qvFdzv9/thU64CqOHCXr6hLkZJvrr1KES+QPBPNXsb1iIGI+n9BYwmWolW735Qm6oAQ8rvXzEWfZvAzutMnTa81fA+Buwkv4bQHxsB1fm+lD+pGOsedawuNKSox4tgZLfVvXHqoBUCkQBCUUDk4sWLdQlE5mKVgNTASl/a8RUqKz7LsrMmQolWHRcdEwV8Cg+iTFGDPerN2sBBuRNtcG4/py/WLSBRw4W9fOdrjMzIXq/nSrQ2bZyMBvMWQBpXuQaoWH4/UCWHkLQx0KviUGYf+RDy8VVXg5E2A3Gt8ACAYT1wmVERy2qy+apb7mizeU0D3Pm5GLoalFfq/d3Cg/UXdRxBMLLbGlMabXF6Qwt/xwsPAMAeqD/k0tKSC4jUIhBp3YxVAtILGobo0XdCJbHW1taCD9q8cjCNzJprkXxBPPQOQQ3KbbD5bwoPNke0yZQX/K1VQMIu7K0XHqhOlB2s7pimafrXhQeBiOihBOxZHowMvXnKu8c3sXf5PW2qUbUVANij/NoXer7jbSRk81XHJEnyf2sdoIYlq/NzMfQm18OHD7u/NnGDa9RxBMHIbmvEwNoGIk8VHgCAEWmXs0qWKuClYEKSJF+uWSk+1dO/rEGaV8oyCJVTVQlI9XkMsBNx3njlX0NRELaBDb7bKl/cC71A7pX7bGpWZC7WZMp7v/fc8yKi2mSG9Pv9XzYRzk9v0tjExefW6Wq/MwAjiZJd45VibuT9IE3TvIkW11Gg3rys7roF5/JrH71OEVAelPPmW63GOGJ7BCO7rfYfCAKRAEJ57bXX8uCZK92TJMmv1HSXUh4cjdEw8VfO0wAAIABJREFUXgFJ9QFUZmPhweEpSHJcE6fQu8ea3kuwZaKUwfQmtE0vgZmPoUJPprz3u47ByNpQyekYfT1adH62QtMn8QBKETujoamb4/L7WMT+tgAC8KoM1a1sab4JkQ0NCCgfz9fwvhSl4o63CZtxxACCkd316YX1yVp/IAhEAghBi8vvvPOOOXv2rOt7dt4OOOra/0XX5uu66Yeu0e5Ktfb7/XOFB4eXl3nVexqSJjrq1ZZl2U9YeG4vb0Lb9GAPi3wVU6Z3aC06PwEA++C1S2jqImL+vLuSgQIguHw+TjASodX1nIrYMoFxxACCkd1V64VeApEA9kuDnOXl5Twb0tY6XzPG/IYxZrkBb26eHalszpC08/Ljjz/Os4r2WApmPMuyNxQEWF0NextxmaBJknyj8CBKl2XZUa9sUDD6LKr/QAuOaD4493Y8BuG953G6xbdDtKxRe37+uPAAUBJXghgA9ikfp9iNmJ3hLXLXddMpAHQVAe5miTaOIBjZXbXd8U0gEsB+KBNSQcj33nvPzM3NucDH63ZxvynZLqtaENfCeOg+DSrVai0UHtzdiSRJDurfCLlzTLvPVaI1y7LHTe8l2BYq5xsjGGk+L7H5d4VvNk+UHY5eqSZsLw9G3rp1a9sf2I80Tf+J9x5V8DYLAaiYxt/qL89xaBavYgUVDgAAlWnRJuzgCEZ2Vy0zIwlEAtgLF8xS6VBlQvpByDRN/xeXadgkaZpeMhGyIxVIVGZjlmVfHbU3RZZl3zReuddQVI5WO66SJPkeO5mBevFK1QEA0BlPnz79R442AAAYldbc+v3+P/PGFRGM7KaHF9Yna7dTjEAkgFG4AOSlS5fMhx9+aJaWlvwSe40NQnpWtCNbgdXQwQAFJJX5pkzHwoPbm02S5KW1tbXg2VunT592f21CCV2gU7zsbHq5AgA64ZNPPjG/8Au/8L9ytAEAwKgYR2zvwLaPoM1qt5hEIBLAbrQgrubJ+vPo0aPPlC9VFmSapn+lAF6apq0py5MkyWVjzJL6KSrrMxRlNurfVKZjkiTDBmzzsq5emdcgdCxtSaW1Bjf3BgAAAOpipOonAAAAnmjjCIKR3VSrYCSBSKC7lPE32BNRwSljG1vrS4Eqr/+Hb91ez1oVgByg6+OSK0EbijIb1ZtqZmbmJdtLc7f7gnq0HVcPwdXVsLcQvTbrSuFBAAAAoDrjDW0hcMTYzAwAAFCZRo8jbt++XXhgvwhGdlNtgpEEIoF6OnfuXJ6FuB319xsMIkbmsubu2WtYV8oF6vVen5iYOKWgXcisRP1bKmtrjJkf4v3MsyJD94pUsNn190zT9GbhB1Apfc5DU++E559//tfSNOXgYq/yydxO9ygAQLdoTBmyjcCtW7fcOPlIQ+cd2khoHj16VHgAAKribZCY5SCgJvK5JeOIgkOF7wRCMLJ7atMvkkAkUF9a5LU3zp2s7fDYKAZvzHe8nUP0KPs8OzJKMFJB57GxsVM22Ljdbq3xLMveePLkSfASrQpESpqmFwsPomprU1NTvdDPQRPQmZmZFwsPNE8+gb57927QJ65JkLXd5xGf3yOC99I19vyMcd4DAKLRXKGnKiohFxFVDcRq9CJijIwGANgrNkighjS3PM44ooDMSARTiw8AgUigMZ7jUFVu9enTp387MzPzm8pGDVlu6caNG+b06dPGZkduVyb1RJIkBxWIDD15eO2119xfh+1biYbTAN9udBimPHCdRck48EpSt7X0dK0pc9c6RA9bAGiO0BtUvM1GszuMkeusR4lWAACGwzjiGSotOx1rHJEUvoO2q3xxi0AkAIzmwIED3zbPBu+C+OCDD/J/Jsuys9v9e1mWfdNEKNGqTE9bBvQ6WWC1tFmuJCRvQHuk4e9PlJ2CMbL92kilnYfI3h+ZyulYTT8/AaAr8o1NodtHaLyiDSpZln2l8GD9nTD0iwQAYBiMI4ryKlCxqisQjOyeSrMQCEQCwJ6saPFdZU1DBoeUpba2tmaSJHlpm74Ns3rs448/DlqyQk6ePOn+2sTd5l2Qb17yMvWC8II9W51vjZFl2e+bCAN0bxJEZuQO0jT9u+0f3bsWBcsBoCui9RFeXV3VGHnMBfca5IR7/gAAYEeMI4ry50swEkFcWJ+sbERKIBIA9i5N0zw70vVZDMXrAzm/xT+pXpJ5OdeQFHCxQZc1gi61lZeoDD0ob/gOQeeQC9KHRs/IoeXXjaNHjwb9R70JV9MmjK3kHV/uEwC2k18fQmc0mGeDeY26J2jDlMZaBCMBANhVlE3YprnjiPEsy74acxxBMLJb1qp6tQQiAWDf8gzC0KVaNcCwzbVP2drwjnqmHddjoQch9IpshDwYGWtxz+4Q3CoA3gTzJlLGgReMZAVxZ1GC5eoBarMjp11fUNQCwXkAO1nTImLoUue6z9tewoNj5DqbV693ApEAAAxtTesejCNyJ7RWE3McQTCyWyoZkRKIBIAgtBh7XX0W1W8xJC87csH7Z/O/v/POO0F/lwZ4yu7MsuwRwchai9I7weyejVt7/X7/awOvIxj1Qez3+z9u4vtSsmiZMK6XLtmRANAYUbLlzbPVQRYKD9aQ6/UeuqoJAAAtxjjCcuOI0OuAPoKR3VJ6MJJAJAAElWdHev0Wg3CL7y7IYkszvBGjNIN77kmSXC48iFpRUCxGsEelMG02bq+B2WfzaZq+qM+MsuhCcpOfNE3jNGdol2jBcnfNy7LsbOFBAEAd5RfuGIuI3/3ud115+XMNyGqYd2XkvR7IAABgZ4wjPpePI7TWsbGxUXgwFIKRHVJ2v0gCkQAQ3B1XQiLkQEkDDQ04FGSx2WoqzXBQmV+hAy5eVidZkTWXpunf6BnOzs4Gf6LeTrvlwoM15nYKxsiK9EqOUlttOFHK6eh6qIVcTcR0+hd+AABQN/l9M8Z4ReNgZTXY8vJXCj9QH+P9fv9P9WwuXrzICYoY8pLpoUvkA0ANRAtGNmkckWXZn5nIWZGGYGSnrJf5YglEAkA0+QAmdKlWN+BQsMUFXLSLKyQtEtnG4NddzzfUWrRBuYJ5Xq/SpgR8FlzGgbI7Q/MWUQlGDifa4vPVq1fdXxsVLAeAjlKQ5F2NMb3ey8FoPNyAMcuyNhXquZIViUjyMoahN4F5jhS+AwDleOA2unZ4HHFFCQl6rjGzIg3ByE4pbWGLQCQARHUzy7KfqO9iyIGSnw2kr7W1teCDkNdee839lazIZogW7JFLly7lf/b7/T8vPFg/4y5Iv7wcPj6lhR31i9Rnm0D90G6aSOengs26HtpSwk0Jls8G/mJREECT5GMWjY9DU1aDu/dnWfZeDcusqarJGS10xs5mAEK7deuW+xfrXr4QQLtFm1s2ZBxxqqxxBMHI7iglGEkgEgDic/0WQ/eO9LKB/EbbQShwqmCL+hCS+dUYUTMN1JtPAZ80TV9uQAbainYKanAeY6egm/QkSfL9woPYzp0syx7FyNw13vUwy7K/KDxYH8t6D4wxnxljPgz8Nc+ZB6BB8o1uoSuHONqkomwBjQX6/f5HNVpIPJJlWb6pSwudodsrACUiGAmgSnkwMsamJrP1OKIuNscR586dK2UccaDwHbRV9IVfApEAUJqVLMv+y7Fjx8YUHAk1YHDZQAo+hS5Defr06fzPNE0vFR5EnWlQflyD8hi75LRwpsD32NjYkh2r1DFQvaD3QGXPYu0U9HZgkjU8AgVvx8bGTmnxOXQfT10DlSHe6/VessHyugXM83H3kydP8pJ8CpKHCJTrvbTltDkXATSJ20B1XJtUYpRTV0UH9cubmZl52bZNqHrThhYQP1IfqvPnz0d5zUBstnShsRUZbvKGA6jIPVuqtadyrTFKnmscoTHK1NTUy3auVatxRFll3glGdsP6hfXJBzFfKYFIACjVgyRJvqdFeAUxQi7Ch17QN7YEpZ6nMniSJGGS2Sw6XtcUoIgRiFPwRAHJixcv6vx4P0mSL7ueNDVxwhjzrcePH0cpz2ps1nCv18tLtCZJUqfX3gT5+DP0ddDRMde/W8Ngef66dV5qo0fIiaPKadtrNecigKbJN1BpzBIrMKesAWXOT01NubWPBRsILdvmAuIHH3wQ5R4IlOHu3bvut1AeHkDVNMfqaT4Ua+7/5ptvDo4jqgpIbo4jlLFZ5jiCMq3dEHUxYXF6Y4FAJACULh8duYzDUDQICT0Q0aLQ2NiYspi+U9GCDfZOx+u6MqVi9Y5UuVYFOjUQrlkPBQ3Q/9LYoFSsnYKuFIwrv4yRrCqIq2BujFLCW/T3OFT4oXId6ff7P4oViPSu1ZQLBtBEK/1+/6eh+6r7dF/QQqK99p6qqGTrCT8QGWvBFCiDPksa02RZ9tu84QAqpgpkjzSO0Ib6GLYYR/yo6nGEMjbLRDCyG6Lt4l6c3lAE/1uFBwAAseVlJBQkitUzLRSvt+UVzopGitqHSRSM1EA4SZKXsiz7HzXYHT3rlyxRwDQW7by0KIu5By6IG6u/h46919/j/QqD5fM6J9VjVeVj9XkMHSCnXDCApkvT9NsmQl91n1tIdH2vsyz7B1tJIbZxO5b+gRufEIhEGyiTWeMsDUU4oACq5OaWZYwjNJfzxhFlXP9qMY4gGNkNUVbQbCDyWuEBAEBZ8pGDF8yoHQVKbf+xd20AFc0TNfvM0UDYC0h+VOGChMY3H7oBesySJS4TTdmnZA3vmTJh/kXXwVg7WLVbVOemJos2C6bMDMlxGxy89uTJk7HLly/nZQJD9Qp2/HLBNe3d2nozMzOm3+//uOvvA7BPV5TVoPtrrHuCsQuJyk53m1W0sGfLxMa6P8zb68MZ9djTIialWVE2ZR6rl1po3rlMdB1A1a7EnlsaO47Q7/DGER+WNY5QNnqV4wgFI3uF76JNPr2wPhl88ZdAJADUQilBov0g66sdkiT5holQFniQApJ2QD5mB+RlLkpsBn00QI8diDTPvp8svuzdgzRN/0pB3Zg7WF2w3O5eLSt794gNfp7S4rPOlxs3bhR+KASvXPB3Snhd2Eaapv+09SMAhqS+6pd1T9DGjdi0WUULerpGq1+lMebv7Vgi1GLivM2YuJam6YsaI2lsHasnJrCTNE3/zm6iC0pVKGy1h15JWcYAsB3NLS/Gnls6ZY8jNJ+N2Vt7GGRGtl/wnc0EIgGgPsoKEu3FQKbNTU6bRoveh8nRgPytt97K+8cYY5ZsH4XYgZ8TdqfgZtAndiBSi4k2a/g6WcP7tpklHnMHqxcs1y/5YeQg8oJfllWvLVbfUvPsxhHKaQNouiuu51MZm/W0oKeFPZWct2OXU3Yx8Y6u5SMuKI7bYEzet0rrLqoYoZKwuk5rjBQ6Mx4YQV7FI8bnyvUss/3aq27XAKDb8nGE1gQaPo74Z38coaCn5rNVjyMOFL6DtrkT8vUQiASA2rmZZdmfzc7OHtQifJ0WKNxOMld3H82WpumfaAygAWzs4Ld2SGvRTb9rZmbmZRv4uW6DPyEDd7P23+ylaZoHmjQJiP050meVrMigdE5cHxsbO6Xrjo5hLFosU1BQGTdjY2NLWZa9YTeFhMr+PmIDgr0nT57kryVWNqRDuWB0WFdKEi+EXheouQf2uvytMsYsjq7XGkeo/67uRVNTU9N6DvrSomaSJB/b47DVdVa9qv+9FgzdN3QP0HhI94CYm1GAEej8Pa5SrRsbG0HfNy3GqyrJ0tLSWL/f/+923sEGqZbxyvyyERN1tjmO0JyvjEoLZvdxxOMkSW63YRxBMLL9gk2wCEQCQC1poPSdsbGxM1pQjr1oPQo9HztookRrO6w8ffr0azMzM7+pAbIGtjFpkUMLiDqP9OfExMQpu0twzQZ+bm4zEN/NuM2EPKesM/2sdgpevXq1tHIlej02+PM2k/FglEn41ddee21M5WdCL5L5lDWrc0UT016vpwnftSzLvmk3Xqzs8byctf1KdY7n5+TFixdLmTgSGEeHdaVlzXjhO+2nnk9/pA1NZYxZHG1m0j1CX8qm0O9W//SjR48eHBsb6+10ziVJkl/77969m99jynrOwAjyTQ0KKMU4P11VknPnzv3i2NjYt/r9/ltpmn7bjvm7tKGitbwKJsx/UHf5OKLX69VlHDHWlnEEwch2e3hhfTLIDZtAJADUmnaNntHuqboEI71Mm++RadMeBw4c0C7lDxWE0QC3jExcNxj3gpJuEH7NBiZX7de9bSa2h+zXrP3KB/DKhNRgXZ+ZMgfqmkzos2qzJAj+hKONGZeUrVjGDlYFO/U7dDxV8mZmZuYlt3PVGLPuLZw9GNjBOu6VH9P5eCTLst+2pV/zfiHaFRu7TLCjz1UDygV3MZCCiOpY2j4Gvc6uvNatpGn6n1RZQdmRutaVXT1E9wmNMdzYXIvwXlbQM7TxhPKraIB8ffGVV16JVoXCbfjStWtubu5FtWywX8aO+80OSRduvLXd4wAwtDRN/1hrH3UZRxi7lrAVzSFjbsYNiWBkuwW5AROIBIDay0sUKnOszF1bO/GafRNsaRedXG9PTEyc0SKB6+9SBheU1EKeJgM6173A5NKwT0EDdX1G9G+VXa5EC5GazJjPdy7+IYH64JZVNlXZimVdC92CmXau6rqnCaItqTNd+OFtqISO/h2dk2Vev+teLtj7fB6h7zCAPVBg4m1VD9G9t6wya9vRImZZFRiASO5lWfYTbcCK2R5EC+r6zCrg6bKCNP63436zU2aQ+bzvpDb8/S5BSQD7tFqncYSxc8+mIxjZbvvOiiQQCQCNodKAp9Rnr+pgpJsw2t2rlGBpn+V+v/8HJ0+efLGK8h8KUCgIqi8FgHSu6evw4cN+6Z9NWihRqRL9f/qqcsegzezUX98luBKHDfKWvoNV55ULzrvsF7dzVRkEvlu3buX/5c7Hqnp4KHha56xIsoQABLDQ7/e/ojJrGiPXqZ0B0ERJknxfFXkUJIxdxWGrrCCNr7Ya7xs7B9XcYG5u7mCWZe8lSfJLhR8CgNEsM44Ii2Bku+1rdZBAJAA0ymq/3/+xeuNoklZlg2oFACx6RbbTgzRN/6ML+GhQXlWAT79XX03oq6TPhYI//X7/p2mazhd+AKFUvoPVZb+4nauxSpnthxbrFBy3fX0XavcEASAQlWvNsuyjs2fPjmkzSJVjZKAFNL87o3FtWSXlfTt9fv2MIQUkbTl8siMB7McDxhFhJW16MXjWhfXJPd90CUQCQPOkaZqn5Sg4VBW7GzUPuBCMbDWNMc6rL+jFixe33aGMz2mDgAuKpWk6R3nW6LSD9ce9Xq/S62GdeeWC/zPnI4CWu5Mkifo+5ZtDtuvbCGAod+wG2HzeV0feJslDHFIAATCOCIhgZHut7fWVEYgEgMZaUY8MBQOrCg7pd5vPAy7fLjyItlE0410/0IYifRY1aVHg1hjzeogy+tiV28H6+OzZs0wYBygjUouIT58+/VtjzJXCDwBA+6zYrPl8MwabqIC9cxtgvb7TteJlSBKMBBAK44hACEa2156yIglEAkCzJUly2dheYFXwSrSywN0N89odrSC0y7TCv9Ek5erVqy4QeZ1s4VKxg3ULeh9cedYDBw78x+JPoApedglZqkA8Kkl9XddB3ZtZSAT2bEVVcDT+Z3wFoEMYRwRAMLK9Rt51TyASAFohDwJWUZpQgciJiQljgy4sqHaDMtC+7DJy67pDugouEGkXafSZoE9k+djB6nFZuubzjSsK1N4r/BCGkc+zjh49GuzNsvdOQ+Y0EN08C4mju3//vvt/jjToaSOiNE3/RP/6W2+9xdsMoEsYR+yBN444RDCyvUbKjCQQCQCtoSDgdS2+e1mKpfB+H9lf3fIgSZL/TZlWCkZSsrUQiHyXQGSl2MFazNJ9m+v0vrDZBmi2ZxYSyezandeDb7zGTxPlWlG5d5V9pz83gI5hHDEiv5cvwch2Wr+wPjn0JJlAJAC0Tp4dWWapVg3ANBlVyc69lgpHo6kkpjIkH+u863LJVpVbJCOydjq/g/XSpUt+cHyh8AMA0C2b9wVljIfMdAa6QuXe6c8NoKMYR+wRwch2GnoRmEAkALSSyrytaWBU1qDI7YhN0/RS4UF0hQtI/kQlWxX86FrQR5+5GzduDAYiyaKqh87uYNXmAG+zCMFxAPjcvCvlrftCW7K7NPby+tACMd3z+3Nz3gHomNaOI2Ku4xCMbKeheo0QiASAVsuzI8so1aqByuzsrFHfQEr/dZ4Ckr+uoEev1+tU0EeTDwUivTKYBH3qp1M7WHVt1jmpzQH6TKq/K8FxAHiGMsVfd9ldTQ+o6L7mrvtASVZci5CLFy/SPw1A1xTGEU2+Dmoc8f7770ddwyEY2U67ZkYSiASA1rvpMtRiL6oo4KkJaJIk3yk8iC56YIMem0GfsvuXlkmTDWWBavKhSYgmI5TBrDWNgc+3bQfrIL9cMIFIANjRiio76FqpLHIF85p2b9BYRD27dd2fmJgoPA5EtrnZy6sQAgBd8cw4QsG8Jo8jNE8+fPhw4WdCIRjZPp9eWJ+8t9OrIhAJAN2QJMllU0LvSO/fv1J4EF31wC5MfP3555//l6WlpVaWbVVGsCYbygLV5EOTELKDG2HZ38HatnNT56VfLphAJADs6k6apv/BbVbRvUHX0SZk0GvDl56rxuO2SgnlMlGFPCCpYLg2ImosAgAdUhhHNKUSj8YRWtPw1w1ttacoCEa2z44lWglEAkCn5EGRmJNB/dt2B/a76htS+AF03ZU0TX/LlW1t4i7BrWiRTwEslaNyZVltwGfHcRhqZXMHqzs3m57B63a0euflefqWAsBItFnlV13vdWUI1HUxUc9Jz00bvuxY/O0kSX7FEIxEdfKNiK5kq3pWU7YVQMdoHPEbGkcoS7Ip4wiv1cyr+guZkRjFtiVaCUQCQOdoAXpNCxSxFiW8xXuyIrGdzV2CypLULkEFfprYr08LKqdPnzbvvffeZjakHbAvEPBppGd2sGoipglZE8uLuWxILzPmVTsZBgCMRpvrtJPvVVdyTYuJusbWYdOKWzzUc9Jze/r06d8yFkGNaE74qmsX0paNiAAwgjt2HPF7uhb6Qck6jiO0ZuiNI/K4UsyNJAQj22fLYCSBSADorPy+EKN/jAKcCshogLXd/QfwLKdp+rKyaHU+1nmX4CAXhNSCiv60wZ6v20AW537z5TtYtaDr+oVpN38TMkvcZFIZCPY6f91mxnBeRkbmEdB6q/Y+/3suU1KbVlZXV/N7RJllKDUOUUBH4xC3eGg3RP3egQMHfotrPmpmNUmSX1eWjStX6MbQ3DsBdMhNOy973c0z3ThC1WzK3AC7wzjiVRs4LW0ccaDwHTTZwwvrk4XyYAQigdbKd77qpvLo0SOOMkrnasq73pTAEJRtcMIOeJdnZmZ6Ggzfv38/D6hoYF6n65kmCBq0a2e3qMegMeZSkiRXyD5onTt2QXc+y7Jvzs3NvaTj/sEHH+STttu3b9fq9SoI+eabb7rdrPlkMk3TP27TgrRXHqhun7Wbxpglt0EBQOvdtF9HlDXwwgsvfHVubm5M94jHjx/nYxfdI/S1sbER7L1Q0EbXegU9tfnPs2Y3eBGARJ09sFk2GjMvT0xMnNJ9U19ra2v550WfnZCfGQCoqZUDBw6s2DWQeY0jTp48Oab1NK2DuDFEV8YRBCPbhUAk0C26cRzXgF69y4AyKQiuEhMKziRJssKbjxGt2sG4vhYmJiaOa5egvhT80eKEvqqgQbsG7JocuIxiZUIq6E4QshNW7DXtmaDkJ598snluVrVwts25+ZMkSb6RpmnrrsNe1lHdFtzv/Ou//uvPJiYmvqj7IAHJ+GL2rWkzMpCC03rLfJIk83Zj1Qk/MClaVNT94u7du/mf2mA1zGYWbX7S2FqLhjrf9d9+VRN7rf+O7QdPj3b4n++6j0vv2V6Sy3Zs9Uav13tJC+PKmPQ/M/qs6DOj/waAFtKcRpnjC24c8cUvfvEroccRLghZ13EEwch2eSYYuTi9cYRAJNBquoksnzx58gu73aB0Q9tu8VQ3qsESnrZ5Mdph1p0DIWmR2J4n3+t6cOb/b+8OYuO6z8SA//2egMCKhAjoIRDdrBWgUC5pydC6tVvS2T0astLsxQK2ktvAuuzWiuUTDyuqB56sgEZPNnYbCwXknrqyrWsRCW1vllaD7iWCgUgbhEYOxcorI7l4xsU3+j9mxCEpinqPfDPv9wPGkvmGFN/jzPCb//f/vi+eP7EpoO6v2RE38u1YJCX7/f6fvfLKKy9U1QbVrum6dwmOqoL26rahXcrNkeTUxIhqU57ZelIybsePH1+IRbO4VYnJTz/9tPEFs3g8njhxYlidu8ljc7Uoimtjn7TP4jn1NG2HNsYvVUVyNQvtwIEDYxsu99s3vvGN/55SejOSkZu9Nm0Xd42q2lRLtm0vnnfsv6qqiaFruf3a2ZHNVYvf/va3Xzp69OihDRUI6+J3RsQ3Kb/WPeE9183878TC5U5fB4+MfaTl4ndpVzR0rq37HbmFWPxeLopiOVcZV8+ZP9nsORPPkyrGGr1u1eJ8kLhsXrxG1TlSYy/inUl8H1Qlj+rQtZgy3idlk9Qt4EFez433m2k0jjh69OhCrAVtFUfcvn17/e8NxRGNi2Tk/ZTSi235hngm65ntnIjUtgOm24O8m+YXFgbYQixILOx0UfRpVC1a8y7XTmsiGdlBEcOcL8tyfZfg888//6cLCwsvVIF4tSgRCxLVY3p0QWInNu4YjP/fJGlyc6Ql26RVH9zL339TX7uLPsi3SJif6vf7//H48ePfr5Ijo4/L+LN6XO5G9biM15R4Y73xDWZuxfo3bX5s1rnQGhXJBw4c+IuxA+2w3O/3/2R+fv77kv+N8n62Hs/6evHA75ZtVRurUl5UPJaTLXM5Fp+rPjk2tlR/7/f7v0kpfZb/90FOKt3Lf+520XD4b0VFRZ0idmpAkwujbXuEY2AnAAAWmUlEQVRcdelcd6J6jK9u8pwZPm8OHjz40vz8/DAIqlrSP0mV7H/C4vwzi++nK0n0iEUnKM5p8n1Qo5uvY9NhVRk37Zp4/uSqv7GPT5AbG2LeuZHXxfU4ot/v/7N471Hdqck4ou7ROSNrLg8O5G9SMnI6DB9oI4nIb3X9gkAHxHP9u7lyYztz2+yUvdP1yrYpFomd2lvIjVSP3dQqajjwm3oNE4FlWabRndPPP//8ifn5+Re2WpAY3SU4KhI7T6g07Y28Abgx4a+HVeKM+sVr3WpZlqtVYjIelwcPHvzh/Pz84c0el1s9JkfFgtkmCfGh/Abz02pHa1mWbX693fiGd6dJpMcW6kdcy5WpbX0+PijL8o9z/LVZfLVd3DXq3obfo5Jvj9u4OMP+uFN12mBHquf1tpXrOc5pRIOLiHU+H8+PfWR6delcd2PsOTOSWBiNEzbGDMfybWg02d+gppJdbaxwvTT2kfo08bt9Et8H3WvwOrcxfmrk+dOmqr+aVMnEx+KIjXFDQ3HE8P1Lg5ua7hzIJ7cXL9g0745EJPvIQsH+uac6jc30+/2fRIAS7QTrFG3pMkkPr31NW985nYPtIxuqDYaLoxt3CY6KuabRCTJ/6N6Gm58fuzFMTG6xo3+4MNbv9/9FJM93+LWrN+bV5qB4XN5refJxoy4utD7IjwOAthjGRVpXMkUebIjXt03yZ9X7hSbiqC5tzLDG1LyureXZ2NR+ww0fdY95GnUgv5C/OXaESfNFXvyQiGS/TNtOFJh0Z8uyfCESkXW2aI0WgtFCJCp2yrKUjGSvPdisUma7XYET3rKFyTC2o3+7xyQANGS4iFj3eIaRGWRd74jCZNiYwARgZxqJI0Zmot4pVnozNxosM2fvPJCIZB/dX+nN7GSHGrBHvvrqq5+kBlq0VrMMyrL867GDAADAvhgMBj+sZhnXaWQGn2QkAEypfr//p3Fmt27dqvUER9q0DmdGptxWRyJrspn7yX4yAwHaZfHAgQP/OuaV1R1EjLRo1ZoOAIA2GauGGgwGLxVFcehJ32O/3//7siz/39iBR1XvbZ5hW5kriuJw3bF/OHHiRPVXyUgAptXcdutcT4gntiv0q9rxtv136Fx0V7t5c7tT2Z2RDgvDmZFppTcTswYXJSSBXXhdVSS0ztnUQFVkJCKPHj0af70yAQsyAAB0yGYLhZ999lmKasEn2Wr2dEppYTAY/OeiKP79DufV7ZfhBuEbN8bysc/s+PHjwxncRVFIRgIwrWL82MJW5/bb3/5201mK0T3g+PHjW35ejiN+XBTFX+bNTW01XEdsMI54WBTFemXkaEIyLsrs2GcBPO5+vFDlVs9Ae8T84DMRJDWRjMzMigQAoFWKooiywIWTJ0/WMu8oYt8LFy7EQmP0F/vb2Ijb0jj4SCx0/u53v6s9/o958blNa/0llwDQIrlLwvdHOgI8s9OnT6e33norfpH+PFdHtnEdPeKI/xBxRN3JyJE44nb8pxg9GAnJld5MlKT+NKX0xdhnAzx6bbi00ps5JhEJrdRIVWTsZJqfnx8GZy0NngAA6LZhjBpxax0ino6E5Icffjj8aoPB4L/kNm5tcz4qQq9evVr7tzVyLcX/AEy1siyHG29eeuml2k4zfjdfunRp+PfBYPBJLiBom4gjDsf3+vDhw1q/tZFrOYwjirF7PEpKruYL8+7YQaDLojVjJCGXPQqgnQaDwVvRiqpaNKlL7OZKj4Kzy370AAC0ULRYq3URMRblLl++nH72s59F5eWhwWDwcVQQjN1x/xzr9/tvNxH/p8ev5Z2xgwAwXW6kmuOIlDc3vf/++8M4ot/vf9K2OCKldHEP4oitk5HpUULywUpvJnrOf/cJQziB6RevAT9Y6c1EW1Zz4qC9zsZupmirUOdupsOHD6fFxcVhj3ctWgEAaKlGFhFTrmy4fv16LCR+p9/v/6+2LCTGomZZlt+MRc66qxlCvAfIVEYCMO2Gv+tGfvfVJn5P3759Ozb4x4zq1bZcx5wcTU3FETtORlZWejP3Vnoz8RN4Oc+IA7ojnvM/iteAaOPs5w7t1u/3L6QcRNQp2lNFj/eiKP6rhwAAAC0VG2d70Vo0ZhTVbXl5eZiQjIXEliQkP4jvJRY3m2jRGtfw6NGjKW9OtikZgGl3L0YTRRwRm/LrFnOo7969G1/1TN7oP9VxRFzHHEd8VH3sicnISsyGixlx5klCJ4zOhbzmRw4TYbEKItbW1mr9fl977bXqr63ZvQUAAJsYdvFooqohbUhIDgaDf9jHGZJxnmc+//zz4eJmE1555ZXqq1oTAKATyrL8n6mhOCIqD8+dO7eekNznjU3DOCK+l6biiChsyNbjiB0nIysj8ySvjB0EpoG5kDCZzqbcQqpO0VJhZCfTPY8NAABabLjgNZJIq10kJGOuUoxHSCn9XXxoDy/HkXyOZ2K+UywgNtFWLW2xiAgAU67RTU1VQrJq2ToYDH6dUjo1dsfmHMktU4eJyPhemoojRq7h7pOR6Q/zJGPR8wfmScLUMBcSJtexamd0zIus0+nTp6uvZlYkAABtt95irYlWrZXLly+nS5cupUgIppQu9vv9/xvrbmN3rNfZXI35aiwgRrIwV1fUzoZEADrqTiQIFxYWGosjIvn3xhtvDEcsFUVxKKX0tzlBeGzszvWq4oiFphORG+KI9TzDrpKRlZghl+dJ/sg8SZhY5kLC5BtWRX7yySe1nkgEXhGA5Z1adkQDANB6ZVn+TWq4OjLl2Ds27lXVDSmlX+TFxLqTkme/+uqr/51S+nlUY0ZVZpMLiOnxqkgbEgHolKIofpb2II6IZORI29aFlNKv8u/dxuOIiF+ajCO2Kmx47uuvvx67824tza5Fa4rzKaVvNXYmQF1iLuSqdqww+QaDwT9FQPHyyy/XGkxE26c8L/Kn5kUCADAhogXZP0bVYlNt1jaKCoCInaMiMz2Kz39dFMX/yItwu9n0G9/4qX6//2dlWb4QH4ik53vvvZdu3bo1duc6xYbEjz/+uDqHP2r0HwOA9tnzOCI2AUW1ZK4mTNHlIW+uulFnHPHOO+801lWhsl0cUWsyMj1KSB7JC5Znxg4CbRFzIc9rxwpTIaoif379+vXh/Jo6RcvXgwcPflkUxXdG2yoAAEDLRRLwTLRSrbt7yHYiKRnVANFdpDIYDB4WRXE7Lyje26Lt6Vxe/FzM1RHrIs6Pc2g6CVmJ9xS5GuR1lZEAdNS+xBGRlIzb/Pz8+scGg0Gsy93KccSDLZKTizmOmGtzHFF7MrKyNLs2l5OSC2MHgf0ScyGXV3oz9Q6VA/ZNzKeJtlCx6FHn7qYIfi5evJjy5oWzY3cAAID2irlLv4qZ6iMtR/fM4cOHh9UUcYsE5aFDh3b8T8f3HAuGcYvNgU22Uduoqmbo9/u/Kcvyn4/dAQC6YV/jiPh9XMUQkxRHRIeIq1evbhlHNJaMrCzNrp3KSckXxw4Ce+V+TkLa1QjTJTb+/F20Woh2DnWK4CG3mfrBFruuAACgzYZVDTGTKW77KRYVo/Xa9773vU0XFGPhcG1tbbi5cC8XDTeK65SrMcaqGQCgY1oXR1R/bjQpcUTjyciKeZKwL77ImwFWtWSFqdRI24hqJ1Oupt6bBvkAAFCvYzFz6fe///03o4tILNKxtajAiFlSeU7Vv9zyjgDQDeKIp1B1WPvqq6/+z4EDB/7NZp9ZjH2kISu9meVc3nqlFVcHpl881+biuScRCVMpesGfiYHadfevjyArG9vFBAAAE+JeWZbvRCXihQsX/My2EW1lq/nzZVn++db3BIDOEEfsUFRsVtfowIEDf7HVZ+1ZMjI9Skg+WOnNnM0t326O3QGoQzy3Xo7n2kpvZrPB+MB0iG4DVQVjbWIhIgZNR393yUgAACbcclQ1LCwsjG64Y4PLly9X7WMvGdEAAOvEETsQG5p2EkfsaTKystKbubPSm4m2bz/Ks+yAZxfPpdfjubXSm7nhesJ06/f7P4kTvH79eq3n+dprrw3/LMvyr8cOAgDAhIlKv8Fg8OVbb71VzURnRFQyxHynWGyN9UTXBgD+oOoY8MYbb4gjNvE0ccS+JCMrK72Zayu9mWM5Y/rF2B2AnfgiP4eiJasqJuiGU2VZvhCJyLp71keP98zrCQAA0+BOURR/Gefx/vvvD1uJ8UjE/rEZcTAYPCzL8o9dFgAYE5V+r0flX8QR0VGMR542jtjXZGTFPEnYNXMhoZuGLVrrnhW5uLiYjh49mvJrizbPAABMi9hodyUWEt955x0LiXkB8eLFi7GA+GVRFP82pWRNAQA2tx5HvPfee+KIXcYRz3399ddjH9xPS7Nri7mcc6FV3xi0S8yFXNaOFTopNu/86vPPPx+tYqxF7PCK1goxdzal5PUFAIBpE4uJZ+7evZvefvvt2ruMTIpqATF7XVcUANiR9Tji3Llz6eHDh528ahviiBjFeG3sTptoXTKysjS7djYnJV8cOwjdFS1Zz2vHCp02DHwuXbpUa2VktKv6+OOPY0fTr4ui+KOxOwAAwHQYxtNffvnlcP5TLCh2iUQkADwTccQu44hWtGndTE62zJknCeviuXBMIhI67chgMPhxBDw3btRbuBg93kNRFH81dhAAAKbH2arV2tWrV9Pp06c786NdXl5eb6mWKxmsLwDA01mPI6LDWN1dy9rsWeOI1lZGjlqaXTuWqyTPjB2E6fdRroY0vw2IgOfnH374Ybp8+XJtFyN63UeV5cGDB6PP+3fMiwEAoAOGsXWc5s2bN4fx9bS2bY0uKDEr8/jx4+nrr7/+p+eeey5GI90ZuyMAsFOPxRGRqJvWtq11xRETkYysmCdJx/RyEtLcNmBoMBj8QyQLT548WetCyUiLhSs5mAIAgC6Y6/f7/60sy+9H95GolIyNf9O0mBiVn9FGLio4Yr00pXTK5kMAqMXcYDD4ONbqIo6ISsmIJaZJnXHERCUjK+ZJMuXMhQQ2ExtyfnH79u1hEFCnCJRid1NK6bspJVXYAAB0zXK/33+7LMtvfv7558OEZHQOmeSk5EsvvZQuXLhQxfnhpyml1bE7AgDP4kis5aeUhrv8I46IpGSMV5qyOOJSzsnt2kQmI9OjhGT1Q47bt8buAJMpntSrK70ZuxSBjYYDst9+++1a50VGcPHee++lvLtpcewOAADQDY+NCIoKh0hIRmJyktq3Rnx/7ty5ND8/X33oZu5+YtMhADRHHPEEE5uMrOR5krGz69WxgzA5zIUEthMbcP4xdlfVPRh7pCry5ZSSttAAAHTdcDFxMBj8uCiKYU+yu3fvpuvXr6dPP/10+Pc2WlxcHLZSqxYPB4PBr4ui+E8ppWtd/4ECwB56LCmZxBHrJj4ZWcnzJCMpOTt2ENrrfuwsMBcSeILhUOzYTXX58uXt7/kUYrj2K6+8klRFAgDAmCN5LtL50bWm2CAYnUriduvWrbFP2kuxqTA2K8YC4tGjR4f/cr/f//uyLC/nzioAwP6YyDgiJyH/qok4YmqSkZU8T3JV61ZaLuZCLq/0ZsxrAHbiTgQuJ0+eHLZ2iJYJOxVBzsZ2EKO7nQaDwcOiKP6Vtk0AALClY3nz3qnRzlzRgi0WEuO2F9UOMzMzw/cC1a1aOMyu5IVDm50BoF22jSMiMfnLX/5y6uOIqUtGpsfnSV4cOwj7792ciDQXEtiJCFh+1cSVyrum/zwnOwEAgCerKh0WB4PBvyuK4nD1GbGoGAuJkZiMv8fC4sOHD596cTEWC2OBsPrzxIkTw+qFQ4cObbzrR7l9WtysMQBA+z1VHBGetoJyszgi/tyQfEx7HUdMZTKyYp4kLXMzt2RVfQQ8jbP5NurOUwQJcznQGXVvJNgAAAB2by5XOyz2+/0TZVm+sN1XikXGWGDcaItk47rBYPBlURS3csXCDRWQADAV1uOIwWDww9Hk5GaiA1rcNpqEOGKqk5EV8yTZZ+ZCAgAAQDccyQuL1abAY/lWWdjsKuTxCbdHPnRj5M97xioAQCfsKo7o9/u/Kcvys5EPtS6O6EQysrI0uxatW5fNk2SPmAsJAAAAAAB0WqeSkekP8yQjIfnm2EGoTwx8PW8uJAAAAAAA0GWdS0ZW8jzJD7Yqa4VdupmTkHdcQAAAAAAAoOs6m4ys5HmSkZR8cewg7Nz9nIS85poBAAAAAAA80vlkZMU8SXYp5kKurvRmll1AAAAAAACAx0lGjsjzJFdTSmfGDsI4cyEBAAAAAAC2IRm5iaXZtbmclDRPks3EXMjlld7MjU2OAQAAAAAAkElGbmNpdu1UTkqaJ0nKcyEjCfmBqwEAAAAAAPBkkpE7sDS7FvMAz5sn2Vlf5KT0qpasAAAAAAAAOycZuUPmSXbWR3ku5L2uXwgAAAAAAICnJRn5lJZm1xajVad5klOvl5OQ5kICAAAAAADskmTkLi3Nrp3NSUnzJKfLFzkJaS4kAAAAAADAM5KMfAa5det58ySnxiVzIQEAAAAAAOojGVmDpdm1Y3me5KsTfzLddDOldNZcSAAAAAAAgHpJRtYoz5OMpOTs1JzUdLufk5DmQgIAAAAAADRAMrIBeZ7kqtatrRVzIZdXejOrXb8QAAAAAAAATSpc3fqt9GY+SCkdyzMIaZd342cjEQkAAAAAANA8lZENy/MkIzm5MNUn2n4xF/L8Sm/mTtcvBAAAAAAAwF6RjNwjeZ5kJCVf7MQJt8f9nIS81vULAQAAAAAAsNckI/fY0uza+ZhXaJ5k42Iu5OpKb2Z5ys8TAAAAAACgtSQj98HS7NqRnJB8s3Mnvzeu5GrIB104WQAAAAAAgLaSjNxHS7Nrc1G9Z55kbWIu5PJKb+bGlJwPAAAAAADARJOMbIGl2bVTOSlpnuTu3M9JyA8m8ZsHAAAAAACYVpKRLbI0uxatW8+bJ/lULuXZkFqyAgAAAAAAtIxkZMvkeZJRJXmm69fiCT7KcyHvbX83AAAAAAAA9otkZEstza4tRutR8yTH9HIS0lxIAAAAAACAlpOMbLml2bWzOSnZ9XmSX+S5kKtjRwAAAAAAAGglycgJkFu3xizJix29BO/mRKS5kAAAAAAAABNEMnKCLM2uHcvzJF/tyCnfTCmdNRcSAAAAAABgMklGTqA8TzKSkrNTeor381zIa2NHAAAAAAAAmBiSkRNsaXbtfJ4n+a0pOaWYC7m60ptZHjsCAAAAAADAxJGMnHB5nmQk796c8FO5kqshzYUEAAAAAACYEpKRUyLPk/wgpbQwYWcUcyGXV3ozN8aOAAAAAAAAMNEkI6dMnicZSckXW35m93MS8oOxIwAAAAAAAEwFycgptTS7Fq1bz7d0nuSlPBtSS1YAAAAAAIApJhk5xfI8ydWU0pmWnOVHeS7kvbEjAAAAAAAATB3JyA5Yml2by0nJ/Zon2ctJSHMhAQAAAAAAOkQyskOWZtfOxpzGPZwn+UWeC7k6dgQAAAAAAICpJxnZMbl16/k9mCf5bk5EmgsJAAAAAADQUZKRHbU0u3Yst259teYrcDOldNZcSAAAAAAAACQjO25pdm0xJyVnn/FK3M9zIa+NHQEAAAAAAKCTJCMZyvMkV3fRujXmQq6u9GaWx44AAAAAAADQaUXXLwCPrPRmPkgpHcuzHnfqSkppTiISAAAAAACAzaiMZEyeJxkJxlNbVEpeydWQd8aOAAAAAAAAQCYZybaWZtfmUkpHqvus9GZubHd/AAAAAAAAGEop/X/v3BJVuTshzwAAAABJRU5ErkJggg==){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 1: Introduction to PyTorch\n", " :card_description: This tutorial will give a short introduction to PyTorch basics, and get you setup for writing your own neural networks. This notebook is part of a lecture series on Deep...\n", " :tags: GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/01-introduction-to-pytorch.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab,colab_type,id,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 60.734817, "end_time": "2021-12-04T15:54:21.407766", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/01-introduction-to-pytorch/Introduction_to_PyTorch.ipynb", "output_path": ".notebooks/course_UvA-DL/01-introduction-to-pytorch.ipynb", "parameters": {}, "start_time": "2021-12-04T15:53:20.672949", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"1258f7118a0c424184d1474fa7c7c996": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2a2ddc2695ca450cbb62218c40c78f0d", "placeholder": "\u200b", "style": "IPY_MODEL_c9b7c58a69a648deb8285855bed4a010", "value": "100%"}}, "2a2ddc2695ca450cbb62218c40c78f0d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3ae1c6ddf0434903b17b358d083da8db": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4d30509a31df4d158d2bb33eb9986e6a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "61c30cfd473447a38bffb83685bd5f10": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cd512299253f4dca9324b2260bf7d58c", "placeholder": "\u200b", "style": "IPY_MODEL_eaacc50a6b044949945e0b3f82453cca", "value": " 100/100 [00:01<00:00, 84.02it/s]"}}, "8222db0c1ce847c18b82712853539f5e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1258f7118a0c424184d1474fa7c7c996", "IPY_MODEL_fa1ac1649a95469d9415b8ad881b0c26", "IPY_MODEL_61c30cfd473447a38bffb83685bd5f10"], "layout": "IPY_MODEL_3ae1c6ddf0434903b17b358d083da8db"}}, "9c98efcaa23c420fa86f3b36f45e2f83": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c9b7c58a69a648deb8285855bed4a010": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "cd512299253f4dca9324b2260bf7d58c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "eaacc50a6b044949945e0b3f82453cca": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fa1ac1649a95469d9415b8ad881b0c26": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9c98efcaa23c420fa86f3b36f45e2f83", "max": 100.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_4d30509a31df4d158d2bb33eb9986e6a", "value": 100.0}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/02-activation-functions.ipynb b/source/notebooks/course_UvA-DL/02-activation-functions.ipynb deleted file mode 100644 index f71b969..0000000 --- a/source/notebooks/course_UvA-DL/02-activation-functions.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "ed3501af", "metadata": {"papermill": {"duration": 0.023462, "end_time": "2021-09-16T12:33:15.496875", "exception": false, "start_time": "2021-09-16T12:33:15.473413", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 2: Activation Functions\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-09-16T14:32:18.973374\n", "\n", "In this tutorial, we will take a closer look at (popular) activation functions and investigate their effect on optimization properties in neural networks.\n", "Activation functions are a crucial part of deep learning models as they add the non-linearity to neural networks.\n", "There is a great variety of activation functions in the literature, and some are more beneficial than others.\n", "The goal of this tutorial is to show the importance of choosing a good activation function (and how to do so), and what problems might occur if we don't.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHUAAAAUCAYAAACzrHJDAAAIuUlEQVRoQ+1ZaVRURxb+qhdolmbTUVSURpZgmLhHbQVFZIlGQBEXcMvJhKiTEzfigjQg7oNEJ9GMGidnjnNMBs2czIzajksEFRE1xklCTKJiQLRFsUGkoUWw+82pamn79etGYoKek1B/4NW99/tu3e/dquJBAGD27NkHALxKf39WY39gyrOi+i3xqGtUoePJrFmznrmgtModorbTu8YRNZk5cybXTvCtwh7o6NR2KzuZMWNGh6jtVt7nA0ymT5/eJlF9POrh7PAQl6s8bGYa3PUum//htmebVtLRqW0q01M5keTk5FZFzU0oRle3+zxwg5Hgtb+PZiL/ZVohxCI+hL5JgjmfjPxZ26+33BG3dA+ealHPM4gQAo5rU59gsI8bRvl54t3Ca62mvHyUAhtOlLd5WSQpKcluBjumnoCLs1EARkVd9E8l3p9y2i7RbQ1B6pFwu/YDgW8KbHJHMTQrwnjz2oZm9M4pavOCfo5jWrgCaaMVcMs6/pNhDr0+AMN93XlxV7R6DNpyzi7W/OE+yIrsjU6rTrbKV5cd/pNyItOmTbMp6sbBB+EqaYJY4cWE3VUciNt1TpgfcRFv71Fi54xT5kSoyLvOBEJMOMxWXkFlBeBSX4u6Zkcs+3KszYRtiapbNRqF31UgetVuc8z9vBXIv1qD+F1f83B6uDlCUyfsZGepGPpmg01OB7EITQbhS9ribKy+DmP1DUiClLz4bnIHVOqa7BY+Z1wg5g3zgUvyehiNpnJKxSLc/ts76LKm0BzX3c0RNy1yXjDcB5lWoro4iNHQxM+f1kWeWQARAWQS++trISJTp061Kep25X/MycwtjuctSC5rxo7ppi7VNUox5+PhPHtrsS2O1qJ6yx1QujQUzm9sh6hbkBlvvGcN8hYnwjUjH6kjfZEd5c/jitz5Jc5U3ENnFynKl4eB7nyEgP2UZ+Yz3/rVEbyYr27qELrtC4FIC0J7sc7xWnmccdHfRRTs0VB+cA4lt+oFcRR/wUeH8FG5w2Mbx8FQ8TXEvv1xYf4wBP3O2WyL3/UVjpXWgIqaFeUPr+wTmDvUB7njH6/bOv+HRg4SqioAg5GDe1aB3ZeMTJkyRSBqkLsWqSEm0fZVBEN94zEZnYvrdx1JL5cxe+a+AbhSJecRRHW/ikTFRTa38dtQlNZ5CRKwFvUtZU/kvBoEF9Uxni/XqIM+dwKbTw3rhcxIf7gmr2M+H6SMwx8iBzJbw5oxeG3Lv5FX9B3AGaHPS8e8z77H7v9VMpvPG5ug1enh7eGK8h0LBTwUb+GInqzInlRUK65DmTPQu4c3+uQKjwKK77zwUxBX4Tq7yR1RuiwUsqlrABCM6esHdXoy47fk4+prYKy8ZF574x4V5BnHQBuf4g9Z9ld8U36L2aktZNNplNfw7zotwWTy5MkCUft4aLEopJj5/OPHl1BQqeAVOnHgNSQOqmBzq9V9cfEm/yx5ubMGKS9cYPZ3vx2OS/c6PVHUuUO7Y1Pci3BO/1zgq18byebfGemLtNF+6JRtOvMk926ibussZqM+1mNz4TWkH7rCbM5phwGRGDAaoF8fY5OHFnlldAA8sgoEXKnDukA1NgSeNjqkJT9brbN4pC9WRweYXyLugR73c+MYvyWfu0yC6+mjzN1Isfw3FKJS98CU/zI1IHFkFPR52cHL2FJk0sB6kMTERIGo9GzcPkLNfA0cwdwi/hfEYO86ZMd9w+y1egfM2T2Eh/vesMNwljSzuZRT420SW3eqy8N6aHMmwmnFUZ7/PGVPbIoNZvNU1BURdHs0bT2+HjL8sDSM2e6vi4Lj5NW8WOLVA6RTT2azxLV+bglaFNqLieqemS/gWkw7NyoAHo+2dEsiivengjKsPFoqWOvbSh/kxPaxyW/JRzH2Fl3EzD9/xjAefJqB3usKUFn/0Gb+S/d/jy3FN2yLOmnSJJtn6oehByEiHPSeXnDxFGPRnoFoaBJjcdQlbDwcjL1zTNuQpoxD7R0OG0uUTMi0fkVwdzBdYIwcwZunxrVJVLplNm54BZp7jfDfYLoNyqQi1K6KxIdHzmN+QQ2WjFIwUT2zTGdlRXo4NFXVUO4sgX5dFC7f0aP/ZlNeUjFBuL8Xjl6uRuP6aMjSjpjzsH62FDU7JhBuGccEXIvDfJFFBc/gHw80dklfCVYnRaDfpiJcutPA4F7qJsfJeUPQI+1fqMlNhFx1FM0GDqkjFVg7NojlQ0Vt4aM5ReSqcbpaCg8nCW5lRsBvbT4T1TLfFptsfh7gItzuKTdJSEiwKSrt1vcmnEXXrsLbYnWDA1bu+z2WKy9Arq+1KRqdfKsoBo0GcdtEpS/B1bO4v0cFiUhkjskvKcMrWwtAPHuwQq8Z+4LZ1vTQANfXt4J0DwZX9gWa9qh4XDM/voC9JXfwYEMMHJcfNtusn82ihvliVUwg5KrPGVf6GH94ZJpEZBen6EC4qYTHA1dXhW0JIex8txzv//c8lhzXIi/BFxOH9jGbQhZsRalTIBZZ8KkGyZAxeRQvXkFF1TWz/Hm46jNYUnjPbt3JxIkT7f6dSj8qfJJyVvBxgaIlblOyjtysNHWN9fjjqWi7glJfW3/S0Hlj2XnA8PhKT9w6g3Qx3XiXhvuxQsuT1proxBKI/AaZqY1Xz5muvY8G8XkRRCaHsfQsRAFDH/tZPbcYuHotOG0FRIqB4HR3wNVoIPLtz8ycTguu+jpEigE218vd1YCr5m+HpHMvEI9u4LTXwNWaLjl0iPwGAmIpeHx1VeCqTJdPs1/vweweQPO3HC24NhOhnTphwoQnfv6QSY2ICbkNmdSA4h87oaLaiYfn5diIEd4att2erOwJXbPUHp953p6orQVSUVWRAXBT8c/dJ5L9xhzaJGp71GR/wFP8P5V2z10NSC9T93QM2xUg8fHxT+zU9ijeU4naHon8CjFJXFzc8/kn+dN06q9QgF98SYSo2Xen2NjYZy5sR6f+4nLSK5Iam2PH/x87a1YN/t5sBgAAAABJRU5ErkJggg==){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/02-activation-functions.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "66b84901", "metadata": {"papermill": {"duration": 0.021804, "end_time": "2021-09-16T12:33:15.540678", "exception": false, "start_time": "2021-09-16T12:33:15.518874", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "4d7c8160", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-09-16T12:33:15.587749Z", "iopub.status.busy": "2021-09-16T12:33:15.587280Z", "iopub.status.idle": "2021-09-16T12:33:15.589355Z", "shell.execute_reply": "2021-09-16T12:33:15.589747Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.027383, "end_time": "2021-09-16T12:33:15.589924", "exception": false, "start_time": "2021-09-16T12:33:15.562541", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"torchmetrics>=0.3\" \"torch>=1.6, <1.9\" \"pytorch-lightning>=1.3\" \"torchvision\" \"seaborn\" \"matplotlib\""]}, {"cell_type": "markdown", "id": "94e4c637", "metadata": {"papermill": {"duration": 0.021895, "end_time": "2021-09-16T12:33:15.635408", "exception": false, "start_time": "2021-09-16T12:33:15.613513", "status": "completed"}, "tags": []}, "source": ["
\n", "Before we start, we import our standard libraries and set up basic functions:"]}, {"cell_type": "code", "execution_count": 2, "id": "afda8dd1", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:15.687272Z", "iopub.status.busy": "2021-09-16T12:33:15.686802Z", "iopub.status.idle": "2021-09-16T12:33:16.799385Z", "shell.execute_reply": "2021-09-16T12:33:16.799794Z"}, "papermill": {"duration": 1.142099, "end_time": "2021-09-16T12:33:16.799933", "exception": false, "start_time": "2021-09-16T12:33:15.657834", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_749/3776275675.py:24: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n"]}], "source": ["import json\n", "import math\n", "import os\n", "import urllib.request\n", "import warnings\n", "from urllib.error import HTTPError\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import seaborn as sns\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision\n", "\n", "# %matplotlib inline\n", "from IPython.display import set_matplotlib_formats\n", "from torchvision import transforms\n", "from torchvision.datasets import FashionMNIST\n", "from tqdm.notebook import tqdm\n", "\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "sns.set()"]}, {"cell_type": "markdown", "id": "2587fcef", "metadata": {"papermill": {"duration": 0.022118, "end_time": "2021-09-16T12:33:16.844975", "exception": false, "start_time": "2021-09-16T12:33:16.822857", "status": "completed"}, "tags": []}, "source": ["We will define a function to set a seed on all libraries we might interact with in this tutorial (here numpy and torch).\n", "This allows us to make our training reproducible.\n", "However, note that in contrast to the CPU, the same seed on different GPU architectures can give different results.\n", "All models here have been trained on an NVIDIA GTX1080Ti.\n", "\n", "Additionally, the following cell defines two paths: `DATASET_PATH` and `CHECKPOINT_PATH`.\n", "The dataset path is the directory where we will download datasets used in the notebooks.\n", "It is recommended to store all datasets from PyTorch in one joined directory to prevent duplicate downloads.\n", "The checkpoint path is the directory where we will store trained model weights and additional files.\n", "The needed files will be automatically downloaded.\n", "In case you are on Google Colab, it is recommended to change the\n", "directories to start from the current directory (i.e. remove `../` for\n", "both dataset and checkpoint path)."]}, {"cell_type": "code", "execution_count": 3, "id": "5ad210ad", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:16.894843Z", "iopub.status.busy": "2021-09-16T12:33:16.894366Z", "iopub.status.idle": "2021-09-16T12:33:16.964040Z", "shell.execute_reply": "2021-09-16T12:33:16.963624Z"}, "papermill": {"duration": 0.096979, "end_time": "2021-09-16T12:33:16.964150", "exception": false, "start_time": "2021-09-16T12:33:16.867171", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Using device cuda:0\n"]}], "source": ["# Path to the folder where the datasets are/should be downloaded (e.g. MNIST)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data/\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/Activation_Functions/\")\n", "\n", "\n", "# Function for setting the seed\n", "def set_seed(seed):\n", " np.random.seed(seed)\n", " torch.manual_seed(seed)\n", " if torch.cuda.is_available(): # GPU operation have separate seed\n", " torch.cuda.manual_seed(seed)\n", " torch.cuda.manual_seed_all(seed)\n", "\n", "\n", "set_seed(42)\n", "\n", "# Additionally, some operations on a GPU are implemented stochastic for efficiency\n", "# We want to ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "# Fetching the device that will be used throughout this notebook\n", "device = torch.device(\"cpu\") if not torch.cuda.is_available() else torch.device(\"cuda:0\")\n", "print(\"Using device\", device)"]}, {"cell_type": "markdown", "id": "5aa7e547", "metadata": {"papermill": {"duration": 0.022623, "end_time": "2021-09-16T12:33:17.010058", "exception": false, "start_time": "2021-09-16T12:33:16.987435", "status": "completed"}, "tags": []}, "source": ["The following cell downloads all pretrained models we will use in this notebook.\n", "The files are stored on a separate [repository](https://github.com/phlippe/saved_models) to reduce the size of the notebook repository, especially for building the documentation on ReadTheDocs.\n", "In case the download below fails, you can download the models from a [Google Drive folder](https://drive.google.com/drive/folders/1sFpZUpDJVjiYEvIqISqfkFizfsTnPf4s?usp=sharing).\n", "Please let me (Phillip) know if an error occurs so it can be fixed for all students."]}, {"cell_type": "code", "execution_count": 4, "id": "58e26273", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:17.060096Z", "iopub.status.busy": "2021-09-16T12:33:17.059620Z", "iopub.status.idle": "2021-09-16T12:33:18.470969Z", "shell.execute_reply": "2021-09-16T12:33:18.470514Z"}, "papermill": {"duration": 1.438459, "end_time": "2021-09-16T12:33:18.471082", "exception": false, "start_time": "2021-09-16T12:33:17.032623", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_elu.config...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_elu.tar...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_leakyrelu.config...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_leakyrelu.tar...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_relu.config...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_relu.tar...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_sigmoid.config...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_sigmoid.tar...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_swish.config...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_swish.tar...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_tanh.config...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/FashionMNIST_tanh.tar...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial3/\"\n", "# Files to download\n", "pretrained_files = [\n", " \"FashionMNIST_elu.config\",\n", " \"FashionMNIST_elu.tar\",\n", " \"FashionMNIST_leakyrelu.config\",\n", " \"FashionMNIST_leakyrelu.tar\",\n", " \"FashionMNIST_relu.config\",\n", " \"FashionMNIST_relu.tar\",\n", " \"FashionMNIST_sigmoid.config\",\n", " \"FashionMNIST_sigmoid.tar\",\n", " \"FashionMNIST_swish.config\",\n", " \"FashionMNIST_swish.tar\",\n", " \"FashionMNIST_tanh.config\",\n", " \"FashionMNIST_tanh.tar\",\n", "]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(f\"Downloading {file_url}...\")\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "acbb3e50", "metadata": {"papermill": {"duration": 0.023807, "end_time": "2021-09-16T12:33:18.519257", "exception": false, "start_time": "2021-09-16T12:33:18.495450", "status": "completed"}, "tags": []}, "source": ["## Common activation functions"]}, {"cell_type": "markdown", "id": "aa4d6503", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.023651, "end_time": "2021-09-16T12:33:18.566824", "exception": false, "start_time": "2021-09-16T12:33:18.543173", "status": "completed"}, "tags": []}, "source": ["As a first step, we will implement some common activation functions by ourselves.\n", "Of course, most of them can also be found in the `torch.nn` package (see the [documentation](https://pytorch.org/docs/stable/nn.html#non-linear-activations-weighted-sum-nonlinearity) for an overview).\n", "However, we'll write our own functions here for a better understanding and insights.\n", "\n", "For an easier time of comparing various activation functions, we start\n", "with defining a base class from which all our future modules will\n", "inherit:"]}, {"cell_type": "code", "execution_count": 5, "id": "3dcfd06e", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:18.618629Z", "iopub.status.busy": "2021-09-16T12:33:18.618166Z", "iopub.status.idle": "2021-09-16T12:33:18.620230Z", "shell.execute_reply": "2021-09-16T12:33:18.619833Z"}, "papermill": {"duration": 0.029814, "end_time": "2021-09-16T12:33:18.620328", "exception": false, "start_time": "2021-09-16T12:33:18.590514", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ActivationFunction(nn.Module):\n", " def __init__(self):\n", " super().__init__()\n", " self.name = self.__class__.__name__\n", " self.config = {\"name\": self.name}"]}, {"cell_type": "markdown", "id": "d528e8e9", "metadata": {"papermill": {"duration": 0.023943, "end_time": "2021-09-16T12:33:18.668417", "exception": false, "start_time": "2021-09-16T12:33:18.644474", "status": "completed"}, "tags": []}, "source": ["Every activation function will be an `nn.Module` so that we can integrate them nicely in a network.\n", "We will use the `config` dictionary to store adjustable parameters for some activation functions.\n", "\n", "Next, we implement two of the \"oldest\" activation functions that are still commonly used for various tasks: sigmoid and tanh.\n", "Both the sigmoid and tanh activation can be also found as PyTorch functions (`torch.sigmoid`, `torch.tanh`) or as modules (`nn.Sigmoid`, `nn.Tanh`).\n", "Here, we implement them by hand:"]}, {"cell_type": "code", "execution_count": 6, "id": "e86a8cd3", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:18.720307Z", "iopub.status.busy": "2021-09-16T12:33:18.719843Z", "iopub.status.idle": "2021-09-16T12:33:18.721916Z", "shell.execute_reply": "2021-09-16T12:33:18.721501Z"}, "papermill": {"duration": 0.029566, "end_time": "2021-09-16T12:33:18.722011", "exception": false, "start_time": "2021-09-16T12:33:18.692445", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Sigmoid(ActivationFunction):\n", " def forward(self, x):\n", " return 1 / (1 + torch.exp(-x))\n", "\n", "\n", "class Tanh(ActivationFunction):\n", " def forward(self, x):\n", " x_exp, neg_x_exp = torch.exp(x), torch.exp(-x)\n", " return (x_exp - neg_x_exp) / (x_exp + neg_x_exp)"]}, {"cell_type": "markdown", "id": "b2beae98", "metadata": {"papermill": {"duration": 0.023891, "end_time": "2021-09-16T12:33:18.770175", "exception": false, "start_time": "2021-09-16T12:33:18.746284", "status": "completed"}, "tags": []}, "source": ["Another popular activation function that has allowed the training of deeper networks, is the Rectified Linear Unit (ReLU).\n", "Despite its simplicity of being a piecewise linear function, ReLU has one major benefit compared to sigmoid and tanh: a strong, stable gradient for a large range of values.\n", "Based on this idea, a lot of variations of ReLU have been proposed, of which we will implement the following three: LeakyReLU, ELU, and Swish.\n", "LeakyReLU replaces the zero settings in the negative part with a smaller slope to allow gradients to flow also in this part of the input.\n", "Similarly, ELU replaces the negative part with an exponential decay.\n", "The third, most recently proposed activation function is Swish, which is actually the result of a large experiment with the purpose of finding the \"optimal\" activation function.\n", "Compared to the other activation functions, Swish is both smooth and non-monotonic (i.e. contains a change of sign in the gradient).\n", "This has been shown to prevent dead neurons as in standard ReLU activation, especially for deep networks.\n", "If interested, a more detailed discussion of the benefits of Swish can be found in [this paper](https://arxiv.org/abs/1710.05941) [1].\n", "\n", "Let's implement the four activation functions below:"]}, {"cell_type": "code", "execution_count": 7, "id": "68254625", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:18.823746Z", "iopub.status.busy": "2021-09-16T12:33:18.823281Z", "iopub.status.idle": "2021-09-16T12:33:18.825314Z", "shell.execute_reply": "2021-09-16T12:33:18.824855Z"}, "papermill": {"duration": 0.030969, "end_time": "2021-09-16T12:33:18.825411", "exception": false, "start_time": "2021-09-16T12:33:18.794442", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ReLU(ActivationFunction):\n", " def forward(self, x):\n", " return x * (x > 0).float()\n", "\n", "\n", "class LeakyReLU(ActivationFunction):\n", " def __init__(self, alpha=0.1):\n", " super().__init__()\n", " self.config[\"alpha\"] = alpha\n", "\n", " def forward(self, x):\n", " return torch.where(x > 0, x, self.config[\"alpha\"] * x)\n", "\n", "\n", "class ELU(ActivationFunction):\n", " def forward(self, x):\n", " return torch.where(x > 0, x, torch.exp(x) - 1)\n", "\n", "\n", "class Swish(ActivationFunction):\n", " def forward(self, x):\n", " return x * torch.sigmoid(x)"]}, {"cell_type": "markdown", "id": "587dfd90", "metadata": {"papermill": {"duration": 0.023958, "end_time": "2021-09-16T12:33:18.873222", "exception": false, "start_time": "2021-09-16T12:33:18.849264", "status": "completed"}, "tags": []}, "source": ["For later usage, we summarize all our activation functions in a dictionary mapping the name to the class object.\n", "In case you implement a new activation function by yourself, add it here to include it in future comparisons as well:"]}, {"cell_type": "code", "execution_count": 8, "id": "61e3706d", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:18.924380Z", "iopub.status.busy": "2021-09-16T12:33:18.923908Z", "iopub.status.idle": "2021-09-16T12:33:18.925974Z", "shell.execute_reply": "2021-09-16T12:33:18.925558Z"}, "papermill": {"duration": 0.02888, "end_time": "2021-09-16T12:33:18.926069", "exception": false, "start_time": "2021-09-16T12:33:18.897189", "status": "completed"}, "tags": []}, "outputs": [], "source": ["act_fn_by_name = {\"sigmoid\": Sigmoid, \"tanh\": Tanh, \"relu\": ReLU, \"leakyrelu\": LeakyReLU, \"elu\": ELU, \"swish\": Swish}"]}, {"cell_type": "markdown", "id": "21624a4a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.024088, "end_time": "2021-09-16T12:33:18.974301", "exception": false, "start_time": "2021-09-16T12:33:18.950213", "status": "completed"}, "tags": []}, "source": ["### Visualizing activation functions\n", "\n", "To get an idea of what each activation function actually does, we will visualize them in the following.\n", "Next to the actual activation value, the gradient of the function is an important aspect as it is crucial for optimizing the neural network.\n", "PyTorch allows us to compute the gradients simply by calling the `backward` function:"]}, {"cell_type": "code", "execution_count": 9, "id": "f0c123df", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:19.026746Z", "iopub.status.busy": "2021-09-16T12:33:19.026268Z", "iopub.status.idle": "2021-09-16T12:33:19.027904Z", "shell.execute_reply": "2021-09-16T12:33:19.028280Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.029948, "end_time": "2021-09-16T12:33:19.028393", "exception": false, "start_time": "2021-09-16T12:33:18.998445", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def get_grads(act_fn, x):\n", " \"\"\"Computes the gradients of an activation function at specified positions.\n", "\n", " Args:\n", " act_fn: An object of the class \"ActivationFunction\" with an implemented forward pass.\n", " x: 1D input tensor.\n", " Returns:\n", " A tensor with the same size of x containing the gradients of act_fn at x.\n", " \"\"\"\n", " x = x.clone().requires_grad_() # Mark the input as tensor for which we want to store gradients\n", " out = act_fn(x)\n", " out.sum().backward() # Summing results in an equal gradient flow to each element in x\n", " return x.grad # Accessing the gradients of x by \"x.grad\""]}, {"cell_type": "markdown", "id": "76f9c1ee", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.024154, "end_time": "2021-09-16T12:33:19.077839", "exception": false, "start_time": "2021-09-16T12:33:19.053685", "status": "completed"}, "tags": []}, "source": ["Now we can visualize all our activation functions including their gradients:"]}, {"cell_type": "code", "execution_count": 10, "id": "a03e014f", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:19.132841Z", "iopub.status.busy": "2021-09-16T12:33:19.132367Z", "iopub.status.idle": "2021-09-16T12:33:20.711026Z", "shell.execute_reply": "2021-09-16T12:33:20.711412Z"}, "papermill": {"duration": 1.60939, "end_time": "2021-09-16T12:33:20.711553", "exception": false, "start_time": "2021-09-16T12:33:19.102163", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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SXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTMgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0EgMTcgMCBSIC9FIDE4IDAgUiAvRiAxOSAwIFIgL0cgMjAgMCBSIC9MIDIxIDAgUiAvUiAyMiAwIFIgL1MgMjMgMCBSCi9UIDI0IDAgUiAvVSAyNSAwIFIgL2EgMjYgMCBSIC9jIDI3IDAgUiAvZCAyOCAwIFIgL2UgMjkgMCBSIC9maXZlIDMwIDAgUgovZm91ciAzMSAwIFIgL2cgMzIgMCBSIC9oIDMzIDAgUiAvaSAzNCAwIFIgL2sgMzUgMCBSIC9tIDM2IDAgUiAvbiAzOCAwIFIKL28gMzkgMCBSIC9vbmUgNDAgMCBSIC9yIDQxIDAgUiAvcyA0MiAwIFIgL3QgNDMgMCBSIC90aHJlZSA0NCAwIFIKL3R3byA0NSAwIFIgL3cgNDYgMCBSIC95IDQ3IDAgUiAvemVybyA0OCAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDAuOCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjggPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzcgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0OSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQzMzIwKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDUwCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDE3MDQ4IDAwMDAwIG4gCjAwMDAwMTY3ODMgMDAwMDAgbiAKMDAwMDAxNjgxNSAwMDAwMCBuIAowMDAwMDE2OTU3IDAwMDAwIG4gCjAwMDAwMTY5NzggMDAwMDAgbiAKMDAwMDAxNjk5OSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDEgMDAwMDAgbiAKMDAwMDAwNjUzMCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDY1MDkgMDAwMDAgbiAKMDAwMDAxNTM4MSAwMDAwMCBuIAowMDAwMDE1MTgxIDAwMDAwIG4gCjAwMDAwMTQ3MzMgMDAwMDAgbiAKMDAwMDAxNjQzNCAwMDAwMCBuIAowMDAwMDA2NTUwIDAwMDAwIG4gCjAwMDAwMDY3MTMgMDAwMDAgbiAKMDAwMDAwNjg2NiAwMDAwMCBuIAowMDAwMDA3MDE0IDAwMDAwIG4gCjAwMDAwMDczMzQgMDAwMDAgbiAKMDAwMDAwNzQ2NyAwMDAwMCBuIAowMDAwMDA3NzcyIDAwMDAwIG4gCjAwMDAwMDgxODYgMDAwMDAgbiAKMDAwMDAwODMyNCAwMDAwMCBuIAowMDAwMDA4NTUzIDAwMDAwIG4gCjAwMDAwMDg5MzMgMDAwMDAgbiAKMDAwMDAwOTIzOCAwMDAwMCBuIAowMDAwMDA5NTQyIDAwMDAwIG4gCjAwMDAwMDk4NjQgMDAwMDAgbiAKMDAwMDAxMDE4NiAwMDAwMCBuIAowMDAwMDEwMzUyIDAwMDAwIG4gCjAwMDAwMTA3NjYgMDAwMDAgbiAKMDAwMDAxMTAwMyAwMDAwMCBuIAowMDAwMDExMTQ3IDAwMDAwIG4gCjAwMDAwMTEzMDIgMDAwMDAgbiAKMDAwMDAxMTYzMyAwMDAwMCBuIAowMDAwMDExODA1IDAwMDAwIG4gCjAwMDAwMTIwNDEgMDAwMDAgbiAKMDAwMDAxMjMzMiAwMDAwMCBuIAowMDAwMDEyNDg3IDAwMDAwIG4gCjAwMDAwMTI3MjAgMDAwMDAgbiAKMDAwMDAxMzEyNyAwMDAwMCBuIAowMDAwMDEzMzMzIDAwMDAwIG4gCjAwMDAwMTM3NDYgMDAwMDAgbiAKMDAwMDAxNDA3MCAwMDAwMCBuIAowMDAwMDE0MjMxIDAwMDAwIG4gCjAwMDAwMTQ0NDUgMDAwMDAgbiAKMDAwMDAxNzEwOCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQ5IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA1MCA+PgpzdGFydHhyZWYKMTcyNjUKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:33:19.790904\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["def vis_act_fn(act_fn, ax, x):\n", " # Run activation function\n", " y = act_fn(x)\n", " y_grads = get_grads(act_fn, x)\n", " # Push x, y and gradients back to cpu for plotting\n", " x, y, y_grads = x.cpu().numpy(), y.cpu().numpy(), y_grads.cpu().numpy()\n", " # Plotting\n", " ax.plot(x, y, linewidth=2, label=\"ActFn\")\n", " ax.plot(x, y_grads, linewidth=2, label=\"Gradient\")\n", " ax.set_title(act_fn.name)\n", " ax.legend()\n", " ax.set_ylim(-1.5, x.max())\n", "\n", "\n", "# Add activation functions if wanted\n", "act_fns = [act_fn() for act_fn in act_fn_by_name.values()]\n", "x = torch.linspace(-5, 5, 1000) # Range on which we want to visualize the activation functions\n", "# Plotting\n", "cols = 2\n", "rows = math.ceil(len(act_fns) / float(cols))\n", "fig, ax = plt.subplots(rows, cols, figsize=(cols * 4, rows * 4))\n", "for i, act_fn in enumerate(act_fns):\n", " vis_act_fn(act_fn, ax[divmod(i, cols)], x)\n", "fig.subplots_adjust(hspace=0.3)\n", "plt.show()"]}, {"cell_type": "markdown", "id": "350e27aa", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.027663, "end_time": "2021-09-16T12:33:20.771273", "exception": false, "start_time": "2021-09-16T12:33:20.743610", "status": "completed"}, "tags": []}, "source": ["## Analysing the effect of activation functions\n", "
"]}, {"cell_type": "markdown", "id": "683c2c7d", "metadata": {"papermill": {"duration": 0.027646, "end_time": "2021-09-16T12:33:20.826418", "exception": false, "start_time": "2021-09-16T12:33:20.798772", "status": "completed"}, "tags": []}, "source": ["After implementing and visualizing the activation functions, we are aiming to gain insights into their effect.\n", "We do this by using a simple neural network trained on\n", "[FashionMNIST](https://github.com/zalandoresearch/fashion-mnist) and\n", "examine various aspects of the model, including the performance and\n", "gradient flow."]}, {"cell_type": "markdown", "id": "84e11974", "metadata": {"papermill": {"duration": 0.027431, "end_time": "2021-09-16T12:33:20.881415", "exception": false, "start_time": "2021-09-16T12:33:20.853984", "status": "completed"}, "tags": []}, "source": ["### Setup"]}, {"cell_type": "markdown", "id": "cf662ea5", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.027344, "end_time": "2021-09-16T12:33:20.936216", "exception": false, "start_time": "2021-09-16T12:33:20.908872", "status": "completed"}, "tags": []}, "source": ["Firstly, let's set up a neural network.\n", "The chosen network views the images as 1D tensors and pushes them through a sequence of linear layers and a specified activation function.\n", "Feel free to experiment with other network architectures."]}, {"cell_type": "code", "execution_count": 11, "id": "517f9479", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:20.997665Z", "iopub.status.busy": "2021-09-16T12:33:20.997195Z", "iopub.status.idle": "2021-09-16T12:33:20.999429Z", "shell.execute_reply": "2021-09-16T12:33:20.998822Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.035645, "end_time": "2021-09-16T12:33:20.999530", "exception": false, "start_time": "2021-09-16T12:33:20.963885", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class BaseNetwork(nn.Module):\n", " def __init__(self, act_fn, input_size=784, num_classes=10, hidden_sizes=[512, 256, 256, 128]):\n", " \"\"\"\n", " Args:\n", " act_fn: Object of the activation function that should be used as non-linearity in the network.\n", " input_size: Size of the input images in pixels\n", " num_classes: Number of classes we want to predict\n", " hidden_sizes: A list of integers specifying the hidden layer sizes in the NN\n", " \"\"\"\n", " super().__init__()\n", "\n", " # Create the network based on the specified hidden sizes\n", " layers = []\n", " layer_sizes = [input_size] + hidden_sizes\n", " layer_size_last = layer_sizes[0]\n", " for layer_size in layer_sizes[1:]:\n", " layers += [nn.Linear(layer_size_last, layer_size), act_fn]\n", " layer_size_last = layer_size\n", " layers += [nn.Linear(layer_sizes[-1], num_classes)]\n", " # nn.Sequential summarizes a list of modules into a single module, applying them in sequence\n", " self.layers = nn.Sequential(*layers)\n", "\n", " # We store all hyperparameters in a dictionary for saving and loading of the model\n", " self.config = {\n", " \"act_fn\": act_fn.config,\n", " \"input_size\": input_size,\n", " \"num_classes\": num_classes,\n", " \"hidden_sizes\": hidden_sizes,\n", " }\n", "\n", " def forward(self, x):\n", " x = x.view(x.size(0), -1) # Reshape images to a flat vector\n", " out = self.layers(x)\n", " return out"]}, {"cell_type": "markdown", "id": "6f191c6f", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.027564, "end_time": "2021-09-16T12:33:21.054674", "exception": false, "start_time": "2021-09-16T12:33:21.027110", "status": "completed"}, "tags": []}, "source": ["We also add functions for loading and saving the model.\n", "The hyperparameters are stored in a configuration file (simple json file):"]}, {"cell_type": "code", "execution_count": 12, "id": "36782eee", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:21.117432Z", "iopub.status.busy": "2021-09-16T12:33:21.116957Z", "iopub.status.idle": "2021-09-16T12:33:21.119088Z", "shell.execute_reply": "2021-09-16T12:33:21.118695Z"}, "papermill": {"duration": 0.036557, "end_time": "2021-09-16T12:33:21.119184", "exception": false, "start_time": "2021-09-16T12:33:21.082627", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def _get_config_file(model_path, model_name):\n", " # Name of the file for storing hyperparameter details\n", " return os.path.join(model_path, model_name + \".config\")\n", "\n", "\n", "def _get_model_file(model_path, model_name):\n", " # Name of the file for storing network parameters\n", " return os.path.join(model_path, model_name + \".tar\")\n", "\n", "\n", "def load_model(model_path, model_name, net=None):\n", " \"\"\"Loads a saved model from disk.\n", "\n", " Args:\n", " model_path: Path of the checkpoint directory\n", " model_name: Name of the model (str)\n", " net: (Optional) If given, the state dict is loaded into this model. Otherwise, a new model is created.\n", " \"\"\"\n", " config_file, model_file = _get_config_file(model_path, model_name), _get_model_file(model_path, model_name)\n", " assert os.path.isfile(\n", " config_file\n", " ), f'Could not find the config file \"{config_file}\". Are you sure this is the correct path and you have your model config stored here?'\n", " assert os.path.isfile(\n", " model_file\n", " ), f'Could not find the model file \"{model_file}\". Are you sure this is the correct path and you have your model stored here?'\n", " with open(config_file) as f:\n", " config_dict = json.load(f)\n", " if net is None:\n", " act_fn_name = config_dict[\"act_fn\"].pop(\"name\").lower()\n", " act_fn = act_fn_by_name[act_fn_name](**config_dict.pop(\"act_fn\"))\n", " net = BaseNetwork(act_fn=act_fn, **config_dict)\n", " net.load_state_dict(torch.load(model_file, map_location=device))\n", " return net\n", "\n", "\n", "def save_model(model, model_path, model_name):\n", " \"\"\"Given a model, we save the state_dict and hyperparameters.\n", "\n", " Args:\n", " model: Network object to save parameters from\n", " model_path: Path of the checkpoint directory\n", " model_name: Name of the model (str)\n", " \"\"\"\n", " config_dict = model.config\n", " os.makedirs(model_path, exist_ok=True)\n", " config_file, model_file = _get_config_file(model_path, model_name), _get_model_file(model_path, model_name)\n", " with open(config_file, \"w\") as f:\n", " json.dump(config_dict, f)\n", " torch.save(model.state_dict(), model_file)"]}, {"cell_type": "markdown", "id": "b2ca0082", "metadata": {"papermill": {"duration": 0.027629, "end_time": "2021-09-16T12:33:21.174646", "exception": false, "start_time": "2021-09-16T12:33:21.147017", "status": "completed"}, "tags": []}, "source": ["We also set up the dataset we want to train it on, namely [FashionMNIST](https://github.com/zalandoresearch/fashion-mnist).\n", "FashionMNIST is a more complex version of MNIST and contains black-and-white images of clothes instead of digits.\n", "The 10 classes include trousers, coats, shoes, bags and more.\n", "To load this dataset, we will make use of yet another PyTorch package, namely `torchvision` ([documentation](https://pytorch.org/vision/stable/index.html)).\n", "The `torchvision` package consists of popular datasets, model architectures, and common image transformations for computer vision.\n", "We will use the package for many of the notebooks in this course to simplify our dataset handling.\n", "\n", "Let's load the dataset below, and visualize a few images to get an impression of the data."]}, {"cell_type": "code", "execution_count": 13, "id": "d28b09ac", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:21.234912Z", "iopub.status.busy": "2021-09-16T12:33:21.234444Z", "iopub.status.idle": "2021-09-16T12:33:25.742469Z", "shell.execute_reply": "2021-09-16T12:33:25.741983Z"}, "papermill": {"duration": 4.539991, "end_time": "2021-09-16T12:33:25.742582", "exception": false, "start_time": "2021-09-16T12:33:21.202591", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to /__w/2/s/.datasets/FashionMNIST/raw/train-images-idx3-ubyte.gz\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "783d0fee8e0b4746ad687e2606ab92b1", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/26421880 [00:00 first make them a tensor, then normalize them in the range -1 to 1\n", "transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n", "\n", "# Loading the training dataset. We need to split it into a training and validation part\n", "train_dataset = FashionMNIST(root=DATASET_PATH, train=True, transform=transform, download=True)\n", "train_set, val_set = torch.utils.data.random_split(train_dataset, [50000, 10000])\n", "\n", "# Loading the test set\n", "test_set = FashionMNIST(root=DATASET_PATH, train=False, transform=transform, download=True)"]}, {"cell_type": "markdown", "id": "22d8c0df", "metadata": {"papermill": {"duration": 0.031277, "end_time": "2021-09-16T12:33:25.805768", "exception": false, "start_time": "2021-09-16T12:33:25.774491", "status": "completed"}, "tags": []}, "source": ["We define a set of data loaders that we can use for various purposes later.\n", "Note that for actually training a model, we will use different data loaders\n", "with a lower batch size."]}, {"cell_type": "code", "execution_count": 14, "id": "079a68d3", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:25.874180Z", "iopub.status.busy": "2021-09-16T12:33:25.873691Z", "iopub.status.idle": "2021-09-16T12:33:25.875574Z", "shell.execute_reply": "2021-09-16T12:33:25.875949Z"}, "papermill": {"duration": 0.036986, "end_time": "2021-09-16T12:33:25.876059", "exception": false, "start_time": "2021-09-16T12:33:25.839073", "status": "completed"}, "tags": []}, "outputs": [], "source": ["train_loader = data.DataLoader(train_set, batch_size=1024, shuffle=True, drop_last=False)\n", "val_loader = data.DataLoader(val_set, batch_size=1024, shuffle=False, drop_last=False)\n", "test_loader = data.DataLoader(test_set, batch_size=1024, shuffle=False, drop_last=False)"]}, {"cell_type": "code", "execution_count": 15, "id": "09ec856b", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:25.943298Z", "iopub.status.busy": "2021-09-16T12:33:25.942829Z", "iopub.status.idle": "2021-09-16T12:33:26.149461Z", "shell.execute_reply": "2021-09-16T12:33:26.148976Z"}, "papermill": {"duration": 0.241991, "end_time": "2021-09-16T12:33:26.149573", "exception": false, "start_time": "2021-09-16T12:33:25.907582", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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YW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvRiAxOCAwIFIgL0kgMTkgMCBSIC9NIDIwIDAgUiAvTiAyMSAwIFIgL1MgMjIgMCBSIC9UIDIzIDAgUiAvYSAyNCAwIFIKL2UgMjUgMCBSIC9oIDI2IDAgUiAvaSAyNyAwIFIgL2wgMjggMCBSIC9tIDI5IDAgUiAvbiAzMCAwIFIgL28gMzEgMCBSCi9wIDMyIDAgUiAvcyAzMyAwIFIgL3NwYWNlIDM0IDAgUiAveCAzNSAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE2IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSID4+CmVuZG9iagoxMyAwIG9iago8PCAvQml0c1BlckNvbXBvbmVudCA4Ci9Db2xvclNwYWNlIFsvSW5kZXhlZCAvRGV2aWNlUkdCIDI1NCAo/////v7+/f39/Pz8+/v7+vr6+fn5+Pj49/f39vb29fX19PT08/Pz8vLy8fHx8PDw7+/v7u7u7e3t7Ozs6+vr6urq6enp6Ojo5+fn5ubm5eXl5OTk4+Pj4uLi4eHh4ODg39/f3t7e3d3d3Nzc29vb2tra2dnZ2NjY19fX1tbW1dXV1NTU09PT0tLS0dHR0NDQz8/Pzs7Ozc3NzMzMy8vLysrKycnJyMjI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vv1oBVn5KsTUkCFDiFz1QlPjUG2E+gezcUaMGMFMmKKiIoZqnHPOOaw+sN2ZVbGcmTMrLzP2VX5AevDBB7ek9rQXqD/96U+gi5a7n0lyWcRnBinVl3UMQ3q7BoHuOSnjo3YxZk899RQtetdffz3Vg6uuugq/zzK35ugGDhzIyOI1a9YUYsqZVVTOzJnlN+XMKqrqY+b67ihW3QlwRVasuhPgiqxYdSfAFVmx6k6AK7Ji1Z0AV2TFqjsBrsj6/0twIqkKZW5kc3RyZWFtCmVuZG9iagozNiAwIG9iagoxNDcyMwplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMzcgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0MzMyNiswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAzOAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAyMjczNCAwMDAwMCBuIAowMDAwMDA2NzU3IDAwMDAwIG4gCjAwMDAwMDY3ODkgMDAwMDAgbiAKMDAwMDAwNjg4OCAwMDAwMCBuIAowMDAwMDA2OTA5IDAwMDAwIG4gCjAwMDAwMDY5MzAgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk1IDAwMDAwIG4gCjAwMDAwMDA3MjUgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNzA1IDAwMDAwIG4gCjAwMDAwMDY5NjIgMDAwMDAgbiAKMDAwMDAwNTQ5OCAwMDAwMCBuIAowMDAwMDA1Mjk4IDAwMDAwIG4gCjAwMDAwMDQ5MDkgMDAwMDAgbiAKMDAwMDAwNjU1MSAwMDAwMCBuIAowMDAwMDAwNzQ1IDAwMDAwIG4gCjAwMDAwMDA4OTMgMDAwMDAgbiAKMDAwMDAwMTAxNiAwMDAwMCBuIAowMDAwMDAxMTc4IDAwMDAwIG4gCjAwMDAwMDEzMjcgMDAwMDAgbiAKMDAwMDAwMTc0MSAwMDAwMCBuIAowMDAwMDAxODc5IDAwMDAwIG4gCjAwMDAwMDIyNTkgMDAwMDAgbiAKMDAwMDAwMjU4MSAwMDAwMCBuIAowMDAwMDAyODE4IDAwMDAwIG4gCjAwMDAwMDI5NjIgMDAwMDAgbiAKMDAwMDAwMzA4MSAwMDAwMCBuIAowMDAwMDAzNDEyIDAwMDAwIG4gCjAwMDAwMDM2NDggMDAwMDAgbiAKMDAwMDAwMzkzOSAwMDAwMCBuIAowMDAwMDA0MjUxIDAwMDAwIG4gCjAwMDAwMDQ2NTggMDAwMDAgbiAKMDAwMDAwNDc0OCAwMDAwMCBuIAowMDAwMDIyNzEyIDAwMDAwIG4gCjAwMDAwMjI3OTQgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAzNyAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMzggPj4Kc3RhcnR4cmVmCjIyOTUxCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:33:26.023661\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["exmp_imgs = [train_set[i][0] for i in range(16)]\n", "# Organize the images into a grid for nicer visualization\n", "img_grid = torchvision.utils.make_grid(torch.stack(exmp_imgs, dim=0), nrow=4, normalize=True, pad_value=0.5)\n", "img_grid = img_grid.permute(1, 2, 0)\n", "\n", "plt.figure(figsize=(8, 8))\n", "plt.title(\"FashionMNIST examples\")\n", "plt.imshow(img_grid)\n", "plt.axis(\"off\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "23a0bd21", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.032861, "end_time": "2021-09-16T12:33:26.216246", "exception": false, "start_time": "2021-09-16T12:33:26.183385", "status": "completed"}, "tags": []}, "source": ["### Visualizing the gradient flow after initialization\n", "\n", "As mentioned previously, one important aspect of activation functions is how they propagate gradients through the network.\n", "Imagine we have a very deep neural network with more than 50 layers.\n", "The gradients for the input layer, i.e. the very first layer, have passed >50 times the activation function, but we still want them to be of a reasonable size.\n", "If the gradient through the activation function is (in expectation) considerably smaller than 1, our gradients will vanish until they reach the input layer.\n", "If the gradient through the activation function is larger than 1, the gradients exponentially increase and might explode.\n", "\n", "To get a feeling of how every activation function influences the\n", "gradients, we can look at a freshly initialized network and measure the\n", "gradients for each parameter for a batch of 256 images:"]}, {"cell_type": "code", "execution_count": 16, "id": "d0313697", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:26.290315Z", "iopub.status.busy": "2021-09-16T12:33:26.289838Z", "iopub.status.idle": "2021-09-16T12:33:26.291904Z", "shell.execute_reply": "2021-09-16T12:33:26.291439Z"}, "papermill": {"duration": 0.043006, "end_time": "2021-09-16T12:33:26.292005", "exception": false, "start_time": "2021-09-16T12:33:26.248999", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def visualize_gradients(net, color=\"C0\"):\n", " \"\"\"\n", " Args:\n", " net: Object of class BaseNetwork\n", " color: Color in which we want to visualize the histogram (for easier separation of activation functions)\n", " \"\"\"\n", " net.eval()\n", " small_loader = data.DataLoader(train_set, batch_size=256, shuffle=False)\n", " imgs, labels = next(iter(small_loader))\n", " imgs, labels = imgs.to(device), labels.to(device)\n", "\n", " # Pass one batch through the network, and calculate the gradients for the weights\n", " net.zero_grad()\n", " preds = net(imgs)\n", " loss = F.cross_entropy(preds, labels)\n", " loss.backward()\n", " # We limit our visualization to the weight parameters and exclude the bias to reduce the number of plots\n", " grads = {\n", " name: params.grad.data.view(-1).cpu().clone().numpy()\n", " for name, params in net.named_parameters()\n", " if \"weight\" in name\n", " }\n", " net.zero_grad()\n", "\n", " # Plotting\n", " columns = len(grads)\n", " fig, ax = plt.subplots(1, columns, figsize=(columns * 3.5, 2.5))\n", " fig_index = 0\n", " for key in grads:\n", " key_ax = ax[fig_index % columns]\n", " sns.histplot(data=grads[key], bins=30, ax=key_ax, color=color, kde=True)\n", " key_ax.set_title(str(key))\n", " key_ax.set_xlabel(\"Grad magnitude\")\n", " fig_index += 1\n", " fig.suptitle(\n", " f\"Gradient magnitude distribution for activation function {net.config['act_fn']['name']}\", fontsize=14, y=1.05\n", " )\n", " fig.subplots_adjust(wspace=0.45)\n", " plt.show()\n", " plt.close()"]}, {"cell_type": "code", "execution_count": 17, "id": "41a44b36", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:26.364210Z", "iopub.status.busy": "2021-09-16T12:33:26.363743Z", "iopub.status.idle": "2021-09-16T12:33:55.550270Z", "shell.execute_reply": "2021-09-16T12:33:55.550658Z"}, "papermill": {"duration": 29.22383, "end_time": "2021-09-16T12:33:55.550789", "exception": false, "start_time": "2021-09-16T12:33:26.326959", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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NiAwIFIKL0VuY29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA0NiAvcGVyaW9kIDQ4IC96ZXJvIC9vbmUgL3R3byAvdGhyZWUgL2ZvdXIgL2ZpdmUgL3NpeCA1NgovZWlnaHQgNjcgL0MgNzEgL0cgODMgL1MgOTcgL2EgL2IgL2MgL2QgL2UgL2YgL2cgL2ggL2kgMTA4IC9sIC9tIC9uIC9vIDExNAovciAvcyAvdCAvdSAvdiAvdyAxMjEgL3kgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE0IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDEzIDAgUiA+PgplbmRvYmoKMTQgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxMyAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNiAwIG9iago8PCAvQyAxNyAwIFIgL0cgMTggMCBSIC9TIDE5IDAgUiAvYSAyMCAwIFIgL2IgMjEgMCBSIC9jIDIyIDAgUiAvZCAyMyAwIFIKL2UgMjQgMCBSIC9laWdodCAyNSAwIFIgL2YgMjYgMCBSIC9maXZlIDI3IDAgUiAvZm91ciAyOCAwIFIgL2cgMjkgMCBSCi9oIDMwIDAgUiAvaSAzMSAwIFIgL2wgMzIgMCBSIC9tIDMzIDAgUiAvbiAzNSAwIFIgL28gMzYgMCBSIC9vbmUgMzcgMCBSCi9wZXJpb2QgMzggMCBSIC9yIDM5IDAgUiAvcyA0MCAwIFIgL3NpeCA0MSAwIFIgL3NwYWNlIDQyIDAgUiAvdCA0MyAwIFIKL3RocmVlIDQ0IDAgUiAvdHdvIDQ1IDAgUiAvdSA0NiAwIFIgL3YgNDcgMCBSIC93IDQ4IDAgUiAveSA0OSAwIFIKL3plcm8gNTAgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvRjEtRGVqYVZ1U2Fucy1taW51cyAzNCAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjUxIDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDMzMzMrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNTIKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjIxMTQgMDAwMDAgbiAKMDAwMDAyMTg1MSAwMDAwMCBuIAowMDAwMDIxODgzIDAwMDAwIG4gCjAwMDAwMjIwMjMgMDAwMDAgbiAKMDAwMDAyMjA0NCAwMDAwMCBuIAowMDAwMDIyMDY1IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwMCAwMDAwMCBuIAowMDAwMDEwNTU0IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAxMDUzMiAwMDAwMCBuIAowMDAwMDIwNDE0IDAwMDAwIG4gCjAwMDAwMjAyMTQgMDAwMDAgbiAKMDAwMDAxOTc1NCAwMDAwMCBuIAowMDAwMDIxNDY3IDAwMDAwIG4gCjAwMDAwMTA1NzQgMDAwMDAgbiAKMDAwMDAxMDg4MiAwMDAwMCBuIAowMDAwMDExMjAyIDAwMDAwIG4gCjAwMDAwMTE2MTYgMDAwMDAgbiAKMDAwMDAxMTk5NiAwMDAwMCBuIAowMDAwMDEyMzEzIDAwMDAwIG4gCjAwMDAwMTI2MTggMDAwMDAgbiAKMDAwMDAxMjkyMiAwMDAwMCBuIAowMDAwMDEzMjQ0IDAwMDAwIG4gCjAwMDAwMTM3MTIgMDAwMDAgbiAKMDAwMDAxMzkyMSAwMDAwMCBuIAowMDAwMDE0MjQzIDAwMDAwIG4gCjAwMDAwMTQ0MDkgMDAwMDAgbiAKMDAwMDAxNDgyMyAwMDAwMCBuIAowMDAwMDE1MDYwIDAwMDAwIG4gCjAwMDAwMTUyMDQgMDAwMDAgbiAKMDAwMDAxNTMyMyAwMDAwMCBuIAowMDAwMDE1NjU0IDAwMDAwIG4gCjAwMDAwMTU4MjYgMDAwMDAgbiAKMDAwMDAxNjA2MiAwMDAwMCBuIAowMDAwMDE2MzUzIDAwMDAwIG4gCjAwMDAwMTY1MDggMDAwMDAgbiAKMDAwMDAxNjYzMSAwMDAwMCBuIAowMDAwMDE2ODY0IDAwMDAwIG4gCjAwMDAwMTcyNzEgMDAwMDAgbiAKMDAwMDAxNzY2NCAwMDAwMCBuIAowMDAwMDE3NzU0IDAwMDAwIG4gCjAwMDAwMTc5NjAgMDAwMDAgbiAKMDAwMDAxODM3MyAwMDAwMCBuIAowMDAwMDE4Njk3IDAwMDAwIG4gCjAwMDAwMTg5NDQgMDAwMDAgbiAKMDAwMDAxOTA5MSAwMDAwMCBuIAowMDAwMDE5MjUyIDAwMDAwIG4gCjAwMDAwMTk0NjYgMDAwMDAgbiAKMDAwMDAyMjE3NCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDUxIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA1MiA+PgpzdGFydHhyZWYKMjIzMzEKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:33:32.587230\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDEwNTEuNzk3NzQ5MTIzNiAyMTYuNjY1NjI1IF0gL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDggMCBSCi9UeXBlIC9QYWdlID4+CmVuZG9iago5IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTIgMCBSID4+CnN0cmVhbQp4nNV9y5JkuZHdPr4iljMLgng/lkNRarPZcaZNWoxp0Wo2yaJ1FY39GJr+Xuc4bsR1IBE5lVlZVYLMqKk8HYG4fi4e7o4Dh7v+9fLbf3HXP/98xf93tde/4n//wL+/4d8Xi7/eX5xNzpRWSmz4+8fhb++yyTlln/Af7PjnXy6XP12saa7kWGyq9Tr/EZt1LdtSrz/xx7958oH7H5fp05dLLibgZ6I3rf/g+4t3+JQvLToF/6hhV6qpN/xoYcDkmf9+fdK4i8Fk69EGHgl/hmTa9acfrv/r+uH623/xncB/xf/+iv8JgZff/v6H/3z3/Q//9s3vrt//fGnBFJ+ab8MTn+jwFJd/v/zh+vdbw9a4hJdza1v+/OZAL3+/ODD3G4v/VIIJID9FX72/+mScZXPfv7/87tvrb/8HrHfXb/90aQZvqbRcS8I3v/3j5T+u/2SNtf6fr//7+u2/Xv77tyAAgGPDVv3r+/ds5Te//+Gv3/3PX//9uw8//+b9uw+//nz9/d+uf7j8QZ747XlzoRpbUswjcQp+A+ac93zumGuNtT2mzg6E2ZOwz2Z99Sb6ZG0drT/ht7A+Y9AWthZdyh9rvdfWq9YafiB62xJGrb82U3Q7fmznm5++++P1/Xd//vDul1//+MPnp/Pj54zXMZmsqfg6+mapqYXyLIdfzNyC12urz24094Rfay5GDc0t7Iw1lRBccM9Y7O0XGTO3aR2WYojUVkazFf6JdmOUYA5y6OvOZ/+M4fHLGo52agsp+MnwE/9UwwOGNWbgGFto4RnD85c1vBSTQna5Toaf+KcaziU25Vix9Pj4jOF1NvzvFzbzGzYIF4lPi6Ufk2Rw40Q7TZD/7W+/fvjlEwkM3dCaj/9X4Esl7WUFj2m/Zo/pim4Y/qN1tdoKR6u7gvrLV/3ly/TlywUEVVBj8zDJYo3yNtKP0HPsiJZoQyY6tHBD4Zf97v9/4xdmvr9grU4pZZcG4xWKjmBTa3mkRKFbGL8yE8Zn02zxro3GnyjMDL65J4wc4B6mL4yEb++wzsAHioPpCoWVES7nU0Ju6BbGr8yE8clkZ+EWjMafaDBwum0OMyV3dA/jF2bC+IZ/e3Th0fgTDSbWULyfKbmjexi/MJPZgmjgCOaQB+s1HI0PoSU/kqLQLcxfGkr7K1MgfgooNJxMRHiU3ESLgjchYGEpCHCYwuBsxXHB0zBjd3jgMy8K3oOAlaUkAM+c6R1PBNzhFNDXk40jLQrdxPyFnTDfO1MQ66Uwmn/C2RtfY7NtZEXDexCwspQESFyYU50IuMOlmcwsXRl50fAmBCwsZXLOmugDg+KBgBNm6JxqQvgzEjPge1CwspUURFNKRDMTBXfYYb73xdfqJmo0vgkFC1tJQYM3h29NC+EJO1jlq/XOTdRofBMKFraCAvp0hdmCkYITdtkZ9Pyc/UyNwvegYGUrKSim+pbLtBiesPNc93OMZaJG45tQsLAVFCRvXIU504J4ws2bkkNMEzMa3oOAlaUkALN6wJQ2LYgnXNDbY7Bu4kXDmxCwsBQEZLRBH2daEE844aXDBWh+5EXDexCwspQEYFEL2bZpOVRwMxUPXuPIi4Y3IWBhKQlocGxKsdNiqOBsfLRFsp26EQVvQsDCUhBQguFugZ2WQgVHkx0MiBMvCt6DgJWlJKAaj+DGTguhgmFC8c5PtJzoJuYv7Oxb1ghukpsWQQUHeDw+hviElTu8BwErS0lANo3PNy2CCmbqtxXZnxt5ucObELCwFAQ0Z+DMMME5EDDCNdUeDoyN3OA9CFhZSgKSyXyR0yI4woiBnrLSwU2MX1j5/uKtRTeubRz+I1qarX7ByAFvYf7KTlofTWSOJ03mD3BJxdWnrBzoa80fN9BNuv7j8tCekYx/++Y6brTPm7OOeqH3F8QpXKttn8JawJP0nY1knW2yl8l97iy7INTGOLxhogmjurYuKUR3d9HKPkDL2YVwwK2V6AOz465El2Rw2GZ8KckXukfNWe97KhWm1IanvzKGag2vUGBuLCKQojOZGlaSW961ZoRZ8L2dgauJZxE4moRHBfcIQ2L0WbYhYaipoRSsQokqCu9z/3TGV0NBhJYiXFZOWEdOE257gfsGBz7nVFNPdTaDJvhH9tTehN7bvTNWwGK8w9P1ZCGYhOOP+L8kQ2FalR/0ycBai5deQW9LPWDw2YSAt5KpKsC66bN4kQigg4XrwLwaiHTB97XFN3GpypUP6i04EEYCOkf1/IwLaLCG5HtWAq+3RY8pyIFBdBM0IDhfILvd1ZGUFLPNRyKHe3iI2hjJoPviIQTHEzu0ygSPFf1a7u1kvlqwC5x7YKW/iVDw7xwdFX7WlJhD941DpcAsBuLeBFDebnhpNaJ1hI0t2dJ6QsEadAw8J1qv1Lm1Ay3cdvBd/ZZd7dn4iL7gfYoBON4ReninDN0dLx8vEjbhqwWPLg+JbonoJKM3kD4bMYzKEcRj+ipohr2vxNLfSMQTBIwVhzcCtvFKegYUvOKJEekw5kcPsT01nOS7FfbBH8IwKLXD6KPRNyaSMVGA976VlNBH0ROzdAwnY9AxgPTBohsldL7YqkwlDn/gdTiLXg7rUyh9uGWH19Ww9B5jonr5NJjAyPOwLHGyqKkvUTmyk3gqMyumMbxDf8QumcIWx1GI7uT6Pg96t0UckzzHLDp26WjlBBe56c/povieDsWL9dwKq5wOQmvZieEV7xIDH5MWvUL8354xqeg1Ftx01UTGgJLnq5UiTev6ZAUW++TREF3TsgPG3JEXUzHGSnsMjyoiz8kVD3ysFv+l5nAlPEaLSyni+4fyZXzjRXrGp7/6bNsWRn2sPIpapoIGgqXm09V4a+mxsunH7/7vDz/9bKz5xw/v/vyXQeN0CNJfohzvsvVZQf5EfD4qyLHEYRw1n0cGMZ/AQMoQBgYVPFClWnmqJGcnuX+Az9FeuWxfVl0rBA4wTEVjqKXgN1AHwx8wkfMdVs5XK6M/HwcYnFiZk5s2HxT8FhxgscUSjakNM3n8WBbSA4W0hw8Ucg7d7k+SSH8+Xl82OF6pqsQyZM+nf51s+otQgBnNx/aEghN+NQXiNdyfHgscR1oo8DKek1K7lbD2izBBDzTBE4PLOFCh8Dfign5xgAsccrX2OZXxUlf+ZciA71jRzBMyTvytyKA3GgrIiFSIPCYjPKM8hqujOXm9AvmV5DIQ/9jY878OxJ+N4rG6V8/twzxtycLvM6KxmCLxCYZTGgQeGzngeyi+BxELi0FEYxzTpj25Ea2IR9tTdm7wXjQs7CULWMsD4rI40TDAdx6W7GxGw8Jg8tCoPrVPusMJU5SNpSlP9Ch4MyIWFsN5tgGOY8xTolLDgZvSrkeNAz93eCsilhaTiGocPtXKRMQJI+zGgi9q7ZGfO7wZEQuLQQQzOFh6p61bDVPDi9EQJn4UvBcRK4tJRGaOM01buBrOJlmENnHiR8GbEbGwGETAM/V4vKlD3NHkTUnRyslK1YSG96JhYS9ZYKLRFxcnGu4wvGBmpX0e2NHoZjQsDCYPzbSIqb9OPNxhrJbR+Z56VY1oeDMiFhYz74RwmFsS4wavgilwRFNOEsWqlQHfi4qVzaSiGnxHdqgGKu4w9zQimixhokjjm1GxsBlU8I/WeDR8oOKEuc3hM3yoPFGk8b2oWNlMKooJiZsgExV3mPr3EmF8nijS+GZULGwGFSwhY32Y1o47WuA7eVt9GPnR8F40LOwlC1l2eeNMwx3Gv3zLXvbhzjY0uhkNC4PBA/eDbUpxWkIVXNEFSnATPRrei4iVxSQicsPcpWkJVTD31mzqvqVqRMGbEbGwmEQ049BimhZQBcOEZJtkYHQjCt6MiIXFIKIgnM6InqblU8HBeGubqFpGfu7wXkSsLCYRxVA4lKfFU8Hc/Lep1Cf83OHNiFhYDCKqNy6HeWQMaLOhzewoeC8aFvaShYxv4XvT0jnCdx6W7GxGw8Jg8NCkjVKmpXOCj42LuZEd9zOWFpOIhBhSNHUjEQ/gJW2bEbEwjUQ0k3JrdZogHsFLfjYjYmEaNSDBNHyyjUvnA/gBP1sRsTSNRFTjiwjxRiIewEt+NiNiYRq36L3JPpZBMPXjE/iYFedG3miy1BKDi+ixrx9Jy1M99rQ/XWKN7djVrCk4W3odqeDk/FCw1GCmmORUTSwldu8ZdgZXqDgBXGJ03ZekNjA6nr1DdEFJqpMlxAWMk8QnjVR6wuWUZIWLxnuXuROG8MwCFT/MiSyksO5pgWtqe0TPpHhM1MFSEetdlOdwlYf9agiUnyKcAfUCN7hyrSbRXrtamohBg7dUkrNcCOLBmlrrn/aeEvQWyjU7Y+EA9f0pH6kMbXjFlJziMUs74OKq9SxiGBL85d40LAiWmfrqTMR/7/T5At+hOpjQsonRetuTehYmHCIYROg8tSatBHjgMfvaJJNl5SzzkexLqVL5S70zc32Cch6O1MdSKosfdSKDJl4L7CDeqLJuovkOgTLWzPp71DtnV0InMYDm6vEOR3k0cczyPEfFz8M3bvVIt0UECSlJ8xY9IvaPU8xiY6GYJYEjezgQwKvNsRU5os4uZHuOBqEo+j5F5ZQUt+xDz2HJhxIlLBwVLcUjpyON5hpECe2dPfoLeon1vmAUVaaH/fGzDPSDqKjxRqxrTiTIeGNUuFf0riLS4F7jj4mS7CJ4Bb8F70c03KAW81pueJepUufMKIkw34Fv+AzP3dYaj6ZZjwi9K4i0H/6CCMcJ1wbzpTOKRLq3XQ1HUJFMhcfgl4P7DNBlU5hjwrV45LOAYq5xWUoBYczW0GNbHvcunocd0CvBh/VHbGsLZxnCFNn2X8RbCc0FCvEChk3uJxJCzga9BVRd+yQU+6cxVaGf5dD38ZPtunP0Byq7mz38Yp/6HgWco1aym0rWTdNkn2wewi+QSD/QGj5S1qLlpQzx/UONLsXVL9Uzrn/92d94iWQ6sNe3jBHW1dcv0Ez7r6iZjjXwDEua6lhjeTKB3xp9BgUPXKlW1prp8wPTMcJPVu8lLl8FU9jYDxT8BnrhWFkgtebairfP1JN+WIo7vUEt7s/IIWc7m2cK7+gbMJiwLlU4GXQ7sNZ8rO78iQz085GAL5uMhqfybwp+Cxrw+KGiNfyjpo8uzf1IeZ5YXqlJC5+iOv98pL5sennl4OQ5u/PpX6c6/yIUcCs4w/cPIwUn/GoKWFXifHr4DL4W+C+FGwjPCK3TlxxgiohW6LI760ciTviNiOB1DHAAXYTD/1xB7/RFZxq9WnoeBm0hjkSc8BsRwUDG88QcD7o+x0T5Wl2CIVUrktYfqTjxV3NBNYriIvJgqa2IAHx87hzCk0MZSnofg9ecvF56/0pyv2z2pTEss21aGhFnGvSoNGbsFcpUBQLHIujZhILvuZc9aHhqL1lAmG0lxzLSMMCI1UQgN7ZxQzejYWEw3EXLWvqpTFVSNcxBgpjVTfQoeCsilhaTiIKQHc8eJiJOWFn8gJ/NiFhYzBjMs7pAm3Y2NRyMp7K8PuHnDu9FxMpiEpFNdKLmGIk44UBtdT/3P/JzhzcjYmExiGCON3s3VVHVMNO6qRcT0I0oeC8iVhaTCER+jiUyRh7uaDI+smrsxI6CN6Phqb1kgTsB6OFxouGEMSfAXKnCotpQ6GY0LAwGD0Eyvm2qqapg5sV9bCmP9Gh4LyJWFpOIalyuaaqsquCEwKAWLyl9zY+CNyNiYTGIwB8Y927avFZwRhCTXREttWpEw3sRsbKYRGTDVMa0ea3gWlgEKkusrBrR8GZELCxmNtax+E6YKq4qGBYZF6gRHAka8L2oWNlMKpJJ03nvG+Zkq7Ak2bHV5Gh8MxJma8lA49ZnttPCecLcK3atRdmhvpOjwc1IWBgMHnI0vlQ/1WBVMItoZRaYGtk50b1oWNlLGii6aHWqxapglp9jeTTZ89b0aHwzKhY2g4rCtIoUwxuoOOEGp4FXPE4MaXgvIlYWkwi4RIHlAici7jC+xxvK8sSPhjcjYmExiKiY+WvMU5VWBacq/rRoOVQjGt6LiJXFJIIqqOynSeKO8rJb2FUnck50MxKeWgsOmIZGe9M5NQ0XgzWiivZMtaHQvWhYGUweoimhUS848nDCyfia5dSqbuNEN6NhYS9paAZzvo3TmFBwNPjFLOc0dSMK3oyIhcVUOQQpjhvHZVPDwaRYjwylakTBWxGxtJhEFFMaC7VPRJxwoL4+ixpy5OcOb0bEwmIQ4RArsEDuuGxqOGAMZFEtjPTc0L1oWNlLGlgRusSZhTsqZZ2zFFCeuLnBm9Hw1F7ux6OJ2Gwel00NDzys2PlUGt5SZj9taDdKdmWfxlEfmeXUKW9urH0/Boueb6XfZ5tj7AkW1xAwYMTLYhCCZ2EcJmepwI22F0BmgWZRszB3XTwP9ErxnBp7CfjEit7JsuwYSy7n2G+JS4F63ei93JfD+mSup/nglcE1jeKrBbkCXWDes5gpKE7OONfqLXmcWuWNI7yH0gdbe86UeaFKG1gzGg5/rbcMYrMx9Usbc/I9iUQxRU3Os5Y5oiQbJJCOUkE2pchgAdN/SLfkG6LK1BKrW3uK36VprpXMxBWW18y9MDjhZFjhHJ208X6oG1GgB9xQHW9BQmL25sjkBIeJhd2Ciu2Sbc948F6ViBcoMX1k4e98ZDx8lIq5lOQXOW3Q4WaDdb2iY8lOqsEnCsXh5YiCn6eGanEdZwX3ZKnhp8a+gqB44JUS+jBq8qmYTL7Y1nHL6tzxyDy4ytpRvRxI8zHWA68UfSc5IwBqao/Pnam2uNa197XBenkcmILXy4rVLDDuQs1yjoHxfAxSFp4vDKT1zsXCPA3seNETWXvcOpP4Uw7fxtM4dF0Lx65/Hqu6x8fT9Va+399CQ7zURKV+yKmL71NhDkEOFRUq/4uX2vwMn/C0GX2AMveKUKpHVVROBt56RAV9QNcRXlj/uyQe2gOjeCV8YYQ5j/L5EuX+3OAQNMJ6dFLP3o+OW6Q6PWE4Ys336wBK6RXb0Gt5AeNRU6ClGtot2sHUwyES2ePRiljD0uJ4vV72lhD1tXpzfK3LFm+Fciov53IIY5qo0aV+9S/ak0pYdA8za1nE446jlHoj6FzlVkDM25zkmAJ9qJr6zMlfcH0d9dLLaq+6VhBwdJg60ZiyzKh4wUWC0mn6tclKrfoH8Av0+w9Um49U3qxFvhJ0vn+oF6fi/6XK0PWvP/sbL9HvJ9u4dSASrpeI9+NXFO9ndsTGAwcDh5iP0aWelKVR8ECUamUt3j8/wIX5LfV/xdJblCNH2gAFv4HmOldmtHi0x2H+ebF2/y2k+5+RQawVDWvtdD+fgt+AwcLTTkwKusz7HF5bNP7zsVBFIiEX1uhxcEffgIOKOdjCJ+HZQSxBn1gyHqsU/Cm4e02Omn6KeP/zsfqy6eWVg5O34ZxP/zrx/hehgLnA6OhNDRSc8KspSFFTUHlU1gGU61Ker5L+VYjgdUNohv12YELhb0SFQzCFvhCKY+WXZ7iIX40L2VHlFTYTFyf+VlxkuKiOlYkzA6/HXOTHonU4EpqS14vWX8ntl81kNAZjVINMvgUvN8t1KnqrYTrNtUg4rxtR8Hl12wZELC0mEbxVRjIBIxEnrCx+wM9mRCwsBhGO96RZO+2ea5gDJdbkn/Jzg/ciYmUxicAjBZenwrcaRvRJVVV+ws8d3oyIhcUgAo6j57n8sYaOhlkTvUWp6DDyc4f3ImJlMYmIJodYp71zDQfTUg69Rwz83OHNiFhYTCKqaTVP+4QKjaxC1rN9ugkFb0bDU3vBQggmhGonz1/D2aRoJamqmjjBvUhYmUsWism15WnvXMO8l7T5OpOj4M2IWFgMIvAHYh8/j4oT5g2WPhQ56qca0fBeRKwsJhHMtMMpmBbOE+Zug2feduRHw5sRsbAYRPAu1RjjVO5WwUy0w32a+dHwXkSsLCYRCb5h3+8biLjD3HrB5Jhlq0a1MuCbUbGwmVQ0E6XS08jEDeXttzXFOtFzopuR8NRapmMDK0SNxYp+1PBRwyvKBqAiR8N7EbEymUxU1iGTbfOBiTvsklQw6/cxa4Y0vhkVC5tBBTfPrY9lWj5PmFUbKtZJUdNoijS+FxUrm0kFK/5FO1VvVXDjtnnLUuRPNaLhzYhYWAwiqjMe899UvVXBBU25WuzEj4b3ImJlMYlIJsExqtOyccLUbDSW8Rj50fBmRCwsJhHNwJw6zZd3tCuBRB+iyTnRzUh4ai04aJE1GONU2FbDlHXEMlKjwL1IWJlLFqrJztup2K2Go2m+y0t0Gye6GQ0Le99fquUfodhx2dRwMBWTQc9bq0YUvBURS4tJRMGcn4ItExEnHKiTkvKyIz03dDMaFvaCBucMvs0ysAMNCg7UDnqpqzKyc4f3ImJlMYnIBo0mlyciTljt3Iz87Lmhs7S46z7Q0+cOcaIDDUt29qJhYS9ZiDzIx8rLIw0DXIuTKGJs44ZuRsPCYPLQjC0UaE88DPDJw5KeTyXiLcXr08Z2Y8lobudWqnPT0b9L7NpgPL/xLJouG9holyLkvlUDBtpRbdrGLnUvnspb1/p5Fm5dSNk97mg4Z/vNuHxDvrdBiS3c7n5hbrO9NHWhHhPf5L5gktZE2lrAd0hVSEvUtPvSs8LW5GI9T6/yHlHGdUfWHFzKwTuwXW3/bGJdqJBFFh8p1hSU+toYrZRDKbH1KiBMN9eaY5R7WsFXrw5eWAe9VdYST9FkPI9o6EuUvpe8KJGxQByNgOBIQRJl8cF753reMpoYc07hKpXdkxWhe6G51vN0XG48JxD7dlBkFaNY8FVWoLatC6gLK6CThEIJUfAx+p76cSy2DxceTQG2sRsJGxDfUfyMN2ioDRJpSWFV85pbpiyHDx5DpwpWJPCaq0jIPbXDvZ1qWHCfR7/lFHi04ZaOSyHxpLiY1A8zFIq1m6PQmbr4llztebpMhXFxIrnFMlLq8eqz48XAXXWodPEF7LnUvDt08Rg9UiOfeAXXoeviW8zHjmqm+NmxSh5/F5T6bhUeGWZyWPAxMTT7WQzirKPOOg/4Z8pY4PvnE/djg+vF7VkTvvbnyaJ8CRTYU+Lvcn9VtB2TQqHAnsFDO1IveDjLq8C9SJLwxKm/w8LJoCV0WTCKvu7d8Xl0LfRTPDQrr1Ow0/MW3ChOmfnuwNfWTx+gC+AFldyuCOcx5lIfiqXyTEIJFAsaXi8QxaRqecSA2iNYWtGXOpNUjYXGwvOZtxdYW3pEyJ15V9HJpFvbHDuMDpxDc5VbMc71U+8F/RC9rVUOGMwvImkrlecSovWiuncsstMD7moouoqiukc3qr2bIvaiiw0WeA8Eb5SX98njNizsmjieK2b4bmJDH+EhFhn7mHVdb6TxUAJ6lBxCzYTlSRr+cJgTRXWPQWflXdKxlamz9rOIKUkXolwTnT7VvlcfnZfdJjyDKTaWWPuBPXd4hdzCT74dNwFjWMrZgso8WyruPtOS9idLB0v5h8fwC3T3DwSXjwTaaHmpxXz/UOpNpf5LRZ3rX3/2N16iu8cswg6WCsY4L5V4gfQ+f0XpfbPW8I6QNAanPBNr6uRy3cGBJ9XCWnZ//0CtfngtnyxgbLK6otkxklLwGwimG08BUgzDykPPiMYf6O7dG8juPyOD1XML29cxfavgt2AQy04qbM1mLPAfKTmftZqfjwSH1QcuW5kLoGr8DWiAI2RZGBXNOf/cCQ77oP8Myvvm4R1j6UtRTp9+ivL+M3avF8wurzzOwNNU57O/Tnf/RQjgbUV0EQcCTvDVBJSsCaDnWnxOBWt+e7Zk/lchAe5WQlCVsybhBN+IBKy9gZ2gOta4eK5c/lchgZFPQCBXBxYU+kY0OIvYQi7UinAgny2W/3V4CAzHfbIjDyf6ah4QbGke4NAyZEL8wAji2UL5X4cIOR3d6NprIk70rYhAQFQcAkYGzs8SMd/Ucj96UXPQfLz+6MUrif2imTjMSUzExEn70TBS0eq0X6FQvD+E2hJj6SYUfM/DbUHDwl6ywMjU56no8UN4xdlmNCwsIw+8eTD6SfPxEF7SsxkRC9NAhKSU+OCjxQpmeqsmyS3pRhS8FxEri0kEHBpb4lT0+CG8pG0zIhamMRD3vADVTkWPNaynStXItlPlymISweyszZPwQ8PZ5J6V103cwc1IWFgLEiJik+z9pPtQMK9Iaoi0/ciNhvciYmUxicAjOW40TUTcYe56MEuQJ34UvBkRC4tBRMKj55TstEaesNQutanN/Ch4LyJWFpMIFtopbtrqV3Dm3ectSnEi1YiGNyNiYTGJkEuwy1T2WME6Y6L5UfBmRCwsBhGZu9w2TGWPFcwNhtZvIldtKHQvGlb2koZiMkLWKVeoYITLBjNDsnGkZ8A3o2JhM6jgjrOfCk39qGHK6UNyTbbrNEUa34uKlc2kIptQkpuqHyvYZcs91H4Fp6ZI45tRsbCZOzWWBRlLmJbQE3ay++J6aXxNkcb3omJlM6lIxrK687SInvAg5hgoUvhmVCxsJhXUFbU2lUFWMMtJ+tJSdygURRrfjIqFzaBC7s5FgD0tpCfMGpQZcWfJE0Ua34uKlc2kghfoBOATFXfYBbnCuV/dqCnS+GZULGxmkl0qVMaSxtVU4xSueeesHNTTJGl8KzLWVgsb2dSQw1QXWeMt4V+2F/DV7Wh8NzIWRpMMpqorlRwTGSde4VuhnVYmkjS+GRkro4WMxM1ZHu2fyLjjOhjX7WwbpK+NFjIaoqtJMP3jgKdsMm86LTNJCt+NjIXRJMNHucSzuomME0/wu6sNopseSFL4ZmSsjBYyKq/pC3VWzCm8URbebJlI0vhuZCyMPmRTCL1bnddWhasE/9DOron/tdFCRjG+YU2Yl1aFq/2voZ033Bd7y4Mb4552SCwJLxuh1uSakrxd70sofcMzyBO2Xl4tZyd3rFBVmWpjYUk5z2GTlDtvHo5FCy3304+8DLQIXHgbh7Mid26BIazAlGlH6vdZkjxR8yNbB45VWHyQelas9icybW6sBMQ4Ra7/gfvipa451aI5wrWVKw188XIIooUo1dZT6Xcm8XS6wPyIDa1fdFBCv4OwhWrwvZhZf5s3sRURb7dITTdv0hB9eqihR548fcLbVupVJB62hg5nnk4pcmMXpobUkxf4HUvdiudFBzlisMgvctrAN12kUB5BbJDLjJgaLvhIkgsQbHPHY3PGdRF8M2NcQisitmqJ1OPVVOZPcy8WL0nVUOHd5sK71HLrLVBZ7uS+NVjoWur5BB47yIFC+QIaUz/HwHxkwiuy8VoKTzpkOYXSeE1AlcL2FW+oHIc3G68JSImF9+E/VZZBuOXyqEEt4mLa3I/CNyZ60aeClwxfySyIfaS7MJhYHZ+VZ8B0OT5PBQzeAeI5n1my34eeK4omUAeNdhIHTuvHFfDmqPfPkZcrgm+eijjyJpih0B37eROecunN8HrWRpm9y7yTofrcP1/ZH4vP/YRHjVbONxDnGigROCIKa0MnvfC6BoyGKnhGz5YbPpmkQEemLcQbT2m4A4eXVeX8CwdBtVJ8Ce+L519qkI9XnnzqUS3LuPJ4oOAplnLH4cbLNRWsy4pR36Md3lEAu+QcTWYpf+fckSiIVBAdNKAX9tHC6wjAuRxccQaDIqSecKhSCIc5FvQUdMd2z0PAA+D5FLkaofYjRvhBlsB30cm5FZaek4oYjTcSYJKLnNZ4FUtN8Ra5puZ5/MVxgjjy/zzhRTF2xPimBg6d2crBFdINe3mVy5XnqdHnSl+BqJTzljW96MLzhof+eUxeWKpZAi/LXTD2mKR53wR6AyZODDMO4BhvDh+GdcNrwajkc8rJGODoPuxj3MKrtfXbJLjccMJpTfxDj6nXtpurhO9afCqx8l7uJ0cw+hB5YlA7caEwUPtF8uJNVASnTcooYGKT0ypYSjgzgN1+VA3k53JbYkAIPiZn6jFB2mPpwXQA9vtVMHhVNvWfxVISPGVe/b4wnm+alzDwimFb3TP4Cw6QPNANPzpqgJYXkmItpZs+/UJd8vp3n2n/JQdHmk/cvKk8YOjqiw6O1MXBEdVyYCFrUZg79PY6thhVizeJ+LsfPvxyysSvf3z38y8/vfs/v/7y7m8frn/620/X777/5d1/ftf//PXD9/KPb//5wpkPDCT0MJ5L+6fvPvwFT3S5q/0u/w9h66yWCmVuZHN0cmVhbQplbmRvYmoKMTIgMCBvYmoKOTEyNAplbmRvYmoKMTAgMCBvYmoKWyBdCmVuZG9iagoxNyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIzNSA+PgpzdHJlYW0KeJw1UUluADEIu+cV/kClsCfvmarqof3/tYZRLwMD2Ngk78FGJD7EkO4oV3zK6jTL8DtZ5MXPSuHkvYgKpCrCCmkHz3JWMwyeG5kClzPxWWY+mRY7FlBNxHF25DSDQYhpXEfL6TDTPOgJuT4YcWOnWa5iSOvdUr2+1/KfKspH1t0st07Z1ErdomfsSVx2Xk9taV8YdRQ3BZEOHzu8B/ki5iwuOpFu9psph5WkITgtgB+JoVTPDq8RJn5mJHjKnk7vozS89kHT9b17QUduJmQqt1BGKp6sNMaMofqNaCap7/+BnvW9vv4AQ01UuQplbmRzdHJlYW0KZW5kb2JqCjE4IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjQ3ID4+CnN0cmVhbQp4nE1RSW7EMAy7+xX8wACWrMV5T4pBD+3/ryUdFO3BECNLXOLuxEQWXrZQ10KH48NGXgmbge+D1pz4GrHiP9pGpJU/VFsgEzFRJHRRNxr3SDe8CtF+pIJXqvdY8xF3K81bOnaxv/fBtOaRKqtCPOTYHNlIWtdE0fE9tN5zQ3TKIIE+NyEHRGmOXoWkv/bDdW00u7U2syeqg0emhPJJsxqa0ylmyGyox20qVjIKN6qMivtURloP8jbOMoCT44QyWk92rCai/NQnl5AXE3HCLjs7FmITCxuHtB+VPrH8fOvN+JtpraWQcUEiNMWl32e8x+d4/wCVT1wmCmVuZHN0cmVhbQplbmRvYmoKMTkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA2NiA+PgpzdHJlYW0KeJwzMzRUMFDQNQISZoYmCuZGlgophlxAPoiVywUTywGzzEzMgCxjU1MklgGQNjI1g9MQGaABcAZEfwZXGgBSaxTACmVuZHN0cmVhbQplbmRvYmoKMjAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMDcgPj4Kc3RyZWFtCnicPZJLbgMxDEP3PoUuEMD62Z7zpCi6mN5/2ycl6Yoc2RZFapa6TFlTHpA0k4R/6fBwsZ3yO2zPZmbgWqKXieWU59AVYu6ifNnMRl1ZJ8XqhGY6t+hRORcHNk2qn6sspd0ueA7XJp5b9hE/vNCgHtQ1Lgk3dFejZSk0Y6r7f9J7/Iwy4GpMXWxSq3sfPF5EVejoB0eJImOXF+fjQQnpSsJoWoiVd0UDQe7ytMp7Ce7b3mrIsgepmM47KWaw63RSLm4XhyEeyPKo8OWj2GtCz/iwKyX0SNiGM3In7mjG5tTI4pD+3o0ES4+uaCHz4K9u1i5gvFM6RWJkTnKsaYtVTvdQFNO5w70MEPVsRUMpc5HV6l/DzgtrlmwWeEr6BR6j3SZLDlbZ26hO76082dD3H1rXdB8KZW5kc3RyZWFtCmVuZG9iagoyMSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI0NCA+PgpzdHJlYW0KeJxFkU1yBSEIhPeeoi/wquRXPc+kUllM7r8NzbwkK1qF5gPTAhNH8BJD7ImVEx8yfC/oMny3MjvwOtmZcE+4blzDZcMzYVvgOyrLO15Dd7ZSP52hqu8aOd4uUjV0ZWSfeqGaC8yQiK4RWXQrl3VA05TuUuEabFuCFPVKrCedoDToEcrwd5RrfHUTT6+x5FTNIVrNrRMairBseEHUySQRtQ2LJ5ZzIVH5qhurOi5gkyXi9IDcoJVmfHpSSREwg3ysyWjMAjbQk7tnF8aaSx5Fjlc0mLA7STXwgPfitr73NnGP8xf4hXff/ysOfdcCPn8AS/5dBgplbmRzdHJlYW0KZW5kb2JqCjIyIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjMyID4+CnN0cmVhbQp4nDVRSW7EMAy7+xX8wADW7rwnxaCH9v/XUsoUCEAltrglYmMjAi8x+DmI3PiSNaMmfmdyV/wsT4VHwq3gSRSBl+FedoLLG8ZlPw4zH7yXVs6kxpMMyEU2PTwRMtglEDowuwZ12Gbaib4h4bMjUs1GltPXEvTSKgTKU7bf6YISbav6c/usC2372hNOdnvqSeUTiOeWrMBl4xWTxVgGPVG5SzF9kOpsoSehvCifg2w+aohElyhn4InBwSjQDuy57WfiVSFoXd2nbWOoRkrH078NTU2SCPlECWe2NO4W/n/Pvb7X+w9OIVQRCmVuZHN0cmVhbQplbmRvYmoKMjMgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMzEgPj4Kc3RyZWFtCnicNU85kgQhDMt5hT4wVRjbQL+np7Y22Pl/upKZTpDwIcnTEx2ZeJkjI7Bmx9taZCBm4FNMxb/2tA8TqvfgHiKUiwthhpFw1qzjbp6OF/92lc9YB+82+IpZXhDYwkzWVxZnLtsFY2mcxDnJboxdE7GNda2nU1hHMKEMhHS2w5Qgc1Sk9MmOMuboOJEnnovv9tssdjl+DusLNo0hFef4KnqCNoOi7HnvAhpyQf9d3fgeRbvoJSAbCRbWUWLunOWEX712dB61KBJzQppBLhMhzekqphCaUKyzo6BSUXCpPqforJ9/5V9cLQplbmRzdHJlYW0KZW5kb2JqCjI0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjQ5ID4+CnN0cmVhbQp4nD1QO45EIQzrOYUv8CTyI3AeRqstZu/frgOaKVBMfrYzJNARgUcMMZSv4yWtoK6Bv4tC8W7i64PCIKtDUiDOeg+IdOymNpETOh2cMz9hN2OOwEUxBpzpdKY9ByY5+8IKhHMbZexWSCeJqiKO6jOOKZ4qe594FiztyDZbJ5I95CDhUlKJyaWflMo/bcqUCjpm0QQsErngZBNNOMu7SVKMGZQy6h6mdiJ9rDzIozroZE3OrCOZ2dNP25n4HHC3X9pkTpXHdB7M+Jy0zoM5Fbr344k2B02N2ujs9xNpKi9Sux1anX51EpXdGOcYEpdnfxnfZP/5B/6HWiIKZW5kc3RyZWFtCmVuZG9iagoyNSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDM5NSA+PgpzdHJlYW0KeJw9UktuxUAI2+cUXKDS8JvPeVJV3bz7b2tDUqkqvIkxxjB9ypC55UtdEnGFybderls8pnwuW1qZeYi7i40lPrbcl+4htl10LrE4HUfyCzKdKkSozarRofhCloUHkE7woQvCfTn+4y+AwdewDbjhPTJBsCTmKULGblEZmhJBEWHnkRWopFCfWcLfUe7r9zIFam+MpQtjHPQJtAVCbUjEAupAAETslFStkI5nJBO/Fd1nYhxg59GyAa4ZVESWe+zHiKnOqIy8RMQ+T036KJZMLVbGblMZX/yUjNR8dAUqqTTylPLQVbPQC1iJeRL2OfxI+OfWbCGGOm7W8onlHzPFMhLOYEs5YKGX40fg21l1Ea4dubjOdIEfldZwTLTrfsj1T/5021rNdbxyCKJA5U1B8LsOrkaxxMQyPp2NKXqiLLAamrxGM8FhEBHW98PIAxr9crwQNKdrIrRYIpu1YkSNimxzPb0E1kzvxTnWwxPCbO+d1qGyMzMqIYLauoZq60B2s77zcLafPzPoom0KZW5kc3RyZWFtCmVuZG9iagoyNiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEzNiA+PgpzdHJlYW0KeJxNj0EOAzEIA+95hZ9AIEB4z1ZVD9v/X0vYdtMLHsmAbFEGgSWHeIcb4dHbD99FNhVn45xfUiliIZhPcJ8wUxyNKXfyY4+AcZRqLKdoeF5Lzk3DFy13Ey2lrZeTGW+47pf3R5VtkQ1Fzy0LQtdskvkygQd8GJhHdeNppcfd9myv9vwAzmw0SQplbmRzdHJlYW0KZW5kb2JqCjI3IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjQ5ID4+CnN0cmVhbQp4nE1RSYoDMAy75xX6QCFek7ynQ5lD5//Xyg6FOQQJr5KTlphYCw8xhB8sPfiRIXM3/Rt+otm7WXqSydn/mOciU1H4UqguYkJdiBvPoRHwPaFrElmxvfE5LKOZc74HH4W4BDOhAWN9STK5qOaVIRNODHUcDlqkwrhrYsPiWtE8jdxu+0ZmZSaEDY9kQtwYgIgg6wKyGCyUNjYTMlnOA+0NyQ1aYNepG1GLgiuU1gl0olbEqszgs+bWdjdDLfLgqH3x+mhWl2CF0Uv1WHhfhT6YqZl27pJCeuFNOyLMHgqkMjstK7V7xOpugfo/y1Lw/cn3+B2vD838XJwKZW5kc3RyZWFtCmVuZG9iagoyOCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDk0ID4+CnN0cmVhbQp4nEWNwRHAIAgE/1RBCQoK2k8mk4f2/40QMnxg5w7uhAULtnlGHwWVJl4VWAdKY9xQj0C94XItydwFD3Anf9rQVJyW03dpkUlVKdykEnn/DmcmkKh50WOd9wtj+yM8CmVuZHN0cmVhbQplbmRvYmoKMjkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzNDEgPj4Kc3RyZWFtCnicRVJLbkQxCNu/U3CBSOGXkPO0qrqY3n9bm0zVzeAJYGx4y1OmZMqwuSUjJNeUT30iQ6ym/DRyJCKm+EkJBXaVj8drS6yN7JGoFJ/a8eOx9Eam2RVa9e7Rpc2iUc3KyDnIEKGeFbqye9QO2fB6XEi675TNIRzL/1CBLGXdcgolQVvQd+wR3w8droIrgmGway6D7WUy1P/6hxZc7333YscugBas577BDgCopxO0BcgZ2u42KWgAVbqLScKj8npudqJso1Xp+RwAMw4wcsCIJVsdvtHeAJZ9XehFjYr9K0BRWUD8yNV2wd4xyUhwFuYGjr1wPMWZcEs4xgJAir3iGHrwJdjmL1euiJrwCXW6ZC+8wp7a5udCkwh3rQAOXmTDraujqJbt6TyC9mdFckaM1Is4OiGSWtI5guLSoB5a41w3seJtI7G5V9/uH+GcL1z26xdL7ITECmVuZHN0cmVhbQplbmRvYmoKMzAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNjQgPj4Kc3RyZWFtCnicRZDHcQUxDEPvqgIlMIAK9azH8w/r/q+G9NNBehhCDGJPwrBcV3FhdMOPty0zDX9HGe7G+jJjvNVYICfoAwyRiavRpPp2xRmq9OTVYq6jolwvOiISzJLjq0AjfDqyx5O2tjP9dF4f7CHvE/8qKuduYQEuqu5A+VIf8dSP2VHqmqGPKitrHmraV4RdEUrbPi6nMk7dvQNa4b2Vqz3a7z8edjryCmVuZHN0cmVhbQplbmRvYmoKMzEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3MiA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlxAvqmJuUIuF0gMxMoBswyAtCWcgohngJggbRDFIBZEsZmJGUQdnAGRy+BKAwAl2xbJCmVuZHN0cmVhbQplbmRvYmoKMzIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA0NyA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlyWEFYuF0wsB8wC0ZZwCiKewZUGALlnDScKZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI1OCA+PgpzdHJlYW0KeJxFkUtyBCAIRPeegiOA/OQ8k0plMbn/Ng3OZDZ2l6j9hEojphIs5xR5MH3J8s1ktul3OVY7GwUURSiYyVXosQKrO1PEmWuJautjZeS40zsGxRvOXTmpZHGjjHVUdSpwTM+V9VHd+XZZlH1HDmUK2KxzHGzgym3DGCdGm63uDveJIE8nU0fF7SDZ8AcnjX2VqytwnWz20UswDgT9QhOY5ItA6wyBxs1T9OQS7OPjdueBYG95EUjZEMiRIRgdgnadXP/i1vm9/3GGO8+1Ga4c7+J3mNZ2x19ikhVzAYvcKajnay5a1xk63pMzx+Sm+4bOuWCXu4NM7/k/1s/6/gMeKWb6CmVuZHN0cmVhbQplbmRvYmoKMzQgMCBvYmoKPDwgL0JCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzkKL1N1YnR5cGUgL0Zvcm0gL1R5cGUgL1hPYmplY3QgPj4Kc3RyZWFtCnic4zI0MFMwNjVVyOUyNzYCs3LALCNzIyALJItgQWQzuNIAFfMKfAplbmRzdHJlYW0KZW5kb2JqCjM1IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTYzID4+CnN0cmVhbQp4nEWQOxIDIQxDe06hI/gjAz7PZjIpNvdvY9hsUsDTWCCDuxOC1NqCieiCh7Yl3QXvrQRnY/zpNm41EuQEdYBWpONolFJ9ucVplXTxaDZzKwutEx1mDnqUoxmgEDoV3u2i5HKm7s75Q3D1X/W/Yt05m4mBycodCM3qU9z5NjuiurrJ/qTH3KzXfivsVWFpWUvLCbedu2ZACdxTOdqrPT8fCjr2CmVuZHN0cmVhbQplbmRvYmoKMzYgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMTggPj4Kc3RyZWFtCnicPVC5jQQxDMtdhRpYwHrtqWcWi0um//RI+fYi0RZFUio1mZIpL3WUJVlT3jp8lsQOeYblbmQ2JSpFL5OwJffQCvF9ieYU993VlrNDNJdoOX4LMyqqGx3TSzaacCoTuqDcwzP6DW10A1aHHrFbINCkYNe2IHLHDxgMwZkTiyIMSk0G/65yj59eixs+w/FDFJGSDuY1/1j98nMNr1OPJ5Fub77iXpypDgMRHJKavCNdWLEuEhFpNUFNz8BaLYC7t17+G7QjugxA9onEcZpSjqG/a3Clzy/lJ1PYCmVuZHN0cmVhbQplbmRvYmoKMzcgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA4MyA+PgpzdHJlYW0KeJxFjLsNwDAIRHumYAR+JvY+UZTC3r8NECVuuCfdPVwdCZkpbjPDQwaeDCyGXXGB9JYwC1xHUI6d7KNh1b7qBI31plLz7w+Unuys4obrAQJCGmYKZW5kc3RyZWFtCmVuZG9iagozOCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDUxID4+CnN0cmVhbQp4nDM2tFAwUDA0MAeSRoZAlpGJQoohF0gAxMzlggnmgFkGQBqiOAeuJocrgysNAOG0DZgKZW5kc3RyZWFtCmVuZG9iagozOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE2MCA+PgpzdHJlYW0KeJxFkDkSAzEIBHO9gidIXIL3rMu1wfr/qQfWR6LpAjQcuhZNynoUaD7psUahutBr6CxKkkTBFpIdUKdjiDsoSExIY5JIth6DI5pYs12YmVQqs1LhtGnFwr/ZWtXIRI1wjfyJ6QZU/E/qXJTwTYOvkjH6GFS8O4OMSfheRdxaMe3+RDCxGfYJb0UmBYSJsanZvs9ghsz3Ctc4x/MNTII36wplbmRzdHJlYW0KZW5kb2JqCjQwIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzM0ID4+CnN0cmVhbQp4nC1SS3LFIAzbcwpdoDP4B+Q86XS6eL3/tpKTRUYOYPQx5YaJSnxZILej1sS3jcxAheGvq8yFz0jbyDqIy5CLuJIthXtELOQxxDzEgu+r8R4e+azMybMHxi/Zdw8r9tSEZSHjxRnaYRXHYRXkWLB1Iap7eFOkw6kk2OOL/z7Fcy0ELXxG0IBf5J+vjuD5khZp95ht0656sEw7qqSwHGxPc14mX1pnuToezwfJ9q7YEVK7AhSFuTPOc+Eo01ZGtBZ2NkhqXGxvjv1YStCFblxGiiOQn6kiPKCkycwmCuKPnB5yKgNh6pqudHIbVXGnnsw1m4u3M0lm675IsZnCeV04s/4MU2a1eSfPcqLUqQjvsWdL0NA5rp69lllodJsTvKSEz8ZOT06+VzPrITkVCaliWlfBaRSZYgnbEl9TUVOaehn++/Lu8Tt+/gEsc3xzCmVuZHN0cmVhbQplbmRvYmoKNDEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3MCA+PgpzdHJlYW0KeJwzMzZTMFCwMAISpqaGCuZGlgophlxAPoiVywUTywGzzCzMgSwjC5CWHC5DC2MwbWJspGBmYgZkWSAxILoyuNIAmJoTAwplbmRzdHJlYW0KZW5kb2JqCjQyIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzIwID4+CnN0cmVhbQp4nDVSS24FMQjbzym4QKXwT87zqqqLvvtvaxO9FUwwYOMpL1nSS77UJdulw+RbH/clsULej+2azFLF9xazFM8tr0fPEbctCgRREz1YmS8VItTP9Og6qHBKn4FXCLcUG7yDSQCDavgHHqUzIFDnQMa7YjJSA4Ik2HNpcQiJciaJf6S8nt8nraSh9D1Zmcvfk0ul0B1NTugBxcrFSaBdSfmgmZhKRJKX632xQvSGwJI8PkcxyYDsNoltogUm5x6lJczEFDqwxwK8ZprVVehgwh6HKYxXC7OoHmzyWxOVpB2t4xnZMN7LMFNioeGwBdTmYmWC7uXjNa/CiO1Rk13DcO6WzXcI0Wj+GxbK4GMVkoBHp7ESDWk4wIjAnl44xV7zEzkOwIhjnZosDGNoJqd6jonA0J6zpWHGxx5a9fMPVOl8hwplbmRzdHJlYW0KZW5kb2JqCjQzIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTggPj4Kc3RyZWFtCnicMza0UDCAwxRDrjQAHeYDUgplbmRzdHJlYW0KZW5kb2JqCjQ0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTMzID4+CnN0cmVhbQp4nEWPSw4EIQhE95yijsDHH+dxMumFc//tgJ1uE2M9hVSBuYKhPS5rA50VHyEZtvG3qZaORVk+VHpSVg/J4Iesxssh3KAs8IJJKoYhUIuYGpEtZW63gNs2DbKylVOljrCLozCP9rRsFR5folsidZI/g8QqL9zjuh3Ipda73qKLvn+kATEJCmVuZHN0cmVhbQplbmRvYmoKNDUgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzNDAgPj4Kc3RyZWFtCnicNVI5bgQxDOv9Cn0ggG7b79kgSJH8vw2p2RQDcXRSlDtaVHbLh4VUtex0+bSV2hI35HdlhcQJyasS7VKGSKi8ViHV75kyr7c1ZwTIUqXC5KTkccmCP8OlpwvH+baxr+XIHY8eWBUjoUTAMsXE6BqWzu6wZlt+lmnAj3iEnCvWLcdYBVIb3TjtiveheS2yBoi9mZaKCh1WiRZ+QfGgR4199hhUWCDR7RxJcIyJUJGAdoHaSAw5eyx2UR/0MygxE+jaG0XcQYElkpg5xbp09N/40LGg/tiMN786KulbWllj0j4b7ZTGLDLpelj0dPPWx4MLNO+i/OfVDBI0ZY2Sxget2jmGoplRVni3Q5MNzTHHIfMOnsMZCUr6PBS/jyUTHZTI3w4NoX9fHqOMnDbeAuaiP20VBw7is8NeuYEVShdrkvcBqUzogen/r/G1vtfXHx3tgMYKZW5kc3RyZWFtCmVuZG9iago0NiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI1MSA+PgpzdHJlYW0KeJwtUUlyA0EIu88r9IRmp99jlyuH5P/XCMoHBg2LQHRa4qCMnyAsV7zlkatow98zMYLfBYd+K9dtWORAVCBJY1A1oXbxevQe2HGYCcyT1rAMZqwP/Iwp3OjF4TEZZ7fXZdQQ7F2vPZlByaxcxCUTF0zVYSNnDj+ZMi60cz03IOdGWJdhkG5WGjMSjjSFSCGFqpukzgRBEoyuRo02chT7pS+PdIZVjagx7HMtbV/PTThr0OxYrPLklB5dcS4nFy+sHPT1NgMXUWms8kBIwP1uD/VzspPfeEvnzhbT43vNyfLCVGDFm9duQDbV4t+8iOP7jK/n5/n8A19gW4gKZW5kc3RyZWFtCmVuZG9iago0NyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE3NCA+PgpzdHJlYW0KeJxNkEkOQyEMQ/ecwheohDPA5zy/qrpo77+tQwd1gfzkIHA8PNBxJC50ZOiMjiubHOPAsyBj4tE4/8m4PsQxQd2iLViXdsfZzBJzwjIxArZGydk8osAPx1wIEmSXH77AICJdj/lW81mT9M+3O92PurRmXz2iwInsCMWwAVeA/brHgUvC+V7T5JcqJWMTh/KB6iJSNjuhELVU7HKqirPdmytwFfT80UPu7QW1IzzfCmVuZHN0cmVhbQplbmRvYmoKNDggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3NSA+PgpzdHJlYW0KeJwztTRSMFAwNgASpmZGCqYm5gophlxAPoiVy2VoZApm5XAZWZopWFgAGSZm5lAhmIYcLmNTc6ABQEXGpmAaqj+HK4MrDQCVkBLvCmVuZHN0cmVhbQplbmRvYmoKNDkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA4OSA+PgpzdHJlYW0KeJw1jLsNgDAMRHtP4RHiv9kHIQrYv8VJcGPf3ZNeUuJA5ToRjqaBJ0H1mV4g2ekBVkXiUUnM/029qUVTz6btq00EJzOO9XUcqJrTetBaKG2TFt5wfQCcHe0KZW5kc3RyZWFtCmVuZG9iago1MCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE0MSA+PgpzdHJlYW0KeJw9j8EOwzAIQ+/5Cv9ApNgpoXxPp2qH7v+vI0u7C3oCY4yF0NAbqprDhmCb48XSJVRr+BTFQCU3yJlgDqWk0h1HkXpiOBhcHrQbjuKx6PoRu5JmfdDGQrolaIB7rFNp3KZxE8QdNQXqKeqco7wQuZ+pZ9g0kt00s5JzuA2/e89T1/+nq7zL+QW9dy7+CmVuZHN0cmVhbQplbmRvYmoKNTEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMTUgPj4Kc3RyZWFtCnicNVE5DgMhDOz3Ff5AJIwveE+iKM3+v82M0VYewVyGtJQhmfJSk6gh5VM+epkunLrc18xqNOeWtC1zgLi2vC+tksCJZoiDwWmYuAGaPAFD19GoUUMXHtDUpVMosNwEPoq3bg/dY7WBl7Yh54kgYigZLEHNqUUTFm3PJ6Q1v16LG96X7d3IU6XGlhiBBgFWOBzX6NfwlT1PJtF0FTLUqzXLGAkTRSI8+Y6m1RPrWjTSMhLUxhGsagO8O/0wTgAAE3HLAmSfSpSz5MRvsfSzBlf6/gGfR1SWCmVuZHN0cmVhbQplbmRvYmoKMTUgMCBvYmoKPDwgL0Jhc2VGb250IC9EZWphVnVTYW5zIC9DaGFyUHJvY3MgMTYgMCBSCi9FbmNvZGluZyA8PAovRGlmZmVyZW5jZXMgWyAzMiAvc3BhY2UgNDYgL3BlcmlvZCA0OCAvemVybyAvb25lIC90d28gL3RocmVlIC9mb3VyIC9maXZlIC9zaXggL3NldmVuCi9laWdodCA2NyAvQyA3MSAvRyA4NCAvVCA5NyAvYSAvYiAvYyAvZCAvZSAvZiAvZyAvaCAvaSAxMDggL2wgL20gL24gL28gMTE0Ci9yIC9zIC90IC91IC92IC93IDEyMSAveSBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9DIDE3IDAgUiAvRyAxOCAwIFIgL1QgMTkgMCBSIC9hIDIwIDAgUiAvYiAyMSAwIFIgL2MgMjIgMCBSIC9kIDIzIDAgUgovZSAyNCAwIFIgL2VpZ2h0IDI1IDAgUiAvZiAyNiAwIFIgL2ZpdmUgMjcgMCBSIC9mb3VyIDI4IDAgUiAvZyAyOSAwIFIKL2ggMzAgMCBSIC9pIDMxIDAgUiAvbCAzMiAwIFIgL20gMzMgMCBSIC9uIDM1IDAgUiAvbyAzNiAwIFIgL29uZSAzNyAwIFIKL3BlcmlvZCAzOCAwIFIgL3IgMzkgMCBSIC9zIDQwIDAgUiAvc2V2ZW4gNDEgMCBSIC9zaXggNDIgMCBSIC9zcGFjZSA0MyAwIFIKL3QgNDQgMCBSIC90aHJlZSA0NSAwIFIgL3R3byA0NiAwIFIgL3UgNDcgMCBSIC92IDQ4IDAgUiAvdyA0OSAwIFIgL3kgNTAgMCBSCi96ZXJvIDUxIDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzQgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago1MiAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQzMzM3KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDUzCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIxMDcxIDAwMDAwIG4gCjAwMDAwMjA4MDggMDAwMDAgbiAKMDAwMDAyMDg0MCAwMDAwMCBuIAowMDAwMDIwOTgwIDAwMDAwIG4gCjAwMDAwMjEwMDEgMDAwMDAgbiAKMDAwMDAyMTAyMiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDcgMDAwMDAgbiAKMDAwMDAwOTYyNyAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDk2MDYgMDAwMDAgbiAKMDAwMDAxOTM1NyAwMDAwMCBuIAowMDAwMDE5MTU3IDAwMDAwIG4gCjAwMDAwMTg2OTMgMDAwMDAgbiAKMDAwMDAyMDQxMCAwMDAwMCBuIAowMDAwMDA5NjQ3IDAwMDAwIG4gCjAwMDAwMDk5NTUgMDAwMDAgbiAKMDAwMDAxMDI3NSAwMDAwMCBuIAowMDAwMDEwNDEzIDAwMDAwIG4gCjAwMDAwMTA3OTMgMDAwMDAgbiAKMDAwMDAxMTExMCAwMDAwMCBuIAowMDAwMDExNDE1IDAwMDAwIG4gCjAwMDAwMTE3MTkgMDAwMDAgbiAKMDAwMDAxMjA0MSAwMDAwMCBuIAowMDAwMDEyNTA5IDAwMDAwIG4gCjAwMDAwMTI3MTggMDAwMDAgbiAKMDAwMDAxMzA0MCAwMDAwMCBuIAowMDAwMDEzMjA2IDAwMDAwIG4gCjAwMDAwMTM2MjAgMDAwMDAgbiAKMDAwMDAxMzg1NyAwMDAwMCBuIAowMDAwMDE0MDAxIDAwMDAwIG4gCjAwMDAwMTQxMjAgMDAwMDAgbiAKMDAwMDAxNDQ1MSAwMDAwMCBuIAowMDAwMDE0NjIzIDAwMDAwIG4gCjAwMDAwMTQ4NTkgMDAwMDAgbiAKMDAwMDAxNTE1MCAwMDAwMCBuIAowMDAwMDE1MzA1IDAwMDAwIG4gCjAwMDAwMTU0MjggMDAwMDAgbiAKMDAwMDAxNTY2MSAwMDAwMCBuIAowMDAwMDE2MDY4IDAwMDAwIG4gCjAwMDAwMTYyMTAgMDAwMDAgbiAKMDAwMDAxNjYwMyAwMDAwMCBuIAowMDAwMDE2NjkzIDAwMDAwIG4gCjAwMDAwMTY4OTkgMDAwMDAgbiAKMDAwMDAxNzMxMiAwMDAwMCBuIAowMDAwMDE3NjM2IDAwMDAwIG4gCjAwMDAwMTc4ODMgMDAwMDAgbiAKMDAwMDAxODAzMCAwMDAwMCBuIAowMDAwMDE4MTkxIDAwMDAwIG4gCjAwMDAwMTg0MDUgMDAwMDAgbiAKMDAwMDAyMTEzMSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDUyIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA1MyA+PgpzdGFydHhyZWYKMjEyODgKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:33:37.105241\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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IC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE0IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDEzIDAgUiA+PgplbmRvYmoKMTQgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxMyAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNiAwIG9iago8PCAvQyAxNyAwIFIgL0cgMTggMCBSIC9MIDE5IDAgUiAvUiAyMCAwIFIgL1UgMjEgMCBSIC9hIDIyIDAgUiAvYiAyMyAwIFIKL2MgMjQgMCBSIC9kIDI1IDAgUiAvZSAyNiAwIFIgL2VpZ2h0IDI3IDAgUiAvZiAyOCAwIFIgL2ZpdmUgMjkgMCBSCi9mb3VyIDMwIDAgUiAvZyAzMSAwIFIgL2ggMzIgMCBSIC9pIDMzIDAgUiAvbCAzNCAwIFIgL20gMzUgMCBSIC9uIDM3IDAgUgovbyAzOCAwIFIgL29uZSAzOSAwIFIgL3BlcmlvZCA0MCAwIFIgL3IgNDEgMCBSIC9zIDQyIDAgUiAvc2V2ZW4gNDMgMCBSCi9zaXggNDQgMCBSIC9zcGFjZSA0NSAwIFIgL3QgNDYgMCBSIC90aHJlZSA0NyAwIFIgL3R3byA0OCAwIFIgL3UgNDkgMCBSCi92IDUwIDAgUiAvdyA1MSAwIFIgL3kgNTIgMCBSIC96ZXJvIDUzIDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzYgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago1NCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQzMzQyKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDU1CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIwOTk2IDAwMDAwIG4gCjAwMDAwMjA3MzMgMDAwMDAgbiAKMDAwMDAyMDc2NSAwMDAwMCBuIAowMDAwMDIwOTA1IDAwMDAwIG4gCjAwMDAwMjA5MjYgMDAwMDAgbiAKMDAwMDAyMDk0NyAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDcgMDAwMDAgbiAKMDAwMDAwODk5MSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDg5NzAgMDAwMDAgbiAKMDAwMDAxOTI2MiAwMDAwMCBuIAowMDAwMDE5MDYyIDAwMDAwIG4gCjAwMDAwMTg1ODYgMDAwMDAgbiAKMDAwMDAyMDMxNSAwMDAwMCBuIAowMDAwMDA5MDExIDAwMDAwIG4gCjAwMDAwMDkzMTkgMDAwMDAgbiAKMDAwMDAwOTYzOSAwMDAwMCBuIAowMDAwMDA5NzcyIDAwMDAwIG4gCjAwMDAwMTAwNzcgMDAwMDAgbiAKMDAwMDAxMDMwNiAwMDAwMCBuIAowMDAwMDEwNjg2IDAwMDAwIG4gCjAwMDAwMTEwMDMgMDAwMDAgbiAKMDAwMDAxMTMwOCAwMDAwMCBuIAowMDAwMDExNjEyIDAwMDAwIG4gCjAwMDAwMTE5MzQgMDAwMDAgbiAKMDAwMDAxMjQwMiAwMDAwMCBuIAowMDAwMDEyNjExIDAwMDAwIG4gCjAwMDAwMTI5MzMgMDAwMDAgbiAKMDAwMDAxMzA5OSAwMDAwMCBuIAowMDAwMDEzNTEzIDAwMDAwIG4gCjAwMDAwMTM3NTAgMDAwMDAgbiAKMDAwMDAxMzg5NCAwMDAwMCBuIAowMDAwMDE0MDEzIDAwMDAwIG4gCjAwMDAwMTQzNDQgMDAwMDAgbiAKMDAwMDAxNDUxNiAwMDAwMCBuIAowMDAwMDE0NzUyIDAwMDAwIG4gCjAwMDAwMTUwNDMgMDAwMDAgbiAKMDAwMDAxNTE5OCAwMDAwMCBuIAowMDAwMDE1MzIxIDAwMDAwIG4gCjAwMDAwMTU1NTQgMDAwMDAgbiAKMDAwMDAxNTk2MSAwMDAwMCBuIAowMDAwMDE2MTAzIDAwMDAwIG4gCjAwMDAwMTY0OTYgMDAwMDAgbiAKMDAwMDAxNjU4NiAwMDAwMCBuIAowMDAwMDE2NzkyIDAwMDAwIG4gCjAwMDAwMTcyMDUgMDAwMDAgbiAKMDAwMDAxNzUyOSAwMDAwMCBuIAowMDAwMDE3Nzc2IDAwMDAwIG4gCjAwMDAwMTc5MjMgMDAwMDAgbiAKMDAwMDAxODA4NCAwMDAwMCBuIAowMDAwMDE4Mjk4IDAwMDAwIG4gCjAwMDAwMjEwNTYgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1NCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTUgPj4Kc3RhcnR4cmVmCjIxMjEzCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:33:41.454585\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDEwNzEuMTk0NjM2MjMwMyAyMTYuNjY1NjI1IF0gL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDggMCBSCi9UeXBlIC9QYWdlID4+CmVuZG9iago5IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTIgMCBSID4+CnN0cmVhbQp4nNVdTZMjt5W881fwKB0EAXj4PFrrXUU4fLGs3T1s+DArje3RTo9CX1b4328mUGQBINia5rB7jHDI0cwhUZVZKOABSDyY43eHz39njn/76Yj/O+rjd/jvV/z9JT8fND49HIyORpnsggR8ftt9tiaoEHywHv+g+49/Pxz+etAqmxhc1D6l4/jBZW1y0DEdf+TFv7z4wvnDYfj24RCdElzGWZXrBR8O1kSVbczONPDbFjYxqXTCtxI6rNzzD8eLwo0TFbRFGbglfBSv8vHH18f/Pr47fv47WwX8A/77Dv8VAQ+f//71P9588/qrL784fvPTIRmltbGQrL3jHe3u4vDnw5+OP5wK1sp4PJxT2eXjlxt6+OFgoNxnGv8UtArOpKATyj9ar4xmcd88HL74+vj5f4C9OX7910NWeEoxhxQ9fvn1t4f/OX6ieRufHv9y/PoPh3//GgIAMCxYN39988BSPvv96+9e/dcvf3717qfPHt68++Wn4++/P/7p8Kdyx/fXzUhSwQabYydcA99BOWOtSpGl6eztdel0J5jeBXs29jko6/G71LPf4XuwT1p5zdJslvS+7E3LviktR2WchY54a+0xq9iWY/tyvvzx1bfHh1d/e/fm51++ff38cr5/m3Gbkj6qhJ+jbsbks8RHNXwxulErZ3PItqe7w7fStbnQDayMeB1cNDk/wrh86cVYox4mPoLcs97hD2SdRUXj0BVmyekR1iT9cqwN3l7xMdnY027wD+RtLDr/mIxk43N8hHh80cdtPJpwa1J2A/Edv5m4rcQduhxnrQ6C3vMR4kaPj/yHA8v5jCUiQJJae9BEiumb2aF5/Lfvf3n38wcqKJVpDCnY6LxBICUMqKx4m/EPYo0v/+ZLDKa80SYlnRBl1Tiw+e2x/e1h+O3hgG4keev0EOJ4ZbVjDNG2rz0aLUISol0JJxQx2Rf/6tQnJEE9K+99ML6n3qEZ0Zq9EOSErkB9QvLhgPqddbQmd9QbFPXfJuukF6RBF6A+IwnqCd0hfuN66jsqaGEMaI6CnNEVqE9IcqRoVTDa2dRxb2FRuHs0faMkZ3QB8lOaZB/wwWqxA/sdBgGEC9FeiHKGl6A/4Qn6xigxEroh7tsOdip6p9OoSgOvQH/Gk/Q9pz7sMJBo4aCC6NK4tWXs6BLkJyxB3nKaICTXd3UN7LUK3rkwaNLCK9Cf8SR93DFQlwf6ZxgjQwPYhUGVBl6C/oQn6WPAa7X2MtA/wyEpDBdSlF6VFl6C/oQn56IQtkQ8x6HX22FEOBE/TIMqLbwC/RlP0o/KWbFh6PZ22IhReN5Jci9Lhy8hwIQpBHAGz9GlMHR8O2wCRpUJMe6gSwOvQH/Gk/SDMja4OHR8O2w0huNJx2AHWVp8CQEmTCEAOjEXwWPo/HYYlEAii/S6tPAK9Gc8Sd+pZHOIQ+e3w1yY4P27XpUWXoL+hCfpZ7zEaNWHzm+HvVWStOjYq9LCS9Cf8AT9IMqLTWno/Bo4oZZ7calXpYVXoD/jSfpRJcYyQ+fXwB7XQJQTB1UaeAn6E56gH62yEnQeur4GRhunk5FBlB1dgfyMJckHheA16qHja2A87lx7vaaIM7gE9QlHUE9aZfRceujyGliU1cGaC0FO6ArkZyxJ3iubNf43kN9hhLgOb3e+0OQML0F/wpP0M9dBvBk6vB5OTrQZVGngJehPeHLtXVTm3Q0dXg9HVPd8qcoJXoH+jCfpJyXORzt0eD2cIpq4S1VO8BL0JzwfDpaztjli+NZbjHr4tHwzFLLQqs6UJ+lHNGEpy8C+RaOImWhygpcgf8mSi8pGOc7b9H3eCIdo3YUmJ/RW8v06uPLHXw/X6PRSfPXlsV8uH1dpkzdxW6+LNtnSRetoaKyqaxReG50L7EKSWq3JLYutU/ro0qIv8Z3OKpooXM3ySqOyu7jNdeMykgxhiRFvRIH5RkRnhVPgOmdrywjBQAcMC5wj7GMmi23KGLR42wl3kn2oZQd0ruJxIZdxq97k0umYpKzzPmVOMGfrUg3CrOXoI6Ix9vgdSsknOJX7OnoMSqBeOs3eGocmOxwDJA8xpjKAtR7XZzHH4BViWloVCQeFIpLHcJd3YnI+zfZpgnjgaPq1LxcUrVBhGAQmR8/edh9ilbZ4bfwxByWCh+S2CTMRPDBOmOAiGhHECUeTghp+LKJZkW2CDSpriyuXCTb8YSVtE08WQRgUMiCUrEat3fBsIS5xDM+illQepySEbfixPTJ6FVTN2qZLRiibLJ65CXgWVkss94MqkkTTDcMKgj7PpfKMUEfwAPBr4BrBQMKDKTiGQAgIwjFrhUriquou4oP2GtpoFULwuX6ZV7LeyTGighprNhhVDvUJP8XgAupyVrlOMkjWAfzwjFBRqzDeQD1OvRwDnjieSh2UeRaIyp2OGJ3iOYtO20g9J9Q+c/SejzbWYBYfgiCaBexAJtdnhPqEdxe19YjS2PIUpXzEO423BhXLoObnjThqGfRO3rF2RsmmFBygjeBVK1UZ/+rrdBHrnrOZk654Tj5AyAKjKueUORkBrSl13mD8kq9OROBZI7DAKQfRoYxFU0AsUm4uBNR1tJJSYJOcPg3SnIuGKxyo3j6gOSpwVs54tGpl7CZoGcr1IghYtG0l5s3QqN5GZNPvxZR2I6FWlNbP4CXQOqKVKkueoFIrVMQzoo2qLAQ7cbYOFxMqry3WRzygFKQumybUD4uXsvpBM/6WSSucxFxFexeQZauK2rr1Er9pGJy5hlHi1Ef4cNV7jF88yYx4edVHy9Yg9d72T7yUaPEjnhIdm6gIp6KuO5Pevvrn6x9/Ulr9+vrN3/7eeZQ2O/lTfN/VdD76vy+s473/G50VXgw0XL2EDu0w6qnVfYDWwJ1WTSmXPnDWkvMXeB/5xg77MKtbQt0TXvQ+wmzgO3h7UT3wgopD+4UO4ummcHsHU/gzSogGG5GFCb05vIHvIKGUySqUFhAx3G4Ofz4V0LOy93epr0gNfAcV0DuohK42obvU762CvWISR7iqJASpvD/IJf58uj6thbnx/UQvrPe7v805/iIScK0ekXNwvQQ7fLMEIN1IgGglZry6iMMQhj7iJp/5ql9Eiew4kcPAqVNih++kBMLiiAESbdbhMaO1+2hK0MmCGDm5QYoGv5MWHOglNMLeYxD4iBbh42mBmDtjUJHsoMWO30sLEEiI1bLj+tkjYqSPJ0ZkbO4wlB7E2PHbxTCdGGiAEjQWCRw63ejKtxjrN6Lc7s6/UV1Obr3njM5vTm49OjHGuDkj3A9hMDFYcHaW491+dquHIyLTUMLmrpATfJ7eWkCGGV/KEDHWz8Nq9oCGPNPmBC8lwiVbjkAsR/gu9Q72Ec6+VoW+jA1dSIQpXaoQlCtj4kGFHk628B0L2eClZJjwhQxGqyQuDFP+AxxRTrpU5wSvJMOML2XAgCcN25DfjnDKJfobytjQpUSYsKUIWXmJeTA8tDA6To3O2A3aNPBSMkz4QgbLOc3sB/NDC4OvMHS/VOcEryTDjC9l4CKLGTb2NSiDKPQH7lKbE7yUCJdsoYHgvrONZugqG1iUBbuyotJpc0ZXEmFGlypwNc3JYIloYVFeZ+MuxTnDS8kw4QsZnFE2g1pvjWhhhwZAF793W8aOriTCjC1F8Ao37e3QUTawL6s5WQZtGngpGSZ8KUPGQDcjJBxk2OGofDS29hFNIQ28lAwTvpyJd0o8l/x6GXbYCzeBhmI3aApp4ZVkmPGlDIkrzzL0Emc0Bq7k2xo27UW08FIiXLKFBkGK6cENIuwwbREuI3CWTpwOXkmGGWHqEJXT3ruhs9zhgMuiAujQy9PCS8kw4QsZolERLZ0fOssGzngPPErt1WnhlWSY8aUMgU54/Msgww5Xc4Yf1WngpWSY8IUMCe940BKG7rKB0SpmqS1kU8aOriTCjC1FcHimJoehs2xgEDBJQh60aeClZJjwpQxZmSDjG7GjQkeXLb6rXpszvJQIl2yhQcbIyHgTh86ygZkvJOniguy0OaMriTCjSxWiSoHGy0GFHUbvSH9duhDnDC8lw4Tvw8Fpqyze79R3lS3MiSUdistxUOcELyTDlC9lCMqHnFMcZOhgmvzsoE4DLyXDhC9tPXQ0G5/7rnKATzPvQyELTshP+VIGryy95n6QoYNjlCrDoM4GLyXDhO/J5OVizoMMHRzFlcR/ozob/KEytN6AQ9mdcHw/US53J4wLy9aWBI5i6fr1aev4LG3JZRLN4dVOhnsIOJUoxWTBSSXvk0iJGh3p1rkXJnnVtBEDTsClftspkyy3oSOmtt7Gsj9PHEYcoG7KlAzu3Zg6YZWYvcBTncQdnbaWTSO3o4m+WroRlZWyafvOKThLAzjz1YbSU3mLQZ0LoewNj9ZI8SWLF5riIkIYmszRlW2w47aAaEu6mJCDKeliCIcIRUIZGVqdK3n8FLJAwiOGjtxgrNMG56zxfTq2MJKKxfsuPqjgAqokbfVF7gpHbiFIeF6J3msbt7LpQE/J5WNGIRE9btyG6qGY/fEg6r6LqknmfoPE/Qa4TBTcathwVLFshn0FQhpMQQscanljddk/AC253yBzvwGrNdqyshGDeMouo04w5yzqW9lcgUfCcTK3X+DecQVfI8XAradRG0+mCVTdCU5BEEZzA0EQ7iUpsMMNZA018AVUERvrrWDUJQZS81FodLNluwkemgJ93B33BLCr0WYbtOINNIjQwacY+utYNnO7g9VliAtZpHjmpeRjFd4Wa1ZEWy2nIR8qDCTlzojoUq1ZIIG6LK4OEPFu2jo0QsWJxpWajwoR/GnAhDfISUm1hXYwV5SEs6vvg8eDLakIJCY8ZFN2LHIDTORWjDrcyHiTYom0k7XVSy8JTyp6X/c1B49oI53icl8DUofv1DAVjynHYLZEf35L/cBGKaQUzhaTGN2kCXPWxevwEzz9V2yd15zgKHnq+Hy46innboCnWkfnV3/0Gk/x+ItGlcpBu1i3CzzB428/osff0QIZS7/RyhjQ7Eu8yDjTwJ1WTSlzj//+hWG76wdb4rxJCnzGlaIGvoc1mxE/WhX0aHw9P4rH/xklpGUQRQ270Bv4DhKiqSqJCXQWbiy71eP/fCogXFLRDBNGZ/AehwcYQwub0dpxF+gH+vvRe6BHKQV8iLf/+fR8Wsty43sZHaPt093f5u1/EQnQ+ThEeSVDfCPBDt8sgQutBKimCBzQDfnwaOrwqbf/RZRgdvOE6LGMOtoOZcfvpAVulqEahhsMwp5q738ZMRh5ZwTUYRBjx+8lBhNhGownMEBDSP0Ef39j4naSW01uN3HfKO5LzgZkDtDQW/URqdcYZXrr+1njAY2+rKqPRWzweS7gX1+EGVtqEGg2NG4UoYc3unNtlhJhQpcRpuYyaRwy0l6D55qtJMOMGGVg2gXcuQwy9PCpMgyFLFgbZnwpQ8Z4O+VhTW2EzzJM1VlKhglfyGBFOaP9sMo8wMlLmWAbCjnBK8kw40sZMGwIGAuGQYYO5ixnulTnBC8lw4QvZBDLmCMO70SHxlxcR6M0ufciLSDBhCsVCMoFL8MqcwuX1Ci5LJY0ZTToUiJM6EIFp1UyIQ/5a1uYs5VWcr4U5wSvJMOML2VgNqPkhzy2LcyAOpd0K4M4G7qUCBO2FCFzKcUMa8wtLMqKSUEutDnDS8kw4QsZPJM9mDisMbcwOoXMJFMX6pzhlWSY8aUMnK8QGbLbtrBDE8C8ZIM6DbyUDBO+kIErasO+4DPmeRiclGmR9ucNvJIAF0zJPnC5Muihk2xgJo3TXORtdGmwpQSYkOVku1FcAx4y3jZwYFHJlgXbppAWXkmGGV/KwCSEOQ2ZbxuYOQNDZJK5Xp4OX0qICWMKkTEgMs4M3eQO08CACxY3QVNICy8lw4QvZEhOiTDXYi9DA0clIVaXTVtIA68kw4wvZUh4qi4MOXFbmLVfXFlIaAtp4KVkmPCFDJxwLlnWOhV2lOkxvZTki20RDbySCBO21AA1G8UNO51aWPCXie5CmzO6lAgTug+HoOnZyU767rKFaUZytqTN7cU5wwvJMOVLGZjX1/Bsu16GHeYIWtd2sFfnDC8lw4QvZDBaOVfSznYyNDBfg3oG3iCODCfjLSDCjC1FoPOMifAHEXaYk0o6F2vfoM0JXkqGCV/KkHnqTfJhkKGDT3OsQyELTr1O+dK4wnkCWlc7FToUvIybaLPBK4kwYVvNOwk3H9wgQgdvU82DNPeZf76jbXtYTI6GDliuNNAdKlFviXC0S6ZMp2e83j5veyETbdZlHikor205H4j7mMSW7pFZu70ONQ+0A/WUt9mWaFKsqaQRVCbZpiByoJu0BpdWSvZqz9zaMeUt+4DTkips6FLKoSSsD97WFNM+OAWyzNXO3fjom4tflsNak8TQKJ55dG8uG+4IJ8lGyhmnMcTT/EDEzXpoVg/6itsVE495Zk5tX7YuumKXJZx0NDkWX7GWmhLDM+l2YD76MpDOzlfqkfnSs0VPGbyKNofSOhKO0duabD6bmOJpFOqE2eAzhfLG1i9bFZ2IHUzYeFr4gPvi6YTMS5FYo4hzSstLkGKFgcSmPkrmtTdGcAcJwxtEdMZtQz6OcHRxW4uJcfs2ejeLwj3TfAY8hzpxBDhAZFRvn2lqt1IHTKhUgloRma7fa+3q44mpJEvQqSirg3i7Da9El8rKnP+II+qETDJMkk0/Pkdd0fi6uJdoAsdDcnUnrTMlcbwvx7DHaOoUFuqMrt8OtINvO07B/RzEOzxYnnfgeBKAbAOcTNt3qhszefRDUSQbur6zLhU24rYqGbw42gSdakyI5q0ceR9Konw0fSUMwKO06RQdoCGsSw/Oxb0PtSlsq9Y5SKHCdjPjbd4OZo80uV+2L3iMj8FPsFVfcdRdM9+i5KnZ7uGqjZdG7Ke69uZXf/QaT7FVexM48108Rk/xVLuP6KkOOfHcDbZprYYJH4K+yNTRwJ1QTSlzT/X+hdLa3dInXTGo4eXkmdFe92F2A9/DDpvRPqL5ttzpEG/wVPs7mKqfUcPit4syJE5v4DtoiI4fRbC0Mp55X1P1hdfxGWVA6+sTeqS+BWrge8iAIjjfJfr9neVt9XluEVLJLoy769c9GvgOIqBDwfuE0lw57/g9ZfD6ir880o5rXEq55uv9AI/58wn7tJb2xnaKHu397m/zmL+IBNx1hygdcWgnwQ7fLAEi6UYCxMyoE8ZhMIJw7DfyQX8UJdAaItpH8Gt7KRr8TlrwaJdgnTgn8enJ9F9GDOEQRpjPpxdjx+8lhvAkJZ4Vhtj7sfdDPp4YgdtxLedAezF2/F5iBMvtzdFzL3V4yu6DxnAfeCLVXubthvsbxX3BORw0KYj06XLpIwWdFYbZaUgFO8K0hRS4L2SDz/M4K8gw4cvY23Fjjxv8AdfguWgryTAjRhl4Yp7Wgz/gKjxVZykZJsQgg8UHMWFIBzvAPJ8uX6pzgleSYcaXMkRlk9ghHewAR47OJ+ps8FIyTPhCBnS7gadQ9ZP9LcwlHq/L/GNbSAOvJMOML2XwnOke1kIblHk2dDUIDdqc4KVEuGTLoxPxRUl6GPm0sJTkjiURRafNGV1JhBldqsAp5BwGd0ALCzNw5JK+rBfnDC8lw4QvZchKO2PHN6KBPfO+1KNU20IaeCkZJnw55cYFF4QBQ2fZwImBe30lmjJ2dCURZmzrvCOesBuSwTYwTz421pTUTE0hLbyUDBO+kAGDMvR3enAHNDBXJnNZkmqK2MGVJJhxpQSBxw+HUYEzyjzAGDcNurTwUiJcsoUGXMjNQ/Kgtx2MxhBFxV6aHVxJghlZasDDp3UKQyfZwMJUYCVPflvGji4lwoQtRcjFEBGHLrKBRdlo4kSbM7yUDBO+kIFr9d7pIQ1sC/PM9ZiKb7JX5wyvJMOML2VIyqKxG9LAtjDT3jpfsuz16pzhpWSY8OXiHPP70fDQy9DDaAJKxr6hkBO8kgwzvpQhqMxDQAcVziiP3LKxrKu0RTTwUiJcsn04JG2UDdoNOWAH+DzR1JWx4DzTlC5VYLJQq4cUsCO8TTaPhaw3Bz3lSxkyPpy9hrsMV+CpaEvJMCEGGYxTwjPae7f5AJ+mF4dCFpx1nPKlDEmFUJLM9jJ08KkRGNVZsG2Y8YUMlpOoyZu+s7wGz0VbSYYZMcpA66ceK8MVdKrNUiJc8qKfxzDVNAcFHd0r8FSblUSY8aIKgYmrxQ6d5QCfOsuhkDt1lnc03g9LytFrxLdce6Lr2TOVebmpXFMtR9AUWo9DTVhBvMBMRag5d8CpdvES6uJEYg5pGsoLbGo2ac5IZ6/F1oNnYjZ13IUL2cCM6GVLo085yzZxm1Jwzm+H9YQ6feXoZHZeF0e+41b5+u3E3NL12KMsxlhb5rq0MuICD84MPMTAFk969EY5F4Ivs6E+6VRnBy3t1aBWfeDaxNOkIUTgFBG/EPCwywWZXJ3JrkLZmwyldNrgaEPklFvZfqA32CvBqwGNuafZO5tr2YFO+Zg5IZlq6oe44bgPGsSZMjEEpivf5u4EI3XJvfket43oHM+6eOxFkrF5g0OwKdFGx60E9Tzh6DM6rURHOTO9R/E+bTA7cijHrOvWu2J4j0GrQP+d8Gy9YHyuS01M5QixgisZ6sXG4mEnnMEulU0NprrtCTOlvNGubGFGVQ91rMls6ahAIZXnI2Z7PiEqg69z+5KHQNDgNI+F51By7jkM0J2tojDRecIbGE71pz5OCORQbWrdZO7QWpOjUxjcSk1Ag0qgQ97mQFDpXamDztmk65f5mmZvt2Nz4gYnvFy4vVCuiHd0W4JMXIn1wdWRc7a6bFJI1qF+O54l8EjLUV/Aq/AT/PRX3IPXjNcoeWosfLhq4aYD/6kOxfnVH73GU/z0ERqjwfB46BT9KZb68BEt9VlHZa3tj8x4OBiNdycNHesZ7HRqSpjb6c9fKP3CTT3NFfsZoj1leExBXwca+B7232xAmgcZipEb7PT3cNM/o4RoSCy6qiFzTAPfQUI00XTsWZ4Sa2/OUP58IqAh5+nWLgw5aVv8DjIYBB7cBRiYN0C/964Cf8VInkXzTHaDXrLkDP4AI/kz1q8ntC+3vp8IQPd7v81G/iICxICnH3gYbCPADt4sQAytAIzKDCJL71J4LFG5eck3rO0kDKqtRcRlWhka9E46oGiVHM+owf89lqTcfiwhUJZHvBBzJ8SO3ksIQbCNgDHz8KVHreMfR4bI44iSy7GTYUfvJQNP3UCRmpt1H3sx3FXTeEL43+hxu2n8RmFfcA4iG0E4m92wtp15+os2wwxtg0rdS28LuhfRwOcZiBVEuGQLDSx6cW/DkHq2hTsVZtqsJMKMLlUI3PFjh5Xtq/BUs6VkmBCDDGLQ8PBnPd8G5gQHgrQ4qNPAK8kw40sZvPI6uiH5bAu370SnzpIvxYwvZcgqMS2HGWTY4bY2dOqsWRsmfCGDYxYSHYbF7RYOnP8LOQ7qNPBKMsz4Uoak8KcdFmpbGJGZBFeC3raQBl5KhglfyODxAUHokIW2hbkXz9syq90W0sAryTDjSxlw4xiW6aFn3GH8LvCsUOnVaeGlZJjwhQxcTTDRDMtzDcwTZk3KxRjdq3OGV5Jhxpcy+NOiXC/DGeaCineubJlo1WngpWSY8IUMkd+sC3KtDDuM32GQlcpiVVNIC68kw4wvZWDGL5OHCbEGjoiYxLhifmsKaeGlZJjwpQyZObb6HElvWxhvAJcrc1kEa0rp8KWEmDDm9DvC4ujNkIu2gbvF11agFl9JiBljChHLYrYMneYO4y/PE7x7eRp0KREmbCFCRuVmkt2hy9zhkBR6BIwgem1aeCUZZnwpQ1B0LQzJaBs4aKVDsOVg81adBl5KhglfTpFqlJEwfu77zBbnLrOgt7diL6aFFxJizrgo4dH9iRl8DS1Ow0j2zk8UOuNrSTGhXKTIyiUXfR6l2HF0EtqYYoTqymnwtaSYUKYUhmnpgwzZaTs8qJzo0RolavClpJhRLlIkZRJNCaMUO95JMZdoLSkmlCmFtcoLxpN2kKLBuWlfSqKirpgdXkqIGeEiRFAYRvemz7c93sxGduWsOUs5p0wpxCjL2fixJ21wcKBdI19KdMaXkmJGuUjhlWeC6rErbfBmqn6QaMUp/DnlIkVW2fmcxq60wbta0Um0aK2YUC7+J+7PR884dqUN3ksxlehDpbij6bxflObMicRtTZfe6JqqxAbH5Odcr0NokCWHsiEXpZ4WbpzSKRidagZ3I3UGn8ZZ70ysyzySTUmfnZmN1PJ0OZqGvS5m3rrskTM4l5PocI1sTssAPllXZsWj09rVNQOPlgeDOVuOZzOi61gPsVsSTV864IxfFa9z9obUig2Y6epjOk81axd4Wou3GBNI9HWONdFZz5TriIqtsbLNQDOfuc6cisTT1qlat8tUpKScaNHWOabKkYkcAhOaMw06M97X+brAoyVpkGC6eo/mok5yRmYz5JG0Aarin0vi8MxM8syVg4Erz/TO9awtCIPvGItny9/hGyVzCCfBTIg0YNdU89u8eaR/RGzk1FhEtarPkYnkY+YQsJivUz2UI0dL77QYe4yRe+piqN+mAbscIp28imLwvLfpJiiGrvCIIZVBJdJ1dipxKOHplBfNlawcTrNW4hlWG6H9EZrU4Tk9fZFnEqPacMe/Nn7DQ+AOB+A8VceEWk3om6dh15bvowqYcmApcbyBPIEIcTzeOu4WKLjF8zHBuXI72iKyjdvkCBopVOGj4enQrlTfOl2AuhnxwFLiiNlUNKHT5+mgtD1ilFDsSHivaaniyQUQNOPp11tkfnjQNsIHR+99nZXCCNxFQ9c5HjOrm6vfFprl+R0MzLMNNfVFzr5kxJDEGoQaLmUDQRm96qRx/ZI1xNT9HTlzkkMba1gNMbjfKlxGnU2gUI5rY8b8WGMYTVOctaFUcWhT90QAR7VNWmxdXtB4Zzac80faoU/DC2TIUyoONWLdoI+XBu+SrqMpg8qY0Bb58taiOdh6SWPo7vM1zQdqQwzuFGc7LvCVRS4NSaWWbwJdT5GZ9kELb6o5x+vRlNfH0ZwPnWq8bj2aLeht6w4Vh8pYcTw+L3rbkBA0rYBbr41LmZxre6aTt6cuLAZ04aU9Z27i8gTRzhsec+ZN7eVT8sZetv9oaX1yj+BP2DpwxS96zWSOkidW0s5H1n/7iX7U+XUfKf8pWwagN5czkuNOk/SkLQNpsmWgKVmM4+tbXLAmp3KnTZmuKfNkDn7z+t3Pu0H4+O2bn37+8c3//vLzm+/fHf/6/Y/HV9/8/OYfr+rHX959U/7446cHVFi8lOiNHLfof/L61f99emRl4mfP9vf4yT+/+rQkeNkQvNafvP7jp+XF0qfvHT/5z08PfzmcPXKH/wdA+UGgCmVuZHN0cmVhbQplbmRvYmoKMTIgMCBvYmoKODUzOQplbmRvYmoKMTAgMCBvYmoKWyBdCmVuZG9iagoxNyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIzNSA+PgpzdHJlYW0KeJw1UUluADEIu+cV/kClsCfvmarqof3/tYZRLwMD2Ngk78FGJD7EkO4oV3zK6jTL8DtZ5MXPSuHkvYgKpCrCCmkHz3JWMwyeG5kClzPxWWY+mRY7FlBNxHF25DSDQYhpXEfL6TDTPOgJuT4YcWOnWa5iSOvdUr2+1/KfKspH1t0st07Z1ErdomfsSVx2Xk9taV8YdRQ3BZEOHzu8B/ki5iwuOpFu9psph5WkITgtgB+JoVTPDq8RJn5mJHjKnk7vozS89kHT9b17QUduJmQqt1BGKp6sNMaMofqNaCap7/+BnvW9vv4AQ01UuQplbmRzdHJlYW0KZW5kb2JqCjE4IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjQ3ID4+CnN0cmVhbQp4nE1RSW7EMAy7+xX8wACWrMV5T4pBD+3/ryUdFO3BECNLXOLuxEQWXrZQ10KH48NGXgmbge+D1pz4GrHiP9pGpJU/VFsgEzFRJHRRNxr3SDe8CtF+pIJXqvdY8xF3K81bOnaxv/fBtOaRKqtCPOTYHNlIWtdE0fE9tN5zQ3TKIIE+NyEHRGmOXoWkv/bDdW00u7U2syeqg0emhPJJsxqa0ylmyGyox20qVjIKN6qMivtURloP8jbOMoCT44QyWk92rCai/NQnl5AXE3HCLjs7FmITCxuHtB+VPrH8fOvN+JtpraWQcUEiNMWl32e8x+d4/wCVT1wmCmVuZHN0cmVhbQplbmRvYmoKMTkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA2MSA+PgpzdHJlYW0KeJwzNTVXMFCwtAASpqZGCuZGlgophlxAPoiVy2VoaQ5m5YBZFsZABkgZnGEApMGac2B6crgyuNIAyxUQzAplbmRzdHJlYW0KZW5kb2JqCjIwIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjMyID4+CnN0cmVhbQp4nD2QS3IEIQxD95xCRwB/4TydSs2i5/7byO6ZbJCqwPITcRwTZ/OICKQc/KxhZlATvIeFQ9VgO6DrwGdATuAaLnQpcKPahHN8ncObCpq4h8dstUisneVMIeowJkls6EnINs5ocuOc3KpU3kxrvcbim3J3u8pr2pbCvYfK+jjjVDmrKmuRNhGZRWsbwUYe7LDPo6toy1kq3DeMTV0TlcObxe5Z3cniiu+vXOPVLMHM98O3vxwfV93oKsfYyoTZUpPm0jn1r5bR+nC0i4V64Ud7JkhwdasgVaXWztpTev1T3CT6/QP0wVcdCmVuZHN0cmVhbQplbmRvYmoKMjEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNTYgPj4Kc3RyZWFtCnicNY9LDsMwCET3PsVcoBJfQ86Tquoivf+2YCeSEU/2zBhCBYQMvFgxzRFy4M2jbpp+g3MuuhZJ1U2UVQR2hkiCOXAO8YlUKAnmhFbVOYdVQIGF96uT3nJXXgFuO3D1bEfT/vYZapOE3cSRmFJ+Cnjd+tE2iYqGea6FeoDuNVgpmoSsBeyxDcy0EmrfDqzWyvPZ/xrf8fkDYBM2+AplbmRzdHJlYW0KZW5kb2JqCjIyIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzA3ID4+CnN0cmVhbQp4nD2SS24DMQxD9z6FLhDA+tme86Qoupjef9snJemKHNkWRWqWukxZUx6QNJOEf+nwcLGd8jtsz2Zm4Fqil4nllOfQFWLuonzZzEZdWSfF6oRmOrfoUTkXBzZNqp+rLKXdLngO1yaeW/YRP7zQoB7UNS4JN3RXo2UpNGOq+3/Se/yMMuBqTF1sUqt7HzxeRFXo6AdHiSJjlxfn40EJ6UrCaFqIlXdFA0Hu8rTKewnu295qyLIHqZjOOylmsOt0Ui5uF4chHsjyqPDlo9hrQs/4sCsl9EjYhjNyJ+5oxubUyOKQ/t6NBEuPrmgh8+CvbtYuYLxTOkViZE5yrGmLVU73UBTTucO9DBD1bEVDKXOR1epfw84La5ZsFnhK+gUeo90mSw5W2duoTu+tPNnQ9x9a13QfCmVuZHN0cmVhbQplbmRvYmoKMjMgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNDQgPj4Kc3RyZWFtCnicRZFNcgUhCIT3nqIv8KrkVz3PpFJZTO6/Dc28JCtaheYD0wITR/ASQ+yJlRMfMnwv6DJ8tzI78DrZmXBPuG5cw2XDM2Fb4DsqyzteQ3e2Uj+doarvGjneLlI1dGVkn3qhmgvMkIiuEVl0K5d1QNOU7lLhGmxbghT1SqwnnaA06BHK8HeUa3x1E0+vseRUzSFaza0TGoqwbHhB1MkkEbUNiyeWcyFR+aobqzouYJMl4vSA3KCVZnx6UkkRMIN8rMlozAI20JO7ZxfGmkseRY5XNJiwO0k18ID34ra+9zZxj/MX+IV33/8rDn3XAj5/AEv+XQYKZW5kc3RyZWFtCmVuZG9iagoyNCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIzMiA+PgpzdHJlYW0KeJw1UUluxDAMu/sV/MAA1u68J8Wgh/b/11LKFAhAJba4JWJjIwIvMfg5iNz4kjWjJn5nclf8LE+FR8Kt4EkUgZfhXnaCyxvGZT8OMx+8l1bOpMaTDMhFNj08ETLYJRA6MLsGddhm2om+IeGzI1LNRpbT1xL00ioEylO23+mCEm2r+nP7rAtt+9oTTnZ76knlE4jnlqzAZeMVk8VYBj1RuUsxfZDqbKEnobwon4NsPmqIRJcoZ+CJwcEo0A7sue1n4lUhaF3dp21jqEZKx9O/DU1Nkgj5RAlntjTuFv5/z72+1/sPTiFUEQplbmRzdHJlYW0KZW5kb2JqCjI1IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjMxID4+CnN0cmVhbQp4nDVPOZIEIQzLeYU+MFUY20C/p6e2Ntj5f7qSmU6Q8CHJ0xMdmXiZIyOwZsfbWmQgZuBTTMW/9rQPE6r34B4ilIsLYYaRcNas426ejhf/dpXPWAfvNviKWV4Q2MJM1lcWZy7bBWNpnMQ5yW6MXROxjXWtp1NYRzChDIR0tsOUIHNUpPTJjjLm6DiRJ56L7/bbLHY5fg7rCzaNIRXn+Cp6gjaDoux57wIackH/Xd34HkW76CUgGwkW1lFi7pzlhF+9dnQetSgSc0KaQS4TIc3pKqYQmlCss6OgUlFwqT6n6Kyff+VfXC0KZW5kc3RyZWFtCmVuZG9iagoyNiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI0OSA+PgpzdHJlYW0KeJw9UDuORCEM6zmFL/Ak8iNwHkarLWbv364DmilQTH62MyTQEYFHDDGUr+MlraCugb+LQvFu4uuDwiCrQ1IgznoPiHTspjaREzodnDM/YTdjjsBFMQac6XSmPQcmOfvCCoRzG2XsVkgniaoijuozjimeKnufeBYs7cg2WyeSPeQg4VJSicmln5TKP23KlAo6ZtEELBK54GQTTTjLu0lSjBmUMuoepnYifaw8yKM66GRNzqwjmdnTT9uZ+Bxwt1/aZE6Vx3QezPictM6DORW69+OJNgdNjdro7PcTaSovUrsdWp1+dRKV3RjnGBKXZ38Z32T/+Qf+h1oiCmVuZHN0cmVhbQplbmRvYmoKMjcgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzOTUgPj4Kc3RyZWFtCnicPVJLbsVACNvnFFyg0vCbz3lSVd28+29rQ1KpKryJMcYwfcqQueVLXRJxhcm3Xq5bPKZ8LltamXmIu4uNJT623JfuIbZddC6xOB1H8gsynSpEqM2q0aH4QpaFB5BO8KELwn05/uMvgMHXsA244T0yQbAk5ilCxm5RGZoSQRFh55EVqKRQn1nC31Hu6/cyBWpvjKULYxz0CbQFQm1IxALqQABE7JRUrZCOZyQTvxXdZ2IcYOfRsgGuGVRElnvsx4ipzqiMvETEPk9N+iiWTC1Wxm5TGV/8lIzUfHQFKqk08pTy0FWz0AtYiXkS9jn8SPjn1mwhhjpu1vKJ5R8zxTISzmBLOWChl+NH4NtZdRGuHbm4znSBH5XWcEy0637I9U/+dNtazXW8cgiiQOVNQfC7Dq5GscTEMj6djSl6oiywGpq8RjPBYRAR1vfDyAMa/XK8EDSnayK0WCKbtWJEjYpscz29BNZM78U51sMTwmzvndahsjMzKiGC2rqGautAdrO+83C2nz8z6KJtCmVuZHN0cmVhbQplbmRvYmoKMjggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMzYgPj4Kc3RyZWFtCnicTY9BDgMxCAPveYWfQCBAeM9WVQ/b/19L2HbTCx7JgGxRBoElh3iHG+HR2w/fRTYVZ+OcX1IpYiGYT3CfMFMcjSl38mOPgHGUaiynaHheS85NwxctdxMtpa2XkxlvuO6X90eVbZENRc8tC0LXbJL5MoEHfBiYR3XjaaXH3fZsr/b8AM5sNEkKZW5kc3RyZWFtCmVuZG9iagoyOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI0OSA+PgpzdHJlYW0KeJxNUUmKAzAMu+cV+kAhXpO8p0OZQ+f/18oOhTkECa+Sk5aYWAsPMYQfLD34kSFzN/0bfqLZu1l6ksnZ/5jnIlNR+FKoLmJCXYgbz6ER8D2haxJZsb3xOSyjmXO+Bx+FuAQzoQFjfUkyuajmlSETTgx1HA5apMK4a2LD4lrRPI3cbvtGZmUmhA2PZELcGICIIOsCshgslDY2EzJZzgPtDckNWmDXqRtRi4IrlNYJdKJWxKrM4LPm1nY3Qy3y4Kh98fpoVpdghdFL9Vh4X4U+mKmZdu6SQnrhTTsizB4KpDI7LSu1e8TqboH6P8tS8P3J9/gdrw/N/FycCmVuZHN0cmVhbQplbmRvYmoKMzAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA5NCA+PgpzdHJlYW0KeJxFjcERwCAIBP9UQQkKCtpPJpOH9v+NEDJ8YOcO7oQFC7Z5Rh8FlSZeFVgHSmPcUI9AveFyLcncBQ9wJ3/a0FScltN3aZFJVSncpBJ5/w5nJpCoedFjnfcLY/sjPAplbmRzdHJlYW0KZW5kb2JqCjMxIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzQxID4+CnN0cmVhbQp4nEVSS25EMQjbv1NwgUjhl5DztKq6mN5/W5tM1c3gCWBseMtTpmTKsLklIyTXlE99IkOspvw0ciQipvhJCQV2lY/Ha0usjeyRqBSf2vHjsfRGptkVWvXu0aXNolHNysg5yBChnhW6snvUDtnwelxIuu+UzSEcy/9QgSxl3XIKJUFb0HfsEd8PHa6CK4JhsGsug+1lMtT/+ocWXO9992LHLoAWrOe+wQ4AqKcTtAXIGdruNiloAFW6i0nCo/J6bnaibKNV6fkcADMOMHLAiCVbHb7R3gCWfV3oRY2K/StAUVlA/MjVdsHeMclIcBbmBo69cDzFmXBLOMYCQIq94hh68CXY5i9Xroia8Al1umQvvMKe2ubnQpMId60ADl5kw62ro6iW7ek8gvZnRXJGjNSLODohklrSOYLi0qAeWuNcN7HibSOxuVff7h/hnC9c9usXS+yExAplbmRzdHJlYW0KZW5kb2JqCjMyIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTY0ID4+CnN0cmVhbQp4nEWQx3EFMQxD76oCJTCACvWsx/MP6/6vhvTTQXoYQgxiT8KwXFdxYXTDj7ctMw1/RxnuxvoyY7zVWCAn6AMMkYmr0aT6dsUZqvTk1WKuo6JcLzoiEsyS46tAI3w6sseTtrYz/XReH+wh7xP/KirnbmEBLqruQPlSH/HUj9lR6pqhjyorax5q2leEXRFK2z4upzJO3b0DWuG9las92u8/HnY68gplbmRzdHJlYW0KZW5kb2JqCjMzIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggNzIgPj4Kc3RyZWFtCnicMzK3UDBQsDQBEoYWJgrmZgYKKYZcQL6piblCLhdIDMTKAbMMgLQlnIKIZ4CYIG0QxSAWRLGZiRlEHZwBkcvgSgMAJdsWyQplbmRzdHJlYW0KZW5kb2JqCjM0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggODMgPj4Kc3RyZWFtCnicPcw5EoAwCAXQnlP8I4TIIvdxHIt4/1Yw0QYeq3qgITiDusGt4WDKunQT71Pj1cacEgmoeEpNlroLetS0vtS+aOC76+ZL1Yk/zc8XnQ+7HRndCmVuZHN0cmVhbQplbmRvYmoKMzUgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA0NyA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlyWEFYuF0wsB8wC0ZZwCiKewZUGALlnDScKZW5kc3RyZWFtCmVuZG9iagozNiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI1OCA+PgpzdHJlYW0KeJxFkUtyBCAIRPeegiOA/OQ8k0plMbn/Ng3OZDZ2l6j9hEojphIs5xR5MH3J8s1ktul3OVY7GwUURSiYyVXosQKrO1PEmWuJautjZeS40zsGxRvOXTmpZHGjjHVUdSpwTM+V9VHd+XZZlH1HDmUK2KxzHGzgym3DGCdGm63uDveJIE8nU0fF7SDZ8AcnjX2VqytwnWz20UswDgT9QhOY5ItA6wyBxs1T9OQS7OPjdueBYG95EUjZEMiRIRgdgnadXP/i1vm9/3GGO8+1Ga4c7+J3mNZ2x19ikhVzAYvcKajnay5a1xk63pMzx+Sm+4bOuWCXu4NM7/k/1s/6/gMeKWb6CmVuZHN0cmVhbQplbmRvYmoKMzcgMCBvYmoKPDwgL0JCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzkKL1N1YnR5cGUgL0Zvcm0gL1R5cGUgL1hPYmplY3QgPj4Kc3RyZWFtCnic4zI0MFMwNjVVyOUyNzYCs3LALCNzIyALJItgQWQzuNIAFfMKfAplbmRzdHJlYW0KZW5kb2JqCjM4IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTYzID4+CnN0cmVhbQp4nEWQOxIDIQxDe06hI/gjAz7PZjIpNvdvY9hsUsDTWCCDuxOC1NqCieiCh7Yl3QXvrQRnY/zpNm41EuQEdYBWpONolFJ9ucVplXTxaDZzKwutEx1mDnqUoxmgEDoV3u2i5HKm7s75Q3D1X/W/Yt05m4mBycodCM3qU9z5NjuiurrJ/qTH3KzXfivsVWFpWUvLCbedu2ZACdxTOdqrPT8fCjr2CmVuZHN0cmVhbQplbmRvYmoKMzkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMTggPj4Kc3RyZWFtCnicPVC5jQQxDMtdhRpYwHrtqWcWi0um//RI+fYi0RZFUio1mZIpL3WUJVlT3jp8lsQOeYblbmQ2JSpFL5OwJffQCvF9ieYU993VlrNDNJdoOX4LMyqqGx3TSzaacCoTuqDcwzP6DW10A1aHHrFbINCkYNe2IHLHDxgMwZkTiyIMSk0G/65yj59eixs+w/FDFJGSDuY1/1j98nMNr1OPJ5Fub77iXpypDgMRHJKavCNdWLEuEhFpNUFNz8BaLYC7t17+G7QjugxA9onEcZpSjqG/a3Clzy/lJ1PYCmVuZHN0cmVhbQplbmRvYmoKNDAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA4MyA+PgpzdHJlYW0KeJxFjLsNwDAIRHumYAR+JvY+UZTC3r8NECVuuCfdPVwdCZkpbjPDQwaeDCyGXXGB9JYwC1xHUI6d7KNh1b7qBI31plLz7w+Unuys4obrAQJCGmYKZW5kc3RyZWFtCmVuZG9iago0MSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDUxID4+CnN0cmVhbQp4nDM2tFAwUDA0MAeSRoZAlpGJQoohF0gAxMzlggnmgFkGQBqiOAeuJocrgysNAOG0DZgKZW5kc3RyZWFtCmVuZG9iago0MiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE2MCA+PgpzdHJlYW0KeJxFkDkSAzEIBHO9gidIXIL3rMu1wfr/qQfWR6LpAjQcuhZNynoUaD7psUahutBr6CxKkkTBFpIdUKdjiDsoSExIY5JIth6DI5pYs12YmVQqs1LhtGnFwr/ZWtXIRI1wjfyJ6QZU/E/qXJTwTYOvkjH6GFS8O4OMSfheRdxaMe3+RDCxGfYJb0UmBYSJsanZvs9ghsz3Ctc4x/MNTII36wplbmRzdHJlYW0KZW5kb2JqCjQzIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzM0ID4+CnN0cmVhbQp4nC1SS3LFIAzbcwpdoDP4B+Q86XS6eL3/tpKTRUYOYPQx5YaJSnxZILej1sS3jcxAheGvq8yFz0jbyDqIy5CLuJIthXtELOQxxDzEgu+r8R4e+azMybMHxi/Zdw8r9tSEZSHjxRnaYRXHYRXkWLB1Iap7eFOkw6kk2OOL/z7Fcy0ELXxG0IBf5J+vjuD5khZp95ht0656sEw7qqSwHGxPc14mX1pnuToezwfJ9q7YEVK7AhSFuTPOc+Eo01ZGtBZ2NkhqXGxvjv1YStCFblxGiiOQn6kiPKCkycwmCuKPnB5yKgNh6pqudHIbVXGnnsw1m4u3M0lm675IsZnCeV04s/4MU2a1eSfPcqLUqQjvsWdL0NA5rp69lllodJsTvKSEz8ZOT06+VzPrITkVCaliWlfBaRSZYgnbEl9TUVOaehn++/Lu8Tt+/gEsc3xzCmVuZHN0cmVhbQplbmRvYmoKNDQgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3MCA+PgpzdHJlYW0KeJwzMzZTMFCwMAISpqaGCuZGlgophlxAPoiVywUTywGzzCzMgSwjC5CWHC5DC2MwbWJspGBmYgZkWSAxILoyuNIAmJoTAwplbmRzdHJlYW0KZW5kb2JqCjQ1IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzIwID4+CnN0cmVhbQp4nDVSS24FMQjbzym4QKXwT87zqqqLvvtvaxO9FUwwYOMpL1nSS77UJdulw+RbH/clsULej+2azFLF9xazFM8tr0fPEbctCgRREz1YmS8VItTP9Og6qHBKn4FXCLcUG7yDSQCDavgHHqUzIFDnQMa7YjJSA4Ik2HNpcQiJciaJf6S8nt8nraSh9D1Zmcvfk0ul0B1NTugBxcrFSaBdSfmgmZhKRJKX632xQvSGwJI8PkcxyYDsNoltogUm5x6lJczEFDqwxwK8ZprVVehgwh6HKYxXC7OoHmzyWxOVpB2t4xnZMN7LMFNioeGwBdTmYmWC7uXjNa/CiO1Rk13DcO6WzXcI0Wj+GxbK4GMVkoBHp7ESDWk4wIjAnl44xV7zEzkOwIhjnZosDGNoJqd6jonA0J6zpWHGxx5a9fMPVOl8hwplbmRzdHJlYW0KZW5kb2JqCjQ2IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTggPj4Kc3RyZWFtCnicMza0UDCAwxRDrjQAHeYDUgplbmRzdHJlYW0KZW5kb2JqCjQ3IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTMzID4+CnN0cmVhbQp4nEWPSw4EIQhE95yijsDHH+dxMumFc//tgJ1uE2M9hVSBuYKhPS5rA50VHyEZtvG3qZaORVk+VHpSVg/J4Iesxssh3KAs8IJJKoYhUIuYGpEtZW63gNs2DbKylVOljrCLozCP9rRsFR5folsidZI/g8QqL9zjuh3Ipda73qKLvn+kATEJCmVuZHN0cmVhbQplbmRvYmoKNDggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzNDAgPj4Kc3RyZWFtCnicNVI5bgQxDOv9Cn0ggG7b79kgSJH8vw2p2RQDcXRSlDtaVHbLh4VUtex0+bSV2hI35HdlhcQJyasS7VKGSKi8ViHV75kyr7c1ZwTIUqXC5KTkccmCP8OlpwvH+baxr+XIHY8eWBUjoUTAMsXE6BqWzu6wZlt+lmnAj3iEnCvWLcdYBVIb3TjtiveheS2yBoi9mZaKCh1WiRZ+QfGgR4199hhUWCDR7RxJcIyJUJGAdoHaSAw5eyx2UR/0MygxE+jaG0XcQYElkpg5xbp09N/40LGg/tiMN786KulbWllj0j4b7ZTGLDLpelj0dPPWx4MLNO+i/OfVDBI0ZY2Sxget2jmGoplRVni3Q5MNzTHHIfMOnsMZCUr6PBS/jyUTHZTI3w4NoX9fHqOMnDbeAuaiP20VBw7is8NeuYEVShdrkvcBqUzogen/r/G1vtfXHx3tgMYKZW5kc3RyZWFtCmVuZG9iago0OSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI1MSA+PgpzdHJlYW0KeJwtUUlyA0EIu88r9IRmp99jlyuH5P/XCMoHBg2LQHRa4qCMnyAsV7zlkatow98zMYLfBYd+K9dtWORAVCBJY1A1oXbxevQe2HGYCcyT1rAMZqwP/Iwp3OjF4TEZZ7fXZdQQ7F2vPZlByaxcxCUTF0zVYSNnDj+ZMi60cz03IOdGWJdhkG5WGjMSjjSFSCGFqpukzgRBEoyuRo02chT7pS+PdIZVjagx7HMtbV/PTThr0OxYrPLklB5dcS4nFy+sHPT1NgMXUWms8kBIwP1uD/VzspPfeEvnzhbT43vNyfLCVGDFm9duQDbV4t+8iOP7jK/n5/n8A19gW4gKZW5kc3RyZWFtCmVuZG9iago1MCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE3NCA+PgpzdHJlYW0KeJxNkEkOQyEMQ/ecwheohDPA5zy/qrpo77+tQwd1gfzkIHA8PNBxJC50ZOiMjiubHOPAsyBj4tE4/8m4PsQxQd2iLViXdsfZzBJzwjIxArZGydk8osAPx1wIEmSXH77AICJdj/lW81mT9M+3O92PurRmXz2iwInsCMWwAVeA/brHgUvC+V7T5JcqJWMTh/KB6iJSNjuhELVU7HKqirPdmytwFfT80UPu7QW1IzzfCmVuZHN0cmVhbQplbmRvYmoKNTEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3NSA+PgpzdHJlYW0KeJwztTRSMFAwNgASpmZGCqYm5gophlxAPoiVy2VoZApm5XAZWZopWFgAGSZm5lAhmIYcLmNTc6ABQEXGpmAaqj+HK4MrDQCVkBLvCmVuZHN0cmVhbQplbmRvYmoKNTIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA4OSA+PgpzdHJlYW0KeJw1jLsNgDAMRHtP4RHiv9kHIQrYv8VJcGPf3ZNeUuJA5ToRjqaBJ0H1mV4g2ekBVkXiUUnM/029qUVTz6btq00EJzOO9XUcqJrTetBaKG2TFt5wfQCcHe0KZW5kc3RyZWFtCmVuZG9iago1MyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE0MSA+PgpzdHJlYW0KeJw9j8EOwzAIQ+/5Cv9ApNgpoXxPp2qH7v+vI0u7C3oCY4yF0NAbqprDhmCb48XSJVRr+BTFQCU3yJlgDqWk0h1HkXpiOBhcHrQbjuKx6PoRu5JmfdDGQrolaIB7rFNp3KZxE8QdNQXqKeqco7wQuZ+pZ9g0kt00s5JzuA2/e89T1/+nq7zL+QW9dy7+CmVuZHN0cmVhbQplbmRvYmoKNTQgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMTUgPj4Kc3RyZWFtCnicNVE5DgMhDOz3Ff5AJIwveE+iKM3+v82M0VYewVyGtJQhmfJSk6gh5VM+epkunLrc18xqNOeWtC1zgLi2vC+tksCJZoiDwWmYuAGaPAFD19GoUUMXHtDUpVMosNwEPoq3bg/dY7WBl7Yh54kgYigZLEHNqUUTFm3PJ6Q1v16LG96X7d3IU6XGlhiBBgFWOBzX6NfwlT1PJtF0FTLUqzXLGAkTRSI8+Y6m1RPrWjTSMhLUxhGsagO8O/0wTgAAE3HLAmSfSpSz5MRvsfSzBlf6/gGfR1SWCmVuZHN0cmVhbQplbmRvYmoKMTUgMCBvYmoKPDwgL0Jhc2VGb250IC9EZWphVnVTYW5zIC9DaGFyUHJvY3MgMTYgMCBSCi9FbmNvZGluZyA8PAovRGlmZmVyZW5jZXMgWyAzMiAvc3BhY2UgNDYgL3BlcmlvZCA0OCAvemVybyAvb25lIC90d28gL3RocmVlIC9mb3VyIC9maXZlIC9zaXggL3NldmVuCi9laWdodCA2NyAvQyA3MSAvRyA3NiAvTCA4MiAvUiA4NSAvVSA5NyAvYSAvYiAvYyAvZCAvZSAvZiAvZyAvaCAvaSAxMDcgL2sKL2wgL20gL24gL28gMTE0IC9yIC9zIC90IC91IC92IC93IDEyMSAveSBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9DIDE3IDAgUiAvRyAxOCAwIFIgL0wgMTkgMCBSIC9SIDIwIDAgUiAvVSAyMSAwIFIgL2EgMjIgMCBSIC9iIDIzIDAgUgovYyAyNCAwIFIgL2QgMjUgMCBSIC9lIDI2IDAgUiAvZWlnaHQgMjcgMCBSIC9mIDI4IDAgUiAvZml2ZSAyOSAwIFIKL2ZvdXIgMzAgMCBSIC9nIDMxIDAgUiAvaCAzMiAwIFIgL2kgMzMgMCBSIC9rIDM0IDAgUiAvbCAzNSAwIFIgL20gMzYgMCBSCi9uIDM4IDAgUiAvbyAzOSAwIFIgL29uZSA0MCAwIFIgL3BlcmlvZCA0MSAwIFIgL3IgNDIgMCBSIC9zIDQzIDAgUgovc2V2ZW4gNDQgMCBSIC9zaXggNDUgMCBSIC9zcGFjZSA0NiAwIFIgL3QgNDcgMCBSIC90aHJlZSA0OCAwIFIgL3R3byA0OSAwIFIKL3UgNTAgMCBSIC92IDUxIDAgUiAvdyA1MiAwIFIgL3kgNTMgMCBSIC96ZXJvIDU0IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzcgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago1NSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQzMzQ2KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDU2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIxMjE1IDAwMDAwIG4gCjAwMDAwMjA5NTIgMDAwMDAgbiAKMDAwMDAyMDk4NCAwMDAwMCBuIAowMDAwMDIxMTI0IDAwMDAwIG4gCjAwMDAwMjExNDUgMDAwMDAgbiAKMDAwMDAyMTE2NiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDcgMDAwMDAgbiAKMDAwMDAwOTA0MiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDkwMjEgMDAwMDAgbiAKMDAwMDAxOTQ3MSAwMDAwMCBuIAowMDAwMDE5MjcxIDAwMDAwIG4gCjAwMDAwMTg3OTIgMDAwMDAgbiAKMDAwMDAyMDUyNCAwMDAwMCBuIAowMDAwMDA5MDYyIDAwMDAwIG4gCjAwMDAwMDkzNzAgMDAwMDAgbiAKMDAwMDAwOTY5MCAwMDAwMCBuIAowMDAwMDA5ODIzIDAwMDAwIG4gCjAwMDAwMTAxMjggMDAwMDAgbiAKMDAwMDAxMDM1NyAwMDAwMCBuIAowMDAwMDEwNzM3IDAwMDAwIG4gCjAwMDAwMTEwNTQgMDAwMDAgbiAKMDAwMDAxMTM1OSAwMDAwMCBuIAowMDAwMDExNjYzIDAwMDAwIG4gCjAwMDAwMTE5ODUgMDAwMDAgbiAKMDAwMDAxMjQ1MyAwMDAwMCBuIAowMDAwMDEyNjYyIDAwMDAwIG4gCjAwMDAwMTI5ODQgMDAwMDAgbiAKMDAwMDAxMzE1MCAwMDAwMCBuIAowMDAwMDEzNTY0IDAwMDAwIG4gCjAwMDAwMTM4MDEgMDAwMDAgbiAKMDAwMDAxMzk0NSAwMDAwMCBuIAowMDAwMDE0MTAwIDAwMDAwIG4gCjAwMDAwMTQyMTkgMDAwMDAgbiAKMDAwMDAxNDU1MCAwMDAwMCBuIAowMDAwMDE0NzIyIDAwMDAwIG4gCjAwMDAwMTQ5NTggMDAwMDAgbiAKMDAwMDAxNTI0OSAwMDAwMCBuIAowMDAwMDE1NDA0IDAwMDAwIG4gCjAwMDAwMTU1MjcgMDAwMDAgbiAKMDAwMDAxNTc2MCAwMDAwMCBuIAowMDAwMDE2MTY3IDAwMDAwIG4gCjAwMDAwMTYzMDkgMDAwMDAgbiAKMDAwMDAxNjcwMiAwMDAwMCBuIAowMDAwMDE2NzkyIDAwMDAwIG4gCjAwMDAwMTY5OTggMDAwMDAgbiAKMDAwMDAxNzQxMSAwMDAwMCBuIAowMDAwMDE3NzM1IDAwMDAwIG4gCjAwMDAwMTc5ODIgMDAwMDAgbiAKMDAwMDAxODEyOSAwMDAwMCBuIAowMDAwMDE4MjkwIDAwMDAwIG4gCjAwMDAwMTg1MDQgMDAwMDAgbiAKMDAwMDAyMTI3NSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDU1IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA1NiA+PgpzdGFydHhyZWYKMjE0MzIKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:33:45.830177\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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Y29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA0NiAvcGVyaW9kIDQ4IC96ZXJvIC9vbmUgL3R3byAvdGhyZWUgL2ZvdXIgL2ZpdmUgL3NpeCAvc2V2ZW4KL2VpZ2h0IDY3IC9DIDY5IC9FIDcxIC9HIDc2IC9MIDg1IC9VIDk3IC9hIC9iIC9jIC9kIC9lIC9mIC9nIC9oIC9pIDEwOCAvbAovbSAvbiAvbyAxMTQgL3IgL3MgL3QgL3UgL3YgL3cgMTIxIC95IF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNCAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxMyAwIFIgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTMgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0MgMTcgMCBSIC9FIDE4IDAgUiAvRyAxOSAwIFIgL0wgMjAgMCBSIC9VIDIxIDAgUiAvYSAyMiAwIFIgL2IgMjMgMCBSCi9jIDI0IDAgUiAvZCAyNSAwIFIgL2UgMjYgMCBSIC9laWdodCAyNyAwIFIgL2YgMjggMCBSIC9maXZlIDI5IDAgUgovZm91ciAzMCAwIFIgL2cgMzEgMCBSIC9oIDMyIDAgUiAvaSAzMyAwIFIgL2wgMzQgMCBSIC9tIDM1IDAgUiAvbiAzNyAwIFIKL28gMzggMCBSIC9vbmUgMzkgMCBSIC9wZXJpb2QgNDAgMCBSIC9yIDQxIDAgUiAvcyA0MiAwIFIgL3NldmVuIDQzIDAgUgovc2l4IDQ0IDAgUiAvc3BhY2UgNDUgMCBSIC90IDQ2IDAgUiAvdGhyZWUgNDcgMCBSIC90d28gNDggMCBSIC91IDQ5IDAgUgovdiA1MCAwIFIgL3cgNTEgMCBSIC95IDUyIDAgUiAvemVybyA1MyAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC41ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMS1EZWphVnVTYW5zLW1pbnVzIDM2IDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKNTQgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0MzM1MSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA1NQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAyMTI1NSAwMDAwMCBuIAowMDAwMDIwOTkyIDAwMDAwIG4gCjAwMDAwMjEwMjQgMDAwMDAgbiAKMDAwMDAyMTE2NCAwMDAwMCBuIAowMDAwMDIxMTg1IDAwMDAwIG4gCjAwMDAwMjEyMDYgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDA3IDAwMDAwIG4gCjAwMDAwMDk0MDIgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDA5MzgxIDAwMDAwIG4gCjAwMDAwMTk1MjEgMDAwMDAgbiAKMDAwMDAxOTMyMSAwMDAwMCBuIAowMDAwMDE4ODQ1IDAwMDAwIG4gCjAwMDAwMjA1NzQgMDAwMDAgbiAKMDAwMDAwOTQyMiAwMDAwMCBuIAowMDAwMDA5NzMwIDAwMDAwIG4gCjAwMDAwMDk4ODMgMDAwMDAgbiAKMDAwMDAxMDIwMyAwMDAwMCBuIAowMDAwMDEwMzM2IDAwMDAwIG4gCjAwMDAwMTA1NjUgMDAwMDAgbiAKMDAwMDAxMDk0NSAwMDAwMCBuIAowMDAwMDExMjYyIDAwMDAwIG4gCjAwMDAwMTE1NjcgMDAwMDAgbiAKMDAwMDAxMTg3MSAwMDAwMCBuIAowMDAwMDEyMTkzIDAwMDAwIG4gCjAwMDAwMTI2NjEgMDAwMDAgbiAKMDAwMDAxMjg3MCAwMDAwMCBuIAowMDAwMDEzMTkyIDAwMDAwIG4gCjAwMDAwMTMzNTggMDAwMDAgbiAKMDAwMDAxMzc3MiAwMDAwMCBuIAowMDAwMDE0MDA5IDAwMDAwIG4gCjAwMDAwMTQxNTMgMDAwMDAgbiAKMDAwMDAxNDI3MiAwMDAwMCBuIAowMDAwMDE0NjAzIDAwMDAwIG4gCjAwMDAwMTQ3NzUgMDAwMDAgbiAKMDAwMDAxNTAxMSAwMDAwMCBuIAowMDAwMDE1MzAyIDAwMDAwIG4gCjAwMDAwMTU0NTcgMDAwMDAgbiAKMDAwMDAxNTU4MCAwMDAwMCBuIAowMDAwMDE1ODEzIDAwMDAwIG4gCjAwMDAwMTYyMjAgMDAwMDAgbiAKMDAwMDAxNjM2MiAwMDAwMCBuIAowMDAwMDE2NzU1IDAwMDAwIG4gCjAwMDAwMTY4NDUgMDAwMDAgbiAKMDAwMDAxNzA1MSAwMDAwMCBuIAowMDAwMDE3NDY0IDAwMDAwIG4gCjAwMDAwMTc3ODggMDAwMDAgbiAKMDAwMDAxODAzNSAwMDAwMCBuIAowMDAwMDE4MTgyIDAwMDAwIG4gCjAwMDAwMTgzNDMgMDAwMDAgbiAKMDAwMDAxODU1NyAwMDAwMCBuIAowMDAwMDIxMzE1IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNTQgMCBSIC9Sb290IDEgMCBSIC9TaXplIDU1ID4+CnN0YXJ0eHJlZgoyMTQ3MgolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:33:50.374176\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0MgMTcgMCBSIC9HIDE4IDAgUiAvUyAxOSAwIFIgL2EgMjAgMCBSIC9iIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSCi9lIDI0IDAgUiAvZWlnaHQgMjUgMCBSIC9mIDI2IDAgUiAvZml2ZSAyNyAwIFIgL2ZvdXIgMjggMCBSIC9nIDI5IDAgUgovaCAzMCAwIFIgL2kgMzEgMCBSIC9sIDMyIDAgUiAvbSAzMyAwIFIgL24gMzUgMCBSIC9vIDM2IDAgUiAvb25lIDM3IDAgUgovcGVyaW9kIDM4IDAgUiAvciAzOSAwIFIgL3MgNDAgMCBSIC9zZXZlbiA0MSAwIFIgL3NpeCA0MiAwIFIgL3NwYWNlIDQzIDAgUgovdCA0NCAwIFIgL3R3byA0NSAwIFIgL3UgNDYgMCBSIC92IDQ3IDAgUiAvdyA0OCAwIFIgL3kgNDkgMCBSIC96ZXJvIDUwIDAgUgo+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzQgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago1MSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQzMzU1KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDUyCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIwNTUyIDAwMDAwIG4gCjAwMDAwMjAyODkgMDAwMDAgbiAKMDAwMDAyMDMyMSAwMDAwMCBuIAowMDAwMDIwNDYxIDAwMDAwIG4gCjAwMDAwMjA0ODIgMDAwMDAgbiAKMDAwMDAyMDUwMyAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDAgMDAwMDAgbiAKMDAwMDAwOTI2MyAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDkyNDIgMDAwMDAgbiAKMDAwMDAxODg1MiAwMDAwMCBuIAowMDAwMDE4NjUyIDAwMDAwIG4gCjAwMDAwMTgxOTIgMDAwMDAgbiAKMDAwMDAxOTkwNSAwMDAwMCBuIAowMDAwMDA5MjgzIDAwMDAwIG4gCjAwMDAwMDk1OTEgMDAwMDAgbiAKMDAwMDAwOTkxMSAwMDAwMCBuIAowMDAwMDEwMzI1IDAwMDAwIG4gCjAwMDAwMTA3MDUgMDAwMDAgbiAKMDAwMDAxMTAyMiAwMDAwMCBuIAowMDAwMDExMzI3IDAwMDAwIG4gCjAwMDAwMTE2MzEgMDAwMDAgbiAKMDAwMDAxMTk1MyAwMDAwMCBuIAowMDAwMDEyNDIxIDAwMDAwIG4gCjAwMDAwMTI2MzAgMDAwMDAgbiAKMDAwMDAxMjk1MiAwMDAwMCBuIAowMDAwMDEzMTE4IDAwMDAwIG4gCjAwMDAwMTM1MzIgMDAwMDAgbiAKMDAwMDAxMzc2OSAwMDAwMCBuIAowMDAwMDEzOTEzIDAwMDAwIG4gCjAwMDAwMTQwMzIgMDAwMDAgbiAKMDAwMDAxNDM2MyAwMDAwMCBuIAowMDAwMDE0NTM1IDAwMDAwIG4gCjAwMDAwMTQ3NzEgMDAwMDAgbiAKMDAwMDAxNTA2MiAwMDAwMCBuIAowMDAwMDE1MjE3IDAwMDAwIG4gCjAwMDAwMTUzNDAgMDAwMDAgbiAKMDAwMDAxNTU3MyAwMDAwMCBuIAowMDAwMDE1OTgwIDAwMDAwIG4gCjAwMDAwMTYxMjIgMDAwMDAgbiAKMDAwMDAxNjUxNSAwMDAwMCBuIAowMDAwMDE2NjA1IDAwMDAwIG4gCjAwMDAwMTY4MTEgMDAwMDAgbiAKMDAwMDAxNzEzNSAwMDAwMCBuIAowMDAwMDE3MzgyIDAwMDAwIG4gCjAwMDAwMTc1MjkgMDAwMDAgbiAKMDAwMDAxNzY5MCAwMDAwMCBuIAowMDAwMDE3OTA0IDAwMDAwIG4gCjAwMDAwMjA2MTIgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1MSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTIgPj4Kc3RhcnR4cmVmCjIwNzY5CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:33:54.684961\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Seaborn prints warnings if histogram has small values. We can ignore them for now\n", "warnings.filterwarnings(\"ignore\")\n", "# Create a plot for every activation function\n", "for i, act_fn_name in enumerate(act_fn_by_name):\n", " # Setting the seed ensures that we have the same weight initialization for each activation function\n", " set_seed(42)\n", " act_fn = act_fn_by_name[act_fn_name]()\n", " net_actfn = BaseNetwork(act_fn=act_fn).to(device)\n", " visualize_gradients(net_actfn, color=f\"C{i}\")"]}, {"cell_type": "markdown", "id": "f73b5f3f", "metadata": {"papermill": {"duration": 0.062002, "end_time": "2021-09-16T12:33:55.677984", "exception": false, "start_time": "2021-09-16T12:33:55.615982", "status": "completed"}, "tags": []}, "source": ["The sigmoid activation function shows a clearly undesirable behavior.\n", "While the gradients for the output layer are very large with up to 0.1, the input layer has the lowest gradient norm across all activation functions with only 1e-5.\n", "This is due to its small maximum gradient of 1/4, and finding a suitable learning rate across all layers is not possible in this setup.\n", "All the other activation functions show to have similar gradient norms across all layers.\n", "Interestingly, the ReLU activation has a spike around 0 which is caused by its zero-part on the left, and dead neurons (we will take a closer look at this later on).\n", "\n", "Note that additionally to the activation, the initialization of the weight parameters can be crucial.\n", "By default, PyTorch uses the [Kaiming](https://pytorch.org/docs/stable/nn.init.html#torch.nn.init.kaiming_uniform_) initialization for linear layers optimized for Tanh activations.\n", "In Tutorial 4, we will take a closer look at initialization, but assume\n", "for now that the Kaiming initialization works for all activation\n", "functions reasonably well."]}, {"cell_type": "markdown", "id": "b6081434", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.061601, "end_time": "2021-09-16T12:33:55.800993", "exception": false, "start_time": "2021-09-16T12:33:55.739392", "status": "completed"}, "tags": []}, "source": ["### Training a model\n", "\n", "Next, we want to train our model with different activation functions on FashionMNIST and compare the gained performance.\n", "All in all, our final goal is to achieve the best possible performance on a dataset of our choice.\n", "Therefore, we write a training loop in the next cell including a\n", "validation after every epoch and a final test on the best model:"]}, {"cell_type": "code", "execution_count": 18, "id": "827e3645", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:55.937494Z", "iopub.status.busy": "2021-09-16T12:33:55.936999Z", "iopub.status.idle": "2021-09-16T12:33:55.939074Z", "shell.execute_reply": "2021-09-16T12:33:55.938676Z"}, "papermill": {"duration": 0.07601, "end_time": "2021-09-16T12:33:55.939174", "exception": false, "start_time": "2021-09-16T12:33:55.863164", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_model(net, model_name, max_epochs=50, patience=7, batch_size=256, overwrite=False):\n", " \"\"\"Train a model on the training set of FashionMNIST.\n", "\n", " Args:\n", " net: Object of BaseNetwork\n", " model_name: (str) Name of the model, used for creating the checkpoint names\n", " max_epochs: Number of epochs we want to (maximally) train for\n", " patience: If the performance on the validation set has not improved for #patience epochs, we stop training early\n", " batch_size: Size of batches used in training\n", " overwrite: Determines how to handle the case when there already exists a checkpoint. If True, it will be overwritten. Otherwise, we skip training.\n", " \"\"\"\n", " file_exists = os.path.isfile(_get_model_file(CHECKPOINT_PATH, model_name))\n", " if file_exists and not overwrite:\n", " print(\"Model file already exists. Skipping training...\")\n", " else:\n", " if file_exists:\n", " print(\"Model file exists, but will be overwritten...\")\n", "\n", " # Defining optimizer, loss and data loader\n", " optimizer = optim.SGD(net.parameters(), lr=1e-2, momentum=0.9) # Default parameters, feel free to change\n", " loss_module = nn.CrossEntropyLoss()\n", " train_loader_local = data.DataLoader(\n", " train_set, batch_size=batch_size, shuffle=True, drop_last=True, pin_memory=True\n", " )\n", "\n", " val_scores = []\n", " best_val_epoch = -1\n", " for epoch in range(max_epochs):\n", " ############\n", " # Training #\n", " ############\n", " net.train()\n", " true_preds, count = 0.0, 0\n", " for imgs, labels in tqdm(train_loader_local, desc=f\"Epoch {epoch+1}\", leave=False):\n", " imgs, labels = imgs.to(device), labels.to(device) # To GPU\n", " optimizer.zero_grad() # Zero-grad can be placed anywhere before \"loss.backward()\"\n", " preds = net(imgs)\n", " loss = loss_module(preds, labels)\n", " loss.backward()\n", " optimizer.step()\n", " # Record statistics during training\n", " true_preds += (preds.argmax(dim=-1) == labels).sum()\n", " count += labels.shape[0]\n", " train_acc = true_preds / count\n", "\n", " ##############\n", " # Validation #\n", " ##############\n", " val_acc = test_model(net, val_loader)\n", " val_scores.append(val_acc)\n", " print(\n", " f\"[Epoch {epoch+1:2i}] Training accuracy: {train_acc*100.0:05.2f}%, Validation accuracy: {val_acc*100.0:05.2f}%\"\n", " )\n", "\n", " if len(val_scores) == 1 or val_acc > val_scores[best_val_epoch]:\n", " print(\"\\t (New best performance, saving model...)\")\n", " save_model(net, CHECKPOINT_PATH, model_name)\n", " best_val_epoch = epoch\n", " elif best_val_epoch <= epoch - patience:\n", " print(f\"Early stopping due to no improvement over the last {patience} epochs\")\n", " break\n", "\n", " # Plot a curve of the validation accuracy\n", " plt.plot([i for i in range(1, len(val_scores) + 1)], val_scores)\n", " plt.xlabel(\"Epochs\")\n", " plt.ylabel(\"Validation accuracy\")\n", " plt.title(f\"Validation performance of {model_name}\")\n", " plt.show()\n", " plt.close()\n", "\n", " load_model(CHECKPOINT_PATH, model_name, net=net)\n", " test_acc = test_model(net, test_loader)\n", " print((f\" Test accuracy: {test_acc*100.0:4.2f}% \").center(50, \"=\") + \"\\n\")\n", " return test_acc\n", "\n", "\n", "def test_model(net, data_loader):\n", " \"\"\"Test a model on a specified dataset.\n", "\n", " Args:\n", " net: Trained model of type BaseNetwork\n", " data_loader: DataLoader object of the dataset to test on (validation or test)\n", " \"\"\"\n", " net.eval()\n", " true_preds, count = 0.0, 0\n", " for imgs, labels in data_loader:\n", " imgs, labels = imgs.to(device), labels.to(device)\n", " with torch.no_grad():\n", " preds = net(imgs).argmax(dim=-1)\n", " true_preds += (preds == labels).sum().item()\n", " count += labels.shape[0]\n", " test_acc = true_preds / count\n", " return test_acc"]}, {"cell_type": "markdown", "id": "685326ae", "metadata": {"papermill": {"duration": 0.06232, "end_time": "2021-09-16T12:33:56.062996", "exception": false, "start_time": "2021-09-16T12:33:56.000676", "status": "completed"}, "tags": []}, "source": ["We train one model for each activation function.\n", "We recommend using the pretrained models to save time if you are running this notebook on CPU."]}, {"cell_type": "code", "execution_count": 19, "id": "1a2237a5", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:33:56.192395Z", "iopub.status.busy": "2021-09-16T12:33:56.191926Z", "iopub.status.idle": "2021-09-16T12:34:06.194481Z", "shell.execute_reply": "2021-09-16T12:34:06.193995Z"}, "papermill": {"duration": 10.068153, "end_time": "2021-09-16T12:34:06.194591", "exception": false, "start_time": "2021-09-16T12:33:56.126438", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Training BaseNetwork with sigmoid activation...\n", "Model file already exists. Skipping training...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 10.00% ==============\n", "\n", "Training BaseNetwork with tanh activation...\n", "Model file already exists. Skipping training...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 87.59% ==============\n", "\n", "Training BaseNetwork with relu activation...\n", "Model file already exists. Skipping training...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 88.62% ==============\n", "\n", "Training BaseNetwork with leakyrelu activation...\n", "Model file already exists. Skipping training...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 88.92% ==============\n", "\n", "Training BaseNetwork with elu activation...\n", "Model file already exists. Skipping training...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 87.27% ==============\n", "\n", "Training BaseNetwork with swish activation...\n", "Model file already exists. Skipping training...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 88.73% ==============\n", "\n"]}], "source": ["for act_fn_name in act_fn_by_name:\n", " print(f\"Training BaseNetwork with {act_fn_name} activation...\")\n", " set_seed(42)\n", " act_fn = act_fn_by_name[act_fn_name]()\n", " net_actfn = BaseNetwork(act_fn=act_fn).to(device)\n", " train_model(net_actfn, f\"FashionMNIST_{act_fn_name}\", overwrite=False)"]}, {"cell_type": "markdown", "id": "34dd5b86", "metadata": {"papermill": {"duration": 0.062788, "end_time": "2021-09-16T12:34:06.322278", "exception": false, "start_time": "2021-09-16T12:34:06.259490", "status": "completed"}, "tags": []}, "source": ["Not surprisingly, the model using the sigmoid activation function shows to fail and does not improve upon random performance (10 classes => 1/10 for random chance).\n", "\n", "All the other activation functions gain similar performance.\n", "To have a more accurate conclusion, we would have to train the models for multiple seeds and look at the averages.\n", "However, the \"optimal\" activation function also depends on many other factors (hidden sizes, number of layers, type of layers, task, dataset, optimizer, learning rate, etc.)\n", "so that a thorough grid search would not be useful in our case.\n", "In the literature, activation functions that have shown to work well\n", "with deep networks are all types of ReLU functions we experiment with\n", "here, with small gains for specific activation functions in specific\n", "networks."]}, {"cell_type": "markdown", "id": "5a74d87d", "metadata": {"papermill": {"duration": 0.063695, "end_time": "2021-09-16T12:34:06.449135", "exception": false, "start_time": "2021-09-16T12:34:06.385440", "status": "completed"}, "tags": []}, "source": ["### Visualizing the activation distribution"]}, {"cell_type": "markdown", "id": "68596e03", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.062527, "end_time": "2021-09-16T12:34:06.574529", "exception": false, "start_time": "2021-09-16T12:34:06.512002", "status": "completed"}, "tags": []}, "source": ["After we have trained the models, we can look at the actual activation values that find inside the model.\n", "For instance, how many neurons are set to zero in ReLU?\n", "Where do we find most values in Tanh?\n", "To answer these questions, we can write a simple function which takes a\n", "trained model, applies it to a batch of images, and plots the histogram\n", "of the activations inside the network:"]}, {"cell_type": "code", "execution_count": 20, "id": "24bf9b21", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:34:06.708975Z", "iopub.status.busy": "2021-09-16T12:34:06.708490Z", "iopub.status.idle": "2021-09-16T12:34:06.710629Z", "shell.execute_reply": "2021-09-16T12:34:06.710158Z"}, "papermill": {"duration": 0.073035, "end_time": "2021-09-16T12:34:06.710735", "exception": false, "start_time": "2021-09-16T12:34:06.637700", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def visualize_activations(net, color=\"C0\"):\n", " activations = {}\n", "\n", " net.eval()\n", " small_loader = data.DataLoader(train_set, batch_size=1024)\n", " imgs, labels = next(iter(small_loader))\n", " with torch.no_grad():\n", " layer_index = 0\n", " imgs = imgs.to(device)\n", " imgs = imgs.view(imgs.size(0), -1)\n", " # We need to manually loop through the layers to save all activations\n", " for layer_index, layer in enumerate(net.layers[:-1]):\n", " imgs = layer(imgs)\n", " activations[layer_index] = imgs.view(-1).cpu().numpy()\n", "\n", " # Plotting\n", " columns = 4\n", " rows = math.ceil(len(activations) / columns)\n", " fig, ax = plt.subplots(rows, columns, figsize=(columns * 2.7, rows * 2.5))\n", " fig_index = 0\n", " for key in activations:\n", " key_ax = ax[fig_index // columns][fig_index % columns]\n", " sns.histplot(data=activations[key], bins=50, ax=key_ax, color=color, kde=True, stat=\"density\")\n", " key_ax.set_title(f\"Layer {key} - {net.layers[key].__class__.__name__}\")\n", " fig_index += 1\n", " fig.suptitle(f\"Activation distribution for activation function {net.config['act_fn']['name']}\", fontsize=14)\n", " fig.subplots_adjust(hspace=0.4, wspace=0.4)\n", " plt.show()\n", " plt.close()"]}, {"cell_type": "code", "execution_count": 21, "id": "d8f346ab", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:34:06.842909Z", "iopub.status.busy": "2021-09-16T12:34:06.842437Z", "iopub.status.idle": "2021-09-16T12:35:27.581742Z", "shell.execute_reply": "2021-09-16T12:35:27.582143Z"}, "papermill": {"duration": 80.807, "end_time": "2021-09-16T12:35:27.582289", "exception": false, "start_time": "2021-09-16T12:34:06.775289", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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cmVlIC9mb3VyIC9maXZlIC9zaXgKL3NldmVuIC9laWdodCA2NSAvQSA2OCAvRCA3NiAvTCA4MyAvUyA5NyAvYSAvYiAvYyAvZCAvZSAvZiAvZyAxMDUgL2kgMTA5IC9tCi9uIC9vIDExNCAvciAvcyAvdCAvdSAvdiAxMjEgL3kgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE0IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDEzIDAgUiA+PgplbmRvYmoKMTQgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxMyAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNiAwIG9iago8PCAvQSAxNyAwIFIgL0QgMTggMCBSIC9MIDE5IDAgUiAvUyAyMCAwIFIgL2EgMjEgMCBSIC9iIDIyIDAgUiAvYyAyMyAwIFIKL2QgMjQgMCBSIC9lIDI1IDAgUiAvZWlnaHQgMjYgMCBSIC9mIDI3IDAgUiAvZml2ZSAyOCAwIFIgL2ZvdXIgMjkgMCBSCi9nIDMwIDAgUiAvaHlwaGVuIDMxIDAgUiAvaSAzMiAwIFIgL20gMzMgMCBSIC9uIDM1IDAgUiAvbyAzNiAwIFIKL29uZSAzNyAwIFIgL3BlcmlvZCAzOCAwIFIgL3IgMzkgMCBSIC9zIDQwIDAgUiAvc2V2ZW4gNDEgMCBSIC9zaXggNDIgMCBSCi9zcGFjZSA0MyAwIFIgL3QgNDQgMCBSIC90aHJlZSA0NSAwIFIgL3R3byA0NiAwIFIgL3UgNDcgMCBSIC92IDQ4IDAgUgoveSA0OSAwIFIgL3plcm8gNTAgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvRjEtRGVqYVZ1U2Fucy1taW51cyAzNCAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjUxIDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDM0MjArMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNTIKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMzM3NTEgMDAwMDAgbiAKMDAwMDAzMzQ4OCAwMDAwMCBuIAowMDAwMDMzNTIwIDAwMDAwIG4gCjAwMDAwMzM2NjAgMDAwMDAgbiAKMDAwMDAzMzY4MSAwMDAwMCBuIAowMDAwMDMzNzAyIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDIyNTEzIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAyMjQ5MSAwMDAwMCBuIAowMDAwMDMyMDQyIDAwMDAwIG4gCjAwMDAwMzE4NDIgMDAwMDAgbiAKMDAwMDAzMTM2OSAwMDAwMCBuIAowMDAwMDMzMDk1IDAwMDAwIG4gCjAwMDAwMjI1MzMgMDAwMDAgbiAKMDAwMDAyMjY5NiAwMDAwMCBuIAowMDAwMDIyOTMzIDAwMDAwIG4gCjAwMDAwMjMwNjYgMDAwMDAgbiAKMDAwMDAyMzQ4MCAwMDAwMCBuIAowMDAwMDIzODYwIDAwMDAwIG4gCjAwMDAwMjQxNzcgMDAwMDAgbiAKMDAwMDAyNDQ4MiAwMDAwMCBuIAowMDAwMDI0Nzg2IDAwMDAwIG4gCjAwMDAwMjUxMDggMDAwMDAgbiAKMDAwMDAyNTU3NiAwMDAwMCBuIAowMDAwMDI1Nzg1IDAwMDAwIG4gCjAwMDAwMjYxMDcgMDAwMDAgbiAKMDAwMDAyNjI3MyAwMDAwMCBuIAowMDAwMDI2Njg3IDAwMDAwIG4gCjAwMDAwMjY4MTMgMDAwMDAgbiAKMDAwMDAyNjk1NyAwMDAwMCBuIAowMDAwMDI3Mjg4IDAwMDAwIG4gCjAwMDAwMjc0NjAgMDAwMDAgbiAKMDAwMDAyNzY5NiAwMDAwMCBuIAowMDAwMDI3OTg3IDAwMDAwIG4gCjAwMDAwMjgxNDIgMDAwMDAgbiAKMDAwMDAyODI2NSAwMDAwMCBuIAowMDAwMDI4NDk4IDAwMDAwIG4gCjAwMDAwMjg5MDUgMDAwMDAgbiAKMDAwMDAyOTA0NyAwMDAwMCBuIAowMDAwMDI5NDQwIDAwMDAwIG4gCjAwMDAwMjk1MzAgMDAwMDAgbiAKMDAwMDAyOTczNiAwMDAwMCBuIAowMDAwMDMwMTQ5IDAwMDAwIG4gCjAwMDAwMzA0NzMgMDAwMDAgbiAKMDAwMDAzMDcyMCAwMDAwMCBuIAowMDAwMDMwODY3IDAwMDAwIG4gCjAwMDAwMzEwODEgMDAwMDAgbiAKMDAwMDAzMzgxMSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDUxIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA1MiA+PgpzdGFydHhyZWYKMzM5NjgKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:34:19.777697\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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IDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0EgMTcgMCBSIC9EIDE4IDAgUiAvTCAxOSAwIFIgL1QgMjAgMCBSIC9hIDIxIDAgUiAvYiAyMiAwIFIgL2MgMjMgMCBSCi9kIDI0IDAgUiAvZSAyNSAwIFIgL2VpZ2h0IDI2IDAgUiAvZiAyNyAwIFIgL2ZpdmUgMjggMCBSIC9mb3VyIDI5IDAgUgovaCAzMCAwIFIgL2h5cGhlbiAzMSAwIFIgL2kgMzIgMCBSIC9uIDM0IDAgUiAvbyAzNSAwIFIgL29uZSAzNiAwIFIKL3BlcmlvZCAzNyAwIFIgL3IgMzggMCBSIC9zIDM5IDAgUiAvc2V2ZW4gNDAgMCBSIC9zaXggNDEgMCBSIC9zcGFjZSA0MiAwIFIKL3QgNDMgMCBSIC90aHJlZSA0NCAwIFIgL3R3byA0NSAwIFIgL3UgNDYgMCBSIC92IDQ3IDAgUiAveSA0OCAwIFIKL3plcm8gNDkgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvRjEtRGVqYVZ1U2Fucy1taW51cyAzMyAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjUwIDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDM0MzUrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNTEKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjg2NTcgMDAwMDAgbiAKMDAwMDAyODM5NCAwMDAwMCBuIAowMDAwMDI4NDI2IDAwMDAwIG4gCjAwMDAwMjg1NjYgMDAwMDAgbiAKMDAwMDAyODU4NyAwMDAwMCBuIAowMDAwMDI4NjA4IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDE4MjE2IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAxODE5NCAwMDAwMCBuIAowMDAwMDI2OTU4IDAwMDAwIG4gCjAwMDAwMjY3NTggMDAwMDAgbiAKMDAwMDAyNjI4OCAwMDAwMCBuIAowMDAwMDI4MDExIDAwMDAwIG4gCjAwMDAwMTgyMzYgMDAwMDAgbiAKMDAwMDAxODM5OSAwMDAwMCBuIAowMDAwMDE4NjM2IDAwMDAwIG4gCjAwMDAwMTg3NjkgMDAwMDAgbiAKMDAwMDAxODkwNyAwMDAwMCBuIAowMDAwMDE5Mjg3IDAwMDAwIG4gCjAwMDAwMTk2MDQgMDAwMDAgbiAKMDAwMDAxOTkwOSAwMDAwMCBuIAowMDAwMDIwMjEzIDAwMDAwIG4gCjAwMDAwMjA1MzUgMDAwMDAgbiAKMDAwMDAyMTAwMyAwMDAwMCBuIAowMDAwMDIxMjEyIDAwMDAwIG4gCjAwMDAwMjE1MzQgMDAwMDAgbiAKMDAwMDAyMTcwMCAwMDAwMCBuIAowMDAwMDIxOTM3IDAwMDAwIG4gCjAwMDAwMjIwNjMgMDAwMDAgbiAKMDAwMDAyMjIwNyAwMDAwMCBuIAowMDAwMDIyMzc5IDAwMDAwIG4gCjAwMDAwMjI2MTUgMDAwMDAgbiAKMDAwMDAyMjkwNiAwMDAwMCBuIAowMDAwMDIzMDYxIDAwMDAwIG4gCjAwMDAwMjMxODQgMDAwMDAgbiAKMDAwMDAyMzQxNyAwMDAwMCBuIAowMDAwMDIzODI0IDAwMDAwIG4gCjAwMDAwMjM5NjYgMDAwMDAgbiAKMDAwMDAyNDM1OSAwMDAwMCBuIAowMDAwMDI0NDQ5IDAwMDAwIG4gCjAwMDAwMjQ2NTUgMDAwMDAgbiAKMDAwMDAyNTA2OCAwMDAwMCBuIAowMDAwMDI1MzkyIDAwMDAwIG4gCjAwMDAwMjU2MzkgMDAwMDAgbiAKMDAwMDAyNTc4NiAwMDAwMCBuIAowMDAwMDI2MDAwIDAwMDAwIG4gCjAwMDAwMjg3MTcgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1MCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTEgPj4Kc3RhcnR4cmVmCjI4ODc0CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:34:33.923518\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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n0qUs+TEb7H0swZX+v4Bn0dUlgplbmRzdHJlYW0KZW5kb2JqCjE1IDAgb2JqCjw8IC9CYXNlRm9udCAvRGVqYVZ1U2FucyAvQ2hhclByb2NzIDE2IDAgUgovRW5jb2RpbmcgPDwKL0RpZmZlcmVuY2VzIFsgMzIgL3NwYWNlIDQ1IC9oeXBoZW4gL3BlcmlvZCA0OCAvemVybyAvb25lIC90d28gL3RocmVlIC9mb3VyIC9maXZlIC9zaXgKL3NldmVuIC9laWdodCA2NSAvQSA2OCAvRCA3NiAvTCA4MiAvUiA4NSAvVSA5NyAvYSAvYiAvYyAvZCAvZSAvZiAxMDUgL2kgMTEwCi9uIC9vIDExNCAvciAvcyAvdCAvdSAvdiAxMjEgL3kgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE0IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDEzIDAgUiA+PgplbmRvYmoKMTQgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxMyAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNiAwIG9iago8PCAvQSAxNyAwIFIgL0QgMTggMCBSIC9MIDE5IDAgUiAvUiAyMCAwIFIgL1UgMjEgMCBSIC9hIDIyIDAgUiAvYiAyMyAwIFIKL2MgMjQgMCBSIC9kIDI1IDAgUiAvZSAyNiAwIFIgL2VpZ2h0IDI3IDAgUiAvZiAyOCAwIFIgL2ZpdmUgMjkgMCBSCi9mb3VyIDMwIDAgUiAvaHlwaGVuIDMxIDAgUiAvaSAzMiAwIFIgL24gMzQgMCBSIC9vIDM1IDAgUiAvb25lIDM2IDAgUgovcGVyaW9kIDM3IDAgUiAvciAzOCAwIFIgL3MgMzkgMCBSIC9zZXZlbiA0MCAwIFIgL3NpeCA0MSAwIFIgL3NwYWNlIDQyIDAgUgovdCA0MyAwIFIgL3RocmVlIDQ0IDAgUiAvdHdvIDQ1IDAgUiAvdSA0NiAwIFIgL3YgNDcgMCBSIC95IDQ4IDAgUgovemVybyA0OSAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC41ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMS1EZWphVnVTYW5zLW1pbnVzIDMzIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKNTAgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0MzQ0NyswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA1MQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAyNTgwOSAwMDAwMCBuIAowMDAwMDI1NTQ2IDAwMDAwIG4gCjAwMDAwMjU1NzggMDAwMDAgbiAKMDAwMDAyNTcxOCAwMDAwMCBuIAowMDAwMDI1NzM5IDAwMDAwIG4gCjAwMDAwMjU3NjAgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk5IDAwMDAwIG4gCjAwMDAwMTUyMDYgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDE1MTg0IDAwMDAwIG4gCjAwMDAwMjQxMTAgMDAwMDAgbiAKMDAwMDAyMzkxMCAwMDAwMCBuIAowMDAwMDIzNDM3IDAwMDAwIG4gCjAwMDAwMjUxNjMgMDAwMDAgbiAKMDAwMDAxNTIyNiAwMDAwMCBuIAowMDAwMDE1Mzg5IDAwMDAwIG4gCjAwMDAwMTU2MjYgMDAwMDAgbiAKMDAwMDAxNTc1OSAwMDAwMCBuIAowMDAwMDE2MDY0IDAwMDAwIG4gCjAwMDAwMTYyOTMgMDAwMDAgbiAKMDAwMDAxNjY3MyAwMDAwMCBuIAowMDAwMDE2OTkwIDAwMDAwIG4gCjAwMDAwMTcyOTUgMDAwMDAgbiAKMDAwMDAxNzU5OSAwMDAwMCBuIAowMDAwMDE3OTIxIDAwMDAwIG4gCjAwMDAwMTgzODkgMDAwMDAgbiAKMDAwMDAxODU5OCAwMDAwMCBuIAowMDAwMDE4OTIwIDAwMDAwIG4gCjAwMDAwMTkwODYgMDAwMDAgbiAKMDAwMDAxOTIxMiAwMDAwMCBuIAowMDAwMDE5MzU2IDAwMDAwIG4gCjAwMDAwMTk1MjggMDAwMDAgbiAKMDAwMDAxOTc2NCAwMDAwMCBuIAowMDAwMDIwMDU1IDAwMDAwIG4gCjAwMDAwMjAyMTAgMDAwMDAgbiAKMDAwMDAyMDMzMyAwMDAwMCBuIAowMDAwMDIwNTY2IDAwMDAwIG4gCjAwMDAwMjA5NzMgMDAwMDAgbiAKMDAwMDAyMTExNSAwMDAwMCBuIAowMDAwMDIxNTA4IDAwMDAwIG4gCjAwMDAwMjE1OTggMDAwMDAgbiAKMDAwMDAyMTgwNCAwMDAwMCBuIAowMDAwMDIyMjE3IDAwMDAwIG4gCjAwMDAwMjI1NDEgMDAwMDAgbiAKMDAwMDAyMjc4OCAwMDAwMCBuIAowMDAwMDIyOTM1IDAwMDAwIG4gCjAwMDAwMjMxNDkgMDAwMDAgbiAKMDAwMDAyNTg2OSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDUwIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA1MSA+PgpzdGFydHhyZWYKMjYwMjYKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:34:46.777369\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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1+jX8JU9TybRdBUy1Ks1yxgJE0UiPPmOptUT61o00jIS1MYRrGoDvDv9ME4AABNxywJkn0qUs+TEb7H0swZX+v4Bn0dUlgplbmRzdHJlYW0KZW5kb2JqCjE1IDAgb2JqCjw8IC9CYXNlRm9udCAvRGVqYVZ1U2FucyAvQ2hhclByb2NzIDE2IDAgUgovRW5jb2RpbmcgPDwKL0RpZmZlcmVuY2VzIFsgMzIgL3NwYWNlIDQ1IC9oeXBoZW4gL3BlcmlvZCA0OCAvemVybyAvb25lIC90d28gL3RocmVlIC9mb3VyIC9maXZlIC9zaXgKL3NldmVuIC9laWdodCA2NSAvQSA2OCAvRCA3NiAvTCA4MiAvUiA4NSAvVSA5NyAvYSAvYiAvYyAvZCAvZSAvZiAxMDUgL2kgMTA3Ci9rIDExMCAvbiAvbyAxMTQgL3IgL3MgL3QgL3UgL3YgMTIxIC95IF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNCAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxMyAwIFIgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTMgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0EgMTcgMCBSIC9EIDE4IDAgUiAvTCAxOSAwIFIgL1IgMjAgMCBSIC9VIDIxIDAgUiAvYSAyMiAwIFIgL2IgMjMgMCBSCi9jIDI0IDAgUiAvZCAyNSAwIFIgL2UgMjYgMCBSIC9laWdodCAyNyAwIFIgL2YgMjggMCBSIC9maXZlIDI5IDAgUgovZm91ciAzMCAwIFIgL2h5cGhlbiAzMSAwIFIgL2kgMzIgMCBSIC9rIDMzIDAgUiAvbiAzNSAwIFIgL28gMzYgMCBSCi9vbmUgMzcgMCBSIC9wZXJpb2QgMzggMCBSIC9yIDM5IDAgUiAvcyA0MCAwIFIgL3NldmVuIDQxIDAgUiAvc2l4IDQyIDAgUgovc3BhY2UgNDMgMCBSIC90IDQ0IDAgUiAvdGhyZWUgNDUgMCBSIC90d28gNDYgMCBSIC91IDQ3IDAgUiAvdiA0OCAwIFIKL3kgNDkgMCBSIC96ZXJvIDUwIDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzQgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago1MSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQzNTAwKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDUyCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDI2Nzc3IDAwMDAwIG4gCjAwMDAwMjY1MTQgMDAwMDAgbiAKMDAwMDAyNjU0NiAwMDAwMCBuIAowMDAwMDI2Njg2IDAwMDAwIG4gCjAwMDAwMjY3MDcgMDAwMDAgbiAKMDAwMDAyNjcyOCAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDYgMDAwMDAgbiAKMDAwMDAxNjAwMiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMTU5ODAgMDAwMDAgbiAKMDAwMDAyNTA2OCAwMDAwMCBuIAowMDAwMDI0ODY4IDAwMDAwIG4gCjAwMDAwMjQzODggMDAwMDAgbiAKMDAwMDAyNjEyMSAwMDAwMCBuIAowMDAwMDE2MDIyIDAwMDAwIG4gCjAwMDAwMTYxODUgMDAwMDAgbiAKMDAwMDAxNjQyMiAwMDAwMCBuIAowMDAwMDE2NTU1IDAwMDAwIG4gCjAwMDAwMTY4NjAgMDAwMDAgbiAKMDAwMDAxNzA4OSAwMDAwMCBuIAowMDAwMDE3NDY5IDAwMDAwIG4gCjAwMDAwMTc3ODYgMDAwMDAgbiAKMDAwMDAxODA5MSAwMDAwMCBuIAowMDAwMDE4Mzk1IDAwMDAwIG4gCjAwMDAwMTg3MTcgMDAwMDAgbiAKMDAwMDAxOTE4NSAwMDAwMCBuIAowMDAwMDE5Mzk0IDAwMDAwIG4gCjAwMDAwMTk3MTYgMDAwMDAgbiAKMDAwMDAxOTg4MiAwMDAwMCBuIAowMDAwMDIwMDA4IDAwMDAwIG4gCjAwMDAwMjAxNTIgMDAwMDAgbiAKMDAwMDAyMDMwNyAwMDAwMCBuIAowMDAwMDIwNDc5IDAwMDAwIG4gCjAwMDAwMjA3MTUgMDAwMDAgbiAKMDAwMDAyMTAwNiAwMDAwMCBuIAowMDAwMDIxMTYxIDAwMDAwIG4gCjAwMDAwMjEyODQgMDAwMDAgbiAKMDAwMDAyMTUxNyAwMDAwMCBuIAowMDAwMDIxOTI0IDAwMDAwIG4gCjAwMDAwMjIwNjYgMDAwMDAgbiAKMDAwMDAyMjQ1OSAwMDAwMCBuIAowMDAwMDIyNTQ5IDAwMDAwIG4gCjAwMDAwMjI3NTUgMDAwMDAgbiAKMDAwMDAyMzE2OCAwMDAwMCBuIAowMDAwMDIzNDkyIDAwMDAwIG4gCjAwMDAwMjM3MzkgMDAwMDAgbiAKMDAwMDAyMzg4NiAwMDAwMCBuIAowMDAwMDI0MTAwIDAwMDAwIG4gCjAwMDAwMjY4MzcgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1MSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTIgPj4Kc3RhcnR4cmVmCjI2OTk0CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:34:59.806501\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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aWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA0NSAvaHlwaGVuIC9wZXJpb2QgNDggL3plcm8gL29uZSAvdHdvIC90aHJlZSAvZm91ciAvZml2ZSAvc2l4Ci9zZXZlbiAvZWlnaHQgNjUgL0EgNjggL0QgL0UgNzYgL0wgODUgL1UgOTcgL2EgL2IgL2MgL2QgL2UgL2YgMTA1IC9pIDExMCAvbgovbyAxMTQgL3IgL3MgL3QgL3UgL3YgMTIxIC95IF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNCAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxMyAwIFIgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTMgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0EgMTcgMCBSIC9EIDE4IDAgUiAvRSAxOSAwIFIgL0wgMjAgMCBSIC9VIDIxIDAgUiAvYSAyMiAwIFIgL2IgMjMgMCBSCi9jIDI0IDAgUiAvZCAyNSAwIFIgL2UgMjYgMCBSIC9laWdodCAyNyAwIFIgL2YgMjggMCBSIC9maXZlIDI5IDAgUgovZm91ciAzMCAwIFIgL2h5cGhlbiAzMSAwIFIgL2kgMzIgMCBSIC9uIDM0IDAgUiAvbyAzNSAwIFIgL29uZSAzNiAwIFIKL3BlcmlvZCAzNyAwIFIgL3IgMzggMCBSIC9zIDM5IDAgUiAvc2V2ZW4gNDAgMCBSIC9zaXggNDEgMCBSIC9zcGFjZSA0MiAwIFIKL3QgNDMgMCBSIC90aHJlZSA0NCAwIFIgL3R3byA0NSAwIFIgL3UgNDYgMCBSIC92IDQ3IDAgUiAveSA0OCAwIFIKL3plcm8gNDkgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvRjEtRGVqYVZ1U2Fucy1taW51cyAzMyAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjUwIDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDM1MTQrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNTEKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjc1MDIgMDAwMDAgbiAKMDAwMDAyNzIzOSAwMDAwMCBuIAowMDAwMDI3MjcxIDAwMDAwIG4gCjAwMDAwMjc0MTEgMDAwMDAgbiAKMDAwMDAyNzQzMiAwMDAwMCBuIAowMDAwMDI3NDUzIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDE3MDU0IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAxNzAzMiAwMDAwMCBuIAowMDAwMDI1ODAzIDAwMDAwIG4gCjAwMDAwMjU2MDMgMDAwMDAgbiAKMDAwMDAyNTEzMyAwMDAwMCBuIAowMDAwMDI2ODU2IDAwMDAwIG4gCjAwMDAwMTcwNzQgMDAwMDAgbiAKMDAwMDAxNzIzNyAwMDAwMCBuIAowMDAwMDE3NDc0IDAwMDAwIG4gCjAwMDAwMTc2MjcgMDAwMDAgbiAKMDAwMDAxNzc2MCAwMDAwMCBuIAowMDAwMDE3OTg5IDAwMDAwIG4gCjAwMDAwMTgzNjkgMDAwMDAgbiAKMDAwMDAxODY4NiAwMDAwMCBuIAowMDAwMDE4OTkxIDAwMDAwIG4gCjAwMDAwMTkyOTUgMDAwMDAgbiAKMDAwMDAxOTYxNyAwMDAwMCBuIAowMDAwMDIwMDg1IDAwMDAwIG4gCjAwMDAwMjAyOTQgMDAwMDAgbiAKMDAwMDAyMDYxNiAwMDAwMCBuIAowMDAwMDIwNzgyIDAwMDAwIG4gCjAwMDAwMjA5MDggMDAwMDAgbiAKMDAwMDAyMTA1MiAwMDAwMCBuIAowMDAwMDIxMjI0IDAwMDAwIG4gCjAwMDAwMjE0NjAgMDAwMDAgbiAKMDAwMDAyMTc1MSAwMDAwMCBuIAowMDAwMDIxOTA2IDAwMDAwIG4gCjAwMDAwMjIwMjkgMDAwMDAgbiAKMDAwMDAyMjI2MiAwMDAwMCBuIAowMDAwMDIyNjY5IDAwMDAwIG4gCjAwMDAwMjI4MTEgMDAwMDAgbiAKMDAwMDAyMzIwNCAwMDAwMCBuIAowMDAwMDIzMjk0IDAwMDAwIG4gCjAwMDAwMjM1MDAgMDAwMDAgbiAKMDAwMDAyMzkxMyAwMDAwMCBuIAowMDAwMDI0MjM3IDAwMDAwIG4gCjAwMDAwMjQ0ODQgMDAwMDAgbiAKMDAwMDAyNDYzMSAwMDAwMCBuIAowMDAwMDI0ODQ1IDAwMDAwIG4gCjAwMDAwMjc1NjIgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1MCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTEgPj4Kc3RhcnR4cmVmCjI3NzE5CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:35:13.285964\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDY1OS42NjUgMzQyLjM0MDYyNSBdIC9QYXJlbnQgMiAwIFIgL1Jlc291cmNlcyA4IDAgUgovVHlwZSAvUGFnZSA+PgplbmRvYmoKOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEyIDAgUiA+PgpzdHJlYW0KeJzVnUuTJLeVpff5K2LZvaAL78dSbZqRWZs2asl6FmO94KipUclYlImUWtb/fs4BPDyAi5vJymRWcbCQLOswE+HnC3cH3HEf9vbnp1/80t7+7w83/N/N3P6M//0DP/+a/34y+NfHpxTrkVLEz99eP/vgDh9MchGimf/5p6enPz6Zo9qcQjaxlJv8R6jG1mRyuX3PD/318gvXP57Ebz89hXoUfIwt8Si1feLHJ5vikW32poz6t6Puaj3s/QjPMSatHfVfb+vw1vJH501O+H/80x9Qv//m9r9u391+8UvX4f0r/vdn/K/Be/rFr775rw9/+Obffv0vtz/88BTT4Qr+zs5H/ZCnA3n63dNvb3+9j2wOG/HN3Adv//z1qT799ckC31cG/ymEI8FqTKE6fE4qh/EW4/3h49O//P72i/8Jzd5+/8enejibck0l8wv9/X8+/e/bP1nzz7f/uP3+X5/+x+9BwBzGclgz/PSHjxziq1998+ev//3vv/v6ux+++vjhu7//cPvVX26/ffptO97PAc6aytPK5yq+74f+DuiswdBtNG9rfIGduYgNwD6j+4SLqtbksjzbL/093Md4jmZDKp/k3n4R+6+50N9m3bnzEywGNdXnF7/y40u6dv0Mj164HvSf6NrhrLfJlJBrzC/6tl/St3dHriaEJHw/9J/q25UjWZNjCTbYF427L2mcswFuQlEaf+g/1Xh0h0kmpVKyfdm4/5LGM1YU1biUhfGH/lON53hE3ORxf8d8+6LxMBr/6xMH+YrD2XR4jmTrkZe7o5tH+dU33/3w4W///ZMR+m413pdCDmuh8ycfPf6Lv/5LW0Ydzhdbc3Ilnku48Y9v4x8/iT9+esIkYKor3oklSj5sxeQQ59utlF3B4uzbZZBTxsrqX3ZAoHkFAtxwTTZVzDhSDsYEhcwp74JA84oHAHvgXunm9eu3QsYivZSykLnkTRCoXoHAH9FZn4JAMMmYn2xQwHR1FwCaUz4EHhgv1CwATHJ1OVe3crnLuyDQvAJBPjLmYVcFgknG06qtdiVzl3dBoHkFAhx0cBhTIJhkPFx6n1cyd3kXBJrXj0/Z8sssVUyJo5wOY7BcsILMIG+CQPUKBOHAaso4MSWOMrz6kEteydzlXRBoXoEg4eEp2CSmxFGG12hiVMjc5V0QaF6BoBwuW1fFlDjKeHRxkO1C5pJ3QaB5/fhU+Iu8vmcEo4wHhxDaNT+DuaubAFCdAoA7QsHJLKbEUU5HMsW0x9eZyyXvgkDzCgThiNXh8ASCQU5YAzq+SpVkLnkXBJpXIMCXWas47m8nOfNVN5YCgswg74JA8woE5Sgm1SimxFHO3NbA49BC5pJ3QaB5/fhUuW8TTBVT4iiXI+POH4MgM8ibIFC9AoE/jLPOiSlxkPGHNiQ8Ds1kRnkXBJpXIIiHdcVHMSUOcnWHL3wZJ8gM8i4INK9AgLuaj0HMiQ+1piPGwBckM5dB3gWA4hT+uUXhkhMz4kN2xh847pD8yGVSdwGgWeX+pD0CHv2jmBEH3dmAHw1czGhGeRMIultS8DijE1//CQqXzm0dG1z1QdAZ9W0waHaJIR4pBeOsxHDp3OQpuW3EzXhGfRsMml1iyEfOONIgMVy6K9ywsf0OMOIZ9W0waHZ76ELJxZUsMVw6TvvD4snAJIFn1LfBoNkFBmuPWmIQc8RDdhnToYk+WwFn1HeBoJklg8D9shgXCJfuAj7GhhDrDGeUt4GguSWFxC2zVORc+dAdjPsQ8Igg6Iz6Nhg0u8RQDm9SsXKyHHQslUI0yQg8k74NBs0uMDj8nfU1ysly0KvlbbC2veUJz6DvgkG1SwyOG2imyMly1Asjgfp+wjTOoG+DQbNLDIGbaM7KyXLU82FyzqZIPIO+DQbNLjEkbqR5eYcc5HSEUlJ2Es6gbwNBMUsGhTtpoXgJYdATPhnnv1/gXPI2EDS3oOBbPHSy8lQYdUwMJubgVjqXvgsG1S4x+MPElIOcLGcd02OoQeIZ9G0waHaJgSGcvhQ5Wc46fReFzilvA0EzSwiZ+2rGyqly1qsr1RYFzl3fBoNmlxgqN9dskFPlrFeboqsKnru+DQbNLjAEyw02GZog5Zq8AufSd4GgmSUDzx02rAclhEmH7Nvb9nmYS94GguaWFCI32WKQU6XUzxDNZZy9Qjd1u8SQudGW8nJFCP0M1l3G2SuIV7dLDBX3OAwsp0qpX2eDGGe3s0GzCwzMAXCmBjlZSt3m/lwpxzn1XTCodokhcNsNz4cSg9DvGJ7Bsw0GzS4x8MkoLhsTQr4g6HC2gaCYJQO+LnE+yMnyWV1ltg0EzRXzbgy33kJOwq3Ur3NBjPOTz4U5YeiAt6dnfc1Q/u3XtzmxaM1DYT7Gx6dML8nflz8lxvYQmHGnj8X7HpzpUvItMrHgIFLwsQcsJltCWxriL3OtufY3TK640F674mc8UpjSI3rwQ2h5S6VyswITSXsth7Om9F1/w7Exu0CuBy6s2MLjK5BYi2+3vct0Mfi7nIN1ocsg0MPmajy8q9xfquHwzI92XYWbmvmeNGJBUzCZ9112nDMMunLGwoHLock4KJPsuScPV+7cjmYecfE93dBhMizn/qyNHn/aEqGCi/zDrqfi8HXcnMMoxrjsz41MqFhU9w0LYOsvJUzAMda2mxO5sQOEfeMz4QvKER+G2ww+qna23ALDUYLGzYEcX/73VzyGqaoRDqG7I0VbUzn1yMeZ2rbMcsj51Cv+OODYKPtss7nvvCWs+1IbPePUbnit5SH4WLn/iAOzuT8+Qk7F921Jfo2uhnJuzuBb4gnpksdjJb6RZhbnOcwmg091jPBwLeCPuxh47gyJLD3PumC6jjMz55ZsBkwuldJi5q3N8IqzkK/7C+4BOTp/6vCacPT8vpkrH/z5Xhym2oULNtmckTXW8nzz1eR2MoXgi6nnC2RnHUHxlEy4yfhGwVk+JibX47SCDanTYW4dvlGeqzz3YzKmv3bz9JX7xYQJE4/XTcbh1+jPOHhXcdz9xQxOaIDy6dbj43FA+Xwk8YUhcvcHM9O3wHhfSaak+7LM2/7OU96n3PklPqPP+YSOtx3ejfr99MfTkLX6ARhSz07++GwZAvzJK5Oc1w9+cXQDY5+aLAlSuD/gdPcB35JndnAf6flMx998/d/ffM+/v/3mw3fffP39mPJ4VpV4TRmIXntCloNYKknM5SBw0h4mrwlbuE0dNaXoBcRBn2gN46xlIXiuXL/QJrA3zmxPVj3HHK4+3FKCTLcZ9HfI9Hc2YWbiaO029aY6B5+VQvZHwJRvxFc56O9BIeGRhqOZwkD0T4AQvyQE77jYcDk7eT5f+jtA8JgG+mi4n7+16MNnPRdeeVm/sfYFl0ijg08qBPEzcXCYik3AisDOHAb9nTi0GwUWNhWLq/rSKfFzkYiMfcBCLgsSD/29SPDB30YTasovlg5YSkV8KRSVITCJi7sZxUN/LxRY5tmUuYWI/38BhX+uhgLWlWY8yd5eS+HNcPmw/anPlT/+sP3ikzqXJpkPySaJh+328MR8YPGwPeh8tKiYqm3Xr3Em/XrY3gWHZpuLHMt4Vy/zqgcdz814osKcVwSmUd8Nh2qbODwW4m0RLnBcOkv3mBBLu+OPmEZ9OxyabeLA0w1+T6aZjnrl9ZGjTTOmSd8Oh2abODIjwrNMOR11PO/zhUDLqpswDfp2ODTbxIHFfAlF3kkHGT8Zz3dEM6RR3w6GYhosPA4et8Uq3lhPejmCj7m9exmHGeTdYKiuSSMwPtY6eWqMej5iwGI/SEqDvh0OzTZxJMbJOpmpPemZyXh8JblguvTtcGi2iYPvMr2XWduTjgduX2N7XziNM+jb4dBsAwdfz2P1IBNXJz0dwYRc84rp0nfDodomDnckl5NMYp30hOOOPTNLYLr07XBotokjHBkLKXnrGORW3KO2/CwB6dK3g6GYJot0lGDb4c0wBj3Bn6vRL5AueTsYmmvSKO19fJST7Ki3mj+4OBRKd307HJpt4IiWAbZYWwocs15rLSVJTIO+Gw7VNnF4Rtp6mQgu9JpcylnBdNe3w6HZJo7I7fAgk8KFzlpZbVtdYrrr2+HQbBMHns5zjjIRVujVV992oiWmu74dDs02cdQjlJCXa2WUq4s9rkRCuuvbwVBMg0WyR6yQ5CQ769XZHJIC6ZR3g6G6Jg1/pFrq8pJj1qs1LmiU7vp2ODTbxBEZmWutnGRnvZpSWvSUxHTXt8Oh2SYOFpDyTiaRC53vulr80oLp1LfDodlmrIBhpK6X2eRCr8baFoe1YDr13XCotonDHdblKBNoF730ULtlnFPfDodmmzgCxg2ynLmUk2k7yMsop74dDMU0WbAIpc0yoXbRU27lqxdI2e74QlB1TRrlCKFUKydZqQfflxxynFPfDodmGzgY2hqTkUXMFj2Yvie7YDJb7smqtonDcVPRFjnJSh2zq4rp1LfDodkmjsBNRS+z0Bfd+hYDvYxz6tvh0GwTR6vSFWQ2+qL71Lch5Tinvh0OzTZxFO4rylKoUna+79fLUU59OxiKabColruKWabiLrrtu25iFLvlnpvqmSz8YWspQU6xz+o6u+1waPaIIx7exLpsq0j9fhOV42x6E1VtE0fmlqK1cop9VtfxbYdDs0cclVuKTmavr/q567aMs+eum2r745NnipnLXmaxL/p1sYhx9rxYdNvE4bmrKIukPSvr8LaDobgji8g9xSQTdVe9XxHLMFteKLpr0shHDVhQJUnjOV2ntx0OzR5wWHOYGPElC9uLfj855Dh7nh2qbeJw3FI0suzq87qObzscmj3iCNxStDlK21I/z45lnE3PDs02cSRuKi55p8/rOr7tcGj2iKNwVzEEOZ0+q+uYtsOh2WNyEVP4bcxynn1OfwbTbjhUe8ThuK2YjZxQF/28dyzjvNe9Y04YY+WM2yfCWStnTKlG5ag1+pBbyhIj522ofi6pwAScYEx1oSXfYVxzlysWWixaHsxR2v7r7UzdwTNbge7tkWNmFs+3Lc852pICaz2zoIEz+Z4CZEKB8VYlwcR8z3nBjdik6qFjxsYntQIVLfejRsfG6Aa2K8td3noSBJtstCLBEUdZW5kLyoUNuepZQiPFHlzh+ErKskBgdXiqSqZ7ZXadNyzaUZsn5tn1MHqMXFtns3r46ltlDXxd+BJyadE75UgxtPaPzoNeDMH0+iHes1YtZTy+JRPOmq1Yrdd7/G3yqdYePuhdta3YBGMsMUp2vTgJTLlWxYFhIQZf0nnNMX/TlXOXM/lc75Uf8WDUOC4nax/+GfkVRROeS999LsseQ+uZvR+fTdjHn7whSVg/gBc/5TVFFJxjzQ58Da5V7H9FFQV7++r2u398+OFPP0cRBZ9x+rs1+Qq/dTjcs5JY/w36RGsYRy+icP2CjWKv+h0y53Mr02Nl+YBBf4/M+cSSOSU7XFqsivZs5nwFoZRrKriTKZUE/oqx2fuN58Hjpz985Ahf/eqbP3/973//3dff/fDVxw/f/f2H26/+cvvtmfv6OREGx0I3danAMOjvgDDg9sL0TdMj5t9ShuKzQsjsTykr513qewDIuH1jLGMw1dS31aD4vFfS624Hb7ySQp4dfFLxheOLngvjDY2D+VpcmkkM+juRYFuLhGuNpZm8fxHGUnbgS8EIWEZh4AXGQ38vGAEwyKGkktKLMMLPBSPDtC9cds4wHvp7wcjM9S7RYwFbX6rMgWXjM1UYPNfj47n29ioMb8b7ZR/gcuWD19K7F0Nykbs07130ZM61jRjn1K8HuE1wqLaJwx/FrK18F917HdOpb4dDs00cjJxe2/ouOlaCScN06tvh0GwTB5Yodm3xK/SKJ9gTxzTOpW+HQ7MNHOzX6tZ2v0LnI1e7+UtMd303HKpt4nAMDV1a/wodJ0SfCxdMp74dDs02cQTGhi5tgIVe+ZqgKJju+nY4NNvEwbKua0tgoeNssF6hdMrbwdBME0bBCm5pDjzKiRH4zi+IBn07GIppLJUNS46ujYInPbFubHtHKhid6mYodM9k4Q4c3tIxeNLTwdSM9i5WMLr07XBotokjHCWvrYMnnYUnkm+7KALTpW+HQ7NNHIlxoUsL4UlPRyqMD1wxXfp2ODTbxMFC3msr4UnPB/sA2gXToG+HQ7MNHNYyMnRpKTzpmTNp740pMF36bjhU28ThGRkqY1lGuR4u+V6BfBpl0LeDoZgmi3hgLbW0GB71igf54EzbtRyGGeXtYGiuSSMfrUmAnGQHvXKrOPuWEztRGvTtcGi2iYOF/teWw4OOBedRrDHnpXKNM+nb4dBsc5PLHtmvrYcHncEJFssM7wWmUd8Nh2qbODwjQ5cWxIPOMARjWFJSYBr17XBotomDJVjWVsSDzjr10djWRnGkNMjbwdBME0ZhXKhsSPyQGSRk2FVHIhr17WAopsHCG1z7a2PiQWfp7GyCb/FCA6RR3g2G6po0HNZQa4PiQWcwUnExZCsojfp2ODTbxBH4q0uj4kFnTbRcfbJVYBr17XBotomD/cjXhsWDznC6kHxfcAyUBnk7GJppwijcUFwaFw86GzFgCnHRC0ijvh0OzTZwMNaxrg2MB93hOT6H4FIQmEZ9NxyqbeJw3FCUjYwfMiNc+TrUZAlp0LeDoZgmCzZLWxsajzrjerH8bDWbh2FGeTsYmmvSyNxMXBobjzpbvmVzPqeMlAZ9OxyabeKo3ExcGhyPemXXjBzrgmnQt8Oh2QaOaDHu2uh40uthcFbEIjCN+m44VNvE4Q9urcqGx5POd6Asfy8xDfp2ODTbxBG5obg0Pp70zBB23+I1BKZL3w6HZps4MufKZVtlkLGyYFfMLCEN+nYwFNNkUbmjuDQ8nnSsQHN1LR1mhnTJ28HQXINGstxQXBoiT3o6XGgdghdKl74bDtU2cXhuKCaZ2zjprG9ek68rpkvfDodmmzjYPxoe5I1j1GE72N5FV2K669vh0GwTRzlMNSXLSXbWa4VFLzEN+nY4NNtMFTHcUjRGTrKzXvGQ5hVKp7wbDNU0YTjuKC4vOSa5sidAVRDd9e1gKKbJInA/0clUaaEPNFRI28HQXPeMqmhrkBnUQq829HRdSemub4dDs00c7Ni+Jq0KvRob+tJcYLrr2+HQbANHMdxOZKvRGcesDzh0TLvhUG0ThztKMMXI++isQ+6JaAumU98Oh2abOALuh7l6OcXO+pV3sGB6t3yE96y+sKQYZRaAZNBwPnAsod77xKRWSyCw3ac5d+5xM2ClgB79l48UrTW+PbQbzKbuDPtiTCA9cecxg0I5I6B8zNb2RoAxG9fXJZbFD2LNPTIqOCzT8hkjhHtx8b0QgscRhx4eEvBZbETdgocMiIX++4mHE3oQTbYm9xdPjJkopsXc8Ccb+1HagjMGzu3NGRxYdqHeoy1cLTjO9rofv5xNPfXisGJghqbBQsqVVsQhOHMU5gz2TsN4aDf2jFKoeHTFN8kKDTg3sRA99+sNe6eGlsPovUk+n3vXmHMwC99Y8jmZmPr2fuIhM8KBu7iYl3s1D+pskVxz68Cciz0vR5fxTIiDzHP9DOp8Top33Ztw30mHWwZwuuoY+N1LgVKHW4YhORabKO1ro16P1sCRemu6lft3Ar3gl3KFzipwLBrRNk88zjSP7+nGDlUmenvqkb5wubQCHTyHWoEO7j0xvhZr1F6ro/rYde7ZluS5VxWO6H2MfZzKwhnVxVagI7H0hD/3KUKOubBwhwe3Ukx/C4lLB1ffuX/B7nr9c4PHueN8O6fYWY0V6c932rDuXC/dweJ2fccwtHanrFcFbDW61MPa2HcKwHil4Gup+On8WPaxwynm2qnMXZP+MI8TiXv0vp35+E+m1Trny0FQKy01oTC/P9X+Yijw2625N0vEyVCcPV8YFXx/qb8wKvZ8IR/xqbgP2B7JDjJnXGbCx6aQ++WMbyQXdz5LY14z5iz2gUfHfpTs5FBYYac/VOL3lVtgwc999Gf0V1T1eC4v/LmyDxhaTxn/+GwFCRYCeX32uX4AL37Ka6p6MJvNprajxPKrr6jq4W5f3X7z4btvvv7+5yjrEX27US7ZfLiijqUJwl2cQA0j6AU9Hr/gxdvVn55iHQMr4RgrU1UH/R2KMUTewjA54Cq1nIw+pRzFWoTgs3LAPSj6tYPWoL8HB64YC0fzBZPpp3Bwx1KW4nNySB5nKBY3MjRv0N+BQ2pFpDiaaXfoTynO8YXPh9dc1m88GbBWnA7/jbU5vhAGll3KmBazHzAM4jthaEsfLLOxCk3m5VoUX/TCGEkkLOywlnZuJPEQ34sEO64ZLEWYVPNSiRI7nxNDIYqI9Z8ZRvwJhSjezPaLPstGrHHZTknmx0ascblTIvNjB52hkcbj4cI3/Rpn0q9n2V1waLb77G7i0m34IbfChFw9ewFp1LeDoZgmi4KF59pteND5mMmI0SIgjfJ2MDTXoIHHMhz70m140Nm/DM98PklKo74bDtU2cTj+6tJteNBd6BHm7T3PiGnUt8Oh2SaOwEfppdvwoPOVBqtjFolp1LfDodkmDhx9XbsNDzqn3GizaW8TRkyjvh0OzTZxlIP54TJHdtD5+rDGYFp30AnToG+HQ7MNHMkceNaT3YYfsjPxcFj2t7eXI6RR3w2GZposWI7SLt2GB73FVGffW/yNkAZ5Oxiaa9LA06xduw2POnvNcKMkzZQmfTscmm3iyAyPXboNj3rln+KnLDEN+nY4NNvEURkeu3QbHnX2qcrBnNfKgGnQt8Oh2QaObBkeu3QbHnUsxEup2VuJadB3w6HaJg5/hLB2Gx71ao6E5WcrcDVhGvTtcGi2iSNyS0m+kRvltt8b2jbPNMqgbwdDMU0W+Uhp7TY86ZV71aZtnc2QLnk7GJpr0mhb8Uu34UnHmsukaIukNOjb4dBscyPDMjx26TY86cyaTqZPsjOmS98Nh2qbODzDY5duw5POhg7WtV710ziDvh0OzTZxsNrb2m140jM+OroW/iIwXfp2ODTbxFEODLp0G570dNQAAkFiGvTtcGi2gYMhSGbpNjzKDF0JseUfCEiXvhsMzTRZMGJr7Zg76QxiOvvlzJAueTsYmmvSCGzMs/QVnvR0sP6GCSulS98Oh2abOBg1sTbMnfR0uBJsX5zPmC59OxyabeIoDJBd+gpPOhO5YmydYyWmu74dDs32xydc/NxUXPoNTzoDC8/aPQLTpW+GQ7dNHI7bikvL3EmHbV/bNSEpnfJ2MDTThBG5qyjroo0yc9qMbxsGAtGlbwdDMU0WrPS29suddLiG/ZYbLCDd5e1gaK5JgxUz167CQq8JT/JeUhr07XBotoGDkfR5bZcr9MrYdQ3TXd8Nh2qbODy3FJeuwkKv0fT4Z4nprm+HQ7NNHJFbiku7XKFXbqEolE55OxiaacLI3FJcmuUKvfrU0z8kpLu+HQ7NNnFU7ikuzXKFXvF0UhRKp7wdDM00YDBHx64dhYXOghMtR0dCuuu74VBtE0fgluLSaVjo17khKG16bqimCQPLa4eHDTnFznp1ITivQLrr2+HQbBMHs+VclYEcQq8mtjfCktIpbwdDM808AMPtRGvkBDvrDxgqo91gqKbvSRHZyXRyobNVY8u0WyCd+nY4NNvEEbif6GU6+aJ7014IL+Oc+nY4NNvEkbihGI2cYqWeezbtMs6pb4dDs00chRuK+M8Sh9AvHDqm7XBotoGDPULzmmm56BcOHdNuOFTbxOGOWlw1cpKd9WqMaxEbYpxL3w6HZps4IjcUjaz+LvQBh45pOxyabeLI3FK0SU60Ur/PLHKcd5tZ3rMUxZhk5OoR+ZNtGSnu8KlWxkUHhnuW0goQUC+1tpbCsRzexdoy7yPdWhZ9aPk5zJ3P/tQZ3uNzK3DgsnO5j8NqcbEmUSih5fya6GtpujWpt5uNpOxDYUFkZvDXam099YQzL9ZWECHk6G3//Qpmjoef8NToUyurECPfPBnH4hL4w1hg69ZTBIKr1jN1oBwlepaT6LHyOKAWGYznC/JoD5wxRh5kbpW7a9sGOHWeCsGwiIR1R/Le2z5O4cGwr7Xjq0CXS+hhgvx2TI+39ph4K4t+U7c8Hu5eszZGML3GYEy+Zf9zVV9xmKHX6IwpMFKIcxT3rhha1kwllq70ucUg8uCht4NJ+HqsM3w3S8L4yfZgG8cnKBN7kF7xd2Y5wGHMZ3mQ7M8A2JzZ+CL3kJ2Ms6OjLDj7SqntGb2l5bDmRQ/WSCn2pLjclly5RzNkIGixxC1qwbZTi/tOlrU87l0Ak3Opnzc18uhrPbft6uO1fCj1zBWp7IkW2hsDg2PP17u1DOJZvWaDcy/or6id8FzS7nMZ9hhayef9qKfp45dfmxSsf+7z47+mXgITrzIY41RhPaZX1Evwt69uv/vHhx/+9HOUSwisJMM6L3wQ6jRsYsWa9qLtIX87yjDGF5NdP0eYtKtUwjL8K27ntzPx92k+qRixwF7wfjrohzodyNsSf5lUycFwy8TNpuJq+pHaCDI59zPYtrhd15hNrfOX9ZDfwbhl2XSOVnLFiD/u3H4J659+jr7NtXN9fEwA6Q35/p/PMU3G1hh5cvyQf6JjTK3M6seSJ6eXTbsvZ9oazLHZFG9n14P+E23jLs8lEzNjX0rjx+T9TBq/TVzsER8m6zdl778Wn+82P3Hh/mOr/mlV/KQk0LWSYlwBDpdcW8/VmuJ0xQmVJZ7aIm8a4VSv1f7/3+4VnwydOnB0ps73G6He3atMNnGv+ORW3RFscLj0RvezyriGBckp7uFdc8n3ZEd01qcwe5/UVPEQthI51U3cKz65NMbzRgms7Te6n1SsFUJemNzVTdwrPuE+4xkvJldn95Oafe7vOGYmp7qJe8Un3OOQg8vJze4nFYeeViZ3dRP3is+PT5lFM1k1cXI/qSHiY4Nkcql7uNd8wj0LZGbj5vluUnGqV7syuaubuFd8wn0rJ2rTPN9NarL4VC+ZXOom7hWfcF8Ol1tF3sn9qBYWQLWSyaVu4l7x+fGp8Pd4NU/uB7VglsP9rc5MBnUP95pPuHd45o8xzjPeoFY26LQmzUwGdRP3ik+4D0es/f3g6P5SPQsm9wr8jwEGcRPvikt4T0diEd95vnuobYPPpjoTGdVN3Cs+4b4cxcDGPN89VNb+w5UumIzqJu4Vnx+f2BPQBlPn+e6hVsc9ENdWtI8RRnUP95pPuMcF7PAtzvPdQ7Wc2lww7c3TY4hJ3sS/4hT+We69+DjPeA/Vsmh8xR2uzFhGeRP/ilP4bwnTYZ7yLrG9d8TytpUxH6CM8ibuV58wXw8W03TzlPdQM/sIhNgWtdcAo7iJd8XmxyfLHWU8scR50htk7kr75Nv++mOMUd3Dv+qUALjVnlrPhQnAJQfe5Hxo1QSGQUZ5FwKKVRKIDJpgqZWZwCVjieOjsVGCGeRdCChWSYCNUXCgQRC4ZB9x0Ky/MoMZ5V0IKFZJAAed8fCaBYFL9u7wNaUW4TeCGeRdCChWQcCyQ0gM8yzwUD23lgNjR2Ysg7yJf8Uo7QduWLGPy+z/IbMZUGIPlxHLqO7iX3FKAIl7ViydPAN4yOWIXPcKLA91F/+KUfovhzepWDEPDnJpy72wULmru/hXjMK/w5/Z1lZr8j/J1TLjTGAZ5E0IaFZJwHH3yhQxC05yztH1e+A0yCXvQkCxSgKBO1jOillwklPyvavVPMgl70JAsUoCibtYXtwERzUGH9qW9jzEJe/ifzVK+wykdfwiZ/+jfO1uTWNstuelOgUA3+IjmXQ+AZjkgMeePg3OXO7yJgQ0qyTgWVqw9dSbCIxyMHDhVjB3eRcCilUSYN8zz+DzmcAoY8mb+11QgDnlXQgoVkkgc1eL7f9mApPMtpRhAXPJuxBQrJJA5c6WDWImnOVqXNXAnPIuBBSrIBAst7fE9v+sphz9SuVUN3Gv2KR5Ngp1WN8J95Mco205JPMYd3UX/4pTAojc4YpBzINCTsWHlctd3oWAYpUEMne58GgvCEwyi/X4Fcxd3oWAYpUEKp7sMK6YB4Xs+tavHMTttCOsWmVqRWtCXIOYB4V8hvfKQbaK+lWtkkDgdldrVTcRmOX7OSAG2esc0KySQGrFlMVF8Iyq0trF/2qJ9gu/SB/ETCjk+yUwj7HZFaA4Zf6J4Y4XrMwAhHxdAfMgP/UKmJNmmNz8qZnfSnKzSMnwWK5+fErM8C3m3BathWm/3z5lPNRUH3oCQzQ+tPTUHI+Mwz7fA7EwWQuBw9+5UnsiVsmlJ5Xm0oowMVkNSACkPRoXy86NLfsSC0Ub8ynGFJjVy77t3udYenhVcp413T3TS31pNROLPyz7mzvuvOB6C62ZWwkHfpGlaX3APOyMPWOUcLjV+5tPR7A1tZA1eKg113DDqi3ZWtpLG3Yed5UxDZjIrYm2H0IimxQDvzdfYm21b5mjywbu8YbLPZtq6hkUEwJ72t9Sa8gYWyBgNfDLTu63yjSmlFqAHLN5Kzun3/gi2VXTe15Utqr3XEfy1VJg7eEWb0KjjuE2fNpmelpbXUFO+N4LZQ9PqbTU8+qJorD1DJ/L6plJ3rLKMWxtv52s7zRaF3mTmLfsmPlqTSvUiYOF8cq6J6YckdngbRCmJGfmX7W/c5Cp4jdwUjBSIOFDXIh9p5jHGgJOsuQOppOfW2ctZcrhERHnoz9BW4NfsQ5HTNIWX1l/sDCeh1druoXAvN12ujDuAt9F231ifmKNDao1qXUSwQIExjMM9nc0hicwTq8Wpoffzea+hWVYsLWdRwb+2tFZA6i+bW2agwZNOLd7QI+XFb+BEoPvewMeZ7axTEStONCUaj43RzyuB5x3OPVxMvtzz4gp15lpYyzBVHNoKeJ8jwzr/JZ4+URv+9jO87V6X02lyLPwfOGEk965/sIJ95D2gbhAcBzF9JcwvpXW7G8m8IG1x7oEdhMpyp2r3QeelV+R660kaWqZwRhRzd38+Gx+Mf7iVQmg66e+OPZrcrwBKTGpx+Le6DBSuo/0Yyne4fbV7Tcfvvvm6+9/jhxv1l8weUls8ty7SS1bfUQ4yBOrYRQ91/v6hTabvXWWe9JOLlaWKKEEsTob5HdIfnZM62yjBbYVeUPa9+ckkHjbczj9ZgIP+T0IJNxK2mi4/j8l8X3p+P0ZCXiGHcRURLLCIL8DAdz8z9ECi0m+JQH+s14Gr7mQ31gCAEuk8fDfmBT/ZSikNsMaX2YKD/mdKCSuP4qPXKq9DOJLXhHjHZnFXXKruT6RGPR3QsH4r4pVReLzgX3pyphPijF9Hk8HcYD79jT6t8L9okW0nG3Pgib5ZfLCkgvz9HTVDvK0hB9GmfTr4XITFoppsHCWYZVeJNoOMlfsMTLrQjAa9c1YaKbJwrdwCpF2O8i5MufOtQecYZBR3o2EYpkksGrHr4lExEHGgty0SmgzoFHejYRimST4biVnkZQ4yHxFgeffFo89DDLKu5FQLJNEPSrW/OKmeaks1Vd9qyg30nmou1FY7QKCx6Hj9lfn17KD7MthvbPBT3BGdTMOmmOCCIzEZGf2GcQlY6o0vrY3RyOeh7obB8UwOSQGZDqRtjvIHut4msgCzyDvRkKxTBKFgZlepPAOsuf7ZkYtCUCDvBsJxTJI8GnY9WqmI4mH7DEunuxsFYAGeTMSmmWS4GZCTiLBcZQrq666UmdAo7wbCcUySYQje6wHBIhLrdw+CAqeS96Nw2qYGBLrtVYn5s9BLvzm+97OMMag7sZBcUwQpb0sjmL+HGTcFlKodeVzybuRUCyDRLQM5MRkOJMYZCyeSt/znPnc1c04aIbJgS/8vBf5wLNcvWkbr/MYd3U3DophcojcfAwiM3iWMx4x00Lnru7GQTFMDvhHzlGkRs4y82Hbhvc8yCXvRkKxTBLcDcByQIAYVHyedXXFc5d347Aa5o4TFsrVtjrwI4dJZjl4XySeS92Mg+aYIPyRaqnyVcQks+p+UPjc5d1IKJZJIjL4k00IZhKjHFlcP6+A7vJuJBTLJMF0SO9EHvEsYyWZWoDLPMgl70ZCsQwS2TAY1IuE4lkO+GcLchKA7vJmJDTLJOEO63IUaZWzHFzxrXKeBHTKu5FQLJNEwLAhyVNiVHM830fMQ9zl3TishomBcSaMpBMcJrlE2/f9pjHu6m4cFMcEwajJUq2YQWc52xY6KMY41d04KIbBoRju4RlRgUrI2bTYXEmnq5tx0AyTg+MOni1i9pzlFKqzK567vBsJxTJJBO7geZGILORUWsSp5NPV3TgohskhcQsviHRkIT/OiBnPpmeEYpkkCjfxogQxqjGVln8thrjLu3FYDQNDC563WaRmCjnhM+2C565uxkFzTBD+sLUwf2EGMcnRmJaJKAa5y7uRUCyTRDy8iVVuasxyDNZ5BdAp70ZCsUwSmXt4TECeScwyPlYBdJd3I6FYJonKPTwnEpilfJFQAe1GQrHM8GimFWUvEpmlXLILCqBT3ouEapkkPLfxRIUroZ5fvRxiyzNCM0wMkZt4SaR0SvkCoeHZjYPimCDyUUPLlppBzPIFQuWzGwnFMkhYc5gYq5lnUClfJFRAm5HQLJOE40aeEXUwpXwnoQPajYRimSRabqjNUZB4Rla57UZC8UYSiXt5MtPwWVkFtBsJxRtJFG7mhSCmUCFfV8c8yKZXh2KZGTbMNLMxi0lUyBcJFdBmJDTLJOG4m5eNmEWFfCehA/rJJN6zC/yQaMMW38Uyp/xjk0u2ofLTy8H3bT3Ynl8u+7+wT0Q67Fn1gjK73EfK8WBKYeuT0VJX8NBVqDPtPdQzvYdB2tXZ2KoC4GNK3xlzeHIPBb5vTJ6POfjYsx2YuJ6qT7fC4WvrW+5aul7MNd9Yw8HhsHqSCG5KLqRYbskfga35bj1NgL0MbLnFdMSC9W6PpeeLdodfurFDvKmxHyC3o4xljYR82Jxs7qkGDAQzOLRb8Fgy1zN20OE5AoBSq55ec86tl7vzFgedXWwB6iaE1LonMDDbpoQv9dbjcVPfKPaeR13xQT4c1WDF0WN2w8E6FSkyepk567nLmUfC/u34FHak7yZ9ZeqebS25YR0LuPbbwfIjQ8vdL5GhbE31HDqWVovChhJbXVuGe5lQjWttnljxsUU2GJzhwNBiwEApmC47DlF6/8uCT2hNEhwJM+aaamK5WHtuhQeDkXqhDXCJ9zfbPgR3ZjvDSTnf4YTk7m83I44ynQtTfDfRnDGvNrRKtPICdCm/IL8iz/+ZHNTnssMxspqe+vHZPHP8xavzXPVPf/EzXpP3364f/GV2rer7KxL/48/Z293ncHi3pFXhl3Dfwd1xXu4P8sRqGEXP+79+wcZ57/unZ7jiWjtMKlG0ox3kd8j5DpyUOBrLc3xKzveS7Pw5CWAOqtF48YJrkN+DQHHnaIb35h8n4L4kgtedxm/M+8ekNh7+G7PevwyFbI4cQoxppvCQ34lCSqzMVFIp7OT7Egj784BgrR3M0iylM5EY9HdCwTRjLNSKMa7Ul5vGu2ey3j1b5Q1w3571/la4X/Z5IVcu9WV3TYzIBYhsrylkVtCsbe6ZBrnL1/PCHiQ0yySBxaZZWm1KmdXZVkB3eTcSimWSiAezEUXOnpQvEiqg3UgolkkCjxJ2acIp5YuECmg3EoplkMDdnNZEwIOQr/vEPMim9wnNMkm4o/UWEG/dZjlZ3wLBxCB3eTcSimWSCHzAlY06hZxtahX3xCB3eTcSimWSYLXDpWXnLIe2dlsAXfJuJBTLJFEOzAUidHJSI8TWlWEe4pJ347AaxuLXsDTh0sZzltmorr2cnMa41L04qI4Jwh04OtnRc5YL7gWteOk8yCXvRkKxTBLhKHlp7TnK7F7vem2wcZBB3o2EYpkkEsMAZYvPUS788nvd4BnQJe9GQrFMEqx7u7T6HGSP5QNmiCgAjfJuJBTLIGEt4wBly89B9u5IMbv2Vn8ENMibkdAsk4RnHKDYun+oPrNCsmv1t0c8g7wbh9UwMcQDayTZAnSQg8faKfU2Z48xRnU3DopjgmBW5tIKdJCjP4z31uWZzyjvRkKxTBL1wOJANgUd5ISpkrX3ygxolHcjoVjmhos9sl/agw5yxd/1Wu0ToFHejIRmmSQ84wBlm9BB7vXYMWiZCU36biwU02QRGQkoG4YOMj2zx0LbcxeMLn03FoppsiiMBRStQx+qZZ1OBwtJEBr13UislgGCS8W0NBEd5JpbwEILU3iMMaqbcdAcE4TDLLA0Ex3kDA/Ft/26YYxB3Y2DYpgcAn9TthQd5JgPdkzpK8zHIKO8GwnFMkmkA9+wbC06yCFhUW1bjZRhjEHdjYNimBwKd/Vkg9FBbj8xS23GM8q7kVAsg0RgENnSaHSQcRY4vrEtAtAgb0ZCs0wSjrt6ouHoQ2VMn3e2hemNeAZ5Nw6rYWII3NKTrUcHmROFMzHbGc+g7sZBcUwQmTt6sgXpKNeDPbuKm/mM8m4kFMskUbmjJ1uRjnI5MsznKgAN8m4kFMsgES2GXVqSjnI5fDKhL6kmQJe8GQnNMkl4/kO2Jh1lboiX3PqazYAueTcSimWSiNzTk003ZznjuSJnAWiQdyOhWCaJzE09uckxqiwE0ZIW5iEueTcOq2FiqNzSky1LZzlGTJtW4rnU3TgojgEiWe7oydalsxxKjC19Yh7kkjcjoVkmCc8dvSRS9WY5uJK8XQHd5d1IKJZJIh61wIG4ScxycablKolB7vJuJBTLJMGkHVOymEFn+UFCBbQbCcUySGTDPT0+VU8kZjmn3oFXDHKXNyOhWSYJx009+VpiUi8OOp7dOKyGiSFwS8+J5FYh32Ms5zH2jLxUHRNES9oLIrdVyA8QKp/dSCiWSaJwR09m3gn5QUIFtBsJxTITrww39FIWM+gzss5tMxKaN5JwRwmmGHG7fE5WAe1GQvFGEuHAerF6MYMK+QzPl4O8V9T+e+bAL3k3rtYWbJsPHEro3RsDLutyj6J0vpqzXTnWBq0HPEMKWXyQYgXwFmzL6DrcKWMPuvPenS9rTDyM6bvtkIstrY0h5eg9FlyUY7S9kmEw7Ipurenp2zmmM16tHC3YoIWxFbjzPWgJh8fcgMjgrVIY+HvGMvmYrU0tvdyannCPWzi+tcje6r4eJuRYetBH4JGkWhjxUx1+/x4eFKtjMkooIGLvcuIBBneLgf1XXY82tYWc8JE3rCctZoq+0wMZX6nF4pLFn5lMX84oChw23+nifhrwyx2JMzzs4N2tej6613ucAT7U1X5emITrresOfxpb1jLfeJiUWj469+KNwbnHvXh3ZBuSue/RR5xvNsxdRnvGrQ8BpyHz73Opxp472TCafcL49ahYIfc30yyFAKAptc9NBqeQP/VsWXXuxh73MJVN13N76Iq5HT8T6PvvM2kfo8B68XgYs73RJ3cJcZieLYDxjMJ90/apHkdfvQXtGNkF3md3bqWl1tKJ/RBTjaEz82wKbyqABPwCzt9Yz/0mRsJihdtqFwBSP5KMEbHKa7tTJuEssueeTMEZXyObygFuPX8bMPD8FFuLNXy47diDBXb8K93aPqd1p+z4lfF1jMf3WIo59zlAwuE7be/3syu5M8HRgi3rG/DsL5hgwvnm1+NYS7uEcoyl3t8TY0K2pl9CuPj72cXWLQmH2Xr52HvteVyNGLHYvi2RYnC2vx0oHCScpfxjzq0IRWCjTdxGbH9ngLtq++XEE8qWnq/Lj+iRYinxdujNWeg7+/5VciGNm/IZlZhB2Ck3zxzzC/Ir6hc8k038XJY7RlYTjT8+my/PigevzVjWP/3Fz3hN/QImitnUdopY+PIV9QvS7avbbz58983X3/8cBQwiLkV8wTJLDpfTIavI37UJ0/D3eumCxy/4+XXqT09TjgwMNTj757NmkN8hcT/yNtNGwzdb31K64HMSYDFJ3CfFm5dBfg8C1fXRDGuxfAIB+0URfPoJ/Eb7bH89HPsb6xZ8GQRc3bRb/YDgob0TAvZUwYRjsJp0LxctiD8PBYsFZipYOA4UHto7UcBq9ogssORwcYSXLof5ZBjqFUSDJeLA9e31Ct7K9Ys+20UszfBYJnuH8n7NvQCRaTnI0zp5GGXSr6e7TVgopskiHSbK7qEPFWvumovLAtAo78ZhNUwMWDCnpXvoIGMljgeL0p6oHmOM6m4cFMcAEQ1jNWX30EHGc03GGj1UwWeQNyOhWSYJx9+U3UMHGQs1G1NPhBgGGeXdSCiWSSIwWlP2Dx1kzwAlPP5ZAWiQdyOhWCaJ9pQlO4gOMl+k4CJwRQAa5N1IKJZJohxMLRa5loPMNvTR5CT4PNTdOCiGwSGZoxjZQfSh9qSYXm5zhDPIm3FQDBODw1P/0kF0kPm2K6fc3iYNeAZ1Nw6KY4KIjMaTHUQH2bMZ4Nnia+ZzybuRUCyTRGaspuwgOsosKIvlQ5oBjfJuJBTLJFEZqyk7iI4yCwtn25p8jYMM8m4kFMsgkS3/ITuIjjLfZyffGkPOgC55MxKaZZLwRwhLB9FRLodz1baX8jOgS96NhGKZJOKBlVKSp8SlFhYlCG2jbMZzybtxWA0TA8v2LB1EZ5nNpq3AM6i7cVAcE0Q9clo6iM4y/si2za15kEvejYRiGSSKZaym7CE6y9k6Y9wK6C5vRkKzTBKesZqyi+gsJxtKnzemQS55NxKKZZJIjNWUXURnGWun2MI1JKBT3o2EYpkkCt/Fyj6isxxTwFP3AuiSdyOhWObuG37TyD6ikxpZ5NiteO7yZhwUw8TgjmCXPqKzHEpILbRiGuNSd+OgOCaIwE4dso/oLAfvUgv3EXzu8m4kFMskkQ78JPuIznJwji8rF0B3eTcSimWSKIzMkn1EZzl4U1o/FgHoLu9GQrH88SkZw+082UdUyDWEFpglBrnLe5FQLZOEa9FbIgtZyCWHFhEqBrnLu5FQLJNE5IaeKIU1q6n4alc8d3k3DqthYsjczpN9RIWcfWyxlvMYd3U3DopjgqiHS0sfUSHjyULBc6q7cVAMgwPjqfPSRVTI0eXWelwMcpc3I6FZJgnPvTzZDVPK1beHbjnIKe9GQrFMEgyPX7phSpnBewqgU96NhGKZJDL382S7UCnnmLIC6JR3I6FYJonKDT3ZRVTImCtaAoMY5C7vRkKxDBLOckdPdhGVcuzJDXKQU96MhGaZJEKrSyIyLYUcrfF5BXSXdyOhWCaJdFiH71bMokJ2rXKmHMP5De+XmmFyYK6UqyJYQsp3Dhqd3TgohsHBG/7DGjGDCvnOQaOzGQfNMDk47uY5kUkr5ZODSmc3DophcgjczPMij1bK9zlDDLLnnKFZJonE3bxoxOz5nKxy242E4o0kmEBaWE15tvyMrALajYTiDSTYYzEvGXbPyTqgzUho3kjCHbW4asT8+ZysAtqNhOKNJCJ384wozf2srALajYTijSQy9/NsEnOokM85VIzxTnPoe1YpGJNtTD2sr3hKZn6GO3yqNbLqOt+/lpB6AoLDkdXWztUy+xl/0Hb3mKoSiwvplnFElUnpTQ4tpzkn5j7FHKzvaR4MPszW5FtkYeJcSjplxtjgT6PnTOPiPfsjmehZNqAeOFZ/5kIwEgW/U1nc2btWbqCnSGCyZjgfjqkVIWu/HQHCsj4AL9OQu5vI10bGYUJrSQTVn7kFmPFdtb4F2uP7c/4ef49jacGj+H4wVt/Pi5GHl3tF4eKca/XXGXycWRSwpakHfPk9Rh0cAAqWWyxuwlXSYxMDBklnMQaPX7/HbmKM1EuyGudYIoiRJ46PLObeZ6q0dgiMzPEWh9ruP8UXNrbmBjTbuoNID0kIvpQeqcB3yDb2+zbo2FzOjahqrOvhPYEVKdL5fi07VpE4nxZty9BfzvqYnlVfkV7+TMbnc5nIGHlNBv2oZzPjd1+XT6p/6POjvyalvJ2qCU/evlW3eUVKeb59dfvdPz788Kc57/Aa2SbX3gyzWgdO2nG8MIzHAX/5z/jtA1emwRnscB3+0x/+9uG/vv7bh798d/vPDz/87fsP/+fv7R9//Mv3t68f/+2Pf//uD+2HfhxP//F0pSs+/T8Xm1h7CmVuZHN0cmVhbQplbmRvYmoKMTIgMCBvYmoKMTUwMTIKZW5kb2JqCjEwIDAgb2JqClsgXQplbmRvYmoKMTcgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA5MSA+PgpzdHJlYW0KeJw1jLsNwDAIRHumuBH4OID3iaIU9v5tiC0X3D3pifNsYGSdhyO04xaypnBTTFJOqHcMaqU3HTvoJc39NMl6Lhr0D3H1FbabA5JRJJGHRJfLlWflX3w+DG8cYgplbmRzdHJlYW0KZW5kb2JqCjE4IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTY0ID4+CnN0cmVhbQp4nD2QwRFDIQhE71axJYCAQD3JZHL4v/9rQJNcZB1g96k7gZBRhzPDZ+LJg9OxNHBvFYxrCK8j9AhNApPAxMGaeAwLAadhkWMu31WWVaeVrpqNnte9Y0HVaZc1DW3agfKtjz/CNd6j8BrsHkIHsSh0bmVaC5lYPGucO8yjzOd+Ttt3PRitptSsN3LZ1z06y9RQXlr7hM5otP0n1y+7MV4fhRQ5CAplbmRzdHJlYW0KZW5kb2JqCjE5IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggNjEgPj4Kc3RyZWFtCnicMzU1VzBQsLQAEqamRgrmRpYKKYZcQD6IlctlaGkOZuWAWRbGQAZIGZxhAKTBmnNgenK4MrjSAMsVEMwKZW5kc3RyZWFtCmVuZG9iagoyMCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDM0MSA+PgpzdHJlYW0KeJw1UjvSm0EI679T6AKeWd7LeZzJpPhz/zYCOxUssEIC0gIHmXiJIapRrvglTzBeJ/B3vTyNn8e7kFrwVKQfuDZt4/1YsyYKlkYshdnHvh8l5Hhq/BsCPRdpwoxMRg4kA3G/1ufPepMph9+ANG1OHyVJD6IFu1vDji8LMkh6UsOSnfywrgVWF6EJc2NNJCOnVqbm+dgzXMYTYySomgUk6RP3qYIRacZj56wlDzIcT/Xixa+38VrmMfWyqkDGNsEcbCcz4RRFBOIXlCQ3cRdNHcXRzFhzu9BQUuS+u4eTk173l5OowCshnMVawjFDT1nmZKdBCVStnAAzrNe+ME7TRgl3arq9K/b188wkjNscdlZKpsE5Du5lkzmCZK87JmzC4xDz3j2CkZg3v4stgiuXOddk+rEfRRvpg+L6nKspsxUl/EOVPLHiGv+f3/v58/z+B4wofiMKZW5kc3RyZWFtCmVuZG9iagoyMSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDMwNyA+PgpzdHJlYW0KeJw9kktuAzEMQ/c+hS4QwPrZnvOkKLqY3n/bJyXpihzZFkVqlrpMWVMekDSThH/p8HCxnfI7bM9mZuBaopeJ5ZTn0BVi7qJ82cxGXVknxeqEZjq36FE5Fwc2Taqfqyyl3S54Dtcmnlv2ET+80KAe1DUuCTd0V6NlKTRjqvt/0nv8jDLgakxdbFKrex88XkRV6OgHR4kiY5cX5+NBCelKwmhaiJV3RQNB7vK0ynsJ7tveasiyB6mYzjspZrDrdFIubheHIR7I8qjw5aPYa0LP+LArJfRI2IYzcifuaMbm1MjikP7ejQRLj65oIfPgr27WLmC8UzpFYmROcqxpi1VO91AU07nDvQwQ9WxFQylzkdXqX8POC2uWbBZ4SvoFHqPdJksOVtnbqE7vrTzZ0PcfWtd0HwplbmRzdHJlYW0KZW5kb2JqCjIyIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjQ0ID4+CnN0cmVhbQp4nEWRTXIFIQiE956iL/Cq5Fc9z6RSWUzuvw3NvCQrWoXmA9MCE0fwEkPsiZUTHzJ8L+gyfLcyO/A62ZlwT7huXMNlwzNhW+A7Kss7XkN3tlI/naGq7xo53i5SNXRlZJ96oZoLzJCIrhFZdCuXdUDTlO5S4RpsW4IU9UqsJ52gNOgRyvB3lGt8dRNPr7HkVM0hWs2tExqKsGx4QdTJJBG1DYsnlnMhUfmqG6s6LmCTJeL0gNyglWZ8elJJETCDfKzJaMwCNtCTu2cXxppLHkWOVzSYsDtJNfCA9+K2vvc2cY/zF/iFd9//Kw591wI+fwBL/l0GCmVuZHN0cmVhbQplbmRvYmoKMjMgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMzIgPj4Kc3RyZWFtCnicNVFJbsQwDLv7FfzAANbuvCfFoIf2/9dSyhQIQCW2uCViYyMCLzH4OYjc+JI1oyZ+Z3JX/CxPhUfCreBJFIGX4V52gssbxmU/DjMfvJdWzqTGkwzIRTY9PBEy2CUQOjC7BnXYZtqJviHhsyNSzUaW09cS9NIqBMpTtt/pghJtq/pz+6wLbfvaE052e+pJ5ROI55aswGXjFZPFWAY9UblLMX2Q6myhJ6G8KJ+DbD5qiESXKGfgicHBKNAO7LntZ+JVIWhd3adtY6hGSsfTvw1NTZII+UQJZ7Y07hb+f8+9vtf7D04hVBEKZW5kc3RyZWFtCmVuZG9iagoyNCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIzMSA+PgpzdHJlYW0KeJw1TzmSBCEMy3mFPjBVGNtAv6entjbY+X+6kplOkPAhydMTHZl4mSMjsGbH21pkIGbgU0zFv/a0DxOq9+AeIpSLC2GGkXDWrONuno4X/3aVz1gH7zb4illeENjCTNZXFmcu2wVjaZzEOclujF0TsY11radTWEcwoQyEdLbDlCBzVKT0yY4y5ug4kSeei+/22yx2OX4O6ws2jSEV5/gqeoI2g6Lsee8CGnJB/13d+B5Fu+glIBsJFtZRYu6c5YRfvXZ0HrUoEnNCmkEuEyHN6SqmEJpQrLOjoFJRcKk+p+isn3/lX1wtCmVuZHN0cmVhbQplbmRvYmoKMjUgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNDkgPj4Kc3RyZWFtCnicPVA7jkQhDOs5hS/wJPIjcB5Gqy1m79+uA5opUEx+tjMk0BGBRwwxlK/jJa2groG/i0LxbuLrg8Igq0NSIM56D4h07KY2kRM6HZwzP2E3Y47ARTEGnOl0pj0HJjn7wgqEcxtl7FZIJ4mqIo7qM44pnip7n3gWLO3INlsnkj3kIOFSUonJpZ+Uyj9typQKOmbRBCwSueBkE004y7tJUowZlDLqHqZ2In2sPMijOuhkTc6sI5nZ00/bmfgccLdf2mROlcd0Hsz4nLTOgzkVuvfjiTYHTY3a6Oz3E2kqL1K7HVqdfnUSld0Y5xgSl2d/Gd9k//kH/odaIgplbmRzdHJlYW0KZW5kb2JqCjI2IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTM2ID4+CnN0cmVhbQp4nE2PQQ4DMQgD73mFn0AgQHjPVlUP2/9fS9h20wseyYBsUQaBJYd4hxvh0dsP30U2FWfjnF9SKWIhmE9wnzBTHI0pd/Jjj4BxlGosp2h4XkvOTcMXLXcTLaWtl5MZb7jul/dHlW2RDUXPLQtC12yS+TKBB3wYmEd142mlx932bK/2/ADObDRJCmVuZHN0cmVhbQplbmRvYmoKMjcgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNDkgPj4Kc3RyZWFtCnicTVFJigMwDLvnFfpAIV6TvKdDmUPn/9fKDoU5BAmvkpOWmFgLDzGEHyw9+JEhczf9G36i2btZepLJ2f+Y5yJTUfhSqC5iQl2IG8+hEfA9oWsSWbG98Tkso5lzvgcfhbgEM6EBY31JMrmo5pUhE04MdRwOWqTCuGtiw+Ja0TyN3G77RmZlJoQNj2RC3BiAiCDrArIYLJQ2NhMyWc4D7Q3JDVpg16kbUYuCK5TWCXSiVsSqzOCz5tZ2N0Mt8uCoffH6aFaXYIXRS/VYeF+FPpipmXbukkJ64U07IsweCqQyOy0rtXvE6m6B+j/LUvD9yff4Ha8PzfxcnAplbmRzdHJlYW0KZW5kb2JqCjI4IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggOTQgPj4Kc3RyZWFtCnicRY3BEcAgCAT/VEEJCgraTyaTh/b/jRAyfGDnDu6EBQu2eUYfBZUmXhVYB0pj3FCPQL3hci3J3AUPcCd/2tBUnJbTd2mRSVUp3KQSef8OZyaQqHnRY533C2P7IzwKZW5kc3RyZWFtCmVuZG9iagoyOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE2NCA+PgpzdHJlYW0KeJxFkMdxBTEMQ++qAiUwgAr1rMfzD+v+r4b000F6GEIMYk/CsFxXcWF0w4+3LTMNf0cZ7sb6MmO81VggJ+gDDJGJq9Gk+nbFGar05NVirqOiXC86IhLMkuOrQCN8OrLHk7a2M/10Xh/sIe8T/yoq525hAS6q7kD5Uh/x1I/ZUeqaoY8qK2seatpXhF0RSts+LqcyTt29A1rhvZWrPdrvPx52OvIKZW5kc3RyZWFtCmVuZG9iagozMCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDU0ID4+CnN0cmVhbQp4nDM2M1QwUDCxVDAyNlEwNjQCYhOFFEMuoAiIlcsFE8sBs0CqcrigynNgqnK4MrjSAAUYDjIKZW5kc3RyZWFtCmVuZG9iagozMSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDcyID4+CnN0cmVhbQp4nDMyt1AwULA0ARKGFiYK5mYGCimGXEC+qYm5Qi4XSAzEygGzDIC0JZyCiGeAmCBtEMUgFkSxmYkZRB2cAZHL4EoDACXbFskKZW5kc3RyZWFtCmVuZG9iagozMiAwIG9iago8PCAvQkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzOQovU3VidHlwZSAvRm9ybSAvVHlwZSAvWE9iamVjdCA+PgpzdHJlYW0KeJzjMjQwUzA2NVXI5TI3NgKzcsAsI3MjIAski2BBZDO40gAV8wp8CmVuZHN0cmVhbQplbmRvYmoKMzMgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNjMgPj4Kc3RyZWFtCnicRZA7EgMhDEN7TqEj+CMDPs9mMik2929j2GxSwNNYIIO7E4LU2oKJ6IKHtiXdBe+tBGdj/Ok2bjUS5AR1gFak42iUUn25xWmVdPFoNnMrC60THWYOepSjGaAQOhXe7aLkcqbuzvlDcPVf9b9i3TmbiYHJyh0IzepT3Pk2O6K6usn+pMfcrNd+K+xVYWlZS8sJt527ZkAJ3FM52qs9Px8KOvYKZW5kc3RyZWFtCmVuZG9iagozNCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIxOCA+PgpzdHJlYW0KeJw9ULmNBDEMy12FGljAeu2pZxaLS6b/9Ej59iLRFkVSKjWZkikvdZQlWVPeOnyWxA55huVuZDYlKkUvk7Al99AK8X2J5hT33dWWs0M0l2g5fgszKqobHdNLNppwKhO6oNzDM/oNbXQDVocesVsg0KRg17YgcscPGAzBmROLIgxKTQb/rnKPn16LGz7D8UMUkZIO5jX/WP3ycw2vU48nkW5vvuJenKkOAxEckpq8I11YsS4SEWk1QU3PwFotgLu3Xv4btCO6DED2icRxmlKOob9rcKXPL+UnU9gKZW5kc3RyZWFtCmVuZG9iagozNSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDgzID4+CnN0cmVhbQp4nEWMuw3AMAhEe6ZgBH4m9j5RlMLevw0QJW64J909XB0JmSluM8NDBp4MLIZdcYH0ljALXEdQjp3so2HVvuoEjfWmUvPvD5Se7KzihusBAkIaZgplbmRzdHJlYW0KZW5kb2JqCjM2IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggNTEgPj4Kc3RyZWFtCnicMza0UDBQMDQwB5JGhkCWkYlCiiEXSADEzOWCCeaAWQZAGqI4B64mhyuDKw0A4bQNmAplbmRzdHJlYW0KZW5kb2JqCjM3IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTYwID4+CnN0cmVhbQp4nEWQORIDMQgEc72CJ0hcgvesy7XB+v+pB9ZHoukCNBy6Fk3KehRoPumxRqG60GvoLEqSRMEWkh1Qp2OIOyhITEhjkki2HoMjmlizXZiZVCqzUuG0acXCv9la1chEjXCN/InpBlT8T+pclPBNg6+SMfoYVLw7g4xJ+F5F3Fox7f5EMLEZ9glvRSYFhImxqdm+z2CGzPcK1zjH8w1MgjfrCmVuZHN0cmVhbQplbmRvYmoKMzggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMzQgPj4Kc3RyZWFtCnicLVJLcsUgDNtzCl2gM/gH5DzpdLp4vf+2kpNFRg5g9DHlholKfFkgt6PWxLeNzECF4a+rzIXPSNvIOojLkIu4ki2Fe0Qs5DHEPMSC76vxHh75rMzJswfGL9l3Dyv21IRlIePFGdphFcdhFeRYsHUhqnt4U6TDqSTY44v/PsVzLQQtfEbQgF/kn6+O4PmSFmn3mG3TrnqwTDuqpLAcbE9zXiZfWme5Oh7PB8n2rtgRUrsCFIW5M85z4SjTVka0FnY2SGpcbG+O/VhK0IVuXEaKI5CfqSI8oKTJzCYK4o+cHnIqA2Hqmq50chtVcaeezDWbi7czSWbrvkixmcJ5XTiz/gxTZrV5J89yotSpCO+xZ0vQ0Dmunr2WWWh0mxO8pITPxk5PTr5XM+shORUJqWJaV8FpFJliCdsSX1NRU5p6Gf778u7xO37+ASxzfHMKZW5kc3RyZWFtCmVuZG9iagozOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDcwID4+CnN0cmVhbQp4nDMzNlMwULAwAhKmpoYK5kaWCimGXEA+iJXLBRPLAbPMLMyBLCMLkJYcLkMLYzBtYmykYGZiBmRZIDEgujK40gCYmhMDCmVuZHN0cmVhbQplbmRvYmoKNDAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMjAgPj4Kc3RyZWFtCnicNVJLbgUxCNvPKbhApfBPzvOqqou++29rE70VTDBg4ykvWdJLvtQl26XD5Fsf9yWxQt6P7ZrMUsX3FrMUzy2vR88Rty0KBFETPViZLxUi1M/06DqocEqfgVcItxQbvINJAINq+AcepTMgUOdAxrtiMlIDgiTYc2lxCIlyJol/pLye3yetpKH0PVmZy9+TS6XQHU1O6AHFysVJoF1J+aCZmEpEkpfrfbFC9IbAkjw+RzHJgOw2iW2iBSbnHqUlzMQUOrDHArxmmtVV6GDCHocpjFcLs6gebPJbE5WkHa3jGdkw3sswU2Kh4bAF1OZiZYLu5eM1r8KI7VGTXcNw7pbNdwjRaP4bFsrgYxWSgEensRINaTjAiMCeXjjFXvMTOQ7AiGOdmiwMY2gmp3qOicDQnrOlYcbHHlr18w9U6XyHCmVuZHN0cmVhbQplbmRvYmoKNDEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxOCA+PgpzdHJlYW0KeJwzNrRQMIDDFEOuNAAd5gNSCmVuZHN0cmVhbQplbmRvYmoKNDIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMzMgPj4Kc3RyZWFtCnicRY9LDgQhCET3nKKOwMcf53Ey6YVz/+2AnW4TYz2FVIG5gqE9LmsDnRUfIRm28beplo5FWT5UelJWD8ngh6zGyyHcoCzwgkkqhiFQi5gakS1lbreA2zYNsrKVU6WOsIujMI/2tGwVHl+iWyJ1kj+DxCov3OO6Hcil1rveoou+f6QBMQkKZW5kc3RyZWFtCmVuZG9iago0MyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDM0MCA+PgpzdHJlYW0KeJw1UjluBDEM6/0KfSCAbtvv2SBIkfy/DanZFANxdFKUO1pUdsuHhVS17HT5tJXaEjfkd2WFxAnJqxLtUoZIqLxWIdXvmTKvtzVnBMhSpcLkpORxyYI/w6WnC8f5trGv5cgdjx5YFSOhRMAyxcToGpbO7rBmW36WacCPeIScK9Ytx1gFUhvdOO2K96F5LbIGiL2ZlooKHVaJFn5B8aBHjX32GFRYINHtHElwjIlQkYB2gdpIDDl7LHZRH/QzKDET6NobRdxBgSWSmDnFunT03/jQsaD+2Iw3vzoq6VtaWWPSPhvtlMYsMul6WPR089bHgws076L859UMEjRljZLGB63aOYaimVFWeLdDkw3NMcch8w6ewxkJSvo8FL+PJRMdlMjfDg2hf18eo4ycNt4C5qI/bRUHDuKzw165gRVKF2uS9wGpTOiB6f+v8bW+19cfHe2AxgplbmRzdHJlYW0KZW5kb2JqCjQ0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjUxID4+CnN0cmVhbQp4nC1RSXIDQQi7zyv0hGan32OXK4fk/9cIygcGDYtAdFrioIyfICxXvOWRq2jD3zMxgt8Fh34r121Y5EBUIEljUDWhdvF69B7YcZgJzJPWsAxmrA/8jCnc6MXhMRlnt9dl1BDsXa89mUHJrFzEJRMXTNVhI2cOP5kyLrRzPTcg50ZYl2GQblYaMxKONIVIIYWqm6TOBEESjK5GjTZyFPulL490hlWNqDHscy1tX89NOGvQ7Fis8uSUHl1xLicXL6wc9PU2AxdRaazyQEjA/W4P9XOyk994S+fOFtPje83J8sJUYMWb125ANtXi37yI4/uMr+fn+fwDX2BbiAplbmRzdHJlYW0KZW5kb2JqCjQ1IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTc0ID4+CnN0cmVhbQp4nE2QSQ5DIQxD95zCF6iEM8DnPL+qumjvv61DB3WB/OQgcDw80HEkLnRk6IyOK5sc48CzIGPi0Tj/ybg+xDFB3aItWJd2x9nMEnPCMjECtkbJ2TyiwA/HXAgSZJcfvsAgIl2P+VbzWZP0z7c73Y+6tGZfPaLAiewIxbABV4D9useBS8L5XtPklyolYxOH8oHqIlI2O6EQtVTscqqKs92bK3AV9PzRQ+7tBbUjPN8KZW5kc3RyZWFtCmVuZG9iago0NiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDc1ID4+CnN0cmVhbQp4nDO1NFIwUDA2ABKmZkYKpibmCimGXEA+iJXLZWhkCmblcBlZmilYWAAZJmbmUCGYhhwuY1NzoAFARcamYBqqP4crgysNAJWQEu8KZW5kc3RyZWFtCmVuZG9iago0NyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDg5ID4+CnN0cmVhbQp4nDWMuw2AMAxEe0/hEeK/2QchCti/xUlwY9/dk15S4kDlOhGOpoEnQfWZXiDZ6QFWReJRScz/Tb2pRVPPpu2rTQQnM471dRyomtN60FoobZMW3nB9AJwd7QplbmRzdHJlYW0KZW5kb2JqCjQ4IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTQxID4+CnN0cmVhbQp4nD2PwQ7DMAhD7/kK/0Ck2CmhfE+naofu/68jS7sLegJjjIXQ0BuqmsOGYJvjxdIlVGv4FMVAJTfImWAOpaTSHUeRemI4GFwetBuO4rHo+hG7kmZ90MZCuiVogHusU2ncpnETxB01Beop6pyjvBC5n6ln2DSS3TSzknO4Db97z1PX/6ervMv5Bb13Lv4KZW5kc3RyZWFtCmVuZG9iago0OSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIxNSA+PgpzdHJlYW0KeJw1UTkOAyEM7PcV/kAkjC94T6Iozf6/zYzRVh7BXIa0lCGZ8lKTqCHlUz56mS6cutzXzGo055a0LXOAuLa8L62SwIlmiIPBaZi4AZo8AUPX0ahRQxce0NSlUyiw3AQ+irduD91jtYGXtiHniSBiKBksQc2pRRMWbc8npDW/Xosb3pft3chTpcaWGIEGAVY4HNfo1/CVPU8m0XQVMtSrNcsYCRNFIjz5jqbVE+taNNIyEtTGEaxqA7w7/TBOAAATccsCZJ9KlLPkxG+x9LMGV/r+AZ9HVJYKZW5kc3RyZWFtCmVuZG9iagoxNSAwIG9iago8PCAvQmFzZUZvbnQgL0RlamFWdVNhbnMgL0NoYXJQcm9jcyAxNiAwIFIKL0VuY29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA0NSAvaHlwaGVuIC9wZXJpb2QgNDggL3plcm8gL29uZSAvdHdvIC90aHJlZSAvZm91ciAvZml2ZSAvc2l4Ci9zZXZlbiA2NSAvQSA2OCAvRCA3NiAvTCA4MyAvUyA5NyAvYSAvYiAvYyAvZCAvZSAvZiAxMDQgL2ggL2kgMTEwIC9uIC9vIDExNAovciAvcyAvdCAvdSAvdiAvdyAxMjEgL3kgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE0IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDEzIDAgUiA+PgplbmRvYmoKMTQgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxMyAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNiAwIG9iago8PCAvQSAxNyAwIFIgL0QgMTggMCBSIC9MIDE5IDAgUiAvUyAyMCAwIFIgL2EgMjEgMCBSIC9iIDIyIDAgUiAvYyAyMyAwIFIKL2QgMjQgMCBSIC9lIDI1IDAgUiAvZiAyNiAwIFIgL2ZpdmUgMjcgMCBSIC9mb3VyIDI4IDAgUiAvaCAyOSAwIFIKL2h5cGhlbiAzMCAwIFIgL2kgMzEgMCBSIC9uIDMzIDAgUiAvbyAzNCAwIFIgL29uZSAzNSAwIFIgL3BlcmlvZCAzNiAwIFIKL3IgMzcgMCBSIC9zIDM4IDAgUiAvc2V2ZW4gMzkgMCBSIC9zaXggNDAgMCBSIC9zcGFjZSA0MSAwIFIgL3QgNDIgMCBSCi90aHJlZSA0MyAwIFIgL3R3byA0NCAwIFIgL3UgNDUgMCBSIC92IDQ2IDAgUiAvdyA0NyAwIFIgL3kgNDggMCBSCi96ZXJvIDQ5IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzIgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago1MCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQzNTI3KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDUxCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDI1OTEwIDAwMDAwIG4gCjAwMDAwMjU2NDcgMDAwMDAgbiAKMDAwMDAyNTY3OSAwMDAwMCBuIAowMDAwMDI1ODE5IDAwMDAwIG4gCjAwMDAwMjU4NDAgMDAwMDAgbiAKMDAwMDAyNTg2MSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAxNTUwOCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMTU0ODYgMDAwMDAgbiAKMDAwMDAyNDIxNSAwMDAwMCBuIAowMDAwMDI0MDE1IDAwMDAwIG4gCjAwMDAwMjM1NDkgMDAwMDAgbiAKMDAwMDAyNTI2OCAwMDAwMCBuIAowMDAwMDE1NTI4IDAwMDAwIG4gCjAwMDAwMTU2OTEgMDAwMDAgbiAKMDAwMDAxNTkyOCAwMDAwMCBuIAowMDAwMDE2MDYxIDAwMDAwIG4gCjAwMDAwMTY0NzUgMDAwMDAgbiAKMDAwMDAxNjg1NSAwMDAwMCBuIAowMDAwMDE3MTcyIDAwMDAwIG4gCjAwMDAwMTc0NzcgMDAwMDAgbiAKMDAwMDAxNzc4MSAwMDAwMCBuIAowMDAwMDE4MTAzIDAwMDAwIG4gCjAwMDAwMTgzMTIgMDAwMDAgbiAKMDAwMDAxODYzNCAwMDAwMCBuIAowMDAwMDE4ODAwIDAwMDAwIG4gCjAwMDAwMTkwMzcgMDAwMDAgbiAKMDAwMDAxOTE2MyAwMDAwMCBuIAowMDAwMDE5MzA3IDAwMDAwIG4gCjAwMDAwMTk0NzkgMDAwMDAgbiAKMDAwMDAxOTcxNSAwMDAwMCBuIAowMDAwMDIwMDA2IDAwMDAwIG4gCjAwMDAwMjAxNjEgMDAwMDAgbiAKMDAwMDAyMDI4NCAwMDAwMCBuIAowMDAwMDIwNTE3IDAwMDAwIG4gCjAwMDAwMjA5MjQgMDAwMDAgbiAKMDAwMDAyMTA2NiAwMDAwMCBuIAowMDAwMDIxNDU5IDAwMDAwIG4gCjAwMDAwMjE1NDkgMDAwMDAgbiAKMDAwMDAyMTc1NSAwMDAwMCBuIAowMDAwMDIyMTY4IDAwMDAwIG4gCjAwMDAwMjI0OTIgMDAwMDAgbiAKMDAwMDAyMjczOSAwMDAwMCBuIAowMDAwMDIyODg2IDAwMDAwIG4gCjAwMDAwMjMwNDcgMDAwMDAgbiAKMDAwMDAyMzI2MSAwMDAwMCBuIAowMDAwMDI1OTcwIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNTAgMCBSIC9Sb290IDEgMCBSIC9TaXplIDUxID4+CnN0YXJ0eHJlZgoyNjEyNwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:35:25.997012\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["for i, act_fn_name in enumerate(act_fn_by_name):\n", " net_actfn = load_model(model_path=CHECKPOINT_PATH, model_name=f\"FashionMNIST_{act_fn_name}\").to(device)\n", " visualize_activations(net_actfn, color=f\"C{i}\")"]}, {"cell_type": "markdown", "id": "1a945558", "metadata": {"papermill": {"duration": 0.111895, "end_time": "2021-09-16T12:35:27.806164", "exception": false, "start_time": "2021-09-16T12:35:27.694269", "status": "completed"}, "tags": []}, "source": ["As the model with sigmoid activation was not able to train properly, the activations are also less informative and all gathered around 0.5 (the activation at input 0).\n", "\n", "The tanh shows a more diverse behavior.\n", "While for the input layer we experience a larger amount of neurons to be close to -1 and 1, where the gradients are close to zero, the activations in the two consecutive layers are closer to zero.\n", "This is probably because the input layers look for specific features in the input image, and the consecutive layers combine those together.\n", "The activations for the last layer are again more biased to the extreme points because the classification layer can be seen as a weighted average of those values (the gradients push the activations to those extremes).\n", "\n", "The ReLU has a strong peak at 0, as we initially expected.\n", "The effect of having no gradients for negative values is that the network does not have a Gaussian-like distribution after the linear layers, but a longer tail towards the positive values.\n", "The LeakyReLU shows a very similar behavior while ELU follows again a more Gaussian-like distribution.\n", "The Swish activation seems to lie in between, although it is worth noting that Swish uses significantly higher values than other activation functions (up to 20).\n", "\n", "As all activation functions show slightly different behavior although\n", "obtaining similar performance for our simple network, it becomes\n", "apparent that the selection of the \"optimal\" activation function really\n", "depends on many factors, and is not the same for all possible networks."]}, {"cell_type": "markdown", "id": "57bfc158", "metadata": {"papermill": {"duration": 0.110593, "end_time": "2021-09-16T12:35:28.026614", "exception": false, "start_time": "2021-09-16T12:35:27.916021", "status": "completed"}, "tags": []}, "source": ["### Finding dead neurons in ReLU networks"]}, {"cell_type": "markdown", "id": "3dbcab2c", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.114666, "end_time": "2021-09-16T12:35:28.251636", "exception": false, "start_time": "2021-09-16T12:35:28.136970", "status": "completed"}, "tags": []}, "source": ["One known drawback of the ReLU activation is the occurrence of \"dead neurons\", i.e. neurons with no gradient for any training input.\n", "The issue of dead neurons is that as no gradient is provided for the layer, we cannot train the parameters of this neuron in the previous layer to obtain output values besides zero.\n", "For dead neurons to happen, the output value of a specific neuron of the linear layer before the ReLU has to be negative for all input images.\n", "Considering the large number of neurons we have in a neural network, it is not unlikely for this to happen.\n", "\n", "To get a better understanding of how much of a problem this is, and when we need to be careful, we will measure how many dead neurons different networks have.\n", "For this, we implement a function which runs the network on the whole\n", "training set and records whether a neuron is exactly 0 for all data\n", "points or not:"]}, {"cell_type": "code", "execution_count": 22, "id": "edc02b80", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:35:28.479594Z", "iopub.status.busy": "2021-09-16T12:35:28.479110Z", "iopub.status.idle": "2021-09-16T12:35:28.481273Z", "shell.execute_reply": "2021-09-16T12:35:28.481901Z"}, "papermill": {"duration": 0.119951, "end_time": "2021-09-16T12:35:28.482036", "exception": false, "start_time": "2021-09-16T12:35:28.362085", "status": "completed"}, "tags": []}, "outputs": [], "source": ["@torch.no_grad()\n", "def measure_number_dead_neurons(net):\n", " \"\"\"Function to measure the number of dead neurons in a trained neural network.\n", "\n", " For each neuron, we create a boolean variable initially set to 1. If it has an activation unequals 0 at any time, we\n", " set this variable to 0. After running through the whole training set, only dead neurons will have a 1.\n", " \"\"\"\n", " neurons_dead = [\n", " torch.ones(layer.weight.shape[0], device=device, dtype=torch.bool)\n", " for layer in net.layers[:-1]\n", " if isinstance(layer, nn.Linear)\n", " ] # Same shapes as hidden size in BaseNetwork\n", "\n", " net.eval()\n", " for imgs, labels in tqdm(train_loader, leave=False): # Run through whole training set\n", " layer_index = 0\n", " imgs = imgs.to(device)\n", " imgs = imgs.view(imgs.size(0), -1)\n", " for layer in net.layers[:-1]:\n", " imgs = layer(imgs)\n", " if isinstance(layer, ActivationFunction):\n", " # Are all activations == 0 in the batch, and we did not record the opposite in the last batches?\n", " neurons_dead[layer_index] = torch.logical_and(neurons_dead[layer_index], (imgs == 0).all(dim=0))\n", " layer_index += 1\n", " number_neurons_dead = [t.sum().item() for t in neurons_dead]\n", " print(\"Number of dead neurons:\", number_neurons_dead)\n", " print(\n", " \"In percentage:\",\n", " \", \".join(\n", " [f\"{(100.0 * num_dead / tens.shape[0]):4.2f}%\" for tens, num_dead in zip(neurons_dead, number_neurons_dead)]\n", " ),\n", " )"]}, {"cell_type": "markdown", "id": "39be5f1b", "metadata": {"papermill": {"duration": 0.109828, "end_time": "2021-09-16T12:35:28.702763", "exception": false, "start_time": "2021-09-16T12:35:28.592935", "status": "completed"}, "tags": []}, "source": ["First, we can measure the number of dead neurons for an untrained network:"]}, {"cell_type": "code", "execution_count": 23, "id": "1f83d14a", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:35:28.929900Z", "iopub.status.busy": "2021-09-16T12:35:28.929410Z", "iopub.status.idle": "2021-09-16T12:35:37.256636Z", "shell.execute_reply": "2021-09-16T12:35:37.256221Z"}, "papermill": {"duration": 8.441781, "end_time": "2021-09-16T12:35:37.256748", "exception": false, "start_time": "2021-09-16T12:35:28.814967", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "4f3bb9c6b5c64d568faac8e14240eab4", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/49 [00:00=0.3\" \"torchvision\" \"pytorch-lightning>=1.3\" \"torch>=1.6, <1.9\" \"matplotlib\""]}, {"cell_type": "markdown", "id": "6502723b", "metadata": {"papermill": {"duration": 0.116681, "end_time": "2021-12-04T15:54:35.117128", "exception": false, "start_time": "2021-12-04T15:54:35.000447", "status": "completed"}, "tags": []}, "source": ["
\n", "In the first half of the notebook, we will review different initialization techniques, and go step by step from the simplest initialization to methods that are nowadays used in very deep networks.\n", "In the second half, we focus on optimization comparing the optimizers SGD, SGD with Momentum, and Adam.\n", "\n", "Let's start with importing our standard libraries:"]}, {"cell_type": "code", "execution_count": 2, "id": "7b848761", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:35.354836Z", "iopub.status.busy": "2021-12-04T15:54:35.354335Z", "iopub.status.idle": "2021-12-04T15:54:37.277810Z", "shell.execute_reply": "2021-12-04T15:54:37.278195Z"}, "papermill": {"duration": 2.046832, "end_time": "2021-12-04T15:54:37.278357", "exception": false, "start_time": "2021-12-04T15:54:35.231525", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_875/1682095326.py:24: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n"]}], "source": ["import copy\n", "import json\n", "import math\n", "import os\n", "import urllib.request\n", "from urllib.error import HTTPError\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.utils.data as data\n", "\n", "%matplotlib inline\n", "from IPython.display import set_matplotlib_formats\n", "from matplotlib import cm\n", "from torchvision import transforms\n", "from torchvision.datasets import FashionMNIST\n", "from tqdm.notebook import tqdm\n", "\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "sns.set()"]}, {"cell_type": "markdown", "id": "34ff3be8", "metadata": {"papermill": {"duration": 0.117573, "end_time": "2021-12-04T15:54:37.513508", "exception": false, "start_time": "2021-12-04T15:54:37.395935", "status": "completed"}, "tags": []}, "source": ["Instead of the `set_seed` function as in Tutorial 3, we can use PyTorch Lightning's build-in function `pl.seed_everything`.\n", "We will reuse the path variables `DATASET_PATH` and `CHECKPOINT_PATH` as in Tutorial 3.\n", "Adjust the paths if necessary."]}, {"cell_type": "code", "execution_count": 3, "id": "35b849b3", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:37.749217Z", "iopub.status.busy": "2021-12-04T15:54:37.748745Z", "iopub.status.idle": "2021-12-04T15:54:37.951118Z", "shell.execute_reply": "2021-12-04T15:54:37.951502Z"}, "papermill": {"duration": 0.323033, "end_time": "2021-12-04T15:54:37.951669", "exception": false, "start_time": "2021-12-04T15:54:37.628636", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Using device cuda:0\n"]}], "source": ["# Path to the folder where the datasets are/should be downloaded (e.g. MNIST)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data/\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/InitOptim/\")\n", "\n", "# Seed everything\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "# Fetching the device that will be used throughout this notebook\n", "device = torch.device(\"cpu\") if not torch.cuda.is_available() else torch.device(\"cuda:0\")\n", "print(\"Using device\", device)"]}, {"cell_type": "markdown", "id": "94ce8a6e", "metadata": {"papermill": {"duration": 0.116608, "end_time": "2021-12-04T15:54:38.185784", "exception": false, "start_time": "2021-12-04T15:54:38.069176", "status": "completed"}, "tags": []}, "source": ["In the last part of the notebook, we will train models using three different optimizers.\n", "The pretrained models for those are downloaded below."]}, {"cell_type": "code", "execution_count": 4, "id": "8e55f09c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:38.430163Z", "iopub.status.busy": "2021-12-04T15:54:38.429683Z", "iopub.status.idle": "2021-12-04T15:54:39.458838Z", "shell.execute_reply": "2021-12-04T15:54:39.458384Z"}, "papermill": {"duration": 1.155736, "end_time": "2021-12-04T15:54:39.458964", "exception": false, "start_time": "2021-12-04T15:54:38.303228", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_SGD.config...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_SGD_results.json...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_SGD.tar...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_SGDMom.config...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_SGDMom_results.json...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_SGDMom.tar...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_Adam.config...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_Adam_results.json...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/FashionMNIST_Adam.tar...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial4/\"\n", "# Files to download\n", "pretrained_files = [\n", " \"FashionMNIST_SGD.config\",\n", " \"FashionMNIST_SGD_results.json\",\n", " \"FashionMNIST_SGD.tar\",\n", " \"FashionMNIST_SGDMom.config\",\n", " \"FashionMNIST_SGDMom_results.json\",\n", " \"FashionMNIST_SGDMom.tar\",\n", " \"FashionMNIST_Adam.config\",\n", " \"FashionMNIST_Adam_results.json\",\n", " \"FashionMNIST_Adam.tar\",\n", "]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(f\"Downloading {file_url}...\")\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "30caea5b", "metadata": {"papermill": {"duration": 0.120012, "end_time": "2021-12-04T15:54:39.700194", "exception": false, "start_time": "2021-12-04T15:54:39.580182", "status": "completed"}, "tags": []}, "source": ["## Preparation"]}, {"cell_type": "markdown", "id": "231720c6", "metadata": {"papermill": {"duration": 0.120402, "end_time": "2021-12-04T15:54:39.942112", "exception": false, "start_time": "2021-12-04T15:54:39.821710", "status": "completed"}, "tags": []}, "source": ["Throughout this notebook, we will use a deep fully connected network, similar to our previous tutorial.\n", "We will also again apply the network to FashionMNIST, so you can relate to the results of Tutorial 3.\n", "We start by loading the FashionMNIST dataset:"]}, {"cell_type": "code", "execution_count": 5, "id": "f973c6a6", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:40.192819Z", "iopub.status.busy": "2021-12-04T15:54:40.192347Z", "iopub.status.idle": "2021-12-04T15:54:45.087304Z", "shell.execute_reply": "2021-12-04T15:54:45.087688Z"}, "papermill": {"duration": 5.025264, "end_time": "2021-12-04T15:54:45.087853", "exception": false, "start_time": "2021-12-04T15:54:40.062589", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to /__w/1/s/.datasets/FashionMNIST/raw/train-images-idx3-ubyte.gz\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "0fdf1596470d464fa9a290533416cb26", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/26421880 [00:00 first make them a tensor, then normalize them with mean 0 and std 1\n", "transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.2861,), (0.3530,))])\n", "\n", "# Loading the training dataset. We need to split it into a training and validation part\n", "train_dataset = FashionMNIST(root=DATASET_PATH, train=True, transform=transform, download=True)\n", "train_set, val_set = torch.utils.data.random_split(train_dataset, [50000, 10000])\n", "\n", "# Loading the test set\n", "test_set = FashionMNIST(root=DATASET_PATH, train=False, transform=transform, download=True)"]}, {"cell_type": "markdown", "id": "62d14135", "metadata": {"papermill": {"duration": 0.132716, "end_time": "2021-12-04T15:54:45.352571", "exception": false, "start_time": "2021-12-04T15:54:45.219855", "status": "completed"}, "tags": []}, "source": ["We define a set of data loaders that we can use for various purposes later.\n", "Note that for actually training a model, we will use different data loaders\n", "with a lower batch size."]}, {"cell_type": "code", "execution_count": 6, "id": "3d28e67a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:45.623150Z", "iopub.status.busy": "2021-12-04T15:54:45.622650Z", "iopub.status.idle": "2021-12-04T15:54:45.624735Z", "shell.execute_reply": "2021-12-04T15:54:45.624337Z"}, "papermill": {"duration": 0.139138, "end_time": "2021-12-04T15:54:45.624842", "exception": false, "start_time": "2021-12-04T15:54:45.485704", "status": "completed"}, "tags": []}, "outputs": [], "source": ["train_loader = data.DataLoader(train_set, batch_size=1024, shuffle=True, drop_last=False)\n", "val_loader = data.DataLoader(val_set, batch_size=1024, shuffle=False, drop_last=False)\n", "test_loader = data.DataLoader(test_set, batch_size=1024, shuffle=False, drop_last=False)"]}, {"cell_type": "markdown", "id": "b7b214a6", "metadata": {"papermill": {"duration": 0.132224, "end_time": "2021-12-04T15:54:45.888887", "exception": false, "start_time": "2021-12-04T15:54:45.756663", "status": "completed"}, "tags": []}, "source": ["In comparison to the previous tutorial, we have changed the parameters of the normalization transformation `transforms.Normalize`.\n", "The normalization is now designed to give us an expected mean of 0 and a standard deviation of 1 across pixels.\n", "This will be particularly relevant for the discussion about initialization we will look at below, and hence we change it here.\n", "It should be noted that in most classification tasks, both normalization techniques (between -1 and 1 or mean 0 and stddev 1) have shown to work well.\n", "We can calculate the normalization parameters by determining the mean and standard deviation on the original images:"]}, {"cell_type": "code", "execution_count": 7, "id": "f3c58d88", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:46.162358Z", "iopub.status.busy": "2021-12-04T15:54:46.161597Z", "iopub.status.idle": "2021-12-04T15:54:46.309115Z", "shell.execute_reply": "2021-12-04T15:54:46.309503Z"}, "papermill": {"duration": 0.288556, "end_time": "2021-12-04T15:54:46.309671", "exception": false, "start_time": "2021-12-04T15:54:46.021115", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Mean 0.28604060411453247\n", "Std 0.3530242443084717\n"]}], "source": ["print(\"Mean\", (train_dataset.data.float() / 255.0).mean().item())\n", "print(\"Std\", (train_dataset.data.float() / 255.0).std().item())"]}, {"cell_type": "markdown", "id": "205aa3d4", "metadata": {"papermill": {"duration": 0.134209, "end_time": "2021-12-04T15:54:46.580091", "exception": false, "start_time": "2021-12-04T15:54:46.445882", "status": "completed"}, "tags": []}, "source": ["We can verify the transformation by looking at the statistics of a single batch:"]}, {"cell_type": "code", "execution_count": 8, "id": "eb1baf45", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:46.853983Z", "iopub.status.busy": "2021-12-04T15:54:46.853514Z", "iopub.status.idle": "2021-12-04T15:54:47.034146Z", "shell.execute_reply": "2021-12-04T15:54:47.034531Z"}, "papermill": {"duration": 0.319767, "end_time": "2021-12-04T15:54:47.034711", "exception": false, "start_time": "2021-12-04T15:54:46.714944", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Mean: 0.009\n", "Standard deviation: 1.012\n", "Maximum: 2.022\n", "Minimum: -0.810\n"]}], "source": ["imgs, _ = next(iter(train_loader))\n", "print(f\"Mean: {imgs.mean().item():5.3f}\")\n", "print(f\"Standard deviation: {imgs.std().item():5.3f}\")\n", "print(f\"Maximum: {imgs.max().item():5.3f}\")\n", "print(f\"Minimum: {imgs.min().item():5.3f}\")"]}, {"cell_type": "markdown", "id": "b800ca4a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.134539, "end_time": "2021-12-04T15:54:47.304930", "exception": false, "start_time": "2021-12-04T15:54:47.170391", "status": "completed"}, "tags": []}, "source": ["Note that the maximum and minimum are not 1 and -1 anymore, but shifted towards the positive values.\n", "This is because FashionMNIST contains a lot of black pixels, similar to MNIST.\n", "\n", "Next, we create a linear neural network. We use the same setup as in the previous tutorial."]}, {"cell_type": "code", "execution_count": 9, "id": "eb8177c7", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:47.584349Z", "iopub.status.busy": "2021-12-04T15:54:47.583870Z", "iopub.status.idle": "2021-12-04T15:54:47.585900Z", "shell.execute_reply": "2021-12-04T15:54:47.585499Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.147005, "end_time": "2021-12-04T15:54:47.586011", "exception": false, "start_time": "2021-12-04T15:54:47.439006", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class BaseNetwork(nn.Module):\n", " def __init__(self, act_fn, input_size=784, num_classes=10, hidden_sizes=[512, 256, 256, 128]):\n", " \"\"\"\n", " Args:\n", " act_fn: Object of the activation function that should be used as non-linearity in the network.\n", " input_size: Size of the input images in pixels\n", " num_classes: Number of classes we want to predict\n", " hidden_sizes: A list of integers specifying the hidden layer sizes in the NN\n", " \"\"\"\n", " super().__init__()\n", "\n", " # Create the network based on the specified hidden sizes\n", " layers = []\n", " layer_sizes = [input_size] + hidden_sizes\n", " for layer_index in range(1, len(layer_sizes)):\n", " layers += [nn.Linear(layer_sizes[layer_index - 1], layer_sizes[layer_index]), act_fn]\n", " layers += [nn.Linear(layer_sizes[-1], num_classes)]\n", " # A module list registers a list of modules as submodules (e.g. for parameters)\n", " self.layers = nn.ModuleList(layers)\n", "\n", " self.config = {\n", " \"act_fn\": act_fn.__class__.__name__,\n", " \"input_size\": input_size,\n", " \"num_classes\": num_classes,\n", " \"hidden_sizes\": hidden_sizes,\n", " }\n", "\n", " def forward(self, x):\n", " x = x.view(x.size(0), -1)\n", " for layer in self.layers:\n", " x = layer(x)\n", " return x"]}, {"cell_type": "markdown", "id": "8635213a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.133716, "end_time": "2021-12-04T15:54:47.853607", "exception": false, "start_time": "2021-12-04T15:54:47.719891", "status": "completed"}, "tags": []}, "source": ["For the activation functions, we make use of PyTorch's `torch.nn` library instead of implementing ourselves.\n", "However, we also define an `Identity` activation function.\n", "Although this activation function would significantly limit the\n", "network's modeling capabilities, we will use it in the first steps of\n", "our discussion about initialization (for simplicity)."]}, {"cell_type": "code", "execution_count": 10, "id": "9f352558", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:48.130790Z", "iopub.status.busy": "2021-12-04T15:54:48.130299Z", "iopub.status.idle": "2021-12-04T15:54:48.132296Z", "shell.execute_reply": "2021-12-04T15:54:48.131898Z"}, "papermill": {"duration": 0.144009, "end_time": "2021-12-04T15:54:48.132404", "exception": false, "start_time": "2021-12-04T15:54:47.988395", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Identity(nn.Module):\n", " def forward(self, x):\n", " return x\n", "\n", "\n", "act_fn_by_name = {\"tanh\": nn.Tanh, \"relu\": nn.ReLU, \"identity\": Identity}"]}, {"cell_type": "markdown", "id": "8eeb9167", "metadata": {"papermill": {"duration": 0.133812, "end_time": "2021-12-04T15:54:48.399825", "exception": false, "start_time": "2021-12-04T15:54:48.266013", "status": "completed"}, "tags": []}, "source": ["Finally, we define a few plotting functions that we will use for our discussions.\n", "These functions help us to (1) visualize the weight/parameter distribution inside a network, (2) visualize the gradients that the parameters at different layers receive, and (3) the activations, i.e. the output of the linear layers.\n", "The detailed code is not important, but feel free to take a closer look if interested."]}, {"cell_type": "code", "execution_count": 11, "id": "16bbefb4", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:48.682589Z", "iopub.status.busy": "2021-12-04T15:54:48.682093Z", "iopub.status.idle": "2021-12-04T15:54:48.683729Z", "shell.execute_reply": "2021-12-04T15:54:48.684129Z"}, "papermill": {"duration": 0.151108, "end_time": "2021-12-04T15:54:48.684257", "exception": false, "start_time": "2021-12-04T15:54:48.533149", "status": "completed"}, "tags": []}, "outputs": [], "source": ["##############################################################\n", "\n", "\n", "def plot_dists(val_dict, color=\"C0\", xlabel=None, stat=\"count\", use_kde=True):\n", " columns = len(val_dict)\n", " fig, ax = plt.subplots(1, columns, figsize=(columns * 3, 2.5))\n", " fig_index = 0\n", " for key in sorted(val_dict.keys()):\n", " key_ax = ax[fig_index % columns]\n", " sns.histplot(\n", " val_dict[key],\n", " ax=key_ax,\n", " color=color,\n", " bins=50,\n", " stat=stat,\n", " kde=use_kde and ((val_dict[key].max() - val_dict[key].min()) > 1e-8),\n", " ) # Only plot kde if there is variance\n", " hidden_dim_str = (\n", " r\"(%i $\\to$ %i)\" % (val_dict[key].shape[1], val_dict[key].shape[0]) if len(val_dict[key].shape) > 1 else \"\"\n", " )\n", " key_ax.set_title(f\"{key} {hidden_dim_str}\")\n", " if xlabel is not None:\n", " key_ax.set_xlabel(xlabel)\n", " fig_index += 1\n", " fig.subplots_adjust(wspace=0.4)\n", " return fig\n", "\n", "\n", "##############################################################\n", "\n", "\n", "def visualize_weight_distribution(model, color=\"C0\"):\n", " weights = {}\n", " for name, param in model.named_parameters():\n", " if name.endswith(\".bias\"):\n", " continue\n", " key_name = f\"Layer {name.split('.')[1]}\"\n", " weights[key_name] = param.detach().view(-1).cpu().numpy()\n", "\n", " # Plotting\n", " fig = plot_dists(weights, color=color, xlabel=\"Weight vals\")\n", " fig.suptitle(\"Weight distribution\", fontsize=14, y=1.05)\n", " plt.show()\n", " plt.close()\n", "\n", "\n", "##############################################################\n", "\n", "\n", "def visualize_gradients(model, color=\"C0\", print_variance=False):\n", " \"\"\"\n", " Args:\n", " net: Object of class BaseNetwork\n", " color: Color in which we want to visualize the histogram (for easier separation of activation functions)\n", " \"\"\"\n", " model.eval()\n", " small_loader = data.DataLoader(train_set, batch_size=1024, shuffle=False)\n", " imgs, labels = next(iter(small_loader))\n", " imgs, labels = imgs.to(device), labels.to(device)\n", "\n", " # Pass one batch through the network, and calculate the gradients for the weights\n", " model.zero_grad()\n", " preds = model(imgs)\n", " loss = F.cross_entropy(preds, labels) # Same as nn.CrossEntropyLoss, but as a function instead of module\n", " loss.backward()\n", " # We limit our visualization to the weight parameters and exclude the bias to reduce the number of plots\n", " grads = {\n", " name: params.grad.view(-1).cpu().clone().numpy()\n", " for name, params in model.named_parameters()\n", " if \"weight\" in name\n", " }\n", " model.zero_grad()\n", "\n", " # Plotting\n", " fig = plot_dists(grads, color=color, xlabel=\"Grad magnitude\")\n", " fig.suptitle(\"Gradient distribution\", fontsize=14, y=1.05)\n", " plt.show()\n", " plt.close()\n", "\n", " if print_variance:\n", " for key in sorted(grads.keys()):\n", " print(f\"{key} - Variance: {np.var(grads[key])}\")\n", "\n", "\n", "##############################################################\n", "\n", "\n", "def visualize_activations(model, color=\"C0\", print_variance=False):\n", " model.eval()\n", " small_loader = data.DataLoader(train_set, batch_size=1024, shuffle=False)\n", " imgs, labels = next(iter(small_loader))\n", " imgs, labels = imgs.to(device), labels.to(device)\n", "\n", " # Pass one batch through the network, and calculate the gradients for the weights\n", " feats = imgs.view(imgs.shape[0], -1)\n", " activations = {}\n", " with torch.no_grad():\n", " for layer_index, layer in enumerate(model.layers):\n", " feats = layer(feats)\n", " if isinstance(layer, nn.Linear):\n", " activations[f\"Layer {layer_index}\"] = feats.view(-1).detach().cpu().numpy()\n", "\n", " # Plotting\n", " fig = plot_dists(activations, color=color, stat=\"density\", xlabel=\"Activation vals\")\n", " fig.suptitle(\"Activation distribution\", fontsize=14, y=1.05)\n", " plt.show()\n", " plt.close()\n", "\n", " if print_variance:\n", " for key in sorted(activations.keys()):\n", " print(f\"{key} - Variance: {np.var(activations[key])}\")\n", "\n", "\n", "##############################################################"]}, {"cell_type": "markdown", "id": "307d0193", "metadata": {"papermill": {"duration": 0.134573, "end_time": "2021-12-04T15:54:48.953411", "exception": false, "start_time": "2021-12-04T15:54:48.818838", "status": "completed"}, "tags": []}, "source": ["## Initialization\n", "\n", "Before starting our discussion about initialization, it should be noted that there exist many very good blog posts about the topic of neural network initialization (for example [deeplearning.ai](https://www.deeplearning.ai/ai-notes/initialization/), or a more [math-focused blog post](https://pouannes.github.io/blog/initialization)).\n", "In case something remains unclear after this tutorial, we recommend skimming through these blog posts as well.\n", "\n", "When initializing a neural network, there are a few properties we would like to have.\n", "First, the variance of the input should be propagated through the model to the last layer, so that we have a similar standard deviation for the output neurons.\n", "If the variance would vanish the deeper we go in our model, it becomes much harder to optimize the model as the input to the next layer is basically a single constant value.\n", "Similarly, if the variance increases, it is likely to explode (i.e. head to infinity) the deeper we design our model.\n", "The second property we look out for in initialization techniques is a gradient distribution with equal variance across layers.\n", "If the first layer receives much smaller gradients than the last layer, we will have difficulties in choosing an appropriate learning rate.\n", "\n", "As a starting point for finding a good method, we will analyze different initialization based on our linear neural network with no activation function (i.e. an identity).\n", "We do this because initializations depend on the specific activation\n", "function used in the network, and we can adjust the initialization\n", "schemes later on for our specific choice."]}, {"cell_type": "code", "execution_count": 12, "id": "cc7ba656", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:49.227365Z", "iopub.status.busy": "2021-12-04T15:54:49.226898Z", "iopub.status.idle": "2021-12-04T15:54:53.850761Z", "shell.execute_reply": "2021-12-04T15:54:53.850280Z"}, "papermill": {"duration": 4.762201, "end_time": "2021-12-04T15:54:53.850902", "exception": false, "start_time": "2021-12-04T15:54:49.088701", "status": "completed"}, "tags": []}, "outputs": [], "source": ["model = BaseNetwork(act_fn=Identity()).to(device)"]}, {"cell_type": "markdown", "id": "7f1e34a7", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.136138, "end_time": "2021-12-04T15:54:54.121872", "exception": false, "start_time": "2021-12-04T15:54:53.985734", "status": "completed"}, "tags": []}, "source": ["### Constant initialization\n", "\n", "The first initialization we can consider is to initialize all weights with the same constant value.\n", "Intuitively, setting all weights to zero is not a good idea as the propagated gradient will be zero.\n", "However, what happens if we set all weights to a value slightly larger or smaller than 0?\n", "To find out, we can implement a function for setting all parameters below and visualize the gradients."]}, {"cell_type": "code", "execution_count": 13, "id": "e3710c8c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:54:54.396221Z", "iopub.status.busy": "2021-12-04T15:54:54.395746Z", "iopub.status.idle": "2021-12-04T15:55:04.530115Z", "shell.execute_reply": "2021-12-04T15:55:04.530499Z"}, "papermill": {"duration": 10.27465, "end_time": "2021-12-04T15:55:04.530680", "exception": false, "start_time": "2021-12-04T15:54:54.256030", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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IDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9DIDE3IDAgUiAvRyAxOCAwIFIgL2EgMTkgMCBSIC9iIDIwIDAgUiAvZCAyMSAwIFIgL2UgMjIgMCBSCi9laWdodCAyMyAwIFIgL2ZpdmUgMjQgMCBSIC9mb3VyIDI1IDAgUiAvZyAyNiAwIFIgL2ggMjcgMCBSIC9pIDI4IDAgUgovbCAyOSAwIFIgL20gMzAgMCBSIC9uIDMyIDAgUiAvbyAzMyAwIFIgL29uZSAzNCAwIFIgL3BlcmlvZCAzNSAwIFIKL3IgMzYgMCBSIC9zIDM3IDAgUiAvc2V2ZW4gMzggMCBSIC9zaXggMzkgMCBSIC9zcGFjZSA0MCAwIFIgL3QgNDEgMCBSCi90aHJlZSA0MiAwIFIgL3R3byA0MyAwIFIgL3UgNDQgMCBSIC93IDQ1IDAgUiAveSA0NiAwIFIgL3plcm8gNDcgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNzUgPj4KL0E0IDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvRjEtRGVqYVZ1U2Fucy1taW51cyAzMSAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjQ4IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEyMDQxNjU0NTYrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDkKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMTc4MzAgMDAwMDAgbiAKMDAwMDAxNzUyNSAwMDAwMCBuIAowMDAwMDE3NTU3IDAwMDAwIG4gCjAwMDAwMTc3MzkgMDAwMDAgbiAKMDAwMDAxNzc2MCAwMDAwMCBuIAowMDAwMDE3NzgxIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDA3MTg2IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwNzE2NSAwMDAwMCBuIAowMDAwMDE2MTE0IDAwMDAwIG4gCjAwMDAwMTU5MTQgMDAwMDAgbiAKMDAwMDAxNTQ1MyAwMDAwMCBuIAowMDAwMDE3MTY3IDAwMDAwIG4gCjAwMDAwMDcyMDYgMDAwMDAgbiAKMDAwMDAwNzUxNCAwMDAwMCBuIAowMDAwMDA3ODM0IDAwMDAwIG4gCjAwMDAwMDgyMTQgMDAwMDAgbiAKMDAwMDAwODUzMSAwMDAwMCBuIAowMDAwMDA4ODM1IDAwMDAwIG4gCjAwMDAwMDkxNTcgMDAwMDAgbiAKMDAwMDAwOTYyNSAwMDAwMCBuIAowMDAwMDA5OTQ3IDAwMDAwIG4gCjAwMDAwMTAxMTMgMDAwMDAgbiAKMDAwMDAxMDUyNyAwMDAwMCBuIAowMDAwMDEwNzY0IDAwMDAwIG4gCjAwMDAwMTA5MDggMDAwMDAgbiAKMDAwMDAxMTAyNyAwMDAwMCBuIAowMDAwMDExMzU4IDAwMDAwIG4gCjAwMDAwMTE1MzAgMDAwMDAgbiAKMDAwMDAxMTc2NiAwMDAwMCBuIAowMDAwMDEyMDU3IDAwMDAwIG4gCjAwMDAwMTIyMTIgMDAwMDAgbiAKMDAwMDAxMjMzNSAwMDAwMCBuIAowMDAwMDEyNTY4IDAwMDAwIG4gCjAwMDAwMTI5NzUgMDAwMDAgbiAKMDAwMDAxMzExNyAwMDAwMCBuIAowMDAwMDEzNTEwIDAwMDAwIG4gCjAwMDAwMTM2MDAgMDAwMDAgbiAKMDAwMDAxMzgwNiAwMDAwMCBuIAowMDAwMDE0MjE5IDAwMDAwIG4gCjAwMDAwMTQ1NDMgMDAwMDAgbiAKMDAwMDAxNDc5MCAwMDAwMCBuIAowMDAwMDE0OTUxIDAwMDAwIG4gCjAwMDAwMTUxNjUgMDAwMDAgbiAKMDAwMDAxNzg5MCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQ4IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0OSA+PgpzdGFydHhyZWYKMTgwNDcKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:54:55.723291\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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QXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9BIDE3IDAgUiAvRCAxOCAwIFIgL0wgMTkgMCBSIC9hIDIwIDAgUiAvYiAyMSAwIFIgL2MgMjIgMCBSIC9kIDIzIDAgUgovZSAyNCAwIFIgL2VpZ2h0IDI1IDAgUiAvZml2ZSAyNiAwIFIgL2ZvdXIgMjcgMCBSIC9pIDI4IDAgUiAvbCAyOSAwIFIKL24gMzEgMCBSIC9vIDMyIDAgUiAvb25lIDMzIDAgUiAvcGVyaW9kIDM0IDAgUiAvciAzNSAwIFIgL3MgMzYgMCBSCi9zZXZlbiAzNyAwIFIgL3NpeCAzOCAwIFIgL3NwYWNlIDM5IDAgUiAvdCA0MCAwIFIgL3RocmVlIDQxIDAgUiAvdHdvIDQyIDAgUgovdSA0MyAwIFIgL3YgNDQgMCBSIC95IDQ1IDAgUiAvemVybyA0NiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC41ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMS1EZWphVnVTYW5zLW1pbnVzIDMwIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKNDcgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMTIwNDE2NTUwNCswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA0OAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAyNDM1MSAwMDAwMCBuIAowMDAwMDI0MDg4IDAwMDAwIG4gCjAwMDAwMjQxMjAgMDAwMDAgbiAKMDAwMDAyNDI2MCAwMDAwMCBuIAowMDAwMDI0MjgxIDAwMDAwIG4gCjAwMDAwMjQzMDIgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk5IDAwMDAwIG4gCjAwMDAwMTQ1NDkgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDE0NTI3IDAwMDAwIG4gCjAwMDAwMjI2ODcgMDAwMDAgbiAKMDAwMDAyMjQ4NyAwMDAwMCBuIAowMDAwMDIyMDMwIDAwMDAwIG4gCjAwMDAwMjM3NDAgMDAwMDAgbiAKMDAwMDAxNDU2OSAwMDAwMCBuIAowMDAwMDE0NzMyIDAwMDAwIG4gCjAwMDAwMTQ5NjkgMDAwMDAgbiAKMDAwMDAxNTEwMiAwMDAwMCBuIAowMDAwMDE1NDgyIDAwMDAwIG4gCjAwMDAwMTU3OTkgMDAwMDAgbiAKMDAwMDAxNjEwNCAwMDAwMCBuIAowMDAwMDE2NDA4IDAwMDAwIG4gCjAwMDAwMTY3MzAgMDAwMDAgbiAKMDAwMDAxNzE5OCAwMDAwMCBuIAowMDAwMDE3NTIwIDAwMDAwIG4gCjAwMDAwMTc2ODYgMDAwMDAgbiAKMDAwMDAxNzgzMCAwMDAwMCBuIAowMDAwMDE3OTQ5IDAwMDAwIG4gCjAwMDAwMTgxMjEgMDAwMDAgbiAKMDAwMDAxODM1NyAwMDAwMCBuIAowMDAwMDE4NjQ4IDAwMDAwIG4gCjAwMDAwMTg4MDMgMDAwMDAgbiAKMDAwMDAxODkyNiAwMDAwMCBuIAowMDAwMDE5MTU5IDAwMDAwIG4gCjAwMDAwMTk1NjYgMDAwMDAgbiAKMDAwMDAxOTcwOCAwMDAwMCBuIAowMDAwMDIwMTAxIDAwMDAwIG4gCjAwMDAwMjAxOTEgMDAwMDAgbiAKMDAwMDAyMDM5NyAwMDAwMCBuIAowMDAwMDIwODEwIDAwMDAwIG4gCjAwMDAwMjExMzQgMDAwMDAgbiAKMDAwMDAyMTM4MSAwMDAwMCBuIAowMDAwMDIxNTI4IDAwMDAwIG4gCjAwMDAwMjE3NDIgMDAwMDAgbiAKMDAwMDAyNDQxMSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQ3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0OCA+PgpzdGFydHhyZWYKMjQ1NjgKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:55:03.610810\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 0 - Variance: 2.0582761764526367\n", "Layer 2 - Variance: 13.489120483398438\n", "Layer 4 - Variance: 22.100574493408203\n", "Layer 6 - Variance: 36.20957946777344\n", "Layer 8 - Variance: 14.831440925598145\n"]}], "source": ["def const_init(model, fill=0.0):\n", " for name, param in model.named_parameters():\n", " param.data.fill_(fill)\n", "\n", "\n", "const_init(model, fill=0.005)\n", "visualize_gradients(model)\n", "visualize_activations(model, print_variance=True)"]}, {"cell_type": "markdown", "id": "8da1230d", "metadata": {"papermill": {"duration": 0.152167, "end_time": "2021-12-04T15:55:04.833884", "exception": false, "start_time": "2021-12-04T15:55:04.681717", "status": "completed"}, "tags": []}, "source": ["As we can see, only the first and the last layer have diverse gradient distributions while the other three layers have the same gradient for all weights (note that this value is unequal 0, but often very close to it).\n", "Having the same gradient for parameters that have been initialized with the same values means that we will always have the same value for those parameters.\n", "This would make our layer useless and reduce our effective number of parameters to 1.\n", "Thus, we cannot use a constant initialization to train our networks."]}, {"cell_type": "markdown", "id": "65128840", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.151605, "end_time": "2021-12-04T15:55:05.137267", "exception": false, "start_time": "2021-12-04T15:55:04.985662", "status": "completed"}, "tags": []}, "source": ["### Constant variance\n", "\n", "From the experiment above, we have seen that a constant value is not working.\n", "So instead, how about we initialize the parameters by randomly sampling from a distribution like a Gaussian?\n", "The most intuitive way would be to choose one variance that is used for all layers in the network.\n", "Let's implement it below, and visualize the activation distribution across layers."]}, {"cell_type": "code", "execution_count": 14, "id": "d6173456", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:55:05.442031Z", "iopub.status.busy": "2021-12-04T15:55:05.441560Z", "iopub.status.idle": "2021-12-04T15:55:13.063195Z", "shell.execute_reply": "2021-12-04T15:55:13.065428Z"}, "papermill": {"duration": 7.778761, "end_time": "2021-12-04T15:55:13.065600", "exception": false, "start_time": "2021-12-04T15:55:05.286839", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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WEhlaWdodCAwID4+CmVuZG9iagoxMyAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNiAwIG9iago8PCAvQSAxNyAwIFIgL0QgMTggMCBSIC9MIDE5IDAgUiAvYSAyMCAwIFIgL2IgMjEgMCBSIC9jIDIyIDAgUiAvZCAyMyAwIFIKL2UgMjQgMCBSIC9laWdodCAyNSAwIFIgL2ZpdmUgMjYgMCBSIC9mb3VyIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSCi9uIDMxIDAgUiAvbyAzMiAwIFIgL29uZSAzMyAwIFIgL3BlcmlvZCAzNCAwIFIgL3IgMzUgMCBSIC9zIDM2IDAgUgovc2l4IDM3IDAgUiAvc3BhY2UgMzggMCBSIC90IDM5IDAgUiAvdGhyZWUgNDAgMCBSIC90d28gNDEgMCBSIC91IDQyIDAgUgovdiA0MyAwIFIgL3kgNDQgMCBSIC96ZXJvIDQ1IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzAgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0NiAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1NTEyKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQ3CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIyMzcwIDAwMDAwIG4gCjAwMDAwMjIxMDcgMDAwMDAgbiAKMDAwMDAyMjEzOSAwMDAwMCBuIAowMDAwMDIyMjc5IDAwMDAwIG4gCjAwMDAwMjIzMDAgMDAwMDAgbiAKMDAwMDAyMjMyMSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAxMjcyOCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMTI3MDYgMDAwMDAgbiAKMDAwMDAyMDcyMCAwMDAwMCBuIAowMDAwMDIwNTIwIDAwMDAwIG4gCjAwMDAwMjAwNjcgMDAwMDAgbiAKMDAwMDAyMTc3MyAwMDAwMCBuIAowMDAwMDEyNzQ4IDAwMDAwIG4gCjAwMDAwMTI5MTEgMDAwMDAgbiAKMDAwMDAxMzE0OCAwMDAwMCBuIAowMDAwMDEzMjgxIDAwMDAwIG4gCjAwMDAwMTM2NjEgMDAwMDAgbiAKMDAwMDAxMzk3OCAwMDAwMCBuIAowMDAwMDE0MjgzIDAwMDAwIG4gCjAwMDAwMTQ1ODcgMDAwMDAgbiAKMDAwMDAxNDkwOSAwMDAwMCBuIAowMDAwMDE1Mzc3IDAwMDAwIG4gCjAwMDAwMTU2OTkgMDAwMDAgbiAKMDAwMDAxNTg2NSAwMDAwMCBuIAowMDAwMDE2MDA5IDAwMDAwIG4gCjAwMDAwMTYxMjggMDAwMDAgbiAKMDAwMDAxNjMwMCAwMDAwMCBuIAowMDAwMDE2NTM2IDAwMDAwIG4gCjAwMDAwMTY4MjcgMDAwMDAgbiAKMDAwMDAxNjk4MiAwMDAwMCBuIAowMDAwMDE3MTA1IDAwMDAwIG4gCjAwMDAwMTczMzggMDAwMDAgbiAKMDAwMDAxNzc0NSAwMDAwMCBuIAowMDAwMDE4MTM4IDAwMDAwIG4gCjAwMDAwMTgyMjggMDAwMDAgbiAKMDAwMDAxODQzNCAwMDAwMCBuIAowMDAwMDE4ODQ3IDAwMDAwIG4gCjAwMDAwMTkxNzEgMDAwMDAgbiAKMDAwMDAxOTQxOCAwMDAwMCBuIAowMDAwMDE5NTY1IDAwMDAwIG4gCjAwMDAwMTk3NzkgMDAwMDAgbiAKMDAwMDAyMjQzMCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQ2IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0NyA+PgpzdGFydHhyZWYKMjI1ODcKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:55:12.112890\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 0 - Variance: 0.0768686905503273\n", "Layer 2 - Variance: 0.00374085595831275\n", "Layer 4 - Variance: 0.00021300435764715075\n", "Layer 6 - Variance: 0.000116668117698282\n", "Layer 8 - Variance: 8.082647400442511e-05\n"]}], "source": ["def var_init(model, std=0.01):\n", " for name, param in model.named_parameters():\n", " param.data.normal_(mean=0.0, std=std)\n", "\n", "\n", "var_init(model, std=0.01)\n", "visualize_activations(model, print_variance=True)"]}, {"cell_type": "markdown", "id": "7c95cf24", "metadata": {"papermill": {"duration": 0.163132, "end_time": "2021-12-04T15:55:13.391937", "exception": false, "start_time": "2021-12-04T15:55:13.228805", "status": "completed"}, "tags": []}, "source": ["The variance of the activation becomes smaller and smaller across layers, and almost vanishes in the last layer.\n", "Alternatively, we could use a higher standard deviation:"]}, {"cell_type": "code", "execution_count": 15, "id": "c6819982", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:55:13.709227Z", "iopub.status.busy": "2021-12-04T15:55:13.708760Z", "iopub.status.idle": "2021-12-04T15:55:21.442388Z", "shell.execute_reply": "2021-12-04T15:55:21.442802Z"}, "papermill": {"duration": 7.894595, "end_time": "2021-12-04T15:55:21.442993", "exception": false, "start_time": "2021-12-04T15:55:13.548398", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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YXJQcm9jcyAxNiAwIFIKL0VuY29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA0NiAvcGVyaW9kIDQ4IC96ZXJvIC9vbmUgL3R3byAvdGhyZWUgL2ZvdXIgL2ZpdmUgL3NpeCA1NgovZWlnaHQgNjUgL0EgNjggL0QgNzYgL0wgOTcgL2EgL2IgL2MgL2QgL2UgMTA1IC9pIDEwOCAvbCAxMTAgL24gL28gMTE0IC9yCi9zIC90IC91IC92IDEyMSAveSBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9BIDE3IDAgUiAvRCAxOCAwIFIgL0wgMTkgMCBSIC9hIDIwIDAgUiAvYiAyMSAwIFIgL2MgMjIgMCBSIC9kIDIzIDAgUgovZSAyNCAwIFIgL2VpZ2h0IDI1IDAgUiAvZml2ZSAyNiAwIFIgL2ZvdXIgMjcgMCBSIC9pIDI4IDAgUiAvbCAyOSAwIFIKL24gMzEgMCBSIC9vIDMyIDAgUiAvb25lIDMzIDAgUiAvcGVyaW9kIDM0IDAgUiAvciAzNSAwIFIgL3MgMzYgMCBSCi9zaXggMzcgMCBSIC9zcGFjZSAzOCAwIFIgL3QgMzkgMCBSIC90aHJlZSA0MCAwIFIgL3R3byA0MSAwIFIgL3UgNDIgMCBSCi92IDQzIDAgUiAveSA0NCAwIFIgL3plcm8gNDUgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvRjEtRGVqYVZ1U2Fucy1taW51cyAzMCAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjQ2IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEyMDQxNjU1MjErMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjExNjYgMDAwMDAgbiAKMDAwMDAyMDkwMyAwMDAwMCBuIAowMDAwMDIwOTM1IDAwMDAwIG4gCjAwMDAwMjEwNzUgMDAwMDAgbiAKMDAwMDAyMTA5NiAwMDAwMCBuIAowMDAwMDIxMTE3IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDExNTI0IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAxMTUwMiAwMDAwMCBuIAowMDAwMDE5NTE2IDAwMDAwIG4gCjAwMDAwMTkzMTYgMDAwMDAgbiAKMDAwMDAxODg2MyAwMDAwMCBuIAowMDAwMDIwNTY5IDAwMDAwIG4gCjAwMDAwMTE1NDQgMDAwMDAgbiAKMDAwMDAxMTcwNyAwMDAwMCBuIAowMDAwMDExOTQ0IDAwMDAwIG4gCjAwMDAwMTIwNzcgMDAwMDAgbiAKMDAwMDAxMjQ1NyAwMDAwMCBuIAowMDAwMDEyNzc0IDAwMDAwIG4gCjAwMDAwMTMwNzkgMDAwMDAgbiAKMDAwMDAxMzM4MyAwMDAwMCBuIAowMDAwMDEzNzA1IDAwMDAwIG4gCjAwMDAwMTQxNzMgMDAwMDAgbiAKMDAwMDAxNDQ5NSAwMDAwMCBuIAowMDAwMDE0NjYxIDAwMDAwIG4gCjAwMDAwMTQ4MDUgMDAwMDAgbiAKMDAwMDAxNDkyNCAwMDAwMCBuIAowMDAwMDE1MDk2IDAwMDAwIG4gCjAwMDAwMTUzMzIgMDAwMDAgbiAKMDAwMDAxNTYyMyAwMDAwMCBuIAowMDAwMDE1Nzc4IDAwMDAwIG4gCjAwMDAwMTU5MDEgMDAwMDAgbiAKMDAwMDAxNjEzNCAwMDAwMCBuIAowMDAwMDE2NTQxIDAwMDAwIG4gCjAwMDAwMTY5MzQgMDAwMDAgbiAKMDAwMDAxNzAyNCAwMDAwMCBuIAowMDAwMDE3MjMwIDAwMDAwIG4gCjAwMDAwMTc2NDMgMDAwMDAgbiAKMDAwMDAxNzk2NyAwMDAwMCBuIAowMDAwMDE4MjE0IDAwMDAwIG4gCjAwMDAwMTgzNjEgMDAwMDAgbiAKMDAwMDAxODU3NSAwMDAwMCBuIAowMDAwMDIxMjI2IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDYgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ3ID4+CnN0YXJ0eHJlZgoyMTM4MwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:55:20.476417\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 0 - Variance: 8.08608341217041\n", "Layer 2 - Variance: 41.400367736816406\n", "Layer 4 - Variance: 104.29255676269531\n", "Layer 6 - Variance: 270.63995361328125\n", "Layer 8 - Variance: 288.26495361328125\n"]}], "source": ["var_init(model, std=0.1)\n", "visualize_activations(model, print_variance=True)"]}, {"cell_type": "markdown", "id": "4e4a9097", "metadata": {"papermill": {"duration": 0.165816, "end_time": "2021-12-04T15:55:21.775202", "exception": false, "start_time": "2021-12-04T15:55:21.609386", "status": "completed"}, "tags": []}, "source": ["With a higher standard deviation, the activations are likely to explode.\n", "You can play around with the specific standard deviation values, but it will be hard to find one that gives us a good activation distribution across layers and is very specific to our model.\n", "If we would change the hidden sizes or number of layers, you would have\n", "to search all over again, which is neither efficient nor recommended."]}, {"cell_type": "markdown", "id": "b785fdfc", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.165929, "end_time": "2021-12-04T15:55:22.105283", "exception": false, "start_time": "2021-12-04T15:55:21.939354", "status": "completed"}, "tags": []}, "source": ["### How to find appropriate initialization values\n", "\n", "From our experiments above, we have seen that we need to sample the weights from a distribution, but are not sure which one exactly.\n", "As a next step, we will try to find the optimal initialization from the perspective of the activation distribution.\n", "For this, we state two requirements:\n", "\n", "1. The mean of the activations should be zero\n", "2. The variance of the activations should stay the same across every layer\n", "\n", "Suppose we want to design an initialization for the following layer: $y=Wx+b$ with $y\\in\\mathbb{R}^{d_y}$, $x\\in\\mathbb{R}^{d_x}$.\n", "Our goal is that the variance of each element of $y$ is the same as the input, i.e. $\\text{Var}(y_i)=\\text{Var}(x_i)=\\sigma_x^{2}$, and that the mean is zero.\n", "We assume $x$ to also have a mean of zero, because, in deep neural networks, $y$ would be the input of another layer.\n", "This requires the bias and weight to have an expectation of 0.\n", "Actually, as $b$ is a single element per output neuron and is constant across different inputs, we set it to 0 overall.\n", "\n", "Next, we need to calculate the variance with which we need to initialize the weight parameters.\n", "Along the calculation, we will need to following variance rule: given two independent variables, the variance of their product is $\\text{Var}(X\\cdot Y) = \\mathbb{E}(Y)^2\\text{Var}(X) + \\mathbb{E}(X)^2\\text{Var}(Y) + \\text{Var}(X)\\text{Var}(Y) = \\mathbb{E}(Y^2)\\mathbb{E}(X^2)-\\mathbb{E}(Y)^2\\mathbb{E}(X)^2$ ($X$ and $Y$ are not refering to $x$ and $y$, but any random variable).\n", "\n", "The needed variance of the weights, $\\text{Var}(w_{ij})$, is calculated as follows:\n", "\n", "$$\n", "\\begin{split}\n", " y_i & = \\sum_{j} w_{ij}x_{j}\\hspace{10mm}\\text{Calculation of a single output neuron without bias}\\\\\n", " \\text{Var}(y_i) = \\sigma_x^{2} & = \\text{Var}\\left(\\sum_{j} w_{ij}x_{j}\\right)\\\\\n", " & = \\sum_{j} \\text{Var}(w_{ij}x_{j}) \\hspace{10mm}\\text{Inputs and weights are independent of each other}\\\\\n", " & = \\sum_{j} \\text{Var}(w_{ij})\\cdot\\text{Var}(x_{j}) \\hspace{10mm}\\text{Variance rule (see above) with expectations being zero}\\\\\n", " & = d_x \\cdot \\text{Var}(w_{ij})\\cdot\\text{Var}(x_{j}) \\hspace{10mm}\\text{Variance equal for all $d_x$ elements}\\\\\n", " & = \\sigma_x^{2} \\cdot d_x \\cdot \\text{Var}(w_{ij})\\\\\n", " \\Rightarrow \\text{Var}(w_{ij}) = \\sigma_{W}^2 & = \\frac{1}{d_x}\\\\\n", "\\end{split}\n", "$$\n", "\n", "Thus, we should initialize the weight distribution with a variance of the inverse of the input dimension $d_x$.\n", "Let's implement it below and check whether this holds:"]}, {"cell_type": "code", "execution_count": 16, "id": "85fe111a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:55:22.441463Z", "iopub.status.busy": "2021-12-04T15:55:22.440990Z", "iopub.status.idle": "2021-12-04T15:55:35.037471Z", "shell.execute_reply": "2021-12-04T15:55:35.037861Z"}, "papermill": {"duration": 12.767059, "end_time": "2021-12-04T15:55:35.038029", "exception": false, "start_time": "2021-12-04T15:55:22.270970", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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MCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9DIDE3IDAgUiAvTCAxOCAwIFIgL1cgMTkgMCBSIC9hIDIwIDAgUiAvYiAyMSAwIFIgL2QgMjIgMCBSIC9lIDIzIDAgUgovZWlnaHQgMjQgMCBSIC9maXZlIDI1IDAgUiAvZm91ciAyNiAwIFIgL2cgMjcgMCBSIC9oIDI4IDAgUiAvaSAyOSAwIFIKL2wgMzAgMCBSIC9uIDMyIDAgUiAvbyAzMyAwIFIgL29uZSAzNCAwIFIgL3BlcmlvZCAzNSAwIFIgL3IgMzYgMCBSCi9zIDM3IDAgUiAvc2l4IDM4IDAgUiAvc3BhY2UgMzkgMCBSIC90IDQwIDAgUiAvdGhyZWUgNDEgMCBSIC90d28gNDIgMCBSCi91IDQzIDAgUiAvdiA0NCAwIFIgL3kgNDUgMCBSIC96ZXJvIDQ2IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzEgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0NyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1NTI3KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQ4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIxNzYxIDAwMDAwIG4gCjAwMDAwMjE0OTggMDAwMDAgbiAKMDAwMDAyMTUzMCAwMDAwMCBuIAowMDAwMDIxNjcwIDAwMDAwIG4gCjAwMDAwMjE2OTEgMDAwMDAgbiAKMDAwMDAyMTcxMiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAxMTY4NCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMTE2NjIgMDAwMDAgbiAKMDAwMDAyMDEwMSAwMDAwMCBuIAowMDAwMDE5OTAxIDAwMDAwIG4gCjAwMDAwMTk0NDEgMDAwMDAgbiAKMDAwMDAyMTE1NCAwMDAwMCBuIAowMDAwMDExNzA0IDAwMDAwIG4gCjAwMDAwMTIwMTIgMDAwMDAgbiAKMDAwMDAxMjE0NSAwMDAwMCBuIAowMDAwMDEyMzA5IDAwMDAwIG4gCjAwMDAwMTI2ODkgMDAwMDAgbiAKMDAwMDAxMzAwNiAwMDAwMCBuIAowMDAwMDEzMzEwIDAwMDAwIG4gCjAwMDAwMTM2MzIgMDAwMDAgbiAKMDAwMDAxNDEwMCAwMDAwMCBuIAowMDAwMDE0NDIyIDAwMDAwIG4gCjAwMDAwMTQ1ODggMDAwMDAgbiAKMDAwMDAxNTAwMiAwMDAwMCBuIAowMDAwMDE1MjM5IDAwMDAwIG4gCjAwMDAwMTUzODMgMDAwMDAgbiAKMDAwMDAxNTUwMiAwMDAwMCBuIAowMDAwMDE1Njc0IDAwMDAwIG4gCjAwMDAwMTU5MTAgMDAwMDAgbiAKMDAwMDAxNjIwMSAwMDAwMCBuIAowMDAwMDE2MzU2IDAwMDAwIG4gCjAwMDAwMTY0NzkgMDAwMDAgbiAKMDAwMDAxNjcxMiAwMDAwMCBuIAowMDAwMDE3MTE5IDAwMDAwIG4gCjAwMDAwMTc1MTIgMDAwMDAgbiAKMDAwMDAxNzYwMiAwMDAwMCBuIAowMDAwMDE3ODA4IDAwMDAwIG4gCjAwMDAwMTgyMjEgMDAwMDAgbiAKMDAwMDAxODU0NSAwMDAwMCBuIAowMDAwMDE4NzkyIDAwMDAwIG4gCjAwMDAwMTg5MzkgMDAwMDAgbiAKMDAwMDAxOTE1MyAwMDAwMCBuIAowMDAwMDIxODIxIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ4ID4+CnN0YXJ0eHJlZgoyMTk3OAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:55:26.361643\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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1MYRrGoDvDv9ME4AABNxywJkn0qUs+TEb7H0swZX+v4Bn0dUlgplbmRzdHJlYW0KZW5kb2JqCjE1IDAgb2JqCjw8IC9CYXNlRm9udCAvRGVqYVZ1U2FucyAvQ2hhclByb2NzIDE2IDAgUgovRW5jb2RpbmcgPDwKL0RpZmZlcmVuY2VzIFsgMzIgL3NwYWNlIDQ2IC9wZXJpb2QgNDggL3plcm8gL29uZSAvdHdvIC90aHJlZSAvZm91ciAvZml2ZSAvc2l4IDU2Ci9laWdodCA2NSAvQSA2OCAvRCA3NiAvTCA5NyAvYSAvYiAvYyAvZCAvZSAxMDUgL2kgMTA4IC9sIDExMCAvbiAvbyAxMTQgL3IKL3MgL3QgL3UgL3YgMTIxIC95IF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNCAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxMyAwIFIgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTMgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0EgMTcgMCBSIC9EIDE4IDAgUiAvTCAxOSAwIFIgL2EgMjAgMCBSIC9iIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSCi9lIDI0IDAgUiAvZWlnaHQgMjUgMCBSIC9maXZlIDI2IDAgUiAvZm91ciAyNyAwIFIgL2kgMjggMCBSIC9sIDI5IDAgUgovbiAzMSAwIFIgL28gMzIgMCBSIC9vbmUgMzMgMCBSIC9wZXJpb2QgMzQgMCBSIC9yIDM1IDAgUiAvcyAzNiAwIFIKL3NpeCAzNyAwIFIgL3NwYWNlIDM4IDAgUiAvdCAzOSAwIFIgL3RocmVlIDQwIDAgUiAvdHdvIDQxIDAgUiAvdSA0MiAwIFIKL3YgNDMgMCBSIC95IDQ0IDAgUiAvemVybyA0NSAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC41ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMS1EZWphVnVTYW5zLW1pbnVzIDMwIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKNDYgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMTIwNDE2NTUzNCswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA0NwowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAyMTI1OCAwMDAwMCBuIAowMDAwMDIwOTk1IDAwMDAwIG4gCjAwMDAwMjEwMjcgMDAwMDAgbiAKMDAwMDAyMTE2NyAwMDAwMCBuIAowMDAwMDIxMTg4IDAwMDAwIG4gCjAwMDAwMjEyMDkgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDA2IDAwMDAwIG4gCjAwMDAwMTE2MTYgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDExNTk0IDAwMDAwIG4gCjAwMDAwMTk2MDggMDAwMDAgbiAKMDAwMDAxOTQwOCAwMDAwMCBuIAowMDAwMDE4OTU1IDAwMDAwIG4gCjAwMDAwMjA2NjEgMDAwMDAgbiAKMDAwMDAxMTYzNiAwMDAwMCBuIAowMDAwMDExNzk5IDAwMDAwIG4gCjAwMDAwMTIwMzYgMDAwMDAgbiAKMDAwMDAxMjE2OSAwMDAwMCBuIAowMDAwMDEyNTQ5IDAwMDAwIG4gCjAwMDAwMTI4NjYgMDAwMDAgbiAKMDAwMDAxMzE3MSAwMDAwMCBuIAowMDAwMDEzNDc1IDAwMDAwIG4gCjAwMDAwMTM3OTcgMDAwMDAgbiAKMDAwMDAxNDI2NSAwMDAwMCBuIAowMDAwMDE0NTg3IDAwMDAwIG4gCjAwMDAwMTQ3NTMgMDAwMDAgbiAKMDAwMDAxNDg5NyAwMDAwMCBuIAowMDAwMDE1MDE2IDAwMDAwIG4gCjAwMDAwMTUxODggMDAwMDAgbiAKMDAwMDAxNTQyNCAwMDAwMCBuIAowMDAwMDE1NzE1IDAwMDAwIG4gCjAwMDAwMTU4NzAgMDAwMDAgbiAKMDAwMDAxNTk5MyAwMDAwMCBuIAowMDAwMDE2MjI2IDAwMDAwIG4gCjAwMDAwMTY2MzMgMDAwMDAgbiAKMDAwMDAxNzAyNiAwMDAwMCBuIAowMDAwMDE3MTE2IDAwMDAwIG4gCjAwMDAwMTczMjIgMDAwMDAgbiAKMDAwMDAxNzczNSAwMDAwMCBuIAowMDAwMDE4MDU5IDAwMDAwIG4gCjAwMDAwMTgzMDYgMDAwMDAgbiAKMDAwMDAxODQ1MyAwMDAwMCBuIAowMDAwMDE4NjY3IDAwMDAwIG4gCjAwMDAwMjEzMTggMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA0NiAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNDcgPj4Kc3RhcnR4cmVmCjIxNDc1CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:55:34.092028\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 0 - Variance: 1.0374585390090942\n", "Layer 2 - Variance: 1.0698715448379517\n", "Layer 4 - Variance: 1.1412183046340942\n", "Layer 6 - Variance: 1.0962424278259277\n", "Layer 8 - Variance: 1.0699418783187866\n"]}], "source": ["def equal_var_init(model):\n", " for name, param in model.named_parameters():\n", " if name.endswith(\".bias\"):\n", " param.data.fill_(0)\n", " else:\n", " param.data.normal_(std=1.0 / math.sqrt(param.shape[1]))\n", "\n", "\n", "equal_var_init(model)\n", "visualize_weight_distribution(model)\n", "visualize_activations(model, print_variance=True)"]}, {"cell_type": "markdown", "id": "ca9c629c", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.183436, "end_time": "2021-12-04T15:55:35.408994", "exception": false, "start_time": "2021-12-04T15:55:35.225558", "status": "completed"}, "tags": []}, "source": ["As we expected, the variance stays indeed constant across layers.\n", "Note that our initialization does not restrict us to a normal distribution, but allows any other distribution with a mean of 0 and variance of $1/d_x$.\n", "You often see that a uniform distribution is used for initialization.\n", "A small benefit of using a uniform instead of a normal distribution is that we can exclude the chance of initializing very large or small weights.\n", "\n", "Besides the variance of the activations, another variance we would like to stabilize is the one of the gradients.\n", "This ensures a stable optimization for deep networks.\n", "It turns out that we can do the same calculation as above starting from $\\Delta x=W\\Delta y$, and come to the conclusion that we should initialize our layers with $1/d_y$ where $d_y$ is the number of output neurons.\n", "You can do the calculation as a practice, or check a thorough explanation in [this blog post](https://pouannes.github.io/blog/initialization).\n", "As a compromise between both constraints, [Glorot and Bengio (2010)](http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf?hc_location=ufi) proposed to use the harmonic mean of both values.\n", "This leads us to the well-known Xavier initialization:\n", "\n", "$$W\\sim \\mathcal{N}\\left(0,\\frac{2}{d_x+d_y}\\right)$$\n", "\n", "If we use a uniform distribution, we would initialize the weights with:\n", "\n", "$$W\\sim U\\left[-\\frac{\\sqrt{6}}{\\sqrt{d_x+d_y}}, \\frac{\\sqrt{6}}{\\sqrt{d_x+d_y}}\\right]$$\n", "\n", "Let's shortly implement it and validate its effectiveness:"]}, {"cell_type": "code", "execution_count": 17, "id": "886f5024", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:55:35.773825Z", "iopub.status.busy": "2021-12-04T15:55:35.773355Z", "iopub.status.idle": "2021-12-04T15:55:48.170979Z", "shell.execute_reply": "2021-12-04T15:55:48.170552Z"}, "papermill": {"duration": 12.581276, "end_time": "2021-12-04T15:55:48.171111", "exception": false, "start_time": "2021-12-04T15:55:35.589835", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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dENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDEzIDAgUiA+PgplbmRvYmoKMTQgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxMyAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNiAwIG9iago8PCAvQyAxNyAwIFIgL0cgMTggMCBSIC9hIDE5IDAgUiAvYiAyMCAwIFIgL2QgMjEgMCBSIC9lIDIyIDAgUgovZWlnaHQgMjMgMCBSIC9maXZlIDI0IDAgUiAvZm91ciAyNSAwIFIgL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIKL2wgMjkgMCBSIC9tIDMwIDAgUiAvbiAzMiAwIFIgL28gMzMgMCBSIC9vbmUgMzQgMCBSIC9wZXJpb2QgMzUgMCBSCi9yIDM2IDAgUiAvcyAzNyAwIFIgL3NldmVuIDM4IDAgUiAvc2l4IDM5IDAgUiAvc3BhY2UgNDAgMCBSIC90IDQxIDAgUgovdGhyZWUgNDIgMCBSIC90d28gNDMgMCBSIC91IDQ0IDAgUiAvdyA0NSAwIFIgL3kgNDYgMCBSIC96ZXJvIDQ3IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzEgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0OCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1NTQwKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQ5CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIxNzU1IDAwMDAwIG4gCjAwMDAwMjE0OTIgMDAwMDAgbiAKMDAwMDAyMTUyNCAwMDAwMCBuIAowMDAwMDIxNjY0IDAwMDAwIG4gCjAwMDAwMjE2ODUgMDAwMDAgbiAKMDAwMDAyMTcwNiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAxMTE1MyAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMTExMzEgMDAwMDAgbiAKMDAwMDAyMDA4MSAwMDAwMCBuIAowMDAwMDE5ODgxIDAwMDAwIG4gCjAwMDAwMTk0MjAgMDAwMDAgbiAKMDAwMDAyMTEzNCAwMDAwMCBuIAowMDAwMDExMTczIDAwMDAwIG4gCjAwMDAwMTE0ODEgMDAwMDAgbiAKMDAwMDAxMTgwMSAwMDAwMCBuIAowMDAwMDEyMTgxIDAwMDAwIG4gCjAwMDAwMTI0OTggMDAwMDAgbiAKMDAwMDAxMjgwMiAwMDAwMCBuIAowMDAwMDEzMTI0IDAwMDAwIG4gCjAwMDAwMTM1OTIgMDAwMDAgbiAKMDAwMDAxMzkxNCAwMDAwMCBuIAowMDAwMDE0MDgwIDAwMDAwIG4gCjAwMDAwMTQ0OTQgMDAwMDAgbiAKMDAwMDAxNDczMSAwMDAwMCBuIAowMDAwMDE0ODc1IDAwMDAwIG4gCjAwMDAwMTQ5OTQgMDAwMDAgbiAKMDAwMDAxNTMyNSAwMDAwMCBuIAowMDAwMDE1NDk3IDAwMDAwIG4gCjAwMDAwMTU3MzMgMDAwMDAgbiAKMDAwMDAxNjAyNCAwMDAwMCBuIAowMDAwMDE2MTc5IDAwMDAwIG4gCjAwMDAwMTYzMDIgMDAwMDAgbiAKMDAwMDAxNjUzNSAwMDAwMCBuIAowMDAwMDE2OTQyIDAwMDAwIG4gCjAwMDAwMTcwODQgMDAwMDAgbiAKMDAwMDAxNzQ3NyAwMDAwMCBuIAowMDAwMDE3NTY3IDAwMDAwIG4gCjAwMDAwMTc3NzMgMDAwMDAgbiAKMDAwMDAxODE4NiAwMDAwMCBuIAowMDAwMDE4NTEwIDAwMDAwIG4gCjAwMDAwMTg3NTcgMDAwMDAgbiAKMDAwMDAxODkxOCAwMDAwMCBuIAowMDAwMDE5MTMyIDAwMDAwIG4gCjAwMDAwMjE4MTUgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA0OCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNDkgPj4Kc3RhcnR4cmVmCjIxOTcyCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:55:39.652330\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["layers.0.weight - Variance: 0.000457785528851673\n", "layers.2.weight - Variance: 0.0006751694600097835\n", "layers.4.weight - Variance: 0.0008508111932314932\n", "layers.6.weight - Variance: 0.001484374050050974\n", "layers.8.weight - Variance: 0.011529149487614632\n"]}, {"data": {"application/pdf": "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bmUgL3R3byAvdGhyZWUgL2ZvdXIgL2ZpdmUgL3NpeCA1NgovZWlnaHQgNjUgL0EgNjggL0QgNzYgL0wgOTcgL2EgL2IgL2MgL2QgL2UgMTA1IC9pIDEwOCAvbCAxMTAgL24gL28gMTE0IC9yCi9zIC90IC91IC92IDEyMSAveSBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9BIDE3IDAgUiAvRCAxOCAwIFIgL0wgMTkgMCBSIC9hIDIwIDAgUiAvYiAyMSAwIFIgL2MgMjIgMCBSIC9kIDIzIDAgUgovZSAyNCAwIFIgL2VpZ2h0IDI1IDAgUiAvZml2ZSAyNiAwIFIgL2ZvdXIgMjcgMCBSIC9pIDI4IDAgUiAvbCAyOSAwIFIKL24gMzEgMCBSIC9vIDMyIDAgUiAvb25lIDMzIDAgUiAvcGVyaW9kIDM0IDAgUiAvciAzNSAwIFIgL3MgMzYgMCBSCi9zaXggMzcgMCBSIC9zcGFjZSAzOCAwIFIgL3QgMzkgMCBSIC90aHJlZSA0MCAwIFIgL3R3byA0MSAwIFIgL3UgNDIgMCBSCi92IDQzIDAgUiAveSA0NCAwIFIgL3plcm8gNDUgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvRjEtRGVqYVZ1U2Fucy1taW51cyAzMCAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjQ2IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEyMDQxNjU1NDcrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjEwOTEgMDAwMDAgbiAKMDAwMDAyMDgyOCAwMDAwMCBuIAowMDAwMDIwODYwIDAwMDAwIG4gCjAwMDAwMjEwMDAgMDAwMDAgbiAKMDAwMDAyMTAyMSAwMDAwMCBuIAowMDAwMDIxMDQyIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDExNDQ5IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAxMTQyNyAwMDAwMCBuIAowMDAwMDE5NDQxIDAwMDAwIG4gCjAwMDAwMTkyNDEgMDAwMDAgbiAKMDAwMDAxODc4OCAwMDAwMCBuIAowMDAwMDIwNDk0IDAwMDAwIG4gCjAwMDAwMTE0NjkgMDAwMDAgbiAKMDAwMDAxMTYzMiAwMDAwMCBuIAowMDAwMDExODY5IDAwMDAwIG4gCjAwMDAwMTIwMDIgMDAwMDAgbiAKMDAwMDAxMjM4MiAwMDAwMCBuIAowMDAwMDEyNjk5IDAwMDAwIG4gCjAwMDAwMTMwMDQgMDAwMDAgbiAKMDAwMDAxMzMwOCAwMDAwMCBuIAowMDAwMDEzNjMwIDAwMDAwIG4gCjAwMDAwMTQwOTggMDAwMDAgbiAKMDAwMDAxNDQyMCAwMDAwMCBuIAowMDAwMDE0NTg2IDAwMDAwIG4gCjAwMDAwMTQ3MzAgMDAwMDAgbiAKMDAwMDAxNDg0OSAwMDAwMCBuIAowMDAwMDE1MDIxIDAwMDAwIG4gCjAwMDAwMTUyNTcgMDAwMDAgbiAKMDAwMDAxNTU0OCAwMDAwMCBuIAowMDAwMDE1NzAzIDAwMDAwIG4gCjAwMDAwMTU4MjYgMDAwMDAgbiAKMDAwMDAxNjA1OSAwMDAwMCBuIAowMDAwMDE2NDY2IDAwMDAwIG4gCjAwMDAwMTY4NTkgMDAwMDAgbiAKMDAwMDAxNjk0OSAwMDAwMCBuIAowMDAwMDE3MTU1IDAwMDAwIG4gCjAwMDAwMTc1NjggMDAwMDAgbiAKMDAwMDAxNzg5MiAwMDAwMCBuIAowMDAwMDE4MTM5IDAwMDAwIG4gCjAwMDAwMTgyODYgMDAwMDAgbiAKMDAwMDAxODUwMCAwMDAwMCBuIAowMDAwMDIxMTUxIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDYgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ3ID4+CnN0YXJ0eHJlZgoyMTMwOAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:55:47.230788\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 0 - Variance: 1.1692266464233398\n", "Layer 2 - Variance: 1.520001769065857\n", "Layer 4 - Variance: 1.585775375366211\n", "Layer 6 - Variance: 1.9146416187286377\n", "Layer 8 - Variance: 3.2868599891662598\n"]}], "source": ["def xavier_init(model):\n", " for name, param in model.named_parameters():\n", " if name.endswith(\".bias\"):\n", " param.data.fill_(0)\n", " else:\n", " bound = math.sqrt(6) / math.sqrt(param.shape[0] + param.shape[1])\n", " param.data.uniform_(-bound, bound)\n", "\n", "\n", "xavier_init(model)\n", "visualize_gradients(model, print_variance=True)\n", "visualize_activations(model, print_variance=True)"]}, {"cell_type": "markdown", "id": "920b63e6", "metadata": {"papermill": {"duration": 0.203664, "end_time": "2021-12-04T15:55:48.573926", "exception": false, "start_time": "2021-12-04T15:55:48.370262", "status": "completed"}, "tags": []}, "source": ["We see that the Xavier initialization balances the variance of gradients and activations.\n", "Note that the significantly higher variance for the output layer is due to the large difference of input and output dimension ($128$ vs $10$).\n", "However, we currently assumed the activation function to be linear.\n", "So what happens if we add a non-linearity?\n", "In a tanh-based network, a common assumption is that for small values during the initial steps in training, the $\\tanh$ works as a linear function such that we don't have to adjust our calculation.\n", "We can check if that is the case for us as well:"]}, {"cell_type": "code", "execution_count": 18, "id": "dff6e720", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:55:48.968877Z", "iopub.status.busy": "2021-12-04T15:55:48.968403Z", "iopub.status.idle": "2021-12-04T15:56:01.734151Z", "shell.execute_reply": "2021-12-04T15:56:01.733706Z"}, "papermill": {"duration": 12.965651, "end_time": "2021-12-04T15:56:01.734278", "exception": false, "start_time": "2021-12-04T15:55:48.768627", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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eXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9DIDE3IDAgUiAvRyAxOCAwIFIgL2EgMTkgMCBSIC9iIDIwIDAgUiAvZCAyMSAwIFIgL2UgMjIgMCBSCi9laWdodCAyMyAwIFIgL2ZpdmUgMjQgMCBSIC9mb3VyIDI1IDAgUiAvZyAyNiAwIFIgL2ggMjcgMCBSIC9pIDI4IDAgUgovbCAyOSAwIFIgL20gMzAgMCBSIC9uIDMyIDAgUiAvbyAzMyAwIFIgL29uZSAzNCAwIFIgL3BlcmlvZCAzNSAwIFIKL3IgMzYgMCBSIC9zIDM3IDAgUiAvc2l4IDM4IDAgUiAvc3BhY2UgMzkgMCBSIC90IDQwIDAgUiAvdGhyZWUgNDEgMCBSCi90d28gNDIgMCBSIC91IDQzIDAgUiAvdyA0NCAwIFIgL3kgNDUgMCBSIC96ZXJvIDQ2IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzEgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0NyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1NTUzKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQ4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIxODY5IDAwMDAwIG4gCjAwMDAwMjE2MDYgMDAwMDAgbiAKMDAwMDAyMTYzOCAwMDAwMCBuIAowMDAwMDIxNzc4IDAwMDAwIG4gCjAwMDAwMjE3OTkgMDAwMDAgbiAKMDAwMDAyMTgyMCAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDYgMDAwMDAgbiAKMDAwMDAxMTQyNyAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMTE0MDUgMDAwMDAgbiAKMDAwMDAyMDIwOSAwMDAwMCBuIAowMDAwMDIwMDA5IDAwMDAwIG4gCjAwMDAwMTk1NTIgMDAwMDAgbiAKMDAwMDAyMTI2MiAwMDAwMCBuIAowMDAwMDExNDQ3IDAwMDAwIG4gCjAwMDAwMTE3NTUgMDAwMDAgbiAKMDAwMDAxMjA3NSAwMDAwMCBuIAowMDAwMDEyNDU1IDAwMDAwIG4gCjAwMDAwMTI3NzIgMDAwMDAgbiAKMDAwMDAxMzA3NiAwMDAwMCBuIAowMDAwMDEzMzk4IDAwMDAwIG4gCjAwMDAwMTM4NjYgMDAwMDAgbiAKMDAwMDAxNDE4OCAwMDAwMCBuIAowMDAwMDE0MzU0IDAwMDAwIG4gCjAwMDAwMTQ3NjggMDAwMDAgbiAKMDAwMDAxNTAwNSAwMDAwMCBuIAowMDAwMDE1MTQ5IDAwMDAwIG4gCjAwMDAwMTUyNjggMDAwMDAgbiAKMDAwMDAxNTU5OSAwMDAwMCBuIAowMDAwMDE1NzcxIDAwMDAwIG4gCjAwMDAwMTYwMDcgMDAwMDAgbiAKMDAwMDAxNjI5OCAwMDAwMCBuIAowMDAwMDE2NDUzIDAwMDAwIG4gCjAwMDAwMTY1NzYgMDAwMDAgbiAKMDAwMDAxNjgwOSAwMDAwMCBuIAowMDAwMDE3MjE2IDAwMDAwIG4gCjAwMDAwMTc2MDkgMDAwMDAgbiAKMDAwMDAxNzY5OSAwMDAwMCBuIAowMDAwMDE3OTA1IDAwMDAwIG4gCjAwMDAwMTgzMTggMDAwMDAgbiAKMDAwMDAxODY0MiAwMDAwMCBuIAowMDAwMDE4ODg5IDAwMDAwIG4gCjAwMDAwMTkwNTAgMDAwMDAgbiAKMDAwMDAxOTI2NCAwMDAwMCBuIAowMDAwMDIxOTI5IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ4ID4+CnN0YXJ0eHJlZgoyMjA4NgolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:55:53.100667\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["layers.0.weight - Variance: 2.4351327738258988e-05\n", "layers.2.weight - Variance: 3.7693978811148554e-05\n", "layers.4.weight - Variance: 5.152593075763434e-05\n", "layers.6.weight - Variance: 6.856555410195142e-05\n", "layers.8.weight - Variance: 0.0004877124447375536\n"]}, {"data": {"application/pdf": "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MCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0EgMTcgMCBSIC9EIDE4IDAgUiAvTCAxOSAwIFIgL2EgMjAgMCBSIC9iIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSCi9lIDI0IDAgUiAvZWlnaHQgMjUgMCBSIC9maXZlIDI2IDAgUiAvZm91ciAyNyAwIFIgL2kgMjggMCBSIC9sIDI5IDAgUgovbiAzMSAwIFIgL28gMzIgMCBSIC9vbmUgMzMgMCBSIC9wZXJpb2QgMzQgMCBSIC9yIDM1IDAgUiAvcyAzNiAwIFIKL3NpeCAzNyAwIFIgL3NwYWNlIDM4IDAgUiAvdCAzOSAwIFIgL3RocmVlIDQwIDAgUiAvdHdvIDQxIDAgUiAvdSA0MiAwIFIKL3YgNDMgMCBSIC95IDQ0IDAgUiAvemVybyA0NSAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC41ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMS1EZWphVnVTYW5zLW1pbnVzIDMwIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKNDYgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMTIwNDE2NTYwMSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA0NwowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAyMTQyOSAwMDAwMCBuIAowMDAwMDIxMTY2IDAwMDAwIG4gCjAwMDAwMjExOTggMDAwMDAgbiAKMDAwMDAyMTMzOCAwMDAwMCBuIAowMDAwMDIxMzU5IDAwMDAwIG4gCjAwMDAwMjEzODAgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDA2IDAwMDAwIG4gCjAwMDAwMTE3ODcgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDExNzY1IDAwMDAwIG4gCjAwMDAwMTk3NzkgMDAwMDAgbiAKMDAwMDAxOTU3OSAwMDAwMCBuIAowMDAwMDE5MTI2IDAwMDAwIG4gCjAwMDAwMjA4MzIgMDAwMDAgbiAKMDAwMDAxMTgwNyAwMDAwMCBuIAowMDAwMDExOTcwIDAwMDAwIG4gCjAwMDAwMTIyMDcgMDAwMDAgbiAKMDAwMDAxMjM0MCAwMDAwMCBuIAowMDAwMDEyNzIwIDAwMDAwIG4gCjAwMDAwMTMwMzcgMDAwMDAgbiAKMDAwMDAxMzM0MiAwMDAwMCBuIAowMDAwMDEzNjQ2IDAwMDAwIG4gCjAwMDAwMTM5NjggMDAwMDAgbiAKMDAwMDAxNDQzNiAwMDAwMCBuIAowMDAwMDE0NzU4IDAwMDAwIG4gCjAwMDAwMTQ5MjQgMDAwMDAgbiAKMDAwMDAxNTA2OCAwMDAwMCBuIAowMDAwMDE1MTg3IDAwMDAwIG4gCjAwMDAwMTUzNTkgMDAwMDAgbiAKMDAwMDAxNTU5NSAwMDAwMCBuIAowMDAwMDE1ODg2IDAwMDAwIG4gCjAwMDAwMTYwNDEgMDAwMDAgbiAKMDAwMDAxNjE2NCAwMDAwMCBuIAowMDAwMDE2Mzk3IDAwMDAwIG4gCjAwMDAwMTY4MDQgMDAwMDAgbiAKMDAwMDAxNzE5NyAwMDAwMCBuIAowMDAwMDE3Mjg3IDAwMDAwIG4gCjAwMDAwMTc0OTMgMDAwMDAgbiAKMDAwMDAxNzkwNiAwMDAwMCBuIAowMDAwMDE4MjMwIDAwMDAwIG4gCjAwMDAwMTg0NzcgMDAwMDAgbiAKMDAwMDAxODYyNCAwMDAwMCBuIAowMDAwMDE4ODM4IDAwMDAwIG4gCjAwMDAwMjE0ODkgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA0NiAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNDcgPj4Kc3RhcnR4cmVmCjIxNjQ2CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:00.772458\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 0 - Variance: 1.2570290565490723\n", "Layer 2 - Variance: 0.5786585807800293\n", "Layer 4 - Variance: 0.2740468978881836\n", "Layer 6 - Variance: 0.2201044261455536\n", "Layer 8 - Variance: 0.3423171937465668\n"]}], "source": ["model = BaseNetwork(act_fn=nn.Tanh()).to(device)\n", "xavier_init(model)\n", "visualize_gradients(model, print_variance=True)\n", "visualize_activations(model, print_variance=True)"]}, {"cell_type": "markdown", "id": "3609de04", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.215252, "end_time": "2021-12-04T15:56:02.163103", "exception": false, "start_time": "2021-12-04T15:56:01.947851", "status": "completed"}, "tags": []}, "source": ["Although the variance decreases over depth, it is apparent that the activation distribution becomes more focused on the low values.\n", "Therefore, our variance will stabilize around 0.25 if we would go even deeper.\n", "Hence, we can conclude that the Xavier initialization works well for Tanh networks.\n", "But what about ReLU networks?\n", "Here, we cannot take the previous assumption of the non-linearity becoming linear for small values.\n", "The ReLU activation function sets (in expectation) half of the inputs to 0 so that also the expectation of the input is not zero.\n", "However, as long as the expectation of $W$ is zero and $b=0$, the expectation of the output is zero.\n", "The part where the calculation of the ReLU initialization differs from the identity is when determining $\\text{Var}(w_{ij}x_{j})$:\n", "\n", "$$\\text{Var}(w_{ij}x_{j})=\\underbrace{\\mathbb{E}[w_{ij}^2]}_{=\\text{Var}(w_{ij})}\\mathbb{E}[x_{j}^2]-\\underbrace{\\mathbb{E}[w_{ij}]^2}_{=0}\\mathbb{E}[x_{j}]^2=\\text{Var}(w_{ij})\\mathbb{E}[x_{j}^2]$$\n", "\n", "If we assume now that $x$ is the output of a ReLU activation (from a previous layer, $x=max(0,\\tilde{y})$), we can calculate the expectation as follows:\n", "\n", "\n", "$$\n", "\\begin{split}\n", " \\mathbb{E}[x^2] & =\\mathbb{E}[\\max(0,\\tilde{y})^2]\\\\\n", " & =\\frac{1}{2}\\mathbb{E}[{\\tilde{y}}^2]\\hspace{2cm}\\tilde{y}\\text{ is zero-centered and symmetric}\\\\\n", " & =\\frac{1}{2}\\text{Var}(\\tilde{y})\n", "\\end{split}$$\n", "\n", "Thus, we see that we have an additional factor of 1/2 in the equation, so that our desired weight variance becomes $2/d_x$.\n", "This gives us the Kaiming initialization (see [He, K. et al.\n", "(2015)](https://arxiv.org/pdf/1502.01852.pdf)).\n", "Note that the Kaiming initialization does not use the harmonic mean between input and output size.\n", "In their paper (Section 2.2, Backward Propagation, last paragraph), they argue that using $d_x$ or $d_y$ both lead to stable gradients throughout the network, and only depend on the overall input and output size of the network.\n", "Hence, we can use here only the input $d_x$:"]}, {"cell_type": "code", "execution_count": 19, "id": "d4b7f43f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:02.594108Z", "iopub.status.busy": "2021-12-04T15:56:02.593625Z", "iopub.status.idle": "2021-12-04T15:56:15.072435Z", "shell.execute_reply": "2021-12-04T15:56:15.071951Z"}, "papermill": {"duration": 12.695197, "end_time": "2021-12-04T15:56:15.072560", "exception": false, "start_time": "2021-12-04T15:56:02.377363", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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L29uZSAvdHdvIC90aHJlZSAvZm91ciAvZml2ZSAvc2l4IDU2Ci9laWdodCA2NyAvQyA3MSAvRyA5NyAvYSAvYiAxMDAgL2QgL2UgMTAzIC9nIC9oIC9pIDEwOCAvbCAvbSAvbiAvbyAxMTQgL3IKL3MgL3QgL3UgMTE5IC93IDEyMSAveSBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9DIDE3IDAgUiAvRyAxOCAwIFIgL2EgMTkgMCBSIC9iIDIwIDAgUiAvZCAyMSAwIFIgL2UgMjIgMCBSCi9laWdodCAyMyAwIFIgL2ZpdmUgMjQgMCBSIC9mb3VyIDI1IDAgUiAvZyAyNiAwIFIgL2ggMjcgMCBSIC9pIDI4IDAgUgovbCAyOSAwIFIgL20gMzAgMCBSIC9uIDMyIDAgUiAvbyAzMyAwIFIgL29uZSAzNCAwIFIgL3BlcmlvZCAzNSAwIFIKL3IgMzYgMCBSIC9zIDM3IDAgUiAvc2l4IDM4IDAgUiAvc3BhY2UgMzkgMCBSIC90IDQwIDAgUiAvdGhyZWUgNDEgMCBSCi90d28gNDIgMCBSIC91IDQzIDAgUiAvdyA0NCAwIFIgL3kgNDUgMCBSIC96ZXJvIDQ2IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzEgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0NyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1NjA3KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQ4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDIxMTA3IDAwMDAwIG4gCjAwMDAwMjA4NDQgMDAwMDAgbiAKMDAwMDAyMDg3NiAwMDAwMCBuIAowMDAwMDIxMDE2IDAwMDAwIG4gCjAwMDAwMjEwMzcgMDAwMDAgbiAKMDAwMDAyMTA1OCAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDYgMDAwMDAgbiAKMDAwMDAxMDY2NSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMTA2NDMgMDAwMDAgbiAKMDAwMDAxOTQ0NyAwMDAwMCBuIAowMDAwMDE5MjQ3IDAwMDAwIG4gCjAwMDAwMTg3OTAgMDAwMDAgbiAKMDAwMDAyMDUwMCAwMDAwMCBuIAowMDAwMDEwNjg1IDAwMDAwIG4gCjAwMDAwMTA5OTMgMDAwMDAgbiAKMDAwMDAxMTMxMyAwMDAwMCBuIAowMDAwMDExNjkzIDAwMDAwIG4gCjAwMDAwMTIwMTAgMDAwMDAgbiAKMDAwMDAxMjMxNCAwMDAwMCBuIAowMDAwMDEyNjM2IDAwMDAwIG4gCjAwMDAwMTMxMDQgMDAwMDAgbiAKMDAwMDAxMzQyNiAwMDAwMCBuIAowMDAwMDEzNTkyIDAwMDAwIG4gCjAwMDAwMTQwMDYgMDAwMDAgbiAKMDAwMDAxNDI0MyAwMDAwMCBuIAowMDAwMDE0Mzg3IDAwMDAwIG4gCjAwMDAwMTQ1MDYgMDAwMDAgbiAKMDAwMDAxNDgzNyAwMDAwMCBuIAowMDAwMDE1MDA5IDAwMDAwIG4gCjAwMDAwMTUyNDUgMDAwMDAgbiAKMDAwMDAxNTUzNiAwMDAwMCBuIAowMDAwMDE1NjkxIDAwMDAwIG4gCjAwMDAwMTU4MTQgMDAwMDAgbiAKMDAwMDAxNjA0NyAwMDAwMCBuIAowMDAwMDE2NDU0IDAwMDAwIG4gCjAwMDAwMTY4NDcgMDAwMDAgbiAKMDAwMDAxNjkzNyAwMDAwMCBuIAowMDAwMDE3MTQzIDAwMDAwIG4gCjAwMDAwMTc1NTYgMDAwMDAgbiAKMDAwMDAxNzg4MCAwMDAwMCBuIAowMDAwMDE4MTI3IDAwMDAwIG4gCjAwMDAwMTgyODggMDAwMDAgbiAKMDAwMDAxODUwMiAwMDAwMCBuIAowMDAwMDIxMTY3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ4ID4+CnN0YXJ0eHJlZgoyMTMyNAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:06.552473\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["layers.0.weight - Variance: 4.737732888315804e-05\n", "layers.2.weight - Variance: 5.9308793424861506e-05\n", "layers.4.weight - Variance: 7.343693141592667e-05\n", "layers.6.weight - Variance: 0.00016474377480335534\n", "layers.8.weight - Variance: 0.0029673215467482805\n"]}, {"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDg5NC4wMjUgMjE2LjY2NTYyNSBdIC9QYXJlbnQgMiAwIFIgL1Jlc291cmNlcyA4IDAgUgovVHlwZSAvUGFnZSA+PgplbmRvYmoKOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEyIDAgUiA+PgpzdHJlYW0KeJzVnU+TJbtx3ff9Ke5SWrCIv5nAkgzajFB4Q4lhLxxe0BRlP8YbKshHUqFv73OAe6sSuOjh65mepqEJ6k2fuY1b+StUFVBInPS337/89Gf+9n9+uOH/3dzt9/jff+Dvv+TPLw4/fXopNR0uZPz9+/PvwcshkgV//R4fG378vy8v//bijupVkrpcym3+IVXnqzgttz/xS3/59IHzh5fp0y8vqR4FX5PCUfsXfnrBpw9J0Zdg5O+t7LUc5aHfWxi0dsx/vD017oMcxd//oJ2Yj3r70+9u/+P2h9tPfxY6uH/C/36P/zVwLz/9xe/++t1vf/fPv/z57bc/vIg7pOSkcTjiSx2O4uVfXn51++OjYXf4jJPyaLv9+Mu7+vLHFw9yP3H4pxyOrOKqsslbyId3bO63n15+/uvbT/+rv3l/+/W/vdQDZ0mrFOWp/PW/vvzP2z/kf7z9r9uv/+nlv/wawbvDeTbqzN9++4kt/OQXv/v9b/77X/7lN3/44SefvvvDX364/eLfb796+VU72vdn5h2YFF99GE/zJb8DNe/yEdmaFzT6OjZ3wnIXrG8WedYDHT1mP0Z+ye8ReY7oMGjNSXX1x0SebeSmJUGfKkW1JFf9rR5q2whjGz/7xxtuEjWoK5H/d/uH3/75u7/+5s/f/fsfbn/9zfc/fHu4P/7O8WVcQ2jtx3JoyTXqZ/vS8QG96d6ixiPi5uDrGPAlf2XA7AVea/Qh1PrZmP2HxexdxH+roBcOQRv9K6OuFW3WGlLNtXw26vBxUcd0BPHiZIr60r8yah/Rc3nXLFKqfjbs+HFhSzqkxjnoh/q1IeNhnX3EYyf5vxFyGu+TbOQnbA43voiWSsGdFpdJ/sxt8he/+8MP3/35P7+SXexxhlpcrAGPD4yjUh9RhYjHP8Dcx1SpDcHQO0IsIaV8Bz388s3+8sv0yy8vOR8Z40IZhzh4ZJWaQyrDnXVU1SWM6r6fWnioGJP9/P//4BdhtvFdcDjXcQh+VDVUt0ByV7cIfhUmgsd41AkObgx+UIsmSU9IHuoewS/CRPAJxxfETWf+UiPH1jjLIxKj7hH8IkwEr4f3JZbpzF8qwsw5OX1C8lD3CH4RJoKvR8IjQtMY/KXGw+PrSp2RnOoewS/C/PSi/kDvxVBoCN6o8QjZ1ywzklPdIvhVmAg+HS5mTeOZN2o8Ek5yTDOSU90j+EWYCB5jm+RzmM78pWLAjbt61hnJqe4R/CJMBF8w2tTopzN/qenApN2rjkiMukfwizA/vRR31BxdHc+8UTNubTW3+7ppwahbBL8KE8Hz1lVKGc+8UYUv5fBrIxKj7hH8IkwEj/G6JJHpzF9qOWp0LtURiVH3CH4RJoLHeB1TlJzH4E81hyMWtBkGJFbdI/hFmJ9eKqbJKmHs9aeIqQym3rn4EYhRtwh9ESQiD0cqwYXxtF8qxrO1+lAGIFbcI/RFlIg9HVq0+DH0h6h8nKl6P/Cw6h6hPweJyPVwNeY6nfVTLeUIGkv1FogV9wh9ESVir0esNY6TuVPkm9QsJaY4ABnkPYJ/DpNrTRinu+xlPPFG9nw9WGOQOlCx6hbhLyMlAIzVvatz/A/VQ3YppjJjsfomAJ4jZfwYrnvRKBOAU/YZY9oSQtQRjJU3AbAItS+3Sgg5+InAKXvM5NWjs+tExuqbIFjECgQeI/dQops6wSV7zOeLxD6Xs2isvgeCVaxEgPF7bOu7I4JT9hIPcVk6AoPG6psgWMRKBBjFx1p07gWn7DG1ScGlMJEx8iYAFpESAMbyKYvMfeCUMcQ9NKQQ4wTG6psgWMQKBIHrgz6lqQ9cMi9+VQx9dEJj9T0QrGIlAozts4Y49YJLrh5PgNReaJo2jLpJ+Is4GT6G9xKd1yn8U1bB+D+FpCMVK28CYBEpAWCQL6W4KcvukkUwC8iYBoxcrLwJgEWkBIBRvqZc5h5wyllxtysiExcrbwJgESkARI+hrYs69YBLzuHQJLXUiYuR9wCwipQA4lGL8PdHAJdcjuox+okjFytvAmARKQEI2vA1zT3gkoXrW7697LONGHkTAItICaAcuaqGuQdccj5iLKoycTHyJgAWkQJAwgDfxeynHmDkhOd9lRgmLkbeA8AqUgLA+N5VNDMBuGSztmkb2W3JcxkpAWB477MrcQJwyRF/K8wzn7mc8iYAFpESAAb3wRWZe8AlR1zswfVLYOByypsAWETKdHQM7gPGM1MPMDJPuqu1PnE55T0ArCIlAIzuY0jTu3ErxwPt+CRPXE55EwCLSAkAo/vIt5wTgEuOzHXlK/EnLg95EwCLSO87MlJybu4Bl2yy2kYueyW7LSMlAIzuU9U694BLRqQxZinPXB7yJgAWkQKAYHSfc9YxCcLKTOqUvuQ/cXnIewBYRUoAGN0LhrhpAjDITHWTiYuRNwGwiJQAMLoX9WnuAYNcgrQ18KmRh7wJgEWkBIDRPaZ1ce4Bg6yquTxzecibAFhEyt0m7iha1E89YJQ1x+gXXO7ylwIY98cc+fYfL69GNOL451/exn008xaO6kphRrNg8iZ95O4kec/zpeGI1SXfRrM5FK09sVOdammzHM0utOV/5T0v5CCc/NWYS9t/VNyRnQ++zYlzyiW2/KnAUDPTxBRjJOgteSwdmW/RI+FgFpG99KwqZSJlvWFkVTClyrEnHCUgDeWW4yG44QY+Y0vB34Pz6ZbbJpxU22froTjgG+/KWbSmnrSDaUoschNm5iKy1BNaRDT5fFMm9WSXed+u3EUm3vmb5gO39badrCLIknDeb8UjmNjHuVW5aRVIblwl96LSRn8V8SSH6RBkHAzayu29uYt8Wcpt0B7QHDpLrPf15RKjeL15BBKC+r7iiC5RQ3SxLbclsOxvXx0gZsc3bq3XJU3J33XFpyO30OI85JQk3Fcuo6uiua1c8u4U6l0vBT0Hh9NOfPL9bo5jTlIi1zeg47nu+gsP7xAW0KbWTuC+qXrXJXBjT1sc5j6PPj32npslNfL48+HwD31lCb0w4ZBx+DhFmD8n35tPbQeV4zK744kN969VXFkVCHDNePTVILmvROH84kAq4eP0a66l3YrwIYkp416M/oFHEaboTUaX1pDRRVTRYfGrj1fZOfqAH8gGp7Ife8iHlIpr9SYY3biEq67JBT8gIvSugn4kvr8MDBUnXxyGgLzYEnpBf0UWeLjKoTG4RJz6/uIoMV0QTwhcDTiO+1mKXFjDFSctkRRXWXm8ZEk48fw0N1Wke/CJHRboqPqKT/TpKELw6EM9MxvXgz5m71WkaruscUraBUzIjqkM7WrH5RmC3EdzuOb6jNbhhtpDaScQd4T7cy/5PvadbobuHvhaHnfqBd7d0IvuN+y/uZ13tbMfLS53+X561R8Av/GmrcLP3/rZth2C+rFbENE7S66u/cGlhtvhvaXXNxD+t9/85+/+hJ/sDsK7xcNbPBm6EcTszfBk6zB6M+ByROd5ShpFH0TnKTLl0hh5YGRaefZoYO84P8A/X/rAfFn1KXR13FVwxxuf80Z+h/33AbcC3JszbkuK28GHGzZ8Q3wV4xAMs8Zh4qW+BzxcEo4jOd6tPgPvM7YN3y7+yBENL42p/1/yOxCIzLBrrTmRH0XgNfuGgKdIQXMxcLz1zv4N37CXveku84XdzDl79F/q6fAhEDRi2B9jGRmc6jshwLDNY/ZaA0bAn/c7eHJ5+BAKHDlLwCGmkYPR34kEhu4YmmLkzOy2tzk/fAwJLrumgsfVROLS34kEB7XKLoEBJWZub3KD+BgUkg5METASm1Bc+nuhEFz+mCznrJjUfJlLBGeVV4tf4RbxhWz5SuXHvkP4269UPvs+BmNF9EZclHXKMsJn8WtOpyyjSVaQKU0eGnnI5yuVPUAsIu5jTtohTJvpJ7kW79Izn4e8GYhFxACBeSyeOUHHlYZJxtFLeubzkPcCsYqYIMLBl2h56hGjXIO0jUdTIw95MxCLiAkiHRj6ljT3iEvmIhN+LU58jLwZiEXEBCF8Wyth7hGXjPtjCNLexIx8TnkzEIuICQKTZc3Jzz3ikhGx5Npe1E58HvJmIBYRA0TGXLt4P2UnWTkesWAy45/4nPJeIFYREwQGU0XqlKVk5YjfK76PIwY+p7wZiEXEBCEH3zRNm/WtnPj2uvYeYRox8mYgFhETRDlSLTmnCcQlJ1r6+bayMvI55c1ALCIGCExViksxTj3CyBlTZswTy8THyHuBWEVMELj7e+cnOwMrc/WlMq6Rj5E3A7GImCAwe/BcaJhAXHI9fAqubWq1jRh5MxCLiAmCWzW81LlHnHIOmHDm3McRVyNW3gzEImKAUK78aZqym4zM5fSaRWTiY+S9QKwiJgjMHmIMU5aTkcUfGqNry/SmEStvBmIRMUHgMRhpTjlyeKhSjxqiT2miY+TNMDzHSwrCzATmrIwYTlk5pFandaBj1c0wLAImBwSUnUxv6U61+qNiptlSDkwTVt4Mw3O8oFA885PSfFFcckuUSS3j52rCiHtBWIVLCpgzSJiS4C6ViTLMOGmpN5aN1TcD8RwxOQjzu5hQM4I4ZaZ0hVqrTyMgK28GYhEySWDOoHEyTLhUeqRgRlFqmPhYfTMQzxEz88Edqm1xfQBxyS07zwXfB9YXICvvBWIVMklEJofFyTzAyEN+oSVk9c1QLGImioyAvPfT1XHJuBqY0NdzIS0iq2+GYhEzUSg+yc9OKE6ZO2fwyFCdEVl9MxSLmD+9RIeZgwtSphy5S2aaJ2bdZSZk5K1ALCMmCFzvriT1E4hTZvaub/8wAbL6ZigWMRMFpg8+hTz3iVNmWriPVVtivUVk9c1QLGImCuaeubbvYEBxynx2Vgwm00jIypuBWERMEJhFhFzC3CdOGbOthOF1mfhYeTMQi4gBwntm5ctkRGBkbl8pffnftGHUvTCs4iUGzCKixqoThlPO9SgVw+sy0rHyZiAWERMEphHP+flGxpChYCDVBpKWj5E3A7GImCBwy0ulytwjTjnjOZlSbRlClo+RNwOxiBggAiYROWmeeoSRuUHHBxnxGHUvDKt4iYG791yOU38wsmCyJak51NlGjLwZiEXEBIHpg0gMc3+4ZNq119i2T9lGjLwZiEXEBIHZgwbuzxpBXDI3GnGv2ojnUjfDsIiXGCq3IZfJwsDKGEVr0LalcqRzypuBWEQMEDFgMh1Fx6QhK8dDaywtKcY2YuS9QKwiJoj0XMru+0GOR8qhtAIeI59T3gzEImKCkFX1PitzewnmFemJzylvBmIRMUHUVSU/K3NjrOY+zRj4nPJmIBYRcw+bX1X1szJT8Utpy1oTn4e8F4hVxAQRVxX+rEyDl6ptk/jI55Q3A7GImCBkVe3PyoiY3nf6zOchbwZiETFBlFXlPysPIJZ8vhaE3Rf10txAbj8Sy7MbyPOuGkyTmWTN0h6x2aMzkb6kvmqFgFKuNPfgte4xd2rLmwgf3b0lDrR7Ybzn2bqWOMcnpvOlzbJCxoM0Y8DdWsCEI9w/jICLMic3cTWsOwbhP7i1ZLoZYFyeitOepigOw9TE/T84HifZteEZTvfhXXCc1ikXD0LpKWwJc7xKb41UcfTS7TSY0JW1cM8VnUUiToG/pzfxpUlOtBbBTFHvjXC5LgFhxoFI8PGRIhXwNaz5VFlhnnWSKXucVp+5kzLgb1HlniiTo0MvaS7JJVb3yBspSel7ohmjTp+L3BMpnBMan9BEJePoetN6eGGO5q0InVGYz025HGSq/saNZdyA3NYY+e3Vabh1j2rE3toumangjMwTYIqPtesU0XNiqxqvPNS+UqlHrtHnbtDBpbrQ17Qr7SqarQlrXNJSxN/16nHWetGIgFPrHiuhmHTTaqUZQjDPtgGodPFAz9VeSYHF6uNdx4yUb757ilFybQm5Blqi0MWj6UzFk7uODhJSL9SAQWvsu6Uqs90l0x6FNigh+hrvOq62SnsUntzo7nBqPjxaoQ0Ki7YWEd8PX7iEL7Q1YR9Gh+qHUw4u3tLVhLanmDv2ASO3DMairl/MUqJ35f7iHte4UzTj0XuKxD4Xd6ktfeLKqkrTHJFyf6nrY+tuOD2Cech9JaRV3EJPoHtJdNndv7QewpLdkblY6NuxFWTAVx+K9pipxglern0NwfujBtDLLYFNU2q7hvi6zJWEy5emJjXw0m1y5lUsNDXhgoM/XykhmnhPi8QoqD8KfeENKXark4puqu2wPS9AdaEViBPcDnsjgf4uWZsDiqR6/8LAvTx8b9ccUNANexuBVjleaTHJHOWWtt2nriHzVtqqDtLnpQUT0emkXdt8KFWM4/1jON+8XVIrXBHdOeSNvXJhbHXu+5B3vOejf7YsllfkN7iavLK5/TVPDLS83Pf+6VV3DfzGmzfQr7/9s9/xFpeTgHtfuj/xuOL/421Owt/F5gTHyv47Jw/j7oY75FOpFCMPkEwra5uT6wNu/Kqv3iGd6OSAJ9Bka2rkd/CpSPSOwuNGVTjM+HCbk2+ID8+SxIfT+D7JyO+BD8/Pytak0iTpS4xOvh2BHDhEEJ2SGIz8DgQybva5tdbsvL7C6AS3ZA56Whd7X5eTb9jJ3nST+UIrGdqcXUf/pS4nHwKB9SQxgI46Qrjkd4JAmz+WqMZ46G/4ODz5nHwIB1r+oXdiFDyCMPo7kaBfIe3cBFcdRnhvMjr5GBRRMSrF+HMiccrvBQIjw+ocxquYt7/R5uRjQHCykj3nWiOJS38vFM04xWNki0vk87eJ12xOIh+TV4tfYXPyhWw/9J1RovNleaokk+hw+VxJZpLx7G/bb6dGHvLlHLsFiEXEBIE52nNFmUm+QCz5bAZiETFB6KqyzCRXNBSe+TzkzUAsIiaIuqowY+VuIV8mPJe6GYZFvMAQwqrOjJWZoOBLMy+e6DzkvUCsIiaItKo3Y+XmmVzaU27i85A3A7GImCBkVXfGyqyu4XOzuhv5nPJmIBYRE0Rd1Z+xMmuwYoQSn/ic8mYgFhEDBIZ6izo0VkbEkrvT/sTnIe8FYhUxQcRVPRorc2FJmsf0iOehboZhES8xyKoqjZUT/ob2w0THyJuBWERMEGVVncbKibUHclsnGPmc8mYgFhEDRHKrKjVWztfaqm3EyHuBWEVMEPzhqVqNlfVwrA9QJj5G3gzEImKCyKuqNVauh+JyaPUobCNG3gzEImKC0FX1GiNnpkG41NbvTSNW3gzEImKCqKsqNkbObS283nvExcfIm4FYRAwQbbk6ZTf1iEsWjqlL7u8irkasvBeIVcQEkY5U3aOUxwXilJUZJSIpjHysvBmIRcQEIYfyY3OPOOWCMRQL20x8rLwZiEXEBFHbLmOZODxUVjli7aUw0rHyZhie4wUFwczBqUx+DkZmQaMYQpARzyDvBWIVMkmgg/s4OfBeqg/MScNf08TH6puBeI6YHIRZvfj9CcQps+BScTnnCZCVNwOxCJkkmG44Y+ga3RoQtNc0sbH6ZhDmaJn/gY9FfGSCcMkskZVCxsTbwrHiXhBWAZMDfogyL22dKlMxedb7vcHQsfpmIJ4jJgdMGRJ+Ye4Pp8zkU+ke7SMgI28GYhEySWDOkIqfRlGnytqFjnMqmfhYfTMQzxGTA6YMGQ1Mw6hL9syazzW2vGADyMqbgViEDBIlMCD1YxqTkVlHkbtNWxKzJWT1vVCsYiYKzBokpzp1iktmUjgaCS172SKy+mYoFjETBbec4wzPveKUazxyqKkZxJlGrLwZiEXEBFEPp+pk7hOnXBxT212c+Fh5MxCLiFnRE2OCEliXYABxyXTcjcwJGflYeS8Qq4gJIh7ynJxvZE6yUpRW08M0YuXNQCwiJgiWPE4pzD3ilDNr4+bcJxtXI1beDMQiYoJgzWY8CKdx9iXngNFTqv0JavgYeTMQi4g/veB+x/06tY4JQ1YuR02Z9V4GPlbeCsQyYoIIrO2sqhOIS+Y+Hqbnj3gudTMMi3iJIXOjVpa5P1xy5q5GSX6iY+TNQCwiJgjl3saY5v5wyVy4QHBh4mPkzUAsIiYIluiuPs494pITrajb3tMRz0PdDMMiXmDw4bnk4veDjKelBJ/rRMfIe4FYRUwQmDk8F5m0Mn1MkmsJMSOfU94MxCJigpBVkUkrx4M7+tuoaeRzypuBWERMEHVVZNLKTKu9b2Ue+ZzyZiAWEXMLn18VmbQyXStyX8qa+DzkvUCsIiaIuCoyOcmPxPupkS3z8ZcREwQO6bnI5CQ/tmJMjbzXDo33tDeZd9PEinPFrOKMizqG2vOla+7zphjQWk7d3SRjJn2mVHr1SVrVuOiK1P5pPRBW6gmGxVffp+OxHkJPxTb0FI/O8cjCK06Ca54lQVMOPbHEA0piBj9G5oHmA/3TLNrJDQ40GcDxS1s1aKZ8aLHSskRTqNrTleTAHCfUzOlexo/NQYTJO15LLZ6GDVq0xP6Nhbsohd6gtJiP+Z7P4A6HAWEVepY4cfe3LNnjqBL62a0tXeHA0z0NJNbopNBoQpQFgJqMYWWhEcyt0CIjlH4kmX7EJUnbAZZz6ZYXXCrGUIP2HT7wDNdWK4brplUxVynoGHTOAJ2+wJjpLiXtFXnE12vo4dMpP+fKpffM5M5cQrivueHD3BDn8U3cX116O/XweLC3tYiEq6j49Fi7izloczMRPO9qfy/vOT6ObJ32IZhMB7mv4Dhaq9RuN+IxpNa7Luqa54qtZpAUw4mEn3qVg+Cc66eDaTShMD2b7bhS0n2tjEaU0jxaEHjN1fWFI1q40iyG6yW4CkIn3FxAUmlu+cCBuHPX0X0kYt4DneWqXL6vNlRaZzTvlkQDhNRt3NDCUVnTq52Rmoq0mEp7n1DRI/iuyaHb9Tdw6GA50hGlJFoN104M57hUTzcVxIDO0H1V+JoqgFgq7EkFD4/SX9oAkqjGdGv2PTU1gxOQwg9Z0e9zwcMGPUHvbzaCgFClD08ueCZ1WVnSs/CFB9MPXN8Qyum/JoSsTGgE5W78myrtkCtdUhK7o3aDl+yAn6Y4LQ8UpzC0E805Y8ApwMOul5LtRjzZMWRcA20qGeiRmO4TK6m8M/OmgEs0pC7jhpc9bVIS3Yi0tq27UA58wpU2yMI11Bd92pMWs7P7Bh/8ezNEme/CiF1el9/gQ/LK9vPXTCvQ8nJn+qdX7S/wG2/e4r7+9s9+x1t8SHiHrW/yH0l/F/8R3Mx4Oc+ZvUqT8+dqM0Ye4JhW1v4j5wf4J33Ro/o1+whcbRl3oPn91SW/h30Ebi54pqvnrfcz9hHfyn/k2+ETPsUjHoIjPiO/Az7hpJ+teRpwfJH/yDckgEeBxpKnDmTk9yDApxNb85zxfoX9iLhmqsVKqbRQel8Hkm94lb7pNvOFF2mzzjuP/ksdSD4EAkYuBUMzpyOES34nCMohMAYgNeLx/SYDkg/B4B1OEkb0pY4cjP5OIFjFJmMu5Vgl8G3+Ix9DAhM18fSWHEGc8ntxiNo2WhTa59W3+Y98DAhl4nugJ+dI4tLfCwUnZNzEilmJ/zyK1/xHMpf9rha/wn/kC9l+7EsdxLeonoMx9Kp6ziQrZtCxDRWHRh7y+VJnCxCriAkirOrnTHKR2BcLx0Ye8mYgFhETRF7Vz7Ey3/Bqf6VhGzHyZiAWEROEHov6OVZGxLjBNW/3ic9D3gzEIuI+HVvUz7FyxJ1e+muYkc8pbwZiETGmFS6sKuhYmc7YEvs6+sDnlLcCsYyYINKqgo6VIy27eyLmyOeUNwOxiJggZFVBx8q0bs4uysTHyJuBWERMEGVVQ8fKCb+HhtMTn1PeDMQiYoDwflVDx8r5cLXGkiY+Rt4LxCpigoirGjpW5oQ5+/acHPmc8mYgFhETRF7V0LFys71ObeXQNmLkzUAsIiaIsqqhY2VFJ0g1pomPkTcDsYgYIIJb1dCxcjkkeN9KrNlGjLwXiFXEBBFWNXSMzCU7r9pW8kwjVt4MxCJigsirGjpG5jJ9CH3t0vIx8mYgFhEThK5q6BiZxVQ0aHvxYPkYeTMQi4gJArMHpj3UCcQpM+citU4w8LHyZiAWEQNExOwh0ol7BHHJiiFk8bXoyMfKe4FYRUwQ3CTvc5h6xCUzuYL5DRMfK28GYhExQQizqKKfe8Qp42F51BRTHflYeTMQi4gJArOHHDFtmECcsg/+KLHnNlk+Rt4MxCJirngzf6WUMvWIS2ZWmPhUWn0AC8jqe6FYxUwUfNeURKY+ccleHJOmgtYJkdU3Q7GImSgyC2GlyXfByKxBlrzoE6FL3gzEImKCKIdXCfPV8VB7DTXVlhpp8Vh9MxDPEYNDxm8V1vYaQVwyz7zLKbScOgPIynuBWIVMEqFl/foJxENlsmzGhLvfIwwfq28G4jlicuBbt5jr3CNOmWnG0Yfk6gjIypuBWIRMEiyLWScH6Uv1fB1TMKqUiY/VNwPxHDE50N40e5l7xClXJhd3+9OrCSNuBmERLigwz597H0YIp4rIsgux3yavJqy8F4ZFvKSQWO9VJ68FI7OSJm4CbUPk1YZVN8OwCJgchDsC8mS1YGSm56eiLQnC4jHyZiAWERMEi56W6OYOccq4MUqMeeZj5c1ALCIGCG1bf+bUfCO3vRbKwsojHyPvBWIVMUFg1hBr0alHGJllqu87ZG0jRt4MxCJigmjLdqyhMIK4ZOG8gsX0Rj5G3gzEImKCoEWbT2nuEZfMcuVB2q4424iRNwOxiBggimNBZwwHRhBG5uKFpJomPkbeC8QqYoLArEGim+wWrJzwJfdteSOfU94MxCJigsC0QUqZ7BasHPmgqK2GvG3EyJuBWERMEHTaSbnMPeKSI5uSlkM/8jnlzUAsIiaI+lwV8ftB5m5e12uwj3xOeTMQi4i5Y8mv6kBaOR7cDt120Y98TnkvEKuI+9atRR1IK8fDl7s78MjnlDcDsYiYIGRVB9LKA4gln81ALCImiLKqA2ll+jS4Zt824bmrm2FYxMvtNH5VB3KSHzsRpka23KCwjJgg4qoOpJXNTgTbyDtuUHhPH5J5V02q8bERQzEQpKs6jjzH3EwhxDk8+mjp0avWZemjJYQfmFud2miJda/7p2nVpKmXdsP8s8o9/1YltRFFOnBc0pd+XD1cCS0VJaHT9C18TM2MteY+c4kNzz1RUT1uwC1/0WPA2t+CeUxjMgbvShljuD7x9eVAZxRpJoMtq6+3UXHOXEwt462U3N2cJeCccU1W6WVSopRY7mlPdJ2i70OgZX7RnvLBhbus9H2gMUTy0lNiOI1ICSyzMLc09xtlUJa4K07piEpXN+1tM8nO0bCFr68APcV7Oknkxt96o0u/K72KKEalB39RMivd+JxKfyxHf4gK6/8ofg+z22bZIBHnCcP4cqvu8Ogj0j+M0xs9fU1ayXbOeLpOj45KaxbvmdQEPn01ivv/cZprs+vAkefcDjHRGiNpW51p5iQxxvt6ZuBl75sdCG0/pC/7JXq1uKqtLLj33Np9X/dzjo8NrnYxN70+VkszaOGyar78dB/vOk66OE8nbsUR8CzWu66x0s9l8DjhpvToS/F37xOX+pAOMm1ocrf9Lwlnt97X3pLinJa21a+kUFI72Ykrce2QeZi+aG3bS7hGhQuvW7S094+1Jz1kzyFjoCkK6FTNNPbpKzn4a2kpAOiEgR4tTWddc9cNZujK4mufnnLhqxCCD81epmckZpZuEDZZWxy5d71ceWEy44ZnXIT53f0FcSxeI1OTcICxp24J7YTYa9nF0JYPj7eo6AUel6g0N5HUnDtE0H0RIRPhlN2+9pPNM6m4wHgJFK7M9DbQf2IsuHK5cAX82t85OO7GRPi8vKrGbhPNl1PAxUVOlnjDBdOTJdDDI+dezSfV807QDkS5J1drz9rFXSPk3givtVy7uWaNoZuSi3JTEJ3S2j0kcltMk3kKg2ibAqvivNb7zM+h6dr9GDHz6UECJn2k+j6SII95YsXdsUhzD8HF2G5UVDOzp5uhNlR0pZ46WXFqcHj3bVqhXU/fz8+Ugm4yP1OM/Ab7lFf2zr/muYGWl9vqP73q3kHDlbfuz19/+2e/4y32Kbx9J7bCP2+yUZG/i42KtsSXp+TkUlgveIT30AY85vfXBirXB/z4JV+991p5cXqnU8KHkd/B/0Iznwc+alXcut9moIK77DtYqHw7gCU4DgfSdLUZ+R0AFm5uoY8WnsB49v8YC5Vna4tvyEBYNgCPyInBJb8HA1p1BbSWmDXwIxAMPWewUVE8NXgt40h5039fG5VveK3++NvMF16nMdpD/1IPlQ8hIBzeuTIQuLR3IiDtnQhuXInjkTc5qHwIhEozw+YBeUG4tHeCUCqLMEZCCPFt5ikfAoGTnIRpwUDBiO+EgXlKESNycMA8823eKR/DoU25MZsbOFzie3HoXqNKDkl/vHHKx3FosxM/3hyN+F4chAl8vpKD+7y9Un7FQAZT9vt+tH6hfbmBzBeC/di3cZJXFYowZ1tVKLIypl+iudUCto0Y+XwbtweIRcQcc7tVhSIrDyCWfPYCsYqYIMKqQpGVIzpBDM0FYuRzypuBWERMEGlVocjK6cCsyvUeYRox8mYgFhEThK4qFFk58SVjzeWJzylvBmIRMUHUVYUiK9ONm5bPT3xOeTMQi4gBgl7SzxWKrIzHcy3BlYmPkfcCsYqYINKqRpGRczgCRq3tNappxMqbgVhETBCyqlFk5MwKGz71VT3Dx8ibgVhETBBlVaPIyBkhVC1tYc3yMfJmIBYRA0T1qypFRpZw5OxzGvEYdS8Mq3iJIR6LGkVGlsRF447BwLnUzTAs4iWGvKpQZOT7pk0nEx0jbwZiETFBlFWFIiNrPpKm4CY+Vt4MxCLiTy+FiRDPFYqMXBQDydiXz00jVt4KxDJiggirCkVGrvWojmalAx6jboZhES8x4I73XJ/IyN7d6yPLiGfQN0OxiJko9Aiak597xCnzBatDo63Yp0Vk9c1QLGImisryL77OveKUfWyr3kVkQmT1zVAsYgYK77lVt06Jc0b2LdE+1paFYRFZfS8Uq5iJIh2+Bp1cMoyMAeQBsefcWURW3wzFImaikCPVknOaUJxyS7SS2LLNLCEjbwZiETFB0CcpxTj3iVNmsS2NrrZMtAGQ0TdDsYiZ6Q9MjXR+cgwxMs1icHP0rarXgMjoe6FYxUwUTN6calN9b+UhzdEisvpmKBYxEwXTVHHZz73ilNuiWhRpyZEWkdU3Q7GImSiYzaL0QRlRnDJDZn25+EzoIW8GYhExQETHFNMgU5+4ZJ8DgnO9jp4FZPW9UKxiJgpMKGJ1U8bgqbbqLRKk5aFaQFbfDMRzxOSA2URKJc4gTtl7PZzUnnZsAFl5MxCLkEkCk4nspnf7l8pc8hDig4PhY/TNQDxHTA6YSWRJT5fGKXtMxmtMGGKPgKy8GYhFyCCRMJGQ4KfHxqkWOTDJ6G9tTRNW3gvDIl5S4P4HrTr1h0vWelTMLdp2hasNq26GYREwOWAKoXHyDrlU5R4+0VImOEbeDMNzvKRQDn3ep2Bk7otxte/UuNqw6mYYFgGDQ2aJ5xwnvwwjS+RmtNCWMiweI+8FYhUxQXBzv/d+ui4uObMGc+53yasNo26GYREvMbBcND87YTjlzG19ue+wsnSMvBmIRcQEwSrfgZXrRxCnnLlzsPY8IcvHyJuBWETMHRmOe1CTTj3ikkcQSz57gVhFTBCBrs0hTz3CyMqHg7Y8IduIkTcDsYiYIDBp4L7luUdcMkJwwbUtvrYRI28GYhExQehzecvvBzkzqzDqzMfIm4FYREwQdVXQ08oml9A2smuK4TJigOB28ueCnlZO3BfsF3xOeS8Qq4gJIq0KelrZpNmOfPbMvl1GTBCYOjwX9LQyN1/lnl1qGzHyZiAWERNEWRX0tDIGk1LTvUdYPqe8GYhFxNxe5FcFPa1sNmeMfN5tz8Z7WsmM+2tcLUz86pt0RLTWtvci4L89ZwzXehaMi/reC9qsdJnpMEWZPsCt+6W7MjAdvW2XSs3DIYfYVraYnO2q+Jay7QPXQVs+JmKgL0obioVUk+95ux6P28AdeIlGDCDY9j7QIgA9jFMXVnnXeOuprRJUQu32uVlrvWd6RnBFJ8QAl9aZzQBEazhS8K4XRlK6lOg9ERC/qP0tgpecm6eNVjztmPvjb2CAgVAJ9Z4tVyp4KC06kjCdtsl6YGKJM8J3LxKcb+YwTClD+yUGvqBz6ss9byLT80PaG23oIefQs2zwuymIxNYDogulOdIUzx0wTmpoeRZRnatdZ/lcTU5vHgBUWC6zr8CniFEscxEqulgq0lfm8+F8xj/d8BjDN9VuDEQ916RtTSrQxKLcdbr2JI8TQH8YXN5Meum65kizGDqpiM/u3r4eMQZulqNea4zNCARdmlvzUu3r4Dh9tfbP10Oya1vteATok30FiPsPC7NquBaGIWhpvkElhCMnKbSU56Ak9yKxXET07cpvcnLV9ZsSdE726ZIDHXelrH2pLbVagW2Jmr4kEn2969xHKdq+NvuQ8mPxMoGZb0cpDn/vzfDEhZpjt5MJ2d0X+NAn+Q/SYDKvsp/DoDhBuN4Ak9Mu9qF6X++quFyltPUun7w8lsckK9dDIRFOP5iIzppTokcO4qO3je9vgjMuWtz6uDsxNb/Y0l+Uo0uX2sxzHI2Vg/RxfmqF8miGUz1tdKT114I7Eu+fOG0FDSouw/4mCWcfHEp/7YoOHfpr13rwyDXwggqa78sUtIyJuCH5G253dPBt11nhah66BK4XxI97g0/l/lYi4MmJc5PxFBHt5jElow/Tn6JZNiE8ba63nLpnPF9wsWZ6Onlx/Su5D0ab5w0rCcv9+kAvj9JGrHwXxk7VZ3vN9626xDtP8uhpjyE/eDW/GmEGpO/hsDOo3P1qMs5bM7fCTQ83AvSF5oRVCjv+fXAUcHdwbbiM52CK5xMy4DbTrMc0JNd50wQKo2tX+l1Xa7+Ax6cLULbit6/Ib/CaecVg4DV7ErT87D3waW1xQl+aN9kXrL/09dbf4i+D58DheCdof97iL1Nur1hKRDzbpBmX+FraAZqm0spRQtl/MZfGJWQcJf71ux/+/Kfv/vdf+MOwyffl/wHjufKCCmVuZHN0cmVhbQplbmRvYmoKMTIgMCBvYmoKMTEyODkKZW5kb2JqCjEwIDAgb2JqClsgXQplbmRvYmoKMTcgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA5MSA+PgpzdHJlYW0KeJw1jLsNwDAIRHumuBH4OID3iaIU9v5tiC0X3D3pifNsYGSdhyO04xaypnBTTFJOqHcMaqU3HTvoJc39NMl6Lhr0D3H1FbabA5JRJJGHRJfLlWflX3w+DG8cYgplbmRzdHJlYW0KZW5kb2JqCjE4IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTY0ID4+CnN0cmVhbQp4nD2QwRFDIQhE71axJYCAQD3JZHL4v/9rQJNcZB1g96k7gZBRhzPDZ+LJg9OxNHBvFYxrCK8j9AhNApPAxMGaeAwLAadhkWMu31WWVaeVrpqNnte9Y0HVaZc1DW3agfKtjz/CNd6j8BrsHkIHsSh0bmVaC5lYPGucO8yjzOd+Ttt3PRitptSsN3LZ1z06y9RQXlr7hM5otP0n1y+7MV4fhRQ5CAplbmRzdHJlYW0KZW5kb2JqCjE5IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggNjEgPj4Kc3RyZWFtCnicMzU1VzBQsLQAEqamRgrmRpYKKYZcQD6IlctlaGkOZuWAWRbGQAZIGZxhAKTBmnNgenK4MrjSAMsVEMwKZW5kc3RyZWFtCmVuZG9iagoyMCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDMwNyA+PgpzdHJlYW0KeJw9kktuAzEMQ/c+hS4QwPrZnvOkKLqY3n/bJyXpihzZFkVqlrpMWVMekDSThH/p8HCxnfI7bM9mZuBaopeJ5ZTn0BVi7qJ82cxGXVknxeqEZjq36FE5Fwc2Taqfqyyl3S54Dtcmnlv2ET+80KAe1DUuCTd0V6NlKTRjqvt/0nv8jDLgakxdbFKrex88XkRV6OgHR4kiY5cX5+NBCelKwmhaiJV3RQNB7vK0ynsJ7tveasiyB6mYzjspZrDrdFIubheHIR7I8qjw5aPYa0LP+LArJfRI2IYzcifuaMbm1MjikP7ejQRLj65oIfPgr27WLmC8UzpFYmROcqxpi1VO91AU07nDvQwQ9WxFQylzkdXqX8POC2uWbBZ4SvoFHqPdJksOVtnbqE7vrTzZ0PcfWtd0HwplbmRzdHJlYW0KZW5kb2JqCjIxIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjQ0ID4+CnN0cmVhbQp4nEWRTXIFIQiE956iL/Cq5Fc9z6RSWUzuvw3NvCQrWoXmA9MCE0fwEkPsiZUTHzJ8L+gyfLcyO/A62ZlwT7huXMNlwzNhW+A7Kss7XkN3tlI/naGq7xo53i5SNXRlZJ96oZoLzJCIrhFZdCuXdUDTlO5S4RpsW4IU9UqsJ52gNOgRyvB3lGt8dRNPr7HkVM0hWs2tExqKsGx4QdTJJBG1DYsnlnMhUfmqG6s6LmCTJeL0gNyglWZ8elJJETCDfKzJaMwCNtCTu2cXxppLHkWOVzSYsDtJNfCA9+K2vvc2cY/zF/iFd9//Kw591wI+fwBL/l0GCmVuZHN0cmVhbQplbmRvYmoKMjIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMzIgPj4Kc3RyZWFtCnicNVFJbsQwDLv7FfzAANbuvCfFoIf2/9dSyhQIQCW2uCViYyMCLzH4OYjc+JI1oyZ+Z3JX/CxPhUfCreBJFIGX4V52gssbxmU/DjMfvJdWzqTGkwzIRTY9PBEy2CUQOjC7BnXYZtqJviHhsyNSzUaW09cS9NIqBMpTtt/pghJtq/pz+6wLbfvaE052e+pJ5ROI55aswGXjFZPFWAY9UblLMX2Q6myhJ6G8KJ+DbD5qiESXKGfgicHBKNAO7LntZ+JVIWhd3adtY6hGSsfTvw1NTZII+UQJZ7Y07hb+f8+9vtf7D04hVBEKZW5kc3RyZWFtCmVuZG9iagoyMyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIzMSA+PgpzdHJlYW0KeJw1TzmSBCEMy3mFPjBVGNtAv6entjbY+X+6kplOkPAhydMTHZl4mSMjsGbH21pkIGbgU0zFv/a0DxOq9+AeIpSLC2GGkXDWrONuno4X/3aVz1gH7zb4illeENjCTNZXFmcu2wVjaZzEOclujF0TsY11radTWEcwoQyEdLbDlCBzVKT0yY4y5ug4kSeei+/22yx2OX4O6ws2jSEV5/gqeoI2g6Lsee8CGnJB/13d+B5Fu+glIBsJFtZRYu6c5YRfvXZ0HrUoEnNCmkEuEyHN6SqmEJpQrLOjoFJRcKk+p+isn3/lX1wtCmVuZHN0cmVhbQplbmRvYmoKMjQgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNDkgPj4Kc3RyZWFtCnicPVA7jkQhDOs5hS/wJPIjcB5Gqy1m79+uA5opUEx+tjMk0BGBRwwxlK/jJa2groG/i0LxbuLrg8Igq0NSIM56D4h07KY2kRM6HZwzP2E3Y47ARTEGnOl0pj0HJjn7wgqEcxtl7FZIJ4mqIo7qM44pnip7n3gWLO3INlsnkj3kIOFSUonJpZ+Uyj9typQKOmbRBCwSueBkE004y7tJUowZlDLqHqZ2In2sPMijOuhkTc6sI5nZ00/bmfgccLdf2mROlcd0Hsz4nLTOgzkVuvfjiTYHTY3a6Oz3E2kqL1K7HVqdfnUSld0Y5xgSl2d/Gd9k//kH/odaIgplbmRzdHJlYW0KZW5kb2JqCjI1IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzk1ID4+CnN0cmVhbQp4nD1SS27FQAjb5xRcoNLwm895UlXdvPtva0NSqSq8iTHGMH3KkLnlS10ScYXJt16uWzymfC5bWpl5iLuLjSU+ttyX7iG2XXQusTgdR/ILMp0qRKjNqtGh+EKWhQeQTvChC8J9Of7jL4DB17ANuOE9MkGwJOYpQsZuURmaEkERYeeRFaikUJ9Zwt9R7uv3MgVqb4ylC2Mc9Am0BUJtSMQC6kAAROyUVK2QjmckE78V3WdiHGDn0bIBrhlURJZ77MeIqc6ojLxExD5PTfoolkwtVsZuUxlf/JSM1Hx0BSqpNPKU8tBVs9ALWIl5EvY5/Ej459ZsIYY6btbyieUfM8UyEs5gSzlgoZfjR+DbWXURrh25uM50gR+V1nBMtOt+yPVP/nTbWs11vHIIokDlTUHwuw6uRrHExDI+nY0peqIssBqavEYzwWEQEdb3w8gDGv1yvBA0p2sitFgim7ViRI2KbHM9vQTWTO/FOdbDE8Js753WobIzMyohgtq6hmrrQHazvvNwtp8/M+iibQplbmRzdHJlYW0KZW5kb2JqCjI2IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjQ5ID4+CnN0cmVhbQp4nE1RSYoDMAy75xX6QCFek7ynQ5lD5//Xyg6FOQQJr5KTlphYCw8xhB8sPfiRIXM3/Rt+otm7WXqSydn/mOciU1H4UqguYkJdiBvPoRHwPaFrElmxvfE5LKOZc74HH4W4BDOhAWN9STK5qOaVIRNODHUcDlqkwrhrYsPiWtE8jdxu+0ZmZSaEDY9kQtwYgIgg6wKyGCyUNjYTMlnOA+0NyQ1aYNepG1GLgiuU1gl0olbEqszgs+bWdjdDLfLgqH3x+mhWl2CF0Uv1WHhfhT6YqZl27pJCeuFNOyLMHgqkMjstK7V7xOpugfo/y1Lw/cn3+B2vD838XJwKZW5kc3RyZWFtCmVuZG9iagoyNyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDk0ID4+CnN0cmVhbQp4nEWNwRHAIAgE/1RBCQoK2k8mk4f2/40QMnxg5w7uhAULtnlGHwWVJl4VWAdKY9xQj0C94XItydwFD3Anf9rQVJyW03dpkUlVKdykEnn/DmcmkKh50WOd9wtj+yM8CmVuZHN0cmVhbQplbmRvYmoKMjggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3MiA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlxAvqmJuUIuF0gMxMoBswyAtCWcgohngJggbRDFIBZEsZmJGUQdnAGRy+BKAwAl2xbJCmVuZHN0cmVhbQplbmRvYmoKMjkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA0NyA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlyWEFYuF0wsB8wC0ZZwCiKewZUGALlnDScKZW5kc3RyZWFtCmVuZG9iagozMCAwIG9iago8PCAvQkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzOQovU3VidHlwZSAvRm9ybSAvVHlwZSAvWE9iamVjdCA+PgpzdHJlYW0KeJzjMjQwUzA2NVXI5TI3NgKzcsAsI3MjIAski2BBZDO40gAV8wp8CmVuZHN0cmVhbQplbmRvYmoKMzEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNjMgPj4Kc3RyZWFtCnicRZA7EgMhDEN7TqEj+CMDPs9mMik2929j2GxSwNNYIIO7E4LU2oKJ6IKHtiXdBe+tBGdj/Ok2bjUS5AR1gFak42iUUn25xWmVdPFoNnMrC60THWYOepSjGaAQOhXe7aLkcqbuzvlDcPVf9b9i3TmbiYHJyh0IzepT3Pk2O6K6usn+pMfcrNd+K+xVYWlZS8sJt527ZkAJ3FM52qs9Px8KOvYKZW5kc3RyZWFtCmVuZG9iagozMiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIxOCA+PgpzdHJlYW0KeJw9ULmNBDEMy12FGljAeu2pZxaLS6b/9Ej59iLRFkVSKjWZkikvdZQlWVPeOnyWxA55huVuZDYlKkUvk7Al99AK8X2J5hT33dWWs0M0l2g5fgszKqobHdNLNppwKhO6oNzDM/oNbXQDVocesVsg0KRg17YgcscPGAzBmROLIgxKTQb/rnKPn16LGz7D8UMUkZIO5jX/WP3ycw2vU48nkW5vvuJenKkOAxEckpq8I11YsS4SEWk1QU3PwFotgLu3Xv4btCO6DED2icRxmlKOob9rcKXPL+UnU9gKZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDgzID4+CnN0cmVhbQp4nEWMuw3AMAhEe6ZgBH4m9j5RlMLevw0QJW64J909XB0JmSluM8NDBp4MLIZdcYH0ljALXEdQjp3so2HVvuoEjfWmUvPvD5Se7KzihusBAkIaZgplbmRzdHJlYW0KZW5kb2JqCjM0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggNTEgPj4Kc3RyZWFtCnicMza0UDBQMDQwB5JGhkCWkYlCiiEXSADEzOWCCeaAWQZAGqI4B64mhyuDKw0A4bQNmAplbmRzdHJlYW0KZW5kb2JqCjM1IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTYwID4+CnN0cmVhbQp4nEWQORIDMQgEc72CJ0hcgvesy7XB+v+pB9ZHoukCNBy6Fk3KehRoPumxRqG60GvoLEqSRMEWkh1Qp2OIOyhITEhjkki2HoMjmlizXZiZVCqzUuG0acXCv9la1chEjXCN/InpBlT8T+pclPBNg6+SMfoYVLw7g4xJ+F5F3Fox7f5EMLEZ9glvRSYFhImxqdm+z2CGzPcK1zjH8w1MgjfrCmVuZHN0cmVhbQplbmRvYmoKMzYgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMzQgPj4Kc3RyZWFtCnicLVJLcsUgDNtzCl2gM/gH5DzpdLp4vf+2kpNFRg5g9DHlholKfFkgt6PWxLeNzECF4a+rzIXPSNvIOojLkIu4ki2Fe0Qs5DHEPMSC76vxHh75rMzJswfGL9l3Dyv21IRlIePFGdphFcdhFeRYsHUhqnt4U6TDqSTY44v/PsVzLQQtfEbQgF/kn6+O4PmSFmn3mG3TrnqwTDuqpLAcbE9zXiZfWme5Oh7PB8n2rtgRUrsCFIW5M85z4SjTVka0FnY2SGpcbG+O/VhK0IVuXEaKI5CfqSI8oKTJzCYK4o+cHnIqA2Hqmq50chtVcaeezDWbi7czSWbrvkixmcJ5XTiz/gxTZrV5J89yotSpCO+xZ0vQ0Dmunr2WWWh0mxO8pITPxk5PTr5XM+shORUJqWJaV8FpFJliCdsSX1NRU5p6Gf778u7xO37+ASxzfHMKZW5kc3RyZWFtCmVuZG9iagozNyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDMyMCA+PgpzdHJlYW0KeJw1UktuBTEI288puECl8E/O86qqi777b2sTvRVMMGDjKS9Z0ku+1CXbpcPkWx/3JbFC3o/tmsxSxfcWsxTPLa9HzxG3LQoEURM9WJkvFSLUz/ToOqhwSp+BVwi3FBu8g0kAg2r4Bx6lMyBQ50DGu2IyUgOCJNhzaXEIiXImiX+kvJ7fJ62kofQ9WZnL35NLpdAdTU7oAcXKxUmgXUn5oJmYSkSSl+t9sUL0hsCSPD5HMcmA7DaJbaIFJucepSXMxBQ6sMcCvGaa1VXoYMIehymMVwuzqB5s8lsTlaQdreMZ2TDeyzBTYqHhsAXU5mJlgu7l4zWvwojtUZNdw3Duls13CNFo/hsWyuBjFZKAR6exEg1pOMCIwJ5eOMVe8xM5DsCIY52aLAxjaCaneo6JwNCes6VhxsceWvXzD1TpfIcKZW5kc3RyZWFtCmVuZG9iagozOCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE4ID4+CnN0cmVhbQp4nDM2tFAwgMMUQ640AB3mA1IKZW5kc3RyZWFtCmVuZG9iagozOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEzMyA+PgpzdHJlYW0KeJxFj0sOBCEIRPecoo7Axx/ncTLphXP/7YCdbhNjPYVUgbmCoT0uawOdFR8hGbbxt6mWjkVZPlR6UlYPyeCHrMbLIdygLPCCSSqGIVCLmBqRLWVut4DbNg2yspVTpY6wi6Mwj/a0bBUeX6JbInWSP4PEKi/c47odyKXWu96ii75/pAExCQplbmRzdHJlYW0KZW5kb2JqCjQwIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzQwID4+CnN0cmVhbQp4nDVSOW4EMQzr/Qp9IIBu2+/ZIEiR/L8NqdkUA3F0UpQ7WlR2y4eFVLXsdPm0ldoSN+R3ZYXECcmrEu1ShkiovFYh1e+ZMq+3NWcEyFKlwuSk5HHJgj/DpacLx/m2sa/lyB2PHlgVI6FEwDLFxOgals7usGZbfpZpwI94hJwr1i3HWAVSG9047Yr3oXktsgaIvZmWigodVokWfkHxoEeNffYYVFgg0e0cSXCMiVCRgHaB2kgMOXssdlEf9DMoMRPo2htF3EGBJZKYOcW6dPTf+NCxoP7YjDe/OirpW1pZY9I+G+2Uxiwy6XpY9HTz1seDCzTvovzn1QwSNGWNksYHrdo5hqKZUVZ4t0OTDc0xxyHzDp7DGQlK+jwUv48lEx2UyN8ODaF/Xx6jjJw23gLmoj9tFQcO4rPDXrmBFUoXa5L3AalM6IHp/6/xtb7X1x8d7YDGCmVuZHN0cmVhbQplbmRvYmoKNDEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNTEgPj4Kc3RyZWFtCnicLVFJcgNBCLvPK/SEZqffY5crh+T/1wjKBwYNi0B0WuKgjJ8gLFe85ZGraMPfMzGC3wWHfivXbVjkQFQgSWNQNaF28Xr0HthxmAnMk9awDGasD/yMKdzoxeExGWe312XUEOxdrz2ZQcmsXMQlExdM1WEjZw4/mTIutHM9NyDnRliXYZBuVhozEo40hUghhaqbpM4EQRKMrkaNNnIU+6Uvj3SGVY2oMexzLW1fz004a9DsWKzy5JQeXXEuJxcvrBz09TYDF1FprPJASMD9bg/1c7KT33hL584W0+N7zcnywlRgxZvXbkA21eLfvIjj+4yv5+f5/ANfYFuICmVuZHN0cmVhbQplbmRvYmoKNDIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNzQgPj4Kc3RyZWFtCnicTZBJDkMhDEP3nMIXqIQzwOc8v6q6aO+/rUMHdYH85CBwPDzQcSQudGTojI4rmxzjwLMgY+LROP/JuD7EMUHdoi1Yl3bH2cwSc8IyMQK2RsnZPKLAD8dcCBJklx++wCAiXY/5VvNZk/TPtzvdj7q0Zl89osCJ7AjFsAFXgP26x4FLwvle0+SXKiVjE4fygeoiUjY7oRC1VOxyqoqz3ZsrcBX0/NFD7u0FtSM83wplbmRzdHJlYW0KZW5kb2JqCjQzIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggNzUgPj4Kc3RyZWFtCnicM7U0UjBQMDYAEqZmRgqmJuYKKYZcQD6IlctlaGQKZuVwGVmaKVhYABkmZuZQIZiGHC5jU3OgAUBFxqZgGqo/hyuDKw0AlZAS7wplbmRzdHJlYW0KZW5kb2JqCjQ0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTQxID4+CnN0cmVhbQp4nD2PwQ7DMAhD7/kK/0Ck2CmhfE+naofu/68jS7sLegJjjIXQ0BuqmsOGYJvjxdIlVGv4FMVAJTfImWAOpaTSHUeRemI4GFwetBuO4rHo+hG7kmZ90MZCuiVogHusU2ncpnETxB01Beop6pyjvBC5n6ln2DSS3TSzknO4Db97z1PX/6ervMv5Bb13Lv4KZW5kc3RyZWFtCmVuZG9iago0NSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIxNSA+PgpzdHJlYW0KeJw1UTkOAyEM7PcV/kAkjC94T6Iozf6/zYzRVh7BXIa0lCGZ8lKTqCHlUz56mS6cutzXzGo055a0LXOAuLa8L62SwIlmiIPBaZi4AZo8AUPX0ahRQxce0NSlUyiw3AQ+irduD91jtYGXtiHniSBiKBksQc2pRRMWbc8npDW/Xosb3pft3chTpcaWGIEGAVY4HNfo1/CVPU8m0XQVMtSrNcsYCRNFIjz5jqbVE+taNNIyEtTGEaxqA7w7/TBOAAATccsCZJ9KlLPkxG+x9LMGV/r+AZ9HVJYKZW5kc3RyZWFtCmVuZG9iagoxNSAwIG9iago8PCAvQmFzZUZvbnQgL0RlamFWdVNhbnMgL0NoYXJQcm9jcyAxNiAwIFIKL0VuY29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA0NiAvcGVyaW9kIDQ4IC96ZXJvIC9vbmUgL3R3byAvdGhyZWUgL2ZvdXIgL2ZpdmUgL3NpeCA1NgovZWlnaHQgNjUgL0EgNjggL0QgNzYgL0wgOTcgL2EgL2IgL2MgL2QgL2UgMTA1IC9pIDEwOCAvbCAxMTAgL24gL28gMTE0IC9yCi9zIC90IC91IC92IDEyMSAveSBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9BIDE3IDAgUiAvRCAxOCAwIFIgL0wgMTkgMCBSIC9hIDIwIDAgUiAvYiAyMSAwIFIgL2MgMjIgMCBSIC9kIDIzIDAgUgovZSAyNCAwIFIgL2VpZ2h0IDI1IDAgUiAvZml2ZSAyNiAwIFIgL2ZvdXIgMjcgMCBSIC9pIDI4IDAgUiAvbCAyOSAwIFIKL24gMzEgMCBSIC9vIDMyIDAgUiAvb25lIDMzIDAgUiAvcGVyaW9kIDM0IDAgUiAvciAzNSAwIFIgL3MgMzYgMCBSCi9zaXggMzcgMCBSIC9zcGFjZSAzOCAwIFIgL3QgMzkgMCBSIC90aHJlZSA0MCAwIFIgL3R3byA0MSAwIFIgL3UgNDIgMCBSCi92IDQzIDAgUiAveSA0NCAwIFIgL3plcm8gNDUgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvRjEtRGVqYVZ1U2Fucy1taW51cyAzMCAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjQ2IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEyMDQxNjU2MTQrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjE0MjcgMDAwMDAgbiAKMDAwMDAyMTE2NCAwMDAwMCBuIAowMDAwMDIxMTk2IDAwMDAwIG4gCjAwMDAwMjEzMzYgMDAwMDAgbiAKMDAwMDAyMTM1NyAwMDAwMCBuIAowMDAwMDIxMzc4IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDExNzg1IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAxMTc2MyAwMDAwMCBuIAowMDAwMDE5Nzc3IDAwMDAwIG4gCjAwMDAwMTk1NzcgMDAwMDAgbiAKMDAwMDAxOTEyNCAwMDAwMCBuIAowMDAwMDIwODMwIDAwMDAwIG4gCjAwMDAwMTE4MDUgMDAwMDAgbiAKMDAwMDAxMTk2OCAwMDAwMCBuIAowMDAwMDEyMjA1IDAwMDAwIG4gCjAwMDAwMTIzMzggMDAwMDAgbiAKMDAwMDAxMjcxOCAwMDAwMCBuIAowMDAwMDEzMDM1IDAwMDAwIG4gCjAwMDAwMTMzNDAgMDAwMDAgbiAKMDAwMDAxMzY0NCAwMDAwMCBuIAowMDAwMDEzOTY2IDAwMDAwIG4gCjAwMDAwMTQ0MzQgMDAwMDAgbiAKMDAwMDAxNDc1NiAwMDAwMCBuIAowMDAwMDE0OTIyIDAwMDAwIG4gCjAwMDAwMTUwNjYgMDAwMDAgbiAKMDAwMDAxNTE4NSAwMDAwMCBuIAowMDAwMDE1MzU3IDAwMDAwIG4gCjAwMDAwMTU1OTMgMDAwMDAgbiAKMDAwMDAxNTg4NCAwMDAwMCBuIAowMDAwMDE2MDM5IDAwMDAwIG4gCjAwMDAwMTYxNjIgMDAwMDAgbiAKMDAwMDAxNjM5NSAwMDAwMCBuIAowMDAwMDE2ODAyIDAwMDAwIG4gCjAwMDAwMTcxOTUgMDAwMDAgbiAKMDAwMDAxNzI4NSAwMDAwMCBuIAowMDAwMDE3NDkxIDAwMDAwIG4gCjAwMDAwMTc5MDQgMDAwMDAgbiAKMDAwMDAxODIyOCAwMDAwMCBuIAowMDAwMDE4NDc1IDAwMDAwIG4gCjAwMDAwMTg2MjIgMDAwMDAgbiAKMDAwMDAxODgzNiAwMDAwMCBuIAowMDAwMDIxNDg3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDYgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ3ID4+CnN0YXJ0eHJlZgoyMTY0NAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:14.115116\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 0 - Variance: 1.037200689315796\n", "Layer 2 - Variance: 1.0582876205444336\n", "Layer 4 - Variance: 1.0638010501861572\n", "Layer 6 - Variance: 1.3167966604232788\n", "Layer 8 - Variance: 0.6909096837043762\n"]}], "source": ["def kaiming_init(model):\n", " for name, param in model.named_parameters():\n", " if name.endswith(\".bias\"):\n", " param.data.fill_(0)\n", " elif name.startswith(\"layers.0\"): # The first layer does not have ReLU applied on its input\n", " param.data.normal_(0, 1 / math.sqrt(param.shape[1]))\n", " else:\n", " param.data.normal_(0, math.sqrt(2) / math.sqrt(param.shape[1]))\n", "\n", "\n", "model = BaseNetwork(act_fn=nn.ReLU()).to(device)\n", "kaiming_init(model)\n", "visualize_gradients(model, print_variance=True)\n", "visualize_activations(model, print_variance=True)"]}, {"cell_type": "markdown", "id": "6af2f0ac", "metadata": {"papermill": {"duration": 0.227785, "end_time": "2021-12-04T15:56:15.529106", "exception": false, "start_time": "2021-12-04T15:56:15.301321", "status": "completed"}, "tags": []}, "source": ["The variance stays stable across layers.\n", "We can conclude that the Kaiming initialization indeed works well for ReLU-based networks.\n", "Note that for Leaky-ReLU etc., we have to slightly adjust the factor of $2$ in the variance as half of the values are not set to zero anymore.\n", "PyTorch provides a function to calculate this factor for many activation\n", "function, see `torch.nn.init.calculate_gain`\n", "([link](https://pytorch.org/docs/stable/nn.init.html#torch.nn.init.calculate_gain))."]}, {"cell_type": "markdown", "id": "0d736eff", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.22843, "end_time": "2021-12-04T15:56:15.988215", "exception": false, "start_time": "2021-12-04T15:56:15.759785", "status": "completed"}, "tags": []}, "source": ["## Optimization\n", "\n", "
\n", "\n", "Besides initialization, selecting a suitable optimization algorithm can be an important choice for deep neural networks.\n", "Before taking a closer look at them, we should define code for training the models.\n", "Most of the following code is copied from the previous tutorial, and only slightly altered to fit our needs."]}, {"cell_type": "code", "execution_count": 20, "id": "b9bfe5e2", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:16.464612Z", "iopub.status.busy": "2021-12-04T15:56:16.453313Z", "iopub.status.idle": "2021-12-04T15:56:16.468178Z", "shell.execute_reply": "2021-12-04T15:56:16.467704Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.251694, "end_time": "2021-12-04T15:56:16.468294", "exception": false, "start_time": "2021-12-04T15:56:16.216600", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def _get_config_file(model_path, model_name):\n", " return os.path.join(model_path, model_name + \".config\")\n", "\n", "\n", "def _get_model_file(model_path, model_name):\n", " return os.path.join(model_path, model_name + \".tar\")\n", "\n", "\n", "def _get_result_file(model_path, model_name):\n", " return os.path.join(model_path, model_name + \"_results.json\")\n", "\n", "\n", "def load_model(model_path, model_name, net=None):\n", " config_file = _get_config_file(model_path, model_name)\n", " model_file = _get_model_file(model_path, model_name)\n", " assert os.path.isfile(\n", " config_file\n", " ), f'Could not find the config file \"{config_file}\". Are you sure this is the correct path and you have your model config stored here?'\n", " assert os.path.isfile(\n", " model_file\n", " ), f'Could not find the model file \"{model_file}\". Are you sure this is the correct path and you have your model stored here?'\n", " with open(config_file) as f:\n", " config_dict = json.load(f)\n", " if net is None:\n", " act_fn_name = config_dict[\"act_fn\"].pop(\"name\").lower()\n", " assert (\n", " act_fn_name in act_fn_by_name\n", " ), f'Unknown activation function \"{act_fn_name}\". Please add it to the \"act_fn_by_name\" dict.'\n", " act_fn = act_fn_by_name[act_fn_name]()\n", " net = BaseNetwork(act_fn=act_fn, **config_dict)\n", " net.load_state_dict(torch.load(model_file))\n", " return net\n", "\n", "\n", "def save_model(model, model_path, model_name):\n", " config_dict = model.config\n", " os.makedirs(model_path, exist_ok=True)\n", " config_file = _get_config_file(model_path, model_name)\n", " model_file = _get_model_file(model_path, model_name)\n", " with open(config_file, \"w\") as f:\n", " json.dump(config_dict, f)\n", " torch.save(model.state_dict(), model_file)\n", "\n", "\n", "def train_model(net, model_name, optim_func, max_epochs=50, batch_size=256, overwrite=False):\n", " \"\"\"Train a model on the training set of FashionMNIST.\n", "\n", " Args:\n", " net: Object of BaseNetwork\n", " model_name: (str) Name of the model, used for creating the checkpoint names\n", " max_epochs: Number of epochs we want to (maximally) train for\n", " patience: If the performance on the validation set has not improved for #patience epochs, we stop training early\n", " batch_size: Size of batches used in training\n", " overwrite: Determines how to handle the case when there already exists a checkpoint. If True, it will be overwritten. Otherwise, we skip training.\n", " \"\"\"\n", " file_exists = os.path.isfile(_get_model_file(CHECKPOINT_PATH, model_name))\n", " if file_exists and not overwrite:\n", " print(f'Model file of \"{model_name}\" already exists. Skipping training...')\n", " with open(_get_result_file(CHECKPOINT_PATH, model_name)) as f:\n", " results = json.load(f)\n", " else:\n", " if file_exists:\n", " print(\"Model file exists, but will be overwritten...\")\n", "\n", " # Defining optimizer, loss and data loader\n", " optimizer = optim_func(net.parameters())\n", " loss_module = nn.CrossEntropyLoss()\n", " train_loader_local = data.DataLoader(\n", " train_set, batch_size=batch_size, shuffle=True, drop_last=True, pin_memory=True\n", " )\n", "\n", " results = None\n", " val_scores = []\n", " train_losses, train_scores = [], []\n", " best_val_epoch = -1\n", " for epoch in range(max_epochs):\n", " train_acc, val_acc, epoch_losses = epoch_iteration(\n", " net, loss_module, optimizer, train_loader_local, val_loader, epoch\n", " )\n", " train_scores.append(train_acc)\n", " val_scores.append(val_acc)\n", " train_losses += epoch_losses\n", "\n", " if len(val_scores) == 1 or val_acc > val_scores[best_val_epoch]:\n", " print(\"\\t (New best performance, saving model...)\")\n", " save_model(net, CHECKPOINT_PATH, model_name)\n", " best_val_epoch = epoch\n", "\n", " if results is None:\n", " load_model(CHECKPOINT_PATH, model_name, net=net)\n", " test_acc = test_model(net, test_loader)\n", " results = {\n", " \"test_acc\": test_acc,\n", " \"val_scores\": val_scores,\n", " \"train_losses\": train_losses,\n", " \"train_scores\": train_scores,\n", " }\n", " with open(_get_result_file(CHECKPOINT_PATH, model_name), \"w\") as f:\n", " json.dump(results, f)\n", "\n", " # Plot a curve of the validation accuracy\n", " sns.set()\n", " plt.plot([i for i in range(1, len(results[\"train_scores\"]) + 1)], results[\"train_scores\"], label=\"Train\")\n", " plt.plot([i for i in range(1, len(results[\"val_scores\"]) + 1)], results[\"val_scores\"], label=\"Val\")\n", " plt.xlabel(\"Epochs\")\n", " plt.ylabel(\"Validation accuracy\")\n", " plt.ylim(min(results[\"val_scores\"]), max(results[\"train_scores\"]) * 1.01)\n", " plt.title(f\"Validation performance of {model_name}\")\n", " plt.legend()\n", " plt.show()\n", " plt.close()\n", "\n", " print((f\" Test accuracy: {results['test_acc']*100.0:4.2f}% \").center(50, \"=\") + \"\\n\")\n", " return results\n", "\n", "\n", "def epoch_iteration(net, loss_module, optimizer, train_loader_local, val_loader, epoch):\n", " ############\n", " # Training #\n", " ############\n", " net.train()\n", " true_preds, count = 0.0, 0\n", " epoch_losses = []\n", " t = tqdm(train_loader_local, leave=False)\n", " for imgs, labels in t:\n", " imgs, labels = imgs.to(device), labels.to(device)\n", " optimizer.zero_grad()\n", " preds = net(imgs)\n", " loss = loss_module(preds, labels)\n", " loss.backward()\n", " optimizer.step()\n", " # Record statistics during training\n", " true_preds += (preds.argmax(dim=-1) == labels).sum().item()\n", " count += labels.shape[0]\n", " t.set_description(f\"Epoch {epoch+1}: loss={loss.item():4.2f}\")\n", " epoch_losses.append(loss.item())\n", " train_acc = true_preds / count\n", "\n", " ##############\n", " # Validation #\n", " ##############\n", " val_acc = test_model(net, val_loader)\n", " print(\n", " f\"[Epoch {epoch+1:2i}] Training accuracy: {train_acc*100.0:05.2f}%, Validation accuracy: {val_acc*100.0:05.2f}%\"\n", " )\n", " return train_acc, val_acc, epoch_losses\n", "\n", "\n", "def test_model(net, data_loader):\n", " \"\"\"Test a model on a specified dataset.\n", "\n", " Args:\n", " net: Trained model of type BaseNetwork\n", " data_loader: DataLoader object of the dataset to test on (validation or test)\n", " \"\"\"\n", " net.eval()\n", " true_preds, count = 0.0, 0\n", " for imgs, labels in data_loader:\n", " imgs, labels = imgs.to(device), labels.to(device)\n", " with torch.no_grad():\n", " preds = net(imgs).argmax(dim=-1)\n", " true_preds += (preds == labels).sum().item()\n", " count += labels.shape[0]\n", " test_acc = true_preds / count\n", " return test_acc"]}, {"cell_type": "markdown", "id": "ebc0236d", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.226171, "end_time": "2021-12-04T15:56:16.921208", "exception": false, "start_time": "2021-12-04T15:56:16.695037", "status": "completed"}, "tags": []}, "source": ["First, we need to understand what an optimizer actually does.\n", "The optimizer is responsible to update the network's parameters given the gradients.\n", "Hence, we effectively implement a function $w^{t} = f(w^{t-1}, g^{t}, ...)$ with $w$ being the parameters, and $g^{t} = \\nabla_{w^{(t-1)}} \\mathcal{L}^{(t)}$ the gradients at time step $t$.\n", "A common, additional parameter to this function is the learning rate, here denoted by $\\eta$.\n", "Usually, the learning rate can be seen as the \"step size\" of the update.\n", "A higher learning rate means that we change the weights more in the direction of the gradients, a smaller means we take shorter steps.\n", "\n", "As most optimizers only differ in the implementation of $f$, we can define a template for an optimizer in PyTorch below.\n", "We take as input the parameters of a model and a learning rate.\n", "The function `zero_grad` sets the gradients of all parameters to zero, which we have to do before calling `loss.backward()`.\n", "Finally, the `step()` function tells the optimizer to update all weights based on their gradients.\n", "The template is setup below:"]}, {"cell_type": "code", "execution_count": 21, "id": "8115a144", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:17.381962Z", "iopub.status.busy": "2021-12-04T15:56:17.381485Z", "iopub.status.idle": "2021-12-04T15:56:17.383062Z", "shell.execute_reply": "2021-12-04T15:56:17.383442Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.234647, "end_time": "2021-12-04T15:56:17.383573", "exception": false, "start_time": "2021-12-04T15:56:17.148926", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class OptimizerTemplate:\n", " def __init__(self, params, lr):\n", " self.params = list(params)\n", " self.lr = lr\n", "\n", " def zero_grad(self):\n", " # Set gradients of all parameters to zero\n", " for p in self.params:\n", " if p.grad is not None:\n", " p.grad.detach_() # For second-order optimizers important\n", " p.grad.zero_()\n", "\n", " @torch.no_grad()\n", " def step(self):\n", " # Apply update step to all parameters\n", " for p in self.params:\n", " if p.grad is None: # We skip parameters without any gradients\n", " continue\n", " self.update_param(p)\n", "\n", " def update_param(self, p):\n", " # To be implemented in optimizer-specific classes\n", " raise NotImplementedError"]}, {"cell_type": "markdown", "id": "11fc5bd8", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.225818, "end_time": "2021-12-04T15:56:17.836069", "exception": false, "start_time": "2021-12-04T15:56:17.610251", "status": "completed"}, "tags": []}, "source": ["The first optimizer we are going to implement is the standard Stochastic Gradient Descent (SGD).\n", "SGD updates the parameters using the following equation:\n", "\n", "$$\n", "\\begin{split}\n", " w^{(t)} & = w^{(t-1)} - \\eta \\cdot g^{(t)}\n", "\\end{split}\n", "$$\n", "\n", "As simple as the equation is also our implementation of SGD:"]}, {"cell_type": "code", "execution_count": 22, "id": "7c5f4955", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:18.300285Z", "iopub.status.busy": "2021-12-04T15:56:18.299810Z", "iopub.status.idle": "2021-12-04T15:56:18.301728Z", "shell.execute_reply": "2021-12-04T15:56:18.301262Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.237501, "end_time": "2021-12-04T15:56:18.301837", "exception": false, "start_time": "2021-12-04T15:56:18.064336", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class SGD(OptimizerTemplate):\n", " def __init__(self, params, lr):\n", " super().__init__(params, lr)\n", "\n", " def update_param(self, p):\n", " p_update = -self.lr * p.grad\n", " p.add_(p_update) # In-place update => saves memory and does not create computation graph"]}, {"cell_type": "markdown", "id": "0f34b439", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.226849, "end_time": "2021-12-04T15:56:18.754221", "exception": false, "start_time": "2021-12-04T15:56:18.527372", "status": "completed"}, "tags": []}, "source": ["In the lecture, we also have discussed the concept of momentum which replaces the gradient in the update by an exponential average of all past gradients including the current one:\n", "\n", "$$\n", "\\begin{split}\n", " m^{(t)} & = \\beta_1 m^{(t-1)} + (1 - \\beta_1)\\cdot g^{(t)}\\\\\n", " w^{(t)} & = w^{(t-1)} - \\eta \\cdot m^{(t)}\\\\\n", "\\end{split}\n", "$$\n", "\n", "Let's also implement it below:"]}, {"cell_type": "code", "execution_count": 23, "id": "48028221", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:19.214485Z", "iopub.status.busy": "2021-12-04T15:56:19.214011Z", "iopub.status.idle": "2021-12-04T15:56:19.215560Z", "shell.execute_reply": "2021-12-04T15:56:19.215939Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.23626, "end_time": "2021-12-04T15:56:19.216068", "exception": false, "start_time": "2021-12-04T15:56:18.979808", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class SGDMomentum(OptimizerTemplate):\n", " def __init__(self, params, lr, momentum=0.0):\n", " super().__init__(params, lr)\n", " self.momentum = momentum # Corresponds to beta_1 in the equation above\n", " self.param_momentum = {p: torch.zeros_like(p.data) for p in self.params} # Dict to store m_t\n", "\n", " def update_param(self, p):\n", " self.param_momentum[p] = (1 - self.momentum) * p.grad + self.momentum * self.param_momentum[p]\n", " p_update = -self.lr * self.param_momentum[p]\n", " p.add_(p_update)"]}, {"cell_type": "markdown", "id": "f9f83484", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.2256, "end_time": "2021-12-04T15:56:19.667175", "exception": false, "start_time": "2021-12-04T15:56:19.441575", "status": "completed"}, "tags": []}, "source": ["Finally, we arrive at Adam.\n", "Adam combines the idea of momentum with an adaptive learning rate, which is based on an exponential average of the squared gradients, i.e. the gradients norm.\n", "Furthermore, we add a bias correction for the momentum and adaptive learning rate for the first iterations:\n", "\n", "$$\n", "\\begin{split}\n", " m^{(t)} & = \\beta_1 m^{(t-1)} + (1 - \\beta_1)\\cdot g^{(t)}\\\\\n", " v^{(t)} & = \\beta_2 v^{(t-1)} + (1 - \\beta_2)\\cdot \\left(g^{(t)}\\right)^2\\\\\n", " \\hat{m}^{(t)} & = \\frac{m^{(t)}}{1-\\beta^{t}_1}, \\hat{v}^{(t)} = \\frac{v^{(t)}}{1-\\beta^{t}_2}\\\\\n", " w^{(t)} & = w^{(t-1)} - \\frac{\\eta}{\\sqrt{v^{(t)}} + \\epsilon}\\circ \\hat{m}^{(t)}\\\\\n", "\\end{split}\n", "$$\n", "\n", "Epsilon is a small constant used to improve numerical stability for very small gradient norms.\n", "Remember that the adaptive learning rate does not replace the learning\n", "rate hyperparameter $\\eta$, but rather acts as an extra factor and\n", "ensures that the gradients of various parameters have a similar norm."]}, {"cell_type": "code", "execution_count": 24, "id": "3f028169", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:20.130949Z", "iopub.status.busy": "2021-12-04T15:56:20.130446Z", "iopub.status.idle": "2021-12-04T15:56:20.132399Z", "shell.execute_reply": "2021-12-04T15:56:20.131929Z"}, "papermill": {"duration": 0.238498, "end_time": "2021-12-04T15:56:20.132510", "exception": false, "start_time": "2021-12-04T15:56:19.894012", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Adam(OptimizerTemplate):\n", " def __init__(self, params, lr, beta1=0.9, beta2=0.999, eps=1e-8):\n", " super().__init__(params, lr)\n", " self.beta1 = beta1\n", " self.beta2 = beta2\n", " self.eps = eps\n", " self.param_step = {p: 0 for p in self.params} # Remembers \"t\" for each parameter for bias correction\n", " self.param_momentum = {p: torch.zeros_like(p.data) for p in self.params}\n", " self.param_2nd_momentum = {p: torch.zeros_like(p.data) for p in self.params}\n", "\n", " def update_param(self, p):\n", " self.param_step[p] += 1\n", "\n", " self.param_momentum[p] = (1 - self.beta1) * p.grad + self.beta1 * self.param_momentum[p]\n", " self.param_2nd_momentum[p] = (1 - self.beta2) * (p.grad) ** 2 + self.beta2 * self.param_2nd_momentum[p]\n", "\n", " bias_correction_1 = 1 - self.beta1 ** self.param_step[p]\n", " bias_correction_2 = 1 - self.beta2 ** self.param_step[p]\n", "\n", " p_2nd_mom = self.param_2nd_momentum[p] / bias_correction_2\n", " p_mom = self.param_momentum[p] / bias_correction_1\n", " p_lr = self.lr / (torch.sqrt(p_2nd_mom) + self.eps)\n", " p_update = -p_lr * p_mom\n", "\n", " p.add_(p_update)"]}, {"cell_type": "markdown", "id": "b6db76a2", "metadata": {"papermill": {"duration": 0.227049, "end_time": "2021-12-04T15:56:20.587369", "exception": false, "start_time": "2021-12-04T15:56:20.360320", "status": "completed"}, "tags": []}, "source": ["### Comparing optimizers on model training\n", "\n", "After we have implemented three optimizers (SGD, SGD with momentum, and Adam), we can start to analyze and compare them.\n", "First, we test them on how well they can optimize a neural network on the FashionMNIST dataset.\n", "We use again our linear network, this time with a ReLU activation and the kaiming initialization, which we have found before to work well for ReLU-based networks.\n", "Note that the model is over-parameterized for this task, and we can achieve similar performance with a much smaller network (for example `100,100,100`).\n", "However, our main interest is in how well the optimizer can train *deep*\n", "neural networks, hence the over-parameterization."]}, {"cell_type": "code", "execution_count": 25, "id": "99328e5e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:21.045265Z", "iopub.status.busy": "2021-12-04T15:56:21.044794Z", "iopub.status.idle": "2021-12-04T15:56:21.057645Z", "shell.execute_reply": "2021-12-04T15:56:21.058024Z"}, "papermill": {"duration": 0.24428, "end_time": "2021-12-04T15:56:21.058155", "exception": false, "start_time": "2021-12-04T15:56:20.813875", "status": "completed"}, "tags": []}, "outputs": [], "source": ["base_model = BaseNetwork(act_fn=nn.ReLU(), hidden_sizes=[512, 256, 256, 128])\n", "kaiming_init(base_model)"]}, {"cell_type": "markdown", "id": "1636493f", "metadata": {"papermill": {"duration": 0.226015, "end_time": "2021-12-04T15:56:21.512106", "exception": false, "start_time": "2021-12-04T15:56:21.286091", "status": "completed"}, "tags": []}, "source": ["For a fair comparison, we train the exact same model with the same seed with the three optimizers below.\n", "Feel free to change the hyperparameters if you want (however, you have to train your own model then)."]}, {"cell_type": "code", "execution_count": 26, "id": "83cf00ee", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:21.971222Z", "iopub.status.busy": "2021-12-04T15:56:21.970734Z", "iopub.status.idle": "2021-12-04T15:56:22.354403Z", "shell.execute_reply": "2021-12-04T15:56:22.353972Z"}, "papermill": {"duration": 0.615668, "end_time": "2021-12-04T15:56:22.354532", "exception": false, "start_time": "2021-12-04T15:56:21.738864", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Model file of \"FashionMNIST_SGD\" already exists. Skipping training...\n"]}, {"data": {"application/pdf": "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Ci9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9EIDE3IDAgUiAvRSAxOCAwIFIgL0YgMTkgMCBSIC9HIDIwIDAgUiAvSSAyMSAwIFIgL00gMjIgMCBSIC9OIDIzIDAgUgovUyAyNCAwIFIgL1QgMjUgMCBSIC9WIDI2IDAgUiAvYSAyNyAwIFIgL2MgMjggMCBSIC9kIDI5IDAgUiAvZSAzMCAwIFIKL2VpZ2h0IDMxIDAgUiAvZiAzMiAwIFIgL2ZpdmUgMzMgMCBSIC9mb3VyIDM0IDAgUiAvaCAzNSAwIFIgL2kgMzYgMCBSCi9sIDM3IDAgUiAvbSAzOCAwIFIgL24gMzkgMCBSIC9uaW5lIDQwIDAgUiAvbyA0MSAwIFIgL29uZSA0MiAwIFIgL3AgNDMgMCBSCi9wZXJpb2QgNDQgMCBSIC9yIDQ1IDAgUiAvcyA0NiAwIFIgL3NpeCA0NyAwIFIgL3NwYWNlIDQ4IDAgUiAvdCA0OSAwIFIKL3RocmVlIDUwIDAgUiAvdHdvIDUxIDAgUiAvdSA1MiAwIFIgL3VuZGVyc2NvcmUgNTMgMCBSIC95IDU0IDAgUgovemVybyA1NSAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDAuOCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjggPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjU2IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEyMDQxNjU2MjIrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNTcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMTQ2NjggMDAwMDAgbiAKMDAwMDAxNDQzMSAwMDAwMCBuIAowMDAwMDE0NDYzIDAwMDAwIG4gCjAwMDAwMTQ2MDUgMDAwMDAgbiAKMDAwMDAxNDYyNiAwMDAwMCBuIAowMDAwMDE0NjQ3IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDAyNDQ4IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMjQyNyAwMDAwMCBuIAowMDAwMDEyOTIyIDAwMDAwIG4gCjAwMDAwMTI3MjIgMDAwMDAgbiAKMDAwMDAxMjIxNiAwMDAwMCBuIAowMDAwMDEzOTc1IDAwMDAwIG4gCjAwMDAwMDI0NjggMDAwMDAgbiAKMDAwMDAwMjcwNSAwMDAwMCBuIAowMDAwMDAyODU4IDAwMDAwIG4gCjAwMDAwMDMwMDYgMDAwMDAgbiAKMDAwMDAwMzMyNiAwMDAwMCBuIAowMDAwMDAzNDQ5IDAwMDAwIG4gCjAwMDAwMDM2MTEgMDAwMDAgbiAKMDAwMDAwMzc2MCAwMDAwMCBuIAowMDAwMDA0MTc0IDAwMDAwIG4gCjAwMDAwMDQzMTIgMDAwMDAgbiAKMDAwMDAwNDQ1NiAwMDAwMCBuIAowMDAwMDA0ODM2IDAwMDAwIG4gCjAwMDAwMDUxNDEgMDAwMDAgbiAKMDAwMDAwNTQ0NSAwMDAwMCBuIAowMDAwMDA1NzY3IDAwMDAwIG4gCjAwMDAwMDYyMzUgMDAwMDAgbiAKMDAwMDAwNjQ0NCAwMDAwMCBuIAowMDAwMDA2NzY2IDAwMDAwIG4gCjAwMDAwMDY5MzIgMDAwMDAgbiAKMDAwMDAwNzE2OSAwMDAwMCBuIAowMDAwMDA3MzEzIDAwMDAwIG4gCjAwMDAwMDc0MzIgMDAwMDAgbiAKMDAwMDAwNzc2MyAwMDAwMCBuIAowMDAwMDA3OTk5IDAwMDAwIG4gCjAwMDAwMDgzOTQgMDAwMDAgbiAKMDAwMDAwODY4NSAwMDAwMCBuIAowMDAwMDA4ODQwIDAwMDAwIG4gCjAwMDAwMDkxNTIgMDAwMDAgbiAKMDAwMDAwOTI3NSAwMDAwMCBuIAowMDAwMDA5NTA4IDAwMDAwIG4gCjAwMDAwMDk5MTUgMDAwMDAgbiAKMDAwMDAxMDMwOCAwMDAwMCBuIAowMDAwMDEwMzk4IDAwMDAwIG4gCjAwMDAwMTA2MDQgMDAwMDAgbiAKMDAwMDAxMTAxNyAwMDAwMCBuIAowMDAwMDExMzQxIDAwMDAwIG4gCjAwMDAwMTE1ODggMDAwMDAgbiAKMDAwMDAxMTcxNCAwMDAwMCBuIAowMDAwMDExOTI4IDAwMDAwIG4gCjAwMDAwMTQ3MjggMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1NiAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTcgPj4Kc3RhcnR4cmVmCjE0ODg1CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:22.120363\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 89.09% ==============\n", "\n"]}], "source": ["SGD_model = copy.deepcopy(base_model).to(device)\n", "SGD_results = train_model(\n", " SGD_model, \"FashionMNIST_SGD\", lambda params: SGD(params, lr=1e-1), max_epochs=40, batch_size=256\n", ")"]}, {"cell_type": "code", "execution_count": 27, "id": "8e4625d5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:22.825235Z", "iopub.status.busy": "2021-12-04T15:56:22.824735Z", "iopub.status.idle": "2021-12-04T15:56:23.187783Z", "shell.execute_reply": "2021-12-04T15:56:23.187344Z"}, "papermill": {"duration": 0.600767, "end_time": "2021-12-04T15:56:23.187909", "exception": false, "start_time": "2021-12-04T15:56:22.587142", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Model file of \"FashionMNIST_SGDMom\" already exists. Skipping training...\n"]}, {"data": {"application/pdf": "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cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9EIDE3IDAgUiAvRSAxOCAwIFIgL0YgMTkgMCBSIC9HIDIwIDAgUiAvSSAyMSAwIFIgL00gMjIgMCBSIC9OIDIzIDAgUgovUyAyNCAwIFIgL1QgMjUgMCBSIC9WIDI2IDAgUiAvYSAyNyAwIFIgL2MgMjggMCBSIC9kIDI5IDAgUiAvZSAzMCAwIFIKL2VpZ2h0IDMxIDAgUiAvZiAzMiAwIFIgL2ZpdmUgMzMgMCBSIC9mb3VyIDM0IDAgUiAvaCAzNSAwIFIgL2kgMzYgMCBSCi9sIDM3IDAgUiAvbSAzOCAwIFIgL24gMzkgMCBSIC9uaW5lIDQwIDAgUiAvbyA0MSAwIFIgL29uZSA0MiAwIFIgL3AgNDMgMCBSCi9wZXJpb2QgNDQgMCBSIC9yIDQ1IDAgUiAvcyA0NiAwIFIgL3NpeCA0NyAwIFIgL3NwYWNlIDQ4IDAgUiAvdCA0OSAwIFIKL3RocmVlIDUwIDAgUiAvdHdvIDUxIDAgUiAvdSA1MiAwIFIgL3VuZGVyc2NvcmUgNTMgMCBSIC95IDU0IDAgUgovemVybyA1NSAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDAuOCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjggPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjU2IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEyMDQxNjU2MjMrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNTcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMTQ1ODcgMDAwMDAgbiAKMDAwMDAxNDM1MCAwMDAwMCBuIAowMDAwMDE0MzgyIDAwMDAwIG4gCjAwMDAwMTQ1MjQgMDAwMDAgbiAKMDAwMDAxNDU0NSAwMDAwMCBuIAowMDAwMDE0NTY2IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDAyMzY3IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMjM0NiAwMDAwMCBuIAowMDAwMDEyODQxIDAwMDAwIG4gCjAwMDAwMTI2NDEgMDAwMDAgbiAKMDAwMDAxMjEzNSAwMDAwMCBuIAowMDAwMDEzODk0IDAwMDAwIG4gCjAwMDAwMDIzODcgMDAwMDAgbiAKMDAwMDAwMjYyNCAwMDAwMCBuIAowMDAwMDAyNzc3IDAwMDAwIG4gCjAwMDAwMDI5MjUgMDAwMDAgbiAKMDAwMDAwMzI0NSAwMDAwMCBuIAowMDAwMDAzMzY4IDAwMDAwIG4gCjAwMDAwMDM1MzAgMDAwMDAgbiAKMDAwMDAwMzY3OSAwMDAwMCBuIAowMDAwMDA0MDkzIDAwMDAwIG4gCjAwMDAwMDQyMzEgMDAwMDAgbiAKMDAwMDAwNDM3NSAwMDAwMCBuIAowMDAwMDA0NzU1IDAwMDAwIG4gCjAwMDAwMDUwNjAgMDAwMDAgbiAKMDAwMDAwNTM2NCAwMDAwMCBuIAowMDAwMDA1Njg2IDAwMDAwIG4gCjAwMDAwMDYxNTQgMDAwMDAgbiAKMDAwMDAwNjM2MyAwMDAwMCBuIAowMDAwMDA2Njg1IDAwMDAwIG4gCjAwMDAwMDY4NTEgMDAwMDAgbiAKMDAwMDAwNzA4OCAwMDAwMCBuIAowMDAwMDA3MjMyIDAwMDAwIG4gCjAwMDAwMDczNTEgMDAwMDAgbiAKMDAwMDAwNzY4MiAwMDAwMCBuIAowMDAwMDA3OTE4IDAwMDAwIG4gCjAwMDAwMDgzMTMgMDAwMDAgbiAKMDAwMDAwODYwNCAwMDAwMCBuIAowMDAwMDA4NzU5IDAwMDAwIG4gCjAwMDAwMDkwNzEgMDAwMDAgbiAKMDAwMDAwOTE5NCAwMDAwMCBuIAowMDAwMDA5NDI3IDAwMDAwIG4gCjAwMDAwMDk4MzQgMDAwMDAgbiAKMDAwMDAxMDIyNyAwMDAwMCBuIAowMDAwMDEwMzE3IDAwMDAwIG4gCjAwMDAwMTA1MjMgMDAwMDAgbiAKMDAwMDAxMDkzNiAwMDAwMCBuIAowMDAwMDExMjYwIDAwMDAwIG4gCjAwMDAwMTE1MDcgMDAwMDAgbiAKMDAwMDAxMTYzMyAwMDAwMCBuIAowMDAwMDExODQ3IDAwMDAwIG4gCjAwMDAwMTQ2NDcgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1NiAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTcgPj4Kc3RhcnR4cmVmCjE0ODA0CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:22.966259\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 88.83% ==============\n", "\n"]}], "source": ["SGDMom_model = copy.deepcopy(base_model).to(device)\n", "SGDMom_results = train_model(\n", " SGDMom_model,\n", " \"FashionMNIST_SGDMom\",\n", " lambda params: SGDMomentum(params, lr=1e-1, momentum=0.9),\n", " max_epochs=40,\n", " batch_size=256,\n", ")"]}, {"cell_type": "code", "execution_count": 28, "id": "2d326436", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:23.673283Z", "iopub.status.busy": "2021-12-04T15:56:23.672814Z", "iopub.status.idle": "2021-12-04T15:56:24.028499Z", "shell.execute_reply": "2021-12-04T15:56:24.028068Z"}, "papermill": {"duration": 0.601587, "end_time": "2021-12-04T15:56:24.028625", "exception": false, "start_time": "2021-12-04T15:56:23.427038", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Model file of \"FashionMNIST_Adam\" already exists. Skipping training...\n"]}, {"data": {"application/pdf": "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ptUT61o00jIS1MYRrGoDvDv9ME4AABNxywJkn0qUs+TEb7H0swZX+v4Bn0dUlgplbmRzdHJlYW0KZW5kb2JqCjE1IDAgb2JqCjw8IC9CYXNlRm9udCAvRGVqYVZ1U2FucyAvQ2hhclByb2NzIDE2IDAgUgovRW5jb2RpbmcgPDwKL0RpZmZlcmVuY2VzIFsgMzIgL3NwYWNlIDQ2IC9wZXJpb2QgNDggL3plcm8gL29uZSAvdHdvIC90aHJlZSAvZm91ciAvZml2ZSAvc2l4IDU2Ci9laWdodCAvbmluZSA2NSAvQSA2OSAvRSAvRiA3MyAvSSA3NyAvTSAvTiA4MyAvUyAvVCA4NiAvViA5NSAvdW5kZXJzY29yZSA5NwovYSA5OSAvYyAvZCAvZSAvZiAxMDQgL2ggL2kgMTA4IC9sIC9tIC9uIC9vIC9wIDExNCAvciAvcyAvdCAvdSAxMjEgL3kgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE0IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDEzIDAgUiA+PgplbmRvYmoKMTQgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxMyAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNiAwIG9iago8PCAvQSAxNyAwIFIgL0UgMTggMCBSIC9GIDE5IDAgUiAvSSAyMCAwIFIgL00gMjEgMCBSIC9OIDIyIDAgUiAvUyAyMyAwIFIKL1QgMjQgMCBSIC9WIDI1IDAgUiAvYSAyNiAwIFIgL2MgMjcgMCBSIC9kIDI4IDAgUiAvZSAyOSAwIFIgL2VpZ2h0IDMwIDAgUgovZiAzMSAwIFIgL2ZpdmUgMzIgMCBSIC9mb3VyIDMzIDAgUiAvaCAzNCAwIFIgL2kgMzUgMCBSIC9sIDM2IDAgUiAvbSAzNyAwIFIKL24gMzggMCBSIC9uaW5lIDM5IDAgUiAvbyA0MCAwIFIgL29uZSA0MSAwIFIgL3AgNDIgMCBSIC9wZXJpb2QgNDMgMCBSCi9yIDQ0IDAgUiAvcyA0NSAwIFIgL3NpeCA0NiAwIFIgL3NwYWNlIDQ3IDAgUiAvdCA0OCAwIFIgL3RocmVlIDQ5IDAgUgovdHdvIDUwIDAgUiAvdSA1MSAwIFIgL3VuZGVyc2NvcmUgNTIgMCBSIC95IDUzIDAgUiAvemVybyA1NCAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDAuOCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjggPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjU1IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEyMDQxNjU2MjMrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNTYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMTQyMDYgMDAwMDAgbiAKMDAwMDAxMzk2OSAwMDAwMCBuIAowMDAwMDE0MDAxIDAwMDAwIG4gCjAwMDAwMTQxNDMgMDAwMDAgbiAKMDAwMDAxNDE2NCAwMDAwMCBuIAowMDAwMDE0MTg1IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDAyMzkwIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMjM2OSAwMDAwMCBuIAowMDAwMDEyNDcwIDAwMDAwIG4gCjAwMDAwMTIyNzAgMDAwMDAgbiAKMDAwMDAxMTc2NCAwMDAwMCBuIAowMDAwMDEzNTIzIDAwMDAwIG4gCjAwMDAwMDI0MTAgMDAwMDAgbiAKMDAwMDAwMjU3MyAwMDAwMCBuIAowMDAwMDAyNzI2IDAwMDAwIG4gCjAwMDAwMDI4NzQgMDAwMDAgbiAKMDAwMDAwMjk5NyAwMDAwMCBuIAowMDAwMDAzMTU5IDAwMDAwIG4gCjAwMDAwMDMzMDggMDAwMDAgbiAKMDAwMDAwMzcyMiAwMDAwMCBuIAowMDAwMDAzODYwIDAwMDAwIG4gCjAwMDAwMDQwMDQgMDAwMDAgbiAKMDAwMDAwNDM4NCAwMDAwMCBuIAowMDAwMDA0Njg5IDAwMDAwIG4gCjAwMDAwMDQ5OTMgMDAwMDAgbiAKMDAwMDAwNTMxNSAwMDAwMCBuIAowMDAwMDA1NzgzIDAwMDAwIG4gCjAwMDAwMDU5OTIgMDAwMDAgbiAKMDAwMDAwNjMxNCAwMDAwMCBuIAowMDAwMDA2NDgwIDAwMDAwIG4gCjAwMDAwMDY3MTcgMDAwMDAgbiAKMDAwMDAwNjg2MSAwMDAwMCBuIAowMDAwMDA2OTgwIDAwMDAwIG4gCjAwMDAwMDczMTEgMDAwMDAgbiAKMDAwMDAwNzU0NyAwMDAwMCBuIAowMDAwMDA3OTQyIDAwMDAwIG4gCjAwMDAwMDgyMzMgMDAwMDAgbiAKMDAwMDAwODM4OCAwMDAwMCBuIAowMDAwMDA4NzAwIDAwMDAwIG4gCjAwMDAwMDg4MjMgMDAwMDAgbiAKMDAwMDAwOTA1NiAwMDAwMCBuIAowMDAwMDA5NDYzIDAwMDAwIG4gCjAwMDAwMDk4NTYgMDAwMDAgbiAKMDAwMDAwOTk0NiAwMDAwMCBuIAowMDAwMDEwMTUyIDAwMDAwIG4gCjAwMDAwMTA1NjUgMDAwMDAgbiAKMDAwMDAxMDg4OSAwMDAwMCBuIAowMDAwMDExMTM2IDAwMDAwIG4gCjAwMDAwMTEyNjIgMDAwMDAgbiAKMDAwMDAxMTQ3NiAwMDAwMCBuIAowMDAwMDE0MjY2IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNTUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDU2ID4+CnN0YXJ0eHJlZgoxNDQyMwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:23.812352\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["============= Test accuracy: 89.46% ==============\n", "\n"]}], "source": ["Adam_model = copy.deepcopy(base_model).to(device)\n", "Adam_results = train_model(\n", " Adam_model, \"FashionMNIST_Adam\", lambda params: Adam(params, lr=1e-3), max_epochs=40, batch_size=256\n", ")"]}, {"cell_type": "markdown", "id": "d48d9738", "metadata": {"papermill": {"duration": 0.242293, "end_time": "2021-12-04T15:56:24.518095", "exception": false, "start_time": "2021-12-04T15:56:24.275802", "status": "completed"}, "tags": []}, "source": ["The result is that all optimizers perform similarly well with the given model.\n", "The differences are too small to find any significant conclusion.\n", "However, keep in mind that this can also be attributed to the initialization we chose.\n", "When changing the initialization to worse (e.g. constant initialization), Adam usually shows to be more robust because of its adaptive learning rate.\n", "To show the specific benefits of the optimizers, we will continue to\n", "look at some possible loss surfaces in which momentum and adaptive\n", "learning rate are crucial."]}, {"cell_type": "markdown", "id": "650284a9", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.243267, "end_time": "2021-12-04T15:56:25.002092", "exception": false, "start_time": "2021-12-04T15:56:24.758825", "status": "completed"}, "tags": []}, "source": ["### Pathological curvatures\n", "\n", "A pathological curvature is a type of surface that is similar to ravines and is particularly tricky for plain SGD optimization.\n", "In words, pathological curvatures typically have a steep gradient in one direction with an optimum at the center, while in a second direction we have a slower gradient towards a (global) optimum.\n", "Let's first create an example surface of this and visualize it:"]}, {"cell_type": "code", "execution_count": 29, "id": "b739a90c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:25.513474Z", "iopub.status.busy": "2021-12-04T15:56:25.513001Z", "iopub.status.idle": "2021-12-04T15:56:25.514972Z", "shell.execute_reply": "2021-12-04T15:56:25.514568Z"}, "papermill": {"duration": 0.271487, "end_time": "2021-12-04T15:56:25.515085", "exception": false, "start_time": "2021-12-04T15:56:25.243598", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def pathological_curve_loss(w1, w2):\n", " # Example of a pathological curvature. There are many more possible, feel free to experiment here!\n", " x1_loss = torch.tanh(w1) ** 2 + 0.01 * torch.abs(w1)\n", " x2_loss = torch.sigmoid(w2)\n", " return x1_loss + x2_loss"]}, {"cell_type": "code", "execution_count": 30, "id": "31ad8756", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:26.015141Z", "iopub.status.busy": "2021-12-04T15:56:26.014659Z", "iopub.status.idle": "2021-12-04T15:56:27.270117Z", "shell.execute_reply": "2021-12-04T15:56:27.270502Z"}, "papermill": {"duration": 1.508289, "end_time": "2021-12-04T15:56:27.270685", "exception": false, "start_time": "2021-12-04T15:56:25.762396", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_875/1102210584.py:5: MatplotlibDeprecationWarning: Calling gca() with keyword arguments was deprecated in Matplotlib 3.4. Starting two minor releases later, gca() will take no keyword arguments. The gca() function should only be used to get the current axes, or if no axes exist, create new axes with default keyword arguments. To create a new axes with non-default arguments, use plt.axes() or plt.subplot().\n", " ax = fig.gca(projection=\"3d\") if plot_3d else fig.gca()\n"]}, {"data": {"application/pdf": "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MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM1MCA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDI4IDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxNyA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjE3IDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDgK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MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjIxIDAgb2JqCjw8IC9QIDIyIDAgUiAvYSAyMyAwIFIgL2MgMjQgMCBSIC9lIDI1IDAgUiAvZml2ZSAyNiAwIFIgL2ZvdXIgMjcgMCBSCi9nIDI4IDAgUiAvaCAyOSAwIFIgL2kgMzAgMCBSIC9sIDMxIDAgUiAvbyAzMyAwIFIgL29uZSAzNCAwIFIKL3BlcmlvZCAzNSAwIFIgL3IgMzYgMCBSIC9zIDM3IDAgUiAvc3BhY2UgMzggMCBSIC90IDM5IDAgUiAvdHdvIDQwIDAgUgovdSA0MSAwIFIgL3YgNDIgMCBSIC96ZXJvIDQzIDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSIC9GMiAyMCAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAwLjUgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC41ID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMi1EZWphVnVTYW5zLW1pbnVzIDMyIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKNDQgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMTIwNDE2NTYyNiswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA0NQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDE1MjYxMCAwMDAwMCBuIAowMDAwMTUyMzM0IDAwMDAwIG4gCjAwMDAxNTIzNzcgMDAwMDAgbiAKMDAwMDE1MjUxOSAwMDAwMCBuIAowMDAwMTUyNTQwIDAwMDAwIG4gCjAwMDAxNTI1NjEgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDEwIDAwMDAwIG4gCjAwMDAxNDMyODEgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMTQzMjU4IDAwMDAwIG4gCjAwMDAxNDM5ODggMDAwMDAgbiAKMDAwMDE0Mzc4MCAwMDAwMCBuIAowMDAwMTQzNDY0IDAwMDAwIG4gCjAwMDAxNDUwNDEgMDAwMDAgbiAKMDAwMDE0MzMwMSAwMDAwMCBuIAowMDAwMTUxMDI3IDAwMDAwIG4gCjAwMDAxNTA4MjcgMDAwMDAgbiAKMDAwMDE1MDQwOCAwMDAwMCBuIAowMDAwMTUyMDgwIDAwMDAwIG4gCjAwMDAxNDUwNzMgMDAwMDAgbiAKMDAwMDE0NTMxNiAwMDAwMCBuIAowMDAwMTQ1Njk2IDAwMDAwIG4gCjAwMDAxNDYwMDEgMDAwMDAgbiAKMDAwMDE0NjMyMyAwMDAwMCBuIAowMDAwMTQ2NjQ1IDAwMDAwIG4gCjAwMDAxNDY4MTEgMDAwMDAgbiAKMDAwMDE0NzIyNSAwMDAwMCBuIAowMDAwMTQ3NDYyIDAwMDAwIG4gCjAwMDAxNDc2MDYgMDAwMDAgbiAKMDAwMDE0NzcyNSAwMDAwMCBuIAowMDAwMTQ3ODk3IDAwMDAwIG4gCjAwMDAxNDgxODggMDAwMDAgbiAKMDAwMDE0ODM0MyAwMDAwMCBuIAowMDAwMTQ4NDY2IDAwMDAwIG4gCjAwMDAxNDg2OTkgMDAwMDAgbiAKMDAwMDE0OTEwNiAwMDAwMCBuIAowMDAwMTQ5MTk2IDAwMDAwIG4gCjAwMDAxNDk0MDIgMDAwMDAgbiAKMDAwMDE0OTcyNiAwMDAwMCBuIAowMDAwMTQ5OTczIDAwMDAwIG4gCjAwMDAxNTAxMjAgMDAwMDAgbiAKMDAwMDE1MjY3MCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQ0IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0NSA+PgpzdGFydHhyZWYKMTUyODI3CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:26.317299\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["def plot_curve(\n", " curve_fn, x_range=(-5, 5), y_range=(-5, 5), plot_3d=False, cmap=cm.viridis, title=\"Pathological curvature\"\n", "):\n", " fig = plt.figure()\n", " ax = fig.gca(projection=\"3d\") if plot_3d else fig.gca()\n", "\n", " x = torch.arange(x_range[0], x_range[1], (x_range[1] - x_range[0]) / 100.0)\n", " y = torch.arange(y_range[0], y_range[1], (y_range[1] - y_range[0]) / 100.0)\n", " x, y = torch.meshgrid([x, y])\n", " z = curve_fn(x, y)\n", " x, y, z = x.numpy(), y.numpy(), z.numpy()\n", "\n", " if plot_3d:\n", " ax.plot_surface(x, y, z, cmap=cmap, linewidth=1, color=\"#000\", antialiased=False)\n", " ax.set_zlabel(\"loss\")\n", " else:\n", " ax.imshow(z.T[::-1], cmap=cmap, extent=(x_range[0], x_range[1], y_range[0], y_range[1]))\n", " plt.title(title)\n", " ax.set_xlabel(r\"$w_1$\")\n", " ax.set_ylabel(r\"$w_2$\")\n", " plt.tight_layout()\n", " return ax\n", "\n", "\n", "sns.reset_orig()\n", "_ = plot_curve(pathological_curve_loss, plot_3d=True)\n", "plt.show()"]}, {"cell_type": "markdown", "id": "3a07e352", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.279049, "end_time": "2021-12-04T15:56:27.829838", "exception": false, "start_time": "2021-12-04T15:56:27.550789", "status": "completed"}, "tags": []}, "source": ["In terms of optimization, you can image that $w_1$ and $w_2$ are weight parameters, and the curvature represents the loss surface over the space of $w_1$ and $w_2$.\n", "Note that in typical networks, we have many, many more parameters than two, and such curvatures can occur in multi-dimensional spaces as well.\n", "\n", "Ideally, our optimization algorithm would find the center of the ravine and focuses on optimizing the parameters towards the direction of $w_2$.\n", "However, if we encounter a point along the ridges, the gradient is much greater in $w_1$ than $w_2$, and we might end up jumping from one side to the other.\n", "Due to the large gradients, we would have to reduce our learning rate slowing down learning significantly.\n", "\n", "To test our algorithms, we can implement a simple function to train two parameters on such a surface:"]}, {"cell_type": "code", "execution_count": 31, "id": "5d2f28de", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:28.389879Z", "iopub.status.busy": "2021-12-04T15:56:28.389383Z", "iopub.status.idle": "2021-12-04T15:56:28.391395Z", "shell.execute_reply": "2021-12-04T15:56:28.390988Z"}, "papermill": {"duration": 0.285299, "end_time": "2021-12-04T15:56:28.391509", "exception": false, "start_time": "2021-12-04T15:56:28.106210", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_curve(optimizer_func, curve_func=pathological_curve_loss, num_updates=100, init=[5, 5]):\n", " \"\"\"\n", " Args:\n", " optimizer_func: Constructor of the optimizer to use. Should only take a parameter list\n", " curve_func: Loss function (e.g. pathological curvature)\n", " num_updates: Number of updates/steps to take when optimizing\n", " init: Initial values of parameters. Must be a list/tuple with two elements representing w_1 and w_2\n", " Returns:\n", " Numpy array of shape [num_updates, 3] with [t,:2] being the parameter values at step t, and [t,2] the loss at t.\n", " \"\"\"\n", " weights = nn.Parameter(torch.FloatTensor(init), requires_grad=True)\n", " optim = optimizer_func([weights])\n", "\n", " list_points = []\n", " for _ in range(num_updates):\n", " loss = curve_func(weights[0], weights[1])\n", " list_points.append(torch.cat([weights.data.detach(), loss.unsqueeze(dim=0).detach()], dim=0))\n", " optim.zero_grad()\n", " loss.backward()\n", " optim.step()\n", " points = torch.stack(list_points, dim=0).numpy()\n", " return points"]}, {"cell_type": "markdown", "id": "34d620d2", "metadata": {"papermill": {"duration": 0.277293, "end_time": "2021-12-04T15:56:28.945849", "exception": false, "start_time": "2021-12-04T15:56:28.668556", "status": "completed"}, "tags": []}, "source": ["Next, let's apply the different optimizers on our curvature.\n", "Note that we set a much higher learning rate for the optimization algorithms as you would in a standard neural network.\n", "This is because we only have 2 parameters instead of tens of thousands or even millions."]}, {"cell_type": "code", "execution_count": 32, "id": "41d2cd89", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:29.513423Z", "iopub.status.busy": "2021-12-04T15:56:29.512941Z", "iopub.status.idle": "2021-12-04T15:56:29.600337Z", "shell.execute_reply": "2021-12-04T15:56:29.599941Z"}, "papermill": {"duration": 0.369932, "end_time": "2021-12-04T15:56:29.600456", "exception": false, "start_time": "2021-12-04T15:56:29.230524", "status": "completed"}, "tags": []}, "outputs": [], "source": ["SGD_points = train_curve(lambda params: SGD(params, lr=10))\n", "SGDMom_points = train_curve(lambda params: SGDMomentum(params, lr=10, momentum=0.9))\n", "Adam_points = train_curve(lambda params: Adam(params, lr=1))"]}, {"cell_type": "markdown", "id": "f8145cab", "metadata": {"papermill": {"duration": 0.28425, "end_time": "2021-12-04T15:56:30.165657", "exception": false, "start_time": "2021-12-04T15:56:29.881407", "status": "completed"}, "tags": []}, "source": ["To understand best how the different algorithms worked, we visualize the update step as a line plot through the loss surface.\n", "We will stick with a 2D representation for readability."]}, {"cell_type": "code", "execution_count": 33, "id": "54af4578", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:30.745262Z", "iopub.status.busy": "2021-12-04T15:56:30.744720Z", "iopub.status.idle": "2021-12-04T15:56:31.275773Z", "shell.execute_reply": "2021-12-04T15:56:31.276161Z"}, "papermill": {"duration": 0.832259, "end_time": "2021-12-04T15:56:31.276328", "exception": false, "start_time": "2021-12-04T15:56:30.444069", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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MDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzUwIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjggNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjE3IDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTcgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwOAo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTk1IDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoyMCAwIG9iago8PCAvdyAyMSAwIFIgPj4KZW5kb2JqCjI2IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggOTEgPj4Kc3RyZWFtCnicNYy7DcAwCER7prgR+DiA94miFPb+bYgtF9w9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CmVuZG9iagoyMyAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjIyIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjI1IDAgb2JqCjw8IC9BIDI2IDAgUiAvRCAyNyAwIFIgL0cgMjggMCBSIC9NIDI5IDAgUiAvUCAzMCAwIFIgL1MgMzEgMCBSIC9hIDMyIDAgUgovYyAzMyAwIFIgL2QgMzQgMCBSIC9lIDM1IDAgUiAvZml2ZSAzNiAwIFIgL2cgMzcgMCBSIC9oIDM4IDAgUiAvaSAzOSAwIFIKL2wgNDAgMCBSIC9tIDQxIDAgUiAvbyA0MyAwIFIgL29uZSA0NCAwIFIgL3BlcmlvZCA0NSAwIFIgL3IgNDYgMCBSCi9zZXZlbiA0NyAwIFIgL3NwYWNlIDQ4IDAgUiAvdCA0OSAwIFIgL3R3byA1MCAwIFIgL3UgNTEgMCBSIC92IDUyIDAgUgovemVybyA1MyAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDI0IDAgUiAvRjIgMTkgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMC44IC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuOCA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRv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biAKMDAwMDAzMDcwMiAwMDAwMCBuIAowMDAwMDEwNTMyIDAwMDAwIG4gCjAwMDAwMTAzMjQgMDAwMDAgbiAKMDAwMDAxMDAwOCAwMDAwMCBuIAowMDAwMDExNTg1IDAwMDAwIG4gCjAwMDAwMDk4NDUgMDAwMDAgbiAKMDAwMDAxOTEwOCAwMDAwMCBuIAowMDAwMDE4OTA4IDAwMDAwIG4gCjAwMDAwMTg0NTIgMDAwMDAgbiAKMDAwMDAyMDE2MSAwMDAwMCBuIAowMDAwMDExNjE3IDAwMDAwIG4gCjAwMDAwMTE3ODAgMDAwMDAgbiAKMDAwMDAxMjAxNyAwMDAwMCBuIAowMDAwMDEyMzM3IDAwMDAwIG4gCjAwMDAwMTI0OTkgMDAwMDAgbiAKMDAwMDAxMjc0MiAwMDAwMCBuIAowMDAwMDEzMTU2IDAwMDAwIG4gCjAwMDAwMTM1MzYgMDAwMDAgbiAKMDAwMDAxMzg0MSAwMDAwMCBuIAowMDAwMDE0MTQ1IDAwMDAwIG4gCjAwMDAwMTQ0NjcgMDAwMDAgbiAKMDAwMDAxNDc4OSAwMDAwMCBuIAowMDAwMDE1MjAzIDAwMDAwIG4gCjAwMDAwMTU0NDAgMDAwMDAgbiAKMDAwMDAxNTU4NCAwMDAwMCBuIAowMDAwMDE1NzAzIDAwMDAwIG4gCjAwMDAwMTYwMzQgMDAwMDAgbiAKMDAwMDAxNjIwNiAwMDAwMCBuIAowMDAwMDE2NDk3IDAwMDAwIG4gCjAwMDAwMTY2NTIgMDAwMDAgbiAKMDAwMDAxNjc3NSAwMDAwMCBuIAowMDAwMDE3MDA4IDAwMDAwIG4gCjAwMDAwMTcxNTAgMDAwMDAgbiAKMDAwMDAxNzI0MCAwMDAwMCBuIAowMDAwMDE3NDQ2IDAwMDAwIG4gCjAwMDAwMTc3NzAgMDAwMDAgbiAKMDAwMDAxODAxNyAwMDAwMCBuIAowMDAwMDE4MTY0IDAwMDAwIG4gCjAwMDAwMzAxNzMgMDAwMDAgbiAKMDAwMDAzMTAxNiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDU1IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA1NiA+PgpzdGFydHhyZWYKMzExNzMKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:30.860693\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["all_points = np.concatenate([SGD_points, SGDMom_points, Adam_points], axis=0)\n", "ax = plot_curve(\n", " pathological_curve_loss,\n", " x_range=(-np.absolute(all_points[:, 0]).max(), np.absolute(all_points[:, 0]).max()),\n", " y_range=(all_points[:, 1].min(), all_points[:, 1].max()),\n", " plot_3d=False,\n", ")\n", "ax.plot(SGD_points[:, 0], SGD_points[:, 1], color=\"red\", marker=\"o\", zorder=1, label=\"SGD\")\n", "ax.plot(SGDMom_points[:, 0], SGDMom_points[:, 1], color=\"blue\", marker=\"o\", zorder=2, label=\"SGDMom\")\n", "ax.plot(Adam_points[:, 0], Adam_points[:, 1], color=\"grey\", marker=\"o\", zorder=3, label=\"Adam\")\n", "plt.legend()\n", "plt.show()"]}, {"cell_type": "markdown", "id": "0e880578", "metadata": {"papermill": {"duration": 0.281894, "end_time": "2021-12-04T15:56:31.843290", "exception": false, "start_time": "2021-12-04T15:56:31.561396", "status": "completed"}, "tags": []}, "source": ["We can clearly see that SGD is not able to find the center of the optimization curve and has a problem converging due to the steep gradients in $w_1$.\n", "In contrast, Adam and SGD with momentum nicely converge as the changing direction of $w_1$ is canceling itself out.\n", "On such surfaces, it is crucial to use momentum."]}, {"cell_type": "markdown", "id": "23673b0c", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.283817, "end_time": "2021-12-04T15:56:32.414094", "exception": false, "start_time": "2021-12-04T15:56:32.130277", "status": "completed"}, "tags": []}, "source": ["### Steep optima\n", "\n", "A second type of challenging loss surfaces are steep optima.\n", "In those, we have a larger part of the surface having very small gradients while around the optimum, we have very large gradients.\n", "For instance, take the following loss surfaces:"]}, {"cell_type": "code", "execution_count": 34, "id": "93f0e0e9", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:33.000196Z", "iopub.status.busy": "2021-12-04T15:56:32.994290Z", "iopub.status.idle": "2021-12-04T15:56:34.120588Z", "shell.execute_reply": "2021-12-04T15:56:34.120986Z"}, "papermill": {"duration": 1.424954, "end_time": "2021-12-04T15:56:34.121153", "exception": false, "start_time": "2021-12-04T15:56:32.696199", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_875/1102210584.py:5: MatplotlibDeprecationWarning: Calling gca() with keyword arguments was deprecated in Matplotlib 3.4. Starting two minor releases later, gca() will take no keyword arguments. The gca() function should only be used to get the current axes, or if no axes exist, create new axes with default keyword arguments. To create a new axes with non-default arguments, use plt.axes() or plt.subplot().\n", " ax = fig.gca(projection=\"3d\") if plot_3d else fig.gca()\n"]}, {"data": {"application/pdf": "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dWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDEzIDAgUiA+PgplbmRvYmoKMTQgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDk2Ci9Gb250QkJveCBbIC0xMDE2IC0zNTEgMTY2MCAxMDY4IF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zLU9ibGlxdWUKL0l0YWxpY0FuZ2xlIDAgL01heFdpZHRoIDEzNTAgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNTAgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAg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NyAvYSAxMDEKL2UgMTA1IC9pIDEwOCAvbCAvbSAxMTEgL28gL3AgMTE1IC9zIC90IF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxOSAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxOCAwIFIgPj4KZW5kb2JqCjE5IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTggMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMjEgMCBvYmoKPDwgL1MgMjIgMCBSIC9hIDIzIDAgUiAvZSAyNCAwIFIgL2ZpdmUgMjUgMCBSIC9pIDI2IDAgUiAvbCAyNyAwIFIKL20gMjggMCBSIC9vIDMwIDAgUiAvb25lIDMxIDAgUiAvcCAzMiAwIFIgL3BlcmlvZCAzMyAwIFIgL3MgMzQgMCBSCi9zcGFjZSAzNSAwIFIgL3QgMzYgMCBSIC90aHJlZSAzNyAwIFIgL3R3byAzOCAwIFIgL3plcm8gMzkgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgL0YyIDIwIDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDAuNSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4KL0EzIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YyLURlamFWdVNhbnMtbWludXMgMjkgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0MCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1NjMzKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQxCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMTM3MDA2IDAwMDAwIG4gCjAwMDAxMzY3MzAgMDAwMDAgbiAKMDAwMDEzNjc3MyAwMDAwMCBuIAowMDAwMTM2OTE1IDAwMDAwIG4gCjAwMDAxMzY5MzYgMDAwMDAgbiAKMDAwMDEzNjk1NyAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDkgMDAwMDAgbiAKMDAwMDEyODI1MiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAxMjgyMjkgMDAwMDAgbiAKMDAwMDEyODk1OSAwMDAwMCBuIAowMDAwMTI4NzUxIDAwMDAwIG4gCjAwMDAxMjg0MzUgMDAwMDAgbiAKMDAwMDEzMDAxMiAwMDAwMCBuIAowMDAwMTI4MjcyIDAwMDAwIG4gCjAwMDAxMzU0NjIgMDAwMDAgbiAKMDAwMDEzNTI2MiAwMDAwMCBuIAowMDAwMTM0ODU3IDAwMDAwIG4gCjAwMDAxMzY1MTUgMDAwMDAgbiAKMDAwMDEzMDA0NCAwMDAwMCBuIAowMDAwMTMwNDU4IDAwMDAwIG4gCjAwMDAxMzA4MzggMDAwMDAgbiAKMDAwMDEzMTE2MCAwMDAwMCBuIAowMDAwMTMxNDgyIDAwMDAwIG4gCjAwMDAxMzE2MjYgMDAwMDAgbiAKMDAwMDEzMTc0NSAwMDAwMCBuIAowMDAwMTMyMDc2IDAwMDAwIG4gCjAwMDAxMzIyNDggMDAwMDAgbiAKMDAwMDEzMjUzOSAwMDAwMCBuIAowMDAwMTMyNjk0IDAwMDAwIG4gCjAwMDAxMzMwMDYgMDAwMDAgbiAKMDAwMDEzMzEyOSAwMDAwMCBuIAowMDAwMTMzNTM2IDAwMDAwIG4gCjAwMDAxMzM2MjYgMDAwMDAgbiAKMDAwMDEzMzgzMiAwMDAwMCBuIAowMDAwMTM0MjQ1IDAwMDAwIG4gCjAwMDAxMzQ1NjkgMDAwMDAgbiAKMDAwMDEzNzA2NiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQwIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0MSA+PgpzdGFydHhyZWYKMTM3MjIzCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:33.220652\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["def bivar_gaussian(w1, w2, x_mean=0.0, y_mean=0.0, x_sig=1.0, y_sig=1.0):\n", " norm = 1 / (2 * np.pi * x_sig * y_sig)\n", " x_exp = (-1 * (w1 - x_mean) ** 2) / (2 * x_sig ** 2)\n", " y_exp = (-1 * (w2 - y_mean) ** 2) / (2 * y_sig ** 2)\n", " return norm * torch.exp(x_exp + y_exp)\n", "\n", "\n", "def comb_func(w1, w2):\n", " z = -bivar_gaussian(w1, w2, x_mean=1.0, y_mean=-0.5, x_sig=0.2, y_sig=0.2)\n", " z -= bivar_gaussian(w1, w2, x_mean=-1.0, y_mean=0.5, x_sig=0.2, y_sig=0.2)\n", " z -= bivar_gaussian(w1, w2, x_mean=-0.5, y_mean=-0.8, x_sig=0.2, y_sig=0.2)\n", " return z\n", "\n", "\n", "_ = plot_curve(comb_func, x_range=(-2, 2), y_range=(-2, 2), plot_3d=True, title=\"Steep optima\")"]}, {"cell_type": "markdown", "id": "7ef1175a", "metadata": {"papermill": {"duration": 0.332877, "end_time": "2021-12-04T15:56:34.786176", "exception": false, "start_time": "2021-12-04T15:56:34.453299", "status": "completed"}, "tags": []}, "source": ["Most of the loss surface has very little to no gradients.\n", "However, close to the optima, we have very steep gradients.\n", "To reach the minimum when starting in a region with lower gradients, we expect an adaptive learning rate to be crucial.\n", "To verify this hypothesis, we can run our three optimizers on the surface:"]}, {"cell_type": "code", "execution_count": 35, "id": "4f8818bb", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:35.429165Z", "iopub.status.busy": "2021-12-04T15:56:35.428679Z", "iopub.status.idle": "2021-12-04T15:56:36.013195Z", "shell.execute_reply": "2021-12-04T15:56:36.013583Z"}, "papermill": {"duration": 0.911295, "end_time": "2021-12-04T15:56:36.013752", "exception": false, "start_time": "2021-12-04T15:56:35.102457", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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Y3MgMjAgMCBSCi9FbmNvZGluZyA8PCAvRGlmZmVyZW5jZXMgWyAxMTkgL3cgXSAvVHlwZSAvRW5jb2RpbmcgPj4gL0ZpcnN0Q2hhciAwCi9Gb250QkJveCBbIC0xMDE2IC0zNTEgMTY2MCAxMDY4IF0gL0ZvbnREZXNjcmlwdG9yIDE4IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zLU9ibGlxdWUKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTcgMCBSID4+CmVuZG9iagoxOCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgOTYKL0ZvbnRCQm94IFsgLTEwMTYgLTM1MSAxNjYwIDEwNjggXSAvRm9udE5hbWUgL0RlamFWdVNhbnMtT2JsaXF1ZQovSXRhbGljQW5nbGUgMCAvTWF4V2lkdGggMTM1MCAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTcgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYz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IC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoyMiAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoyNSAwIG9iago8PCAvQSAyNiAwIFIgL0QgMjcgMCBSIC9HIDI4IDAgUiAvTSAyOSAwIFIgL1MgMzAgMCBSIC9hIDMxIDAgUiAvZCAzMiAwIFIKL2UgMzMgMCBSIC9maXZlIDM0IDAgUiAvaSAzNSAwIFIgL20gMzYgMCBSIC9vIDM4IDAgUiAvb25lIDM5IDAgUiAvcCA0MCAwIFIKL3BlcmlvZCA0MSAwIFIgL3NwYWNlIDQyIDAgUiAvdCA0MyAwIFIgL3R3byA0NCAwIFIgL3plcm8gNDUgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAyNCAwIFIgL0YyIDE5IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDAuNyAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BNCA8PCAvQ0EgMC43IC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNyA+PgovQTUgPDwgL0NBIDAuOCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjggPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzcgMCBSIC9JMSAxMyAwIFIgL00wIDE0IDAgUiAvTTEgMTUgMCBSIC9NMiAxNiAwIFIKPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMjUzICj95yT65iL45iH25h/z5R7x5Rzu5Rvs5Brp5Bnn5Bnk4xjh4xjf4xjc4hja4hjX4hnU4RrS4RvP4RzN4B3K4B7H4B/F3yHC3yK/3yS93ia63ie33Vwptd0rst0sr9wurdwwqtsyp9szpdo1oto3n9k4ndk6mtg8l9g+ldc/ktdBkNZDjdZEi9VGiNVHhtRJg9NLgdNMftJOfNJPedFRd9BSdNBUcs9VcM5Wbc5Ya81ZacxbZ8xcXGTLXWLKX2DJYF7JYVvIYlnHZFfGZVXGZlPFZ1HEaE/DaU3Ca0vCbEnBbUfAbkW/b0S+cEK+cUC9cj68cz27dDu6dTm5dji5dja4dzW3eDO2eTK1ejC0ei+zey6yfCyxfSuxfSqwflwpr39cKK5/J62AJqyBJauBJKqCI6mCI6iDIqeEIaeEIaaFIKWFIKSFH6OGH6KGH6GHHqCHHp+IHp6IHp2IHpyJHpuJHpqJHpmKHpiKHpeKH5aLH5WLH5SLH5OLH5KMIJGMIJCMII+MIY6MIY2MIYyNIouNIoqNI4mNIomNI4iNI4eNJIaNJIWNJISNJYONJYKOJoGOJoCOJn+OJ36OJ32OJ3yOXCh7jlwoeo5cKXmOXCl4jip3jip2jip1jit0jitzjixyjixxjixwji1vji1uji5tji5sji5rji9qjS9pjTBojTBnjTFmjTFljTFkjTJjjTJijTNhjTNgjTRfjTRejTVdjDVcXIw2W4w2Wow3WYw3WIw4V4w4Vos5VYs5VIs6U4s6Uos7UYo7UIo8Too8TYo9TIk9S4k9Sok+SYk+SIg/R4g/RYdARIdAQ4dBQoZBQYZCQIVCPoVCPYRDPIRDO4NDOoNEOYJEN4FFNoFFNYBFNH9FMn9GMX5GMH1GL3xGLXxHLHtHK3pHKnlHXCh4Ryd3RyZ2RyV1SCN0SCJzSCFySCBxSB5wSB1vSBxuSBpsSBlrRxhqRxZpRxVnRxRmRxJlRxFjRw9iRg5hRgxfRgteRglcXEUIW0UGWkUFWEQDV0QCVUQBVCldCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDEgL0NvbHVtbnMgMjE4IC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAyMTggL0xlbmd0aCA0NiAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAyMTggPj4Kc3RyZWFtCnic7V15WJTl+o5TssPs+87MMBsDMwPDOizDvq8yssimAgoCoiEoKioqiJi4YJq7uGCa5a7llpaaplIel8pMTf2Z1cnUTnU6Xdfve79ZQdDUb47DOd99XWp/zXz3PM/7vM9yP1+vvIICBQoUKFCgQIECBQoUKFCgQIECBQoUKFCgQIECBQoUKFCgQIECBQoUKGwJdia87CdBEnaP42U/EiIw0/kbwH8PO0tWZgx+cha0XrWEBb2X/YjPib7EXoPRh9zLfsjngZGXntMQGPb6f/QEjexe9oM+MwwmMxGzN8OS3ODjZjAZ4AVYOTg4Ojo66QH9l4MD4AfYDT5ylswgYoCWs7OzCwD0L6BnQW5QcTP6IrAYZDAnZxdXVzc3dz3c3FxdXZxhdhA5o+Fe9iP/RZiZAYs5ubhAtDBYLA6Hx+NxOCwWg3Fzg8zn6AhZbnBxMzADvghbDPDC4wlEEolEhv4QiXg8FuPu5gpZDjIc5JSDxyf1xwxyRgcHJ2dXN4gXkUim0Gh0BoPJYDDoNAqFRITYmcgZDtzLfu6nw2gze3vIF13dMTgCRIzOYLLYHAA2i8WgUykkAg7j7uripHfKwWE3AzPgjM4ugBiZSmexeR4CoUgMIPIUevA4LDqVTMBj3F1cLOz2sh/9aehlM4gZicpgcXlCT7FE5uUl9/KSSSQQOS6LQSUTcVg3V4NP2r5LwiHEaDMMlkCiMdgeAonUW+Hr5x8YGBQYGKD2U/jIJEIPDoNGImIxri6DhJvJHSGbuWFxJAqDzROKvbx9/QKDQkJDw0JDNUGBal8fuVjowWZSyHjYbnqXtHlqBmbQOcMSSXQmTyjxVgYEhUXExCUkJSUnJyXGxWjDgwNU3lKhB4tBJmLdXZ0dB4HZYKNBB83B0cUViyNTWByBWK7y14RFxyalpGdmDc3KzEhNjosJ10DcJEIuiwrs5uLkYA+uN1umBh80yGjQQXNzxxPpTA+hTKEOCY9LzMjKzS8qGTFyREnR8LyhmUlxWo2/wkvIZ9JJBIwbdNxs3SX1+RUUQ5wgdyRRmBxPCcQsMi4tM69gZFnF2KrqqrEVZaMK87PS4qNC1UqpiMuikHDu8HGz7QvAdNKgEIKn0Lh8qbe/JjIxLXd46eia8XUNkxonN0ycMG5MWWFuRlK0JsBHJuDRqQSDS9ryaTOdNGdXdzyJzhKIFH6h0UlD80aNHl83dfqc1rnz2lpnz5g2cUJF6fChKdHhaqVYyGaQ8RhXZwfgkrZNzXDSMEQykyOS+gZqEzLyiseOmzxtdtuCRYs7lyzqaGtpahw/dkR+VkJUkFomhlySiDWcNpv1SH0QGTLE0QnKQsiQO3opgiMSM/PLqhqmtr3x5oq1GzZt3rRh3YplC+Y1TaopK8hMjtAo5ZBLArMZTpuNms1AzR5Qw1PoHkJvVWhUiq6ocvy05o7O1eu7t21/d/u2LV1rOhfNaXq9coQuPSrc18fTg0GBgqSzjVPTBxEoPOKIdJZQ4hsYnagrrpg4rX3Rmg3v7v7gyIfHPzxycM+OTeuWzJ8+sao4Jyk2SC315NBJOIw5kLxsGv1BTw2O/HgSgy2SqoNjU3JHVk+euXDpxrf3fvDhiVOnT504/sG+bZuWLZozedzI4anxIf5eIi4IJC7Otk5NHx8xBAqLJ/UOCkvOKq6cOGv+yq5d+z8+/fmlK199cenCmZPv7964qmPOpMqRQ1MjQnxkHiwo/ru6ODjYbkZipoYlUtkeMp+QiNTskdWTWheu695/+EzPla+/uXH96y8/O3v0/bfXL5k7paZsWHpkqNKLz6YRccYYaZPUTKHfxQ1HonEEclVYdGbe6NebFry55b1jpy5+9e3dez9+f/fW1cufHN+1dUVH8+uV+UNjIny9hVw6Ce/mOlio0blCH9+ImKHDKyc2L165fc/Js1/duPfTz48e/PT9za/Pf7L/vTVLZtdXFwyLi1T7ePIYJLz74KDmjifTeUKFnzZOV1g9aXbnmp0Hznx+/c4/Hv3z918f3f+/mxfOHty1funcSbVFuQlR/goRj0EmQHmk7V7aJmpQlkVmeHgq1FHxOcXjGluXrd9z6PylW/ce/vbHn//+/Zfv71z57Oi+Tcvbp0woyUuMDlCKPZgwNUeU2n8e/yvU6DyIWmT8sKKayS1vrt31wdkLN+/e/+dvf/z+zwff3bp4/vCeDW+2TR5fnJsQ7a8UDR5q5ggZm10wtr55ycp39546d/XmvZ8ePHpw/4dvr/WcPvDe2iUt9TWFIEIqBlOEBPcaV+CtCo/Jyq+om7HwrW27Pj5z5dqdH/5x/6cf7n7z5acn9r6zatGcuqrhulitn4+nzd9rxmwEdHxANuKl0GjTdKXjGtsWd739/tGzF7765uatm9evXjh/7OC2DZ1t08aVD8uIgrIRAcfWsxHLRItAYfNkPsHhKUNHVDW0LFizae/BU+cufnn12tdfXj53+tD+7rWLWhqrSrPTtBqFjM+mEnGuNk9Nn/ljoBDJEcn8Q+JT80vHTZ2z5K0t298/cuL0p+fOnj555IN3317Z2Tp1QmlhWqImUC7mMimDIfOHqMH1GonO9pT4BcUkDSsZ2zB9Qef6zTv3HT720YmPjx3Zv7u7a2nHzIbqkrzkuGC1TMRhkHE2T81QZTu7YQlUBt9T4Rcek547oqauubVzeVf39p279uze+W73xreWtjXX15TmZsZq1UoRn0klYt2cnextNvb37o3g9b2RkIikrOHl1ZOb5i1YtnL9ps1bujd1rXprYfuMxnGjC7NStBqlt5BHBw0E2+6NGDsIIPxjiRQ2V+LlHxKdPLSodFxdU3N7R+ey5SveWtbZMX/WjIm15UW65FhNgFzqwaaScBb945dNon881ocUK9Sh0UnZeaVjJtRNnTF7btu8trmzZzRNfL2idHi2uQ9JwLg623J8BDDHSDccdNo8hHJlUHhsalZBSUV1Xf2UadNnTJ82paGupnJEYXZafHgw5I58JnSpuesHGjbcPTb1/B31PX8Wx1Oq9NdExadn5ReWlldW1YyrqRo7urRoeHZGQnSov0oq4gJ3xBhGbLYaRABMkxrIbBgCicESiOSqwPCopNTsnMLiUWXlo8vLRpUU5erSkqLDg3x9REI2E0zYIKMNsfFxhiGP1LskFk+msrmeUm+/gLCIuITU9KxsnU6XnZWelhinDQvyU0hFPDaNMkjma6Z5r4OjsysGSyIz2HyRTOEbpImMjk9MSUtLT09LSUyIjgwN9lN4iQRsJgW4IxRDbPykARhdEnTH3XCQ3Zgcvkjqo1QHhGjCI7RabUR4aEiAv9JHJhJwWTQKAecOR0dbd8dXLFUjQFqBJZLpLK7AU+qlUKkDgoJDQkKCgwL8VUo5zIxOJuGwsCwG6VG2WbuN4K9lwc3ZFTpvJAqdxfEQiMQyL28fhULh4y33kogFfA6LTiHhce5QhoWotsKufyDwycYLALiko4uLOwZPJNMYbI4H31MklkikEolY5Mn34LIZNAqRgHU32gwhdzRz6S1NR+5n03NzcHKCxWckMo3OZHG4PA8AHpfDYtJpZCDSAgI05PRnvUn9zSjeRlJ7b5J5AskgRA6HJ5IoFFhYx2QCZR0VKOtgYs7IKesseL36qrWk93ZmoadBDokDakgyhUKlUikUMpkIiLm7uSCpGTQSM4vuXxvymll9j5T03q4XOVjFisHoRaywjBUDhJ4GYnAEeWFmZmKv9dWnG+XpCJEzUnvNJD0GEl2IHhYL84KJOTnoNawI2MxsMQMvBwBH+G97o34bIXW6nZkczM7RJBiHNeNOTkAPj5hi3Gwx47eBr9PDSf9d9hbS+xfnpj/QJpm/6etgKTySQn+L31EvvAe/oqsexh/S3h5B5b2l9+t938HB6CH2Zv9HyGavmlXcrq7u7rDv4/Tu7+5qONf2yHj/K70PALxS89jeCSJH286k4jYwAwJ1OGQRCPqg1StmIZT29LpoXjPDdNsgcJWagzHYKIBTBAIJukapNADoriGRCEB7D6U9TsaAjBS3gW9RxJk5A306yA+oUH7ABGDQ6Yb8wB3R/MBEru/OIXLZnYmZfqUAiyMA3T2bw/PgCwD4HjwOm0GHFdxwJu5gWpl4cW6PZ6yWrF6Um2Uu7vpYLi4FubiAb8zFMQjn4r3ZPR3P+sGmCsrNHawUMNhQeSiRyX0UKpWvSqX08ZZJPQU8NhOsFUC5nRVqw2fi98zM9CsFhrpXJlf6+gdCZa9GExIcGOCrAnUvj8WgQHWvm4vV5OkDsunrqH/x02B3NNqMQmPzBBIvpW9gUGi4NioqOjoqMjwsONBPKZcIeWw6FdgNrqKs2a3ow6m/APOXPsTYiIdsBqTOArGXUh0SGhWTmJyWnpGZkZ6WnBQbHRbir5KLhRwWlQz3K6zbY+rNa4Bb4a9QM7Rh3HB4Co3DE8kU/kERUQlJ6Vm6nJzc3BxdVkZyYnREsL9SJvbgQHYDEm4HK7ZzLYj1ucz7XOdPZ2aUpxPJTLZQ7OMXFBGTkj4sr3hk2ZiKyoox5SNL8nMyUmK1IX4KsSeHSYH7uVaUp5uYGYkNMeG1Xqu/T/xuUxfeGXZHNlckUwWExiRkZhcUl4+pGlc7vnZcdcXokkJdZlJMeICvTMxjQy6JsVgGsQozc3VlzpsNyfOQv1YS2BnHQrBolsbke3qrgrXxGbriUVW19Y1NM5vnzJo5vbFhfHVpSU5Golbj6yMSsOgkvDs8gLXKaevLDF5sdtRvNT9DuWNn6MEDo+FJDJZQrFSHxSTr8ssqXq9vmtnSNm9+e1tr8/SGusqyAl1qTIS/SuJp3YmXXa+6Ea7jLKorF2N1pU+InvD1xvAIT86pbJ5UHqCJTdWVlE+ob27pWLJs5ao1q1cuX7Kwddak1ytKclPjQ4O8ZR4cmn4ZZMgQ5OeUepuZ6kbQOTEvNoPOgonck6tiu77TZbl5utzesXzV+k3dW7o3bVj11sL5YLpcNDRFG6ryFvIYVpsuG2ymrxsdnZ2huhGsNeMNsKyunpylm4OIG5YAnTSRUh0Rm5lfWls/e96ylZu37ty7/8D+vbu2da9ePn/25Nry/KFxkf4qsYBFg4KkNTQBJpsZihBjI49EhgEWm3FwceWsbwoNzM1Cng4rOaRAyZEzAlZydHXv2n/k+McnPz5+9MCeLRuWdjRPqhkBlBz+VlRy2Flskjq7uGEweDxJX14B0Ok0KtlQXbk8pUdppOYM9DdMrtgrQJOQXlA+vqll6cqtOw4d++Rsz+c9504fP7zzndVvtjXVlRdlJIUGySU8JgWPdTXFSKSZwX1lqAiBCmJQXrGg+opn6JmzmVABYl5sHpibpTydYpanN7R0rN2879Dp85evXrt+7eqVnjOHD2xZt7h1SnWZLl2rMcjTkVdNWdoMZgamASwOWGz2FEHwFIJBB5NOJcG7v5YznL6PYLjV9DoOKofvpQiNTB9WVjtl3pKNWw8eO3/x65u37ty6ee3SZx8d3r5paXtT7eiczKgwldw6WjcLm4FNUjCfYnJ5QpEUlFdKCD4+XjIxVF1xGDSoLMaACsRhgMrRrq9C0SBPn7loxTu7T3z65fW7P95/cP/Hezeunju1b/vqxbMnVhfo4oBCEYgvEVcoWpZXblB5RaGxOHyhROatUPmp/SH4+SkVcqlI4AFXjmBeOuDsrRc1Os+kK521ZNV7+z45//W3399/+MvDn3+89c1nZ97fsbazpaGmcFi8XldKNulKkTpsduZFUshmBDJETCDy8vZVB2nCtJFRELQRoZpAtVIuFfLBlNswwO+Xm4laHzVw65vrdh889/dvv3vw6+///tevD+/dvtRzZO/GZfMax+uFzlZRA5tqEEN5xeB4iKQKVUBwmDY6Ni4BQlxspFYTpFZ6iz25bPOcu7+iuBe1l63hNhgNLokxBBKNxfOUKnyDQ6Njk9MydcNyIeiyM9ISY7WaQIickMOEF2QH2Nm2MWqGDg14gQARlMQSuV9AeGRCcqYut6CwuKSkuGB4TnZ6cpw2VK2CKhAOjUzAwgV/P5qSJ1O7qKf2x2+Pvr9z+TPIIZe3N1qPmj5aG0QXBCqDK5Qq1KHaxBRdXnHpmKqaWgjVY8tHFeZmJceFh6igCoRNJ0O3a/9bpOYwot9y8YG3XKoa5nSu3rH/dM83t3988Muvvzz46faNzz/9YOf6ztaGcUU58eYtFyTDiPGkgWV0Aqy681YHR8VlZBeOqKieMLGhsbFxcv3E2uryEflZqTHaAD/IbiwqdAX1v0XaO0L2s5v03U8/PzTuJr1r1d0k80mDhWlsnljup9EmpecXVVRPbJw+p6WtrW3u7FlTJ0+oKisYlpIQFqzwMqTp/eayltSINA5frgyLysgtnzDtjaWbtx/5+MKV67fv3rt75+YXF09+uGPL8jdmTKjIy4oO9/UWcoDyHskr2/zaB1csngp5kJcqKDJ+aG5pRd2k5tY3lnQuW758Wefiea1NDbWjS3QZUOWolAjYNGMu2/c57CwTrT57gJv3HzrdAyVaN765+kXPp0cOvL1+iT7RMu0BIppomQtH6KSBGkThHxaXmlc0tnbqzPZFy1at3bBhQ9ealZ2LWmdMqhk9fFhiTLDaCzoXFPNp6+uRltubXIk8KDQps2hM3cx5b619b8+xk+cuXLx86ULPqeP7dnatmN9cXzEiKyU82Eeq395EsoVgXG8AgR8so3upNTFpulEV9VPnLVyxrvvdnXv37t3z3vaN65Z2zJ78ellxZkpEiFIGRRKiKSvq65EWRQ0DKmrUQTHJOSOqJs3ogIqa3QeOfnTi1ImPPjyw9+2Nby4ERU1+irGoQVh5b4yP8J499CN7B4TFZ+aNrp46c+HS9d3v7Xv/8OHDh/bv2da9qrN9ekPFKF16VKiv3JPDIA0QqU2b0s6g6cMSilUBUQnZhaPrGts6Vq3btmP/wcNHDx88sHN71+pF86bVVRYOS4wO9JMK2XQSEM3aI0bNeOrBbwxX+ypNdGreyPENrQtWb9hx4MNTZ3p6es6fPnlo/7auZe3NddWFOQlRgUqJB5tCxLgbru3H44h5v50nkCs12uTswjHjpsx4Y9GKNRu3bN22dcumtSsXL2ieOn5McXZqJGggeCDeQDBTw+AoNJ7Q2zcsNqOgtK6xffH6LXsPnzj32aXLly+eP3v80I7uVR0t9bXFeUkxQb4yPodKMtX7j8UR0BtxcHIFvxYHCiRB4QkZeaWV9Y2t7Z3LVq/r2ti1bs3ypfPnTq2vKh2ekRQeovAScOkgOTZRe3Fmr5iWG0B8ZPBFqoDIJN3IsVNmdULX0PEzf796/fbt27eufdVz5tDuzcs7mhrKSzISQ/29PXl0imnTfoCbTX982Z4SVUBEXGpO4Ziq+snNs+e90bGw4432llmNDTUVxTkZcZEBflIRlL0RsMhWaxbUoPgoEPsGRqfklNU0tSxfu/PAqZ4vbty+9/333317/eL5Y/u3rloys3HMyMzk8EAfkZ6a8wDUTMp7Ip0pEPn4aaISs3JHltfWNTY1z26d2zp7VtPUuvFjRuUPTY4K81OKhSzovsa49nebPD8z00oKeDsCFM6C49KHV0yY1b5m04Gj56/cvPePR48ePfjhu68vf3JkZ9fylqbqcl2aNlgh5jOoeGz/Ac3iqgTtOo4HlJMGR8Sn6HJHlFZWj6+rm1hXN6FmbNnIPF1qfGSwWi714MJSZ2cEg4iZmouBmn9IfEbB2LqWBeu3HPzowtU7Pz387ffff73/442vzh7fs2lF28xxY4alR4UoJXwm1dCl6Z/a42/biU3LzBs+snRM5djqqsqKslEF4G07kRprvW2nFzUGWyTz1yRkFlZNbO3o2nr4xKVv7v78yx9//PGvh/dvXev5eF/3qnnNtRU5GVEhKomASRuYmtls7ngCjcHlS7x9g0Kj41LTs3PyCwqLCqFSQpeRGh8TFuTnLRXwGDQifuAS8MWoQbkIHqYWEJqQVVRdP3fRxm1HT125ce/hr3/++ee/f3lw5/pnJw9sWTN/1vjK3MxojUoKUSPg9NQei2h2xmvbcnSoUhvebJWRmZWZkZ6SBN5s5a/y1o8OkVfeD2y1Dc9vNYsSEHJJDIEI7Cby8vEL0IRFRsfGxyckxMdFR4ZpAtQKLzGfB7/Xyh3pQb1VzlqfMT14ixwY04tkcpVvQGBwiCbUMKb39hILrTamt0qEtJA569/XiCdS6SweXySReyv1PTK1GiIml4gEYEhPImDdXfTbEq8iIrAzPgTi95rxc40bE2CtgAAZjsHh8gUisUQqk8mkUrFIyOdxmDAxa0hirJKNGD7XomsLBBYkCo3J4vL4+n60p6eAz+OymYZeu4tJpYXg+Kn/HLKsrnG+ZQ7Zc/b44SfmkP1/sHGmBcgBXR2ZSmMwWSw2m81iMRk0GoVMwGOBitvR0R5JiVZfakhl/n246d9pC7jBr36l0uh0w+iHQiYScPp32jo4DEHaHV95Sr22xViv7X1HX69VPr1e68PNRA4e2eHwBAIRBgGPw2EwbqaBnRXeaIt4lW1JzfL90QYZKxanBxaMWeGNAkeLMSuiM2zEeyN9uZnIOerH/m56GITVCOuq+6OGWEerf24v5V3tSPch+yPXR4ligFGIYr2X0CPcPe6Hm4X0foD/L4KdVZgh3fMfmFsf0dezqL6enxuSk5oByFl7yWvAr0ZwvjYguScpLK1D7BXzaTNUjtQXmoo+iZ2JYG/Vr9WIWeppEZhlP43bY7AiMQtuSCgQnvZF/zlWxm9ETDfyV77sP0TL+HVIqX1sDhZ2e1GNls3BXDm+qLLO5mBRgbygHtL2YIeUitUGYbLbC2qPbREW3F5IMW6TsCwdn1/nb5vow+35tjNsFebq6vl3amwUpvT8BTahbBeW1cfz7a/ZLh4vrZ5x69CmMWB1NdiJwfhv5WXAfyktFChQoECBAgUKFChQoECBAgUKFChQoECBAgUKFM+G/wd4Y59ZCmVuZHN0cmVhbQplbmRvYmoKNDYgMCBvYmoKNjczMQplbmRvYmoKMTQgMCBvYmoKPDwgL0JCb3ggWyAtOCAtOCA4IDggXSAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEzMSAvU3VidHlwZSAvRm9ybQovVHlwZSAvWE9iamVjdCA+PgpzdHJlYW0KeJxtkEEOhCAMRfc9RS/wSUtFZevSa7iZTOL9twNxQEzdNNC+PH5R/pLwTqXA+CQJS06z5HrTkNK6TIwY5tWyKMegUS3WznU4qM/QcGN0i7EUptTW6Hijm+k23pM/+rBZIUY/HA6vhHsWQyZcKTEGh98LL9vD/xGeXtTAH6KNfmNaQ/0KZW5kc3RyZWFtCmVuZG9iagoxNSAwIG9iago8PCAvQkJveCBbIC04IC04IDggOCBdIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTMxIC9TdWJ0eXBlIC9Gb3JtCi9UeXBlIC9YT2JqZWN0ID4+CnN0cmVhbQp4nG2QQQ6EIAxF9z1FL/BJS0Vl69JruJlM4v23A3FATN000L48flH+kvBOpcD4JAlLTrPketOQ0rpMjBjm1bIox6BRLdbOdTioz9BwY3SLsRSm1NboeKOb6Tbekz/6sFkhRj8cDq+EexZDJlwpMQaH3wsv28P/EZ5e1MAfoo1+Y1pD/QplbmRzdHJlYW0KZW5kb2JqCjE2IDAgb2JqCjw8IC9CQm94IFsgLTggLTggOCA4IF0gL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMzEgL1N1YnR5cGUgL0Zvcm0KL1R5cGUgL1hPYmplY3QgPj4Kc3RyZWFtCnicbZBBDoQgDEX3PUUv8ElLRWXr0mu4mUzi/bcDcUBM3TTQvjx+Uf6S8E6lwPgkCUtOs+R605DSukyMGObVsijHoFEt1s51OKjP0HBjdIuxFKbU1uh4o5vpNt6TP/qwWSFGPxwOr4R7FkMmXCkxBoffCy/bw/8Rnl7UwB+ijX5jWkP9CmVuZHN0cmVhbQplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKNDcgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMTIwNDE2NTYzNSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA0OAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAyNTU2MCAwMDAwMCBuIAowMDAwMDE2NjIyIDAwMDAwIG4gCjAwMDAwMTY2NjUgMDAwMDAgbiAKMDAwMDAxNjg5MSAwMDAwMCBuIAowMDAwMDE2OTEyIDAwMDAwIG4gCjAwMDAwMTY5MzMgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDAyIDAwMDAwIG4gCjAwMDAwMDc4NjcgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDA3ODQ2IDAwMDAwIG4gCjAwMDAwMTcwMjYgMDAwMDAgbiAKMDAwMDAyNDc5OCAwMDAwMCBuIAowMDAwMDI1MDUyIDAwMDAwIG4gCjAwMDAwMjUzMDYgMDAwMDAgbiAKMDAwMDAwODU3NCAwMDAwMCBuIAowMDAwMDA4MzY2IDAwMDAwIG4gCjAwMDAwMDgwNTAgMDAwMDAgbiAKMDAwMDAwOTYyNyAwMDAwMCBuIAowMDAwMDA3ODg3IDAwMDAwIG4gCjAwMDAwMTUzMzggMDAwMDAgbiAKMDAwMDAxNTEzOCAwMDAwMCBuIAowMDAwMDE0NzE5IDAwMDAwIG4gCjAwMDAwMTYzOTEgMDAwMDAgbiAKMDAwMDAwOTY1OSAwMDAwMCBuIAowMDAwMDA5ODIyIDAwMDAwIG4gCjAwMDAwMTAwNTkgMDAwMDAgbiAKMDAwMDAxMDM3OSAwMDAwMCBuIAowMDAwMDEwNTQxIDAwMDAwIG4gCjAwMDAwMTA5NTUgMDAwMDAgbiAKMDAwMDAxMTMzNSAwMDAwMCBuIAowMDAwMDExNjM5IDAwMDAwIG4gCjAwMDAwMTE5NjEgMDAwMDAgbiAKMDAwMDAxMjI4MyAwMDAwMCBuIAowMDAwMDEyNDI3IDAwMDAwIG4gCjAwMDAwMTI3NTggMDAwMDAgbiAKMDAwMDAxMjkzMCAwMDAwMCBuIAowMDAwMDEzMjIxIDAwMDAwIG4gCjAwMDAwMTMzNzYgMDAwMDAgbiAKMDAwMDAxMzY4OCAwMDAwMCBuIAowMDAwMDEzODExIDAwMDAwIG4gCjAwMDAwMTM5MDEgMDAwMDAgbiAKMDAwMDAxNDEwNyAwMDAwMCBuIAowMDAwMDE0NDMxIDAwMDAwIG4gCjAwMDAwMjQ3NzcgMDAwMDAgbiAKMDAwMDAyNTYyMCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQ3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0OCA+PgpzdGFydHhyZWYKMjU3NzcKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:56:35.780390\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["SGD_points = train_curve(lambda params: SGD(params, lr=0.5), comb_func, init=[0, 0])\n", "SGDMom_points = train_curve(lambda params: SGDMomentum(params, lr=1, momentum=0.9), comb_func, init=[0, 0])\n", "Adam_points = train_curve(lambda params: Adam(params, lr=0.2), comb_func, init=[0, 0])\n", "\n", "all_points = np.concatenate([SGD_points, SGDMom_points, Adam_points], axis=0)\n", "ax = plot_curve(comb_func, x_range=(-2, 2), y_range=(-2, 2), plot_3d=False, title=\"Steep optima\")\n", "ax.plot(SGD_points[:, 0], SGD_points[:, 1], color=\"red\", marker=\"o\", zorder=3, label=\"SGD\", alpha=0.7)\n", "ax.plot(SGDMom_points[:, 0], SGDMom_points[:, 1], color=\"blue\", marker=\"o\", zorder=2, label=\"SGDMom\", alpha=0.7)\n", "ax.plot(Adam_points[:, 0], Adam_points[:, 1], color=\"grey\", marker=\"o\", zorder=1, label=\"Adam\", alpha=0.7)\n", "ax.set_xlim(-2, 2)\n", "ax.set_ylim(-2, 2)\n", "plt.legend()\n", "plt.show()"]}, {"cell_type": "markdown", "id": "494d99c5", "metadata": {"papermill": {"duration": 0.323237, "end_time": "2021-12-04T15:56:36.662651", "exception": false, "start_time": "2021-12-04T15:56:36.339414", "status": "completed"}, "tags": []}, "source": ["SGD first takes very small steps until it touches the border of the optimum.\n", "First reaching a point around $(-0.75,-0.5)$, the gradient direction has changed and pushes the parameters to $(0.8,0.5)$ from which SGD cannot recover anymore (only with many, many steps).\n", "A similar problem has SGD with momentum, only that it continues the direction of the touch of the optimum.\n", "The gradients from this time step are so much larger than any other point that the momentum $m_t$ is overpowered by it.\n", "Finally, Adam is able to converge in the optimum showing the importance of adaptive learning rates."]}, {"cell_type": "markdown", "id": "753e94b5", "metadata": {"papermill": {"duration": 0.327421, "end_time": "2021-12-04T15:56:37.310853", "exception": false, "start_time": "2021-12-04T15:56:36.983432", "status": "completed"}, "tags": []}, "source": ["### What optimizer to take\n", "\n", "After seeing the results on optimization, what is our conclusion?\n", "Should we always use Adam and never look at SGD anymore?\n", "The short answer: no.\n", "There are many papers saying that in certain situations, SGD (with momentum) generalizes better where Adam often tends to overfit [5,6].\n", "This is related to the idea of finding wider optima.\n", "For instance, see the illustration of different optima below (credit: [Keskar et al., 2017](https://arxiv.org/pdf/1609.04836.pdf)):\n", "\n", "
\n", "\n", "The black line represents the training loss surface, while the dotted red line is the test loss.\n", "Finding sharp, narrow minima can be helpful for finding the minimal training loss.\n", "However, this doesn't mean that it also minimizes the test loss as especially flat minima have shown to generalize better.\n", "You can imagine that the test dataset has a slightly shifted loss surface due to the different examples than in the training set.\n", "A small change can have a significant influence for sharp minima, while flat minima are generally more robust to this change.\n", "\n", "In the next tutorial, we will see that some network types can still be better optimized with SGD and learning rate scheduling than Adam.\n", "Nevertheless, Adam is the most commonly used optimizer in Deep Learning\n", "as it usually performs better than other optimizers, especially for deep\n", "networks."]}, {"cell_type": "markdown", "id": "7c5857ba", "metadata": {"papermill": {"duration": 0.328086, "end_time": "2021-12-04T15:56:37.960730", "exception": false, "start_time": "2021-12-04T15:56:37.632644", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this tutorial, we have looked at initialization and optimization techniques for neural networks.\n", "We have seen that a good initialization has to balance the preservation of the gradient variance as well as the activation variance.\n", "This can be achieved with the Xavier initialization for tanh-based networks, and the Kaiming initialization for ReLU-based networks.\n", "In optimization, concepts like momentum and adaptive learning rate can help with challenging loss surfaces but don't guarantee an increase in performance for neural networks.\n", "\n", "\n", "## References\n", "\n", "[1] Glorot, Xavier, and Yoshua Bengio.\n", "\"Understanding the difficulty of training deep feedforward neural networks.\"\n", "Proceedings of the thirteenth international conference on artificial intelligence and statistics.\n", "2010.\n", "[link](http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf)\n", "\n", "[2] He, Kaiming, et al.\n", "\"Delving deep into rectifiers: Surpassing human-level performance on imagenet classification.\"\n", "Proceedings of the IEEE international conference on computer vision.\n", "2015.\n", "[link](https://www.cv-foundation.org/openaccess/content_iccv_2015/html/He_Delving_Deep_into_ICCV_2015_paper.html)\n", "\n", "[3] Kingma, Diederik P. & Ba, Jimmy.\n", "\"Adam: A Method for Stochastic Optimization.\"\n", "Proceedings of the third international conference for learning representations (ICLR).\n", "2015.\n", "[link](https://arxiv.org/abs/1412.6980)\n", "\n", "[4] Keskar, Nitish Shirish, et al.\n", "\"On large-batch training for deep learning: Generalization gap and sharp minima.\"\n", "Proceedings of the fifth international conference for learning representations (ICLR).\n", "2017.\n", "[link](https://arxiv.org/abs/1609.04836)\n", "\n", "[5] Wilson, Ashia C., et al.\n", "\"The Marginal Value of Adaptive Gradient Methods in Machine Learning.\"\n", "Advances in neural information processing systems.\n", "2017.\n", "[link](https://papers.nips.cc/paper/7003-the-marginal-value-of-adaptive-gradient-methods-in-machine-learning.pdf)\n", "\n", "[6] Ruder, Sebastian.\n", "\"An overview of gradient descent optimization algorithms.\"\n", "arXiv preprint.\n", "2017.\n", "[link](https://arxiv.org/abs/1609.04747)"]}, {"cell_type": "markdown", "id": "717a390c", "metadata": {"papermill": {"duration": 0.321185, "end_time": "2021-12-04T15:56:38.605839", "exception": false, "start_time": "2021-12-04T15:56:38.284654", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 3: Initialization and Optimization\n", " :card_description: In this tutorial, we will review techniques for optimization and initialization of neural networks. When increasing the depth of neural networks, there are various challenges...\n", " :tags: Image,Initialization,Optimizers,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/03-initialization-and-optimization.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab,colab_type,id,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 129.529808, "end_time": "2021-12-04T15:56:39.938276", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/03-initialization-and-optimization/Initialization_and_Optimization.ipynb", "output_path": ".notebooks/course_UvA-DL/03-initialization-and-optimization.ipynb", "parameters": {}, "start_time": "2021-12-04T15:54:30.408468", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"016d946a5dab4a79a18764f5c0041ba5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "026aebbc34a44feb8b457365361afafd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_22e8949053614c0192e311e8ff9aef02", "max": 26421880.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_093f9a4bfafa4740b5f88601d54044ac", "value": 26421880.0}}, "08483ba1c3a04915abdbad783c25de3c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "093f9a4bfafa4740b5f88601d54044ac": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "0d49022c934a4041bdedb88894434ccb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "0fdf1596470d464fa9a290533416cb26": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_354ac47ccada4df9998175a3f34d76c9", "IPY_MODEL_026aebbc34a44feb8b457365361afafd", "IPY_MODEL_27b5f2c48c6a4bad9b67ba6eae81cbfb"], "layout": "IPY_MODEL_7c2dc452217b4a448d843765a9de3589"}}, "165b31c1d551419c9c3e957a59b4dbe8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_016d946a5dab4a79a18764f5c0041ba5", "placeholder": "\u200b", "style": "IPY_MODEL_f08b86836fdc45349d6e4f05cb5a13b9", "value": " 4422656/? [00:00<00:00, 5533630.76it/s]"}}, "17db2890ca0d456d8cf158dc64abf9d5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "19d14e54732d4a498f8b2802a85f5149": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "221bb2a4278543d480edebc7722b71fc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "22e8949053614c0192e311e8ff9aef02": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "27b5f2c48c6a4bad9b67ba6eae81cbfb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a4e1072e043c4055a83cd5a886ed7ee2", "placeholder": "\u200b", "style": "IPY_MODEL_f07427933ca0436591fbab57cc611476", "value": " 26422272/? [00:01<00:00, 28038507.13it/s]"}}, "2c36103f487546b8b37b85e3aefafdd5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "354ac47ccada4df9998175a3f34d76c9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_221bb2a4278543d480edebc7722b71fc", "placeholder": "\u200b", "style": "IPY_MODEL_fc2fa21a06584df18e1935b0d4c97740", "value": ""}}, "3d1754bbcdcf43fab677cb91b0efa22e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "44bb90b0e4f54124ba64039651776ce9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cf04ff1d2808421493a90ea67b0c6c65", "max": 5148.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3d1754bbcdcf43fab677cb91b0efa22e", "value": 5148.0}}, "4c894e4c1ae9405faec2c75640591ff0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4ef9603d92f346419ff8be3c2984b0e6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5552c3e370d84c4abe093fd198faf878": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "56a2c8c742d5459e92de2eb14a2908ed": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_7c769972b877422bb50730dcab448940", "IPY_MODEL_5d9e259f88794c08b0124f67612517ba", "IPY_MODEL_165b31c1d551419c9c3e957a59b4dbe8"], "layout": "IPY_MODEL_17db2890ca0d456d8cf158dc64abf9d5"}}, "5d9e259f88794c08b0124f67612517ba": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4c894e4c1ae9405faec2c75640591ff0", "max": 4422102.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_7a0e21be27384807ab12f5a8bfac4978", "value": 4422102.0}}, "65057ed2e8c044468d5be827e3a2e007": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4ef9603d92f346419ff8be3c2984b0e6", "max": 29515.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_d0fa3935d2454032aa3820b5314c22e5", "value": 29515.0}}, "68ab1848fe7942fd914800ef133729fd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_e6202326d4104925b4cb7cfba26f1f56", "IPY_MODEL_65057ed2e8c044468d5be827e3a2e007", "IPY_MODEL_98a9c5a0f7f1451e8a11f46e53321208"], "layout": "IPY_MODEL_a06f5823643545f49353d80444b7313f"}}, "7136c44874c14f3a9db1e1bea7d49b11": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b16b51a3dfd9446aa90ad8458f381041", "placeholder": "\u200b", "style": "IPY_MODEL_5552c3e370d84c4abe093fd198faf878", "value": ""}}, "7198121599234cd586ed1a5516fd079d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7a0e21be27384807ab12f5a8bfac4978": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "7c2dc452217b4a448d843765a9de3589": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7c769972b877422bb50730dcab448940": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c9bee2a106034b52b0f23a884a670400", "placeholder": "\u200b", "style": "IPY_MODEL_0d49022c934a4041bdedb88894434ccb", "value": ""}}, "94ba0c8691ce48feb9c6331ba8e9396b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "98a9c5a0f7f1451e8a11f46e53321208": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2c36103f487546b8b37b85e3aefafdd5", "placeholder": "\u200b", "style": "IPY_MODEL_a0566c7ab89149f4b7490c6da055358f", "value": " 29696/? [00:00<00:00, 318811.85it/s]"}}, "a0566c7ab89149f4b7490c6da055358f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a06f5823643545f49353d80444b7313f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a4e1072e043c4055a83cd5a886ed7ee2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b16b51a3dfd9446aa90ad8458f381041": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b34dadf61f28405fbaf0146f3b1a4611": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_7136c44874c14f3a9db1e1bea7d49b11", "IPY_MODEL_44bb90b0e4f54124ba64039651776ce9", "IPY_MODEL_d568fb874f04460aafc60684ebbc0770"], "layout": "IPY_MODEL_7198121599234cd586ed1a5516fd079d"}}, "c9bee2a106034b52b0f23a884a670400": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cf04ff1d2808421493a90ea67b0c6c65": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d0fa3935d2454032aa3820b5314c22e5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "d568fb874f04460aafc60684ebbc0770": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ec31a74304f344579b5f66384dd658be", "placeholder": "\u200b", "style": "IPY_MODEL_19d14e54732d4a498f8b2802a85f5149", "value": " 6144/? [00:00<00:00, 355068.46it/s]"}}, "e6202326d4104925b4cb7cfba26f1f56": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_08483ba1c3a04915abdbad783c25de3c", "placeholder": "\u200b", "style": "IPY_MODEL_94ba0c8691ce48feb9c6331ba8e9396b", "value": ""}}, "ec31a74304f344579b5f66384dd658be": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f07427933ca0436591fbab57cc611476": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f08b86836fdc45349d6e4f05cb5a13b9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fc2fa21a06584df18e1935b0d4c97740": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/04-inception-resnet-densenet.ipynb b/source/notebooks/course_UvA-DL/04-inception-resnet-densenet.ipynb deleted file mode 100644 index 8ede350..0000000 --- a/source/notebooks/course_UvA-DL/04-inception-resnet-densenet.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "ab9a7af5", "metadata": {"papermill": {"duration": 0.123414, "end_time": "2021-12-04T15:56:50.350891", "exception": false, "start_time": "2021-12-04T15:56:50.227477", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 4: Inception, ResNet and DenseNet\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:52:48.435460\n", "\n", "In this tutorial, we will implement and discuss variants of modern CNN architectures.\n", "There have been many different architectures been proposed over the past few years.\n", "Some of the most impactful ones, and still relevant today, are the following: [GoogleNet](https://arxiv.org/abs/1409.4842)/Inception architecture (winner of ILSVRC 2014), [ResNet](https://arxiv.org/abs/1512.03385) (winner of ILSVRC 2015), and [DenseNet](https://arxiv.org/abs/1608.06993) (best paper award CVPR 2017).\n", "All of them were state-of-the-art models when being proposed, and the core ideas of these networks are the foundations for most current state-of-the-art architectures.\n", "Thus, it is important to understand these architectures in detail and learn how to implement them.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/04-inception-resnet-densenet.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "047ed0ea", "metadata": {"papermill": {"duration": 0.122263, "end_time": "2021-12-04T15:56:50.594054", "exception": false, "start_time": "2021-12-04T15:56:50.471791", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "487ba510", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T15:56:50.845118Z", "iopub.status.busy": "2021-12-04T15:56:50.844642Z", "iopub.status.idle": "2021-12-04T15:56:53.309346Z", "shell.execute_reply": "2021-12-04T15:56:53.309758Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.594206, "end_time": "2021-12-04T15:56:53.310042", "exception": false, "start_time": "2021-12-04T15:56:50.715836", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"pytorch-lightning>=1.3\" \"torchvision\" \"matplotlib\" \"torch>=1.6, <1.9\" \"tabulate\" \"seaborn\" \"torchmetrics>=0.3\""]}, {"cell_type": "markdown", "id": "3d41adef", "metadata": {"papermill": {"duration": 0.122031, "end_time": "2021-12-04T15:56:53.555792", "exception": false, "start_time": "2021-12-04T15:56:53.433761", "status": "completed"}, "tags": []}, "source": ["
\n", "Let's start with importing our standard libraries here."]}, {"cell_type": "code", "execution_count": 2, "id": "b6e98e07", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:53.808117Z", "iopub.status.busy": "2021-12-04T15:56:53.807640Z", "iopub.status.idle": "2021-12-04T15:56:55.699547Z", "shell.execute_reply": "2021-12-04T15:56:55.699937Z"}, "papermill": {"duration": 2.02344, "end_time": "2021-12-04T15:56:55.700099", "exception": false, "start_time": "2021-12-04T15:56:53.676659", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_1009/1100401100.py:25: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n"]}], "source": ["import os\n", "import urllib.request\n", "from types import SimpleNamespace\n", "from urllib.error import HTTPError\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "import tabulate\n", "import torch\n", "import torch.nn as nn\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision\n", "\n", "%matplotlib inline\n", "from IPython.display import HTML, display, set_matplotlib_formats\n", "from PIL import Image\n", "from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint\n", "from torchvision import transforms\n", "from torchvision.datasets import CIFAR10\n", "\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "matplotlib.rcParams[\"lines.linewidth\"] = 2.0\n", "sns.reset_orig()\n", "\n", "# PyTorch\n", "# Torchvision"]}, {"cell_type": "markdown", "id": "ca1ba35c", "metadata": {"papermill": {"duration": 0.122671, "end_time": "2021-12-04T15:56:55.949046", "exception": false, "start_time": "2021-12-04T15:56:55.826375", "status": "completed"}, "tags": []}, "source": ["We will use the same `set_seed` function as in the previous tutorials, as well as the path variables `DATASET_PATH` and `CHECKPOINT_PATH`.\n", "Adjust the paths if necessary."]}, {"cell_type": "code", "execution_count": 3, "id": "8c2ed3ca", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:56.198762Z", "iopub.status.busy": "2021-12-04T15:56:56.198264Z", "iopub.status.idle": "2021-12-04T15:56:56.395384Z", "shell.execute_reply": "2021-12-04T15:56:56.394919Z"}, "papermill": {"duration": 0.324279, "end_time": "2021-12-04T15:56:56.395518", "exception": false, "start_time": "2021-12-04T15:56:56.071239", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}], "source": ["# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data/\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/ConvNets\")\n", "\n", "\n", "# Function for setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")"]}, {"cell_type": "markdown", "id": "335391a2", "metadata": {"papermill": {"duration": 0.122428, "end_time": "2021-12-04T15:56:56.642177", "exception": false, "start_time": "2021-12-04T15:56:56.519749", "status": "completed"}, "tags": []}, "source": ["We also have pretrained models and Tensorboards (more on this later) for this tutorial, and download them below."]}, {"cell_type": "code", "execution_count": 4, "id": "218d11ab", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:56.895357Z", "iopub.status.busy": "2021-12-04T15:56:56.893762Z", "iopub.status.idle": "2021-12-04T15:56:57.854104Z", "shell.execute_reply": "2021-12-04T15:56:57.853693Z"}, "papermill": {"duration": 1.089563, "end_time": "2021-12-04T15:56:57.854235", "exception": false, "start_time": "2021-12-04T15:56:56.764672", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/GoogleNet.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/ResNet.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/ResNetPreAct.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/DenseNet.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/tensorboards/GoogleNet/events.out.tfevents.googlenet...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/tensorboards/ResNet/events.out.tfevents.resnet...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/tensorboards/ResNetPreAct/events.out.tfevents.resnetpreact...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/tensorboards/DenseNet/events.out.tfevents.densenet...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/\"\n", "# Files to download\n", "pretrained_files = [\n", " \"GoogleNet.ckpt\",\n", " \"ResNet.ckpt\",\n", " \"ResNetPreAct.ckpt\",\n", " \"DenseNet.ckpt\",\n", " \"tensorboards/GoogleNet/events.out.tfevents.googlenet\",\n", " \"tensorboards/ResNet/events.out.tfevents.resnet\",\n", " \"tensorboards/ResNetPreAct/events.out.tfevents.resnetpreact\",\n", " \"tensorboards/DenseNet/events.out.tfevents.densenet\",\n", "]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if \"/\" in file_name:\n", " os.makedirs(file_path.rsplit(\"/\", 1)[0], exist_ok=True)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(f\"Downloading {file_url}...\")\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "8e425fea", "metadata": {"papermill": {"duration": 0.128244, "end_time": "2021-12-04T15:56:58.108919", "exception": false, "start_time": "2021-12-04T15:56:57.980675", "status": "completed"}, "tags": []}, "source": ["Throughout this tutorial, we will train and evaluate the models on the CIFAR10 dataset.\n", "This allows you to compare the results obtained here with the model you have implemented in the first assignment.\n", "As we have learned from the previous tutorial about initialization, it is important to have the data preprocessed with a zero mean.\n", "Therefore, as a first step, we will calculate the mean and standard deviation of the CIFAR dataset:"]}, {"cell_type": "code", "execution_count": 5, "id": "f33e6af1", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:56:58.376340Z", "iopub.status.busy": "2021-12-04T15:56:58.375859Z", "iopub.status.idle": "2021-12-04T15:57:01.995135Z", "shell.execute_reply": "2021-12-04T15:57:01.995523Z"}, "papermill": {"duration": 3.752594, "end_time": "2021-12-04T15:57:01.995688", "exception": false, "start_time": "2021-12-04T15:56:58.243094", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Data mean [0.49139968 0.48215841 0.44653091]\n", "Data std [0.24703223 0.24348513 0.26158784]\n"]}], "source": ["train_dataset = CIFAR10(root=DATASET_PATH, train=True, download=True)\n", "DATA_MEANS = (train_dataset.data / 255.0).mean(axis=(0, 1, 2))\n", "DATA_STD = (train_dataset.data / 255.0).std(axis=(0, 1, 2))\n", "print(\"Data mean\", DATA_MEANS)\n", "print(\"Data std\", DATA_STD)"]}, {"cell_type": "markdown", "id": "b37fa62e", "metadata": {"papermill": {"duration": 0.128325, "end_time": "2021-12-04T15:57:02.252178", "exception": false, "start_time": "2021-12-04T15:57:02.123853", "status": "completed"}, "tags": []}, "source": ["We will use this information to define a `transforms.Normalize` module which will normalize our data accordingly.\n", "Additionally, we will use data augmentation during training.\n", "This reduces the risk of overfitting and helps CNNs to generalize better.\n", "Specifically, we will apply two random augmentations.\n", "\n", "First, we will flip each image horizontally by a chance of 50% (`transforms.RandomHorizontalFlip`).\n", "The object class usually does not change when flipping an image, and we don't expect any image information to be dependent on the horizontal orientation.\n", "This would be however different if we would try to detect digits or letters in an image, as those have a certain orientation.\n", "\n", "The second augmentation we use is called `transforms.RandomResizedCrop`.\n", "This transformation scales the image in a small range, while eventually changing the aspect ratio, and crops it afterward in the previous size.\n", "Therefore, the actual pixel values change while the content or overall semantics of the image stays the same.\n", "\n", "We will randomly split the training dataset into a training and a validation set.\n", "The validation set will be used for determining early stopping.\n", "After finishing the training, we test the models on the CIFAR test set."]}, {"cell_type": "code", "execution_count": 6, "id": "5e411a23", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:02.522094Z", "iopub.status.busy": "2021-12-04T15:57:02.521619Z", "iopub.status.idle": "2021-12-04T15:57:05.007752Z", "shell.execute_reply": "2021-12-04T15:57:05.007283Z"}, "papermill": {"duration": 2.621367, "end_time": "2021-12-04T15:57:05.007890", "exception": false, "start_time": "2021-12-04T15:57:02.386523", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}], "source": ["test_transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(DATA_MEANS, DATA_STD)])\n", "# For training, we add some augmentation. Networks are too powerful and would overfit.\n", "train_transform = transforms.Compose(\n", " [\n", " transforms.RandomHorizontalFlip(),\n", " transforms.RandomResizedCrop((32, 32), scale=(0.8, 1.0), ratio=(0.9, 1.1)),\n", " transforms.ToTensor(),\n", " transforms.Normalize(DATA_MEANS, DATA_STD),\n", " ]\n", ")\n", "# Loading the training dataset. We need to split it into a training and validation part\n", "# We need to do a little trick because the validation set should not use the augmentation.\n", "train_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=train_transform, download=True)\n", "val_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=test_transform, download=True)\n", "pl.seed_everything(42)\n", "train_set, _ = torch.utils.data.random_split(train_dataset, [45000, 5000])\n", "pl.seed_everything(42)\n", "_, val_set = torch.utils.data.random_split(val_dataset, [45000, 5000])\n", "\n", "# Loading the test set\n", "test_set = CIFAR10(root=DATASET_PATH, train=False, transform=test_transform, download=True)\n", "\n", "# We define a set of data loaders that we can use for various purposes later.\n", "train_loader = data.DataLoader(train_set, batch_size=128, shuffle=True, drop_last=True, pin_memory=True, num_workers=4)\n", "val_loader = data.DataLoader(val_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4)\n", "test_loader = data.DataLoader(test_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4)"]}, {"cell_type": "markdown", "id": "939e3864", "metadata": {"papermill": {"duration": 0.132752, "end_time": "2021-12-04T15:57:05.272691", "exception": false, "start_time": "2021-12-04T15:57:05.139939", "status": "completed"}, "tags": []}, "source": ["To verify that our normalization works, we can print out the mean and standard deviation of the single batch.\n", "The mean should be close to 0 and the standard deviation close to 1 for each channel:"]}, {"cell_type": "code", "execution_count": 7, "id": "1cecadf2", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:05.545013Z", "iopub.status.busy": "2021-12-04T15:57:05.544534Z", "iopub.status.idle": "2021-12-04T15:57:09.246817Z", "shell.execute_reply": "2021-12-04T15:57:09.247213Z"}, "papermill": {"duration": 3.840259, "end_time": "2021-12-04T15:57:09.247384", "exception": false, "start_time": "2021-12-04T15:57:05.407125", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Batch mean tensor([-0.0088, -0.0180, -0.0446])\n", "Batch std tensor([0.9446, 0.9240, 0.9487])\n"]}], "source": ["imgs, _ = next(iter(train_loader))\n", "print(\"Batch mean\", imgs.mean(dim=[0, 2, 3]))\n", "print(\"Batch std\", imgs.std(dim=[0, 2, 3]))"]}, {"cell_type": "markdown", "id": "4523d138", "metadata": {"papermill": {"duration": 0.134355, "end_time": "2021-12-04T15:57:09.514896", "exception": false, "start_time": "2021-12-04T15:57:09.380541", "status": "completed"}, "tags": []}, "source": ["Finally, let's visualize a few images from the training set, and how they look like after random data augmentation:"]}, {"cell_type": "code", "execution_count": 8, "id": "380c3c39", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:09.787847Z", "iopub.status.busy": "2021-12-04T15:57:09.787368Z", "iopub.status.idle": "2021-12-04T15:57:09.932044Z", "shell.execute_reply": "2021-12-04T15:57:09.932440Z"}, "papermill": {"duration": 0.284584, "end_time": "2021-12-04T15:57:09.932602", "exception": false, "start_time": "2021-12-04T15:57:09.648018", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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V2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvQSAxOCAwIFIgL0MgMTkgMCBSIC9GIDIwIDAgUiAvSSAyMSAwIFIgL1IgMjIgMCBSIC9hIDIzIDAgUiAvZSAyNCAwIFIKL2cgMjUgMCBSIC9pIDI2IDAgUiAvbCAyNyAwIFIgL20gMjggMCBSIC9uIDI5IDAgUiAvbyAzMCAwIFIgL29uZSAzMSAwIFIKL3AgMzIgMCBSIC9zIDMzIDAgUiAvc3BhY2UgMzQgMCBSIC90IDM1IDAgUiAvdSAzNiAwIFIgL3ggMzcgMCBSCi96ZXJvIDM4IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgL0RldmljZVJHQgovRGVjb2RlUGFybXMgPDwgL0NvbG9y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NzAgMDAwMDAgbiAKMDAwMDAwNzk5MSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDEgMDAwMDAgbiAKMDAwMDAwMDc0MSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3MjEgMDAwMDAgbiAKMDAwMDAwODAyMyAwMDAwMCBuIAowMDAwMDA2NTI0IDAwMDAwIG4gCjAwMDAwMDYzMjQgMDAwMDAgbiAKMDAwMDAwNTkxMSAwMDAwMCBuIAowMDAwMDA3NTc3IDAwMDAwIG4gCjAwMDAwMDA3NjEgMDAwMDAgbiAKMDAwMDAwMDkyNCAwMDAwMCBuIAowMDAwMDAxMjMyIDAwMDAwIG4gCjAwMDAwMDEzODAgMDAwMDAgbiAKMDAwMDAwMTUwMyAwMDAwMCBuIAowMDAwMDAxODA4IDAwMDAwIG4gCjAwMDAwMDIxODggMDAwMDAgbiAKMDAwMDAwMjUxMCAwMDAwMCBuIAowMDAwMDAyOTI0IDAwMDAwIG4gCjAwMDAwMDMwNjggMDAwMDAgbiAKMDAwMDAwMzE4NyAwMDAwMCBuIAowMDAwMDAzNTE4IDAwMDAwIG4gCjAwMDAwMDM3NTQgMDAwMDAgbiAKMDAwMDAwNDA0NSAwMDAwMCBuIAowMDAwMDA0MjAwIDAwMDAwIG4gCjAwMDAwMDQ1MTIgMDAwMDAgbiAKMDAwMDAwNDkxOSAwMDAwMCBuIAowMDAwMDA1MDA5IDAwMDAwIG4gCjAwMDAwMDUyMTUgMDAwMDAgbiAKMDAwMDAwNTQ2MiAwMDAwMCBuIAowMDAwMDA1NjIzIDAwMDAwIG4gCjAwMDAwMzc0MjMgMDAwMDAgbiAKMDAwMDAzNzUwNSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQwIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0MSA+PgpzdGFydHhyZWYKMzc2NjIKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:57:09.848870\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["NUM_IMAGES = 4\n", "images = [train_dataset[idx][0] for idx in range(NUM_IMAGES)]\n", "orig_images = [Image.fromarray(train_dataset.data[idx]) for idx in range(NUM_IMAGES)]\n", "orig_images = [test_transform(img) for img in orig_images]\n", "\n", "img_grid = torchvision.utils.make_grid(torch.stack(images + orig_images, dim=0), nrow=4, normalize=True, pad_value=0.5)\n", "img_grid = img_grid.permute(1, 2, 0)\n", "\n", "plt.figure(figsize=(8, 8))\n", "plt.title(\"Augmentation examples on CIFAR10\")\n", "plt.imshow(img_grid)\n", "plt.axis(\"off\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "1cf109b4", "metadata": {"papermill": {"duration": 0.136475, "end_time": "2021-12-04T15:57:10.205519", "exception": false, "start_time": "2021-12-04T15:57:10.069044", "status": "completed"}, "tags": []}, "source": ["## PyTorch Lightning\n", "\n", "In this notebook and in many following ones, we will make use of the library [PyTorch Lightning](https://www.pytorchlightning.ai/).\n", "PyTorch Lightning is a framework that simplifies your code needed to train, evaluate, and test a model in PyTorch.\n", "It also handles logging into [TensorBoard](https://pytorch.org/tutorials/intermediate/tensorboard_tutorial.html), a visualization toolkit for ML experiments, and saving model checkpoints automatically with minimal code overhead from our side.\n", "This is extremely helpful for us as we want to focus on implementing different model architectures and spend little time on other code overhead.\n", "Note that at the time of writing/teaching, the framework has been released in version 1.3.\n", "Future versions might have a slightly changed interface and thus might not work perfectly with the code (we will try to keep it up-to-date as much as possible).\n", "\n", "Now, we will take the first step in PyTorch Lightning, and continue to explore the framework in our other tutorials.\n", "PyTorch Lightning comes with a lot of useful functions, such as one for setting the seed as we have seen before:"]}, {"cell_type": "code", "execution_count": 9, "id": "9f8167cf", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:10.483015Z", "iopub.status.busy": "2021-12-04T15:57:10.482484Z", "iopub.status.idle": "2021-12-04T15:57:10.486516Z", "shell.execute_reply": "2021-12-04T15:57:10.486045Z"}, "papermill": {"duration": 0.143057, "end_time": "2021-12-04T15:57:10.486644", "exception": false, "start_time": "2021-12-04T15:57:10.343587", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"data": {"text/plain": ["42"]}, "execution_count": 9, "metadata": {}, "output_type": "execute_result"}], "source": ["# Setting the seed\n", "pl.seed_everything(42)"]}, {"cell_type": "markdown", "id": "b8164877", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.137277, "end_time": "2021-12-04T15:57:10.761911", "exception": false, "start_time": "2021-12-04T15:57:10.624634", "status": "completed"}, "tags": []}, "source": ["Thus, in the future, we don't have to define our own `set_seed` function anymore.\n", "\n", "In PyTorch Lightning, we define `pl.LightningModule`'s (inheriting from `torch.nn.Module`) that organize our code into 5 main sections:\n", "\n", "1. Initialization (`__init__`), where we create all necessary parameters/models\n", "2. Optimizers (`configure_optimizers`) where we create the optimizers, learning rate scheduler, etc.\n", "3.\n", "Training loop (`training_step`) where we only have to define the loss calculation for a single batch (the loop of optimizer.zero_grad(), loss.backward() and optimizer.step(), as well as any logging/saving operation, is done in the background)\n", "4.\n", "Validation loop (`validation_step`) where similarly to the training, we only have to define what should happen per step\n", "5. Test loop (`test_step`) which is the same as validation, only on a test set.\n", "\n", "Therefore, we don't abstract the PyTorch code, but rather organize it and define some default operations that are commonly used.\n", "If you need to change something else in your training/validation/test loop, there are many possible functions you can overwrite (see the [docs](https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html) for details).\n", "\n", "Now we can look at an example of how a Lightning Module for training a CNN looks like:"]}, {"cell_type": "code", "execution_count": 10, "id": "6fac8494", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:11.047878Z", "iopub.status.busy": "2021-12-04T15:57:11.039872Z", "iopub.status.idle": "2021-12-04T15:57:11.049883Z", "shell.execute_reply": "2021-12-04T15:57:11.049481Z"}, "papermill": {"duration": 0.14967, "end_time": "2021-12-04T15:57:11.049991", "exception": false, "start_time": "2021-12-04T15:57:10.900321", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class CIFARModule(pl.LightningModule):\n", " def __init__(self, model_name, model_hparams, optimizer_name, optimizer_hparams):\n", " \"\"\"\n", " Inputs:\n", " model_name - Name of the model/CNN to run. Used for creating the model (see function below)\n", " model_hparams - Hyperparameters for the model, as dictionary.\n", " optimizer_name - Name of the optimizer to use. Currently supported: Adam, SGD\n", " optimizer_hparams - Hyperparameters for the optimizer, as dictionary. This includes learning rate, weight decay, etc.\n", " \"\"\"\n", " super().__init__()\n", " # Exports the hyperparameters to a YAML file, and create \"self.hparams\" namespace\n", " self.save_hyperparameters()\n", " # Create model\n", " self.model = create_model(model_name, model_hparams)\n", " # Create loss module\n", " self.loss_module = nn.CrossEntropyLoss()\n", " # Example input for visualizing the graph in Tensorboard\n", " self.example_input_array = torch.zeros((1, 3, 32, 32), dtype=torch.float32)\n", "\n", " def forward(self, imgs):\n", " # Forward function that is run when visualizing the graph\n", " return self.model(imgs)\n", "\n", " def configure_optimizers(self):\n", " # We will support Adam or SGD as optimizers.\n", " if self.hparams.optimizer_name == \"Adam\":\n", " # AdamW is Adam with a correct implementation of weight decay (see here\n", " # for details: https://arxiv.org/pdf/1711.05101.pdf)\n", " optimizer = optim.AdamW(self.parameters(), **self.hparams.optimizer_hparams)\n", " elif self.hparams.optimizer_name == \"SGD\":\n", " optimizer = optim.SGD(self.parameters(), **self.hparams.optimizer_hparams)\n", " else:\n", " assert False, f'Unknown optimizer: \"{self.hparams.optimizer_name}\"'\n", "\n", " # We will reduce the learning rate by 0.1 after 100 and 150 epochs\n", " scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[100, 150], gamma=0.1)\n", " return [optimizer], [scheduler]\n", "\n", " def training_step(self, batch, batch_idx):\n", " # \"batch\" is the output of the training data loader.\n", " imgs, labels = batch\n", " preds = self.model(imgs)\n", " loss = self.loss_module(preds, labels)\n", " acc = (preds.argmax(dim=-1) == labels).float().mean()\n", "\n", " # Logs the accuracy per epoch to tensorboard (weighted average over batches)\n", " self.log(\"train_acc\", acc, on_step=False, on_epoch=True)\n", " self.log(\"train_loss\", loss)\n", " return loss # Return tensor to call \".backward\" on\n", "\n", " def validation_step(self, batch, batch_idx):\n", " imgs, labels = batch\n", " preds = self.model(imgs).argmax(dim=-1)\n", " acc = (labels == preds).float().mean()\n", " # By default logs it per epoch (weighted average over batches)\n", " self.log(\"val_acc\", acc)\n", "\n", " def test_step(self, batch, batch_idx):\n", " imgs, labels = batch\n", " preds = self.model(imgs).argmax(dim=-1)\n", " acc = (labels == preds).float().mean()\n", " # By default logs it per epoch (weighted average over batches), and returns it afterwards\n", " self.log(\"test_acc\", acc)"]}, {"cell_type": "markdown", "id": "41d60bc2", "metadata": {"papermill": {"duration": 0.137514, "end_time": "2021-12-04T15:57:11.325129", "exception": false, "start_time": "2021-12-04T15:57:11.187615", "status": "completed"}, "tags": []}, "source": ["We see that the code is organized and clear, which helps if someone else tries to understand your code.\n", "\n", "Another important part of PyTorch Lightning is the concept of callbacks.\n", "Callbacks are self-contained functions that contain the non-essential logic of your Lightning Module.\n", "They are usually called after finishing a training epoch, but can also influence other parts of your training loop.\n", "For instance, we will use the following two pre-defined callbacks: `LearningRateMonitor` and `ModelCheckpoint`.\n", "The learning rate monitor adds the current learning rate to our TensorBoard, which helps to verify that our learning rate scheduler works correctly.\n", "The model checkpoint callback allows you to customize the saving routine of your checkpoints.\n", "For instance, how many checkpoints to keep, when to save, which metric to look out for, etc.\n", "We import them below:"]}, {"cell_type": "code", "execution_count": 11, "id": "e1f870e3", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:11.605285Z", "iopub.status.busy": "2021-12-04T15:57:11.604819Z", "iopub.status.idle": "2021-12-04T15:57:11.606792Z", "shell.execute_reply": "2021-12-04T15:57:11.606380Z"}, "papermill": {"duration": 0.143447, "end_time": "2021-12-04T15:57:11.606903", "exception": false, "start_time": "2021-12-04T15:57:11.463456", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Callbacks"]}, {"cell_type": "markdown", "id": "b3894841", "metadata": {"papermill": {"duration": 0.137143, "end_time": "2021-12-04T15:57:11.881319", "exception": false, "start_time": "2021-12-04T15:57:11.744176", "status": "completed"}, "tags": []}, "source": ["To allow running multiple different models with the same Lightning module, we define a function below that maps a model name to the model class.\n", "At this stage, the dictionary `model_dict` is empty, but we will fill it throughout the notebook with our new models."]}, {"cell_type": "code", "execution_count": 12, "id": "df79310c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:12.170411Z", "iopub.status.busy": "2021-12-04T15:57:12.169937Z", "iopub.status.idle": "2021-12-04T15:57:12.171891Z", "shell.execute_reply": "2021-12-04T15:57:12.171423Z"}, "papermill": {"duration": 0.151484, "end_time": "2021-12-04T15:57:12.172000", "exception": false, "start_time": "2021-12-04T15:57:12.020516", "status": "completed"}, "tags": []}, "outputs": [], "source": ["model_dict = {}\n", "\n", "\n", "def create_model(model_name, model_hparams):\n", " if model_name in model_dict:\n", " return model_dict[model_name](**model_hparams)\n", " else:\n", " assert False, f'Unknown model name \"{model_name}\". Available models are: {str(model_dict.keys())}'"]}, {"cell_type": "markdown", "id": "c4ffa0dc", "metadata": {"papermill": {"duration": 0.140094, "end_time": "2021-12-04T15:57:12.450270", "exception": false, "start_time": "2021-12-04T15:57:12.310176", "status": "completed"}, "tags": []}, "source": ["Similarly, to use the activation function as another hyperparameter in\n", "our model, we define a \"name to function\" dict below:"]}, {"cell_type": "code", "execution_count": 13, "id": "75f7c5d8", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:12.729899Z", "iopub.status.busy": "2021-12-04T15:57:12.729422Z", "iopub.status.idle": "2021-12-04T15:57:12.731428Z", "shell.execute_reply": "2021-12-04T15:57:12.730960Z"}, "papermill": {"duration": 0.142657, "end_time": "2021-12-04T15:57:12.731536", "exception": false, "start_time": "2021-12-04T15:57:12.588879", "status": "completed"}, "tags": []}, "outputs": [], "source": ["act_fn_by_name = {\"tanh\": nn.Tanh, \"relu\": nn.ReLU, \"leakyrelu\": nn.LeakyReLU, \"gelu\": nn.GELU}"]}, {"cell_type": "markdown", "id": "9e4e507a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.137281, "end_time": "2021-12-04T15:57:13.005846", "exception": false, "start_time": "2021-12-04T15:57:12.868565", "status": "completed"}, "tags": []}, "source": ["If we pass the classes or objects directly as an argument to the Lightning module, we couldn't take advantage of PyTorch Lightning's automatically hyperparameter saving and loading.\n", "\n", "Besides the Lightning module, the second most important module in PyTorch Lightning is the `Trainer`.\n", "The trainer is responsible to execute the training steps defined in the Lightning module and completes the framework.\n", "Similar to the Lightning module, you can override any key part that you don't want to be automated, but the default settings are often the best practice to do.\n", "For a full overview, see the [documentation](https://pytorch-lightning.readthedocs.io/en/stable/common/trainer.html).\n", "The most important functions we use below are:\n", "\n", "* `trainer.fit`: Takes as input a lightning module, a training dataset, and an (optional) validation dataset.\n", "This function trains the given module on the training dataset with occasional validation (default once per epoch, can be changed)\n", "* `trainer.test`: Takes as input a model and a dataset on which we want to test.\n", "It returns the test metric on the dataset.\n", "\n", "For training and testing, we don't have to worry about things like setting the model to eval mode (`model.eval()`) as this is all done automatically.\n", "See below how we define a training function for our models:"]}, {"cell_type": "code", "execution_count": 14, "id": "70fa7f16", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:13.293251Z", "iopub.status.busy": "2021-12-04T15:57:13.292766Z", "iopub.status.idle": "2021-12-04T15:57:13.294781Z", "shell.execute_reply": "2021-12-04T15:57:13.294298Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.149211, "end_time": "2021-12-04T15:57:13.294889", "exception": false, "start_time": "2021-12-04T15:57:13.145678", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_model(model_name, save_name=None, **kwargs):\n", " \"\"\"\n", " Inputs:\n", " model_name - Name of the model you want to run. Is used to look up the class in \"model_dict\"\n", " save_name (optional) - If specified, this name will be used for creating the checkpoint and logging directory.\n", " \"\"\"\n", " if save_name is None:\n", " save_name = model_name\n", "\n", " # Create a PyTorch Lightning trainer with the generation callback\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, save_name), # Where to save models\n", " # We run on a single GPU (if possible)\n", " gpus=1 if str(device) == \"cuda:0\" else 0,\n", " # How many epochs to train for if no patience is set\n", " max_epochs=180,\n", " callbacks=[\n", " ModelCheckpoint(\n", " save_weights_only=True, mode=\"max\", monitor=\"val_acc\"\n", " ), # Save the best checkpoint based on the maximum val_acc recorded. Saves only weights and not optimizer\n", " LearningRateMonitor(\"epoch\"),\n", " ], # Log learning rate every epoch\n", " progress_bar_refresh_rate=1,\n", " ) # In case your notebook crashes due to the progress bar, consider increasing the refresh rate\n", " trainer.logger._log_graph = True # If True, we plot the computation graph in tensorboard\n", " trainer.logger._default_hp_metric = None # Optional logging argument that we don't need\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, save_name + \".ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(f\"Found pretrained model at {pretrained_filename}, loading...\")\n", " # Automatically loads the model with the saved hyperparameters\n", " model = CIFARModule.load_from_checkpoint(pretrained_filename)\n", " else:\n", " pl.seed_everything(42) # To be reproducable\n", " model = CIFARModule(model_name=model_name, **kwargs)\n", " trainer.fit(model, train_loader, val_loader)\n", " model = CIFARModule.load_from_checkpoint(\n", " trainer.checkpoint_callback.best_model_path\n", " ) # Load best checkpoint after training\n", "\n", " # Test best model on validation and test set\n", " val_result = trainer.test(model, test_dataloaders=val_loader, verbose=False)\n", " test_result = trainer.test(model, test_dataloaders=test_loader, verbose=False)\n", " result = {\"test\": test_result[0][\"test_acc\"], \"val\": val_result[0][\"test_acc\"]}\n", "\n", " return model, result"]}, {"cell_type": "markdown", "id": "3091fdb8", "metadata": {"papermill": {"duration": 0.139091, "end_time": "2021-12-04T15:57:13.571355", "exception": false, "start_time": "2021-12-04T15:57:13.432264", "status": "completed"}, "tags": []}, "source": ["Finally, we can focus on the Convolutional Neural Networks we want to\n", "implement today: GoogleNet, ResNet, and DenseNet."]}, {"cell_type": "markdown", "id": "13b5e914", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.137839, "end_time": "2021-12-04T15:57:13.846237", "exception": false, "start_time": "2021-12-04T15:57:13.708398", "status": "completed"}, "tags": []}, "source": ["## Inception\n", "\n", "
\n", "\n", "The [GoogleNet](https://arxiv.org/abs/1409.4842), proposed in 2014, won the ImageNet Challenge because of its usage of the Inception modules.\n", "In general, we will mainly focus on the concept of Inception in this tutorial instead of the specifics of the GoogleNet, as based on Inception, there have been many follow-up works ([Inception-v2](https://arxiv.org/abs/1512.00567), [Inception-v3](https://arxiv.org/abs/1512.00567), [Inception-v4](https://arxiv.org/abs/1602.07261), [Inception-ResNet](https://arxiv.org/abs/1602.07261),...).\n", "The follow-up works mainly focus on increasing efficiency and enabling very deep Inception networks.\n", "However, for a fundamental understanding, it is sufficient to look at the original Inception block.\n", "\n", "An Inception block applies four convolution blocks separately on the same feature map: a 1x1, 3x3, and 5x5 convolution, and a max pool operation.\n", "This allows the network to look at the same data with different receptive fields.\n", "Of course, learning only 5x5 convolution would be theoretically more powerful.\n", "However, this is not only more computation and memory heavy but also tends to overfit much easier.\n", "The overall inception block looks like below (figure credit - [Szegedy et al. ](https://arxiv.org/abs/1409.4842)):\n", "\n", "
\n", "\n", "The additional 1x1 convolutions before the 3x3 and 5x5 convolutions are used for dimensionality reduction.\n", "This is especially crucial as the feature maps of all branches are merged afterward, and we don't want any explosion of feature size.\n", "As 5x5 convolutions are 25 times more expensive than 1x1 convolutions, we can save a lot of computation and parameters by reducing the dimensionality before the large convolutions.\n", "\n", "We can now try to implement the Inception Block ourselves:"]}, {"cell_type": "code", "execution_count": 15, "id": "4fa882ae", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:14.130278Z", "iopub.status.busy": "2021-12-04T15:57:14.129749Z", "iopub.status.idle": "2021-12-04T15:57:14.132368Z", "shell.execute_reply": "2021-12-04T15:57:14.131969Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.148391, "end_time": "2021-12-04T15:57:14.132478", "exception": false, "start_time": "2021-12-04T15:57:13.984087", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class InceptionBlock(nn.Module):\n", " def __init__(self, c_in, c_red: dict, c_out: dict, act_fn):\n", " \"\"\"\n", " Inputs:\n", " c_in - Number of input feature maps from the previous layers\n", " c_red - Dictionary with keys \"3x3\" and \"5x5\" specifying the output of the dimensionality reducing 1x1 convolutions\n", " c_out - Dictionary with keys \"1x1\", \"3x3\", \"5x5\", and \"max\"\n", " act_fn - Activation class constructor (e.g. nn.ReLU)\n", " \"\"\"\n", " super().__init__()\n", "\n", " # 1x1 convolution branch\n", " self.conv_1x1 = nn.Sequential(\n", " nn.Conv2d(c_in, c_out[\"1x1\"], kernel_size=1), nn.BatchNorm2d(c_out[\"1x1\"]), act_fn()\n", " )\n", "\n", " # 3x3 convolution branch\n", " self.conv_3x3 = nn.Sequential(\n", " nn.Conv2d(c_in, c_red[\"3x3\"], kernel_size=1),\n", " nn.BatchNorm2d(c_red[\"3x3\"]),\n", " act_fn(),\n", " nn.Conv2d(c_red[\"3x3\"], c_out[\"3x3\"], kernel_size=3, padding=1),\n", " nn.BatchNorm2d(c_out[\"3x3\"]),\n", " act_fn(),\n", " )\n", "\n", " # 5x5 convolution branch\n", " self.conv_5x5 = nn.Sequential(\n", " nn.Conv2d(c_in, c_red[\"5x5\"], kernel_size=1),\n", " nn.BatchNorm2d(c_red[\"5x5\"]),\n", " act_fn(),\n", " nn.Conv2d(c_red[\"5x5\"], c_out[\"5x5\"], kernel_size=5, padding=2),\n", " nn.BatchNorm2d(c_out[\"5x5\"]),\n", " act_fn(),\n", " )\n", "\n", " # Max-pool branch\n", " self.max_pool = nn.Sequential(\n", " nn.MaxPool2d(kernel_size=3, padding=1, stride=1),\n", " nn.Conv2d(c_in, c_out[\"max\"], kernel_size=1),\n", " nn.BatchNorm2d(c_out[\"max\"]),\n", " act_fn(),\n", " )\n", "\n", " def forward(self, x):\n", " x_1x1 = self.conv_1x1(x)\n", " x_3x3 = self.conv_3x3(x)\n", " x_5x5 = self.conv_5x5(x)\n", " x_max = self.max_pool(x)\n", " x_out = torch.cat([x_1x1, x_3x3, x_5x5, x_max], dim=1)\n", " return x_out"]}, {"cell_type": "markdown", "id": "bffa7c2d", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.137965, "end_time": "2021-12-04T15:57:14.408272", "exception": false, "start_time": "2021-12-04T15:57:14.270307", "status": "completed"}, "tags": []}, "source": ["The GoogleNet architecture consists of stacking multiple Inception blocks with occasional max pooling to reduce the height and width of the feature maps.\n", "The original GoogleNet was designed for image sizes of ImageNet (224x224 pixels) and had almost 7 million parameters.\n", "As we train on CIFAR10 with image sizes of 32x32, we don't require such a heavy architecture, and instead, apply a reduced version.\n", "The number of channels for dimensionality reduction and output per filter (1x1, 3x3, 5x5, and max pooling) need to be manually specified and can be changed if interested.\n", "The general intuition is to have the most filters for the 3x3\n", "convolutions, as they are powerful enough to take the context into\n", "account while requiring almost a third of the parameters of the 5x5\n", "convolution."]}, {"cell_type": "code", "execution_count": 16, "id": "85cd0c39", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:14.698719Z", "iopub.status.busy": "2021-12-04T15:57:14.693514Z", "iopub.status.idle": "2021-12-04T15:57:14.700699Z", "shell.execute_reply": "2021-12-04T15:57:14.700227Z"}, "papermill": {"duration": 0.152836, "end_time": "2021-12-04T15:57:14.700807", "exception": false, "start_time": "2021-12-04T15:57:14.547971", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GoogleNet(nn.Module):\n", " def __init__(self, num_classes=10, act_fn_name=\"relu\", **kwargs):\n", " super().__init__()\n", " self.hparams = SimpleNamespace(\n", " num_classes=num_classes, act_fn_name=act_fn_name, act_fn=act_fn_by_name[act_fn_name]\n", " )\n", " self._create_network()\n", " self._init_params()\n", "\n", " def _create_network(self):\n", " # A first convolution on the original image to scale up the channel size\n", " self.input_net = nn.Sequential(\n", " nn.Conv2d(3, 64, kernel_size=3, padding=1), nn.BatchNorm2d(64), self.hparams.act_fn()\n", " )\n", " # Stacking inception blocks\n", " self.inception_blocks = nn.Sequential(\n", " InceptionBlock(\n", " 64,\n", " c_red={\"3x3\": 32, \"5x5\": 16},\n", " c_out={\"1x1\": 16, \"3x3\": 32, \"5x5\": 8, \"max\": 8},\n", " act_fn=self.hparams.act_fn,\n", " ),\n", " InceptionBlock(\n", " 64,\n", " c_red={\"3x3\": 32, \"5x5\": 16},\n", " c_out={\"1x1\": 24, \"3x3\": 48, \"5x5\": 12, \"max\": 12},\n", " act_fn=self.hparams.act_fn,\n", " ),\n", " nn.MaxPool2d(3, stride=2, padding=1), # 32x32 => 16x16\n", " InceptionBlock(\n", " 96,\n", " c_red={\"3x3\": 32, \"5x5\": 16},\n", " c_out={\"1x1\": 24, \"3x3\": 48, \"5x5\": 12, \"max\": 12},\n", " act_fn=self.hparams.act_fn,\n", " ),\n", " InceptionBlock(\n", " 96,\n", " c_red={\"3x3\": 32, \"5x5\": 16},\n", " c_out={\"1x1\": 16, \"3x3\": 48, \"5x5\": 16, \"max\": 16},\n", " act_fn=self.hparams.act_fn,\n", " ),\n", " InceptionBlock(\n", " 96,\n", " c_red={\"3x3\": 32, \"5x5\": 16},\n", " c_out={\"1x1\": 16, \"3x3\": 48, \"5x5\": 16, \"max\": 16},\n", " act_fn=self.hparams.act_fn,\n", " ),\n", " InceptionBlock(\n", " 96,\n", " c_red={\"3x3\": 32, \"5x5\": 16},\n", " c_out={\"1x1\": 32, \"3x3\": 48, \"5x5\": 24, \"max\": 24},\n", " act_fn=self.hparams.act_fn,\n", " ),\n", " nn.MaxPool2d(3, stride=2, padding=1), # 16x16 => 8x8\n", " InceptionBlock(\n", " 128,\n", " c_red={\"3x3\": 48, \"5x5\": 16},\n", " c_out={\"1x1\": 32, \"3x3\": 64, \"5x5\": 16, \"max\": 16},\n", " act_fn=self.hparams.act_fn,\n", " ),\n", " InceptionBlock(\n", " 128,\n", " c_red={\"3x3\": 48, \"5x5\": 16},\n", " c_out={\"1x1\": 32, \"3x3\": 64, \"5x5\": 16, \"max\": 16},\n", " act_fn=self.hparams.act_fn,\n", " ),\n", " )\n", " # Mapping to classification output\n", " self.output_net = nn.Sequential(\n", " nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(128, self.hparams.num_classes)\n", " )\n", "\n", " def _init_params(self):\n", " # Based on our discussion in Tutorial 4, we should initialize the\n", " # convolutions according to the activation function\n", " for m in self.modules():\n", " if isinstance(m, nn.Conv2d):\n", " nn.init.kaiming_normal_(m.weight, nonlinearity=self.hparams.act_fn_name)\n", " elif isinstance(m, nn.BatchNorm2d):\n", " nn.init.constant_(m.weight, 1)\n", " nn.init.constant_(m.bias, 0)\n", "\n", " def forward(self, x):\n", " x = self.input_net(x)\n", " x = self.inception_blocks(x)\n", " x = self.output_net(x)\n", " return x"]}, {"cell_type": "markdown", "id": "5a8f15ba", "metadata": {"papermill": {"duration": 0.138296, "end_time": "2021-12-04T15:57:14.978565", "exception": false, "start_time": "2021-12-04T15:57:14.840269", "status": "completed"}, "tags": []}, "source": ["Now, we can integrate our model to the model dictionary we defined above:"]}, {"cell_type": "code", "execution_count": 17, "id": "0d93d35b", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:15.257555Z", "iopub.status.busy": "2021-12-04T15:57:15.257085Z", "iopub.status.idle": "2021-12-04T15:57:15.259035Z", "shell.execute_reply": "2021-12-04T15:57:15.258543Z"}, "papermill": {"duration": 0.143062, "end_time": "2021-12-04T15:57:15.259141", "exception": false, "start_time": "2021-12-04T15:57:15.116079", "status": "completed"}, "tags": []}, "outputs": [], "source": ["model_dict[\"GoogleNet\"] = GoogleNet"]}, {"cell_type": "markdown", "id": "d57cb4bb", "metadata": {"papermill": {"duration": 0.138614, "end_time": "2021-12-04T15:57:15.536507", "exception": false, "start_time": "2021-12-04T15:57:15.397893", "status": "completed"}, "tags": []}, "source": ["The training of the model is handled by PyTorch Lightning, and we just have to define the command to start.\n", "Note that we train for almost 200 epochs, which takes about an hour on Lisa's default GPUs (GTX1080Ti).\n", "We would recommend using the saved models and train your own model if you are interested."]}, {"cell_type": "code", "execution_count": 18, "id": "0a7d2c3d", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:15.822049Z", "iopub.status.busy": "2021-12-04T15:57:15.821573Z", "iopub.status.idle": "2021-12-04T15:57:20.480077Z", "shell.execute_reply": "2021-12-04T15:57:20.480477Z"}, "papermill": {"duration": 4.805519, "end_time": "2021-12-04T15:57:20.480642", "exception": false, "start_time": "2021-12-04T15:57:15.675123", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=1)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:901: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torch/_jit_internal.py:603: LightningDeprecationWarning: The `LightningModule.loaded_optimizer_states_dict` property is deprecated in v1.4 and will be removed in v1.6.\n", " if hasattr(mod, name):\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/ConvNets/GoogleNet.ckpt, loading...\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "d4a54ef3af5d4bcaafad8c61c3360423", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "88b1b202de814778ae51007742b5610c", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["googlenet_model, googlenet_results = train_model(\n", " model_name=\"GoogleNet\",\n", " model_hparams={\"num_classes\": 10, \"act_fn_name\": \"relu\"},\n", " optimizer_name=\"Adam\",\n", " optimizer_hparams={\"lr\": 1e-3, \"weight_decay\": 1e-4},\n", ")"]}, {"cell_type": "markdown", "id": "580ed6ce", "metadata": {"papermill": {"duration": 0.145507, "end_time": "2021-12-04T15:57:20.772152", "exception": false, "start_time": "2021-12-04T15:57:20.626645", "status": "completed"}, "tags": []}, "source": ["We will compare the results later in the notebooks, but we can already print them here for a first glance:"]}, {"cell_type": "code", "execution_count": 19, "id": "00f40b04", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:21.066499Z", "iopub.status.busy": "2021-12-04T15:57:21.066033Z", "iopub.status.idle": "2021-12-04T15:57:21.068511Z", "shell.execute_reply": "2021-12-04T15:57:21.068131Z"}, "papermill": {"duration": 0.151434, "end_time": "2021-12-04T15:57:21.068618", "exception": false, "start_time": "2021-12-04T15:57:20.917184", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["GoogleNet Results {'test': 0.8970000147819519, 'val': 0.9039999842643738}\n"]}], "source": ["print(\"GoogleNet Results\", googlenet_results)"]}, {"cell_type": "markdown", "id": "c1e798a1", "metadata": {"papermill": {"duration": 0.15013, "end_time": "2021-12-04T15:57:21.364739", "exception": false, "start_time": "2021-12-04T15:57:21.214609", "status": "completed"}, "tags": []}, "source": ["### Tensorboard log\n", "\n", "A nice extra of PyTorch Lightning is the automatic logging into TensorBoard.\n", "To give you a better intuition of what TensorBoard can be used, we can look at the board that PyTorch Lightning has been generated when training the GoogleNet.\n", "TensorBoard provides an inline functionality for Jupyter notebooks, and we use it here:"]}, {"cell_type": "code", "execution_count": 20, "id": "636c7e8a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:21.662240Z", "iopub.status.busy": "2021-12-04T15:57:21.661769Z", "iopub.status.idle": "2021-12-04T15:57:21.665743Z", "shell.execute_reply": "2021-12-04T15:57:21.665337Z"}, "papermill": {"duration": 0.154976, "end_time": "2021-12-04T15:57:21.665854", "exception": false, "start_time": "2021-12-04T15:57:21.510878", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Import tensorboard\n", "%load_ext tensorboard"]}, {"cell_type": "code", "execution_count": 21, "id": "172e6d41", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:21.964674Z", "iopub.status.busy": "2021-12-04T15:57:21.964207Z", "iopub.status.idle": "2021-12-04T15:57:23.517538Z", "shell.execute_reply": "2021-12-04T15:57:23.517924Z"}, "papermill": {"duration": 1.704031, "end_time": "2021-12-04T15:57:23.518088", "exception": false, "start_time": "2021-12-04T15:57:21.814057", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", " \n", " \n", " "], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH!\n", "%tensorboard --logdir ../saved_models/tutorial5/tensorboards/GoogleNet/"]}, {"cell_type": "markdown", "id": "12cc6a1a", "metadata": {"papermill": {"duration": 0.152112, "end_time": "2021-12-04T15:57:23.821918", "exception": false, "start_time": "2021-12-04T15:57:23.669806", "status": "completed"}, "tags": []}, "source": ["
\n", "\n", "TensorBoard is organized in multiple tabs.\n", "The main tab is the scalar tab where we can log the development of single numbers.\n", "For example, we have plotted the training loss, accuracy, learning rate, etc.\n", "If we look at the training or validation accuracy, we can really see the impact of using a learning rate scheduler.\n", "Reducing the learning rate gives our model a nice increase in training performance.\n", "Similarly, when looking at the training loss, we see a sudden decrease at this point.\n", "However, the high numbers on the training set compared to validation indicate that our model was overfitting which is inevitable for such large networks.\n", "\n", "Another interesting tab in TensorBoard is the graph tab.\n", "It shows us the network architecture organized by building blocks from the input to the output.\n", "It basically shows the operations taken in the forward step of `CIFARModule`.\n", "Double-click on a module to open it.\n", "Feel free to explore the architecture from a different perspective.\n", "The graph visualization can often help you to validate that your model\n", "is actually doing what it is supposed to do, and you don't miss any\n", "layers in the computation graph."]}, {"cell_type": "markdown", "id": "9836e715", "metadata": {"papermill": {"duration": 0.149221, "end_time": "2021-12-04T15:57:24.120321", "exception": false, "start_time": "2021-12-04T15:57:23.971100", "status": "completed"}, "tags": []}, "source": ["## ResNet\n", "\n", "The [ResNet](https://arxiv.org/abs/1512.03385) paper is one of the [most cited AI papers](https://www.natureindex.com/news-blog/google-scholar-reveals-most-influential-papers-research-citations-twenty-twenty), and has been the foundation for neural networks with more than 1,000 layers.\n", "Despite its simplicity, the idea of residual connections is highly effective as it supports stable gradient propagation through the network.\n", "Instead of modeling $x_{l+1}=F(x_{l})$, we model $x_{l+1}=x_{l}+F(x_{l})$ where $F$ is a non-linear mapping (usually a sequence of NN modules likes convolutions, activation functions, and normalizations).\n", "If we do backpropagation on such residual connections, we obtain:\n", "\n", "$$\\frac{\\partial x_{l+1}}{\\partial x_{l}} = \\mathbf{I} + \\frac{\\partial F(x_{l})}{\\partial x_{l}}$$\n", "\n", "The bias towards the identity matrix guarantees a stable gradient propagation being less effected by $F$ itself.\n", "There have been many variants of ResNet proposed, which mostly concern the function $F$, or operations applied on the sum.\n", "In this tutorial, we look at two of them: the original ResNet block, and the [Pre-Activation ResNet block](https://arxiv.org/abs/1603.05027).\n", "We visually compare the blocks below (figure credit - [He et al. ](https://arxiv.org/abs/1603.05027)):\n", "\n", "
\n", "\n", "The original ResNet block applies a non-linear activation function, usually ReLU, after the skip connection.\n", "In contrast, the pre-activation ResNet block applies the non-linearity at the beginning of $F$.\n", "Both have their advantages and disadvantages.\n", "For very deep network, however, the pre-activation ResNet has shown to perform better as the gradient flow is guaranteed to have the identity matrix as calculated above, and is not harmed by any non-linear activation applied to it.\n", "For comparison, in this notebook, we implement both ResNet types as shallow networks.\n", "\n", "Let's start with the original ResNet block.\n", "The visualization above already shows what layers are included in $F$.\n", "One special case we have to handle is when we want to reduce the image dimensions in terms of width and height.\n", "The basic ResNet block requires $F(x_{l})$ to be of the same shape as $x_{l}$.\n", "Thus, we need to change the dimensionality of $x_{l}$ as well before adding to $F(x_{l})$.\n", "The original implementation used an identity mapping with stride 2 and padded additional feature dimensions with 0.\n", "However, the more common implementation is to use a 1x1 convolution with stride 2 as it allows us to change the feature dimensionality while being efficient in parameter and computation cost.\n", "The code for the ResNet block is relatively simple, and shown below:"]}, {"cell_type": "code", "execution_count": 22, "id": "9a388e6a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:24.427355Z", "iopub.status.busy": "2021-12-04T15:57:24.426861Z", "iopub.status.idle": "2021-12-04T15:57:24.428757Z", "shell.execute_reply": "2021-12-04T15:57:24.428366Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.16061, "end_time": "2021-12-04T15:57:24.428877", "exception": false, "start_time": "2021-12-04T15:57:24.268267", "status": "completed"}, "tags": []}, "outputs": [], "source": ["\n", "\n", "class ResNetBlock(nn.Module):\n", " def __init__(self, c_in, act_fn, subsample=False, c_out=-1):\n", " \"\"\"\n", " Inputs:\n", " c_in - Number of input features\n", " act_fn - Activation class constructor (e.g. nn.ReLU)\n", " subsample - If True, we want to apply a stride inside the block and reduce the output shape by 2 in height and width\n", " c_out - Number of output features. Note that this is only relevant if subsample is True, as otherwise, c_out = c_in\n", " \"\"\"\n", " super().__init__()\n", " if not subsample:\n", " c_out = c_in\n", "\n", " # Network representing F\n", " self.net = nn.Sequential(\n", " nn.Conv2d(\n", " c_in, c_out, kernel_size=3, padding=1, stride=1 if not subsample else 2, bias=False\n", " ), # No bias needed as the Batch Norm handles it\n", " nn.BatchNorm2d(c_out),\n", " act_fn(),\n", " nn.Conv2d(c_out, c_out, kernel_size=3, padding=1, bias=False),\n", " nn.BatchNorm2d(c_out),\n", " )\n", "\n", " # 1x1 convolution with stride 2 means we take the upper left value, and transform it to new output size\n", " self.downsample = nn.Conv2d(c_in, c_out, kernel_size=1, stride=2) if subsample else None\n", " self.act_fn = act_fn()\n", "\n", " def forward(self, x):\n", " z = self.net(x)\n", " if self.downsample is not None:\n", " x = self.downsample(x)\n", " out = z + x\n", " out = self.act_fn(out)\n", " return out"]}, {"cell_type": "markdown", "id": "5935648f", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.153709, "end_time": "2021-12-04T15:57:24.734386", "exception": false, "start_time": "2021-12-04T15:57:24.580677", "status": "completed"}, "tags": []}, "source": ["The second block we implement is the pre-activation ResNet block.\n", "For this, we have to change the order of layer in `self.net`, and do not apply an activation function on the output.\n", "Additionally, the downsampling operation has to apply a non-linearity as well as the input, $x_l$, has not been processed by a non-linearity yet.\n", "Hence, the block looks as follows:"]}, {"cell_type": "code", "execution_count": 23, "id": "d3772982", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:25.038147Z", "iopub.status.busy": "2021-12-04T15:57:25.037663Z", "iopub.status.idle": "2021-12-04T15:57:25.039227Z", "shell.execute_reply": "2021-12-04T15:57:25.039608Z"}, "papermill": {"duration": 0.15651, "end_time": "2021-12-04T15:57:25.039740", "exception": false, "start_time": "2021-12-04T15:57:24.883230", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class PreActResNetBlock(nn.Module):\n", " def __init__(self, c_in, act_fn, subsample=False, c_out=-1):\n", " \"\"\"\n", " Inputs:\n", " c_in - Number of input features\n", " act_fn - Activation class constructor (e.g. nn.ReLU)\n", " subsample - If True, we want to apply a stride inside the block and reduce the output shape by 2 in height and width\n", " c_out - Number of output features. Note that this is only relevant if subsample is True, as otherwise, c_out = c_in\n", " \"\"\"\n", " super().__init__()\n", " if not subsample:\n", " c_out = c_in\n", "\n", " # Network representing F\n", " self.net = nn.Sequential(\n", " nn.BatchNorm2d(c_in),\n", " act_fn(),\n", " nn.Conv2d(c_in, c_out, kernel_size=3, padding=1, stride=1 if not subsample else 2, bias=False),\n", " nn.BatchNorm2d(c_out),\n", " act_fn(),\n", " nn.Conv2d(c_out, c_out, kernel_size=3, padding=1, bias=False),\n", " )\n", "\n", " # 1x1 convolution needs to apply non-linearity as well as not done on skip connection\n", " self.downsample = (\n", " nn.Sequential(nn.BatchNorm2d(c_in), act_fn(), nn.Conv2d(c_in, c_out, kernel_size=1, stride=2, bias=False))\n", " if subsample\n", " else None\n", " )\n", "\n", " def forward(self, x):\n", " z = self.net(x)\n", " if self.downsample is not None:\n", " x = self.downsample(x)\n", " out = z + x\n", " return out"]}, {"cell_type": "markdown", "id": "ca3b8840", "metadata": {"papermill": {"duration": 0.154836, "end_time": "2021-12-04T15:57:25.343780", "exception": false, "start_time": "2021-12-04T15:57:25.188944", "status": "completed"}, "tags": []}, "source": ["Similarly to the model selection, we define a dictionary to create a mapping from string to block class.\n", "We will use the string name as hyperparameter value in our model to choose between the ResNet blocks.\n", "Feel free to implement any other ResNet block type and add it here as well."]}, {"cell_type": "code", "execution_count": 24, "id": "c456c8ca", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:25.653668Z", "iopub.status.busy": "2021-12-04T15:57:25.653194Z", "iopub.status.idle": "2021-12-04T15:57:25.654948Z", "shell.execute_reply": "2021-12-04T15:57:25.654524Z"}, "papermill": {"duration": 0.157547, "end_time": "2021-12-04T15:57:25.655397", "exception": false, "start_time": "2021-12-04T15:57:25.497850", "status": "completed"}, "tags": []}, "outputs": [], "source": ["resnet_blocks_by_name = {\"ResNetBlock\": ResNetBlock, \"PreActResNetBlock\": PreActResNetBlock}"]}, {"cell_type": "markdown", "id": "c37cfa5a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.149278, "end_time": "2021-12-04T15:57:25.960549", "exception": false, "start_time": "2021-12-04T15:57:25.811271", "status": "completed"}, "tags": []}, "source": ["The overall ResNet architecture consists of stacking multiple ResNet blocks, of which some are downsampling the input.\n", "When talking about ResNet blocks in the whole network, we usually group them by the same output shape.\n", "Hence, if we say the ResNet has `[3,3,3]` blocks, it means that we have 3 times a group of 3 ResNet blocks, where a subsampling is taking place in the fourth and seventh block.\n", "The ResNet with `[3,3,3]` blocks on CIFAR10 is visualized below.\n", "\n", "
\n", "\n", "The three groups operate on the resolutions $32\\times32$, $16\\times16$ and $8\\times8$ respectively.\n", "The blocks in orange denote ResNet blocks with downsampling.\n", "The same notation is used by many other implementations such as in the [torchvision library](https://pytorch.org/vision/0.11/models.html#torchvision.models.resnet18) from PyTorch.\n", "Thus, our code looks as follows:"]}, {"cell_type": "code", "execution_count": 25, "id": "8c994805", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:26.274254Z", "iopub.status.busy": "2021-12-04T15:57:26.273764Z", "iopub.status.idle": "2021-12-04T15:57:26.275799Z", "shell.execute_reply": "2021-12-04T15:57:26.275419Z"}, "papermill": {"duration": 0.162052, "end_time": "2021-12-04T15:57:26.275911", "exception": false, "start_time": "2021-12-04T15:57:26.113859", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ResNet(nn.Module):\n", " def __init__(\n", " self,\n", " num_classes=10,\n", " num_blocks=[3, 3, 3],\n", " c_hidden=[16, 32, 64],\n", " act_fn_name=\"relu\",\n", " block_name=\"ResNetBlock\",\n", " **kwargs,\n", " ):\n", " \"\"\"\n", " Inputs:\n", " num_classes - Number of classification outputs (10 for CIFAR10)\n", " num_blocks - List with the number of ResNet blocks to use. The first block of each group uses downsampling, except the first.\n", " c_hidden - List with the hidden dimensionalities in the different blocks. Usually multiplied by 2 the deeper we go.\n", " act_fn_name - Name of the activation function to use, looked up in \"act_fn_by_name\"\n", " block_name - Name of the ResNet block, looked up in \"resnet_blocks_by_name\"\n", " \"\"\"\n", " super().__init__()\n", " assert block_name in resnet_blocks_by_name\n", " self.hparams = SimpleNamespace(\n", " num_classes=num_classes,\n", " c_hidden=c_hidden,\n", " num_blocks=num_blocks,\n", " act_fn_name=act_fn_name,\n", " act_fn=act_fn_by_name[act_fn_name],\n", " block_class=resnet_blocks_by_name[block_name],\n", " )\n", " self._create_network()\n", " self._init_params()\n", "\n", " def _create_network(self):\n", " c_hidden = self.hparams.c_hidden\n", "\n", " # A first convolution on the original image to scale up the channel size\n", " if self.hparams.block_class == PreActResNetBlock: # => Don't apply non-linearity on output\n", " self.input_net = nn.Sequential(nn.Conv2d(3, c_hidden[0], kernel_size=3, padding=1, bias=False))\n", " else:\n", " self.input_net = nn.Sequential(\n", " nn.Conv2d(3, c_hidden[0], kernel_size=3, padding=1, bias=False),\n", " nn.BatchNorm2d(c_hidden[0]),\n", " self.hparams.act_fn(),\n", " )\n", "\n", " # Creating the ResNet blocks\n", " blocks = []\n", " for block_idx, block_count in enumerate(self.hparams.num_blocks):\n", " for bc in range(block_count):\n", " # Subsample the first block of each group, except the very first one.\n", " subsample = bc == 0 and block_idx > 0\n", " blocks.append(\n", " self.hparams.block_class(\n", " c_in=c_hidden[block_idx if not subsample else (block_idx - 1)],\n", " act_fn=self.hparams.act_fn,\n", " subsample=subsample,\n", " c_out=c_hidden[block_idx],\n", " )\n", " )\n", " self.blocks = nn.Sequential(*blocks)\n", "\n", " # Mapping to classification output\n", " self.output_net = nn.Sequential(\n", " nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(c_hidden[-1], self.hparams.num_classes)\n", " )\n", "\n", " def _init_params(self):\n", " # Based on our discussion in Tutorial 4, we should initialize the convolutions according to the activation function\n", " # Fan-out focuses on the gradient distribution, and is commonly used in ResNets\n", " for m in self.modules():\n", " if isinstance(m, nn.Conv2d):\n", " nn.init.kaiming_normal_(m.weight, mode=\"fan_out\", nonlinearity=self.hparams.act_fn_name)\n", " elif isinstance(m, nn.BatchNorm2d):\n", " nn.init.constant_(m.weight, 1)\n", " nn.init.constant_(m.bias, 0)\n", "\n", " def forward(self, x):\n", " x = self.input_net(x)\n", " x = self.blocks(x)\n", " x = self.output_net(x)\n", " return x"]}, {"cell_type": "markdown", "id": "37b8bdf0", "metadata": {"papermill": {"duration": 0.149083, "end_time": "2021-12-04T15:57:26.573227", "exception": false, "start_time": "2021-12-04T15:57:26.424144", "status": "completed"}, "tags": []}, "source": ["We also need to add the new ResNet class to our model dictionary:"]}, {"cell_type": "code", "execution_count": 26, "id": "468df0ab", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:26.873446Z", "iopub.status.busy": "2021-12-04T15:57:26.872978Z", "iopub.status.idle": "2021-12-04T15:57:26.874996Z", "shell.execute_reply": "2021-12-04T15:57:26.874511Z"}, "papermill": {"duration": 0.153475, "end_time": "2021-12-04T15:57:26.875104", "exception": false, "start_time": "2021-12-04T15:57:26.721629", "status": "completed"}, "tags": []}, "outputs": [], "source": ["model_dict[\"ResNet\"] = ResNet"]}, {"cell_type": "markdown", "id": "b23a2b2b", "metadata": {"papermill": {"duration": 0.151943, "end_time": "2021-12-04T15:57:27.179422", "exception": false, "start_time": "2021-12-04T15:57:27.027479", "status": "completed"}, "tags": []}, "source": ["Finally, we can train our ResNet models.\n", "One difference to the GoogleNet training is that we explicitly use SGD with Momentum as optimizer instead of Adam.\n", "Adam often leads to a slightly worse accuracy on plain, shallow ResNets.\n", "It is not 100% clear why Adam performs worse in this context, but one possible explanation is related to ResNet's loss surface.\n", "ResNet has been shown to produce smoother loss surfaces than networks without skip connection (see [Li et al., 2018](https://arxiv.org/pdf/1712.09913.pdf) for details).\n", "A possible visualization of the loss surface with/out skip connections is below (figure credit - [Li et al. ](https://arxiv.org/pdf/1712.09913.pdf)):\n", "\n", "
\n", "\n", "The $x$ and $y$ axis shows a projection of the parameter space, and the $z$ axis shows the loss values achieved by different parameter values.\n", "On smooth surfaces like the one on the right, we might not require an adaptive learning rate as Adam provides.\n", "Instead, Adam can get stuck in local optima while SGD finds the wider minima that tend to generalize better.\n", "However, to answer this question in detail, we would need an extra tutorial because it is not easy to answer.\n", "For now, we conclude: for ResNet architectures, consider the optimizer to be an important hyperparameter, and try training with both Adam and SGD.\n", "Let's train the model below with SGD:"]}, {"cell_type": "code", "execution_count": 27, "id": "73edd595", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:27.485930Z", "iopub.status.busy": "2021-12-04T15:57:27.485459Z", "iopub.status.idle": "2021-12-04T15:57:29.804705Z", "shell.execute_reply": "2021-12-04T15:57:29.804239Z"}, "papermill": {"duration": 2.475739, "end_time": "2021-12-04T15:57:29.804902", "exception": false, "start_time": "2021-12-04T15:57:27.329163", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=1)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:901: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/ConvNets/ResNet.ckpt, loading...\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b92f8c8a741b439084618bba61742bb0", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "6000f7a133b44b9b96fd7c1bf554a644", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["resnet_model, resnet_results = train_model(\n", " model_name=\"ResNet\",\n", " model_hparams={\"num_classes\": 10, \"c_hidden\": [16, 32, 64], \"num_blocks\": [3, 3, 3], \"act_fn_name\": \"relu\"},\n", " optimizer_name=\"SGD\",\n", " optimizer_hparams={\"lr\": 0.1, \"momentum\": 0.9, \"weight_decay\": 1e-4},\n", ")"]}, {"cell_type": "markdown", "id": "48da8881", "metadata": {"papermill": {"duration": 0.159561, "end_time": "2021-12-04T15:57:30.121598", "exception": false, "start_time": "2021-12-04T15:57:29.962037", "status": "completed"}, "tags": []}, "source": ["Let's also train the pre-activation ResNet as comparison:"]}, {"cell_type": "code", "execution_count": 28, "id": "2a7deb12", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:30.440774Z", "iopub.status.busy": "2021-12-04T15:57:30.440297Z", "iopub.status.idle": "2021-12-04T15:57:32.785696Z", "shell.execute_reply": "2021-12-04T15:57:32.786096Z"}, "papermill": {"duration": 2.508623, "end_time": "2021-12-04T15:57:32.786264", "exception": false, "start_time": "2021-12-04T15:57:30.277641", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/ConvNets/ResNetPreAct.ckpt, loading...\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "e84f1647ff4244d5992786cbc17d3e1b", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "4542daedea6845f9b0e7df869e150283", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["resnetpreact_model, resnetpreact_results = train_model(\n", " model_name=\"ResNet\",\n", " model_hparams={\n", " \"num_classes\": 10,\n", " \"c_hidden\": [16, 32, 64],\n", " \"num_blocks\": [3, 3, 3],\n", " \"act_fn_name\": \"relu\",\n", " \"block_name\": \"PreActResNetBlock\",\n", " },\n", " optimizer_name=\"SGD\",\n", " optimizer_hparams={\"lr\": 0.1, \"momentum\": 0.9, \"weight_decay\": 1e-4},\n", " save_name=\"ResNetPreAct\",\n", ")"]}, {"cell_type": "markdown", "id": "9753f382", "metadata": {"papermill": {"duration": 0.166613, "end_time": "2021-12-04T15:57:33.120458", "exception": false, "start_time": "2021-12-04T15:57:32.953845", "status": "completed"}, "tags": []}, "source": ["### Tensorboard log\n", "\n", "Similarly to our GoogleNet model, we also have a TensorBoard log for the ResNet model. We can open it below."]}, {"cell_type": "code", "execution_count": 29, "id": "0880db27", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:33.458069Z", "iopub.status.busy": "2021-12-04T15:57:33.457596Z", "iopub.status.idle": "2021-12-04T15:57:35.014300Z", "shell.execute_reply": "2021-12-04T15:57:35.014709Z"}, "papermill": {"duration": 1.728728, "end_time": "2021-12-04T15:57:35.014873", "exception": false, "start_time": "2021-12-04T15:57:33.286145", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", " \n", " \n", " "], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH! Feel free to change \"ResNet\" to \"ResNetPreAct\"\n", "%tensorboard --logdir ../saved_models/tutorial5/tensorboards/ResNet/"]}, {"cell_type": "markdown", "id": "99d58ffe", "metadata": {"papermill": {"duration": 0.167371, "end_time": "2021-12-04T15:57:35.347711", "exception": false, "start_time": "2021-12-04T15:57:35.180340", "status": "completed"}, "tags": []}, "source": ["
\n", "\n", "Feel free to explore the TensorBoard yourself, including the computation graph.\n", "In general, we can see that with SGD, the ResNet has a higher training loss than the GoogleNet in the first stage of the training.\n", "After reducing the learning rate however, the model achieves even higher validation accuracies.\n", "We compare the precise scores at the end of the notebook."]}, {"cell_type": "markdown", "id": "d632a3fd", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.165036, "end_time": "2021-12-04T15:57:35.677544", "exception": false, "start_time": "2021-12-04T15:57:35.512508", "status": "completed"}, "tags": []}, "source": ["## DenseNet\n", "\n", "
\n", "\n", "[DenseNet](https://arxiv.org/abs/1608.06993) is another architecture for enabling very deep neural networks and takes a slightly different perspective on residual connections.\n", "Instead of modeling the difference between layers, DenseNet considers residual connections as a possible way to reuse features across layers, removing any necessity to learn redundant feature maps.\n", "If we go deeper into the network, the model learns abstract features to recognize patterns.\n", "However, some complex patterns consist of a combination of abstract features (e.g. hand, face, etc.\n", "), and low-level features (e.g. edges, basic color, etc.).\n", "To find these low-level features in the deep layers, standard CNNs have to learn copy such feature maps, which wastes a lot of parameter complexity.\n", "DenseNet provides an efficient way of reusing features by having each convolution depends on all previous input features, but add only a small amount of filters to it.\n", "See the figure below for an illustration (figure credit - [Hu et al. ](https://arxiv.org/abs/1608.06993)):\n", "\n", "
\n", "\n", "The last layer, called the transition layer, is responsible for reducing the dimensionality of the feature maps in height, width, and channel size.\n", "Although those technically break the identity backpropagation, there are only a few in a network so that it doesn't affect the gradient flow much.\n", "\n", "We split the implementation of the layers in DenseNet into three parts: a `DenseLayer`, and a `DenseBlock`, and a `TransitionLayer`.\n", "The module `DenseLayer` implements a single layer inside a dense block.\n", "It applies a 1x1 convolution for dimensionality reduction with a subsequential 3x3 convolution.\n", "The output channels are concatenated to the originals and returned.\n", "Note that we apply the Batch Normalization as the first layer of each block.\n", "This allows slightly different activations for the same features to different layers, depending on what is needed.\n", "Overall, we can implement it as follows:"]}, {"cell_type": "code", "execution_count": 30, "id": "a46957dd", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:36.017059Z", "iopub.status.busy": "2021-12-04T15:57:36.016566Z", "iopub.status.idle": "2021-12-04T15:57:36.018483Z", "shell.execute_reply": "2021-12-04T15:57:36.018007Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.173649, "end_time": "2021-12-04T15:57:36.018596", "exception": false, "start_time": "2021-12-04T15:57:35.844947", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class DenseLayer(nn.Module):\n", " def __init__(self, c_in, bn_size, growth_rate, act_fn):\n", " \"\"\"\n", " Inputs:\n", " c_in - Number of input channels\n", " bn_size - Bottleneck size (factor of growth rate) for the output of the 1x1 convolution. Typically between 2 and 4.\n", " growth_rate - Number of output channels of the 3x3 convolution\n", " act_fn - Activation class constructor (e.g. nn.ReLU)\n", " \"\"\"\n", " super().__init__()\n", " self.net = nn.Sequential(\n", " nn.BatchNorm2d(c_in),\n", " act_fn(),\n", " nn.Conv2d(c_in, bn_size * growth_rate, kernel_size=1, bias=False),\n", " nn.BatchNorm2d(bn_size * growth_rate),\n", " act_fn(),\n", " nn.Conv2d(bn_size * growth_rate, growth_rate, kernel_size=3, padding=1, bias=False),\n", " )\n", "\n", " def forward(self, x):\n", " out = self.net(x)\n", " out = torch.cat([out, x], dim=1)\n", " return out"]}, {"cell_type": "markdown", "id": "22c3bb8d", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.166194, "end_time": "2021-12-04T15:57:36.351919", "exception": false, "start_time": "2021-12-04T15:57:36.185725", "status": "completed"}, "tags": []}, "source": ["The module `DenseBlock` summarizes multiple dense layers applied in sequence.\n", "Each dense layer takes as input the original input concatenated with all previous layers' feature maps:"]}, {"cell_type": "code", "execution_count": 31, "id": "e2b77d5c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:36.689854Z", "iopub.status.busy": "2021-12-04T15:57:36.689367Z", "iopub.status.idle": "2021-12-04T15:57:36.691378Z", "shell.execute_reply": "2021-12-04T15:57:36.690906Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.173592, "end_time": "2021-12-04T15:57:36.691485", "exception": false, "start_time": "2021-12-04T15:57:36.517893", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class DenseBlock(nn.Module):\n", " def __init__(self, c_in, num_layers, bn_size, growth_rate, act_fn):\n", " \"\"\"\n", " Inputs:\n", " c_in - Number of input channels\n", " num_layers - Number of dense layers to apply in the block\n", " bn_size - Bottleneck size to use in the dense layers\n", " growth_rate - Growth rate to use in the dense layers\n", " act_fn - Activation function to use in the dense layers\n", " \"\"\"\n", " super().__init__()\n", " layers = []\n", " for layer_idx in range(num_layers):\n", " # Input channels are original plus the feature maps from previous layers\n", " layer_c_in = c_in + layer_idx * growth_rate\n", " layers.append(DenseLayer(c_in=layer_c_in, bn_size=bn_size, growth_rate=growth_rate, act_fn=act_fn))\n", " self.block = nn.Sequential(*layers)\n", "\n", " def forward(self, x):\n", " out = self.block(x)\n", " return out"]}, {"cell_type": "markdown", "id": "41bcb65f", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.168003, "end_time": "2021-12-04T15:57:37.026310", "exception": false, "start_time": "2021-12-04T15:57:36.858307", "status": "completed"}, "tags": []}, "source": ["Finally, the `TransitionLayer` takes as input the final output of a dense block and reduces its channel dimensionality using a 1x1 convolution.\n", "To reduce the height and width dimension, we take a slightly different approach than in ResNet and apply an average pooling with kernel size 2 and stride 2.\n", "This is because we don't have an additional connection to the output that would consider the full 2x2 patch instead of a single value.\n", "Besides, it is more parameter efficient than using a 3x3 convolution with stride 2.\n", "Thus, the layer is implemented as follows:"]}, {"cell_type": "code", "execution_count": 32, "id": "2ea6e9bd", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:37.371597Z", "iopub.status.busy": "2021-12-04T15:57:37.371122Z", "iopub.status.idle": "2021-12-04T15:57:37.373121Z", "shell.execute_reply": "2021-12-04T15:57:37.372717Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.178559, "end_time": "2021-12-04T15:57:37.373229", "exception": false, "start_time": "2021-12-04T15:57:37.194670", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class TransitionLayer(nn.Module):\n", " def __init__(self, c_in, c_out, act_fn):\n", " super().__init__()\n", " self.transition = nn.Sequential(\n", " nn.BatchNorm2d(c_in),\n", " act_fn(),\n", " nn.Conv2d(c_in, c_out, kernel_size=1, bias=False),\n", " nn.AvgPool2d(kernel_size=2, stride=2), # Average the output for each 2x2 pixel group\n", " )\n", "\n", " def forward(self, x):\n", " return self.transition(x)"]}, {"cell_type": "markdown", "id": "2a5d631f", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.167305, "end_time": "2021-12-04T15:57:37.707771", "exception": false, "start_time": "2021-12-04T15:57:37.540466", "status": "completed"}, "tags": []}, "source": ["Now we can put everything together and create our DenseNet.\n", "To specify the number of layers, we use a similar notation as in ResNets and pass on a list of ints representing the number of layers per block.\n", "After each dense block except the last one, we apply a transition layer to reduce the dimensionality by 2."]}, {"cell_type": "code", "execution_count": 33, "id": "e61967ce", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:38.057964Z", "iopub.status.busy": "2021-12-04T15:57:38.049763Z", "iopub.status.idle": "2021-12-04T15:57:38.059615Z", "shell.execute_reply": "2021-12-04T15:57:38.060000Z"}, "papermill": {"duration": 0.187694, "end_time": "2021-12-04T15:57:38.060130", "exception": false, "start_time": "2021-12-04T15:57:37.872436", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class DenseNet(nn.Module):\n", " def __init__(\n", " self, num_classes=10, num_layers=[6, 6, 6, 6], bn_size=2, growth_rate=16, act_fn_name=\"relu\", **kwargs\n", " ):\n", " super().__init__()\n", " self.hparams = SimpleNamespace(\n", " num_classes=num_classes,\n", " num_layers=num_layers,\n", " bn_size=bn_size,\n", " growth_rate=growth_rate,\n", " act_fn_name=act_fn_name,\n", " act_fn=act_fn_by_name[act_fn_name],\n", " )\n", " self._create_network()\n", " self._init_params()\n", "\n", " def _create_network(self):\n", " c_hidden = self.hparams.growth_rate * self.hparams.bn_size # The start number of hidden channels\n", "\n", " # A first convolution on the original image to scale up the channel size\n", " self.input_net = nn.Sequential(\n", " # No batch norm or activation function as done inside the Dense layers\n", " nn.Conv2d(3, c_hidden, kernel_size=3, padding=1)\n", " )\n", "\n", " # Creating the dense blocks, eventually including transition layers\n", " blocks = []\n", " for block_idx, num_layers in enumerate(self.hparams.num_layers):\n", " blocks.append(\n", " DenseBlock(\n", " c_in=c_hidden,\n", " num_layers=num_layers,\n", " bn_size=self.hparams.bn_size,\n", " growth_rate=self.hparams.growth_rate,\n", " act_fn=self.hparams.act_fn,\n", " )\n", " )\n", " c_hidden = c_hidden + num_layers * self.hparams.growth_rate # Overall output of the dense block\n", " if block_idx < len(self.hparams.num_layers) - 1: # Don't apply transition layer on last block\n", " blocks.append(TransitionLayer(c_in=c_hidden, c_out=c_hidden // 2, act_fn=self.hparams.act_fn))\n", " c_hidden = c_hidden // 2\n", "\n", " self.blocks = nn.Sequential(*blocks)\n", "\n", " # Mapping to classification output\n", " self.output_net = nn.Sequential(\n", " nn.BatchNorm2d(c_hidden), # The features have not passed a non-linearity until here.\n", " self.hparams.act_fn(),\n", " nn.AdaptiveAvgPool2d((1, 1)),\n", " nn.Flatten(),\n", " nn.Linear(c_hidden, self.hparams.num_classes),\n", " )\n", "\n", " def _init_params(self):\n", " # Based on our discussion in Tutorial 4, we should initialize the\n", " # convolutions according to the activation function\n", " for m in self.modules():\n", " if isinstance(m, nn.Conv2d):\n", " nn.init.kaiming_normal_(m.weight, nonlinearity=self.hparams.act_fn_name)\n", " elif isinstance(m, nn.BatchNorm2d):\n", " nn.init.constant_(m.weight, 1)\n", " nn.init.constant_(m.bias, 0)\n", "\n", " def forward(self, x):\n", " x = self.input_net(x)\n", " x = self.blocks(x)\n", " x = self.output_net(x)\n", " return x"]}, {"cell_type": "markdown", "id": "8a17a3e1", "metadata": {"papermill": {"duration": 0.169685, "end_time": "2021-12-04T15:57:38.400764", "exception": false, "start_time": "2021-12-04T15:57:38.231079", "status": "completed"}, "tags": []}, "source": ["Let's also add the DenseNet to our model dictionary:"]}, {"cell_type": "code", "execution_count": 34, "id": "42fb9154", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:38.737268Z", "iopub.status.busy": "2021-12-04T15:57:38.736793Z", "iopub.status.idle": "2021-12-04T15:57:38.738365Z", "shell.execute_reply": "2021-12-04T15:57:38.738764Z"}, "papermill": {"duration": 0.172954, "end_time": "2021-12-04T15:57:38.738890", "exception": false, "start_time": "2021-12-04T15:57:38.565936", "status": "completed"}, "tags": []}, "outputs": [], "source": ["model_dict[\"DenseNet\"] = DenseNet"]}, {"cell_type": "markdown", "id": "4be858c3", "metadata": {"papermill": {"duration": 0.168419, "end_time": "2021-12-04T15:57:39.078283", "exception": false, "start_time": "2021-12-04T15:57:38.909864", "status": "completed"}, "tags": []}, "source": ["Lastly, we train our network.\n", "In contrast to ResNet, DenseNet does not show any issues with Adam, and hence we train it with this optimizer.\n", "The other hyperparameters are chosen to result in a network with a similar parameter size as the ResNet and GoogleNet.\n", "Commonly, when designing very deep networks, DenseNet is more parameter\n", "efficient than ResNet while achieving a similar or even better\n", "performance."]}, {"cell_type": "code", "execution_count": 35, "id": "e36f4881", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:39.415290Z", "iopub.status.busy": "2021-12-04T15:57:39.414811Z", "iopub.status.idle": "2021-12-04T15:57:44.113020Z", "shell.execute_reply": "2021-12-04T15:57:44.113406Z"}, "papermill": {"duration": 4.869343, "end_time": "2021-12-04T15:57:44.113572", "exception": false, "start_time": "2021-12-04T15:57:39.244229", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=1)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:901: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/ConvNets/DenseNet.ckpt, loading...\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "c37fd9f09225462dbf011a96be37c084", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "cf02514bd53f4511a0b664fc86c842b7", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["densenet_model, densenet_results = train_model(\n", " model_name=\"DenseNet\",\n", " model_hparams={\n", " \"num_classes\": 10,\n", " \"num_layers\": [6, 6, 6, 6],\n", " \"bn_size\": 2,\n", " \"growth_rate\": 16,\n", " \"act_fn_name\": \"relu\",\n", " },\n", " optimizer_name=\"Adam\",\n", " optimizer_hparams={\"lr\": 1e-3, \"weight_decay\": 1e-4},\n", ")"]}, {"cell_type": "markdown", "id": "6a712390", "metadata": {"papermill": {"duration": 0.170458, "end_time": "2021-12-04T15:57:44.457192", "exception": false, "start_time": "2021-12-04T15:57:44.286734", "status": "completed"}, "tags": []}, "source": ["### Tensorboard log\n", "\n", "Finally, we also have another TensorBoard for the DenseNet training. We take a look at it below:"]}, {"cell_type": "code", "execution_count": 36, "id": "1b728973", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:44.807975Z", "iopub.status.busy": "2021-12-04T15:57:44.807514Z", "iopub.status.idle": "2021-12-04T15:57:46.369275Z", "shell.execute_reply": "2021-12-04T15:57:46.368869Z"}, "papermill": {"duration": 1.742356, "end_time": "2021-12-04T15:57:46.369408", "exception": false, "start_time": "2021-12-04T15:57:44.627052", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", " \n", " \n", " "], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH! Feel free to change \"ResNet\" to \"ResNetPreAct\"\n", "%tensorboard --logdir ../saved_models/tutorial5/tensorboards/DenseNet/"]}, {"cell_type": "markdown", "id": "e39ad034", "metadata": {"papermill": {"duration": 0.172568, "end_time": "2021-12-04T15:57:46.718239", "exception": false, "start_time": "2021-12-04T15:57:46.545671", "status": "completed"}, "tags": []}, "source": ["
\n", "\n", "The overall course of the validation accuracy and training loss resemble the training of GoogleNet, which is also related to training the network with Adam.\n", "Feel free to explore the training metrics yourself."]}, {"cell_type": "markdown", "id": "6bad2177", "metadata": {"papermill": {"duration": 0.171587, "end_time": "2021-12-04T15:57:47.073199", "exception": false, "start_time": "2021-12-04T15:57:46.901612", "status": "completed"}, "tags": []}, "source": ["## Conclusion and Comparison\n", "\n", "After discussing each model separately, and training all of them, we can finally compare them.\n", "First, let's organize the results of all models in a table:"]}, {"cell_type": "code", "execution_count": 37, "id": "8e60b85a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:47.427909Z", "iopub.status.busy": "2021-12-04T15:57:47.427445Z", "iopub.status.idle": "2021-12-04T15:57:47.430369Z", "shell.execute_reply": "2021-12-04T15:57:47.429973Z"}, "papermill": {"duration": 0.183449, "end_time": "2021-12-04T15:57:47.430477", "exception": false, "start_time": "2021-12-04T15:57:47.247028", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", "\n"], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["%%html\n", "\n", ""]}, {"cell_type": "code", "execution_count": 38, "id": "800aabe6", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:57:47.801905Z", "iopub.status.busy": "2021-12-04T15:57:47.801437Z", "iopub.status.idle": "2021-12-04T15:57:47.804116Z", "shell.execute_reply": "2021-12-04T15:57:47.803654Z"}, "papermill": {"duration": 0.198645, "end_time": "2021-12-04T15:57:47.804225", "exception": false, "start_time": "2021-12-04T15:57:47.605580", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Model Val Accuracy Test Accuracy Num Parameters
GoogleNet 90.40% 89.70% 260,650
ResNet 91.84% 91.06% 272,378
ResNetPreAct91.80% 91.07% 272,250
DenseNet 90.72% 90.23% 239,146
"], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["all_models = [\n", " (\"GoogleNet\", googlenet_results, googlenet_model),\n", " (\"ResNet\", resnet_results, resnet_model),\n", " (\"ResNetPreAct\", resnetpreact_results, resnetpreact_model),\n", " (\"DenseNet\", densenet_results, densenet_model),\n", "]\n", "table = [\n", " [\n", " model_name,\n", " f\"{100.0*model_results['val']:4.2f}%\",\n", " f\"{100.0*model_results['test']:4.2f}%\",\n", " f\"{sum(np.prod(p.shape) for p in model.parameters()):,}\",\n", " ]\n", " for model_name, model_results, model in all_models\n", "]\n", "display(\n", " HTML(\n", " tabulate.tabulate(table, tablefmt=\"html\", headers=[\"Model\", \"Val Accuracy\", \"Test Accuracy\", \"Num Parameters\"])\n", " )\n", ")"]}, {"cell_type": "markdown", "id": "73c70307", "metadata": {"papermill": {"duration": 0.17902, "end_time": "2021-12-04T15:57:48.160604", "exception": false, "start_time": "2021-12-04T15:57:47.981584", "status": "completed"}, "tags": []}, "source": ["First of all, we see that all models are performing reasonably well.\n", "Simple models as you have implemented them in the practical achieve considerably lower performance, which is beside the lower number of parameters also attributed to the architecture design choice.\n", "GoogleNet is the model to obtain the lowest performance on the validation and test set, although it is very close to DenseNet.\n", "A proper hyperparameter search over all the channel sizes in GoogleNet would likely improve the accuracy of the model to a similar level, but this is also expensive given a large number of hyperparameters.\n", "ResNet outperforms both DenseNet and GoogleNet by more than 1% on the validation set, while there is a minor difference between both versions, original and pre-activation.\n", "We can conclude that for shallow networks, the place of the activation function does not seem to be crucial, although papers have reported the contrary for very deep networks (e.g. [He et al. ](https://arxiv.org/abs/1603.05027)).\n", "\n", "In general, we can conclude that ResNet is a simple, but powerful architecture.\n", "If we would apply the models on more complex tasks with larger images and more layers inside the networks, we would likely see a bigger gap between GoogleNet and skip-connection architectures like ResNet and DenseNet.\n", "A comparison with deeper models on CIFAR10 can be for example found [here](https://github.com/kuangliu/pytorch-cifar).\n", "Interestingly, DenseNet outperforms the original ResNet on their setup but comes closely behind the Pre-Activation ResNet.\n", "The best model, a Dual Path Network ([Chen et.\n", "al](https://arxiv.org/abs/1707.01629)), is actually a combination of\n", "ResNet and DenseNet showing that both offer different advantages."]}, {"cell_type": "markdown", "id": "202e03f1", "metadata": {"papermill": {"duration": 0.175905, "end_time": "2021-12-04T15:57:48.512460", "exception": false, "start_time": "2021-12-04T15:57:48.336555", "status": "completed"}, "tags": []}, "source": ["### Which model should I choose for my task?\n", "\n", "We have reviewed four different models.\n", "So, which one should we choose if have given a new task?\n", "Usually, starting with a ResNet is a good idea given the superior performance of the CIFAR dataset and its simple implementation.\n", "Besides, for the parameter number we have chosen here, ResNet is the fastest as DenseNet and GoogleNet have many more layers that are applied in sequence in our primitive implementation.\n", "However, if you have a really difficult task, such as semantic\n", "segmentation on HD images, more complex variants of ResNet and DenseNet\n", "are recommended."]}, {"cell_type": "markdown", "id": "50fb705d", "metadata": {"papermill": {"duration": 0.175468, "end_time": "2021-12-04T15:57:48.863314", "exception": false, "start_time": "2021-12-04T15:57:48.687846", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 4: Inception, ResNet and DenseNet\n", " :card_description: In this tutorial, we will implement and discuss variants of modern CNN architectures. There have been many different architectures been proposed over the past few years. Some...\n", " :tags: Image,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/04-inception-resnet-densenet.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab,colab_type,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 61.494311, "end_time": "2021-12-04T15:57:50.353007", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/04-inception-resnet-densenet/Inception_ResNet_DenseNet.ipynb", "output_path": ".notebooks/course_UvA-DL/04-inception-resnet-densenet.ipynb", "parameters": {}, "start_time": "2021-12-04T15:56:48.858696", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"004122da18ba429f94c52eb1fae5340c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_38d235ee850740368e877d28bb73f631", "placeholder": "\u200b", "style": "IPY_MODEL_022d04c15a7a40c1870c2a80cd397672", "value": "Testing: 100%"}}, "00a940952c5f49e98ca15e3623f74168": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "022d04c15a7a40c1870c2a80cd397672": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "04e7c8c74bca444e941f03d85f3cfb3f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e627c1b61a864374b8d87fead8d00fd1", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_b02d79366d6a49c7b9f5840d94533fae", "value": 1.0}}, "076947cb26da40f89c1c1972f76139a3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0da45269292640a09a53ff5a1a4d44ab": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "118ae85712494119a362f39f76d62852": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "14b309fa97bf436ebea932b9d1c99b7a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6d72bfd4b6f94f61a3f626f29c690a39", "placeholder": "\u200b", "style": "IPY_MODEL_42a8b2e0205b4d1097c8b97d532f6ed9", "value": " 40/40 [00:00<00:00, 74.95it/s]"}}, "16bf296c023d48c0a08547e9c8332279": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "185245d8a5a14167bfdcddbd1262cd1a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_076947cb26da40f89c1c1972f76139a3", "placeholder": "\u200b", "style": "IPY_MODEL_8390973957a74be9969d427649734c45", "value": " 79/79 [00:01<00:00, 88.62it/s]"}}, "1963d06751de49fba491644790475b8d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7d5327eea6524d07acda5225f552e010", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_6f8ec09e1ef24c11ae3bc5ae151a4568", "value": 1.0}}, "1c5ea7a5eb5d46f18b5b5d2da5d0e1cc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_00a940952c5f49e98ca15e3623f74168", "placeholder": "\u200b", "style": "IPY_MODEL_74efd30822274ed89911d1bf644ce3ca", "value": " 79/79 [00:00<00:00, 123.78it/s]"}}, "352ddbe0e937449b9c2cac2f6e201121": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "38d235ee850740368e877d28bb73f631": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3a6311c097a3455cb2d71f023e8d4d8f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "3b2ff86eb87949bcaa63b16a35aebaca": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_93686022ddb8443faf4f5451d731b796", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3a6311c097a3455cb2d71f023e8d4d8f", "value": 1.0}}, "41a45b5de3914d8f942a3cef02cf377b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "4296ffe355d14d4bac17df7d1425a410": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "42a8b2e0205b4d1097c8b97d532f6ed9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "44586163a7ec4db7bd0c32aff969aefc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "4542daedea6845f9b0e7df869e150283": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_c0250bb5c8c4472fafccb35b520586e4", "IPY_MODEL_c0d08635227c47b4ad6cad2c2a37f925", "IPY_MODEL_1c5ea7a5eb5d46f18b5b5d2da5d0e1cc"], "layout": "IPY_MODEL_a125c1f239c640b1ab4cbec01e92bbb6"}}, "473521a5bc274985a92e69de0abd4f4b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4a3504d97bfa49f0ac3f87e109978777": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4b76c0b6cd5e461e84eba9db81f50cbe": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "513cfb186fbd4c01bdacc7feea4bdb5c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "53f2e1fd00bc487297fc47bac77b0732": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a92128f9324349a7aefb31c2e503006c", "placeholder": "\u200b", "style": "IPY_MODEL_f974bb4fc58d40ccac4644c1c3e6666d", "value": "Testing: 100%"}}, "591231fc56eb4c06ad9194cff69c82c8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5cafd0505815497a88ef0bd271cef82a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_473521a5bc274985a92e69de0abd4f4b", "placeholder": "\u200b", "style": "IPY_MODEL_fc013fd8d5e14e40a7d95a00ac2e2f90", "value": " 40/40 [00:00<00:00, 88.89it/s]"}}, "6000f7a133b44b9b96fd7c1bf554a644": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_004122da18ba429f94c52eb1fae5340c", "IPY_MODEL_6a5c7a9b7da04f2e82ef909e900754f0", "IPY_MODEL_e9a8f29f80c94231b08fadfd08e7467d"], "layout": "IPY_MODEL_dab6f379137447b0ace973fcdb0c4883"}}, "64a7eb6a4ab24ae098f0c43f6cd7af26": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "65496b20e7a24e40b31fa8f48e60a2d4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d7d3481e866640fbac41b5386ad2e9c3", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_41a45b5de3914d8f942a3cef02cf377b", "value": 1.0}}, "6a5c7a9b7da04f2e82ef909e900754f0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_513cfb186fbd4c01bdacc7feea4bdb5c", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_ed64940063374f128c2013deb6314870", "value": 1.0}}, "6ca97f4b62c14c7bbfeb1db85fc993a2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_77cca86506734df5b72869411d27da60", "placeholder": "\u200b", "style": "IPY_MODEL_b7279c1fe219460498493b83c026738e", "value": "Testing: 100%"}}, "6d72bfd4b6f94f61a3f626f29c690a39": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6e026d193e4743a69f3692b485b5ac17": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0da45269292640a09a53ff5a1a4d44ab", "placeholder": "\u200b", "style": "IPY_MODEL_591231fc56eb4c06ad9194cff69c82c8", "value": " 40/40 [00:00<00:00, 75.32it/s]"}}, "6f2f0240233b41469935805c777db4c2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "6f8ec09e1ef24c11ae3bc5ae151a4568": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "74efd30822274ed89911d1bf644ce3ca": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "75655468633246a19e817d74aa226268": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "77cca86506734df5b72869411d27da60": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7b7ad0926c604a54834aa8f89f2c0ebc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7d5327eea6524d07acda5225f552e010": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7f0bfa83aa584625b4fb04a2f3e7ec2a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4b76c0b6cd5e461e84eba9db81f50cbe", "placeholder": "\u200b", "style": "IPY_MODEL_be040e2360db45b0a40f43d3787798b1", "value": "Testing: 100%"}}, "8390973957a74be9969d427649734c45": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "863d7a5eb148478c8c413b42460f9e98": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c53aaa5c2481470289ce280db63719e4", "placeholder": "\u200b", "style": "IPY_MODEL_4a3504d97bfa49f0ac3f87e109978777", "value": "Testing: 100%"}}, "88b1b202de814778ae51007742b5610c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_ad24953ed75e41d6bfcc8077104e37db", "IPY_MODEL_fb3273ec5ef647f4aeccf20834da1954", "IPY_MODEL_185245d8a5a14167bfdcddbd1262cd1a"], "layout": "IPY_MODEL_f55ab7d5e72a4c4a9ef5848f433e1b3d"}}, "8b06c3c950c14130b00f529e021afdac": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fb6d4630bdf244f99cbf9e68a82bdc20", "placeholder": "\u200b", "style": "IPY_MODEL_16bf296c023d48c0a08547e9c8332279", "value": "Testing: 100%"}}, "8f66c046bbff4be7bd5a9214f0f14626": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "93686022ddb8443faf4f5451d731b796": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9441a5cfb1b14598abb9a6c652e59289": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b697e02382f6498b8a29aba192f231db", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_352ddbe0e937449b9c2cac2f6e201121", "value": 1.0}}, "9704fd63690c487b9beb24e4d7e2209c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "9f659821a81047c2b875574e3a5e54f7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a125c1f239c640b1ab4cbec01e92bbb6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "a92128f9324349a7aefb31c2e503006c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ad24953ed75e41d6bfcc8077104e37db": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_64a7eb6a4ab24ae098f0c43f6cd7af26", "placeholder": "\u200b", "style": "IPY_MODEL_e53518709a804cdab8e26116c179730b", "value": "Testing: 100%"}}, "b02d79366d6a49c7b9f5840d94533fae": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "b0d8426a216e46af8354fe3ad3d2249f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "b49f280f709c4cc1908bc5280cc5e728": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b697e02382f6498b8a29aba192f231db": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b7279c1fe219460498493b83c026738e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b92f8c8a741b439084618bba61742bb0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_7f0bfa83aa584625b4fb04a2f3e7ec2a", "IPY_MODEL_65496b20e7a24e40b31fa8f48e60a2d4", "IPY_MODEL_5cafd0505815497a88ef0bd271cef82a"], "layout": "IPY_MODEL_6f2f0240233b41469935805c777db4c2"}}, "bbbcce7c1cd14711a307fac5253f8d00": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_75655468633246a19e817d74aa226268", "placeholder": "\u200b", "style": "IPY_MODEL_bfc08e0d2dc94323808770cf5150de04", "value": " 40/40 [00:00<00:00, 94.67it/s]"}}, "be040e2360db45b0a40f43d3787798b1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "bfc08e0d2dc94323808770cf5150de04": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c0250bb5c8c4472fafccb35b520586e4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9f659821a81047c2b875574e3a5e54f7", "placeholder": "\u200b", "style": "IPY_MODEL_9704fd63690c487b9beb24e4d7e2209c", "value": "Testing: 100%"}}, "c0d08635227c47b4ad6cad2c2a37f925": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7b7ad0926c604a54834aa8f89f2c0ebc", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_c2080e16ca2f44b28902656b9c395539", "value": 1.0}}, "c2080e16ca2f44b28902656b9c395539": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "c37fd9f09225462dbf011a96be37c084": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_863d7a5eb148478c8c413b42460f9e98", "IPY_MODEL_3b2ff86eb87949bcaa63b16a35aebaca", "IPY_MODEL_14b309fa97bf436ebea932b9d1c99b7a"], "layout": "IPY_MODEL_44586163a7ec4db7bd0c32aff969aefc"}}, "c53aaa5c2481470289ce280db63719e4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cf02514bd53f4511a0b664fc86c842b7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_53f2e1fd00bc487297fc47bac77b0732", "IPY_MODEL_1963d06751de49fba491644790475b8d", "IPY_MODEL_da95394060554551a2465f7a2505d130"], "layout": "IPY_MODEL_ff9888303fc846d189e4ce73414c0a85"}}, "d4a54ef3af5d4bcaafad8c61c3360423": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_6ca97f4b62c14c7bbfeb1db85fc993a2", "IPY_MODEL_04e7c8c74bca444e941f03d85f3cfb3f", "IPY_MODEL_6e026d193e4743a69f3692b485b5ac17"], "layout": "IPY_MODEL_eb6d3d7ea0714bf181daacd91321c814"}}, "d7d3481e866640fbac41b5386ad2e9c3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "da95394060554551a2465f7a2505d130": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b49f280f709c4cc1908bc5280cc5e728", "placeholder": "\u200b", "style": "IPY_MODEL_f1dc0af14e3a4e15b57164c87a249c7d", "value": " 79/79 [00:01<00:00, 91.28it/s]"}}, "dab6f379137447b0ace973fcdb0c4883": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "dfc83023ccbd496084ceb29ff0a8612c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e53518709a804cdab8e26116c179730b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e627c1b61a864374b8d87fead8d00fd1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e84f1647ff4244d5992786cbc17d3e1b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_8b06c3c950c14130b00f529e021afdac", "IPY_MODEL_9441a5cfb1b14598abb9a6c652e59289", "IPY_MODEL_bbbcce7c1cd14711a307fac5253f8d00"], "layout": "IPY_MODEL_4296ffe355d14d4bac17df7d1425a410"}}, "e9a8f29f80c94231b08fadfd08e7467d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8f66c046bbff4be7bd5a9214f0f14626", "placeholder": "\u200b", "style": "IPY_MODEL_118ae85712494119a362f39f76d62852", "value": " 79/79 [00:00<00:00, 121.91it/s]"}}, "eb6d3d7ea0714bf181daacd91321c814": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "ed64940063374f128c2013deb6314870": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "f1dc0af14e3a4e15b57164c87a249c7d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f55ab7d5e72a4c4a9ef5848f433e1b3d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "f974bb4fc58d40ccac4644c1c3e6666d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fb3273ec5ef647f4aeccf20834da1954": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_dfc83023ccbd496084ceb29ff0a8612c", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_b0d8426a216e46af8354fe3ad3d2249f", "value": 1.0}}, "fb6d4630bdf244f99cbf9e68a82bdc20": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fc013fd8d5e14e40a7d95a00ac2e2f90": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ff9888303fc846d189e4ce73414c0a85": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/05-transformers-and-MH-attention.ipynb b/source/notebooks/course_UvA-DL/05-transformers-and-MH-attention.ipynb deleted file mode 100644 index 1700ac0..0000000 --- a/source/notebooks/course_UvA-DL/05-transformers-and-MH-attention.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "829d77e5", "metadata": {"papermill": {"duration": 0.139908, "end_time": "2021-12-04T15:58:00.173929", "exception": false, "start_time": "2021-12-04T15:58:00.034021", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 5: Transformers and Multi-Head Attention\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:52:50.580472\n", "\n", "In this tutorial, we will discuss one of the most impactful architectures of the last 2 years: the Transformer model.\n", "Since the paper Attention Is All You Need by Vaswani et al. had been published in 2017,\n", "the Transformer architecture has continued to beat benchmarks in many domains, most importantly in Natural Language Processing.\n", "Transformers with an incredible amount of parameters can generate long, convincing essays, and opened up new application fields of AI.\n", "As the hype of the Transformer architecture seems not to come to an end in the next years,\n", "it is important to understand how it works, and have implemented it yourself, which we will do in this notebook.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/05-transformers-and-MH-attention.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "4bd97ecd", "metadata": {"papermill": {"duration": 0.139186, "end_time": "2021-12-04T15:58:00.454648", "exception": false, "start_time": "2021-12-04T15:58:00.315462", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "6df421c0", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T15:58:00.739046Z", "iopub.status.busy": "2021-12-04T15:58:00.738548Z", "iopub.status.idle": "2021-12-04T15:58:03.208213Z", "shell.execute_reply": "2021-12-04T15:58:03.207638Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.615354, "end_time": "2021-12-04T15:58:03.208358", "exception": false, "start_time": "2021-12-04T15:58:00.593004", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"torchmetrics>=0.3\" \"pytorch-lightning>=1.3\" \"torchvision\" \"torch>=1.6, <1.9\" \"matplotlib\" \"seaborn\""]}, {"cell_type": "markdown", "id": "a5126640", "metadata": {"papermill": {"duration": 0.138671, "end_time": "2021-12-04T15:58:03.486977", "exception": false, "start_time": "2021-12-04T15:58:03.348306", "status": "completed"}, "tags": []}, "source": ["
\n", "Despite the huge success of Transformers in NLP, we will _not_ include the NLP domain in our notebook here.\n", "There are many courses at the University of Amsterdam that focus on Natural Language Processing\n", "and take a closer look at the application of the Transformer architecture in NLP\n", "([NLP2](https://studiegids.uva.nl/xmlpages/page/2020-2021/zoek-vak/vak/79628),\n", "[Advanced Topics in Computational Semantics](https://studiegids.uva.nl/xmlpages/page/2020-2021/zoek-vak/vak/80162)).\n", "Furthermore, and most importantly, there is so much more to the Transformer architecture.\n", "NLP is the domain the Transformer architecture has been originally proposed for and had the greatest impact on,\n", "but it also accelerated research in other domains, recently even [Computer Vision](https://arxiv.org/abs/2010.11929).\n", "Thus, we focus here on what makes the Transformer and self-attention so powerful in general.\n", "In a second notebook, we will look at Vision Transformers, i.e. Transformers for image classification\n", "([link to notebook](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial15/Vision_Transformer.html)).\n", "\n", "Below, we import our standard libraries."]}, {"cell_type": "code", "execution_count": 2, "id": "31508898", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:03.772463Z", "iopub.status.busy": "2021-12-04T15:58:03.771979Z", "iopub.status.idle": "2021-12-04T15:58:05.871634Z", "shell.execute_reply": "2021-12-04T15:58:05.871226Z"}, "papermill": {"duration": 2.246581, "end_time": "2021-12-04T15:58:05.871763", "exception": false, "start_time": "2021-12-04T15:58:03.625182", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_1492/2689201066.py:34: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Device: cuda:0\n"]}], "source": ["# Standard libraries\n", "import math\n", "import os\n", "import urllib.request\n", "from functools import partial\n", "from urllib.error import HTTPError\n", "\n", "# Plotting\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "# PyTorch Lightning\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "\n", "# PyTorch\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "\n", "# Torchvision\n", "import torchvision\n", "from IPython.display import set_matplotlib_formats\n", "from pytorch_lightning.callbacks import ModelCheckpoint\n", "from torchvision import transforms\n", "from torchvision.datasets import CIFAR100\n", "from tqdm.notebook import tqdm\n", "\n", "plt.set_cmap(\"cividis\")\n", "%matplotlib inline\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "matplotlib.rcParams[\"lines.linewidth\"] = 2.0\n", "sns.reset_orig()\n", "\n", "# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data/\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/Transformers/\")\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", "print(\"Device:\", device)"]}, {"cell_type": "markdown", "id": "940525e1", "metadata": {"papermill": {"duration": 0.144686, "end_time": "2021-12-04T15:58:06.158145", "exception": false, "start_time": "2021-12-04T15:58:06.013459", "status": "completed"}, "tags": []}, "source": ["Two pre-trained models are downloaded below.\n", "Make sure to have adjusted your `CHECKPOINT_PATH` before running this code if not already done."]}, {"cell_type": "code", "execution_count": 3, "id": "a7a72c1a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:06.442497Z", "iopub.status.busy": "2021-12-04T15:58:06.442012Z", "iopub.status.idle": "2021-12-04T15:58:06.793103Z", "shell.execute_reply": "2021-12-04T15:58:06.792670Z"}, "papermill": {"duration": 0.496404, "end_time": "2021-12-04T15:58:06.793239", "exception": false, "start_time": "2021-12-04T15:58:06.296835", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial6/ReverseTask.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial6/SetAnomalyTask.ckpt...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial6/\"\n", "# Files to download\n", "pretrained_files = [\"ReverseTask.ckpt\", \"SetAnomalyTask.ckpt\"]\n", "\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if \"/\" in file_name:\n", " os.makedirs(file_path.rsplit(\"/\", 1)[0], exist_ok=True)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(\"Downloading %s...\" % file_url)\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file manually,\"\n", " \" or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "1e17404e", "metadata": {"papermill": {"duration": 0.140163, "end_time": "2021-12-04T15:58:07.075728", "exception": false, "start_time": "2021-12-04T15:58:06.935565", "status": "completed"}, "tags": []}, "source": ["## The Transformer architecture\n", "\n", "In the first part of this notebook, we will implement the Transformer architecture by hand.\n", "As the architecture is so popular, there already exists a Pytorch module `nn.Transformer`\n", "([documentation](https://pytorch.org/docs/stable/generated/torch.nn.Transformer.html))\n", "and a [tutorial](https://pytorch.org/tutorials/beginner/transformer_tutorial.html)\n", "on how to use it for next token prediction.\n", "However, we will implement it here ourselves, to get through to the smallest details.\n", "\n", "There are of course many more tutorials out there about attention and Transformers.\n", "Below, we list a few that are worth exploring if you are interested in the topic\n", "and might want yet another perspective on the topic after this one:\n", "\n", "* [Transformer: A Novel Neural Network Architecture for Language Understanding\n", "(Jakob Uszkoreit, 2017)](https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html) - The original Google blog post about the Transformer paper, focusing on the application in machine translation.\n", "* [The Illustrated Transformer (Jay Alammar, 2018)](http://jalammar.github.io/illustrated-transformer/) - A very popular and great blog post intuitively explaining the Transformer architecture with many nice visualizations.\n", "The focus is on NLP.\n", "* [Attention?\n", "Attention!\n", "(Lilian Weng, 2018)](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html) - A nice blog post summarizing attention mechanisms in many domains including vision.\n", "* [Illustrated: Self-Attention (Raimi Karim, 2019)](https://towardsdatascience.com/illustrated-self-attention-2d627e33b20a) - A nice visualization of the steps of self-attention.\n", "Recommended going through if the explanation below is too abstract for you.\n", "* [The Transformer family (Lilian Weng, 2020)](https://lilianweng.github.io/lil-log/2020/04/07/the-transformer-family.html) - A very detailed blog post reviewing more variants of Transformers besides the original one."]}, {"cell_type": "markdown", "id": "c7a1d465", "metadata": {"papermill": {"duration": 0.14018, "end_time": "2021-12-04T15:58:07.355646", "exception": false, "start_time": "2021-12-04T15:58:07.215466", "status": "completed"}, "tags": []}, "source": ["### What is Attention?\n", "\n", "The attention mechanism describes a recent new group of layers in neural networks that has attracted\n", "a lot of interest in the past few years, especially in sequence tasks.\n", "There are a lot of different possible definitions of \"attention\" in the literature,\n", "but the one we will use here is the following: _the attention mechanism describes a weighted average\n", "of (sequence) elements with the weights dynamically computed based on an input query and elements' keys_.\n", "So what does this exactly mean?\n", "The goal is to take an average over the features of multiple elements.\n", "However, instead of weighting each element equally, we want to weight them depending on their actual values.\n", "In other words, we want to dynamically decide on which inputs we want to \"attend\" more than others.\n", "In particular, an attention mechanism has usually four parts we need to specify:\n", "\n", "* **Query**: The query is a feature vector that describes what we are looking for in the sequence, i.e. what would we maybe want to pay attention to.\n", "* **Keys**: For each input element, we have a key which is again a feature vector.\n", "This feature vector roughly describes what the element is \"offering\", or when it might be important.\n", "The keys should be designed such that we can identify the elements we want to pay attention to based on the query.\n", "* **Values**: For each input element, we also have a value vector.\n", "This feature vector is the one we want to average over.\n", "* **Score function**: To rate which elements we want to pay attention to, we need to specify a score function $f_{attn}$.\n", "The score function takes the query and a key as input, and output the score/attention weight of the query-key pair.\n", "It is usually implemented by simple similarity metrics like a dot product, or a small MLP.\n", "\n", "\n", "The weights of the average are calculated by a softmax over all score function outputs.\n", "Hence, we assign those value vectors a higher weight whose corresponding key is most similar to the query.\n", "If we try to describe it with pseudo-math, we can write:\n", "\n", "$$\n", "\\alpha_i = \\frac{\\exp\\left(f_{attn}\\left(\\text{key}_i, \\text{query}\\right)\\right)}{\\sum_j \\exp\\left(f_{attn}\\left(\\text{key}_j, \\text{query}\\right)\\right)}, \\hspace{5mm} \\text{out} = \\sum_i \\alpha_i \\cdot \\text{value}_i\n", "$$\n", "\n", "Visually, we can show the attention over a sequence of words as follows:\n", "\n", "
\n", "\n", "For every word, we have one key and one value vector.\n", "The query is compared to all keys with a score function (in this case the dot product) to determine the weights.\n", "The softmax is not visualized for simplicity.\n", "Finally, the value vectors of all words are averaged using the attention weights.\n", "\n", "Most attention mechanisms differ in terms of what queries they use, how the key and value vectors are defined,\n", "and what score function is used.\n", "The attention applied inside the Transformer architecture is called **self-attention**.\n", "In self-attention, each sequence element provides a key, value, and query.\n", "For each element, we perform an attention layer where based on its query,\n", "we check the similarity of the all sequence elements' keys, and returned a different,\n", "averaged value vector for each element.\n", "We will now go into a bit more detail by first looking at the specific implementation of the attention mechanism\n", "which is in the Transformer case the scaled dot product attention."]}, {"cell_type": "markdown", "id": "d9697f03", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.139603, "end_time": "2021-12-04T15:58:07.635701", "exception": false, "start_time": "2021-12-04T15:58:07.496098", "status": "completed"}, "tags": []}, "source": ["### Scaled Dot Product Attention\n", "\n", "The core concept behind self-attention is the scaled dot product attention.\n", "Our goal is to have an attention mechanism with which any element in a sequence can attend to any other while\n", "still being efficient to compute.\n", "The dot product attention takes as input a set of queries\n", "$Q\\in\\mathbb{R}^{T\\times d_k}$, keys $K\\in\\mathbb{R}^{T\\times d_k}$\n", "and values $V\\in\\mathbb{R}^{T\\times d_v}$ where $T$ is the sequence length,\n", "and $d_k$ and $d_v$ are the hidden dimensionality for queries/keys and values respectively.\n", "For simplicity, we neglect the batch dimension for now.\n", "The attention value from element $i$ to $j$ is based on its similarity of the query $Q_i$ and key $K_j$,\n", "using the dot product as the similarity metric.\n", "In math, we calculate the dot product attention as follows:\n", "\n", "$$\\text{Attention}(Q,K,V)=\\text{softmax}\\left(\\frac{QK^T}{\\sqrt{d_k}}\\right)V$$\n", "\n", "The matrix multiplication $QK^T$ performs the dot product for every possible pair of queries and keys,\n", "resulting in a matrix of the shape $T\\times T$.\n", "Each row represents the attention logits for a specific element $i$ to all other elements in the sequence.\n", "On these, we apply a softmax and multiply with the value vector to obtain a weighted mean\n", "(the weights being determined by the attention).\n", "Another perspective on this attention mechanism offers the computation graph which is visualized below\n", "(figure credit - [Vaswani et al., 2017](https://arxiv.org/abs/1706.03762)).\n", "\n", "
\n", "\n", "One aspect we haven't discussed yet is the scaling factor of $1/\\sqrt{d_k}$.\n", "This scaling factor is crucial to maintain an appropriate variance of attention values after initialization.\n", "Remember that we intialize our layers with the intention of having equal variance throughout the model, and hence,\n", "$Q$ and $K$ might also have a variance close to $1$.\n", "However, performing a dot product over two vectors with a variance $\\sigma$ results\n", "in a scalar having $d_k$-times higher variance:\n", "\n", "$$q_i \\sim \\mathcal{N}(0,\\sigma), k_i \\sim \\mathcal{N}(0,\\sigma) \\to \\text{Var}\\left(\\sum_{i=1}^{d_k} q_i\\cdot k_i\\right) = \\sigma\\cdot d_k$$\n", "\n", "\n", "If we do not scale down the variance back to $\\sigma$, the softmax over the logits will already saturate\n", "to $1$ for one random element and $0$ for all others.\n", "The gradients through the softmax will be close to zero so that we can't learn the parameters appropriately.\n", "\n", "The block `Mask (opt.\n", ")` in the diagram above represents the optional masking of specific entries in the attention matrix.\n", "This is for instance used if we stack multiple sequences with different lengths into a batch.\n", "To still benefit from parallelization in PyTorch, we pad the sentences to the same length and mask out the padding\n", "tokens during the calculation of the attention values.\n", "This is usually done by setting the respective attention logits to a very low value.\n", "\n", "After we have discussed the details of the scaled dot product attention block, we can write a function below\n", "which computes the output features given the triple of queries, keys, and values:"]}, {"cell_type": "code", "execution_count": 4, "id": "f65c1f5d", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:07.923041Z", "iopub.status.busy": "2021-12-04T15:58:07.922459Z", "iopub.status.idle": "2021-12-04T15:58:07.924745Z", "shell.execute_reply": "2021-12-04T15:58:07.925122Z"}, "papermill": {"duration": 0.148521, "end_time": "2021-12-04T15:58:07.925250", "exception": false, "start_time": "2021-12-04T15:58:07.776729", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def scaled_dot_product(q, k, v, mask=None):\n", " d_k = q.size()[-1]\n", " attn_logits = torch.matmul(q, k.transpose(-2, -1))\n", " attn_logits = attn_logits / math.sqrt(d_k)\n", " if mask is not None:\n", " attn_logits = attn_logits.masked_fill(mask == 0, -9e15)\n", " attention = F.softmax(attn_logits, dim=-1)\n", " values = torch.matmul(attention, v)\n", " return values, attention"]}, {"cell_type": "markdown", "id": "10dc2e7e", "metadata": {"papermill": {"duration": 0.139995, "end_time": "2021-12-04T15:58:08.204876", "exception": false, "start_time": "2021-12-04T15:58:08.064881", "status": "completed"}, "tags": []}, "source": ["Note that our code above supports any additional dimensionality in front of the sequence length\n", "so that we can also use it for batches.\n", "However, for a better understanding, let's generate a few random queries, keys, and value vectors,\n", "and calculate the attention outputs:"]}, {"cell_type": "code", "execution_count": 5, "id": "c417bca9", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:08.490138Z", "iopub.status.busy": "2021-12-04T15:58:08.489665Z", "iopub.status.idle": "2021-12-04T15:58:08.498288Z", "shell.execute_reply": "2021-12-04T15:58:08.497857Z"}, "papermill": {"duration": 0.15228, "end_time": "2021-12-04T15:58:08.498402", "exception": false, "start_time": "2021-12-04T15:58:08.346122", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Q\n", " tensor([[ 0.3367, 0.1288],\n", " [ 0.2345, 0.2303],\n", " [-1.1229, -0.1863]])\n", "K\n", " tensor([[ 2.2082, -0.6380],\n", " [ 0.4617, 0.2674],\n", " [ 0.5349, 0.8094]])\n", "V\n", " tensor([[ 1.1103, -1.6898],\n", " [-0.9890, 0.9580],\n", " [ 1.3221, 0.8172]])\n", "Values\n", " tensor([[ 0.5698, -0.1520],\n", " [ 0.5379, -0.0265],\n", " [ 0.2246, 0.5556]])\n", "Attention\n", " tensor([[0.4028, 0.2886, 0.3086],\n", " [0.3538, 0.3069, 0.3393],\n", " [0.1303, 0.4630, 0.4067]])\n"]}], "source": ["seq_len, d_k = 3, 2\n", "pl.seed_everything(42)\n", "q = torch.randn(seq_len, d_k)\n", "k = torch.randn(seq_len, d_k)\n", "v = torch.randn(seq_len, d_k)\n", "values, attention = scaled_dot_product(q, k, v)\n", "print(\"Q\\n\", q)\n", "print(\"K\\n\", k)\n", "print(\"V\\n\", v)\n", "print(\"Values\\n\", values)\n", "print(\"Attention\\n\", attention)"]}, {"cell_type": "markdown", "id": "a9d22885", "metadata": {"papermill": {"duration": 0.141388, "end_time": "2021-12-04T15:58:08.781187", "exception": false, "start_time": "2021-12-04T15:58:08.639799", "status": "completed"}, "tags": []}, "source": ["Before continuing, make sure you can follow the calculation of the specific values here, and also check it by hand.\n", "It is important to fully understand how the scaled dot product attention is calculated."]}, {"cell_type": "markdown", "id": "09bc5c7a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.141201, "end_time": "2021-12-04T15:58:09.064542", "exception": false, "start_time": "2021-12-04T15:58:08.923341", "status": "completed"}, "tags": []}, "source": ["### Multi-Head Attention\n", "\n", "The scaled dot product attention allows a network to attend over a sequence.\n", "However, often there are multiple different aspects a sequence element wants to attend to,\n", "and a single weighted average is not a good option for it.\n", "This is why we extend the attention mechanisms to multiple heads,\n", "i.e. multiple different query-key-value triplets on the same features.\n", "Specifically, given a query, key, and value matrix, we transform those into $h$ sub-queries, sub-keys,\n", "and sub-values, which we pass through the scaled dot product attention independently.\n", "Afterward, we concatenate the heads and combine them with a final weight matrix.\n", "Mathematically, we can express this operation as:\n", "\n", "$$\n", "\\begin{split}\n", " \\text{Multihead}(Q,K,V) & = \\text{Concat}(\\text{head}_1,...,\\text{head}_h)W^{O}\\\\\n", " \\text{where } \\text{head}_i & = \\text{Attention}(QW_i^Q,KW_i^K, VW_i^V)\n", "\\end{split}\n", "$$\n", "\n", "We refer to this as Multi-Head Attention layer with the learnable parameters\n", "$W_{1...h}^{Q}\\in\\mathbb{R}^{D\\times d_k}$,\n", "$W_{1...h}^{K}\\in\\mathbb{R}^{D\\times d_k}$,\n", "$W_{1...h}^{V}\\in\\mathbb{R}^{D\\times d_v}$,\n", "and $W^{O}\\in\\mathbb{R}^{h\\cdot d_k\\times d_{out}}$ ($D$ being the input dimensionality).\n", "Expressed in a computational graph, we can visualize it as below\n", "(figure credit - [Vaswani et al., 2017](https://arxiv.org/abs/1706.03762)).\n", "\n", "
\n", "\n", "How are we applying a Multi-Head Attention layer in a neural network,\n", "where we don't have an arbitrary query, key, and value vector as input?\n", "Looking at the computation graph above, a simple but effective implementation is to set the current\n", "feature map in a NN, $X\\in\\mathbb{R}^{B\\times T\\times d_{\\text{model}}}$, as $Q$, $K$ and $V$\n", "($B$ being the batch size, $T$ the sequence length, $d_{\\text{model}}$ the hidden dimensionality of $X$).\n", "The consecutive weight matrices $W^{Q}$, $W^{K}$, and $W^{V}$ can transform $X$ to the corresponding\n", "feature vectors that represent the queries, keys, and values of the input.\n", "Using this approach, we can implement the Multi-Head Attention module below."]}, {"cell_type": "code", "execution_count": 6, "id": "9a857f4f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:09.357800Z", "iopub.status.busy": "2021-12-04T15:58:09.357266Z", "iopub.status.idle": "2021-12-04T15:58:09.359052Z", "shell.execute_reply": "2021-12-04T15:58:09.358641Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.152505, "end_time": "2021-12-04T15:58:09.359159", "exception": false, "start_time": "2021-12-04T15:58:09.206654", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class MultiheadAttention(nn.Module):\n", " def __init__(self, input_dim, embed_dim, num_heads):\n", " super().__init__()\n", " assert embed_dim % num_heads == 0, \"Embedding dimension must be 0 modulo number of heads.\"\n", "\n", " self.embed_dim = embed_dim\n", " self.num_heads = num_heads\n", " self.head_dim = embed_dim // num_heads\n", "\n", " # Stack all weight matrices 1...h together for efficiency\n", " # Note that in many implementations you see \"bias=False\" which is optional\n", " self.qkv_proj = nn.Linear(input_dim, 3 * embed_dim)\n", " self.o_proj = nn.Linear(embed_dim, embed_dim)\n", "\n", " self._reset_parameters()\n", "\n", " def _reset_parameters(self):\n", " # Original Transformer initialization, see PyTorch documentation\n", " nn.init.xavier_uniform_(self.qkv_proj.weight)\n", " self.qkv_proj.bias.data.fill_(0)\n", " nn.init.xavier_uniform_(self.o_proj.weight)\n", " self.o_proj.bias.data.fill_(0)\n", "\n", " def forward(self, x, mask=None, return_attention=False):\n", " batch_size, seq_length, embed_dim = x.size()\n", " qkv = self.qkv_proj(x)\n", "\n", " # Separate Q, K, V from linear output\n", " qkv = qkv.reshape(batch_size, seq_length, self.num_heads, 3 * self.head_dim)\n", " qkv = qkv.permute(0, 2, 1, 3) # [Batch, Head, SeqLen, Dims]\n", " q, k, v = qkv.chunk(3, dim=-1)\n", "\n", " # Determine value outputs\n", " values, attention = scaled_dot_product(q, k, v, mask=mask)\n", " values = values.permute(0, 2, 1, 3) # [Batch, SeqLen, Head, Dims]\n", " values = values.reshape(batch_size, seq_length, embed_dim)\n", " o = self.o_proj(values)\n", "\n", " if return_attention:\n", " return o, attention\n", " else:\n", " return o"]}, {"cell_type": "markdown", "id": "b56973f7", "metadata": {"papermill": {"duration": 0.142069, "end_time": "2021-12-04T15:58:09.642785", "exception": false, "start_time": "2021-12-04T15:58:09.500716", "status": "completed"}, "tags": []}, "source": ["One crucial characteristic of the multi-head attention is that it is permutation-equivariant with respect to its inputs.\n", "This means that if we switch two input elements in the sequence, e.g. $X_1\\leftrightarrow X_2$\n", "(neglecting the batch dimension for now), the output is exactly the same besides the elements 1 and 2 switched.\n", "Hence, the multi-head attention is actually looking at the input not as a sequence, but as a set of elements.\n", "This property makes the multi-head attention block and the Transformer architecture so powerful and widely applicable!\n", "But what if the order of the input is actually important for solving the task, like language modeling?\n", "The answer is to encode the position in the input features, which we will take a closer look at later\n", "(topic _Positional encodings_ below).\n", "\n", "Before moving on to creating the Transformer architecture, we can compare the self-attention operation\n", "with our other common layer competitors for sequence data: convolutions and recurrent neural networks.\n", "Below you can find a table by [Vaswani et al.\n", "(2017)](https://arxiv.org/abs/1706.03762) on the complexity per layer, the number of sequential operations,\n", "and maximum path length.\n", "The complexity is measured by the upper bound of the number of operations to perform, while the maximum path\n", "length represents the maximum number of steps a forward or backward signal has to traverse to reach any other position.\n", "The lower this length, the better gradient signals can backpropagate for long-range dependencies.\n", "Let's take a look at the table below:\n", "\n", "\n", "
\n", "\n", "$n$ is the sequence length, $d$ is the representation dimension and $k$ is the kernel size of convolutions.\n", "In contrast to recurrent networks, the self-attention layer can parallelize all its operations making it much faster\n", "to execute for smaller sequence lengths.\n", "However, when the sequence length exceeds the hidden dimensionality, self-attention becomes more expensive than RNNs.\n", "One way of reducing the computational cost for long sequences is by restricting the self-attention to a neighborhood\n", "of inputs to attend over, denoted by $r$.\n", "Nevertheless, there has been recently a lot of work on more efficient Transformer architectures that still allow long\n", "dependencies, of which you can find an overview in the paper by [Tay et al.\n", "(2020)](https://arxiv.org/abs/2009.06732) if interested."]}, {"cell_type": "markdown", "id": "c9ea3bc6", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.142611, "end_time": "2021-12-04T15:58:09.927152", "exception": false, "start_time": "2021-12-04T15:58:09.784541", "status": "completed"}, "tags": []}, "source": ["### Transformer Encoder\n", "\n", "
\n", "\n", "Next, we will look at how to apply the multi-head attention blog inside the Transformer architecture.\n", "Originally, the Transformer model was designed for machine translation.\n", "Hence, it got an encoder-decoder structure where the encoder takes as input the sentence in the original language\n", "and generates an attention-based representation.\n", "On the other hand, the decoder attends over the encoded information and generates the translated sentence\n", "in an autoregressive manner, as in a standard RNN.\n", "While this structure is extremely useful for Sequence-to-Sequence tasks with the necessity of autoregressive decoding,\n", "we will focus here on the encoder part.\n", "Many advances in NLP have been made using pure encoder-based Transformer models (if interested, models include the\n", "[BERT](https://arxiv.org/abs/1810.04805)-family,\n", "the [Vision Transformer](https://arxiv.org/abs/2010.11929), and more),\n", "and in our tutorial, we will also mainly focus on the encoder part.\n", "If you have understood the encoder architecture, the decoder is a very small step to implement as well.\n", "The full Transformer architecture looks as follows\n", "(figure credit - [Vaswani et al., 2017](https://arxiv.org/abs/1706.03762)).\n", ":\n", "\n", "
\n", "\n", "The encoder consists of $N$ identical blocks that are applied in sequence.\n", "Taking as input $x$, it is first passed through a Multi-Head Attention block as we have implemented above.\n", "The output is added to the original input using a residual connection,\n", "and we apply a consecutive Layer Normalization on the sum.\n", "Overall, it calculates $\\text{LayerNorm}(x+\\text{Multihead}(x,x,x))$\n", "($x$ being $Q$, $K$ and $V$ input to the attention layer).\n", "The residual connection is crucial in the Transformer architecture for two reasons:\n", "\n", "1.\n", "Similar to ResNets, Transformers are designed to be very deep.\n", "Some models contain more than 24 blocks in the encoder.\n", "Hence, the residual connections are crucial for enabling a smooth gradient flow through the model.\n", "2.\n", "Without the residual connection, the information about the original sequence is lost.\n", "Remember that the Multi-Head Attention layer ignores the position of elements in a sequence,\n", "and can only learn it based on the input features.\n", "Removing the residual connections would mean that this information is lost after the first attention layer\n", "(after initialization), and with a randomly initialized query and key vector,\n", "the output vectors for position $i$ has no relation to its original input.\n", "All outputs of the attention are likely to represent similar/same information,\n", "and there is no chance for the model to distinguish which information came from which input element.\n", "An alternative option to residual connection would be to fix at least one head to focus on its original input,\n", "but this is very inefficient and does not have the benefit of the improved gradient flow.\n", "\n", "The Layer Normalization also plays an important role in the Transformer architecture as it enables faster\n", "training and provides small regularization.\n", "Additionally, it ensures that the features are in a similar magnitude among the elements in the sequence.\n", "We are not using Batch Normalization because it depends on the batch size which is often small with Transformers\n", "(they require a lot of GPU memory), and BatchNorm has shown to perform particularly bad in language\n", "as the features of words tend to have a much higher variance (there are many, very rare words\n", "which need to be considered for a good distribution estimate).\n", "\n", "Additionally to the Multi-Head Attention, a small fully connected feed-forward network is added to the model,\n", "which is applied to each position separately and identically.\n", "Specifically, the model uses a Linear$\\to$ReLU$\\to$Linear MLP.\n", "The full transformation including the residual connection can be expressed as:\n", "\n", "$$\n", "\\begin{split}\n", " \\text{FFN}(x) & = \\max(0, xW_1+b_1)W_2 + b_2\\\\\n", " x & = \\text{LayerNorm}(x + \\text{FFN}(x))\n", "\\end{split}\n", "$$\n", "\n", "This MLP adds extra complexity to the model and allows transformations on each sequence element separately.\n", "You can imagine as this allows the model to \"post-process\" the new information added\n", "by the previous Multi-Head Attention, and prepare it for the next attention block.\n", "Usually, the inner dimensionality of the MLP is 2-8$\\times$ larger than $d_{\\text{model}}$,\n", "i.e. the dimensionality of the original input $x$.\n", "The general advantage of a wider layer instead of a narrow, multi-layer MLP is the faster, parallelizable execution.\n", "\n", "Finally, after looking at all parts of the encoder architecture, we can start implementing it below.\n", "We first start by implementing a single encoder block.\n", "Additionally to the layers described above, we will add dropout layers in the MLP and on the output\n", "of the MLP and Multi-Head Attention for regularization."]}, {"cell_type": "code", "execution_count": 7, "id": "23103d12", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:10.224411Z", "iopub.status.busy": "2021-12-04T15:58:10.223932Z", "iopub.status.idle": "2021-12-04T15:58:10.225971Z", "shell.execute_reply": "2021-12-04T15:58:10.225565Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.157144, "end_time": "2021-12-04T15:58:10.226077", "exception": false, "start_time": "2021-12-04T15:58:10.068933", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class EncoderBlock(nn.Module):\n", " def __init__(self, input_dim, num_heads, dim_feedforward, dropout=0.0):\n", " \"\"\"\n", " Args:\n", " input_dim: Dimensionality of the input\n", " num_heads: Number of heads to use in the attention block\n", " dim_feedforward: Dimensionality of the hidden layer in the MLP\n", " dropout: Dropout probability to use in the dropout layers\n", " \"\"\"\n", " super().__init__()\n", "\n", " # Attention layer\n", " self.self_attn = MultiheadAttention(input_dim, input_dim, num_heads)\n", "\n", " # Two-layer MLP\n", " self.linear_net = nn.Sequential(\n", " nn.Linear(input_dim, dim_feedforward),\n", " nn.Dropout(dropout),\n", " nn.ReLU(inplace=True),\n", " nn.Linear(dim_feedforward, input_dim),\n", " )\n", "\n", " # Layers to apply in between the main layers\n", " self.norm1 = nn.LayerNorm(input_dim)\n", " self.norm2 = nn.LayerNorm(input_dim)\n", " self.dropout = nn.Dropout(dropout)\n", "\n", " def forward(self, x, mask=None):\n", " # Attention part\n", " attn_out = self.self_attn(x, mask=mask)\n", " x = x + self.dropout(attn_out)\n", " x = self.norm1(x)\n", "\n", " # MLP part\n", " linear_out = self.linear_net(x)\n", " x = x + self.dropout(linear_out)\n", " x = self.norm2(x)\n", "\n", " return x"]}, {"cell_type": "markdown", "id": "7820dae1", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.143711, "end_time": "2021-12-04T15:58:10.516987", "exception": false, "start_time": "2021-12-04T15:58:10.373276", "status": "completed"}, "tags": []}, "source": ["Based on this block, we can implement a module for the full Transformer encoder.\n", "Additionally to a forward function that iterates through the sequence of encoder blocks,\n", "we also provide a function called `get_attention_maps`.\n", "The idea of this function is to return the attention probabilities for all Multi-Head Attention blocks in the encoder.\n", "This helps us in understanding, and in a sense, explaining the model.\n", "However, the attention probabilities should be interpreted with a grain of salt as it does not necessarily\n", "reflect the true interpretation of the model (there is a series of papers about this,\n", "including [Attention is not Explanation](https://arxiv.org/abs/1902.10186)\n", "and [Attention is not not Explanation](https://arxiv.org/abs/1908.04626))."]}, {"cell_type": "code", "execution_count": 8, "id": "5619cdd3", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:10.816718Z", "iopub.status.busy": "2021-12-04T15:58:10.816231Z", "iopub.status.idle": "2021-12-04T15:58:10.818205Z", "shell.execute_reply": "2021-12-04T15:58:10.817797Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.158399, "end_time": "2021-12-04T15:58:10.818313", "exception": false, "start_time": "2021-12-04T15:58:10.659914", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class TransformerEncoder(nn.Module):\n", " def __init__(self, num_layers, **block_args):\n", " super().__init__()\n", " self.layers = nn.ModuleList([EncoderBlock(**block_args) for _ in range(num_layers)])\n", "\n", " def forward(self, x, mask=None):\n", " for layer in self.layers:\n", " x = layer(x, mask=mask)\n", " return x\n", "\n", " def get_attention_maps(self, x, mask=None):\n", " attention_maps = []\n", " for layer in self.layers:\n", " _, attn_map = layer.self_attn(x, mask=mask, return_attention=True)\n", " attention_maps.append(attn_map)\n", " x = layer(x)\n", " return attention_maps"]}, {"cell_type": "markdown", "id": "8a328d53", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.142344, "end_time": "2021-12-04T15:58:11.102765", "exception": false, "start_time": "2021-12-04T15:58:10.960421", "status": "completed"}, "tags": []}, "source": ["### Positional encoding\n", "\n", "We have discussed before that the Multi-Head Attention block is permutation-equivariant,\n", "and cannot distinguish whether an input comes before another one in the sequence or not.\n", "In tasks like language understanding, however, the position is important for interpreting the input words.\n", "The position information can therefore be added via the input features.\n", "We could learn a embedding for every possible position, but this would not generalize to a dynamical\n", "input sequence length.\n", "Hence, the better option is to use feature patterns that the network can identify from the features\n", "and potentially generalize to larger sequences.\n", "The specific pattern chosen by Vaswani et al.\n", "are sine and cosine functions of different frequencies, as follows:\n", "\n", "$$\n", "PE_{(pos,i)} = \\begin{cases}\n", " \\sin\\left(\\frac{pos}{10000^{i/d_{\\text{model}}}}\\right) & \\text{if}\\hspace{3mm} i \\text{ mod } 2=0\\\\\n", " \\cos\\left(\\frac{pos}{10000^{(i-1)/d_{\\text{model}}}}\\right) & \\text{otherwise}\\\\\n", "\\end{cases}\n", "$$\n", "\n", "$PE_{(pos,i)}$ represents the position encoding at position $pos$ in the sequence, and hidden dimensionality $i$.\n", "These values, concatenated for all hidden dimensions, are added to the original input features\n", "(in the Transformer visualization above, see \"Positional encoding\"), and constitute the position information.\n", "We distinguish between even ($i \\text{ mod } 2=0$) and uneven ($i \\text{ mod } 2=1$)\n", "hidden dimensionalities where we apply a sine/cosine respectively.\n", "The intuition behind this encoding is that you can represent $PE_{(pos+k,:)}$ as a linear function\n", "of $PE_{(pos,:)}$, which might allow the model to easily attend to relative positions.\n", "The wavelengths in different dimensions range from $2\\pi$ to $10000\\cdot 2\\pi$.\n", "\n", "The positional encoding is implemented below.\n", "The code is taken from the [PyTorch tutorial](https://pytorch.org/tutorials/beginner/transformer_tutorial.html#define-the-model)\n", "about Transformers on NLP and adjusted for our purposes."]}, {"cell_type": "code", "execution_count": 9, "id": "9a4179be", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:11.394604Z", "iopub.status.busy": "2021-12-04T15:58:11.394124Z", "iopub.status.idle": "2021-12-04T15:58:11.395735Z", "shell.execute_reply": "2021-12-04T15:58:11.396116Z"}, "papermill": {"duration": 0.151358, "end_time": "2021-12-04T15:58:11.396243", "exception": false, "start_time": "2021-12-04T15:58:11.244885", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class PositionalEncoding(nn.Module):\n", " def __init__(self, d_model, max_len=5000):\n", " \"\"\"\n", " Args\n", " d_model: Hidden dimensionality of the input.\n", " max_len: Maximum length of a sequence to expect.\n", " \"\"\"\n", " super().__init__()\n", "\n", " # Create matrix of [SeqLen, HiddenDim] representing the positional encoding for max_len inputs\n", " pe = torch.zeros(max_len, d_model)\n", " position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)\n", " div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))\n", " pe[:, 0::2] = torch.sin(position * div_term)\n", " pe[:, 1::2] = torch.cos(position * div_term)\n", " pe = pe.unsqueeze(0)\n", "\n", " # register_buffer => Tensor which is not a parameter, but should be part of the modules state.\n", " # Used for tensors that need to be on the same device as the module.\n", " # persistent=False tells PyTorch to not add the buffer to the state dict (e.g. when we save the model)\n", " self.register_buffer(\"pe\", pe, persistent=False)\n", "\n", " def forward(self, x):\n", " x = x + self.pe[:, : x.size(1)]\n", " return x"]}, {"cell_type": "markdown", "id": "ab85f000", "metadata": {"papermill": {"duration": 0.143264, "end_time": "2021-12-04T15:58:11.687431", "exception": false, "start_time": "2021-12-04T15:58:11.544167", "status": "completed"}, "tags": []}, "source": ["To understand the positional encoding, we can visualize it below.\n", "We will generate an image of the positional encoding over hidden dimensionality and position in a sequence.\n", "Each pixel, therefore, represents the change of the input feature we perform to encode the specific position.\n", "Let's do it below."]}, {"cell_type": "code", "execution_count": 10, "id": "f3c3e710", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:11.979041Z", "iopub.status.busy": "2021-12-04T15:58:11.978542Z", "iopub.status.idle": "2021-12-04T15:58:12.357315Z", "shell.execute_reply": "2021-12-04T15:58:12.357708Z"}, "papermill": {"duration": 0.527144, "end_time": "2021-12-04T15:58:12.357871", "exception": false, "start_time": "2021-12-04T15:58:11.830727", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTggMCBvYmoKPDwgL0ggMTkgMCBSIC9QIDIwIDAgUiAvYSAyMSAwIFIgL2MgMjIgMCBSIC9kIDIzIDAgUiAvZSAyNCAwIFIKL2VpZ2h0IDI1IDAgUiAvZml2ZSAyNiAwIFIgL2ZvdXIgMjcgMCBSIC9nIDI4IDAgUiAvaCAyOSAwIFIgL2kgMzAgMCBSCi9sIDMxIDAgUiAvbSAzMiAwIFIgL24gMzQgMCBSIC9uaW5lIDM1IDAgUiAvbyAzNiAwIFIgL29uZSAzNyAwIFIKL3BlcmlvZCAzOCAwIFIgL3EgMzkgMCBSIC9yIDQwIDAgUiAvcyA0MSAwIFIgL3NldmVuIDQyIDAgUiAvc2l4IDQzIDAgUgovc3BhY2UgNDQgMCBSIC90IDQ1IDAgUiAvdGhyZWUgNDYgMCBSIC90d28gNDcgMCBSIC91IDQ4IDAgUiAvdiA0OSAwIFIKL3plcm8gNTAgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNyAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMzMgMCBSIC9JMSAxMyAwIFIgL0kyIDE0IDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOAovQ29sb3JTcGFjZSBbL0luZGV4ZWQgL0RldmljZVJHQiAyNTUgKP7+/v7+/f39/f78+/77+fv7+/769/r6+v749fn5+fj4+P738v718Pf39/X19f707v7z7P7x6vT09PPz8/Ly8vHx8f7w5/7u5f7t4+/v7+7u7u3t7f3r4f3q3/3p3P3n2v3m2P3k1uzs7Orq6unp6ejo6Ofn5+bm5uTk5P3j1P3i0f3gz+Pj4+Li4uHh4eDg4P3f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MDAwIG4gCjAwMDAwMTIzMTMgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDAwIDAwMDAwIG4gCjAwMDAwMDE1NTQgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAxNTMzIDAwMDAwIG4gCjAwMDAwMTIzODQgMDAwMDAgbiAKMDAwMDAxOTI1OCAwMDAwMCBuIAowMDAwMDEwNzE2IDAwMDAwIG4gCjAwMDAwMTA1MTYgMDAwMDAgbiAKMDAwMDAxMDA1OCAwMDAwMCBuIAowMDAwMDExNzY5IDAwMDAwIG4gCjAwMDAwMDE1NzQgMDAwMDAgbiAKMDAwMDAwMTcyNSAwMDAwMCBuIAowMDAwMDAxOTY4IDAwMDAwIG4gCjAwMDAwMDIzNDggMDAwMDAgbiAKMDAwMDAwMjY1MyAwMDAwMCBuIAowMDAwMDAyOTU3IDAwMDAwIG4gCjAwMDAwMDMyNzkgMDAwMDAgbiAKMDAwMDAwMzc0NyAwMDAwMCBuIAowMDAwMDA0MDY5IDAwMDAwIG4gCjAwMDAwMDQyMzUgMDAwMDAgbiAKMDAwMDAwNDY0OSAwMDAwMCBuIAowMDAwMDA0ODg2IDAwMDAwIG4gCjAwMDAwMDUwMzAgMDAwMDAgbiAKMDAwMDAwNTE0OSAwMDAwMCBuIAowMDAwMDA1NDgwIDAwMDAwIG4gCjAwMDAwMDU2NTIgMDAwMDAgbiAKMDAwMDAwNTg4OCAwMDAwMCBuIAowMDAwMDA2MjgzIDAwMDAwIG4gCjAwMDAwMDY1NzQgMDAwMDAgbiAKMDAwMDAwNjcyOSAwMDAwMCBuIAowMDAwMDA2ODUyIDAwMDAwIG4gCjAwMDAwMDcxNjggMDAwMDAgbiAKMDAwMDAwNzQwMSAwMDAwMCBuIAowMDAwMDA3ODA4IDAwMDAwIG4gCjAwMDAwMDc5NTAgMDAwMDAgbiAKMDAwMDAwODM0MyAwMDAwMCBuIAowMDAwMDA4NDMzIDAwMDAwIG4gCjAwMDAwMDg2MzkgMDAwMDAgbiAKMDAwMDAwOTA1MiAwMDAwMCBuIAowMDAwMDA5Mzc2IDAwMDAwIG4gCjAwMDAwMDk2MjMgMDAwMDAgbiAKMDAwMDAwOTc3MCAwMDAwMCBuIAowMDAwMDE5MjM3IDAwMDAwIG4gCjAwMDAwMjAzMjAgMDAwMDAgbiAKMDAwMDAyMDQwMCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDUzIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA1NCA+PgpzdGFydHhyZWYKMjA1NTcKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:58:12.126505\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["encod_block = PositionalEncoding(d_model=48, max_len=96)\n", "pe = encod_block.pe.squeeze().T.cpu().numpy()\n", "\n", "fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8, 3))\n", "pos = ax.imshow(pe, cmap=\"RdGy\", extent=(1, pe.shape[1] + 1, pe.shape[0] + 1, 1))\n", "fig.colorbar(pos, ax=ax)\n", "ax.set_xlabel(\"Position in sequence\")\n", "ax.set_ylabel(\"Hidden dimension\")\n", "ax.set_title(\"Positional encoding over hidden dimensions\")\n", "ax.set_xticks([1] + [i * 10 for i in range(1, 1 + pe.shape[1] // 10)])\n", "ax.set_yticks([1] + [i * 10 for i in range(1, 1 + pe.shape[0] // 10)])\n", "plt.show()"]}, {"cell_type": "markdown", "id": "d5cc9a34", "metadata": {"papermill": {"duration": 0.154874, "end_time": "2021-12-04T15:58:12.659676", "exception": false, "start_time": "2021-12-04T15:58:12.504802", "status": "completed"}, "tags": []}, "source": ["You can clearly see the sine and cosine waves with different wavelengths that encode the position\n", "in the hidden dimensions.\n", "Specifically, we can look at the sine/cosine wave for each hidden dimension separately,\n", "to get a better intuition of the pattern.\n", "Below we visualize the positional encoding for the hidden dimensions $1$, $2$, $3$ and $4$."]}, {"cell_type": "code", "execution_count": 11, "id": "a476a12e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:12.994247Z", "iopub.status.busy": "2021-12-04T15:58:12.979090Z", "iopub.status.idle": "2021-12-04T15:58:14.181132Z", "shell.execute_reply": "2021-12-04T15:58:14.181526Z"}, "papermill": {"duration": 1.37322, "end_time": "2021-12-04T15:58:14.181688", "exception": false, "start_time": "2021-12-04T15:58:12.808468", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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Ci9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTggMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTcgMCBSID4+CmVuZG9iagoxOCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjE3IDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjIwIDAgb2JqCjw8IC9FIDIxIDAgUiAvUCAyMiAwIFIgL2EgMjMgMCBSIC9jIDI0IDAgUiAvZCAyNSAwIFIgL2UgMjYgMCBSCi9laWdodCAyNyAwIFIgL2ZpdmUgMjggMCBSIC9mb3VyIDI5IDAgUiAvZyAzMCAwIFIgL2ggMzEgMCBSIC9pIDMyIDAgUgovbCAzMyAwIFIgL20gMzQgMCBSIC9uIDM2IDAgUiAvbmluZSAzNyAwIFIgL28gMzggMCBSIC9vbmUgMzkgMCBSIC9xIDQwIDAgUgovcyA0MSAwIFIgL3NldmVuIDQyIDAgUiAvc2l4IDQzIDAgUiAvc3BhY2UgNDQgMCBSIC90IDQ1IDAgUiAvdGhyZWUgNDYgMCBSCi90d28gNDcgMCBSIC91IDQ4IDAgUiAvemVybyA0OSAwIFIgPj4KZW5kb2Jq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IDAwMDAwIG4gCjAwMDAwMTUxNzUgMDAwMDAgbiAKMDAwMDAxNTQyOSAwMDAwMCBuIAowMDAwMDEzMDEyIDAwMDAwIG4gCjAwMDAwMTI4MTIgMDAwMDAgbiAKMDAwMDAxMjM2NyAwMDAwMCBuIAowMDAwMDE0MDY1IDAwMDAwIG4gCjAwMDAwMDQzODQgMDAwMDAgbiAKMDAwMDAwNDUzNyAwMDAwMCBuIAowMDAwMDA0NzgwIDAwMDAwIG4gCjAwMDAwMDUxNjAgMDAwMDAgbiAKMDAwMDAwNTQ2NSAwMDAwMCBuIAowMDAwMDA1NzY5IDAwMDAwIG4gCjAwMDAwMDYwOTEgMDAwMDAgbiAKMDAwMDAwNjU1OSAwMDAwMCBuIAowMDAwMDA2ODgxIDAwMDAwIG4gCjAwMDAwMDcwNDcgMDAwMDAgbiAKMDAwMDAwNzQ2MSAwMDAwMCBuIAowMDAwMDA3Njk4IDAwMDAwIG4gCjAwMDAwMDc4NDIgMDAwMDAgbiAKMDAwMDAwNzk2MSAwMDAwMCBuIAowMDAwMDA4MjkyIDAwMDAwIG4gCjAwMDAwMDg0NjQgMDAwMDAgbiAKMDAwMDAwODcwMCAwMDAwMCBuIAowMDAwMDA5MDk1IDAwMDAwIG4gCjAwMDAwMDkzODYgMDAwMDAgbiAKMDAwMDAwOTU0MSAwMDAwMCBuIAowMDAwMDA5ODU3IDAwMDAwIG4gCjAwMDAwMTAyNjQgMDAwMDAgbiAKMDAwMDAxMDQwNiAwMDAwMCBuIAowMDAwMDEwNzk5IDAwMDAwIG4gCjAwMDAwMTA4ODkgMDAwMDAgbiAKMDAwMDAxMTA5NSAwMDAwMCBuIAowMDAwMDExNTA4IDAwMDAwIG4gCjAwMDAwMTE4MzIgMDAwMDAgbiAKMDAwMDAxMjA3OSAwMDAwMCBuIAowMDAwMDE1NzQzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNTAgMCBSIC9Sb290IDEgMCBSIC9TaXplIDUxID4+CnN0YXJ0eHJlZgoxNTkwMAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:58:13.463512\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["sns.set_theme()\n", "fig, ax = plt.subplots(2, 2, figsize=(12, 4))\n", "ax = [a for a_list in ax for a in a_list]\n", "for i in range(len(ax)):\n", " ax[i].plot(np.arange(1, 17), pe[i, :16], color=\"C%i\" % i, marker=\"o\", markersize=6, markeredgecolor=\"black\")\n", " ax[i].set_title(\"Encoding in hidden dimension %i\" % (i + 1))\n", " ax[i].set_xlabel(\"Position in sequence\", fontsize=10)\n", " ax[i].set_ylabel(\"Positional encoding\", fontsize=10)\n", " ax[i].set_xticks(np.arange(1, 17))\n", " ax[i].tick_params(axis=\"both\", which=\"major\", labelsize=10)\n", " ax[i].tick_params(axis=\"both\", which=\"minor\", labelsize=8)\n", " ax[i].set_ylim(-1.2, 1.2)\n", "fig.subplots_adjust(hspace=0.8)\n", "sns.reset_orig()\n", "plt.show()"]}, {"cell_type": "markdown", "id": "323263e4", "metadata": {"papermill": {"duration": 0.162339, "end_time": "2021-12-04T15:58:14.500062", "exception": false, "start_time": "2021-12-04T15:58:14.337723", "status": "completed"}, "tags": []}, "source": ["As we can see, the patterns between the hidden dimension $1$ and $2$ only differ in the starting angle.\n", "The wavelength is $2\\pi$, hence the repetition after position $6$.\n", "The hidden dimensions $2$ and $3$ have about twice the wavelength."]}, {"cell_type": "markdown", "id": "2a423f6a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.150745, "end_time": "2021-12-04T15:58:14.801934", "exception": false, "start_time": "2021-12-04T15:58:14.651189", "status": "completed"}, "tags": []}, "source": ["### Learning rate warm-up\n", "\n", "One commonly used technique for training a Transformer is learning rate warm-up.\n", "This means that we gradually increase the learning rate from 0 on to our originally specified\n", "learning rate in the first few iterations.\n", "Thus, we slowly start learning instead of taking very large steps from the beginning.\n", "In fact, training a deep Transformer without learning rate warm-up can make the model diverge\n", "and achieve a much worse performance on training and testing.\n", "Take for instance the following plot by [Liu et al.\n", "(2019)](https://arxiv.org/pdf/1908.03265.pdf) comparing Adam-vanilla (i.e. Adam without warm-up)\n", "vs Adam with a warm-up:\n", "\n", "
\n", "\n", "Clearly, the warm-up is a crucial hyperparameter in the Transformer architecture.\n", "Why is it so important?\n", "There are currently two common explanations.\n", "Firstly, Adam uses the bias correction factors which however can lead to a higher variance in the adaptive\n", "learning rate during the first iterations.\n", "Improved optimizers like [RAdam](https://arxiv.org/abs/1908.03265) have been shown to overcome this issue,\n", "not requiring warm-up for training Transformers.\n", "Secondly, the iteratively applied Layer Normalization across layers can lead to very high gradients during\n", "the first iterations, which can be solved by using Pre-Layer Normalization\n", "(similar to Pre-Activation ResNet), or replacing Layer Normalization by other techniques\n", "(Adaptive Normalization,\n", "[Power Normalization](https://arxiv.org/abs/2003.07845)).\n", "\n", "Nevertheless, many applications and papers still use the original Transformer architecture with Adam,\n", "because warm-up is a simple, yet effective way of solving the gradient problem in the first iterations.\n", "There are many different schedulers we could use.\n", "For instance, the original Transformer paper used an exponential decay scheduler with a warm-up.\n", "However, the currently most popular scheduler is the cosine warm-up scheduler,\n", "which combines warm-up with a cosine-shaped learning rate decay.\n", "We can implement it below, and visualize the learning rate factor over epochs."]}, {"cell_type": "code", "execution_count": 12, "id": "da5c81cf", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:15.111318Z", "iopub.status.busy": "2021-12-04T15:58:15.110838Z", "iopub.status.idle": "2021-12-04T15:58:15.112740Z", "shell.execute_reply": "2021-12-04T15:58:15.112354Z"}, "papermill": {"duration": 0.159462, "end_time": "2021-12-04T15:58:15.112851", "exception": false, "start_time": "2021-12-04T15:58:14.953389", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class CosineWarmupScheduler(optim.lr_scheduler._LRScheduler):\n", " def __init__(self, optimizer, warmup, max_iters):\n", " self.warmup = warmup\n", " self.max_num_iters = max_iters\n", " super().__init__(optimizer)\n", "\n", " def get_lr(self):\n", " lr_factor = self.get_lr_factor(epoch=self.last_epoch)\n", " return [base_lr * lr_factor for base_lr in self.base_lrs]\n", "\n", " def get_lr_factor(self, epoch):\n", " lr_factor = 0.5 * (1 + np.cos(np.pi * epoch / self.max_num_iters))\n", " if epoch <= self.warmup:\n", " lr_factor *= epoch * 1.0 / self.warmup\n", " return lr_factor"]}, {"cell_type": "code", "execution_count": 13, "id": "a0668690", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:15.442783Z", "iopub.status.busy": "2021-12-04T15:58:15.438778Z", "iopub.status.idle": "2021-12-04T15:58:15.738564Z", "shell.execute_reply": "2021-12-04T15:58:15.738973Z"}, "papermill": {"duration": 0.475589, "end_time": "2021-12-04T15:58:15.739126", "exception": false, "start_time": "2021-12-04T15:58:15.263537", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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Ci9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9DIDE3IDAgUiAvSSAxOCAwIFIgL0wgMTkgMCBSIC9SIDIwIDAgUiAvUyAyMSAwIFIgL1cgMjIgMCBSIC9hIDIzIDAgUgovYiAyNCAwIFIgL2MgMjUgMCBSIC9kIDI2IDAgUiAvZSAyNyAwIFIgL2VpZ2h0IDI4IDAgUiAvZiAyOSAwIFIKL2ZpdmUgMzAgMCBSIC9mb3VyIDMxIDAgUiAvZyAzMiAwIFIgL2ggMzMgMCBSIC9oeXBoZW4gMzQgMCBSIC9pIDM1IDAgUgovbCAzNiAwIFIgL20gMzcgMCBSIC9uIDM4IDAgUiAvbyAzOSAwIFIgL29uZSA0MCAwIFIgL3AgNDEgMCBSCi9wYXJlbmxlZnQgNDIgMCBSIC9wYXJlbnJpZ2h0IDQzIDAgUiAvcGVyaW9kIDQ0IDAgUiAvciA0NSAwIFIgL3MgNDYgMCBSCi9zZXZlbiA0NyAwIFIgL3NpeCA0OCAwIFIgL3NwYWNlIDQ5IDAgUiAvdCA1MCAwIFIgL3R3byA1MSAwIFIgL3UgNTIgMCBSCi96ZXJvIDUzIDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8ID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago1NCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1ODE1KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDU1CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDEzODA1IDAwMDAwIG4gCjAwMDAwMTM2MTEgMDAwMDAgbiAKMDAwMDAxMzY0MyAwMDAwMCBuIAowMDAwMDEzNzQyIDAwMDAwIG4gCjAwMDAwMTM3NjMgMDAwMDAgbiAKMDAwMDAxMzc4NCAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAwMTg4OCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDE4NjcgMDAwMDAgbiAKMDAwMDAxMjExMiAwMDAwMCBuIAowMDAwMDExOTEyIDAwMDAwIG4gCjAwMDAwMTE0MTMgMDAwMDAgbiAKMDAwMDAxMzE2NSAwMDAwMCBuIAowMDAwMDAxOTA4IDAwMDAwIG4gCjAwMDAwMDIyMTYgMDAwMDAgbiAKMDAwMDAwMjMzOSAwMDAwMCBuIAowMDAwMDAyNDcyIDAwMDAwIG4gCjAwMDAwMDI3NzcgMDAwMDAgbiAKMDAwMDAwMzE5MSAwMDAwMCBuIAowMDAwMDAzMzU1IDAwMDAwIG4gCjAwMDAwMDM3MzUgMDAwMDAgbiAKMDAwMDAwNDA1MiAwMDAwMCBuIAowMDAwMDA0MzU3IDAwMDAwIG4gCjAwMDAwMDQ2NjEgMDAwMDAgbiAKMDAwMDAwNDk4MyAwMDAwMCBuIAowMDAwMDA1NDUxIDAwMDAwIG4gCjAwMDAwMDU2NjAgMDAwMDAgbiAKMDAwMDAwNTk4MiAwMDAwMCBuIAowMDAwMDA2MTQ4IDAwMDAwIG4gCjAwMDAwMDY1NjIgMDAwMDAgbiAKMDAwMDAwNjc5OSAwMDAwMCBuIAowMDAwMDA2OTI1IDAwMDAwIG4gCjAwMDAwMDcwNjkgMDAwMDAgbiAKMDAwMDAwNzE4OCAwMDAwMCBuIAowMDAwMDA3NTE5IDAwMDAwIG4gCjAwMDAwMDc3NTUgMDAwMDAgbiAKMDAwMDAwODA0NiAwMDAwMCBuIAowMDAwMDA4MjAxIDAwMDAwIG4gCjAwMDAwMDg1MTMgMDAwMDAgbiAKMDAwMDAwODczNiAwMDAwMCBuIAowMDAwMDA4OTYwIDAwMDAwIG4gCjAwMDAwMDkwODMgMDAwMDAgbiAKMDAwMDAwOTMxNiAwMDAwMCBuIAowMDAwMDA5NzIzIDAwMDAwIG4gCjAwMDAwMDk4NjUgMDAwMDAgbiAKMDAwMDAxMDI1OCAwMDAwMCBuIAowMDAwMDEwMzQ4IDAwMDAwIG4gCjAwMDAwMTA1NTQgMDAwMDAgbiAKMDAwMDAxMDg3OCAwMDAwMCBuIAowMDAwMDExMTI1IDAwMDAwIG4gCjAwMDAwMTM4NjUgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1NCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTUgPj4Kc3RhcnR4cmVmCjE0MDIyCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:58:15.545554\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Needed for initializing the lr scheduler\n", "p = nn.Parameter(torch.empty(4, 4))\n", "optimizer = optim.Adam([p], lr=1e-3)\n", "lr_scheduler = CosineWarmupScheduler(optimizer=optimizer, warmup=100, max_iters=2000)\n", "\n", "# Plotting\n", "epochs = list(range(2000))\n", "sns.set()\n", "plt.figure(figsize=(8, 3))\n", "plt.plot(epochs, [lr_scheduler.get_lr_factor(e) for e in epochs])\n", "plt.ylabel(\"Learning rate factor\")\n", "plt.xlabel(\"Iterations (in batches)\")\n", "plt.title(\"Cosine Warm-up Learning Rate Scheduler\")\n", "plt.show()\n", "sns.reset_orig()"]}, {"cell_type": "markdown", "id": "54af6807", "metadata": {"papermill": {"duration": 0.154142, "end_time": "2021-12-04T15:58:16.048540", "exception": false, "start_time": "2021-12-04T15:58:15.894398", "status": "completed"}, "tags": []}, "source": ["In the first 100 iterations, we increase the learning rate factor from 0 to 1,\n", "whereas for all later iterations, we decay it using the cosine wave.\n", "Pre-implementations of this scheduler can be found in the popular NLP Transformer library\n", "[huggingface](https://huggingface.co/transformers/main_classes/optimizer_schedules.html?highlight=cosine#transformers.get_cosine_schedule_with_warmup)."]}, {"cell_type": "markdown", "id": "a8c7d2a2", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.154296, "end_time": "2021-12-04T15:58:16.357362", "exception": false, "start_time": "2021-12-04T15:58:16.203066", "status": "completed"}, "tags": []}, "source": ["### PyTorch Lightning Module\n", "\n", "Finally, we can embed the Transformer architecture into a PyTorch lightning module.\n", "From Tutorial 5, you know that PyTorch Lightning simplifies our training and test code,\n", "as well as structures the code nicely in separate functions.\n", "We will implement a template for a classifier based on the Transformer encoder.\n", "Thereby, we have a prediction output per sequence element.\n", "If we would need a classifier over the whole sequence, the common approach is to add an additional\n", "`[CLS]` token to the sequence, representing the classifier token.\n", "However, here we focus on tasks where we have an output per element.\n", "\n", "Additionally to the Transformer architecture, we add a small input network (maps input dimensions to model dimensions),\n", "the positional encoding, and an output network (transforms output encodings to predictions).\n", "We also add the learning rate scheduler, which takes a step each iteration instead of once per epoch.\n", "This is needed for the warmup and the smooth cosine decay.\n", "The training, validation, and test step is left empty for now and will be filled for our task-specific models."]}, {"cell_type": "code", "execution_count": 14, "id": "a392e677", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:16.678659Z", "iopub.status.busy": "2021-12-04T15:58:16.678143Z", "iopub.status.idle": "2021-12-04T15:58:16.680126Z", "shell.execute_reply": "2021-12-04T15:58:16.679738Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.168727, "end_time": "2021-12-04T15:58:16.680237", "exception": false, "start_time": "2021-12-04T15:58:16.511510", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class TransformerPredictor(pl.LightningModule):\n", " def __init__(\n", " self,\n", " input_dim,\n", " model_dim,\n", " num_classes,\n", " num_heads,\n", " num_layers,\n", " lr,\n", " warmup,\n", " max_iters,\n", " dropout=0.0,\n", " input_dropout=0.0,\n", " ):\n", " \"\"\"\n", " Args:\n", " input_dim: Hidden dimensionality of the input\n", " model_dim: Hidden dimensionality to use inside the Transformer\n", " num_classes: Number of classes to predict per sequence element\n", " num_heads: Number of heads to use in the Multi-Head Attention blocks\n", " num_layers: Number of encoder blocks to use.\n", " lr: Learning rate in the optimizer\n", " warmup: Number of warmup steps. Usually between 50 and 500\n", " max_iters: Number of maximum iterations the model is trained for. This is needed for the CosineWarmup scheduler\n", " dropout: Dropout to apply inside the model\n", " input_dropout: Dropout to apply on the input features\n", " \"\"\"\n", " super().__init__()\n", " self.save_hyperparameters()\n", " self._create_model()\n", "\n", " def _create_model(self):\n", " # Input dim -> Model dim\n", " self.input_net = nn.Sequential(\n", " nn.Dropout(self.hparams.input_dropout), nn.Linear(self.hparams.input_dim, self.hparams.model_dim)\n", " )\n", " # Positional encoding for sequences\n", " self.positional_encoding = PositionalEncoding(d_model=self.hparams.model_dim)\n", " # Transformer\n", " self.transformer = TransformerEncoder(\n", " num_layers=self.hparams.num_layers,\n", " input_dim=self.hparams.model_dim,\n", " dim_feedforward=2 * self.hparams.model_dim,\n", " num_heads=self.hparams.num_heads,\n", " dropout=self.hparams.dropout,\n", " )\n", " # Output classifier per sequence lement\n", " self.output_net = nn.Sequential(\n", " nn.Linear(self.hparams.model_dim, self.hparams.model_dim),\n", " nn.LayerNorm(self.hparams.model_dim),\n", " nn.ReLU(inplace=True),\n", " nn.Dropout(self.hparams.dropout),\n", " nn.Linear(self.hparams.model_dim, self.hparams.num_classes),\n", " )\n", "\n", " def forward(self, x, mask=None, add_positional_encoding=True):\n", " \"\"\"\n", " Args:\n", " x: Input features of shape [Batch, SeqLen, input_dim]\n", " mask: Mask to apply on the attention outputs (optional)\n", " add_positional_encoding: If True, we add the positional encoding to the input.\n", " Might not be desired for some tasks.\n", " \"\"\"\n", " x = self.input_net(x)\n", " if add_positional_encoding:\n", " x = self.positional_encoding(x)\n", " x = self.transformer(x, mask=mask)\n", " x = self.output_net(x)\n", " return x\n", "\n", " @torch.no_grad()\n", " def get_attention_maps(self, x, mask=None, add_positional_encoding=True):\n", " \"\"\"Function for extracting the attention matrices of the whole Transformer for a single batch.\n", "\n", " Input arguments same as the forward pass.\n", " \"\"\"\n", " x = self.input_net(x)\n", " if add_positional_encoding:\n", " x = self.positional_encoding(x)\n", " attention_maps = self.transformer.get_attention_maps(x, mask=mask)\n", " return attention_maps\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.Adam(self.parameters(), lr=self.hparams.lr)\n", "\n", " # We don't return the lr scheduler because we need to apply it per iteration, not per epoch\n", " self.lr_scheduler = CosineWarmupScheduler(\n", " optimizer, warmup=self.hparams.warmup, max_iters=self.hparams.max_iters\n", " )\n", " return optimizer\n", "\n", " def optimizer_step(self, *args, **kwargs):\n", " super().optimizer_step(*args, **kwargs)\n", " self.lr_scheduler.step() # Step per iteration\n", "\n", " def training_step(self, batch, batch_idx):\n", " raise NotImplementedError\n", "\n", " def validation_step(self, batch, batch_idx):\n", " raise NotImplementedError\n", "\n", " def test_step(self, batch, batch_idx):\n", " raise NotImplementedError"]}, {"cell_type": "markdown", "id": "2818f62b", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.154523, "end_time": "2021-12-04T15:58:16.990037", "exception": false, "start_time": "2021-12-04T15:58:16.835514", "status": "completed"}, "tags": []}, "source": ["## Experiments\n", "\n", "
\n", "\n", "After having finished the implementation of the Transformer architecture, we can start experimenting\n", "and apply it to various tasks.\n", "In this notebook, we will focus on two tasks: parallel Sequence-to-Sequence, and set anomaly detection.\n", "The two tasks focus on different properties of the Transformer architecture, and we go through them below.\n", "\n", "### Sequence to Sequence\n", "\n", "A Sequence-to-Sequence task represents a task where the input _and_ the output is a sequence,\n", "not necessarily of the same length.\n", "Popular tasks in this domain include machine translation and summarization.\n", "For this, we usually have a Transformer encoder for interpreting the input sequence,\n", "and a decoder for generating the output in an autoregressive manner.\n", "Here, however, we will go back to a much simpler example task and use only the encoder.\n", "Given a sequence of $N$ numbers between $0$ and $M$, the task is to reverse the input sequence.\n", "In Numpy notation, if our input is $x$, the output should be $x$[::-1].\n", "Although this task sounds very simple, RNNs can have issues with such because the task requires long-term dependencies.\n", "Transformers are built to support such, and hence, we expect it to perform very well.\n", "\n", "First, let's create a dataset class below."]}, {"cell_type": "code", "execution_count": 15, "id": "d73f1841", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:17.308123Z", "iopub.status.busy": "2021-12-04T15:58:17.307642Z", "iopub.status.idle": "2021-12-04T15:58:17.309563Z", "shell.execute_reply": "2021-12-04T15:58:17.309179Z"}, "papermill": {"duration": 0.163367, "end_time": "2021-12-04T15:58:17.309674", "exception": false, "start_time": "2021-12-04T15:58:17.146307", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ReverseDataset(data.Dataset):\n", " def __init__(self, num_categories, seq_len, size):\n", " super().__init__()\n", " self.num_categories = num_categories\n", " self.seq_len = seq_len\n", " self.size = size\n", "\n", " self.data = torch.randint(self.num_categories, size=(self.size, self.seq_len))\n", "\n", " def __len__(self):\n", " return self.size\n", "\n", " def __getitem__(self, idx):\n", " inp_data = self.data[idx]\n", " labels = torch.flip(inp_data, dims=(0,))\n", " return inp_data, labels"]}, {"cell_type": "markdown", "id": "5640c756", "metadata": {"papermill": {"duration": 0.157285, "end_time": "2021-12-04T15:58:17.623302", "exception": false, "start_time": "2021-12-04T15:58:17.466017", "status": "completed"}, "tags": []}, "source": ["We create an arbitrary number of random sequences of numbers between 0 and `num_categories-1`.\n", "The label is simply the tensor flipped over the sequence dimension.\n", "We can create the corresponding data loaders below."]}, {"cell_type": "code", "execution_count": 16, "id": "8e1d16b7", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:17.940041Z", "iopub.status.busy": "2021-12-04T15:58:17.939564Z", "iopub.status.idle": "2021-12-04T15:58:17.956756Z", "shell.execute_reply": "2021-12-04T15:58:17.957137Z"}, "papermill": {"duration": 0.177314, "end_time": "2021-12-04T15:58:17.957279", "exception": false, "start_time": "2021-12-04T15:58:17.779965", "status": "completed"}, "tags": []}, "outputs": [], "source": ["dataset = partial(ReverseDataset, 10, 16)\n", "train_loader = data.DataLoader(dataset(50000), batch_size=128, shuffle=True, drop_last=True, pin_memory=True)\n", "val_loader = data.DataLoader(dataset(1000), batch_size=128)\n", "test_loader = data.DataLoader(dataset(10000), batch_size=128)"]}, {"cell_type": "markdown", "id": "7a21b9dc", "metadata": {"papermill": {"duration": 0.156233, "end_time": "2021-12-04T15:58:18.269306", "exception": false, "start_time": "2021-12-04T15:58:18.113073", "status": "completed"}, "tags": []}, "source": ["Let's look at an arbitrary sample of the dataset:"]}, {"cell_type": "code", "execution_count": 17, "id": "aeda9084", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:18.587916Z", "iopub.status.busy": "2021-12-04T15:58:18.587443Z", "iopub.status.idle": "2021-12-04T15:58:18.590343Z", "shell.execute_reply": "2021-12-04T15:58:18.590793Z"}, "papermill": {"duration": 0.16296, "end_time": "2021-12-04T15:58:18.590927", "exception": false, "start_time": "2021-12-04T15:58:18.427967", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Input data: tensor([9, 6, 2, 0, 6, 2, 7, 9, 7, 3, 3, 4, 3, 7, 0, 9])\n", "Labels: tensor([9, 0, 7, 3, 4, 3, 3, 7, 9, 7, 2, 6, 0, 2, 6, 9])\n"]}], "source": ["inp_data, labels = train_loader.dataset[0]\n", "print(\"Input data:\", inp_data)\n", "print(\"Labels: \", labels)"]}, {"cell_type": "markdown", "id": "e5c8430c", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.156663, "end_time": "2021-12-04T15:58:18.903232", "exception": false, "start_time": "2021-12-04T15:58:18.746569", "status": "completed"}, "tags": []}, "source": ["During training, we pass the input sequence through the Transformer encoder and predict the output for each input token.\n", "We use the standard Cross-Entropy loss to perform this.\n", "Every number is represented as a one-hot vector.\n", "Remember that representing the categories as single scalars decreases the expressiveness of the model extremely\n", "as $0$ and $1$ are not closer related than $0$ and $9$ in our example.\n", "An alternative to a one-hot vector is using a learned embedding vector as it is provided by the PyTorch module `nn.Embedding`.\n", "However, using a one-hot vector with an additional linear layer as in our case has the same effect\n", "as an embedding layer (`self.input_net` maps one-hot vector to a dense vector,\n", "where each row of the weight matrix represents the embedding for a specific category).\n", "\n", "To implement the training dynamic, we create a new class inheriting from `TransformerPredictor`\n", "and overwriting the training, validation and test step functions."]}, {"cell_type": "code", "execution_count": 18, "id": "9e00bf72", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:19.222837Z", "iopub.status.busy": "2021-12-04T15:58:19.222299Z", "iopub.status.idle": "2021-12-04T15:58:19.224262Z", "shell.execute_reply": "2021-12-04T15:58:19.223880Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.164853, "end_time": "2021-12-04T15:58:19.224372", "exception": false, "start_time": "2021-12-04T15:58:19.059519", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ReversePredictor(TransformerPredictor):\n", " def _calculate_loss(self, batch, mode=\"train\"):\n", " # Fetch data and transform categories to one-hot vectors\n", " inp_data, labels = batch\n", " inp_data = F.one_hot(inp_data, num_classes=self.hparams.num_classes).float()\n", "\n", " # Perform prediction and calculate loss and accuracy\n", " preds = self.forward(inp_data, add_positional_encoding=True)\n", " loss = F.cross_entropy(preds.view(-1, preds.size(-1)), labels.view(-1))\n", " acc = (preds.argmax(dim=-1) == labels).float().mean()\n", "\n", " # Logging\n", " self.log(\"%s_loss\" % mode, loss)\n", " self.log(\"%s_acc\" % mode, acc)\n", " return loss, acc\n", "\n", " def training_step(self, batch, batch_idx):\n", " loss, _ = self._calculate_loss(batch, mode=\"train\")\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " _ = self._calculate_loss(batch, mode=\"val\")\n", "\n", " def test_step(self, batch, batch_idx):\n", " _ = self._calculate_loss(batch, mode=\"test\")"]}, {"cell_type": "markdown", "id": "5bbaf3a6", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.155556, "end_time": "2021-12-04T15:58:19.534838", "exception": false, "start_time": "2021-12-04T15:58:19.379282", "status": "completed"}, "tags": []}, "source": ["Finally, we can create a training function similar to the one we have seen in Tutorial 5 for PyTorch Lightning.\n", "We create a `pl.Trainer` object, running for $N$ epochs, logging in TensorBoard, and saving our best model based on the validation.\n", "Afterward, we test our models on the test set.\n", "An additional parameter we pass to the trainer here is `gradient_clip_val`.\n", "This clips the norm of the gradients for all parameters before taking an optimizer step and prevents the model\n", "from diverging if we obtain very high gradients at, for instance, sharp loss surfaces (see many good blog posts\n", "on gradient clipping, like [DeepAI glossary](https://deepai.org/machine-learning-glossary-and-terms/gradient-clipping)).\n", "For Transformers, gradient clipping can help to further stabilize the training during the first few iterations, and also afterward.\n", "In plain PyTorch, you can apply gradient clipping via `torch.nn.utils.clip_grad_norm_(...)`\n", "(see [documentation](https://pytorch.org/docs/stable/generated/torch.nn.utils.clip_grad_norm_.html#torch.nn.utils.clip_grad_norm_)).\n", "The clip value is usually between 0.5 and 10, depending on how harsh you want to clip large gradients.\n", "After having explained this, let's implement the training function:"]}, {"cell_type": "code", "execution_count": 19, "id": "13fba27b", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:19.861262Z", "iopub.status.busy": "2021-12-04T15:58:19.860777Z", "iopub.status.idle": "2021-12-04T15:58:19.862292Z", "shell.execute_reply": "2021-12-04T15:58:19.862689Z"}, "papermill": {"duration": 0.170942, "end_time": "2021-12-04T15:58:19.862822", "exception": false, "start_time": "2021-12-04T15:58:19.691880", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_reverse(**kwargs):\n", " # Create a PyTorch Lightning trainer with the generation callback\n", " root_dir = os.path.join(CHECKPOINT_PATH, \"ReverseTask\")\n", " os.makedirs(root_dir, exist_ok=True)\n", " trainer = pl.Trainer(\n", " default_root_dir=root_dir,\n", " callbacks=[ModelCheckpoint(save_weights_only=True, mode=\"max\", monitor=\"val_acc\")],\n", " gpus=1 if str(device).startswith(\"cuda\") else 0,\n", " max_epochs=10,\n", " gradient_clip_val=5,\n", " progress_bar_refresh_rate=1,\n", " )\n", " trainer.logger._default_hp_metric = None # Optional logging argument that we don't need\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"ReverseTask.ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model, loading...\")\n", " model = ReversePredictor.load_from_checkpoint(pretrained_filename)\n", " else:\n", " model = ReversePredictor(max_iters=trainer.max_epochs * len(train_loader), **kwargs)\n", " trainer.fit(model, train_loader, val_loader)\n", "\n", " # Test best model on validation and test set\n", " val_result = trainer.test(model, test_dataloaders=val_loader, verbose=False)\n", " test_result = trainer.test(model, test_dataloaders=test_loader, verbose=False)\n", " result = {\"test_acc\": test_result[0][\"test_acc\"], \"val_acc\": val_result[0][\"test_acc\"]}\n", "\n", " model = model.to(device)\n", " return model, result"]}, {"cell_type": "markdown", "id": "11c5e6ce", "metadata": {"papermill": {"duration": 0.157063, "end_time": "2021-12-04T15:58:20.175228", "exception": false, "start_time": "2021-12-04T15:58:20.018165", "status": "completed"}, "tags": []}, "source": ["Finally, we can train the model.\n", "In this setup, we will use a single encoder block and a single head in the Multi-Head Attention.\n", "This is chosen because of the simplicity of the task, and in this case, the attention can actually be interpreted\n", "as an \"explanation\" of the predictions (compared to the other papers above dealing with deep Transformers)."]}, {"cell_type": "code", "execution_count": 20, "id": "a41d7448", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:20.491778Z", "iopub.status.busy": "2021-12-04T15:58:20.491301Z", "iopub.status.idle": "2021-12-04T15:58:24.308685Z", "shell.execute_reply": "2021-12-04T15:58:24.308229Z"}, "papermill": {"duration": 3.977688, "end_time": "2021-12-04T15:58:24.308813", "exception": false, "start_time": "2021-12-04T15:58:20.331125", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=1)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:901: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Missing logger folder: saved_models/Transformers/ReverseTask/lightning_logs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, test_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b9ed1cfedbeb4586bab29909930e47b1", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "89c0145b83d34648bacf9da6ad15d74d", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["reverse_model, reverse_result = train_reverse(\n", " input_dim=train_loader.dataset.num_categories,\n", " model_dim=32,\n", " num_heads=1,\n", " num_classes=train_loader.dataset.num_categories,\n", " num_layers=1,\n", " dropout=0.0,\n", " lr=5e-4,\n", " warmup=50,\n", ")"]}, {"cell_type": "markdown", "id": "ccc85090", "metadata": {"papermill": {"duration": 0.163772, "end_time": "2021-12-04T15:58:24.638088", "exception": false, "start_time": "2021-12-04T15:58:24.474316", "status": "completed"}, "tags": []}, "source": ["The warning of PyTorch Lightning regarding the number of workers can be ignored for now.\n", "As the data set is so simple and the `__getitem__` finishes a neglectable time, we don't need subprocesses\n", "to provide us the data (in fact, more workers can slow down the training as we have communication overhead among processes/threads).\n", "First, let's print the results:"]}, {"cell_type": "code", "execution_count": 21, "id": "02e867c0", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:24.972467Z", "iopub.status.busy": "2021-12-04T15:58:24.971991Z", "iopub.status.idle": "2021-12-04T15:58:24.974029Z", "shell.execute_reply": "2021-12-04T15:58:24.974648Z"}, "papermill": {"duration": 0.171511, "end_time": "2021-12-04T15:58:24.974782", "exception": false, "start_time": "2021-12-04T15:58:24.803271", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Val accuracy: 100.00%\n", "Test accuracy: 100.00%\n"]}], "source": ["print(\"Val accuracy: %4.2f%%\" % (100.0 * reverse_result[\"val_acc\"]))\n", "print(\"Test accuracy: %4.2f%%\" % (100.0 * reverse_result[\"test_acc\"]))"]}, {"cell_type": "markdown", "id": "389ff9fb", "metadata": {"papermill": {"duration": 0.164894, "end_time": "2021-12-04T15:58:25.304480", "exception": false, "start_time": "2021-12-04T15:58:25.139586", "status": "completed"}, "tags": []}, "source": ["As we would have expected, the Transformer can correctly solve the task.\n", "However, how does the attention in the Multi-Head Attention block looks like for an arbitrary input?\n", "Let's try to visualize it below."]}, {"cell_type": "code", "execution_count": 22, "id": "f740e35c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:25.645172Z", "iopub.status.busy": "2021-12-04T15:58:25.644677Z", "iopub.status.idle": "2021-12-04T15:58:25.650646Z", "shell.execute_reply": "2021-12-04T15:58:25.650153Z"}, "papermill": {"duration": 0.178894, "end_time": "2021-12-04T15:58:25.650756", "exception": false, "start_time": "2021-12-04T15:58:25.471862", "status": "completed"}, "tags": []}, "outputs": [], "source": ["data_input, labels = next(iter(val_loader))\n", "inp_data = F.one_hot(data_input, num_classes=reverse_model.hparams.num_classes).float()\n", "inp_data = inp_data.to(device)\n", "attention_maps = reverse_model.get_attention_maps(inp_data)"]}, {"cell_type": "markdown", "id": "82e9b36d", "metadata": {"papermill": {"duration": 0.164929, "end_time": "2021-12-04T15:58:25.982199", "exception": false, "start_time": "2021-12-04T15:58:25.817270", "status": "completed"}, "tags": []}, "source": ["The object `attention_maps` is a list of length $N$ where $N$ is the number of layers.\n", "Each element is a tensor of shape [Batch, Heads, SeqLen, SeqLen], which we can verify below."]}, {"cell_type": "code", "execution_count": 23, "id": "ba03c69a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:26.324791Z", "iopub.status.busy": "2021-12-04T15:58:26.324320Z", "iopub.status.idle": "2021-12-04T15:58:26.326971Z", "shell.execute_reply": "2021-12-04T15:58:26.326543Z"}, "papermill": {"duration": 0.178216, "end_time": "2021-12-04T15:58:26.327081", "exception": false, "start_time": "2021-12-04T15:58:26.148865", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/plain": ["torch.Size([128, 1, 16, 16])"]}, "execution_count": 23, "metadata": {}, "output_type": "execute_result"}], "source": ["attention_maps[0].shape"]}, {"cell_type": "markdown", "id": "1ff46b13", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.16452, "end_time": "2021-12-04T15:58:26.658828", "exception": false, "start_time": "2021-12-04T15:58:26.494308", "status": "completed"}, "tags": []}, "source": ["Next, we will write a plotting function that takes as input the sequences, attention maps, and an index\n", "indicating for which batch element we want to visualize the attention map.\n", "We will create a plot where over rows, we have different layers, while over columns, we show the different heads.\n", "Remember that the softmax has been applied for each row separately."]}, {"cell_type": "code", "execution_count": 24, "id": "f23fa88e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:26.999173Z", "iopub.status.busy": "2021-12-04T15:58:26.998690Z", "iopub.status.idle": "2021-12-04T15:58:27.000240Z", "shell.execute_reply": "2021-12-04T15:58:27.000618Z"}, "papermill": {"duration": 0.176536, "end_time": "2021-12-04T15:58:27.000749", "exception": false, "start_time": "2021-12-04T15:58:26.824213", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def plot_attention_maps(input_data, attn_maps, idx=0):\n", " if input_data is not None:\n", " input_data = input_data[idx].detach().cpu().numpy()\n", " else:\n", " input_data = np.arange(attn_maps[0][idx].shape[-1])\n", " attn_maps = [m[idx].detach().cpu().numpy() for m in attn_maps]\n", "\n", " num_heads = attn_maps[0].shape[0]\n", " num_layers = len(attn_maps)\n", " seq_len = input_data.shape[0]\n", " fig_size = 4 if num_heads == 1 else 3\n", " fig, ax = plt.subplots(num_layers, num_heads, figsize=(num_heads * fig_size, num_layers * fig_size))\n", " if num_layers == 1:\n", " ax = [ax]\n", " if num_heads == 1:\n", " ax = [[a] for a in ax]\n", " for row in range(num_layers):\n", " for column in range(num_heads):\n", " ax[row][column].imshow(attn_maps[row][column], origin=\"lower\", vmin=0)\n", " ax[row][column].set_xticks(list(range(seq_len)))\n", " ax[row][column].set_xticklabels(input_data.tolist())\n", " ax[row][column].set_yticks(list(range(seq_len)))\n", " ax[row][column].set_yticklabels(input_data.tolist())\n", " ax[row][column].set_title(\"Layer %i, Head %i\" % (row + 1, column + 1))\n", " fig.subplots_adjust(hspace=0.5)\n", " plt.show()"]}, {"cell_type": "markdown", "id": "5575de2c", "metadata": {"papermill": {"duration": 0.165585, "end_time": "2021-12-04T15:58:27.339327", "exception": false, "start_time": "2021-12-04T15:58:27.173742", "status": "completed"}, "tags": []}, "source": ["Finally, we can plot the attention map of our trained Transformer on the reverse task:"]}, {"cell_type": "code", "execution_count": 25, "id": "70711ff5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:27.678174Z", "iopub.status.busy": "2021-12-04T15:58:27.677704Z", "iopub.status.idle": "2021-12-04T15:58:28.093751Z", "shell.execute_reply": "2021-12-04T15:58:28.094142Z"}, "papermill": {"duration": 0.587062, "end_time": "2021-12-04T15:58:28.094303", "exception": false, "start_time": "2021-12-04T15:58:27.507241", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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MzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvSCAxOCAwIFIg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MDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAxMzM0IDAwMDAwIG4gCjAwMDAwMDg1MzMgMDAwMDAgbiAKMDAwMDAwNzAyNSAwMDAwMCBuIAowMDAwMDA2ODI1IDAwMDAwIG4gCjAwMDAwMDY0MDcgMDAwMDAgbiAKMDAwMDAwODA3OCAwMDAwMCBuIAowMDAwMDAxMzc0IDAwMDAwIG4gCjAwMDAwMDE1MjUgMDAwMDAgbiAKMDAwMDAwMTY1OCAwMDAwMCBuIAowMDAwMDAyMDM4IDAwMDAwIG4gCjAwMDAwMDIxNzggMDAwMDAgbiAKMDAwMDAwMjQ4MiAwMDAwMCBuIAowMDAwMDAyODA0IDAwMDAwIG4gCjAwMDAwMDMyNzIgMDAwMDAgbiAKMDAwMDAwMzU5NCAwMDAwMCBuIAowMDAwMDAzNzYwIDAwMDAwIG4gCjAwMDAwMDQxNTUgMDAwMDAgbiAKMDAwMDAwNDMxMCAwMDAwMCBuIAowMDAwMDA0NTQzIDAwMDAwIG4gCjAwMDAwMDQ2ODUgMDAwMDAgbiAKMDAwMDAwNTA3OCAwMDAwMCBuIAowMDAwMDA1MTY4IDAwMDAwIG4gCjAwMDAwMDU1ODEgMDAwMDAgbiAKMDAwMDAwNTkwNSAwMDAwMCBuIAowMDAwMDA2MTE5IDAwMDAwIG4gCjAwMDAwMDk0NDIgMDAwMDAgbiAKMDAwMDAwOTUyMiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM4IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOSA+PgpzdGFydHhyZWYKOTY3OQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:58:27.918905\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["plot_attention_maps(data_input, attention_maps, idx=0)"]}, {"cell_type": "markdown", "id": "163001ca", "metadata": {"papermill": {"duration": 0.169201, "end_time": "2021-12-04T15:58:28.432387", "exception": false, "start_time": "2021-12-04T15:58:28.263186", "status": "completed"}, "tags": []}, "source": ["The model has learned to attend to the token that is on the flipped index of itself.\n", "Hence, it actually does what we intended it to do.\n", "We see that it however also pays some attention to values close to the flipped index.\n", "This is because the model doesn't need the perfect, hard attention to solve this problem,\n", "but is fine with this approximate, noisy attention map.\n", "The close-by indices are caused by the similarity of the positional encoding,\n", "which we also intended with the positional encoding."]}, {"cell_type": "markdown", "id": "d8ac4d91", "metadata": {"papermill": {"duration": 0.169547, "end_time": "2021-12-04T15:58:28.770515", "exception": false, "start_time": "2021-12-04T15:58:28.600968", "status": "completed"}, "tags": []}, "source": ["### Set Anomaly Detection\n", "\n", "Besides sequences, sets are another data structure that is relevant for many applications.\n", "In contrast to sequences, elements are unordered in a set.\n", "RNNs can only be applied on sets by assuming an order in the data, which however biases the model towards\n", "a non-existing order in the data.\n", "[Vinyals et al.\n", "(2015)](https://arxiv.org/abs/1511.06391) and other papers have shown that the assumed order can have a significant\n", "impact on the model's performance, and hence, we should try to not use RNNs on sets.\n", "Ideally, our model should be permutation-equivariant/invariant such that the output is the same no matter how we sort the elements in a set.\n", "\n", "Transformers offer the perfect architecture for this as the Multi-Head Attention is permutation-equivariant, and thus,\n", "outputs the same values no matter in what order we enter the inputs (inputs and outputs are permuted equally).\n", "The task we are looking at for sets is _Set Anomaly Detection_ which means that we try to find the element(s)\n", "in a set that does not fit the others.\n", "In the research community, the common application of anomaly detection is performed on a set of images,\n", "where $N-1$ images belong to the same category/have the same high-level features while one belongs to another category.\n", "Note that category does not necessarily have to relate to a class in a standard classification problem,\n", "but could be the combination of multiple features.\n", "For instance, on a face dataset, this could be people with glasses, male, beard, etc.\n", "An example of distinguishing different animals can be seen below.\n", "The first four images show foxes, while the last represents a different animal.\n", "We want to recognize that the last image shows a different animal, but it is not relevant which class of animal it is.\n", "\n", "
\n", "\n", "In this tutorial, we will use the CIFAR100 dataset.\n", "CIFAR100 has 600 images for 100 classes each with a resolution of 32x32, similar to CIFAR10.\n", "The larger amount of classes requires the model to attend to specific features in the images instead\n", "of coarse features as in CIFAR10, therefore making the task harder.\n", "We will show the model a set of 9 images of one class, and 1 image from another class.\n", "The task is to find the image that is from a different class than the other images.\n", "Using the raw images directly as input to the Transformer is not a good idea, because it is not translation\n", "invariant as a CNN, and would need to learn to detect image features from high-dimensional input first of all.\n", "Instead, we will use a pre-trained ResNet34 model from the torchvision package to obtain high-level,\n", "low-dimensional features of the images.\n", "The ResNet model has been pre-trained on the [ImageNet](http://image-net.org/) dataset which contains\n", "1 million images of 1k classes and varying resolutions.\n", "However, during training and testing, the images are usually scaled to a resolution of 224x224,\n", "and hence we rescale our CIFAR images to this resolution as well.\n", "Below, we will load the dataset, and prepare the data for being processed by the ResNet model."]}, {"cell_type": "code", "execution_count": 26, "id": "5ff1954f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:29.115622Z", "iopub.status.busy": "2021-12-04T15:58:29.115139Z", "iopub.status.idle": "2021-12-04T15:58:34.276229Z", "shell.execute_reply": "2021-12-04T15:58:34.275775Z"}, "papermill": {"duration": 5.338193, "end_time": "2021-12-04T15:58:34.276369", "exception": false, "start_time": "2021-12-04T15:58:28.938176", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz to /__w/1/s/.datasets/cifar-100-python.tar.gz\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "efc763b5cd4e4ed9b3e3881bad434e2b", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/169001437 [00:00150MB free disk space.\n", "So it is recommended to run this only on a local computer if you have enough free disk and a GPU (GoogleColab is fine for this).\n", "If you do not have a GPU, you can download the features from the\n", "[GoogleDrive folder](https://drive.google.com/drive/folders/1DF7POc6j03pRiWQPWSl5QJX5iY-xK0sV?usp=sharing)."]}, {"cell_type": "code", "execution_count": 28, "id": "68fcd0ab", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:58:37.486821Z", "iopub.status.busy": "2021-12-04T15:58:37.484217Z", "iopub.status.idle": "2021-12-04T15:59:11.196064Z", "shell.execute_reply": "2021-12-04T15:59:11.196470Z"}, "papermill": {"duration": 33.893166, "end_time": "2021-12-04T15:59:11.196649", "exception": false, "start_time": "2021-12-04T15:58:37.303483", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "336b58f1c17b4c2998f77978c46f92b2", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/391 [00:00= anomaly_label:\n", " set_label += 1\n", "\n", " # Sample images from the class determined above\n", " img_indices = np.random.choice(self.img_idx_by_label.shape[1], size=self.set_size, replace=False)\n", " img_indices = self.img_idx_by_label[set_label, img_indices]\n", " return img_indices\n", "\n", " def __len__(self):\n", " return self.img_feats.shape[0]\n", "\n", " def __getitem__(self, idx):\n", " anomaly = self.img_feats[idx]\n", " if self.train: # If train => sample\n", " img_indices = self.sample_img_set(self.labels[idx])\n", " else: # If test => use pre-generated ones\n", " img_indices = self.test_sets[idx]\n", "\n", " # Concatenate images. The anomaly is always the last image for simplicity\n", " img_set = torch.cat([self.img_feats[img_indices], anomaly[None]], dim=0)\n", " indices = torch.cat([img_indices, torch.LongTensor([idx])], dim=0)\n", " label = img_set.shape[0] - 1\n", "\n", " # We return the indices of the images for visualization purpose. \"Label\" is the index of the anomaly\n", " return img_set, indices, label"]}, {"cell_type": "markdown", "id": "2fc781ed", "metadata": {"papermill": {"duration": 0.177543, "end_time": "2021-12-04T15:59:13.783170", "exception": false, "start_time": "2021-12-04T15:59:13.605627", "status": "completed"}, "tags": []}, "source": ["Next, we can setup our datasets and data loaders below.\n", "Here, we will use a set size of 10, i.e. 9 images from one category + 1 anomaly.\n", "Feel free to change it if you want to experiment with the sizes."]}, {"cell_type": "code", "execution_count": 32, "id": "d089f9e7", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:14.150564Z", "iopub.status.busy": "2021-12-04T15:59:14.150080Z", "iopub.status.idle": "2021-12-04T15:59:15.603980Z", "shell.execute_reply": "2021-12-04T15:59:15.603533Z"}, "papermill": {"duration": 1.641455, "end_time": "2021-12-04T15:59:15.604117", "exception": false, "start_time": "2021-12-04T15:59:13.962662", "status": "completed"}, "tags": []}, "outputs": [], "source": ["SET_SIZE = 10\n", "test_labels = torch.LongTensor(test_set.targets)\n", "\n", "train_anom_dataset = SetAnomalyDataset(train_feats, train_labels, set_size=SET_SIZE, train=True)\n", "val_anom_dataset = SetAnomalyDataset(val_feats, val_labels, set_size=SET_SIZE, train=False)\n", "test_anom_dataset = SetAnomalyDataset(test_feats, test_labels, set_size=SET_SIZE, train=False)\n", "\n", "train_anom_loader = data.DataLoader(\n", " train_anom_dataset, batch_size=64, shuffle=True, drop_last=True, num_workers=4, pin_memory=True\n", ")\n", "val_anom_loader = data.DataLoader(val_anom_dataset, batch_size=64, shuffle=False, drop_last=False, num_workers=4)\n", "test_anom_loader = data.DataLoader(test_anom_dataset, batch_size=64, shuffle=False, drop_last=False, num_workers=4)"]}, {"cell_type": "markdown", "id": "70f843f4", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.178644, "end_time": "2021-12-04T15:59:15.961336", "exception": false, "start_time": "2021-12-04T15:59:15.782692", "status": "completed"}, "tags": []}, "source": ["To understand the dataset a little better, we can plot below a few sets from the test dataset.\n", "Each row shows a different input set, where the first 9 are from the same class."]}, {"cell_type": "code", "execution_count": 33, "id": "cf7e1059", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:16.325926Z", "iopub.status.busy": "2021-12-04T15:59:16.325451Z", "iopub.status.idle": "2021-12-04T15:59:17.628117Z", "shell.execute_reply": "2021-12-04T15:59:17.628507Z"}, "papermill": {"duration": 1.488315, "end_time": "2021-12-04T15:59:17.628671", "exception": false, "start_time": "2021-12-04T15:59:16.140356", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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IDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvQSAxOCAwIFIgL0MgMTkgMCBSIC9GIDIwIDAgUiAvSSAyMSAwIFIgL1IgMjIgMCBSIC9hIDIzIDAgUiAvZSAyNCAwIFIKL2wgMjUgMCBSIC9tIDI2IDAgUiAvbiAyNyAwIFIgL28gMjggMCBSIC9vbmUgMjkgMCBSIC9wIDMwIDAgUiAvcyAzMSAwIFIKL3NwYWNlIDMyIDAgUiAveCAzMyAwIFIgL3kgMzQgMCBSIC96ZXJvIDM1IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50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urp9Mw5dsDEE38TfapXW29cUDhGREJjJB27Ro2z05koagL5pJNWqUg1QReSd+yDl09Xu9vr67d3NF3e3d63yf5ksxE5B0XD42vsZ273rxSpYGxO6dC5b1MJqxD7W5eEBVtMOUAoItn7Ehs/qtIXWSsqIoopCpI4VTatayyM6Di5ybKUdB8RKqQo0cEijNXLomYPjGDi4RgxNBK2JhxF8QHPKvDAA1KYPrda4XBWtkfAbgGrXkmr5bcDWK/lyySOPbmhNM4zGZIqqiiJqis7HwQfH7F1T0YgxDt45MJim6dOnT0c7lVpN2ZRNCaARPCATe+9vrm/evn2bUnryvlaZplm09/KqCiI04SXuJDkttNZm3K131lnPfzmOMbx+/eY3f/23acnfjz8cD/vpdDjvvz+++wMHQUAUw6rOIBKjmWiXglFCI6wmWUqVolaD9zEERiYjsibwUWutYrVJkFWzuOPbL3d+GwHQDVrkCJZrSWmeSxIpsMx1OpUiikzOuxAjO8fOD+M4bjbMQChIwIyAgI5QCXqvdlUrwA7sub2nlnqsx/mUclIVizGDuWG48VsnVEURgIewDcGjpWX6lE7vrS6mRSSnkpac50VSKaUWEQVFExbxtWhNueQitQzburmupdg8qXPj67e3IlJrLjnvHxYXwAVEOBAujp7QOKUqaiGOb25249evrqgEh+aoISc9kwpk+DzS/XD/wEdeavU+HA7Hw2k6HKday9NpQkSpejxNSykCQN4579G5onqcliKyP55UNef8+HSal5rruQcD2Dkfg6gYWhyGYRhjCOxdVfEhDOPo2DOzdz60XFwToAmOHHMjG0LUKjmnItXAkJlb25cIODbHcNH81uB4p+Pxh3ffZdXv3n83bHYhjNOUTtMyhGG73e622xD8fv+03+9zSdA0OYbYRCkBwQxKUzMMzocwhugAIZcP7959/PSjAZpDYzikY/ARAFNaTqfp8elxv9+bmvPu5vr65vZ2u9uGENOy5JR7N59zyASmx/3j97//+8eP78o87QbXM3UX482bt2nk0x//MD0cii2liBXdhDCM2xhCy1G7AJstx+AcqSDeXF19/bNvgOk0JcvO1ZGdA4+fHt8j6+766vrmZn883T89nJbHw/EJlGvCw+MyHwsYajUASK5Y67ilDbPlVASqAyPCSpZRZivOA3pkwhDh6jqURPef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xwzAXURVqwIYU0mcmXEREhAzFAMFZCQnzkyO6G5ISKmnlB0JgBzJdBaZmqiIUIBDDrhwI55GIk8BeXj870du5+JDBhmBkQiIwNzJQNfM5bIsFt9qxfeXoyEAYYCobkAGikyL2AIiICwCPu6rZ+CxM5/s+pkVj0Mvs+C7VlH4AK21cZ7v7u6vb27v7u53+8M0N1ElIk45wIOY07gya9xPNTBcSolgKJCDpXqAmBNHCocTES3kgSZNR21tWoSH2jyp3Az9ZrMhxHJx8cH9/s71//LFyz/9wct3777+9ttye/PNbm8qVU10Bm1okICx0abxlrqr1F1h7hVsPtweDu/beFeGy4RWcipdl0pBZgdQ0Wkad/e72+ub25tbM0spd313cXHR933OCYlTLgVREXM3pG4QcGBASoi82PWgQ6iKamvCXBKnUrq+65iLO4r4DRzOLsUBDE1JnSC6sTZwAa0w7XE+YqsgCmpurmpNdRabmjVHC2kcAEqMhPM01/koAu6wlNEBEqec8zAMV88uzCuAE4IhgIE/tCaKibS47Babfbj86KgQ3qktGyQ4hDv3Tzft7t5E7u937969e/Pm7fX1Ta2ViMIG393dffvtt998883d7a2qBQl8GIbtdvv69Seff/7Fixcvuq57//767ds37969C8O28MxLWarIx/Fw2O92u2DabzabUxmxqk7TFDmm3960P06vukcv85ubm91+f7HdXj17Fl6LNAGH0nUXFxex9apIE6nzPM/zOE21VhMlopCBC27aOQrYllFLKe7gbvM87Xb7u7vbcZxUJedU6zyOx91uNwx91Jp+MJbNARFzTtvt9pNPPvnjP/7B+/fv3717P42zqhGslv1swflJRvrMQkQws/CHg2S3BjOw4udB13d3aTLCOE9jq/Xi59sgDBLR1dVVxO6/0cCfxy1nu9waYsFintcwb71cRGYqOfx1FFVTxQdccfncCbWEkIBNS6UuRHdxSk5sS107LKHcgyzE+snFmHtk7c9O8emFnC7nCUuOkB7l3h9z5h+u/RQ7xgHpoc5mCSzP/nkOJC8wzFocsHScAiLmrus32+102Hb9nviwJtrO786jUVK37S/ihNeKzUzIAGAUyTs4wdmUqFDBzB6S2Y5uiEAEnDAxEDuYmYYit5gDIKFnwEKhpkM5ASZxNCdHQuTMiYP84K5I5mROYlH+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YmoKMjQzNjk0CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozNyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMjA0MTY1OTE3KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDM4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMjUxMTE3IDAwMDAwIG4gCjAwMDAwMDY5NjYgMDAwMDAgbiAKMDAwMDAwNjk5OCAwMDAwMCBuIAowMDAwMDA3MDk3IDAwMDAwIG4gCjAwMDAwMDcxMTggMDAwMDAgbiAKMDAwMDAwNzEzOSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAwMDczMyAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3MTMgMDAwMDAgbiAKMDAwMDAwNzE3MSAwMDAwMCBuIAowMDAwMDA1NzAyIDAwMDAwIG4gCjAwMDAwMDU1MDIgMDAwMDAgbiAKMDAwMDAwNTEwNiAwMDAwMCBuIAowMDAwMDA2NzU1IDAwMDAwIG4gCjAwMDAwMDA3NTMgMDAwMDAgbiAKMDAwMDAwMDkxNiAwMDAwMCBuIAowMDAwMDAxMjI0IDAwMDAwIG4gCjAwMDAwMDEzNzIgMDAwMDAgbiAKMDAwMDAwMTQ5NSAwMDAwMCBuIAowMDAwMDAxODAwIDAwMDAwIG4gCjAwMDAwMDIxODAgMDAwMDAgbiAKMDAwMDAwMjUwMiAwMDAwMCBuIAowMDAwMDAyNjIxIDAwMDAwIG4gCjAwMDAwMDI5NTIgMDAwMDAgbiAKMDAwMDAwMzE4OCAwMDAwMCBuIAowMDAwMDAzNDc5IDAwMDAwIG4gCjAwMDAwMDM2MzQgMDAwMDAgbiAKMDAwMDAwMzk0NiAwMDAwMCBuIAowMDAwMDA0MzUzIDAwMDAwIG4gCjAwMDAwMDQ0NDMgMDAwMDAgbiAKMDAwMDAwNDYwNCAwMDAwMCBuIAowMDAwMDA0ODE4IDAwMDAwIG4gCjAwMDAyNTEwOTQgMDAwMDAgbiAKMDAwMDI1MTE3NyAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOCA+PgpzdGFydHhyZWYKMjUxMzM0CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:59:16.867016\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["def visualize_exmp(indices, orig_dataset):\n", " images = [orig_dataset[idx][0] for idx in indices.reshape(-1)]\n", " images = torch.stack(images, dim=0)\n", " images = images * TORCH_DATA_STD + TORCH_DATA_MEANS\n", "\n", " img_grid = torchvision.utils.make_grid(images, nrow=SET_SIZE, normalize=True, pad_value=0.5, padding=16)\n", " img_grid = img_grid.permute(1, 2, 0)\n", "\n", " plt.figure(figsize=(12, 8))\n", " plt.title(\"Anomaly examples on CIFAR100\")\n", " plt.imshow(img_grid)\n", " plt.axis(\"off\")\n", " plt.show()\n", " plt.close()\n", "\n", "\n", "_, indices, _ = next(iter(test_anom_loader))\n", "visualize_exmp(indices[:4], test_set)"]}, {"cell_type": "markdown", "id": "48c08ca7", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.1952, "end_time": "2021-12-04T15:59:18.016890", "exception": false, "start_time": "2021-12-04T15:59:17.821690", "status": "completed"}, "tags": []}, "source": ["We can already see that for some sets the task might be easier than for others.\n", "Difficulties can especially arise if the anomaly is in a different, but yet visually similar class\n", "(e.g. train vs bus, flour vs worm, etc.\n", ").\n", "\n", "After having prepared the data, we can look closer at the model.\n", "Here, we have a classification of the whole set.\n", "For the prediction to be permutation-equivariant, we will output one logit for each image.\n", "Over these logits, we apply a softmax and train the anomaly image to have the highest score/probability.\n", "This is a bit different than a standard classification layer as the softmax is applied over images,\n", "not over output classes in the classical sense.\n", "However, if we swap two images in their position, we effectively swap their position in the output softmax.\n", "Hence, the prediction is equivariant with respect to the input.\n", "We implement this idea below in the subclass of the Transformer Lightning module."]}, {"cell_type": "code", "execution_count": 34, "id": "cad86c76", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:18.412870Z", "iopub.status.busy": "2021-12-04T15:59:18.412388Z", "iopub.status.idle": "2021-12-04T15:59:18.414272Z", "shell.execute_reply": "2021-12-04T15:59:18.413887Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.20176, "end_time": "2021-12-04T15:59:18.414384", "exception": false, "start_time": "2021-12-04T15:59:18.212624", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class AnomalyPredictor(TransformerPredictor):\n", " def _calculate_loss(self, batch, mode=\"train\"):\n", " img_sets, _, labels = batch\n", " # No positional encodings as it is a set, not a sequence!\n", " preds = self.forward(img_sets, add_positional_encoding=False)\n", " preds = preds.squeeze(dim=-1) # Shape: [Batch_size, set_size]\n", " loss = F.cross_entropy(preds, labels) # Softmax/CE over set dimension\n", " acc = (preds.argmax(dim=-1) == labels).float().mean()\n", " self.log(\"%s_loss\" % mode, loss)\n", " self.log(\"%s_acc\" % mode, acc, on_step=False, on_epoch=True)\n", " return loss, acc\n", "\n", " def training_step(self, batch, batch_idx):\n", " loss, _ = self._calculate_loss(batch, mode=\"train\")\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " _ = self._calculate_loss(batch, mode=\"val\")\n", "\n", " def test_step(self, batch, batch_idx):\n", " _ = self._calculate_loss(batch, mode=\"test\")"]}, {"cell_type": "markdown", "id": "d732d394", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.190651, "end_time": "2021-12-04T15:59:18.796683", "exception": false, "start_time": "2021-12-04T15:59:18.606032", "status": "completed"}, "tags": []}, "source": ["Finally, we write our train function below.\n", "It has the exact same structure as the reverse task one, hence not much of an explanation is needed here."]}, {"cell_type": "code", "execution_count": 35, "id": "261279ac", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:19.193945Z", "iopub.status.busy": "2021-12-04T15:59:19.193455Z", "iopub.status.idle": "2021-12-04T15:59:19.195328Z", "shell.execute_reply": "2021-12-04T15:59:19.194922Z"}, "papermill": {"duration": 0.207853, "end_time": "2021-12-04T15:59:19.195437", "exception": false, "start_time": "2021-12-04T15:59:18.987584", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_anomaly(**kwargs):\n", " # Create a PyTorch Lightning trainer with the generation callback\n", " root_dir = os.path.join(CHECKPOINT_PATH, \"SetAnomalyTask\")\n", " os.makedirs(root_dir, exist_ok=True)\n", " trainer = pl.Trainer(\n", " default_root_dir=root_dir,\n", " callbacks=[ModelCheckpoint(save_weights_only=True, mode=\"max\", monitor=\"val_acc\")],\n", " gpus=1 if str(device).startswith(\"cuda\") else 0,\n", " max_epochs=100,\n", " gradient_clip_val=2,\n", " progress_bar_refresh_rate=1,\n", " )\n", " trainer.logger._default_hp_metric = None # Optional logging argument that we don't need\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"SetAnomalyTask.ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model, loading...\")\n", " model = AnomalyPredictor.load_from_checkpoint(pretrained_filename)\n", " else:\n", " model = AnomalyPredictor(max_iters=trainer.max_epochs * len(train_anom_loader), **kwargs)\n", " trainer.fit(model, train_anom_loader, val_anom_loader)\n", " model = AnomalyPredictor.load_from_checkpoint(trainer.checkpoint_callback.best_model_path)\n", "\n", " # Test best model on validation and test set\n", " train_result = trainer.test(model, test_dataloaders=train_anom_loader, verbose=False)\n", " val_result = trainer.test(model, test_dataloaders=val_anom_loader, verbose=False)\n", " test_result = trainer.test(model, test_dataloaders=test_anom_loader, verbose=False)\n", " result = {\n", " \"test_acc\": test_result[0][\"test_acc\"],\n", " \"val_acc\": val_result[0][\"test_acc\"],\n", " \"train_acc\": train_result[0][\"test_acc\"],\n", " }\n", "\n", " model = model.to(device)\n", " return model, result"]}, {"cell_type": "markdown", "id": "3a251275", "metadata": {"papermill": {"duration": 0.192005, "end_time": "2021-12-04T15:59:19.580159", "exception": false, "start_time": "2021-12-04T15:59:19.388154", "status": "completed"}, "tags": []}, "source": ["Let's finally train our model.\n", "We will use 4 layers with 4 attention heads each.\n", "The hidden dimensionality of the model is 256, and we use a dropout of 0.1 throughout the model for good regularization.\n", "Note that we also apply the dropout on the input features, as this makes the model more robust against\n", "image noise and generalizes better.\n", "Again, we use warmup to slowly start our model training."]}, {"cell_type": "code", "execution_count": 36, "id": "adc7b1bb", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:19.979138Z", "iopub.status.busy": "2021-12-04T15:59:19.978661Z", "iopub.status.idle": "2021-12-04T15:59:25.752247Z", "shell.execute_reply": "2021-12-04T15:59:25.752638Z"}, "papermill": {"duration": 5.981928, "end_time": "2021-12-04T15:59:25.752808", "exception": false, "start_time": "2021-12-04T15:59:19.770880", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=1)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:901: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Missing logger folder: saved_models/Transformers/SetAnomalyTask/lightning_logs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:453: UserWarning: Your `test_dataloader` has `shuffle=True`,it is strongly recommended that you turn this off for val/test/predict dataloaders.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "9c0f4106b2fa430d807e9c34f98e5bd2", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "983722707bb64460bfbdc38056f7d32d", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "ab0dc6637c5348a9839eded7ec182961", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["anomaly_model, anomaly_result = train_anomaly(\n", " input_dim=train_anom_dataset.img_feats.shape[-1],\n", " model_dim=256,\n", " num_heads=4,\n", " num_classes=1,\n", " num_layers=4,\n", " dropout=0.1,\n", " input_dropout=0.1,\n", " lr=5e-4,\n", " warmup=100,\n", ")"]}, {"cell_type": "markdown", "id": "b752953a", "metadata": {"papermill": {"duration": 0.212151, "end_time": "2021-12-04T15:59:26.177915", "exception": false, "start_time": "2021-12-04T15:59:25.965764", "status": "completed"}, "tags": []}, "source": ["We can print the achieved accuracy below."]}, {"cell_type": "code", "execution_count": 37, "id": "0c9ae3d1", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:26.590345Z", "iopub.status.busy": "2021-12-04T15:59:26.589872Z", "iopub.status.idle": "2021-12-04T15:59:26.592352Z", "shell.execute_reply": "2021-12-04T15:59:26.591950Z"}, "papermill": {"duration": 0.211453, "end_time": "2021-12-04T15:59:26.592461", "exception": false, "start_time": "2021-12-04T15:59:26.381008", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Train accuracy: 96.33%\n", "Val accuracy: 95.92%\n", "Test accuracy: 94.41%\n"]}], "source": ["print(\"Train accuracy: %4.2f%%\" % (100.0 * anomaly_result[\"train_acc\"]))\n", "print(\"Val accuracy: %4.2f%%\" % (100.0 * anomaly_result[\"val_acc\"]))\n", "print(\"Test accuracy: %4.2f%%\" % (100.0 * anomaly_result[\"test_acc\"]))"]}, {"cell_type": "markdown", "id": "93d2718b", "metadata": {"papermill": {"duration": 0.201663, "end_time": "2021-12-04T15:59:26.995547", "exception": false, "start_time": "2021-12-04T15:59:26.793884", "status": "completed"}, "tags": []}, "source": ["With ~94% validation and test accuracy, the model generalizes quite well.\n", "It should be noted that you might see slightly different scores depending on what computer/device you are running this notebook.\n", "This is because despite setting the seed before generating the test dataset, it is not the same across platforms and numpy versions.\n", "Nevertheless, we can conclude that the model performs quite well and can solve the task for most sets.\n", "Before trying to interpret the model, let's verify that our model is permutation-equivariant,\n", "and assigns the same predictions for different permutations of the input set.\n", "For this, we sample a batch from the test set and run it through the model to obtain the probabilities."]}, {"cell_type": "code", "execution_count": 38, "id": "dccefbde", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:27.411424Z", "iopub.status.busy": "2021-12-04T15:59:27.410941Z", "iopub.status.idle": "2021-12-04T15:59:27.581026Z", "shell.execute_reply": "2021-12-04T15:59:27.581442Z"}, "papermill": {"duration": 0.381038, "end_time": "2021-12-04T15:59:27.581610", "exception": false, "start_time": "2021-12-04T15:59:27.200572", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Preds\n", " [2.7691365e-05 1.8979923e-05 1.7386470e-05 2.7843047e-05 1.6143023e-05\n", " 1.7020926e-05 5.7294892e-05 9.9977750e-01 2.1365197e-05 1.8681889e-05]\n", "Permuted preds\n", " [2.7691472e-05 1.8979976e-05 1.7386521e-05 2.7843154e-05 1.6143069e-05\n", " 1.7020990e-05 5.7295114e-05 9.9977750e-01 2.1365277e-05 1.8681943e-05]\n"]}], "source": ["inp_data, indices, labels = next(iter(test_anom_loader))\n", "inp_data = inp_data.to(device)\n", "\n", "anomaly_model.eval()\n", "\n", "with torch.no_grad():\n", " preds = anomaly_model.forward(inp_data, add_positional_encoding=False)\n", " preds = F.softmax(preds.squeeze(dim=-1), dim=-1)\n", "\n", " # Permut input data\n", " permut = np.random.permutation(inp_data.shape[1])\n", " perm_inp_data = inp_data[:, permut]\n", " perm_preds = anomaly_model.forward(perm_inp_data, add_positional_encoding=False)\n", " perm_preds = F.softmax(perm_preds.squeeze(dim=-1), dim=-1)\n", "\n", "assert (preds[:, permut] - perm_preds).abs().max() < 1e-5, \"Predictions are not permutation equivariant\"\n", "\n", "print(\"Preds\\n\", preds[0, permut].cpu().numpy())\n", "print(\"Permuted preds\\n\", perm_preds[0].cpu().numpy())"]}, {"cell_type": "markdown", "id": "e44810c4", "metadata": {"papermill": {"duration": 0.202216, "end_time": "2021-12-04T15:59:27.986209", "exception": false, "start_time": "2021-12-04T15:59:27.783993", "status": "completed"}, "tags": []}, "source": ["You can see that the predictions are almost exactly the same, and only differ because of slight numerical\n", "differences inside the network operation.\n", "\n", "To interpret the model a little more, we can plot the attention maps inside the model.\n", "This will give us an idea of what information the model is sharing/communicating between images,\n", "and what each head might represent.\n", "First, we need to extract the attention maps for the test batch above, and determine the discrete predictions for simplicity."]}, {"cell_type": "code", "execution_count": 39, "id": "a3aa3c54", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:28.404309Z", "iopub.status.busy": "2021-12-04T15:59:28.403839Z", "iopub.status.idle": "2021-12-04T15:59:28.411125Z", "shell.execute_reply": "2021-12-04T15:59:28.410728Z"}, "papermill": {"duration": 0.214883, "end_time": "2021-12-04T15:59:28.411233", "exception": false, "start_time": "2021-12-04T15:59:28.196350", "status": "completed"}, "tags": []}, "outputs": [], "source": ["attention_maps = anomaly_model.get_attention_maps(inp_data, add_positional_encoding=False)\n", "predictions = preds.argmax(dim=-1)"]}, {"cell_type": "markdown", "id": "53fdeaca", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.203066, "end_time": "2021-12-04T15:59:28.821317", "exception": false, "start_time": "2021-12-04T15:59:28.618251", "status": "completed"}, "tags": []}, "source": ["Below we write a plot function which plots the images in the input set, the prediction of the model,\n", "and the attention maps of the different heads on layers of the transformer.\n", "Feel free to explore the attention maps for different input examples as well."]}, {"cell_type": "code", "execution_count": 40, "id": "73a6c7b3", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:29.243841Z", "iopub.status.busy": "2021-12-04T15:59:29.243370Z", "iopub.status.idle": "2021-12-04T15:59:32.280160Z", "shell.execute_reply": "2021-12-04T15:59:32.280550Z"}, "papermill": {"duration": 3.254644, "end_time": "2021-12-04T15:59:32.280702", "exception": false, "start_time": "2021-12-04T15:59:29.026058", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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IDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvQSAxOCAwIFIgL0MgMTkgMCBSIC9GIDIwIDAgUiAvSSAyMSAwIFIgL1IgMjIgMCBSIC9hIDIzIDAgUiAvZSAyNCAwIFIKL2wgMjUgMCBSIC9tIDI2IDAgUiAvbiAyNyAwIFIgL28gMjggMCBSIC9vbmUgMjkgMCBSIC9wIDMwIDAgUiAvcyAzMSAwIFIKL3NwYWNlIDMyIDAgUiAveCAzMyAwIFIgL3kgMzQgMCBSIC96ZXJvIDM1IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50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MDcxMyAwMDAwMCBuIAowMDAwMDA3MTcxIDAwMDAwIG4gCjAwMDAwMDU3MDIgMDAwMDAgbiAKMDAwMDAwNTUwMiAwMDAwMCBuIAowMDAwMDA1MTA2IDAwMDAwIG4gCjAwMDAwMDY3NTUgMDAwMDAgbiAKMDAwMDAwMDc1MyAwMDAwMCBuIAowMDAwMDAwOTE2IDAwMDAwIG4gCjAwMDAwMDEyMjQgMDAwMDAgbiAKMDAwMDAwMTM3MiAwMDAwMCBuIAowMDAwMDAxNDk1IDAwMDAwIG4gCjAwMDAwMDE4MDAgMDAwMDAgbiAKMDAwMDAwMjE4MCAwMDAwMCBuIAowMDAwMDAyNTAyIDAwMDAwIG4gCjAwMDAwMDI2MjEgMDAwMDAgbiAKMDAwMDAwMjk1MiAwMDAwMCBuIAowMDAwMDAzMTg4IDAwMDAwIG4gCjAwMDAwMDM0NzkgMDAwMDAgbiAKMDAwMDAwMzYzNCAwMDAwMCBuIAowMDAwMDAzOTQ2IDAwMDAwIG4gCjAwMDAwMDQzNTMgMDAwMDAgbiAKMDAwMDAwNDQ0MyAwMDAwMCBuIAowMDAwMDA0NjA0IDAwMDAwIG4gCjAwMDAwMDQ4MTggMDAwMDAgbiAKMDAwMDA3MTUwNSAwMDAwMCBuIAowMDAwMDcxNTg3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDM4ID4+CnN0YXJ0eHJlZgo3MTc0NAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:59:29.364891\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Prediction: 9\n"]}, {"data": {"application/pdf": "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MCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjMyIDAgb2JqCjw8IC9IIDMzIDAgUiAvTCAzNCAwIFIgL2EgMzUgMCBSIC9jb21tYSAzNiAwIFIgL2QgMzcgMCBSIC9lIDM4IDAgUgovZWlnaHQgMzkgMCBSIC9maXZlIDQwIDAgUiAvZm91ciA0MSAwIFIgL25pbmUgNDIgMCBSIC9vbmUgNDMgMCBSIC9yIDQ0IDAgUgovc2V2ZW4gNDUgMCBSIC9zaXggNDYgMCBSIC9zcGFjZSA0NyAwIFIgL3RocmVlIDQ4IDAgUiAvdHdvIDQ5IDAgUiAveSA1MCAwIFIKL3plcm8gNTEgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAzMSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0kxIDEzIDAgUiAvSTEwIDIyIDAgUiAvSTExIDIzIDAgUiAvSTEyIDI0IDAgUiAvSTEzIDI1IDAgUiAvSTE0IDI2IDAgUgovSTE1IDI3IDAgUiAvSTE2IDI4IDAgUiAvSTIgMTQgMCBSIC9JMyAxNSAwIFIgL0k0IDE2IDAgUiAvSTUgMTcgMCBSCi9JNiAxOCAwIFIgL0k3IDE5IDAgUiAvSTggMjAgMCBSIC9JOSAyMSAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMjEgKP3nJPjmIeTjGLLdLK3cMJ3ZOlfGZSGNjFwoe45FNoFGMH1HJ3dHJXVIInNHEmVHEWNGC15FCFtFBVhEA1dEAlVEAVQpXQovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAxIC9Db2x1bW5zIDExOSAvUHJlZGlj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q/uE+4laqI/gck/O9fk+PfSHdHV1dXVz3A36QA1Edx6aIM6FrOepuaCrq6urG3dxTrKsIc4F7tuxJr//RebCGenq6uq+jfvs9xlny1yf3+Gs841/pKurq6ub425RFX0j3g8uQtwnZ90P0k3dD+rq6urqxt0jKqEvxIX2oTXiGOGo4fY/9f86XV1dXd24+6JzlSL79gvS1dXV1Y27DySRyHkKZW5kc3RyZWFtCmVuZG9iago2NyAwIG9iagoyNjYKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjY4IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEyMDQxNjU5MzErMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNjkKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjQ0OTkgMDAwMDAgbiAKMDAwMDAxMzgzOCAwMDAwMCBuIAowMDAwMDEzODcwIDAwMDAwIG4gCjAwMDAwMTM5NjkgMDAwMDAgbiAKMDAwMDAxMzk5MCAwMDAwMCBuIAowMDAwMDE0MDExIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwNiAwMDAwMCBuIAowMDAwMDA2ODY0IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwNjg0MyAwMDAwMCBuIAowMDAwMDE0MjE1IDAwMDAwIG4gCjAwMDAwMTQ3MzYgMDAwMDAgbiAKMDAwMDAxNTI0MCAwMDAwMCBuIAowMDAwMDE1ODUxIDAwMDAwIG4gCjAwMDAwMTYzNDggMDAwMDAgbiAKMDAwMDAxNzA1NiAwMDAwMCBuIAowMDAwMDE3Nzg0IDAwMDAwIG4gCjAwMDAwMTgyNDUgMDAwMDAgbiAKMDAwMDAxODkzNiAwMDAwMCBuIAowMDAwMDE5NjE4IDAwMDAwIG4gCjAwMDAwMjAzNDQgMDAwMDAgbiAKMDAwMDAyMTA2NyAwMDAwMCBuIAowMDAwMDIxNzM5IDAwMDAwIG4gCjAwMDAwMjI0MjYgMDAwMDAgbiAKMDAwMDAyMzE4OCAwMDAwMCBuIAowMDAwMDIzODI0IDAwMDAwIG4gCjAwMDAwMTI1MzUgMDAwMDAgbiAKMDAwMDAxMjMzNSAwMDAwMCBuIAowMDAwMDExOTE3IDAwMDAwIG4gCjAwMDAwMTM1ODggMDAwMDAgbiAKMDAwMDAwNjg4NCAwMDAwMCBuIAowMDAwMDA3MDM1IDAwMDAwIG4gCjAwMDAwMDcxNjggMDAwMDAgbiAKMDAwMDAwNzU0OCAwMDAwMCBuIAowMDAwMDA3Njg4IDAwMDAwIG4gCjAwMDAwMDc5OTIgMDAwMDAgbiAKMDAwMDAwODMxNCAwMDAwMCBuIAowMDAwMDA4NzgyIDAwMDAwIG4gCjAwMDAwMDkxMDQgMDAwMDAgbiAKMDAwMDAwOTI3MCAwMDAwMCBuIAowMDAwMDA5NjY1IDAwMDAwIG4gCjAwMDAwMDk4MjAgMDAwMDAgbiAKMDAwMDAxMDA1MyAwMDAwMCBuIAowMDAwMDEwMTk1IDAwMDAwIG4gCjAwMDAwMTA1ODggMDAwMDAgbiAKMDAwMDAxMDY3OCAwMDAwMCBuIAowMDAwMDExMDkxIDAwMDAwIG4gCjAwMDAwMTE0MTUgMDAwMDAgbiAKMDAwMDAxMTYyOSAwMDAwMCBuIAowMDAwMDE0NzE2IDAwMDAwIG4gCjAwMDAwMTUyMjAgMDAwMDAgbiAKMDAwMDAxNTgzMSAwMDAwMCBuIAowMDAwMDE2MzI4IDAwMDAwIG4gCjAwMDAwMTcwMzYgMDAwMDAgbiAKMDAwMDAxNzc2NCAwMDAwMCBuIAowMDAwMDE4MjI1IDAwMDAwIG4gCjAwMDAwMTg5MTYgMDAwMDAgbiAKMDAwMDAxOTU5OCAwMDAwMCBuIAowMDAwMDIwMzI0IDAwMDAwIG4gCjAwMDAwMjEwNDcgMDAwMDAgbiAKMDAwMDAyMTcxOSAwMDAwMCBuIAowMDAwMDIyNDA2IDAwMDAwIG4gCjAwMDAwMjMxNjggMDAwMDAgbiAKMDAwMDAyMzgwNCAwMDAwMCBuIAowMDAwMDI0NDc5IDAwMDAwIG4gCjAwMDAwMjQ1NTkgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA2OCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNjkgPj4Kc3RhcnR4cmVmCjI0NzE2CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:59:30.887739\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["def visualize_prediction(idx):\n", " visualize_exmp(indices[idx : idx + 1], test_set)\n", " print(\"Prediction:\", predictions[idx].item())\n", " plot_attention_maps(input_data=None, attn_maps=attention_maps, idx=idx)\n", "\n", "\n", "visualize_prediction(0)"]}, {"cell_type": "markdown", "id": "c342c0bd", "metadata": {"papermill": {"duration": 0.217865, "end_time": "2021-12-04T15:59:32.717940", "exception": false, "start_time": "2021-12-04T15:59:32.500075", "status": "completed"}, "tags": []}, "source": ["Depending on the random seed, you might see a slightly different input set.\n", "For the version on the website, we compare 9 tree images with a volcano.\n", "We see that multiple heads, for instance, Layer 2 Head 1, Layer 2 Head 3, and Layer 3 Head 1 focus on the last image.\n", "Additionally, the heads in Layer 4 all seem to ignore the last image and assign a very low attention probability to it.\n", "This shows that the model has indeed recognized that the image doesn't fit the setting, and hence predicted it to be the anomaly.\n", "Layer 3 Head 2-4 seems to take a slightly weighted average of all images.\n", "That might indicate that the model extracts the \"average\" information of all images, to compare it to the image features itself.\n", "\n", "Let's try to find where the model actually makes a mistake.\n", "We can do this by identifying the sets where the model predicts something else than 9, as in the dataset,\n", "we ensured that the anomaly is always at the last position in the set."]}, {"cell_type": "code", "execution_count": 41, "id": "7d06d854", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:33.159783Z", "iopub.status.busy": "2021-12-04T15:59:33.159310Z", "iopub.status.idle": "2021-12-04T15:59:33.162672Z", "shell.execute_reply": "2021-12-04T15:59:33.162185Z"}, "papermill": {"duration": 0.227646, "end_time": "2021-12-04T15:59:33.162786", "exception": false, "start_time": "2021-12-04T15:59:32.935140", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Indices with mistake: [49]\n"]}], "source": ["mistakes = torch.where(predictions != 9)[0].cpu().numpy()\n", "print(\"Indices with mistake:\", mistakes)"]}, {"cell_type": "markdown", "id": "20752dd7", "metadata": {"papermill": {"duration": 0.217506, "end_time": "2021-12-04T15:59:33.599090", "exception": false, "start_time": "2021-12-04T15:59:33.381584", "status": "completed"}, "tags": []}, "source": ["As our model achieves ~94% accuracy, we only have very little number of mistakes in a batch of 64 sets.\n", "Still, let's visualize one of them, for example the last one:"]}, {"cell_type": "code", "execution_count": 42, "id": "aff3ca25", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T15:59:34.041610Z", "iopub.status.busy": "2021-12-04T15:59:34.041142Z", "iopub.status.idle": "2021-12-04T15:59:36.836836Z", "shell.execute_reply": "2021-12-04T15:59:36.836406Z"}, "papermill": {"duration": 3.018115, "end_time": "2021-12-04T15:59:36.836962", "exception": false, "start_time": "2021-12-04T15:59:33.818847", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDY4NCAxMDAuNDc1OTkzMzc3NSBdIC9QYXJlbnQgMiAwIFIgL1Jlc291cmNlcyA4IDAgUgovVHlwZSAvUGFnZSA+PgplbmRvYmoKOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEyIDAgUiA+PgpzdHJlYW0KeJxVkElPwzAQhe/zK96xOeB4HC/xMaU0KrdWkTggDlUIhSoLIRLLv2cSxGZp9DQzfvONzThTWjBOEzTOEm9glEg3zetT3RzKNeqJtNQ78rkVbRdlrZUNLsZMCvp/+kjU04igzBLeR+URtIpOy4UsBIeXBjfokRZmBrOAWcAapfh8mHEagX9G1B3SHWMzYE97jN8+jdNf75zTSCx6ISvBWCvMjI1DbhX/0uuO1hXSLYMNqofledU93WJV9EN3bD/QJGCvogk6z+aD1fuxe26bCUOPy902QWTF1n11pV0c5AsS3KG6pquKZE36BPRTS9gKZW5kc3RyZWFtCmVuZG9iagoxMiAwIG9iagoyMzkKZW5kb2JqCjEwIDAgb2JqClsgXQplbmRvYmoKMTggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA5MSA+PgpzdHJlYW0KeJw1jLsNwDAIRHumuBH4OID3iaIU9v5tiC0X3D3pifNsYGSdhyO04xaypnBTTFJOqHcMaqU3HTvoJc39NMl6Lhr0D3H1FbabA5JRJJGHRJfLlWflX3w+DG8cYgplbmRzdHJlYW0KZW5kb2JqCjE5IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjM1ID4+CnN0cmVhbQp4nDVRSW4AMQi75xX+QKWwJ++Zquqh/f+1hlEvAwPY2CTvwUYkPsSQ7ihXfMrqNMvwO1nkxc9K4eS9iAqkKsIKaQfPclYzDJ4bmQKXM/FZZj6ZFjsWUE3EcXbkNINBiGlcR8vpMNM86Am5PhhxY6dZrmJI691Svb7X8p8qykfW3Sy3TtnUSt2iZ+xJXHZeT21pXxh1FDcFkQ4fO7wH+SLmLC46kW72mymHlaQhOC2AH4mhVM8OrxEmfmYkeMqeTu+jNLz2QdP1vXtBR24mZCq3UEYqnqw0xoyh+o1oJqnv/4Ge9b2+/gBDTVS5CmVuZHN0cmVhbQplbmRvYmoKMjAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3NiA+PgpzdHJlYW0KeJwzNTdVMFCwtAASpobmCuZGlgophlxAPoiVywUTywGzzEzMgCxDS2SWibEhkGViYYbEMjaxgMoiWAZAGmxNDsz0HK4MrjQANRcZBQplbmRzdHJlYW0KZW5kb2JqCjIxIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggNTEgPj4Kc3RyZWFtCnicM7I0VTBQsLQAEoaW5grmRpYKKYZcQD6IlcsFE8sBswyANFhpDkxFDlcGVxoAv4wNVgplbmRzdHJlYW0KZW5kb2JqCjIyIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjMyID4+CnN0cmVhbQp4nD2QS3IEIQxD95xCRwB/4TydSs2i5/7byO6ZbJCqwPITcRwTZ/OICKQc/KxhZlATvIeFQ9VgO6DrwGdATuAaLnQpcKPahHN8ncObCpq4h8dstUisneVMIeowJkls6EnINs5ocuOc3KpU3kxrvcbim3J3u8pr2pbCvYfK+jjjVDmrKmuRNhGZRWsbwUYe7LDPo6toy1kq3DeMTV0TlcObxe5Z3cniiu+vXOPVLMHM98O3vxwfV93oKsfYyoTZUpPm0jn1r5bR+nC0i4V64Ud7JkhwdasgVaXWztpTev1T3CT6/QP0wVcdCmVuZHN0cmVhbQplbmRvYmoKMjMgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMDcgPj4Kc3RyZWFtCnicPZJLbgMxDEP3PoUuEMD62Z7zpCi6mN5/2ycl6Yoc2RZFapa6TFlTHpA0k4R/6fBwsZ3yO2zPZmbgWqKXieWU59AVYu6ifNnMRl1ZJ8XqhGY6t+hRORcHNk2qn6sspd0ueA7XJp5b9hE/vNCgHtQ1Lgk3dFejZSk0Y6r7f9J7/Iwy4GpMXWxSq3sfPF5EVejoB0eJImOXF+fjQQnpSsJoWoiVd0UDQe7ytMp7Ce7b3mrIsgepmM47KWaw63RSLm4XhyEeyPKo8OWj2GtCz/iwKyX0SNiGM3In7mjG5tTI4pD+3o0ES4+uaCHz4K9u1i5gvFM6RWJkTnKsaYtVTvdQFNO5w70MEPVsRUMpc5HV6l/DzgtrlmwWeEr6BR6j3SZLDlbZ26hO76082dD3H1rXdB8KZW5kc3RyZWFtCmVuZG9iagoyNCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI0OSA+PgpzdHJlYW0KeJw9UDuORCEM6zmFL/Ak8iNwHkarLWbv364DmilQTH62MyTQEYFHDDGUr+MlraCugb+LQvFu4uuDwiCrQ1IgznoPiHTspjaREzodnDM/YTdjjsBFMQac6XSmPQcmOfvCCoRzG2XsVkgniaoijuozjimeKnufeBYs7cg2WyeSPeQg4VJSicmln5TKP23KlAo6ZtEELBK54GQTTTjLu0lSjBmUMuoepnYifaw8yKM66GRNzqwjmdnTT9uZ+Bxwt1/aZE6Vx3QezPictM6DORW69+OJNgdNjdro7PcTaSovUrsdWp1+dRKV3RjnGBKXZ38Z32T/+Qf+h1oiCmVuZHN0cmVhbQplbmRvYmoKMjUgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA0NyA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlyWEFYuF0wsB8wC0ZZwCiKewZUGALlnDScKZW5kc3RyZWFtCmVuZG9iagoyNiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI1OCA+PgpzdHJlYW0KeJxFkUtyBCAIRPeegiOA/OQ8k0plMbn/Ng3OZDZ2l6j9hEojphIs5xR5MH3J8s1ktul3OVY7GwUURSiYyVXosQKrO1PEmWuJautjZeS40zsGxRvOXTmpZHGjjHVUdSpwTM+V9VHd+XZZlH1HDmUK2KxzHGzgym3DGCdGm63uDveJIE8nU0fF7SDZ8AcnjX2VqytwnWz20UswDgT9QhOY5ItA6wyBxs1T9OQS7OPjdueBYG95EUjZEMiRIRgdgnadXP/i1vm9/3GGO8+1Ga4c7+J3mNZ2x19ikhVzAYvcKajnay5a1xk63pMzx+Sm+4bOuWCXu4NM7/k/1s/6/gMeKWb6CmVuZHN0cmVhbQplbmRvYmoKMjcgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNjMgPj4Kc3RyZWFtCnicRZA7EgMhDEN7TqEj+CMDPs9mMik2929j2GxSwNNYIIO7E4LU2oKJ6IKHtiXdBe+tBGdj/Ok2bjUS5AR1gFak42iUUn25xWmVdPFoNnMrC60THWYOepSjGaAQOhXe7aLkcqbuzvlDcPVf9b9i3TmbiYHJyh0IzepT3Pk2O6K6usn+pMfcrNd+K+xVYWlZS8sJt527ZkAJ3FM52qs9Px8KOvYKZW5kc3RyZWFtCmVuZG9iagoyOCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIxOCA+PgpzdHJlYW0KeJw9ULmNBDEMy12FGljAeu2pZxaLS6b/9Ej59iLRFkVSKjWZkikvdZQlWVPeOnyWxA55huVuZDYlKkUvk7Al99AK8X2J5hT33dWWs0M0l2g5fgszKqobHdNLNppwKhO6oNzDM/oNbXQDVocesVsg0KRg17YgcscPGAzBmROLIgxKTQb/rnKPn16LGz7D8UMUkZIO5jX/WP3ycw2vU48nkW5vvuJenKkOAxEckpq8I11YsS4SEWk1QU3PwFotgLu3Xv4btCO6DED2icRxmlKOob9rcKXPL+UnU9gKZW5kc3RyZWFtCmVuZG9iagoyOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDgzID4+CnN0cmVhbQp4nEWMuw3AMAhEe6ZgBH4m9j5RlMLevw0QJW64J909XB0JmSluM8NDBp4MLIZdcYH0ljALXEdQjp3so2HVvuoEjfWmUvPvD5Se7KzihusBAkIaZgplbmRzdHJlYW0KZW5kb2JqCjMwIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjM5ID4+CnN0cmVhbQp4nE1QyW0EMQz7uwo1MMDoHLseB4s8sv1/Q8oJkpdoS+Kh8pRblspl9yM5b8m65UOHTpVp8m7Qza+x/qMMAnb/UFQQrSWxSsxc0m6xNEkv2cM4jZdrtY7nqXuEWaN48OPY0ymB6T0ywWazvTkwqz3ODpBOuMav6tM7lSQDibqQ80KlCuse1CWijyvbmFKdTi3lGJef6Ht8jgA9xd6N3NHHyxeMRrUtqNFqlTgPMBNT0ZVxq5GBlBMGQ2dHVzQLpcjKekI1wo05oZm9w3BgA8uzhKSlrVK8D2UB6AJd2jrjNEqCjgDC3yiM9foGqvxeNwplbmRzdHJlYW0KZW5kb2JqCjMxIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzM0ID4+CnN0cmVhbQp4nC1SS3LFIAzbcwpdoDP4B+Q86XS6eL3/tpKTRUYOYPQx5YaJSnxZILej1sS3jcxAheGvq8yFz0jbyDqIy5CLuJIthXtELOQxxDzEgu+r8R4e+azMybMHxi/Zdw8r9tSEZSHjxRnaYRXHYRXkWLB1Iap7eFOkw6kk2OOL/z7Fcy0ELXxG0IBf5J+vjuD5khZp95ht0656sEw7qqSwHGxPc14mX1pnuToezwfJ9q7YEVK7AhSFuTPOc+Eo01ZGtBZ2NkhqXGxvjv1YStCFblxGiiOQn6kiPKCkycwmCuKPnB5yKgNh6pqudHIbVXGnnsw1m4u3M0lm675IsZnCeV04s/4MU2a1eSfPcqLUqQjvsWdL0NA5rp69lllodJsTvKSEz8ZOT06+VzPrITkVCaliWlfBaRSZYgnbEl9TUVOaehn++/Lu8Tt+/gEsc3xzCmVuZHN0cmVhbQplbmRvYmoKMzIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxOCA+PgpzdHJlYW0KeJwzNrRQMIDDFEOuNAAd5gNSCmVuZHN0cmVhbQplbmRvYmoKMzMgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA4OSA+PgpzdHJlYW0KeJw1TbkRgDAM6z2FR8CPSLwPx1GE/VvshDSWTp8Rygdr5AGC4Y0vIfiiLxmEtQsPKvtIdNhEDWcVJBPDryzwqpwVbXMlE9lZTKOzQcv0re1vgx66P92OHAoKZW5kc3RyZWFtCmVuZG9iagozNCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE0MSA+PgpzdHJlYW0KeJw9j8EOwzAIQ+/5Cv9ApNgpoXxPp2qH7v+vI0u7C3oCY4yF0NAbqprDhmCb48XSJVRr+BTFQCU3yJlgDqWk0h1HkXpiOBhcHrQbjuKx6PoRu5JmfdDGQrolaIB7rFNp3KZxE8QdNQXqKeqco7wQuZ+pZ9g0kt00s5JzuA2/e89T1/+nq7zL+QW9dy7+CmVuZHN0cmVhbQplbmRvYmoKMzUgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMTUgPj4Kc3RyZWFtCnicNVE5DgMhDOz3Ff5AJIwveE+iKM3+v82M0VYewVyGtJQhmfJSk6gh5VM+epkunLrc18xqNOeWtC1zgLi2vC+tksCJZoiDwWmYuAGaPAFD19GoUUMXHtDUpVMosNwEPoq3bg/dY7WBl7Yh54kgYigZLEHNqUUTFm3PJ6Q1v16LG96X7d3IU6XGlhiBBgFWOBzX6NfwlT1PJtF0FTLUqzXLGAkTRSI8+Y6m1RPrWjTSMhLUxhGsagO8O/0wTgAAE3HLAmSfSpSz5MRvsfSzBlf6/gGfR1SWCmVuZHN0cmVhbQplbmRvYmoKMTYgMCBvYmoKPDwgL0Jhc2VGb250IC9EZWphVnVTYW5zIC9DaGFyUHJvY3MgMTcgMCBSCi9FbmNvZGluZyA8PAovRGlmZmVyZW5jZXMgWyAzMiAvc3BhY2UgNDggL3plcm8gL29uZSA2NSAvQSA2NyAvQyA3MCAvRiA3MyAvSSA4MiAvUiA5NyAvYSAxMDEgL2UgMTA4Ci9sIC9tIC9uIC9vIC9wIDExNSAvcyAxMjAgL3ggL3kgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE1IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvQSAxOCAwIFIgL0MgMTkgMCBSIC9GIDIwIDAgUiAvSSAyMSAwIFIgL1IgMjIgMCBSIC9hIDIzIDAgUiAvZSAyNCAwIFIKL2wgMjUgMCBSIC9tIDI2IDAgUiAvbiAyNyAwIFIgL28gMjggMCBSIC9vbmUgMjkgMCBSIC9wIDMwIDAgUiAvcyAzMSAwIFIKL3NwYWNlIDMyIDAgUiAveCAzMyAwIFIgL3kgMzQgMCBSIC96ZXJvIDM1IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgL0RldmljZVJHQgovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAzIC9Db2x1bW5zIDY3MCAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgNzEgL0xlbmd0aCAzNiAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCA2NzAgPj4Kc3RyZWFtCnic7P1pb2ZLtiaGrSEi9vAOJDPz5Bmq6l51Sy3JhmEb8g/UT5QBA1a3qm/1vbdO1Rkyk+Q77CEi1uAPsV8mM6u6LdkGDAgniqjDZDLJPUSs4VnPehb+j//j/wi/rd/Wb+u39dv6bf22/vey6P/fF/Db+m39tn5bv63f1m/r/5frN9f+2/pt/bZ+W7+t39b/rtZvrv239dv6bf22flu/rf9drfD6D//uv/n9H373nggQEZERGJEAEMDc1U1dxVVd1VXM1NyAkEPgGDlGpABEruq1SilSq5m6GSIiABESIgC4m6qKqpq5OxExMRESkgMAgoEDIXHgGEJMRIztn/rtQpEAGYgB2QHB8Z//+cf/8B/+tP0lUtodAdDMTcHUiDiG0HWp71KKgRlLKdO8LMu6rMXMAD0GipGZkRBKyaVkqaKq4Mgcxn4MIZpqzut1utRaOFCMHGMkIncnYiY2d1FzcEQnZmICB3MDAHAXVVMFRCJKKfV9v9sfxt2Yuo6ITU1Efvrzj7XWdiP37/7h7bf/tZmpuruDAyIiASIgQmBkJiZkxrD9PyCCO9j27YAIhMCMREAIRNDegLkt03Q+n3/59dfHp6fj3Tdv3nzHYQAMolbFVFzN3dzd3c0BEAAQEBAAXr+Jly+8/l9dn6bTP7fvIOb/0//lf+iHAdyl1nVZ8rrkvNaaay0qoqol55xXUzV3U6si59Pz6emx5lWloAMAmBkR3t0dxt1YSnGH/f7w5s27H373u91uf7lepmnKeZVaHTzGNIz7fhi7ftztD3d3d6nrOQREfLn0v7c+/6U7uPv//X/6n06nU/uK0GGN37sDGAIQAJmBmau5ticODuAIjoiI2DZ8O03tbwmBCWOgGDBw+wZnZmZGYgAsVdZc1yxrMVFQRzNQA3c3dwBAdEIkxhgpBiQGRAc3AmAiotuLZgyBYuAUuQss+frpx/8ZvN0d2nf/Z+jvEAkJoe0I8O0dAkB7Qt5eePuygau5uom7uJubSL5IvkidrM7uFcBC6Dj0xIk4IbE7aC1mhkjEMcQ+pIHjyKEn7ogjUYgMiaGPMEQYIg4RGREBzMEcxKEaFPVi25b+8Mf/x/zpl/Y6rtfrH//4H0MIfd8/PDx88837H3744bvvvk8pEcHtdWx7dHsj2EwRmLk5uLua1lo/ffr0T3/609PTk7m3e57n+Xw6lZLBIcaQYqy1rOsiImYGSMzc933X94Ro5mtepUpMcTfuvnn3/t27bx4e3u52e78987ajAMDdReovv/zZm1kA2N19u9vfvXt7f3/cx0BE0K4e3AEQ29XTdvHtDtSsis7TcrlM65JrqQCAhO4mUk+n09PT8+V8yXk9Ho8P9/fH42EcB0J0t5yrmXKgYRweHu5TTPOS17VUkXHov3n/dhj69pQ+nwn8+vNmXv7Df/j3//LP2zFfhf/Dp4P5yz9F/Px/iICE2PWh64K5i2qtJnJ73G33be8LCTCSJ4bI4IBrxaqg2758dWHw1VHe9rCDowM59FFSUAQ0p6VQEQRwQAd3AL+9Gg9k/927S6Dtp53Xca49ITAiMzMTIcVA/RDHPo1DIsZ1LaWKGJi3g4607S4ihObtnDGQBpTIyBwcWJ3baXIgAGqvFsARgen2U24PDG8Psn3S/vzycTugAOgIgOAI8PGXv/71z//p5XF84dp//8M3/7f/679jRmIiigQRMQCQu7hVl2xSvFarRWtVrWqChKFLsR9C10OITmyl6LqWeV7nSUXcjYkCc2BiJnA301JLqbXUqmYpxBhiZCYiQLTm2plCSrHv49CH2BExNa/V7okYKLYPB3Igc/vs2gnH4xGATaAWq0UJOaU0jsNhP45j1yWe5hn5VPVs6yRWHZwDI4eYmMjVFHIWkVIqOKaINIQUenFZtczXZV2n1IW+T4QOzG1bIjEYmIiBAUFAJw6OL2bZRauIEFGkEBKP++Hh7cPdw8Ow24UQSq41l19/+vnFtR/u3v/u3/wPIlqrmTm407YDnBlS5BQ5BYqRUsDIzmSEDkAG9OLaA0NzJMzADERgJrXmTx8//PTXv56vZ/mkw+7u+9//u66/B+rXImuWUqxWUzEzM1d3v+01RITbqbjtOAcAN3MHb0dmufz5s2tH+q//2//+eHeP7jXny/l0vZ7n6bIs07rMJS95XU9aLqcp52LmqlZr+fDzX//645/ny3NdZ3RHADNl5h9+9+2bN/fzvDgAvv/+7WG8G+P9/c7qlKdaZAXJ6B4j7odwuNvvD/fv3n/7ww+/2x+PqeuReLv41ybixTa4v/7czP7jH//4yrWPc/iDGxoiAoOzOih4ddMtAHJwQ3AiZCRGCowtWHR3BCfyFCh2gRPFSIGRyGMIMSWkYI6+5OW6Zi/XWjNANaoKoqDmZi04cGKMgEMKXaAQgdDAjBAiU2BihsAIkTBx7MNuiPs+leuHxx//55ujQ3/77+D4AxID8c2AbHEJvEQ+DgiIWyAjrhl0dV1Vs2k1zeuVMqxFVWAxnQEkhn1iDilyIOLgDgVFxYgwxNANow/3OLyh7p66fYi7mPoh8T75ccD7Ae97uuspMqKDOlSDVWERn8TnCmqu7udffnxx7cuy/PGPf+z7/ni8Cxy/++6H999893/47/+P425kIgd1N/BbmIVERITIRAAo6upm7rXKPM9/+tOf/uM//el8vTqAuZvb0+PjX//612Wa3H3ou3Ec8rKcz6ec1yoVkEJIx7u74/EYQnCz8/k8L8s4DG8e3sTYv3n77fH48O7d+y3yRQIAM2/x4rrOv/7648te2x/fvfv29//2H3/43ffv+sSBsQXU4A7NrBMxNZtMhACAVeq05sdPpw+/fDyfrsu8ghsSmkkp6zKXWh7Pp+k6Xbs4xHfdcX//8HBHhCoyXadSS0x8d3f8/R9+Pw7j83marksp9c2bh//mv/23Dw/HV679y1D+lWs3gNPp9OLaq9E/PR3FX4zE9r/t8QMEor33+9CpaamyLLquakAA2OJohBbrYCAc2IcAY0IHPC24VCyABoSvLsk/RwPtwrx90cBIITgchrzvCwGL8mnmayZHBzBwg/aEwQC9Y/1v3lxfXPtc+sf5yISRMcUQQwjMHsIQh24/HO92MdL5OsNSQMCckIiJAyETExEjMiEEskCJy0BrlzjFpNCJJ3cFM0cCYERGRERH8hAohBaavwQJtyQOgVqeQPg5PWs2EZtrdwJoOct/1rW7qUklJKSAbgDmpuBmXk2LSbFaQBTEwAAdqf149bqWUlUcqnotuSzLOk/LNLkZIe5242G/R0YEdHc1r2q5lGmeRWQ/joEICNGxeTBEQABUA1EXM1QM5LiFvu4Ohk4KREDk6A4tsdwWEb7/Zk+UXHhdbL62XE6WeUUAM9EhVqmIQIREbq6iFQDNSIVjZHcPMQZRM3AFcBJxYQck5sAcmWMIMYQucAoxEG7GHMCig4I4KpIDGLQMHgEAUuIUKXXdOAzH493heNzt912Mrl5qWZYlr9nMXnkXsOZbzdz8dtK3zd0iRUB09ypSS5ZyRrSuO4TYE0a4xf9maDeT7e7zfH16+vDXv/zrn//ln3/88c9Pn56/efcPgbBPgUICRDU3AzPYfumWzTX/3n7gdqhuOcUtQPeX/3yxRMVMA3PqusPd/TAOUu5qWUtepuvl9Pz0/Pjpxx9/PD+f1TylFGNa5lXVRLwUdVMCIMIYt7flZsxht9v1XXz8+Ovjpw8fP328XidECiGk1BEFRDLzUkopRVTcoZn3//WuvRmoV6cDRJrLB0RDBAN3skAQ215CAgNwb3lzDBgCBaaWRBI6E6ZIfccpcghECAiGREguJkV8qWWptYiouRqoqRm6tZjWAbf0DwDUTAQcgRDMAB0qOZMxYQgQxU3VDQJAF9BMX9/mLYdtjuLFWrYYzWF7j816OHpBX9UuKGevFy2T1FzLkqen9fqxLs81n1Vm9xKWYY2PHDoOiTkAgEl1M+Igobe8r8td7t+k/k03PvS7N5EeQhpSCH3gLmBkZAIEcAQxKAarwixwLTBVFwVzEP3iLpgppTiOwzAOwzCkLjUIhAnNXW824Zb3NrvZfKUDcCRChMtVRCsyDkOf+q5WOZ1PaoaIxAzuzByYoe8PboBQJ3VzM3M1dBj6PsXYdk6M0d3neT6fnudlVpW+7wMHdzBvrt3dXeULq3s8jm/fHHe7LgZiJmZsETLcUktscbO7oiKiO0zz8vh8evr0dDqdpsu0LotKNRcEN9NaFnDruog0Ho/7w37sUkA3rZrzOs/XeZ7M5Xp+qmUaxl2pakYhJDN1NVO/wXtfnmW/WSUEaFnnl4cIKdCWtSN8TgQQkQgAmYgCc3REVEd0JCAnb0iaO6IjOHjDF53ZQySzFtIgf85X2xm4BaPt0/YvcUvZkZDcGZmREZk8UDv76AAEbg6OW/bsSLcf/HcMApi7molqqXXNYVmLKNeqau6OL4jGLSje8moAIiAwM8vtPgHZILrT7XThdpxv+fhrq4RbbIQvh7U90JbWIwGgI4IjILTnBoT+1fv40rWruYgzgxNsRt3c3LSaVi3FpIIYqoE7efslAOaitWheSp1zXpZlnud1ntdldrPA/CD3FKMjOqKZiehSyrys12mSKinEsbvle6rujtT8PKCBqzuZo3lzVAbu1j4BQmQyBMcvjDIhvnuzS3FwS+tsp7SeT9P5PEmVZVncRS26q7khORIgNogIBJEoIDoTpZRM3Z0UFIFUXcSYW9zPHGKMXUpdjF1KkSkAoDsAWAsfHQEZmNvb8/aeOkQOvBt3+8Ph7ni32x84JAAqpeS1TtdpXdYvXbtvnn2DxoGaf/9sngEA3UxBS57Op19My/Hwzbi777sjIwO6mW/xCbT42K7X86+//vjnP//H//Sf/vjLr5+WxVRyZOpT5BjVvVatRdyqalXRl90HG27iL1gi3Lbjq4QeXoFJ233kdcl54HFMKaWuJzAENcklr89Pj6aior/+8uvHDx/BYRh3u91+mRepKmKlqqsgeNclRDbzWsXMYsAuRkZ4/PRhXpfL5Wru+/2x74fd/rg/3u33d90wcorEtyiYiJjcv0Brv7BNG3L++fPXvv1zJtIOGDixEmiMlCJHDoHJlcEM0ZkxRo6BYyQmBHAkCEwxUJe4RejgpmZmIOZgJqbVRF2t+dcNIL+ZUgQE3440uCpUdANEIlN0d3RH9MAYGWMEVXTzLqCpfvm+YPPriIj0+d62jOz2q83ABayAL2hXlCeon2x90nyuec7rkudzmU5lvdT1ajKrFSRGikSBOARmIkQ3RCAKxF2NI8c9x4c0vtXyLXvxSDx4oiExJqZA0CKNBsVng0VhEpgqXAuIgRnKq5dGRLvdeDgcHh4e3rx5uLu7G8cxxO1XQ/O+zTnidtcN03YAJGTCGIM1u22SUtjtx91uN83L8+nZzUKIXecI0Pdd1w1dZykGEZnnRaGFyo6Iu3E8HA5+iwWZeV3X8+U8T5NICbzr+24Lzm+u3TS8Ph/Hw+7Nm+Nu7ONLwOdbBOe3f2Hm7urQ4hs7na+//PLx9HyaL9d1WfIyl7JIXZmRkGpZEbzrYteHw3Hc7YYYyVtCv0zLfJmu15KXC8G6nPpxJAyp3x2PD25ipqrm/oIGAxLhC2S+HQe7OfAvthVx8JbFvFgoAEBqJ4aImCNxJABEa4YXCBHo5tqtBTVEzgwcMAQyYyZiIn2xNZ/NzmaMbl9tuf+WG7I7MzMF3D4lJjLcbGDLUBpOQPy1X7/FMLg9ewTVViwry8KioVRVNTUC9A2rhJdrIGxQBCComM4GZGzG0bF3b7W811YFt2LnK79++/wVDP/i3TeA3reUHfEla/8vuvaWZjr6rcLjSIAALQNFNAc3A1NqddyGNhKBqogueT2dL8+n0/l0klrdzUTdNNdSan14uD8e9uBeSzmdLufLudYSiB0JOTiyGVRx8+1lMgWmQNhyG3dTb4Vrc0dvQTugAdHnt3F7HmPq+r4n6nY9jn2/36Vx5Hlecy25rkWWrcwmyoG6PqWOY6CUQgwcmAjQzRACQi4groCIblZVai1mxkRdin3fDUPfXLuZlypoCu6ESCGmLqYUG3zdimYppaHvx3E37sZhGGNMteq65uu0TtM8Xaecs+nnxGRLuA3NwLyBYA0pAYQtL0F0IEDEIvJ0+rjMp2maH+7LN98MIXRubgji7gDUQg7XeZ6enj98evrp8fRTLiXGfd/FvospMjFGJiLL6+n5+XGZZzXY79/0/R5vNqc57y2d2HbbZgX8tlFfbzJz//WXn1Qlfvtdn9L2ihzdQUXyuk6XS8lrINqNQwgRHNd5Xqa55CKiDT9AAETmEFStlOJmpjpdLk8xOQGn9MMf/nB3/+bNm2/u7t/uDnfjuB/GgUMAwr4fdodj6lLz8bfD84Vrf+3RP1+5feERU+R3d507aPMYZgDEhEMfxz51KQbmFgojOhE09saWoDNy29CBmKllVyI151KqVTEiQEKEQAhMSihIhuiogAhq+LkG4g7mpibg5oxoZi3BdkRXdWNwM0aCxM2BqfDrG7ll7c1of/6ybzVIcy0mi+sMOqFNZFerZy1nK2dbL5ovuk6WZ5AZLSNIg/dEqtkKSEwcYwoccAMPGbHAuhDNHJYur6ASCcY+2uCogA7o6IBiCAjqUFqJXaGoVwU1MAdD9Fc2seu6P/zh92/evP3d737/hz/8wx/+8A/v3r3t+44DAzjYyzsCM2vn1273/4IUt8A4BHrzcI8I6mYmUgsi7nZ72kEMYei7YehV6jxP87wwMwLGGJlDCOGwP7z/5j0zM/HGHGKqtazrvK4L3N3FGLwllbe6lVl9/Tr6sd8fxn5IIRJt0dZnx+mIDsjkDmQOtZRpXh6fTr/++mmeJlcTUTWVWnNeY+DAwdUBgCNzQI4EpCJZq8/LNM+T1BVBmRzc8jJLLUhhEB36wU3MXFWlNjqUBA5dlzjwdnZaktzA2i8dCSISx+baX4zCzSEhATIThUghIgCSITU+xsuPdXQBUEAgciRrJBUiJ0YypC1HV2iA5a3E/ArL8luhwBGBHZiM2Vvdm4k5MG6u3cFtSxHBkT+7VfgcTn3+pLlQMxCxNYuoVVUxt+0omRs6mAEStPTL0QHNrea6nliQYLQYLYyOfHuzzaNuQUArv9w+GgSAr1IQvGVmN3AN8MaJ2RCLZlFfry9cO2yhCoAB0AalOAA4wY2iY40G54boCA1EoapapK55vU7X59Pp6fHR3QIHrbU24HVdq4iaMWEt5dPz6XR6RsShH9ZS51wYpBWBASClDjl0yMyh1Wk2aNjbwTAHAkJGvYGVXydh5BSQuxSwD7sxjSP3PZ4ufD7DvKxrLirWasMpxr5PzNil0HWBqSUbplURAziBFQFFQDMVLbVmdyWGmLjvYt+nGCMiiiipIwEzceCUYj90Xd+Bu5qZORKOw7Db7fq+77qOKZh6Xuo0zdfzfL1O8zyXUpD8y3eBjVJk/hLFw8tuMHNDR0RGVoN5mU/Pj/N1VeXj8buu38O2ed0BN5zPZMnT9frpev04zY8OsR8ehjENfUyRHIBAXfP18uHThz9Py0wUU5fGcU+EAGS3MOUlWX/tC2/Z0RdfdPcPv/xkJodx7FIkCi0NqLXO1+l6vlwvV1Xd7cYupaEfp3l+fnoWqbUWVW0FsS3VQjJ3FWVCBMjrsi7zcDjc3z/8/h//q+9+9/t333x/d/923B27fgiBETeaAxA3ksIt9f4SaLxVGvBvrNXrNGHs+fh+196FqpoqgjH5OKTd0KUUI3OzCQhOBIExBo6JW/oeAofYCr4gUvO6zItaXRWqmkVkijEQdbFLQZmZWYiMqxdxVbgx9QzcoLHADM0cid235HvDtQCZMAXqEvdd6PtYNXx1Xy+0rK8xVwcwVVklP2t5tvqMemabQBbTRepiZbJ8tXJxyWAFXQjdEQmpSi21ujsiS5QYIjVwAqhxXRELc3X1QNynVMa+DiwjaQVJJIqV0BHUoOjm3auCugMAIbZs6LPlCuHh4eHt2zfv3r199+7t27dvjsdD13VE2Hz5zWW4g9vm3r295Y2th5vpiDHe3d+J6fPpudaqqsx8PPRd6sahH4eh77t1mTnw6XSKIUDAvuv7rksxHg6Ht2/fNi5wYwarGiGqikolwpQiIjYAu5XYVDN8EaOkYTfELhK3/PWFgeY394m3wABU7Xydn0/n0/lSc43MtiEPKiIIgECtqklEFBDIxDKUYiLTNK3r4uqEHgjMXEqutQAwYVCp3opwaqKac16WpbEFU4oxxY1SBnDjcX1ldG9Z+82df4aVkRCAmIgTcSQDJCUiItvAZXNwATB3QTR3BbBWPkdAYmBzBgMX9wqIhBGBYWOPUfvwhibizbUDMGtz7Qa8lTqwoV62hX5g7v5V1t7Kuw03ee0szVHUc1FVaziYoyNZK80hAm3m2s2dzBDByqLLidQJC8BgdLfh3FvdYnu54K92qjdAoVFdGky3xSzto0UQNwuG/lIu/RsY8qtae8Pk0UgIA6A5EhA0dJkwEKpbzSLLMteSRcUBkMgQ1XxZllqyq4C7idYWTNZaSr5eryWXZV5iDO726fHxfDnHGJdcci6/fvhEDgQA5jHFu7u7e/RuN8atBmF4i6EaXgTICOBOAPy3ptrNz0+zVcQjdr3FiPsDpW7cH8Pd/Xi5LOfzPE3rshZECJGHsRuHPiUOjI2sV4vkNbuhFtPo4K3cJSpFLQNIK9JzgJSI2FWkkQoDY9/1/TCM4xhCQMRaq9eK5ISIzlo9e61Z3V3Vcq45VxVl4hQ7Qioyv9pQ/sVqTBwybwDMixdFAkTmrksHwqfz+cT06/TtZRgPzLTtHkBUclOzUutSZBJbAT2mMI79OHb9EFJiEZcyXU8/f/j5P/71xz850W7/FqByQARyx8beeAnYP+fA//nlbo8ff1HJQwxS193+OPRDH0NelsdPn56fnkrOQ9d//+13gXm/P5xOp59SFCmX8/NmogDQvUGRCJRSCoRD3419/3A8/vCP//jtH/7hm+++e3j37fHuzTAeYjcwh1bAanmpv5Sn4POR+jvrcwvArdb+6nsPu/Tf/dsHB3RAU28cCHBr7QkN324mmNAJkRlDwJapB26unYnA3RbN1/x0ff74+PhxWbIohLjrh/uxO+6H3dinYbTLpJdJplmWRYuIqJpVN2kAu3ur6UeCQNxKiRgZu8j7Idwf+7t9POzCcR+P+zR/WdxFArpl7VsCsJkxd1PTUst1mT6V+Ze6fEA5sU0EQgDu6lbdDB3QHd0ILSBwYIZYSzXxKmJWCwuHEAKHECgEpoAYmBABwVTrWpbLfP40BR4CBjAEN9+JIxIr4KqQFVq+TgCJGq8GTq98u0j99OljKaVWIaL9fn88HkMgZlZV9wDgAqCq20G6Ze0OTs5bpogYYuy6buiHwCHnUkrhEMZdGLrhsD/cHY/73W4YuufnJxHph6HrhpTi4XDoun632+33+7vjHRHFGEWkhf9d1+33u3EchqEfhq4RoZprF5VS0usj06g7TtjwOnqpsn7ee35L7GzN5fl0muaVOWIiJspSq6iYOZDqlni1vE9dlzKfJ2BzF6mlqGhzGe7acnQABEKzW3sEACIxuYpN17mUAoAhhq7vdrtxvxu71MUY/s4ZQiCO4PwZGLsBIw1CJkLmECgYGRMTATG6E7i7V9PVdHZdEBRIxbwCa+yRnRgZMEIFXWtdwaPzHom3+nvzftCiagI0AGuuPYQQAjigIWMIZORbBWqL95pX4S9ZAy9lt1chTCvjuyqI3BB4BCcnd3Mjx1dmuu0zRzCri+ZnNhMWiveeFMFgK6x5i1bRARucemONmRGRAaAD4Qs/EMA3nMDptjW8/WJw2piBX7yQL7P2jTagXt1JnIJTA+c3woGji8tS18t8Weal1mqt+IvogGvOyzyXnE1VRExNVVUl51xrrUWWNfddR4Sny/k6TSnGFPMFr5EoMkfmwDwMfUhp2O/E1MDQYMN1NmB7i6cQDEABFFsjwSvXbuanx0mrI7p73w0cUui6NO7S/uC7/dAP/en5ej5PABBTOB73x+MuRuZGO1PNuS6BrarkKlVMxE3dxay4VUJjIkIjNCJHMNViJoiWUrcbd7vdYRh37igiZsgK5oYAKr56BSjt/auqiKsBIceQCFk1yXXVW2W00T3hJV9v9N2XzratZnrbl06ISRUv1wtiuF5P+/1934/M7O4ibiaApd1crbNqBfAQwjB0/ZBSH5ihSq35eb78NF3+uswfuuEuBAiBA7M7mcHnQ4AvhuDrM/71cn/6+Ms6ndEkr8u3334P9w80DMs8PX76dD6f3Xy/33//3fcpxmEYYuBpun5Mwd0AjBgRqYHQtQoAhhD6GMa+G/tuPw5v3zx88+bNfhwjEbiaiqkiIQEAEm3Y4GssF1uV9HZ1X6TsX+ENr+9jHMIfvt/DRl1ENzA1UzXTRohoFHm49Xny61Y0Ig4UIwGCqc1aluvT06e//PzXP8/TpIbDeH+8+/ZwJ7sDY9chMzasStVEwQt6NijqGbyKqAMBRIQeqWOEEGJKNHS8G/jh0H3zZrw/xLGnvuMuks5fyFe8oPEt/N8qKw5mjmgA5lpF1pKnvJy9PFG9MCpvJtBNK1hjIguYgishAjEBgYOJVVEhpVpDDCHEEC0GCMyAzUGZS9Uyl/m0dN089DEGDtEpKEZkdKBiWBXUAQEiQccQGSLBh1f3UUr56eefnp+fz+dzSundu2/ev38P4A0nAAibQ99yM2+ttnCrR5iRqrlDCCGlxCECYq2iajEmprAb93d3928eHo7H/TgMiHg5n4d+HPphHIeH+4cY49APu3Hc73Yxhr7raq21Sqklpe7+7rjf78ax7/vm2hEAzZwUQ/iiPkLMHNnRdSPSIMFLYdu3Nwbu7iKyLOvpdFlziTEZuZsagNwoSu6gauYbHQTIcl2nWdCM1GCr3QVApAaEIbjjjaq5pY+EgBwa4XRZ87pkQIop3d8JAhJSCAFaavuKvIyAxBGcPjt0eJ29N9fOTByIGQMzELsbgpm7umatk+uEXhC1qlcPJQhHA/TAiJTdF/HZrSPsCEODcx0MgQESIG/cPqSGy3D0GNAAFYlCQOPtWrZMaePJ01faLrdrf8kYW5uZOai5aKtuNj6ctcCipfnublv/jAEAumidZD2xqSYDnREEICIwIzC3nuRmujdOjRsYOBGYERHcEvX2CZo7GQCCfTZfDUMFu6EWr+/jy3AeADfWGoCKc3VwAzITlSplWebL+fnpej4v8yIihGwOUqVUybnM83y9TvOacym1qqiambu1A3OeliKWUhcC55Jr0VpkpdzHREN/3O33u12XYt914zgys5tprW5EzFtkhUTMkVulnbAxjzeW2Bfss+fna841l7w/DvtDv9sPw4ghxN3AMfDQhV3P+5FrVQA47LvDrkspBEZTNZE+UkfgpWjOsq6KZqDglbySayQICOTuolIKIGqtCNB3YRzH490hpYE4iKiaI3NMSVVEdC2l9fO7W+vEaawKjjFwSh26+2V+enUrcHu1W9jY6uvcduYWDxOgi9RlnS+Xy/l8vl4viPR8+rjf38eYiIKZqxXV2X01nddlKaVKFRWjjlKXUkohMLjWOq3rL7X+dNjZ73941+9/2N/94XB4CDGpooMhINrN5rzgha8c4oYbvqaam3/89SdCOJ+eLucTuCGYlv35+fT89Fxy3o3D0KXjbldrKaXkvC7LVMrqLq2N2/DWWHFTGuhSGvshBjap508fwZR/6tIw7o73h/uHh7fv98e7fhxD6qABd/BSvsS/Uyd8fQRex+9f4hFEmLptGzb+uDuZkSm36s6tNWgrNW54ATTGb0MEucUoJcvp+fLLT7/86z//6Xw+mXrq9/v9z2+++cO791Ps751HXR2qkqxsS/DZfVFfwbKUrEUcEtFIdIwBIkIfebfrj4f+4a57OHZv7uJ+pC4AM6LDV8aL0In8M0nidvgJEIDZQ+zHQe/JSwAtYCJZdTbNCIqgsPWUlFqySDVVpq2MREQc+CWIMtXaolF1iIjAjMGtglUwaR9uqmqlKhU1EmR0AnMSR0LoAiSCjqBjSAz/+son5pz/8uOPXdefz5c3b9+ez6dpmnLOjQvdIlpTExR1M9VbUxE200vuqlZVmQJTFNFaFQCZY4oQQuq7vqlWEHEIoe/63W6/2+37YRh3u/3hEJj7ru+6LnVdTLHrulJKrdXM+7579+7tw/3dbhy7FP1zHdSoUcdebznaqo7WCsgI1ljb8FJoBcDWt7ZcLvM8FxHvYlKQNa9aK7gxYwjB1FSMokcEJnImAM1FyS0A9CnFkMDYFRCdAjugKpYK6iyKamDuQBiYdvvB7IFDPNF1Wst1EYo5DSV2XdebiVUREXl9cphDg1FfEMWba4dWkKBGiGuKH+TEGy0exREUvJpmkAWgULVViSDHfsG0D8yBFGlVXV0h+EoqgGaQzbJhRBycR6eBKBExARNhDJAiCUBFwsBojLeuoZdKOm7Evy8swC08gQYaN4q3mau6gDlviSQBeqOB+S19t2YiGcAQqtSlrM/sZgOTZXRBNEbqIncdh8DEZAqirq1qSwgbKmtmrf6Ejd3k4LD9cNiiv4YNbInuC8/x8/ob146I4OTWiD5qLua5rHld1vkyX07n0/N8nZpIQozJHXKuOed1XedlWea11FrFqmitohvf0txBc81FQighhFYyRHdgj0PYDbvj4Xg8HGJoBR2SWqfpWqVSIGbmEJgDByaOHCMT3w7EhoF84UscSlG3XKWsa16XoZUOxh30PfeRI/UMHhjyWkSs70KXQuORqVQRSIECQlm6tYtLwIwO0AoVQqCBIFDzNiqlIqG7hxiGYTgcxv1+xxxFwZxCCgECAtRaci5iUotKrWYWQgiBoDVkNcZ9CpvXeLkR2LrewFuvAoC3xB1u3Wc3zMhNVUup65rXdSHi5+ePx+ObYXckSg5QpeZ8VplU5nk653UpuYoYIqfUpRiZaV2Xaf645g9up7u7/nD3ttv9Qzf+ruvvmGN7iQgAt7bb189824G35/96j7n786dfa8mPnz7ldd3vdpGpHtbT0+n0/ORmY59SSpHpIjJdr8/PT6fn52We3ZTAWwHSFEy1VhdRd08xDn3PiGVdHj/8cjmfgIhTN+z2d2/eXr9/fvv+2/s373aHu9QNFCIQv4I38e/95/PBhs9o/Fd/tQXaN6SkeXd0p+2BwEt58XMtpZmBm1MBVSu1zPP0/PT48cOvH375+fn5UUSY+254vE5rLbq/+yb1xyJe16rrDGUmXdhW0Gx11XWuiwL2HA6JK0dNuO8D73s47MJx3x32aRy4TxiCo7vq1+H8Zm0J8LXLd0BukWJKMCK8YYJAyKCrLLKKSnZbQQuAoZvWbE3AqgGQiIQYOACgsbUnIE0YQcQcFYmJnQO6ICihBQJmbEG7A6p5EQM3IAJ0JEgMjLBjHAP0DIm9e+XaReTx6TGlrop8+PXXn3/++f379w8PD2/evBnHkV4WkrqYmd+I3YgE5igE1HIPQiQ3QKCu64dBiHIIaRiGlBJiy56cQ+j7YWgQ+zDsxjFw6FLqUpdijDE6eMml1oqI/dA/3N8dDvuui8ybtFHjR9ywuM/rJqTjDreqa+t4gK11ueV0pdTzZbpc5lLEHUMIYK1xH2JkhEjspRR1IbfAjozApKIqBuDAyJFjDFpQ3SnGSH3qegeeZ2HuHEh06xgkpq5LeDw4oChkm+eyrtWXIlXMGwe5VP2SZ0ohgDNsLv3VaWiIOSEHDsymSkTIjGZIAIoQwAWMWq+TmhaBimYIq9rc4RooEaC5sBUTJwkIbr6arwqrcwe8h/iAiREDUSRkYuSAHNAcEDbXToDUint+w9XdaUtWXtuA23HdTkYrf7uZK5g3ggCi4Wsg3hqFDxqkbwpeJE91PScC0B4sNw4BEfYp7HddSsxMtVopmouIquNmXc0BsDGkthpfw/Fb5xz551alhlD5i3d/tb4qwtHWI9AchlkRmdb89PR4en68np/X+WpSpIqKNnajA7YWPwDoUiLkWOuSK+biDpJVRFqms3Vyaam1EmIIOHTp7rD//v2379++2419JG45m5gAOD2HEJhavS6GmFLX9eNuN+4PXdeHrXL3wih4BQ0R7g87N1vzvK7r5XK5XHbLnO/uj4ejxcCtbhFDsARAhkiqBogUgoOzO4KFBLFPqYscmQh00+MTMG0qfdg0rdQYOTAP/XA8Hnf7Q993DuhoIaU9hwYml1LWdW34ZClVRZkDMYMjIsUQQqSui7e+25ux9ZYigQFYU1tw0O2jiaAZKTIjh5C6ruuH0HUOmMtyunx8Pj2Mu3vEGGOvpqVM6/qYl+fz+eP1elrXRasQcUpdiBFcr9OHj4//vOTnkCgNDxzfc/ctxzvEBNC0PsAQ4daMd2Nz3pLi2377yiM6+OX5tC5TSBcm/PHuiADfvHs/Xabn50dTRZe+75nw8fHxxx//8pe//PTLLx+ulyuoBabIXMVqayVGVFV3Tyn1fWci18t5nq4UQkgdhgBEv/zy888///TNt9/97vd/eP/97958892wO3DsXrQeAG5g28s5hL/j4P+ud391U40YBq/L9y8Zezt15nZjeW4Py9yyLJfr09PTL4+PP18vn2qdtS55zarLdMlgzBjzOu/2O9Gylinna5GZ2SK5l6JLzudpmYW4T+loPFGaI7zrCAMk1FgzrwtEiqYUGAjBzXP5yrW3AOWVa/ftLhwRiRt2GgLHwIxKlhcUnbKUWcoCVtEN3BibNYe2M5ljTMjuABgCg3vJudZqZgjuKq4FLCJIYIiRU5+GoRuGYej7rkscwu0dOZNHgo6xY9gHHBkSeSQMX8IPzSOKyMdPH/+XP/4vIQR3+Df/5t/8/ve/b+RWuNnpja2xURG1IdOwtb2KA4YQx3H31j2E7nQ6M/Fut++7jplUNeeiooFDiqnruqHrh76PIaaUupRSjOM4cgi1lCpChF3X7cYxxYAALbtpRRtVbfnZF3cBzuBwqxi9AHU3InQLkmxZ8/PzeZoWQkYGMCfwFAPh0PcIUA2qoaxVIAijcUQKXLILeCButREgLCYiFjjsdofvf/hd3+9Op3VdKwBVMVUzU3BuPNCuS8M4jAJFKaaIn9vLt/bq1++C+SayBi+OsTWLOqIzYQjEkciImIhsM94ECEwQGfsKIlbNKlg1qWVZwJDpSt5B7F0IXF2q1tlqUZkMqwXwuIeOKOyYHBmJubl2pJtgURMLvXXINO7ZLT1xRIavD/oLU+BWbW/nGawR9IGQfMtx4LNrb3A8G6JbhbpKmbVcPTJ5RVcwA0Yk6vru7jCMQ4yBSpGc67zSWmoD/GvbMABGhtwy2ObZ3QARwTZ6gbkbAfhrVsar9TV1tjHEG7G+FaiqyLIs0zTN8yQ5h0AxRkIS0dtq+Rw2D7/V3rEBziCiN+2HxjxyAAiRQ4gpdcO4G/f7bhjcYc1lXZZcspo4Oja3HlOIIcQQRVQViUJMzCGE+BlV/TopwePdrtZaNS9LXtbGM9Gc67qUGAMi6AbHmaiVWIsIINziD1e1RlxSVzURraJFpIgUlUpETmiqqqKVEDFQDBy71KeYmLkRUWLXdd2ASGZQckkphRADp3XNpch2qMEJMQRKkbsubvHKZ3v7au9sGwzgVvJRM1M0ahJ1HFO/OzwcDm+eHn8ueXp+/rXvx76/I+6Oh+RuouuyPF/Ov1wuH5b5UmsGgBhi3w2IsKzT49PPv374Z9MlcBjGt/3wA8Q7h6EWk7qKVjMHYGgqEIBbkNqcO8LfCxy325iv0zJfur6bL+fnx0/DMIDpMq+X8zOYBXQtQ4zxfDp9+PDh44dPp+dzzYUdElEkAjTd+rY3PlSzg2ZaShYxZB53HlKn7jmXdVnLsljOmgsa+Dsfj/ec+OZ+4aWTdLs++PynF8ThVjJ8fR+feap484Y3O/bKr+Nmnt3Q22PainqmqvMyP58eH59+fXr+cL0+17KqVpVSC6hJiH3qBtO5rJ36XOpJdQbI3EUObGvJl2W5zMtUYxxouEharSsoSJbQRte+ZJ6JELBUDpsMLczrl5I1N1baF659+y8BADkat7SkWNlrP5a1AyBTk1pdM7o2GI2QEbnpsSA1dRYAxNYHyEhEpLWaKbq6FvfoVsxW00V1EVlqnaX2XOMWSVFE4oAUmQeiXaBdgIExEgb011zm9oLcXaQ+Pn76p3/6J6bAHIdhfHhoiTvexKwBoNEDGjfCbWMXgLcWR4e+64+Hu74bYujckZGOx2MIbCLmVksxU2YKIcQYU+r6rm9+vUsphND1fdd1kpKINCGd1obn4KJiN8RVtX188ToYgG87sJGaXqhbN2cPZk2cVdSMKJhKzRndUggxdA5kkMVxFfJgLgqoGJACshEAphBSiMhsBmIi7jH0w/7w/rsfjoeHcXd5ejqfTxeRlyqqtypSjGEY+ixQlbiJrTXEh5rw2uv3ARTCC6/5RkNDIAe0xjjhSCGSaEthDEHAFaEiZKIKZM4bxd7BTYrJAiaBAnnPOqpELVCLwlq9FtMCgZE6xIjcUegoJA6ROSAytX6axk1sxYDALR55keDCLWv/m8Z2gC+c5ea5sVXoHVoj3tYKeAvet02FgIjmXr3OVieXGS2yV/bmrwGJQ+CUwjjEoeNaKSdKkXKhIpaLztnWomoG7hxisyU3JiW83MKGDG6Eg5ed8nn9bVcMQRMWdUfCGGPf++FwQLDjfkTXLkUEz2te17wuyzwv0zQvS25rXUtVN0dtrVnqKuauSrd2zU3ShDmk0PUU0mVeS/lQc9Za0Z2YUp+6vhvGoR+6vu9SihwYt4CBWkRxc3afKycvd0GIx+NOTYk8RpymuYo8Pj5frtOnj8/EjYnvamYAThBT7Id+Wo7H4yHFQAjrsszX6XI+ny6n83ye1ouULGUp66wiRCSWGukEANohUDURE1FW7fou9V3q+tQlcFL1GEMIiSkSRqQAkEsRVQEwZGSGrqNxCCEE/Kry0zK+7b1tb7PVZVTBAplDw0NDGN68/aHk5fn5l48fpqfHn90s8hjiMI5HRHOQUq+X6cN1+rTmq7vEyH0/dN2ulPrx0y8///IvP/3yL10cjof3B77rh7dIfRVf62W+npf1Yu4pHkM6cBiRQru0G4K9sf3gtrs/3wB4KUVFA3EKjGDLPP0imte8LFNAqiVL4EBQ8zpfp7ysKsoAKXDHHIgcsYI7IQViBFfN67LMIRAyh6qCFPpxvz8eY4yOWEqtOf/85z/XZdWqKhZi13Ok8DnP+MImAeAtTXodnuBXbPrtUX+uJbaNt30Xfo4LfEtkG+UVzd3dRDzner7On56ePz09nc7n6zSteRUVRCQGR1DN8/QMMK0rGFzULoElJYAwOKR1mq5P03Sa1yzeD4FzmesarB86KXuTe6niYAbq4LEaoIOru63X+vrQ8yvX/jqueQlZwHyzvCDgFbyAV3DxTYbV3A05MLddHQDQAFPTYGhiha5qEqMBgBCqFFN1ENO1FlomdxeRtZaplGma3477N914n4YDh55DDCkBJKaYIETkJpZPf8OQaK691vL89LwuOcVuvz9+//3v/vD7GZw4cHOnqm43c1TFVMReGErt5zjtd4cYu1JKjAkBifBwOJrpPE0bP8aMCLlp3YTQdV3f933fx65rEpUpRmaOqsQUYwgxNFH3RhPZQvHWpaby2ggzYrzVdPG2FTdqdONPuzs6R+5341BUZJ7XZZ4vXQj73QgIYorEAMyRKICbihVTCETIoQupjylxbAVyRceAcejH/eFwvL+7e1DjnOV8ukit2ihC7u15B+a+SztFAwa3yNscEKaQEjF/LpAgIIeAwDe/Dlu1cGNGAQagyBSJKxAqWPY6u2WwBWXyOlvJXjNBRRIVMRPXjFYqAdpKkqvwsnhdzcTIA8cu9Hdp94aGtzi8of6I3T5QZArYJGlJWt8vICITGd2CcN8Oq8MGyH+9s/wliN+cy43Q9vKCPv/d5vKbI0JDJDSwbPWCMgUsgYBJiBRpU77TFiIbMzInSiEMPVfpStV5rXgxMVlXNfDYaPybhu6GUG+kOXiRS8Cvs1sA+Dt97a2F3IzYEZGQUoy7cSQE1wHBYmA3i2FhCuBYqxFld6u1Lss6TUtVM2cgAsBaVbSxOJVu9UaiRnjGKj4vJeeK5jVnN00x9l0aAIFjUI+3fm5E4sCIFDjcZsy0kAUdvjymAEi42/cA0CSNEOFyvU7Tcp2mFxO29Q8xUqDYpb7vlmW5Xqeh7wLTMk/T9TJdr9fr+XJ9npeL1SIll7yaCCJFFWh8gdYhQKHRcFTcHZjj0A+xSxyCN80MZWZj3hqCWsuyGxFjl2g3psO+PxyGEMOXKiLwYmxxo9psmeJGrDMw9SYFDBDG8eH+zXdv3nw7Xz99/PjJVPvuYXd4++7t9yGJu4qu63pa81UkI3iKXQjB3Z9PT5fp+ce//OuvHz588/b3bx4OQ38Y+n7NdZ3Pp8e/Pj9/WPKVONzd/T7GLvCAhK008HJ5Nw2Fv7Na5tQ0A0ykrEsppeSa82rEeZkZzGtcp2tZJq2ZTCPzmLjvYgzBHWoVAudNwwjd3NQxBEYkdQeoVWqpMcZABCGoiJYyXy5Pnz7u7x7uv/kuDbsml+E3u/nlNf6dsPfro9FKpS/nG1++vwFkTWICXx11MMfWcaLmpdiyynmS62JLIfFOoRONqrwVOAnN65qvBrCWCjABXLuE6IHMEPL1dL2crutcRN0CmQSri+S5LFNZpjqu3ImCKKq6IKGZNh1Cmb/QSAkMMQASwE0HZAMoAdybHH51WU1mLZPVq8kMuqJXcqXWw7vZhibiEFoi5xDcWwYPrasbN+PTxIGrq7fqnLupSi1LrUsp8zKfdofTeHgz7h9St0txCP3Qaa/QG3YG0SE6kRN99X5egNAGrF2v0zyv65LzWmIsLI1v2rJQUzUVrVWkit7Y3c0eIULf9Smmmiq1SjziuBtLXmvOYOrgjs2vEyHGGPq+H1tzW9eFcFOIJdok0Jqa/80JtEIHEGGT2qQv0KCNzb9tws/Yl3mjQnsTPQei2CViFqnrMq/zxEMfeAdEWn0rBIMCurlWLQpuyF3kmEKXUqCYLVuT0A5x3O/H/a4f+9Snvk8pBQBT21L2diIQkZm7RAZEzE3IITA3YkVAptf8TMQta38FjAG07i5DAgpIgVqYxuQEgrZauZpcPJ+sXrUWMyFHdEWvYGIioqWAgmaqUhXLYlUQIIaYQr/n4YGHdzy8of6O0oixY+KABEDNp/tGWEBiIg8bqQEAbgKm7k7wBYjyd8yAw0u55PM7bR8bgR229HkrxqnrauWEdo1cAzOhMqqjOpqjV5Flxf0QrI/MGALHgH2CKhaYWo91Eat2q+59cUGf7Q7CTWz27xmuL1x7M0OuhmDRvXGLmTClCNZJcallXbOUmnPJuZQmQ2NuhubtA2q1XKs5IqK0GWK6KUYwN9IY1GqwFJHz5TI15mpAjBzMyaAWg6XItKzdJXaJ+z6N47jf7/b7Q4ix6/oYIgB66/17CbFe3XrXx8AcI4WAiGZec15yXtc1txje3A2cAnPksMZ1jvP18hRT36XAVMqa13mZr8tyXeZrXhcQ0SolZxUlpCrSmuFEzBFD7FRdqqoBYQwhhZgIgylIlVplzTWvdc05l6wqiB4CBY59z7sx3d/tj3f7/W5s+tevdhT6Rkr5TIZH9EAYmBjJHUVcTZsSA3Echrtv3v9umT89P/1lmS+Pn365f/j1Op92SFtfo62q1QyIY4idu0/T+TpflnX+608/Xi7Tt2+HN/fv7+93XVefn/76y8//+uOPf3p6+tXc9vuH4/6+i+/6BICwmpu1VhvkJgfhmyjHV1VqokDIAGRmJa+p61I3AqCqaRU0LcscCC/PT1oWshrJhy4e9+M4jjFGA1xLYfMQqA/cxZBSl7qhiYEkwyWvv/7y08cPvx4O+8PhcDzs97td3w1p2LljznXjNyT/Ojr/37jc0RxBtyq7v0z+gVvtxDb43W5yQ22SXhZZS12zzkuocB/674fD2p0Ug4hBKRcACwHdTGQ1MJYKmKkVPcQWX6Xa89M0XRZRRI6IkaBHH0BTWX2ZSxwzphyplypLdVEXUUSPjFi+SBP7gF1EIHBqynqN5GXq6iomVetqdZL1WZdPujx6fgaZyDKjBgJjgjbH4qaM15TsCQGYGBiA3DtVaZVmgGLmtbZ5LeiOrq6itWapuazzOl+W6byfT3U+D8Oh7/c07HjYhTJyGWEcsB+6kCDcRvts76IR+BwRQohd6vb7w/3dfd+PDijbZKPP0wtFVEWqVNmKmW1oF3GAxt1lJHTc9YZ37ggxBjCNzB4YnAhcZZvdEZi7rmtN7cM4trp+I2cQEQeicGvvpvY1cAd2MHMOmtbui9OBQNjM821QBoC6V1FzCEyOoLc7l1ov5/P1enHJPqQ2YE+1FlmKzLVkcDGrIgXMDSmFjplCDJE4rwjE49CPw/7ueDfuByRXLe7V4QbJvNBBCbfBHoTInGJsYzxT4s8p+ZeniQMjhAaC3ZrzNzOG5BygSRhtyAcBo6muspzr8knWZ9MMrpEiI6MZuVZVMyUXV+BsVUFXR+q4H2O3T+ND6O8w9ICIUBELEwVGbiw3J0CyrRRNxCFg9JfoCW5uwx2BQb64kRci3Xb5n4fuwY18d0spN47dxqglp9b8ZrJIeQo+pegxOpEgVeTqpI0ROU2262PfdTESMzIBIcbAQ48HA4PgHtaiuLWcvtitLTJxRwTaYB4H3NRKv1hfCc2ibXo9SI5grl6lqpRac8lrzuuyLHPD3ksu65qXZblepnlZ13UtRVRB1Vqy7lvPQGv9VSQ0c+YW4KiZ1yKtvzYQpRA0mqrm0tpNuOtiTBwj9X0ax3nNRQ2IYt+Nm5JNq3b611m7A6hWok3vkwMQO6I6VLMsWlt0qmakzBpUgtaYZ0TEyCEwAqhqWdfrulzXdSp5BTEVlSKmTsQOQJWQCIBS6rYAsDV9ciQMblTbCcs551KKtMYtIuj6lFJ0ByIYh7jfdcfDeNiPw9A3mvFXm6y9Otpa2AHRGQ3d3NkdAAgMHYwYATh145s338/Tx19//qcn+TAvj6fTL6fTB6KdQ6vKQ1MUJeIQokg5nT9Ny3I6n6frQtSNw8Px+GBWnp7+8pe//PFf/+VPnx5/WvOyGw9d4hgMYa3lVAWmaV1zUbWY0m6/77ohpt4dRSzwF3ynjb5Q6rqsyzQhchU3dTOPRCklNF3meV2uVnPHGPfj8bh/uDv2wwBIay6X69XFI1MXuI/dYX+8f3gbUwR3jpO5z9NU8lXq6lL7SHfH/cP9XRoPSh1yqFVrlWgO9JKyf1Fi/1+3cEtwYQv57bPIv8mWFNZapFYR0dYxXA2q2Fp1rVqqllpLhiyD0z3FdxgvEMTQwLOBi4lVRRXEyiTMhkpOJrXmtc5TqcUdA1MyTVUiS+RCPlfgK8RHgdCbY9xVi1WxVifCFCmKDi/3ADAGG6MZgYGLuZgpqDRFprzUZa75qvla80nWpzp9lPVkdQar5MaITSC0zYSErR1bAYCZCDlw5NCF0Jl5IAJwFalY3bKpSUt7zMzFzN0aeg8EEBATUgQzdEWroIsJWSUwAsDOwZN9ab58q+xDSul4PLx7+/b9+28P+wMCbWKTt8xss0U3Ocs2JpmQHD9XTBGAGzOcmhA5Sq1d3zEjApRCIrUlJzHGcRyPd3d3x7txN/Z9H8I2gOQlgbn9XkdsEsLNSzoihfg3fUmvPt/ssNqyFlGNMRCTg5da52W5Ttd5mkRqFwMHNK8ipeRlLXOuk0phB3Q3NdNqTjXURo0DgsABOu66PqUOCUXy5fpU8nS9rMtyqTV3XfKN2NNsDwIgMxBRCOBGrSoBdEvrvzBUSBygCanijXfSDDQ6kXHwTemBiUMIqY/9zqyaieqKkt0ETEwKApA7mFDrCnIwcUc1JTBi7mIYYxyZOnBQyY7qMLH3YAPFgcKA1AMFAIXt+RNjYAoA8IqvuKHo7AKKX+S9X94Y3GKV27aDv/sVcAInMHMQq7PmU7AlRYixYRUOJEDm6Kq+Zp9X7VeJQiFgFylGJOIYue9SFZgXEdkw9y/Km/Bac7oRM3xDR7707V8z5IHb6AJ1IjXNpazzcr1cp+u0zG1NyzIv65JzKaUuyzpdl5yLiLgBIKiBG9ombnwbieCO2xwzUHUzZ+N4awBzBFXLWhZTcCPCGLnr+9SFmHhe+HKZLtd5mlaprTM+MkeCGwjchF0+H3g7n08cyFSXZZ7nSymLQWG21CMJiRiIelVofaCYDAwMzLyCMWLXhcBAXsGyyaJ1MTETFzHwbWBWC1r8JhrPxMyBQwwhgVNepYqseV2WZV1XdwfAlOI49q3ZDBGJceh46OPQhz7FEIJ+le/iS/zoRK1j1BEMNJureTBIIQxEfKtzewjxcPf+3fqH99/9Ti1P03S5/vz4+NcYv9ntMMat0a2BbYQh5yXLz8+ny/ky9d3d2zfv3755P477n3/563/6l3/64x///S+//NR1dHd39/vf/eHdu98N/SDlOk0fz9fpdLosSxaV4/H+u+9/N6Tv9v3OMZRi62vj5VBF1pyJoBFla6kYLyF0MXbH4913796WZfr1p8Wkgspu7Pb7w7t3796+feOAy5pP50sMwcE6bnWp/s3bd9/97g8xJVM5n56YSVWu57PWuszXZepN3ozjuH94Y3Hodns1K1U6M/L/7xJ3vJXothmGTQdBpEouec3LNF2n63W6Xtd5VXUzNCAxKopFUQzMyRzzkqeVK+yofxtGDY5azmqT1UJSASugRAYIVBkMvOSS1yqChJ1j59CXGg1IAcQl2lz0ca0yLNO+zHF8MD6Kd1U2MU9XGF7dxD7aIYohVvfill2KFqurr3OdTvPlY55PkictV8lXLScrZ62rqbg5+KaWRhgIAhh5q+26Ont0Iur6mHa7Ow7xEiMhgYIUXTGrgStrkxYhJgruwY3cENRBjUzZPREyuKuUsqAr44YQAODX3PKt7kvjML59++77H374wx9+f39/3+RW/DbRtfW4+03oC4mQDAw23R5sZ3m7K2ZOt7kPXZd242jWIcIycy2FiQkppW6/3795eHh48yalFEJoLbsbDZlax1yDr5q4P9zKVfjFVJ4vlr9kZgigatfrvOba9RsBd13Wx4+fnh8fS15D4MNxTB2vZcnrNM/nZZ1KndsIIgYCBVH1WhKllbsIHHrsUupSAKda6uRnkaWUS+CQZ7mc13Wdh6FvoKbfMOvNCDUNnVYUxJtz8a9x4Ebn3tL1bZ7a5hgZNZK2GcccmFLPHrqw5+FNOsxpelemD/n6a50/wXLWsrhXdGE0JGQKzcE5R+YhxGOX7gl7r6pycThDUGSjkDj22t11wx11d6EbAQVRiW4zHCB+TrXhdgPu/AU784uAZbvjrUC/BWEvBNtbNu23PL4pWblbsTxLuXSYA3OMKcWOAjmZkzsTGIj6Wuw6VyKMDLsxjBhiQEDk9hBbHz0iEvhNyrBV1RFh687Z2to/X9rr1/Flrb2FZ4AAjOhmUkqe5ul0Op1P5+k6T9M0z9d5mdd1aXB8KbIuuZSqqgBIzKqwTVMS1dbK+QJlbLWxrTSrqAjg7O7WYAxVcVMilBrc3b0DTADoLjCvbs8pdYf9IYYYODTBseZiX595M/v4+JGJzNoQw2lZF7VKAXuO5izKJWPGLNqEkKpB6zYwVRE3goSJQAVd0RRUXN3UXd3BTclQTAhIICiYuqlpNS1SSy5ru5qGdNRaVYUDpxSGoRvGpkidWo2vi5gidpFiICJA8dcxShMrNXip9Diio5tpMV1dSTV5spA6IgRCcEfilHb7wzffvP/HWheAP6Mv8/TLdKWUDggUY4ophRBdiQiqLHkt12laVjnuv7u/e8scL9fLv/75n//9v/9//uUv/zrP1/fv3/b90HcjE8/TZV7W59Pz+XS+XK61CjP3naG/ScF2QwBIK8k1fqG3ZQZqrdlA1mUGN4rd8T69e/vwzbt3b+6Oz59UTQGg77vj8fj+22+//e67N2/fLev6+Pg07h77vquLtspSQz1j6u4eHlJKh+Nhf9h3w/D46eP5+QnBRFXVKMRht4/7Bx72xOGWYVsTHdwgn7+Tu+OrWPzLMPgmxuFAja5RxWopeV3XZZ7n6Xo9n06Pp+fH0/PTfL2IiJkDBoNgENSjQQBKFHozLCU7IsaeuwNrdRfJBS2DV/cCLhgIjYBIAUrWWl01OCTHHqF3GAx7g2jAZi41+3QyMCRIoti74a5qQg4AgP66muhYLrg+boRMEatVa65lKctlvT6tp1/zctYyq6xWV5VVZTEtbo7ATNSaaQkZnQHcTbWqasVIjILRCShyDLHv0tB3u9KV3JXcVfAVG/wMDB5aok6YEBicWr3opmNTVRFBASzVmEoXQ+QQvsram2UlomEYWkf7/f1D1/fm5mLQwnBqwh+ffTwCKOKtGx9ujZyOiAzcvm0jRXad2+3nIK3L3IhjW+K+2x2Px1aKevWLtp3Tqt+IBt6Gmr4gi/jVttp4GZ//6GK+5nI+z0vOR9gRITGq1GVZai0cmBkUbFrXWpe8XtflUsuimlOKXZfa1LRiIFpLyCWslgZG7roeIEzzsq6TuTDDPPcxRBMsi9VaVauZmn+98Td3gv7CON0y2S/ODXDLym/DxaB1iQEiACMEhjbDjThQRIYuBCTzKDV2u9QfY3fIaV/wF/GPWs+giqCETsTE3HRpCHccdhQ6AFQpBmagoIJkyGx1BVVwDeBApmzOjhSIvE3d/kw+g89Ze3D+8m4BX8Tobjf5+f0gvvqWz4bhhTJoWmS91uUi+aK8SjKpVop0IgEEyY1BAdVgLYqLEFgMSAQhbEw0NRORkkvJhdgpRt6e4s1qvRDq/Fb333bdF+vLrJ2JYqMXGoBqlSJ1WqbT5fz0/Hy9TvN1mudrY6qIKADaTbt241WKqkMVV7GXlH2T196IbxuVsEW14o7aAOdt4Em7Rr11MDOFlLoYIyKsaz6fTp/GXYyxCdaSkbo1/urLXZjZr7/+igjgVqXmvIqKu4fAKXVNwm5dVmLObfCWi4kybnxbUylFXNFMyIEcCah1KLelVtGcHJ0CmYKJlVzzsi4hBAJyJBZ1UVPTEDilOIzDbjcOQz8MbajEpnAeyCI78yam8KIo0BYRRibx2y+HFqxBYxerChKKrJ3tUtcHSps+GnDqju+++bdulSCrFMmP8xS7gdwtxtT3fdclrYCoUnLOi6oESvv98XB4mOf5n//ln/6XP/6HP/3pP87LnEJIaUhxWNZS6qfpOl+ul8vleZ6mknOM6e74MHa0G8JuiGMXHAMipPiVsikxhRBTYDapWjBwuN/v/uv/6h/v7+9R9fTpU61CHO4e3nz77bd/+Md/+O677+/evH16eipiTY77WlapxdzU9DrP07y8/+H37799b/btPF0fvvn1l5/++tcf/7xcz0hRHMScY3f38Ib7XTEg5jZYk24zpf924d8cj6+/oQ07cWrV9Fplnpfpcrqcn8+np/Pzp6fHD09PH56fPlwvT1KzmRAF4kjcI3UOHYUxdkeKgzMjVApAKbIMKgPWGZRbcd1MTU0NnZAQTNE9mEf1hN4hDZzGrh9T38c2c47ITSzP6/XZnIKxR1fv0bvi/lUtcT1/QMxVpEptg51yzWuZ1/W6zs/r5WPNk2txE9PWclXdnICazpdDm93QCPjiunkEA3NyE7WqUgVJwBuVuh+GvVYL1IuqO8BWcg2MibljSm3wj2itNde6EDmgASQkFJWqtVqNFl/7HEQkYgBA4tT1h8Nht9un1AFAKbVV7Jos3Tar250QY4yBg8Umgb01777kG+7WBLJeku8moMZM4D5dL8S0EXUAQgh93zeP3gD5DRpokL/pNnYOt/E4Lzi32Re8rdZQ9TKdwdxLrdOyni7XUupuNzQUAN3JLYYQ9qHIerqc5uU8z2eR2XV1KwTisGMmAk7cr7CoWi25disR9l3f9b0q5LI+nz/luhL6YTeO/RipN0FVcavm2mrHAC888q200KrUjfB+i3O/cG+tG46wjQv1WzW+AZTAhFtvOSMykbcSM2C0GGLXjV1/XIeHKY4Lh/UsKktwQQeiGMKAYe+8MxoNozqoVbPbCLqWCzu6gUqp+WoUHFEiaESOkUgpANFLU5Pfkl0HADZ+ffI3wbcbv+l2j5/d1/ZVbK9sY1PgRsNwqXm5PMn05GUqnBeqSKvgdNcv3V1lUg+YHcUgV1MoBB4ZUqSUuBWRc5ZlzfM8L3NJ/dAEEQE/F0oAYOt0v3GC/aX97dX6OyOhENANSs7X6+XTp08ffv3w9Ph8Pp3naV7mZV3nknPLSBpzCl8UBDfeCnz1M18eDLwwC7cxuIiM8DLZDlt4CwBuZjkXaB3xAGYOACo1hnA6PR/2u/pwHzUgb2Ul869c+y/gDujWNCYJQwwphb5PXdeFEEop4zxfr9P1OuWca6mADfFWRFNREwdTlWpirvAyMt21TQUB5EBNtcPNpNS8LIzuVqQ4BnUgDjHGfuj2+91+v9/tx5RCCIEZv5DFbecZEN1v2j/bIsQYsAlvth4OZmSKCD2h1lpUpdZVpKwLh5hSt0/dEGLiMOyP36muptN8/SCyLvNjvIQQViJmDkQoUEWlSq5SADjGLqWemR+fP5zOn3755c+X6xMicAiqMs+LyjNSrLXWWhB5HHeH/XG/O755+/79+x/ePLzfjfsQgzvGSOHLBuRx3BF438fAiKAEHgkY1GqWvLa32w1D1w/Hw+Hb7779/offvXv/7fHunlO35vz4+PHx436drrWW2A/j/kAcxBSJumHsx+Hu7Tfj3X2/O3DsTo8fXWrsRnVy5G7YxXHPuslktGZmv53wl/35+ci8ei1/u25dbSBqJZfr9Xo+PZ2ePj4/fjg9fnh++ng5fTqfH6+X52U+1bKqFndAYuaElBASx13qj7HfhWFwJ3KJVCESpSTSOyyIrIbSeG0KTQVDLTok4p5woDjE1Me+Sx0zO4GYmAE5gJgrskBM0FFHwAZbqeYL157nM7Fs2iluqBXLgnmC9eLrs+eT59ms2ibdYKJ66x9q/SkMQK5uqhs6J+quCE5kAKK2Lss519wauFPs9zuK3Odccimq5gDEHEJMXer71hnOZr6sCyI6eF9L1/Up9eb6Mlne3UW/uBG8jcsmohASc0BAMzeTNoDgJZ/e0PINMcetFt6ecMsN3AFa8545AgMTEAAyc4whhpC6FOPmvxv8AwAtbvBtlusGP7ammFvjMdysH7xUsVW/MJEtaf+889xzrfOS52U1NQRok6a7vjsej2pQitRLnpf5dDnN89k9BxYmbfFQCyyaLKqpqJJqaU3XKppLnabz6fycy4LgWkod6pAqOtdiIsW0+g3j8ReY4cYA+rtH5eXPMYC3JA1aZAab5OHNtRMTEhEjMTAwUQBAckBmiInjgLEX8ArKvjpkKspggWMMI3Z3Hg7CAwCZCZrRxm5iotDa2UNg4kAhUrfj0BEJNWmJrQ8/wGcue0t50QH4S+2drTDTPPvf3OMLoxlvnzeZN6CX2UdrXk9er5FrTJj6BAhlnZfpOV0+JXpgvjcLomAAbIZgiXHOIaUAyES+FlnWMs3LvGQnxia6f/PjAODgZk7uhM5MzYm+DLB4WX+rIQ/uLrWez+ePH379y48/fvj1Q6utr+tacq6lqCneJFGxucS2O5tCBDgBEG2C9RvhyGyjvbXSsZuqIxJDa49tI3P9hrCrmeQstdZS67x0fddzIEIIzJfLZZqnWov1kYEbsfO1dqOpffj1VzPFppPAoevTSD0hdCkd9vv9fmduOeenp+cQ+PT8LGU1VQdgRCY00SYJq7WWUrRu8z/amSZHMCOAQBgJCcFNal7UtdTMywwckMK42w9Dv9uN9/f3u/1uGHrYxHvFqrU4zwMCEhoZoquX+gXiSISRCLlRgAAAY+AQEbsAvit5zXlZpvO6PJc8EdHh/tv98e04HjiE2D0cjgJeTyE8Pf7rmk9+8nHELgEimWmtudVTTI2xC6EnolLXT49//fnXfz1fPhJLl1LqcF3nT58+hTB33W7c7fbH+3uKfb87HO7u79483L8/HO6HcZ9ijxgcnAhet/Ah4t39Q5cigTJqdIhMXcC6Tn/98z9fj/e73R7AHx7eHu/uf/jd7+4f3hzvjncPb8b9AZhLyU+fPnz8+fD4+Mky7+7u3n377bg/cAiiUlXvxkM/7objfb87pn73+OGX+XIKITgGMUcOXddH3HoNaJs3739rmv7frsYkNzc1X3Od5+X59Pzpwy+PH//69PGn50+/Xp4/rsu15ElrRldok6+3WlUrosUQupR23Tj2dYchsXMCDkxdYoXeeLCyFCgIxVzdzYAA2IGRusT7EHex71IXQ0RiNVMta1FUI+BI0Rk4waxwicaxI96Kv589ogNIWSW3cQyUIER0UgRydSlWg1XR6lJUq5iJqprT1rL6UpVo89lFazUpZkLkFDxEIBa16Tqv7oE4MaUYU9eN+z2JyLIsVcQciEKMqetSPyRENxeRfJmmXMta89CPwzAMw66qtPEValWkVClfvZEbvd/dwdRFTKo4tC4+fdmBL8LyfMvib0H1S57tAK4GLT9hZtpGDAE4ECFvSp1041jcEpkbNvnlhW2dyFva57cRzA3ZlL/ptnpVGTLzUiWXLFUAgJliDDGF/W73zbfvKaTn59Pp8rSu67qspWQOygFTiikAB3Z00VryKrW4ihmaiVmtteS1TvNyPj9dricpBQGgmq6ifQ0UVVDqolrMmzqCmRkgtrnpt6L5S6n6b7weQgyNW2Yb+QkbgRmAkJs8XGhEByB2Atqo7ODkAZkgROMQXROI2Rqw0uRJcwxDSCP1Rw1Ho94REWybWUiBKMY0pLSLqWua/4gEnJBCb1OATBQNQxtAtgV8n/vWwKEJ6ry+EfwsCvpl9o4AG5tiG2niRBSYkNkJEQzB3LKUC8PaD3jc9w/HIFXWdV2vHyH8uKNjFx6q9UUYzVCJwT3gutY5sAMz0VpsLTIv67ysTYSHYgiE7rcGCjORClbRpesScU/e2JP/edfeyl21rMs8nR6fnh8f5+u15myiYGaiptpQmYbYNNXb27H5jFl81uCEDeaGW2RKhLcYent8RBRCwA1ZMHBvw/5IFdoYxKoZMisHxmVZTid6fNzd3x/Bdbcb2ul8HbCY2+n5SVWIMcbYdYnJoU9tGFfYWi9iYKolr2u/LmGevFZxU2QOxK2VVKuUXGqpIrKRot1a/MGIMXBsBqDF/SKOoG4owqmPPVGgEJmZAMHdRMTdNlNphoREZEqqKGoAkEvN6/palpkQeJt7CE6AiByIqVEqMCZyJxWtZa3lUylTzvM0Pe6Pb4fxLqUeuR/372ud5uXTPD/P8yN4YOSGQ1ZpspjuThxCTLHW5fn8y9Pp58v1o0Pp+9gyJ6YOILozcdrtHu7v347Dcb+/Oxzud7vDOBxi6gMHIDIHe5FwebXac9ooh4zcNLulSl5URkTaH47j4f6bb7/7wz/84/5wSP0w7vap783tejl3fd9OFYd49+btt7/7feq6fhiQSNUwhG7cc9djSMRxfzzOl7NUIQ6OXEXVjGOC7Wz8f06jM7MliygW8Xlepuvl8en56enj6fnj9fIxL49Sn11n8IyuAOaOplQFanU3A1AEYaolZKmTyTX2iWJK2BEnj2Zg4lABCZgp+pZOEEA0HwB2MY4xjd2QYkK1Ilq0aKleKoozcCLTiAgUMCQOEUJCC+RMr7J2BIghdl2fYopt7rAURnXLNccUYhc7lbqx5tVM1dUMUUmBBDE0WN5UVTZQ36wSee8RSIGrk2s1NWLvgRWgb4YwMKeui6kjjin1/bBrIrNmdc3zNJ/qtZYiZquIV7GtqiXm5rRJW76KUZpTd0e0WiWvOa8558whYIPATJrfRbwhi8wxhtCmUSCY6YaGIRL5zfW6g6vqJkBPpKqqpLYRzD4nc7f5nbfx8J9ZVe1Jt3++gZLbfBFtunKv8yszV29TZl/OC6WUxnEAgDbfgRCJOcYYQnA3qbXmbCqBqUvU9xSCUxs1LLnUXOpqJq3W7abrOl8uz7X6PC/TdCnLolXQYCnuq9miMSTE2MLQpvYoIsu8qoi5E22d+kzEHJj4K/mN276iDUh5IT9hAG4ewomRGJGhfRACtW8BZHdyQGDnrscHYOmpYE9p6rlcwcnD3ocjpqPRSMSNhckciVuncR/TGGLHIW7vAAnBu4JRvA1+CEzKdHvEjq9cOyt9kbRvaavfEveX+sOtwg0ACLcuPuJGD0ZkwogegwauY8Jv7vfv7v3hwHldL+cVWBmuLhcpl+pQtWulWwYHp1xtLQYoxJSzrkXmdZ2XmWMKKUZNxuTuCMSIorIuc17Odb0ejvs3b98F6gEZ/gtZu5tZqeu0XM6n09Pz5XRy0T4mBkLzQrkCMBMibFLIDVR/VW5ow5Ebk4Jw411snt3cwYi4ibYQobsiATVMBRHdVAwQYkjttziA3UgmZqqA67qaSkqx76JpBXiTug6Zv+KhTNeLqoRA2iUC8z4xQiBAd5WaW1WcMDD2XUiRmKBYrblQjBziVlQy0yrNtTeiJIEDITtG5i6GFAIhgJubgpM7NXWLSF3fp76LMbK75ry4a87hdnXuTYyXqHKkAIQuqtdpmudZ5GsOMBNiaOHtDUfc1DM5xGEcGc2X66fr5Zfn55/oYzjefXe8f3+4ezcMQ+SxHx6G3X0u1+X6CZ1i2JVaRbWK5CJuiEjMHALO6/Ocn67To9oSE3VdF8MuxX0X71pQvD/cvXnz/bu339/dvdvv7rphYA5urgbmRuTUhi98dejBpZSas1tFdgrEiK5KbkPX7cfd4XAYDnfd7vDt99//7vf/0I875hBSx8xSyrg/IFLJWdVC7O7fvvv+D38IIRBxTFFN3RE5BA4jhRjT8f6+5nWZpstlwhByzjGvQwgE4Yva4P/2JerXVUqxebFpmi7ny+npdDqfluWs9cq0Dp0EFEZd3doUMYPkjtbGkzTIB5TJVKrJOtQwjDGkoYsDBFTXYoLuhJRianAWUnAIqp1bH+KQUt8PgdinZcrrXLKs2YuyAjsJuzgBRYqSXDu0njySB8QvJGuG8XA4PAx9F0NwgJIXABUtJS+1jqrSNCpEHUHBwHVzga2dNHBwYlNTbaoNVaQieidx8BQACR1ZG3bYwkiE6B4QGZBi7Pphtz/cHY9vdvt93/ci5TqdiblUKWUt1cyqKqqCCoARU2DODvSVRGvrPATAUuo8L9M8L8vCITJzU51oZqp9c8vYVWOMGi0QYZtLiYjMdINpt2hAN9odBWYhJIJaqtRqqi/JzO0CTFVfogeAVj2Dm1Vscaybb80UmxLZl3ehqoDI1EBi7Lv+uLfyIO7epYhNhmY7syWvuay55sLuQ9f1I/UDm5dSV9Oi6mtZi6wOFpiZGNyX+apFavV1LXmdTaqLmUDJxciEa4xd6sZadGPRuUuV8/k8Xaeccwjc931KKaY0DuMwDPg38TsixIgAjmrq4q6AgGRAgAzESATIvqXsAciAG24CrU1CCQgCWdjHSKGj7jju8h3MT9M0rR5rf8B09LCD0DPHGLuYhth6qEIkCsDBMTafgwDkNS0l5uzEys4MITY0ZQNsXlw74VfEoAa4v+TrG9F/E3BER2yFHg9tYBkTEjlhYOwYLcHQ+f2u++GH8O7BjztYlzgMUaxTAqes5SoWqwcgREZDQPAsFosaCCEV0VxkWddpmUOKqe96G9mDmSMZAYrUeb4+f/z5/PTzN++/2e9HjoyI8F/I2k1EStFa2qDAwDwMQwixrAUda61mWqSY6a2Utd38ra8biDAEQkdmV4Omx6BmKqou4NikiUPgEEgVADY5TOKAQGbgDth6H7m1AGgbhN32kdRaS378hDFgjLTb9Rw4xfiFLpJ7WVczAY8xMIHHwEOfUgwIXstaa2ZGIsolmxYmT5EKQXWRai4C7lZVStU2rF0V4VawAyTf5nC7m1ZpaB8EboEeBYoxpBRCZCRXlTWvItK4tb6RUDarZIAOZA5VdJ7nZV3klfFqHFPcdOg2ykJTCwT3JgeGseuGw+HuvVpx8GWdnp5+mqbn0+mX/eHusD8glC7t+363TB+l5nmCdV2b1IzZVhFULblMS76qi9S19QiFEMfhsBvfjv3DMByHcTwc79+8/e7u+G4c77tuF2IEQPVNwQAduI0K/colOuR1KXkNDCGGLvV9H7sYxnF/PN7fv3n38M37w8Pb8Xj/9ptvDm/epNQhMnFAgDj0twACCChF3u32x4c3MUbYimJ0g8k4cQiBu34wlX63cH92B0MUUVVrY0xh26z/ORf/X2LRVbHLtazZ5kWWOS9rqSKIGoPjQF3orNvX2peiqUDKEFcPq6WsterNrhfXCqCBnRgRnFRZFyYjDJGIyAGRAyHG3X7cH/chRAe+XmWejdljgnHXcwjVHAtZEWNDYnJUdzOvNVPmEFIIKYaOQsIQDb5w7RwCM4tpXUspeVnmaTrN85SLIIZhvOPQMafAiTFknCugSnVTB1czBMCtNiWt+6+KONg0LxSwkxITb/vXxG01Y1NSpUYt74c9Eo27HQfq++FwOLpbTB0gmsO6LiK1yWuqYF6lS2aGTTnyNSfb3VWbY+YQWjrrOecQFmZ22C7vlj5CyzrN1CyaStOGwk1tiW5VKmwzMdq/8g1EJFbaFHBubCHYIE41sxcsodmILfN/IVn7dqkiTfBASimvd1qD95EJnRp5e1Oh75Katus0s5LLdJ1Oz8+Pnz5N1ysh9l3XD8TBVKRpbLVIxUxDQNBN7bsxBgVKKVVqjQF3u96qWXHNbuJZxIw5uDuawQtuiICqOs8zIuacmUOMwe6tSfTcKjS3b0YMYcNpwUHRHB3RseXr5IGBGYiBGEIAMbCNcY/swACOTs4QggfqovU9jCtaDOKsijSOPOxjd4dhJI4x9CH1IaYQIhIBkSM5bIVdBCeFKCEaOwEFsGBNB8RfRDN9Ky/8jZQIEEHLp9qMBAdH8kCtxX+biRwYhz72XUohIbM5gK9ktfiCeiWdCTAG6gbs+qEfd0VSqf1ivvjqVreBnmgIqGC16kpF1BqfNBdZ8rosU2sCP9jRb/QNA1CVvC6n50+//PVfCOq3371PfUohfHUfX7h2FZFSTAURmtySmkmVhRdwF61qKlO12zZvQp8AeLtQ5MDcojUHVSAKiJxzXXXd9CkBCYEDxkBMZO7oDYRusD5t3B4wbg3kvA0qR0ACFLWS8/lsRH7YD+/fvxn3O2JG+tzA4ODNEkFgRkwhDF3aj2PfJUKvJeeSwQ0QzFVVCKxLXCIVAqmlirm6idVca60mBg5ITghN5RSshRyqUlTFwTly8EjgocnWdhtrGcDVBIoL1luPQEu9sZSSc81Fs6hoE54otZav6nAbO3Pr13AH2xoMWuGH8P9F2X9uSXYkWZqoECWHmDmJAMmsql490/3+DzTrzkx3dSUBEOHMzA5RVSH3hx4PeCCravVYxgKQATjg5qZHRVVk7287UMzTw+d/ycOch9Pz019++/Jvz89/JcJ5vvv8+U93d6d5SsNwyimVUpblum9bR2ceVgU30dXKrubm/m7LJUKep/tPjz/P06fz+f7u7v50dz/P9ynPhKym1hCQHN59nQDu3pf+x2feAeq+SS3DPAzDME+n0zQMKdzdPX76/OPnn/706c//fP706XT3cDrfpXEMHBEIkR08xJyGMcRMGAIzhZiHYZxPecgIqK2FEPro92ieMkFMCBDyyHms9TDzdFrJkbMO/+HLP/zxH8u8iF1vbdtt21prImJMOOSYcIB0co0m5yZUhapyabhsvhWrzbSHG7da6yZtVy3kmtgSFcIFXUD2wIkxEaITGTBH/OHT+ac//TDkbA6//PryxS4OLSYYxjGk86JDkDOqIFlEcrMmm1pVkVZKpS1QDjwgZwxZgwJ93IZdrNV939bb5XpZbrdSVpHmYCmFcToNwynFIcUcKGzEO1CDvbV+pnfX92CNDpS3I4TwtuxNJeWQc0iJOZArmaKIt+qtmBkSxXG+U9OU8/nuHsByTswhpkTETKGUvbXWe+vSWmuuig6RQ055Ivp9v3IHM+spU9M0TfPMHGqtRBsReU9J7PsS9MOfMLGqqYqE0POjQ2DEEGNgDuDOzK21rvXrUjl340Bm7O9EhOONH3T6Xjal/yZz4H68hS4O9o7T7/VVjme81fqdYsBMxYwQ+/wY/HC9dq5u716aWen+oK9Pv/3627bdUowphzRAk3Vd1uvtdltvRJhTyCnkHL30y4kKkicjdHAF1HFKTOwCbdftVvdVpBh2oRGQG7qBO3IIwzCs6woAx3I3QwTmcDqfODD+QxxUN1pCJwEpAHo/rRACM0TG7ms//oiH0A7Be5MeEAkQLWDgHOchSMS9yU5xCQHDOOTzCeZHjBNCIIoUUo+s6Vfpbyf3nm5GABwwKCKDBbfoHtSADsQnvLMLvqlH3l/UJ6HdvxGICd0EwGPEbrhmAmbPic5zOs/zNIzE3FTrLvuteL3q/lrpst+o3UXAPJ3u7z491BbXLfLCsjQ6sIwAbg7khq0JgHGT3tmqTcq+r+uNCIZhENUEYMfRz81MWl0uL19/+cuQcL39n6fzHU8Tfz8l+YeGvGpgnqeRGcYhbdu2Lmsr9R2FBu8DJETsAlEGQBFFAGYKIfY7gTqoOCCDk6ltXbd55I87ATITM7v1sBM1FcJOsDmKtHWjfABEBoBAIYYIDmZKCBxYHZqoGhCH72/tgO6AHhhT5CGnaRimcTxNU4jBTZtkkSramgCC5RzBRxdx1W1Z99paOX5p6wYeIqTAFJixV/XWVrttGwFgHkYDoMBpzOM4zHf34/k8nE55GGPKxIHwADjV0mqrKiYieynbVpZ1X7f3gb6Kmw4f4pT6PKIHtB86hg+eUQB2QEMApoA8YgSKyIM6wdP/en39y7o+3a5fHj99+vnnnxglpSxt3Ze1brvtzau6mAEaqKiIei+owJ2AezrNj+fz4zTOiLbvV4dS2nXbX1OaA+cQhpAGDpl4BAwAREwIgEDwfflEgLvzDHP+008/ff70eHeeUwhgev/46fGHPz388PP9px9OD4/T6TSMA4eA2GWtCAAxpWk+zafTPM1DHhwxhhhjGqeZiFstTIyIZt4TlAm5D1MiEhCF1LrzHt/txv8bHfn/sPSL+rZZE3NAZqYhMY6BzpgBdTZzVSwSSAMrsxIVS9UPrKmKSm1tl1ZUC5pEFNIbthfWG/lOiIk4JI88xHEazsOPf3r86edHIqylvr0hM2AY0/jIw0+YHlla9DokI+lVxEQ2lU1lJVBidkfVdrjS3wPF+qu1updtXa635bosy75vfePuxmz4du85VKXETMZs1mfYHWvuvSHvroAeUwjADlaqNNVSa8+i7uIZVVdxbf1LA5LHFK63YbxM0zzd3d3lPCFSCDHlkTk6QK21lL3Woqp5mEPMIQ4xDfThBI+EIabz+fzjjz/+6U9//vHHn4ZpbCK27+BwJCGDA8CH9AoMgUMLMXIMMaeIGGNghOMOH53dtGF/ypsDmFJgVo7w3m/rQ3Nz/SasVdEeEMnUiBkPMf6RIAoO5i7S3QaH5e7jujra+4jflHSEwIjM3TZ0THi1yb6sZduhhxqPDFhqXdZtvS3rupZWNUY+5shmKtqqCoqJjTF7iACG6EhG3J8vdk+AuKMcnXRXNTVXAA8chnG4v78nQhFxh1JKrYWY/JA4fG9sR4CjERIwKJoTRg6hc+OZMTDEgDGQmQd1MWQ/HHQMdMjdgdARDSIiNCut1LKat8AxDIGnAU4zxAmcEBk59MOA4+8bDgKQvx+PyA8nHntgtwDvmqxvDRX8Bvf9/eNACAw5YI40pBADmREiDOMQY3CwXn2mMd2fh9Oc5yEhkQi9VbksL/vtSfaL8iKVpWUz5IDjPA4w5jlBGJvR6k7VGIkDZQ4p9LhQV3MHJQI1a62WfSPwfT71UgjfDJKEgdnN9vW2Ldd9vYHLOKScvqvm35d2dwCPMQxDnOehtvnl+VlqZcJ+WHC3rkbpDas+8gQgd2woIYR5nlLOIQY9qNGoAtu2qamqdtdbv3Qel3LH2qqqqgAhjOMYOIjKcWZGCBg4ECHllKdxGnJOOYm01mrMWR0NECjAx2f+6KhQCmHIqbvJxyHP0zgMAzMCeKll29dtW7eyMWHkgAboYE3LWqS1su1lL9Y0hMApEsZAHJjB3VxarZusvUE3zieMnKeBA8/T9PjpYTqf0zjFPMSUD7iQY2+76Srbuq3bvm37uu6Xy3K9rdu+11q7AO3PP/8QQ3gvMofnrluh3I2586Co/8zNe/ePAJDiab6bYrqP6cQh3JZfn77+7cuXf71ePyPW+7shhRQoSmmyFduqVTFRRVKEJqKigXMIMWCIYTifPj08/Hw63XPgdb2UfREtHOh0Ok3zeRzmcTyN4znnh5AeKc4UhogBGBH8H4Ocf/z8acjxv/23//bzzz+P4+Smy+12Ot8//Pjn+08/nu4/TafzMA0hhPevO/4UU5rm+XQ6n8/ncRiaGhMxcx7GmFLdQ7+O+ZGLfnytAyBRSpk5xNgZqPy+L/9nLff//GUGpb1bnmIihMZnYWc/E5gYi0WVqBrcCBSxQZBDfOomrk21qTbThtbYq9dXWX7z/QnKK/seSENCZjo9nu5/uv/04/n+cWy1XN9qCMAcOd2n8U+Y/2zpM7nn4H7C3I0m6GbVZNV6tXKDtjAogIFV0AL8nSWg1GKb3dbrui1NBJA5IMPBmBYpJrXVrbVdtboJghNRjMHsHfBgrt3rYUYEKUbkcMRKFCnF9oLf8onfZdVd1KVmXOtlXfLba57n+f7+sW/IpgaOIaQQY87jOM5NmorGFGMaYxpjHD8254hoGMfHT5/+y3/5r//8X/7Ljz//PE5TU62iat/wlP5+G8HeMGShwBwk5KSIHpg8KYKhK3aZF/Xv3Pvzeqj/YjqKGfRGhZprb6HBe9O7ty17wvK7eu5bLQE/AhuPWNePy5CZY2B3MHd+H/MeDho7tHn9P9xqA/NxHJGGPPK6ye1tud6u27a2Ju4dp8pu3lRabbU0N9UgdRiGHBGden8HlTBwCgMnisFxNQEnUa9qzUwdnEMYR0oxnc+nri683W7X63XIwxGHQv9wTu6JOBYQjAEJQwiBOBATszNTCBgjqVFQZyPu2mAEAu5zNUciR1RDF9Ftub3U2zO0JcZ5zBTGBOPgcXCH7if3nlvR/S/9GOdOfjjcmBzRCNHRA7nzt+byceSCftr/forIBAlwSDQNYRrikNjMmWk+zTnnHtgRI+ccT2MahzBmIsQmuEBZL1/Wy1cti+XdJKqSagSEkELM4wiTQSotXJuHJiHwkHka0hCjg4nqVqyp98Un0sq+ubZ1uVMR6O0nRAdkCjnlwNw789u2oOtpzEOOHz+N70o7ETIjIzA4ElEIU8ol5yXwwR4HIKYQAgAgYoyZOSASYm2tDcPw8PAQc3Tw1hoRqjqA43Ek7aoJYuYYQkopRSK0bYO9FDdD92nI0zwDQGt1WVZzjSGGyESUhzxMw8P9/cPjIyLWVqdxHKYzhwEwwsdbCeCYY2C8O093p/k8jUOKjB2RIwRMRAE5UhROgopEyJyS5qwp7RxWDoEDhUBqRuDoxoiM5GYqUsreajU1RIopxcjdnRJT7KC3GI8UZ6JjixCx1mRfbutyvV5ut9uybfu6bbfbuq5769Hw4PS9FAIBCN1AEe3YRwABsNO/apMmPd+1/5PEFNwkxvF8/umHH/8Pc3l5/qtoe3v5gjqeJmq1SNmtFGgNRVEdCACBnAAhUsxxGPLdON2P4wNSfrtca/l6vbyu682sxRgeHu4eHu7v7u9Ubm1/C/HKccnTj/PdDyF877b49i4Q+uAw55yGcZhO7qAYpofH86cfz4+fp9NdGjJz6GbR774U3/fa90ZRv5ynnMdpDjG5eUy5R0f/4Sv77v+tDPy739v/p5c51OMe6ORAyOqTEvf9ogFXp9WpGKqhGhiikVs0YANX6O1pU3BFE/Ji+wwwKI6AmeESeZ2zziOcHufT4ykPrF5Fq7hSyGmMmO4x3UGcMY2JwpQpOquTEzn2YZyQF5AV2w10BashcBymgfTbvcYBSqvGrmYc0imfEYODuTbVXWSt9VbLUval7Gutm1h1UGKnQD38RqqIdBmKHouv27INXMHek0yQMRyGDuglMHCYhnEYp2GapymlBABN2r7tbAbLslyvV0SI/cpPnGLgYUgpD8M4TXMeRv5Q2gOHu/vHx8cfPv/4893dY4jZHGufZ/nvEFnsjmo6RM5HmedvMI0eqyZdONtlQDkF89jxiP3QaL0gvxvGvRN3iUKMgKbmgN0Op6oGvRltYOb9X0LHkRxNVcz0e/QHAnT1QrfZER1GTTiYdr3xwOMwfnr8JOLDbRLdHeuyWmdymPp7Yg+BgzRpUve11L0yARL1Ob+B9YhCQhxSzmlkGqRZDFy26mYOzb25S/+uiAjDYXJ2h9ZaKaVruuH3fsLvb4MCABEZG0WkDu1FIkcyZkihA+kgMASGeITXIiJwX0QI3g0saLbv+/52eftNL19nljiE6JVBgAECObxD4rqf+53vh+Do1jFqSAIo4M3dHAKSEhsesQK/h7oiAJJ+7NUNKeSQpyGcpnh3yuOQ3J2I52nOQwb0o/fDGBgCYwxoaqBF9mW/vbXtRqCBgBm+wRT6bRMIQrAYJEUdow9TOJ/Hu3kaU6qqa2nqRXc9PNK1bsttNY0x367XcT5TVxU4InGIkUIw91L22/Wtli0w/GcNeSIMTODqquQYzMYY6jhcUuTAQIiEHBjefaI5DyHEfs4spQzj8Pj4yJH2umEPbq2K0m8Dh3yUOcQYu6X1NOXAQOhuWmtjwnkaPz0+xBhLLV/gSyklxxQiE1NKMeV4uj///Oc/DcNgBtBjlvKEFOGDyhERpiGlGB7uzg9359M8DSmiu9ZaVIU5MJs5KKASGXe3P1MKMXPXH6VmouSgiC5K4IyAACJaStm3TaQxUc55nIf5PI2nMY85pAhdRNP1IW4mrbffS6nbtl+vt8vb5XK5Xq+3bet0+dJaA0A+bKPfja8QDL2BN9faXTwAZAqie63rsty2bev7DQAgcuAYODKFmOYff/rvHJgZ9/Vlvb2RrWhD21cpxVtDEVJD9Y4cYicGjhRTGObpbp4/x3gWoafn315evl7fLmXfCGkaR3SITEMOkbztK+AGXM7KeX54f8Lw937Xt3WFAO61aWmaHYkjD5Dmu/nhcbp7yNMcAh92iu9fh2rJj3HON3dljHmYTiE2N4t55GPi/uEL309I9M4Z/Yd/9//nl4E3hyNPFh2BAEaHyRxVqQiU5lvTXfRdi0TQPfToSAbuQA5uBIYuaAUtoEb24EgMHAPMJ/t0H8dTikN0b+taWtNmyOluPCWlTxBPGDOmMOQUIKmzOh3ZFsSBKUWKKOSr1Wvb38AthBRth9u3peWtNQgISGnI8/wppdFcpK379na77qWsy/Ja9rXVolLNxF27ncQdTVGbqjRT8QO9gn347O5H7QAk4ECcYgyxt/zczIaUHh/vzqfzMJ5SnkMKkU1kM7Pa9Ha7XS4Xdw+Bcx6GYZymU05xmqZxOk3jPOSRPsQOhRAfH394ePzh4eFTHmY18KaAzkSBmZlj7433cPp34jZ3e07gEJjDcUnseXToTIghkEPsIYkApGKIqOai2n+9My76OSAhuioCqqNo8ybN3E1dREVMpDl4CCHEwDEAeBUT9Y+PuZmraG9SMqEDH3N86dbC1r/lYRx//PEn4jy8vV6Xl3V7cVNVcQPGcFAze8xlk1JK2UrbCg+JM7ma1ArUx2XIyGMaT/PdMJxUPTBer9d9rUjiXdz+AbXzbh6EjgV4P6n9EZCCCP2Wx07O7F1yhY6kSEcJDEyMFshjcLW+WzgisDuC2bsg1lyLbvt2uV2++OXrPHPQgWQjr4iG3DVGeDgR/NsNHBB7zrS6GaCgN4cKRuCMKNxV2o4H8MCPyGHA9nG7moaYp+k0hfOcHu7HecrggMg5jznnECkwUyAAM6lmCu5VROtet1tZrlr3gB4DxcAh9Ej7DhJSxIZgRJ6CTgOeT+nhYX48TWNOSxFcy1ptrw5gJtLqvi7XdVkQ+fLyfL57GE9nggg9hCwmZnaAveyX15dtvYHJHwjGf6DRASKAGZqBObxry4chD0MehkFVzd3EWmshxLu7uxizmiHSXso0TvcP98gAN+sZve61VkGEEMidESDFkFIacp7G8f7uNGSOgQPzui4xpru78+fPn8Zx7Hicy/VCREwh5U6R4x5QOExTCBmJASiExCH1efz7u8CUUpfOnef57nSaxilyMLNWmwMgYjflllr3vYqqmNZW91LVDN/9rwxgRFLKkVSqok26sz8w5ZTm8/zw+HC6vx+mAQn3suP14ohNpNTCIQCANKm1bfu+Ltv1drteb7frsizrvu+llNZ6S/OdltXTH99ftSzXt78ty9uyvImoOTBFIlTfar1dLk/Lcu2bjTuA0zciSowpRIhheHz4c4lD3S5opa2b7Dv0lBJ1rdpEq0nzSkiB2DwpSy2Vw9aczYOoIaVpfpznz0OeT6fzp08Pj4/3Dw+nnJOLmUfFicPowB1E6OjvGQwf1xWq2rqu1+uN4hgzqLkCqqMj9azr7zJ+PqgNDqPx+1+amzkgcYiJOLgZc2RiIPpDBX/voP47r//w9wG+xSwdIsPvnhbszSF6z3YEQO+bukNXY8MxNQRk+OaEPbZBV1ABq6Y7aDVraBo5QBrRp+QDEQOCKay3qkvFiJRCSA/DeDo/DMZj8QehexoHyoECIDa3og4K7kBIiTmFkAMnBHQNbUruThCgXuH2+7tQJwI2dKLIKVEMUspe99t6fbu+vV3etvXW6m7a3ARBj1NXbxCDISqxhYBIoc8+OARkQoxExx22P6c5Rw7UcW8qrSk0cVFXM3NxL01u2/rMYXTnQD6PuS9mE923zVRFKnOY51OPXvroU4op/fzzPz3cPzJnEd+31tPSnRkjMCIQfuN/Um/WEXEk7iW/Uy4PkIyZaa80aihq6oDYwZEOSGreREuppbYmUmrbS9tK20szx6Yu6s2gmTd3U1D1Jt6atirqRqxUhQO7exMpe/m4qi6X29PTCxEx047grqW027K+vL61Jpe3achDSlEV9tI6drNfhUOkcRjIXcRUmqocAzxzF7dm2syDg7hWa2yG7uBECJEkKAw0xJEyq1Q3RccY+aCE/E7QOZ6HrqxK6VCu4R/v7IAAiQWR2BVcDQXd0AGMEChQiBAiciR2VlV1PHKGEIBB0U37yRAAQGKAwH0+ugmxjhtKC67ETvEA2tuB8nqPPEMAADVRLWCOKgQSQAHdQRDVyay7qQ8yqx/wVN8/7lfzlB8eT6c53p3SeU7jkLB3RZGIPTLEhCEQIgm7Cql6s9q2va5r23drjdgDYwgYAjMHd6rNgJWsmRsT5gTKYcwxxxCYwF1VOuDV3U2l1n1bbtfXl7e3F3B/+vrr/ePnPE7vpn0KkUNKIcVay9PTb69PX5brWy37x0/kj03U7uXHTqBEiMw553EcpmmcSwEENxCRspdhGD7/8ENKuZTi5uu2jdN4Pp+BrOmG5DEEU9i2yr2H74CAqRNNc5qn8eH+7nTKOXcSAxHx3d358dPj3fm87/vtdu3HVmYehyENKYTe3wEOYZwmDgkhQFesfN9xDcwhhBTTMIzjNA15IKLW2rbuIqKmrbZaW6m11NYDXqU3rZrAQTwPiORIRVUPKZSLiqt2Te08Dw/358+fH6fTGVNSgHVZqmgV2fZ9WZbudmtNa63rui3LervdbrdlW/dt31utrYmpOXh33AIgMX0cBtd6e3v96+vz31+e/95ac0MOiUNw3Jtc3y6/3a4v0peEdsIlEQ0pn+f54eHhx2ka70+fW8grsuzPbX3RsqMYGZEzqMgue9MqHpkxRvGIVJBvAhSqIg+ANM/38W4YhtP5/Pnu7vF8d386n6ZpCkwmqgbNOOYTQDi2yYPk+sfSbubbvt+WNYzbgITEYlalB9/j0Zh7/8f9WIjHGcE/siOOuQ4Rxx7o1bfu94r87V/SKcj/yWT9363uv3/5v/93jXuy1XGfOVqCPbXdET0QYCDrLT/8rq3q2kxX05uXi0sF94ghh8hIhIGVg6Gb77uWUte9pmma7h/Ojz9O85+N7iHNi+TiEZNz1Bwl0rt7CdCRATOSEjlyRCKHQT24oxnr6uVDPwgpAmUABg5OpK5bWa+3jt95urxdSllMKrgQGJMHcnIwwH5TIrIQgZiPJGwiCgRIIVDnxbr3es8xRWQUUVNr4qoS14q0qaOaZVdAdPOcTjHNKQ5DOql4rVpqrXsp27Zua87Dw8Nj/8w/6rZSyn/6+Z9yHghZmpk0ZvcAGJzAjcDZwQ6lC3rHZxzMp57t+z41J3UCU3A3R1Vo6iKujg6hL6LWG2V72WsttW17W9ZyW/brsjtQ6zI60yYqaqau4q1Zq1qqikpfHEjobk1U6v5xfT2/vOXhSx8bmtSy77dluVyXy3VpokPKOedhyCEkB6611bqLVPcaAkzjwEBSZXMQqR1v3/VHYAgK3sAaWAMhryZqSoiWcOCmgwVMKaUxjzVXadbTZv0DAOzjIZi5Z3BQCF2X/odnwyPsBEyuPTMeTBG0i9YDpeA5eupyS0VzhIMMCRDQ0O04Iru6t8iQwsH5rFtr2+5SyVqARtgIFZAcD8G0IwJ0yrpVb9J276XdWkBxJ3NBEIfWP/H3boSjmqh43T4+79OQHh+mc6/rmXPsFw9sVcwaupMDIxITQiAgcHeFupW6blJ2l0ZsTHSU9hDNqRQzlBDJzBApRhgCh0CI0BfPtpdtLyKHl7Lu+3q7Xd9eXp+/gPvTl19++OlPj59/IDisu/3hiimtt/Xp65enr7++vXzdt+XjJ/L9rd0drZsHDnkDBg4Yh3E8nc4OmPNQSi37joDzPH/69CnnfLvdWmv7vs3zmIcIaOMwpBgdkChJg7JJ3Vuh6qY58Zh5GuI85rvT9PBwnsZhGAdgVNVhnsZ5Ot3fDdP0421xwOvtCohpGIYxp5yGYew95xA6W4YAyL8rDODuy7KZ6DKv47SN49QJtrWUdV22bd/LXkqtpZTaamsd0/ANLgx2RNgd/DwAFW2y9vVLhDmGYch3p9P9+fRwdx7mkzGvRdZ9ayJmLiK1FEQyMxE9QkSW9Xq93W63fS+tNrV31LO/T628R5h8j+B/fxExEoUQOAaiMQZ0s0C5tdr1iaauYqruIKa3fSPGOU5jRJkINpNtXa0VNkxpwDgirKVYM1XRgBjR2QWk1MpCGIEiUOAcUophyDmngx2A5qyemIaYiYHIiDg6kKorHvE2fUbwbVmlPBBizmPKOec8DkNIcczJTUWqqgSmb0n1377yGMV16n0MKSe2kIch5tQVHu8F9OP94X9fJfeP/yT+51+u6nvVrrKyY2jXQ3bBj9irg9fjgH70+hxBQJvWVeTW6rXtV9lvBJ7TMA/hnDGaQakoDcQJwMD3qrebBNEG5mQKIBiBZwwjYkQ2ZAWs7hWRmeVDyauIgkCEaAhqpkCKEfB3txUCzKf7fD6ptP4mtrJel7fXy/Pr2/P1+rpuWy37e2nXyOCd9+NH/kdHl6i5am+gUoiBiPa9IkBrqnrcjlTc1VvTvbRtL6Zeq1yvy5DTOOZxysM4T+PraX48nT+fT5+HaYIUY3LcdgMsddfaSmmlttIkiH7kMMcQHh8fA4cYj5lgbxh3P5Sp1moiQnQcDDmEwH3bYA4YIiGAEjXxDh8177dtq81UQQ07wrZPhK63221Z1m3fS1vW/eX1+vXpLQ/PjtTk6NVXbaJi4qourTfk5b2H37lbJqZgLX94I1+fXszDw90pJ17eXq+Xt2Xd161sVVQ9MOecT/M8DGOMuUq93l73+tbsUuveD5gq1mqre0mZh5RCzGRoSaEBYwBll2DEKlqbA6hXHGNtJ+l0zda0vVvzu13pW20/5oTgABA44NCfSKLuzPvueVIsL9D7TiooDVzAFQiQCDS7DmpJWjiGoRgZAhBR588iKh4kE7DdrLgWaaXUWgFq22W/tuXJ3gbY3zqYMsREFLr20ZEAwFR9XXS5mSqphPpmbaUQAqPuNzNrtWorrtpFkq22fd+slqzyrevbbfeEvfXhABBDZAoI0ETcpYkiGTubk6iX0tZtX9fbvt+0rQCVg8aIMWFMMYRszmV3AUtgTdysidQi1aiIx5UFAW57WfYqgh01WEsp+1r2tZZtWy8vT7+9vnz90/4v4zwjcYfBICOHICLXy+vz1y9ffv377Xr9+IF8f2t3ADuAPYgIhB2mPIzj2SzmPI7b9XpbiIj4dDo9Pj7knImw1dZqnU9TTtFRhyEjUYyZKUvDumvZGrpL85xoGuI8xdOU707T491Z/JSncZe67XuehjgOwzwD4A+lOAIGUtVhHMZpGqdpms9du9db2F0hid8Pd939ellqKjkPMeYYc87NzUop67rebrdlWfa9lFJaa02amhk4MXEMKYYYuAdIOKK8D3pLK2aWYgg5ppTnaTifprvz6e4052lSJNFFW0+Rs9ZaSRUAtJtdRLetrMt6vVyu11ut3bx+CLbfZ8jH9O5jaScKMY5pOI3TIzgyhz60I0IHm+cfa91qjxdDcLO+IrZ9V1XT0hqoeDJJ0Jo023ZXiSGMeRhSdqN1aXUXBUmEiRHJHVRVVCqGGlg5QGQO5OC11mufGYmZKI0zjcPEnBgZkMxA1ZQItYcXfHjuEVIemGgYp3Gccs7DMAxDyim4Nqm7tBZDoC62PJTcDh9Ke48SyTkbwDAMOQ+9I4LQ5WPw+9cAwH92U/99hXz89t7/5O/PwHE0/gNyS83Xqkzwjal8iCPcHRyZ+nGjdwv7byMoooDsrV2rXrxetNyw7hx4CvlusMe5Rt8gLVaLiTWx0qCabcXIVLEZblVvlGfPZx8IUwZEB1etYJVQuj6IUaFHwWJBdEYwdzB1Z7eI/t1x/nx6mB8epdUqpbatlH1Zr7fr6/X6crtda9lbrdp2MCFQC4iZERiBIjJH5hgoBOmOSQciyjlyCKbWqoibmyKig4qButWmZW/dy3azlQhj5DyEcYzzPM3z+eFhU4cYhml+SCmFHI1Y3Jtak1rF9tpSa9TkOwwz0TTGzq3uP3UiJ1RCACdRa7+PtB0AkblDV1IKeQjZ+chvoI7fcVVrorVprSriavAu8DAAu16v12XZ9r026aX9y9cXCqMjimhTaT1zopd26eUDeqGUb/BOM3VjtD9PvwOP3y4XMQbQKfHX3355eXpatrIXbQrmSEhDznWv01xyGkrbX9++Frko3lSaqrm6qkuVVmoOYwqJHBKwJ/PiAOjKoIwavKkVEVWIUIcmVcEQHFvTWkXVzbpJ75sS8tuACrsGIoSjAuI/gGbBxG6/IfQph7oJuIALESIzhGwySEsY2BzNECggxs6iOPoplAjIpGndvNykLK1uTaowq9a2X/Dyi1mxkN0x5mGcTjGNHDNxRAqmrk3kdmvLVaWRN5JbsI1yRnQ3lPVat7WVzZqYtFbqvu3L7Wqt/fynCO+JVtjzmNSkqlCwgEyUUnBwBy2liii4cIiOoYqve1nXZV2v+35T3QBriJYy5MwpxZiGarFUMHJgb2IiUGvZ6rZp5hoIGRyK1KbiENy8l/a677XsJqXuy8vzry9Pv23r7XR3zykimIN1sJua7Pv2/Pzll7/9pXwHOfwH89t7oxH63tbVmXkcMIRZfZ/3rm2vtZ1Op/P5HGM0NRUFhGHIw5jVJOcUYhrHOYYx0KDN12WTVsE0pzTP4/k0n8/zaR7naTJCDHR3Ob+3wbttIp7v7sw8pFBbYw7DOEyn0/l8nqZTjLnDUg6d5PeKjm7BlFafn17NoFbp2Yu11nVd13Xdt60v5K4v76YUx0DH2fpgVLk5sIYQNAQAd7AYOMWYQoiBc4zTkM+nKU9zM1+23VTKXkttNbWcGwB1MY6Klr2UfW+1SmtSW7/cd/yk27cfN3AIHytTjPP5/GcO52n6E1Iv7RwCv1MLWpMjYhmg/5a0tm/7WuourSJIDur7m0pzaWzgPS4HmZGGEM/DIEVMnDA6jZROcTjhMGIamSK5t7LWff3m8AFkpJTy6XR+PN893j08TtN9Sqc8nNNwJhwkpN7y+a68OrQ+QHVTU2m1lY1RaoA1UB7yfLrTGAkCHsls7wWzO43ePxLmEJhiTKHrbt81ru/mqv+t1z+o7b6dIj6mInbAYqf1/f5v7mnFRMb2zZHUjWDmDkAGnfUDGrwEr4QVobnVVveybnXbVZFpnIYUgybaSd5kXRjegr0wrsbliECRqq4I0bzty3MtO4Q3Gp7j5/8a4z8BTwDRjAwZUBuquAYQdCaUQMbUBcfNrbiIWwVZPz4hd/Pp4f5BVErdlo1avTK4aWul1H2rpUorpq3f2tGR0HoyEyABsKp3/nJrAgDd/hoVyt72rZTSTC0E5kAUkBCdsMHhT1F1AGi1tUq1cCmtlKaKBtEhGw3nOxrnB0o5Dh4NvEbg2Bz2JlDKx9LepL68PvVpXP/oeoJLzDmlwYnf4STHRwrERBqjZTExcwvghM5mRgiiqmIi2kT6LVZEW2siDdAJYVlv2761JtrD1K+3r8/PjsEAmoj0r7ae5Owd12nHdAq0L6X31ZIYfArfllvMaZyH03k6T4mhpUBfnl/17dZ28S79IWQiqXVdbtfl8vL6teoNucYQchxjHBiitaptH/NwGmc0rY4brCYKDuZIhgFDZofQ6+qhKUwx5zymmJlC1SZNW62tNTu2Jzgexvcf4e9PyLc/vL9MZfv6b12ITcd7FXdhIo6BZAAejUMjVgMxdyRHhp7/6gjIeToRR932erssL0+3L3/V/RZQU46Bzepledpuv/5rUVDHOEzz6X6c74bpHNNIHKVa2eu2rWVf3BtBM2iOOuSB41LgpRg2qdqqSZO9rMuyXpfldnVT++m/d+AOHIIzU/Mmlo37rJCIYwRRaUspZasthJiQcxPY676V27q9tXYFKjHoMOE4x3HK0zQO4+R1wBLdgylJq6W0Uva97KYVtPWLaY8nIAQzq7Xs+1rKJq324LGX5y9fv/z99eXr+f5ujnfErigO2uWpKvXy+vLXv/xbiONHp9gfSruZKZp5BzR0/QxhHsc0IiHXUgk55ywi0zTN0xRCcDMADzHEGKdpaq2KtpTzfLobB8hpXm7b19+eNr4ZUc7pNE/n8+nufJqmcRgzEDnB+TQDQjyGPcZM0zwRUx5zbdUcUs7z6TRN8zCMzOFdGon+j9Ry91rFTd2gNdnWfRiGmKI0Wbe1lCqtdZ8uESIFV3ewd58CQJdMIDoRcDgiJQITeWCKkXOMOcUh53EY5mlK41DEApOplH0TgxBrG1onZ6m6NNlLLdveSu/YidTud/vWqENEZP7jSTjGaZp/TsMPeqfIyIFiYA54eGOPV1fTmpvDEQS27mXZt1srF5PXtt+aqKlFCuDEGBDAzRhpiGmIubApR6Mx5sd0+pSGiWMyVW2ttL21YqYiUmpptbamRGGazuf7h8fPP9zdfT6dPp/ufjrf/wn8kehMmPgfjGZNzBnU3dSk1koIWtAF0cdprPtDihEB6HC3Htm+DtAr+zGqZOIu+n/ne7gDHHFE320573/570zT/dv1/Lu+//tVHfHD/7Pvmw9gDkWg8+LoCAHrwN4jNsQR0CX4nu0CfktcAKSJ7kX3VaUieQoxJ7ZIa4RXr1/q/hvShUJhMkIwq6Xuog5Endgosu3r1eCN89v9NObHT8ijUxRkseCgioqojkIQIxkGPICAUlEWhJV8I5cPbxhP4/hwOqvpXiNAXW6BEeBAwlepRaS6NXQFVHVsaIHZE7iTO2rT2lqprVQBB2YGB4lhW/dt3VsVdweLhDEEQkYwawTk78F5Bu6uoq1Jayqi6qwQFbJiNh7DdI8xxykMwBQKxdzMt1Zlw4+hrq2WX3/9N0T8BnlipBh4nM7DfAZOhrHfT3r0J/QF1DS3IMIm0RRUgNkBSKSJHIA5Uam11rLvZS916yrgfd+X5VZrdfNS6/V6i+m5iZlDk84uOB7C4z32Y2Hv4XhPOScAcHQIBDB/W6w5x2nO05zP5zEHI/al7Jfl5i5m3ifbTGDSbpdjbtJ0o6DzNA/hPKSBMrqJaTlN83maXYTF3gC0NXAgQARPHOh9aQA6IzFxSnkYpnGYU7xtsLcmpdRaa/9g+pn6+N/HYzH2ie133S/Ttnz9CwdOOYVAhGiuZo2ZkyQKjakpBQUS0aZ64EcQzL1WdSC4f0xptHWVy+v+9KW8/EayxUjTkHJElHW/bc/PL8texTGNp/n8eLr/dLr7lIeZQ6677Ote6i5SECWwMjkzUSsctiJWzA3N3ay1tm7b8+vt9W29LQgA9n9+qB5dVKkK6Ja6VNYROTILtrYty2uMMeYhJFAj0draVspF5EpUYtQYA8cjUIeI3UkEHYHIa+3tq7XVTbxnqHapEBBiZDeTsi/rci37JlLBTFp5e336+uXvX7/8/f7xPg8BE6rsKsVN3NVUr5fL3/7yb+eHH6e7z9/eyPegWbN2dGOcoetImTsYkJiAuxpumkYV7aGMRDROEzMP48hMOeemKSQKMQ3DpIqIMefcB6JImHM+nc/39/d393d5yESAjDnw3TwRQI5dUCsd8jyMY8yplz8KIaUcU+IQELljCrqc833w+d1LVfd9V7V9LynFGKOZ1VrNDBGHIccYQ2Rg5BZqq0R4JDl+u4qZmikgHJnNkbulIcVwOs3nu/N8mlNOzAzauVRaa9lLQyp7KUyRkES1VimllL3u+15Kba11REwXTbybS0i5sxZ+fyMOaNAT3+gdgQ7ebbJH5pCCNxEppbRW+/egJq1pLbXsm5SblKLMYRwGEpDaZ2mr1HVva1FRwH6TDyGNp+H8Q4yZkFSESUO+AySi4IDSStnXZbmUfVWRy8vzti5f0y8pjef7Hx5/+JcffvyXH378l4e7xzif/ji05oCBOcaQQujkxlIbmMXQ1mW/XgggppFCQMb38EzujloCImIMwQBdTUS7U6iTcvvT+gfx20GcxPcp64dXXy7dvtQPr0fw8kG9OYiJ7k70RyStOYhhT61ENMQjSOO90+VgDeXV9SXRE/INHA1j07H5YCEhcQANsJOtaFeyC9rNdVHamhVld8a9bvu2mudhvDvf/3C+/3PZZVs3VaCQpyGdhkhTxBi73wyxz4kV0QiNCWIgPrrhrZVZZTPZC79c8W/fOhCdFABd16Virfl7Axr0iPr2riZGd3A37HUfAV3N3FRMqsre+tMEqiHwvnVKuZq6NTe1QDkSM1LoMSl9lmDuDup+KPMQkVbEK9Ir0jyefrg3GMYchxDzUEtB9qabrhULt1a+fRy1lV9/+0vnWpIDgjNRDGGc74b1zjAqBCAmYo6JQyKOzJGYAuPKcIs0jmPOmTk7YqdkgYO7mra9LOvtum63vfTSHlqty7Iut1vvl15vV0Day+6OdqwnA7R32aSb9kFt733Sofbtt4ccwOdvJ0/RfS/X14vWGsqyvDw9/+3Xv/zy5akWIwg2zAyW2JkpRTpNo/n9XqN6TXGKcRqGIYfAYJH8PA9388lqYZFI6CrmFggILAVIlHIKgc3AUgyBKYYw5OHu/n6v+7pubr5v+3JbltuN8EhvwX+o7viec/oxiM9Mb9eXEILK0IPt1UylBQ6WDBJRZAR18442IqbQrc9gslc3aF7DMCaDMzRngCHSNKGE8zANMToZOYAZgecYUuBIGNAZlL2xYyT1oOimDDGGIcUBIQMQEIClCAxEMTlAKwVFFgQCC/TByNILR5O2VyD3RH0vEFEW5YDgou0i5ZkhY7zP6QEwg+oanXTxdkMt6m1Z7PWFfx1JgbcKt214ucRhnE6nXLbbfttUEtkMfO6OIgSjjjEmEymXy9PLy6/repXWwNENyra+Pv32b//j/4psLss4pdaW5e2r7DeXBubLbf3119+c8n9Y2s1du9K6R5J0KHdgCpGYqZtCiScZVa1PrxCAck4xTu9BpWqSciTmELMKunGI8ZsaMsY0TdPpfDqdTykFQCDwyHQaR0ZEDgGPGTWFkGLsEFk/RH097pffr2ToPWfpu77psYObahVttW3b2nVYCGBqxBRjBMwhchoyBeLAxNglch3M0w9t3VOC4CHwMKQ8pBQ4pjDkfD6fzvd34zxTCO+kBQyBEbzWolaoFKZAxE201lpKLXttrYeufbtz23tpJ0QkY/g+c9fcxbpg1c0d1BWtD7/coWeB7/u2rsttuZZSjsa0ayvbcnsp27PUZ5CVGfM4nLJjpbqXrejeZC9tr9LMv4X8cEghT8wJgQCVA3DIHMYQB+bgrrVuy+1lub4sl9d9X9bbfrUbgF4ur/u+geuQ0pSij8MfHJZATCGmPHQHI7q2fScTyakty3p5dbU4tu4DpdAHD13vBOjeP3R1UJF6HI3aUeFVoRs13yeCh5yeiainTxJ+aB92ioiKqhxcxZ7544dpyNRURMy9eza+u7c7qh1b27sZ7/d+PqqArFCfQH+h+ER5dx8V75qfG82QZwYIvrDcoFxAXtFuqKtrEaruFd3daK+t1IYw5mGez5/nh3/itRretFXimGPMkTkzDYzQE6+R4N1OTh3nyYTohqaibVIpoHXpB9aPDwi8X09ETBUcGClyjBwbVoR+1DFAwz78UJPaQN2Y+n0NxLx1/Lm7KAeszUzU5KC7uNqQOAYiBz6AkSR+MJMB3AxUrKIgFcAF+UJhflgWUWUOeZhiTjHHUm61rVbV3UR+L+0i7fn5N0LslGkCCMwphlr3bd/EuRkBBeaUhiGmMaSBOHYkTPAWCYY8xJQ5DkDc6fhMhKCmdd8u18vzut62/SjtIrpvdV1uIgKlruvNzLZ9JejUOXNXoiNUAfyILuhNpV7aAfmwuerg8NO3erKXBW5ayhOB7cvy8vzyr3/929PzBSHmMLgou0WCcRyGIXEkDmHd81a3FHPknOIwDjGQ5wBDDkPK5m49J9sF3BE9sMdIHFidHZKqEqKZSmtunmIe8hg4qsK+l+W2XK83AII+7CD8WNWPP/bS3j5k7Jott0uI0bTFlJhCf5RCiCaAGlAZHFS0Twzj4Y1kRIdWXNUWcZ2GYRozhTmHNtE+Ww1DzDGwuXX8fAwYuxU7xcQY0AiEHAM6BANwVRsSjzkMiAmwx1cxM8TIKQPgRiR7CYxM2DNIP3b4VEyKUlAj6g3R1gSoRUeRve5vbX+KNDMMQwrMg9c9oni7Wb2hFpW2rf76sjN7a3Bd2m1Nz5d0Pp8eH04uSyurQkCaIH4GEAcG7wvYzbTU5fX1t+enX9b1KiLdjSOtXV+f/vqv/z+CBrbOp9G1vH39pa5Xk+rm27bXKtP508dN94/mt35S8/cPsLcZ+6QUj+0jIBLzcd0EBGLu5bT/fMgQGXp4F5h/mNF4r+49jXgYMqG7mQMw4pRzZLbuoQFAMCLgQMSBjsQnPJTw71N2/6aew+8GQd4D4lrt6k4EVEZVfodNBgAEEAcBjMQcMTAfTiWVJrVIqdoqqpN3t3gvNMQpDPN4f39/d38/nc885OoGhsA8neeffv5R3avIsuxl33pnuZvf9lJKKZ0e920ZHaRGO1SAfzSDA6h6bQf+k53giJRWaXst63J7XW+Xdbtu27Ltu5rFmIjAtZb9ulyfWr0i1sSSgg8BZgghDpb4uhYDK+GQgzUxRIMm+3qDy8swnGIaEQMiq5FU2csVCWPMMQyPn/7L4+O/1FJK2UvdW9ta22IKp/kuhxHd0ZW+gzoDAIhpwjTN03yaUa3tZd82bZxi4pAcudY2lkIxEROF2GmFMaYYqJVdahPVHqyxrstyu423mxM3FWnNRECtdzvtYHdhTGkY55xzjIkQD2c5Ys/WbKpNhZwjAbrTAV3x3oxtrfV4MGL+g+DuXTCMHwVGAICuCAvBS6CnhJchQYqz8L3Ag4ZP4FNEZF1xf4Lym5dn0Bt6MW1dscihgqNpEEvgkeiB+UFsXkpcql6FwCCQhdpw2chHatwP34GICZGcyEPoqcMACEzASJGiJwSPWr+Tzlbzqu794AjMnKbpfHf/WaV1Qdrm1kzcFc2ZITITgDVtokYUQgiBMhGG0BzU1JtqcwRMiEQkBk1V3FuR0F1SCt14Vo8mGyBBD1xzA2lWS9u3sq7rti51X2Seswci52C2l1Iuqs1dVH+X+ru7SjNAZwz9eMrICG6tldtebW+GFDmkVseYRg65e0cJAXQHEw6BY45pRI5ujoiBA4K47Pt+Xa8vpaxNWw9fFLFapJbiqsai2mrdzISxU7jFOmcQjdnx/XF2R3dU62+aAJCIUOaPz/p1eXpdZV9u+7KUrSy37eXttheJPGmGAJyIU4wpp3kYgnkTa6rmwMimBmZMhCmCj266rsXr1moxVQKMMfW5Z0xBrYN0tEkTVXz6+pd/+9fL7aqg1/VWa3On2tq6bZfLxax3K2OMhyzgsJ92pxmAg39/a/eylVqq1BZjDCH2o2OMQg5oLrWaamu1taoq4DnwkNMQY4xoqsjkFGw6pZzGNAQHvV5fW9urqWvfJJk5RbRE/c6HqNK2xaQGjt02tpettH27+RL4FPM55RRiiCnGQDlRjGJmKlKrmyJ41zF8VxGZUyDQ2vbahtyGCsiiXlC35fXy/HW7PWU2hsccHFC1XPfr8/L2db+9SNmYzS1KxeVqbtvt1p7f8MszzvP86XGasg5RnVlDBvqBwmcgQswdbVz39fr2/OW3v3/9+su2LW7ae2uBUFt9efriLrfr8zgNgX29PK/Xa9ubVEM6tq/v3sh3xaSPOd1+Lza9I+cKBkhIQEjIQP6NiPo7Sg8B3DrS1zrNkFUN8BtQ4Pfq27Wq6AKmCEBIY84pJnHrmfbvSEgiJkJypA8CAUREh3cBM77Pgz489E2aNIH3MBV0NNceSAPo2Ds7ru4KwEzQ9wRwU21SSt03qZUBA1GIMQSiABQxDmE8jXef7u/u7lPOTlRUkJw4jNP46fPjXsvb5VJK2YuoGjjW1kqtXZCvqm7+DTHdxQz9ifl2EP6uIirs1YiA+bhQmuzS1nV5WW5Pl9cv1+tzrVtrRxpgzpnQTdZSlm27uEtKoffxBvIRLAaASOa215oiMLm5taYI5KX57SoYVNo4WUwzcTADkbaXm5nkNM2n+4eHn6fpnjibg0irba3lhmhDCHen05inGAJ/0zV9WFfEGGIMIahVVWsiZrbtO4XFgczMRDgmZKIQKcYQY4gpp6CtlrL1HaHWupd9XddlWYyotlprtSo9HcwRD40IUR7HIzo42IeEKvcea3LAPjuHzAwOn3qfZ5iaqiK9R/X94YXva86xKwYRBFGI1sCXBGtGiWnCeFJ4FLg3PJNzxJX1ovU3336DdnUrCopWVQVJzBWcDcgwM2fCO/d5r2Fd7LrbbVPyFrXCcrN0ZclUGJm5v0MmIgsBopk6mVkHuzA6ISKFnqPy/brS1gMdgIhjzvPp/Gim/YkXqSbVtVoTBAxEiY/7lrk7KjkQxgREHIJhM6ytqRozQp97I4q5qbddGBCItMOJ8IAVoPfQTOpYMTcwfff8lG3flrrfciJkcG8qSy1Xkd2sqv6eTuvuqooA4N1GQf0yAK6tbmWr+96QY4jZtZpUpEgcY8rMqG3TVhQcKaZh5pABsWPs0EXbWsu1rBfR5m69qIlAa6ba3BUOIgwY2EH5kqbS3ARRPSBxtz6CO5mDdME8IDggUsTvtuC369N1e335+nx5fqtFW+sg8TjlECi3KLW1Ukpt0ltSAMQYIgOBdwELuBISc6y1rduGbdN9N1ECSiHmNMSYiLA06VHvtZXusCeOb5cLMBVpt2XlkPZSlnW7XK4iTnRgSIgAjrkhAJJjD8xCkQ9hwe6tNnOvpQYO3yzyOSdwU2lMrD3LrxNTDN0ZPTG6M1i3SRo7OyViCRhJwYo2FjR0Iu4p4V2O27uqzb3VQiEws5uJSk/nMFVG1OnE8xnHKVAgB+q9XJFW9lZ3N2VCZ+zp4b9XRMYYqIm0WkQmFQForaq07fb2/Pr8pW1fzxOjbgzVDOr2ut2etttz22/ojYljH7Erlc3KXp6+tl9+1ZyW5TX/8MiPD0yMHgOGZ0pvEBNyRgQ329bl9eXr09dfnp++1HK47bv+07Qtt7dS1uenLznHIQe0ZmXXJiYHi/UPm9X3oFlmChFM3FXAzBUNoSsmAMDebyl+XNG/VWDAnpaJ2MWOzn0Jogr2eNue3Ats6irac4WZAzpzJ0aZkxuZGUIfsx6/jgX1MXHk/Tb+bnn6Rye4mYsZ9BBZBALq3QUKgWMMKVEIfcBqKv0irCLaai1724upgFlPFQPo/HYNAc9348Pj6XQeYiazqgrmThwSD8SeEg1DmKa0l2zmPctNTeg9ygmQDB0cXN3RANCtC7v65kTM3wWriPq2GxMQqtmi7fX29vfL698ub3+/Xb/sZTGzYTjnNKeYiEDlremubQeg0/kxplMezpl8hHXU19he0FX6FBX7s9TNDebWxJ2AyLXWdSvXef5hmD7FeCKm3ocHsyFP23p5+PTPd4//PM6PYxoAwKxGximF85TvTsMwDDFl/BC+CYjTNMYU130LVwoIjsApE4I6NNWordWdEKgFJAIOwKErAMacwHRdbrVsIu1wM5jWsgNzk9pqs6bWRJs4QEiRY+AQ0J3ACboe+PdF76rgRgSJY+/H9+V8aIu4E1LBLHTPwfeDBX+/vvhxInNAMMbGuEcuIVrwmX0UPlc4F5ibZ3JEX6h+wf0X3J+sXLGjTbyiV0JlR3YmyERzSieb5lbCspm0tS2XpdalbKCXiEV1MB2yhHiKHgfkxOLM3lMmQ/NAfbV8q51IDES+r98141WrWg1EKYbTdCKwwBSZGdGkbctV9hXari5okJgjhf4D7AQIMS0CkUOiEGOIFNm5WM96djcABQIyh1bU1BCxlzdXQ3NyBEQmjIGR0ByJA1NgYkYyaft6XW+RUDiQQWvlqu1ayq3WnWSg9y3L3VtTACdEYwIP6gjEqlZqrbWItIBOwASCvVPTmLxhDK5FtWx7McAsNQ1TSgmcTFylSF2kbiK7u+Jxd+mAEWpNusyC0SJD6nlGZooGqIAGaB3VZq5ugHhY7BydnDo5g75nyD8/f/369st6WfelqJEbqnV1U4gpxhwpkpgu62JPiIitVnePjIQY0F21lh3ARdq+123dUas3EQNA7kglESvV1q3eln0te5MGYGIQL5d127daS21VZZxOHMZpXq6X67Zse9nRvWvisHcZe+eWwnw6zadT+T6dFp1MWpUGYHics30YsmrJ45BSBHcn7xkiMTFQl5fv+7aWWhAopbxXzWkoW7m8vL48P7d15WlCTUhUtn1btq0UJIgpD+MYU6TAXS7T6by11tKqqTHg5LHSELy6IpTqgZywit6u17pvBJ4iBwCm724ihMbYxJu5UE83BWi1vL08P335+8uX36x9fTjFuj7K/iwWt9uXfXsx3QN7GtIwxfmU5zlNY+BAavB62xDXUsr1UsYUp5yY0WsI8UuInxBPxGdEF2u36+vL05e3l5dtuZnKOxfYEQwB3aBVa63VQuUIgQb0bnN/L8z/UWlHYg4B5PByuhu6oWvvgDvYcVkxALf36ybA0aFBh56Q16ndBNArdrdakAO70wFMVuv/OULoRwNHQ3+/EBEe+zLYtz7/71X99z3qMEHju03v+O1vv7oylQDMkbyff8LxOoDIjKjvOTu17FKqSkUARqTftS993/dxjKd5GMcUApXaTNXckNwPYrIGhpRoHKKrlYoVRRq+p/8yAnbkVSe6+LttD+mw6dD35ixV24ugN9etlt/W9W8vX/7fl6//en37ddveKPI4nab5fhznmLJbXberWSGilO/muz/n8ZHDKege629sOwK5uxoYIBADkoGru5q6q4ECI7JWXfe2qJoZzneJeUAiVd2Xt/X2WqU2NQgD5ynPd8MwM/GYw2mI88DTwExg7vgR6Q8wjgOiX68XafU0DJFDzBkRDUg6fansrsKBkYMTG7IBApJM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MDAwNzEzOSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAwMDczMyAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3MTMgMDAwMDAgbiAKMDAwMDAwNzE3MSAwMDAwMCBuIAowMDAwMDA1NzAyIDAwMDAwIG4gCjAwMDAwMDU1MDIgMDAwMDAgbiAKMDAwMDAwNTEwNiAwMDAwMCBuIAowMDAwMDA2NzU1IDAwMDAwIG4gCjAwMDAwMDA3NTMgMDAwMDAgbiAKMDAwMDAwMDkxNiAwMDAwMCBuIAowMDAwMDAxMjI0IDAwMDAwIG4gCjAwMDAwMDEzNzIgMDAwMDAgbiAKMDAwMDAwMTQ5NSAwMDAwMCBuIAowMDAwMDAxODAwIDAwMDAwIG4gCjAwMDAwMDIxODAgMDAwMDAgbiAKMDAwMDAwMjUwMiAwMDAwMCBuIAowMDAwMDAyNjIxIDAwMDAwIG4gCjAwMDAwMDI5NTIgMDAwMDAgbiAKMDAwMDAwMzE4OCAwMDAwMCBuIAowMDAwMDAzNDc5IDAwMDAwIG4gCjAwMDAwMDM2MzQgMDAwMDAgbiAKMDAwMDAwMzk0NiAwMDAwMCBuIAowMDAwMDA0MzUzIDAwMDAwIG4gCjAwMDAwMDQ0NDMgMDAwMDAgbiAKMDAwMDAwNDYwNCAwMDAwMCBuIAowMDAwMDA0ODE4IDAwMDAwIG4gCjAwMDAwNzI1MDggMDAwMDAgbiAKMDAwMDA3MjU5MCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOCA+PgpzdGFydHhyZWYKNzI3NDcKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:59:34.151133\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Prediction: 7\n"]}, {"data": {"application/pdf": "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MCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAy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MyAwMDAwMCBuIAowMDAwMDE0MjE1IDAwMDAwIG4gCjAwMDAwMTQ3MTggMDAwMDAgbiAKMDAwMDAxNTI1NCAwMDAwMCBuIAowMDAwMDE1ODUzIDAwMDAwIG4gCjAwMDAwMTYzMzQgMDAwMDAgbiAKMDAwMDAxNzA4OSAwMDAwMCBuIAowMDAwMDE3ODQwIDAwMDAwIG4gCjAwMDAwMTg0MTkgMDAwMDAgbiAKMDAwMDAxOTE4NyAwMDAwMCBuIAowMDAwMDE5ODk2IDAwMDAwIG4gCjAwMDAwMjA2NTMgMDAwMDAgbiAKMDAwMDAyMTM5OSAwMDAwMCBuIAowMDAwMDIyMTMwIDAwMDAwIG4gCjAwMDAwMjI3ODggMDAwMDAgbiAKMDAwMDAyMzQzMSAwMDAwMCBuIAowMDAwMDI0MDYyIDAwMDAwIG4gCjAwMDAwMTI1MzUgMDAwMDAgbiAKMDAwMDAxMjMzNSAwMDAwMCBuIAowMDAwMDExOTE3IDAwMDAwIG4gCjAwMDAwMTM1ODggMDAwMDAgbiAKMDAwMDAwNjg4NCAwMDAwMCBuIAowMDAwMDA3MDM1IDAwMDAwIG4gCjAwMDAwMDcxNjggMDAwMDAgbiAKMDAwMDAwNzU0OCAwMDAwMCBuIAowMDAwMDA3Njg4IDAwMDAwIG4gCjAwMDAwMDc5OTIgMDAwMDAgbiAKMDAwMDAwODMxNCAwMDAwMCBuIAowMDAwMDA4NzgyIDAwMDAwIG4gCjAwMDAwMDkxMDQgMDAwMDAgbiAKMDAwMDAwOTI3MCAwMDAwMCBuIAowMDAwMDA5NjY1IDAwMDAwIG4gCjAwMDAwMDk4MjAgMDAwMDAgbiAKMDAwMDAxMDA1MyAwMDAwMCBuIAowMDAwMDEwMTk1IDAwMDAwIG4gCjAwMDAwMTA1ODggMDAwMDAgbiAKMDAwMDAxMDY3OCAwMDAwMCBuIAowMDAwMDExMDkxIDAwMDAwIG4gCjAwMDAwMTE0MTUgMDAwMDAgbiAKMDAwMDAxMTYyOSAwMDAwMCBuIAowMDAwMDE0Njk4IDAwMDAwIG4gCjAwMDAwMTUyMzQgMDAwMDAgbiAKMDAwMDAxNTgzMyAwMDAwMCBuIAowMDAwMDE2MzE0IDAwMDAwIG4gCjAwMDAwMTcwNjkgMDAwMDAgbiAKMDAwMDAxNzgyMCAwMDAwMCBuIAowMDAwMDE4Mzk5IDAwMDAwIG4gCjAwMDAwMTkxNjcgMDAwMDAgbiAKMDAwMDAxOTg3NiAwMDAwMCBuIAowMDAwMDIwNjMzIDAwMDAwIG4gCjAwMDAwMjEzNzkgMDAwMDAgbiAKMDAwMDAyMjExMCAwMDAwMCBuIAowMDAwMDIyNzY4IDAwMDAwIG4gCjAwMDAwMjM0MTEgMDAwMDAgbiAKMDAwMDAyNDA0MiAwMDAwMCBuIAowMDAwMDI0NzA0IDAwMDAwIG4gCjAwMDAwMjQ3ODQgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA2OCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNjkgPj4Kc3RhcnR4cmVmCjI0OTQxCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-12-04T16:59:35.443130\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Probabilities:\n", "Image 0: 0.07%\n", "Image 1: 0.11%\n", "Image 2: 0.07%\n", "Image 3: 0.11%\n", "Image 4: 0.17%\n", "Image 5: 23.27%\n", "Image 6: 0.16%\n", "Image 7: 48.91%\n", "Image 8: 0.10%\n", "Image 9: 27.03%\n"]}], "source": ["visualize_prediction(mistakes[-1])\n", "print(\"Probabilities:\")\n", "for i, p in enumerate(preds[mistakes[-1]].cpu().numpy()):\n", " print(\"Image %i: %4.2f%%\" % (i, 100.0 * p))"]}, {"cell_type": "markdown", "id": "fafec094", "metadata": {"papermill": {"duration": 0.236848, "end_time": "2021-12-04T15:59:37.319392", "exception": false, "start_time": "2021-12-04T15:59:37.082544", "status": "completed"}, "tags": []}, "source": ["In this example, the model confuses a palm tree with a building, giving a probability of ~90% to image 2, and 8% to the actual anomaly.\n", "However, the difficulty here is that the picture of the building has been taken at a similar angle as the palms.\n", "Meanwhile, image 2 shows a rather unusual palm with a different color palette, which is why the model fails here.\n", "Nevertheless, in general, the model performs quite well."]}, {"cell_type": "markdown", "id": "3ca49cba", "metadata": {"papermill": {"duration": 0.234811, "end_time": "2021-12-04T15:59:37.793046", "exception": false, "start_time": "2021-12-04T15:59:37.558235", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this tutorial, we took a closer look at the Multi-Head Attention layer which uses a scaled dot product between\n", "queries and keys to find correlations and similarities between input elements.\n", "The Transformer architecture is based on the Multi-Head Attention layer and applies multiple of them in a ResNet-like block.\n", "The Transformer is a very important, recent architecture that can be applied to many tasks and datasets.\n", "Although it is best known for its success in NLP, there is so much more to it.\n", "We have seen its application on sequence-to-sequence tasks and set anomaly detection.\n", "Its property of being permutation-equivariant if we do not provide any positional encodings, allows it to generalize to many settings.\n", "Hence, it is important to know the architecture, but also its possible issues such as the gradient problem during\n", "the first iterations solved by learning rate warm-up.\n", "If you are interested in continuing with the study of the Transformer architecture,\n", "please have a look at the blog posts listed at the beginning of the tutorial notebook."]}, {"cell_type": "markdown", "id": "88cdba30", "metadata": {"papermill": {"duration": 0.240818, "end_time": "2021-12-04T15:59:38.270965", "exception": false, "start_time": "2021-12-04T15:59:38.030147", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 5: Transformers and Multi-Head Attention\n", " :card_description: In this tutorial, we will discuss one of the most impactful architectures of the last 2 years: the Transformer model. Since the paper Attention Is All You Need by Vaswani et...\n", " :tags: Text,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/05-transformers-and-MH-attention.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab,colab_type,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 100.878464, "end_time": "2021-12-04T15:59:39.519049", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/05-transformers-and-MH-attention/Transformers_MHAttention.ipynb", "output_path": ".notebooks/course_UvA-DL/05-transformers-and-MH-attention.ipynb", "parameters": {}, "start_time": "2021-12-04T15:57:58.640585", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"0b303c4195e64ef39fbeec6143263f98": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "0b80c7deefb24e7ba07594e61433755d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0e657111ed994cd18ee73800d5175c7d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "117e6a6b8bf847059db276d064c2b13e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "13f1b84e24984ba0b79e6125f96f99f3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "169f7657723445bea5d0a4765d680159": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "178462769c7f44169c8ced0b55763811": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "236a5851e62d43d7b60a7d52dce28c1a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_e3e6a9b6686f420f80e224eca063a19f", "IPY_MODEL_24d5d210a2b24f36a66613fa0ccb4233", "IPY_MODEL_e1bd4f10532642b8a15721a759eeb58b"], "layout": "IPY_MODEL_b49848bb32524b95a97c93ad92127987"}}, "24471f587ee94549b81ff8a94a6e0a78": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_512f768512c74743b343a6e8251d6ff1", "placeholder": "\u200b", "style": "IPY_MODEL_b7ff55f2178541fc99b01101431fd146", "value": " 79/79 [00:00<00:00, 147.56it/s]"}}, "248e84e3800948aaa5d4b99a97521525": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "24d5d210a2b24f36a66613fa0ccb4233": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_87fe3739fd654f94bc44066ccbb7f227", "max": 87306240.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_dd2bb7a927774638a5d1894442cb22b2", "value": 87306240.0}}, "29f9153bb21a44b487d35377cb438123": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2b58bae07432436298a33aaaf21cd6e4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2bd4ea3a82024c858491e89acc4aa047": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "2ca1e45629ec4679a85bd781162d6b1c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2f853c9d6857487ea62b0226abe31f79": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_178462769c7f44169c8ced0b55763811", "placeholder": "\u200b", "style": "IPY_MODEL_13f1b84e24984ba0b79e6125f96f99f3", "value": " 79/79 [00:05<00:00, 15.23it/s]"}}, "2f8d30ea351341eb8601ad7c4e38834c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "301dc13411cd4b16957a001be48e9feb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "334865d5a9b24d738acc57ecf7793f00": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "336b58f1c17b4c2998f77978c46f92b2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b68f68938fb444f8bda9725eb32bac90", "IPY_MODEL_7aa0e6f3f2884d28b3ab91fbe177cdd1", "IPY_MODEL_5f2d0ce055ee405b923e0a374e5932c6"], "layout": "IPY_MODEL_e3bf53dac0194713bd220e0fe48fee33"}}, "344aba03213349f591500fe672e8afdf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "35f58d761f794fdea716c3eeb2c6ec80": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_77e443eccd6342b989fbcb88945429d3", "placeholder": "\u200b", "style": "IPY_MODEL_a58d18832c084ab792d67eeb729c7635", "value": "100%"}}, "37ccf9ab76824da89ed0187799f2ea08": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "3a3f8db09b6d4cc5b53b747e2b7b9da6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "3aa4aeee7b5f4eb5aee77dce65dcade9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_35f58d761f794fdea716c3eeb2c6ec80", "IPY_MODEL_4fd7a34138db4283a966e6fb6a31b988", "IPY_MODEL_2f853c9d6857487ea62b0226abe31f79"], "layout": "IPY_MODEL_0e657111ed994cd18ee73800d5175c7d"}}, "3c5caa9157bc4baf9a570f8782e29475": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "4180b35ca339453686f92f651153f871": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ce6b8e1c9e7346ff92a0fd9dd9816ef0", "placeholder": "\u200b", "style": "IPY_MODEL_2b58bae07432436298a33aaaf21cd6e4", "value": "Testing: 100%"}}, "45a9d84f4f2c4e7bb899ba0057f9745d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f48bac3019244c07803f84c2e6d95ea0", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_522def02fad0470a9d6abc40677772dc", "value": 1.0}}, "47a76bee08d74e2799d591493ecb37f2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4fd7a34138db4283a966e6fb6a31b988": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b15347bd314048409dbc07933d388917", "max": 79.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_248e84e3800948aaa5d4b99a97521525", "value": 79.0}}, "4fe895cd281b4d2f91f744ffc6f1a0bd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5007ffc533144720b1d8aabfe6bc2ec6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "508f4a8250ff41eab6f008a7094da94a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "50e6daea1e294714a838e9a83f7f66ed": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "512f768512c74743b343a6e8251d6ff1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "522def02fad0470a9d6abc40677772dc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "563fcf83e0ce41379f34ddca6f1339a0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "575514ed749f42a997a2baa322b6efa5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d4079c1c7a0e419fb8aca46a9ecefa25", "placeholder": "\u200b", "style": "IPY_MODEL_7da963515d2c41e889a004ca662079e4", "value": " 79/79 [00:00<00:00, 288.13it/s]"}}, "5c105436e67d40e0b6da633b6a00ead8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5f2d0ce055ee405b923e0a374e5932c6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d2f30746e64b4ebba654f67c9f23b32f", "placeholder": "\u200b", "style": "IPY_MODEL_bcf6aea6625647039795bd8c30585346", "value": " 391/391 [00:27<00:00, 14.99it/s]"}}, "60febcf280f34ad8849e28a112b83802": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "65135f33d2c940b19ad2b625abb25376": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "6a189067bc0f406594c18d8e8f5640eb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "71e812687f0749799bf97615573d22f9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_50e6daea1e294714a838e9a83f7f66ed", "placeholder": "\u200b", "style": "IPY_MODEL_5007ffc533144720b1d8aabfe6bc2ec6", "value": ""}}, "73ef64d0e797442ab771d4e2728baaab": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7428277b5ed04997b19990cc745ba65f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "767da7c44af140cdb3413c158988e913": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "77e443eccd6342b989fbcb88945429d3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7aa0e6f3f2884d28b3ab91fbe177cdd1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_db005c58c17c46bc904b0e844c4c1dc8", "max": 391.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_e5dedb39aecd428ea3dd6d2e15dbb49d", "value": 391.0}}, "7c565ce6f14141dca6e213b4c0a463a0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7da963515d2c41e889a004ca662079e4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7e7c9b0794174e2aaa68e3682517db1d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0b80c7deefb24e7ba07594e61433755d", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_8c3f67d4af6d4c9cae34014915f7cc58", "value": 1.0}}, "7f73079d25064959932573707f3e2c52": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8474a73379874c7a9597a35a15c7e585": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c8a127a3c7d8451f9a464af27ccec9b4", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_896c1b7cd27a4e46b437fdb3666edf55", "value": 1.0}}, "87fe3739fd654f94bc44066ccbb7f227": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "896c1b7cd27a4e46b437fdb3666edf55": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "89c0145b83d34648bacf9da6ad15d74d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_dd915efd78e44054a1826e4be665b035", "IPY_MODEL_8474a73379874c7a9597a35a15c7e585", "IPY_MODEL_575514ed749f42a997a2baa322b6efa5"], "layout": "IPY_MODEL_60febcf280f34ad8849e28a112b83802"}}, "8c3f67d4af6d4c9cae34014915f7cc58": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "983722707bb64460bfbdc38056f7d32d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_d83de3be21e04615a11e3659d3a83abc", "IPY_MODEL_7e7c9b0794174e2aaa68e3682517db1d", "IPY_MODEL_24471f587ee94549b81ff8a94a6e0a78"], "layout": "IPY_MODEL_334865d5a9b24d738acc57ecf7793f00"}}, "99532397f9914f338666889ca9cd1285": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "99f1cedd123941169dc16b83030bced1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9c0f4106b2fa430d807e9c34f98e5bd2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_f47589b9dfad41088d797c8ce2a9761a", "IPY_MODEL_d51b5e09340f4c24b51ac50bd1ef8c8f", "IPY_MODEL_cfc6b274acec498c870a3d5ef0252cf4"], "layout": "IPY_MODEL_65135f33d2c940b19ad2b625abb25376"}}, "9fe2ccea8542438f8b6f3adaf6a5e302": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a58d18832c084ab792d67eeb729c7635": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "aad913a268f3461a947ea946df80ce7d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ab0dc6637c5348a9839eded7ec182961": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_ce0141f331ad484189ed4500c2287b0d", "IPY_MODEL_aba0095cda6a462d94fe0e0979e1bd9a", "IPY_MODEL_c7741f8ad62e493a97a27ea1f32acc15"], "layout": "IPY_MODEL_0b303c4195e64ef39fbeec6143263f98"}}, "ab80d9dadcc34224ad525011c3a46c9f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_117e6a6b8bf847059db276d064c2b13e", "max": 169001437.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3c5caa9157bc4baf9a570f8782e29475", "value": 169001437.0}}, "aba0095cda6a462d94fe0e0979e1bd9a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_47a76bee08d74e2799d591493ecb37f2", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_2bd4ea3a82024c858491e89acc4aa047", "value": 1.0}}, "b15347bd314048409dbc07933d388917": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b49848bb32524b95a97c93ad92127987": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b68f68938fb444f8bda9725eb32bac90": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_344aba03213349f591500fe672e8afdf", "placeholder": "\u200b", "style": "IPY_MODEL_508f4a8250ff41eab6f008a7094da94a", "value": "100%"}}, "b7ff55f2178541fc99b01101431fd146": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b9ed1cfedbeb4586bab29909930e47b1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_4180b35ca339453686f92f651153f871", "IPY_MODEL_45a9d84f4f2c4e7bb899ba0057f9745d", "IPY_MODEL_d51f7aa60a654aae991f9b2ccd974311"], "layout": "IPY_MODEL_301dc13411cd4b16957a001be48e9feb"}}, "bacb5883fee34339abc34dd04428fd19": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bcf6aea6625647039795bd8c30585346": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c7741f8ad62e493a97a27ea1f32acc15": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7428277b5ed04997b19990cc745ba65f", "placeholder": "\u200b", "style": "IPY_MODEL_2ca1e45629ec4679a85bd781162d6b1c", "value": " 157/157 [00:01<00:00, 163.00it/s]"}}, "c8a127a3c7d8451f9a464af27ccec9b4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c9833783e4f24255a6dbfb6716d13e22": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ce0141f331ad484189ed4500c2287b0d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_767da7c44af140cdb3413c158988e913", "placeholder": "\u200b", "style": "IPY_MODEL_dc3260543b5f40d58b6b604961c71781", "value": "Testing: 100%"}}, "ce6b8e1c9e7346ff92a0fd9dd9816ef0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cfc6b274acec498c870a3d5ef0252cf4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c9833783e4f24255a6dbfb6716d13e22", "placeholder": "\u200b", "style": "IPY_MODEL_3a3f8db09b6d4cc5b53b747e2b7b9da6", "value": " 703/703 [00:03<00:00, 197.32it/s]"}}, "d2f30746e64b4ebba654f67c9f23b32f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d4079c1c7a0e419fb8aca46a9ecefa25": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d4443194a5e84e52a7f9e44f7e4d7e6b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d51b5e09340f4c24b51ac50bd1ef8c8f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7c565ce6f14141dca6e213b4c0a463a0", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_37ccf9ab76824da89ed0187799f2ea08", "value": 1.0}}, "d51f7aa60a654aae991f9b2ccd974311": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2f8d30ea351341eb8601ad7c4e38834c", "placeholder": "\u200b", "style": "IPY_MODEL_d4443194a5e84e52a7f9e44f7e4d7e6b", "value": " 8/8 [00:00<00:00, 132.04it/s]"}}, "d83de3be21e04615a11e3659d3a83abc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_aad913a268f3461a947ea946df80ce7d", "placeholder": "\u200b", "style": "IPY_MODEL_9fe2ccea8542438f8b6f3adaf6a5e302", "value": "Testing: 100%"}}, "db005c58c17c46bc904b0e844c4c1dc8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "dc3260543b5f40d58b6b604961c71781": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "dd2bb7a927774638a5d1894442cb22b2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "dd915efd78e44054a1826e4be665b035": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_bacb5883fee34339abc34dd04428fd19", "placeholder": "\u200b", "style": "IPY_MODEL_29f9153bb21a44b487d35377cb438123", "value": "Testing: 100%"}}, "e1bd4f10532642b8a15721a759eeb58b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_73ef64d0e797442ab771d4e2728baaab", "placeholder": "\u200b", "style": "IPY_MODEL_7f73079d25064959932573707f3e2c52", "value": " 83.3M/83.3M [00:00<00:00, 116MB/s]"}}, "e3bf53dac0194713bd220e0fe48fee33": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e3e6a9b6686f420f80e224eca063a19f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_99f1cedd123941169dc16b83030bced1", "placeholder": "\u200b", "style": "IPY_MODEL_169f7657723445bea5d0a4765d680159", "value": "100%"}}, "e5dedb39aecd428ea3dd6d2e15dbb49d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "efc763b5cd4e4ed9b3e3881bad434e2b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_71e812687f0749799bf97615573d22f9", "IPY_MODEL_ab80d9dadcc34224ad525011c3a46c9f", "IPY_MODEL_f72acb891d864ee79f5d536286cc4667"], "layout": "IPY_MODEL_99532397f9914f338666889ca9cd1285"}}, "f47589b9dfad41088d797c8ce2a9761a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4fe895cd281b4d2f91f744ffc6f1a0bd", "placeholder": "\u200b", "style": "IPY_MODEL_6a189067bc0f406594c18d8e8f5640eb", "value": "Testing: 100%"}}, "f48bac3019244c07803f84c2e6d95ea0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f72acb891d864ee79f5d536286cc4667": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5c105436e67d40e0b6da633b6a00ead8", "placeholder": "\u200b", "style": "IPY_MODEL_563fcf83e0ce41379f34ddca6f1339a0", "value": " 169001984/? [00:01<00:00, 103277285.22it/s]"}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/06-graph-neural-networks.ipynb b/source/notebooks/course_UvA-DL/06-graph-neural-networks.ipynb deleted file mode 100644 index beccef6..0000000 --- a/source/notebooks/course_UvA-DL/06-graph-neural-networks.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "c005cd13", "metadata": {"papermill": {"duration": 0.099647, "end_time": "2021-12-04T16:14:55.293606", "exception": false, "start_time": "2021-12-04T16:14:55.193959", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 6: Basics of Graph Neural Networks\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:52:52.769733\n", "\n", "In this tutorial, we will discuss the application of neural networks on graphs.\n", "Graph Neural Networks (GNNs) have recently gained increasing popularity in both applications and research,\n", "including domains such as social networks, knowledge graphs, recommender systems, and bioinformatics.\n", "While the theory and math behind GNNs might first seem complicated,\n", "the implementation of those models is quite simple and helps in understanding the methodology.\n", "Therefore, we will discuss the implementation of basic network layers of a GNN,\n", "namely graph convolutions, and attention layers.\n", "Finally, we will apply a GNN on semi-supervised node classification and molecule categorization.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/06-graph-neural-networks.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "8f81ec41", "metadata": {"papermill": {"duration": 0.098016, "end_time": "2021-12-04T16:14:55.489441", "exception": false, "start_time": "2021-12-04T16:14:55.391425", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "9f21578c", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T16:14:55.691145Z", "iopub.status.busy": "2021-12-04T16:14:55.690666Z", "iopub.status.idle": "2021-12-04T16:14:58.205336Z", "shell.execute_reply": "2021-12-04T16:14:58.204782Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.618634, "end_time": "2021-12-04T16:14:58.205486", "exception": false, "start_time": "2021-12-04T16:14:55.586852", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"torch>=1.6, <1.9\" \"torch-sparse\" \"torch-cluster\" \"pytorch-lightning>=1.3\" \"torch-scatter\" \"torch-spline-conv\" \"torchmetrics>=0.3\" \"torch-geometric==2.0.2\""]}, {"cell_type": "markdown", "id": "5f4b1636", "metadata": {"papermill": {"duration": 0.097058, "end_time": "2021-12-04T16:14:58.401563", "exception": false, "start_time": "2021-12-04T16:14:58.304505", "status": "completed"}, "tags": []}, "source": ["
\n", "We start by importing our standard libraries below."]}, {"cell_type": "code", "execution_count": 2, "id": "1fa61214", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:14:58.603576Z", "iopub.status.busy": "2021-12-04T16:14:58.603090Z", "iopub.status.idle": "2021-12-04T16:15:00.563541Z", "shell.execute_reply": "2021-12-04T16:15:00.563928Z"}, "papermill": {"duration": 2.064677, "end_time": "2021-12-04T16:15:00.564089", "exception": false, "start_time": "2021-12-04T16:14:58.499412", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}], "source": ["# Standard libraries\n", "import os\n", "\n", "# For downloading pre-trained models\n", "import urllib.request\n", "from urllib.error import HTTPError\n", "\n", "# PyTorch Lightning\n", "import pytorch_lightning as pl\n", "\n", "# PyTorch\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "\n", "# PyTorch geometric\n", "import torch_geometric\n", "import torch_geometric.data as geom_data\n", "import torch_geometric.nn as geom_nn\n", "\n", "# PL callbacks\n", "from pytorch_lightning.callbacks import ModelCheckpoint\n", "\n", "AVAIL_GPUS = min(1, torch.cuda.device_count())\n", "BATCH_SIZE = 256 if AVAIL_GPUS else 64\n", "# Path to the folder where the datasets are/should be downloaded\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data/\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/GNNs/\")\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False"]}, {"cell_type": "markdown", "id": "1a6c7487", "metadata": {"papermill": {"duration": 0.096843, "end_time": "2021-12-04T16:15:00.760797", "exception": false, "start_time": "2021-12-04T16:15:00.663954", "status": "completed"}, "tags": []}, "source": ["We also have a few pre-trained models we download below."]}, {"cell_type": "code", "execution_count": 3, "id": "e4661ab9", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:00.961509Z", "iopub.status.busy": "2021-12-04T16:15:00.961033Z", "iopub.status.idle": "2021-12-04T16:15:01.312867Z", "shell.execute_reply": "2021-12-04T16:15:01.312426Z"}, "papermill": {"duration": 0.455182, "end_time": "2021-12-04T16:15:01.313005", "exception": false, "start_time": "2021-12-04T16:15:00.857823", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial7/NodeLevelMLP.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial7/NodeLevelGNN.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial7/GraphLevelGraphConv.ckpt...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial7/\"\n", "# Files to download\n", "pretrained_files = [\"NodeLevelMLP.ckpt\", \"NodeLevelGNN.ckpt\", \"GraphLevelGraphConv.ckpt\"]\n", "\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if \"/\" in file_name:\n", " os.makedirs(file_path.rsplit(\"/\", 1)[0], exist_ok=True)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(\"Downloading %s...\" % file_url)\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder,\"\n", " \" or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "2f0eab58", "metadata": {"papermill": {"duration": 0.097897, "end_time": "2021-12-04T16:15:01.509592", "exception": false, "start_time": "2021-12-04T16:15:01.411695", "status": "completed"}, "tags": []}, "source": ["## Graph Neural Networks"]}, {"cell_type": "markdown", "id": "3f505697", "metadata": {"papermill": {"duration": 0.102332, "end_time": "2021-12-04T16:15:01.708754", "exception": false, "start_time": "2021-12-04T16:15:01.606422", "status": "completed"}, "tags": []}, "source": ["### Graph representation\n", "\n", "Before starting the discussion of specific neural network operations on graphs, we should consider how to represent a graph.\n", "Mathematically, a graph $\\mathcal{G}$ is defined as a tuple of a set of nodes/vertices $V$, and a set of edges/links $E$: $\\mathcal{G}=(V,E)$.\n", "Each edge is a pair of two vertices, and represents a connection between them.\n", "For instance, let's look at the following graph:\n", "\n", "
\n", "\n", "The vertices are $V=\\{1,2,3,4\\}$, and edges $E=\\{(1,2), (2,3), (2,4), (3,4)\\}$.\n", "Note that for simplicity, we assume the graph to be undirected and hence don't add mirrored pairs like $(2,1)$.\n", "In application, vertices and edge can often have specific attributes, and edges can even be directed.\n", "The question is how we could represent this diversity in an efficient way for matrix operations.\n", "Usually, for the edges, we decide between two variants: an adjacency matrix, or a list of paired vertex indices.\n", "\n", "The **adjacency matrix** $A$ is a square matrix whose elements indicate whether pairs of vertices are adjacent,\n", "i.e. connected, or not.\n", "In the simplest case, $A_{ij}$ is 1 if there is a connection from node $i$ to $j$, and otherwise 0.\n", "If we have edge attributes or different categories of edges in a graph, this information can be added to the matrix as well.\n", "For an undirected graph, keep in mind that $A$ is a symmetric matrix ($A_{ij}=A_{ji}$).\n", "For the example graph above, we have the following adjacency matrix:\n", "\n", "$$\n", "A = \\begin{bmatrix}\n", " 0 & 1 & 0 & 0\\\\\n", " 1 & 0 & 1 & 1\\\\\n", " 0 & 1 & 0 & 1\\\\\n", " 0 & 1 & 1 & 0\n", "\\end{bmatrix}\n", "$$\n", "\n", "While expressing a graph as a list of edges is more efficient in terms of memory and (possibly) computation,\n", "using an adjacency matrix is more intuitive and simpler to implement.\n", "In our implementations below, we will rely on the adjacency matrix to keep the code simple.\n", "However, common libraries use edge lists, which we will discuss later more.\n", "Alternatively, we could also use the list of edges to define a sparse adjacency matrix with which we can work\n", "as if it was a dense matrix, but allows more memory-efficient operations.\n", "PyTorch supports this with the sub-package `torch.sparse`\n", "([documentation](https://pytorch.org/docs/stable/sparse.html)) which is however still in a beta-stage\n", "(API might change in future)."]}, {"cell_type": "markdown", "id": "9fea25df", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.097611, "end_time": "2021-12-04T16:15:01.904168", "exception": false, "start_time": "2021-12-04T16:15:01.806557", "status": "completed"}, "tags": []}, "source": ["### Graph Convolutions\n", "\n", "Graph Convolutional Networks have been introduced by [Kipf et al. ](https://openreview.net/pdf?id=SJU4ayYgl)\n", "in 2016 at the University of Amsterdam.\n", "He also wrote a great [blog post](https://tkipf.github.io/graph-convolutional-networks/) about this topic,\n", "which is recommended if you want to read about GCNs from a different perspective.\n", "GCNs are similar to convolutions in images in the sense that the \"filter\" parameters are typically shared over all locations in the graph.\n", "At the same time, GCNs rely on message passing methods, which means that vertices exchange information with the neighbors,\n", "and send \"messages\" to each other.\n", "Before looking at the math, we can try to visually understand how GCNs work.\n", "The first step is that each node creates a feature vector that represents the message it wants to send to all its neighbors.\n", "In the second step, the messages are sent to the neighbors, so that a node receives one message per adjacent node.\n", "Below we have visualized the two steps for our example graph.\n", "\n", "
\n", "\n", "If we want to formulate that in more mathematical terms, we need to first decide how to combine\n", "all the messages a node receives.\n", "As the number of messages vary across nodes, we need an operation that works for any number.\n", "Hence, the usual way to go is to sum or take the mean.\n", "Given the previous features of nodes $H^{(l)}$, the GCN layer is defined as follows:\n", "\n", "$$H^{(l+1)} = \\sigma\\left(\\hat{D}^{-1/2}\\hat{A}\\hat{D}^{-1/2}H^{(l)}W^{(l)}\\right)$$\n", "\n", "$W^{(l)}$ is the weight parameters with which we transform the input features into messages ($H^{(l)}W^{(l)}$).\n", "To the adjacency matrix $A$ we add the identity matrix so that each node sends its own message also to itself:\n", "$\\hat{A}=A+I$.\n", "Finally, to take the average instead of summing, we calculate the matrix $\\hat{D}$ which is a diagonal\n", "matrix with $D_{ii}$ denoting the number of neighbors node $i$ has.\n", "$\\sigma$ represents an arbitrary activation function, and not necessarily the sigmoid (usually a ReLU-based\n", "activation function is used in GNNs).\n", "\n", "When implementing the GCN layer in PyTorch, we can take advantage of the flexible operations on tensors.\n", "Instead of defining a matrix $\\hat{D}$, we can simply divide the summed messages by the number of neighbors afterward.\n", "Additionally, we replace the weight matrix with a linear layer, which additionally allows us to add a bias.\n", "Written as a PyTorch module, the GCN layer is defined as follows:"]}, {"cell_type": "code", "execution_count": 4, "id": "27e21d7b", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:02.104708Z", "iopub.status.busy": "2021-12-04T16:15:02.104230Z", "iopub.status.idle": "2021-12-04T16:15:02.106193Z", "shell.execute_reply": "2021-12-04T16:15:02.105814Z"}, "papermill": {"duration": 0.105512, "end_time": "2021-12-04T16:15:02.106302", "exception": false, "start_time": "2021-12-04T16:15:02.000790", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GCNLayer(nn.Module):\n", " def __init__(self, c_in, c_out):\n", " super().__init__()\n", " self.projection = nn.Linear(c_in, c_out)\n", "\n", " def forward(self, node_feats, adj_matrix):\n", " \"\"\"\n", " Args:\n", " node_feats: Tensor with node features of shape [batch_size, num_nodes, c_in]\n", " adj_matrix: Batch of adjacency matrices of the graph. If there is an edge from i to j,\n", " adj_matrix[b,i,j]=1 else 0. Supports directed edges by non-symmetric matrices.\n", " Assumes to already have added the identity connections.\n", " Shape: [batch_size, num_nodes, num_nodes]\n", " \"\"\"\n", " # Num neighbours = number of incoming edges\n", " num_neighbours = adj_matrix.sum(dim=-1, keepdims=True)\n", " node_feats = self.projection(node_feats)\n", " node_feats = torch.bmm(adj_matrix, node_feats)\n", " node_feats = node_feats / num_neighbours\n", " return node_feats"]}, {"cell_type": "markdown", "id": "e0c30193", "metadata": {"papermill": {"duration": 0.097045, "end_time": "2021-12-04T16:15:02.300782", "exception": false, "start_time": "2021-12-04T16:15:02.203737", "status": "completed"}, "tags": []}, "source": ["To further understand the GCN layer, we can apply it to our example graph above.\n", "First, let's specify some node features and the adjacency matrix with added self-connections:"]}, {"cell_type": "code", "execution_count": 5, "id": "1a809047", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:02.499953Z", "iopub.status.busy": "2021-12-04T16:15:02.499486Z", "iopub.status.idle": "2021-12-04T16:15:02.504131Z", "shell.execute_reply": "2021-12-04T16:15:02.504506Z"}, "papermill": {"duration": 0.106672, "end_time": "2021-12-04T16:15:02.504629", "exception": false, "start_time": "2021-12-04T16:15:02.397957", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Node features:\n", " tensor([[[0., 1.],\n", " [2., 3.],\n", " [4., 5.],\n", " [6., 7.]]])\n", "\n", "Adjacency matrix:\n", " tensor([[[1., 1., 0., 0.],\n", " [1., 1., 1., 1.],\n", " [0., 1., 1., 1.],\n", " [0., 1., 1., 1.]]])\n"]}], "source": ["node_feats = torch.arange(8, dtype=torch.float32).view(1, 4, 2)\n", "adj_matrix = torch.Tensor([[[1, 1, 0, 0], [1, 1, 1, 1], [0, 1, 1, 1], [0, 1, 1, 1]]])\n", "\n", "print(\"Node features:\\n\", node_feats)\n", "print(\"\\nAdjacency matrix:\\n\", adj_matrix)"]}, {"cell_type": "markdown", "id": "e83b46bb", "metadata": {"papermill": {"duration": 0.09825, "end_time": "2021-12-04T16:15:02.700721", "exception": false, "start_time": "2021-12-04T16:15:02.602471", "status": "completed"}, "tags": []}, "source": ["Next, let's apply a GCN layer to it.\n", "For simplicity, we initialize the linear weight matrix as an identity matrix so that the input features are equal to the messages.\n", "This makes it easier for us to verify the message passing operation."]}, {"cell_type": "code", "execution_count": 6, "id": "74912306", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:02.902678Z", "iopub.status.busy": "2021-12-04T16:15:02.902191Z", "iopub.status.idle": "2021-12-04T16:15:02.907793Z", "shell.execute_reply": "2021-12-04T16:15:02.907388Z"}, "papermill": {"duration": 0.108228, "end_time": "2021-12-04T16:15:02.907895", "exception": false, "start_time": "2021-12-04T16:15:02.799667", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Adjacency matrix tensor([[[1., 1., 0., 0.],\n", " [1., 1., 1., 1.],\n", " [0., 1., 1., 1.],\n", " [0., 1., 1., 1.]]])\n", "Input features tensor([[[0., 1.],\n", " [2., 3.],\n", " [4., 5.],\n", " [6., 7.]]])\n", "Output features tensor([[[1., 2.],\n", " [3., 4.],\n", " [4., 5.],\n", " [4., 5.]]])\n"]}], "source": ["layer = GCNLayer(c_in=2, c_out=2)\n", "layer.projection.weight.data = torch.Tensor([[1.0, 0.0], [0.0, 1.0]])\n", "layer.projection.bias.data = torch.Tensor([0.0, 0.0])\n", "\n", "with torch.no_grad():\n", " out_feats = layer(node_feats, adj_matrix)\n", "\n", "print(\"Adjacency matrix\", adj_matrix)\n", "print(\"Input features\", node_feats)\n", "print(\"Output features\", out_feats)"]}, {"cell_type": "markdown", "id": "e60f06b6", "metadata": {"papermill": {"duration": 0.100382, "end_time": "2021-12-04T16:15:03.107772", "exception": false, "start_time": "2021-12-04T16:15:03.007390", "status": "completed"}, "tags": []}, "source": ["As we can see, the first node's output values are the average of itself and the second node.\n", "Similarly, we can verify all other nodes.\n", "However, in a GNN, we would also want to allow feature exchange between nodes beyond its neighbors.\n", "This can be achieved by applying multiple GCN layers, which gives us the final layout of a GNN.\n", "The GNN can be build up by a sequence of GCN layers and non-linearities such as ReLU.\n", "For a visualization, see below (figure credit - [Thomas Kipf, 2016](https://tkipf.github.io/graph-convolutional-networks/)).\n", "\n", "
\n", "\n", "However, one issue we can see from looking at the example above is that the output features for nodes 3 and 4 are\n", "the same because they have the same adjacent nodes (including itself).\n", "Therefore, GCN layers can make the network forget node-specific information if we just take a mean over all messages.\n", "Multiple possible improvements have been proposed.\n", "While the simplest option might be using residual connections, the more common approach is to either weigh\n", "the self-connections higher or define a separate weight matrix for the self-connections.\n", "Alternatively, we can use a well-known concept: attention."]}, {"cell_type": "markdown", "id": "fcca2c46", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.098504, "end_time": "2021-12-04T16:15:03.308884", "exception": false, "start_time": "2021-12-04T16:15:03.210380", "status": "completed"}, "tags": []}, "source": ["### Graph Attention\n", "\n", "Attention describes a weighted average of multiple elements with the weights dynamically computed based on an input\n", "query and elements' keys (if you don't know what attention is, it is recommended to at least go through\n", "the very first section called [What is Attention?](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial6/Transformers_and_MHAttention.html#What-is-Attention?)).\n", "This concept can be similarly applied to graphs, one of such is the Graph Attention Network\n", "(called GAT, proposed by [Velickovic et al., 2017](https://arxiv.org/abs/1710.10903)).\n", "Similarly to the GCN, the graph attention layer creates a message for each node using a linear layer/weight matrix.\n", "For the attention part, it uses the message from the node itself as a query, and the messages to average as both\n", "keys and values (note that this also includes the message to itself).\n", "The score function $f_{attn}$ is implemented as a one-layer MLP which maps the query and key to a single value.\n", "The MLP looks as follows (figure credit - [Velickovic et al. ](https://arxiv.org/abs/1710.10903)):\n", "\n", "
\n", "\n", "$h_i$ and $h_j$ are the original features from node $i$ and $j$ respectively, and represent the messages\n", "of the layer with $\\mathbf{W}$ as weight matrix.\n", "$\\mathbf{a}$ is the weight matrix of the MLP, which has the shape $[1,2\\times d_{\\text{message}}]$,\n", "and $\\alpha_{ij}$ the final attention weight from node $i$ to $j$.\n", "The calculation can be described as follows:\n", "\n", "$$\\alpha_{ij} = \\frac{\\exp\\left(\\text{LeakyReLU}\\left(\\mathbf{a}\\left[\\mathbf{W}h_i||\\mathbf{W}h_j\\right]\\right)\\right)}{\\sum_{k\\in\\mathcal{N}_i} \\exp\\left(\\text{LeakyReLU}\\left(\\mathbf{a}\\left[\\mathbf{W}h_i||\\mathbf{W}h_k\\right]\\right)\\right)}$$\n", "\n", "The operator $||$ represents the concatenation, and $\\mathcal{N}_i$ the indices of the neighbors of node $i$.\n", "Note that in contrast to usual practice, we apply a non-linearity (here LeakyReLU) before the softmax over elements.\n", "Although it seems like a minor change at first, it is crucial for the attention to depend on the original input.\n", "Specifically, let's remove the non-linearity for a second, and try to simplify the expression:\n", "\n", "$$\n", "\\begin{split}\n", " \\alpha_{ij} & = \\frac{\\exp\\left(\\mathbf{a}\\left[\\mathbf{W}h_i||\\mathbf{W}h_j\\right]\\right)}{\\sum_{k\\in\\mathcal{N}_i} \\exp\\left(\\mathbf{a}\\left[\\mathbf{W}h_i||\\mathbf{W}h_k\\right]\\right)}\\\\[5pt]\n", " & = \\frac{\\exp\\left(\\mathbf{a}_{:,:d/2}\\mathbf{W}h_i+\\mathbf{a}_{:,d/2:}\\mathbf{W}h_j\\right)}{\\sum_{k\\in\\mathcal{N}_i} \\exp\\left(\\mathbf{a}_{:,:d/2}\\mathbf{W}h_i+\\mathbf{a}_{:,d/2:}\\mathbf{W}h_k\\right)}\\\\[5pt]\n", " & = \\frac{\\exp\\left(\\mathbf{a}_{:,:d/2}\\mathbf{W}h_i\\right)\\cdot\\exp\\left(\\mathbf{a}_{:,d/2:}\\mathbf{W}h_j\\right)}{\\sum_{k\\in\\mathcal{N}_i} \\exp\\left(\\mathbf{a}_{:,:d/2}\\mathbf{W}h_i\\right)\\cdot\\exp\\left(\\mathbf{a}_{:,d/2:}\\mathbf{W}h_k\\right)}\\\\[5pt]\n", " & = \\frac{\\exp\\left(\\mathbf{a}_{:,d/2:}\\mathbf{W}h_j\\right)}{\\sum_{k\\in\\mathcal{N}_i} \\exp\\left(\\mathbf{a}_{:,d/2:}\\mathbf{W}h_k\\right)}\\\\\n", "\\end{split}\n", "$$\n", "\n", "We can see that without the non-linearity, the attention term with $h_i$ actually cancels itself out,\n", "resulting in the attention being independent of the node itself.\n", "Hence, we would have the same issue as the GCN of creating the same output features for nodes with the same neighbors.\n", "This is why the LeakyReLU is crucial and adds some dependency on $h_i$ to the attention.\n", "\n", "Once we obtain all attention factors, we can calculate the output features for each node by performing\n", "the weighted average:\n", "\n", "$$h_i'=\\sigma\\left(\\sum_{j\\in\\mathcal{N}_i}\\alpha_{ij}\\mathbf{W}h_j\\right)$$\n", "\n", "$\\sigma$ is yet another non-linearity, as in the GCN layer.\n", "Visually, we can represent the full message passing in an attention layer as follows\n", "(figure credit - [Velickovic et al. ](https://arxiv.org/abs/1710.10903)):\n", "\n", "
\n", "\n", "To increase the expressiveness of the graph attention network, [Velickovic et al. ](https://arxiv.org/abs/1710.10903)\n", "proposed to extend it to multiple heads similar to the Multi-Head Attention block in Transformers.\n", "This results in $N$ attention layers being applied in parallel.\n", "In the image above, it is visualized as three different colors of arrows (green, blue, and purple)\n", "that are afterward concatenated.\n", "The average is only applied for the very final prediction layer in a network.\n", "\n", "After having discussed the graph attention layer in detail, we can implement it below:"]}, {"cell_type": "code", "execution_count": 7, "id": "a5cd3c11", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:03.518254Z", "iopub.status.busy": "2021-12-04T16:15:03.517760Z", "iopub.status.idle": "2021-12-04T16:15:03.519768Z", "shell.execute_reply": "2021-12-04T16:15:03.519389Z"}, "papermill": {"duration": 0.112404, "end_time": "2021-12-04T16:15:03.519875", "exception": false, "start_time": "2021-12-04T16:15:03.407471", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GATLayer(nn.Module):\n", " def __init__(self, c_in, c_out, num_heads=1, concat_heads=True, alpha=0.2):\n", " \"\"\"\n", " Args:\n", " c_in: Dimensionality of input features\n", " c_out: Dimensionality of output features\n", " num_heads: Number of heads, i.e. attention mechanisms to apply in parallel. The\n", " output features are equally split up over the heads if concat_heads=True.\n", " concat_heads: If True, the output of the different heads is concatenated instead of averaged.\n", " alpha: Negative slope of the LeakyReLU activation.\n", " \"\"\"\n", " super().__init__()\n", " self.num_heads = num_heads\n", " self.concat_heads = concat_heads\n", " if self.concat_heads:\n", " assert c_out % num_heads == 0, \"Number of output features must be a multiple of the count of heads.\"\n", " c_out = c_out // num_heads\n", "\n", " # Sub-modules and parameters needed in the layer\n", " self.projection = nn.Linear(c_in, c_out * num_heads)\n", " self.a = nn.Parameter(torch.Tensor(num_heads, 2 * c_out)) # One per head\n", " self.leakyrelu = nn.LeakyReLU(alpha)\n", "\n", " # Initialization from the original implementation\n", " nn.init.xavier_uniform_(self.projection.weight.data, gain=1.414)\n", " nn.init.xavier_uniform_(self.a.data, gain=1.414)\n", "\n", " def forward(self, node_feats, adj_matrix, print_attn_probs=False):\n", " \"\"\"\n", " Args:\n", " node_feats: Input features of the node. Shape: [batch_size, c_in]\n", " adj_matrix: Adjacency matrix including self-connections. Shape: [batch_size, num_nodes, num_nodes]\n", " print_attn_probs: If True, the attention weights are printed during the forward pass\n", " (for debugging purposes)\n", " \"\"\"\n", " batch_size, num_nodes = node_feats.size(0), node_feats.size(1)\n", "\n", " # Apply linear layer and sort nodes by head\n", " node_feats = self.projection(node_feats)\n", " node_feats = node_feats.view(batch_size, num_nodes, self.num_heads, -1)\n", "\n", " # We need to calculate the attention logits for every edge in the adjacency matrix\n", " # Doing this on all possible combinations of nodes is very expensive\n", " # => Create a tensor of [W*h_i||W*h_j] with i and j being the indices of all edges\n", " # Returns indices where the adjacency matrix is not 0 => edges\n", " edges = adj_matrix.nonzero(as_tuple=False)\n", " node_feats_flat = node_feats.view(batch_size * num_nodes, self.num_heads, -1)\n", " edge_indices_row = edges[:, 0] * num_nodes + edges[:, 1]\n", " edge_indices_col = edges[:, 0] * num_nodes + edges[:, 2]\n", " a_input = torch.cat(\n", " [\n", " torch.index_select(input=node_feats_flat, index=edge_indices_row, dim=0),\n", " torch.index_select(input=node_feats_flat, index=edge_indices_col, dim=0),\n", " ],\n", " dim=-1,\n", " ) # Index select returns a tensor with node_feats_flat being indexed at the desired positions\n", "\n", " # Calculate attention MLP output (independent for each head)\n", " attn_logits = torch.einsum(\"bhc,hc->bh\", a_input, self.a)\n", " attn_logits = self.leakyrelu(attn_logits)\n", "\n", " # Map list of attention values back into a matrix\n", " attn_matrix = attn_logits.new_zeros(adj_matrix.shape + (self.num_heads,)).fill_(-9e15)\n", " attn_matrix[adj_matrix[..., None].repeat(1, 1, 1, self.num_heads) == 1] = attn_logits.reshape(-1)\n", "\n", " # Weighted average of attention\n", " attn_probs = F.softmax(attn_matrix, dim=2)\n", " if print_attn_probs:\n", " print(\"Attention probs\\n\", attn_probs.permute(0, 3, 1, 2))\n", " node_feats = torch.einsum(\"bijh,bjhc->bihc\", attn_probs, node_feats)\n", "\n", " # If heads should be concatenated, we can do this by reshaping. Otherwise, take mean\n", " if self.concat_heads:\n", " node_feats = node_feats.reshape(batch_size, num_nodes, -1)\n", " else:\n", " node_feats = node_feats.mean(dim=2)\n", "\n", " return node_feats"]}, {"cell_type": "markdown", "id": "653c0d4e", "metadata": {"papermill": {"duration": 0.098839, "end_time": "2021-12-04T16:15:03.718323", "exception": false, "start_time": "2021-12-04T16:15:03.619484", "status": "completed"}, "tags": []}, "source": ["Again, we can apply the graph attention layer on our example graph above to understand the dynamics better.\n", "As before, the input layer is initialized as an identity matrix, but we set $\\mathbf{a}$\n", "to be a vector of arbitrary numbers to obtain different attention values.\n", "We use two heads to show the parallel, independent attention mechanisms working in the layer."]}, {"cell_type": "code", "execution_count": 8, "id": "18b9b0b5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:03.921969Z", "iopub.status.busy": "2021-12-04T16:15:03.921496Z", "iopub.status.idle": "2021-12-04T16:15:03.929280Z", "shell.execute_reply": "2021-12-04T16:15:03.928808Z"}, "papermill": {"duration": 0.111598, "end_time": "2021-12-04T16:15:03.929387", "exception": false, "start_time": "2021-12-04T16:15:03.817789", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Attention probs\n", " tensor([[[[0.3543, 0.6457, 0.0000, 0.0000],\n", " [0.1096, 0.1450, 0.2642, 0.4813],\n", " [0.0000, 0.1858, 0.2885, 0.5257],\n", " [0.0000, 0.2391, 0.2696, 0.4913]],\n", "\n", " [[0.5100, 0.4900, 0.0000, 0.0000],\n", " [0.2975, 0.2436, 0.2340, 0.2249],\n", " [0.0000, 0.3838, 0.3142, 0.3019],\n", " [0.0000, 0.4018, 0.3289, 0.2693]]]])\n", "Adjacency matrix tensor([[[1., 1., 0., 0.],\n", " [1., 1., 1., 1.],\n", " [0., 1., 1., 1.],\n", " [0., 1., 1., 1.]]])\n", "Input features tensor([[[0., 1.],\n", " [2., 3.],\n", " [4., 5.],\n", " [6., 7.]]])\n", "Output features tensor([[[1.2913, 1.9800],\n", " [4.2344, 3.7725],\n", " [4.6798, 4.8362],\n", " [4.5043, 4.7351]]])\n"]}], "source": ["layer = GATLayer(2, 2, num_heads=2)\n", "layer.projection.weight.data = torch.Tensor([[1.0, 0.0], [0.0, 1.0]])\n", "layer.projection.bias.data = torch.Tensor([0.0, 0.0])\n", "layer.a.data = torch.Tensor([[-0.2, 0.3], [0.1, -0.1]])\n", "\n", "with torch.no_grad():\n", " out_feats = layer(node_feats, adj_matrix, print_attn_probs=True)\n", "\n", "print(\"Adjacency matrix\", adj_matrix)\n", "print(\"Input features\", node_feats)\n", "print(\"Output features\", out_feats)"]}, {"cell_type": "markdown", "id": "fdec7a34", "metadata": {"papermill": {"duration": 0.09994, "end_time": "2021-12-04T16:15:04.129204", "exception": false, "start_time": "2021-12-04T16:15:04.029264", "status": "completed"}, "tags": []}, "source": ["We recommend that you try to calculate the attention matrix at least for one head and one node for yourself.\n", "The entries are 0 where there does not exist an edge between $i$ and $j$.\n", "For the others, we see a diverse set of attention probabilities.\n", "Moreover, the output features of node 3 and 4 are now different although they have the same neighbors."]}, {"cell_type": "markdown", "id": "d4ac3429", "metadata": {"papermill": {"duration": 0.099386, "end_time": "2021-12-04T16:15:04.328973", "exception": false, "start_time": "2021-12-04T16:15:04.229587", "status": "completed"}, "tags": []}, "source": ["## PyTorch Geometric\n", "\n", "We had mentioned before that implementing graph networks with adjacency matrix is simple and straight-forward\n", "but can be computationally expensive for large graphs.\n", "Many real-world graphs can reach over 200k nodes, for which adjacency matrix-based implementations fail.\n", "There are a lot of optimizations possible when implementing GNNs, and luckily, there exist packages that provide such layers.\n", "The most popular packages for PyTorch are [PyTorch Geometric](https://pytorch-geometric.readthedocs.io/en/latest/)\n", "and the [Deep Graph Library](https://www.dgl.ai/) (the latter being actually framework agnostic).\n", "Which one to use depends on the project you are planning to do and personal taste.\n", "In this tutorial, we will look at PyTorch Geometric as part of the PyTorch family.\n", "\n", "PyTorch Geometric provides us a set of common graph layers, including the GCN and GAT layer we implemented above.\n", "Additionally, similar to PyTorch's torchvision, it provides the common graph datasets and transformations\n", "on those to simplify training.\n", "Compared to our implementation above, PyTorch Geometric uses a list of index pairs to represent the edges.\n", "The details of this library will be explored further in our experiments.\n", "\n", "In our tasks below, we want to allow us to pick from a multitude of graph layers.\n", "Thus, we define again below a dictionary to access those using a string:"]}, {"cell_type": "code", "execution_count": 9, "id": "bef53fb6", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:04.539895Z", "iopub.status.busy": "2021-12-04T16:15:04.539244Z", "iopub.status.idle": "2021-12-04T16:15:04.541427Z", "shell.execute_reply": "2021-12-04T16:15:04.540961Z"}, "papermill": {"duration": 0.109903, "end_time": "2021-12-04T16:15:04.541533", "exception": false, "start_time": "2021-12-04T16:15:04.431630", "status": "completed"}, "tags": []}, "outputs": [], "source": ["gnn_layer_by_name = {\"GCN\": geom_nn.GCNConv, \"GAT\": geom_nn.GATConv, \"GraphConv\": geom_nn.GraphConv}"]}, {"cell_type": "markdown", "id": "b62d2986", "metadata": {"papermill": {"duration": 0.101051, "end_time": "2021-12-04T16:15:04.742512", "exception": false, "start_time": "2021-12-04T16:15:04.641461", "status": "completed"}, "tags": []}, "source": ["Additionally to GCN and GAT, we added the layer `geom_nn.GraphConv`\n", "([documentation](https://pytorch-geometric.readthedocs.io/en/latest/modules/nn.html#torch_geometric.nn.conv.GraphConv)).\n", "GraphConv is a GCN with a separate weight matrix for the self-connections.\n", "Mathematically, this would be:\n", "\n", "$$\n", "\\mathbf{x}_i^{(l+1)} = \\mathbf{W}^{(l + 1)}_1 \\mathbf{x}_i^{(l)} + \\mathbf{W}^{(\\ell + 1)}_2 \\sum_{j \\in \\mathcal{N}_i} \\mathbf{x}_j^{(l)}\n", "$$\n", "\n", "In this formula, the neighbor's messages are added instead of averaged.\n", "However, PyTorch Geometric provides the argument `aggr` to switch between summing, averaging, and max pooling."]}, {"cell_type": "markdown", "id": "cd5a3c9a", "metadata": {"papermill": {"duration": 0.100612, "end_time": "2021-12-04T16:15:04.943942", "exception": false, "start_time": "2021-12-04T16:15:04.843330", "status": "completed"}, "tags": []}, "source": ["## Experiments on graph structures\n", "\n", "
\n", "\n", "Tasks on graph-structured data can be grouped into three groups: node-level, edge-level and graph-level.\n", "The different levels describe on which level we want to perform classification/regression.\n", "We will discuss all three types in more detail below."]}, {"cell_type": "markdown", "id": "4231c6c6", "metadata": {"papermill": {"duration": 0.101137, "end_time": "2021-12-04T16:15:05.148024", "exception": false, "start_time": "2021-12-04T16:15:05.046887", "status": "completed"}, "tags": []}, "source": ["### Node-level tasks: Semi-supervised node classification\n", "\n", "Node-level tasks have the goal to classify nodes in a graph.\n", "Usually, we have given a single, large graph with >1000 nodes of which a certain amount of nodes are labeled.\n", "We learn to classify those labeled examples during training and try to generalize to the unlabeled nodes.\n", "\n", "A popular example that we will use in this tutorial is the Cora dataset, a citation network among papers.\n", "The Cora consists of 2708 scientific publications with links between each other representing\n", "the citation of one paper by another.\n", "The task is to classify each publication into one of seven classes.\n", "Each publication is represented by a bag-of-words vector.\n", "This means that we have a vector of 1433 elements for each publication, where a 1 at feature $i$ indicates\n", "that the $i$-th word of a pre-defined dictionary is in the article.\n", "Binary bag-of-words representations are commonly used when we need very simple encodings,\n", "and already have an intuition of what words to expect in a network.\n", "There exist much better approaches, but we will leave this to the NLP courses to discuss.\n", "\n", "We will load the dataset below:"]}, {"cell_type": "code", "execution_count": 10, "id": "4805971d", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:05.356051Z", "iopub.status.busy": "2021-12-04T16:15:05.355583Z", "iopub.status.idle": "2021-12-04T16:15:06.678721Z", "shell.execute_reply": "2021-12-04T16:15:06.679111Z"}, "papermill": {"duration": 1.427385, "end_time": "2021-12-04T16:15:06.679294", "exception": false, "start_time": "2021-12-04T16:15:05.251909", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.x\n", "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.tx\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.allx\n", "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.y\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.ty\n", "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.ally\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.graph\n", "Downloading https://github.com/kimiyoung/planetoid/raw/master/data/ind.cora.test.index\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Processing...\n", "Done!\n"]}], "source": ["cora_dataset = torch_geometric.datasets.Planetoid(root=DATASET_PATH, name=\"Cora\")"]}, {"cell_type": "markdown", "id": "c922d88d", "metadata": {"papermill": {"duration": 0.105227, "end_time": "2021-12-04T16:15:06.889432", "exception": false, "start_time": "2021-12-04T16:15:06.784205", "status": "completed"}, "tags": []}, "source": ["Let's look at how PyTorch Geometric represents the graph data.\n", "Note that although we have a single graph, PyTorch Geometric returns a dataset for compatibility to other datasets."]}, {"cell_type": "code", "execution_count": 11, "id": "a89178e2", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:07.109728Z", "iopub.status.busy": "2021-12-04T16:15:07.109256Z", "iopub.status.idle": "2021-12-04T16:15:07.112556Z", "shell.execute_reply": "2021-12-04T16:15:07.112172Z"}, "papermill": {"duration": 0.11818, "end_time": "2021-12-04T16:15:07.112667", "exception": false, "start_time": "2021-12-04T16:15:06.994487", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/plain": ["Data(x=[2708, 1433], edge_index=[2, 10556], y=[2708], train_mask=[2708], val_mask=[2708], test_mask=[2708])"]}, "execution_count": 11, "metadata": {}, "output_type": "execute_result"}], "source": ["cora_dataset[0]"]}, {"cell_type": "markdown", "id": "fbb76b54", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.104743, "end_time": "2021-12-04T16:15:07.323421", "exception": false, "start_time": "2021-12-04T16:15:07.218678", "status": "completed"}, "tags": []}, "source": ["The graph is represented by a `Data` object\n", "([documentation](https://pytorch-geometric.readthedocs.io/en/latest/modules/data.html#torch_geometric.data.Data))\n", "which we can access as a standard Python namespace.\n", "The edge index tensor is the list of edges in the graph and contains the mirrored version of each edge for undirected graphs.\n", "The `train_mask`, `val_mask`, and `test_mask` are boolean masks that indicate which nodes we should use for training,\n", "validation, and testing.\n", "The `x` tensor is the feature tensor of our 2708 publications, and `y` the labels for all nodes.\n", "\n", "After having seen the data, we can implement a simple graph neural network.\n", "The GNN applies a sequence of graph layers (GCN, GAT, or GraphConv), ReLU as activation function,\n", "and dropout for regularization.\n", "See below for the specific implementation."]}, {"cell_type": "code", "execution_count": 12, "id": "9a909772", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:07.543303Z", "iopub.status.busy": "2021-12-04T16:15:07.542816Z", "iopub.status.idle": "2021-12-04T16:15:07.544744Z", "shell.execute_reply": "2021-12-04T16:15:07.544369Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.115163, "end_time": "2021-12-04T16:15:07.544851", "exception": false, "start_time": "2021-12-04T16:15:07.429688", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GNNModel(nn.Module):\n", " def __init__(\n", " self,\n", " c_in,\n", " c_hidden,\n", " c_out,\n", " num_layers=2,\n", " layer_name=\"GCN\",\n", " dp_rate=0.1,\n", " **kwargs,\n", " ):\n", " \"\"\"\n", " Args:\n", " c_in: Dimension of input features\n", " c_hidden: Dimension of hidden features\n", " c_out: Dimension of the output features. Usually number of classes in classification\n", " num_layers: Number of \"hidden\" graph layers\n", " layer_name: String of the graph layer to use\n", " dp_rate: Dropout rate to apply throughout the network\n", " kwargs: Additional arguments for the graph layer (e.g. number of heads for GAT)\n", " \"\"\"\n", " super().__init__()\n", " gnn_layer = gnn_layer_by_name[layer_name]\n", "\n", " layers = []\n", " in_channels, out_channels = c_in, c_hidden\n", " for l_idx in range(num_layers - 1):\n", " layers += [\n", " gnn_layer(in_channels=in_channels, out_channels=out_channels, **kwargs),\n", " nn.ReLU(inplace=True),\n", " nn.Dropout(dp_rate),\n", " ]\n", " in_channels = c_hidden\n", " layers += [gnn_layer(in_channels=in_channels, out_channels=c_out, **kwargs)]\n", " self.layers = nn.ModuleList(layers)\n", "\n", " def forward(self, x, edge_index):\n", " \"\"\"\n", " Args:\n", " x: Input features per node\n", " edge_index: List of vertex index pairs representing the edges in the graph (PyTorch geometric notation)\n", " \"\"\"\n", " for layer in self.layers:\n", " # For graph layers, we need to add the \"edge_index\" tensor as additional input\n", " # All PyTorch Geometric graph layer inherit the class \"MessagePassing\", hence\n", " # we can simply check the class type.\n", " if isinstance(layer, geom_nn.MessagePassing):\n", " x = layer(x, edge_index)\n", " else:\n", " x = layer(x)\n", " return x"]}, {"cell_type": "markdown", "id": "5645bafd", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.104054, "end_time": "2021-12-04T16:15:07.753237", "exception": false, "start_time": "2021-12-04T16:15:07.649183", "status": "completed"}, "tags": []}, "source": ["Good practice in node-level tasks is to create an MLP baseline that is applied to each node independently.\n", "This way we can verify whether adding the graph information to the model indeed improves the prediction, or not.\n", "It might also be that the features per node are already expressive enough to clearly point towards a specific class.\n", "To check this, we implement a simple MLP below."]}, {"cell_type": "code", "execution_count": 13, "id": "089e96c6", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:07.969616Z", "iopub.status.busy": "2021-12-04T16:15:07.969143Z", "iopub.status.idle": "2021-12-04T16:15:07.971505Z", "shell.execute_reply": "2021-12-04T16:15:07.971032Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.113096, "end_time": "2021-12-04T16:15:07.971610", "exception": false, "start_time": "2021-12-04T16:15:07.858514", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class MLPModel(nn.Module):\n", " def __init__(self, c_in, c_hidden, c_out, num_layers=2, dp_rate=0.1):\n", " \"\"\"\n", " Args:\n", " c_in: Dimension of input features\n", " c_hidden: Dimension of hidden features\n", " c_out: Dimension of the output features. Usually number of classes in classification\n", " num_layers: Number of hidden layers\n", " dp_rate: Dropout rate to apply throughout the network\n", " \"\"\"\n", " super().__init__()\n", " layers = []\n", " in_channels, out_channels = c_in, c_hidden\n", " for l_idx in range(num_layers - 1):\n", " layers += [nn.Linear(in_channels, out_channels), nn.ReLU(inplace=True), nn.Dropout(dp_rate)]\n", " in_channels = c_hidden\n", " layers += [nn.Linear(in_channels, c_out)]\n", " self.layers = nn.Sequential(*layers)\n", "\n", " def forward(self, x, *args, **kwargs):\n", " \"\"\"\n", " Args:\n", " x: Input features per node\n", " \"\"\"\n", " return self.layers(x)"]}, {"cell_type": "markdown", "id": "b76ef536", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.104726, "end_time": "2021-12-04T16:15:08.181306", "exception": false, "start_time": "2021-12-04T16:15:08.076580", "status": "completed"}, "tags": []}, "source": ["Finally, we can merge the models into a PyTorch Lightning module which handles the training,\n", "validation, and testing for us."]}, {"cell_type": "code", "execution_count": 14, "id": "505b841a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:08.400501Z", "iopub.status.busy": "2021-12-04T16:15:08.400021Z", "iopub.status.idle": "2021-12-04T16:15:08.402029Z", "shell.execute_reply": "2021-12-04T16:15:08.401651Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.116664, "end_time": "2021-12-04T16:15:08.402137", "exception": false, "start_time": "2021-12-04T16:15:08.285473", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class NodeLevelGNN(pl.LightningModule):\n", " def __init__(self, model_name, **model_kwargs):\n", " super().__init__()\n", " # Saving hyperparameters\n", " self.save_hyperparameters()\n", "\n", " if model_name == \"MLP\":\n", " self.model = MLPModel(**model_kwargs)\n", " else:\n", " self.model = GNNModel(**model_kwargs)\n", " self.loss_module = nn.CrossEntropyLoss()\n", "\n", " def forward(self, data, mode=\"train\"):\n", " x, edge_index = data.x, data.edge_index\n", " x = self.model(x, edge_index)\n", "\n", " # Only calculate the loss on the nodes corresponding to the mask\n", " if mode == \"train\":\n", " mask = data.train_mask\n", " elif mode == \"val\":\n", " mask = data.val_mask\n", " elif mode == \"test\":\n", " mask = data.test_mask\n", " else:\n", " assert False, \"Unknown forward mode: %s\" % mode\n", "\n", " loss = self.loss_module(x[mask], data.y[mask])\n", " acc = (x[mask].argmax(dim=-1) == data.y[mask]).sum().float() / mask.sum()\n", " return loss, acc\n", "\n", " def configure_optimizers(self):\n", " # We use SGD here, but Adam works as well\n", " optimizer = optim.SGD(self.parameters(), lr=0.1, momentum=0.9, weight_decay=2e-3)\n", " return optimizer\n", "\n", " def training_step(self, batch, batch_idx):\n", " loss, acc = self.forward(batch, mode=\"train\")\n", " self.log(\"train_loss\", loss)\n", " self.log(\"train_acc\", acc)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " _, acc = self.forward(batch, mode=\"val\")\n", " self.log(\"val_acc\", acc)\n", "\n", " def test_step(self, batch, batch_idx):\n", " _, acc = self.forward(batch, mode=\"test\")\n", " self.log(\"test_acc\", acc)"]}, {"cell_type": "markdown", "id": "ca73bf4a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.106691, "end_time": "2021-12-04T16:15:08.614156", "exception": false, "start_time": "2021-12-04T16:15:08.507465", "status": "completed"}, "tags": []}, "source": ["Additionally to the Lightning module, we define a training function below.\n", "As we have a single graph, we use a batch size of 1 for the data loader and share the same data loader for the train,\n", "validation, and test set (the mask is picked inside the Lightning module).\n", "Besides, we set the argument `progress_bar_refresh_rate` to zero as it usually shows the progress per epoch,\n", "but an epoch only consists of a single step.\n", "If you have downloaded the pre-trained models in the beginning of the tutorial, we load those instead of training from scratch.\n", "Finally, we test the model and return the results."]}, {"cell_type": "code", "execution_count": 15, "id": "43920ae6", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:08.833594Z", "iopub.status.busy": "2021-12-04T16:15:08.833097Z", "iopub.status.idle": "2021-12-04T16:15:08.835277Z", "shell.execute_reply": "2021-12-04T16:15:08.834895Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.115214, "end_time": "2021-12-04T16:15:08.835384", "exception": false, "start_time": "2021-12-04T16:15:08.720170", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_node_classifier(model_name, dataset, **model_kwargs):\n", " pl.seed_everything(42)\n", " node_data_loader = geom_data.DataLoader(dataset, batch_size=1)\n", "\n", " # Create a PyTorch Lightning trainer\n", " root_dir = os.path.join(CHECKPOINT_PATH, \"NodeLevel\" + model_name)\n", " os.makedirs(root_dir, exist_ok=True)\n", " trainer = pl.Trainer(\n", " default_root_dir=root_dir,\n", " callbacks=[ModelCheckpoint(save_weights_only=True, mode=\"max\", monitor=\"val_acc\")],\n", " gpus=AVAIL_GPUS,\n", " max_epochs=200,\n", " progress_bar_refresh_rate=0,\n", " ) # 0 because epoch size is 1\n", " trainer.logger._default_hp_metric = None # Optional logging argument that we don't need\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"NodeLevel%s.ckpt\" % model_name)\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model, loading...\")\n", " model = NodeLevelGNN.load_from_checkpoint(pretrained_filename)\n", " else:\n", " pl.seed_everything()\n", " model = NodeLevelGNN(\n", " model_name=model_name, c_in=dataset.num_node_features, c_out=dataset.num_classes, **model_kwargs\n", " )\n", " trainer.fit(model, node_data_loader, node_data_loader)\n", " model = NodeLevelGNN.load_from_checkpoint(trainer.checkpoint_callback.best_model_path)\n", "\n", " # Test best model on the test set\n", " test_result = trainer.test(model, test_dataloaders=node_data_loader, verbose=False)\n", " batch = next(iter(node_data_loader))\n", " batch = batch.to(model.device)\n", " _, train_acc = model.forward(batch, mode=\"train\")\n", " _, val_acc = model.forward(batch, mode=\"val\")\n", " result = {\"train\": train_acc, \"val\": val_acc, \"test\": test_result[0][\"test_acc\"]}\n", " return model, result"]}, {"cell_type": "markdown", "id": "b36ea590", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.104889, "end_time": "2021-12-04T16:15:09.046049", "exception": false, "start_time": "2021-12-04T16:15:08.941160", "status": "completed"}, "tags": []}, "source": ["Now, we can train our models. First, let's train the simple MLP:"]}, {"cell_type": "code", "execution_count": 16, "id": "ad9c97ab", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:09.261430Z", "iopub.status.busy": "2021-12-04T16:15:09.260757Z", "iopub.status.idle": "2021-12-04T16:15:09.262448Z", "shell.execute_reply": "2021-12-04T16:15:09.262843Z"}, "papermill": {"duration": 0.11123, "end_time": "2021-12-04T16:15:09.262970", "exception": false, "start_time": "2021-12-04T16:15:09.151740", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Small function for printing the test scores\n", "def print_results(result_dict):\n", " if \"train\" in result_dict:\n", " print(\"Train accuracy: %4.2f%%\" % (100.0 * result_dict[\"train\"]))\n", " if \"val\" in result_dict:\n", " print(\"Val accuracy: %4.2f%%\" % (100.0 * result_dict[\"val\"]))\n", " print(\"Test accuracy: %4.2f%%\" % (100.0 * result_dict[\"test\"]))"]}, {"cell_type": "code", "execution_count": 17, "id": "8bd3817a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:09.478007Z", "iopub.status.busy": "2021-12-04T16:15:09.477544Z", "iopub.status.idle": "2021-12-04T16:15:13.025779Z", "shell.execute_reply": "2021-12-04T16:15:13.026163Z"}, "papermill": {"duration": 3.657601, "end_time": "2021-12-04T16:15:13.026329", "exception": false, "start_time": "2021-12-04T16:15:09.368728", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/torch_geometric/deprecation.py:13: UserWarning: 'data.DataLoader' is deprecated, use 'loader.DataLoader' instead\n", " warnings.warn(out)\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=0)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:901: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Missing logger folder: saved_models/GNNs/NodeLevelMLP/lightning_logs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Train accuracy: 97.14%\n", "Val accuracy: 54.60%\n", "Test accuracy: 60.60%\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, test_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/utilities/data.py:59: UserWarning: Trying to infer the `batch_size` from an ambiguous collection. The batch size we found is 1. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.\n", " warning_cache.warn(\n"]}], "source": ["node_mlp_model, node_mlp_result = train_node_classifier(\n", " model_name=\"MLP\", dataset=cora_dataset, c_hidden=16, num_layers=2, dp_rate=0.1\n", ")\n", "\n", "print_results(node_mlp_result)"]}, {"cell_type": "markdown", "id": "31d6a6c3", "metadata": {"papermill": {"duration": 0.111534, "end_time": "2021-12-04T16:15:13.251810", "exception": false, "start_time": "2021-12-04T16:15:13.140276", "status": "completed"}, "tags": []}, "source": ["Although the MLP can overfit on the training dataset because of the high-dimensional input features,\n", "it does not perform too well on the test set.\n", "Let's see if we can beat this score with our graph networks:"]}, {"cell_type": "code", "execution_count": 18, "id": "cdfebd3f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:13.479708Z", "iopub.status.busy": "2021-12-04T16:15:13.479240Z", "iopub.status.idle": "2021-12-04T16:15:13.518967Z", "shell.execute_reply": "2021-12-04T16:15:13.518494Z"}, "papermill": {"duration": 0.155618, "end_time": "2021-12-04T16:15:13.519222", "exception": false, "start_time": "2021-12-04T16:15:13.363604", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Missing logger folder: saved_models/GNNs/NodeLevelGNN/lightning_logs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n", "Train accuracy: 100.00%\n", "Val accuracy: 78.60%\n", "Test accuracy: 82.40%\n"]}], "source": ["node_gnn_model, node_gnn_result = train_node_classifier(\n", " model_name=\"GNN\", layer_name=\"GCN\", dataset=cora_dataset, c_hidden=16, num_layers=2, dp_rate=0.1\n", ")\n", "print_results(node_gnn_result)"]}, {"cell_type": "markdown", "id": "ad396406", "metadata": {"papermill": {"duration": 0.117918, "end_time": "2021-12-04T16:15:13.755461", "exception": false, "start_time": "2021-12-04T16:15:13.637543", "status": "completed"}, "tags": []}, "source": ["As we would have hoped for, the GNN model outperforms the MLP by quite a margin.\n", "This shows that using the graph information indeed improves our predictions and lets us generalizes better.\n", "\n", "The hyperparameters in the model have been chosen to create a relatively small network.\n", "This is because the first layer with an input dimension of 1433 can be relatively expensive to perform for large graphs.\n", "In general, GNNs can become relatively expensive for very big graphs.\n", "This is why such GNNs either have a small hidden size or use a special batching strategy\n", "where we sample a connected subgraph of the big, original graph."]}, {"cell_type": "markdown", "id": "088207b7", "metadata": {"papermill": {"duration": 0.118156, "end_time": "2021-12-04T16:15:13.991933", "exception": false, "start_time": "2021-12-04T16:15:13.873777", "status": "completed"}, "tags": []}, "source": ["### Edge-level tasks: Link prediction\n", "\n", "In some applications, we might have to predict on an edge-level instead of node-level.\n", "The most common edge-level task in GNN is link prediction.\n", "Link prediction means that given a graph, we want to predict whether there will be/should be an edge between two nodes or not.\n", "For example, in a social network, this is used by Facebook and co to propose new friends to you.\n", "Again, graph level information can be crucial to perform this task.\n", "The output prediction is usually done by performing a similarity metric on the pair of node features,\n", "which should be 1 if there should be a link, and otherwise close to 0.\n", "To keep the tutorial short, we will not implement this task ourselves.\n", "Nevertheless, there are many good resources out there if you are interested in looking closer at this task.\n", "Tutorials and papers for this topic include:\n", "\n", "* [PyTorch Geometric example](https://github.com/rusty1s/pytorch_geometric/blob/master/examples/link_pred.py)\n", "* [Graph Neural Networks: A Review of Methods and Applications](https://arxiv.org/pdf/1812.08434.pdf), Zhou et al.\n", "2019\n", "* [Link Prediction Based on Graph Neural Networks](https://papers.nips.cc/paper/2018/file/53f0d7c537d99b3824f0f99d62ea2428-Paper.pdf), Zhang and Chen, 2018."]}, {"cell_type": "markdown", "id": "dcf6a450", "metadata": {"papermill": {"duration": 0.116763, "end_time": "2021-12-04T16:15:14.228118", "exception": false, "start_time": "2021-12-04T16:15:14.111355", "status": "completed"}, "tags": []}, "source": ["### Graph-level tasks: Graph classification\n", "\n", "Finally, in this part of the tutorial, we will have a closer look at how to apply GNNs to the task of graph classification.\n", "The goal is to classify an entire graph instead of single nodes or edges.\n", "Therefore, we are also given a dataset of multiple graphs that we need to classify based on some structural graph properties.\n", "The most common task for graph classification is molecular property prediction, in which molecules are represented as graphs.\n", "Each atom is linked to a node, and edges in the graph are the bonds between atoms.\n", "For example, look at the figure below.\n", "\n", "
\n", "\n", "On the left, we have an arbitrary, small molecule with different atoms, whereas the right part of the image shows the graph representation.\n", "The atom types are abstracted as node features (e.g. a one-hot vector), and the different bond types are used as edge features.\n", "For simplicity, we will neglect the edge attributes in this tutorial, but you can include by using methods like the\n", "[Relational Graph Convolution](https://arxiv.org/abs/1703.06103) that uses a different weight matrix for each edge type.\n", "\n", "The dataset we will use below is called the MUTAG dataset.\n", "It is a common small benchmark for graph classification algorithms, and contain 188 graphs with 18 nodes\n", "and 20 edges on average for each graph.\n", "The graph nodes have 7 different labels/atom types, and the binary graph labels represent \"their mutagenic effect\n", "on a specific gram negative bacterium\" (the specific meaning of the labels are not too important here).\n", "The dataset is part of a large collection of different graph classification datasets, known as the\n", "[TUDatasets](https://chrsmrrs.github.io/datasets/), which is directly accessible\n", "via `torch_geometric.datasets.TUDataset` ([documentation](https://pytorch-geometric.readthedocs.io/en/latest/modules/datasets.html#torch_geometric.datasets.TUDataset)) in PyTorch Geometric.\n", "We can load the dataset below."]}, {"cell_type": "code", "execution_count": 19, "id": "4438474b", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:14.467759Z", "iopub.status.busy": "2021-12-04T16:15:14.467292Z", "iopub.status.idle": "2021-12-04T16:15:15.161679Z", "shell.execute_reply": "2021-12-04T16:15:15.162066Z"}, "papermill": {"duration": 0.81606, "end_time": "2021-12-04T16:15:15.162230", "exception": false, "start_time": "2021-12-04T16:15:14.346170", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Downloading https://www.chrsmrrs.com/graphkerneldatasets/MUTAG.zip\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Extracting /__w/1/s/.datasets/MUTAG/MUTAG.zip\n", "Processing...\n", "Done!\n"]}], "source": ["tu_dataset = torch_geometric.datasets.TUDataset(root=DATASET_PATH, name=\"MUTAG\")"]}, {"cell_type": "markdown", "id": "c86832d3", "metadata": {"papermill": {"duration": 0.118059, "end_time": "2021-12-04T16:15:15.401214", "exception": false, "start_time": "2021-12-04T16:15:15.283155", "status": "completed"}, "tags": []}, "source": ["Let's look at some statistics for the dataset:"]}, {"cell_type": "code", "execution_count": 20, "id": "e45b7974", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:15.655613Z", "iopub.status.busy": "2021-12-04T16:15:15.655131Z", "iopub.status.idle": "2021-12-04T16:15:15.657630Z", "shell.execute_reply": "2021-12-04T16:15:15.658011Z"}, "papermill": {"duration": 0.125603, "end_time": "2021-12-04T16:15:15.658142", "exception": false, "start_time": "2021-12-04T16:15:15.532539", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Data object: Data(x=[3371, 7], edge_index=[2, 7442], edge_attr=[7442, 4], y=[188])\n", "Length: 188\n", "Average label: 0.66\n"]}], "source": ["print(\"Data object:\", tu_dataset.data)\n", "print(\"Length:\", len(tu_dataset))\n", "print(\"Average label: %4.2f\" % (tu_dataset.data.y.float().mean().item()))"]}, {"cell_type": "markdown", "id": "e0d230ba", "metadata": {"papermill": {"duration": 0.11992, "end_time": "2021-12-04T16:15:15.897420", "exception": false, "start_time": "2021-12-04T16:15:15.777500", "status": "completed"}, "tags": []}, "source": ["The first line shows how the dataset stores different graphs.\n", "The nodes, edges, and labels of each graph are concatenated to one tensor, and the dataset stores the indices\n", "where to split the tensors correspondingly.\n", "The length of the dataset is the number of graphs we have, and the \"average label\"\n", "denotes the percentage of the graph with label 1.\n", "As long as the percentage is in the range of 0.5, we have a relatively balanced dataset.\n", "It happens quite often that graph datasets are very imbalanced, hence checking the class balance\n", "is always a good thing to do.\n", "\n", "Next, we will split our dataset into a training and test part.\n", "Note that we do not use a validation set this time because of the small size of the dataset.\n", "Therefore, our model might overfit slightly on the validation set due to the noise of the evaluation,\n", "but we still get an estimate of the performance on untrained data."]}, {"cell_type": "code", "execution_count": 21, "id": "39a5c558", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:16.156484Z", "iopub.status.busy": "2021-12-04T16:15:16.156008Z", "iopub.status.idle": "2021-12-04T16:15:16.157556Z", "shell.execute_reply": "2021-12-04T16:15:16.157962Z"}, "papermill": {"duration": 0.129586, "end_time": "2021-12-04T16:15:16.158089", "exception": false, "start_time": "2021-12-04T16:15:16.028503", "status": "completed"}, "tags": []}, "outputs": [], "source": ["torch.manual_seed(42)\n", "tu_dataset.shuffle()\n", "train_dataset = tu_dataset[:150]\n", "test_dataset = tu_dataset[150:]"]}, {"cell_type": "markdown", "id": "11db2e62", "metadata": {"papermill": {"duration": 0.121176, "end_time": "2021-12-04T16:15:16.398733", "exception": false, "start_time": "2021-12-04T16:15:16.277557", "status": "completed"}, "tags": []}, "source": ["When using a data loader, we encounter a problem with batching $N$ graphs.\n", "Each graph in the batch can have a different number of nodes and edges, and hence we would require a lot of padding to obtain a single tensor.\n", "Torch geometric uses a different, more efficient approach: we can view the $N$ graphs in a batch as a single large graph with concatenated node and edge list.\n", "As there is no edge between the $N$ graphs, running GNN layers on the large graph gives us the same output as running the GNN on each graph separately.\n", "Visually, this batching strategy is visualized below (figure credit - PyTorch Geometric team,\n", "[tutorial here](https://colab.research.google.com/drive/1I8a0DfQ3fI7Njc62__mVXUlcAleUclnb)).\n", "\n", "
\n", "\n", "The adjacency matrix is zero for any nodes that come from two different graphs, and otherwise according to the adjacency matrix of the individual graph.\n", "Luckily, this strategy is already implemented in torch geometric, and hence we can use the corresponding data loader:"]}, {"cell_type": "code", "execution_count": 22, "id": "5bb78c6b", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:16.644269Z", "iopub.status.busy": "2021-12-04T16:15:16.643796Z", "iopub.status.idle": "2021-12-04T16:15:16.645405Z", "shell.execute_reply": "2021-12-04T16:15:16.645784Z"}, "papermill": {"duration": 0.12704, "end_time": "2021-12-04T16:15:16.645907", "exception": false, "start_time": "2021-12-04T16:15:16.518867", "status": "completed"}, "tags": []}, "outputs": [], "source": ["graph_train_loader = geom_data.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)\n", "graph_val_loader = geom_data.DataLoader(test_dataset, batch_size=BATCH_SIZE) # Additional loader for a larger datasets\n", "graph_test_loader = geom_data.DataLoader(test_dataset, batch_size=BATCH_SIZE)"]}, {"cell_type": "markdown", "id": "2146847f", "metadata": {"papermill": {"duration": 0.121317, "end_time": "2021-12-04T16:15:16.886556", "exception": false, "start_time": "2021-12-04T16:15:16.765239", "status": "completed"}, "tags": []}, "source": ["Let's load a batch below to see the batching in action:"]}, {"cell_type": "code", "execution_count": 23, "id": "6bc7f675", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:17.130695Z", "iopub.status.busy": "2021-12-04T16:15:17.130205Z", "iopub.status.idle": "2021-12-04T16:15:17.140406Z", "shell.execute_reply": "2021-12-04T16:15:17.139998Z"}, "papermill": {"duration": 0.134013, "end_time": "2021-12-04T16:15:17.140515", "exception": false, "start_time": "2021-12-04T16:15:17.006502", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Batch: DataBatch(edge_index=[2, 1512], x=[687, 7], edge_attr=[1512, 4], y=[38], batch=[687], ptr=[39])\n", "Labels: tensor([1, 1, 1, 0, 0, 0, 1, 1, 1, 0])\n", "Batch indices: tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n", " 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2])\n"]}], "source": ["batch = next(iter(graph_test_loader))\n", "print(\"Batch:\", batch)\n", "print(\"Labels:\", batch.y[:10])\n", "print(\"Batch indices:\", batch.batch[:40])"]}, {"cell_type": "markdown", "id": "28b5c41f", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.119685, "end_time": "2021-12-04T16:15:17.380797", "exception": false, "start_time": "2021-12-04T16:15:17.261112", "status": "completed"}, "tags": []}, "source": ["We have 38 graphs stacked together for the test dataset.\n", "The batch indices, stored in `batch`, show that the first 12 nodes belong to the first graph,\n", "the next 22 to the second graph, and so on.\n", "These indices are important for performing the final prediction.\n", "To perform a prediction over a whole graph, we usually perform a pooling operation over all nodes after running the GNN model.\n", "In this case, we will use the average pooling.\n", "Hence, we need to know which nodes should be included in which average pool.\n", "Using this pooling, we can already create our graph network below.\n", "Specifically, we re-use our class `GNNModel` from before,\n", "and simply add an average pool and single linear layer for the graph prediction task."]}, {"cell_type": "code", "execution_count": 24, "id": "1c7e611b", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:17.629106Z", "iopub.status.busy": "2021-12-04T16:15:17.628629Z", "iopub.status.idle": "2021-12-04T16:15:17.630171Z", "shell.execute_reply": "2021-12-04T16:15:17.630568Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.127887, "end_time": "2021-12-04T16:15:17.630712", "exception": false, "start_time": "2021-12-04T16:15:17.502825", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GraphGNNModel(nn.Module):\n", " def __init__(self, c_in, c_hidden, c_out, dp_rate_linear=0.5, **kwargs):\n", " \"\"\"\n", " Args:\n", " c_in: Dimension of input features\n", " c_hidden: Dimension of hidden features\n", " c_out: Dimension of output features (usually number of classes)\n", " dp_rate_linear: Dropout rate before the linear layer (usually much higher than inside the GNN)\n", " kwargs: Additional arguments for the GNNModel object\n", " \"\"\"\n", " super().__init__()\n", " self.GNN = GNNModel(c_in=c_in, c_hidden=c_hidden, c_out=c_hidden, **kwargs) # Not our prediction output yet!\n", " self.head = nn.Sequential(nn.Dropout(dp_rate_linear), nn.Linear(c_hidden, c_out))\n", "\n", " def forward(self, x, edge_index, batch_idx):\n", " \"\"\"\n", " Args:\n", " x: Input features per node\n", " edge_index: List of vertex index pairs representing the edges in the graph (PyTorch geometric notation)\n", " batch_idx: Index of batch element for each node\n", " \"\"\"\n", " x = self.GNN(x, edge_index)\n", " x = geom_nn.global_mean_pool(x, batch_idx) # Average pooling\n", " x = self.head(x)\n", " return x"]}, {"cell_type": "markdown", "id": "99675752", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.119943, "end_time": "2021-12-04T16:15:17.871766", "exception": false, "start_time": "2021-12-04T16:15:17.751823", "status": "completed"}, "tags": []}, "source": ["Finally, we can create a PyTorch Lightning module to handle the training.\n", "It is similar to the modules we have seen before and does nothing surprising in terms of training.\n", "As we have a binary classification task, we use the Binary Cross Entropy loss."]}, {"cell_type": "code", "execution_count": 25, "id": "f954ef0e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:18.121084Z", "iopub.status.busy": "2021-12-04T16:15:18.120606Z", "iopub.status.idle": "2021-12-04T16:15:18.122171Z", "shell.execute_reply": "2021-12-04T16:15:18.122544Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.131105, "end_time": "2021-12-04T16:15:18.122688", "exception": false, "start_time": "2021-12-04T16:15:17.991583", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GraphLevelGNN(pl.LightningModule):\n", " def __init__(self, **model_kwargs):\n", " super().__init__()\n", " # Saving hyperparameters\n", " self.save_hyperparameters()\n", "\n", " self.model = GraphGNNModel(**model_kwargs)\n", " self.loss_module = nn.BCEWithLogitsLoss() if self.hparams.c_out == 1 else nn.CrossEntropyLoss()\n", "\n", " def forward(self, data, mode=\"train\"):\n", " x, edge_index, batch_idx = data.x, data.edge_index, data.batch\n", " x = self.model(x, edge_index, batch_idx)\n", " x = x.squeeze(dim=-1)\n", "\n", " if self.hparams.c_out == 1:\n", " preds = (x > 0).float()\n", " data.y = data.y.float()\n", " else:\n", " preds = x.argmax(dim=-1)\n", " loss = self.loss_module(x, data.y)\n", " acc = (preds == data.y).sum().float() / preds.shape[0]\n", " return loss, acc\n", "\n", " def configure_optimizers(self):\n", " # High lr because of small dataset and small model\n", " optimizer = optim.AdamW(self.parameters(), lr=1e-2, weight_decay=0.0)\n", " return optimizer\n", "\n", " def training_step(self, batch, batch_idx):\n", " loss, acc = self.forward(batch, mode=\"train\")\n", " self.log(\"train_loss\", loss)\n", " self.log(\"train_acc\", acc)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " _, acc = self.forward(batch, mode=\"val\")\n", " self.log(\"val_acc\", acc)\n", "\n", " def test_step(self, batch, batch_idx):\n", " _, acc = self.forward(batch, mode=\"test\")\n", " self.log(\"test_acc\", acc)"]}, {"cell_type": "markdown", "id": "c09f6bca", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.120516, "end_time": "2021-12-04T16:15:18.365106", "exception": false, "start_time": "2021-12-04T16:15:18.244590", "status": "completed"}, "tags": []}, "source": ["Below we train the model on our dataset. It resembles the typical training functions we have seen so far."]}, {"cell_type": "code", "execution_count": 26, "id": "48f499d3", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:18.612530Z", "iopub.status.busy": "2021-12-04T16:15:18.612053Z", "iopub.status.idle": "2021-12-04T16:15:18.614039Z", "shell.execute_reply": "2021-12-04T16:15:18.613578Z"}, "papermill": {"duration": 0.128866, "end_time": "2021-12-04T16:15:18.614142", "exception": false, "start_time": "2021-12-04T16:15:18.485276", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_graph_classifier(model_name, **model_kwargs):\n", " pl.seed_everything(42)\n", "\n", " # Create a PyTorch Lightning trainer with the generation callback\n", " root_dir = os.path.join(CHECKPOINT_PATH, \"GraphLevel\" + model_name)\n", " os.makedirs(root_dir, exist_ok=True)\n", " trainer = pl.Trainer(\n", " default_root_dir=root_dir,\n", " callbacks=[ModelCheckpoint(save_weights_only=True, mode=\"max\", monitor=\"val_acc\")],\n", " gpus=AVAIL_GPUS,\n", " max_epochs=500,\n", " progress_bar_refresh_rate=0,\n", " )\n", " trainer.logger._default_hp_metric = None\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"GraphLevel%s.ckpt\" % model_name)\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model, loading...\")\n", " model = GraphLevelGNN.load_from_checkpoint(pretrained_filename)\n", " else:\n", " pl.seed_everything(42)\n", " model = GraphLevelGNN(\n", " c_in=tu_dataset.num_node_features,\n", " c_out=1 if tu_dataset.num_classes == 2 else tu_dataset.num_classes,\n", " **model_kwargs,\n", " )\n", " trainer.fit(model, graph_train_loader, graph_val_loader)\n", " model = GraphLevelGNN.load_from_checkpoint(trainer.checkpoint_callback.best_model_path)\n", "\n", " # Test best model on validation and test set\n", " train_result = trainer.test(model, test_dataloaders=graph_train_loader, verbose=False)\n", " test_result = trainer.test(model, test_dataloaders=graph_test_loader, verbose=False)\n", " result = {\"test\": test_result[0][\"test_acc\"], \"train\": train_result[0][\"test_acc\"]}\n", " return model, result"]}, {"cell_type": "markdown", "id": "ca9673c3", "metadata": {"papermill": {"duration": 0.119315, "end_time": "2021-12-04T16:15:18.853469", "exception": false, "start_time": "2021-12-04T16:15:18.734154", "status": "completed"}, "tags": []}, "source": ["Finally, let's perform the training and testing.\n", "Feel free to experiment with different GNN layers, hyperparameters, etc."]}, {"cell_type": "code", "execution_count": 27, "id": "bc380c0d", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:19.098231Z", "iopub.status.busy": "2021-12-04T16:15:19.097768Z", "iopub.status.idle": "2021-12-04T16:15:19.155078Z", "shell.execute_reply": "2021-12-04T16:15:19.155459Z"}, "papermill": {"duration": 0.18191, "end_time": "2021-12-04T16:15:19.155600", "exception": false, "start_time": "2021-12-04T16:15:18.973690", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Missing logger folder: saved_models/GNNs/GraphLevelGraphConv/lightning_logs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:453: UserWarning: Your `test_dataloader` has `shuffle=True`,it is strongly recommended that you turn this off for val/test/predict dataloaders.\n", " rank_zero_warn(\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/utilities/data.py:59: UserWarning: Trying to infer the `batch_size` from an ambiguous collection. The batch size we found is 10. To avoid any miscalculations, use `self.log(..., batch_size=batch_size)`.\n", " warning_cache.warn(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}], "source": ["model, result = train_graph_classifier(\n", " model_name=\"GraphConv\", c_hidden=256, layer_name=\"GraphConv\", num_layers=3, dp_rate_linear=0.5, dp_rate=0.0\n", ")"]}, {"cell_type": "code", "execution_count": 28, "id": "9db3f0a3", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:15:19.414152Z", "iopub.status.busy": "2021-12-04T16:15:19.413689Z", "iopub.status.idle": "2021-12-04T16:15:19.416116Z", "shell.execute_reply": "2021-12-04T16:15:19.415738Z"}, "papermill": {"duration": 0.133162, "end_time": "2021-12-04T16:15:19.416222", "exception": false, "start_time": "2021-12-04T16:15:19.283060", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Train performance: 92.67%\n", "Test performance: 92.11%\n"]}], "source": ["print(\"Train performance: %4.2f%%\" % (100.0 * result[\"train\"]))\n", "print(\"Test performance: %4.2f%%\" % (100.0 * result[\"test\"]))"]}, {"cell_type": "markdown", "id": "3737c725", "metadata": {"papermill": {"duration": 0.126889, "end_time": "2021-12-04T16:15:19.671142", "exception": false, "start_time": "2021-12-04T16:15:19.544253", "status": "completed"}, "tags": []}, "source": ["The test performance shows that we obtain quite good scores on an unseen part of the dataset.\n", "It should be noted that as we have been using the test set for validation as well, we might have overfitted slightly to this set.\n", "Nevertheless, the experiment shows us that GNNs can be indeed powerful to predict the properties of graphs and/or molecules."]}, {"cell_type": "markdown", "id": "df0d1c2e", "metadata": {"papermill": {"duration": 0.13482, "end_time": "2021-12-04T16:15:19.932515", "exception": false, "start_time": "2021-12-04T16:15:19.797695", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this tutorial, we have seen the application of neural networks to graph structures.\n", "We looked at how a graph can be represented (adjacency matrix or edge list),\n", "and discussed the implementation of common graph layers: GCN and GAT.\n", "The implementations showed the practical side of the layers, which is often easier than the theory.\n", "Finally, we experimented with different tasks, on node-, edge- and graph-level.\n", "Overall, we have seen that including graph information in the predictions can be crucial for achieving high performance.\n", "There are a lot of applications that benefit from GNNs,\n", "and the importance of these networks will likely increase over the next years."]}, {"cell_type": "markdown", "id": "84b8f473", "metadata": {"papermill": {"duration": 0.135396, "end_time": "2021-12-04T16:15:20.206281", "exception": false, "start_time": "2021-12-04T16:15:20.070885", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAByMAAAE8CAYAAABjFR0gAAAACXBIWXMAAAsSAAALEgHS3X78AAAgAElEQVR4nOzdT2ydV3rn+eP3FQLYoVJsZFEgPU4pjYDCAO5QobxLai6dypKKlFQ2Mroj2inImwnEkrwZIl2kqgOtJJWcrGTUlKikoZpFqkr+s0zGZLuzk1xiV23kDiZyKkVNLYKRIkWFALqvB7/X5zBH9+Wfe8lz3r/fD0BI5qXFe+/73vc95zzneZ7nPvvsMwNsZ3F6Y9YYM2sfvmeMWb2wPnlvmx8HAAAAAAAAAAAANhGMRMHi9Ma4MWbBfn2h8APGvGuMWb6wPnmn8AgAAAAAAAAAAABgEYzEMxanN+YVaDTGfGmId+a6ApYX1icfFB4BAAAAAAAAAABA5xGMRG5xeuOIMeaKMaY34jvyUP/fhfXJ5cIjAAAAAAAAAAAA6DSCkR1nS7IqCHlqn+/Ep8aY+Qvrk6uFRwAAAAAAAAAAANBJBCM7bHF6Y3mHvpB7tWaDkve6/v4CAAAAAAAAAAB0HcHIDlqc3pg1xqwM2Rdyr95W70n6SQIAAAAAAAAAAHQXwcgOWZzeOGSDkKP2hdwr9ZNcuLA+udL19x4AAAAAAAAAAKCLCEZ2gO0LqZKsZyp6tes2KEk/SQAAAAAAAAAAgA4hGNlyi9MbCzYQGbIv5F69a4OS9JMEAAAAAAAAAADoAIKRLWX7Ql4xxkzX7BU+tM/rCv0kAQAAAAAAAAAA2o1gZMvYvpAK9h2v+Sv7VBmb9JMEAAAAAAAAAABoL4KRLWH7Qqok61LDXtGaLd16p/AIAAAAAAAAAAAAGo1gZAssTm/M276QX2rwq7lug5KUbgUAAAAAAAAAAGgJgpENZvtCKgjZa8lLemh7SS4XHgEAAAAAAAAAAEDjEIxsIFuSVX0hT7X0Jaqf5PyF9cnVwiMAAAAAAAAAAABoDIKRDbM4vbFse0N+oQMvd80GJe8VHgEAAAAAAAAAAEDtEYxsiMXpjRM2G7LJfSH36m2Vo6WfJAAAAAAAAAAAQLMQjKy5xemNIzYI2Za+kHv10AYkrzTz6QMAAAAAAAAAAHQPwciasn0hVZL1TNffiwHrKlNLP0kAAAAAAAAAAID6IxhZQ4vTGws2ENmFvpB79a4NStJPEgAAAAAAAAAAoKYIRtbI4vTGrDFmpaN9IffqvMrY0k8SAAAAAAAAAACgfghG1sDi9MYh2xfyeNffiz361PaTXGnkswcAAAAAAAAAAGgpgpEVsn0hVZJ1qbNvQlhrNihJP0kAAAAAAAAAAIAaIBhZkcXpjXmbDUlfyPCu236SlG4FAAAAAAAAAACoEMHIktm+kApCTnfqhZfvoe0ludy1Fw4AAAAAAAAAAFAXBCNLYvtCKjB2qhMvuD4+tVmSN7v+RgAAAAAAAAAAAJSNYGRkXl/IBUqyVmrNBiXvdPg9AAAAAAAAAAAAKBXByIhsX0hlQ36ptS+yed7WMaGfJAAAAAAAAAAAQHwEIyNYnN44YvtC9lr34trhoQ1IXun6GwEAAAAAAAAAABATwciAbEnWK/SFbAz1k5y/sD652vU3AgAAAAAAAAAAIAaCkYEsTm8s0xeysd61/STvdf2NAAAAAAAAAAAACIlg5D4tTm+csNmQ9IVsvvM6lvSTBAAAAAAAAAAACINg5B7RF7K1HtosyZWuvxEAAAAAAAAAAAD7RTByRLYvpEqynmnUE8eo1m1Qkn6SAAAAAAAAAAAAe0QwcgSL0xsLNhBJX8juuK5jTj9JAAAAAAAAAACA0RGMHMLi9MasMWaFvpCd9dCW5KWfJAAAAAAAAAAAwAgIRu5gcXrjkA1C0hcS8qnNkqSfJAAAAAAAAAAAwBAIRm6BvpDYxZrtJ3ln5x8DAAAAAAAAAADoNoKRAxanN+ZtSU76QmI3121QktKtAAAAAAAAAAAAWyAYadm+kApCThceBLb30PaSXN72JwAAAAAAAAAAADqq88FI2xdSQcjjhQeB4X1qsyRv8p4BAAAAAAAAAAB8rrPBSNsXcsEYs1R4ENg79ZOcv7A+eY/3EAAAAAAAAAAAdF0ng5H0hUQJ3jbGLNNPEgAAAAAAAAAAdFmngpG2L6R6+/UKDwLhPbQBySu8twAAAAAAAAAAoIs6EYy0fSEVhDxVeBCI71NbunWV9xoAAAAAAAAAAHRJq4ORXl/IBUqyogbe1blIP0kAAAAAAAAAANAVrQ1G2r6Qyob8UuFBoFrn1bOUfpIAAAAAAAAAAKDtWheMpC8kGuKhzZJc4YABAAAAAAAAAIC2ak0wkr6QaKh1G5SknyQAAAAAAAAAAGidVgQjF6c3lukLiYajnyQAAAAAAAAAAGidRgcjF6c3Tqj3Hn0h0RIP7flMP0kAAAAAAAAAANAKjQxGLk5vHLFBG/pCoo0+Vclh+kkCAAAAAAAAAICma1QwcnF6Y9wGIekLiS5Ys0FJ+kkCAAAAAAAAAIBGakwwkr6Q6LDrtp8kpVsBAAAAAAAAAECj1D4YSV9IIPfQ9pJc5u0AAAAAAAAAAABNUdtgJH0hgS19arMkb271IAAAAAAAAAAAQJ3ULhhJX0hgKGs2KHmHtwsAAAAAAAAAANRVrYKRi9Mb6gm5TF9IYGj0kwQAAAAAAAAAALVVi2Dk4vTGrDFmhb6QwJ6on+TyhfXJK7x9AAAAAAAAAACgTioNRi5ObxyyQUj6QgL7p36S8xfWJ1d5LwEAAAAAAAAAQB1UEoy0fSFVjvVM4UEA+7Vmg5L3eCcBAAAAAAAAAECVSg9G0hcSKM3btnwr/SQBAAAAAAAAAEAlSgtG0hcSqAT9JAEAAAAAAAAAQGWiByPpCwnUAv0kAQAAAAAAAABA6aIFI+kLCdTSu8aYBfpJAgAAAAAAAACAMkQJRtIXEqi988aYK/STBAAAAAAAAAAAMQUNRtIXEmiUhzZLcoXDBgAAAAAAAAAAYggSjKQvJNBo6zYoST9JAAAAAAAAAAAQ1L6CkfSFBFqFfpIAAAAAAAAAACCoPQcjF6c35tVzjr6QQOvQTxIAAAAAAAAAAAQxcjDS9oVUEHK68CCAtvhUWc/0kwQAAAAAAAAAAPsxdDDS9oVUEPJ44UEAbbVmg5L0kwQAAAAAAAAAACPbNRhp+0IuGGOWCg8C6IrrNihJP0kAAAAAAAAAADC0HYOR9IUE4Hlorwf0kwQAAAAAAAAAAEPZMhhJX0gAO6CfJAAAAAAAAAAAGMozwUj6QgIYgfpJLlxYn7zDmwYAAAAAAAAAALayGYxcnN44YYxZoSQrgBG9TpYkAAAAAAAAAADYSh6MtGVZP9zicQAYBgFJAAAAAAAAAABQ8Nz/8es/HTfGqMzilwqPAuVYpz9p4z00xhy6sD75oOtvBAAAAAAAAAAA+DeJMeYEgUhU6PqF9ckjxphPOQiN9gV7LQEAAAAAAAAAANiUEEBAhRSInF+c3jhEQLwVZrv+BgAAAAAAAAAAgGcpGDle+C4QXx6ItL/lCO93Kxzq+hsAAAAAAAAAAACepWBkr/BdIC4/EGnIqAMAAAAAAAAAAGinhOOKkg0GIg3BSAAAAAAAAAAAgHYiGIkybRWIlOnCdwAAAAAAAAAAANB4BCNRli0DkYvTG2RFAgAAAAAAAAAAtBTBSJRhy0CkRTASAAAAAAAAAACgpQhGIradApFypPAdAAAAAAAAAAAAtALBSMS0WyDSkBkJAAAAAAAAAADQXgQjEcuugcjF6Q1lRX6h8AAAAAAAAAAAAABagWAkYhgmI9JQohUAAAAAAAAAAKDdCEYitPUhA5GGEq0AAAAAAAAAAADtRjASIa2PGGAkGAkAAAAAAAAAANBiBCMRSh6IvLA++WCYf29xemPcGPOlwgMAAAAAAAAAAABoDYKRCGGkQKRFViQAAAAAAAAAAEDLEYzEfu0lEClHCt8BAAAAAAAAAABAqxCMxH7sNRBpyIwEAAAAAAAAAABoP4KR2Kv9BCKlV/gOAAAAAAAAAAAAWoVgJPZiX4HIxekNsiIBAAAAAAAAAAA6gGAkRrXfjEhDv0gAAAAAAAAAAIBuUDByjWONIYUIRBr6RQIAAAAAAAAAAHTDAY4zhhQqEGkIRgIAAAAAdnHIfsWyygEY2nKkf3fFGHOv8F10gSpmjUd6nXeMMSHWrgA0R8wxA9cUIBCCkRhGsEDk4vSGBpxfKDwAYC/0eTphA/y9hr6DO2XnuwWiB3bwd4/Fik6Yt19ongX7WQXQzWt3rAUgghXdpfNqKeKrf67wHWwn1nFY5fPdWVcizmFfZbMB0DkxxwxcU4BACEZiN58GzIg09IsEgjrhBlv379/Pv5pkbGzMTE1N7TQBLTyWZdnjJElu24HgKgPCVjq01bFHI8Ta3Q6g/uYjXrsJVnTcBx98YDY2NoK9CceOHTMTExOF72Nnn3zyibl06dKOPzPKMZibmyt8H93zzjvvBHvNr7zyipmZmSl8H0B3hBwzcE0BwiMYiZ08VLAjYCDSUKIVCO/NN980t2/fbtU7e/DgQQUq878fPnw4D1xqIDgxMTE2MTHRswuebtfbu8aYm/aL0hkt0cbzuq1Onz6dfwGArt2hEKyA8/777wcdE9gxZeH72Nnjx4+DHYejR48WvoduChmMFAIHQLeFHjNwTQHCIhiJ7Ty0GZGhy60RjASwq0ePHm0OIN2fbqI6OTmZL2C4r4mJiePGGH1dM8Zct+XcyJgEAKBkIRd/CFYAAAAAQHsQjMRWogQiF6c3VHrvS4UHAGAEKrmhL+14E2VQKntCX2NjY6eMMaf6/f6P0zS9ZAOTAAAAAAAAAICKJLzxGBArI9LQLxIILv9MNa1XZGiuf83s7Kw5f/68+fjjj02api8rUzLLsn+wvTUBAAAAAAAAABUgGAlfzECkoUQrENy4sZmC+JyyJdW7Tj2rFJRMkuQlY8wPbNlWNkQAAAAAAAAAQMkIRsKJHYg0BCMBlEU9q/ygpDGmZ4z5oTFmmYMAAAAAAAAAAOUhGAlTRiBycXpDGVzThQcAICIXlLx8+bJ5/PixftFSv9//EVmSAAAAAABUapy2KgDQHQQjUUZGpGHhH0CVbty4YY4dO2bW1tbyfpJZln1kjJnnoAAAAAAAUCrNxW8aY/4/Y8wCbz0AdAPByG4rKxBpKNEKoGqPHj0y586dy7MkkyQZM8ZcM8ascGAAAAAAAIhKGZArWZY9snPx47zdANAtBCO7q8xApCEYCaAulCX52muvubKtp+yOzHEOEAAAAAAAwahK2pV+v/+PxpgfaP795MmTsQ8++CCfkwMAuuUAx7uTyg5ESq/wHQCoyCeffJKXbb169aqZmpo63u/3P0rT9MvGmAccEwAAAAAA9uSQsiCzLDubJMlL+gfSNDUKQK6uruZfAIBuIhjZPaUHIhenN8iKBFA7Ktv65ptvuoDkywQkAQAAAADYk3lbijUvv5okiVlbW9sMQGr+DQDoNoKR3bNQckaksWUZAKB2BgOSxphVrlkAAAAAAOxKWZDzNgvyoH74/v375rvf/W4egNzY2OAdBABsIhjZLa9fWJ9cqeAVkxkJoLYGApLTaqpvd3UCAAAAAIBnjasXpHpAGpsFqTKs77//vrl9+3bhhwEAMAQjO6WqQKQhGAmg7gYCkppQ3TPGLHPgAAAAAADYNG5bnLzssiAVhKQMKwBgN8kuj6MdKgtELk5vqNzhFwoPAEDNaPK0vLxsHj9+rCe2xEYKAAAAAACesaBApPpBHjt2zNy4cYNAJABgKAQj26/KjEjDYj6AJvnkk0/ygKRkWfaeLT8DAAAAAEDnqT+kNvC6eTMAAMMiGNluVQci5UjhOwBQY2q0r1IztgF/1ddQAAAAAADqYFbzZM2ZyYYEAIyKnpHtVYdApCEzEkATXbp0yRw9elT9I48bY04YY25yIFGmqakpc/DgwUa855OTk4XvAQAAAGidPOHg9u3bHFnU0uHDh4M9Lea5QHgEI9upFoHIxemNQ8aYLxUeAFB76v0wMTGx7dNUkGS3QZ7+/+3+jY8//rjwvbt3727urnSTmyonOQpIXr16VWVo/iJJkl8xxjwo/BCiOXfunOvfWXs6d3W+hPTWW2+ZmZkZTjAAAFALGpfcunWLgwF0W97GZGNjo+vvA2rq7NmzHBqgxghGtk9dMiINWZFAcykYGSgQsjb4jX6//8szMzMvD35/u9+nPo4KSupL5WDKot+ncq0nT55UetqCMYamGCVSZiBy5xv0NtwrfAcAALRBYUwfCJv9AAAhxFwsYp4LBEIwsl3qFIg0BCOBVnh1hxexp8FemqaF71numjFuy78ou/rQ1NRUT4GpkydPmvv375t33nnHvP/++4X/OQb9LgVmn3/++bfSNF1hEFqK5YYFfj8rfCcsguAAAKBqzO0BAHW2GjkgCSCAhDexNeoWiDRMWIBWWN3hKzT3u27aAMy8vY78O2PM7xlj3lXZ16WlpTwYOTsb/xKjsrE3btxQAPUXCQoBAAAAAAAAwOgIRrZD7QKR9IsEENADG6A8YYz5VWPMdQUlL168aJaXl/P+lTGpVKt6F/b7/T9wPTIAAAAAAAAAAMMhGNl852uYEWnIigQQyT2bMflqlmU/mZubM1evXo0akBzIjlwo/AAAAAAAAAAAYFsEI5vt+oX1ybqWDSQYCSCm1SRJft0Ys6Z+krEDkh988EH+Z7/f/1rhQQAAAAAAAADAtghGNpcCkfM1fvYEIwHE9sBea667gGQsGxsbZm1tTdmRL9pysQAAAAAAAACAIRCMbKZaByLpFwmgZPMuQ/LcuXPRfvP777/v/kowEgAAAAAAAACGRDCyeeqeEWnIigRQgRPqIXny5Elz9OjRKL99dXXVPH782GRZ9vuFBwEAAAAAAAAAWyIY2SxNCEQagpEAKvAgSZI/1K9dXo7XSlcBySRJDnKdAwAAAAAAAIDhEIxsjqYEIg2L9AAqsqpr5cTEhDl9+nSUZ3D79m33V65zAAAAAAAAADAEgpHN0KRApKFfJIAK5WmRr732WpRnQDASAAAAAAAAAEZDMLL+mhaIBIAq3dN1c2xszBw7diz409jY2DD379/XX3uFBwEAAAAAAAAABQQj641AJACM7or+j9nZOMmLn3zyifvrkcKDAAAAAAAAAIBnEIysryYHIh8WvgMA5bmTZdlPer2eOXjwYPBfevfuXffX8cKDAAAAAIA2GbdtOga/2JwKAMAIDvBm1VLTMyJvGmNOFb4LACVJkuT7xpgzyo58//33g/5SLzNSE9DVwg8AKItLfx5Mg/b/e/AzurrN94FQZu2ipVugHN9msfKOMebBwN85L+M5Yr8OuWtEv9//5TRNXx74jev2WDywx8V93WvRewEgnnHvejPuXW9+LU3TF7f5re66c89+rQ7cI1AuF3A8kmXZTJIkL+3227Mse5wkye2B+8adwg8CALC73eaS/pyxceMFgpH1824LSrMuE4wEUDFtijgzNTUV/Fk8evSo8D0AUbkBeL44tMuC3qDB/q5L7i/9fv+naZresgP41ZYEgma9YOxWE5f9WNjHwpoLArnnc8h+bWdwUvXAXtfrFhDyz8tXRjgvzRbnZs47L1e9BWmMTufXCfu15Xudpqn5+OOPN/9b/aanpqamvR857v6SZdkju9Fp1Z6LBAnQJaHvJ757DQ/2H/LuA7+z3X3g5z//ub+hcdPAdcddq/KxSr/f/3Gapn/jXXdGeU473WP3o+nHaztagzuRZdlXkiQZcz+TJMnmfeLWrVuF//Xw4cN5JZ6JiYmxiYmJnn+/8e4bN0c8fjGEPCdinFtHShiHx+nhEs7KHj5bux3X7TZltnWzQ8xrX9Pes+XCd3Z3ZIfqX1td+1d3eAzDc+MIt5Fpy3nLgKXCd4xZs8fhjre+sVezEa+Z9whG1su6HQQ12oX1yXuL0xtvKxDQ9QMKoDL5jVcTxNAGMiMHaSDx94Xv7oNdmP47+y/cswGBNkwe9F4tZ1n2+0mShK+na8yvMihutG0DCW5BTyWTtTlAf/c3Cbj/1uKQvyFhcnJSi0X5n/qamZnRguGLNuDgBvTveot+TTx/ZreZnISw3eR0K7P22OnP6S0e381Wk7DxPU6sQzthFyyfuXa5wNbGxkb+5Z+Xg+eoHD16NP/Tnaf6U/esgfPSZVt8jyDYUHSOzPf7/T/yMx7v379vbt++vXnd0H/rGO3EXS90nHRcjh49enBsbOyU3XB5zV4rrpDNio7Q4tiHkV7q+Zpc20eRj1EGrzXuPqDrjK43g/eC3eh64+4Jr7zyiu4H+rf1dcYGtr5jrzu7jU8WIq7FNPF4bUfHcSHLsjfc/fxnP/tZfr9wX7vdKwZ594xn7hvevXylovvGfMTxYQhf2Gbs1yUxEjq23ZTpZfOueoGLpo8xY57nrzZszBf6fdjq8zn4O/xgGBsqd6Zx1bydTz6Tfa95ir78DTC6Hzn+GoebP9qNTW5DjH8tWbPzx1GPx4mI44g1gpH1oUDk7IX1yVYsMFxYn1xYnN4YJ0MSQFXUN3JmZmbXsjqj2mVB4Z5doDyuxYfHjx8XfmBUU1NTL46Njbld1r1+v380TdMvN3yyoLJHH2nn8ZMnT7bcKb4XWjjWlzcQRrNsG0hYXV3dDCYMuzCkz6o/cPf/7ijgYBeM8q+JiYnjNgj0LTs2u9LEANDly5fzxdCtXvOoTp8+nX8NobCoZ2wgbqtF2Z2emwvUGbup5OzZs4WfKdkR+9q+6jImdO1yi5WaLI56HfNfv85vnyaYbjFzdnZ2zFvM/POKFzPr6pBfmUUBAR2PDz74IH9vR11MFhdU9o+TjsuxY8d0TDavFRprJEnyDXtMgFZToG2r7LC90P13bm6uaW/XCRvoyxdl3bXGjVF2uq8Nw/3/+vfeeeedfJHR3gf0pcDWGbs4uGavedvdB/IsVv0boTT0eG3nmXuG7ud6z2/cuLHvOYk7D/RviT12z9zLbcbrpSruG7ov7uWeuBWNz0NQ4D0mfQ5mZmai/o4A8s/s2tra5rkzDLcpcyuDmzIHN79tlc3rBS5WmrzWEPI8txtDCt9vAp0bly5dGvqZ7rRZz507jn9+6T3aKhhmA95/42WHd31D5bjdyHTOrXW47HuNrUYZRwzOHR23IUbHxq5tbH7G7Zzl+/bzvVtgMr8mvfnmm4UH9uPq1av5/00wsh5aFYh0VG52cTq/kBGQBFC6JEn+H2NM8GDkEG66YOTyctiNw/r35ubmXrYDiBOFH2iGQy4QqclhyIUS+/4YG0BCcxyxi3vBAgnDcgEH11vWZSPoPLJl0655AaDlpgS5QwUizbOLTdv1yd1yUc8tzu6ygWNLoZ57APP+hNF/bdtNAkNwAVz/vFQQ7NixY35gkiDYwLmnDUBaxAu5COVzizr60uLya6+9ZuymJ10nvpkkyR9u8xkBWkGLZaHGbVoka1Bwa95+xvN5he6L3/3ud6OPUXT/dPcbLfzquqPNQXZx8UMbONi2hHrIMXbDjtd2XIWFPNvDHUfda2O14fDHC/Y+7jJer/X7/T9N0/RPyryP67XWaIyFf5NXHwk5fh/clGm2CGC4DQ9u04MXuNCmzOtN3fwW+jxvajBS4+JQ78NWQW93Pvn3GjeX1p92E4bb6HutyefUPo3bTa1ntWHXVVHQear3MOT9ZzCgqY1E+mzrM97r9V6y978zdlPM/7nbxoNY9wuCkYiKgCSACilo0NONt+RJ10qWZX+mHcwa4IccXGgBVAO7qamp4zsEBupuOUYg0u3YtmVtq+7LguHM2kWhfLeeFoU0KI8VSBiGCwIpoLFVAMhOohoTlAxhh2NRWNTTZzr0pKoimwvP/oTRBQfLtlMQrIrFzBrY8twr8/i4BWbde7QRxh6PD211hPmdJvYAGkMluf9M9wJlL7ispSqCObqvuvuQ5jbKVpiZmdH46YfGmLftNZHrzvZ0LP9CC8FaoNc9Y5QMtBDc8dP4Uvfxubm5F9nMgir5Gx40xnTzac1/JiYm3Nxnt0xsYJObSzsDVUUqzw6vwLILQrpNrbr/7DC/Dkq/R/c6fblNTd6mmG95Gw+u+Bub+v3+r6k1TixJyw5yU023+UOogKQdIANAmSoLFqini0pV6GYfkiYMXqmNJvZrURbLKbdwHJLbrZ2m6cVqXyKGcMhOaLVw39P5cP78+XxgXObgfDcuAKTnpednMwRP2b6wKyP2UGwblVr+HwoGaVHPHb+Y2QUlme33+z/S4qAWnxUY14KvMlGqCkQO0iRWz+d3f/d388B9mqZuMfMfGpwxP4oT9rUWzr0q6Hql46HzxC6+5KVbO3IsgLZy45QfuHuBrrnnzp2rRVaZnoOuO97Y5Iy9LrqJR2+rLJaOGrdVa3QsDyoTUveMsgORPlc9R+eUAtw24/ZD+zy7PLZExTSm0VzMzX20Gc9uGv3QXhOPcIwwCn8+/dZbb+XnlK04c83OucIumNXHrL0vL+ne4z5XuvZXtdbhNjW5eaSek20rdcpubFp1x0Pzy5jjCIKR9XF8cXqjzQFJlQ95vfAAALRTfj0/efJk8Bfn+ubZicGhwg/Umzan5CWRQtMOY4u+XfU1bnfd/f1gELIugZ6tuIH7YFDSBhwWtvhfWsULLrrrjT7HP3QLtHU/fkMat9eODzVB1kRZ1xRNGOtazkwT2S0WM39gJ5JNuzcMY8sF5bqcezpPdM5oYm/7iv6AkuFAIy27cYruBdpoUOXi4U50/dN1R9dD26v5Q3fdCdG33uctSjZp4XjWbpw6rrGbjqUWxeuyaUrnlALcel52bHncPl8CPqicC1p4m61cJnbXN2Rij9yGSp1TXlCybRsx3HrHh5qbaY7mAn912rDrNh4oeULrG95n/MOnT5/+98L/EBjByHo51fKA5AoBSQAdoRIHa65xdGheMK9R2ZH9fv9rxk5uQtKCtDJRbYkJSlTV0+x2mXRN4hb+NHh/8uSJTrpv2V2drV048hYgD9kFiGvuGGqBtgUlWV2mXZ61rV27mqg9p3AAACAASURBVCg3JavEX8x0E8ksy37UskC5O0a1XFD26dqg52cDAWfIdAEa45BdgFvS5/fy5cv5vaDu/fVc1ZSB606U39MwK24xWBunNHar67F0m1n0PO3Goh+6DZzojNpuInPn58CGzK5U40AELrt/YCPGP7QgS/JIv9//yK13aE6pOVodNzP53PqGCxIfOHDgN83OrVr2jWBk/RCQBIB2yK/lCriEpl1lGuBkWfbVBi1ynlC5B020Qy9oeO8xWSj1dMVfEGp6Jp3OXwUcNGj3dnX+sANZktoteUrXnjqVLd2nK36mnY6prq9NtEVm3rdaFAg74x+jugcH9Px0nXNlW+3CBAFJoL60YepHWoBzmfFVlvHci4HrTu0XP0vwJbfBqAkbp/T89DwV8LFB5WsNbcmBvcmDkXUe32yRif0DsiSxH27uMpDd39Tr3nyWZR9pXUDZkLofN21O6QeJNRYiGNk9BCQBYP/ynVUVZres9Pv9n6qXoZrBh+SazdsF56bsSsx3+IYOYCj7dGZmRlmXP/abbqMWDtmswXx3oCt11oJMupzrFafsCbtw1Kbgz5ZcILIFvajG7fVi89ysa6bdqFygfCAQ1ujMXRccaNIx0vN02apamCAgCdTWvN0wNabrp+5xTQ3k6brjMuy6HIz02wA0bTHYlca048ol2k+gTvxMbJcl2YZxJqqzRXb/UgPn0wqgXtM4QusCyoZs8pzSBSVjtFZyCEbWV1cCkg8LDwJAQKEHAl7Z1XuFBwekafptfUcBydC0YGI+L316rgHnyyFXXi/0jk/XKzJN00uFB1GlPMvA3x1Y92ymvVL2hBega0XwZ5BeW4sCkUdsyeBpvZY2npt6XZrU67Onz6B26jbxnNQ9w5VKbOJ5NxiQtIsrAOrjmdLjbmzddNr4pYBkF7WhAofuGV5A8hQlW1E3LqOt6eNM1Ic7pxpYVWTFlXfXmL9pVRV2EjOgSjCy3roQkJwlIAkgkp7dsRfLrsFIVzbUBcxC0o5nr0Rk3Qf/+SQ69O6qgwcP5oHeLMsesXO4Vp7JMmj67sBhuOCPFsHaOCnXtbRFgciP/B5SbT039br02bOlj8aaeE5qMbnpk3o/IGlLHXOvAuphpYWlxzd1NTOyLa97ICB5jf58qBs3zvTaA9DrFPui63fDqoo8M45o68brGAhG1l/bA5J3CEgCiCBfcI2xcH748GH312GCkQ+MMdfHxsai9I70Fk5q3auu3+9/zUQo0ere0yRJLhceRFXm7aJJq7IMhuH6/bhJuQ3+NL0Rf06vq0WByDEFIvWaukClj/RZbGpAsg1cQNKVNGOxDqjcMwuILbi/oYV0XirYI1mW/aXrKwjUieY9Gmda1xjjYD/cmNlt8K1xQDIfR+g67WV0YkgEI5uBgCQAjCYPAMTYnTQxMeH+Okww0rjsyJMnTxYe2C8F97wSPnXdNTafpumLGlCGzkDy3lMyTeph3pU706C8bVkGw3KTcrtL+MM2TMpbkD24GYjUselKINLRZ5GAZLVcBoH5fFH5z1lUBipDIHIPRmlTgXA0l3Wb3J4+ffpfeWtbK1+7aOr1yI0zrWtt2YyJargNvq4McA0DkpuBSAVOu9yjea8IRjYHAUkAGF4+AL5161bwt8zLjLxTeHBr+rm1qakpfyIfjBfwqWvAI39eoQNTs7OzLjD8LgsjtbAZiGRx79lJuQ08EPypzqEsy/6bFvLUf7CrQXK9br1+G5B8ryF9WFpF10WvnBmbaIDyzROI3DfG3CXTfUOtOQ4cOPCbda+Gg/1p8ua/gbnP+8x9sF+uMo9tS3SlJm/oghtHvPXWW61vRRMLwchmISAJALtTtsFxlUOLscigoGK/3/+pLcE6rPzaHaNUq+vDmGXZ2cKD1dOx6Ok4hM5S9fpw1mVg2mUEIrdANloYk5OT7t8Z5ZrrjPf7/feTJDmo7Oym9x/cL71+vQ/qmWmMudnsV9NMWlT2+kdSygwozwnGKmgqV9Ehy7L/wmYi1NXA3IeNb9iXgb7rqgRWdWkbjSO+5cYRZETuHcHI5iEgCQA7yxf3XJAuJAUi1f8xTdNRUy5XFMCcm5vzF9aD0CBIJSzs4nLdSqLku3dDHwu9hzMzM5qQ/8QYs1r4AZTpiOsRyeJekZ+N9tlnn60xKR+dVxp72Gx034p203apR+RuvP6fvRpM6jvJnYv9fv9PuSYApThke+61pQcyOkbzPS+znns3aktzH2/jG1UgsC+uZKttTbRU4XrXETeOUNsFxhGj89ZB7xGMbCYFJO8sTm+0cvJKQBLAPowrQ1CDlRil+F555RX315EDYGmaXtSfCkiGVtNSrToWb+hYrK6GjRe6XpFJknyj8CDKlJe/1O/TLlgG5Vtz2WjPPffcL9meFyiHFuuO67y8dOkSb7lHZYVqMKnvLJ2TuiaonzIl94D4bIb8mII5ocekQFm0udPeu8+wkQV15m36OM44B/ulc8nLDv+LCq5/qrTzl67lR+iKX13hbTAmGNlg01oMJyAJAM9YUDk+Lf7HqN/u9Xzcy0qGsiP/xSsvGowWVlSW1pavqMt94YSOhZ5byGNx8ODBvNxtlmWPKDNYLVf+UosjXe3DN6ya9ryoPX3WvWvmKGVaNYZcop/H1pRhUfGkvvMUFDGfX0e/1vX3Aojsiu696rnnPndAE2ks45WbJ8DTIv1+/9dsoLk1BkoLH+r6Mcb+aE1Jaw4247bs7PBljSNUjazrLT9COdCOl9FZLiA5e2F9ci99dGpNAUm9Nrvo/4WuH2wAuzriFp9jlGg1Nhipcqtpmu6lXOCDNE3/amxs7JQW2EMHb/TvqUymnZxWXr6n3++fS9M0+MLP7OxsXirXGPOdPfaQQxjLbnGvTllnKqWsgLWjIH1d+jmo54U+p2NjY2dsIJ30jG3oWqvPusuCNsa8O0KZVmVl/0WSJPlCSB2O/+B56VOQuopgqSb1mlT3er2X6nLfiEVlgbzduPn7XXUmt85LZUfOzc29aKsaUMoMCE9rCWc0N1BZtaro+q/7gNvUqP8+fPhw/nddj+7evZv/XdclfdGHqho6Rjouul/oz8H79q1bt/LMRB2vqjJjNMfVfE+VgJIkGfm+rTmot7l2aK7qUMzxiu7V3jwvGP/+X1eqlNC2CjN6PVoHOH36tCstTJ9s7IvOJ10jJiYmypxLb44jqmz54cYRujfpGtn0cQTByOYjIAkAtnSCgl8aJMSYKHmTo78uPDg8jWBOaRIZOhipRU39u8qySNO06kXlWReoCj0QsgFXQ3ZZpTYD/1Ut7mkArs+kG5Srh+hO9Fw1MNdCkv6sokybrkt6v65evarP6X+1WZKtDqhrYUmLXu44ub67I3p7xEDZsnbN6ppYxXHWa1RJb/3pvnaj81MLm3q+obPJd6L7pQ2QL9lg2L0dfrwR3GKmO+92WoT0rwt6/8teXNbualu6fYFgJBCevzGl7E0fuv64wM9u94Fer/fMf7sWB7omUXkiLh0fHadhgmCDY03Nc3R8yrxv6/fYjSyKlJ4YtUrMftqFaA4Wqz+8jsHS0lLh+x3R2uoUCh7p2E5MTJyy4xw2YpajleeUrn/aBH3x4kXz9OnTPz1w4MBvFX4osKaPI9xX3RCMbIeuBCRX7GsFgEErWtiPufisSaq1n9KgWuhdm5iY6GlQEXLhcyDLYuTJaWD5zsfQCyh6z+zC8lobFs2bKnbgfyduwWhwwG0zlv/OBvf8DLr8g/vCCy8cnZmZGfMXkpQV5haRyqLPvHa1nzx58kUbYGtdmS1XSlkLXttMnNYK3ylyx/HmCBmRxt+9WmbGrgJgyuK0u3UHH17b4rx0tFhwROdnr9cb03mtxThdy3Vuxg6O6fOrhaKzZ88au8HjROGHGkLnnI7BFuecO9/8D7o2VIy/8MILMzMzMwfddUHnjQKE+oyWcW1zu5enpqambQkz7mtAOPnGFN3ry7zP61qkgM3AvWDN3gPueJ9z96SO2HvBuP277glfmZubG9N9VJuYdD/QdYmMyXA0p1DFCnf9z7JMNTL/xh4Xd5z8a7I7PrpWz2ZZ9tszMzMv6f/XMSrz3qHz2QYVR5nv7TcQNKuNSy64FPJ1agylsY+OQZIk/3mb8VKbHWnzi9M5YwPNy/QpL01rzyld/7QRZGZm5jdLqCxSp3HE+sD9yXjX9EP2a8txhMtsr9M4gmBke3QpQ5KAJACfBiDHtaAXa/FZi+u6kduAx36DfFrw7akPWuiFZg0y7OR0vsJgpAZCp1QeM3Qw0usdR1ZkdTZ7JpQ1KNfnT0EGHX+3az3Lsp8kSfJ9V6JFwdGdaEejHaBrLHGi3+//Tq/Xe1HBH52rmiiXlX1QUYmZ6LSYpInTwK77de813omdCdrv9/+8zED54GKm9a59vavDLqjZ81OTx/l+v/8H2lSi91FBSd3XYr4WLaDq8zUxMXHcfj4adT5uMWFf9xZdd3z/7ft+yL5uLbTmlQt0rdH7UkZ/OR1jGww+wb0NCOZQv99/6+c//3lpG1O2uBat2WvRzV3uff51Kh+722uTrkvzL7zwwldPnjw5puu0FhJ1XaIP8t5pTKkxgrepLT9OSZLstqD9wLs/rvj37eeff/5rp0+f/kWdA/q3Y28k0vhbi8s6N5IkGbb05WBwdeRfq3G0Wg1oDBtyzOxK8idJ8scNqRLwWeE7AWgsqUoNo9AcRl+DXNnGOpRsdK1ktBmbjVdb0rX+w60e2K+9nFMK9G1F55POqyoqiQxSlSF9ZVn2zSGu3Xulth/nnjx50oRxhH99b8Q4gmBku7Q9IPmAgCQAz7i92fY0yNaCcKybqgYG5vN+Dn9VeHB0NxVIUZ8uLd6HnBxoYKgJiV1UrmqwHyUrUu+VFg5sEKrKrM8uq2RQrh3nXums61qwT5JkxyDDNh7Ya8ZNG7zMB+gqHaQduxr8l7GIVEWJmdgUvNH7p+OUZdmjJEkuV1D2c96Vh44dKNdips5LF3i116VvDDFZ3InO6YU0TZUtO68J9tzc3Eta9NN5GfM1NXHXuo6BPkdeIPi6ff6jnnP37Lm6YjOVF1544YVz6nHk3vuYfZx0XG0wcp5gJBCMNk79ojYVxF6E1/hU14kA16JB+aaWJEny65J6BJ48efKgC3jVsexa3SlzXvcNLfTa+/Yf7nMDjrtv63hf0XhSC+S6p8bezGKzI8dK3kSkMc6ZLSpA7Ivre1ZxVZ+RuDLvIWjsbKs6ZMaY/2mM+X+H/We/+MUvHp2YmNiyvrBfRUbP1VVnqSIwOTDOpHfkFrYLLO/FwDn1o2HnJlmW/XtlfBce2KZMta5DsXvJbkXnss2OfCniNVDrDWOMI+IgGNk+LiB54sL6ZOt2nBCQBGDN2p5rL8YORBpvx2aohUK7UP8t/buhgzqaZHgLm6X3jrQ9K/PnEZJb9LfvHapR9aB8IXBmnctec4tIx7WIpKzP2Jl1JZeYicbPMOj3+/9ijLmYJMmVKnph2t2xeZA3JmVD6rptA695EDLCrtwVu+liQWXR9JouX76cZ+vFMLBrvfbZkf6Ccr/f/3Gapv8pUFm3B7Ykk87hlampqeNaQIvVI8vYEuteqdbxtveRBUqQV+hQsCD0WHSQ27Cg+8HTp0//9sCBA/97hBKT/nVp855QRuZ8m+i+oeu53dx23S7OhrrePnBVabIs+0ttZnHj2Fi0GG/nRo2raLCDxtz/dN/W2CAEjSs1/zDGfDTqhjCb/bQVV1JYf57QGEOfAa09aP6h31dmdpvGmdrEZ7N5Q8/nWkHvUahNDN459T9HKdm6w/m0bZlqrTvpfqTnXmag22VH2vWB0NfA0sYR/sbrksYRy8pwd+OImPepnWx7pqHRNJlVWdNW1om2WZ+ztgwTgG45ZBftP1QgUoMDZeTEnIjbpuvGBkNCbfJYUV8M/dtazA/JZSQqKBj0Hx6OMpNe1MAm9DHRcbbBjkYGbVqgtEG5JlAKumiCo0CDMeY37CJPrInrPVsi8VWbtZz//i16zwVlJ1B5EC3qL4pE1y69BhuIVEDoZRvYrWKBYV49PXTtiZnFpmu2XrNdzHw7SZJfj3hNemDfT52XjzXZjzlh9BZAat3H1C0ou/tymqb/IdKkXdeEr+tY6/fFvB54C4L0UgL2L79Q6j4ec36gcakW8+z94LytchCz1527J/yGxioKROl+FHoe0UZ6j7xA5PmIY8qbSZJ8+bPPPvtnHZ9QwaqtcN9opZABFdd3fdkGkf6dMeZ1XTs0v3IZvAqal0VrFNrU2uT+5A30fwV6yq5M9Yqdc/2KMeZXdT1VRRxd79577708qFbWPclVBFO7JhssDSmfC8UeR+geoYxhe2/6eknjiAV/HKHXWMU4gmBke33BZkgSkATQBrN28PP3rh+hsiHLKBXpTSRDLjg/SJLkexp4aFd1SBowaUFeQcEKyqDkvy909o4CAHqvbJlcdlJWo5TFvYFgz/lIgYbtrNrg0nUFOsoIQGh3soJoTStZ5AKR9v1RQOjLVfaAcQHdmGXRdG5qwqjAoDHm9yJk6m5H5+WX9Xs1aXRlw0PTIpE3qT9Uwusa2TYLyjFpB/HrLiAZa7LuLSq3ct4GlEjl5L8ae+OUNoZog4i9H7xaciWSO3assqZ7MAHJ3blqBl7pu5juPPfccz2dG5pDhp7nOcpA0nmeZdnRwoNA0QNbdUNBpFe1iVBBSc3rYo0rB3ktXAhGtsM9m233SzbQ/UiZt2Vs6HW8cyrkfEDjiDfKGEfoHuGNI8ps1VD5OIJgZLsRkATQVEdc6cJ+v/+Ptql3HoQ8f/58Pmguo7SIlxW5FqH8Qz4ZjrFrNtLAbDdHXP/O0JlJocvkYmTjZWRFDgR7Xq+izLBXaivPiNKEKtZCkvGyI5sWjNQEygUiI2etDuOEAroqrxurPJB/biowWEFfozsuIKnnEeuc9D7ftcyOLHlB2dFGpLf1e2NtgPLumWS4APujjI2xmD2slBGpjSHe/aCKEpluHeQ6Acmd6f7tVdooa6yle3Ye4dF4Kdax0b3DZpqFzgpCu63azZ75XEfjyjJKNep8tQH0rxQeRNO5QPfbWjsrK8itDfhWyPGz5pUHY44jtP7X9XEEwcj2cwHJVu4+UUDywvqkFsDfLjwIIITVIb+W9/B1c/Df1o4qY8xnxpgfGmOuKQCiDD8tMr/11lv5oMYLtEWlm7FKTVgxJq/aTbamAZvKUobkla3olZjhki9ehw5W6b2xQY+1EjPk8KzNjNdYg/Itgj1Vl+O9YgOifuAtOH1WbSCi15SsKC3EutKsNQlabZbSiUHXoIFzs6rrkBY31Rsx2uKm7q92oeiNwoMVq2hB2VlwWQQxAsEuiN7v93+58CCAofX7/T8yEcaijj7/LiOy4vuBM+8vJOJZ/lzO9hYu06rbyBKrXOutW7fcX8mqx15orvMbrqxwGQFJ9cy3AXQ2X7WPKwH6uts8GTsgqfGzV9UliH6/n980Yo0j9J64jMi6jSPKqDrnEIzsBgUkf7A4vdGoXfejuLA+uWBTmwGE1Rvya2kPX8cH/+2f/exnYypbqOCjSqKpFOsrr7ySTyQ1eC2TBgmuL1jE8oP5qF+L+6GVnOGymTkXOljsDWLpFVmRLMvOmmd3Hwalwa8mLObzxvl1GJQ7K36Jxlh9VeqejebTwp6ujerfmqbpsRqUTdZmi54mojGy5fV63cQsSZI/rsG5edMtbsZYNNJmA7tQdLBu2bpuMbeCBWX3e3X8/U1KQWnsY/uuAtibI/oM6bMUI0teYwB33bVZb3UZq8xrs4TGUrGuT02lyipeNn0Vx2vZlS6M3JuvlqXV0QgqK/wlXUPKCEhSlr4TVvxqLrEDkt4aYYgA9yGNI7RRONY4wt2n6ziO0KbLmL2OfQcK30GbXVuc3lDgrnULuovTG+OU7wPCGeUmpMXavWQNafE4Vkm9/VImjCaOmkAmSRJzVK5s0J/0er2XNDgJ+X4oKKjj+MILL7yRJEnsIEeUzCSdW5oY9fv9n6ZpSjCyGrMxS2C6HnDW6zXMftV5d2RsbOzMxYsXo2wc0GdVE5MXXnjhq0mS1HrjmJ6nXdi7WGWPSE9e+SPW7lXv9b5dow0RytL7Sq/Xe1nnjj4/ITeB6Dqu6659b2vxmr2S6VUtKBub5fLuxMTEcWVHlb1BCsCu8vtnrAoqWqT3+tXW6gKgvs1Zlv3DyZMnD+raVEYriybwFsGrKPsvD5IkuawNu5pXhs468Y4zwUjsxwNdQz777LNP5+bmfknnVazr6N27d91fZ1m/bTXXXuKjpaWlMR330G18nIFWB/u9N+fzylgbsL1xxNfrOo44ffr0QV0DYo8jCEZ2T+sCkjYQqQ/ydOFBANEpk6JNk14FR7ydz38YO/MnSZJv6NocepLqMlzm5uZchku0636/3/9amqbBB26uV2Sapt8uPIiy5It7sRbevUF5nYI9gxRsPzI1NdVTgN8LngZjP6tjdhJUdj/CobjNAXaTRi0WEFSST9eeGOenNqXY1/uTyJtSRmazA29OTEx8SbuOXWZxYMdt1nvV2a9+3+Cqj4PO++Na4A59zmmhRjuSAy2mAJ2TZdnvJ0kS5X7gl4lO07RW9wPrgZ2z/EDjqjJ6ddWdNo14m1iq3Dyl+8aSjkmZJfCAET147rnnVKnqh9qIp7WdGJtQvcARAfT2u2OrylzTtU8bemO0e/EC3PvOto05r3TjCNt6qI6B+FLHEZRp7aZrbSnZSiASQGi6+XqT1zKCAje1uK8bfugeYF6mYsxr/rz6eioQGXrSQonW6vX7/d8xkTINtFBUs96DO5lXuRlNpGKU2vImPbXt8e0+j3aXf+UBqtildFQm3PzbhpE6vF7fHTvp1s7ad+3ENsZX5T199Hmz1RfWa5CN66oZBL9fx+rHC3TEIVVxUInWGJ+lqstED+mmzd6OUsWhaWo0h9D44bo23oXuP+6d65S8RAgaW349VisAxwYkWb/thhV3X/I2FgblBbjH9/nvRptXujYnVp3XPDbHEQQjEcsVG8hrLAKRAELT5L2C4Ih2IX1HA//QN30NpuwArRdxB2KUslgDZfnqUA6yi9R/6UWVaI3B9Uxwvdhq7l6SJJf0OY3RS0HBSPVcVWZH4cGasKU7TY02B0QrpaOsSJcFU+PNEA/sztoTNmgY46vyLF1tWrBqcRySJPm+secIgNrILxSxshlqUCZ6WPncpayeT3Vm53M/rUmmef4cvPtZEAEX4QHniusdF2uco/kOOkUbeh/pvhSrd6633rUf0cYRA5n6nR9HGIKRnfaFyJkyURGIBBCaFhvOnj2rYMBju/O5zEyYvFRDjB1jXi+1GMFVBTh7GgCGLtVLVmQt5MGeyIt77zaoJOGysqIUlIsxmdJnKEmSg3Xc4V6z7DQnnzTeunWr8MB+uetPmqbUVKuYl0lSl+tE/jxCZ7gM9LwBMJr8c+OVawvGW5CrY3nWQfdcFl6XS7W6IEqapn9deLAa+X3j8OHDNXk6wPbcJlFXISS0gb6RaD/XOzdagCtQgDs/H2O0n2riOCJ2diTByG6rbSmwnRCIBBCaFhVdllaSJMcq2LF0z5VECL1r1su4eiPCztl8QOUFPIPQ8fCykuidVZ0yBuV1L8/6DFuyM1p2pFW7ybm3O7o2/SyzLPuKrm1eECcI1xvTZlSwGaJiXuC/LjuJ8+fxyiuvFB7YD8q0AnuXZdlvmwjjFY1H7captQZV6cjH5rFK4jVBDccs+bljzyWg7vKS9JqLx9h8yXink64oO1LrXKHbHAzYcyWwfr+f3zhCjyN0P/I2YDOOsAhGdlvjassTiAQQmhYa3nnnHaNdxMaY1ysMfuXZkaH7vGjAryCHzbgKuQllPMuyryoYELpEq3sPyEqqVpZlR+/fvx+8b4I3KG/S4p6zEmsyVeedwt4CWl0CQkeSJBmLESh3G0LSNP2rwoMonbcxpS5YVAZqxvWLDK2hVTp0jVrX/CZWSby68zaL1KYcnu5joTPqgVhcJluXNzUgqM22RKE33ptnq+TsORjp+kWGxjhiawQj0RgEIgGEpsHQQCCyykFCtF2IN27cyP/s9/vnCg/u3byCAaEDkQrw6Lgo4ENWUqUO6fhGHpRfKTzYALEmU3qvFdzv9/thU64CqOHCXr6hLkZJvrr1KES+QPBPNXsb1iIGI+n9BYwmWolW735Qm6oAQ8rvXzEWfZvAzutMnTa81fA+Buwkv4bQHxsB1fm+lD+pGOsedawuNKSox4tgZLfVvXHqoBUCkQBCUUDk4sWLdQlE5mKVgNTASl/a8RUqKz7LsrMmQolWHRcdEwV8Cg+iTFGDPerN2sBBuRNtcG4/py/WLSBRw4W9fOdrjMzIXq/nSrQ2bZyMBvMWQBpXuQaoWH4/UCWHkLQx0KviUGYf+RDy8VVXg5E2A3Gt8ACAYT1wmVERy2qy+apb7mizeU0D3Pm5GLoalFfq/d3Cg/UXdRxBMLLbGlMabXF6Qwt/xwsPAMAeqD/k0tKSC4jUIhBp3YxVAtILGobo0XdCJbHW1taCD9q8cjCNzJprkXxBPPQOQQ3KbbD5bwoPNke0yZQX/K1VQMIu7K0XHqhOlB2s7pimafrXhQeBiOihBOxZHowMvXnKu8c3sXf5PW2qUbUVANij/NoXer7jbSRk81XHJEnyf2sdoIYlq/NzMfQm18OHD7u/NnGDa9RxBMHIbmvEwNoGIk8VHgCAEWmXs0qWKuClYEKSJF+uWSk+1dO/rEGaV8oyCJVTVQlI9XkMsBNx3njlX0NRELaBDb7bKl/cC71A7pX7bGpWZC7WZMp7v/fc8yKi2mSG9Pv9XzYRzk9v0tjExefW6Wq/MwAjiZJd45VibuT9IE3TvIkW11Gg3rys7roF5/JrH71OEVAelPPmW63GOGJ7BCO7rfYfCAKRAEJ57bXX8uCZK92TJMmv1HSXUh4cjdEw8VfO0wAAIABJREFUXgFJ9QFUZmPhweEpSHJcE6fQu8ea3kuwZaKUwfQmtE0vgZmPoUJPprz3u47ByNpQyekYfT1adH62QtMn8QBKETujoamb4/L7WMT+tgAC8KoM1a1sab4JkQ0NCCgfz9fwvhSl4o63CZtxxACCkd316YX1yVp/IAhEAghBi8vvvPOOOXv2rOt7dt4OOOra/0XX5uu66Yeu0e5Ktfb7/XOFB4eXl3nVexqSJjrq1ZZl2U9YeG4vb0Lb9GAPi3wVU6Z3aC06PwEA++C1S2jqImL+vLuSgQIguHw+TjASodX1nIrYMoFxxACCkd1V64VeApEA9kuDnOXl5Twb0tY6XzPG/IYxZrkBb26eHalszpC08/Ljjz/Os4r2WApmPMuyNxQEWF0NextxmaBJknyj8CBKl2XZUa9sUDD6LKr/QAuOaD4493Y8BuG953G6xbdDtKxRe37+uPAAUBJXghgA9ikfp9iNmJ3hLXLXddMpAHQVAe5miTaOIBjZXbXd8U0gEsB+KBNSQcj33nvPzM3NucDH63ZxvynZLqtaENfCeOg+DSrVai0UHtzdiSRJDurfCLlzTLvPVaI1y7LHTe8l2BYq5xsjGGk+L7H5d4VvNk+UHY5eqSZsLw9G3rp1a9sf2I80Tf+J9x5V8DYLAaiYxt/qL89xaBavYgUVDgAAlWnRJuzgCEZ2Vy0zIwlEAtgLF8xS6VBlQvpByDRN/xeXadgkaZpeMhGyIxVIVGZjlmVfHbU3RZZl3zReuddQVI5WO66SJPkeO5mBevFK1QEA0BlPnz79R442AAAYldbc+v3+P/PGFRGM7KaHF9Yna7dTjEAkgFG4AOSlS5fMhx9+aJaWlvwSe40NQnpWtCNbgdXQwQAFJJX5pkzHwoPbm02S5KW1tbXg2VunT592f21CCV2gU7zsbHq5AgA64ZNPPjG/8Au/8L9ytAEAwKgYR2zvwLaPoM1qt5hEIBLAbrQgrubJ+vPo0aPPlC9VFmSapn+lAF6apq0py5MkyWVjzJL6KSrrMxRlNurfVKZjkiTDBmzzsq5emdcgdCxtSaW1Bjf3BgAAAOpipOonAAAAnmjjCIKR3VSrYCSBSKC7lPE32BNRwSljG1vrS4Eqr/+Hb91ez1oVgByg6+OSK0EbijIb1ZtqZmbmJdtLc7f7gnq0HVcPwdXVsLcQvTbrSuFBAAAAoDrjDW0hcMTYzAwAAFCZRo8jbt++XXhgvwhGdlNtgpEEIoF6OnfuXJ6FuB319xsMIkbmsubu2WtYV8oF6vVen5iYOKWgXcisRP1bKmtrjJkf4v3MsyJD94pUsNn190zT9GbhB1Apfc5DU++E559//tfSNOXgYq/yydxO9ygAQLdoTBmyjcCtW7fcOPlIQ+cd2khoHj16VHgAAKribZCY5SCgJvK5JeOIgkOF7wRCMLJ7atMvkkAkUF9a5LU3zp2s7fDYKAZvzHe8nUP0KPs8OzJKMFJB57GxsVM22Ljdbq3xLMveePLkSfASrQpESpqmFwsPomprU1NTvdDPQRPQmZmZFwsPNE8+gb57927QJ65JkLXd5xGf3yOC99I19vyMcd4DAKLRXKGnKiohFxFVDcRq9CJijIwGANgrNkighjS3PM44ooDMSARTiw8AgUigMZ7jUFVu9enTp387MzPzm8pGDVlu6caNG+b06dPGZkduVyb1RJIkBxWIDD15eO2119xfh+1biYbTAN9udBimPHCdRck48EpSt7X0dK0pc9c6RA9bAGiO0BtUvM1GszuMkeusR4lWAACGwzjiGSotOx1rHJEUvoO2q3xxi0AkAIzmwIED3zbPBu+C+OCDD/J/Jsuys9v9e1mWfdNEKNGqTE9bBvQ6WWC1tFmuJCRvQHuk4e9PlJ2CMbL92kilnYfI3h+ZyulYTT8/AaAr8o1NodtHaLyiDSpZln2l8GD9nTD0iwQAYBiMI4ryKlCxqisQjOyeSrMQCEQCwJ6saPFdZU1DBoeUpba2tmaSJHlpm74Ns3rs448/DlqyQk6ePOn+2sTd5l2Qb17yMvWC8II9W51vjZFl2e+bCAN0bxJEZuQO0jT9u+0f3bsWBcsBoCui9RFeXV3VGHnMBfca5IR7/gAAYEeMI4ry50swEkFcWJ+sbERKIBIA9i5N0zw70vVZDMXrAzm/xT+pXpJ5OdeQFHCxQZc1gi61lZeoDD0ob/gOQeeQC9KHRs/IoeXXjaNHjwb9R70JV9MmjK3kHV/uEwC2k18fQmc0mGeDeY26J2jDlMZaBCMBANhVlE3YprnjiPEsy74acxxBMLJb1qp6tQQiAWDf8gzC0KVaNcCwzbVP2drwjnqmHddjoQch9IpshDwYGWtxz+4Q3CoA3gTzJlLGgReMZAVxZ1GC5eoBarMjp11fUNQCwXkAO1nTImLoUue6z9tewoNj5DqbV693ApEAAAxtTesejCNyJ7RWE3McQTCyWyoZkRKIBIAgtBh7XX0W1W8xJC87csH7Z/O/v/POO0F/lwZ4yu7MsuwRwchai9I7weyejVt7/X7/awOvIxj1Qez3+z9u4vtSsmiZMK6XLtmRANAYUbLlzbPVQRYKD9aQ6/UeuqoJAAAtxjjCcuOI0OuAPoKR3VJ6MJJAJAAElWdHev0Wg3CL7y7IYkszvBGjNIN77kmSXC48iFpRUCxGsEelMG02bq+B2WfzaZq+qM+MsuhCcpOfNE3jNGdol2jBcnfNy7LsbOFBAEAd5RfuGIuI3/3ud115+XMNyGqYd2XkvR7IAABgZ4wjPpePI7TWsbGxUXgwFIKRHVJ2v0gCkQAQ3B1XQiLkQEkDDQ04FGSx2WoqzXBQmV+hAy5eVidZkTWXpunf6BnOzs4Gf6LeTrvlwoM15nYKxsiK9EqOUlttOFHK6eh6qIVcTcR0+hd+AABQN/l9M8Z4ReNgZTXY8vJXCj9QH+P9fv9P9WwuXrzICYoY8pLpoUvkA0ANRAtGNmkckWXZn5nIWZGGYGSnrJf5YglEAkA0+QAmdKlWN+BQsMUFXLSLKyQtEtnG4NddzzfUWrRBuYJ5Xq/SpgR8FlzGgbI7Q/MWUQlGDifa4vPVq1fdXxsVLAeAjlKQ5F2NMb3ey8FoPNyAMcuyNhXquZIViUjyMoahN4F5jhS+AwDleOA2unZ4HHFFCQl6rjGzIg3ByE4pbWGLQCQARHUzy7KfqO9iyIGSnw2kr7W1teCDkNdee839lazIZogW7JFLly7lf/b7/T8vPFg/4y5Iv7wcPj6lhR31i9Rnm0D90G6aSOengs26HtpSwk0Jls8G/mJREECT5GMWjY9DU1aDu/dnWfZeDcusqarJGS10xs5mAEK7deuW+xfrXr4QQLtFm1s2ZBxxqqxxBMHI7iglGEkgEgDic/0WQ/eO9LKB/EbbQShwqmCL+hCS+dUYUTMN1JtPAZ80TV9uQAbainYKanAeY6egm/QkSfL9woPYzp0syx7FyNw13vUwy7K/KDxYH8t6D4wxnxljPgz8Nc+ZB6BB8o1uoSuHONqkomwBjQX6/f5HNVpIPJJlWb6pSwudodsrACUiGAmgSnkwMsamJrP1OKIuNscR586dK2UccaDwHbRV9IVfApEAUJqVLMv+y7Fjx8YUHAk1YHDZQAo+hS5Defr06fzPNE0vFR5EnWlQflyD8hi75LRwpsD32NjYkh2r1DFQvaD3QGXPYu0U9HZgkjU8AgVvx8bGTmnxOXQfT10DlSHe6/VessHyugXM83H3kydP8pJ8CpKHCJTrvbTltDkXATSJ20B1XJtUYpRTV0UH9cubmZl52bZNqHrThhYQP1IfqvPnz0d5zUBstnShsRUZbvKGA6jIPVuqtadyrTFKnmscoTHK1NTUy3auVatxRFll3glGdsP6hfXJBzFfKYFIACjVgyRJvqdFeAUxQi7Ch17QN7YEpZ6nMniSJGGS2Sw6XtcUoIgRiFPwRAHJixcv6vx4P0mSL7ueNDVxwhjzrcePH0cpz2ps1nCv18tLtCZJUqfX3gT5+DP0ddDRMde/W8Ngef66dV5qo0fIiaPKadtrNecigKbJN1BpzBIrMKesAWXOT01NubWPBRsILdvmAuIHH3wQ5R4IlOHu3bvut1AeHkDVNMfqaT4Ua+7/5ptvDo4jqgpIbo4jlLFZ5jiCMq3dEHUxYXF6Y4FAJACULh8duYzDUDQICT0Q0aLQ2NiYspi+U9GCDfZOx+u6MqVi9Y5UuVYFOjUQrlkPBQ3Q/9LYoFSsnYKuFIwrv4yRrCqIq2BujFLCW/T3OFT4oXId6ff7P4oViPSu1ZQLBtBEK/1+/6eh+6r7dF/QQqK99p6qqGTrCT8QGWvBFCiDPksa02RZ9tu84QAqpgpkjzSO0Ib6GLYYR/yo6nGEMjbLRDCyG6Lt4l6c3lAE/1uFBwAAseVlJBQkitUzLRSvt+UVzopGitqHSRSM1EA4SZKXsiz7HzXYHT3rlyxRwDQW7by0KIu5By6IG6u/h46919/j/QqD5fM6J9VjVeVj9XkMHSCnXDCApkvT9NsmQl91n1tIdH2vsyz7B1tJIbZxO5b+gRufEIhEGyiTWeMsDUU4oACq5OaWZYwjNJfzxhFlXP9qMY4gGNkNUVbQbCDyWuEBAEBZ8pGDF8yoHQVKbf+xd20AFc0TNfvM0UDYC0h+VOGChMY3H7oBesySJS4TTdmnZA3vmTJh/kXXwVg7WLVbVOemJos2C6bMDMlxGxy89uTJk7HLly/nZQJD9Qp2/HLBNe3d2nozMzOm3+//uOvvA7BPV5TVoPtrrHuCsQuJyk53m1W0sGfLxMa6P8zb68MZ9djTIialWVE2ZR6rl1po3rlMdB1A1a7EnlsaO47Q7/DGER+WNY5QNnqV4wgFI3uF76JNPr2wPhl88ZdAJADUQilBov0g66sdkiT5holQFniQApJ2QD5mB+RlLkpsBn00QI8diDTPvp8svuzdgzRN/0pB3Zg7WF2w3O5eLSt794gNfp7S4rPOlxs3bhR+KASvXPB3Snhd2Eaapv+09SMAhqS+6pd1T9DGjdi0WUULerpGq1+lMebv7Vgi1GLivM2YuJam6YsaI2lsHasnJrCTNE3/zm6iC0pVKGy1h15JWcYAsB3NLS/Gnls6ZY8jNJ+N2Vt7GGRGtl/wnc0EIgGgPsoKEu3FQKbNTU6bRoveh8nRgPytt97K+8cYY5ZsH4XYgZ8TdqfgZtAndiBSi4k2a/g6WcP7tpklHnMHqxcs1y/5YeQg8oJfllWvLVbfUvPsxhHKaQNouiuu51MZm/W0oKeFPZWct2OXU3Yx8Y6u5SMuKI7bYEzet0rrLqoYoZKwuk5rjBQ6Mx4YQV7FI8bnyvUss/3aq27XAKDb8nGE1gQaPo74Z38coaCn5rNVjyMOFL6DtrkT8vUQiASA2rmZZdmfzc7OHtQifJ0WKNxOMld3H82WpumfaAygAWzs4Ld2SGvRTb9rZmbmZRv4uW6DPyEDd7P23+ylaZoHmjQJiP050meVrMigdE5cHxsbO6Xrjo5hLFosU1BQGTdjY2NLWZa9YTeFhMr+PmIDgr0nT57kryVWNqRDuWB0WFdKEi+EXheouQf2uvytMsYsjq7XGkeo/67uRVNTU9N6DvrSomaSJB/b47DVdVa9qv+9FgzdN3QP0HhI94CYm1GAEej8Pa5SrRsbG0HfNy3GqyrJ0tLSWL/f/+923sEGqZbxyvyyERN1tjmO0JyvjEoLZvdxxOMkSW63YRxBMLL9gk2wCEQCQC1poPSdsbGxM1pQjr1oPQo9HztookRrO6w8ffr0azMzM7+pAbIGtjFpkUMLiDqP9OfExMQpu0twzQZ+bm4zEN/NuM2EPKesM/2sdgpevXq1tHIlej02+PM2k/FglEn41ddee21M5WdCL5L5lDWrc0UT016vpwnftSzLvmk3Xqzs8byctf1KdY7n5+TFixdLmTgSGEeHdaVlzXjhO+2nnk9/pA1NZYxZHG1m0j1CX8qm0O9W//SjR48eHBsb6+10ziVJkl/77969m99jynrOwAjyTQ0KKMU4P11VknPnzv3i2NjYt/r9/ltpmn7bjvm7tKGitbwKJsx/UHf5OKLX69VlHDHWlnEEwch2e3hhfTLIDZtAJADUmnaNntHuqboEI71Mm++RadMeBw4c0C7lDxWE0QC3jExcNxj3gpJuEH7NBiZX7de9bSa2h+zXrP3KB/DKhNRgXZ+ZMgfqmkzos2qzJAj+hKONGZeUrVjGDlYFO/U7dDxV8mZmZuYlt3PVGLPuLZw9GNjBOu6VH9P5eCTLst+2pV/zfiHaFRu7TLCjz1UDygV3MZCCiOpY2j4Gvc6uvNatpGn6n1RZQdmRutaVXT1E9wmNMdzYXIvwXlbQM7TxhPKraIB8ffGVV16JVoXCbfjStWtubu5FtWywX8aO+80OSRduvLXd4wAwtDRN/1hrH3UZRxi7lrAVzSFjbsYNiWBkuwW5AROIBIDay0sUKnOszF1bO/GafRNsaRedXG9PTEyc0SKB6+9SBheU1EKeJgM6173A5NKwT0EDdX1G9G+VXa5EC5GazJjPdy7+IYH64JZVNlXZimVdC92CmXau6rqnCaItqTNd+OFtqISO/h2dk2Vev+teLtj7fB6h7zCAPVBg4m1VD9G9t6wya9vRImZZFRiASO5lWfYTbcCK2R5EC+r6zCrg6bKCNP63436zU2aQ+bzvpDb8/S5BSQD7tFqncYSxc8+mIxjZbvvOiiQQCQCNodKAp9Rnr+pgpJsw2t2rlGBpn+V+v/8HJ0+efLGK8h8KUCgIqi8FgHSu6evw4cN+6Z9NWihRqRL9f/qqcsegzezUX98luBKHDfKWvoNV55ULzrvsF7dzVRkEvlu3buX/5c7Hqnp4KHha56xIsoQABLDQ7/e/ojJrGiPXqZ0B0ERJknxfFXkUJIxdxWGrrCCNr7Ya7xs7B9XcYG5u7mCWZe8lSfJLhR8CgNEsM44Ii2Bku+1rdZBAJAA0ymq/3/+xeuNoklZlg2oFACx6RbbTgzRN/6ML+GhQXlWAT79XX03oq6TPhYI//X7/p2mazhd+AKFUvoPVZb+4nauxSpnthxbrFBy3fX0XavcEASAQlWvNsuyjs2fPjmkzSJVjZKAFNL87o3FtWSXlfTt9fv2MIQUkbTl8siMB7McDxhFhJW16MXjWhfXJPd90CUQCQPOkaZqn5Sg4VBW7GzUPuBCMbDWNMc6rL+jFixe33aGMz2mDgAuKpWk6R3nW6LSD9ce9Xq/S62GdeeWC/zPnI4CWu5Mkifo+5ZtDtuvbCGAod+wG2HzeV0feJslDHFIAATCOCIhgZHut7fWVEYgEgMZaUY8MBQOrCg7pd5vPAy7fLjyItlE0410/0IYifRY1aVHg1hjzeogy+tiV28H6+OzZs0wYBygjUouIT58+/VtjzJXCDwBA+6zYrPl8MwabqIC9cxtgvb7TteJlSBKMBBAK44hACEa2156yIglEAkCzJUly2dheYFXwSrSywN0N89odrSC0y7TCv9Ek5erVqy4QeZ1s4VKxg3ULeh9cedYDBw78x+JPoApedglZqkA8Kkl9XddB3ZtZSAT2bEVVcDT+Z3wFoEMYRwRAMLK9Rt51TyASAFohDwJWUZpQgciJiQljgy4sqHaDMtC+7DJy67pDugouEGkXafSZoE9k+djB6nFZuubzjSsK1N4r/BCGkc+zjh49GuzNsvdOQ+Y0EN08C4mju3//vvt/jjToaSOiNE3/RP/6W2+9xdsMoEsYR+yBN444RDCyvUbKjCQQCQCtoSDgdS2+e1mKpfB+H9lf3fIgSZL/TZlWCkZSsrUQiHyXQGSl2MFazNJ9m+v0vrDZBmi2ZxYSyezandeDb7zGTxPlWlG5d5V9pz83gI5hHDEiv5cvwch2Wr+wPjn0JJlAJAC0Tp4dWWapVg3ANBlVyc69lgpHo6kkpjIkH+u863LJVpVbJCOydjq/g/XSpUt+cHyh8AMA0C2b9wVljIfMdAa6QuXe6c8NoKMYR+wRwch2GnoRmEAkALSSyrytaWBU1qDI7YhN0/RS4UF0hQtI/kQlWxX86FrQR5+5GzduDAYiyaKqh87uYNXmAG+zCMFxAPjcvCvlrftCW7K7NPby+tACMd3z+3Nz3gHomNaOI2Ku4xCMbKeheo0QiASAVsuzI8so1aqByuzsrFHfQEr/dZ4Ckr+uoEev1+tU0EeTDwUivTKYBH3qp1M7WHVt1jmpzQH6TKq/K8FxAHiGMsVfd9ldTQ+o6L7mrvtASVZci5CLFy/SPw1A1xTGEU2+Dmoc8f7770ddwyEY2U67ZkYSiASA1rvpMtRiL6oo4KkJaJIk3yk8iC56YIMem0GfsvuXlkmTDWWBavKhSYgmI5TBrDWNgc+3bQfrIL9cMIFIANjRiio76FqpLHIF85p2b9BYRD27dd2fmJgoPA5EtrnZy6sQAgBd8cw4QsG8Jo8jNE8+fPhw4WdCIRjZPp9eWJ+8t9OrIhAJAN2QJMllU0LvSO/fv1J4EF31wC5MfP3555//l6WlpVaWbVVGsCYbygLV5EOTELKDG2HZ38HatnNT56VfLphAJADs6k6apv/BbVbRvUHX0SZk0GvDl56rxuO2SgnlMlGFPCCpYLg2ImosAgAdUhhHNKUSj8YRWtPw1w1ttacoCEa2z44lWglEAkCn5EGRmJNB/dt2B/a76htS+AF03ZU0TX/LlW1t4i7BrWiRTwEslaNyZVltwGfHcRhqZXMHqzs3m57B63a0euflefqWAsBItFnlV13vdWUI1HUxUc9Jz00bvuxY/O0kSX7FEIxEdfKNiK5kq3pWU7YVQMdoHPEbGkcoS7Ip4wiv1cyr+guZkRjFtiVaCUQCQOdoAXpNCxSxFiW8xXuyIrGdzV2CypLULkEFfprYr08LKqdPnzbvvffeZjakHbAvEPBppGd2sGoipglZE8uLuWxILzPmVTsZBgCMRpvrtJPvVVdyTYuJusbWYdOKWzzUc9Jze/r06d8yFkGNaE74qmsX0paNiAAwgjt2HPF7uhb6Qck6jiO0ZuiNI/K4UsyNJAQj22fLYCSBSADorPy+EKN/jAKcCshogLXd/QfwLKdp+rKyaHU+1nmX4CAXhNSCiv60wZ6v20AW537z5TtYtaDr+oVpN38TMkvcZFIZCPY6f91mxnBeRkbmEdB6q/Y+/3suU1KbVlZXV/N7RJllKDUOUUBH4xC3eGg3RP3egQMHfotrPmpmNUmSX1eWjStX6MbQ3DsBdMhNOy973c0z3ThC1WzK3AC7wzjiVRs4LW0ccaDwHTTZwwvrk4XyYAQigdbKd77qpvLo0SOOMkrnasq73pTAEJRtcMIOeJdnZmZ6Ggzfv38/D6hoYF6n65kmCBq0a2e3qMegMeZSkiRXyD5onTt2QXc+y7Jvzs3NvaTj/sEHH+STttu3b9fq9SoI+eabb7rdrPlkMk3TP27TgrRXHqhun7Wbxpglt0EBQOvdtF9HlDXwwgsvfHVubm5M94jHjx/nYxfdI/S1sbER7L1Q0EbXegU9tfnPs2Y3eBGARJ09sFk2GjMvT0xMnNJ9U19ra2v550WfnZCfGQCoqZUDBw6s2DWQeY0jTp48Oab1NK2DuDFEV8YRBCPbhUAk0C26cRzXgF69y4AyKQiuEhMKziRJssKbjxGt2sG4vhYmJiaOa5egvhT80eKEvqqgQbsG7JocuIxiZUIq6E4QshNW7DXtmaDkJ598snluVrVwts25+ZMkSb6RpmnrrsNe1lHdFtzv/Ou//uvPJiYmvqj7IAHJ+GL2rWkzMpCC03rLfJIk83Zj1Qk/MClaVNT94u7du/mf2mA1zGYWbX7S2FqLhjrf9d9+VRN7rf+O7QdPj3b4n++6j0vv2V6Sy3Zs9Uav13tJC+PKmPQ/M/qs6DOj/waAFtKcRpnjC24c8cUvfvEroccRLghZ13EEwch2eSYYuTi9cYRAJNBquoksnzx58gu73aB0Q9tu8VQ3qsESnrZ5Mdph1p0DIWmR2J4n3+t6cOb/b+8OYuO6z8SA//2egMCKhAjoIRDdrBWgUC5pydC6tVvS2T0astLsxQK2ktvAuuzWiuUTDyuqB56sgEZPNnYbCwXknrqyrWsRCW1vllaD7iWCgUgbhEYOxcorI7l4xsU3+j9mxCEpinqPfDPv9wPGkvmGFN/jzPCb//f/vi+eP7EpoO6v2RE38u1YJCX7/f6fvfLKKy9U1QbVrum6dwmOqoL26rahXcrNkeTUxIhqU57ZelIybsePH1+IRbO4VYnJTz/9tPEFs3g8njhxYlidu8ljc7Uoimtjn7TP4jn1NG2HNsYvVUVyNQvtwIEDYxsu99s3vvGN/55SejOSkZu9Nm0Xd42q2lRLtm0vnnfsv6qqiaFruf3a2ZHNVYvf/va3Xzp69OihDRUI6+J3RsQ3Kb/WPeE9183878TC5U5fB4+MfaTl4ndpVzR0rq37HbmFWPxeLopiOVcZV8+ZP9nsORPPkyrGGr1u1eJ8kLhsXrxG1TlSYy/inUl8H1Qlj+rQtZgy3idlk9Qt4EFez433m2k0jjh69OhCrAVtFUfcvn17/e8NxRGNi2Tk/ZTSi235hngm65ntnIjUtgOm24O8m+YXFgbYQixILOx0UfRpVC1a8y7XTmsiGdlBEcOcL8tyfZfg888//6cLCwsvVIF4tSgRCxLVY3p0QWInNu4YjP/fJGlyc6Ql26RVH9zL339TX7uLPsi3SJif6vf7//H48ePfr5Ijo4/L+LN6XO5G9biM15R4Y73xDWZuxfo3bX5s1rnQGhXJBw4c+IuxA+2w3O/3/2R+fv77kv+N8n62Hs/6evHA75ZtVRurUl5UPJaTLXM5Fp+rPjk2tlR/7/f7v0kpfZb/90FOKt3Lf+520XD4b0VFRZ0idmpAkwujbXuEY2AnAAAWmUlEQVRcdelcd6J6jK9u8pwZPm8OHjz40vz8/DAIqlrSP0mV7H/C4vwzi++nK0n0iEUnKM5p8n1Qo5uvY9NhVRk37Zp4/uSqv7GPT5AbG2LeuZHXxfU4ot/v/7N471Hdqck4ou7ROSNrLg8O5G9SMnI6DB9oI4nIb3X9gkAHxHP9u7lyYztz2+yUvdP1yrYpFomd2lvIjVSP3dQqajjwm3oNE4FlWabRndPPP//8ifn5+Re2WpAY3SU4KhI7T6g07Y28Abgx4a+HVeKM+sVr3WpZlqtVYjIelwcPHvzh/Pz84c0el1s9JkfFgtkmCfGh/Abz02pHa1mWbX693fiGd6dJpMcW6kdcy5WpbX0+PijL8o9z/LVZfLVd3DXq3obfo5Jvj9u4OMP+uFN12mBHquf1tpXrOc5pRIOLiHU+H8+PfWR6delcd2PsOTOSWBiNEzbGDMfybWg02d+gppJdbaxwvTT2kfo08bt9Et8H3WvwOrcxfmrk+dOmqr+aVMnEx+KIjXFDQ3HE8P1Lg5ua7hzIJ7cXL9g0745EJPvIQsH+uac6jc30+/2fRIAS7QTrFG3pMkkPr31NW985nYPtIxuqDYaLoxt3CY6KuabRCTJ/6N6Gm58fuzFMTG6xo3+4MNbv9/9FJM93+LWrN+bV5qB4XN5refJxoy4utD7IjwOAthjGRVpXMkUebIjXt03yZ9X7hSbiqC5tzLDG1LyureXZ2NR+ww0fdY95GnUgv5C/OXaESfNFXvyQiGS/TNtOFJh0Z8uyfCESkXW2aI0WgtFCJCp2yrKUjGSvPdisUma7XYET3rKFyTC2o3+7xyQANGS4iFj3eIaRGWRd74jCZNiYwARgZxqJI0Zmot4pVnozNxosM2fvPJCIZB/dX+nN7GSHGrBHvvrqq5+kBlq0VrMMyrL867GDAADAvhgMBj+sZhnXaWQGn2QkAEypfr//p3Fmt27dqvUER9q0DmdGptxWRyJrspn7yX4yAwHaZfHAgQP/OuaV1R1EjLRo1ZoOAIA2GauGGgwGLxVFcehJ32O/3//7siz/39iBR1XvbZ5hW5kriuJw3bF/OHHiRPVXyUgAptXcdutcT4gntiv0q9rxtv136Fx0V7t5c7tT2Z2RDgvDmZFppTcTswYXJSSBXXhdVSS0ztnUQFVkJCKPHj0af70yAQsyAAB0yGYLhZ999lmKasEn2Wr2dEppYTAY/OeiKP79DufV7ZfhBuEbN8bysc/s+PHjwxncRVFIRgIwrWL82MJW5/bb3/5201mK0T3g+PHjW35ejiN+XBTFX+bNTW01XEdsMI54WBTFemXkaEIyLsrs2GcBPO5+vFDlVs9Ae8T84DMRJDWRjMzMigQAoFWKooiywIWTJ0/WMu8oYt8LFy7EQmP0F/vb2Ijb0jj4SCx0/u53v6s9/o958blNa/0llwDQIrlLwvdHOgI8s9OnT6e33norfpH+PFdHtnEdPeKI/xBxRN3JyJE44nb8pxg9GAnJld5MlKT+NKX0xdhnAzx6bbi00ps5JhEJrdRIVWTsZJqfnx8GZy0NngAA6LZhjBpxax0ino6E5Icffjj8aoPB4L/kNm5tcz4qQq9evVr7tzVyLcX/AEy1siyHG29eeuml2k4zfjdfunRp+PfBYPBJLiBom4gjDsf3+vDhw1q/tZFrOYwjirF7PEpKruYL8+7YQaDLojVjJCGXPQqgnQaDwVvRiqpaNKlL7OZKj4Kzy370AAC0ULRYq3URMRblLl++nH72s59F5eWhwWDwcVQQjN1x/xzr9/tvNxH/p8ev5Z2xgwAwXW6kmuOIlDc3vf/++8M4ot/vf9K2OCKldHEP4oitk5HpUULywUpvJnrOf/cJQziB6RevAT9Y6c1EW1Zz4qC9zsZupmirUOdupsOHD6fFxcVhj3ctWgEAaKlGFhFTrmy4fv16LCR+p9/v/6+2LCTGomZZlt+MRc66qxlCvAfIVEYCMO2Gv+tGfvfVJn5P3759Ozb4x4zq1bZcx5wcTU3FETtORlZWejP3Vnoz8RN4Oc+IA7ojnvM/iteAaOPs5w7t1u/3L6QcRNQp2lNFj/eiKP6rhwAAAC0VG2d70Vo0ZhTVbXl5eZiQjIXEliQkP4jvJRY3m2jRGtfw6NGjKW9OtikZgGl3L0YTRRwRm/LrFnOo7969G1/1TN7oP9VxRFzHHEd8VH3sicnISsyGixlx5klCJ4zOhbzmRw4TYbEKItbW1mr9fl977bXqr63ZvQUAAJsYdvFooqohbUhIDgaDf9jHGZJxnmc+//zz4eJmE1555ZXqq1oTAKATyrL8n6mhOCIqD8+dO7eekNznjU3DOCK+l6biiChsyNbjiB0nIysj8ySvjB0EpoG5kDCZzqbcQqpO0VJhZCfTPY8NAABabLjgNZJIq10kJGOuUoxHSCn9XXxoDy/HkXyOZ2K+UywgNtFWLW2xiAgAU67RTU1VQrJq2ToYDH6dUjo1dsfmHMktU4eJyPhemoojRq7h7pOR6Q/zJGPR8wfmScLUMBcSJtexamd0zIus0+nTp6uvZlYkAABtt95irYlWrZXLly+nS5cupUgIppQu9vv9/xvrbmN3rNfZXI35aiwgRrIwV1fUzoZEADrqTiQIFxYWGosjIvn3xhtvDEcsFUVxKKX0tzlBeGzszvWq4oiFphORG+KI9TzDrpKRlZghl+dJ/sg8SZhY5kLC5BtWRX7yySe1nkgEXhGA5Z1adkQDANB6ZVn+TWq4OjLl2Ds27lXVDSmlX+TFxLqTkme/+uqr/51S+nlUY0ZVZpMLiOnxqkgbEgHolKIofpb2II6IZORI29aFlNKv8u/dxuOIiF+ajCO2Kmx47uuvvx67824tza5Fa4rzKaVvNXYmQF1iLuSqdqww+QaDwT9FQPHyyy/XGkxE26c8L/Kn5kUCADAhogXZP0bVYlNt1jaKCoCInaMiMz2Kz39dFMX/yItwu9n0G9/4qX6//2dlWb4QH4ik53vvvZdu3bo1duc6xYbEjz/+uDqHP2r0HwOA9tnzOCI2AUW1ZK4mTNHlIW+uulFnHPHOO+801lWhsl0cUWsyMj1KSB7JC5Znxg4CbRFzIc9rxwpTIaoif379+vXh/Jo6RcvXgwcPflkUxXdG2yoAAEDLRRLwTLRSrbt7yHYiKRnVANFdpDIYDB4WRXE7Lyje26Lt6Vxe/FzM1RHrIs6Pc2g6CVmJ9xS5GuR1lZEAdNS+xBGRlIzb/Pz8+scGg0Gsy93KccSDLZKTizmOmGtzHFF7MrKyNLs2l5OSC2MHgf0ScyGXV3oz9Q6VA/ZNzKeJtlCx6FHn7qYIfi5evJjy5oWzY3cAAID2irlLv4qZ6iMtR/fM4cOHh9UUcYsE5aFDh3b8T8f3HAuGcYvNgU22Uduoqmbo9/u/Kcvyn4/dAQC6YV/jiPh9XMUQkxRHRIeIq1evbhlHNJaMrCzNrp3KSckXxw4Ce+V+TkLa1QjTJTb+/F20Woh2DnWK4CG3mfrBFruuAACgzYZVDTGTKW77KRYVo/Xa9773vU0XFGPhcG1tbbi5cC8XDTeK65SrMcaqGQCgY1oXR1R/bjQpcUTjyciKeZKwL77ImwFWtWSFqdRI24hqJ1Oupt6bBvkAAFCvYzFz6fe///03o4tILNKxtajAiFlSeU7Vv9zyjgDQDeKIp1B1WPvqq6/+z4EDB/7NZp9ZjH2kISu9meVc3nqlFVcHpl881+biuScRCVMpesGfiYHadfevjyArG9vFBAAAE+JeWZbvRCXihQsX/My2EW1lq/nzZVn++db3BIDOEEfsUFRsVtfowIEDf7HVZ+1ZMjI9Skg+WOnNnM0t326O3QGoQzy3Xo7n2kpvZrPB+MB0iG4DVQVjbWIhIgZNR393yUgAACbcclQ1LCwsjG64Y4PLly9X7WMvGdEAAOvEETsQG5p2EkfsaTKystKbubPSm4m2bz/Ks+yAZxfPpdfjubXSm7nhesJ06/f7P4kTvH79eq3n+dprrw3/LMvyr8cOAgDAhIlKv8Fg8OVbb71VzURnRFQyxHynWGyN9UTXBgD+oOoY8MYbb4gjNvE0ccS+JCMrK72Zayu9mWM5Y/rF2B2AnfgiP4eiJasqJuiGU2VZvhCJyLp71keP98zrCQAA0+BOURR/Gefx/vvvD1uJ8UjE/rEZcTAYPCzL8o9dFgAYE5V+r0flX8QR0VGMR542jtjXZGTFPEnYNXMhoZuGLVrrnhW5uLiYjh49mvJrizbPAABMi9hodyUWEt955x0LiXkB8eLFi7GA+GVRFP82pWRNAQA2tx5HvPfee+KIXcYRz3399ddjH9xPS7Nri7mcc6FV3xi0S8yFXNaOFTopNu/86vPPPx+tYqxF7PCK1goxdzal5PUFAIBpE4uJZ+7evZvefvvt2ruMTIpqATF7XVcUANiR9Tji3Llz6eHDh528ahviiBjFeG3sTptoXTKysjS7djYnJV8cOwjdFS1Zz2vHCp02DHwuXbpUa2VktKv6+OOPY0fTr4ui+KOxOwAAwHQYxtNffvnlcP5TLCh2iUQkADwTccQu44hWtGndTE62zJknCeviuXBMIhI67chgMPhxBDw3btRbuBg93kNRFH81dhAAAKbH2arV2tWrV9Pp06c786NdXl5eb6mWKxmsLwDA01mPI6LDWN1dy9rsWeOI1lZGjlqaXTuWqyTPjB2E6fdRroY0vw2IgOfnH374Ybp8+XJtFyN63UeV5cGDB6PP+3fMiwEAoAOGsXWc5s2bN4fx9bS2bY0uKDEr8/jx4+nrr7/+p+eeey5GI90ZuyMAsFOPxRGRqJvWtq11xRETkYysmCdJx/RyEtLcNmBoMBj8QyQLT548WetCyUiLhSs5mAIAgC6Y6/f7/60sy+9H95GolIyNf9O0mBiVn9FGLio4Yr00pXTK5kMAqMXcYDD4ONbqIo6ISsmIJaZJnXHERCUjK+ZJMuXMhQQ2ExtyfnH79u1hEFCnCJRid1NK6bspJVXYAAB0zXK/33+7LMtvfv7558OEZHQOmeSk5EsvvZQuXLhQxfnhpyml1bE7AgDP4kis5aeUhrv8I46IpGSMV5qyOOJSzsnt2kQmI9OjhGT1Q47bt8buAJMpntSrK70ZuxSBjYYDst9+++1a50VGcPHee++lvLtpcewOAADQDY+NCIoKh0hIRmJyktq3Rnx/7ty5ND8/X33oZu5+YtMhADRHHPEEE5uMrOR5krGz69WxgzA5zIUEthMbcP4xdlfVPRh7pCry5ZSSttAAAHTdcDFxMBj8uCiKYU+yu3fvpuvXr6dPP/10+Pc2WlxcHLZSqxYPB4PBr4ui+E8ppWtd/4ECwB56LCmZxBHrJj4ZWcnzJCMpOTt2ENrrfuwsMBcSeILhUOzYTXX58uXt7/kUYrj2K6+8klRFAgDAmCN5LtL50bWm2CAYnUriduvWrbFP2kuxqTA2K8YC4tGjR4f/cr/f//uyLC/nzioAwP6YyDgiJyH/qok4YmqSkZU8T3JV61ZaLuZCLq/0ZsxrAHbiTgQuJ0+eHLZ2iJYJOxVBzsZ2EKO7nQaDwcOiKP6Vtk0AALClY3nz3qnRzlzRgi0WEuO2F9UOMzMzw/cC1a1aOMyu5IVDm50BoF22jSMiMfnLX/5y6uOIqUtGpsfnSV4cOwj7792ciDQXEtiJCFh+1cSVyrum/zwnOwEAgCerKh0WB4PBvyuK4nD1GbGoGAuJkZiMv8fC4sOHD596cTEWC2OBsPrzxIkTw+qFQ4cObbzrR7l9WtysMQBA+z1VHBGetoJyszgi/tyQfEx7HUdMZTKyYp4kLXMzt2RVfQQ8jbP5NurOUwQJcznQGXVvJNgAAAB2by5XOyz2+/0TZVm+sN1XikXGWGDcaItk47rBYPBlURS3csXCDRWQADAV1uOIwWDww9Hk5GaiA1rcNpqEOGKqk5EV8yTZZ+ZCAgAAQDccyQuL1abAY/lWWdjsKuTxCbdHPnRj5M97xioAQCfsKo7o9/u/Kcvys5EPtS6O6EQysrI0uxatW5fNk2SPmAsJAAAAAAB0WqeSkekP8yQjIfnm2EGoTwx8PW8uJAAAAAAA0GWdS0ZW8jzJD7Yqa4VdupmTkHdcQAAAAAAAoOs6m4ys5HmSkZR8cewg7Nz9nIS85poBAAAAAAA80vlkZMU8SXYp5kKurvRmll1AAAAAAACAx0lGjsjzJFdTSmfGDsI4cyEBAAAAAAC2IRm5iaXZtbmclDRPks3EXMjlld7MjU2OAQAAAAAAkElGbmNpdu1UTkqaJ0nKcyEjCfmBqwEAAAAAAPBkkpE7sDS7FvMAz5sn2Vlf5KT0qpasAAAAAAAAOycZuUPmSXbWR3ku5L2uXwgAAAAAAICnJRn5lJZm1xajVad5klOvl5OQ5kICAAAAAADskmTkLi3Nrp3NSUnzJKfLFzkJaS4kAAAAAADAM5KMfAa5det58ySnxiVzIQEAAAAAAOojGVmDpdm1Y3me5KsTfzLddDOldNZcSAAAAAAAgHpJRtYoz5OMpOTs1JzUdLufk5DmQgIAAAAAADRAMrIBeZ7kqtatrRVzIZdXejOrXb8QAAAAAAAATSpc3fqt9GY+SCkdyzMIaZd342cjEQkAAAAAANA8lZENy/MkIzm5MNUn2n4xF/L8Sm/mTtcvBAAAAAAAwF6RjNwjeZ5kJCVf7MQJt8f9nIS81vULAQAAAAAAsNckI/fY0uza+ZhXaJ5k42Iu5OpKb2Z5ys8TAAAAAACgtSQj98HS7NqRnJB8s3Mnvzeu5GrIB104WQAAAAAAgLaSjNxHS7Nrc1G9Z55kbWIu5PJKb+bGlJwPAAAAAADARJOMbIGl2bVTOSlpnuTu3M9JyA8m8ZsHAAAAAACYVpKRLbI0uxatW8+bJ/lULuXZkFqyAgAAAAAAtIxkZMvkeZJRJXmm69fiCT7KcyHvbX83AAAAAAAA9otkZEstza4tRutR8yTH9HIS0lxIAAAAAACAlpOMbLml2bWzOSnZ9XmSX+S5kKtjRwAAAAAAAGglycgJkFu3xizJix29BO/mRKS5kAAAAAAAABNEMnKCLM2uHcvzJF/tyCnfTCmdNRcSAAAAAABgMklGTqA8TzKSkrNTeor381zIa2NHAAAAAAAAmBiSkRNsaXbtfJ4n+a0pOaWYC7m60ptZHjsCAAAAAADAxJGMnHB5nmQk796c8FO5kqshzYUEAAAAAACYEpKRUyLPk/wgpbQwYWcUcyGXV3ozN8aOAAAAAAAAMNEkI6dMnicZSckXW35m93MS8oOxIwAAAAAAAEwFycgptTS7Fq1bz7d0nuSlPBtSS1YAAAAAAIApJhk5xfI8ydWU0pmWnOVHeS7kvbEjAAAAAAAATB3JyA5Yml2by0nJ/Zon2ctJSHMhAQAAAAAAOkQyskOWZtfOxpzGPZwn+UWeC7k6dgQAAAAAAICpJxnZMbl16/k9mCf5bk5EmgsJAAAAAADQUZKRHbU0u3Yst259teYrcDOldNZcSAAAAAAAACQjO25pdm0xJyVnn/FK3M9zIa+NHQEAAAAAAKCTJCMZyvMkV3fRujXmQq6u9GaWx44AAAAAAADQaUXXLwCPrPRmPkgpHcuzHnfqSkppTiISAAAAAACAzaiMZEyeJxkJxlNbVEpeydWQd8aOAAAAAAAAQCYZybaWZtfmUkpHqvus9GZubHd/AAAAAAAAGEop/X/v3BJVuTshzwAAAABJRU5ErkJggg==){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 6: Basics of Graph Neural Networks\n", " :card_description: In this tutorial, we will discuss the application of neural networks on graphs. Graph Neural Networks (GNNs) have recently gained increasing popularity in both applications...\n", " :tags: Graph,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/06-graph-neural-networks.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab_type,colab,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 27.294729, "end_time": "2021-12-04T16:15:21.142769", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/06-graph-neural-networks/GNN_overview.ipynb", "output_path": ".notebooks/course_UvA-DL/06-graph-neural-networks.ipynb", "parameters": {}, "start_time": "2021-12-04T16:14:53.848040", "version": "2.3.3"}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/07-deep-energy-based-generative-models.ipynb b/source/notebooks/course_UvA-DL/07-deep-energy-based-generative-models.ipynb deleted file mode 100644 index 7ebe824..0000000 --- a/source/notebooks/course_UvA-DL/07-deep-energy-based-generative-models.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "7a37951c", "metadata": {"papermill": {"duration": 0.023386, "end_time": "2021-09-16T12:40:39.258673", "exception": false, "start_time": "2021-09-16T12:40:39.235287", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 7: Deep Energy-Based Generative Models\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-09-16T14:32:29.871712\n", "\n", "In this tutorial, we will look at energy-based deep learning models, and focus on their application as generative models.\n", "Energy models have been a popular tool before the huge deep learning hype around 2012 hit.\n", "However, in recent years, energy-based models have gained increasing attention because of improved training methods and tricks being proposed.\n", "Although they are still in a research stage, they have shown to outperform strong Generative Adversarial Networks\n", "in certain cases which have been the state of the art of generating images\n", "([blog post](https://ajolicoeur.wordpress.com/the-new-contender-to-gans-score-matching-with-langevin-sampling/)about strong energy-based models,\n", "[blog post](https://medium.com/syncedreview/nvidia-open-sources-hyper-realistic-face-generator-stylegan-f346e1a73826) about the power of GANs).\n", "Hence, it is important to be aware of energy-based models, and as the theory can be abstract sometimes,\n", "we will show the idea of energy-based models with a lot of examples.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/07-deep-energy-based-generative-models.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "7f00f90d", "metadata": {"papermill": {"duration": 0.021442, "end_time": "2021-09-16T12:40:39.301749", "exception": false, "start_time": "2021-09-16T12:40:39.280307", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "33e8ad5c", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-09-16T12:40:39.347844Z", "iopub.status.busy": "2021-09-16T12:40:39.347375Z", "iopub.status.idle": "2021-09-16T12:40:39.349918Z", "shell.execute_reply": "2021-09-16T12:40:39.349436Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.026972, "end_time": "2021-09-16T12:40:39.350031", "exception": false, "start_time": "2021-09-16T12:40:39.323059", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"torchvision\" \"torch>=1.6, <1.9\" \"tensorboard\" \"matplotlib\" \"pytorch-lightning>=1.3\" \"torchmetrics>=0.3\""]}, {"cell_type": "markdown", "id": "9eea4d0b", "metadata": {"papermill": {"duration": 0.022362, "end_time": "2021-09-16T12:40:39.394135", "exception": false, "start_time": "2021-09-16T12:40:39.371773", "status": "completed"}, "tags": []}, "source": ["
\n", "First, let's import our standard libraries below."]}, {"cell_type": "code", "execution_count": 2, "id": "8882a3d0", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:39.460040Z", "iopub.status.busy": "2021-09-16T12:40:39.459550Z", "iopub.status.idle": "2021-09-16T12:40:40.727064Z", "shell.execute_reply": "2021-09-16T12:40:40.726625Z"}, "papermill": {"duration": 1.298062, "end_time": "2021-09-16T12:40:40.727182", "exception": false, "start_time": "2021-09-16T12:40:39.429120", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_1940/3480345581.py:30: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "Global seed set to 42\n"]}], "source": ["# Standard libraries\n", "import os\n", "import random\n", "import urllib.request\n", "from urllib.error import HTTPError\n", "\n", "# Plotting\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "# PyTorch Lightning\n", "import pytorch_lightning as pl\n", "\n", "# PyTorch\n", "import torch\n", "import torch.nn as nn\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "\n", "# Torchvision\n", "import torchvision\n", "\n", "# %matplotlib inline\n", "from IPython.display import set_matplotlib_formats\n", "from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint\n", "from torchvision import transforms\n", "from torchvision.datasets import MNIST\n", "\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "matplotlib.rcParams[\"lines.linewidth\"] = 2.0\n", "\n", "# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/tutorial8\")\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")"]}, {"cell_type": "markdown", "id": "8fa2ad82", "metadata": {"papermill": {"duration": 0.022316, "end_time": "2021-09-16T12:40:40.772238", "exception": false, "start_time": "2021-09-16T12:40:40.749922", "status": "completed"}, "tags": []}, "source": ["We also have pre-trained models that we download below."]}, {"cell_type": "code", "execution_count": 3, "id": "8ddb6202", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:40.823433Z", "iopub.status.busy": "2021-09-16T12:40:40.820802Z", "iopub.status.idle": "2021-09-16T12:40:41.030878Z", "shell.execute_reply": "2021-09-16T12:40:41.030402Z"}, "papermill": {"duration": 0.236743, "end_time": "2021-09-16T12:40:41.030985", "exception": false, "start_time": "2021-09-16T12:40:40.794242", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial8/MNIST.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial8/tensorboards/events.out.tfevents.MNIST...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial8/\"\n", "# Files to download\n", "pretrained_files = [\"MNIST.ckpt\", \"tensorboards/events.out.tfevents.MNIST\"]\n", "\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if \"/\" in file_name:\n", " os.makedirs(file_path.rsplit(\"/\", 1)[0], exist_ok=True)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(\"Downloading %s...\" % file_url)\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the files manually,\"\n", " \" or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "54b9725b", "metadata": {"papermill": {"duration": 0.02216, "end_time": "2021-09-16T12:40:41.075989", "exception": false, "start_time": "2021-09-16T12:40:41.053829", "status": "completed"}, "tags": []}, "source": ["## Energy Models\n", "\n", "In the first part of this tutorial, we will review the theory of the energy-based models\n", "(the same theory has been discussed in Lecture 8).\n", "While most of the previous models had the goal of classification or regression,\n", "energy-based models are motivated from a different perspective: density estimation.\n", "Given a dataset with a lot of elements, we want to estimate the probability distribution over the whole data space.\n", "As an example, if we model images from CIFAR10, our goal would be to have a probability distribution\n", "over all possible images of size $32\\times32\\times3$ where those images have a high likelihood\n", "that look realistic and are one of the 10 CIFAR classes.\n", "Simple methods like interpolation between images don't work because images are extremely high-dimensional\n", "(especially for large HD images).\n", "Hence, we turn to deep learning methods that have performed well on complex data.\n", "\n", "However, how do we predict a probability distribution $p(\\mathbf{x})$ over so many dimensions using a simple neural network?\n", "The problem is that we cannot just predict a score between 0 and 1,\n", "because a probability distribution over data needs to fulfill two properties:\n", "\n", "1.\n", "The probability distribution needs to assign any possible value of\n", "$\\mathbf{x}$ a non-negative value: $p(\\mathbf{x}) \\geq 0$.\n", "2.\n", "The probability density must sum/integrate to 1 over **all** possible inputs:\n", "$\\int_{\\mathbf{x}} p(\\mathbf{x}) d\\mathbf{x} = 1$.\n", "\n", "Luckily, there are actually many approaches for this, and one of them are energy-based models.\n", "The fundamental idea of energy-based models is that you can turn any function\n", "that predicts values larger than zero into a probability distribution by dviding by its volume.\n", "Imagine we have a neural network, which has as output a single neuron, like in regression.\n", "We can call this network $E_{\\theta}(\\mathbf{x})$, where $\\theta$ are our parameters of the network,\n", "and $\\mathbf{x}$ the input data (e.g. an image).\n", "The output of $E_{\\theta}$ is a scalar value between $-\\infty$ and $\\infty$.\n", "Now, we can use basic probability theory to *normalize* the scores of all possible inputs:\n", "\n", "$$\n", "q_{\\theta}(\\mathbf{x}) = \\frac{\\exp\\left(-E_{\\theta}(\\mathbf{x})\\right)}{Z_{\\theta}} \\hspace{5mm}\\text{where}\\hspace{5mm}\n", "Z_{\\theta} = \\begin{cases}\n", " \\int_{\\mathbf{x}}\\exp\\left(-E_{\\theta}(\\mathbf{x})\\right) d\\mathbf{x} & \\text{if }x\\text{ is continuous}\\\\\n", " \\sum_{\\mathbf{x}}\\exp\\left(-E_{\\theta}(\\mathbf{x})\\right) & \\text{if }x\\text{ is discrete}\n", "\\end{cases}\n", "$$\n", "\n", "The $\\exp$-function ensures that we assign a probability greater than zero to any possible input.\n", "We use a negative sign in front of $E$ because we call $E_{\\theta}$ to be the energy function:\n", "data points with high likelihood have a low energy, while data points with low likelihood have a high energy.\n", "$Z_{\\theta}$ is our normalization terms that ensures that the density integrates/sums to 1.\n", "We can show this by integrating over $q_{\\theta}(\\mathbf{x})$:\n", "\n", "$$\n", "\\int_{\\mathbf{x}}q_{\\theta}(\\mathbf{x})d\\mathbf{x} =\n", "\\int_{\\mathbf{x}}\\frac{\\exp\\left(-E_{\\theta}(\\mathbf{x})\\right)}{\\int_{\\mathbf{\\tilde{x}}}\\exp\\left(-E_{\\theta}(\\mathbf{\\tilde{x}})\\right) d\\mathbf{\\tilde{x}}}d\\mathbf{x} =\n", "\\frac{\\int_{\\mathbf{x}}\\exp\\left(-E_{\\theta}(\\mathbf{x})\\right)d\\mathbf{x}}{\\int_{\\mathbf{\\tilde{x}}}\\exp\\left(-E_{\\theta}(\\mathbf{\\tilde{x}})\\right) d\\mathbf{\\tilde{x}}} = 1\n", "$$\n", "\n", "Note that we call the probability distribution $q_{\\theta}(\\mathbf{x})$ because this is the learned distribution by the model,\n", "and is trained to be as close as possible to the *true*, unknown distribution $p(\\mathbf{x})$.\n", "\n", "The main benefit of this formulation of the probability distribution is its great flexibility as we can choose\n", "$E_{\\theta}$ in whatever way we like, without any constraints.\n", "Nevertheless, when looking at the equation above, we can see a fundamental issue: How do we calculate $Z_{\\theta}$?\n", "There is no chance that we can calculate $Z_{\\theta}$ analytically for high-dimensional input\n", "and/or larger neural networks, but the task requires us to know $Z_{\\theta}$.\n", "Although we can't determine the exact likelihood of a point, there exist methods with which we can train energy-based models.\n", "Thus, we will look next at \"Contrastive Divergence\" for training the model."]}, {"cell_type": "markdown", "id": "5f67ccab", "metadata": {"papermill": {"duration": 0.022199, "end_time": "2021-09-16T12:40:41.120470", "exception": false, "start_time": "2021-09-16T12:40:41.098271", "status": "completed"}, "tags": []}, "source": ["### Contrastive Divergence\n", "\n", "When we train a model on generative modeling, it is usually done by maximum likelihood estimation.\n", "In other words, we try to maximize the likelihood of the examples in the training set.\n", "As the exact likelihood of a point cannot be determined due to the unknown normalization constant $Z_{\\theta}$,\n", "we need to train energy-based models slightly different.\n", "We cannot just maximize the un-normalized probability $\\exp(-E_{\\theta}(\\mathbf{x}_{\\text{train}}))$\n", "because there is no guarantee that $Z_{\\theta}$ stays constant, or that $\\mathbf{x}_{\\text{train}}$\n", "is becoming more likely than the others.\n", "However, if we base our training on comparing the likelihood of points, we can create a stable objective.\n", "Namely, we can re-write our maximum likelihood objective where we maximize the probability\n", "of $\\mathbf{x}_{\\text{train}}$ compared to a randomly sampled data point of our model:\n", "\n", "$$\n", "\\begin{split}\n", " \\nabla_{\\theta}\\mathcal{L}_{\\text{MLE}}(\\mathbf{\\theta};p) & = -\\mathbb{E}_{p(\\mathbf{x})}\\left[\\nabla_{\\theta}\\log q_{\\theta}(\\mathbf{x})\\right]\\\\[5pt]\n", " & = \\mathbb{E}_{p(\\mathbf{x})}\\left[\\nabla_{\\theta}E_{\\theta}(\\mathbf{x})\\right] - \\mathbb{E}_{q_{\\theta}(\\mathbf{x})}\\left[\\nabla_{\\theta}E_{\\theta}(\\mathbf{x})\\right]\n", "\\end{split}\n", "$$\n", "\n", "Note that the loss is still an objective we want to minimize.\n", "Thus, we try to minimize the energy for data points from the dataset, while maximizing the energy for randomly\n", "sampled data points from our model (how we sample will be explained below).\n", "Although this objective sounds intuitive, how is it actually derived from our original distribution $q_{\\theta}(\\mathbf{x})$?\n", "The trick is that we approximate $Z_{\\theta}$ by a single Monte-Carlo sample.\n", "This gives us the exact same objective as written above.\n", "\n", "Visually, we can look at the objective as follows (figure credit\n", "- [Stefano Ermon and Aditya Grover](https://deepgenerativemodels.github.io/assets/slides/cs236_lecture11.pdf)):\n", "\n", "
\n", "\n", "$f_{\\theta}$ represents $\\exp(-E_{\\theta}(\\mathbf{x}))$ in our case.\n", "The point on the right, called \"correct answer\", represents a data point from the dataset\n", "(i.e. $x_{\\text{train}}$), and the left point, \"wrong answer\", a sample from our model (i.e. $x_{\\text{sample}}$).\n", "Thus, we try to \"pull up\" the probability of the data points in the dataset,\n", "while \"pushing down\" randomly sampled points.\n", "The two forces for pulling and pushing are in balance iff $q_{\\theta}(\\mathbf{x})=p(\\mathbf{x})$."]}, {"cell_type": "markdown", "id": "2b9dc4a8", "metadata": {"papermill": {"duration": 0.022022, "end_time": "2021-09-16T12:40:41.164700", "exception": false, "start_time": "2021-09-16T12:40:41.142678", "status": "completed"}, "tags": []}, "source": ["### Sampling from Energy-Based Models\n", "\n", "For sampling from an energy-based model, we can apply a Markov Chain Monte Carlo using Langevin Dynamics.\n", "The idea of the algorithm is to start from a random point, and slowly move towards the direction\n", "of higher probability using the gradients of $E_{\\theta}$.\n", "Nevertheless, this is not enough to fully capture the probability distribution.\n", "We need to add noise $\\omega$ at each gradient step to the current sample.\n", "Under certain conditions such as that we perform the gradient steps an infinite amount of times,\n", "we would be able to create an exact sample from our modeled distribution.\n", "However, as this is not practically possible, we usually limit the chain to $K$ steps\n", "($K$ a hyperparameter that needs to be finetuned).\n", "Overall, the sampling procedure can be summarized in the following algorithm:\n", "\n", "
"]}, {"cell_type": "markdown", "id": "c20bf67b", "metadata": {"papermill": {"duration": 0.02196, "end_time": "2021-09-16T12:40:41.209186", "exception": false, "start_time": "2021-09-16T12:40:41.187226", "status": "completed"}, "tags": []}, "source": ["### Applications of Energy-based models beyond generation\n", "\n", "Modeling the probability distribution for sampling new data is not the only application of energy-based models.\n", "Any application which requires us to compare two elements is much simpler to learn\n", "because we just need to go for the higher energy.\n", "A couple of examples are shown below (figure credit\n", "- [Stefano Ermon and Aditya Grover](https://deepgenerativemodels.github.io/assets/slides/cs236_lecture11.pdf)).\n", "A classification setup like object recognition or sequence labeling can be considered as an energy-based\n", "task as we just need to find the $Y$ input that minimizes the output $E(X, Y)$ (hence maximizes probability).\n", "Similarly, a popular application of energy-based models is denoising of images.\n", "Given an image $X$ with a lot of noise, we try to minimize the energy by finding the true input image $Y$.\n", "\n", "
\n", "\n", "Nonetheless, we will focus on generative modeling here as in the next couple of lectures,\n", "we will discuss more generative deep learning approaches."]}, {"cell_type": "markdown", "id": "89370a6d", "metadata": {"papermill": {"duration": 0.022103, "end_time": "2021-09-16T12:40:41.253274", "exception": false, "start_time": "2021-09-16T12:40:41.231171", "status": "completed"}, "tags": []}, "source": ["## Image generation\n", "\n", "
\n", "\n", "As an example for energy-based models, we will train a model on image generation.\n", "Specifically, we will look at how we can generate MNIST digits with a very simple CNN model.\n", "However, it should be noted that energy models are not easy to train and often diverge\n", "if the hyperparameters are not well tuned.\n", "We will rely on training tricks proposed in the paper\n", "[Implicit Generation and Generalization in Energy-Based Models](https://arxiv.org/abs/1903.08689)\n", "by Yilun Du and Igor Mordatch ([blog](https://openai.com/blog/energy-based-models/)).\n", "The important part of this notebook is however to see how the theory above can actually be used in a model.\n", "\n", "### Dataset\n", "\n", "First, we can load the MNIST dataset below.\n", "Note that we need to normalize the images between -1 and 1 instead of mean 0 and std 1 because during sampling,\n", "we have to limit the input space.\n", "Scaling between -1 and 1 makes it easier to implement it."]}, {"cell_type": "code", "execution_count": 4, "id": "d20babd4", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:41.303522Z", "iopub.status.busy": "2021-09-16T12:40:41.302963Z", "iopub.status.idle": "2021-09-16T12:40:41.332018Z", "shell.execute_reply": "2021-09-16T12:40:41.331579Z"}, "papermill": {"duration": 0.056251, "end_time": "2021-09-16T12:40:41.332136", "exception": false, "start_time": "2021-09-16T12:40:41.275885", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Transformations applied on each image => make them a tensor and normalize between -1 and 1\n", "transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n", "\n", "# Loading the training dataset. We need to split it into a training and validation part\n", "train_set = MNIST(root=DATASET_PATH, train=True, transform=transform, download=True)\n", "\n", "# Loading the test set\n", "test_set = MNIST(root=DATASET_PATH, train=False, transform=transform, download=True)\n", "\n", "# We define a set of data loaders that we can use for various purposes later.\n", "# Note that for actually training a model, we will use different data loaders\n", "# with a lower batch size.\n", "train_loader = data.DataLoader(train_set, batch_size=128, shuffle=True, drop_last=True, num_workers=4, pin_memory=True)\n", "test_loader = data.DataLoader(test_set, batch_size=256, shuffle=False, drop_last=False, num_workers=4)"]}, {"cell_type": "markdown", "id": "3e1b26e0", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.02242, "end_time": "2021-09-16T12:40:41.378644", "exception": false, "start_time": "2021-09-16T12:40:41.356224", "status": "completed"}, "tags": []}, "source": ["### CNN Model\n", "\n", "First, we implement our CNN model.\n", "The MNIST images are of size 28x28, hence we only need a small model.\n", "As an example, we will apply several convolutions with stride 2 that downscale the images.\n", "If you are interested, you can also use a deeper model such as a small ResNet, but for simplicity,\n", "we will stick with the tiny network.\n", "\n", "It is a good practice to use a smooth activation function like Swish instead of ReLU in the energy model.\n", "This is because we will rely on the gradients we get back with respect to the input image, which should not be sparse."]}, {"cell_type": "code", "execution_count": 5, "id": "38169d71", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:41.429707Z", "iopub.status.busy": "2021-09-16T12:40:41.429207Z", "iopub.status.idle": "2021-09-16T12:40:41.431319Z", "shell.execute_reply": "2021-09-16T12:40:41.430920Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.030493, "end_time": "2021-09-16T12:40:41.431417", "exception": false, "start_time": "2021-09-16T12:40:41.400924", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class CNNModel(nn.Module):\n", " def __init__(self, hidden_features=32, out_dim=1, **kwargs):\n", " super().__init__()\n", " # We increase the hidden dimension over layers. Here pre-calculated for simplicity.\n", " c_hid1 = hidden_features // 2\n", " c_hid2 = hidden_features\n", " c_hid3 = hidden_features * 2\n", "\n", " # Series of convolutions and Swish activation functions\n", " self.cnn_layers = nn.Sequential(\n", " nn.Conv2d(1, c_hid1, kernel_size=5, stride=2, padding=4), # [16x16] - Larger padding to get 32x32 image\n", " nn.SiLU(),\n", " nn.Conv2d(c_hid1, c_hid2, kernel_size=3, stride=2, padding=1), # [8x8]\n", " nn.SiLU(),\n", " nn.Conv2d(c_hid2, c_hid3, kernel_size=3, stride=2, padding=1), # [4x4]\n", " nn.SiLU(),\n", " nn.Conv2d(c_hid3, c_hid3, kernel_size=3, stride=2, padding=1), # [2x2]\n", " nn.SiLU(),\n", " nn.Flatten(),\n", " nn.Linear(c_hid3 * 4, c_hid3),\n", " nn.SiLU(),\n", " nn.Linear(c_hid3, out_dim),\n", " )\n", "\n", " def forward(self, x):\n", " x = self.cnn_layers(x).squeeze(dim=-1)\n", " return x"]}, {"cell_type": "markdown", "id": "86a8d447", "metadata": {"papermill": {"duration": 0.022437, "end_time": "2021-09-16T12:40:41.476079", "exception": false, "start_time": "2021-09-16T12:40:41.453642", "status": "completed"}, "tags": []}, "source": ["In the rest of the notebook, the output of the model will actually not represent\n", "$E_{\\theta}(\\mathbf{x})$, but $-E_{\\theta}(\\mathbf{x})$.\n", "This is a standard implementation practice for energy-based models, as some people also write the energy probability\n", "density as $q_{\\theta}(\\mathbf{x}) = \\frac{\\exp\\left(f_{\\theta}(\\mathbf{x})\\right)}{Z_{\\theta}}$.\n", "In that case, the model would actually represent $f_{\\theta}(\\mathbf{x})$.\n", "In the training loss etc., we need to be careful to not switch up the signs."]}, {"cell_type": "markdown", "id": "5321df9e", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.022306, "end_time": "2021-09-16T12:40:41.520797", "exception": false, "start_time": "2021-09-16T12:40:41.498491", "status": "completed"}, "tags": []}, "source": ["### Sampling buffer\n", "\n", "In the next part, we look at the training with sampled elements.\n", "To use the contrastive divergence objective, we need to generate samples during training.\n", "Previous work has shown that due to the high dimensionality of images, we need a lot of iterations\n", "inside the MCMC sampling to obtain reasonable samples.\n", "However, there is a training trick that significantly reduces the sampling cost: using a sampling buffer.\n", "The idea is that we store the samples of the last couple of batches in a buffer,\n", "and re-use those as the starting point of the MCMC algorithm for the next batches.\n", "This reduces the sampling cost because the model requires a significantly\n", "lower number of steps to converge to reasonable samples.\n", "However, to not solely rely on previous samples and allow novel samples as well,\n", "we re-initialize 5% of our samples from scratch (random noise between -1 and 1).\n", "\n", "Below, we implement the sampling buffer.\n", "The function `sample_new_exmps` returns a new batch of \"fake\" images.\n", "We refer to those as fake images because they have been generated, but are not actually part of the dataset.\n", "As mentioned before, we use initialize 5% randomly, and 95% are randomly picked from our buffer.\n", "On this initial batch, we perform MCMC for 60 iterations to improve the image quality\n", "and come closer to samples from $q_{\\theta}(\\mathbf{x})$.\n", "In the function `generate_samples`, we implemented the MCMC for images.\n", "Note that the hyperparameters of `step_size`, `steps`, the noise standard deviation\n", "$\\sigma$ are specifically set for MNIST, and need to be finetuned for a different dataset if you want to use such."]}, {"cell_type": "code", "execution_count": 6, "id": "84649cbd", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:41.577495Z", "iopub.status.busy": "2021-09-16T12:40:41.576997Z", "iopub.status.idle": "2021-09-16T12:40:41.579100Z", "shell.execute_reply": "2021-09-16T12:40:41.578638Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.035877, "end_time": "2021-09-16T12:40:41.579195", "exception": false, "start_time": "2021-09-16T12:40:41.543318", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Sampler:\n", " def __init__(self, model, img_shape, sample_size, max_len=8192):\n", " \"\"\"\n", " Args:\n", " model: Neural network to use for modeling E_theta\n", " img_shape: Shape of the images to model\n", " sample_size: Batch size of the samples\n", " max_len: Maximum number of data points to keep in the buffer\n", " \"\"\"\n", " super().__init__()\n", " self.model = model\n", " self.img_shape = img_shape\n", " self.sample_size = sample_size\n", " self.max_len = max_len\n", " self.examples = [(torch.rand((1,) + img_shape) * 2 - 1) for _ in range(self.sample_size)]\n", "\n", " def sample_new_exmps(self, steps=60, step_size=10):\n", " \"\"\"Function for getting a new batch of \"fake\" images.\n", "\n", " Args:\n", " steps: Number of iterations in the MCMC algorithm\n", " step_size: Learning rate nu in the algorithm above\n", " \"\"\"\n", " # Choose 95% of the batch from the buffer, 5% generate from scratch\n", " n_new = np.random.binomial(self.sample_size, 0.05)\n", " rand_imgs = torch.rand((n_new,) + self.img_shape) * 2 - 1\n", " old_imgs = torch.cat(random.choices(self.examples, k=self.sample_size - n_new), dim=0)\n", " inp_imgs = torch.cat([rand_imgs, old_imgs], dim=0).detach().to(device)\n", "\n", " # Perform MCMC sampling\n", " inp_imgs = Sampler.generate_samples(self.model, inp_imgs, steps=steps, step_size=step_size)\n", "\n", " # Add new images to the buffer and remove old ones if needed\n", " self.examples = list(inp_imgs.to(torch.device(\"cpu\")).chunk(self.sample_size, dim=0)) + self.examples\n", " self.examples = self.examples[: self.max_len]\n", " return inp_imgs\n", "\n", " @staticmethod\n", " def generate_samples(model, inp_imgs, steps=60, step_size=10, return_img_per_step=False):\n", " \"\"\"Function for sampling images for a given model.\n", "\n", " Args:\n", " model: Neural network to use for modeling E_theta\n", " inp_imgs: Images to start from for sampling. If you want to generate new images, enter noise between -1 and 1.\n", " steps: Number of iterations in the MCMC algorithm.\n", " step_size: Learning rate nu in the algorithm above\n", " return_img_per_step: If True, we return the sample at every iteration of the MCMC\n", " \"\"\"\n", " # Before MCMC: set model parameters to \"required_grad=False\"\n", " # because we are only interested in the gradients of the input.\n", " is_training = model.training\n", " model.eval()\n", " for p in model.parameters():\n", " p.requires_grad = False\n", " inp_imgs.requires_grad = True\n", "\n", " # Enable gradient calculation if not already the case\n", " had_gradients_enabled = torch.is_grad_enabled()\n", " torch.set_grad_enabled(True)\n", "\n", " # We use a buffer tensor in which we generate noise each loop iteration.\n", " # More efficient than creating a new tensor every iteration.\n", " noise = torch.randn(inp_imgs.shape, device=inp_imgs.device)\n", "\n", " # List for storing generations at each step (for later analysis)\n", " imgs_per_step = []\n", "\n", " # Loop over K (steps)\n", " for _ in range(steps):\n", " # Part 1: Add noise to the input.\n", " noise.normal_(0, 0.005)\n", " inp_imgs.data.add_(noise.data)\n", " inp_imgs.data.clamp_(min=-1.0, max=1.0)\n", "\n", " # Part 2: calculate gradients for the current input.\n", " out_imgs = -model(inp_imgs)\n", " out_imgs.sum().backward()\n", " inp_imgs.grad.data.clamp_(-0.03, 0.03) # For stabilizing and preventing too high gradients\n", "\n", " # Apply gradients to our current samples\n", " inp_imgs.data.add_(-step_size * inp_imgs.grad.data)\n", " inp_imgs.grad.detach_()\n", " inp_imgs.grad.zero_()\n", " inp_imgs.data.clamp_(min=-1.0, max=1.0)\n", "\n", " if return_img_per_step:\n", " imgs_per_step.append(inp_imgs.clone().detach())\n", "\n", " # Reactivate gradients for parameters for training\n", " for p in model.parameters():\n", " p.requires_grad = True\n", " model.train(is_training)\n", "\n", " # Reset gradient calculation to setting before this function\n", " torch.set_grad_enabled(had_gradients_enabled)\n", "\n", " if return_img_per_step:\n", " return torch.stack(imgs_per_step, dim=0)\n", " else:\n", " return inp_imgs"]}, {"cell_type": "markdown", "id": "da0da692", "metadata": {"papermill": {"duration": 0.022214, "end_time": "2021-09-16T12:40:41.623643", "exception": false, "start_time": "2021-09-16T12:40:41.601429", "status": "completed"}, "tags": []}, "source": ["The idea of the buffer becomes a bit clearer in the following algorithm."]}, {"cell_type": "markdown", "id": "a39ea7ee", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.022698, "end_time": "2021-09-16T12:40:41.668518", "exception": false, "start_time": "2021-09-16T12:40:41.645820", "status": "completed"}, "tags": []}, "source": ["### Training algorithm\n", "\n", "With the sampling buffer being ready, we can complete our training algorithm.\n", "Below is shown a summary of the full training algorithm of an energy model on image modeling:\n", "\n", "
\n", "\n", "The first few statements in each training iteration concern the sampling of the real and fake data,\n", "as we have seen above with the sample buffer.\n", "Next, we calculate the contrastive divergence objective using our energy model $E_{\\theta}$.\n", "However, one additional training trick we need is to add a regularization loss on the output of $E_{\\theta}$.\n", "As the output of the network is not constrained and adding a large bias or not to the output\n", "doesn't change the contrastive divergence loss, we need to ensure somehow else that the output values are in a reasonable range.\n", "Without the regularization loss, the output values will fluctuate in a very large range.\n", "With this, we ensure that the values for the real data are around 0, and the fake data likely slightly lower\n", "(for noise or outliers the score can be still significantly lower).\n", "As the regularization loss is less important than the Contrastive Divergence, we have a weight factor\n", "$\\alpha$ which is usually quite some smaller than 1.\n", "Finally, we perform an update step with an optimizer on the combined loss and add the new samples to the buffer.\n", "\n", "Below, we put this training dynamic into a PyTorch Lightning module:"]}, {"cell_type": "code", "execution_count": 7, "id": "5733f772", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:41.724998Z", "iopub.status.busy": "2021-09-16T12:40:41.717399Z", "iopub.status.idle": "2021-09-16T12:40:41.727028Z", "shell.execute_reply": "2021-09-16T12:40:41.726628Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.034742, "end_time": "2021-09-16T12:40:41.727126", "exception": false, "start_time": "2021-09-16T12:40:41.692384", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class DeepEnergyModel(pl.LightningModule):\n", " def __init__(self, img_shape, batch_size, alpha=0.1, lr=1e-4, beta1=0.0, **CNN_args):\n", " super().__init__()\n", " self.save_hyperparameters()\n", "\n", " self.cnn = CNNModel(**CNN_args)\n", " self.sampler = Sampler(self.cnn, img_shape=img_shape, sample_size=batch_size)\n", " self.example_input_array = torch.zeros(1, *img_shape)\n", "\n", " def forward(self, x):\n", " z = self.cnn(x)\n", " return z\n", "\n", " def configure_optimizers(self):\n", " # Energy models can have issues with momentum as the loss surfaces changes with its parameters.\n", " # Hence, we set it to 0 by default.\n", " optimizer = optim.Adam(self.parameters(), lr=self.hparams.lr, betas=(self.hparams.beta1, 0.999))\n", " scheduler = optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.97) # Exponential decay over epochs\n", " return [optimizer], [scheduler]\n", "\n", " def training_step(self, batch, batch_idx):\n", " # We add minimal noise to the original images to prevent the model from focusing on purely \"clean\" inputs\n", " real_imgs, _ = batch\n", " small_noise = torch.randn_like(real_imgs) * 0.005\n", " real_imgs.add_(small_noise).clamp_(min=-1.0, max=1.0)\n", "\n", " # Obtain samples\n", " fake_imgs = self.sampler.sample_new_exmps(steps=60, step_size=10)\n", "\n", " # Predict energy score for all images\n", " inp_imgs = torch.cat([real_imgs, fake_imgs], dim=0)\n", " real_out, fake_out = self.cnn(inp_imgs).chunk(2, dim=0)\n", "\n", " # Calculate losses\n", " reg_loss = self.hparams.alpha * (real_out ** 2 + fake_out ** 2).mean()\n", " cdiv_loss = fake_out.mean() - real_out.mean()\n", " loss = reg_loss + cdiv_loss\n", "\n", " # Logging\n", " self.log(\"loss\", loss)\n", " self.log(\"loss_regularization\", reg_loss)\n", " self.log(\"loss_contrastive_divergence\", cdiv_loss)\n", " self.log(\"metrics_avg_real\", real_out.mean())\n", " self.log(\"metrics_avg_fake\", fake_out.mean())\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " # For validating, we calculate the contrastive divergence between purely random images and unseen examples\n", " # Note that the validation/test step of energy-based models depends on what we are interested in the model\n", " real_imgs, _ = batch\n", " fake_imgs = torch.rand_like(real_imgs) * 2 - 1\n", "\n", " inp_imgs = torch.cat([real_imgs, fake_imgs], dim=0)\n", " real_out, fake_out = self.cnn(inp_imgs).chunk(2, dim=0)\n", "\n", " cdiv = fake_out.mean() - real_out.mean()\n", " self.log(\"val_contrastive_divergence\", cdiv)\n", " self.log(\"val_fake_out\", fake_out.mean())\n", " self.log(\"val_real_out\", real_out.mean())"]}, {"cell_type": "markdown", "id": "1ae0ae51", "metadata": {"papermill": {"duration": 0.022675, "end_time": "2021-09-16T12:40:41.772185", "exception": false, "start_time": "2021-09-16T12:40:41.749510", "status": "completed"}, "tags": []}, "source": ["We do not implement a test step because energy-based, generative models are usually not evaluated on a test set.\n", "The validation step however is used to get an idea of the difference between ennergy/likelihood\n", "of random images to unseen examples of the dataset."]}, {"cell_type": "markdown", "id": "6ae9a058", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.022224, "end_time": "2021-09-16T12:40:41.816602", "exception": false, "start_time": "2021-09-16T12:40:41.794378", "status": "completed"}, "tags": []}, "source": ["### Callbacks\n", "\n", "To track the performance of our model during training, we will make extensive use of PyTorch Lightning's callback framework.\n", "Remember that callbacks can be used for running small functions at any point of the training,\n", "for instance after finishing an epoch.\n", "Here, we will use three different callbacks we define ourselves.\n", "\n", "The first callback, called `GenerateCallback`, is used for adding image generations to the model during training.\n", "After every $N$ epochs (usually $N=5$ to reduce output to TensorBoard), we take a small batch\n", "of random images and perform many MCMC iterations until the model's generation converges.\n", "Compared to the training that used 60 iterations, we use 256 here because\n", "(1) we only have to do it once compared to the training that has to do it every iteration, and\n", "(2) we do not start from a buffer here, but from scratch.\n", "It is implemented as follows:"]}, {"cell_type": "code", "execution_count": 8, "id": "553fb562", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:41.868808Z", "iopub.status.busy": "2021-09-16T12:40:41.868332Z", "iopub.status.idle": "2021-09-16T12:40:41.870405Z", "shell.execute_reply": "2021-09-16T12:40:41.870008Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.031581, "end_time": "2021-09-16T12:40:41.870500", "exception": false, "start_time": "2021-09-16T12:40:41.838919", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GenerateCallback(pl.Callback):\n", " def __init__(self, batch_size=8, vis_steps=8, num_steps=256, every_n_epochs=5):\n", " super().__init__()\n", " self.batch_size = batch_size # Number of images to generate\n", " self.vis_steps = vis_steps # Number of steps within generation to visualize\n", " self.num_steps = num_steps # Number of steps to take during generation\n", " # Only save those images every N epochs (otherwise tensorboard gets quite large)\n", " self.every_n_epochs = every_n_epochs\n", "\n", " def on_epoch_end(self, trainer, pl_module):\n", " # Skip for all other epochs\n", " if trainer.current_epoch % self.every_n_epochs == 0:\n", " # Generate images\n", " imgs_per_step = self.generate_imgs(pl_module)\n", " # Plot and add to tensorboard\n", " for i in range(imgs_per_step.shape[1]):\n", " step_size = self.num_steps // self.vis_steps\n", " imgs_to_plot = imgs_per_step[step_size - 1 :: step_size, i]\n", " grid = torchvision.utils.make_grid(\n", " imgs_to_plot, nrow=imgs_to_plot.shape[0], normalize=True, range=(-1, 1)\n", " )\n", " trainer.logger.experiment.add_image(\"generation_%i\" % i, grid, global_step=trainer.current_epoch)\n", "\n", " def generate_imgs(self, pl_module):\n", " pl_module.eval()\n", " start_imgs = torch.rand((self.batch_size,) + pl_module.hparams[\"img_shape\"]).to(pl_module.device)\n", " start_imgs = start_imgs * 2 - 1\n", " imgs_per_step = Sampler.generate_samples(\n", " pl_module.cnn, start_imgs, steps=self.num_steps, step_size=10, return_img_per_step=True\n", " )\n", " pl_module.train()\n", " return imgs_per_step"]}, {"cell_type": "markdown", "id": "10fb6c28", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.022491, "end_time": "2021-09-16T12:40:41.915420", "exception": false, "start_time": "2021-09-16T12:40:41.892929", "status": "completed"}, "tags": []}, "source": ["The second callback is called `SamplerCallback`, and simply adds a randomly picked subset of images\n", "in the sampling buffer to the TensorBoard.\n", "This helps to understand what images are currently shown to the model as \"fake\"."]}, {"cell_type": "code", "execution_count": 9, "id": "d973c3d0", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:41.965194Z", "iopub.status.busy": "2021-09-16T12:40:41.964725Z", "iopub.status.idle": "2021-09-16T12:40:41.966940Z", "shell.execute_reply": "2021-09-16T12:40:41.966479Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.029353, "end_time": "2021-09-16T12:40:41.967038", "exception": false, "start_time": "2021-09-16T12:40:41.937685", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class SamplerCallback(pl.Callback):\n", " def __init__(self, num_imgs=32, every_n_epochs=5):\n", " super().__init__()\n", " self.num_imgs = num_imgs # Number of images to plot\n", " # Only save those images every N epochs (otherwise tensorboard gets quite large)\n", " self.every_n_epochs = every_n_epochs\n", "\n", " def on_epoch_end(self, trainer, pl_module):\n", " if trainer.current_epoch % self.every_n_epochs == 0:\n", " exmp_imgs = torch.cat(random.choices(pl_module.sampler.examples, k=self.num_imgs), dim=0)\n", " grid = torchvision.utils.make_grid(exmp_imgs, nrow=4, normalize=True, range=(-1, 1))\n", " trainer.logger.experiment.add_image(\"sampler\", grid, global_step=trainer.current_epoch)"]}, {"cell_type": "markdown", "id": "adaa80d7", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.022387, "end_time": "2021-09-16T12:40:42.011824", "exception": false, "start_time": "2021-09-16T12:40:41.989437", "status": "completed"}, "tags": []}, "source": ["Finally, our last callback is `OutlierCallback`.\n", "This callback evaluates the model by recording the (negative) energy assigned to random noise.\n", "While our training loss is almost constant across iterations,\n", "this score is likely showing the progress of the model to detect \"outliers\"."]}, {"cell_type": "code", "execution_count": 10, "id": "9ac8745d", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:42.064684Z", "iopub.status.busy": "2021-09-16T12:40:42.064208Z", "iopub.status.idle": "2021-09-16T12:40:42.066806Z", "shell.execute_reply": "2021-09-16T12:40:42.066321Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.03245, "end_time": "2021-09-16T12:40:42.066912", "exception": false, "start_time": "2021-09-16T12:40:42.034462", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class OutlierCallback(pl.Callback):\n", " def __init__(self, batch_size=1024):\n", " super().__init__()\n", " self.batch_size = batch_size\n", "\n", " def on_epoch_end(self, trainer, pl_module):\n", " with torch.no_grad():\n", " pl_module.eval()\n", " rand_imgs = torch.rand((self.batch_size,) + pl_module.hparams[\"img_shape\"]).to(pl_module.device)\n", " rand_imgs = rand_imgs * 2 - 1.0\n", " rand_out = pl_module.cnn(rand_imgs).mean()\n", " pl_module.train()\n", "\n", " trainer.logger.experiment.add_scalar(\"rand_out\", rand_out, global_step=trainer.current_epoch)"]}, {"cell_type": "markdown", "id": "70834e47", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.022843, "end_time": "2021-09-16T12:40:42.112671", "exception": false, "start_time": "2021-09-16T12:40:42.089828", "status": "completed"}, "tags": []}, "source": ["### Running the model\n", "\n", "Finally, we can add everything together to create our final training function.\n", "The function is very similar to any other PyTorch Lightning training function we have seen so far.\n", "However, there is the small difference of that we do not test the model on a test set\n", "because we will analyse the model afterward by checking its prediction and ability to perform outlier detection."]}, {"cell_type": "code", "execution_count": 11, "id": "5561a42f", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:42.164214Z", "iopub.status.busy": "2021-09-16T12:40:42.163735Z", "iopub.status.idle": "2021-09-16T12:40:42.165765Z", "shell.execute_reply": "2021-09-16T12:40:42.165284Z"}, "papermill": {"duration": 0.030454, "end_time": "2021-09-16T12:40:42.165867", "exception": false, "start_time": "2021-09-16T12:40:42.135413", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_model(**kwargs):\n", " # Create a PyTorch Lightning trainer with the generation callback\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, \"MNIST\"),\n", " gpus=1 if str(device).startswith(\"cuda\") else 0,\n", " max_epochs=60,\n", " gradient_clip_val=0.1,\n", " callbacks=[\n", " ModelCheckpoint(save_weights_only=True, mode=\"min\", monitor=\"val_contrastive_divergence\"),\n", " GenerateCallback(every_n_epochs=5),\n", " SamplerCallback(every_n_epochs=5),\n", " OutlierCallback(),\n", " LearningRateMonitor(\"epoch\"),\n", " ],\n", " progress_bar_refresh_rate=1,\n", " )\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"MNIST.ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model, loading...\")\n", " model = DeepEnergyModel.load_from_checkpoint(pretrained_filename)\n", " else:\n", " pl.seed_everything(42)\n", " model = DeepEnergyModel(**kwargs)\n", " trainer.fit(model, train_loader, test_loader)\n", " model = DeepEnergyModel.load_from_checkpoint(trainer.checkpoint_callback.best_model_path)\n", " # No testing as we are more interested in other properties\n", " return model"]}, {"cell_type": "code", "execution_count": 12, "id": "0d734155", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:42.214793Z", "iopub.status.busy": "2021-09-16T12:40:42.214331Z", "iopub.status.idle": "2021-09-16T12:40:42.228657Z", "shell.execute_reply": "2021-09-16T12:40:42.229038Z"}, "papermill": {"duration": 0.040314, "end_time": "2021-09-16T12:40:42.229150", "exception": false, "start_time": "2021-09-16T12:40:42.188836", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}], "source": ["model = train_model(img_shape=(1, 28, 28), batch_size=train_loader.batch_size, lr=1e-4, beta1=0.0)"]}, {"cell_type": "markdown", "id": "3de610c0", "metadata": {"papermill": {"duration": 0.023634, "end_time": "2021-09-16T12:40:42.276465", "exception": false, "start_time": "2021-09-16T12:40:42.252831", "status": "completed"}, "tags": []}, "source": ["## Analysis\n", "\n", "In the last part of the notebook, we will try to take the trained energy-based generative model,\n", "and analyse its properties."]}, {"cell_type": "markdown", "id": "879c2039", "metadata": {"papermill": {"duration": 0.023472, "end_time": "2021-09-16T12:40:42.323822", "exception": false, "start_time": "2021-09-16T12:40:42.300350", "status": "completed"}, "tags": []}, "source": ["### TensorBoard\n", "\n", "The first thing we can look at is the TensorBoard generate during training.\n", "This can help us to understand the training dynamic even better, and shows potential issues.\n", "Let's load the TensorBoard below:"]}, {"cell_type": "code", "execution_count": 13, "id": "c4ffadfc", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:42.374084Z", "iopub.status.busy": "2021-09-16T12:40:42.373589Z", "iopub.status.idle": "2021-09-16T12:40:42.375710Z", "shell.execute_reply": "2021-09-16T12:40:42.375247Z"}, "papermill": {"duration": 0.028238, "end_time": "2021-09-16T12:40:42.375807", "exception": false, "start_time": "2021-09-16T12:40:42.347569", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Uncomment the following two lines to open a tensorboard in the notebook.\n", "# Adjust the path to your CHECKPOINT_PATH if needed.\n", "# %load_ext tensorboard\n", "# %tensorboard --logdir ../saved_models/tutorial8/tensorboards/"]}, {"cell_type": "markdown", "id": "c7b8f333", "metadata": {"papermill": {"duration": 0.023692, "end_time": "2021-09-16T12:40:42.423585", "exception": false, "start_time": "2021-09-16T12:40:42.399893", "status": "completed"}, "tags": []}, "source": ["
"]}, {"cell_type": "markdown", "id": "e608ca53", "metadata": {"papermill": {"duration": 0.023833, "end_time": "2021-09-16T12:40:42.471015", "exception": false, "start_time": "2021-09-16T12:40:42.447182", "status": "completed"}, "tags": []}, "source": ["We see that the contrastive divergence as well as the regularization converge quickly to 0.\n", "However, the training continues although the loss is always close to zero.\n", "This is because our \"training\" data changes with the model by sampling.\n", "The progress of training can be best measured by looking at the samples across iterations,\n", "and the score for random images that decreases constantly over time."]}, {"cell_type": "markdown", "id": "a1184e57", "metadata": {"papermill": {"duration": 0.023719, "end_time": "2021-09-16T12:40:42.518253", "exception": false, "start_time": "2021-09-16T12:40:42.494534", "status": "completed"}, "tags": []}, "source": ["### Image Generation\n", "\n", "Another way of evaluating generative models is by sampling a few generated images.\n", "Generative models need to be good at generating realistic images as this truely shows that they have modeled the true data distribution.\n", "Thus, let's sample a few images of the model below:"]}, {"cell_type": "code", "execution_count": 14, "id": "f84da507", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:42.570336Z", "iopub.status.busy": "2021-09-16T12:40:42.569866Z", "iopub.status.idle": "2021-09-16T12:40:45.522168Z", "shell.execute_reply": "2021-09-16T12:40:45.521632Z"}, "papermill": {"duration": 2.980431, "end_time": "2021-09-16T12:40:45.522288", "exception": false, "start_time": "2021-09-16T12:40:42.541857", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 43\n"]}], "source": ["model.to(device)\n", "pl.seed_everything(43)\n", "callback = GenerateCallback(batch_size=4, vis_steps=8, num_steps=256)\n", "imgs_per_step = callback.generate_imgs(model)\n", "imgs_per_step = imgs_per_step.cpu()"]}, {"cell_type": "markdown", "id": "96a6dec4", "metadata": {"papermill": {"duration": 0.260591, "end_time": "2021-09-16T12:40:45.862389", "exception": false, "start_time": "2021-09-16T12:40:45.601798", "status": "completed"}, "tags": []}, "source": ["The characteristic of sampling with energy-based models is that they require the iterative MCMC algorithm.\n", "To gain an insight in how the images change over iterations, we plot a few intermediate samples in the MCMC as well:"]}, {"cell_type": "code", "execution_count": 15, "id": "8a53c31a", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:46.095876Z", "iopub.status.busy": "2021-09-16T12:40:46.095395Z", "iopub.status.idle": "2021-09-16T12:40:46.738218Z", "shell.execute_reply": "2021-09-16T12:40:46.738604Z"}, "papermill": {"duration": 0.675706, "end_time": "2021-09-16T12:40:46.738750", "exception": false, "start_time": "2021-09-16T12:40:46.063044", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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cmVlIC9mb3VyIC9maXZlIC9zaXggNTYgL2VpZ2h0IC9uaW5lIDcxCi9HIDk3IC9hIDEwMSAvZSAxMDUgL2kgMTEwIC9uIC9vIDExNCAvciAxMTYgL3QgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE1IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvRyAxOCAwIFIgL2EgMTkgMCBSIC9lIDIwIDAgUiAvZWlnaHQgMjEgMCBSIC9maXZlIDIyIDAgUiAvZm91ciAyMyAwIFIKL2kgMjQgMCBSIC9uIDI1IDAgUiAvbmluZSAyNiAwIFIgL28gMjcgMCBSIC9vbmUgMjggMCBSIC9yIDI5IDAgUgovc2l4IDMwIDAgUiAvc3BhY2UgMzEgMCBSIC90IDMyIDAgUiAvdGhyZWUgMzMgMCBSIC90d28gMzQgMCBSIC96ZXJvIDM1IDAgUgo+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMjU1ICj////+/v79/f38/Pz7+/v6+vr5+fn4+Pj39/f29vb19fX09PTz8/Py8vLx8fHw8PDv7+/u7u7t7e3s7Ozr6+vq6urp6eno6Ojn5+fm5ubl5eXk5OTj4+Pi4uLh4eHg4ODf39/e3t7d3d3c3Nzb29va2trZ2dnY2NjX19fW1tbV1dXU1NTT09PS0tLR0dHQ0NDPz8/Ozs7Nzc3MzMzLy8vKysrJycnIyMjHx8fGxsbFxcXExMTDw8PCwsLBwcHAwMC/v7++vr69vb28vLy7u7u6urq5ubm4uLi3t7e2tra1tbW0tLSzs7OysrKxsbGwsLCvr6+urq6tra2srKyrq6uqqqqpqamoqKinp6empqalpaWkpKSjo6OioqKhoaGgoKCfn5+enp6dnZ2cnJybm5uampqZmZmYmJiXl5eWlpaVlZWUlJSTk5OSkpKRkZGQkJCPj4+Ojo6NjY2MjIyLi4uKioqJiYmIiIiHh4eGhoaFhYWEhISDg4OCgoKBgYGAgIB/f39+fn59fX18fHx7e3t6enp5eXl4eHh3d3d2dnZ1dXV0dHRzc3NycnJxcXFwcHBvb29ubm5tbW1sbGxra2tqamppaWloaGhnZ2dmZmZlZWVkZGRjY2NiYmJhYWFgYGBfX19eXl5dXV1cXFxcXFxbW1taWlpZWVlYWFhXV1dWVlZVVVVUVFRTU1NSUlJRUVFQUFBPT09OTk5NTU1MTExLS0tKSkpJSUlISEhHR0dGRkZFRUVERERDQ0NCQkJBQUFAQEA/Pz8+Pj49PT08PDw7Ozs6Ojo5OTk4ODg3Nzc2NjY1NTU0NDQzMzMyMjIxMTEwMDAvLy8uLi4tLS0sLCwr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MDAwODIzNyAwMDAwMCBuIAowMDAwMDA4MjU4IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwMCAwMDAwMCBuIAowMDAwMDAxMDIyIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMTAwMiAwMDAwMCBuIAowMDAwMDA4MjkwIDAwMDAwIG4gCjAwMDAwMDY4MDAgMDAwMDAgbiAKMDAwMDAwNjYwMCAwMDAwMCBuIAowMDAwMDA2MTg4IDAwMDAwIG4gCjAwMDAwMDc4NTMgMDAwMDAgbiAKMDAwMDAwMTA0MiAwMDAwMCBuIAowMDAwMDAxMzYyIDAwMDAwIG4gCjAwMDAwMDE3NDIgMDAwMDAgbiAKMDAwMDAwMjA2NCAwMDAwMCBuIAowMDAwMDAyNTMyIDAwMDAwIG4gCjAwMDAwMDI4NTQgMDAwMDAgbiAKMDAwMDAwMzAyMCAwMDAwMCBuIAowMDAwMDAzMTY0IDAwMDAwIG4gCjAwMDAwMDM0MDAgMDAwMDAgbiAKMDAwMDAwMzc5NSAwMDAwMCBuIAowMDAwMDA0MDg2IDAwMDAwIG4gCjAwMDAwMDQyNDEgMDAwMDAgbiAKMDAwMDAwNDQ3NCAwMDAwMCBuIAowMDAwMDA0ODY3IDAwMDAwIG4gCjAwMDAwMDQ5NTcgMDAwMDAgbiAKMDAwMDAwNTE2MyAwMDAwMCBuIAowMDAwMDA1NTc2IDAwMDAwIG4gCjAwMDAwMDU5MDAgMDAwMDAgbiAKMDAwMDAxODUyOSAwMDAwMCBuIAowMDAwMDE4NjEwIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDM4ID4+CnN0YXJ0eHJlZgoxODc2NwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:40:46.157923\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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cmVlIC9mb3VyIC9maXZlIC9zaXggNTYgL2VpZ2h0IC9uaW5lIDcxCi9HIDk3IC9hIDEwMSAvZSAxMDUgL2kgMTEwIC9uIC9vIDExNCAvciAxMTYgL3QgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE1IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvRyAxOCAwIFIgL2EgMTkgMCBSIC9lIDIwIDAgUiAvZWlnaHQgMjEgMCBSIC9maXZlIDIyIDAgUiAvZm91ciAyMyAwIFIKL2kgMjQgMCBSIC9uIDI1IDAgUiAvbmluZSAyNiAwIFIgL28gMjcgMCBSIC9vbmUgMjggMCBSIC9yIDI5IDAgUgovc2l4IDMwIDAgUiAvc3BhY2UgMzEgMCBSIC90IDMyIDAgUiAvdGhyZWUgMzMgMCBSIC90d28gMzQgMCBSIC96ZXJvIDM1IDAgUgo+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMjU1ICj////+/v79/f38/Pz7+/v6+vr5+fn4+Pj39/f29vb19fX09PTz8/Py8vLx8fHw8PDv7+/u7u7t7e3s7Ozr6+vq6urp6eno6Ojn5+fm5ubl5eXk5OTj4+Pi4uLh4eHg4ODf39/e3t7d3d3c3Nzb29va2trZ2dnY2NjX19fW1tbV1dXU1NTT09PS0tLR0dHQ0NDPz8/Ozs7Nzc3MzMzLy8vKysrJycnIyMjHx8fGxsbFxcXExMTDw8PCwsLBwcHAwMC/v7++vr69vb28vLy7u7u6urq5ubm4uLi3t7e2tra1tbW0tLSzs7OysrKxsbGwsLCvr6+urq6tra2srKyrq6uqqqqpqamoqKinp6empqalpaWkpKSjo6OioqKhoaGgoKCfn5+enp6dnZ2cnJybm5uampqZmZmYmJiXl5eWlpaVlZWUlJSTk5OSkpKRkZGQkJCPj4+Ojo6NjY2MjIyLi4uKioqJiYmIiIiHh4eGhoaFhYWEhISDg4OCgoKBgYGAgIB/f39+fn59fX18fHx7e3t6enp5eXl4eHh3d3d2dnZ1dXV0dHRzc3NycnJxcXFwcHBvb29ubm5tbW1sbGxra2tqamppaWloaGhnZ2dmZmZlZWVkZGRjY2NiYmJhYWFgYGBfX19eXl5dXV1cXFxcXFxbW1taWlpZWVlYWFhXV1dWVlZVVVVUVFRTU1NSUlJRUVFQUFBPT09OTk5NTU1MTExLS0tKSkpJSUlISEhHR0dGRkZFRUVERERDQ0NCQkJBQUFAQEA/Pz8+Pj49PT08PDw7Ozs6Ojo5OTk4ODg3Nzc2NjY1NTU0NDQzMzMyMjIxMTEwMDAvLy8uLi4tLS0sLCwr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IDAwMDAwIG4gCjAwMDAwMDEwMDIgMDAwMDAgbiAKMDAwMDAwODI5MCAwMDAwMCBuIAowMDAwMDA2ODAwIDAwMDAwIG4gCjAwMDAwMDY2MDAgMDAwMDAgbiAKMDAwMDAwNjE4OCAwMDAwMCBuIAowMDAwMDA3ODUzIDAwMDAwIG4gCjAwMDAwMDEwNDIgMDAwMDAgbiAKMDAwMDAwMTM2MiAwMDAwMCBuIAowMDAwMDAxNzQyIDAwMDAwIG4gCjAwMDAwMDIwNjQgMDAwMDAgbiAKMDAwMDAwMjUzMiAwMDAwMCBuIAowMDAwMDAyODU0IDAwMDAwIG4gCjAwMDAwMDMwMjAgMDAwMDAgbiAKMDAwMDAwMzE2NCAwMDAwMCBuIAowMDAwMDAzNDAwIDAwMDAwIG4gCjAwMDAwMDM3OTUgMDAwMDAgbiAKMDAwMDAwNDA4NiAwMDAwMCBuIAowMDAwMDA0MjQxIDAwMDAwIG4gCjAwMDAwMDQ0NzQgMDAwMDAgbiAKMDAwMDAwNDg2NyAwMDAwMCBuIAowMDAwMDA0OTU3IDAwMDAwIG4gCjAwMDAwMDUxNjMgMDAwMDAgbiAKMDAwMDAwNTU3NiAwMDAwMCBuIAowMDAwMDA1OTAwIDAwMDAwIG4gCjAwMDAwMTg0MjIgMDAwMDAgbiAKMDAwMDAxODUwMyAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOCA+PgpzdGFydHhyZWYKMTg2NjAKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:40:46.343108\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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cmVlIC9mb3VyIC9maXZlIC9zaXggNTYgL2VpZ2h0IC9uaW5lIDcxCi9HIDk3IC9hIDEwMSAvZSAxMDUgL2kgMTEwIC9uIC9vIDExNCAvciAxMTYgL3QgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE1IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvRyAxOCAwIFIgL2EgMTkgMCBSIC9lIDIwIDAgUiAvZWlnaHQgMjEgMCBSIC9maXZlIDIyIDAgUiAvZm91ciAyMyAwIFIKL2kgMjQgMCBSIC9uIDI1IDAgUiAvbmluZSAyNiAwIFIgL28gMjcgMCBSIC9vbmUgMjggMCBSIC9yIDI5IDAgUgovc2l4IDMwIDAgUiAvc3BhY2UgMzEgMCBSIC90IDMyIDAgUiAvdGhyZWUgMzMgMCBSIC90d28gMzQgMCBSIC96ZXJvIDM1IDAgUgo+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMjU1ICj////+/v79/f38/Pz7+/v6+vr5+fn4+Pj39/f29vb19fX09PTz8/Py8vLx8fHw8PDv7+/u7u7t7e3s7Ozr6+vq6urp6eno6Ojn5+fm5ubl5eXk5OTj4+Pi4uLh4eHg4ODf39/e3t7d3d3c3Nzb29va2trZ2dnY2NjX19fW1tbV1dXU1NTT09PS0tLR0dHQ0NDPz8/Ozs7Nzc3MzMzLy8vKysrJycnIyMjHx8fGxsbFxcXExMTDw8PCwsLBwcHAwMC/v7++vr69vb28vLy7u7u6urq5ubm4uLi3t7e2tra1tbW0tLSzs7OysrKxsbGwsLCvr6+urq6tra2srKyrq6uqqqqpqamoqKinp6empqalpaWkpKSjo6OioqKhoaGgoKCfn5+enp6dnZ2cnJybm5uampqZmZmYmJiXl5eWlpaVlZWUlJSTk5OSkpKRkZGQkJCPj4+Ojo6NjY2MjIyLi4uKioqJiYmIiIiHh4eGhoaFhYWEhISDg4OCgoKBgYGAgIB/f39+fn59fX18fHx7e3t6enp5eXl4eHh3d3d2dnZ1dXV0dHRzc3NycnJxcXFwcHBvb29ubm5tbW1sbGxra2tqamppaWloaGhnZ2dmZmZlZWVkZGRjY2NiYmJhYWFgYGBfX19eXl5dXV1cXFxcXFxbW1taWlpZWVlYWFhXV1dWVlZVVVVUVFRTU1NSUlJRUVFQUFBPT09OTk5NTU1MTExLS0tKSkpJSUlISEhHR0dGRkZFRUVERERDQ0NCQkJBQUFAQEA/Pz8+Pj49PT08PDw7Ozs6Ojo5OTk4ODg3Nzc2NjY1NTU0NDQzMzMyMjIxMTEwMDAvLy8uLi4tLS0sLCwr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biAKMDAwMDAwNjYwMCAwMDAwMCBuIAowMDAwMDA2MTg4IDAwMDAwIG4gCjAwMDAwMDc4NTMgMDAwMDAgbiAKMDAwMDAwMTA0MiAwMDAwMCBuIAowMDAwMDAxMzYyIDAwMDAwIG4gCjAwMDAwMDE3NDIgMDAwMDAgbiAKMDAwMDAwMjA2NCAwMDAwMCBuIAowMDAwMDAyNTMyIDAwMDAwIG4gCjAwMDAwMDI4NTQgMDAwMDAgbiAKMDAwMDAwMzAyMCAwMDAwMCBuIAowMDAwMDAzMTY0IDAwMDAwIG4gCjAwMDAwMDM0MDAgMDAwMDAgbiAKMDAwMDAwMzc5NSAwMDAwMCBuIAowMDAwMDA0MDg2IDAwMDAwIG4gCjAwMDAwMDQyNDEgMDAwMDAgbiAKMDAwMDAwNDQ3NCAwMDAwMCBuIAowMDAwMDA0ODY3IDAwMDAwIG4gCjAwMDAwMDQ5NTcgMDAwMDAgbiAKMDAwMDAwNTE2MyAwMDAwMCBuIAowMDAwMDA1NTc2IDAwMDAwIG4gCjAwMDAwMDU5MDAgMDAwMDAgbiAKMDAwMDAxODM1NSAwMDAwMCBuIAowMDAwMDE4NDM2IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDM4ID4+CnN0YXJ0eHJlZgoxODU5MwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:40:46.488312\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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cmVlIC9mb3VyIC9maXZlIC9zaXggNTYgL2VpZ2h0IC9uaW5lIDcxCi9HIDk3IC9hIDEwMSAvZSAxMDUgL2kgMTEwIC9uIC9vIDExNCAvciAxMTYgL3QgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE1IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvRyAxOCAwIFIgL2EgMTkgMCBSIC9lIDIwIDAgUiAvZWlnaHQgMjEgMCBSIC9maXZlIDIyIDAgUiAvZm91ciAyMyAwIFIKL2kgMjQgMCBSIC9uIDI1IDAgUiAvbmluZSAyNiAwIFIgL28gMjcgMCBSIC9vbmUgMjggMCBSIC9yIDI5IDAgUgovc2l4IDMwIDAgUiAvc3BhY2UgMzEgMCBSIC90IDMyIDAgUiAvdGhyZWUgMzMgMCBSIC90d28gMzQgMCBSIC96ZXJvIDM1IDAgUgo+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMjU1ICj////+/v79/f38/Pz7+/v6+vr5+fn4+Pj39/f29vb19fX09PTz8/Py8vLx8fHw8PDv7+/u7u7t7e3s7Ozr6+vq6urp6eno6Ojn5+fm5ubl5eXk5OTj4+Pi4uLh4eHg4ODf39/e3t7d3d3c3Nzb29va2trZ2dnY2NjX19fW1tbV1dXU1NTT09PS0tLR0dHQ0NDPz8/Ozs7Nzc3MzMzLy8vKysrJycnIyMjHx8fGxsbFxcXExMTDw8PCwsLBwcHAwMC/v7++vr69vb28vLy7u7u6urq5ubm4uLi3t7e2tra1tbW0tLSzs7OysrKxsbGwsLCvr6+urq6tra2srKyrq6uqqqqpqamoqKinp6empqalpaWkpKSjo6OioqKhoaGgoKCfn5+enp6dnZ2cnJybm5uampqZmZmYmJiXl5eWlpaVlZWUlJSTk5OSkpKRkZGQkJCPj4+Ojo6NjY2MjIyLi4uKioqJiYmIiIiHh4eGhoaFhYWEhISDg4OCgoKBgYGAgIB/f39+fn59fX18fHx7e3t6enp5eXl4eHh3d3d2dnZ1dXV0dHRzc3NycnJxcXFwcHBvb29ubm5tbW1sbGxra2tqamppaWloaGhnZ2dmZmZlZWVkZGRjY2NiYmJhYWFgYGBfX19eXl5dXV1cXFxcXFxbW1taWlpZWVlYWFhXV1dWVlZVVVVUVFRTU1NSUlJRUVFQUFBPT09OTk5NTU1MTExLS0tKSkpJSUlISEhHR0dGRkZFRUVERERDQ0NCQkJBQUFAQEA/Pz8+Pj49PT08PDw7Ozs6Ojo5OTk4ODg3Nzc2NjY1NTU0NDQzMzMyMjIxMTEwMDAvLy8uLi4tLS0sLCwr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NiAwIG9iago4NjgwCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozNyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MDQ2KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDM4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDE4MDIwIDAwMDAwIG4gCjAwMDAwMDgwODUgMDAwMDAgbiAKMDAwMDAwODExNyAwMDAwMCBuIAowMDAwMDA4MjE2IDAwMDAwIG4gCjAwMDAwMDgyMzcgMDAwMDAgbiAKMDAwMDAwODI1OCAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDAgMDAwMDAgbiAKMDAwMDAwMTAyMiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDEwMDIgMDAwMDAgbiAKMDAwMDAwODI5MCAwMDAwMCBuIAowMDAwMDA2ODAwIDAwMDAwIG4gCjAwMDAwMDY2MDAgMDAwMDAgbiAKMDAwMDAwNjE4OCAwMDAwMCBuIAowMDAwMDA3ODUzIDAwMDAwIG4gCjAwMDAwMDEwNDIgMDAwMDAgbiAKMDAwMDAwMTM2MiAwMDAwMCBuIAowMDAwMDAxNzQyIDAwMDAwIG4gCjAwMDAwMDIwNjQgMDAwMDAgbiAKMDAwMDAwMjUzMiAwMDAwMCBuIAowMDAwMDAyODU0IDAwMDAwIG4gCjAwMDAwMDMwMjAgMDAwMDAgbiAKMDAwMDAwMzE2NCAwMDAwMCBuIAowMDAwMDAzNDAwIDAwMDAwIG4gCjAwMDAwMDM3OTUgMDAwMDAgbiAKMDAwMDAwNDA4NiAwMDAwMCBuIAowMDAwMDA0MjQxIDAwMDAwIG4gCjAwMDAwMDQ0NzQgMDAwMDAgbiAKMDAwMDAwNDg2NyAwMDAwMCBuIAowMDAwMDA0OTU3IDAwMDAwIG4gCjAwMDAwMDUxNjMgMDAwMDAgbiAKMDAwMDAwNTU3NiAwMDAwMCBuIAowMDAwMDA1OTAwIDAwMDAwIG4gCjAwMDAwMTc5OTkgMDAwMDAgbiAKMDAwMDAxODA4MCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOCA+PgpzdGFydHhyZWYKMTgyMzcKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:40:46.639358\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["for i in range(imgs_per_step.shape[1]):\n", " step_size = callback.num_steps // callback.vis_steps\n", " imgs_to_plot = imgs_per_step[step_size - 1 :: step_size, i]\n", " imgs_to_plot = torch.cat([imgs_per_step[0:1, i], imgs_to_plot], dim=0)\n", " grid = torchvision.utils.make_grid(\n", " imgs_to_plot, nrow=imgs_to_plot.shape[0], normalize=True, range=(-1, 1), pad_value=0.5, padding=2\n", " )\n", " grid = grid.permute(1, 2, 0)\n", " plt.figure(figsize=(8, 8))\n", " plt.imshow(grid)\n", " plt.xlabel(\"Generation iteration\")\n", " plt.xticks(\n", " [(imgs_per_step.shape[-1] + 2) * (0.5 + j) for j in range(callback.vis_steps + 1)],\n", " labels=[1] + list(range(step_size, imgs_per_step.shape[0] + 1, step_size)),\n", " )\n", " plt.yticks([])\n", " plt.show()"]}, {"cell_type": "markdown", "id": "d85bacf3", "metadata": {"papermill": {"duration": 0.032666, "end_time": "2021-09-16T12:40:46.804930", "exception": false, "start_time": "2021-09-16T12:40:46.772264", "status": "completed"}, "tags": []}, "source": ["We see that although starting from noise in the very first step, the sampling algorithm obtains reasonable shapes after only 32 steps.\n", "Over the next 200 steps, the shapes become clearer and changed towards realistic digits.\n", "The specific samples can differ when you run the code on Colab, hence the following description is specific to the plots shown on the website.\n", "The first row shows an 8, where we remove unnecessary white parts over iterations.\n", "The transformation across iterations can be seen at best for the second sample, which creates a digit of 2.\n", "While the first sample after 32 iterations looks a bit like a digit, but not really,\n", "the sample is transformed more and more to a typical image of the digit 2."]}, {"cell_type": "markdown", "id": "9b5682ed", "metadata": {"papermill": {"duration": 0.03313, "end_time": "2021-09-16T12:40:46.870416", "exception": false, "start_time": "2021-09-16T12:40:46.837286", "status": "completed"}, "tags": []}, "source": ["### Out-of-distribution detection\n", "\n", "A very common and strong application of energy-based models is out-of-distribution detection\n", "(sometimes referred to as \"anomaly\" detection).\n", "As more and more deep learning models are applied in production and applications,\n", "a crucial aspect of these models is to know what the models don't know.\n", "Deep learning models are usually overconfident, meaning that they classify even random images sometimes with 100% probability.\n", "Clearly, this is not something that we want to see in applications.\n", "Energy-based models can help with this problem because they are trained to detect images that do not fit the training dataset distribution.\n", "Thus, in those applications, you could train an energy-based model along with the classifier,\n", "and only output predictions if the energy-based models assign a (unnormalized) probability higher than $\\delta$ to the image.\n", "You can actually combine classifiers and energy-based objectives in a single model,\n", "as proposed in this [paper](https://arxiv.org/abs/1912.03263).\n", "\n", "In this part of the analysis, we want to test the out-of-distribution capability of our energy-based model.\n", "Remember that a lower output of the model denotes a low probability.\n", "Thus, we hope to see low scores if we enter random noise to the model:"]}, {"cell_type": "code", "execution_count": 16, "id": "303c00f7", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:46.941073Z", "iopub.status.busy": "2021-09-16T12:40:46.940608Z", "iopub.status.idle": "2021-09-16T12:40:46.945276Z", "shell.execute_reply": "2021-09-16T12:40:46.944875Z"}, "papermill": {"duration": 0.041937, "end_time": "2021-09-16T12:40:46.945424", "exception": false, "start_time": "2021-09-16T12:40:46.903487", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Average score for random images: -17.88\n"]}], "source": ["with torch.no_grad():\n", " rand_imgs = torch.rand((128,) + model.hparams.img_shape).to(model.device)\n", " rand_imgs = rand_imgs * 2 - 1.0\n", " rand_out = model.cnn(rand_imgs).mean()\n", " print(\"Average score for random images: %4.2f\" % (rand_out.item()))"]}, {"cell_type": "markdown", "id": "0a485fdb", "metadata": {"papermill": {"duration": 0.032686, "end_time": "2021-09-16T12:40:47.012111", "exception": false, "start_time": "2021-09-16T12:40:46.979425", "status": "completed"}, "tags": []}, "source": ["As we hoped, the model assigns very low probability to those noisy images.\n", "As another reference, let's look at predictions for a batch of images from the training set:"]}, {"cell_type": "code", "execution_count": 17, "id": "2bcb1282", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:47.081066Z", "iopub.status.busy": "2021-09-16T12:40:47.080584Z", "iopub.status.idle": "2021-09-16T12:40:47.233910Z", "shell.execute_reply": "2021-09-16T12:40:47.233401Z"}, "papermill": {"duration": 0.189477, "end_time": "2021-09-16T12:40:47.234026", "exception": false, "start_time": "2021-09-16T12:40:47.044549", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Average score for training images: -0.00\n"]}], "source": ["with torch.no_grad():\n", " train_imgs, _ = next(iter(train_loader))\n", " train_imgs = train_imgs.to(model.device)\n", " train_out = model.cnn(train_imgs).mean()\n", " print(\"Average score for training images: %4.2f\" % (train_out.item()))"]}, {"cell_type": "markdown", "id": "a559009f", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.032727, "end_time": "2021-09-16T12:40:47.300644", "exception": false, "start_time": "2021-09-16T12:40:47.267917", "status": "completed"}, "tags": []}, "source": ["The scores are close to 0 because of the regularization objective that was added to the training.\n", "So clearly, the model can distinguish between noise and real digits.\n", "However, what happens if we change the training images a little, and see which ones gets a very low score?"]}, {"cell_type": "code", "execution_count": 18, "id": "edff6bb0", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:47.372822Z", "iopub.status.busy": "2021-09-16T12:40:47.372352Z", "iopub.status.idle": "2021-09-16T12:40:47.374505Z", "shell.execute_reply": "2021-09-16T12:40:47.374110Z"}, "papermill": {"duration": 0.041215, "end_time": "2021-09-16T12:40:47.374604", "exception": false, "start_time": "2021-09-16T12:40:47.333389", "status": "completed"}, "tags": []}, "outputs": [], "source": ["@torch.no_grad()\n", "def compare_images(img1, img2):\n", " imgs = torch.stack([img1, img2], dim=0).to(model.device)\n", " score1, score2 = model.cnn(imgs).cpu().chunk(2, dim=0)\n", " grid = torchvision.utils.make_grid(\n", " [img1.cpu(), img2.cpu()], nrow=2, normalize=True, range=(-1, 1), pad_value=0.5, padding=2\n", " )\n", " grid = grid.permute(1, 2, 0)\n", " plt.figure(figsize=(4, 4))\n", " plt.imshow(grid)\n", " plt.xticks([(img1.shape[2] + 2) * (0.5 + j) for j in range(2)], labels=[\"Original image\", \"Transformed image\"])\n", " plt.yticks([])\n", " plt.show()\n", " print(\"Score original image: %4.2f\" % score1)\n", " print(\"Score transformed image: %4.2f\" % score2)"]}, {"cell_type": "markdown", "id": "7c540cbd", "metadata": {"papermill": {"duration": 0.033108, "end_time": "2021-09-16T12:40:47.440512", "exception": false, "start_time": "2021-09-16T12:40:47.407404", "status": "completed"}, "tags": []}, "source": ["We use a random test image for this. Feel free to change it to experiment with the model yourself."]}, {"cell_type": "code", "execution_count": 19, "id": "befba0f4", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:47.509319Z", "iopub.status.busy": "2021-09-16T12:40:47.508856Z", "iopub.status.idle": "2021-09-16T12:40:47.685776Z", "shell.execute_reply": "2021-09-16T12:40:47.686151Z"}, "papermill": {"duration": 0.212897, "end_time": "2021-09-16T12:40:47.686303", "exception": false, "start_time": "2021-09-16T12:40:47.473406", "status": "completed"}, "tags": []}, "outputs": [], "source": ["test_imgs, _ = next(iter(test_loader))\n", "exmp_img = test_imgs[0].to(model.device)"]}, {"cell_type": "markdown", "id": "12364d29", "metadata": {"papermill": {"duration": 0.033603, "end_time": "2021-09-16T12:40:47.754499", "exception": false, "start_time": "2021-09-16T12:40:47.720896", "status": "completed"}, "tags": []}, "source": ["The first transformation is to add some random noise to the image:"]}, {"cell_type": "code", "execution_count": 20, "id": "bc21923d", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:47.824609Z", "iopub.status.busy": "2021-09-16T12:40:47.824143Z", "iopub.status.idle": "2021-09-16T12:40:47.919759Z", "shell.execute_reply": "2021-09-16T12:40:47.919292Z"}, "papermill": {"duration": 0.132328, "end_time": "2021-09-16T12:40:47.919863", "exception": false, "start_time": "2021-09-16T12:40:47.787535", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDI0MS4xIDE0Ni4yNzE4NzUgXSAvUGFyZW50IDIgMCBSIC9SZXNvdXJjZXMgOCAwIFIKL1R5cGUgL1BhZ2UgPj4KZW5kb2JqCjkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMiAwIFIgPj4Kc3RyZWFtCnicjVJNb4JQELzvr5ijHvp4+/g+amxNvTS0JD00PRhEihEMmNS/3+UrEWkbD5B9w+4wM28ZB7IWjOwMjYM8FzDWsFbpd56kr+slkjNpwQsyDiuW6thX7HjK+Bz4rkB6fPwiKklYpX0thBmxVj6MrYKuQ9hsW4VXyLFH2A6VHqB2aoQI8Z4q3NAZKQ2YXXnXKd5RwlqYxhSLKRZTemKqkikHjTVm75YwKWA9M1YnRBShGti0OGoYtQp6TkHI85Q78tYBWtmDalpKGBeqqMnvQf4IEyrf9QM2LkKJrS2SgpYxrCeJViPet7HHO/rA7KXOs7zcHpEX2yyd4xPxhh5jiqhVQuw7NxJ65F8NbDzVfrxDQTxvL7jpntXb8rw/1YL43fysSHdTaQabbqfasMbhT9ZhetP0NtmR4rcdkb47lmvouhr9iy2iHyt1pwQKZW5kc3RyZWFtCmVuZG9iagoxMiAwIG9iagozMzEKZW5kb2JqCjEwIDAgb2JqClsgXQplbmRvYmoKMTggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMTUgPj4Kc3RyZWFtCnicNVBLrgMxCNvnFL5ApUASCOeZqnqb3n/7MKiLEdbgH/HrmDiGlyz4EvhWvGWs2DBTfMdSLaR2YOtAdeFcxTPkCo5eiE3stOBctrlJpK4gQyJKI9tyQ5dQtCk6JX9vmlu6KbcnTZpu08rA1MuQsyOIGEoGS1DTtWjCou2p+J3yjL86ixd+xw4rdNzh01MR9T3DZz6IS73G9qjZmUS6L8iQ05pLCU002dHvyBTOPDekkM4gQVJcgmtlkP3pl6MDEjAxtyxAdleinCVpx9K/M3jS5x9hXFSNCmVuZHN0cmVhbQplbmRvYmoKMTkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA2NiA+PgpzdHJlYW0KeJwzMzRUMFDQNQISZoYmCuZGlgophlxAPoiVywUTywGzzEzMgCxjU1MklgGQNjI1g9MQGaABcAZEfwZXGgBSaxTACmVuZHN0cmVhbQplbmRvYmoKMjAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMDcgPj4Kc3RyZWFtCnicPZJLbgMxDEP3PoUuEMD62Z7zpCi6mN5/2ycl6Yoc2RZFapa6TFlTHpA0k4R/6fBwsZ3yO2zPZmbgWqKXieWU59AVYu6ifNnMRl1ZJ8XqhGY6t+hRORcHNk2qn6sspd0ueA7XJp5b9hE/vNCgHtQ1Lgk3dFejZSk0Y6r7f9J7/Iwy4GpMXWxSq3sfPF5EVejoB0eJImOXF+fjQQnpSsJoWoiVd0UDQe7ytMp7Ce7b3mrIsgepmM47KWaw63RSLm4XhyEeyPKo8OWj2GtCz/iwKyX0SNiGM3In7mjG5tTI4pD+3o0ES4+uaCHz4K9u1i5gvFM6RWJkTnKsaYtVTvdQFNO5w70MEPVsRUMpc5HV6l/DzgtrlmwWeEr6BR6j3SZLDlbZ26hO76082dD3H1rXdB8KZW5kc3RyZWFtCmVuZG9iagoyMSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIzMSA+PgpzdHJlYW0KeJw1TzmSBCEMy3mFPjBVGNtAv6entjbY+X+6kplOkPAhydMTHZl4mSMjsGbH21pkIGbgU0zFv/a0DxOq9+AeIpSLC2GGkXDWrONuno4X/3aVz1gH7zb4illeENjCTNZXFmcu2wVjaZzEOclujF0TsY11radTWEcwoQyEdLbDlCBzVKT0yY4y5ug4kSeei+/22yx2OX4O6ws2jSEV5/gqeoI2g6Lsee8CGnJB/13d+B5Fu+glIBsJFtZRYu6c5YRfvXZ0HrUoEnNCmkEuEyHN6SqmEJpQrLOjoFJRcKk+p+isn3/lX1wtCmVuZHN0cmVhbQplbmRvYmoKMjIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNDkgPj4Kc3RyZWFtCnicPVA7jkQhDOs5hS/wJPIjcB5Gqy1m79+uA5opUEx+tjMk0BGBRwwxlK/jJa2groG/i0LxbuLrg8Igq0NSIM56D4h07KY2kRM6HZwzP2E3Y47ARTEGnOl0pj0HJjn7wgqEcxtl7FZIJ4mqIo7qM44pnip7n3gWLO3INlsnkj3kIOFSUonJpZ+Uyj9typQKOmbRBCwSueBkE004y7tJUowZlDLqHqZ2In2sPMijOuhkTc6sI5nZ00/bmfgccLdf2mROlcd0Hsz4nLTOgzkVuvfjiTYHTY3a6Oz3E2kqL1K7HVqdfnUSld0Y5xgSl2d/Gd9k//kH/odaIgplbmRzdHJlYW0KZW5kb2JqCjIzIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTM2ID4+CnN0cmVhbQp4nE2PQQ4DMQgD73mFn0AgQHjPVlUP2/9fS9h20wseyYBsUQaBJYd4hxvh0dsP30U2FWfjnF9SKWIhmE9wnzBTHI0pd/Jjj4BxlGosp2h4XkvOTcMXLXcTLaWtl5MZb7jul/dHlW2RDUXPLQtC12yS+TKBB3wYmEd142mlx932bK/2/ADObDRJCmVuZHN0cmVhbQplbmRvYmoKMjQgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzNDEgPj4Kc3RyZWFtCnicRVJLbkQxCNu/U3CBSOGXkPO0qrqY3n9bm0zVzeAJYGx4y1OmZMqwuSUjJNeUT30iQ6ym/DRyJCKm+EkJBXaVj8drS6yN7JGoFJ/a8eOx9Eam2RVa9e7Rpc2iUc3KyDnIEKGeFbqye9QO2fB6XEi675TNIRzL/1CBLGXdcgolQVvQd+wR3w8droIrgmGway6D7WUy1P/6hxZc7333YscugBas577BDgCopxO0BcgZ2u42KWgAVbqLScKj8npudqJso1Xp+RwAMw4wcsCIJVsdvtHeAJZ9XehFjYr9K0BRWUD8yNV2wd4xyUhwFuYGjr1wPMWZcEs4xgJAir3iGHrwJdjmL1euiJrwCXW6ZC+8wp7a5udCkwh3rQAOXmTDraujqJbt6TyC9mdFckaM1Is4OiGSWtI5guLSoB5a41w3seJtI7G5V9/uH+GcL1z26xdL7ITECmVuZHN0cmVhbQplbmRvYmoKMjUgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3MiA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlxAvqmJuUIuF0gMxMoBswyAtCWcgohngJggbRDFIBZEsZmJGUQdnAGRy+BKAwAl2xbJCmVuZHN0cmVhbQplbmRvYmoKMjYgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA0NyA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlyWEFYuF0wsB8wC0ZZwCiKewZUGALlnDScKZW5kc3RyZWFtCmVuZG9iagoyNyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI1OCA+PgpzdHJlYW0KeJxFkUtyBCAIRPeegiOA/OQ8k0plMbn/Ng3OZDZ2l6j9hEojphIs5xR5MH3J8s1ktul3OVY7GwUURSiYyVXosQKrO1PEmWuJautjZeS40zsGxRvOXTmpZHGjjHVUdSpwTM+V9VHd+XZZlH1HDmUK2KxzHGzgym3DGCdGm63uDveJIE8nU0fF7SDZ8AcnjX2VqytwnWz20UswDgT9QhOY5ItA6wyBxs1T9OQS7OPjdueBYG95EUjZEMiRIRgdgnadXP/i1vm9/3GGO8+1Ga4c7+J3mNZ2x19ikhVzAYvcKajnay5a1xk63pMzx+Sm+4bOuWCXu4NM7/k/1s/6/gMeKWb6CmVuZHN0cmVhbQplbmRvYmoKMjggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNjMgPj4Kc3RyZWFtCnicRZA7EgMhDEN7TqEj+CMDPs9mMik2929j2GxSwNNYIIO7E4LU2oKJ6IKHtiXdBe+tBGdj/Ok2bjUS5AR1gFak42iUUn25xWmVdPFoNnMrC60THWYOepSjGaAQOhXe7aLkcqbuzvlDcPVf9b9i3TmbiYHJyh0IzepT3Pk2O6K6usn+pMfcrNd+K+xVYWlZS8sJt527ZkAJ3FM52qs9Px8KOvYKZW5kc3RyZWFtCmVuZG9iagoyOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIxOCA+PgpzdHJlYW0KeJw9ULmNBDEMy12FGljAeu2pZxaLS6b/9Ej59iLRFkVSKjWZkikvdZQlWVPeOnyWxA55huVuZDYlKkUvk7Al99AK8X2J5hT33dWWs0M0l2g5fgszKqobHdNLNppwKhO6oNzDM/oNbXQDVocesVsg0KRg17YgcscPGAzBmROLIgxKTQb/rnKPn16LGz7D8UMUkZIO5jX/WP3ycw2vU48nkW5vvuJenKkOAxEckpq8I11YsS4SEWk1QU3PwFotgLu3Xv4btCO6DED2icRxmlKOob9rcKXPL+UnU9gKZW5kc3RyZWFtCmVuZG9iagozMCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE2MCA+PgpzdHJlYW0KeJxFkDkSAzEIBHO9gidIXIL3rMu1wfr/qQfWR6LpAjQcuhZNynoUaD7psUahutBr6CxKkkTBFpIdUKdjiDsoSExIY5JIth6DI5pYs12YmVQqs1LhtGnFwr/ZWtXIRI1wjfyJ6QZU/E/qXJTwTYOvkjH6GFS8O4OMSfheRdxaMe3+RDCxGfYJb0UmBYSJsanZvs9ghsz3Ctc4x/MNTII36wplbmRzdHJlYW0KZW5kb2JqCjMxIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzM0ID4+CnN0cmVhbQp4nC1SS3LFIAzbcwpdoDP4B+Q86XS6eL3/tpKTRUYOYPQx5YaJSnxZILej1sS3jcxAheGvq8yFz0jbyDqIy5CLuJIthXtELOQxxDzEgu+r8R4e+azMybMHxi/Zdw8r9tSEZSHjxRnaYRXHYRXkWLB1Iap7eFOkw6kk2OOL/z7Fcy0ELXxG0IBf5J+vjuD5khZp95ht0656sEw7qqSwHGxPc14mX1pnuToezwfJ9q7YEVK7AhSFuTPOc+Eo01ZGtBZ2NkhqXGxvjv1YStCFblxGiiOQn6kiPKCkycwmCuKPnB5yKgNh6pqudHIbVXGnnsw1m4u3M0lm675IsZnCeV04s/4MU2a1eSfPcqLUqQjvsWdL0NA5rp69lllodJsTvKSEz8ZOT06+VzPrITkVCaliWlfBaRSZYgnbEl9TUVOaehn++/Lu8Tt+/gEsc3xzCmVuZHN0cmVhbQplbmRvYmoKMzIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxOCA+PgpzdHJlYW0KeJwzNrRQMIDDFEOuNAAd5gNSCmVuZHN0cmVhbQplbmRvYmoKMTYgMCBvYmoKPDwgL0Jhc2VGb250IC9EZWphVnVTYW5zIC9DaGFyUHJvY3MgMTcgMCBSCi9FbmNvZGluZyA8PAovRGlmZmVyZW5jZXMgWyAzMiAvc3BhY2UgNzkgL08gODQgL1QgOTcgL2EgMTAwIC9kIC9lIC9mIC9nIDEwNSAvaSAxMDggL2wgL20gL24gL28gMTE0Ci9yIC9zIF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNSAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxNCAwIFIgPj4KZW5kb2JqCjE1IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTQgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL08gMTggMCBSIC9UIDE5IDAgUiAvYSAyMCAwIFIgL2QgMjEgMCBSIC9lIDIyIDAgUiAvZiAyMyAwIFIgL2cgMjQgMCBSCi9pIDI1IDAgUiAvbCAyNiAwIFIgL20gMjcgMCBSIC9uIDI4IDAgUiAvbyAyOSAwIFIgL3IgMzAgMCBSIC9zIDMxIDAgUgovc3BhY2UgMzIgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0kxIDEzIDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOAovQ29sb3JTcGFjZSBbL0luZGV4ZWQgL0RldmljZVJHQiAxNjAgKP////7+/v39/fz8/Pv7+/r6+vn5+fj4+PLy8vHx8fDw8O7u7uzs7Orq6unp6efn5+Xl5ePj4+Hh4eDg4N/f397e3t3d3dvb29ra2tjY2NTU1NHR0c/Pz87Ozs3NzcvLy8nJycbGxsLCwsDAwL+/v76+vru7u7q6urm5uba2trW1tbKysrGxsa2traqqqqenp6ampqOjo6KioqGhoZ+fn5ubm5qampmZmZeXl4+Pj46OjoyMjIuLi4aGhoWFhYODg4KCgoGBgX9/f35+fnt7e3l5eXd3d3Nzc3Jycm5ubm1tbWtra2pqamRkZGBgYFxcXFxcXFdXV1VVVVRUVFNTU09PT05OTk1NTUtLS0lJSUhISEdHR0ZGRkVFRURERENDQ0JCQkFBQT8/Pz4+Pj09PTw8PDs7Ozo6Ojk5OTg4ODc3NzY2NjU1NTQ0NDMzMzIyMjExMTAwMC8vLy4uLi0tLSwsLCsrKyoqKlwpXClcKVwoXChcKCcnJyYm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W5Uqhqy/xVlubMlLcgpMVlZW2gm9FqFqhSpFG44+AJ2h9gn6tFpl6oIlLLhr1668q5bXJdje3q5DKzpq3FDW+8WaC41e+dQ46Vp4CrK/RB2zoNlM/ffastByVZuTckEXLD7By+ZodmaZydkUYsE/bAY9SsHYx/b0Dri6rpYyPT2tR6L13zmzxC64EbigHW2EYNHigoWOCxY6Lljo/AuybeG+CmVuZHN0cmVhbQplbmRvYmoKMzMgMCBvYmoKMTY4MgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMzQgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDA0NyswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAzNQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwOTIwMSAwMDAwMCBuIAowMDAwMDA2NTQ4IDAwMDAwIG4gCjAwMDAwMDY1ODAgMDAwMDAgbiAKMDAwMDAwNjY3OSAwMDAwMCBuIAowMDAwMDA2NzAwIDAwMDAwIG4gCjAwMDAwMDY3MjEgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk3IDAwMDAwIG4gCjAwMDAwMDA4MjMgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwODAzIDAwMDAwIG4gCjAwMDAwMDY3NTMgMDAwMDAgbiAKMDAwMDAwNTMxOSAwMDAwMCBuIAowMDAwMDA1MTE5IDAwMDAwIG4gCjAwMDAwMDQ3NDkgMDAwMDAgbiAKMDAwMDAwNjM3MiAwMDAwMCBuIAowMDAwMDAwODQzIDAwMDAwIG4gCjAwMDAwMDExMzEgMDAwMDAgbiAKMDAwMDAwMTI2OSAwMDAwMCBuIAowMDAwMDAxNjQ5IDAwMDAwIG4gCjAwMDAwMDE5NTMgMDAwMDAgbiAKMDAwMDAwMjI3NSAwMDAwMCBuIAowMDAwMDAyNDg0IDAwMDAwIG4gCjAwMDAwMDI4OTggMDAwMDAgbiAKMDAwMDAwMzA0MiAwMDAwMCBuIAowMDAwMDAzMTYxIDAwMDAwIG4gCjAwMDAwMDM0OTIgMDAwMDAgbiAKMDAwMDAwMzcyOCAwMDAwMCBuIAowMDAwMDA0MDE5IDAwMDAwIG4gCjAwMDAwMDQyNTIgMDAwMDAgbiAKMDAwMDAwNDY1OSAwMDAwMCBuIAowMDAwMDA5MTgwIDAwMDAwIG4gCjAwMDAwMDkyNjEgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAzNCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMzUgPj4Kc3RhcnR4cmVmCjk0MTgKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:40:47.862252\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Score original image: 0.03\n", "Score transformed image: -0.07\n"]}], "source": ["img_noisy = exmp_img + torch.randn_like(exmp_img) * 0.3\n", "img_noisy.clamp_(min=-1.0, max=1.0)\n", "compare_images(exmp_img, img_noisy)"]}, {"cell_type": "markdown", "id": "2544f964", "metadata": {"papermill": {"duration": 0.03459, "end_time": "2021-09-16T12:40:47.989386", "exception": false, "start_time": "2021-09-16T12:40:47.954796", "status": "completed"}, "tags": []}, "source": ["We can see that the score considerably drops.\n", "Hence, the model can detect random Gaussian noise on the image.\n", "This is also to expect as initially, the \"fake\" samples are pure noise images.\n", "\n", "Next, we flip an image and check how this influences the score:"]}, {"cell_type": "code", "execution_count": 21, "id": "cc6e4e18", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:48.060789Z", "iopub.status.busy": "2021-09-16T12:40:48.060321Z", "iopub.status.idle": "2021-09-16T12:40:48.147462Z", "shell.execute_reply": "2021-09-16T12:40:48.147064Z"}, "papermill": {"duration": 0.12397, "end_time": "2021-09-16T12:40:48.147562", "exception": false, "start_time": "2021-09-16T12:40:48.023592", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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MAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL08gMTggMCBSIC9UIDE5IDAgUiAvYSAyMCAwIFIgL2QgMjEgMCBSIC9lIDIyIDAgUiAvZiAyMyAwIFIgL2cgMjQgMCBSCi9pIDI1IDAgUiAvbCAyNiAwIFIgL20gMjcgMCBSIC9uIDI4IDAgUiAvbyAyOSAwIFIgL3IgMzAgMCBSIC9zIDMxIDAgUgovc3BhY2UgMzIgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0kxIDEzIDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOAovQ29sb3JTcGFjZSBbL0luZGV4ZWQgL0RldmljZVJHQiA3MSAo/////v7+/f39+/v7+vr6+fn5+Pj48vLy8fHx8PDw7u7u7Ozs6enp5eXl4+Pj4eHh4ODg3t7e3d3d29vb0dHRz8/Pzc3Ny8vLxsbGu7u7ubm5tra2qqqqpqamo6Ojn5+fl5eXjIyMhYWFgYGBf39/fn5+eXl5c3NzcnJyampqVFRUU1NTTU1NS0tLSEhIQ0NDQkJCPT09PDw8Ozs7Ojo6OTk5ODg4MzMzKysrJycnJSUlIyMjIiIiHh4eFRUVFBQUEhISEREREBAQXHJcclxyCAgIBAQEAgICAAAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAyMjQgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDExNiAvTGVuZ3RoIDMzIDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDIyNCA+PgpzdHJlYW0KeJztnGl30kAYhaECKrggigu4RKmCCxYUQVCpG/T//yPvPcZTMJNkJh3EhPt86ekH5r5Pzmlne0PpuOCU9l3ArpFg3pFg3jkYwbN/wfEewiS4g0wJ+gzLieA30G63y+AFcAiToE8kaGY+n98G5d9IcDtMgj6RoJnZbBbKXQbvgEOYBH0iQTMHJPgMOIZJ0CcSNNPv92nXbDa/AMcwCfpEgmYKLfgGhP9hFotFhjAJ+kSCUXYs+Ap0Op0nIAiC9+ATcIkxZdoKfgbhRvAeWK/XGcIkWGzBW6C8xRVwP4GHAGvHmQ/BR4CZrVbrK3Cxk6AECyJ4AgaDwVuAH4/BDYDMm2BDu1KpXAfhb0/BhQWn0+lVcAkMh0NHOQkejGCU72A0Gv0AJ+dMJpNT0Gg0KPgSJGVahXW7XY71HNjWZgyT4F9IMO+CsbwGR0dHdwCvY5MyU8M+gFqtdhf8BNkKkqAjEjzLs+ByubwGSqUSPdMyk8NWq9UDUHY7qI8Nk6AVhRfs9XqctOr1+keQlpkcNh6POVa1WsXsOslekwRdkOBWZs4EWQaqYVGozSYzPoyPB0+JY+GZZa9pI0yCqUgwkpkzQU7HKIiTM+Zom8z4MC4TsFrgogFrh+w1bYRJMJVCC3Izw00N9jbc4lhmmsO4yeJmC3uuxAWtLRK0QYLGzBwJ8lCPk1a323XINIfxsJFjNRoNHkFidt04l+Qx5Wg04qGlbW0StKHYgsPhkHc/vAOaTqcOmeYw3kmF11O8qapUKhvXV+FtFu+2eMd1fuNFewlK8AAF2RTQarWYzVYBy08lCrJ1gS0MSS0ObIEob8E2CQlK8NAE1+s1m+MwPFvl2DLnQ9AGtgKyJTAIAjYIdjodtgsmhknQQOEFF4tF+BfAhmOHD15Y0AUJxiPBlMz/WpAv1zSbTdr1+/1smRL0ggTNFF4wPKgncZ3ZqZkS9IIEo/AVfb6qL8GUTAl6QYJR+FU1oRw3gvP5PFumBL0gwSh/BNvtdmzXsk2mBL0gwSiFF/SVKUGfYRLcQeZeBAuLBPOOBPOOBPPOLyx+TD4KZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iago3OTQKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjM0IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQwNDgrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgMzUKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDgwMzIgMDAwMDAgbiAKMDAwMDAwNjU0OCAwMDAwMCBuIAowMDAwMDA2NTgwIDAwMDAwIG4gCjAwMDAwMDY2NzkgMDAwMDAgbiAKMDAwMDAwNjcwMCAwMDAwMCBuIAowMDAwMDA2NzIxIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5NyAwMDAwMCBuIAowMDAwMDAwODIzIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDgwMyAwMDAwMCBuIAowMDAwMDA2NzUzIDAwMDAwIG4gCjAwMDAwMDUzMTkgMDAwMDAgbiAKMDAwMDAwNTExOSAwMDAwMCBuIAowMDAwMDA0NzQ5IDAwMDAwIG4gCjAwMDAwMDYzNzIgMDAwMDAgbiAKMDAwMDAwMDg0MyAwMDAwMCBuIAowMDAwMDAxMTMxIDAwMDAwIG4gCjAwMDAwMDEyNjkgMDAwMDAgbiAKMDAwMDAwMTY0OSAwMDAwMCBuIAowMDAwMDAxOTUzIDAwMDAwIG4gCjAwMDAwMDIyNzUgMDAwMDAgbiAKMDAwMDAwMjQ4NCAwMDAwMCBuIAowMDAwMDAyODk4IDAwMDAwIG4gCjAwMDAwMDMwNDIgMDAwMDAgbiAKMDAwMDAwMzE2MSAwMDAwMCBuIAowMDAwMDAzNDkyIDAwMDAwIG4gCjAwMDAwMDM3MjggMDAwMDAgbiAKMDAwMDAwNDAxOSAwMDAwMCBuIAowMDAwMDA0MjUyIDAwMDAwIG4gCjAwMDAwMDQ2NTkgMDAwMDAgbiAKMDAwMDAwODAxMiAwMDAwMCBuIAowMDAwMDA4MDkyIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzQgMCBSIC9Sb290IDEgMCBSIC9TaXplIDM1ID4+CnN0YXJ0eHJlZgo4MjQ5CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:40:48.094457\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Score original image: 0.03\n", "Score transformed image: -0.00\n"]}], "source": ["img_flipped = exmp_img.flip(dims=(1, 2))\n", "compare_images(exmp_img, img_flipped)"]}, {"cell_type": "markdown", "id": "d6f1c7b3", "metadata": {"papermill": {"duration": 0.036128, "end_time": "2021-09-16T12:40:48.219851", "exception": false, "start_time": "2021-09-16T12:40:48.183723", "status": "completed"}, "tags": []}, "source": ["If the digit can only be read in this way, for example, the 7, then we can see that the score drops.\n", "However, the score only drops slightly.\n", "This is likely because of the small size of our model.\n", "Keep in mind that generative modeling is a much harder task than classification,\n", "as we do not only need to distinguish between classes but learn **all** details/characteristics of the digits.\n", "With a deeper model, this could eventually be captured better (but at the cost of greater training instability).\n", "\n", "Finally, we check what happens if we reduce the digit significantly in size:"]}, {"cell_type": "code", "execution_count": 22, "id": "799ea05d", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:48.296476Z", "iopub.status.busy": "2021-09-16T12:40:48.296010Z", "iopub.status.idle": "2021-09-16T12:40:48.386691Z", "shell.execute_reply": "2021-09-16T12:40:48.387068Z"}, "papermill": {"duration": 0.131568, "end_time": "2021-09-16T12:40:48.387188", "exception": false, "start_time": "2021-09-16T12:40:48.255620", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDI0MS4xIDE0Ni4yNzE4NzUgXSAvUGFyZW50IDIgMCBSIC9SZXNvdXJjZXMgOCAwIFIKL1R5cGUgL1BhZ2UgPj4KZW5kb2JqCjkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMiAwIFIgPj4Kc3RyZWFtCnicjVJNb4JQELzvr5ijHvp4+/g+amxNvTS0JD00PRhEihEMmNS/3+UrEWkbD5B9w+4wM28ZB7IWjOwMjYM8FzDWsFbpd56kr+slkjNpwQsyDiuW6thX7HjK+Bz4rkB6fPwiKklYpX0thBmxVj6MrYKuQ9hsW4VXyLFH2A6VHqB2aoQI8Z4q3NAZKQ2YXXnXKd5RwlqYxhSLKRZTemKqkikHjTVm75YwKWA9M1YnRBShGti0OGoYtQp6TkHI85Q78tYBWtmDalpKGBeqqMnvQf4IEyrf9QM2LkKJrS2SgpYxrCeJViPet7HHO/rA7KXOs7zcHpEX2yyd4xPxhh5jiqhVQuw7NxJ65F8NbDzVfrxDQTxvL7jpntXb8rw/1YL43fysSHdTaQabbqfasMbhT9ZhetP0NtmR4rcdkb47lmvouhr9iy2iHyt1pwQKZW5kc3RyZWFtCmVuZG9iagoxMiAwIG9iagozMzEKZW5kb2JqCjEwIDAgb2JqClsgXQplbmRvYmoKMTggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMTUgPj4Kc3RyZWFtCnicNVBLrgMxCNvnFL5ApUASCOeZqnqb3n/7MKiLEdbgH/HrmDiGlyz4EvhWvGWs2DBTfMdSLaR2YOtAdeFcxTPkCo5eiE3stOBctrlJpK4gQyJKI9tyQ5dQtCk6JX9vmlu6KbcnTZpu08rA1MuQsyOIGEoGS1DTtWjCou2p+J3yjL86ixd+xw4rdNzh01MR9T3DZz6IS73G9qjZmUS6L8iQ05pLCU002dHvyBTOPDekkM4gQVJcgmtlkP3pl6MDEjAxtyxAdleinCVpx9K/M3jS5x9hXFSNCmVuZHN0cmVhbQplbmRvYmoKMTkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA2NiA+PgpzdHJlYW0KeJwzMzRUMFDQNQISZoYmCuZGlgophlxAPoiVywUTywGzzEzMgCxjU1MklgGQNjI1g9MQGaABcAZEfwZXGgBSaxTACmVuZHN0cmVhbQplbmRvYmoKMjAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMDcgPj4Kc3RyZWFtCnicPZJLbgMxDEP3PoUuEMD62Z7zpCi6mN5/2ycl6Yoc2RZFapa6TFlTHpA0k4R/6fBwsZ3yO2zPZmbgWqKXieWU59AVYu6ifNnMRl1ZJ8XqhGY6t+hRORcHNk2qn6sspd0ueA7XJp5b9hE/vNCgHtQ1Lgk3dFejZSk0Y6r7f9J7/Iwy4GpMXWxSq3sfPF5EVejoB0eJImOXF+fjQQnpSsJoWoiVd0UDQe7ytMp7Ce7b3mrIsgepmM47KWaw63RSLm4XhyEeyPKo8OWj2GtCz/iwKyX0SNiGM3In7mjG5tTI4pD+3o0ES4+uaCHz4K9u1i5gvFM6RWJkTnKsaYtVTvdQFNO5w70MEPVsRUMpc5HV6l/DzgtrlmwWeEr6BR6j3SZLDlbZ26hO76082dD3H1rXdB8KZW5kc3RyZWFtCmVuZG9iagoyMSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIzMSA+PgpzdHJlYW0KeJw1TzmSBCEMy3mFPjBVGNtAv6entjbY+X+6kplOkPAhydMTHZl4mSMjsGbH21pkIGbgU0zFv/a0DxOq9+AeIpSLC2GGkXDWrONuno4X/3aVz1gH7zb4illeENjCTNZXFmcu2wVjaZzEOclujF0TsY11radTWEcwoQyEdLbDlCBzVKT0yY4y5ug4kSeei+/22yx2OX4O6ws2jSEV5/gqeoI2g6Lsee8CGnJB/13d+B5Fu+glIBsJFtZRYu6c5YRfvXZ0HrUoEnNCmkEuEyHN6SqmEJpQrLOjoFJRcKk+p+isn3/lX1wtCmVuZHN0cmVhbQplbmRvYmoKMjIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNDkgPj4Kc3RyZWFtCnicPVA7jkQhDOs5hS/wJPIjcB5Gqy1m79+uA5opUEx+tjMk0BGBRwwxlK/jJa2groG/i0LxbuLrg8Igq0NSIM56D4h07KY2kRM6HZwzP2E3Y47ARTEGnOl0pj0HJjn7wgqEcxtl7FZIJ4mqIo7qM44pnip7n3gWLO3INlsnkj3kIOFSUonJpZ+Uyj9typQKOmbRBCwSueBkE004y7tJUowZlDLqHqZ2In2sPMijOuhkTc6sI5nZ00/bmfgccLdf2mROlcd0Hsz4nLTOgzkVuvfjiTYHTY3a6Oz3E2kqL1K7HVqdfnUSld0Y5xgSl2d/Gd9k//kH/odaIgplbmRzdHJlYW0KZW5kb2JqCjIzIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTM2ID4+CnN0cmVhbQp4nE2PQQ4DMQgD73mFn0AgQHjPVlUP2/9fS9h20wseyYBsUQaBJYd4hxvh0dsP30U2FWfjnF9SKWIhmE9wnzBTHI0pd/Jjj4BxlGosp2h4XkvOTcMXLXcTLaWtl5MZb7jul/dHlW2RDUXPLQtC12yS+TKBB3wYmEd142mlx932bK/2/ADObDRJCmVuZHN0cmVhbQplbmRvYmoKMjQgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzNDEgPj4Kc3RyZWFtCnicRVJLbkQxCNu/U3CBSOGXkPO0qrqY3n9bm0zVzeAJYGx4y1OmZMqwuSUjJNeUT30iQ6ym/DRyJCKm+EkJBXaVj8drS6yN7JGoFJ/a8eOx9Eam2RVa9e7Rpc2iUc3KyDnIEKGeFbqye9QO2fB6XEi675TNIRzL/1CBLGXdcgolQVvQd+wR3w8droIrgmGway6D7WUy1P/6hxZc7333YscugBas577BDgCopxO0BcgZ2u42KWgAVbqLScKj8npudqJso1Xp+RwAMw4wcsCIJVsdvtHeAJZ9XehFjYr9K0BRWUD8yNV2wd4xyUhwFuYGjr1wPMWZcEs4xgJAir3iGHrwJdjmL1euiJrwCXW6ZC+8wp7a5udCkwh3rQAOXmTDraujqJbt6TyC9mdFckaM1Is4OiGSWtI5guLSoB5a41w3seJtI7G5V9/uH+GcL1z26xdL7ITECmVuZHN0cmVhbQplbmRvYmoKMjUgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA3MiA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlxAvqmJuUIuF0gMxMoBswyAtCWcgohngJggbRDFIBZEsZmJGUQdnAGRy+BKAwAl2xbJCmVuZHN0cmVhbQplbmRvYmoKMjYgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCA0NyA+PgpzdHJlYW0KeJwzMrdQMFCwNAEShhYmCuZmBgophlyWEFYuF0wsB8wC0ZZwCiKewZUGALlnDScKZW5kc3RyZWFtCmVuZG9iagoyNyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI1OCA+PgpzdHJlYW0KeJxFkUtyBCAIRPeegiOA/OQ8k0plMbn/Ng3OZDZ2l6j9hEojphIs5xR5MH3J8s1ktul3OVY7GwUURSiYyVXosQKrO1PEmWuJautjZeS40zsGxRvOXTmpZHGjjHVUdSpwTM+V9VHd+XZZlH1HDmUK2KxzHGzgym3DGCdGm63uDveJIE8nU0fF7SDZ8AcnjX2VqytwnWz20UswDgT9QhOY5ItA6wyBxs1T9OQS7OPjdueBYG95EUjZEMiRIRgdgnadXP/i1vm9/3GGO8+1Ga4c7+J3mNZ2x19ikhVzAYvcKajnay5a1xk63pMzx+Sm+4bOuWCXu4NM7/k/1s/6/gMeKWb6CmVuZHN0cmVhbQplbmRvYmoKMjggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNjMgPj4Kc3RyZWFtCnicRZA7EgMhDEN7TqEj+CMDPs9mMik2929j2GxSwNNYIIO7E4LU2oKJ6IKHtiXdBe+tBGdj/Ok2bjUS5AR1gFak42iUUn25xWmVdPFoNnMrC60THWYOepSjGaAQOhXe7aLkcqbuzvlDcPVf9b9i3TmbiYHJyh0IzepT3Pk2O6K6usn+pMfcrNd+K+xVYWlZS8sJt527ZkAJ3FM52qs9Px8KOvYKZW5kc3RyZWFtCmVuZG9iagoyOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIxOCA+PgpzdHJlYW0KeJw9ULmNBDEMy12FGljAeu2pZxaLS6b/9Ej59iLRFkVSKjWZkikvdZQlWVPeOnyWxA55huVuZDYlKkUvk7Al99AK8X2J5hT33dWWs0M0l2g5fgszKqobHdNLNppwKhO6oNzDM/oNbXQDVocesVsg0KRg17YgcscPGAzBmROLIgxKTQb/rnKPn16LGz7D8UMUkZIO5jX/WP3ycw2vU48nkW5vvuJenKkOAxEckpq8I11YsS4SEWk1QU3PwFotgLu3Xv4btCO6DED2icRxmlKOob9rcKXPL+UnU9gKZW5kc3RyZWFtCmVuZG9iagozMCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE2MCA+PgpzdHJlYW0KeJxFkDkSAzEIBHO9gidIXIL3rMu1wfr/qQfWR6LpAjQcuhZNynoUaD7psUahutBr6CxKkkTBFpIdUKdjiDsoSExIY5JIth6DI5pYs12YmVQqs1LhtGnFwr/ZWtXIRI1wjfyJ6QZU/E/qXJTwTYOvkjH6GFS8O4OMSfheRdxaMe3+RDCxGfYJb0UmBYSJsanZvs9ghsz3Ctc4x/MNTII36wplbmRzdHJlYW0KZW5kb2JqCjMxIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMzM0ID4+CnN0cmVhbQp4nC1SS3LFIAzbcwpdoDP4B+Q86XS6eL3/tpKTRUYOYPQx5YaJSnxZILej1sS3jcxAheGvq8yFz0jbyDqIy5CLuJIthXtELOQxxDzEgu+r8R4e+azMybMHxi/Zdw8r9tSEZSHjxRnaYRXHYRXkWLB1Iap7eFOkw6kk2OOL/z7Fcy0ELXxG0IBf5J+vjuD5khZp95ht0656sEw7qqSwHGxPc14mX1pnuToezwfJ9q7YEVK7AhSFuTPOc+Eo01ZGtBZ2NkhqXGxvjv1YStCFblxGiiOQn6kiPKCkycwmCuKPnB5yKgNh6pqudHIbVXGnnsw1m4u3M0lm675IsZnCeV04s/4MU2a1eSfPcqLUqQjvsWdL0NA5rp69lllodJsTvKSEz8ZOT06+VzPrITkVCaliWlfBaRSZYgnbEl9TUVOaehn++/Lu8Tt+/gEsc3xzCmVuZHN0cmVhbQplbmRvYmoKMzIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxOCA+PgpzdHJlYW0KeJwzNrRQMIDDFEOuNAAd5gNSCmVuZHN0cmVhbQplbmRvYmoKMTYgMCBvYmoKPDwgL0Jhc2VGb250IC9EZWphVnVTYW5zIC9DaGFyUHJvY3MgMTcgMCBSCi9FbmNvZGluZyA8PAovRGlmZmVyZW5jZXMgWyAzMiAvc3BhY2UgNzkgL08gODQgL1QgOTcgL2EgMTAwIC9kIC9lIC9mIC9nIDEwNSAvaSAxMDggL2wgL20gL24gL28gMTE0Ci9yIC9zIF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNSAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxNCAwIFIgPj4KZW5kb2JqCjE1IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTQgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL08gMTggMCBSIC9UIDE5IDAgUiAvYSAyMCAwIFIgL2QgMjEgMCBSIC9lIDIyIDAgUiAvZiAyMyAwIFIgL2cgMjQgMCBSCi9pIDI1IDAgUiAvbCAyNiAwIFIgL20gMjcgMCBSIC9uIDI4IDAgUiAvbyAyOSAwIFIgL3IgMzAgMCBSIC9zIDMxIDAgUgovc3BhY2UgMzIgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0kxIDEzIDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOAovQ29sb3JTcGFjZSBbL0luZGV4ZWQgL0RldmljZVJHQiA3MSAo/////v7+/f39+/v7+vr6+fn5+Pj48vLy8fHx8PDw7u7u7Ozs6enp5eXl4+Pj4eHh4ODg3t7e3d3d29vb0dHRz8/Pzc3Ny8vLxsbGu7u7ubm5tra2qqqqpqamo6Ojn5+fl5eXjIyMhYWFgYGBf39/fn5+eXl5c3NzcnJyampqVFRUU1NTTU1NS0tLSEhIQ0NDQkJCPT09PDw8Ozs7Ojo6OTk5ODg4MzMzKysrJycnJSUlIyMjIiIiHh4eFRUVFBQUEhISEREREBAQXHJcclxyCAgIBAQEAgICAAAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAyMjQgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDExNiAvTGVuZ3RoIDMzIDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDIyNCA+PgpzdHJlYW0KeJzt2mtz0lAUheGIgAqoiOIFvFBpC16wQZEKKngp+P//kWuPdJo2OUnOsI8lh/V86fQDXXmZKYGE4NBzwXUfgGsMLDoGFt3eBP75Hw6vYYyBDjYZqDnGQAebDNQcY6CDTQZqjjHQwSYDNccY6GCTgZpjDHSwyUDNMQY62GSg5hgDHWwyUHNszwPfQqfTeQW9Xu8TfIVtNxmogoHiAdy45DY8TfEcTiFtk4EqGCi8DzyB4XD4AfDjJdwDdN6HSHa5XL4Lm98OIG2TgSoYmOwXhGH4G04uTCaTJdTrdQl8A2mbDFTBwGTeBxq9g1Kp9Ah+QtomA1Uw0JLfgYvFogFBEEhn1iYDVTDQRr/flxNgrVb7AlmbDFTBQBteB+J96KRSqUjgeDzOs8lAFQzMy/vA14C4Z7BarfJsMlAFA/M4g8dQrVY/Q85NBqpgYB7eBx6BnAC73a7FJgNVMDDTaDS6CXdgOp1abG4XGLkpG7/3Gh9joBkDYwoU+ANarZasvICcj9J+kQnD8Pz+1nK5lIOJjzEwGQMTFSVwvV4/Afy9h/ANcj5QOzCi0WhcuWvAQDPvA+fz+eZE9B4sHugoUK7rXf7/i4wxMAEDTYoQ+B2azabUDQYDyyNxFCgXTeIXTBiYjIFpihC4uVAvTN/MNnMQiA+lI/lEahxj4BVeB36EW7AzgXhTLAeS/G6YgXEMzLLrgcewiZMPgrPZzPKAtANxQh4Yz8YMjNubwHa7bfzWcirNQHlB+PdCcJo6xsAIBmbZ9cCtaQbKs41nOnOMgZoYaME20FsMLDoGFh0Di+4vuN8LXwplbmRzdHJlYW0KZW5kb2JqCjMzIDAgb2JqCjYxMAplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMzQgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDA0OCswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAzNQowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwNzg0OCAwMDAwMCBuIAowMDAwMDA2NTQ4IDAwMDAwIG4gCjAwMDAwMDY1ODAgMDAwMDAgbiAKMDAwMDAwNjY3OSAwMDAwMCBuIAowMDAwMDA2NzAwIDAwMDAwIG4gCjAwMDAwMDY3MjEgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk3IDAwMDAwIG4gCjAwMDAwMDA4MjMgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwODAzIDAwMDAwIG4gCjAwMDAwMDY3NTMgMDAwMDAgbiAKMDAwMDAwNTMxOSAwMDAwMCBuIAowMDAwMDA1MTE5IDAwMDAwIG4gCjAwMDAwMDQ3NDkgMDAwMDAgbiAKMDAwMDAwNjM3MiAwMDAwMCBuIAowMDAwMDAwODQzIDAwMDAwIG4gCjAwMDAwMDExMzEgMDAwMDAgbiAKMDAwMDAwMTI2OSAwMDAwMCBuIAowMDAwMDAxNjQ5IDAwMDAwIG4gCjAwMDAwMDE5NTMgMDAwMDAgbiAKMDAwMDAwMjI3NSAwMDAwMCBuIAowMDAwMDAyNDg0IDAwMDAwIG4gCjAwMDAwMDI4OTggMDAwMDAgbiAKMDAwMDAwMzA0MiAwMDAwMCBuIAowMDAwMDAzMTYxIDAwMDAwIG4gCjAwMDAwMDM0OTIgMDAwMDAgbiAKMDAwMDAwMzcyOCAwMDAwMCBuIAowMDAwMDA0MDE5IDAwMDAwIG4gCjAwMDAwMDQyNTIgMDAwMDAgbiAKMDAwMDAwNDY1OSAwMDAwMCBuIAowMDAwMDA3ODI4IDAwMDAwIG4gCjAwMDAwMDc5MDggMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAzNCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMzUgPj4Kc3RhcnR4cmVmCjgwNjUKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:40:48.333827\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Score original image: 0.03\n", "Score transformed image: -0.02\n"]}], "source": ["img_tiny = torch.zeros_like(exmp_img) - 1\n", "img_tiny[:, exmp_img.shape[1] // 2 :, exmp_img.shape[2] // 2 :] = exmp_img[:, ::2, ::2]\n", "compare_images(exmp_img, img_tiny)"]}, {"cell_type": "markdown", "id": "abbe7583", "metadata": {"papermill": {"duration": 0.037748, "end_time": "2021-09-16T12:40:48.462802", "exception": false, "start_time": "2021-09-16T12:40:48.425054", "status": "completed"}, "tags": []}, "source": ["The score again drops but not by a large margin, although digits in the MNIST dataset usually are much larger.\n", "\n", "Overall, we can conclude that our model is good for detecting Gaussian noise and smaller transformations to existing digits.\n", "Nonetheless, to obtain a very good out-of-distribution model, we would need to train deeper models and for more iterations."]}, {"cell_type": "markdown", "id": "714313c8", "metadata": {"papermill": {"duration": 0.037582, "end_time": "2021-09-16T12:40:48.538109", "exception": false, "start_time": "2021-09-16T12:40:48.500527", "status": "completed"}, "tags": []}, "source": ["### Instability\n", "\n", "Finally, we should discuss the possible instabilities of energy-based models,\n", "in particular for the example of image generation that we have implemented in this notebook.\n", "In the process of hyperparameter search for this notebook, there have been several models that diverged.\n", "Divergence in energy-based models means that the models assign a high probability to examples of the training set which is a good thing.\n", "However, at the same time, the sampling algorithm fails and only generates noise images that obtain minimal probability scores.\n", "This happens because the model has created many local maxima in which the generated noise images fall.\n", "The energy surface over which we calculate the gradients to reach data points with high probability has \"diverged\" and is not useful for our MCMC sampling.\n", "\n", "Besides finding the optimal hyperparameters, a common trick in energy-based models is to reload stable checkpoints.\n", "If we detect that the model is diverging, we stop the training, load the model from one epoch ago where it did not diverge yet.\n", "Afterward, we continue training and hope that with a different seed the model is not diverging again.\n", "Nevertheless, this should be considered as the \"last hope\" for stabilizing the models,\n", "and careful hyperparameter tuning is the better way to do so.\n", "Sensitive hyperparameters include `step_size`, `steps` and the noise standard deviation in the sampler,\n", "and the learning rate and feature dimensionality in the CNN model."]}, {"cell_type": "markdown", "id": "a34043a7", "metadata": {"papermill": {"duration": 0.037416, "end_time": "2021-09-16T12:40:48.621320", "exception": false, "start_time": "2021-09-16T12:40:48.583904", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this tutorial, we have discussed energy-based models for generative modeling.\n", "The concept relies on the idea that any strictly positive function can be turned into a probability\n", "distribution by normalizing over the whole dataset.\n", "As this is not reasonable to calculate for high dimensional data like images,\n", "we train the model using contrastive divergence and sampling via MCMC.\n", "While the idea allows us to turn any neural network into an energy-based model,\n", "we have seen that there are multiple training tricks needed to stabilize the training.\n", "Furthermore, the training time of these models is relatively long as, during every training iteration,\n", "we need to sample new \"fake\" images, even with a sampling buffer.\n", "In the next lectures and assignment, we will see different generative models (e.g. VAE, GAN, NF)\n", "that allow us to do generative modeling more stably, but with the cost of more parameters."]}, {"cell_type": "markdown", "id": "6ca06d61", "metadata": {"papermill": {"duration": 0.038052, "end_time": "2021-09-16T12:40:48.697258", "exception": false, "start_time": "2021-09-16T12:40:48.659206", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 7: Deep Energy-Based Generative Models\n", " :card_description: In this tutorial, we will look at energy-based deep learning models, and focus on their application as generative models. Energy models have been a popular tool before the...\n", " :tags: Image,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/07-deep-energy-based-generative-models.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab_type,id,colab,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 11.264697, "end_time": "2021-09-16T12:40:49.242921", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/07-deep-energy-based-generative-models/Deep_Energy_Models.ipynb", "output_path": ".notebooks/course_UvA-DL/07-deep-energy-based-generative-models.ipynb", "parameters": {}, "start_time": "2021-09-16T12:40:37.978224", "version": "2.3.3"}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/08-deep-autoencoders.ipynb b/source/notebooks/course_UvA-DL/08-deep-autoencoders.ipynb deleted file mode 100644 index f3e2f32..0000000 --- a/source/notebooks/course_UvA-DL/08-deep-autoencoders.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "37d6e7ea", "metadata": {"papermill": {"duration": 0.02405, "end_time": "2021-09-16T12:40:58.041596", "exception": false, "start_time": "2021-09-16T12:40:58.017546", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 8: Deep Autoencoders\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-09-16T14:32:32.123712\n", "\n", "In this tutorial, we will take a closer look at autoencoders (AE).\n", "Autoencoders are trained on encoding input data such as images into a smaller feature vector,\n", "and afterward, reconstruct it by a second neural network, called a decoder.\n", "The feature vector is called the \"bottleneck\" of the network as we aim to compress the input data into a smaller amount of features.\n", "This property is useful in many applications, in particular in compressing data or comparing images on a metric beyond pixel-level comparisons.\n", "Besides learning about the autoencoder framework, we will also see the \"deconvolution\"\n", "(or transposed convolution) operator in action for scaling up feature maps in height and width.\n", "Such deconvolution networks are necessary wherever we start from a small feature vector\n", "and need to output an image of full size (e.g. in VAE, GANs, or super-resolution applications).\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/08-deep-autoencoders.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "858d9128", "metadata": {"papermill": {"duration": 0.021931, "end_time": "2021-09-16T12:40:58.085647", "exception": false, "start_time": "2021-09-16T12:40:58.063716", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "7358cb32", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-09-16T12:40:58.133268Z", "iopub.status.busy": "2021-09-16T12:40:58.132753Z", "iopub.status.idle": "2021-09-16T12:40:58.135280Z", "shell.execute_reply": "2021-09-16T12:40:58.134801Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.02804, "end_time": "2021-09-16T12:40:58.135398", "exception": false, "start_time": "2021-09-16T12:40:58.107358", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"torch>=1.6, <1.9\" \"pytorch-lightning>=1.3\" \"torchvision\" \"seaborn\" \"torchmetrics>=0.3\" \"matplotlib\""]}, {"cell_type": "markdown", "id": "33e2dd60", "metadata": {"papermill": {"duration": 0.021405, "end_time": "2021-09-16T12:40:58.179525", "exception": false, "start_time": "2021-09-16T12:40:58.158120", "status": "completed"}, "tags": []}, "source": ["
"]}, {"cell_type": "code", "execution_count": 2, "id": "52180301", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:40:58.232432Z", "iopub.status.busy": "2021-09-16T12:40:58.231957Z", "iopub.status.idle": "2021-09-16T12:40:59.951493Z", "shell.execute_reply": "2021-09-16T12:40:59.951078Z"}, "papermill": {"duration": 1.749593, "end_time": "2021-09-16T12:40:59.951608", "exception": false, "start_time": "2021-09-16T12:40:58.202015", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_2146/3711936426.py:23: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Device: cuda:0\n"]}], "source": ["import os\n", "import urllib.request\n", "from urllib.error import HTTPError\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision\n", "from IPython.display import set_matplotlib_formats\n", "from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint\n", "from torch.utils.tensorboard import SummaryWriter\n", "from torchvision import transforms\n", "from torchvision.datasets import CIFAR10\n", "from tqdm.notebook import tqdm\n", "\n", "# %matplotlib inline\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "matplotlib.rcParams[\"lines.linewidth\"] = 2.0\n", "sns.reset_orig()\n", "sns.set()\n", "\n", "# Tensorboard extension (for visualization purposes later)\n", "# %load_ext tensorboard\n", "\n", "# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/tutorial9\")\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", "print(\"Device:\", device)"]}, {"cell_type": "markdown", "id": "28912006", "metadata": {"papermill": {"duration": 0.021859, "end_time": "2021-09-16T12:40:59.996263", "exception": false, "start_time": "2021-09-16T12:40:59.974404", "status": "completed"}, "tags": []}, "source": ["We have 4 pretrained models that we have to download.\n", "Remember the adjust the variables `DATASET_PATH` and `CHECKPOINT_PATH` if needed."]}, {"cell_type": "code", "execution_count": 3, "id": "4a75be18", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:00.045043Z", "iopub.status.busy": "2021-09-16T12:41:00.044569Z", "iopub.status.idle": "2021-09-16T12:41:00.679694Z", "shell.execute_reply": "2021-09-16T12:41:00.679188Z"}, "papermill": {"duration": 0.661412, "end_time": "2021-09-16T12:41:00.679814", "exception": false, "start_time": "2021-09-16T12:41:00.018402", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial9/cifar10_64.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial9/cifar10_128.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial9/cifar10_256.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial9/cifar10_384.ckpt...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial9/\"\n", "# Files to download\n", "pretrained_files = [\"cifar10_64.ckpt\", \"cifar10_128.ckpt\", \"cifar10_256.ckpt\", \"cifar10_384.ckpt\"]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(\"Downloading %s...\" % file_url)\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the files manually,\"\n", " \" or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "c8fc2858", "metadata": {"papermill": {"duration": 0.023053, "end_time": "2021-09-16T12:41:00.726214", "exception": false, "start_time": "2021-09-16T12:41:00.703161", "status": "completed"}, "tags": []}, "source": ["In this tutorial, we work with the CIFAR10 dataset.\n", "In CIFAR10, each image has 3 color channels and is 32x32 pixels large.\n", "As autoencoders do not have the constrain of modeling images probabilistic, we can work on more complex image data\n", "(i.e. 3 color channels instead of black-and-white) much easier than for VAEs.\n", "In case you have downloaded CIFAR10 already in a different directory, make sure to set DATASET_PATH\n", "accordingly to prevent another download.\n", "\n", "In contrast to previous tutorials on CIFAR10 like\n", "[Tutorial 5](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial5/Inception_ResNet_DenseNet.html)\n", "(CNN classification), we do not normalize the data explicitly with a mean of 0 and std of 1,\n", "but roughly estimate it scaling the data between -1 and 1.\n", "This is because limiting the range will make our task of predicting/reconstructing images easier."]}, {"cell_type": "code", "execution_count": 4, "id": "e7a54614", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:00.777942Z", "iopub.status.busy": "2021-09-16T12:41:00.777448Z", "iopub.status.idle": "2021-09-16T12:41:02.307895Z", "shell.execute_reply": "2021-09-16T12:41:02.307448Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 1.558951, "end_time": "2021-09-16T12:41:02.308015", "exception": false, "start_time": "2021-09-16T12:41:00.749064", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}], "source": ["# Transformations applied on each image => only make them a tensor\n", "transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n", "\n", "# Loading the training dataset. We need to split it into a training and validation part\n", "train_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=transform, download=True)\n", "pl.seed_everything(42)\n", "train_set, val_set = torch.utils.data.random_split(train_dataset, [45000, 5000])\n", "\n", "# Loading the test set\n", "test_set = CIFAR10(root=DATASET_PATH, train=False, transform=transform, download=True)\n", "\n", "# We define a set of data loaders that we can use for various purposes later.\n", "train_loader = data.DataLoader(train_set, batch_size=256, shuffle=True, drop_last=True, pin_memory=True, num_workers=4)\n", "val_loader = data.DataLoader(val_set, batch_size=256, shuffle=False, drop_last=False, num_workers=4)\n", "test_loader = data.DataLoader(test_set, batch_size=256, shuffle=False, drop_last=False, num_workers=4)\n", "\n", "\n", "def get_train_images(num):\n", " return torch.stack([train_dataset[i][0] for i in range(num)], dim=0)"]}, {"cell_type": "markdown", "id": "ac14f5b0", "metadata": {"papermill": {"duration": 0.023214, "end_time": "2021-09-16T12:41:02.355175", "exception": false, "start_time": "2021-09-16T12:41:02.331961", "status": "completed"}, "tags": []}, "source": ["## Building the autoencoder\n", "\n", "In general, an autoencoder consists of an **encoder** that maps the input $x$ to a lower-dimensional feature vector $z$,\n", "and a **decoder** that reconstructs the input $\\hat{x}$ from $z$.\n", "We train the model by comparing $x$ to $\\hat{x}$ and optimizing the parameters to increase the similarity between $x$ and $\\hat{x}$.\n", "See below for a small illustration of the autoencoder framework."]}, {"cell_type": "markdown", "id": "8aa77f09", "metadata": {"papermill": {"duration": 0.023251, "end_time": "2021-09-16T12:41:02.401674", "exception": false, "start_time": "2021-09-16T12:41:02.378423", "status": "completed"}, "tags": []}, "source": ["
"]}, {"cell_type": "markdown", "id": "b4e16e51", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.023141, "end_time": "2021-09-16T12:41:02.447908", "exception": false, "start_time": "2021-09-16T12:41:02.424767", "status": "completed"}, "tags": []}, "source": ["We first start by implementing the encoder.\n", "The encoder effectively consists of a deep convolutional network, where we scale down the image layer-by-layer using strided convolutions.\n", "After downscaling the image three times, we flatten the features and apply linear layers.\n", "The latent representation $z$ is therefore a vector of size *d* which can be flexibly selected."]}, {"cell_type": "code", "execution_count": 5, "id": "7a89bc88", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:02.501193Z", "iopub.status.busy": "2021-09-16T12:41:02.500706Z", "iopub.status.idle": "2021-09-16T12:41:02.502735Z", "shell.execute_reply": "2021-09-16T12:41:02.502267Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.031646, "end_time": "2021-09-16T12:41:02.502835", "exception": false, "start_time": "2021-09-16T12:41:02.471189", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Encoder(nn.Module):\n", " def __init__(self, num_input_channels: int, base_channel_size: int, latent_dim: int, act_fn: object = nn.GELU):\n", " \"\"\"\n", " Args:\n", " num_input_channels : Number of input channels of the image. For CIFAR, this parameter is 3\n", " base_channel_size : Number of channels we use in the first convolutional layers. Deeper layers might use a duplicate of it.\n", " latent_dim : Dimensionality of latent representation z\n", " act_fn : Activation function used throughout the encoder network\n", " \"\"\"\n", " super().__init__()\n", " c_hid = base_channel_size\n", " self.net = nn.Sequential(\n", " nn.Conv2d(num_input_channels, c_hid, kernel_size=3, padding=1, stride=2), # 32x32 => 16x16\n", " act_fn(),\n", " nn.Conv2d(c_hid, c_hid, kernel_size=3, padding=1),\n", " act_fn(),\n", " nn.Conv2d(c_hid, 2 * c_hid, kernel_size=3, padding=1, stride=2), # 16x16 => 8x8\n", " act_fn(),\n", " nn.Conv2d(2 * c_hid, 2 * c_hid, kernel_size=3, padding=1),\n", " act_fn(),\n", " nn.Conv2d(2 * c_hid, 2 * c_hid, kernel_size=3, padding=1, stride=2), # 8x8 => 4x4\n", " act_fn(),\n", " nn.Flatten(), # Image grid to single feature vector\n", " nn.Linear(2 * 16 * c_hid, latent_dim),\n", " )\n", "\n", " def forward(self, x):\n", " return self.net(x)"]}, {"cell_type": "markdown", "id": "76a60820", "metadata": {"papermill": {"duration": 0.023574, "end_time": "2021-09-16T12:41:02.549985", "exception": false, "start_time": "2021-09-16T12:41:02.526411", "status": "completed"}, "tags": []}, "source": ["Note that we do not apply Batch Normalization here.\n", "This is because we want the encoding of each image to be independent of all the other images.\n", "Otherwise, we might introduce correlations into the encoding or decoding that we do not want to have.\n", "In some implementations, you still can see Batch Normalization being used, because it can also serve as a form of regularization.\n", "Nevertheless, the better practice is to go with other normalization techniques if necessary like Instance Normalization or Layer Normalization.\n", "Given the small size of the model, we can neglect normalization for now."]}, {"cell_type": "markdown", "id": "56b7a01a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.023466, "end_time": "2021-09-16T12:41:02.596962", "exception": false, "start_time": "2021-09-16T12:41:02.573496", "status": "completed"}, "tags": []}, "source": ["The decoder is a mirrored, flipped version of the encoder.\n", "The only difference is that we replace strided convolutions by transposed convolutions\n", "(i.e. deconvolutions) to upscale the features.\n", "Transposed convolutions can be imagined as adding the stride to the input instead of the output,\n", "and can thus upscale the input.\n", "For an illustration of a `nn.ConvTranspose2d` layer with kernel size 3, stride 2, and padding 1,\n", "see below (figure credit - [Vincent Dumoulin and Francesco Visin](https://arxiv.org/abs/1603.07285)):\n", "\n", "
\n", "\n", "You see that for an input of size $3\\times3$, we obtain an output of $5\\times5$.\n", "However, to truly have a reverse operation of the convolution,\n", "we need to ensure that the layer scales the input shape by a factor of 2 (e.g. $4\\times4\\to8\\times8$).\n", "For this, we can specify the parameter `output_padding` which adds additional values to the output shape.\n", "Note that we do not perform zero-padding with this, but rather increase the output shape for calculation.\n", "\n", "Overall, the decoder can be implemented as follows:"]}, {"cell_type": "code", "execution_count": 6, "id": "8197fb2e", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:02.650981Z", "iopub.status.busy": "2021-09-16T12:41:02.650503Z", "iopub.status.idle": "2021-09-16T12:41:02.652477Z", "shell.execute_reply": "2021-09-16T12:41:02.652080Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.032082, "end_time": "2021-09-16T12:41:02.652576", "exception": false, "start_time": "2021-09-16T12:41:02.620494", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Decoder(nn.Module):\n", " def __init__(self, num_input_channels: int, base_channel_size: int, latent_dim: int, act_fn: object = nn.GELU):\n", " \"\"\"\n", " Args:\n", " num_input_channels : Number of channels of the image to reconstruct. For CIFAR, this parameter is 3\n", " base_channel_size : Number of channels we use in the last convolutional layers. Early layers might use a duplicate of it.\n", " latent_dim : Dimensionality of latent representation z\n", " act_fn : Activation function used throughout the decoder network\n", " \"\"\"\n", " super().__init__()\n", " c_hid = base_channel_size\n", " self.linear = nn.Sequential(nn.Linear(latent_dim, 2 * 16 * c_hid), act_fn())\n", " self.net = nn.Sequential(\n", " nn.ConvTranspose2d(\n", " 2 * c_hid, 2 * c_hid, kernel_size=3, output_padding=1, padding=1, stride=2\n", " ), # 4x4 => 8x8\n", " act_fn(),\n", " nn.Conv2d(2 * c_hid, 2 * c_hid, kernel_size=3, padding=1),\n", " act_fn(),\n", " nn.ConvTranspose2d(2 * c_hid, c_hid, kernel_size=3, output_padding=1, padding=1, stride=2), # 8x8 => 16x16\n", " act_fn(),\n", " nn.Conv2d(c_hid, c_hid, kernel_size=3, padding=1),\n", " act_fn(),\n", " nn.ConvTranspose2d(\n", " c_hid, num_input_channels, kernel_size=3, output_padding=1, padding=1, stride=2\n", " ), # 16x16 => 32x32\n", " nn.Tanh(), # The input images is scaled between -1 and 1, hence the output has to be bounded as well\n", " )\n", "\n", " def forward(self, x):\n", " x = self.linear(x)\n", " x = x.reshape(x.shape[0], -1, 4, 4)\n", " x = self.net(x)\n", " return x"]}, {"cell_type": "markdown", "id": "d2ea7d77", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.023491, "end_time": "2021-09-16T12:41:02.699586", "exception": false, "start_time": "2021-09-16T12:41:02.676095", "status": "completed"}, "tags": []}, "source": ["The encoder and decoder networks we chose here are relatively simple.\n", "Usually, more complex networks are applied, especially when using a ResNet-based architecture.\n", "For example, see [VQ-VAE](https://arxiv.org/abs/1711.00937) and\n", "[NVAE](https://arxiv.org/abs/2007.03898) (although the papers discuss architectures for VAEs,\n", "they can equally be applied to standard autoencoders).\n", "\n", "In a final step, we add the encoder and decoder together into the autoencoder architecture.\n", "We define the autoencoder as PyTorch Lightning Module to simplify the needed training code:"]}, {"cell_type": "code", "execution_count": 7, "id": "3addaf83", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:02.755000Z", "iopub.status.busy": "2021-09-16T12:41:02.748144Z", "iopub.status.idle": "2021-09-16T12:41:02.756989Z", "shell.execute_reply": "2021-09-16T12:41:02.756529Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.034085, "end_time": "2021-09-16T12:41:02.757086", "exception": false, "start_time": "2021-09-16T12:41:02.723001", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Autoencoder(pl.LightningModule):\n", " def __init__(\n", " self,\n", " base_channel_size: int,\n", " latent_dim: int,\n", " encoder_class: object = Encoder,\n", " decoder_class: object = Decoder,\n", " num_input_channels: int = 3,\n", " width: int = 32,\n", " height: int = 32,\n", " ):\n", " super().__init__()\n", " # Saving hyperparameters of autoencoder\n", " self.save_hyperparameters()\n", " # Creating encoder and decoder\n", " self.encoder = encoder_class(num_input_channels, base_channel_size, latent_dim)\n", " self.decoder = decoder_class(num_input_channels, base_channel_size, latent_dim)\n", " # Example input array needed for visualizing the graph of the network\n", " self.example_input_array = torch.zeros(2, num_input_channels, width, height)\n", "\n", " def forward(self, x):\n", " \"\"\"The forward function takes in an image and returns the reconstructed image.\"\"\"\n", " z = self.encoder(x)\n", " x_hat = self.decoder(z)\n", " return x_hat\n", "\n", " def _get_reconstruction_loss(self, batch):\n", " \"\"\"Given a batch of images, this function returns the reconstruction loss (MSE in our case)\"\"\"\n", " x, _ = batch # We do not need the labels\n", " x_hat = self.forward(x)\n", " loss = F.mse_loss(x, x_hat, reduction=\"none\")\n", " loss = loss.sum(dim=[1, 2, 3]).mean(dim=[0])\n", " return loss\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.Adam(self.parameters(), lr=1e-3)\n", " # Using a scheduler is optional but can be helpful.\n", " # The scheduler reduces the LR if the validation performance hasn't improved for the last N epochs\n", " scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode=\"min\", factor=0.2, patience=20, min_lr=5e-5)\n", " return {\"optimizer\": optimizer, \"lr_scheduler\": scheduler, \"monitor\": \"val_loss\"}\n", "\n", " def training_step(self, batch, batch_idx):\n", " loss = self._get_reconstruction_loss(batch)\n", " self.log(\"train_loss\", loss)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " loss = self._get_reconstruction_loss(batch)\n", " self.log(\"val_loss\", loss)\n", "\n", " def test_step(self, batch, batch_idx):\n", " loss = self._get_reconstruction_loss(batch)\n", " self.log(\"test_loss\", loss)"]}, {"cell_type": "markdown", "id": "5ce14e90", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.02353, "end_time": "2021-09-16T12:41:02.804310", "exception": false, "start_time": "2021-09-16T12:41:02.780780", "status": "completed"}, "tags": []}, "source": ["For the loss function, we use the mean squared error (MSE).\n", "The mean squared error pushes the network to pay special attention to those pixel values its estimate is far away.\n", "Predicting 127 instead of 128 is not important when reconstructing, but confusing 0 with 128 is much worse.\n", "Note that in contrast to VAEs, we do not predict the probability per pixel value, but instead use a distance measure.\n", "This saves a lot of parameters and simplifies training.\n", "To get a better intuition per pixel, we report the summed squared error averaged over the batch dimension\n", "(any other mean/sum leads to the same result/parameters).\n", "\n", "However, MSE has also some considerable disadvantages.\n", "Usually, MSE leads to blurry images where small noise/high-frequent patterns are removed as those cause a very low error.\n", "To ensure realistic images to be reconstructed, one could combine Generative Adversarial Networks\n", "(lecture 10) with autoencoders as done in several works (e.g. see [here](https://arxiv.org/abs/1704.02304),\n", "[here](https://arxiv.org/abs/1511.05644) or these [slides](http://elarosca.net/slides/iccv_autoencoder_gans.pdf)).\n", "Additionally, comparing two images using MSE does not necessarily reflect their visual similarity.\n", "For instance, suppose the autoencoder reconstructs an image shifted by one pixel to the right and bottom.\n", "Although the images are almost identical, we can get a higher loss than predicting a constant pixel value for half of the image (see code below).\n", "An example solution for this issue includes using a separate, pre-trained CNN,\n", "and use a distance of visual features in lower layers as a distance measure instead of the original pixel-level comparison."]}, {"cell_type": "code", "execution_count": 8, "id": "031f9cd6", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:02.857901Z", "iopub.status.busy": "2021-09-16T12:41:02.852996Z", "iopub.status.idle": "2021-09-16T12:41:03.403790Z", "shell.execute_reply": "2021-09-16T12:41:03.403315Z"}, "papermill": {"duration": 0.576043, "end_time": "2021-09-16T12:41:03.403902", "exception": false, "start_time": "2021-09-16T12:41:02.827859", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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YmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvTCAxOCAwIFIgL1MgMTkgMCBSIC9jb2xvbiAyMCAwIFIgL2QgMjEgMCBSIC9lIDIyIDAgUiAvZiAyMyAwIFIKL2ZpdmUgMjQgMCBSIC9mb3VyIDI1IDAgUiAvaCAyNiAwIFIgL2h5cGhlbiAyNyAwIFIgL2kgMjggMCBSIC9vIDI5IDAgUgovcGVyaW9kIDMwIDAgUiAvcyAzMSAwIFIgL3NwYWNlIDMyIDAgUiAvdCAzMyAwIFIgL3R3byAzNCAwIFIgL3plcm8gMzUgMCBSCj4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0kxIDEzIDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOCAvQ29sb3JTcGFjZSAvRGV2aWNlUkdCCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDMgL0NvbHVtbnMgMjEyIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAxMDkgL0xlbmd0aCAzNiAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAyMTIgPj4Kc3RyZWFtCnic7V1JjyTHdY5cq7L2ru6uXmemOTOcNklzSEmmII9tgbIu8sXLzX/BgG/+C77qLOhi/QFvEAwDhmGLEGwKtklQoLnPPr1Nb9XVXVmZVZWbD/F9WeJk9bQPhtMw3neZh6zIiKieqPe9F+/FC6UEAoFAIBAIBAKBQCD4/waj+Oje73xXC4NBXwsVM9XCoptp4fpSTQvL3boWljpNLbiWowW74qFHy9b/9s8GWpjG6Geh09aCmURamEwmWhiPx/mUql5VC4lKtBCEvhbanRYaZfhoOpliWIWZWJalhWajoYV6HdN2HPQc8q3MMNGhab/QYZzhz/Wnf/5j9XX88E/eVYKX4s9+9N4LT8x5zQSC/w3I4hOUBrv46NPPPtXC+empFhYq+MhYhLSUgGQNr6eFUQqO9hNQama4WgjGoK0gBKVGCXj8xAKRVW28Fcf4yDJnc6tUKuxqhGYp+jTGi1owQa0qInF7NijVJ2/2k1gLtRpo1zBBzQatBWWaHAuWQBxBsGz+IS7HX32C+YvR8kKHSr2nvg7RfILSIItPUBrm0K5n0wUGbaobZNtXVqDne70uGuf8ZeCtcAKFP45AARk/cj1SCYkjS9Gm3QUTxRE+ch02VioBJyjLxUwmU4wSxei8xo/sOl6s8klsgKzNDAwY080n7asGqcQfjdgzuMxkm+HFuboKYrRwrBeNliJE8wlKgyw+QWmYQ7tVA9q12YQu3t5Y0MKihydOCtbz+9DJSYp1HI7wukm6aXXgJdnkwcH5EE84frcJ2h1egBqm5AilVEgdnpEuc5aMpiGGS9CXQ5ZJ6APapKfJBE9cB6RmppjtxAfxKRJfhUwUp+Cyc3+iroIYLez5RaOlCNF8gtIgi09QGubQ7kIFDz3yV7sBVbzcglOTpFDl1OjKsslS9HcmKVmP5GpTbycTcGVmofHR0QAfRehyGAT5lIIE5N7wuCk6QTNLoU/TAONY3J4NRxil5rQ5AbQZ04UMI9BuqvDRwAclDUaYvx+gzTi6+rcqRov+t2i0FCGaT1AaZPEJSsMc2u11sLvYdKA6q1UIpgWl6tHzimLQX0qVnmWgkjwEmUyhrtOM+p80mtlQ4MMpaCJJMFaQzNR1vnc69NHD3gTtHUZOWz4mED0/1kI4AHFfX34VX613TQtGE9vFk7MTLfg+Ojy/ACeenIOSHu9cYG6Mcr4EYrRAKBgtRYjmE5QGWXyC0jCHdtd7cIVaLhRmowZyNLI8TpfxCfT2JISeN8m/i02o63odPH5xDo5rt6D/h3TEnu6S/iYgIPdXnKSNGqnHARc8OR1oYZzRhSRxdFqInN574x2MewCWyQK0aS+BAScBevZ9/A4rDj66top+er0VLRySkZ9+vKMugRgtWigaLUWI5hOUBll8gtIwh3a7TZCCPR1ooeKgWa2CzcxJCAUecZux08FWakafaJpgZUcRVHGNqbD7xwhKPnwCBX40RD+5b7TlzVzLP/zuN7SwuYYe/vLDh1r4xf3nWsjThGwTExgOjtDnEMM1m8z8SZiMVMUTl+RYM/AkZgbR9fV1vN7H7u4/X067YrRooWi0PPqX++rrEM0nKA2y+ASlYd4mc3dJC2EfdGka1K4B9Hw4BafYBt0rbm/myzmMwIOdBdDElIG/Rzv7Wji9oEqn42Zx47RVzbdgVc+Gx1TllF5trWnhoIv2hyTZSYBxP/ryK0yJ7l7U4C5rG1yQn3Npt2FRNFNupdLNzKYYfWu5rq6CGC3osGC0KKFdwf8dyOITlIZ5KVVLyxAYlDR5TmRwcaaFaAS/z0zybVJQW0aWaTTgpkUK/Xz+8Est+NzkrFYRAPVchkTr4KYFaxYT/PDBoRbiKZpN2qtaWO5iFEOBUqMYPBUwX2hEN23KWKRBkyCvF+Iw4zbjWRqHQdWYZ2oymg0vgRgtWigaLUWI5hOUBll8gtIwh3ZVfhiTG4Y5KvRuaorptVy+JnOBIvJvxcM26clzcHRwAta+Ra7kcRlVJdtu395Ah/lnSsU8FnpB3rctOHpNFx7o4sJtdP7qdS08fvbvWvjiyz0tuA4JNEO9iDjGX8AkczkuxkqZgpsHXg3j6t+qGC14UjBaihDNJygNsvgEpWEO7ebnTYwo5DPo29EIZDflUZrYBCn4AZyaiwCcsnENnWcxPrqxBOV8awNMFIzxZOPO21pwM6j9s/NZmQWvAxdSnUKrX1vF1uWAB0Vv/hoyf1oLNQqvo6tjTOlsgPk7JGszA3NFzC7Oz7skZJn85Gm+A/wyiNGilJpntBQhmk9QGmTxCUrDHNpNDG5dMjyX041XhXpv8KDo/nGesIoMWNthxYZDqOsxk2NfXYFO/v67oMiHezjv2dyAk7i0CEfs6Pgwn1KnQ5ZMGUykV3V0jFHs6kALx4MDLewdgB0cB7PttMEFYciKEzZ+fgbJNSX/mqxTYZAT/xt7zGK0YKii0VKEaD5BaZDFJygNc2i3w/oMsQ3N6fMwZsYQ5PlwoIWnTw/ZBhznVbGgDx6BL1aqcIU2Nm5giPWbWnCGVNP0BDff+jYePN/Lp+TFIO5EYSajEYS1Gvh6yiMzRh3z36wzA7kDKh+eIoPo6BCpvxFrMI5ZuEkxp6hegVM5ZQXj3JV7CcRowVgFo6UI0XyC0iCLT1Aa5tDucIBiwvYUbo6TxzSZHmuzcETgwwNaaEK3dxiUDPug3d4GCv9u3H1XC5/sIjj41QMI99ZQL3EwwJOVW2/lUzIVztdMJ+CgDo/eXBxhth5zeNa67CqBL+bcRZ5wSE7517//qRZ2d5BKZM0olXUa6dtG+Vbw5eWFc4jRov8tGi1FiOYTlAZZfILSMId280K7SQjazYsKmtwvTZiC2ycR2Rf0gFg5f41O1jvf+10tbG5/Rwt//ZO/0MIqlbzFBJ69Rzjbsnrz9XxK1UVEHusZA5190KWXglKnPPp6MoTQWX5FC4urW1oIfSQOmczJTVzwRe6vRcwXMljLwuDtPHko8yUQowWNC0ZLEaL5BKVBFp+gNMzhERZOUAmdu3yfkHuKKuPJU1Y5Ut1FbEWu1kHN3/yNbS28dg9se3YEx6cSD7RwcxOFj1IDHa324H/F41kubkA2mcZ4GIWYecL0pId7u1r4z08+0MK97+CtxVUw18UQNMF9U7W0Bb5L8wDulCRL++H8GLOdDPna5RCjBU8KRksRovkEpUEWn6A0yOITlIY5Nl9Ktg4nsMNc2hZ2fjjZREb17TUYDVUP63jrBvKw3/rt72lhbfuuFn75i59o4fo1+POrb7yJIZZvYYga0seD8WxnPLyAsXK4j9pkZ4ew8JIINorXxCbFEgt47ex/pIWVNSSXxwH6zHiDozFCZnmSsco2bV6vwjD8KtPQK5fnphFiMWMCBYu5CNF8gtIgi09QGubQrmPh4Rm97oTp2l4Nad8W48c98sXO/kALt/7oB1rYfPMH7BLUHA2RtN1m2c1l5n+PbBDxpx/9hxYm4ewCp4sLdH6y9wwTYFHsahWz3XgF3Hr3DnYWYqvOb9SB4PJyizFINniKAHxubMT8PfqMQ9QW0c/K+qK6CmK0oMOC0VKEaD5BaZDFJygNc2h3ErIUJm9yMlh30jGZGs4cca+Bj37/j/9AC/d+7/taaC3x+opHn2vB4uuDIQLqx09Qf2R/CLZ672//RgsNb6auxxMo/FXel9xqgsse74KIp+y8u76lhTtvfgvvM0beH4BugjF+dWch649k+LLjEHTpM/09Y0Leax11JcRo0SgaLUWI5hOUBll8gtIwb5OZNzApnkQyWJsy5k0SBp2aagVB5re/BY7Lb2L47Jdwl872Ee2esIDI8AxpZDsPPtOCn4GSnARtGvlFjEq1qlDdywsdLRwcIrcs5l5uMAQ17zx+xvc+Rec+3L2qjWnHlZ4WTmPM3/Pg7tVY0duzQdZDnuiO09nO7WUQo0ULRaOlCNF8gtIgi09QGubmhbO4Vcy7GRjMS+jUTJmattKGn/UPP/07LXRXQHa9NUQepwFrfDhQ4I067w/m6eU6yXq1x4Law34+Ic/Ci6fHSASPGENsVsGSU3LrfXp8B1+gpPUkZtkU3gSZ5ONu0her48uaFdBElSS7wNqgr71xkzP6SF0CMVrwpGC0KDX7D9UQzScoDbL4BKVhnrebYlPUpequ2kz9yes+cwcy5amnkxNocv8Yghf9Otrw2FZ3AbHRzjozfxJECff2cSA5442MpjmbW54LZPF2pTrrnpDTlJVLJLWEF1CZ/EYXjE5OKyDi5jomMPLQeMiLoMYj/DIXW2Dbpd7VsV0xWrRQNFqKEM0nKA2y+ASlYQ7tmga0dLUChZmRJuoeGKTeBG8GvBNxsemyRzSenqMQSWrio8ABJa2s4GRUOoW63r67qYX3f/ZPeD2bRScd1tsKfQRMW034Wa4NxrFYm8wfY0qPD5DwMzjDlCYG+lzexq9uo0PeyTDJsxMM4Y5J8Ty2HQaXHsTKIUaLFopGSxGi+QSlQRafoDTMoV2XB10CXlxkcZcypd8UMAPWYiXNigtN7jho7DKrtt3Ck+cslxlsgGR715DAs3eEqltvvPNbWvCP9/MpPfoKPuDIH2DeFjR/uw26NOhmHuzhxWe8C9msYAKtVUxyuQvWNsjRRh9tFs6YZdSDK7rZgeP54LPn6iqI0YKeC0ZLEaL5BKVBFp+gNMyh3ZVlXsV0ihhiyLKVvLVBZSa0tM3LBVstbG+63PAMRwjqebxJUfHWww/ef18LN7dBLru7YDSTLmGtMksKskj3ngdyHPmg3TCEEHNTt+Gh8b1v3tFClam/sQXvMj84E+7wSuYhopO9WlML37gDf7PXQXbThweP1FUQo0ULRaOlCNF8gtIgi09QGubQ7vVrUMVtA0z0YAc0cXjMe3+Z3dpooIdRMNBCkiJKaHFl949BCkMfftM4gkq3MrzVbEBLHz4H1++OZhmwaQYuXlkGuRspCPRsgCBmpY4pddrgTZdnWCZTZiDz5OyI6UlTn05ZitnevramhXWWidjZBd+dHgfqKojRooWi0VKEaD5BaZDFJygNc2i3tUDNT5ZZ6DEtlpcKnxzClRtzn9N24QFN80xeXjsRMQR5HmLfsk7dPg6gt8Mxsn2mfCuJZoHULMME/AtQV6vFyxFbIIUwr615ilEaDbBMXifKiHm5lI3dUd4WoVwXQ2zd3kKHARr//OfwFj/+8khdBTFatFA0WooQzScoDbL4BKVhDu3aLKFQbUHNdhssb8jyRI7Hixy4qagStPGq8G4SxiKTCXjQraGxk9drssCeE94MMWVF6SybVWLMKy5mvGaJB2VmXSkXxDE4w3AhE5baHZgENvnX5PGWgLHUwxNeaUx2G/Ju5n/82Rdoc7WzK0bLFjosGC1FiOYTlAZZfILSMId2fbowykJRhUYd6t3xXrxTsM0LgP2LkAI2PH3m/UZjHldx4QFVuZUaMwBqMyTq8ufgVGYnTw3eIFWje5gn6sYJuNVlgYhWB5zS74NJh+T0VhcTCGKMe/8J3MPPP0bhxBUGLlfy0zEsnbxET/BJ/9ISEGK0aKFotBQhmk9QGmTxCUrDHNrdfQphMgC3NpehS6seVTEIWXW76MEfwV0aDCCcnTK7FcymrBRMmrKEUZLQKWNxifzXkN+opJSyGAMNSU8ZNz4d7pfGAfZLEzpuCdN0B8zg5XFV1aeR8Pg+Jjc4ZZsRGq22ccnx6zdQOJEvqQ8e8ysVIEYLhILRUoRoPkFpkMUnKA1zaDdxEAGM3He0MEmhZs0YccZqG5zYWQaDLOTVBQOo2UEfW5GDE1BAOOLFNzGdrAyrP+W50TGLK7ou2yhl8QzscIxmIUsOOqwK1eRdiKkJPyuKMFylzqpQDmbbcfGNbqmOFu6+DZrYvvu2FrZuI0/4278JRt7d5/0CHzxWl0CMFi0UjZZ/e/biH000n6A0yOITCAQCgUAgEAgEAoFAIPgfw38BeLpnJQplbmRzdHJlYW0KZW5kb2JqCjM2IDAgb2JqCjQ0NjQKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjM3IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQxMDIrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgMzgKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMTE4MDYgMDAwMDAgbiAKMDAwMDAwNjg4NyAwMDAwMCBuIAowMDAwMDA2OTE5IDAwMDAwIG4gCjAwMDAwMDcwMTggMDAwMDAgbiAKMDAwMDAwNzAzOSAwMDAwMCBuIAowMDAwMDA3MDYwIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5NCAwMDAwMCBuIAowMDAwMDAwNzE2IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDY5NiAwMDAwMCBuIAowMDAwMDA3MDkyIDAwMDAwIG4gCjAwMDAwMDU2MDMgMDAwMDAgbiAKMDAwMDAwNTQwMyAwMDAwMCBuIAowMDAwMDA0OTg3IDAwMDAwIG4gCjAwMDAwMDY2NTYgMDAwMDAgbiAKMDAwMDAwMDczNiAwMDAwMCBuIAowMDAwMDAwODY5IDAwMDAwIG4gCjAwMDAwMDEyODMgMDAwMDAgbiAKMDAwMDAwMTQyOCAwMDAwMCBuIAowMDAwMDAxNzMyIDAwMDAwIG4gCjAwMDAwMDIwNTQgMDAwMDAgbiAKMDAwMDAwMjI2MyAwMDAwMCBuIAowMDAwMDAyNTg1IDAwMDAwIG4gCjAwMDAwMDI3NTEgMDAwMDAgbiAKMDAwMDAwMjk4OCAwMDAwMCBuIAowMDAwMDAzMTE0IDAwMDAwIG4gCjAwMDAwMDMyNTggMDAwMDAgbiAKMDAwMDAwMzU0OSAwMDAwMCBuIAowMDAwMDAzNjcyIDAwMDAwIG4gCjAwMDAwMDQwNzkgMDAwMDAgbiAKMDAwMDAwNDE2OSAwMDAwMCBuIAowMDAwMDA0Mzc1IDAwMDAwIG4gCjAwMDAwMDQ2OTkgMDAwMDAgbiAKMDAwMDAxMTc4NSAwMDAwMCBuIAowMDAwMDExODY2IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDM4ID4+CnN0YXJ0eHJlZgoxMjAyMwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:02.925215\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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IC9CYXNlRm9udCAvRGVqYVZ1U2FucyAvQ2hhclByb2NzIDE3IDAgUgovRW5jb2RpbmcgPDwKL0RpZmZlcmVuY2VzIFsgMzIgL3NwYWNlIDQ1IC9oeXBoZW4gL3BlcmlvZCA0OSAvb25lIDUyIC9mb3VyIC9maXZlIDU2IC9laWdodCA1OCAvY29sb24KNzYgL0wgL00gOTcgL2EgMTAwIC9kIC9lIDEwNyAvayAxMTEgL28gMTE1IC9zIF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNSAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxNCAwIFIgPj4KZW5kb2JqCjE1IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTQgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL0wgMTggMCBSIC9NIDE5IDAgUiAvYSAyMCAwIFIgL2NvbG9uIDIxIDAgUiAvZCAyMiAwIFIgL2UgMjMgMCBSCi9laWdodCAyNCAwIFIgL2ZpdmUgMjUgMCBSIC9mb3VyIDI2IDAgUiAvaHlwaGVuIDI3IDAgUiAvayAyOCAwIFIgL28gMjkgMCBSCi9vbmUgMzAgMCBSIC9wZXJpb2QgMzEgMCBSIC9zIDMyIDAgUiAvc3BhY2UgMzMgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0kxIDEzIDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOCAvQ29sb3JTcGFjZSAvRGV2aWNlUkdCCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDMgL0NvbHVtbnMgMjEyIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAxMDkgL0xlbmd0aCAzNCAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAyMTIgPj4Kc3RyZWFtCnic7V3JjyRXXn6xZkbulVWVtXZ3ubvdjd243TMej4ZmGHmYy3BhERck/gIkbvwLXDkjODBC4ogAjdBIiGWsQXgksGXLjPdeXVvXlhlVGRmRGUsGh/d9UT3tLIQ0BZFCv+9SP0W8ePHq6eX74rc+pQQCgUAgEAgEAoFAIPj/BuOrl+7/2ne04Pt9LVTMqRYW3VwLV5dqWlju1rWw1GlqwbUcLdgVDz1atv7bH/haiFP0s9Bpa8HMEi1MJhMtjMfjYkhVr6qFTGVaCKNAC+1OC41y3IonMV6rMBLLsrTQbDS0UK9j2I6DniM+lRsmOjTtFzpMc0zXH/7xn6mfx5/8wVtK8N/ij/707ReumLOaCQT/F5DFJygN9lcvffTxR1o4PTnRwkIFt4xFSEsZSNbweloYTcHRQQZKzQ1XC+EYtBVGoNQkA48fWyCyqo2n0hS3LPN8bJVKhV2N0GyKPo3xohZMUKtKSNyeDUoNyJv9LNVCrQbaNUxQs8GvBWWafBe+BNIEgmVzIgSXAdn5BKVBFp+gNMygXc+mCgzaVNfIti+tQDnt9bpoXPCXgaeiCbTUcQL6y3nL9aj/UtvNp2jT7kJ9ThPcch02ViqDIqssFyOZxHhLkqLzGm/ZdTxY5ZXUAFmbOTg9pZpP2lcN6r/BaMSewbYm2wzPTpXg8iA7n6A0yOITlIYZtFs1oBI2m1Agb28saGHRwxVnCtYL+lAksynWcTTC4yZVw1YHpl2bPOifDnGF7+82QbvDM7BeTMVWKRVR8cxJlwVLJnGE12Xoy6FqnNFwbZNcJxNccR18UphTjHYSQFtX1NYrVJ/TKcj6NJgoweVBdj5BaZDFJygNM2h3oYKLHvmr3YD+uNyCJTabQv+kGqosmyxFI+1kStYjudpUNrMJuDK30Pjw0MetBF0Ow7AYUpiB3BsePbkTNLMU+jQN0KVFn3I0wltqTpsDQJsx7d5RAtqdKtzyA3xR+COMPwjRZpzIb/UyIbMpKA2y+ASlYQbt9jpwiTYdMGm1CsG0wE0ezcVJCvqbUg/NczBaETeVxeCvaU6llTSa29A6hzF02yzDu0L6f9VzDt9hgB52J2jvMNyrFWAAybMjLUQ+iPvq8sv413pXtGA0YS6eDI61EATo8PQMtHt8CtZ+vH2GsTE0S3ApkJ1PUBpk8QlKwwzaXe/BfttyoeU1aiBHg7ypqBsaVGAnETjOJP8uNqFj1uvg8bNTcFy7BaV1SOvx0x3S3wTU5p6zrtqoUV92QIVPTnwtjHPavantdloI97p/5028dx/fBnmINu0lqO2TED0HAX6HFQe3rqyin15vRQsHZOSnH24rwS8M2fkEpUEWn6A0zKDdbhOarB37Wqg4aFarwAM7iUCXCX2jnQ78vzkNuXGGlZ0kYKsa83f2juAkffgEWufhEP3QoKu2vHPV8re/8zUtbK6hh79+76EWfvrFMy0Usc22iQEM/UP0OcTrmk2GK2eMoK7iikuNvmbgSsqw56vr63i8D5f0vwjtXgZk5xOUBll8gtIwy8jcXdJC1AddmgZVwhBsG8WgJNugTZg+2WI5Rwl4sLMA3TZmtNKj7T0tnJxRD6W12aK3t1Ut/MaqZ8PMW+WQXm6taWG/i/YHJNlJiPe+/9nnGBJt1EmDruE2FNgiObfdxhdFc0r/L23jeYy3by3XleDyIDufoDTI4hOUhlkhVUvLEBhJZTK51T8baCEZQe8zs8K3C2rLqRo3GrAtJwr9fPLwMy0E9MxWq4ja8lzGcdVBfwsW9V6l3ntwoIU0RrNJe1ULy128xVCg1CQFNYcMch7RthwzgMrgJ0FRL8RhmlDOBGCHkWApE4FzfjYILgWy8wlKgyw+QWmYQbuqqCBBL2eBCk2yNcWcIC5fkwHMCfm34sG3e/wMHB0eg7VvkCuZ46uqZNvbNzfQ4eS8SlXKWhZn5H3bgnW66UIDXVy4ic5fvqqFx1/+uxY+/WxXC65DAs1R5CpNMQMm1W3HxbumzBsqosUMQ36rlwmZTUFpkMUnKA0zaLdIkjWSiNegJI5GILuYqTSpCU02CGGJPQtBshtX0Hme4ta1JfDXjQ1QWzjGlY1b97Tg5mDbwWkRvqW8Duze6gSq6JVV+Ft9Vre4/ksIV24t1Ci8iq6OMKSBj/E7JGszh7qdMCWKZKsyqsZFuYzCbS24FMjOJygNsvgEpWEG7WYG/a2MKSroxquC0RqsbrF3VGTZIG3Hdlhm6gA65pgZPS+vgG2/9xYo8uEuilQ0N2DZXlqE9fjw6KAYUqdDlpwyAoqm4MMjvMWu+lo48ve1sLsPldZxMNpOG5waRSyTZePnZ5Bcp+Rfk8W1DCryYmO+XMjOJygNsvgEpWEG7XZYVCq1QbsBK0jkjJs6HfpaePr0gG3AcV4VC3r/EZTclSrstxsb1/CK9etacIbULWm+3nz9m7jwbLcYkpeCuDOFkYxGENZq4OuYeb5GHePfrDMCuQMqH54g7PnwAPlKCQtHj1ltUjEQul6BJTzmsQuF/VlwKZCdT1AaZPEJSsMM2h36OAHBjmGbdQqfJnN6bBaOCAOYbReaUEg7jKSK+qDd3gZOK9i4+5YWfraDiKbPH0C4v4Yiz76PKys3Xi+GZCokBccT8G+H+cJnhxitx8DjtS67ymBAdu4iuSmiIvxvP/qhFna2Ef9snVMqi0tTt00K/3VybvcW/OKQnU9QGmTxCUrDDNotTgfIItBuUQnZpJM3Y95Qn0Rkn9Fsy+N+1mgZfvO7v66Fzdvf0sLf/OAvtLBKzdRi1PHuIyTkrl5/tRhSdRHhUvWc0Vl90KU3BaXGrNdxPITQWX5JC4urW1qIAkQ7m0wkylwouYWROWGQs8ECXAaPFCzirwSXAtn5BKVBFp+gNMzgEVZ7UhmVu8K5SUeoylkug6UZVXcR/tPVOqj569+4rYVX7oNtB4ew1lZSXwvXN1GtcWqgo9UejMbp+DyBKPSLgpO4mEQYecaY6oe7O1r4z5+9q4X738JTi6tQt8+GIGs6e9XSFr4NpoUDNybJ8vvh9AijnQz52FdQrSFsOz5+qoVi0iqctJMA/+M/fYChLi0iVGyTk/YGJ+3bv/v7Whgc4oPkX//qz9HmDiatyriyzrW7Wli59Q0tmPVuMbY45ZlKEatcsib2gwefauG5SYOR/9V7OHb5YB8HQb79o7/UQv8UFobnJg0h4il7/p9Mmux8gtIgi09QGmTxCUrDjG++KU0M0QTfKC4NInZRUcUEx99cg6Wj6mEdb11D8tjr3/6uFtZu44vkg5/+QAtXr+CLZPXOa3jF8g28gh9P4TgohhSdwcJysIfaZIMDfDZlCQwrXhOelSVWHd3ee18LK2vIiEtD9Jnz2GljhHS4LOfRIPzm9SqMHVxl7lyFVqivYN4mrZgxNceTJjufoDTI4hOUhhm061i4OKCrIGOOmVdDrprFoLceLSzbe74WbvzO97Ww+dr32SVYJhlC52+zVvgyk9ZGNjjlo/f/QwuT6PzUybMzdH68+yUGwJM8qlWMduMl0MTdW3CHpFad/1EHgssTucbgi/ApogYL3kz5ewwYPFFbRD8r64vqAszbpBUzpuZ40mTnE5QGWXyC0jCDdicR63fz+EmDxbIdk/lsTGzzGrj1m7/3W1q4/xvf00JricdXPPpECxYf94eIAjx6gqJpe0Ns4G//3d9qoeGdx6yPJ1C4VldAPa0mdMnHO+CUmJ1317e0cOu1N/A8A/v6PtS9cIxf3SBi0bQc/+w4groaMGcvZxbBKx11EeZt0ooZU3M8abLzCUqDLD5BaZhlZOaxkYrp0wYLaqc8/sqgUbFaQWTcvTewXRfHR338AcyVgz2E6E1Y9Ww4gGd6+8HHWghyqIROhjaN4vRopVpVqE7LCx0t7B8gID5l9EM4BNFsP/6Sz8EjHgSwuFZtDDut9LRwkmL8ngdza43HkHg2eGfIMjTp9DzW4QXM26QVM6bmeNJk5xOUBll8gtIwMy6cFTlTHijF8LeMRsWY8fQrbdg5/+GHf6+F7gr27d4aws7ikIXJHOzJjTr0L5slV+rkndUeTwEZ9osBeRYePDlC9lrCqLtmFRt+TJr4ghbX/U9xDsckZa03Hl+dFe/dJD3V8c+aFVBYlXyxwILmr9y5zhG9r17EfE1aMWNqjidNdj5BaZDFJygNs7TdKZySLlWnqs1g+eKwCnoAp0zVPj6GJhUcQfCSX0Yb5pp3F+Dm66wzVj5DlM7uHqqo5DxG2jTPx1ZEz1s8ErLOYm3UKZVVSFQqM56aafI/OmN0UFwBpzTXMYCRh8ZDnl45HuGXudgCcSz1LnRTztukFTOm5njSZOcTlAZZfILSMIN2TQOKUrUChSWnmlb3sG/Xm6CAkAc5LzZd9ojG8Smqp01N3AodbPIrK0jnnsbYrm/f3dTCOz/+Zzyen4dUOSwSGgUIWGo1Yed0bXCKxYKqwRhDeryPgFt/gCFNDPS5fBu/uo0O9b4cgxwc4xXumGzFWjNReH4Q5guYt0krZkzN86RddEMg+N+GLD5BaZhBuy5Tw0OetmjRUTil6TJkBorF8t8VF+TiOGjsMqul3cKVZ6zxHW6AL3pXEEC7e4hSoXfe/FUtBEd7xZAefQ4b7CjwMW4Lmle7jZ3foJl3fxcPfvkEdlqzggG0VjHI5S4IyCDdGH20WRgwyrcHU/BmB4bfBx8/Uxdg3iatmDE1x5MmO5+gNMjiE5SGGbS7sszzI08QwxOx1jaPmlK5CRXG5onIrRbciy4djtEIQTUej39WPKr53Xfe0cL12+CUnR1sziZNsrXKeSSzRebyPOzzowAMEkUQUjpVGx4a3//6LS1UmXqTWrDuFomr0TYYxBwiOqhXa2rha7dg7+11EF383v4jdQHmbdKs53y7cztpsvMJSoMsPkFpmEG7V69AFWob2FQfbGO/PTiCmhYzu6TRQA+j0NdCNkWUjsWV3T+CUjYMYLccJ1CprBxPNRvQkg6egbZ2eNKGUmqag1ZWlsFTxhRcMPARRFSpY0idNijAZQ7pJKajk5UrRgwPjgMaRacY7c0ra1pYZ2G17R3Q3MlRqC7AvE1aMWNqjidNdj5BaZDFJygNM2i3tUDNixvmQo+5PHXYG48PYEod089ou7BAxkUmDc/KShgCdBrBb1inbjUOsZNHY0TbxnwqS859gnmOAQRnUB1bLZ7o3IJSFhUFwU/wlkYDWl5RJNRIeSKmDe8kj7hSrotXbN3cQochGv/kJzDYfvjZoboA8zZpxYypOZ402fkEpUEWn6A0zKBdmyWMqi2oOd0GC4KzPKDj8fQpOvVUhjZeFdbFjLFA2QRbultDY6eol2iBCCY8zirmMRj5c/paUaM859mQTFQ970q5YCV/gNdFDBhud8BuNqnEZHppyFimg2PomwNql8MRtMt//DGqZh9cqLfN3aQVM6bmeNJk5xOUBll8gtIwg3YDmhCVhaJGjTr2a8d78SDkdpvlic4iCnA4BgxhTcZMF3VhgazSlZkyAMlmSJLLn4NTOdfXDB57WaN5tsguSjPQhMsCTa0OWKnfBykMSU+tLgYQpnjvF09gnv3kQxQuXmHg0EqRncrDRpZoiX3SPzeA45+ds0krZkzN8aTJzicoDbL4BKVhBu3u4PwmNfFBE81l6DJVj6oQuEV1u+ghGEGr8X0IgxNml2CTVtYUpDBlCcEsoyWZxZ2KX0NxDKRSymIMUkT1MKfj0aG/Mg3hr8xoOM2YJuMzg4blIlSffPf4CwzOP2GbERqttle18Oo1FC7mQ+rdx/yXiHmbtGLG1BxPmux8gtIgi09QGmbQbuYgAidx39TCZAo1x0wR51NtY3vvLINlForqviHUHL8PV6B/DOKIRjwqMqWRM8fqn7Juw5jFjV2XbZSyWINiOEaziCV/HVZlbPIA56kJO2eS4HWVOqsyOhhtx8V/dEN1tHD3HtS023fvaWHrJvJ0vvkrIJedPRY6fvex+nnM26RZz5XWnNtJk51PUBpk8QkEAoFAIBAIBAKBQCAQXBr+C7mW3JsKZW5kc3RyZWFtCmVuZG9iagozNCAwIG9iago0NTE5CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozNSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTAzKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDM2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDExMzM5IDAwMDAwIG4gCjAwMDAwMDYzNjUgMDAwMDAgbiAKMDAwMDAwNjM5NyAwMDAwMCBuIAowMDAwMDA2NDk2IDAwMDAwIG4gCjAwMDAwMDY1MTcgMDAwMDAgbiAKMDAwMDAwNjUzOCAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTQgMDAwMDAgbiAKMDAwMDAwMDcyMCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3MDAgMDAwMDAgbiAKMDAwMDAwNjU3MCAwMDAwMCBuIAowMDAwMDA1MTAwIDAwMDAwIG4gCjAwMDAwMDQ5MDAgMDAwMDAgbiAKMDAwMDAwNDQ4OSAwMDAwMCBuIAowMDAwMDA2MTUzIDAwMDAwIG4gCjAwMDAwMDA3NDAgMDAwMDAgbiAKMDAwMDAwMDg3MyAwMDAwMCBuIAowMDAwMDAxMDM1IDAwMDAwIG4gCjAwMDAwMDE0MTUgMDAwMDAgbiAKMDAwMDAwMTU2MCAwMDAwMCBuIAowMDAwMDAxODY0IDAwMDAwIG4gCjAwMDAwMDIxODYgMDAwMDAgbiAKMDAwMDAwMjY1NCAwMDAwMCBuIAowMDAwMDAyOTc2IDAwMDAwIG4gCjAwMDAwMDMxNDIgMDAwMDAgbiAKMDAwMDAwMzI2OCAwMDAwMCBuIAowMDAwMDAzNDIzIDAwMDAwIG4gCjAwMDAwMDM3MTQgMDAwMDAgbiAKMDAwMDAwMzg2OSAwMDAwMCBuIAowMDAwMDAzOTkyIDAwMDAwIG4gCjAwMDAwMDQzOTkgMDAwMDAgbiAKMDAwMDAxMTMxOCAwMDAwMCBuIAowMDAwMDExMzk5IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDM2ID4+CnN0YXJ0eHJlZgoxMTU1NgolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:03.075694\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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Rm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTQgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL0wgMTggMCBSIC9TIDE5IDAgUiAvY29sb24gMjAgMCBSIC9kIDIxIDAgUiAvZSAyMiAwIFIgL2VpZ2h0IDIzIDAgUgovZiAyNCAwIFIgL2ZvdXIgMjUgMCBSIC9oIDI2IDAgUiAvaHlwaGVuIDI3IDAgUiAvaSAyOCAwIFIgL28gMjkgMCBSCi9vbmUgMzAgMCBSIC9wZXJpb2QgMzEgMCBSIC9zIDMyIDAgUiAvc2V2ZW4gMzMgMCBSIC9zcGFjZSAzNCAwIFIgL3QgMzUgMCBSCj4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0kxIDEzIDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOCAvQ29sb3JTcGFjZSAvRGV2aWNlUkdCCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDMgL0NvbHVtbnMgMjEyIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAxMDkgL0xlbmd0aCAzNiAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAyMTIgPj4Kc3RyZWFtCnic7V1ZjxzXdb7dXb2v0zPTs+/D4TYkRVmmREmRAjixHMeGHTjrS35BgDzkXQ/6GXkL8pDASGB5EYxIsWNLorbQUkiRFGe4zD7s2XvfqysP9/vumKoezkuAAoLzvfBDd3XVrcHl+c4599xzlRIIBAKBQCAQCAQCgeD/G3zuj/7xp+9psnn/d5rsrXyliW1bmgxNntNkcu68Jn3Dk5pEorhm+e5Hmqw9vK1Ju1zRJMD7pPrSmliRmCbXXnlNk/mFc2ZIjeKhJnfvfKFJt9vSpNVuaHLv7pealAr7mjRbTTy3FdDk8KCmSbla16Rj45pcrh8vkk3gZZ0yrmlzGHVHk5/+5F31NN566y0leCbefPPNr33i92QcAoGSySfwEJb7o9IRNK4/k9XEGRwCsVKajEzOamJ3oUn+LhStW+to0jg6wK/qUMaxgZwmkxPzmkzMT2kyOjauSS6HZwWDYTOkTgaiPDE+jE86kN1GAwJaOIKm7+9j/FYogt/7ILt9/bhnJI6viiVcHI7gT9F1MP6ghYtLxSNNWk1HnYbhK9c1EacF19BpcUMsn8AzyOQTeIYesqvaMJStJkitBis9vTCmSaVaxTW029kBSkAQE/rMmQVNXn7pBU3GhqCt6fQgHmXZmsQi0DiLyubrdMyI6lUoTpNji0UhNH0ZSPnc7AVNvvpqibfAxc0m9CKd6tMkGMIlxRJezVEg3S5GcHSEd6zXIC7O6aorTsuJTosbYvkEnkEmn8Az9JDdDk2xrwNNDIeimhT3EQr1D8POT16EBOQmRjUJGkljnNPuQDjuP4GU1B7v4Ss/7P/Sl7c0+eZ5qOdr175phuRQ8Eqloibra9uahIKQgFAIojYwCN9gfeMBvmIwWKlXeR+8iBVEmj2VwjX1OhTQpux3Ol38HcJ8tWdAnBalVC+nxQ2xfALPIJNP4Bl6yG6zBoOZiELRUlnY+eevPKfJxOwZTco070uPNzQp1WClK4WCJgcFqO2TPAKfFIVD+WGTf/6v/6ZJ8K/+UpPXr79qhhQMQgKGhyHuyoFuFo6QzPz8CyRjLQZ68SSEuGNDC1oVDCnA/3SDg8iO2jaE4+AQLoFfQZssC3+lTCajToM4LZq4nRY3xPIJPINMPoFn6CG74XBQk3YgqUk9igW7lRI05X8+/EyTwwNEUlvbO5oEAzDFQT/sbfM4pQkyMojn7ubXNEmFoZXlQkmT5ZUVM6SRkQHcM4gfjkwgXzpKsp6H7i99CZIbgbivrkMmVBtD6rZAbAaMkRAGELbw+vUGvkqlGIpax5nbkyBOC4bmclrcEMsn8Awy+QSeoYdJjMWwPrhbgCg83IAo3Lt7RxM/5c9mKrVehtwEqLb1JgT0qAxSrkCjVzZRZZSIQtnPzZ/F46nRNz74jRnS1MyMJgtnkXrt74cUmlXFdAp64e8gpqs28V/L5DnrBaiMbcN/iEQhspUSvkolzZ2xptlqmXRxTZ0GcVrw1i6nxQ2xfALPIJNP4Bl6yG4mCyv9cGNZk+1V2PB4EPpVqCLyqhShF74uTHGBBbcF1gJZVKKBIQh6lNI2Nn1Fkwlq3MqtjzUJ+FpmSG0bNnxvH6HfpUuoAZ4/g/KkCcpE4qWrmty+v65Js4HAsxmkcCiEcqYEKJ/f0iRECUv3DfH5XKasny674rTwzl93WtwQyyfwDDL5BJ6hh+w+eoRY7P6jh5psbz/SxKakJtOI4M4twKQvnl/U5MkebPLaHqRkcBhKNDWHi5P9qOTZYeGNsw9lX1tFBLfH5KpSiiuW6o8XoLbVCp7ShSArpwXFufsJhPvM2ec0GRrLaPLJZ+9rkt+BlrXbrBxm4dPhIRQkmkAFUdeBFFaYQH4GxGnRxO20uCGWT+AZZPIJPEMP2f3kfbTLsIYQQ81fuKRJlNnF8xewOnl2AdVBdgOW3/FDEKvKFN7AbgcCGU3aHRjnahk7UNItGHCzkri2c2iGFEnAqpti2tm5aTyO/3/qBYSi9z/F7lSnjtEuvvEdTS5dhsrUb0J2Hz2EJsbicCTSfQN8LNSqxG0yzcbp0a44LZq4nRY3xPIJPINMPoFn6CG7O+uoinn+yp9qEg4jFMpCWtXIKOKdQyYeNx5Cm1pdpit9sOkBi0uBDveSdEyWFfbfsXFNMoNnHZSPoyR/KK5J93jrLAmNeiKCIU2PsvVEANf4FfTu0iKUy9Qk/6wOvcg/wfjHcqg7sn2IN4NBfGIqgZW6r06AOC187NedFjfE8gk8g0w+gWfoVVKVQHlqkMpWKOxqEs5mNKlxV0gD0qSifVhnDHfZcJJFNQ4f0mjDtpteTH7mQrt+fJLoh8aFnGNzHYhCL5wQ5Knrw618NhTZH8AdgnHswYkmQDpN+AYHW0jq9sch7j/47hua3Ly1qkmFQtxowv1o1iGFmWSfOg3itGBsLqdFqd+ppyGWT+AZZPIJPEMP2R2dgnX1+TE1Gw1ENzslXB/KIKhpdyBtviBLWFn503bwc7PvphMAiaVg5HP9BU2cQyhIi3lLX/f4P0Y0iq2vfiqXWVW0uXDpDzJg5P6WSrXMW7HfBd+otAf9jcbQS+q165c1WXqEPO2de3ncpwQJM7tcnwFxWjBal9Pihlg+gWeQySfwDD1k12EzQLNyVyvD8IYpf2VmDlsNhGA11rKyg4JKxiGyg32QtlQWRn4wg/vYFqqD6mE863AKwtG0nxyPqW2aMJhuSHiMzdJfH2U3k+Wqos1f8UXSaTw35IMoFsoFvHUb3sJz57GnJpPE+H/xCxx8sJffU6dBnBZN3E6LG2L5BJ5BJp/AM/RqZUBps9gwP80gbyINsTs3m9EkEYFJD/gwj6ulgiaNGlZCo3FsITl7BlZ6Ygprmv4gugqb7hATIyO4eGXXjCiVxQiyfVAcy4JgsRmhcqgpkTgaNXQaEBc/rwkaKVTwFvoHsChZ4Z7cagF6MTaImO6H3/+2Jm+/85/qNIjTgvu4nBY3xPIJPINMPoFn6CG7r1//hiazF7A/ZXsLNTljo5CAhTNzmgwPoqo24MCSl2mKm7T2Pj++SsQhHIkEy4TYODFIia9XuTa6OGWGNL0wrUmbZweYWqBOF6LgsNFEgLti2w2oQ5fC4bfwK1+E8sZPTL9iK4DA027hRQYpza/+ATou/vjf31MnQZwWpVQvp+XHP/mVehpi+QSeQSafwDP0kN1vXMaZcRevQnbrixDZeJobNnmx44OU+KlW2TjiHWZJjyd4l+nKDnXQHBzQZHXQ3DxKeqIsBFJK1atF3pMD9lkcALWV9UI2h2Ta8re4vGh3uZRpcdgcXZmn2q2toLvFK69iB2utjVA0ZsT6ZIjToonbaXFDLJ/AM8jkE3gGmXwCz9DD54sa34KHKsVjvMxiQZg5Zsn4fMbT4ibhrmlkSW/MrLV36DTSn1EOcw0JHtbYsY1jqeyuWRLHDxxujvKbW9jM2rM5pmOKxZn+8HGTdJg3DNp4btzsH9uBg7j3GMvn42eR2tj3V9RpEI9ZE7fH7IZYPoFnkMkn8Aw9ZDeZ5inF1IJak4cLNpHabvKTasUcoNjiV9ACc+5Rm+rQ5jWms3aNRWMdakoym+YwMmZImSQK4CIhpOZtJheUj4kABZJMIhNxsMuS7jrkstvFqrlPMcVv441SXAifmkRvlDobojnMR6STx1p2EsRp0cTttLghlk/gGWTyCTxDD9l9+2e/1MQOfqDJ0RFMaKWIBiJmsdno784OrrEpKlmm7/sGsKErzC1S1cOCJssP0Nu6yKK3ydlpTQKsLFdKpZK4w8wMorlxHiAxM4tTErNhCEcygh92GV2qAGvseBZigKn5AH81NE1lZ2vstsNt2zwIMpvlDU+GOC2auJ0WN8TyCTyDTD6BZ+ghu+/910eaZMbR5MuxYXg/v/FrTaYnJjQZ6Icgbm6gbrvDmCjGPdIt1mrvbCID+a1r1zV57vJFTWpN7J82Z1SsrK+ZIS0/QHPP21+igVdfBsvVP/rzP9PklYs4ZCLE/Oz4CAbZouyaRXoTQrZN3MeTnMIZ6E6U0WU3AAE69gNOhjgtuNjltLghlk/gGWTyCTxDD9n9i7/5W03COTTQrJVRG718+5YmI8NQND+1KRpBnNXqIs24sIif941AQWoDCJe+9yd/pEksiYq0KmXX9Crp/F4j6UYH3+7uYt/X2so27hCDOuQ30QJ79e4DjI2tUB7nUVN+7dsvaDI1jV1eRkr8EcpDEHrhM2Eau5WFfCf2tjYQp0UTt9Pihlg+gWeQySfwDD1kNxzCjFy+j1MSS0XIrmPsLY9tqDBNapYpI+a04xpCsOIefrWzDuH45X8gJDximFasoOwnyUYkaW6WVkrFGUNtbkJtcwMI0yIpaPoH7+Cehw/gG5gzHR/mEUtuVtE25cx5qEw6FePj4DZEYxCOdBwvEuThUrFYWJ0GcVr0v26nxQ2xfALPIJNP4Bl6yG75ADLxq7ff0WQjD7n0tyEKt27xRAqqbadjzCwM/rs/xyZh09Dz6vPPa9IKoRFnqYllysfrsO0HB0icthrHwrH1BOc9rKzi2xeuYpPY3//dP2jy2ccIMztFKEiRa6l1Vgc9+m+8yPs3EV3GLUhzkF07Azw3MUXZHZ9G17Mf/Oiv1WkQpwWfuJwWN8TyCTyDTD6BZ+ghuyND6PexMAO5cVg6awVIjne+YPo6XJQMRVjuS7UdHYWR/8M30D86GaO5jiCCu3cH1n7pAQ5rHB6fMUNqMPMZiOKHd5ZxANW9ZZypHJvB4YpbW1CcbB9uHmQpUSyB8PAwjxzs/iaCu719iEuDJ0i1GTpuF/BXevlbp28aF6dFE7fT4oZYPoFnkMkn8Aw9ZPdwD6nIl158WZOXX39dk3AY1tWi2po0qdn5ElCswGkhu1hvQR0ONmH/Dxsw14f7eNYjqu32LmQrkRs7HlMY0uMLQXZbHYjCu7/9UJPpORzuOJllKMdtqrEgtKDZQHj4qIhQNJlEeGizR3b+CEuxAwPTmtS4l+fXv/1MnQZxWjRxOy1uiOUTeAaZfALP0MMkxrmCeVDC6t4Xt3FeYC4HUzyU40kS3N5ydFTA77kmaLEh19gMlgIneMjT1jLCpWoF6jk0DLWKcQXT4jmISqlaHfccGUEtbn57U5P9fTx3dJQJW+ZyK0yTKh5l0TY7T6OoKQpTAVsHPGPDjzBtiMrVanAHkDm08WSI06KJ22lxQyyfwDPI5BN4hl4lVUHYyWajoMmNGzh/wmlD/lIxBD7mtIkGexlZnNDT3K6y+BIiqblJ6G9hA6KZP8KemlAUtn2+H3tb9vaOW0JdPreoycVLKA/+l3/+Jz6OklrF2FotEKfDYp4Id74wBTozO6vJ7sYSruHZUFEegHGBK5iNGkYyweqmZ0CcFgzJ5bSsPP5cPQ2xfALPIJNP4Bl6yG6tjvBKMRb7zne/r0m3BeMcoNp22Q3J4TaTAM94MCc65AtQ5HIBKc3DOg9HjCAQu/8FNrkcfAQDPjtzzgzp2jzKeltUkGgIWuCYJhL8ypxbbMp66+wmYbHydmocstuoYCnzYgrZ3U9vQh2216DI9So7P9SO1GkQp0UTt9Py6Q31NYjlE3gGmXwCz9AryZyAbqaZU00OwoQ2WWYT4awN+XCxwyMVwzG2MGrA3pbLqIANcLtKbi6jyVwMwrG8AtlVPLUxSAOulNp6sq5JP6O5gUEsQTbZDanRRJ1StdLgV0iKtll6ZEXgCQyN4nCm1W0sSu6sI0/bYFXwwzvY5drfj4ud3ysPPgnitGjidlrcEMsn8Awy+QSeoVe0W4Z5V11MzaAPScWdHUjSg3urmkQsHgDMboQDTKWODmBTiUUB6k+jO4RpGNyoI34cykGRx3lG1HY+b4a0tHRPk5kWDT6PSTbnRdVquL5UhMo3GWfZLShXIAx1uHMHStqiI5HLoavh+BWscuYG8cnAIELICH/+DIjToonbaXFDLJ/AM8jkE3iGHrLbZZrRnC9jtWHMU8yg3vz4N5rkd2D5fSy8efFFNFV49TpIsQhTfPvzTzWpcgVzaQ2K8Hh1VZM6Gw47PIhRKRVJwXSXStCCMvOr1RKE21xt8QDFdBIyMcqi4uwA8rS5USjp6FWIbJbxWsgEniRGy46PBToZ4rTgE5fT8uHNT9TTEMsn8Awy+QQCgUAgEAgEAoFAIBAI/s/wv8O4lCMKZW5kc3RyZWFtCmVuZG9iagozNiAwIG9iago0NjE4CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozNyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTAzKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDM4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDExNzk1IDAwMDAwIG4gCjAwMDAwMDY3MjIgMDAwMDAgbiAKMDAwMDAwNjc1NCAwMDAwMCBuIAowMDAwMDA2ODUzIDAwMDAwIG4gCjAwMDAwMDY4NzQgMDAwMDAgbiAKMDAwMDAwNjg5NSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTQgMDAwMDAgbiAKMDAwMDAwMDcxNiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA2OTYgMDAwMDAgbiAKMDAwMDAwNjkyNyAwMDAwMCBuIAowMDAwMDA1NDM2IDAwMDAwIG4gCjAwMDAwMDUyMzYgMDAwMDAgbiAKMDAwMDAwNDgxOCAwMDAwMCBuIAowMDAwMDA2NDg5IDAwMDAwIG4gCjAwMDAwMDA3MzYgMDAwMDAgbiAKMDAwMDAwMDg2OSAwMDAwMCBuIAowMDAwMDAxMjgzIDAwMDAwIG4gCjAwMDAwMDE0MjggMDAwMDAgbiAKMDAwMDAwMTczMiAwMDAwMCBuIAowMDAwMDAyMDU0IDAwMDAwIG4gCjAwMDAwMDI1MjIgMDAwMDAgbiAKMDAwMDAwMjczMSAwMDAwMCBuIAowMDAwMDAyODk3IDAwMDAwIG4gCjAwMDAwMDMxMzQgMDAwMDAgbiAKMDAwMDAwMzI2MCAwMDAwMCBuIAowMDAwMDAzNDA0IDAwMDAwIG4gCjAwMDAwMDM2OTUgMDAwMDAgbiAKMDAwMDAwMzg1MCAwMDAwMCBuIAowMDAwMDAzOTczIDAwMDAwIG4gCjAwMDAwMDQzODAgMDAwMDAgbiAKMDAwMDAwNDUyMiAwMDAwMCBuIAowMDAwMDA0NjEyIDAwMDAwIG4gCjAwMDAwMTE3NzQgMDAwMDAgbiAKMDAwMDAxMTg1NSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOCA+PgpzdGFydHhyZWYKMTIwMTIKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:03.202620\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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NjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEy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UiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMzUgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDEwMyswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAzNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAxMTY2NyAwMDAwMCBuIAowMDAwMDA2NTgxIDAwMDAwIG4gCjAwMDAwMDY2MTMgMDAwMDAgbiAKMDAwMDAwNjcxMiAwMDAwMCBuIAowMDAwMDA2NzMzIDAwMDAwIG4gCjAwMDAwMDY3NTQgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk0IDAwMDAwIG4gCjAwMDAwMDA3MjAgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNzAwIDAwMDAwIG4gCjAwMDAwMDY3ODYgMDAwMDAgbiAKMDAwMDAwNTMxNyAwMDAwMCBuIAowMDAwMDA1MTE3IDAwMDAwIG4gCjAwMDAwMDQ3MDcgMDAwMDAgbiAKMDAwMDAwNjM3MCAwMDAwMCBuIAowMDAwMDAwNzQwIDAwMDAwIG4gCjAwMDAwMDA4NzMgMDAwMDAgbiAKMDAwMDAwMTAzNSAwMDAwMCBuIAowMDAwMDAxNDE1IDAwMDAwIG4gCjAwMDAwMDE1NjAgMDAwMDAgbiAKMDAwMDAwMTg2NCAwMDAwMCBuIAowMDAwMDAyMTg2IDAwMDAwIG4gCjAwMDAwMDI1MDggMDAwMDAgbiAKMDAwMDAwMjYzNCAwMDAwMCBuIAowMDAwMDAyNzg5IDAwMDAwIG4gCjAwMDAwMDMxODQgMDAwMDAgbiAKMDAwMDAwMzQ3NSAwMDAwMCBuIAowMDAwMDAzNTk4IDAwMDAwIG4gCjAwMDAwMDQwMDUgMDAwMDAgbiAKMDAwMDAwNDA5NSAwMDAwMCBuIAowMDAwMDA0NDE5IDAwMDAwIG4gCjAwMDAwMTE2NDYgMDAwMDAgbiAKMDAwMDAxMTcyNyAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM1IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzNiA+PgpzdGFydHhyZWYKMTE4ODQKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:03.332269\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["def compare_imgs(img1, img2, title_prefix=\"\"):\n", " # Calculate MSE loss between both images\n", " loss = F.mse_loss(img1, img2, reduction=\"sum\")\n", " # Plot images for visual comparison\n", " grid = torchvision.utils.make_grid(torch.stack([img1, img2], dim=0), nrow=2, normalize=True, range=(-1, 1))\n", " grid = grid.permute(1, 2, 0)\n", " plt.figure(figsize=(4, 2))\n", " plt.title(f\"{title_prefix} Loss: {loss.item():4.2f}\")\n", " plt.imshow(grid)\n", " plt.axis(\"off\")\n", " plt.show()\n", "\n", "\n", "for i in range(2):\n", " # Load example image\n", " img, _ = train_dataset[i]\n", " img_mean = img.mean(dim=[1, 2], keepdims=True)\n", "\n", " # Shift image by one pixel\n", " SHIFT = 1\n", " img_shifted = torch.roll(img, shifts=SHIFT, dims=1)\n", " img_shifted = torch.roll(img_shifted, shifts=SHIFT, dims=2)\n", " img_shifted[:, :1, :] = img_mean\n", " img_shifted[:, :, :1] = img_mean\n", " compare_imgs(img, img_shifted, \"Shifted -\")\n", "\n", " # Set half of the image to zero\n", " img_masked = img.clone()\n", " img_masked[:, : img_masked.shape[1] // 2, :] = img_mean\n", " compare_imgs(img, img_masked, \"Masked -\")"]}, {"cell_type": "markdown", "id": "7bfc9b9f", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.028564, "end_time": "2021-09-16T12:41:03.462047", "exception": false, "start_time": "2021-09-16T12:41:03.433483", "status": "completed"}, "tags": []}, "source": ["### Training the model\n", "\n", "During the training, we want to keep track of the learning progress by seeing reconstructions made by our model.\n", "For this, we implement a callback object in PyTorch Lightning which will add reconstructions every $N$ epochs to our tensorboard:"]}, {"cell_type": "code", "execution_count": 9, "id": "e6e82979", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:03.525671Z", "iopub.status.busy": "2021-09-16T12:41:03.525205Z", "iopub.status.idle": "2021-09-16T12:41:03.527269Z", "shell.execute_reply": "2021-09-16T12:41:03.526813Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.036008, "end_time": "2021-09-16T12:41:03.527366", "exception": false, "start_time": "2021-09-16T12:41:03.491358", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GenerateCallback(pl.Callback):\n", " def __init__(self, input_imgs, every_n_epochs=1):\n", " super().__init__()\n", " self.input_imgs = input_imgs # Images to reconstruct during training\n", " # Only save those images every N epochs (otherwise tensorboard gets quite large)\n", " self.every_n_epochs = every_n_epochs\n", "\n", " def on_epoch_end(self, trainer, pl_module):\n", " if trainer.current_epoch % self.every_n_epochs == 0:\n", " # Reconstruct images\n", " input_imgs = self.input_imgs.to(pl_module.device)\n", " with torch.no_grad():\n", " pl_module.eval()\n", " reconst_imgs = pl_module(input_imgs)\n", " pl_module.train()\n", " # Plot and add to tensorboard\n", " imgs = torch.stack([input_imgs, reconst_imgs], dim=1).flatten(0, 1)\n", " grid = torchvision.utils.make_grid(imgs, nrow=2, normalize=True, range=(-1, 1))\n", " trainer.logger.experiment.add_image(\"Reconstructions\", grid, global_step=trainer.global_step)"]}, {"cell_type": "markdown", "id": "0825de1a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.028704, "end_time": "2021-09-16T12:41:03.584828", "exception": false, "start_time": "2021-09-16T12:41:03.556124", "status": "completed"}, "tags": []}, "source": ["We will now write a training function that allows us to train the autoencoder with different latent dimensionality\n", "and returns both the test and validation score.\n", "We provide pre-trained models and recommend you using those, especially when you work on a computer without GPU.\n", "Of course, feel free to train your own models on Lisa."]}, {"cell_type": "code", "execution_count": 10, "id": "da4ec41c", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:03.648305Z", "iopub.status.busy": "2021-09-16T12:41:03.647832Z", "iopub.status.idle": "2021-09-16T12:41:03.649849Z", "shell.execute_reply": "2021-09-16T12:41:03.649433Z"}, "papermill": {"duration": 0.036319, "end_time": "2021-09-16T12:41:03.649948", "exception": false, "start_time": "2021-09-16T12:41:03.613629", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_cifar(latent_dim):\n", " # Create a PyTorch Lightning trainer with the generation callback\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, \"cifar10_%i\" % latent_dim),\n", " gpus=1 if str(device).startswith(\"cuda\") else 0,\n", " max_epochs=500,\n", " callbacks=[\n", " ModelCheckpoint(save_weights_only=True),\n", " GenerateCallback(get_train_images(8), every_n_epochs=10),\n", " LearningRateMonitor(\"epoch\"),\n", " ],\n", " )\n", " trainer.logger._log_graph = True # If True, we plot the computation graph in tensorboard\n", " trainer.logger._default_hp_metric = None # Optional logging argument that we don't need\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"cifar10_%i.ckpt\" % latent_dim)\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model, loading...\")\n", " model = Autoencoder.load_from_checkpoint(pretrained_filename)\n", " else:\n", " model = Autoencoder(base_channel_size=32, latent_dim=latent_dim)\n", " trainer.fit(model, train_loader, val_loader)\n", " # Test best model on validation and test set\n", " val_result = trainer.test(model, test_dataloaders=val_loader, verbose=False)\n", " test_result = trainer.test(model, test_dataloaders=test_loader, verbose=False)\n", " result = {\"test\": test_result, \"val\": val_result}\n", " return model, result"]}, {"cell_type": "markdown", "id": "f9c30a4b", "metadata": {"papermill": {"duration": 0.028762, "end_time": "2021-09-16T12:41:03.707706", "exception": false, "start_time": "2021-09-16T12:41:03.678944", "status": "completed"}, "tags": []}, "source": ["### Comparing latent dimensionality\n", "\n", "
\n", "\n", "When training an autoencoder, we need to choose a dimensionality for the latent representation $z$.\n", "The higher the latent dimensionality, the better we expect the reconstruction to be.\n", "However, the idea of autoencoders is to *compress* data.\n", "Hence, we are also interested in keeping the dimensionality low.\n", "To find the best tradeoff, we can train multiple models with different latent dimensionalities.\n", "The original input has $32\\times 32\\times 3 = 3072$ pixels.\n", "Keeping this in mind, a reasonable choice for the latent dimensionality might be between 64 and 384:"]}, {"cell_type": "code", "execution_count": 11, "id": "af2a8f9a", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:03.768726Z", "iopub.status.busy": "2021-09-16T12:41:03.768265Z", "iopub.status.idle": "2021-09-16T12:41:12.482167Z", "shell.execute_reply": "2021-09-16T12:41:12.482555Z"}, "papermill": {"duration": 8.746282, "end_time": "2021-09-16T12:41:12.482697", "exception": false, "start_time": "2021-09-16T12:41:03.736415", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:678: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torch/_jit_internal.py:603: LightningDeprecationWarning: The `LightningModule.datamodule` property is deprecated in v1.3 and will be removed in v1.5. Access the datamodule through using `self.trainer.datamodule` instead.\n", " if hasattr(mod, name):\n", "/usr/local/lib/python3.9/dist-packages/torch/_jit_internal.py:603: LightningDeprecationWarning: The `LightningModule.loaded_optimizer_states_dict` property is deprecated in v1.4 and will be removed in v1.6.\n", " if hasattr(mod, name):\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "0f11cd1f8ebc4c9cbc69478f55dac457", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "1eeadd6528c746868701b83f54b200dc", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "47c8df90b5414d1e8ee6e8afc2057c52", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "0cdd717c36684008add2d6d3f93caa65", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "8bedc95274e84f799bbef9fc04ca9934", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "213f1af06c854f0fa5ac9c7f42e86b8b", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "07a7fcadf97f442ca3088a5bbbcba662", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "5cf0706e7c07460880d2fe93a1e1d4c4", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["model_dict = {}\n", "for latent_dim in [64, 128, 256, 384]:\n", " model_ld, result_ld = train_cifar(latent_dim)\n", " model_dict[latent_dim] = {\"model\": model_ld, \"result\": result_ld}"]}, {"cell_type": "markdown", "id": "e6e59925", "metadata": {"papermill": {"duration": 0.036287, "end_time": "2021-09-16T12:41:12.556351", "exception": false, "start_time": "2021-09-16T12:41:12.520064", "status": "completed"}, "tags": []}, "source": ["After training the models, we can plot the reconstruction loss over the latent dimensionality to get an intuition\n", "how these two properties are correlated:"]}, {"cell_type": "code", "execution_count": 12, "id": "1b5b80aa", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:12.655386Z", "iopub.status.busy": "2021-09-16T12:41:12.644902Z", "iopub.status.idle": "2021-09-16T12:41:12.911585Z", "shell.execute_reply": "2021-09-16T12:41:12.911109Z"}, "papermill": {"duration": 0.318993, "end_time": "2021-09-16T12:41:12.911693", "exception": false, "start_time": "2021-09-16T12:41:12.592700", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvTCAxOCAwIFIgL1IgMTkgMCBSIC9hIDIwIDAgUiAvYyAyMSAwIFIgL2QgMjIgMCBSIC9lIDIzIDAgUgovZWlnaHQgMjQgMCBSIC9maXZlIDI1IDAgUiAvZm91ciAyNiAwIFIgL2kgMjcgMCBSIC9sIDI4IDAgUiAvbSAyOSAwIFIKL24gMzAgMCBSIC9vIDMxIDAgUiAvb25lIDMyIDAgUiAvciAzMyAwIFIgL3MgMzQgMCBSIC9zaXggMzUgMCBSCi9zcGFjZSAzNiAwIFIgL3QgMzcgMCBSIC90aHJlZSAzOCAwIFIgL3R3byAzOSAwIFIgL3UgNDAgMCBSIC92IDQxIDAgUgoveSA0MiAwIFIgL3plcm8gNDMgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL00wIDEzIDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JCb3ggWyAtMTIuNjA4NDUyMTMwNCAtMTEuNDcyMTM1OTU1IDEyLjYwODQ1MjEzMDQgMTMgXQovRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDk5IC9TdWJ0eXBlIC9Gb3JtIC9UeXBlIC9YT2JqZWN0ID4+CnN0cmVhbQp4nG2OsQ2AQAwD+0yRBRIlJp/8t5RMghDs3wIdSN9YsnWyDT7IeKNHOl8krjXSHQyNgi/JJ0lpWo/2D6HD0ovFdESg8IahZUAHS35QY1nUWiv0x06ZWdtsdvZvJ1rpBiCOJoAKZW5kc3RyZWFtCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago0NCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTEyKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQ1CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDEwNzM2IDAwMDAwIG4gCjAwMDAwMTAyNzQgMDAwMDAgbiAKMDAwMDAxMDMwNiAwMDAwMCBuIAowMDAwMDEwNDA1IDAwMDAwIG4gCjAwMDAwMTA0MjYgMDAwMDAgbiAKMDAwMDAxMDQ0NyAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTggMDAwMDAgbiAKMDAwMDAwMTMxNCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDEyOTQgMDAwMDAgbiAKMDAwMDAxMDQ3OSAwMDAwMCBuIAowMDAwMDA4OTEyIDAwMDAwIG4gCjAwMDAwMDg3MTIgMDAwMDAgbiAKMDAwMDAwODI3NyAwMDAwMCBuIAowMDAwMDA5OTY1IDAwMDAwIG4gCjAwMDAwMDEzMzQgMDAwMDAgbiAKMDAwMDAwMTQ2NyAwMDAwMCBuIAowMDAwMDAxNzcyIDAwMDAwIG4gCjAwMDAwMDIxNTIgMDAwMDAgbiAKMDAwMDAwMjQ1NyAwMDAwMCBuIAowMDAwMDAyNzYxIDAwMDAwIG4gCjAwMDAwMDMwODMgMDAwMDAgbiAKMDAwMDAwMzU1MSAwMDAwMCBuIAowMDAwMDAzODczIDAwMDAwIG4gCjAwMDAwMDQwMzkgMDAwMDAgbiAKMDAwMDAwNDE4MyAwMDAwMCBuIAowMDAwMDA0MzAyIDAwMDAwIG4gCjAwMDAwMDQ2MzMgMDAwMDAgbiAKMDAwMDAwNDg2OSAwMDAwMCBuIAowMDAwMDA1MTYwIDAwMDAwIG4gCjAwMDAwMDUzMTUgMDAwMDAgbiAKMDAwMDAwNTU0OCAwMDAwMCBuIAowMDAwMDA1OTU1IDAwMDAwIG4gCjAwMDAwMDYzNDggMDAwMDAgbiAKMDAwMDAwNjQzOCAwMDAwMCBuIAowMDAwMDA2NjQ0IDAwMDAwIG4gCjAwMDAwMDcwNTcgMDAwMDAgbiAKMDAwMDAwNzM4MSAwMDAwMCBuIAowMDAwMDA3NjI4IDAwMDAwIG4gCjAwMDAwMDc3NzUgMDAwMDAgbiAKMDAwMDAwNzk4OSAwMDAwMCBuIAowMDAwMDEwNzk2IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDQgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ1ID4+CnN0YXJ0eHJlZgoxMDk1MwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:12.740985\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["latent_dims = sorted(k for k in model_dict)\n", "val_scores = [model_dict[k][\"result\"][\"val\"][0][\"test_loss\"] for k in latent_dims]\n", "\n", "fig = plt.figure(figsize=(6, 4))\n", "plt.plot(\n", " latent_dims, val_scores, \"--\", color=\"#000\", marker=\"*\", markeredgecolor=\"#000\", markerfacecolor=\"y\", markersize=16\n", ")\n", "plt.xscale(\"log\")\n", "plt.xticks(latent_dims, labels=latent_dims)\n", "plt.title(\"Reconstruction error over latent dimensionality\", fontsize=14)\n", "plt.xlabel(\"Latent dimensionality\")\n", "plt.ylabel(\"Reconstruction error\")\n", "plt.minorticks_off()\n", "plt.ylim(0, 100)\n", "plt.show()"]}, {"cell_type": "markdown", "id": "e03a327e", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.037468, "end_time": "2021-09-16T12:41:12.988817", "exception": false, "start_time": "2021-09-16T12:41:12.951349", "status": "completed"}, "tags": []}, "source": ["As we initially expected, the reconstruction loss goes down with increasing latent dimensionality.\n", "For our model and setup, the two properties seem to be exponentially (or double exponentially) correlated.\n", "To understand what these differences in reconstruction error mean, we can visualize example reconstructions of the four models:"]}, {"cell_type": "code", "execution_count": 13, "id": "24dd2b64", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:13.069820Z", "iopub.status.busy": "2021-09-16T12:41:13.069333Z", "iopub.status.idle": "2021-09-16T12:41:13.071023Z", "shell.execute_reply": "2021-09-16T12:41:13.071399Z"}, "papermill": {"duration": 0.045096, "end_time": "2021-09-16T12:41:13.071514", "exception": false, "start_time": "2021-09-16T12:41:13.026418", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def visualize_reconstructions(model, input_imgs):\n", " # Reconstruct images\n", " model.eval()\n", " with torch.no_grad():\n", " reconst_imgs = model(input_imgs.to(model.device))\n", " reconst_imgs = reconst_imgs.cpu()\n", "\n", " # Plotting\n", " imgs = torch.stack([input_imgs, reconst_imgs], dim=1).flatten(0, 1)\n", " grid = torchvision.utils.make_grid(imgs, nrow=4, normalize=True, range=(-1, 1))\n", " grid = grid.permute(1, 2, 0)\n", " plt.figure(figsize=(7, 4.5))\n", " plt.title(\"Reconstructed from %i latents\" % (model.hparams.latent_dim))\n", " plt.imshow(grid)\n", " plt.axis(\"off\")\n", " plt.show()"]}, {"cell_type": "code", "execution_count": 14, "id": "2b81b994", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:13.150447Z", "iopub.status.busy": "2021-09-16T12:41:13.149978Z", "iopub.status.idle": "2021-09-16T12:41:14.196317Z", "shell.execute_reply": "2021-09-16T12:41:14.196704Z"}, "papermill": {"duration": 1.087391, "end_time": "2021-09-16T12:41:14.196840", "exception": false, "start_time": "2021-09-16T12:41:13.109449", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDQwNSAyMjcuNjU1NDM0NzgyNiBdIC9QYXJlbnQgMiAwIFIgL1Jlc291cmNlcyA4IDAgUgovVHlwZSAvUGFnZSA+PgplbmRvYmoKOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEyIDAgUiA+PgpzdHJlYW0KeJxVj01PwzAMhu/+Fe9xPZDGSfp13DSo4LapEgfEYcpSoFo79QP293HLGCKSY7+29dhmNBSvGW8j5INGI3aRuJw1aVEtOZ2IPy3emEylSeJsIgn9X74T1dQjU2YxW2iVgotcsdXOuiw3KYaAZ3SI1+ZnZiN2EXqJeBu+PnzYlxv4UTC2YMzzuShuSN8ifmRsz9jRDv0vRitOZPsbbZblNUs9zZw7AYGZldGWjdwhof1by7e0qRA/SI9BVS+HV0d6wWofwTllMp3b5WEV/Lkbp+HTT+GIeohgtLoWl/q5RepwOkyhm8YIr6ie6L4i2Zi+AR1UUP8KZW5kc3RyZWFtCmVuZG9iagoxMiAwIG9iagoyNDUKZW5kb2JqCjEwIDAgb2JqClsgXQplbmRvYmoKMTggMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMzIgPj4Kc3RyZWFtCnicPZBLcgQhDEP3nEJHAH/hPJ1KzaLn/tvI7plskKrA8hNxHBNn84gIpBz8rGFmUBO8h4VD1WA7oOvAZ0BO4BoudClwo9qEc3ydw5sKmriHx2y1SKyd5Uwh6jAmSWzoScg2zmhy45zcqlTeTGu9xuKbcne7ymvalsK9h8r6OONUOasqa5E2EZlFaxvBRh7ssM+jq2jLWSrcN4xNXROVw5vF7lndyeKK769c49Uswcz3w7e/HB9X3egqx9jKhNlSk+bSOfWvltH6cLSLhXrhR3smSHB1qyBVpdbO2lN6/VPcJPr9A/TBVx0KZW5kc3RyZWFtCmVuZG9iagoxOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDMwNyA+PgpzdHJlYW0KeJw9kktuAzEMQ/c+hS4QwPrZnvOkKLqY3n/bJyXpihzZFkVqlrpMWVMekDSThH/p8HCxnfI7bM9mZuBaopeJ5ZTn0BVi7qJ82cxGXVknxeqEZjq36FE5Fwc2Taqfqyyl3S54Dtcmnlv2ET+80KAe1DUuCTd0V6NlKTRjqvt/0nv8jDLgakxdbFKrex88XkRV6OgHR4kiY5cX5+NBCelKwmhaiJV3RQNB7vK0ynsJ7tveasiyB6mYzjspZrDrdFIubheHIR7I8qjw5aPYa0LP+LArJfRI2IYzcifuaMbm1MjikP7ejQRLj65oIfPgr27WLmC8UzpFYmROcqxpi1VO91AU07nDvQwQ9WxFQylzkdXqX8POC2uWbBZ4SvoFHqPdJksOVtnbqE7vrTzZ0PcfWtd0HwplbmRzdHJlYW0KZW5kb2JqCjIwIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjMyID4+CnN0cmVhbQp4nDVRSW7EMAy7+xX8wADW7rwnxaCH9v/XUsoUCEAltrglYmMjAi8x+DmI3PiSNaMmfmdyV/wsT4VHwq3gSRSBl+FedoLLG8ZlPw4zH7yXVs6kxpMMyEU2PTwRMtglEDowuwZ12Gbaib4h4bMjUs1GltPXEvTSKgTKU7bf6YISbav6c/usC2372hNOdnvqSeUTiOeWrMBl4xWTxVgGPVG5SzF9kOpsoSehvCifg2w+aohElyhn4InBwSjQDuy57WfiVSFoXd2nbWOoRkrH078NTU2SCPlECWe2NO4W/n/Pvb7X+w9OIVQRCmVuZHN0cmVhbQplbmRvYmoKMjEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMzEgPj4Kc3RyZWFtCnicNU85kgQhDMt5hT4wVRjbQL+np7Y22Pl/upKZTpDwIcnTEx2ZeJkjI7Bmx9taZCBm4FNMxb/2tA8TqvfgHiKUiwthhpFw1qzjbp6OF/92lc9YB+82+IpZXhDYwkzWVxZnLtsFY2mcxDnJboxdE7GNda2nU1hHMKEMhHS2w5Qgc1Sk9MmOMuboOJEnnovv9tssdjl+DusLNo0hFef4KnqCNoOi7HnvAhpyQf9d3fgeRbvoJSAbCRbWUWLunOWEX712dB61KBJzQppBLhMhzekqphCaUKyzo6BSUXCpPqforJ9/5V9cLQplbmRzdHJlYW0KZW5kb2JqCjIyIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjQ5ID4+CnN0cmVhbQp4nD1QO45EIQzrOYUv8CTyI3AeRqstZu/frgOaKVBMfrYzJNARgUcMMZSv4yWtoK6Bv4tC8W7i64PCIKtDUiDOeg+IdOymNpETOh2cMz9hN2OOwEUxBpzpdKY9ByY5+8IKhHMbZexWSCeJqiKO6jOOKZ4qe594FiztyDZbJ5I95CDhUlKJyaWflMo/bcqUCjpm0QQsErngZBNNOMu7SVKMGZQy6h6mdiJ9rDzIozroZE3OrCOZ2dNP25n4HHC3X9pkTpXHdB7M+Jy0zoM5Fbr344k2B02N2ujs9xNpKi9Sux1anX51EpXdGOcYEpdnfxnfZP/5B/6HWiIKZW5kc3RyZWFtCmVuZG9iagoyMyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEzNiA+PgpzdHJlYW0KeJxNj0EOAzEIA+95hZ9AIEB4z1ZVD9v/X0vYdtMLHsmAbFEGgSWHeIcb4dHbD99FNhVn45xfUiliIZhPcJ8wUxyNKXfyY4+AcZRqLKdoeF5Lzk3DFy13Ey2lrZeTGW+47pf3R5VtkQ1Fzy0LQtdskvkygQd8GJhHdeNppcfd9myv9vwAzmw0SQplbmRzdHJlYW0KZW5kb2JqCjI0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggOTQgPj4Kc3RyZWFtCnicRY3BEcAgCAT/VEEJCgraTyaTh/b/jRAyfGDnDu6EBQu2eUYfBZUmXhVYB0pj3FCPQL3hci3J3AUPcCd/2tBUnJbTd2mRSVUp3KQSef8OZyaQqHnRY533C2P7IzwKZW5kc3RyZWFtCmVuZG9iagoyNSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDQ3ID4+CnN0cmVhbQp4nDMyt1AwULA0ARKGFiYK5mYGCimGXJYQVi4XTCwHzALRlnAKIp7BlQYAuWcNJwplbmRzdHJlYW0KZW5kb2JqCjI2IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjU4ID4+CnN0cmVhbQp4nEWRS3IEIAhE956CI4D85DyTSmUxuf82Dc5kNnaXqP2ESiOmEiznFHkwfcnyzWS26Xc5VjsbBRRFKJjJVeixAqs7U8SZa4lq62Nl5LjTOwbFG85dOalkcaOMdVR1KnBMz5X1Ud35dlmUfUcOZQrYrHMcbODKbcMYJ0abre4O94kgTydTR8XtINnwByeNfZWrK3CdbPbRSzAOBP1CE5jki0DrDIHGzVP05BLs4+N254Fgb3kRSNkQyJEhGB2Cdp1c/+LW+b3/cYY7z7UZrhzv4neY1nbHX2KSFXMBi9wpqOdrLlrXGTrekzPH5Kb7hs65YJe7g0zv+T/Wz/r+Ax4pZvoKZW5kc3RyZWFtCmVuZG9iagoyNyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE2MyA+PgpzdHJlYW0KeJxFkDsSAyEMQ3tOoSP4IwM+z2YyKTb3b2PYbFLA01ggg7sTgtTagonogoe2Jd0F760EZ2P86TZuNRLkBHWAVqTjaJRSfbnFaZV08Wg2cysLrRMdZg56lKMZoBA6Fd7touRypu7O+UNw9V/1v2LdOZuJgcnKHQjN6lPc+TY7orq6yf6kx9ys134r7FVhaVlLywm3nbtmQAncUznaqz0/Hwo69gplbmRzdHJlYW0KZW5kb2JqCjI4IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjE4ID4+CnN0cmVhbQp4nD1QuY0EMQzLXYUaWMB67alnFotLpv/0SPn2ItEWRVIqNZmSKS91lCVZU946fJbEDnmG5W5kNiUqRS+TsCX30ArxfYnmFPfd1ZazQzSXaDl+CzMqqhsd00s2mnAqE7qg3MMz+g1tdANWhx6xWyDQpGDXtiByxw8YDMGZE4siDEpNBv+uco+fXosbPsPxQxSRkg7mNf9Y/fJzDa9TjyeRbm++4l6cqQ4DERySmrwjXVixLhIRaTVBTc/AWi2Au7de/hu0I7oMQPaJxHGaUo6hv2twpc8v5SdT2AplbmRzdHJlYW0KZW5kb2JqCjI5IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTYwID4+CnN0cmVhbQp4nEWQORIDMQgEc72CJ0hcgvesy7XB+v+pB9ZHoukCNBy6Fk3KehRoPumxRqG60GvoLEqSRMEWkh1Qp2OIOyhITEhjkki2HoMjmlizXZiZVCqzUuG0acXCv9la1chEjXCN/InpBlT8T+pclPBNg6+SMfoYVLw7g4xJ+F5F3Fox7f5EMLEZ9glvRSYFhImxqdm+z2CGzPcK1zjH8w1MgjfrCmVuZHN0cmVhbQplbmRvYmoKMzAgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMzQgPj4Kc3RyZWFtCnicLVJLcsUgDNtzCl2gM/gH5DzpdLp4vf+2kpNFRg5g9DHlholKfFkgt6PWxLeNzECF4a+rzIXPSNvIOojLkIu4ki2Fe0Qs5DHEPMSC76vxHh75rMzJswfGL9l3Dyv21IRlIePFGdphFcdhFeRYsHUhqnt4U6TDqSTY44v/PsVzLQQtfEbQgF/kn6+O4PmSFmn3mG3TrnqwTDuqpLAcbE9zXiZfWme5Oh7PB8n2rtgRUrsCFIW5M85z4SjTVka0FnY2SGpcbG+O/VhK0IVuXEaKI5CfqSI8oKTJzCYK4o+cHnIqA2Hqmq50chtVcaeezDWbi7czSWbrvkixmcJ5XTiz/gxTZrV5J89yotSpCO+xZ0vQ0Dmunr2WWWh0mxO8pITPxk5PTr5XM+shORUJqWJaV8FpFJliCdsSX1NRU5p6Gf778u7xO37+ASxzfHMKZW5kc3RyZWFtCmVuZG9iagozMSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDMyMCA+PgpzdHJlYW0KeJw1UktuBTEI288puECl8E/O86qqi777b2sTvRVMMGDjKS9Z0ku+1CXbpcPkWx/3JbFC3o/tmsxSxfcWsxTPLa9HzxG3LQoEURM9WJkvFSLUz/ToOqhwSp+BVwi3FBu8g0kAg2r4Bx6lMyBQ50DGu2IyUgOCJNhzaXEIiXImiX+kvJ7fJ62kofQ9WZnL35NLpdAdTU7oAcXKxUmgXUn5oJmYSkSSl+t9sUL0hsCSPD5HMcmA7DaJbaIFJucepSXMxBQ6sMcCvGaa1VXoYMIehymMVwuzqB5s8lsTlaQdreMZ2TDeyzBTYqHhsAXU5mJlgu7l4zWvwojtUZNdw3Duls13CNFo/hsWyuBjFZKAR6exEg1pOMCIwJ5eOMVe8xM5DsCIY52aLAxjaCaneo6JwNCes6VhxsceWvXzD1TpfIcKZW5kc3RyZWFtCmVuZG9iagozMiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE4ID4+CnN0cmVhbQp4nDM2tFAwgMMUQ640AB3mA1IKZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEzMyA+PgpzdHJlYW0KeJxFj0sOBCEIRPecoo7Axx/ncTLphXP/7YCdbhNjPYVUgbmCoT0uawOdFR8hGbbxt6mWjkVZPlR6UlYPyeCHrMbLIdygLPCCSSqGIVCLmBqRLWVut4DbNg2yspVTpY6wi6Mwj/a0bBUeX6JbInWSP4PEKi/c47odyKXWu96ii75/pAExCQplbmRzdHJlYW0KZW5kb2JqCjM0IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTc0ID4+CnN0cmVhbQp4nE2QSQ5DIQxD95zCF6iEM8DnPL+qumjvv61DB3WB/OQgcDw80HEkLnRk6IyOK5sc48CzIGPi0Tj/ybg+xDFB3aItWJd2x9nMEnPCMjECtkbJ2TyiwA/HXAgSZJcfvsAgIl2P+VbzWZP0z7c73Y+6tGZfPaLAiewIxbABV4D9useBS8L5XtPklyolYxOH8oHqIlI2O6EQtVTscqqKs92bK3AV9PzRQ+7tBbUjPN8KZW5kc3RyZWFtCmVuZG9iagoxNiAwIG9iago8PCAvQmFzZUZvbnQgL0RlamFWdVNhbnMgL0NoYXJQcm9jcyAxNyAwIFIKL0VuY29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA1MiAvZm91ciA1NCAvc2l4IDgyIC9SIDk3IC9hIDk5IC9jIC9kIC9lIC9mIDEwOCAvbCAvbSAvbiAvbwoxMTQgL3IgL3MgL3QgL3UgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE1IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE0IDAgUiA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvUiAxOCAwIFIgL2EgMTkgMCBSIC9jIDIwIDAgUiAvZCAyMSAwIFIgL2UgMjIgMCBSIC9mIDIzIDAgUgovZm91ciAyNCAwIFIgL2wgMjUgMCBSIC9tIDI2IDAgUiAvbiAyNyAwIFIgL28gMjggMCBSIC9yIDI5IDAgUiAvcyAzMCAwIFIKL3NpeCAzMSAwIFIgL3NwYWNlIDMyIDAgUiAvdCAzMyAwIFIgL3UgMzQgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9F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wz8lYP3szZ+f/MM8/P9gfP6Z47/32T6OD4D/p0fon5v/03v+2YE5Qft/4fDPT/RBBNz/6f9ji+j/xuOzFfTn8efx5/Hn8efx5/Hn8efx5/Hn8efx5/Hn8efx5/Hn8efx5/Hff/xfTVGNHAplbmRzdHJlYW0KZW5kb2JqCjM1IDAgb2JqCjgzNDU0CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozNiAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTEzKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDM3CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDkxMDQ2IDAwMDAwIG4gCjAwMDAwMDcxMzYgMDAwMDAgbiAKMDAwMDAwNzE2OCAwMDAwMCBuIAowMDAwMDA3MjY3IDAwMDAwIG4gCjAwMDAwMDcyODggMDAwMDAgbiAKMDAwMDAwNzMwOSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAwMDczOSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3MTkgMDAwMDAgbiAKMDAwMDAwNzM0MSAwMDAwMCBuIAowMDAwMDA1ODgyIDAwMDAwIG4gCjAwMDAwMDU2ODIgMDAwMDAgbiAKMDAwMDAwNTMwMyAwMDAwMCBuIAowMDAwMDA2OTM1IDAwMDAwIG4gCjAwMDAwMDA3NTkgMDAwMDAgbiAKMDAwMDAwMTA2NCAwMDAwMCBuIAowMDAwMDAxNDQ0IDAwMDAwIG4gCjAwMDAwMDE3NDkgMDAwMDAgbiAKMDAwMDAwMjA1MyAwMDAwMCBuIAowMDAwMDAyMzc1IDAwMDAwIG4gCjAwMDAwMDI1ODQgMDAwMDAgbiAKMDAwMDAwMjc1MCAwMDAwMCBuIAowMDAwMDAyODY5IDAwMDAwIG4gCjAwMDAwMDMyMDAgMDAwMDAgbiAKMDAwMDAwMzQzNiAwMDAwMCBuIAowMDAwMDAzNzI3IDAwMDAwIG4gCjAwMDAwMDM5NjAgMDAwMDAgbiAKMDAwMDAwNDM2NyAwMDAwMCBuIAowMDAwMDA0NzYwIDAwMDAwIG4gCjAwMDAwMDQ4NTAgMDAwMDAgbiAKMDAwMDAwNTA1NiAwMDAwMCBuIAowMDAwMDkxMDI0IDAwMDAwIG4gCjAwMDAwOTExMDYgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAzNiAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMzcgPj4Kc3RhcnR4cmVmCjkxMjYzCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:13.259063\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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IDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYx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T+H3//8fn4wMgP9sND45z4fxwQcz/f/y42/N/+kzf+Ov7zfu3/7kpyc6K5D3f/q/YnzO1/ibT/q733p3/NdP8p8dvzNifz/+fvz9+Pvx9+Pvx9+Pvx9/P/5+/P34+/H34+/H34+/H38//n78f3X8vwFGZnGmCmVuZHN0cmVhbQplbmRvYmoKMzYgMCBvYmoKODU3NTYKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjM3IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQxMTMrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgMzgKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwOTM3NTcgMDAwMDAgbiAKMDAwMDAwNzU0NSAwMDAwMCBuIAowMDAwMDA3NTc3IDAwMDAwIG4gCjAwMDAwMDc2NzYgMDAwMDAgbiAKMDAwMDAwNzY5NyAwMDAwMCBuIAowMDAwMDA3NzE4IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDAwNzQxIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDcyMSAwMDAwMCBuIAowMDAwMDA3NzUwIDAwMDAwIG4gCjAwMDAwMDYyNzggMDAwMDAgbiAKMDAwMDAwNjA3OCAwMDAwMCBuIAowMDAwMDA1NjkzIDAwMDAwIG4gCjAwMDAwMDczMzEgMDAwMDAgbiAKMDAwMDAwMDc2MSAwMDAwMCBuIAowMDAwMDAxMDY2IDAwMDAwIG4gCjAwMDAwMDE0NDYgMDAwMDAgbiAKMDAwMDAwMTc1MSAwMDAwMCBuIAowMDAwMDAyMDU1IDAwMDAwIG4gCjAwMDAwMDIzNzcgMDAwMDAgbiAKMDAwMDAwMjg0NSAwMDAwMCBuIAowMDAwMDAzMDU0IDAwMDAwIG4gCjAwMDAwMDMxNzMgMDAwMDAgbiAKMDAwMDAwMzUwNCAwMDAwMCBuIAowMDAwMDAzNzQwIDAwMDAwIG4gCjAwMDAwMDQwMzEgMDAwMDAgbiAKMDAwMDAwNDE4NiAwMDAwMCBuIAowMDAwMDA0NDE5IDAwMDAwIG4gCjAwMDAwMDQ4MjYgMDAwMDAgbiAKMDAwMDAwNDkxNiAwMDAwMCBuIAowMDAwMDA1MTIyIDAwMDAwIG4gCjAwMDAwMDU0NDYgMDAwMDAgbiAKMDAwMDA5MzczNSAwMDAwMCBuIAowMDAwMDkzODE3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDM4ID4+CnN0YXJ0eHJlZgo5Mzk3NAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:13.520083\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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MAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTQgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL1IgMTggMCBSIC9hIDE5IDAgUiAvYyAyMCAwIFIgL2QgMjEgMCBSIC9lIDIyIDAgUiAvZiAyMyAwIFIKL2ZpdmUgMjQgMCBSIC9sIDI1IDAgUiAvbSAyNiAwIFIgL24gMjcgMCBSIC9vIDI4IDAgUiAvciAyOSAwIFIgL3MgMzAgMCBSCi9zaXggMzEgMCBSIC9zcGFjZSAzMiAwIFIgL3QgMzMgMCBSIC90d28gMzQgMCBSIC91IDM1IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgL0RldmljZVJHQgovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAzIC9Db2x1bW5zIDM5MSAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgMTk5IC9MZW5ndGggMzYgMCBSIC9TdWJ0eXBlIC9JbWFnZQovVHlwZSAvWE9iamVjdCAvV2lkdGggMzkxID4+CnN0cmVhbQp4nOz9SZMkSbImiPEmIqq2uEdEblWv3uuenu4+4gYQTjjgggPwo3EG4TgXEIEwg2nq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tbmQKKkHJwdFl00NaocLYdKQL2c+ZuPOyz4SSBp2LqJaVoe7kEdpCXaLdgjggHTkzx+DzAclMzMmkp3e+pk+MWgSMWKeipXcdNW2/I8TfhzOU6xj29en9fP2IeQj/utPy34rOLB4C6pIGR6XPmibyyZWYr7OQxVHzcQ4Dzb/El1mZuASRGq1Bdi+0V5zHYrAUeLj6fzj52hruNArZl7L3455u7nbt0QrLT68/PKZWPxz/LmDBf7cwK06L3Wba435x3Gv/c0zd2tvTPtPsBAYqaTLt/4fCbqLGwy2rP19Cezo6Ojo6Ojo6Ojo6Ojo6Ojo6PgEvwD+mw8lCmVuZHN0cmVhbQplbmRvYmoKMzYgMCBvYmoKODkzOTgKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjM3IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQxMTMrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgMzgKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwOTc0ODkgMDAwMDAgbiAKMDAwMDAwNzYzNSAwMDAwMCBuIAowMDAwMDA3NjY3IDAwMDAwIG4gCjAwMDAwMDc3NjYgMDAwMDAgbiAKMDAwMDAwNzc4NyAwMDAwMCBuIAowMDAwMDA3ODA4IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDAwNzQxIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDcyMSAwMDAwMCBuIAowMDAwMDA3ODQwIDAwMDAwIG4gCjAwMDAwMDYzNjkgMDAwMDAgbiAKMDAwMDAwNjE2OSAwMDAwMCBuIAowMDAwMDA1Nzg1IDAwMDAwIG4gCjAwMDAwMDc0MjIgMDAwMDAgbiAKMDAwMDAwMDc2MSAwMDAwMCBuIAowMDAwMDAxMDY2IDAwMDAwIG4gCjAwMDAwMDE0NDYgMDAwMDAgbiAKMDAwMDAwMTc1MSAwMDAwMCBuIAowMDAwMDAyMDU1IDAwMDAwIG4gCjAwMDAwMDIzNzcgMDAwMDAgbiAKMDAwMDAwMjU4NiAwMDAwMCBuIAowMDAwMDAyOTA4IDAwMDAwIG4gCjAwMDAwMDMwMjcgMDAwMDAgbiAKMDAwMDAwMzM1OCAwMDAwMCBuIAowMDAwMDAzNTk0IDAwMDAwIG4gCjAwMDAwMDM4ODUgMDAwMDAgbiAKMDAwMDAwNDExOCAwMDAwMCBuIAowMDAwMDA0NTI1IDAwMDAwIG4gCjAwMDAwMDQ5MTggMDAwMDAgbiAKMDAwMDAwNTAwOCAwMDAwMCBuIAowMDAwMDA1MjE0IDAwMDAwIG4gCjAwMDAwMDU1MzggMDAwMDAgbiAKMDAwMDA5NzQ2NyAwMDAwMCBuIAowMDAwMDk3NTQ5IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDM4ID4+CnN0YXJ0eHJlZgo5NzcwNgolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:13.776275\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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SXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTQgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL1IgMTggMCBSIC9hIDE5IDAgUiAvYyAyMCAwIFIgL2QgMjEgMCBSIC9lIDIyIDAgUiAvZWlnaHQgMjMgMCBSCi9mIDI0IDAgUiAvZm91ciAyNSAwIFIgL2wgMjYgMCBSIC9tIDI3IDAgUiAvbiAyOCAwIFIgL28gMjkgMCBSIC9yIDMwIDAgUgovcyAzMSAwIFIgL3NwYWNlIDMyIDAgUiAvdCAzMyAwIFIgL3RocmVlIDM0IDAgUiAvdSAzNSAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE2IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSID4+CmVuZG9iagoxMyAwIG9iago8PCAvQml0c1BlckNvbXBvbmVudCA4IC9Db2xvclNwYWNlIC9EZXZpY2VSR0IKL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMyAvQ29sdW1ucyAzOTEgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE5OSAvTGVuZ3RoIDM2IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDM5MSA+PgpzdHJlYW0KeJzs/UtzJEmyJYzpy8zcIwJAZlZV933PcD5+S+5I4YoL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CmVuZG9iagozNiAwIG9iago5MDkyMwplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMzcgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDExNCswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAzOAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDA5OTAzMCAwMDAwMCBuIAowMDAwMDA3NjUxIDAwMDAwIG4gCjAwMDAwMDc2ODMgMDAwMDAgbiAKMDAwMDAwNzc4MiAwMDAwMCBuIAowMDAwMDA3ODAzIDAwMDAwIG4gCjAwMDAwMDc4MjQgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk5IDAwMDAwIG4gCjAwMDAwMDA3NDEgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNzIxIDAwMDAwIG4gCjAwMDAwMDc4NTYgMDAwMDAgbiAKMDAwMDAwNjM4MSAwMDAwMCBuIAowMDAwMDA2MTgxIDAwMDAwIG4gCjAwMDAwMDU3OTMgMDAwMDAgbiAKMDAwMDAwNzQzNCAwMDAwMCBuIAowMDAwMDAwNzYxIDAwMDAwIG4gCjAwMDAwMDEwNjYgMDAwMDAgbiAKMDAwMDAwMTQ0NiAwMDAwMCBuIAowMDAwMDAxNzUxIDAwMDAwIG4gCjAwMDAwMDIwNTUgMDAwMDAgbiAKMDAwMDAwMjM3NyAwMDAwMCBuIAowMDAwMDAyODQ1IDAwMDAwIG4gCjAwMDAwMDMwNTQgMDAwMDAgbiAKMDAwMDAwMzIyMCAwMDAwMCBuIAowMDAwMDAzMzM5IDAwMDAwIG4gCjAwMDAwMDM2NzAgMDAwMDAgbiAKMDAwMDAwMzkwNiAwMDAwMCBuIAowMDAwMDA0MTk3IDAwMDAwIG4gCjAwMDAwMDQ0MzAgMDAwMDAgbiAKMDAwMDAwNDgzNyAwMDAwMCBuIAowMDAwMDA0OTI3IDAwMDAwIG4gCjAwMDAwMDUxMzMgMDAwMDAgbiAKMDAwMDAwNTU0NiAwMDAwMCBuIAowMDAwMDk5MDA4IDAwMDAwIG4gCjAwMDAwOTkwOTAgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAzNyAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMzggPj4Kc3RhcnR4cmVmCjk5MjQ3CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:14.034943\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["input_imgs = get_train_images(4)\n", "for latent_dim in model_dict:\n", " visualize_reconstructions(model_dict[latent_dim][\"model\"], input_imgs)"]}, {"cell_type": "markdown", "id": "ef21b4fe", "metadata": {"papermill": {"duration": 0.057981, "end_time": "2021-09-16T12:41:14.313924", "exception": false, "start_time": "2021-09-16T12:41:14.255943", "status": "completed"}, "tags": []}, "source": ["Clearly, the smallest latent dimensionality can only save information about the rough shape and color of the object,\n", "but the reconstructed image is extremely blurry and it is hard to recognize the original object in the reconstruction.\n", "With 128 features, we can recognize some shapes again although the picture remains blurry.\n", "The models with the highest two dimensionalities reconstruct the images quite well.\n", "The difference between 256 and 384 is marginal at first sight but can be noticed when comparing, for instance,\n", "the backgrounds of the first image (the 384 features model more of the pattern than 256)."]}, {"cell_type": "markdown", "id": "f812acd6", "metadata": {"papermill": {"duration": 0.057213, "end_time": "2021-09-16T12:41:14.428039", "exception": false, "start_time": "2021-09-16T12:41:14.370826", "status": "completed"}, "tags": []}, "source": ["### Out-of-distribution images\n", "\n", "Before continuing with the applications of autoencoder, we can actually explore some limitations of our autoencoder.\n", "For example, what happens if we try to reconstruct an image that is clearly out of the distribution of our dataset?\n", "We expect the decoder to have learned some common patterns in the dataset,\n", "and thus might in particular fail to reconstruct images that do not follow these patterns.\n", "\n", "The first experiment we can try is to reconstruct noise.\n", "We, therefore, create two images whose pixels are randomly sampled from a uniform distribution over pixel values,\n", "and visualize the reconstruction of the model (feel free to test different latent dimensionalities):"]}, {"cell_type": "code", "execution_count": 15, "id": "7df92e6f", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:14.545606Z", "iopub.status.busy": "2021-09-16T12:41:14.545125Z", "iopub.status.idle": "2021-09-16T12:41:14.748703Z", "shell.execute_reply": "2021-09-16T12:41:14.749086Z"}, "papermill": {"duration": 0.264385, "end_time": "2021-09-16T12:41:14.749227", "exception": false, "start_time": "2021-09-16T12:41:14.484842", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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Ci9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNCAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxNyAwIG9iago8PCAvUiAxOCAwIFIgL2EgMTkgMCBSIC9jIDIwIDAgUiAvZCAyMSAwIFIgL2UgMjIgMCBSIC9mIDIzIDAgUgovZml2ZSAyNCAwIFIgL2wgMjUgMCBSIC9tIDI2IDAgUiAvbiAyNyAwIFIgL28gMjggMCBSIC9yIDI5IDAgUiAvcyAzMCAwIFIKL3NpeCAzMSAwIFIgL3NwYWNlIDMyIDAgUiAvdCAzMyAwIFIgL3R3byAzNCAwIFIgL3UgMzUgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0kxIDEzIDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOCAvQ29sb3JTcGFjZSAvRGV2aWNlUkdCCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDMgL0NvbHVtbnMgMzkxIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAxMDIgL0xlbmd0aCAzNiAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAzOTEgPj4Kc3RyZWFtCnic7P1Xk2XZkh6IuS+x9ZGhVWpRWt66XVf37W50A9M9HMB6bDjGoRnHhmMk/w8fSJrR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Cjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozNyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTE0KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDM4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDc2NDkzIDAwMDAwIG4gCjAwMDAwMDc2MzQgMDAwMDAgbiAKMDAwMDAwNzY2NiAwMDAwMCBuIAowMDAwMDA3NzY1IDAwMDAwIG4gCjAwMDAwMDc3ODYgMDAwMDAgbiAKMDAwMDAwNzgwNyAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAwMDc0MCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3MjAgMDAwMDAgbiAKMDAwMDAwNzgzOSAwMDAwMCBuIAowMDAwMDA2MzY4IDAwMDAwIG4gCjAwMDAwMDYxNjggMDAwMDAgbiAKMDAwMDAwNTc4NCAwMDAwMCBuIAowMDAwMDA3NDIxIDAwMDAwIG4gCjAwMDAwMDA3NjAgMDAwMDAgbiAKMDAwMDAwMTA2NSAwMDAwMCBuIAowMDAwMDAxNDQ1IDAwMDAwIG4gCjAwMDAwMDE3NTAgMDAwMDAgbiAKMDAwMDAwMjA1NCAwMDAwMCBuIAowMDAwMDAyMzc2IDAwMDAwIG4gCjAwMDAwMDI1ODUgMDAwMDAgbiAKMDAwMDAwMjkwNyAwMDAwMCBuIAowMDAwMDAzMDI2IDAwMDAwIG4gCjAwMDAwMDMzNTcgMDAwMDAgbiAKMDAwMDAwMzU5MyAwMDAwMCBuIAowMDAwMDAzODg0IDAwMDAwIG4gCjAwMDAwMDQxMTcgMDAwMDAgbiAKMDAwMDAwNDUyNCAwMDAwMCBuIAowMDAwMDA0OTE3IDAwMDAwIG4gCjAwMDAwMDUwMDcgMDAwMDAgbiAKMDAwMDAwNTIxMyAwMDAwMCBuIAowMDAwMDA1NTM3IDAwMDAwIG4gCjAwMDAwNzY0NzEgMDAwMDAgbiAKMDAwMDA3NjU1MyAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOCA+PgpzdGFydHhyZWYKNzY3MTAKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:14.633242\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["rand_imgs = torch.rand(2, 3, 32, 32) * 2 - 1\n", "visualize_reconstructions(model_dict[256][\"model\"], rand_imgs)"]}, {"cell_type": "markdown", "id": "267b07f5", "metadata": {"papermill": {"duration": 0.06058, "end_time": "2021-09-16T12:41:14.872630", "exception": false, "start_time": "2021-09-16T12:41:14.812050", "status": "completed"}, "tags": []}, "source": ["The reconstruction of the noise is quite poor, and seems to introduce some rough patterns.\n", "As the input does not follow the patterns of the CIFAR dataset, the model has issues reconstructing it accurately.\n", "\n", "We can also check how well the model can reconstruct other manually-coded patterns:"]}, {"cell_type": "code", "execution_count": 16, "id": "21864c63", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:14.998551Z", "iopub.status.busy": "2021-09-16T12:41:14.998040Z", "iopub.status.idle": "2021-09-16T12:41:15.222492Z", "shell.execute_reply": "2021-09-16T12:41:15.222012Z"}, "papermill": {"duration": 0.289797, "end_time": "2021-09-16T12:41:15.222633", "exception": false, "start_time": "2021-09-16T12:41:14.932836", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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MAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTQgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL1IgMTggMCBSIC9hIDE5IDAgUiAvYyAyMCAwIFIgL2QgMjEgMCBSIC9lIDIyIDAgUiAvZiAyMyAwIFIKL2ZpdmUgMjQgMCBSIC9sIDI1IDAgUiAvbSAyNiAwIFIgL24gMjcgMCBSIC9vIDI4IDAgUiAvciAyOSAwIFIgL3MgMzAgMCBSCi9zaXggMzEgMCBSIC9zcGFjZSAzMiAwIFIgL3QgMzMgMCBSIC90d28gMzQgMCBSIC91IDM1IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgL0RldmljZVJHQgovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAzIC9Db2x1bW5zIDM5MSAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgMTk5IC9MZW5ndGggMzYgMCBSIC9TdWJ0eXBlIC9JbWFnZQovVHlwZSAvWE9iamVjdCAvV2lkdGggMzkxID4+CnN0cmVhbQp4nO29265sSXYdNsaMlftUV1d101ALapLiiw3TEPRgERZAQPAPCDJgvRnQD/iX/BF+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dHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDM4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDM1ODQ5IDAwMDAwIG4gCjAwMDAwMDc2MzUgMDAwMDAgbiAKMDAwMDAwNzY2NyAwMDAwMCBuIAowMDAwMDA3NzY2IDAwMDAwIG4gCjAwMDAwMDc3ODcgMDAwMDAgbiAKMDAwMDAwNzgwOCAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTkgMDAwMDAgbiAKMDAwMDAwMDc0MSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3MjEgMDAwMDAgbiAKMDAwMDAwNzg0MCAwMDAwMCBuIAowMDAwMDA2MzY5IDAwMDAwIG4gCjAwMDAwMDYxNjkgMDAwMDAgbiAKMDAwMDAwNTc4NSAwMDAwMCBuIAowMDAwMDA3NDIyIDAwMDAwIG4gCjAwMDAwMDA3NjEgMDAwMDAgbiAKMDAwMDAwMTA2NiAwMDAwMCBuIAowMDAwMDAxNDQ2IDAwMDAwIG4gCjAwMDAwMDE3NTEgMDAwMDAgbiAKMDAwMDAwMjA1NSAwMDAwMCBuIAowMDAwMDAyMzc3IDAwMDAwIG4gCjAwMDAwMDI1ODYgMDAwMDAgbiAKMDAwMDAwMjkwOCAwMDAwMCBuIAowMDAwMDAzMDI3IDAwMDAwIG4gCjAwMDAwMDMzNTggMDAwMDAgbiAKMDAwMDAwMzU5NCAwMDAwMCBuIAowMDAwMDAzODg1IDAwMDAwIG4gCjAwMDAwMDQxMTggMDAwMDAgbiAKMDAwMDAwNDUyNSAwMDAwMCBuIAowMDAwMDA0OTE4IDAwMDAwIG4gCjAwMDAwMDUwMDggMDAwMDAgbiAKMDAwMDAwNTIxNCAwMDAwMCBuIAowMDAwMDA1NTM4IDAwMDAwIG4gCjAwMDAwMzU4MjcgMDAwMDAgbiAKMDAwMDAzNTkwOSAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOCA+PgpzdGFydHhyZWYKMzYwNjYKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:15.098251\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["plain_imgs = torch.zeros(4, 3, 32, 32)\n", "\n", "# Single color channel\n", "plain_imgs[1, 0] = 1\n", "# Checkboard pattern\n", "plain_imgs[2, :, :16, :16] = 1\n", "plain_imgs[2, :, 16:, 16:] = -1\n", "# Color progression\n", "xx, yy = torch.meshgrid(torch.linspace(-1, 1, 32), torch.linspace(-1, 1, 32))\n", "plain_imgs[3, 0, :, :] = xx\n", "plain_imgs[3, 1, :, :] = yy\n", "\n", "visualize_reconstructions(model_dict[256][\"model\"], plain_imgs)"]}, {"cell_type": "markdown", "id": "76301ef8", "metadata": {"papermill": {"duration": 0.06382, "end_time": "2021-09-16T12:41:15.352722", "exception": false, "start_time": "2021-09-16T12:41:15.288902", "status": "completed"}, "tags": []}, "source": ["The plain, constant images are reconstructed relatively good although the single color channel contains some noticeable noise.\n", "The hard borders of the checkboard pattern are not as sharp as intended, as well as the color progression,\n", "both because such patterns never occur in the real-world pictures of CIFAR.\n", "\n", "In general, autoencoders tend to fail reconstructing high-frequent noise (i.e. sudden, big changes across few pixels)\n", "due to the choice of MSE as loss function (see our previous discussion about loss functions in autoencoders).\n", "Small misalignments in the decoder can lead to huge losses so that the model settles for the expected value/mean in these regions.\n", "For low-frequent noise, a misalignment of a few pixels does not result in a big difference to the original image.\n", "However, the larger the latent dimensionality becomes, the more of this high-frequent noise can be accurately reconstructed."]}, {"cell_type": "markdown", "id": "7be3c419", "metadata": {"papermill": {"duration": 0.064246, "end_time": "2021-09-16T12:41:15.481865", "exception": false, "start_time": "2021-09-16T12:41:15.417619", "status": "completed"}, "tags": []}, "source": ["### Generating new images\n", "\n", "Variational autoencoders are a generative version of the autoencoders because we regularize the latent space to follow a Gaussian distribution.\n", "However, in vanilla autoencoders, we do not have any restrictions on the latent vector.\n", "So what happens if we would actually input a randomly sampled latent vector into the decoder?\n", "Let's find it out below:"]}, {"cell_type": "code", "execution_count": 17, "id": "d7f283ff", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:15.629358Z", "iopub.status.busy": "2021-09-16T12:41:15.628883Z", "iopub.status.idle": "2021-09-16T12:41:15.799212Z", "shell.execute_reply": "2021-09-16T12:41:15.798800Z"}, "papermill": {"duration": 0.241041, "end_time": "2021-09-16T12:41:15.799325", "exception": false, "start_time": "2021-09-16T12:41:15.558284", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torchvision/utils.py:50: UserWarning: range will be deprecated, please use value_range instead.\n", " warnings.warn(warning)\n"]}, {"data": {"application/pdf": "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\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:15.713164\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["model = model_dict[256][\"model\"]\n", "latent_vectors = torch.randn(8, model.hparams.latent_dim, device=model.device)\n", "with torch.no_grad():\n", " imgs = model.decoder(latent_vectors)\n", " imgs = imgs.cpu()\n", "\n", "grid = torchvision.utils.make_grid(imgs, nrow=4, normalize=True, range=(-1, 1), pad_value=0.5)\n", "grid = grid.permute(1, 2, 0)\n", "plt.figure(figsize=(8, 5))\n", "plt.imshow(grid)\n", "plt.axis(\"off\")\n", "plt.show()"]}, {"cell_type": "markdown", "id": "dc366247", "metadata": {"papermill": {"duration": 0.065573, "end_time": "2021-09-16T12:41:15.933017", "exception": false, "start_time": "2021-09-16T12:41:15.867444", "status": "completed"}, "tags": []}, "source": ["As we can see, the generated images more look like art than realistic images.\n", "As the autoencoder was allowed to structure the latent space in whichever way it suits the reconstruction best,\n", "there is no incentive to map every possible latent vector to realistic images.\n", "Furthermore, the distribution in latent space is unknown to us and doesn't necessarily follow a multivariate normal distribution.\n", "Thus, we can conclude that vanilla autoencoders are indeed not generative."]}, {"cell_type": "markdown", "id": "beb39fdc", "metadata": {"papermill": {"duration": 0.064649, "end_time": "2021-09-16T12:41:16.062528", "exception": false, "start_time": "2021-09-16T12:41:15.997879", "status": "completed"}, "tags": []}, "source": ["## Finding visually similar images\n", "\n", "One application of autoencoders is to build an image-based search engine to retrieve visually similar images.\n", "This can be done by representing all images as their latent dimensionality, and find the closest $K$ images in this domain.\n", "The first step to such a search engine is to encode all images into $z$.\n", "In the following, we will use the training set as a search corpus, and the test set as queries to the system.\n", "\n", "(Warning: the following cells can be computationally heavy for a weak CPU-only system.\n", "If you do not have a strong computer and are not on Google Colab,\n", "you might want to skip the execution of the following cells and rely on the results shown in the filled notebook)"]}, {"cell_type": "code", "execution_count": 18, "id": "87bcc28e", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:16.196119Z", "iopub.status.busy": "2021-09-16T12:41:16.195656Z", "iopub.status.idle": "2021-09-16T12:41:16.197888Z", "shell.execute_reply": "2021-09-16T12:41:16.197398Z"}, "papermill": {"duration": 0.07074, "end_time": "2021-09-16T12:41:16.197996", "exception": false, "start_time": "2021-09-16T12:41:16.127256", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# We use the following model throughout this section.\n", "# If you want to try a different latent dimensionality, change it here!\n", "model = model_dict[128][\"model\"]"]}, {"cell_type": "code", "execution_count": 19, "id": "3abba35a", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:16.349216Z", "iopub.status.busy": "2021-09-16T12:41:16.336154Z", "iopub.status.idle": "2021-09-16T12:41:29.806879Z", "shell.execute_reply": "2021-09-16T12:41:29.806369Z"}, "papermill": {"duration": 13.543186, "end_time": "2021-09-16T12:41:29.807006", "exception": false, "start_time": "2021-09-16T12:41:16.263820", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "fc07ca3d23014b6f9d5fb7b961f16005", "version_major": 2, "version_minor": 0}, "text/plain": ["Encoding images: 0%| | 0/175 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:30.318327\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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IDAgb2JqCjUxMTI2CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagoxNSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTMwKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDE2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDUyMjIwIDAwMDAwIG4gCjAwMDAwMDA2NTAgMDAwMDAgbiAKMDAwMDAwMDY3MSAwMDAwMCBuIAowMDAwMDAwNzcwIDAwMDAwIG4gCjAwMDAwMDA3OTEgMDAwMDAgbiAKMDAwMDAwMDgxMiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTggMDAwMDAgbiAKMDAwMDAwMDYzMCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA2MTAgMDAwMDAgbiAKMDAwMDAwMDg0NCAwMDAwMCBuIAowMDAwMDUyMTk4IDAwMDAwIG4gCjAwMDAwNTIyODAgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAxNSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMTYgPj4Kc3RhcnR4cmVmCjUyNDM3CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:30.522324\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDY4NCA5Mi42NjQ5MzUwNjQ5IF0gL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDggMCBSCi9UeXBlIC9QYWdlID4+CmVuZG9iago5IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTIgMCBSID4+CnN0cmVhbQp4nFWNzQrCMBCE7/sU8wT5a900x0oheKwXHyDEn2AVW7Cv7zaC4GFm9oNlxqKQ7i0uC8RgUESr3HFjMkITcddK3msGp5jb0OyEzR9dic70gleuijkohu+Uqw9GHHPGCQ/o3n0Hi2iV6gg95Pct5WPcIy3Swn6bNvDhV5gm6IPF8MRII30AE9onwAplbmRzdHJlYW0KZW5kb2JqCjEyIDAgb2JqCjEzNwplbmRvYmoKMTAgMCBvYmoKWyBdCmVuZG9iagozIDAgb2JqCjw8ID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgL0RldmljZVJHQgovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAzIC9Db2x1bW5zIDY3MCAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgNzkgL0xlbmd0aCAxNCAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCA2NzAgPj4Kc3RyZWFtCnic7P3rsyxbtheG/cYYc2ZW1Vr7dfp0972ExENg2YD8kJARIrAs4AYhhGyHP/gPdNhfJQs5wkgICQsCHMY4LCAURmAJuLfv7dfZj/Woqsycc4zhD2POzKy19+luXiHZcWbvrlOrKitzPsfjN17Ad+279l37rn3Xvmvfte/ad+279l37rn3Xvmvfte/ad+279l37rn3XvmvftX/ujfZ/MAsx978cvr+QXl79hb9/4d1vm//yX//TNnevta5/igjRbnQ37Z93X/7ZN3dT1fVPZt6t3f/PN3e3m9EJ838v1sjN1vfE/OJYOPY76wvv4lJ3v1k7kbjR/380Mze7GR39sx4d/YK/WvPPPiHQFy51367//DdfuO/t6OSfw+j+O2h95OZmux1+O7r/7gfq/oVl/VV6Fb97QTN/6dp99t2XNtB/b5rZzei2nrKkP/on/70/8If/VbjDvZYyz5O7aVURfvX6dUp5GLKwMIGImIiYmMBEhPZKcALc1d1SYpH+FYEIBCIid1+W2cxSSsFumdfvQYADDg3ev595AplZrbURVaJYG1Vzdzd1d2YWlnjSj370o//j/+F///6bbwDknP/Mn/1zf+gP/493U7E70yBvs0G/4ID7P/W6+rf/+aVd+4vaj3/7R//xn/8PHh8eAKSc/43f+NO/+3/w+3ff04t37ZVWZhT/3zYr0S/pQL/PL5kF/6UU0vs1vrv+9qOf/vbv/NX/+C9ez2cA4zj+yT/1Z37P7/2XblgkeQygsdd168QYiQAiYoebKXybXkfMdfxJ3PYfMTMAc1upPFG/E4jgBC+X8+M/+kc2z0mrMP/wf/Q/vP/+910G52zuqqbuS1UzL9XMvaibuzu5AzDAmSBCv/WP/sF/9hf+o+vlAuBwPP6p3/hzv/v3/D7vXfyl83vz8o/V3OmLv9vviS88rf32y33x3b5xAP6bv/kP/q9/+S/M8wTgeLr7t/7M/+bX/oXfE4Nz9x0vJQTNII4ZRicggJPHteZERtyPPITA5EJggjAlpr6pyZ0At/Yzc3MH3EEEZsrCp+OQhE+HkZlMzc0uU5mXOhedihlg0UtCu4e/FNXc/Tf/wd/76//5XyjLDOD+/vW/8xv/61//tX/BEeOibTq/dR39i9+tx3P33a9GEdwbZdpfHovpu0vWty9vGoehffr3/pu/+5f/6n9aagHw5s3b/9W/97/99V//9TgGOWVJElygnRdaicg6sP4ZdZbbp9AdZubuqmbt/DWWABAxrZ1biRKBHB7yhlqptZ7Pz7VqKdXNU0rMPB4O4zCCghsxs6ARGNd4kntjSOZ/5+/8v/6Tv/B/Dn3v3Zs3f/ZP/8bXX30vBHTmRgmCGog0mnD7ykzcOJ8wEcVviRmgtkGZCQTurIs2qtlnhraPf2Wd8jPyFR9SH6vD/a//9b/27/8H//7K3dP6Y2b+g/+zP/a/+LP/O7jDbZ6uz08PZlaWJeX8wx/+2uEwno7HnDMTCRMzCRMTpXglIoLEE62465A5Z5bO/plDIGB3P5+fa62HwyHnzNHa921R1Stg1LfOyodUdZkXACLCzDlnIqq1mpmpuqmI5JSJCMR/+2/9l//Rn/8/BWsXSX/0j/3xf+ff/XO3ez32HXlj6ivbutn+3l/9V1+KLy7P/ta0dWN/8H71G/1Xf/tv/Rd/6T/prD39oX/9X/sj/8t/CzfC0EZo1uFtR67Tkb7VOqX99rZSnV/A3X+pgLKR+C+9rm/+67/9d/7Gf/FXgrXnnP/I//zf+KN/9N982Rs4M+IQ7r+Iw0fMROzuqtU9CHUjM+7tPRGYJYkwsyQBoFZbT6jLj01ydYJPH97/5P/xN/R8HpZ5yOlf/o0//fUf+AOeTp4O1axWK1UvS6nm17lW82lRNVcnNwAKmDByov/yb/7f/+p//heDtY/D+Ef/2J/4V//IH1t79YV536bv5s0vl6JeTljnrLe/63vgy8v6Ymle3NUd8Bu28v/8m3/9r/21/yxY+3g4/mt//N/+g/+Tf917ixuhsXZxIiB1YugE57idu5u5u4GUBUQizISBIYzEyIzENEhoGQyQg9xhpt7IeuMgRBCmw5jfvT6OQ3r3+i4J61JN7ePj5fkyP0/6NFV1L53JObyqriS0T56742/+9b/8N/7Kfxqs/XQ8/Yl/80/94T/4P/VNWIyBfTt374LQrSTd5p92a+Av/nuzJLd/tGVtIuT+GLs3Bu/7DbMTWdqi9PX7y3/tL/2V/9tfDtZ+d3f3p/7kb/yhP/SvBIUeezsej8wsqStk9HKs8WGTuX074EGna9VaOzBA7YixCJpg3TTAmBB3A4KvX+d5/uab96WU62VStXEcU8r396/u7u6IhVhYJKW8DqosRVXdzcyquZofj8e/9Bf/L8Ha7+/u/+0//id+/+/9vcF8RCR4igiLSMrxXogop8TMIolZmIXjoiTMzEmIiFMiIpJEzPEPTE0xpSa/rsS2a7H/2JxkncYQWL0JdXB3N4M7i/yHf/4//AJrD1El5wQ3cjKVIadaqzGEXBjClBKnRELBrcHkzAjmnUJBhxOg7uYmzIk5LiBqujszuUGYvBFkDzmdCNKVd3d3NXdrahm4TQvYzfq+4VXfop2sK8QpYJa45EZ53WvdG2+n3UHxL2nt6841/FM16kTCbz+JJ//jqGB7/abfapOLNrm3IR8OAAwCwDsi3nfbSlh+URf2fP3bNqW32/0ijuNEoUkF1XV4zHkjqPEkf4GPxOlnAHCP9w6HKwBd8cNOiZMkEWEiSeLmgJt5tepdQQnNbj1sLAxqWmLI+bEwtE4U4GaotTodf+3X6vl8/c3fnC/z4zefhrufH773/eHNQYIpJRpdknl0jAnVvFSv6g29oi9BP//46jd+8SzfXtiJOKgTeHp5hQec9jl3fyF4fakXdPvlyytDvQ5RqjOS1p1O+JraRkQMJzjc4A5md3NQJQI1NUnEhZAYIiRMASH2RwXd5NDUzdg81P6gtA5YYCdtCphSlmHMI7gSmaM2imnmtixmXRwMZc7Mq3ngBOtgCcZkHexpu+XbCfeqHXxOZAgA+Q5F+5YZ/wz6c8TTvd+8q77YNvL6M18xs36lr2qLA/CdvYlIEktiEWHilIL3hcx8o5TeYKudIocFjdzjxQFxImIzE6GQ3Aje0N8wt1kXcdBJCbV9ayGrAQ4iSUzOaaCUnVMFw+BmbFAnoMEDtdaG4TiqeVXTb0Wets/b6sSseqM5TfpxC2YVX3n7k2HmTOTm5iDAvOMQ1Og0cT9iBN7r8b9SW2XieLVVMl577g6zFzwk3fyReMiBeBkslSwM08WFPAlyoiFRTiy0bt92zJiRE/G6Hc3JXQhx9hqr7dKDs4uwO/N2tL0JBwQmuLtWg1s8RdrCk68yHpF0UYuItBRzZ8BBwpxTipsm4f3k7c9bCBD4Erv9BY3/CYnwTR9uH3erhf2qLXpu+x9xo1/AZ+oXEch3QiNeHEvasetv7UZf8V9i2GoK7y8eDpH7xtdXYuMrMaIvCi6dBzAzc5xYc1uVcuw4EAkLIaXU+LSqVzd3M4M35YC5i5xdYzC3YhqHkIhgGgwPDitVrwuB7/7Ff7Gcnz/8ox/N5+vHn37DnL83HI9v3xEDIU4yqbkImbkIqdpl1j750Uf9bA43xatP9i9pvyJf7yL+9hD6EnXDt3P3b2PtfTBYWVp7xmdNiITabuv6IXmwXwB9apoCEGwmuDAczk7EYHS7XSIS9iScGIkDx20qQt/+4u4BD5qZqVFsLnLA4AayECIISDmNIBdFEgdpAwvMVM9WtLbhDUlSkqK+VM3pxi+CyRgaQkubmV+Jtd9O5s0l9O3ffbF1vr5KbHRzn05zGo/dlrJzc2qHJy62/QMlSc7B0iXlJMIi3Pm6vyAm/c/Q0Fb1lDr3JDiIGqkOsYmoCV7SFnJTP1Yo24wUrqpaG47CKZOD8yiSjVJxNnNzJbImVdfqcLMVIEExL2rVfokG1bYKOl93izkj7yc02LnDTQnsRmC4Exk5KTHDACYYrZIrrcqMx/tNS3L8Inq6R8v23H1bv01/1y7hbe2GtVMAj23GfVPN4x+MCUKQIPBuMIUzgRgkzVpiAaEAtpsIDuG+iwTr5m1TRtT76B7Y+rLMbpazCDOY4qd7KCNuSp0yMJHFDVei07f7i9FhE9PWoxhT7H57jv4pufi3NdqjBLTpVL+y3t4Z52eXbwSlE+i9dNf/BlGcsbhsk+rpc23uCzdfNYL2pP01wa6/rXu7H3jw8+01tsGqUviLtXMzU7OmQrvThixj4xCdxzigZmTKtbq7Bg5oZu6h+gsYXVxw96qKsM66qzbWvmkfDgBqXs2FwWlI4/H41TtJSc0vz+fT9XpYJoCcGgUgOBPASAQwJSaT9Xi2B93sTAJT42VtkjuE4vuZDvm2aS6/3PaBvtkacY//xGm7tQJtlr9t1+xW6xc/aDW1+vbJ7fe+ekD6esu2Fx0AkWET8i3cGkTQB0x934KaIkFJOAsxwOTuUDP4Ct7FFm1MlNoRC0JkjXGDSimqNpe6lFq1Wcq7V6SDXBDEmUDIiXMWQFVD+NiNzo1gDNtGv8KtXc7dz8WXPv3iB71929z3Q0a+Jx3rMafdDnNagRIAsBWe29lH1gWxjTASpZRyzqGtpySSJOThdbX23B3tTG07zoPC9CeHKk9MIowmTYYLaYNwmcjhK1LmgAWEX+t1mpZlWZaiapDMLCBxYnV4DXTGmMgciFOMhg+FpFHNavO++CWT3qll4/HUzlD7Rw2k8p0Q0JFH+DaTK13dWLc3KedWevft9L3sTdMrXvxJN8RvpRD+mSfYC9ZuDCMycheyxO5sQi5kmTSxZbbMLgwm0qpqC4HZnIkTMwFaC8zIlWDwCnMHe/NLd2NOCJnOw3jZQW4K6u2utSy11uenJzd9dXeSITfS3fD3hvk3WY+cmZS6IGUGuDDcXTWEjK1xiBgbcVmb99N2s/J72reytX8m/H5/H4f17fArUOpdP+nWPkBmFNA0dS1mx+z7m9BV9luJaSUH5L8Akn9xn91QXl7TFb3P7/GCJroDZE3d3varO8xdNxLjQFFdammADQTcVYFGvEFM3lw43eCu1czDpGeq1j3Sm2+HMDObNn5f58lWHx83D0Cp6R+tqdqilinx4U7y+NW/9Pvr5Tw/XKafvJevv0dfvUkpp2EIywG8eXt5ghjMQIRSrGg1g5nbbnRokPVmnECnHH0ZV77e3v8SjPxbFm4jMyFHrdKkrxv8W+5wS2Juv9o9o6/eDTyL8Hpr9LoZ0IPsdiipTXb0QxWuKaUxSTgrmUFCW2MH0SAkTGNOQxY3c6umZlrhROzBXEABigbfDb6ucMANbrVWY3u+XstSrsuylOpgUBhwEgAjZ1giI7KUkjCPY8pDZnitLjcz5YTKKP1ItimhPm94MbFdYPtVQJdfsCLrEQvJmJpYvqo0eEHf0ITnrkX0/7vbBtC3u+1crIkO43g6HYP7dmP0hl1+prW/6GLM+DqQps8nwJpJmohYJKFj8CEcx3szdzOttdbler18+vRpKeX5fAX49OogeYAkI1F117C1NXYbAsE62uh8VQvufjPDRJs/zX4sjalbiNwEYzQ8Izg99wtCtiGELkzU1EQO8eaWaRBgdHtifuk6r0DaHhPCTnla6cTa+bV9QWuHx6j6KzlR15LdCEYgJjg1lZzIGGAYERkMMIKRG9zcCBQsw92ddw9aN0D/j5tbrfVyudZarpeLu58Oo3vy1cltpy120Wm7YYcsNon0Mz3YGztZJ2c3hZ0o3Z6I3fuYyM98Xz5r+w+/dH59W3XcqDFbz79027Wb61g+eyz56irwEhqITbnBE7vLNlbfFZ1vfXp7pZdT83KA3hnv5/fYWNXGyAF484imDj69IE3ubh54EoGIVAFvCsvu4k7VHIDBQsIOLdnMtruvZI7aneOCkP2BYMZtuvqdA8+Fg4glnU7MhMWNFiepamAjM2qMqs1Wl0FJmLQbFN1367/OLW1LtvoHo2kwNwuw6kS/cqQj7d51Ka4ZBJu0v/qld1rh209jnr5Fsbgdwku+vg6tm6cdDgpyQO0L6iGE7fCSuXti5CThimzsGktGoGbjaz42aPpUmGPcrQPARH2VbQMIqM1YrHMpdam1lFpKDb2BuFkr3WBm5M6ANPku/O+b6XUbmyMIoTd+EIvundl98UDFUL5IGr595V7eokmxjdl4P9f9Lr7CP+21wWCrSNHNYF2tANAcXDYzHxECge+Ndm3l64GVUJexX2wAwmdUuCFizOxY9XXa7bsNeXY3U1WttZZSa6lmTuTNE54ZxO60kk7rk9Mt7HAA5kRmYQH4bMIbRLfB3evzw5ru/ctAoOHOgMXHTZDwMDFg3XPkhg3ciY27qfIvlzUsJTuiSp8doc86fkM5OxN5ye/2rN2FLJG5q0PZK0OFVMiETKCCyl7IXDglYmIjNuGwqYuwEuBUjYy9uqtXU2fyBCY3q6oiAg/Nhpg6fOkOQFWL6vn5+bd+6zfLspR5FqbDOOSUA5ojIg43Pe+2EKNVvArB3F29LaJFvMPuuMTa1L00sL5tE9+8Bdry9j3ethz1Zeo/X0XyX0TvsB0dbPfZAUNxAulLP9n/cPWyoY4BEXajc3SzR/t7vz+4vzYqsDGR7sPY+PWOoH/WaBvynrTdKuj76dhphDspswmwhH6WzLqo3aUxd6jCdL9P1bVqjcdRJSaOwcSM0K3r/0rvTHXF2Fu0LhVmFrcIdaPQIz0Af/Vw3wRYGIH3xdQQg8yIjFDVhfn49ffJzF5fsdT0+vVcraIGiwgvTm74iDOQM4mIMOXEtfpS7IVXfxuC07pwtwj5zYr0gJfb5ttl3+6f0wGaiAqzxi/39HRF0fb65kunxm9vRJ8TciSmJI1ya2gO1FQ4TsxEKQkBtaiZgg3kp0N6/eoYW7KqwZegy0QYszTbL6Gir5zWoCKNyxB5j68CwEyJObEIJwaXuZr782W6zvOyLKVWahG8xCSrBsEOZspMLBxBtzBj2owLaPK0MgJV8O4VjSbE3AhVXf8Abk7uP1Hb5GCgI1fdbLGZurCxvJ2f3cZy1tO3P56+JQIhUM5pGDL3CMOu5WI9dN1Pnj7bdV2G30EC3haRWJjYOYKisMq7sFX2dg9MbSnler2eL9fn57OaGUiYUkopD+AE4uqkBkJgtBQGduvKgDt0/dp3LrfRuZXgxMYNNz2Cg8yIya1LLO5McDMWgTuLOIWdiZycOGRwAoV7nREJEyLkywFCozbcZsbRyVaL7GhTFtBFt5NiPZXuXdTxXbcNAVS7twH/Ils7dTjJg400d8Dtn9umuMOa8O3W9L+mEjia1h5Pb9NnZkHRCbsT0L91d3Uv3aZiZUksqg1ZudXiVvlnlUZvbA837Pymfa7Hx7CdwLtd315XKbg9L+jmjo2FDvxSAH/514Zyb3Lbjlj6C2Z5c5ebz7vy3Tr1+XM3DymiF/S4Hc1QGPec2Ds/WE84fTZtt/e5fehubLsLHK0zvm7bvj3pxW8b1LK+99CqAim8eXKzMYVEbaH/gcDsn/VrG4PvGgKpw+5whZL44iJg50y2WQ63uxNJygyYAkkp5ThoZgZmix2+s3UyETi8xNnZuKN/L2Z1x05p9Xv4XAluUuhnH683+hwGfnkDIofbCpHvl2SVYbugtE7u/rrPHrHONm4PYH9gk0Y2StpigwkUETSA87YzE1NO3LlBGFsa1tQ94r0BfSHKm3W21TjQtpir8tz3mKqpW6laq1U1VeMmDFKHBrhpH9imIQAAejmzm/F1h8a0I3/jsdjV0m64/SePtmkExFdFI9CE/tdL/u1wc1g/aTuEC/3ExZ/tf7uOEW4R61Vwitf13wZovzgou65QmxcHOvyOvQtG74tHdgFrnFnNaq09lNHRXPhaX5yIvFO7Dl5vvd+Eim+jaTve0QReNCHNKLz93MiJYWbEzNEtmBO7kVE76mREBFNnbpvNujTX9l5sDdtOV+uA7dE3X3/kuxX0XT/3mnrM1oYo/CKtHUyUunOaMQnBYLBKBoayK3mFgZ0E5K6w4g4zYiSixASCkiu8wtWNHexKMLFa5+tVRMYhiwjcyN1NHU4EFq6ml6Wc5+X5utRSEoGY1KgaqoLYE7k02uPukRPMVROzVa21FsATM8epDivqrWUFbrCKzxvBETtm51G/Q7S6qLOhlkD3buyWIbTNue3U9d5f4KQ7hCXEuRuKQQjIwG8+A7pw7oA3i8mL0dnu6s+Ie6P7O+q0sZTuBrL//Ettz6qB/V7aDvANBLDtxc7c+w+akT1ObJPezJp7smkt+/7knMZxUNWQyr3DUCvBWGe0nbUWTNPsgtQJd63V4c3K15Ajb0E11hXWXWaKPrUO7/GfOaXQwQm4T+6g8ADBig5v4EVXpkgIJM0d1IyS/AI0va0Kw0Hgz9fiS6uzcjGEJEG7Nd6R3EYT+twFEugrsrgqe7sNFNaw+LbdYd0qu7VdO/KSiLpbLVrmZvBwd/eUwoRNSSK4BgSIk3G4o/mYfCB3clNzqKAC7s4AQStgRS3ssMtSIlQaTavvqW32M8NEytWtTFKJikPNL5cyl9rctPuIaP0JAKFtexhiq/Jn00+mbKWJrXt5sE/9dtb7bupT9EVu89mHTVr6gvzfPnJq/Njp9if9xAWfxI1W1zGaTcFC56U3j2AQg1ueiB6DvvOkay7uuyn3voOCrBhkjXOlLTJic01r5jB1c6+lRFBDoweOeV7O12kpRVJmB4kwi5nVUjhz+D0yQgzjNlozMLlDXFZaESO89ZMw18V0JmOA3BjM4RRvDDUmJrdMRG5KzO6VmM0D9hN4IuIQ1KFCTLBI4cIgJhGYgBjKYAKLb1MWIiPH+3Wi0HC7nUyybQcPjtktkBbEEu5uCov35lr2w3uptXM4/HOjSiHfh/YdDikEJQhTs6Y3DmTUBZVN8kF/fKjsWgvcTJWCjPZzzh501apqqXUpVauScGowQ/TcV/K9vsaWcKNg5AHRon/XxZz9IXnpWNc/D+Vtx70/a4TPLCYEdDvN/rJbqfnmUG6/vSUQdPvJ+tMXdv29OPolrX07uhtvXSWPm37uhY39R367nz6bgdtdd8vdu//njrrt16tTmd2tm7i5svbQwboSZrrvBjevWjfqmnfvxgb/7AZCZk5ELcJ0BV7QvdOdIlylK4B7yQBNXen7qQ+YWoRhC+wlAmUBmjcYQD3r3Iv5Iya4N4u7sXGjSLuZ+MJ8x5Ru8uUvaTtpohGQ/Xh2D1onpCFysUGdOmK7igSNu+9Yezc9tk3dXNdvlaP1IbuHunlzWTQ0KtukoXhtTnZM7E3UjcAcd/fmuBOOPQDITZ1gtQS5KKW4u6k11k7EbYxt6dydQAY3I1UDqJhHfqFarUmC7rGe68ZeMYB+qNY9skkAfYI7eezfxINXl5TVA6qJXzd83b9w7venC7tffpk0eQMOXlywPtMbawfcdy5y/SdhxFvPYKhDO4JGjSWtmdaoJZh4obhj9fTqE99MWat3K6hPz27zW+9f5JOokaPYdNXKVK3WqubEQgCxMEvwbzYndgohuB3ZAHeIqYVerFPgfX/sZ8hN3ao3VLwHsQDu5GRwDlTQiFrUGCzMwgS3ZoaQNvKIdgt/DOJQvZwYkAZaNUbe7hVL0PF+oCH3u+O+p5WbXm6Aw4xW3hqJlUzdDPv1fcHaBZ7gTuZuQibs6mZalbA6vXuIwSAmZVIiSEgvUALBK0zD38jcCDCoQdXqvMzBHLlloohD7ie+H8asWs/n88PDpx//+Mdm9v2vviJmgztD3cmVjRJkNaJHkoAk7sSqs5mChEisGWm01qVq2fN2N3X9ktaOsCWsOemw7tzt2BH2pAufsbL1xO+13767+vsb7k7bpX7zm1U+uBECQtptrhxNt72RXNyxos07WMo9kCq0OMs9zNDJejPt/MKUNU3ywe6ZjWWvhIuaAESEcCfwncjcWftGPINyW0igjQaFw5uavfAhbyEVQs4cLIXQgEFhBlaVQzuu9mL++/ln9hDidipVpyONGhBArjAOwtFVOXVTN3ZTaHONdWKElxx1q2HAn9Qcl/dzRQADiYGELDfsIdazE5am7juFzLztgi9Lndsc9ZuxrwIKyG+Wu73u/E87d2+uATtxqY+cHDAic5DDvENXjbnHlvDGRtsD9kTJy7LM0zW09g50B65OWsFMsMhWtRqN4VqX+Wruplar1rKoNVJWRYio1ohzjrTTMefd/8LX6KnOkENlMbuCQLQY1FHUzbm7Ca9SFK3DqGoEr7TGebuZq1ut5eYMaEFdNrm5U23fTvheAt6vFL7E2m33h28/9nV7roSo37EJ+ZtJDf1Uuu/U8c+1dqfNjtEPaSRXetFWy/qKwLeu0Nq5uGpPtwDA3KuTtXglDz+41hVzVQtxHmGFs0gceUMzi5mD8zC+O46mdp3nTnUs8g2Tm/o2I25GwMBCFHBaIzrm5rBBZDcDdp2u5/NZiJkosTCzMERCkYj4jERMUVolCUd62biuSiIiYenxe2tOJSZiltRuETFhW1ZaYpYGCxKDBCxt22xWjY2HtGXvDmSIbMjBzrW4m2sN0dlNdbrs99kuG10Q0FCVI4o9sB5VD6bSY0gISmAi425rYDIKe4Ort/D5kH7anJprLYs7zLSd7t7/UUciN63zdL2cLx8+foT761evBh0NcCKDqbm5uFvERUVUhBGpCpjdqpsaE4Eb2zNTrWa6H2pAB5/t27ZJm3bSN6lv1iN0AoZt227Kyw3/+cKdva8VujbdWOD+BL2U02JH0v4+WCmnb3rt/hYNXMF6zlchxXfce6dFdKVsjei/PZYvh4IdidrNxU64DBWZtk3Z1fHO2rEh5+3zHU9rwt4K3O4ChUOWpJVkEwBuOawoRcIZNXcvFYoO5+1Fo+BycA5Cvg3Utzft6tW/JNg392VucAIsspo3CLCdSF5dp6mxdvbtlv32gbGTRA61/dr556ydmsrT5v6X8PV1oAS3VUSNXdzvGVft8Il1gqgzCFp1K6L1pusPzNrORL+5b0y9vdkdo9a0lrIscWXkkDRCLYUJzmAmQYDzDdYluLuWsniwdjWtxcxVYQBqQShzamau4QjWVESmyFW743BNkjODy0IFoMVZw1PT18oXO3Wpn0zVlwhfsxbt8SR3qEJLm4PGeZuIsuP/26lZGfSXmuOFh92Ke8XU+i5txHbugsGuGsF+5F0LXQVr72segOiazL37gZu9HPXKzlf1ch1BR3+oP2I7/mhnntTdDFXV3JcSVvPG12uNM9WPa396uOS3zWPuRDmn02mstS6qZhYSdDgOuxuvu9DczYgo0suHBaHPA5tbks3A5ebLPM/zJMRMrMxMLEISezIRE4lU6pVKXIiZg9WxiDIzs7KsFzR+HV60kiL3LXHciIgo6iRFjlYnIWKwgGTj69ThtsaS4E1HMjODGUxXdo66wNS1Bl83q7os+4W7BeTN2NWsuhWySqauVZeFTL0WaEJ1h6IyCbMpu0a3BUZWAbgWV2s+UE30N5Ca11IWVb1enYhSTiThuQbOGSKXy1WXalWF2cznZZHp+v7jx6JlSCxM93enV/d31raGBmnQwmC2unhVZyd2ZbiSW0VdoGV/TnY7u8+e9+OzU9fjyuCHLw6n767Bxrh3H9Lu691/N1lsf4c9BXzxdt9R9Ki2nqczQAnTG8FlFadun+3UXeC9e9HddLJxjRe0hm7erqK475FwpjUFl7tHhqI45xTCcvwz+A1p2SiTN3n0hrF1dM52fhLeMld0c0MnM3F4pUdQeA9d47U3tx5i7gDUt2DeFVII6k7UEpVzStJJA69ywkKLwTWi1IksUiKukCWCYEJDl1XDCloCzcrY6ecLESpcDPb7c4WMGsr4clW+pXXFfZNRNz5CCNli77m9k0xv9ltHd6jfy1YnXN94E1EHhDqoFZdsAYAAuqjdZTmsGXOIItEkIutfEiaiFOn8HFVjn6uaz0tkJugMxKHWQxZbii9CI4Jubps1oUEK5AxXD4iqejKn7pbVtNmGVXbf3133295Bi6TQWm70WteKWrARvFWH7ouxHZ++DN+6kF08an/0CXUHiJq5h9Yvth76jcree4yOw/dtvkr+cReiYRBhVnM3L8VqqV7ri/us7s9xIMwCdu0I/KbN30gz2gys9jxXNStVW7Ud99Vk3FIPxIZjIkC6jLYSZ2I292UptS6x5kwU7i45cRISYlNzkAHOMCMiyjnxCjIE5XJ3kPDmLKFmz+fLw+NTBFkmFmEWbqy97UbhThMoiTCTrMVigk1HFZkUaa0zEVU1dSMSYkl5OBxP1GTOJgEwJ2IWThysnSMJq3Q4pFG3MBM0U0oopR76upkWN0Nd3NVrbTnyrU7Xab+Zblm7K1klLWSFtJAptNZ5horXgpqcDc6UhYzZVNyYKIEEICvu7rVYtabwtzmMoINSyrQsdZ5nd4zHg6TmWeBM6j5Ppc6LV00klWxaFgV+9uGb5+vzmDgJvXv71uFhnHOtpcxM0IUgbLW4qpuDXchN4rzNN0BZI6t7SGrF0v3zIOH98fG9PrsTXW/I845gbh/txHOiW11mU4peigL7h25X+8rZWyIH1T2JCUZqa2wndvpZnL82273fqxpPhI0B7bu1TdQqAd0aucHWvrEG1SBiJSR0wJUT7KjLxtrb57bnaw64WivTtJ+AAGLWmWwSNlOUdSHAwi/TNLgGr1L8fkrd0cJFeuSGrySyid1RCmIImsdCLSRW3Sxmr6pFyF3TLIjALfND72ooRN4RuEhfswoSPYf5tnKtGuOuM60/jffS7tJf1npKzK3tbDPgbYFXCnoj02LV2Sl8jyno7+bXtOte/3lkEGPELjRfVa7d6KzvBCWi+ISZIlpAw5pLIKKcc0qJqzFT2FnNvFQ3bzBkT3G0E4K421Z7rbYYxXriDCBH3A/gCgQniP4S3I0UMHM1eCTtXPlmyIOqXZDwUutOIHLU6qV4H2xIRTsf+ZWvrwz7y+u2LSCtv9jNeevpirx0r4WVtd/qBv2rHWs3NL+muAUbMx3uZBh4WawWV1crxepLLDB0aWo6ZItZ3qpJeVPreyRba6o6zcu01I9Pl6pWSnVHTpmjSFpESKx8HU2jbfrxuo6OCDmpdZnnCxMNwyiSxiGnlILXOsGJHWQNt2BiTqlVa11dGx3uTnnnvmpmj8/PHx8eg7VLq/3Sq50JMyH8slNiIsoiTBwqfkOImFgYka+NOaeRiC/XaSkFxA4+nU6v37wlImanht6TSI5EPcICFoiAOKrjdIoSJ4tBpA5zmKlpdTPX4m61FphZXeBqwdq1mtbLedqd0Bce8oAE3Yn0IG5kxu7inhwJyKAEEjUqlVWhxk5CLOqkCndSJVNygXOLDQu3dFMRHrIQHdyhgBadS62qnx4voPfzVC7P0+Pz84dPj2r2PC8pyeX8PA5ZyAT+1bu3X3/ve6fj4as3r5lIyMCk5kRuVVWrubrPYiPEoBV1gi77M1NN51L9RQvSxdJndLWIRHWZ/cnrFJm2oI4dfvLyjG4/cG+G2ObcAWrp+YzWe4I6yE+dCwZ39+aqveohDjXXcNu95e0dXfPttt477LTmncPW702q8I21E27vuyMhtuHc3eTalYPgWNatlmtA0Kqt3bL2rjSv2pc1Smbmgfvc8HYWFml+LtyB23b7SGMRKgSHLb3NofEL58G2tgBa4PPKy9ArDnVZv1NY6oaAndizEouWrmL3DHfdPO29uasTOsDR+cMN80U3X918/uUt9YtaW6VQsLePaeu+NQtp8wfdDWs/S8AmF8YsUBdvu4y08deopdzmkYiIqday77I1L+gYKPWsXc3FgaIaFaI4L3mcaQK3YHJz99JdhfoGv9FtqUlwO307HKk2btqi9xUAYmtFxQmYKSGSIbEZIse4tZy/XQANKKCx2RdLh0CZbjizY+PB201eiHNfXr4+uP5mnfb9uWlnssP8vvvN7pf90K5vuyeiOzFSggiGgYdRwpRHYH9ZYaS5MIebdNACImKKWLPuWOOkiDATR88Os5Q6LXVe6jxXNQvrhkg4uTS001aIL9KYArU4AZKEu4WgLpfp8jhP16enTzmlt+++4jzAD5F1PmJOnAktzyb6xvaugnXTvW82hTZJ7kutcylCTAhezpFaSpjMhIhUnIjEwUzFeE243iw/TCJOTGxO5NWU2C9LXZaiBjVXSD5WbmXPKJAEFmOCiElzJXUwM4fm1ZxIqcvhEdvQWLub1+JuWou5d6aubqpaTetcb4wpt2505BlgoALiTqbsmtyS8+g4GB2dMigVpWrkzm4ilB1kzuLmTmUhVaEMYkSBvgJVoNYxi+d0lwYzfPPp8TrN7z89PV+uHz8+fPz0OM/L5flSq17mom5F1eFCynAtk9fl66+++uHXX/+uX//hv/KH/+DpcHj7+i4nqdXcUMqipWhZtBQ5HZzuYZWWiZbzetrdMS31eZqrtlZLjfTEBKSUuGcVzSmnlIZhGIaxSaPrGYq8ldR2BnxT3kKCbTSGCGgmX3VXMyImkoAoiBByH7vCw33BwAJiczJnB3qIlwJIIt0luwF+qlq0Fr2h4OZQ8zUFNwfy1fJtRsZm3owQq57OBAe3w8Y77t5nbWM5IZKhk3XAuWkSZCAFnMnQzBjUjH89Vm3P4jskG3nfHECki7GmIHvteWGjESGnNAxDQLsdqoaZOzu1Ii5AsPZ+fM0dakA4y3sMKtxrmcntluz2umzCIO7IYtNUPBgIEVrWJG4ijLoBZM5r0l9zK63nTCBPkYgtdMPO2s1Xx5+VynzG8h0ImeEGQSHaeMtnFtu2/cy7f2Wbp2a8ANB0Y3hHZPs9127EoW0azybB0gbPw8xqLWY2L3OU22zmZ0fO+TCO0zzvWURVFF2nOW7nBiOQsgPh8dvELlEIG3EQTDdV617A687FFqPe70mKhoggND90OysauTf3Vk6OPAFgMiLAi5tC2ZXNoRa5OzadeJ2K9WTbfu0crubVm5y9AiCrI+PLJdp3e3fO0KXw/a5Y5YFg5nGSgButvV3Ufda3I7vecf1PHFh3VCEMowwD393JeEgwcjOmapV9Vx3G3U29qrE7EcQ5rB+BFcfOZmZQeLqgmpthqbpUrdWWUpdSnp7nOHFR+5S5O4g6CEhEWUS1TtNkqssyOfzVq9M4ZFN1s+n54+P7Hz89Pvz0xz86nk7H9C/L3T3bXULiiG1ldm+VVNdu12pwZxYQh0UmnFv22Ky5naf56XLtbnQsxCKUmJg5pURhMieWhEhWGBIpOa/1ToWdmCQxM6XMxPz8PE/zPM1lXsrbAj6+ZuYODIBaYTNPEkXeFUzhiLeKHvH/YDPN69BqsHBrrF3NvaiZu9ViblaLaj1PdS+Y3bD2WuuyzKpFa238stbAZGoppSyJzJULQPCwspsQ3LmRdNdSrBkjObLRhs9+FD+OBxu8lDovZZrLdSrXuV7nWhat6rXl9tmc7Bym6rXo9To/Pz8/Pt49PDzWZclCQ04CJOFlutZS6jJpWRh2GJJr1eU6L/Nexr5M08PTs2pVtR1rLwRKW1IlyiknkWEcx3ERkcB2gmGQK6GT4NAghYVTP5uuDY3tDMFRVEut4QxJRJQSMUXNe/YKuNXiVg3kYDWUiG4IEzWUgJRaSXvqSaHUtJTlxei8ydc9W1/THfpBbxa6jSuE9EwG6tGtrddrbOVKaBpP9q4EoOWe8PWWzaBpPf8l3AOTXG3t1hlbuwkC62up/mtVa5ine2QqvgnkoBUB7vJNaHgIfkY7MtZnJLoErP7S1DZfW8mug6/a6ap0UvN2jG7a6urTqFFXX9YZc1gMvj/fsdofO0fcTeDnzb0lJXihzd8GKze+vmPtO2ayDn63xn1YMd6Nzq+mslWbBbCunG0/bl9tliQ3U6tmtiyzms7LHG7qAbK4I6dUlvl6PW8acysI0J6y+SQYqHOoWAOOhDBmDrCTcRd37CZnWlfN+s7z8B24OQjoW61NfE8BqhSwmAMGMiJ3UodGTpeVtTt1rX1FsVYme7t+jli5tuV/JdPJyrFfoGOb5HNzj/V4dda/4ijeBUZqO85XaZW6QOC3FwdhZhJIBievVlC0KKlCrQUb75t1PxhafXaInQwUynKT1c3JHVVdo3aqWnO42gCvrpr4uimJQLWWcrnM8/Tp/Te1lmWZHfbm9f04Dm7qZp8+vn98+Hg5Py/zVZim65kIl8OplppyZUmSskhGx6Kiu6oGBzJ1kWclyfu29mIT3mNnoJse1UGOqgAD5qDIKu/ilAAmTw5yT3BiUjJizOqLYq42LXUqOhVldmYQSCJwVowIyayBj0zMLt6TLW1WjRDUui+S9kSr5hpxRC0xtvu2/27axtrd/fz09P7nPzdVtVqWcr1Oy7JoLWT68PCwzNOYRITKstRajuN4Oh6S8DhmYoiYw8NdgtPQ/AOJq9WlVoUv7iAukFLt4+P58fny8w9Pj8/TZapzTWBJx5HdSc2BgzsIoxCTP3365vr8WKt//Pjohix8dzr++g+/fxjyq7tjYl7mi9ZSpktZpq/eva1lsVrn6fzzbz7Ubjoys9/5nZ/8v//u32tVB1RLLWZuqiAkSUSNOkcay/EwjofjYRzv7+9F5DAORCQwuC3LsmZHGoY8DGNjTqbBboMPsAgxP18ul+vkDnOSlA+nU8r51ZvXSSSRkvt0fl7maak6Fy2q10Uj5idEcSIahkFEckrSfLvEYWb285+/X1VbB9S8aLekdEw+1IcgVTe298YfN0mRgFb0abtotzPQGQeopUtc/WLb3gpAXveOKxtx8U17B7XcEnH8r9drrXWa5lIWQlSFSjnnUqa9Y0TXLym0z3YfwIH6mU/vXp0JRTWkbgCBiUQc8ioib6o7oSPW4bwCDzTMTLU4FKAWCQJdyb6ZhXeee5RXECIIc1TUIGK30kUDADtwpC9eNSthjm6Z7LqnAm7J0W7Od2aCl6rhqk+tYlBQAjg8UjZRn0PfOTHsEYOdwspETDCtVuu8zM/np1rL9fKsWqdlVjPj8F8111aF+bd/+7f2oEs1X1a3kJWcmkc8DhEERj0xt9qNDBezZt0r8qaHXaiMYuodiKImW0fJgIitaq5+ZESOqFfphEIwwgIqsATLHh4bQdi7fMtrRer23Jdp5KqiVHyJtK5M7Vvkr9t1i5H6Tkppkmnj6Ggw2PpVl2/6T1ovt8vibmjBoOG8ljKdjikPGF+5iH26fKil6HJn9XCZba5cdKts5+6l1GUpFKBIzkhCZB6W8uiamgPqZI5pqbVatbAQU+ydJAlw2dV3437OiPDx4eFH//AffHj/zX/1t//WNF2rFoK/ffvqMA6xw02r1QVwdtVl/vFv/9YwjB8/PqQ8DONdysPbd1+9fvNWmJKQqc7zFBYXIj6eTpIShZ9a20A7hYEo5ZyG3BLFREZ6JggZUSE2ULV4hQHh3EQkRD4kPmQRopGdQTkAWVUiuy6+VHqe9Xxd6nlO55lZmDOBmJ3ALCDiJGCOEFAXoZx6vowWPUbcjB3aqKcpWrYP92aaqnAjr3BjaBgL9jvqRmuf5+VyvgQtK6XM81yWUmtx5st0NdMliTCVZa6lVK0OT0mq1ci3A7iqOZyrMwuJEEnRutRiQBV2cngt1eZSl6UuRUs1s0ZrOAHua3lEAkTABJEsklha0pJpmoV5nmaYRQ68abrUspT5WpZpGIa701W1zNfr+Trbjj3M83K+XEPGiZID8YYiyKGfOhFhpqJaVGutYE5J1FSYhJzcl3mpWrt4qGoRfVvUdJ7nNWpLcibm6+X6fL7EyZSU1ZGGzCmlJJmM4NP1Gqx9KTpXvcxd4GgJUqiUIrJVTe6lyn2a5326vbBohlKysvYbM/BLJQ9NIYlsjWjFsrZf0Pa7lc10K2d8x52iNVt799Iz8pbBbUXlu61yZcke+l8pSyllnqdlmcMjPSUFXPeeuh6hMnWTMaKDvAfYtuadmDUFOhhyi08L/c02bf7Fbykw9gbDtvR4pqVU1QJ4LUs3wcAtqzQ7TpgTmFh4IGYIc9MZPMI1vRN1j/j7Va9FBHT2KKDOjH1dNHRZLAwqbk0C2Kl+7YehoTeLiO0cWd1MEePu3KbFJqh2Nrweu9bThpATiMi0alnmebpeL7WWy/UcrN3MnBOYraqpkYPc5/m6H506dF2ypl+2aHInJ0dohNwlGeoaTOtPz9ewhzQ6voMA51coosWTe4+4b9e1fdoxAocbuzmUSFtKLjcH2W7SqS8edlaA9bG9H13X39CUveYeuPO2z/oG7idj76OzOgzsYPrGyFfuvq6T33y+yqXbV1hl8VXsiEmiNHAakbKSkFpdatGqVrSqN8V9N75uAjMgJHiGw8h4H+MKmLN5hGiZr7ghQikFWg6iQLPDKbVRkvPz08cP7z+8f//hw/tpuqoWImidDkMOjzW4kZsIDUkIdH5+WobFKaU8DsVSHo6nk2olkBOHN5lZbCA3V27FPrmLTje6eyTgxXbUnB0tbTrcgAjZLO4GKwrtG9NAIE8MOKTtV4S9vBiqo5gvVYvqUitzHCVmIxDEldjVmZmYnBniBHJykig5KURANy06ISxE1mnvGuLSQdlQ4PglLbvR2n/y05/913//74cYaKHb1jrNE4APj48sgSlTnITjYTweDkl4HBITUhgziEDUZCVJRFJVl1ohQuMIZues5h8eLpfrPBVTJ0rDwCNgIFfV6gUddlMt6iosx8Px+1+9+eH3vyI4uwpzrRVudb666ccPHy7XS6laVX/66frjjxdVXebpR7/9s2XRdXTTPD0/PyNOrPX4KncANahbiOpWQXQtha9nkTR8+sTC4zCI8GkchMlWQKQVE/Luyqu1LACGnFlkOBwkpU8PDw+PT5GwkVjk+UzE9NOfMVFmCDm5wo04QdJc6vN1UdWyTHAPJX3IWUR6PFGzlpv7z795X1fdyBHp/Hoo0qq1G62I6m7lQzT0JiM2FYJMsfOOa9Sjq+XtJJgvS1FriSGD0uUkw5BFOOcEeMshYd3LjwJa66nhyUEwWNFFVT99+jRN0+VymedZohEnkQ8ffq7dJd7cP3788JOf/GRjGGH0+4y1b5pMHGO3RlfW8TdSr3Bb/RNbiAAaIg+K3ISY56XW0hEvczci+vhe0HLRe86DCFdt/hu11nEYX796l9NwPL5iFkmZiJ3WaDTUWud5fv/+/T6AeKl1WopqXUE2NNV8DZjh8PMjdDmsMZl2tZqtFMpKCdE1elVqhbsHxaKGeTtcq5ppqbWWijBRUIskDLwkpSzCwsLMWkutZVmWYOrzfFbVeZnNjCUxs5aqquwgx/T0vCIu7r5UvS61b7w+4w2MNCYM0tLS0eoLcruo/bOONa3cMXLPNaxl5XyduVFX+L0pckoEB7nCyVEJCipBut3cIUAi30IHm/GpPW9NbbAJ1OZ+me3pukU3xAj6eVuDvPdiamfL1K0TTdsO/rv9BNjwJO9+H7j5Nj7jVnmsiQ8RJBIxIKHuwwySkJMfDsNXP7zPA8bj5F4eH7kana91us5l4bLIsjPXunspZZ6XlJw5wInwnyKiYECNdauFlaPlTY05MAeRGingAiaQuMPpw/v3T09P58v5+Xz+2U9+8vf/7t+dp+v5+cEi15n7xw8XuB0OwzBkLaWWJed8dzqA+GfffJCU3nz1/WE8HE53OQ+ScLobhyQ0ZDcnmDRro1st1S3iXIJURYKjaGr+dC3DuVUeaiQ6XGRASmSgAnGQInUjDTGTEOUkh5wS05g4SgwLkTiIoNVMfS51WpbzND1dz8QJciBi5hRmdSZKUpkkMTW8ASSMITmYxoFF+P40DIOQEtzrjDLBHaq0QTsabu7kHgW9KaUbBO+GtV8ul4+fHmKE4dZqqkuZ3f2yzNQhrwj2uS7LcVmSyJCFQTkKqEf27ZSYmTgTS1VbqlJKogCzs6n5dS5zqRGwFl4EAIXTQLMoti4Z3ISZczoeT2/evKllma9nREQNeYhpz5fz+fm8mFdDda5gVStl/vR83Zf6CQYccleX5tvx6RpAM4yCSF0BENW5FGGelyUJux1TV/6Cs2tLitVYu5aFiMw9iZCIA8tS5rkQEVjASho3dwADOxMSkzAkDylTrUEh67IscO+ZysFm/Zg3q7W5T9NntvYeMUqdqTf1joLD7bQKR8/PZluEquvK5vvDNrbmLQzD52muqrVGakgHoMNA5D0hhKtWNJtCIzfWmqspCM5ubkXnWus0X+dpmqbLPM2SUgw5Md1ofu7TPJ/P503kaFz4M9a+04zh3da3185bvkZFMIaWTq5LvETghJ7/cp7nZVliq4SERMDCgIcs6DlnESllKbVqraWWw+EkJDmPcBZJLJmIIV3vIyzLcr1er9dpPzo1LVpjC3WREdHDlFLE7hORu2zaLLyd0I5CrdxFp8lqLWWppdRa56WgD4FZAETGvlqrqZZSSynBY1r+n3YEkNPAIimJsFRtZTXnaYr9qaZlWcxNVJlYa9VSCcSg8Hbe0VBrRpMbe6ITQcicoC1P0A1hWmGL9bVhEj07kLest52jrbOCjtV417Ph7i0fLJpSCfRykYC6s/b60+iHxBuCsFfyApq54bBFPcJuOmvfK9/dprDXsFem/ZK1b5e1AW6zsBNv1gcH146D6312ApmBe0DoYA91zwBBEqeE8ZiGETmzGgMw86XYtGgtpOo3AbUOU1PVIBBdUwzH24ZshNysCgM5hEhouxJwhUVQEodvhQPXy/PT48eHx8eHh8dvfvbTn//sp+H+3YUqW+arao2CdWVZlnkahiEJu/tcKrNwHsayVNNhGOb5UuvMlLPStgphFnRzgwFMkbTAb4OffVGbSovL7PK3w8MrmAxUCC1ivsuPwszUKncmZocLM9iEOYWHr7tHEJNq1VpqIQGRgjy8z9yciUAceBg5mZPtEmkxSxIfBx9HhG2jmKO4Wbg4UXd1AgBFHzHwghzuWTs+PD3/6OffCIdjQsyGlroQYTgcWURYmCL1rp6LDnPhnmVilMzdq4Mi4pgTsZizQyhZWhKI1BdVe3h8XkqtVdVaYJiF463aPC9mFuGV92POwsPdIdM4DjLP17LM18uTDvn+bhAZvv76XRI+X57nZV4qqdJzwfnTJcjx03xTGTQNeTgc47h0kthIXjN+9apS6FwenforHO6lqvHqThIJ9x0mkiTlwc1qSsx0f3eXUuKUifl0d2/gql5VnchJwmIM91omaC0wgufBs5E6UkoAsSR4g2xaIYHm5uQRDqHudU9jEGkwtQ8ltObwzTJ4BZy5S01r7HNEOEZWCoCj5ExTbs19SxQTJruiXkr95v37eV6WZa6qEV06jofT6ZhSGocB8CgWoKruiFIqQdCbf4NbtWquS13MdJ5nVbW6wCvUHFLNSq3z5bxpfmaPnx6/+dk3X2Tte+LbSGXjBI6WQXhv7nQ0sLonPQ0Lf2ftTgIK+g/tJaciXD5wlHFIjeI71M0IGlH9RuSixZ6fzsLzdImgF2n7pFlCqSzLdZo+fXpYtXZ3f356/Pjh/TRdyrJorbVHTgvzMI7CMgyZJYrSNP2sBQmqlVpMbSklzAZuulzOWuZ5nsNyVEoJQQFASBkbeNzfcE+/s5XsBIkIMQ/DKDlLSpISA6fD4J7rmNxtWWZT81K8avXmfcCEYb8tgaK6lNJIzGZIp0gezFExqwUFdexkW6u2pAQwtbLv1HFyC9zdwt++sci9hNBxpUhh1LZCZF1IZAw1r+ZL2xQAkELb94b/NKt7f5y7ed1lgXTHZfbHqVngqeWF8r5LORTxjX9vs7KTs1dpe/+CFVBfX7Y/OmNtWomt2wvGqIAbzIPuEzvUYYdj/vqHh9ev86vXSOJW4NWvD8vTh+nxzJdJHObOi23Hyd2meTqfn6fpudZlHHIgiEky3BA5JMjdfJoXM9y/eTceTg4Ox0Uzf3p8+K1/+N9qrQE7CZM7fvazn3769FBKWcry/PBAUUtMq8NDlyvzVGvJDIHN8zRdp1oLADO/TldJ6f7Na0m8zKRWHh8/fPhwGvMwH46ECHbjPAwcACBSKKQeyc5u1+5a6rDU8C9cGbs5WY9LNjYQSVCJjpQ4kbqXhtm6sIEgQkeWJDQklizTGewqrmxlyOnV3cgsARNmaTK0iCRGYiTxIbswjiOJ4Di6iB4PU0rkrK5GyZFcFcVg7sXULDzWI7lXi4UuN5lObuu1n6fp/eNzTkNOA+CRxaHowkwnyeJIiZlIq5kqVxUqgYkJy3EwAmkQQaGW4YcTKBEPnCl5daJaq5k9X+a6y/xABDVVr1W11MXU6lIJwMhJ5DgMhywpsWrgQzPBVAuQXr2+G3MeDyNLgpMaVdVlmgEQ8bVsKSxAJJLyMJgZWhaGFVHexJ5Vkl61iHg1gHrIcigdIbzAGOwRLAE3ZjDR4XhMOYcr7jAcjk5LKT4XD/8cIqQE9zq71cjLa0binMDCnFg8YrR6YnIiYm9BZk0HMXfb3HjbOTRT7qw9In3catiJAHMGwYOvR77UMDJG0kfAhTbnLO8Wh/BFCLF3KTYvy4cP76/XaZoutdZxHIdhOBwOy3JKKY3DAe6mi7tpre4ePoApJUlSa9P2ljqZ2VLnqDLggGshr+7i5lZrmaa6TDvwBtfL5enhCS/aF8qi7VdtRWpfvPrKIRpC3XXhiG8LHuDNXQCR6jTgfeHIG9nEY6tO1AJ6YURgU5+uM3NZFiXq/nLE6BGSy1KmabqcbzGJ6Xo+P12en6bpWmupPWFkEjkcjyJyOBwaKWi5FkLqVlUNp855nk11mSfTOp+fyjzN0zRNUwDyqwbc+FofMjd8kEU4iVDPG9NTbRERlcMx5WE8HsfTXbe5YEBys8xiqqVVmWjuUYxmm9v2pVptmeS87+dmCuLghQwmSHPPon7++oKiY/XRt8as19u34tY9iUqPg0TXeru+udq8icDkAuOutRsUUIA3HG/Tibvqv6FmN5nY5+rXpZlBI4EKaPU0j2fzSmDWIe1Y+Kbixp5aLezYcff97zrq0F69dTueYQkGmMINUJDCiQ2sPORX7/L9XToewYSleFVfrvX6tFyn8bI4iYOt7jEJR1mWeZ4eHh7m+XIYx3EYch6GYYCZaYnMrmp2Pl+1m2YiUtvdTfXy9PDj3/6tsizEXY52//k33zw+PsYmnK4XioCY8Bdzc9daAyJKWrmWsiyzu4tkVb1cLymlUpfRhlJng16vz09Pn+p4gCpH7tZg6UjwIeqPA2Hir3vWbvBFbdamMMeaB4ZjoO6B7EQgibhh73oDeQRDhUneiZUSaHS4c0oyiGQmhpEruWb2V4ckkiL0KzMxU2qp9JDFhJGzp4TTgURoHJzZczIWN6iRxWYlp4bEmpp51aIR/+am1v5h13ZaO1ANS6vtS8zMIoSUEkuSu1dvch4CjZknWhS11qWWlOQwjpSyHF4RUVkmM42iTikfOI0smeUAEuXB3Yu7GXEahNithrJDADMxRIRAMDVlYqYffO/dq7vTu/vj3XFgcmKfrudPA8P06elBy+z265LGu7vj69f30+Os0xJxFwBAVncSKABJKeWhS/JbOJY3VXfVWRod2EywcDdjQFodAFB3QA7PvmEYXr16BbjVAiDnxMxqMHdOwjVlMCjZmmY9CeDJ1TS7VrhKHtMwgASSufAyT+Q4HEcRGfLAwqZmbrWUZVkiyEOaFaOtXi1LmS5wJw+m3RJ1uqnVCfAhBR5EcJRqtagZtK4gFoT3yTFXs3UwSFaz67wspZRlMl2Ox8w03L+6Px6PqlZrmadlulzQEzsv82xmd3enYRjT/d04HJKQMNW6uFF1hxaY8ZZ/KkRkZ3IZZMg3BRip6yXbJ3T7PTbtfGXu1H+J4OjUPuVutV053Ar3omG83LaAk3mtVkPYSSmdjnc98RZFmsmmF7qZezcir6hkg4NWpW1MxAc+DId1OG52ffj49POfXM/PyzzXutRSmpJNPA+ZmCMntvSKc43uNglP3bzUambLPJvW+XqtpZRlKaVoz3MXnEmwJeWJFFnmFcxk5C4gIiGE0wVawndjN6+LV6szUxtyK7wR2QjK4loJFtgE002eWQBqxbR4w6sDgwSFfYhA5NVB1GKLmqiFTepqC+yOoG5bbAOhF0cIzfozhAYbbybA1cjCoCrAISMxVSNzmqpbUQNHuJM1q84W3d7jO9DcGlbFGijV5+JrZHnPoGDYdgBWrT12qcN9LfKwVy2AANK7gr4nXy+19nX7O4qjMgXP4JwyHMtUqlqFGdndke/u5d27w7t3d4cBcC/L8uGbj+fn6dP7p8dP04KTAwpzoO6yIKvW9x/e//SnP314+DDPl8M4DDmHXqO1LNcLyEXIHdNc3DHNy/39a0kDSyIiZn5+fmRYEuQk1HOAvXv76njIsUylvPneu3dm1nL8ucOtLLNpFWFhnubper0Gyzazu+WemY+Hg4iYVV3q4+MnSXQ6nOrdDIeqEjgNmVnG8SCS1Cwc5onk08eP+0ndHDzpZpqpZfchAhiU2YQxiAixSDOWC4OJkhAT5eTCGNkyI4tncUI1Xep8nZ4fB/KRvkoMMgOQiZloYEnMOfmYPWU6HEgSHQ7MgjwQM0l2ZlglNySCOGpxB6LMucOK1qqmXWtX86LfwtoBVMfSc4ELyZCEyBOnnPOr12+HPGrVwM0xa611ui7jOI6HRDLK8RWI3KlqCVcpGY40nljGlE7mKApTK6buSjIkFleYFe7WGyFxZxZ2sypIwj/4/rvvvX3z/Xev3766q1qqLufnJ2G7ns8/+/Fvl/lqrinx6e74urz6eKm11trTmPmWF6ytXUopD8N2ZHw7eU0Z34DT7u+zCs9aySPH3k7ha4ZcPhzGt2/fMFNkJA1kIjKmsiRJxgmc4REqQpCUgEh3X6HFTTllkgxOkNzYDdPxeMo5h+IbmWVnniM1jbtzShty6ajLvEwXrNERFm59xU3rfCbYYZAk5EYAzXOZ56rVSwnvqsibQESw20zT8QgmLrWer9eitcxXdzsd7w6H8d27N69ev3p4eHr/zcdlKZfzlYCcBO6Xy9lU4eonvbs7jkM2k5xkWVDLBeaoi6sKj0xsMA+Q3EwIMsiYZc+8m4Nt79SOTm7IysYGaP9hlwu2wtKBNK0sbl3KDR9lJEKQIjav1Qoc5pYln06vRST8V0O7bXfvFvqAO1oih+aOsObmAzINeTyMh23tTC+fPjz+9DBPl7rMtRQtzcCPFuzXxsu7LqMDDyy8qpVh3ViWqlWrrmZ7X20ucYtwiImUnA6YgpjMY7wCoPnMM4NZ2dirlalce9AjUU6ZwsUPsIhyIQRkRQS6sfm5adW6rEvT9W9eAZNwSTbb6fQ9+VBfmK4uUcQebuvLaP7wUUrnRt7bznh3JkZL58CMUXxIpE7qZG6lVncyWPd473PaWLtttMJXf3+4o1QspTPcEG28hVh29To6ZQ3zp7B7rex7PWxBBjWwufVxfRwrht93fhsROQywxJQkkaThcHAnm2xRKKrBhsP47nvjV987fvXuTkhRL2Uu73/28fHh/PH98+OnYoeKAequCD2wPUJV379/fzodn54+zvM05JRzqlVrqcs8PT89EHA4ZBCpOsCXy+Xu7tUwHobhEFm/lunCUGaMQ4vcBWHMb8xercJuZO+P+Jc45BHiVcqitc7LPE1TVZ2WamZFFxDGcWTm67yUWh4eP5Y6vzrdo1RVm8MDKQCpPDDzsiy16jAex/H44cOHDS0DFKhdDN+n6+rXGAMSbFj8NHAWyZFrnkAAM3IoeOxMPgoltoE9izPUdakLpiecEo1UMgNWAUTdmJFrYj4Mfhh9GPl0lzjJcAQLSWYSMBsoYAQURgKW2dXcyTBVcyuRp0bN3CNDf90JnXiZQ56ZOOU8jodj5IV1aK2Tm2vVSrU7h7u5E0seD+PxeLx7NYzjeDwSsTNVrUWrmRFnhzjYvaG+4XRmVl2ru8YSRgBAOCjAnFlBnkYakhzG4TDkIYkwCadxEHKr85tM9ClnIZ6v0yVnAR+HkcyX69VZnCUUAKOb09DE41VEjq3leyn/xfHZPuwkJuaO4J5yTimFX3TOchgHEEWWLjMHSCDEjrmomq+xCw4mHofMTPmYyb1r7UPKoxEreJrmWou5RxqkMHqHysMiKecAvHIe9tJmmafpcjZteYsCindVuHmdCF7Bzi0upS61LhoVfqmncGu67E70oVVJdCNAmB08jtncxiENA49jOhzSMufjcRAmrZWALAJ308HN7k7H093p7nQ8HQ9mVrXmxEyllExemgzkxvDIcExGDXa0m5K1wTCbsNUrsgauELllqRe4o5fUndZ1Xa0vqw7l1PXrVmUkPkMLirFYamtgciP3m7dicCWsAL43Q8ZKPnYsuLN2dHVg6yBJysMwEKyKWC5as0f5VCJ06YF6rDx1Dt/wZ6Y+NB+XopGZauXrK/MIpzxu6bKpJ/2OSWQiaSbAcK1qOemIKKXMKW04BIGIJNSyGF6qFg5EgQUQp+GwjtDd5+vl/Py0TkiTrYSZOaVMzBL+//3mTbQ1atgYXM3DBLaXw9bXlrlrdSH/whHudYIIiQiDM3jIchxglB0oWp6uxQzFFnPUlgJkBeI3PB648bIGoIaqfWexwYyhCRpJvcKfizY+3beAW/OHcuoRe9S88HsGqU2yaKPqCwlEHMf+UyQyI4UvZYG72uSuw4Fl4Ddvhq++Ot3fDUJw1ek8XZ4vjw/n58cLkPMwLJSqQtmUbiq/mfn1ejmfz9frdSmzWaoqWsM9bInMAVWVELlIvASmSEzEbuquWmvOGUBujtUEIu4HJHCcMMxH0s92MN3gLiKqVVIKzTvXamZq1YFII5aHrK7Hw2E8jMMwdEnJzb0FtkyTO+ZlqbXe3SvAL9YutiMD1CqdEhGxs1P8ARYweWLLRJl1YGT2xMatlDPlxEwUvqeJnRkiECERTkmY2M0oMpsJOwTuLW5dSDLSQMOR88ByEEksQ/jfU3jrdyDRjRtbY3ZhE/HknhJ5RN8oyM3rzvoM4Ja1E3OWNB4Od/ev3ohQFqp1eS6TVV2W2T3SMHqtWtQk5ePheHd///brH+Q8HE93xHywV2Z+Pl+WsjjEnc1YGdWslEW1LssUJkILYxBMhIVIIIlHImOokJ/y8ZDT67vT/elwGFIWGvJhGNL9ON4Pw8Ph8P7HP3azp0+PupTE6fXpnlSvTw88HuR4Z8SVSG8h3BV5vl3dJut3IrgD23bAXoNyfZMWhuF4Oh5LKctCp+N4f38EcLlSuI+pGoEdhPO0lOpoCBoRMfP96TAO+c3dcczJtLjp4Xgaj3dFbap2vlxZZKllWRYP7+KWbZsk5YOkuNvheFiT3Lv79fL8+Ol9Wea6LG5qqkSIXMzihcmpNDcWdyrFyqJwgQuxyCANRyVqmcM6ICzc6uQyec4sniSNIL+7G4Yx3d/nV/cjvJreTVMJW1pmdvchkbt/9dXb+/v7t2/fvH79Kiz3ZrUuY63L3ZCWZf706dM8L9Kz5ThCDDS7qUDV5EK81Mi/oKZ/W3P3UA2DDayKL3q8UcR9CCK0hBC5zSkQ79YZZ3WrRu7e0m/0DIG81kZrtve2mToTXh8G74lv+wYkGg/j8e5uPIzhoOpaHdCmYDZsk8JbPopFh3a4D+qDO2BhewsI0s3h1BF4akUvopr0GvUXLhehktpqk9i7bzVMwrb8wqFyA1EpFWoR6NLGwsT548O6Fmb2+Pjx8P6n22gBZhYRSfl4d88iKQ/hWIrm29h7jGY7ZzdyxG7v3gYvgJmmZu2Y4aoKr/nEnQlZ+F7uWNLdId+fEiUiHq7Lo328Fi3XYmoo2g1Sa2vyrsN9nudV6PReHSZcy53VSTMpuKZelKBlQTB1J2viooWHXhcHGS3HC1rqJwKhJWNvPv7NQS9qZIM4gWRLDmBkSqOQk8Ge3W3Ri0Lfvn5993r4Xb/r7vf87neDIJHPS/n04eH54flnP37//DyDftfx7r4sQ62uohWqe2u06cPjw+F4mOezasmJUxKtZmo9AxpQtB0r0DzP3RfCFyJmFsbheFjztmK3Mm2dYnc18yi6eyDIEaGbZlqrWngNtyBFN1d3j6otkkRE4GEDicm163SttT49X0op0zyXUr/+/g9BcpvBM6RTZjIGEpThLYE7A+G/LmDCyJ5YD0wD+8gYouorcQv85lb3Rd2dkBOlxMOQD4eRgahXmJKknKKkRaMzA9KI8S6dXosMPB6FhNIgYKJExODGdJzgns2yurkkTe45O9hHE1YGA5FhBmttsNZuy8OIpJTDG6KpMN5IX60KUJSLLbWaO0eWAEep6lSl1Jbkvhs2unLT3JGoMzaHO6QnLjSJJKqSRIScCSSMwyDjkFLzyHdTVaaQKplIWA7jQbXOSwHocDiyJHdHbIRlcRGTZDtwqVHVL/B2NEV8v+86Y6e+BVashtCKG45DPh7GnGXI6XgYUxJ3F2EAIk7kUbQnnMjMNxSPiFLinGQc0phSJXN1JlDzalOCp5zcvVD1rXbUHnve/t7Oobd/2r09W9ZEAhM7IUqMW2g3TEgMZ7iAhbjVKAYRObhnkosiOY2yOA1CDs/OIM+JheGmWmezChiz58zkGIThLsjuGHMakkgwyqjjsC6GezjxNa7Rta8OX39hpT5n4V2J+1xZ3y9ueFLA+39WbaddEVw/7EnNzQxrTJw334V+KdzRzmfoTrE7mFbisk89j1Vbb7zUwbzbWwCIhsPxcP/KtCJYu6k7FDvWToxwe+FexGYXzbGOIrR00+4f6ntOSUTUfeVakS4iWMuiG7U7e7I/64CwNytVC/zuvL1l03B3h9ygFEREe60dHbK7ASyYKUAPrSBXF4rYJzi3BMgcezcgsjBSujfzunezmXcBhVpyNu99bzNP6MptnBpufvbMSIlyJjCcHWRmakqmLSVD19pXpGf9n28JGxBTZWpdFiBlspxwGiWL3B0GJgIJnCJHUUvZ2IshmXqrw+U9KtN6TD2gxj3PQWRYMjgaReCGw7RUJtbM98zUypnmwcnfvD3evz7enfKQWOBWtSzL+fF8OV8BFhkKsiEbNsH0FirzeZ6v12tZJrViKrUyfKubSC3UC5FGNnZRAJlEJMLuJK0SgnUtdCVct8d4f8CBZtUhImNicXdpiFnAxurwFtMhRMyuEeYWwHPPaMhEzCJiXS68pSltsEk4MVLgY02nhkvTrZkot+KNq3QFRFkyajp50B8DwaGqIDZTM2fmJIk5CvB4OPnE9hOKMDKJohRmxBROpdT8fVY7X9v64bZvG2lpRqhWiMT927V2AsbhcHf3ahwPzAK3UmqoSu64XiYimqZZqzoxiMEg5+us7z8+SkqnwyJJDocji7gzc9YaxRwtpOkkAboM7maeAPRs8Q54kjQMA8PFNTF9dT8eBrk7DENi0zJfl7pwvbK7wzyxfO97X8/z/OnTo5l9//s/OB5PpgrTebperxOlnE6nOZAGtKPeCqhsK4v92GOv7a246GV4qAeGCTnDj2PKOX/97vXbt2+DYEuSYUhB4lQ9ZVfDvFRVPZ5OClqWcp3noPI5yd1hOB2G+2MeU5qnWhxe5qkuRX2qpuqHYWCWeanhzt/FdmoAkjejyH4hjaRSquwqACWnFMSdCc1fM5NyeI6Q58hqE5ZLJklEnIYBRKiBrxohhNNGwZgpD4m4GYGqXs3qMj8/2HVZqmoRpvs7EeKTZAKsjgDu7w7jYUiwOl0CRfRa67QsZZmuZVmWealL0cix32BLr9B6u0tDxthHdTdCQD2Ry6o14rZ1qW3V5pwiYVFXELFJEU7uBiUAQTAkkQhMXWtsD3Myr+ROnJkppTDeMwX5jlxbtoabRzFQhPQS/gwh/QaCF41Z3nz/177/u39fqNstMUpEdhG1moRd1mhd3g8zGK7vNnQQkhCdbqzVQMspsm1st5ZIt1QVkfFwcKAsxTswG4hoeOGuT+pSD7CmL1474Dj97D3v7e2q0LKWVQnph0TgWsZEEEpGxMmM3BNIHA4GNQmVAfEgmyBAm46wQgxN7O592IgfCAxbBS1ihiQw5YRx4OMdH++lmqur+TLN16pJdZ28Pp3eWMBq1wiLaXugo9SylJlQCZZJE9ubu+MPf3B/f3f84Q++IuZazMzLUqOsuDv6lFut6h7uMaitshdM23hq1VrVzSJRBLm6+1KrhSjtULi2xGdO5GTImd+9GcYD3b19Nxzk7fdene4Pd6OcBqnLMp2nx/ePv/Xf/qgs5TDcDTn//Ho/1cMCK+6R1GetpwVAVT++/wA31dldI/5RJGXJ1KMlo0BW+AM5UGp1TKVUlvD9pFITE/VKHMGMIzVwFNbqwEv8t9VwAwBhiSQwq2rlQPcdadLoKnipV9MS1VEFlEejlI6OXOvhdDL3w+kEphf0wc3gdhqHw8CHJAM3ACgELRAjCRB+3hAA8EjlHgqAMC2q0l1PQgiuANVymedpKfeH093p9ZiPZTYyH/LIxEMSERozxgQBwWCVygxJnCSTNDIZbLFzc0OECHrdnEAs/pWANF3VfY907rX28F1hATW1qaNbQQE8cNGqRhLaV5TeIfNIcNOr74J6yR0DWZTBBdydw1VqC9WQrkvBk8iQMsPZa5R+GZI07MDMoISWYyF+ndJQ1ZZSy1Iul8lBpZSQ56o5g0ir78Al9AHd0sSbRp1ctb9W+dlbXE1iFqYeApIi+Voc/rLMqj7P1QzqbB4J7xwU2KNJlbhpkkAjun4UWhrIQBbZPTr8TkzMvA+A/nbdFE7sJGCDZJCtEkAkEgKTc5Rmg7tHhWNqdew4OBlxAhGx90PYCo2Hys5COSdmSskBn0upajCrRSPjfQPHqG1OE3RcuwXCRaqDgNkiB78aHD0n9UqiW1qolyNd1fObT27lsZsJ2aPKG7Unf7Hw3hY5Dr7EiEVALDmxJAiRxnog4vgiwnxt0TE3cPhKtcUDuhtzjYxv7Xg4EZZ5S1lD1GI3zJRbyOLKWVrsfoy6+5Xt+Xq7SYOjmvXSVpe+9bfodgh0RhigvEED5K1VEUIe4KjWz0IS4ZRC4IgNS1sfQmqim/l2j+LTa2OCEBnHSW8DabQDXWM1j/rR7OHzYdRpC8EjHKnrMH2TrOjHup7eJr0Trcjt0bl75GD2MNtDmJLAANUuTrlHor7uVEHAWvzPO/rzwqrXMtRJ6OvihwGHEccDHY98ukvMXJaq5iJktfkrmLEbVEkr3GFObogilrVgNXZrpapkwcndokzBUFmNKsTA1VGNEkMcQ+bDIOPAh0MaRz4dh+EgxzGNWVoiqaLTdZqu8zwtWu1wyEyDXblYiPvrqG+0drWW1bCXY6Qk6qnlSYyYWGZhifomNYwIIiYWJRZ5fSViFmciAZzR8F10IK2jKysJ3ulYWxFt3zXVLcVlqaWUxcyqqapFzpSWlcLbATGzfeAisO4HjxpuIsSAOIIag4myrFaCKFJIHXigJm56RFFTO3fkpogCGSHdO5lhWRReCUk4jKRcCzhs+TNJQiJmsJtEbUG0BP3u6ohiZhp4dFkqSqWqrZZPoJ7YxTWs7YUbnbBkM1pKBYxDkuMcBxYA0SDi3gJNh5QP43C4f/UqJTkcRmYex5GFD9zUmYAsqQMZ7q5WAYxjFhYK/N7NTZk8kcMUOjPhfuAslMihxbySt2py4Z9fzfI4FvOH8/z8/Pyzj4+Av//4qZRSG9swZyrTTWl6M9O6JZTdRh0QBDpVbkRVYeSAEjFBxFPi73/17jim169fjcOQh5wEl8t0Pl+maXp4eFiKPl2Lg4/3byS34MbqziIDsaQh4LIxy/3xkIWX6TKbztdLWZaUB8lZiy7XUhxFWR05D5Ic06S9doh5y8ksPZJk26Uy+HDi7ENHjwO/IoATE8E5UhNGmr0AMZ003JOZiDmPBHYRNhsyJ4nSpw0IEubjmEXoMCZiTDNXnZ/OT5fzJfaNCB+GA5nTrG7h1oHz0zP4QixEkoY8jIda9XpdSq1TRTFWHpElMqq3gDMSJmGZ9qdbhEVko+Krlf12Ldfl3otB1vi6UZc9RVJiQsfeHUgp3Z2OwiyRUmgYSVI+HNMwJFiCMVGWyJBD3iXdlZ96VB3RnuoOHo6HSdjNf+fnP/vw4dP58ny5PBOBxX/6k5+UUnpXA7FWv8lh2hXRZgjvuPM69lsSHK+1FHfnXZR0gyWJmNipSwyNdhKD5uk6Xa5Pz8/fvP9wd3//e3/f7yOih08fI8kMEb19+/b+VfItnnsV97uwhB13j4ne2fyI6HQYX9+dghyvokUgGELE7rwUgiczdkvhoYMklMy8VDfXorPDIOGXnIhYhswp9XC55n7VowkiTVAI6h7lEwP3zIpBkNWy6UHomEVVixbSymrSbRNkJfDmNvM9mQkAh8vmbg+HV52LXg8jDhk//Hr8+t14OPH9q3I8zafjlYgSq6mRLZU1hBEJW2pYdIklZTjaEQdHlvuOw0Skt5rWej67G9HBQUpJIcVQDLX6XDwLn0bJiV6/SpIojURCXrScJyS2xB9//vF3/uGPrs+XMimRDMPJeHyu9v4yaWVTsCmbum5MIoT1lJPqVFUvl2lZ5qZwE4skYk45s8g4RJhuFkk5pZRySpLzICLDMDJzxAOnnIU5VJuURCKkM3HbED0HUUhQZoaOWUZCMzMrpVgPQVrmJbh35H1byqxmpRY1X5ZibqVGaBg58PbtUt9gmuZtZ8IFmlBQ3MAOAUREchKOTiYZDofmVgJYbWVnVuK6wkVR4zIqNVtRAKXM1cq0LJ+er4uR5I85p8M4CMtxTGECYMbxOJyOw+nu8O6rOxuS8EESERYS1Tq5LvU66bws8zxdp6fn5Sc/fSrKcz06JA8HYq6KUhvE9kLBuXGjawAgIhAlsEtmFrgT2IHIfBq5eyTllIaUh2E4pCR5GCRWmplTJhEJI+7qMmRwd9VKhOPhkJKEBhO+lOTKXmGqRQUe4fxB7KKuuRMZBWygZk4sxFzUpqVM81Rrna5TQ+NW896aWLvT0L3AvfPqcA9faW9q3XaQO/wZSfeO43g6DnfH4zgOQdpNdZnn6+Xy+PCwFH28Vic2znnQPHiS1PLOcJQBppwlvD8ALMtitczzVJdijgyqpZayVCe1ZCBmCdO+7QcSnezmxt142DltylSUzLTmZxz6Udgtw5YVScmIjBwE5sCfQezicaSTwBRWw2YSAZ2JOafETKopmGatyhL1CkmY4W5WV3OiqhsiF5Bkc5A0rEVVI9UXSwDhIUYSQFEZgW8TIoOYuw3+BpTvc7KqjW2td+9bDfLGWtyb0kbrrgCS0GEcWtAqE40HSmk8ntIwZsJALkzhHhgZYGrtBea9Fb9Bq00a7x1Qb/k4fZ6n5+enh4ePDw+for7Up0+f9q7IXQ+56fbWfTQpZBVbvjRGePNSNMaaMDFQGCaiyJtivP0wXINrqVH15enpiYhUlYnmZVnm2QEiqrX28j6+e3q8D+a5x7q2KW3rQ0giOckOpaD+X0rB39QInkzZTczZXYiEWNVtUbhanQ3uAhBRMiIhYRbZJoxWfYo89KuwfbsiyigwBfGO6BJ2CJNw2Ie1GfDdGW5uYfiPELY1hGId203YjXfNhEkEh4FfnVIebUiapTIWAjEUZISZXAPuEIpyHCmMrSkzADXrSfF7wtSGBrkbW8UChkFSArFyNsjSWftSIUyHxEnoeJAw4AKAmsLJDcbXy/zw8Fyuk1awRNZFKVrn6q4eqbfoMzOfsATS7u5LKdM0UZTrJo6jkoaBWVRNRGqpIpLzkHNNKakaNzs3qxkzDw5mTuYiZu7JwcIGobZVQSEUhoteKKTh52labQl2bubB4Odp6YUSLCLy1HQu1dxKqe5eG2JPDiyR/dRuhhcGVnjUwyAYIMQkwhRBbkOOclwAoERmHV3ooE5sibDvtW7v7KcRVDkv5XydU6mqJsJmOQvHliuLaSHC8PpVEk5WBwJMjQAtrqpl0TLXMutSbJrr82UulWcTUDpRZkZIL+Eb8kJvv9Hax8Px/tXrwPNEwm3KXGcAjMTE4+GUJEf9u5SHPA4p5fFwFOZhTEwkEcPPjZ8RbS5DBHKzUhZ3S8KRcD7spO6wWrXMZZnODx9gekgkjLvMWegw5jGJSBJJqlaqOsiInRPnkfJhfj5fr9OY5c3x1VH9WE1BBVxW+g0ALVwhVmNljF0FooaygJoV2hWmQ07HMQ9DfvfmfhzS997eDTkxkdZ6nZZ5qZfr9Hwpl1mvlRR5uL8DiclQwFqNraxSfzzm2aoQzg8g2OXxoZS5LsVqFRGWVNVLNRfBeJKU716/ZgkPzMB7vMG80fObdVzrugQfCM8uDg+2IKahS3lDO1utrQRLTEdhYR4yE5FKMtMhSRKOkxMiQjW9qgpzeOtcplLKMl+tzpRyEj7Uhc/Frdp8jiBJOHypVcMoEInP5MnMw/lRhkyc0jELsJRSaw24WUADUTos+/jobm3rysxGabuv+26lV8hudZxGwwNIhIT5q7dv7u9OtbTE7/M8Hw/D11+9zUPO40DCljNYkBKxnHI6DRmmXqoDR0JTKbbM7Y3hubtWnaZpWZZvvvl5KdWdVG2ZJ631erk+PDyollKn8/P5JspoKza0pSLpO7PhgA0o/lKLWTGzpSy11jLPWgoc7h42nTY/hA5iA0BiEeLr+XI9nx8eHx8+fXL389MTi8zX6zLP8zybWRahqG2aJO7T2elK3V72q+pNiFHU3NhQVgIRs3AGv4KI4bAUNq9kTl493CDMybToMpei9Xm6VNcaCZ9SIpY33/tePg5ada6FBTJAEqVTAtF8XkrRp4fneZoGIIPuj4ev7l8JUxYb1JNBnMRZIG6kxU2dIqlblFEwdbcWTEQMYl/LrYS5d515RMpnLFbFvdRzWWbUQvNUH6V8+imcVN3N57llmgZcOLEI0yh8xyJpHJ29+OzwnEfm1J1qVmdmM1XViQDOiRNxImKMRtlhxqrsZlYVRGow9Cznak64Xq/X6/Wbn73/zd9+D3MxIeKHnz5WT5+eZJqJXOBEqIQ67/J8icjbt2+/+uqr6Xo+z+fLZT4/X9cIi1YYUBIz52EQlmEYUpKUchKJGAgmlpwpgjuIcih+EsZ2aQVHwnzTxE3EMY9TEUilNckyql2HSN3OWpgDhNkJADuRJBYg5ZBLuaFDxHd394fDIee8kgkmOo5yd5AxUWKSODCqlWCuBmXmahVRTqkDVEPOKadQE5koMm5xJ7DeHeFSevf69el6Kc8P81z90yNzc6fn4CBPT5fpMr+5f/3Vq7e/9ms/ePP611zHJCcT10Wd7OPDZZoeyjzXZRmGNI4ndcxFzpfyk5//rJQ4Jnj37u3pdIy9qLfn8MaNLqc8Ho7hbZeExiyAuWUAiTKzvH79bhwPYCYWDs1ORFIOfZS55bxvlIm2VKAxzW5G5G7BWjxUPXDkUfGqpS7z5fnJTWtCIvKBszD5kcchJlejED0AYScmSSypqM9LOR7y8XhI6qJezKloTy2ArQ/Ma9E+QneQALR/EAe2BZS4JvAx03GU7706DUO6Px5EuKip2rKUy2Wa5jIXnYstRiDO4xEsIFGwWvPZXG2+ZlqXidyuqG769OnjMs9eNZyTqaWfJk45v/I8jKf7e05gglF77Zkmm7j0grv3vwNIbKlw12sCVKRmKXV2by7yRDmRMA2JiLg6OUsSTkwOqLectG5eqhpxkcxMZda51FrclFwYlsypqtfqlylK+7oDcymlVvREPyCYeSkqIvcpCwvnTMx1K/1FTCycJB1ewhIxh6u03EVG9x4Ou+rhvjFbrFb2MKUzifDpdHj96r4syzLPywKry5jT/d1pGIfhdCCRmpIRRTTB4TDeHQ9Wa7lOADZK1AnNhssRaqkp0eUCs1rrrIparWox01KWaZqWZbpen6P47zYub3Ss74Rmf8Rnqjm+jbsD7lZrJJK6lmUJP/kVkG/XBWLkoMhiTTxdrtO1tWEY5mkSkVpKKWW6Xmsp0/39dDikYUgYaJWIvevwwAvW7r0s3m5X+i57WpjpSJgH8Mklm58K2Hxmr2wzvMCjZEY4mpVS53mppkukl0yJRE5vDCQOq+pMAIMEdBAQ6kVnW57Oj88Pz0eSkTgrMN5xYmGIOzuxgxHh1wjZNfoeWju7WeRId4DF3do22uLTthbFlBVWW5aohTDzfFFgAeAUXnJlaV50IGcW5iR8FFZOko6jky90dfJxPIrksFS0HC8xqWZqhZldHALKIIkM9eTGblyrz9p9t41CsI5Q3aeny8dPD+8/PL7/dBaS03gH0HSdqtJ1GmtN1FxEFN1Zsi0V8/F4uru7J+JatSx1ngszSQiLzAAxV2auVZlZVVNKIiXJGtYRfletvlHA8tSFg8isQKsMu1l6toMRrD3g2H5J7KmmpuZhGPJATCICchZGq4hKknKEWUZmupSy7LxAiDAkHrMkbqYeAjziS8CuRkbhRR4CfBwkidwPQamj/jdRSgLAoE4mQkRISU6nwyd+fnq8VPXLdGbmUqPUUwLom28+PT2ez69qmfhweF2WlDjrkl0cKmb09Kk8X65R5OnVvQzjYFiq8jzbhw9P12mZywRAUs7DQM2c8S2sHQSRnPNoZKaWEueo1pqOFMg8NcsKpywpUTD4KKtLFLUQ3HllmyvzCb1GoyKiFzOt8+ymLdM8jGB1mebrebqen5+fYIZDNuFDPoQcWjTAMjKHomNW4NPdvROPY9Zavv/Vm3dv7s/X+fF8vUzzNx8fbZn72QDCeYeo58ZApMrsgT7hKMWNEpGPOR/yeH86fP3Vm3HIr1/fh4GIona1oygtimux56nMxasLSAhCkADtVoTVI2qHwl7U0Gc3N0oubl6tlV5tLNmJzaDmpVRrRjjsEZdwFv7McaJrRC/01xuNtrk/wJyskitpScNwd0hCbFpd3bW6GfMgLBCmnLXWea5urtWYiLkwcyluysKHcUwio/ABYCMx1Mq1Wl10Mbe51KolvPQkSR4ygyIEQw4nkUSDRHCecAoveWYRyXwTQAVt+d3MzLnzqvUlZq/9RRTR2tIUVnevREgCYqRMIqSE2b3UUpZJVZnh5JdaCmNZwMLsA0vUbyRyddOllKfrxQGWxERDSgEah48YM0VEslavtdRaIrNHqabqp7s7cKaUDvevlmW6XM6fPn18fDqblc4ePHJBNeq2QyZ2Zpi2ou3PbU29+z8ZJ06UhjFzFDyIkJpeRGclnBZwZzUHzArIhyyvX5+OhzRfnoRJlmmsVWuRWurz0xk+3N+Pr173HrSyfi1AtKf2a/uMoLdlKqg7Y/Y/KSc5Ho+ZSDhB9UwzakQYethDNA01jzD3V4eB6dfyDwGfl0WrPp/P81J4yNEDdspEd8I58R3D3J6Xma7T9PD4/PFRZdCUj2koUcrokNI4pNNBTkPNuXCyYYQeLF+KBLzmSfgwHNzs6XKtpo6w5veRtJO3NXOqTpfFF9VvHp3M74b6eoQQkiB8JcyhrEbewvGcXUXVzC4gup4vRj77bHDmx+ZN0w6ChEGtqp0vxQBL2Zk9sbfsBJHIJMGIlJl5PByYqaqr2XW+zmV5eHp4eHqYp3KZlOCXZXbQXEiNZmdrK8holu1taO6+LGWZSx7Gu/tXKQ9v3ryNKr+d4jQuHWGZ4ziuXqZxFB1rti0CYS1lic6oI3LPvZWxWDdM3/OBV9TY4xRdbYJCI+fjeBjGMVh4J49ddGjWNSLinLOw0B4IBBIjR+LJVj0hzp7HRBAoPLNMYYbn8+OylNjMd3d3r1+/ZpaUKqilXVZbHHY4DCml0A2Z+HgcvFfdcqvqqKoOFKtKPtv8tDx/uHz6ycefHI+nd6jCMH9UnX7+/vnpfLlcLtM03d9d3nw612opjTmbmWottRSHR7BFHGy95e03tnaRlNJgZEY1JUk5pyR3x0wUzldIKTNLzllyjkIaMcUBZWNTYLDj63EAWupTs6Km03TWWmJDMbkQyjLN1+fr5fz89AR3wdFzstMIYnVUgysimVozOxuc6HS6S3kYv/5eTvy7fvDV97/35tPj88/ff/r08GTqy+W8aq0h2XMDTzyURDhbFJ9bk24AgW2PQ351N759ff/DH3ydkxzHgQg1LF/F1L0aimEqdp5rUSueCMIQaiIOthiohjVRqHnN4cJcOblEqV3temdIFhJxestSpOes2CPRHQ/dUxjQZ3wdXefzjQs6N9ZuUCUr0CVlvh8TEV3Ok6pCq5uTM0NImCktZm6mastcASIUZtaWMCqxgDkTD4AwJ0WpfFnIL2pV61KLamWQEA/MaWDmlPnAnNLhRCzI7ASBuFR2sEMkcRp4GPc29fCUacOxDgnSCrZQryBCIDgDBhbKWQAzI4KnBCZPmVhYCbN5qbUuM+DM5OTXWhbyLCbCB/LkkjiJJDI106Uuj9PVHSxJmO+PxxxGFOIoumxQc6XFVYtGQriomW5+vLsb716Nd/evv/p6nufL5TL8+Lf/wX/z/6mlAAjipmt0+Ao33LYVBt/zyHVvBFDBLMzsQ5bA+NVaCrA1aigkna10rJtVIs+DvH51zCLL5VFAsiirjqVIKfX56VwWhdPhgGZY9JaJNnrRhHtekX/dRX73BdpTbR+y3J0OwpQGsVov6lp0KCbmSUQ4lXGw8GBizuP4g+9/LUTzZapL+fGPfgdPTzzkmAgGZaL7JFnonqHmaZlxvc6PT+ePn2w4+HC8G4+1qo2eDjkdh3Q6yHHQlBdOmge3g+ZcmdiRzEX47jCY2XWezBHh9kQhyPRaNLsWnPu6OMgSqi769s4zUxI/cKg07nDjcLEUGFngZdVQJjWblqrus1V1qz6bR2IDZ5GUUli1F/Vvnuus9qxWmy8fCbEwZ0+jD8LpIEdJ6Xh3T8xzUVX98PDhfD0/nh+ezo85j4fxjuDAbEZLgTvxOITHQa+EcKv5eSSYK0Me7+5evX79hojCHwUObwhTwPKyZ+1dmoRZM3vHzVcpM8LfIzdp0ah24WvkVAdbHfCqqrV2uJ6iiEf45ItkERmPx3EchUUkrak/9thADCVUUP6ctUvwoAAWe97AJnW07AFuomofPz49PT0ty1LK8sMf/CClQ8jNq7duqVd3vX/16nA4hC8wER0Oo5tbdTOvVU29GtS9WjX2yRaan46Xw08+/fRuuYOwMGl5qnX+5v3z4/P54fHp+Xy5O6ZXd8PxcHjz+m1K6pE6oBSHt/RUzST3bVp7VFbWSPyUWSLtDOa5OLwuxR3MiUju7u8POHFKknLAa42qrjIPRaHcSMbcM9jr4qZRd7LW2bQSkRM1p0Rk8gPcX716Dfj96ZBzOhxOw5BZ2Igp0it7z35BbuaBhAwDJ2HVermcyzITfMjpzetXz8+PstPasbn9eCczEeiFBIoELcR0fzgchvz29f27N/eHcRgPR8CvS1Wz6zKr2vk6L1Wfr/N1Lpe5zNXUIhKCSlWQtbxd5g6Ev37H20Pd8VYpndgFBGKxllgwsr0yUx5YRB2rFN2KLne7MhH7i1TdkTOu7+YbHh92ZxgASZRFhDMbkRJVS8y1FgbgSlh9iMxM4SCwO0qNyOzMxCyZmZ1grp26hcejFtO5lKkuRZdK1VhlFAFJYhHKQ85jYkpCiUiiWot5OM+QszTLReghNzJKQ6sBOJw5dW1mBeWbJtqi5piYZTweXt3fuWkpE2AMJUI+jCyi8HOZ2ZWEg40u8PfXCy1ysiGnNI7HnAcyQG06X6fn87SU+Xwl5nFkCOlSwOqgpdEFN1cznedlWUo1zePgREaF1BRCzjnn8UAsiVgOh9NnzgHYudH59n7HSjrVbQnL4itzw3qxG9yul/MyXUzVVJs7c0RS0lbsnAmUmIhyYndXzYdjFuJDGhjgpG7uBK61iihTcZtbZntbZUwiBpydyEAcSTmImF6EGPVUGzEGAGRarRYaRHPSzLPlWlFLZbMsKbH4kGhMmeTEKQ+ZRyLmhEQZr766zyMfeBgkm7CJjMnvCQk4Fq2mB/cZOOZ0GIdhHGUYMEghq+TMlITHYRiHEc5awZAswyDDIQ0eHpHs6DLitj7tDb2QuUJUMneHwGkq/IhqZqaW2ccchpUm6qML1q+P4+vTkd0zvBbl56mq2ULUrI0VnOAOYgMT2F0WtfOiU6VPsy5mIXBEHcID1TupicVSIZZLnRw0LVrVni7naZnm4kaDIldPQe2cog5QBCE2q1Yf4Y1uQMQiMgwDdfN6/Av/QcLG2sOgHlnnbrBS5j1rJ6KWlrg/waypPe4xS+j5BkNa5RbZ0o3x60N72GnfVo0r9/Pz4jVwib3vKqIafVBuUGSMCdtLDyoPOBXO5ogSLAGC/n/b+7cmSZIcXQwEoGpm7hGZWdXdNRdySOEDhf//56zsPixld8+Zme66ZGREuLuZKoBvH6CqZu6R1WeEwiM8PFLW1ZER7nZTVSju+ADmyLx3c+6VrkSJiF++vru/5TzlNMV8unndipldrzczV4c51lKLmtZ1vV054fy8fH7+7JiWeT4viXihdJZp28pvr99ebu/0+oIff/jh6elZhH/8059OT8+lbiCcTydhrmY1+isejmO/dqhZqXXO8zTNUbnrru/r1Uxvt6ubAUwkf/F/+IFpOZ3zNIVXnokDaidK+Qzm8GqlWjVTtequrjeCUzTRqq3rlyTJ6fx0mmySeZJlnrMQEz2d5pzS03mZspiamymLgd2h6mF2A1jmeZ6wTJyEa9levl7VIKDzPP3TP/xU1lu6K7FFt5HQms1RQ6kO0RvOpH/+p5/+9MOP//DTn//hH35SrdEc87dff9m27bdv37ZS36+3UlTBRrRVuxUjYnCCU1kL9fyPuGe0deGeahGaaSQzuSSwSJbUFfCI5bS27kTVQG5xqXd0U2kNtqJP6MF11YO1R12186XGgogwyXzK6ZTSkgh1s42SUF2vzS5hMBlHSw9m5iSSzWnblDgt0ymlJNNJROCJYB0citRQVa+lbrq9l/fq6qSccDrN85RT5pwkT2meJqJEPhEl5xTl9XAyTt7miCFiIn4YHRFFc6O2rYWmHGAPPe6OaI/bkMokZUnp+cuX/+Gf/wfTer2+uavVlQnz01ly+npbL9e3Z6bnKTkxMd0c//7ylUR+XM/nef7pTz+dT896W62Ut9fX128vBlJwnublh4USb7oxE0x3ZAI3dzP3UqsZlufnfDJc16rGxgaaJctEbn5+8tdvr0cDwgM584AJE0t55LmDFba4477mu+R3V5i9fPvt+vZiqqY1spZSStFRY55nEZnylJLM85KnLCwiiULtdWJDRLoJSM9LNX0rdavVofV2iQSDYOJhPA0dk62ncYCPDJSICEawxn/DNDTRumKa+emkKV0+LwWJa2X3WfLEsuTpNC0L5yc5SUr5SYh5Ps3keVr+gu0HXo2LiSFVTKTPWBN82mo1+2zkLJ+XpXx6ktM5LWc+Tze2hS1lmqf0fH4+n07sZIWE5iXR03T+vJyL14uviMQWFrQWrTj68z7mt3gkmGMC8+uml9W+kv77rzUJL5mFSUKJFWemOfkkmM7P//zjj6fsnxYra5n+5qW4v3JVqiQEG/7OUOLgsqq9rHop/rdXXVXV1eGZU2L5MvOfF54SnSdx0M1IHZfN1VAMCsppzulZKbPPzJJk4pYVJSxTT6b/7sEBpvn09OTwPeTSOBkowItoF+1DqA8Do/cnugNGjD9FRMSJ2MypVfwFQkandGocLYpNw6QJvJsU7c+HDhF5Kgc1ZTylP11Y3I7CL9rH1ooWJkBgGkR9Quv8FkOk0V3NQMQpEbNRpL8aE/VA7wT3f/3PP3/97eV0Oi3L8vz89Oc//2ha1+trLeXXX7/W6MborQFwVSpKv7399vXy+uXzj5eNPz1//p//5X9elpnnL9n8cv1//+2v/17LrayX/+lf/uWf/vGfUp7/5V/+R3dyCrgDBkit3rZS9XdEewtYMru7ajVFLQCslqu7a1UAkiaRRMS+Jy62AI27EeDkAMyrw9WruXYIbmvx98CYnCYGUmsBSRTAZzkDPs8LE/I0Z2mFryyBMyIt2NxCX+P/qGpGKKgMdSd3RhTN3qfRjdA3Ra159GhJAf7H1Dtr5WiLBbfIZt7KVqs6FFQURX0tthU1kBFXQ3Q4DiDyVjoMB1GSNALB1O0cCuYngYEjTEjRkyd8qU3hDdx/bjp000gDg5qIiEHywWeLey31SMHBIhJBGImRmLLwLIwpEy8iFPpUQzBDNXUiMndmtsgriVBGyiQpiKm6uWsNWElDVaj5WrW6tgwlTsTIOTW/dmYmNjN3WF2JMufEknlKDb19OH46FNP9IBBor2ByEzMLiA1mTpICeYAISALh6TRP8zKfzzJNEJ78BDefEhPyaeEks0OZZ2AiVEcxq8DmTkTKYil5Si5ZQWq+lXK5XJzFOU2gagoOiAlSHc14jAhMMEd0WlRTNdeorApe1zLIJKXez62vHHrkZk8V+r5o566iDel+N0WmZlrLVtZ1M62myk22S5QnmbmI2GQiyRxZ8+CPzoExzAyCqrtvtaiZNsh77h4S6TkrvXfb3RYb4nu82x5Eat5OIsDNlFRE1QhGZtEpmx2JTJJl1bSJeIqSSgk7DyyERUlcxGWCGCUjB2dIInLKpqSnZJ7kPGWd59NpOp2WKUu0IBJOSaZ5yvPsXrw6BIH/OXEyiTkgRQPoQce1bVxnYBA/HOEJRHjam9RLTb6QILKmhRjmVNmv1a9qJFgCA0HEGdWxKt6r38w5wih9Gp3oVrl6dmJOS6IMVwYSSw7bRKi5vgKPlBApdmGhpzSlNIsklok56v6bE3q3DT42zTvuvO98vP/Lfd2PKt0Q4XeTdPgE/aCD6jQmeRQVjW96oAqHf0e9KTpd7Te/HwCi1vdhBOYRCIsoEkQQ4OntVVrrsMCvpDyleZlj5NM8YfT8bTCR1NQwNzMFXCTA5BMyJX7SeVI1rXUrxcwh0VyHqtF8kiSVfL1evsLLb1+XZZ7r9qp6ezqln/7yyepUy/zjj88BpKnm7iSRzuse8XtV+91+7cScRKaUaqnrdqm1rLdLVG6LyOl8yjl/eT4vy5JyUrNsalpdRODuvpZi7gEJ5F67/gVpRdPISYQlIOamJEJktbhZSgICiyyBdMBChElSeM+EmciZ3UZvm+aGcSJyMzMt28201rqqFuHEkub59PnLDyzpwD/DZ2yR0jBP049fPi/z/PnzkzALA+7bejPTKcF0u17fWXjdyuv7uznUqLhclW4FL9dy2zbz2EXiCIrYWVjYv8uSU6SxetebhCUJMXFuf3Eg3LHklKaUw0wmZuZErYkEtKqb2baZOzdUB7C73m+5qJ/quupwyDMRGMaEnCgzLYyF6Zzkec4pzfP0SYQCgizk7uX9VovetlpKNZi71erOc0qTLE8sUt3d7Vq2quu63baylqrrVhDiQSSf8iRTAIcsU46Ay5RSKeX2divV396UJJ8//TlN8/nzc5bc/BA9YaBnDR5G1+LRoU8Gfh9AyDnNz5+SCC8TMXkWJP70w5+ev3w5L0/y9MzufDozIYeyJARCOl8/a5mqTWpv6/b1/XIxf6lV8vR5OS2npzqdSl5uftlK/fr69te//Y0k8bwsp3M+n+d5/nReiPi2rXXb4A63aL9rbrdaVe2y3tS9amRyJEarjuUemz4KCO/71bt6NSTk4GztAyC6c4RxM5yfoZHfbmst28u317eXb6oa1VChL+acRNK8zCISNUg5Tyk15u5ExhStGcixXW5uLSN9eXqazqccnTKYJUtPUyLm6Ep3519pGv8dVw7Uo/gDALnVbYVQxYU1o/J7Za1SncEp85KQiqcrS+I0kzAC+j6LEE9zSuDJJTknp+Q8gZ6QEtITLVbtDdvlnQSfzuf8w+nzl9NnNuVaZ/g8ybJMz5+/LKfT6/uvpW4pW0qUPT/nBYneUzahi6m7VyLlJja573AQ8Sj7iW0Hd3IggcgwGbFFPxujzTw01d7VAYlXYT+/1dO3y+cze2KYb1k2xdeyvV/t//Oyva46LUuepnD0IzzHoA0nYz5/+rRQCCRP4OT0lGnOJEyUQtCQgOcsmQiUaPdbBw0NtrBnQRzJ7ZGpWEPFdbeIc2O8VtSGCDE1P42ZHa12kej5eWwl551CEKF2+N1W3x++a4cHId3NMzjADY45DmIKjP2uaxA1fIzvKywx0qK2bgbqfaeYmRBtTfKUmag1HGYl4vPzaTkvOYmklFMyr9TTSFNmYYapkREpSKdZnp6Xz1/OP/7pOSeZ0p8JKKua2tvrSykrUIBKIpTE3KtV+MvPf/1/COdf/vq/5zz96Qc5n/hf/vn0v/0v/2sSTiKOVB1bWV9eizmez0tKUszNcVu3y20rpR6ZRr4bKxAmeylbraXWSoTwgOQ8T9M0Lcu0LCllYo7lYffAly5a3F2t9roRF2mZa4HWkg+967MIM7kJuYHICanpeSwpMdCZHw86CU9BLLkD5OaBPaS6bkW1lG2rdRPJKWXi1JzFd4sZmiMivziikFFbzzBw26daCxFYGMTrVt4vFxA5pKoVtWoefV+cyNHMTQ6hERulG9uR6RGGCjNHDF1yYo6gTANhThIta1PHSB/ijWIE4dJXM7MQnURM0rbTA91/R70malUwLf+cIOQMhllUmrO0DE/3gPNsIG1ObL3LXxhu6mB4tepuWy1Vy6alRGE+aWSzcU9WlNBcwvHkDCZXaLVavJTKgqwKkY6RsQOooYUQcBzd+Jh3C19YqAFfpRRGiGdBkjRlyQnMtXsDB7wsuxNBJM3TPJFlMhEbnXWCPpy5AsWtwpVICQonMMEZtnmF8WIZwrEucIM7iyT3AEMMY9fMPSa1QZ32NJ2dsPvYmhPMvbVsuzNlxvq2hIJWDtTdHARiYpA71KyqBqR082kEqx+yBYzWbKU1wQunjDkqLOdpyjOBqpmpVjcQZmLhtBvqAXfbkz4/ssvvWXl3S9nbiDi7kxsbEnlmh5ixsxIlN7jDKAnYgSjHb6owKCcSa7VhlJwrweEJDRpmXaTU5OfMPstpTsuECqCYoLBv4is7sd9gm9vkltg3WIEXeHEDiCscUMCJQtE+kOd39heaY6wtbrf1MfpFD3BjgIToVvztpgDPmclcN98KborVsClvJm5ivIeoQQRip0RMoRYKO4CEAQVN6A2i+iSHN1KIdmwPYLwK0+gg1ccwttnD2IZHHbsS0H9ycNM7E7m/wB4E//DhbrL3ew5rm2ivkzx6DNq7037RMYU0vCboDxzOoQPV4ZFeQaSG2sp00YtEow0Py+42bR6X2AbRaK7p0+G0H/djIqZoVD/P0zwHZ+KUWnVcomRurqcpM1MimgJ4s5quZTOjbTUCYDenKrzklJ7O04+fckqSU7pteHkzDRhdp9kMRGpmjgbcc7/z8nGoZVuvl7fL++Xy/s7MKaXltPz001/maX7+/GWaptPplHPq3Q38cnlH2MLkmysRck4svMwtf3ESSUmmlJhpNBoJ32FrmWSRYmMuTWkM2QanaCYOZm4pk5XQmvyp6vVyUdXL5b3Wuq43Vb3dblvZcsrzNH/+/DmfPt82PdoPgJMbGEzkVrUWhl2ZiFzL1Uy39WZaq8HgnCZJszqKOZEgTQ66FVOHk6RpaTC5DTFdDpkdFOl40zSlJCGXOXRCkTQlMCERcaRDDkcOoiNeaMhVN3O/3W5qdr1eo5NSrEiAP0jiUuud38ndzEbmeMQ5AGdytsKMPKVZJKGKealuwHKaiZ5SliwT4NfrTVXXtap6JBiVrbxdVneqyqD6bX0BfK03cy36br45VZDlKZ2fJpaU0gSwmRBxpszEbAKjsmKzWja9XbyobtdKyWW+GWzRU0xDow4woj703rkURBJYGPM8L8syzfPT83NKaVlOLKxuTqApIwlNadOyVn25XQDADHCrBcAknIX/6U8//PD0lGdOTjcX/+0VSpMSM7lBzX67vBdTtkIT62niz8/KtOW0Zirb+6R5szKz+O2G2vLNS7W1aFW9bjd3VzOHa1V3J8/kDE/wMHDlwWFoWrWUQPCL7iQ8opWDQdKImHjjqkE61ibKzN4vt7KtCuFpWVLDuk/NG5RYZJrmQAvhntW8bVvdttu2vr6/Pp3PP/7wF2G+pQJwkkxEp+Xp6fwppSxpGsZfCByLf+6LweINHz4hGO4yOx1Qdp6rk2FyOGClwAsvTJnWiW8z18y3OUEoOlwG3k6RBAnEqgRmbypHYpIsCzJvP5I9TX76hM1rOpd88o3qpejZPb89Ea7bzzOdbv5uVLPXBP2tvP66vVyv68vra4DkC/MpLyIyiyQSgMh6+elxcFHv4pUocsoSUyJ2zsG6QgOLpq4EgoEV6a8v+nrVJeHTDCZigzteb1RNVnnGzCo5BDk34P0QbNGxSagVsFJ2SkYAFQ1PXQolYEc6b/qGd4C39soHUcoUpaJDhN4xTKipqqKNGdRD4rTX7fTAA9HorBB0Ep4kIqMuZRvJHo4O7d5LiTAcs0Pk32kfDrC7qokAzNLN9hEYilxO5+G/HKyFmeiY4OmOy2Z8UyZnYMqtNK+F1sMFyeGlia6QMbsMopl5as4QJmKv7pFOLumnf/zphz/9eF6W02mZ5+xuCodXZp4kTYn//OdPTM/LLHOvulMrRa9mqFuzi5n5H/4yPT9Nf/ry9Pl5iXz7X7/d/vWXl/dVv61VnYwppajwwq2UUqv9XhodUWtJFKn1IUNyzqfTeV6W8/kpTTnS61RLLIKqRtjM4cZGzJkiYZJzkikH8ok0QCsiIkSVtls0DTFVa/496sItAi6NARABke7WVH2zaoG4tVWt19tata63VVVv67Zt25TdnPJctlJqrbtG2TW+6E/T7mVUtRK8bltEKVXrWrWqgRNxdpASEyekCSQ1UFmj/400iU6cuMMSNeKIWs8pS+ryPihdRLKA2QXDaidvHVRbrVzg4dSiauu6NgQS1ZxzZPsLM4QRVvtBSet6LLcWWTw0V0QBTqD6NmD8SO4TnlVBiS05vNToY2JmMLCDAxTWwWoCkHp1+FpXc1XfHIXESDynlOckKeWU3RkrWniRJMoRrLpV1821IHoUMcHcpIEBOzVFJF6XP3ogYsvlJM29P+Vlnk+nU0ppmiciJiMHaMqcpDCbmbrWwBNxh1spG8FnkSzi9jm1VF9mif5SEUBoe6DUuibJcIkIbU5gssTGgFWFb9H8UI2sdYJwkIHUNNpLo9c+ujuZRUOMDrfzaNj6sNqHaOfAnxsaT/dW7RwaI9Eo/mreAjNi5pTTNE1TYDFMjQhFpjyzjAZcSZilGnF1UK2qs7OkUE6jYoNbdXUObeAYmu2b6vFoltaj7dcgfpsdyy24wOYCiIEArwZ1dlAGKbkTZ1YkdHTk6KQb6RSeWq6lJQYLUiZOrZw1sstPiUU8iQsrkSrR5G9UKrazXifxYptDs9VE+qLri65rXd/LCoeAEqd0nqbeE4T6oPhx6agbjb1fW3iGORCPDtDeTNQap8parFafBFtyJspMDlqVDWyUSFKA4dLe56Z1s2sOvdbahwJGwxCNY8Qj+ro35aNuvra2OnsC+R3juCPI71JmDOQokuNPPqzzUNyGK/57hIGjdN+J5cOdD1ccd8s4wTHaXR+v7eZ8kNkoTQoxT3gcdzVUA0fhuQRnJWciQUCtkxCBIO362ILc3IfERNJr98HRNYXnZZmmeZ7yNOWUAsfQySASbVx5TjkJnZdpnltPaPM0G9xcc6QiGTM9n9LTKZ2WtMyZGjS2VPMaKWxGm1ruCNDWGuHcTfp9U9fT+fnTl+V0/vzjjwEVOE1TXk6cUqmVzWopI+EpnMSAuZkk+fL0LEmmeZKOlx4+C4N7dbh5qWa2rrfItw+3v1n94Ycfvnz5vExT4mVEIINOzN2IXCvcL+vlcn0rpVxuV1Mr22Zmt1uz17XWYHxV1d2r6vvl8vLbr6XUA2kZXJ1IGGXb3r55nrLriZk43G8MFkrCnqQlsoHYI0UuqjISiBJL9MgLv7M0n/aAXqJmhoZSlziLBKt317KaExQOclMHXEvtPlxrSWnupWjgDw9yl+WU5zmEg0SN/uPe2XeqMHL08nUjN6srwW5uKlRYEnHkts+3+bJdU8rz6eTA7bapeSmq5tVgjsttfb/enMghRNFS28GVEpazSDpNWXKWeZ5PpyU0OlW81U3VXQFXU3eDFrVqqlaLGSilzDn1DjceyZccFkoAy9NdAg4zf/n86ae//ClHU8mUpjxJThQZGLcaQhSEiSFTNi1rrU6kDRuciDBFWYuqM1/eLmIEEYi8u+bPz2fVn9bkAL2+1ssFp4lTyzRyJiWq5ltVIq60Kqfy5JIyq5JHsig46mPczOBmtRR301rcQTwx51bdQ0wkDaF2Z6Bmpg7y1rb74MNsJ+wpdYOH+gg3EAByi5Bhmk9PKU/zNM3TJCmlnLmnNUXRYE81TsI8EbmwM1fX0/nMKZFIXhaOdpZEMmUEIH6zCbrV3l29g1mPn0frodmcoMZwuedTMLv57XpjBqMQGRQE4mJcPW306cZg+kGMGSmkSGJiKsLOVIVVyEUsiScuWSCskkECFYBRCUZi1xuLuZWpEKe3ouLXn//VJeUIqYlXgb78+29f//1Xv1Z/2RLJk8xTzk95SgQDMzt5j7QT88Fsb7ZrGHlExE4SxDyAn5tQCOpzMIGckpJUohr6LwFE1nIje0pciJLuuuEekeaj2zkq6hiQwO+zwTn7EkUofcAFgo7SfaiIBxZ5zHEBUErZtjVOHR0WevEbgq0R0TRNR58MM6MBubvqoNJGHi1jtItZP3r8m6IxBHYrfyfs+RvMLJ6IImLRTDQ3N7YhPsJhfoy1pwRJ6T7wj8takNcp0OhAlJNIJFND1cNUI24rLsIilAQkFDyNmJkSiKszMUtoAwATqUMMLAwXahFYIk7M5HByrJvWSg5xEkmc5zML8lQZlE8iTPOcmPn9Uq7XEgnQv3y7vXx9v2x+XT2abqam/qGqee/NOI77NLo8zcsyL0ubwahqzBMzqRu7KYGJcpaUxN0Cf5TgTLJMc8ppmnMkvUe/u6FTuFpd13Cha63vl9daa62bquacz+dTYsY8j/Xm3cyOYLPe1vXtct229e39zXvzn23bTPW2rVo16sdCfdlK+fbt2+u3l3suA7jFXjGtq+tk05REhLM4943LwuzcM5rb3AEg8sgbauDJ0psOd9O8OeQ6VEJQqkQyRoRPYarV3YurwwMNtKyb1mpVI11OVc28VB17LEpKMM2R9NKKMD9EOseOj3C5NGYAh8GqQ6tXsGuomwY4qqkBKeW5GkC3zcxQ4gVUq9ltW6/bGjoqiEicBJJdhPI8T1Ne5mme8jxNy7yEXV3ZrlydSE3dUIuauhatRd1hCjC4cYioi27cBa1DG7gFho+inaKSpHVT5SQiJByB58DEiF6YeZ4oiZeq262lCvfmg619gBlAZd2uJJaTJqnucpqmKs/uprquN2emUkgXmoRI0ByabmoAWJ0kWV48RY0M1L2a9/ZgPWZeq5tZVXfnLMQBXkxhCDxsQvR26CHX/X4XHLhhZ/U9Xn20dSxAqZjzNEUbrih1C39S29F7g2xO4ZZ3z8DkvpzP07y0qHZuvW3Dgm9qxT0gxgj139th+y+HfdcJtHtGuyuCainMYFLqwkwAhufA+CNKICFM4e4QIubCpExVoEye2JNo4m1mY96SgBnIRGwQB7mTMilBxY2lVMAKvjmxcGZmSlbY7dvrby9vv8oN+WYzpyk7++yRrckG2aEfP6bHj2gxABGi1pyRqedL9NKH/T+i8GmRNT9ytJsN3y9zjxT3SdtlZkx0pG2jifbWTxTU+2C0y7wzA4msvrsX34U6Wmxn5K7fSXqKKqGmHUMAybljugEBXsT3ZDDu5t6STQY9DA01LMyDLMeBbMYn3VvvTeUIvYBHFkJ8ADeL9pwYkDVjj+AwSOI7gwFAqSpFKTOEVVyYCK0JKDUn536/3FBQicBwGFkkkIJYISAkBKYGMSE5mSM5HTpQ9tQYAGSqbuTmop6mJefpRAQREqbTlJNwEhAhutoQhDy9v2+3W9kKqlJUC4og+q9FF/mHPIk7h3xwsR4uaf/E9A/PRMsbI0oikpMbqRPBtRRXsVqjmkb6BBEAt+22vvzya9m2by9fa61buTUsIrfz6fTp03MKagC12ISpO8pWzGzb1lLLut2u2021bqpuFvWB67pGKpMTErOI1FK2Umop1+v1enl/KGRseyUc4O6mtK03EaZZ2kKyWGiKMAWZo6qD2CKmQhk0xDh342fsPbZg8MxEZG6DGwYxq9lWNm9wdpHABG/hWIchXE1ElOJFA3g5R+JCxPEoekrKdxJ7mkOKKDoUsnnV9eameluFfP50nnMyAwCK8vk8ES9V/XJZzext3azl5sHYnbyiIpAUE7NQnllE5iUnkdOyTCnPeZpkYghvrGrr9VbV3t+uqlaquaNWNWvjAwiR1jMx57AAEsFhFhIlSLDFB+8kCU85zVMORugNPUHGfqfIAQR5qQaI6oyoNzci2gOFIAIxy1ar5emi24VaEkoCPYEy0TlSBb++XG43ZzjhdrliLXNKfzl9IgBqiXiWJCxFq7lVjVibCImb6ba6able3KyU6u75LDKNZu6Olhu2sxjz5gFrDJqO2Tn7Ia3vdWMQQdtHbhWp7/AJhJwC2KcjKnR50Bkigt85gZNMy/REn3IOqE6knHm0Bu79wzp7Glvp0P39YLgHtR/pkrujoN2h52p2fEzq5Q8AEKV4KUQ7kEDhso7LiNnQ0ZsIwuGbxyQehEFEsTmM2EBVUJgKYWUoJ+TJWDZ5daaK6nCqlczWr1f/epmQP9FynvI//vDDMs1fPj1NOUtz3aDlCDn1TmB9fZwa6i7II+Tt6LKv26BER3ndfxtFNbtM9YMoiK/47oPmGo+P+vxR8/tjPIEJ0vRkdK/3eEZfQNx98N0jxLMNNyRRNAX1drOehW6ta4uLiKpSwEeqEhFzOhJwf3pY3Ie2SN1M73ZUs6Z2qd8YggQMGEDJncyZo7zDA1glnMXhih9We7uXRwT5YDM0WmQidpA60BK6wyPFDSKcZeDkEISc1N3IQQxO5rgWA3rrSCFhUrMkkqVcb2tOMs9TTsK05CRLjip4Y6JS9Xq75ZqrlSQ8pyTMpsZExIXISqlaFc7k6bp5x71UmFd37vjZHuAav2u1B2hi7MKWEJE65QHNoqWR8iTCxKnhdgBWqresApLMrfo1cQRTr7frr7/9ut5uv/z8cy3FXSO4TIQvXz5v649znmIZS63uHv726+Vaa73cLtu2VatqOjwwkTVw3TazHaWVmdXserlEA/Vtvd1nFqAtZ1CXuwIbPCXp+QAMFgfUvJpVi3REA1oOjVPuEbVWgBmR9eDJDlQnB2JsptozmZv+qKrrtgXsYH8TJg8f+6FWWERSJpYkzCJRPxa98gJFQ/p/90PbfVxMSORuqtvVVXXdknCW0zIthU3dWBJxEsngXLV8e3kvtb5cL+ous3BizuBEYEVyTiQTpSync0qJn85TSumUlszTJHOiyYvb5nqr779diuplvZp7UXP3auaOaJ9FzHs3yyzRsil0eAJYpGlexNyE32685pSmnD16clEDYOv+xdj4Tg4rlcwZNoHcPSHURAcAC3MpM8umutX6m2+/2RYXnjk9pyUTJjib15dvVbi41mhzCVpO+fPyxERWKhPNnJjYzDetaq4eQGNitZb15rWW69U0RDuWdMoyNzjPxmnuNmFkvbZMp1b/3v2qdwYNDeuqZw/dHQHzGdRwV/nTLueeQw1gQIqChad5ztMUmx0gyYnRcv1AGMCx3C2Pg9e2ye3+a7/7WLvuL6AeFGhvAiIKHz/U3HunNQlgVaLEFD4TbgnyHGKejAK/iYlSOKjIElWGZ68MsDOBlciYbuw3QXSurCRVFmJyUoVf9Vat+qauRlejm6X5/Py0fMr5H3/4sszz+XQWkch96KyT3CH3IFjendIx8hAlLTZ92JKdkB8aBNx533owfLjU+6l70gU3M3A0PLzzI3SDv80wDn6C/gU/3Pq/cASZNcyNtvKwgCdqfiDZT+ti3vvBLNM0U4/ReMehY2b3kZECjATRZuL74GZ9p4wtEEK6A28IzJ2aWeZMDNlVoXCqtoEAzPeQNX0Bmo8fXUUjiPCQ9xFS7/Is3KbRSlWdyMjU/PW6AeR4nqYcsNPcqz2FKaf0dDrPU57nGURTlnCkM0NNL9drylJ0mnJ+Pp8Tc/EojLqal22rtSic4WJIzCdhJttgbmZBDiI9C+f3RDuA9Xb99u3l6enp+fmJqNVE9V1NXTNHqRY2r5uaai2FiCLJCwRmeno+RawvSw4Si+lJIlOrF5x6zA5Tzua2buu311d3C0u91rDXN1XbwiuhWrQG9rCbbdtm7qWqmwXVrWoV2+12W9e1lOoHhjgWMrIjIETUYFGCnmpVNTI3h18v13XbStWiama1mkeTB2KL1HbaJ4QbWgEIcGIjQdQ6oVntg50BRPAkJGBB53ThL8ZoXcPCEoW8xCxTEpZI3ZySpBT8DtEE7qEiR81qVSc40SRTSlMlqtumtdbrmiXBEmOp9bYWSAYnKPuUUI2M2ZOkkxCBJmIBJ7AQJ5EkKfM0JxFelizCiRI7l6rFDbrByIvr5mWr75erql3L2orQg/Eh4F8RQ2v5CYEz01RjIo6Wbh2ihwlHrsgUBZMKihTM6uHgMQKRgaLmQFhyTjlHuAHRF4za3h2bmJjV1MtmUCJndzIwE9zgEAcBVkr40oQp55zzlOcpJTb3zSoBoMzM1TSCTa01tMNUrdy0luv1TdXKpg7m0yeenKU1GkFDh92J01vnpGZJcwckGkL9gZKP8v7OphuEPnjibhC27dZdufcmXM/A7OpCUwD6pjEa4Ol3ov3ew7AnATxsvDtxHkvMkVXH3UFNIiQYt2+FNNEpggIHv4sFIo6uFc6RqAkxJCY2BwNiTCCLmgoJ5BgSR6b0xNmJckoGn1gLk/bcfZb0JNPCaSKGGSwUyFbNus/gvdcTTartAmNsdyICPepeh/UAqHNX7k8hIqbejXIX00zci7eO+ZXtk11PGCcP2Y1QPqKhNx1OOHqNGsZ1J6m7d04ppZTlnsa4nU4USPa/r4YOTTQ+P+h2TRsw68F4780i+8t1d3uT925O3DBo0Sw8V9JEhBak2G8+jPWDMhGdR48me7A3oQC7DX3XycVYPEEGw0gpi7AGSm5O7tLZGxtBncAJvYH1Mk3IEo4pEAiR+EY5ibpPkzyfZMo8Z88J11WrBfRAJkpqbIRtLXATsfboJZmxGVuldatbbfPraDD+3cfx+w55AK+vrz//7d9/+umn83lOKVJOudsS1CUY1vVWtrWWUrfVzLQUAN70MmPmn/7hL8+fnp75eZoyupcxieSUT8vJJ8sR4mYSoXmZVfWier1czGzdVncvtXjHSghiiRacFq54s21bI+IOINrJbutqpZRSrtdrv+5+VzX90CkwtEP7hbvTVgzAVjc1e3t7W2+3rWxbCSWjDuXXIN1BEA4a6q6btkWQZ2JhmUZcZ6B7hKdoChuEiHqaa3iihVtVu7CA2XKI9iwikWmZWztEMLkQS7cA+9Coqq5byYTEWKY0TdN2o/V6rWtZ39Ysk/3jRKfzum7vV5OJeZKcMGc3hUpyRj6JMCg7BDG0aUrTnHNOp9MsLJNkIkJxN7++38pWb5d1vRZXaHE3L0VD+HkXotTwWUSIwBQNPyQliXrP3UEXMxiBdsgHiyKLTJJA5AQzWslVrVQlIDkL82masiSZcp5mUkBhxmZRf9EbjROxCLHUqIUQcAKFug44ioOSg9zLelPTvCxpns7T/Pz5EyVBEqt209XcC03CbLW4GkoljU4X1ayWequlfPv2Wy26FQCSzz/mBSzWapkAMx0DjPh8a3EfdHSf/Tu4ZHg4P3LJ/U5tLkHhRLpj4e2cwZUftka/skmpIaBw8DANtr3z0Caw797EQuXaj902HQvdmpW2+nwhCvg174oDevNpD6TKJs7jdHYhFwrb3YkCFcbFhQExYlDkPmgSsBCQCInlTJmEfGJnfzUrLoWooiZhyTyn5Sx5IYGZaYVFxi0D929/7y2LWtyxRi1oIjTszn0W+tT32fbDVPPdaYdFoRBLfc57PJR5TzV9nGvqvAFdPIZ094MuyI39ERGxH7xB2NEymDnnaZqmkCPj9eLCYPUpDzQO0u6nPD5lOJnonvACriNEOx0Npq6UBJ1bz13xFtQPi9iYRVUFcGA0mmNmF2c6ZjsN95Uzy70SRjnlnDIRHASLBTIiF6GUJPYQD1hlFmbWOc9TDqQQB1urtksgum01tNaF54iFuFtElqvehOnr6yUn/vSU50nOJ1kmNiVXkpRYZrBUZTN/+XYzq5+eZJ75NM/LlEultdDm9X291hZS4KhjB1nY1MyPgbD7WLubWTWrbhpNAqlDAXQe7ARs63q7Xeu2lW31CFljp1eR5myhSBsGARCSaZrgHohd4WGLUEqtuq5rLKu7lVrgiAL8KA4OD0/dSlk3dzfTAZEUEObqYDKrNbbZUXO8Gx11dheOP6bRhznKjmopampazbR5iVtuChDKdN+EXbRzZFTuxBKspLWNHVto90PurksONIzGNKOEjpilYchTB3wh6doD766TB15KFDjk7pGSQw3VWTwSB7RKppwxzzxNniajTJwcSVzMkyJvBOOsws4TkVAWkUD/kczEVsxgRSvcdVUzX69brbqtpW7aMNQdihFV3/1/iNSBqP4PSd8bAfPB8xynI6TFB5zJ0MrMVL06ECn7OSV3mKmBUJBEJGdjQijhjdPKlGbqJhRIQJxJnPgU/jUmYl5YZsoZSACxnBYyuCyTzBNPuQa0i2vVNkQPvIiqXpVUydxVzbTWcrtet227vL9XNWBizsGbmDgyn2MsD5TZDOc+2IeffQa+b8E/ThV1rbYL7PaQvhzfNffHzYfFs1P18YTGeTvjJhD4wysdcshBaBCK92/Z7901f+wvOLxRTBRpSRGJatpGwOLG1978PEKEJOQEiDdQURCbiDXgUBeWOTB+J3EBoCWlzUUpExE7UsrhSjJVZim1Zoudxt7EI/mdATxecp9MtDK/R6HLI6vrTrq3edwn5XFfoyul1LXlmJQQsGM5unrV/09tqSIDYA9njyXdXYnD0xCJvvdrZ3tdOyIHCxAihsPMufsRpEsK9Fy2Tsl+GPT+E0DUjPa3Gjrjrp6iV3Y1+z7sb/YGIMMucDgHxlRT75q2sqPH9xVp/tAjoWK8XrflmSiwehsWzx1najln6k6qwpQ4ejGPoqVupDs0wFrCM8cChsEMhKpizAnVxCG1Rk5aUkMpFkkDHqAjRmrITkQQDpUL0dhaDebs+2ZpJPegINJjrN2qlVXLra5X5Ezemk5G2jdRAx78+vW3l5evddu2dRWmqI9NvedPpKnDnNyjz4Q4ZUnPT0/zNDGTmZZa3K1WDdK5XC7dY9pWN0Djiqq6betaSqm3rVxXZk4SvaoULffGt6gfqxEbHQb7o9bsaFEzAhwMTgSwOeBmxd3W20W1llK8KrtPTC4sKSDbmYhJcmuIMKrfGpQbSSvkTtEHCyCR1jmWeulKY4fU+5PRqAZqYj6+btYtN7Tuholzt+H50flCZGalqjOEAOZ5OeU8aTUttZSL0PT8bD/+ma6wmoolmICZTOBu4BuRiygL5XmWlE7TaU4zObNL2er17Va2+vbyVote3m+mHfmIeylkvGBU3HVXFEXCMxFH85uUUy/HkpRIEkkaYfjGcugARnXYhVEWuF4v1+2GnCXPKeflvFTVb6Wo6rYVwD+7LsuyTGlJGSIpz1PKT6dnIvJqcGh1AInlzI0pxLtnlqc0MchLJdCn08w5+ZwwpauWF928qq0rWjNcqK5wlPerlZpAQqRaay3X6/vPv/x1XW8//+0Xd3z6/Od5eVLV6LDA3vi7mR2J01vhU+OCwY/8YJoPo+doxO/SorPGjgBh/dxHFWEomHQfjP+u3nB0bw55dn9CcDQ++u8BqN6PrqdE7lu8pzbyjjPKjiZsuCl4Da+KSQQTMUucEMh75MaBlgOEpAeBmB0sAS7IRISUkHNwgyz8aZKc0/nTzImv5yc13Za1lqrXWqU6s3Nm0HrbpJgpRJJw6ho5EzGil8khVnTU3e+1pXvmExirI6jcJ30/dRdwQ05/vGd3LjPHonS5efCcHCImdwTQzK+Dm3+/6/5H1XKIPvjtdrtc3sOUn+cp8jRVmQB3AwDfiGie55FqFzGGEUU323F7jvCaDzQTswr4gbHF5xpJS6bKzACzeMDVMAsSoSfYATsPPfZoaOAVOSXKdyo1yNS0RqZHg89NwimFROv3iUxpkchyvamSYkrplJMbSjFi4XxiokAp3dScCO4Eyzkv82zmlcnM67YRcN1STrxMMiV5Xs6fzos5tF4BUnUHVEFEy4wkoNmECEBVrLVcSlGjapFLHfEokENya/F4JLk70U4RXHJ3NzjDeg+icDSM7jy9qi3cZaESBQJLkpRSa8obYXg3c7daSq11OGEi8fKoS3Jn7LHiRuRAy0Cuurt6uqOmZ2G0YMwx0/JIsg8Hukjs+6jdruuF1jIfW4SPSAjNdxiivYEMBMW0dr3N+Gzq9FDXe74hN0nsw4pv1jp3ud6riGj83M+k8cDOhXdP6YPiQkCEB0NzjQa77u4EI0ppQp6QJpfJnAys4IhYGknhrh6SOxEbtAqhsiuVtVxfb6XU29umqtsasEdR7CcNQVyi8qpFSYkQNUAYu21PExTqaDGdXd6vFgDiY348CNHm/Ha9rtuNp1kWYoBShrWiqWEKqGkDaDdjdxbyUB+EwUjuITIbuplw6FFJZEoZoJZzJ0Ii0VvWG4RnEL6zWYv/ucO8Qbo7Sq3btt7W9bbeQhn1nqoWbxfWQLNgHiw/7AlFOBh26EnFg0fj3hyhZnl0Guh5QCMvaZ/RD8fxw++K9u+ef3+CUwvYja0L7C6bcWFcxeNzdPdXX7lm3IfZCvSt0bcPEY9U8QZVwFFxEGKcIoEX4IhUBSwoIXosRiyom27ds5olMZHniY0oAckt0CIaQTeIcuybO2hVHsz2sfWP5NrX926Gxzx7Y0R9zscJD4J3rPV+fxARx1S0kqJHRzdRd4bsX+yi/f7k3b6P38B3gwMCccFC4kZSE3eAsSA8uBOzmgokRYhtqBvu3rRY7qZn1LPtoA594zahPuZmDLZviMOAPCagw9Y0wdAVmhEJ3dVWIXI4O9/FaMPCbnIjDL6eGc0cw6Jgt2rOu9LhIE9EFm8AJ0Qf7z1hor8qB7Scd/01epxHO7HoWjIlr62s3AFE90pzYiY1VuNSfUq0FazFuxLSSGW00Arh4gPorR93oj0zT4kJZmUVZJfuA+oOjShIXpb5fD5NOS1zTikt88QiMk0iMrfw8GSq316+fbNfaynbequq1zUAODV2XJNhjJYp3SYUVdXc3q9XNS1aW3YuMHF6Pj8hbuFmtTXZ86aIHIuBqYvLuy3ngPWkgfBvxPpSAz2IfChj8sj0I0lwMW5iDMSQliHf4HB57GrukjmYXLT5ZenWOIibl1668+hRH4/bNHsl1K/gQ93SamvRX+bBKQEH1BFVcQqomVata7VSE+mU5OlJP33RZb1NdinlutZbBHWJSDIRWGwhE72upvT1/aWsul3K7a1ote2mBGZJxMxTYsmSpUU/Jdpxxzb3oTy1DANJlJKkLDnLNMk0pyQpTyJCnMBsNPwZrbKdAAep2Ridm/2n//T/S2zbttZalqfn86cvMs15OXNK0zRNnNI8G4EctdbIw2B3NluWE/IypXyaTokln5mJ3RSwzJKZJaWcMyXhnKv7layaqRcU8hr490TMmZLIZFa32+ZqpgoHqYr5uq7bVt7e376+/HZbb9++/aaq67qJpNboDYApeoIUg+CHaHS3HnaWiOGSbqnFR1Lp7GnwRBzIfpfrj5R1uEP8NjKMjseRLR4z8A+kToeH0jAXm75JNOJe4zCzqiaH4Mv4h/v51INl8R9LdC0WkR3Fr38dkPgOErCDhDrKXwh+yk7ewloc7ZOZCSTMBmKHqkcjAmHJaeJJMAs0goTCkmSao3coRzchGjiAzX903HjBG3EUsYhEmKbl4rBg3iW4Dyt6zLz77wjeu2W8+2KoAkNFOC7o0LGGlxvHax5u2r33bgfCRK2llDVctterDjUjJZmmqbtaWU2JaFmWnHPYb2Fno2WM9v8BIJqmnFKKDyLeilH7GOg/rWSGmbj3mGDmRMRmIPYcPkLb85RFJKUU4V8mgvcEp4CIYQac3cz0MD0oVXmrnWMF54YpSYIbMEoeCUzIOUsSYQiDE1NOTCzIRO5bIWptchNRIiIWEjHF+7o5NbkLYpCoJxBTjebZpZQwEhRErX8okwilfKqeLrcts102e7upOntIERih55Y2pcFRvJTfibU3ccbM4WxxaeWjzavsPfeFRDilRAADOaWcM4tITiwBTM1R+aVls0Azul3V7LatAJycmAZmSahInTKjUim66BZVraqGhs5LkmLxnIyoKX/Nxto3XuxuJmqdW76zOxrVHqz2wQ2HAdLNZAhR9EeMzuIc5sKdY3P3W7Y9hBbwoYGruZvdTdD3i74j3eNbOprvdBDiPVP2wTAKFTuweoncPUB8Azc4KjZEwOLMRqyOqroSgyLrhJghXBNMyhVWcX1d12tZ37fr6+aKujmL5HkJNFlmbvl/7f3xgLUyFLew11t3+UgfaOi8EloMWhZ0iyfGQoYqPUbnwPVy+fbyopEGQZzyzFVr9ZTz8vwUWL4BjRBY+mpKDnJj1WIK5omoqRrMzGCnxJI7LD+ELQkxlKkStHGXvkjCgUfWql/NoBZaJtxrqeu6Xq/X9/fLVtZ12wL1sblmeJjdu8foQcHeE+R72GgIUrS3eMQexE73uzAGvi/aH+Q6Djd5tDg/mPLfI9EHC77r0LscOdpGUR7meHAXHiQOjXBD11clOqyg7YajItH/a/lt4f69e2InpoOra3/BgFOXtgW74SLCKTVdfG9ay914Dj7RxvJBbdo1nj7bu9uFaPfcNC1h91DgKGbxOKuPAnj/7KjR35mhj3R10P+PGsCjbMfd7weToUcwI88uitqosafUUBO4nUcj6T0aKLiPYne6H1r0Shn2eg9S7F6rMOulyaJuo3akGqbDlexD6Wz+rWZuNwyJYc23/k7uxxn35joeLL+bGQTt8ijEfRBiBkWBkzO7k1Bq/hH3wDsJCmvkRQx3rQYmylGq3KQBOr5V9N9yN6vaRC0TMUS4qlcVgxfobbPbpqB0J306AcQGuivCJKJHh3xooB1BqXGlSFtqWcQAnNVmERPRlIiolkLkdq1d3npzO7pHu+pQyJ2YmFLgtSUa/oxqFj3tzcL9XkbquxAkOoUASGzEZla1atVSShMAAHUm4C1zXKJ/tyW7Y2oRH49GDfBqRgDDAIc5ItWxCe9hNqHZzSwduGafqjZh7R8+8pE7tjLYSPfsxYzwwwl3O63Zb+E2ImE00GQHWurXMZeVQOau5gAZ07e3d1Z9/+2399c3aD2lBM3fXixP9eVXvL7x65XebqhaS1ndrK4FTtgERlbI46cSu5Al4fR0mlhE5kzCPAlHfT0GQTQMnWbHB08MmP2UKWWOsHpKlISipxKPSCmYmEe5ddew7MBC3f2vf/tb2a7PT+fTaeF0g6Rtq6/fLsxyOj9N8/TDP/y0nJbl01OepyzZUi5ab5taLfrtRSQt0zVJeno655xPc57mJGmaclZQBYrV99u2qf5yeVOz03LKKZ1SniXX21qua63b9XphILMn5kpk7m+v327r7bev3769vV1vt/fLxRGoPzzNc05TSrlV1rrf65lHjoyGW4fmdwxdRzrXpO+4ecJZFQnzRLQXBbWfXVnuWmfLlWk3ehAhH0z8h+Ojz2D8HCKQg2N1+j5eUGq9beVg9u8j7+DmrT0otQbQDXQRHrwZPEBZ+oy1fhNtWkDsEQtiatnc3PJfW1fyoE0kceHaEAFBTEYUFfAmrT9e0wtCcTho1qE93xctEo38czhGBllsDALcqJvpYyrBH0TpHlB6XIjvLE3n6PSgQH1PFeA70X7UGbrieDi5YWoKHzlX8yP1egVmCvRxZk4p7WJYhHqGfIuNdokera1CD4g36HjnIcV9hFbHkMcnjWJ2VegO/O5Qts4B59DnmD+GsUJBV9WjLqXmonvNKfeaW2YR6Y1ig5hC14xMYJGcOE9JSLJMbtDNHW7FiKlqldR6hblCK1gokQjTEq1DEonQ8zKdpiQEAdy4MjG1RiSSwEzkZbttWZCEiHnKsxrWTZl5mWZm1iiur9E2opXwHY870c5ducVgM6EHtalH6xbpFlljwhwp64DVenW3UlYbPT8a3ISItJr21iCtx7OgcCDK481UNWrbtngH7pnhANwc7L15jUXe90N9m9PYNoFb1RLeDktOzNwSB2JEGP/FvuIHQ3pX57oRSh+k+4PpjQ+2+HDaDwXzYcMOYr3fyRiqLfcd1jdjsK17yw/kjvBXrNv2Wsv1/VLWwm60JHJZr35519vF1wutV9xuKJvdrkWr3i5XV/cVcHIlcmZkoTSleUqJU5rywsKUU+DXoyeERs4FSa+AaiqOhB7FrberjByFSP4Pe/bOdtwl+12+2DjhcnlnqDBNOdVauWyX98svf/uFiU/L03JaTqdTAi3LkvIUyZxqagRz021llk1NJFniGTNPwpItC6YcsP2b29t221Tfy2ruLd1PZE7JHVg337btck3M03lioqi62bb18v7++vrt68u3rdZ125gpAo4pwNtFuGHj71CfncvuPCb8km3btXIubkWSjT0dnefNshn0z8ze+6Gj+4Tb7wcvemdd+2kf5TR9OB7IcizNePQga3RgryNyCjogz8ebh8zsSaqtbALdXo/bo+u57V4U8grdgG/Si1suX0u84HatcPRWjW3avG7s3OQ6tX4t5MSQpmDgoKQPO5y6kNtfYx9fF+QHY3dYNhiGMg3DkA7EP2TW3xfthxc5bJu7d/meDtBfdye2vmrthR4ukIFHdvcOd2sdru/Byrz1QoaI7In49z3audePdSv8kCl1f4yXvC+i61Kpf34vvBuTNtvtrAYj1m/l7ixMzsfiNwK5w/aE4OOigx3ccqs6tmB81VtWiyRhkYa1Z+4IMARxIkV0XXSDOQlRApg4JyamJMRMc5bTnBkuMGUmk64AUc7MAvZirpJbYCilZGamJiJpEWY2UgKCfbEQcxoiO467pq61btt6hdW63XLOl2UhIKCz3eNGCnjt0RS3gQvoRBVwRJOxWBVpld/d+DcyMoAIDgW8FlN1GNw8fLR3DMiju0krk6itvshKKQFOvG8bDN06/A5BDo/EHkkT0UseXQsNZuREvUQiofVB72SDoRQw79bIbg4NegpqOZzwaKT8nkT/jjkTJzQTLuzAVtxK++PubueAOUAQxqYOt7KW6Guz3aop/l//z//v8rT88vrt/Xa9lW0tqwdKqrnVDDS/KItwkkC2TSlLyhCuqRIziQZXYOfe/WIEMIKnB5dOxHzMfm8stedFOyL9CeTDfOgsbGzIe+GnZlVrqWWtWyGsbpfr7bquZv76+p5yruSnp/OnL1+W82l+Os9PCyc5TSnn6XQ+E0t0nam1VK2bbimnZZqXeYYDZoiSFacv0+LwyVx8u71fV4PetnpdCTZnAXy93Uz19eVb2da//vzz2+X92/vl/XqLJMyUhHNiEU6ZUwKNbppDJ6Mjqx2UEUMesVjsmN77mYM59hnC8fIjhR21gOMJXbN8fDr9XdH+8eSPf3oEwBlEfBdNCVfeAJUdt42fzf8pHI4HNLQvOFmDDwvC6oXdLc+EumgfRaneBSiHwG7GOnave5SuERHUqffVYAQQpAjDA6G86Q7sTty7fPfWa51AjzPQVZHG+buq1rykdDcT1JMCxqqMf2ONjpPdZSpCEWl91h72S9hjQ3v5znFUOeJvIiAKewIzu61BmGB8iJtwgAPeGUgASilh4VO/X4hP6orgmKTwyaPb7vH7sTfSEOpHq70zZ+KgpUbvRD0hIaQPH46hXgSMiOrB49Ls1KZ9HuelUyYRcCwY6UTTSC4S9mo1M1Q1EcrCOQsTRcPfWp1ANGxugWThRCngSghMKszLvEhKU84ismROZETOsETRa4QYLg1ZnJe85JS/fD59Oi+v1/rbW32HvejGJDcWZq61dVGpqiyZJanpkbHcWe1Wa91uWtb1lpJIzglALRWAW43C4mM2+pgIEUrJw2QJIRjOkoPYaszIXAGvdXO3da1aLLZNzmk+5d1qdgDk1bxZ517ZVCQwZAYlHQmu8c6Q7Q8KTDvH3ayZ6d0rFXZ857VCBGYZQOZ9LzEPB8BH7PYP7I9oH/bRuUXNsmmT1t/2zg3Qx9FyjpkI7iwNR68VxgkRP75HS/N0kFCtVUvxUsKrWaptm17+93+F8OvteqtVzcycEYKXhBITKAsL5SVLkjxNKWVJiZKASMl2EPEWRJbuw4gUkRhJM8BCcx5R9pDrEnHHFrNsbP3ICPoUBbM4Ll8op9FlpsKdtF5v67WsWvT2fmWW4rqcTp++fF5Op88/fvn85fP5+enLn74sOX9+fgbLpmrul3WrZrYROrpqBL4y80lyYnrOExPZVtxsfb/U2wY1UktJ5iWb+W1dS9m+/vrL9Xr75def3y7v16Jr1abOJxZJkoRTYknUMLzvaOD47yDNyNH1LiE+CulOIePi7ygHTX4P8ro/gX7/eNAMDo97rFmn7wr7vn/QcqPuBmwOtV2bGa/HGATUgus00taaRGshJ4mQastzo7DF++37nXulxeGPtrXbU/qbubmAqIv2oFfhYec1Ox9jnjGcB9+bOvaDQg8QowWa4I894mifpvs5H5tg+Fn6ydhXVgZPuiMAYCjW/MBu+O60w09qjs0wP2OqDmLyuNR3oh3d1CZqeY7hCf9OdRIRHUT+AJoNMT/0gKFrPvRC5M5Ount/L1tjpmhcTE0GS9joIdqH5/9w8/7qjytH5i7ufE/SXagP9w1HYLSqEXdAp4a3EH6emD6Z50WSJO44diH+gj2qCssySc5pnqckksgEIYldiFJzfDpT9EOX56d5mfBPf/n8px/OP3+9bfpeSvFaHBy9XEK/KaVUVRaXlEx/r187UNbr9e1lX2Vh6hWH8V/LvQ+5zszUQUN53GO3EiLHubUoIAZiRxoBaiWwu0U4T1POE7d8OreqgLt6cxL0zj/g7ls5kNFQOXtCV8+dg5tVd71T01qkGvFOwzlEvQDigJQtff+0WhxmRiuj4SOZHBS9ltHTY9DdLKC+w9q9CWPVf8ceGnMZn43kAI8qkqGN3vdrP5jNaI0JmQMAW7UCDnYSVlfAOJKQiTOlsLJJiDJIiCchYU9MiYyNWyPKMH+Emy+GGAFDIvvG6P5Y3zl2dM0bbGXX0BvRH8jm3r+Io+UHIjXfql3XApHoI1vVmplLBMJtWwNrKU/5tt7eXl8/ff5U1/X89EROKWeZpsT8aZ6dSJmMkHKWnFuHXNAEIodvG7lrKTBj08xAYpC42+W6lVK+vXzd1u3Xry/rbb3cblvVUPxFOKWcUgOajhyfIaICS4tHG0Hcay6NMkLn6cLqKCDb5AQ7u9OEDhN4EO1jUe7NwAf+dqRA3Lsljx/i2L7qe8Ke+mLHqrv73bs5Dor48ErvJGKtbgjuzMIyWp+3XktMAiaWyO9o8gvc7eijshwXdDV9qJtEwX9i/xtH+spArQvVvmtNYYRwJ1FuIMg0wmJ3ww9f4DCNObL3D974JoTAHT7uoEXhkQqIiI7hqbGsAwaHx7o0BiWN++1xBBrnPS4jjfcZHcC6jGzLcv9GUdK5jYS4+6IJDME/JPTOxO4/Ofrwjy80dIXjnYe2QdZiVTH53Nhr8La4G7caQESNmQfB3GsObYAsAn/AkEdPu4t/R5WTpFD10JEbqNfvMEB7tdNowCQcgKEyzZITp8TRWyZ8oNP0FI1CKbYDuRCIqRTdtmLOaq0lCDNdqqbEavx04k/X6Tzztm1aKwPn88mdWRJhb4GTUmKJ/+4279Ehj9v1Lf32y4GovK0Sk0xtEH2iSCTllJiTyNz2QdR8dJoefQICSjAgKYSjoF8JSHlOkp+ezs/Pn7WWUlatdQujvJX4GXURbqPS7RBiH6kr1HZwC+IbTHVTLffsLzwzzesZFfzkhuiouRsb3FWG444KqdZY83c1+FAv0MEax4vd0VIoeh8U5J0vB3k10g/UcQGD3CJFmzkRWJqov9dze614mMtgUri6bXVzGEhIWM1AoEAiYMmchSVLIiHMgLS+HAHj6ewtHMlg5sxZiFNY991gjx89hYqH1T5y5TDgEDwwmTzyWgm7nOiCfF8At11xAahUcwByW6tOy7KcUYpG8pIxAX65XImJ3l6JaJ6maZp+/OHL7aefPn35TGrL6fTlzz9O8/z06VOapgJXgjMjKtolwRxbgdv2/u6qWgvMUkqTCISR5HYr315fr9frv/3bv23r+vLb17KVTau6xXiTpAD0CPXKzbmjwgXZhsbcPrEHtQwBs7LHkLt5h91K29nukNpHFkkfLJPBxAeN+aFZHH/wtX68w/GTOxlw+KVLqS5hiY6IZrG4bt7f+5hh141pciay8LB2rTeEjki0DaKmCXLrONBiO43pDvgEQaiiTAhEqFYAzk0f6PU0fZv0oXjUPnexa+GLb1ord+iGR6neRIOZ31mc6PhDd6d2lnJwUX2Hh1DX4D4+aHx2H98B9bAnP/Ca/uBBGui2LPcctO8I5sNbObBtW0pp2zZVjaq2g+N6X+Wg/ODMR7Y2zOj42ToT9heKLPpaKx1okohSSuNuPSOvRVkH1+z3FKAhyAa3dPfdYXpQYlhYIHYn2ruNGl6MYLatnWFT7iK1uumFYw0CZ5coZ5aUk/CURZhTSsJyXqZpTu5qbkJIRHPOn5/OzFRVATJzOCcBhK5rffl2UUj13NaeYFARqM0/PKenhZfk10spWyGiT5+e3UlrVOc4gChPI5ZAWjku/H2GPJygzc5y9OxcBkcdHDcK3cs2QYRIkekrurOYbl53v3bPneLm4YIwJeGURJKwcYhts+jS0jJiwnaNgPPActrJdmhloW1w1BGZu5pWN73fYIP59Kw07NGzvmy7IckD4HFcSt+V6fe3HzyZ23bhD2ACd1N+4MtDd+5ldGh3xKjlkCgWi3KFu/v0SGUML9bAhF3YEwMiUybhJEwtjs2JWmuHgMbvjr0mse9HRRwAkxEMaEiMNDTNMTGdXTP3QGr7/CCnB3kwD77TJ6HztYcZMwepl6ItHSAlUxtWlO+oFUxMtaqbX/Pt2/zqwKfPn2utJDzPExNNy4IgOWYSZneQuWq5rK7a7HV0sDag1rqVcr1ev337drutl8t1K2Xb800giSJM2QJ9xEPR6SbWMA5ak8AHvo/mfmictckr6hGLB6urT85H0b6vWWfWuzJxMBjHaQ9RrXEh3Vvnd3LrAxItuqsAbZff6wcxKseQV+M77i+DpkSHFGLmyH9rxbEj5h0uEOehQ+/RZ0YPUB3UIgI3RyGjvxeImg1OIO50HjKkeQF2wTp+ifFEcshjYSH6cZiNzrboTs49TmznC8drmfluhY4qVNtwxy9jWnD/4ePx8IZHu2JQyNhHD3kStdZSSljtA59LJLJI2+sf1YUH0f5gwByz57oH1j/MHtEhSN83C3cokX0EYXcd1FPEK4zHxXZ0BxGzk0XuxmHuOvdnosfXbrftYchYq4FLTS2UQ9EeI+foZA4mF4aExeMAuzkpuGRl5iiCYTYmcqEspNpgrHrIn4i6zxVsxutml1u9rboVrUpmDO9K6cEt1H3pv1/XLjBBafmejpZNH/ko0ckvag2JmMmcnYWVTXtv4Ps2AD03XpIkis6+xALvb4ZIRsg5T/OkWky11rqtG9wTc1h/RBw1kt7T9j/STUx/NAutpqWsWkvZbqb1Xg84knsoDOCdsHZHJx/O7ks+vn1ko8ejqebeyKbbq7uGcPzlqADd3WRX2p2I4cYszRBByyZnsPtBcQl1SZpoAMHJlVES2yS2TERYnk8ppUAEArpHppk4AEX3tF31bzK/i2gmZkqMFqCnDqvTVVqiAAZrKoJECx6003aFKfpKNCFwEELHeQ3t+6hGbcWYqajLWk8nt6ipjNpyNYeng0WylU3V1tv2+vr25ctnIprm6Xw+TfP0z//8T0/PT8+fPi3nsySRyN4otWzby8s3uIm7MH96epryFNCJb6+vv/z66/v75W+//LJu5du3t4BHHpXokiVLzilNvQKHiZOk1CrSIl3Hgl0MFno0whSmruj6WdOLQqR3OFEMsdF1qAfBQAeuPT4CIEP232sAuL/8aJQf5fo4+ffI/qNWYfcftdKwOPf+2pCt4QpsSf7BcqSlIPcI1IArv9v13H2IvbCva4a7lkg9QMRgsuZgZeZDeJxGCVbT7omo6cgcdaocbvsgU3/wSRzCz33c/Ua9VIDvmckuTWlfxHEHfuQ8oUaj2zpj2g+vH6d9SAN6UMLiGK74fh8M92qUFu/cx/1yuahqNAuepimM7zCpay1EPebRrf8HKjo+F0D00fHDgUPSO3XxUUp5UERoN3YicT0xs5gQU065l+F5BMXGaTnnY96+A9vhzkTdndb0klZxGshs05SJoBq6njQBCpcmd4kciVkYWfi85CRsxQiUGRMRAe4BRVVrUqsgavvCVB2+JJ6EDCDOIDdVEMFJhJd5ylkcUpRf3mrZ7LLW10tt7emZpzQxUdTIhB7iZlVN7fdi7Y1QuliintHczE8manU5TZ1tmgxbMzO/Q0adxHZxtqvxByslJj6IK+A7aG8VT52Hdur/ri7cdyTcor+Luz64rPdXO67ufoOdH6D70g+2+l1C1INy2pVf6rmy+5373B1euJkg+60euHMoqdT91dwUmsbLD+7Zw1Q3JoEWdGiOx4E0LMRo+P4QRKJpqLcd5+vIKe8mdswIh0+Ax2R3dIdQaQ/yZIDqNZI4oGEB6J06dwudH0U7fU+BOtroPfhHnTQeLctxWhSHpJSCwkrZwr52s5SSpORmtWoppW6ru2dm6T3WIkXler1dL5fr9Xq7rSWwku+jQnw00fuf3AOZw9k5cjvQZchh9fpOGGJhF+CE4X3us9JTGB7JYIjkYcF2F8jYO0TD7Kbv3OHvHP9B0f6dTx5Uivvv+Phhn8I+U81kZ4YQHBzeFIxcM2LZ7XcM3wE14jw4Lfo8+BASh/fZK9PbV2gv01dqLErTke5G0K/adzRRk+t9Ib63Rh8nKsb1wFvo3iYZMzV8Np1hPcpR+nDg3otz/B2HEvP7R+/x+ON9DqcBIOupJA9CnQ8bYPyMpxyr28clIdqHY/9OxbxTgUJ/BTM7W0wFAHdm9vFWoUBH6u7QI+4G0n7cS5NBK41lDaVpz1cMF3+ShtsV+zwQ1pt3KIRnT9Ew8ybaW8Kge2CgtYKQxtdlb0vC0ZBSndRD62UiuBlxJF/RmDwnWMdvP47jrvgN7mipZ3FZaAFMYBhR947tTorOw2Kk3GMqcvg8TiWihtZoCriXCrhJIlAtWynrVtZ1XbXWsMiiqFdrt43QsFuHkOjsibiHwtid3Ot6u7x9c1et5YFSW4b80clCfR8zE0kk0TYVmHrsIcCofUiW7/Eyah6VLsUOUr/X0cfaBLsI//qR0B/2AHedhjtbp4B4iez9CPnfL6SbR+qpOEi1ubQCNHCemIjzxCmqd5nJIoI1UM5Aww4P8d6EfNPauCXN7JZ6W9Qm4/kglnpXIqLmum/VIw5XKLN4wENS1/n7mh5X1u+TBHnfQ+Ru27ahW+0hLN2FqCGeMVHOIbglon3TNH3+9DnndH2/XN7e//bvfwM855xzDt7l7hHzm3IW5vV2Y+L3y2W93b5+/frLr79upb5frh1MvhlqYRb0+7C7jxBmgG5OU4B2AWTuTr3G/SGdx4mMKOgCzddJQ74QwY9qQbjw0HD2dw2jJwbfpTQf2GWb77iEqQcaO8kdmPK4w7Avjw96JP4Pguq4dqFHRo8Qapxg3x3cqtFp7OZBdd6GC2aEnyfAEaSjxbUWKcJM8Ea7+0CaKGijCrTgKKije9Ilis6hZi3ZoSGCces118DPhwmCkbf/9w/0B+zikx7tgYNkHfpBcNljVfeYqvHW7fK2WJwSJ6LA52v3tPukivHQoPMjqcTvxw4G4xCRT58+PT09BYCYuw+ze8xwvH5DGGshKR5kE7tjDPbIkEMaPTzyu0SI/XGtKJasEqLNOVtOqUFMRi7UTqvm2j/mFrW/aw8TrpCW0ht1wh4Ocm/Imm4gYpFITgILCUsSzjkt08IEZk/S4tynT+ckUouWaGumxJKXJRMhkNyqFgeEnYmyTDknNlXT5C5uLGlaFhFJWYRJzVfy0zJBpjynZ5J1q9frqmbrpgCnPDFLUTPztdRSNeLx47hv6jos44O62gXg71MzEHuzS3ORQ0jkSAKRLBsOykD54+TeS+S9Yd0MvtaoYSxV18d3RxN3zwEzh9XvZqY1btfudnhP3FuX9zcmCsUs5qBvBz5o+d+X64O1NQMC3S8/1MKDXKfmXvuuydUHNQR8f58oAG+v0Obg4U0a5/cBxUNh+LdwUFfFWi5gtzL5bjrunOSDz47f+y/cZTlzj4HhsNB0f8lRMACNkw8LhQ6OwbvJfLgXMx8gNdCS8nbW0OetnSsdoI268pSS9ISgWraiqlPOeZqC+wSlhY7NzKUQES6Xy/V6fb9cb7dbqdEvmOggOLuhInywbLpaK10I9dExDXCk71DRkOsthYW6V6Std0wcWp0JmuH+IHG7/fJAGLRLmkFYzVWwU2c344apNBj03xft31u7+xeg4ysx7RIOw/5unp59v7QPAx+Uw2D3QbPeqIGilIWl2+z3R6eIIWQp5PRhYvubN++PHyphu5joZ9I+G3+PFw4u1f/pg/o7rOPA23YfwFgO6ltx10gOjkDe85zaKcdF7Oc8ugpCDf07axrHSLVrWVB3dva4XzPBh2gf1Wjj/eP3UeHGzA+i/btCvd8efQfQYREgBGYOJ3R7KDcgxHFXomjzFO/zAUpw2O271EETVOEsbRqdcHeMhFUlQ8i1niHw0ASFG0yGwRyBvAq0HmYxXpbAwAlJhAGb1vuLtJcyAzd1hIg4STTlsfC9AyQph9W056j9nX7t5l5Vg0+1uWTm3thu/M7t/9I5W09mHb/HhnFHL00IXkxwqYXcbVvh5sTJPS/L1Fukh50KglYluksk7htuLDeo9yCJo5atrtd1vapu1ETIA4s5uMgIiI5GkWrXgsWDBYwHtRozHHY2HSyku72BYbV/PIY0JxD4Tnf8IKGbXU+ShBDu9AZTPGpAiLlD67ShubmpwV0AashmxCKCzClqDxt2IolwBJAOZgATefdmDKaxv5b0h1L3PPOQ601TiOoEdP68M++DyRiqBPNoecLdrt/5TucF92SaU0ocMb9+pt0n/ewsIuc8TY2wt23729/+9unT85cvn+YFpWy11uvbZVvXeZ7naWrtYRp/5HUlgG63W6319e3ter1u23Zbb0TR/4F6qZsw8ZSnlFLOaXC0cBLEEVk8YYSZ6WDK7q52lwUSYKWGjlrDndU0UOmj0RPWRLQ/GwK1GeOxPCBI51cYBHZkx0eHSFdBeczgh2DTeNUHF8vDcS88cPj847n04VsMfbg/lLinK0c9sbQ/D1glkcnTcCebJdHAWIIk0XTzNqNRTTeMhSb3OxePZBG0liOgITNbm+YDA/gwiiF46IEgmyowdCg+TM29Fx2PZgZ6Sl8oz227Pqo/BELrW8/dmjrqZ+1RTbYdnjzuEcTrHp1AdsRWd397fY02htbbNXX5HQK4GeK11rFzhxMrBGp8dfS078Hvrg3QwbCho4ep1UMb9Y5ZMeD75DsAiN3mTuFp7lMfoxDqGQYfdavmj2UOE7wJLo6aDo5cJGllfiHOneBaAdMWiSO63VyYruc1J3FnOKuZqadU87QRADcAqpXI0yQMdiUlrWpVraoXdTJfDcyckwjTlCyzz0kzn4RJiN1NzRzI80wk07yIiIGJeSZmlinfN3u7HykckLG3m523y28evROGRO3ynvrHj3SN1gCgZcKpkrvWCneeFCLWco3vjGyMHmJD7d4X635tuOk6pla2TWsZ3s67fRMXN0ZI1GshD8LhzrDY62dB1A3uI/Mamwc7K8S4/oFlPjzhUXX8wExpvzN1lSE4RBeu0YMGdzfx1nzQ94g/Cwk4UqkGumSUtPe/+iv1ex0W8XA0uc4tiXLY7u3Pg9FxmPFusI5tfJwxjBTlg+wZN3hY6ehI1Fq5AACY/Yib9PHkYApmdrlcmbmqppzCVqi1lG0jgNxTSlF0nlICkQHu/vZ+2bbt7e3ter1GsxkRSTntJNJN86h4OaqYR7dkXxofLlZ8qA+OIXvzuWCkn1Cru95JDruvviGkH76kplI3MDei4Qru9uJOiejy4mAyD/ul+VQOD8X9Vtof+buffCTwgy5xSAhAM4Po6FPofw5SiUqkURECopZdJykFX8aBHwWRCQ/PUB8ed4jDjo36sO/up7NrVzI+G1rO4+g64Y97tnu1pepCCOFx6dd0HvtwK9p3zthPTXdu6k8LIX6YXqZePNB54tHr0/SCO3k/DI4wzaiDVuz3LVtx9xDtwd5jDw4oumNOHPdnjy2Abqn7wePv997/sXB0r0cOMW9Rz8Y0ttS4nFu+LYncVYJ0yjnOatNFPq5dY6+9ciI2E4Zns4mXbtiGLHBVN6YGNKtamcnhKQlzZmp8xsG9N6wD7qZMoExM0i2T7rB2j657HC5+JkoGca25VsmSOIX176DoH9+MBxFjkSRA7u0q+nGPId88/U1KB3FIAwAfMCt3m4EIrfXFgerD0h06WiDFRlIxlQI32zZyn9zzsqRpycu5bjXaqpraLrQGyTY63GUH0DUpJqtF3bbttm03s9q0Yw5f3c4bO/9s3NVMByUczNQGn3EUev2L2BuP9ULtUmYmtq6bU6OBwz7ttxvs60Cme2UBupzb3fIeYVUetlNbDrp7E+/eeAbIna11IyImiHeFnwfuTnMd7O+2+7MfOXk3nYaBPvSeVpOH4KTU5Q6IaLRlintEqmqbKJZuq38nI5J2zb0PmHlZTssyhdWuqrXWSKFGoBH3WrUodeXu7jNVZi6lAP7rr7+ez+cpp5RSnvI0T8RUzdSMa0Xfapuqml9vN611K6WqElFUjsa4dgOdRZJQhDgO9nqrbg8D013VmKs7DnHNx2SlO4bDQ6r5nQepy/uhCT1cNPi9917TD5rf/SPvlFEcSBzH0nPeG6Lw4zP/o8eRX394hT0yddCEDoIvGL6A70FY2cDMBgvnfLQUTCP3m5mp5Qn1+4AocuSxgyKCoo4hpEaYcJ3fhFeMo5eoUzP1qYex+7zBYY46DPNB/yPecXST3suasTtCXt8PkLsuf/xwiOJdC4vn+VifoITgxXsp7L1LAEQt/4VGpic35P5jNizz6Xw6nU6RaJSntgH3nm9AoI1spUQU3w/4r0nEhggIX3R4yFk6Ks+u/Y8ofqMWUBSwRBhAulM4UtUaBwMBEcVtT+G7zH8KXm1m7sg5T9MUzuAHCowpAPb9xUy9mTMJcxKRxFOSlLhValvvsK7VHaoKgqoxs0hmTiIUjVNSEkL0LZK2LpxIJE/TNCWj6lZaDyOwMJLQ85Jykk/n0zzJlMnNN/UNVNXyNAvYKYNoXVe4F7XevE4e+PadaBeRlPNBker61IHd8z4njbrQAti+u6y82SboWcpmVmqBu5cN7l4K3I1lcsznrWwlwMxD5NJAi+j752hO4KBeJ2JhLlat1lLWUkpL/et5WndHvFkL9rt14dEFVxdtvc5tqNh9xExkx5sd90DfH0PT5t3+RX9GvzQ+GSy6O452R2Gj+/iXG50eBhJweUfRjtaCHmCArceVgjZbyU+bmPbG34m0PaTZPk5guEibE74l8Dc2E89y7Lbhbql0eSbMDoyU2+BD35fuh2khImaa5+l8Pg9noHvLye2mSsuuCk0WiN7AVusAVcbrt9da659+/GGep5RyyjkSM4JgzWzbNnW/rFv7vccFm8+wvXx7+0igO+6REO3BQcaI3J25dYmONKM2OHq02vtmGl5X0OA1+4TsHl/0mWm0NDTFRkG79BiPuFtafvzkqILefdWFw3cI4n69fueLXbRzG+n9ijMwCvg/bCsaUpkb/R2u62/KzR0P6SDej28aT3cixS58iYgS0DN/Zcxds9valgexOBH6jn3wuDjMocc3HwK+/ez7lHnPJ6AInTRLCW08bTS7xO/cZCwvjcX9YC/EXjyU6NBgnQeWMtQ32rfnmC7wHWVGNuj5fI581GVZpi7d+/0b9Pi6bWa2rmtI97HcTJGa7fGhjATrDq4yRMbRVx9pmGKSknS4+Oi6JERHVDsM0kL3C8Zqjk/CpDQz9zm2+SNhhNHaCHBn0a0KtRvr0Roip5ST5CTKVhyq3tqvqTpQWt2miaRpStMk0R+KQE6CqK6MBZYkecpTFnOqTN6LbAhJ+DTLPKUvn8+nOdeymRYNvHVQylnATskMtVxrVQMcJJKP6T5x3AHNrpd3DMC9vrHiZ0zDuJT7Sx7mqO/MY2Q6woPugKsZecuQhxoItF5VK9y228Wqasj7sJPuFoAjxNGU4SHymDUnIVYtMCvrLQxxgvf42c6l4Pi3f/23bSvUcW9Cmz5YO9RX+GHxe0sHIvhDye64ilmkj/7wRf+y/9n5MqM/rRFUrGzX+2PMw0vS9IKDqGNivrxfIqk7Rnf7+WfbNm7cHy2/QjUUGo4GBNzrzL7nyu4P/jC8wzD3le/fDFcoMbeOkCKRUrJfsyuJ8Yc83uPDsb2/uzUVG6DbdVW16F5hHRjL7Ahi5JH1Ja2BQGActTHVar99/Ta9X9/fLzmlshWtNbhIUJU71NQdpVbvMNdB0Cxg9rELwsZLooMHd89YO3LOY9fklELhGKVwcc/L+2VwGTP77T//p9vb69BZu0jYBflYon0PfG/WPipJbQF27fg7J+NQATk8SeOMoOm/I9R3kdMVgNv725B/ZvbzX//98v529z778z9IqXji3XkYG+heIRkn0DAHuecBEe17f48XRapiHzYRBXIRR3J1D2EMbb8b1iOzjgj09evXw+j0t6//+XJ92e94sD2G6k73dD6YKd+5Jo77j4fVTgc+QpEmgTbfO/ONBbof9VEdOKz+PtkxZe1hnUgul2/jTczs28u36/U61NmUcnirhsbsB1j4WtXcel1D+zrEXuzFAwegnXAapR+p3qkjFoUQoZ7lJR2ZvU91m/NQruPHfmcHCGYGR0maUt62va4d8HJ717IKN9j5ZlWF1d5kIJh5u2URzimJcBKRZrWbA6pAM6na24T6EuU5Kcs8JWpuIoRoe3vNKck85ZylVN1qNYeqD0PsfZ5ykp9P05TFNKq40WEXwvQXANu2hcEOEEtilvf3tzvNmH73+Dtf/f7pR43xP37l3bX/8QvHg/9Dl3zXOvy/9XEnnvfd8p0T/2u+xX+1Wf0/5P79+8edMnowf/9PfcT+69857X7t/qOk+R3J9t/e0UVPP/6723j3avF/YXgjnPGd4/+seTk6VT6SyN8h8Q8vcPTbP5z5wRfy8dr/qnzm//LjXn36vYn63vlxdIX5O6fuM/dfnOX7y47m3n/v8//H8cfxx/HH8cfxx/HH8cfxx/HH8cfxx/HH8cfxx/HH8cfxx/HH8cfxx/HH8d/i8f8HtxaUxQplbmRzdHJlYW0KZW5kb2JqCjE0IDAgb2JqCjU1Mjg0CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagoxNSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTMwKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDE2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDU2Mzc4IDAwMDAwIG4gCjAwMDAwMDA2NTAgMDAwMDAgbiAKMDAwMDAwMDY3MSAwMDAwMCBuIAowMDAwMDAwNzcwIDAwMDAwIG4gCjAwMDAwMDA3OTEgMDAwMDAgbiAKMDAwMDAwMDgxMiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTggMDAwMDAgbiAKMDAwMDAwMDYzMCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA2MTAgMDAwMDAgbiAKMDAwMDAwMDg0NCAwMDAwMCBuIAowMDAwMDU2MzU2IDAwMDAwIG4gCjAwMDAwNTY0MzggMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAxNSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMTYgPj4Kc3RhcnR4cmVmCjU2NTk1CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:30.820816\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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++VWviGaJ+nCn3TxKktobE2JyzIsO9maD4HgSFDzuWrDWNMGsdDLekEv2Os8AXT1u//v2z/+UZZ+kpYPIoPOSTYtC6GRZ/MGNTnWQiq4YO2YtaYm6tZTsbLvZjlRa+pWql779JLBrch32Ts2T9DFlWaq03HJIc+5/P7bn95/6OYOsAZsusdt/2HWOL1OpTrDvGyBQECMseu8wYeqWSmTqGbJosocmHiapmnKqiaiqpgmJaKui4EpBWKuiouJqohvT2JCZICEDKCQAgcqJYsUpsDkWelsatOURWQ4T6qW0hBCSCnFFL9/97ienotZXjO7Szr4zMU/c9ifd9l/73GlBVxzH7v4dFl74Pmb/fM/fl6O//cc64n7nHBsjMcv/vxsLpziTygZ7ZJnlPbz37i2xf//ccwjqnxpsQcujzUvBRGghrXxtVgyixb380/9pyTu5y/4/3Tyrjb9pYpJlzruP/fjyqq+coD9cz8+7zP4E8c/r4W9fLNrm3I+d3VccMY/4+azdvcnD/rMdl9UTx/azHEvrcTnd64a5f+7EuGX45fjl+OX45fjl+OX45fjl+OX45fjl+OX45fjl+OX45fjl+OX45fjl+OX45fjnzz+H93bKG4KZW5kc3RyZWFtCmVuZG9iagoxNCAwIG9iago1MTYxMwplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDEzMSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDA1MjcwNyAwMDAwMCBuIAowMDAwMDAwNjUwIDAwMDAwIG4gCjAwMDAwMDA2NzEgMDAwMDAgbiAKMDAwMDAwMDc3MCAwMDAwMCBuIAowMDAwMDAwNzkxIDAwMDAwIG4gCjAwMDAwMDA4MTIgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk4IDAwMDAwIG4gCjAwMDAwMDA2MzAgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNjEwIDAwMDAwIG4gCjAwMDAwMDA4NDQgMDAwMDAgbiAKMDAwMDA1MjY4NSAwMDAwMCBuIAowMDAwMDUyNzY3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMTUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDE2ID4+CnN0YXJ0eHJlZgo1MjkyNAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:31.016958\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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MDIxMDkxNjE0NDEzMSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDA1OTMxMCAwMDAwMCBuIAowMDAwMDAwNjUwIDAwMDAwIG4gCjAwMDAwMDA2NzEgMDAwMDAgbiAKMDAwMDAwMDc3MCAwMDAwMCBuIAowMDAwMDAwNzkxIDAwMDAwIG4gCjAwMDAwMDA4MTIgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk4IDAwMDAwIG4gCjAwMDAwMDA2MzAgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNjEwIDAwMDAwIG4gCjAwMDAwMDA4NDQgMDAwMDAgbiAKMDAwMDA1OTI4OCAwMDAwMCBuIAowMDAwMDU5MzcwIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMTUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDE2ID4+CnN0YXJ0eHJlZgo1OTUyNwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:31.210678\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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6/mev7m7367frt+u367frt+u367frt+u367frt+u367frt+u367frt+u367frt+u367frv8/XP9vVT9WpwplbmRzdHJlYW0KZW5kb2JqCjE0IDAgb2JqCjU3NTA3CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagoxNSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTMxKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDE2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDU4NjAxIDAwMDAwIG4gCjAwMDAwMDA2NTAgMDAwMDAgbiAKMDAwMDAwMDY3MSAwMDAwMCBuIAowMDAwMDAwNzcwIDAwMDAwIG4gCjAwMDAwMDA3OTEgMDAwMDAgbiAKMDAwMDAwMDgxMiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTggMDAwMDAgbiAKMDAwMDAwMDYzMCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA2MTAgMDAwMDAgbiAKMDAwMDAwMDg0NCAwMDAwMCBuIAowMDAwMDU4NTc5IDAwMDAwIG4gCjAwMDAwNTg2NjEgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAxNSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMTYgPj4Kc3RhcnR4cmVmCjU4ODE4CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:31.412500\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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MkyItVYiRGJAGIdr9ZHfuQf/IQLjV/PvNOlCQkD66+OLHwdh27b/5X/+n/78T/9pHyQcR+dXGS+87sIOAWbqCWOo4b6fH6e78NfT+s3pnifosfnnibw/4a+3A1JJG5adFADQQZyGXCGvX3o/2t9fR4uAAP/ylz+/nn/bpv/r//zX/+Pu0TYJwzY21b6jCcAQxSxu6xaA88+PLJzz+3W7bduWeAIhMJG7rtvV3RgJAcYA0/0De7n580WJiBnd7OXlRXXHokzD9OBTIyT3UfiYVOQBFpA++ACQkz5mI8JSR1awANBHmIG8OE/x/EW/rcrgt3/9tqT9m/IW3rVQ//nrtZr67Sve/uG3VfS/4cv/W6+vk+7f/KN33/3bhfbm5cS/+lXgb+rdv/2b+Kb9fPMt3/Wn79/2m5d9LF/cv9nbvvYwwoN//cP8jQv/5iPGv/kMIt4Nt/eHKLGR9//47T99f0XE10X0dXiN797v23cK7wcqv13b/yvvKX7rTv8GA/r9r173qW9f+r/1+/4bXtmbF/GOLvC6y/1nFtf7r/T60t4yAY5Xj3/7Abxd4fH1vQZ+82jHty/h+Gx+S6/wmy/qb1bm37yWf+233v7Bt6vyN37z7fPzm1e8X2T/6oX/yiPz2//qN9b832wf/2ev7L/wel2nb+7Zf24z/y/6Fu9uHb7NSvu3f5Fvxr7/938S/4XXfpQe1xEllH/2f+VJ/62VCV8Xx/vH87f/EcBvfFB/e77+7aP9ykH6Nx3Gv1+/X79fv1+/X79fv1+/X79fv1+/X79fv1+/X79fv1+/X79fv1+/X79fv1+/X/+3Xv9/W5oO6QplbmRzdHJlYW0KZW5kb2JqCjE0IDAgb2JqCjY5NzcxCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagoxNSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTMxKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDE2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDcwODY1IDAwMDAwIG4gCjAwMDAwMDA2NTAgMDAwMDAgbiAKMDAwMDAwMDY3MSAwMDAwMCBuIAowMDAwMDAwNzcwIDAwMDAwIG4gCjAwMDAwMDA3OTEgMDAwMDAgbiAKMDAwMDAwMDgxMiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTggMDAwMDAgbiAKMDAwMDAwMDYzMCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA2MTAgMDAwMDAgbiAKMDAwMDAwMDg0NCAwMDAwMCBuIAowMDAwMDcwODQzIDAwMDAwIG4gCjAwMDAwNzA5MjUgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAxNSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMTYgPj4Kc3RhcnR4cmVmCjcxMDgyCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:31.614177\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/pdf": "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rK6SpgL0Mdwj3XrVal+xLG0GoaqybYZMSBYljqqt66Jq2XJmjMHg8BjZQQ5ora23FeQYIxj0Tkb4yHLQmPlmItJmNwUQeZ/EMnvvvfd/+Ie/Hef6fr//5//X//O//t3/Ng3GQ6d/sMCmKDnX70N1Egv+yY+IABjBUbJuGjOQJfdYvzOGMoQRo4ePsd/3968oDSNtvalm06XDHVOIjb7f334R0eXpCyB9jCDb7a/WlvzS//ZPf7nK3Icxfuff/9br+3Mj51b6KO7/517Xbztd1kcl+d93HfEYfjirj9/5K2P5H3ERV915sUO+ueS7f/2ffn3ngT/O1b8wK/zm6f6HXN/e6L93Xfi/aza/93Tfvd2/Znjz8483+Ma0+s4b/g0Ty+PHh7F93Hp8BMzKGv//m+ubp/sftjkPtx//xx7U6xD4cO5mx6l/2z0uf/83fPZf3r0nAP7feV7T4HlYO/1Yyfh/8HU9UefK/++5Hb89879dv12/Xb9dv12/Xb9dv12/Xb9dv12/Xb9dv12/Xb9dv12/Xb9dv12/Xb9dv13/M6//L3UEDL4KZW5kc3RyZWFtCmVuZG9iagoxNCAwIG9iago1NDI2OAplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDEzMSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDA1NTM2MiAwMDAwMCBuIAowMDAwMDAwNjUwIDAwMDAwIG4gCjAwMDAwMDA2NzEgMDAwMDAgbiAKMDAwMDAwMDc3MCAwMDAwMCBuIAowMDAwMDAwNzkxIDAwMDAwIG4gCjAwMDAwMDA4MTIgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk4IDAwMDAwIG4gCjAwMDAwMDA2MzAgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNjEwIDAwMDAwIG4gCjAwMDAwMDA4NDQgMDAwMDAgbiAKMDAwMDA1NTM0MCAwMDAwMCBuIAowMDAwMDU1NDIyIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMTUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDE2ID4+CnN0YXJ0eHJlZgo1NTU3OQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:31.816167\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Plot the closest images for the first N test images as example\n", "for i in range(8):\n", " find_similar_images(test_img_embeds[0][i], test_img_embeds[1][i], key_embeds=train_img_embeds)"]}, {"cell_type": "markdown", "id": "219968d0", "metadata": {"papermill": {"duration": 0.089878, "end_time": "2021-09-16T12:41:32.117877", "exception": false, "start_time": "2021-09-16T12:41:32.027999", "status": "completed"}, "tags": []}, "source": ["Based on our autoencoder, we see that we are able to retrieve many similar images to the test input.\n", "In particular, in row 4, we can spot that some test images might not be that different\n", "from the training set as we thought (same poster, just different scaling/color scaling).\n", "We also see that although we haven't given the model any labels,\n", "it can cluster different classes in different parts of the latent space (airplane + ship, animals, etc.).\n", "This is why autoencoders can also be used as a pre-training strategy for deep networks,\n", "especially when we have a large set of unlabeled images (often the case).\n", "However, it should be noted that the background still plays a big role in autoencoders while it doesn't for classification.\n", "Hence, we don't get \"perfect\" clusters and need to finetune such models for classification."]}, {"cell_type": "markdown", "id": "29ef8f54", "metadata": {"papermill": {"duration": 0.10508, "end_time": "2021-09-16T12:41:32.310940", "exception": false, "start_time": "2021-09-16T12:41:32.205860", "status": "completed"}, "tags": []}, "source": ["### Tensorboard clustering\n", "\n", "Another way of exploring the similarity of images in the latent space is by dimensionality-reduction methods like PCA or T-SNE.\n", "Luckily, Tensorboard provides a nice interface for this and we can make use of it in the following:"]}, {"cell_type": "code", "execution_count": 22, "id": "24541e26", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:32.501797Z", "iopub.status.busy": "2021-09-16T12:41:32.501308Z", "iopub.status.idle": "2021-09-16T12:41:32.503351Z", "shell.execute_reply": "2021-09-16T12:41:32.502954Z"}, "papermill": {"duration": 0.094572, "end_time": "2021-09-16T12:41:32.503454", "exception": false, "start_time": "2021-09-16T12:41:32.408882", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# We use the following model throughout this section.\n", "# If you want to try a different latent dimensionality, change it here!\n", "model = model_dict[128][\"model\"]"]}, {"cell_type": "code", "execution_count": 23, "id": "e6814b9e", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:32.685746Z", "iopub.status.busy": "2021-09-16T12:41:32.685203Z", "iopub.status.idle": "2021-09-16T12:41:32.687965Z", "shell.execute_reply": "2021-09-16T12:41:32.687568Z"}, "papermill": {"duration": 0.09501, "end_time": "2021-09-16T12:41:32.688067", "exception": false, "start_time": "2021-09-16T12:41:32.593057", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Create a summary writer\n", "writer = SummaryWriter(\"tensorboard/\")"]}, {"cell_type": "markdown", "id": "2e102b0e", "metadata": {"papermill": {"duration": 0.08963, "end_time": "2021-09-16T12:41:32.865990", "exception": false, "start_time": "2021-09-16T12:41:32.776360", "status": "completed"}, "tags": []}, "source": ["The function `add_embedding` allows us to add high-dimensional feature vectors to TensorBoard on which we can perform clustering.\n", "What we have to provide in the function are the feature vectors, additional metadata such as the labels,\n", "and the original images so that we can identify a specific image in the clustering."]}, {"cell_type": "code", "execution_count": 24, "id": "ad24adb2", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:33.048000Z", "iopub.status.busy": "2021-09-16T12:41:33.046657Z", "iopub.status.idle": "2021-09-16T12:41:33.048961Z", "shell.execute_reply": "2021-09-16T12:41:33.048577Z"}, "papermill": {"duration": 0.093963, "end_time": "2021-09-16T12:41:33.049070", "exception": false, "start_time": "2021-09-16T12:41:32.955107", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# In case you obtain the following error in the next cell, execute the import statements and last line in this cell\n", "# AttributeError: module 'tensorflow._api.v2.io.gfile' has no attribute 'get_filesystem'\n", "\n", "# import tensorflow as tf\n", "# import tensorboard as tb\n", "# tf.io.gfile = tb.compat.tensorflow_stub.io.gfile"]}, {"cell_type": "code", "execution_count": 25, "id": "5c4b01da", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:33.232442Z", "iopub.status.busy": "2021-09-16T12:41:33.231963Z", "iopub.status.idle": "2021-09-16T12:41:40.340354Z", "shell.execute_reply": "2021-09-16T12:41:40.339846Z"}, "papermill": {"duration": 7.201161, "end_time": "2021-09-16T12:41:40.340471", "exception": false, "start_time": "2021-09-16T12:41:33.139310", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Note: the embedding projector in tensorboard is computationally heavy.\n", "# Reduce the image amount below if your computer struggles with visualizing all 10k points\n", "NUM_IMGS = len(test_set)\n", "\n", "writer.add_embedding(\n", " test_img_embeds[1][:NUM_IMGS], # Encodings per image\n", " metadata=[test_set[i][1] for i in range(NUM_IMGS)], # Adding the labels per image to the plot\n", " label_img=(test_img_embeds[0][:NUM_IMGS] + 1) / 2.0,\n", ") # Adding the original images to the plot"]}, {"cell_type": "markdown", "id": "ac506cfa", "metadata": {"papermill": {"duration": 0.089483, "end_time": "2021-09-16T12:41:40.519563", "exception": false, "start_time": "2021-09-16T12:41:40.430080", "status": "completed"}, "tags": []}, "source": ["Finally, we can run tensorboard to explore similarities among images:"]}, {"cell_type": "code", "execution_count": 26, "id": "76094ffa", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:40.700535Z", "iopub.status.busy": "2021-09-16T12:41:40.700065Z", "iopub.status.idle": "2021-09-16T12:41:40.702162Z", "shell.execute_reply": "2021-09-16T12:41:40.701752Z"}, "papermill": {"duration": 0.094032, "end_time": "2021-09-16T12:41:40.702268", "exception": false, "start_time": "2021-09-16T12:41:40.608236", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Uncomment the next line to start the tensorboard\n", "# %tensorboard --logdir tensorboard/"]}, {"cell_type": "markdown", "id": "954ad9a4", "metadata": {"papermill": {"duration": 0.089519, "end_time": "2021-09-16T12:41:40.880536", "exception": false, "start_time": "2021-09-16T12:41:40.791017", "status": "completed"}, "tags": []}, "source": ["You should be able to see something similar as in the following image.\n", "In case the projector stays empty, try to start the TensorBoard outside of the Jupyter notebook.\n", "\n", "
\n", "\n", "Overall, we can see that the model indeed clustered images together that are visually similar.\n", "Especially the background color seems to be a crucial factor in the encoding.\n", "This correlates to the chosen loss function, here Mean Squared Error on pixel-level\n", "because the background is responsible for more than half of the pixels in an average image.\n", "Hence, the model learns to focus on it.\n", "Nevertheless, we can see that the encodings also separate a couple of classes in the latent space although it hasn't seen any labels.\n", "This shows again that autoencoding can also be used as a \"pre-training\"/transfer learning task before classification."]}, {"cell_type": "code", "execution_count": 27, "id": "ecbf40ea", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:41.069819Z", "iopub.status.busy": "2021-09-16T12:41:41.069325Z", "iopub.status.idle": "2021-09-16T12:41:41.071450Z", "shell.execute_reply": "2021-09-16T12:41:41.070984Z"}, "papermill": {"duration": 0.101313, "end_time": "2021-09-16T12:41:41.071558", "exception": false, "start_time": "2021-09-16T12:41:40.970245", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Closing the summary writer\n", "writer.close()"]}, {"cell_type": "markdown", "id": "d031e7eb", "metadata": {"papermill": {"duration": 0.088397, "end_time": "2021-09-16T12:41:41.248822", "exception": false, "start_time": "2021-09-16T12:41:41.160425", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this tutorial, we have implemented our own autoencoder on small RGB images and explored various properties of the model.\n", "In contrast to variational autoencoders, vanilla AEs are not generative and can work on MSE loss functions.\n", "This makes them often easier to train.\n", "Both versions of AE can be used for dimensionality reduction, as we have seen for finding visually similar images beyond pixel distances.\n", "Despite autoencoders gaining less interest in the research community due to their more \"theoretically\"\n", "challenging counterpart of VAEs, autoencoders still find usage in a lot of applications like denoising and compression.\n", "Hence, AEs are an essential tool that every Deep Learning engineer/researcher should be familiar with."]}, {"cell_type": "markdown", "id": "a94ed780", "metadata": {"papermill": {"duration": 0.088321, "end_time": "2021-09-16T12:41:41.426630", "exception": false, "start_time": "2021-09-16T12:41:41.338309", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 8: Deep Autoencoders\n", " :card_description: In this tutorial, we will take a closer look at autoencoders (AE). Autoencoders are trained on encoding input data such as images into a smaller feature vector, and afterward,...\n", " :tags: Image,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/08-deep-autoencoders.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab_type,colab,id,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 45.56469, "end_time": "2021-09-16T12:41:42.325691", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/08-deep-autoencoders/Deep_Autoencoders.ipynb", "output_path": ".notebooks/course_UvA-DL/08-deep-autoencoders.ipynb", "parameters": {}, "start_time": "2021-09-16T12:40:56.761001", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"02ff956b061147d68a9e47093990d34a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "032dc48ab9b841529716759ce8fda730": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_857875e983ac42ea9452284506e04bc6", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3f9a3874e40547b38769ed9fa108dae9", "value": 1.0}}, "052e7097bcfc4f65a96e2a7a9268431e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "07a7fcadf97f442ca3088a5bbbcba662": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_674fe5a450c3481c9a34d5e9192e82c0", "IPY_MODEL_bf0e3475b48945bc9619bf292f63051c", "IPY_MODEL_88da781ac6a04e4c9e80f207b9785e92"], "layout": "IPY_MODEL_44aeb54ba2f14b3bb7e032fabd114c92"}}, "07b7c4404f814aa5a589ef143940b644": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0cbcb50fc4904a38bf209a5b60ba6de2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0cdd717c36684008add2d6d3f93caa65": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_83d382db2c604f96864c7ab7bb703e70", "IPY_MODEL_e8bc86b2c81446f9bfcdb05b0c482531", "IPY_MODEL_5eeb17acc1fb4264ba59ec99aef37eb0"], "layout": "IPY_MODEL_214f0cd9ddc44f778d07cd0c33ebbc9d"}}, "0d066a20bb76435498f29b34812ebe38": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_11b51cc5b5204ca68070a5453a53a936", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_11acd217386e4efcacffe4cf2d9ff5d5", "value": 1.0}}, "0e75a210006e4d18be55bf96806992bb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "0f11cd1f8ebc4c9cbc69478f55dac457": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_2f4fba8d2685419fa7765bef262e0040", "IPY_MODEL_ba6b2c1b4af4442ca18b2a842fd567dc", "IPY_MODEL_74cec57148994f20859b662fef841d95"], "layout": "IPY_MODEL_b6fc2a070765455d9e39949c9f1ebcbb"}}, "11acd217386e4efcacffe4cf2d9ff5d5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "11b51cc5b5204ca68070a5453a53a936": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "11fb9b7b81cb40eab94743b2e2d33846": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "12134f3f7f2c45bb80b55727e4077eec": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5b35c5a56116497d9a3cb0665e9f8834", "placeholder": "\u200b", "style": "IPY_MODEL_62f2a45626e640b68afcc925672c9c9b", "value": "Testing: 100%"}}, "1242f7a22a564aaeb2af5c69667e5b5f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_585e2c48fa264803aa392ef3e9b5293e", "max": 175.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_47ee5b345c494618bf130b0afe0f0ee3", "value": 175.0}}, "12661357d2f64a5e81125f174cdb1093": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "143085e1474446dc918656c1e9794bce": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "18580ac83490417aa9cc17a95c1b9ff4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "1912fb8fcada468ebf4a0aa5485ace52": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1b5ee62eae394996877439600f6099ee": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1c6d3153688c46c98c7c47bf74d52109": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1eeadd6528c746868701b83f54b200dc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_f95afa94d4fc4c24982982b1189a2676", "IPY_MODEL_0d066a20bb76435498f29b34812ebe38", "IPY_MODEL_fe252e86330c4fb7a39557edc2947f62"], "layout": "IPY_MODEL_3bfb9f151a9b4190915c20d6daee85e6"}}, "213f1af06c854f0fa5ac9c7f42e86b8b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_587fdc2ccf4c434a98f70a1326e2090e", "IPY_MODEL_f6fd3371cdab45bdb95c5bbefc92bcbc", "IPY_MODEL_e4225879509b44d78af0a9382ffafb9e"], "layout": "IPY_MODEL_d88f9144beae4195b8b4061fd162f798"}}, "214f0cd9ddc44f778d07cd0c33ebbc9d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "23042c045dfa410788b0f5920d93ec12": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_31e9b650f9854da0889238dfe45f463e", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_be4e06b3dab944b4b164305c4639fa3f", "value": 1.0}}, "27a44cca69884cefac383be100126891": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2d005f68d81b4b48b123df6224ce852e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2d578b2eca444fd68545eb1bf1e2e4e3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2de27b190618498d9d521357b81fa7fe": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2f4fba8d2685419fa7765bef262e0040": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2d578b2eca444fd68545eb1bf1e2e4e3", "placeholder": "\u200b", "style": "IPY_MODEL_6db10f3f605d44cfa3ae7abd547994c6", "value": "Testing: 100%"}}, "2fb2f64a98454ffa9d94d792eedf790f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "31e9b650f9854da0889238dfe45f463e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3bfb9f151a9b4190915c20d6daee85e6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "3f9a3874e40547b38769ed9fa108dae9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "4019b8c2ccf54144af57dfeb0cc89fa1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f41f045f24ce4c3496a79fcd2cb4de07", "placeholder": "\u200b", "style": "IPY_MODEL_52802f27f74f437a86f46ec7faf29012", "value": " 173/175 [00:10<00:00, 22.02it/s]"}}, "4046be872d3b49a99d2e9e038031024f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4082d73bb40f41e38f650c4eb97de99a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "413b085486e84775a6d12632a2c21ae1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7800181f82774088bec790534ea16b09", "placeholder": "\u200b", "style": "IPY_MODEL_58efd7c3b680415992c128d95298a9e7", "value": " 36/40 [00:02<00:00, 23.55it/s]"}}, "41de7a8567ee401d91f43538a6074c58": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_bbf19a83e8f84c9f9aff45a99ca95f62", "placeholder": "\u200b", "style": "IPY_MODEL_4082d73bb40f41e38f650c4eb97de99a", "value": "Encoding images: 99%"}}, "43ed398167354898a99662e48cd235fc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "44aeb54ba2f14b3bb7e032fabd114c92": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "47c8df90b5414d1e8ee6e8afc2057c52": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_96b3327de0924445bda563cf42aa0924", "IPY_MODEL_4d5fc2a2e0f74d0cb56cdc13d48d251d", "IPY_MODEL_f5624c88273640a6af037ce8918fa0dc"], "layout": "IPY_MODEL_e3387fbc3e8e4cc4bdff0f609206a150"}}, "47ee5b345c494618bf130b0afe0f0ee3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "4c1be50487394dac88a9368c031dc7ac": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4d5fc2a2e0f74d0cb56cdc13d48d251d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f9a48cf7360a4948abc62fafc7e2ae37", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_4f32d4b2dd764f2a8af01d4382ea8605", "value": 1.0}}, "4da71fe1cdb746d781dbfcfb0078ea11": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d217a0bdcc694c7faa75b69d0b1d454a", "placeholder": "\u200b", "style": "IPY_MODEL_b22da0fb272442a6a9009a443d8af0aa", "value": "Testing: 100%"}}, "4f32d4b2dd764f2a8af01d4382ea8605": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "52802f27f74f437a86f46ec7faf29012": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "57b7fd1cc58c48e38d1ff2a4d1b52646": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "585e2c48fa264803aa392ef3e9b5293e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "587fdc2ccf4c434a98f70a1326e2090e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0cbcb50fc4904a38bf209a5b60ba6de2", "placeholder": "\u200b", "style": "IPY_MODEL_143085e1474446dc918656c1e9794bce", "value": "Testing: 100%"}}, "58efd7c3b680415992c128d95298a9e7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5adc386ad05f4b3cb2ee9972198d120a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5b35c5a56116497d9a3cb0665e9f8834": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5b9b18fb8e734702ad6e5fc7c5b33876": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "5cf0706e7c07460880d2fe93a1e1d4c4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_12134f3f7f2c45bb80b55727e4077eec", "IPY_MODEL_23042c045dfa410788b0f5920d93ec12", "IPY_MODEL_ad8c8c3a834347e4ad64626d981788b4"], "layout": "IPY_MODEL_9f61b932d4844223ba828954c8bed3fd"}}, "5da12d195bd64785adab94f45d45a515": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "5eeb17acc1fb4264ba59ec99aef37eb0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_abea5e10b6774b328a1a788602d20f15", "placeholder": "\u200b", "style": "IPY_MODEL_4046be872d3b49a99d2e9e038031024f", "value": " 40/40 [00:00<00:00, 64.57it/s]"}}, "62f2a45626e640b68afcc925672c9c9b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "674fe5a450c3481c9a34d5e9192e82c0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_07b7c4404f814aa5a589ef143940b644", "placeholder": "\u200b", "style": "IPY_MODEL_2d005f68d81b4b48b123df6224ce852e", "value": "Testing: 100%"}}, "6864ca7ec5744fae8c73bff0ac83cf8b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_a8007361e51a49a5bc066a39d3678f91", "IPY_MODEL_6ca429fcea5f49caa41079c5e0628bb2", "IPY_MODEL_413b085486e84775a6d12632a2c21ae1"], "layout": "IPY_MODEL_1b5ee62eae394996877439600f6099ee"}}, "6b9d419c65ee495298ceab7998051791": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "6ca429fcea5f49caa41079c5e0628bb2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8a4bb250050840ba8a1005f97cf7be6c", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_8bf4d98e6a9e4136b0902e882efc0796", "value": 40.0}}, "6db10f3f605d44cfa3ae7abd547994c6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "74cec57148994f20859b662fef841d95": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4c1be50487394dac88a9368c031dc7ac", "placeholder": "\u200b", "style": "IPY_MODEL_1c6d3153688c46c98c7c47bf74d52109", "value": " 20/20 [00:00<00:00, 49.16it/s]"}}, "75d4f899cdbb4ddb9f24362935235e37": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7648e1309ffe4d7b9df012f6c619e838": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "7800181f82774088bec790534ea16b09": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7f45f3088a694253bc20a6d06382a53a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "83d382db2c604f96864c7ab7bb703e70": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_12661357d2f64a5e81125f174cdb1093", "placeholder": "\u200b", "style": "IPY_MODEL_2de27b190618498d9d521357b81fa7fe", "value": "Testing: 100%"}}, "857875e983ac42ea9452284506e04bc6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8839373ba012435c92a3f7c82bec4d8e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "88da781ac6a04e4c9e80f207b9785e92": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_052e7097bcfc4f65a96e2a7a9268431e", "placeholder": "\u200b", "style": "IPY_MODEL_e3da440d50ac44b49c12bed6e738ead0", "value": " 20/20 [00:00<00:00, 47.70it/s]"}}, "8a4bb250050840ba8a1005f97cf7be6c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8bedc95274e84f799bbef9fc04ca9934": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_4da71fe1cdb746d781dbfcfb0078ea11", "IPY_MODEL_032dc48ab9b841529716759ce8fda730", "IPY_MODEL_d44f42635e084ca8afad50efccc551c5"], "layout": "IPY_MODEL_6b9d419c65ee495298ceab7998051791"}}, "8bf4d98e6a9e4136b0902e882efc0796": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "92a150b2ba204b55884a4344b05daa57": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "96b3327de0924445bda563cf42aa0924": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_02ff956b061147d68a9e47093990d34a", "placeholder": "\u200b", "style": "IPY_MODEL_7f45f3088a694253bc20a6d06382a53a", "value": "Testing: 100%"}}, "9f61b932d4844223ba828954c8bed3fd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "a8007361e51a49a5bc066a39d3678f91": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_92a150b2ba204b55884a4344b05daa57", "placeholder": "\u200b", "style": "IPY_MODEL_11fb9b7b81cb40eab94743b2e2d33846", "value": "Encoding images: 90%"}}, "abea5e10b6774b328a1a788602d20f15": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ad8c8c3a834347e4ad64626d981788b4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f92786d9462849c3b05616928d6445f3", "placeholder": "\u200b", "style": "IPY_MODEL_1912fb8fcada468ebf4a0aa5485ace52", "value": " 40/40 [00:00<00:00, 66.65it/s]"}}, "b03085321f064773bcbd73645caea3e5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b22da0fb272442a6a9009a443d8af0aa": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b6b8ff22558b4341af2937a7ea644f98": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b6fc2a070765455d9e39949c9f1ebcbb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "ba6b2c1b4af4442ca18b2a842fd567dc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_43ed398167354898a99662e48cd235fc", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_5b9b18fb8e734702ad6e5fc7c5b33876", "value": 1.0}}, "bbf19a83e8f84c9f9aff45a99ca95f62": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "be4e06b3dab944b4b164305c4639fa3f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "bec0502d94244f92bde1e6e0245723a2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "bf0e3475b48945bc9619bf292f63051c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8839373ba012435c92a3f7c82bec4d8e", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_7648e1309ffe4d7b9df012f6c619e838", "value": 1.0}}, "cdd59b859bba4f88984895bf930fcc0f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d217a0bdcc694c7faa75b69d0b1d454a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d44f42635e084ca8afad50efccc551c5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f1e290d5524546e0b62af75e706b6738", "placeholder": "\u200b", "style": "IPY_MODEL_b03085321f064773bcbd73645caea3e5", "value": " 20/20 [00:00<00:00, 47.57it/s]"}}, "d88f9144beae4195b8b4061fd162f798": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "da089a2c86cf484ab1e2470e7b666dc1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e22e704f14db414bb6c784ee29e158d6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e3387fbc3e8e4cc4bdff0f609206a150": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "e3da440d50ac44b49c12bed6e738ead0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e4225879509b44d78af0a9382ffafb9e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5adc386ad05f4b3cb2ee9972198d120a", "placeholder": "\u200b", "style": "IPY_MODEL_bec0502d94244f92bde1e6e0245723a2", "value": " 40/40 [00:00<00:00, 59.08it/s]"}}, "e8bc86b2c81446f9bfcdb05b0c482531": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cdd59b859bba4f88984895bf930fcc0f", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_5da12d195bd64785adab94f45d45a515", "value": 1.0}}, "f1e290d5524546e0b62af75e706b6738": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f41f045f24ce4c3496a79fcd2cb4de07": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f5624c88273640a6af037ce8918fa0dc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_da089a2c86cf484ab1e2470e7b666dc1", "placeholder": "\u200b", "style": "IPY_MODEL_b6b8ff22558b4341af2937a7ea644f98", "value": " 20/20 [00:00<00:00, 58.91it/s]"}}, "f6fd3371cdab45bdb95c5bbefc92bcbc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_75d4f899cdbb4ddb9f24362935235e37", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_18580ac83490417aa9cc17a95c1b9ff4", "value": 1.0}}, "f92786d9462849c3b05616928d6445f3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f95afa94d4fc4c24982982b1189a2676": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e22e704f14db414bb6c784ee29e158d6", "placeholder": "\u200b", "style": "IPY_MODEL_27a44cca69884cefac383be100126891", "value": "Testing: 100%"}}, "f9a48cf7360a4948abc62fafc7e2ae37": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fc07ca3d23014b6f9d5fb7b961f16005": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_41de7a8567ee401d91f43538a6074c58", "IPY_MODEL_1242f7a22a564aaeb2af5c69667e5b5f", "IPY_MODEL_4019b8c2ccf54144af57dfeb0cc89fa1"], "layout": "IPY_MODEL_57b7fd1cc58c48e38d1ff2a4d1b52646"}}, "fe252e86330c4fb7a39557edc2947f62": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2fb2f64a98454ffa9d94d792eedf790f", "placeholder": "\u200b", "style": "IPY_MODEL_0e75a210006e4d18be55bf96806992bb", "value": " 40/40 [00:00<00:00, 68.86it/s]"}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/09-normalizing-flows.ipynb b/source/notebooks/course_UvA-DL/09-normalizing-flows.ipynb deleted file mode 100644 index 704aefd..0000000 --- a/source/notebooks/course_UvA-DL/09-normalizing-flows.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "c331ad54", "metadata": {"papermill": {"duration": 0.030587, "end_time": "2021-09-16T12:41:51.358101", "exception": false, "start_time": "2021-09-16T12:41:51.327514", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 9: Normalizing Flows for Image Modeling\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-09-16T14:32:34.242172\n", "\n", "In this tutorial, we will take a closer look at complex, deep normalizing flows.\n", "The most popular, current application of deep normalizing flows is to model datasets of images.\n", "As for other generative models, images are a good domain to start working on because\n", "(1) CNNs are widely studied and strong models exist,\n", "(2) images are high-dimensional and complex,\n", "and (3) images are discrete integers.\n", "In this tutorial, we will review current advances in normalizing flows for image modeling,\n", "and get hands-on experience on coding normalizing flows.\n", "Note that normalizing flows are commonly parameter heavy and therefore computationally expensive.\n", "We will use relatively simple and shallow flows to save computational cost and allow you to run the notebook on CPU,\n", "but keep in mind that a simple way to improve the scores of the flows we study here is to make them deeper.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/09-normalizing-flows.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "cb8074e8", "metadata": {"papermill": {"duration": 0.029722, "end_time": "2021-09-16T12:41:51.416816", "exception": false, "start_time": "2021-09-16T12:41:51.387094", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "4c53849b", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-09-16T12:41:51.477802Z", "iopub.status.busy": "2021-09-16T12:41:51.477304Z", "iopub.status.idle": "2021-09-16T12:41:51.479831Z", "shell.execute_reply": "2021-09-16T12:41:51.479366Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.03456, "end_time": "2021-09-16T12:41:51.479947", "exception": false, "start_time": "2021-09-16T12:41:51.445387", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"seaborn\" \"tabulate\" \"matplotlib\" \"pytorch-lightning>=1.3\" \"torch>=1.6, <1.9\" \"torchmetrics>=0.3\" \"torchvision\""]}, {"cell_type": "markdown", "id": "03ad92b7", "metadata": {"papermill": {"duration": 0.02881, "end_time": "2021-09-16T12:41:51.538089", "exception": false, "start_time": "2021-09-16T12:41:51.509279", "status": "completed"}, "tags": []}, "source": ["
\n", "Throughout this notebook, we make use of [PyTorch Lightning](https://pytorch-lightning.readthedocs.io/en/latest/).\n", "The first cell imports our usual libraries."]}, {"cell_type": "code", "execution_count": 2, "id": "6dcb9f29", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:51.604711Z", "iopub.status.busy": "2021-09-16T12:41:51.604228Z", "iopub.status.idle": "2021-09-16T12:41:53.319528Z", "shell.execute_reply": "2021-09-16T12:41:53.320210Z"}, "papermill": {"duration": 1.753622, "end_time": "2021-09-16T12:41:53.320362", "exception": false, "start_time": "2021-09-16T12:41:51.566740", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_3359/964175757.py:27: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Using device cuda:0\n"]}], "source": ["import math\n", "import os\n", "import time\n", "import urllib.request\n", "from urllib.error import HTTPError\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "import tabulate\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision\n", "from IPython.display import HTML, display, set_matplotlib_formats\n", "from matplotlib.colors import to_rgb\n", "from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint\n", "from torchvision import transforms\n", "from torchvision.datasets import MNIST\n", "from tqdm.notebook import tqdm\n", "\n", "# %matplotlib inline\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "matplotlib.rcParams[\"lines.linewidth\"] = 2.0\n", "sns.reset_orig()\n", "\n", "# Path to the folder where the datasets are/should be downloaded (e.g. MNIST)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/tutorial11\")\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "# Fetching the device that will be used throughout this notebook\n", "device = torch.device(\"cpu\") if not torch.cuda.is_available() else torch.device(\"cuda:0\")\n", "print(\"Using device\", device)"]}, {"cell_type": "markdown", "id": "3cd3ed62", "metadata": {"papermill": {"duration": 0.029414, "end_time": "2021-09-16T12:41:53.382047", "exception": false, "start_time": "2021-09-16T12:41:53.352633", "status": "completed"}, "tags": []}, "source": ["Again, we have a few pretrained models. We download them below to the specified path above."]}, {"cell_type": "code", "execution_count": 3, "id": "78d63583", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:53.446715Z", "iopub.status.busy": "2021-09-16T12:41:53.445099Z", "iopub.status.idle": "2021-09-16T12:41:54.071021Z", "shell.execute_reply": "2021-09-16T12:41:54.070611Z"}, "papermill": {"duration": 0.659621, "end_time": "2021-09-16T12:41:54.071141", "exception": false, "start_time": "2021-09-16T12:41:53.411520", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial11/MNISTFlow_simple.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial11/MNISTFlow_vardeq.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial11/MNISTFlow_multiscale.ckpt...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial11/\"\n", "# Files to download\n", "pretrained_files = [\"MNISTFlow_simple.ckpt\", \"MNISTFlow_vardeq.ckpt\", \"MNISTFlow_multiscale.ckpt\"]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(\"Downloading %s...\" % file_url)\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "98588147", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.02986, "end_time": "2021-09-16T12:41:54.131396", "exception": false, "start_time": "2021-09-16T12:41:54.101536", "status": "completed"}, "tags": []}, "source": ["We will use the MNIST dataset in this notebook.\n", "MNIST constitutes, despite its simplicity, a challenge for small generative models as it requires the global understanding of an image.\n", "At the same time, we can easily judge whether generated images come from the same distribution as the dataset\n", "(i.e. represent real digits), or not.\n", "\n", "To deal better with the discrete nature of the images, we transform them\n", "from a range of 0-1 to a range of 0-255 as integers."]}, {"cell_type": "code", "execution_count": 4, "id": "47598513", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:54.196645Z", "iopub.status.busy": "2021-09-16T12:41:54.192801Z", "iopub.status.idle": "2021-09-16T12:41:54.232017Z", "shell.execute_reply": "2021-09-16T12:41:54.231591Z"}, "papermill": {"duration": 0.071068, "end_time": "2021-09-16T12:41:54.232133", "exception": false, "start_time": "2021-09-16T12:41:54.161065", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}], "source": ["# Convert images from 0-1 to 0-255 (integers)\n", "def discretize(sample):\n", " return (sample * 255).to(torch.int32)\n", "\n", "\n", "# Transformations applied on each image => make them a tensor and discretize\n", "transform = transforms.Compose([transforms.ToTensor(), discretize])\n", "\n", "# Loading the training dataset. We need to split it into a training and validation part\n", "train_dataset = MNIST(root=DATASET_PATH, train=True, transform=transform, download=True)\n", "pl.seed_everything(42)\n", "train_set, val_set = torch.utils.data.random_split(train_dataset, [50000, 10000])\n", "\n", "# Loading the test set\n", "test_set = MNIST(root=DATASET_PATH, train=False, transform=transform, download=True)\n", "\n", "# We define a set of data loaders that we can use for various purposes later.\n", "# Note that for actually training a model, we will use different data loaders\n", "# with a lower batch size.\n", "train_loader = data.DataLoader(train_set, batch_size=256, shuffle=False, drop_last=False)\n", "val_loader = data.DataLoader(val_set, batch_size=64, shuffle=False, drop_last=False, num_workers=4)\n", "test_loader = data.DataLoader(test_set, batch_size=64, shuffle=False, drop_last=False, num_workers=4)"]}, {"cell_type": "markdown", "id": "a20ba18c", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.030353, "end_time": "2021-09-16T12:41:54.293285", "exception": false, "start_time": "2021-09-16T12:41:54.262932", "status": "completed"}, "tags": []}, "source": ["In addition, we will define below a function to simplify the visualization of images/samples.\n", "Some training examples of the MNIST dataset is shown below."]}, {"cell_type": "code", "execution_count": 5, "id": "9f49ed4e", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:54.360403Z", "iopub.status.busy": "2021-09-16T12:41:54.359918Z", "iopub.status.idle": "2021-09-16T12:41:54.446277Z", "shell.execute_reply": "2021-09-16T12:41:54.445797Z"}, "papermill": {"duration": 0.12252, "end_time": "2021-09-16T12:41:54.446381", "exception": false, "start_time": "2021-09-16T12:41:54.323861", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:54.410044\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["def show_imgs(imgs, title=None, row_size=4):\n", " # Form a grid of pictures (we use max. 8 columns)\n", " num_imgs = imgs.shape[0] if isinstance(imgs, torch.Tensor) else len(imgs)\n", " is_int = imgs.dtype == torch.int32 if isinstance(imgs, torch.Tensor) else imgs[0].dtype == torch.int32\n", " nrow = min(num_imgs, row_size)\n", " ncol = int(math.ceil(num_imgs / nrow))\n", " imgs = torchvision.utils.make_grid(imgs, nrow=nrow, pad_value=128 if is_int else 0.5)\n", " np_imgs = imgs.cpu().numpy()\n", " # Plot the grid\n", " plt.figure(figsize=(1.5 * nrow, 1.5 * ncol))\n", " plt.imshow(np.transpose(np_imgs, (1, 2, 0)), interpolation=\"nearest\")\n", " plt.axis(\"off\")\n", " if title is not None:\n", " plt.title(title)\n", " plt.show()\n", " plt.close()\n", "\n", "\n", "show_imgs([train_set[i][0] for i in range(8)])"]}, {"cell_type": "markdown", "id": "122fbad4", "metadata": {"papermill": {"duration": 0.031364, "end_time": "2021-09-16T12:41:54.509186", "exception": false, "start_time": "2021-09-16T12:41:54.477822", "status": "completed"}, "tags": []}, "source": ["## Normalizing Flows as generative model\n", "\n", "In the previous lectures, we have seen Energy-based models, Variational Autoencoders (VAEs)\n", "and Generative Adversarial Networks (GANs) as example of generative models.\n", "However, none of them explicitly learn the probability density function $p(x)$ of the real input data.\n", "While VAEs model a lower bound, energy-based models only implicitly learn the probability density.\n", "GANs on the other hand provide us a sampling mechanism for generating new data, without offering a likelihood estimate.\n", "The generative model we will look at here, called Normalizing Flows, actually models the true data distribution\n", "$p(x)$ and provides us with an exact likelihood estimate.\n", "Below, we can visually compare VAEs, GANs and Flows\n", "(figure credit - [Lilian Weng](https://lilianweng.github.io/lil-log/2018/10/13/flow-based-deep-generative-models.html)):\n", "\n", "
\n", "\n", "The major difference compared to VAEs is that flows use *invertible* functions $f$\n", "to map the input data $x$ to a latent representation $z$.\n", "To realize this, $z$ must be of the same shape as $x$.\n", "This is in contrast to VAEs where $z$ is usually much lower dimensional than the original input data.\n", "However, an invertible mapping also means that for every data point $x$, we have a corresponding latent representation\n", "$z$ which allows us to perform lossless reconstruction ($z$ to $x$).\n", "In the visualization above, this means that $x=x'$ for flows, no matter what invertible function $f$ and input $x$ we choose.\n", "\n", "Nonetheless, how are normalizing flows modeling a probability density with an invertible function?\n", "The answer to this question is the rule for change of variables.\n", "Specifically, given a prior density $p_z(z)$ (e.g. Gaussian) and an invertible function $f$,\n", "we can determine $p_x(x)$ as follows:\n", "\n", "$$\n", "\\begin{split}\n", " \\int p_x(x) dx & = \\int p_z(z) dz = 1 \\hspace{1cm}\\text{(by definition of a probability distribution)}\\\\\n", " \\Leftrightarrow p_x(x) & = p_z(z) \\left|\\frac{dz}{dx}\\right| = p_z(f(x)) \\left|\\frac{df(x)}{dx}\\right|\n", "\\end{split}\n", "$$\n", "\n", "Hence, in order to determine the probability of $x$, we only need to determine its probability in latent space,\n", "and get the derivate of $f$.\n", "Note that this is for a univariate distribution, and $f$ is required to be invertible and smooth.\n", "For a multivariate case, the derivative becomes a Jacobian of which we need to take the determinant.\n", "As we usually use the log-likelihood as objective, we write the multivariate term with logarithms below:\n", "\n", "$$\n", "\\log p_x(\\mathbf{x}) = \\log p_z(f(\\mathbf{x})) + \\log{} \\left|\\det \\frac{df(\\mathbf{x})}{d\\mathbf{x}}\\right|\n", "$$\n", "\n", "Although we now know how a normalizing flow obtains its likelihood, it might not be clear what a normalizing flow does intuitively.\n", "For this, we should look from the inverse perspective of the flow starting with the prior probability density $p_z(z)$.\n", "If we apply an invertible function on it, we effectively \"transform\" its probability density.\n", "For instance, if $f^{-1}(z)=z+1$, we shift the density by one while still remaining a valid probability distribution,\n", "and being invertible.\n", "We can also apply more complex transformations, like scaling: $f^{-1}(z)=2z+1$, but there you might see a difference.\n", "When you scale, you also change the volume of the probability density, as for example on uniform distributions\n", "(figure credit - [Eric Jang](https://blog.evjang.com/2018/01/nf1.html)):\n", "\n", "
\n", "\n", "You can see that the height of $p(y)$ should be lower than $p(x)$ after scaling.\n", "This change in volume represents $\\left|\\frac{df(x)}{dx}\\right|$ in our equation above,\n", "and ensures that even after scaling, we still have a valid probability distribution.\n", "We can go on with making our function $f$ more complex.\n", "However, the more complex $f$ becomes, the harder it will be to find the inverse $f^{-1}$ of it,\n", "and to calculate the log-determinant of the Jacobian $\\log{} \\left|\\det \\frac{df(\\mathbf{x})}{d\\mathbf{x}}\\right|$.\n", "An easier trick to stack multiple invertible functions $f_{1,...,K}$ after each other, as all together,\n", "they still represent a single, invertible function.\n", "Using multiple, learnable invertible functions, a normalizing flow attempts to transform\n", "$p_z(z)$ slowly into a more complex distribution which should finally be $p_x(x)$.\n", "We visualize the idea below\n", "(figure credit - [Lilian Weng](https://lilianweng.github.io/lil-log/2018/10/13/flow-based-deep-generative-models.html)):\n", "\n", "
\n", "\n", "Starting from $z_0$, which follows the prior Gaussian distribution, we sequentially apply the invertible\n", "functions $f_1,f_2,...,f_K$, until $z_K$ represents $x$.\n", "Note that in the figure above, the functions $f$ represent the inverted function from $f$ we had above\n", "(here: $f:Z\\to X$, above: $f:X\\to Z$).\n", "This is just a different notation and has no impact on the actual flow design because all $f$ need to be invertible anyways.\n", "When we estimate the log likelihood of a data point $x$ as in the equations above,\n", "we run the flows in the opposite direction than visualized above.\n", "Multiple flow layers have been proposed that use a neural network as learnable parameters,\n", "such as the planar and radial flow.\n", "However, we will focus here on flows that are commonly used in image\n", "modeling, and will discuss them in the rest of the notebook along with\n", "the details of how to train a normalizing flow."]}, {"cell_type": "markdown", "id": "3f9b2256", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.030707, "end_time": "2021-09-16T12:41:54.571000", "exception": false, "start_time": "2021-09-16T12:41:54.540293", "status": "completed"}, "tags": []}, "source": ["## Normalizing Flows on images\n", "\n", "
\n", "\n", "To become familiar with normalizing flows, especially for the application of image modeling,\n", "it is best to discuss the different elements in a flow along with the implementation.\n", "As a general concept, we want to build a normalizing flow that maps an input image (here MNIST) to an equally sized latent space:\n", "\n", "
\n", "\n", "As a first step, we will implement a template of a normalizing flow in PyTorch Lightning.\n", "During training and validation, a normalizing flow performs density estimation in the forward direction.\n", "For this, we apply a series of flow transformations on the input $x$ and estimate the probability\n", "of the input by determining the probability of the transformed point $z$ given a prior,\n", "and the change of volume caused by the transformations.\n", "During inference, we can do both density estimation and sampling new points by inverting the flow transformations.\n", "Therefore, we define a function `_get_likelihood` which performs density estimation,\n", "and `sample` to generate new examples.\n", "The functions `training_step`, `validation_step` and `test_step` all make use of `_get_likelihood`.\n", "\n", "The standard metric used in generative models, and in particular normalizing flows, is bits per dimensions (bpd).\n", "Bpd is motivated from an information theory perspective and describes how many bits we would need to encode a particular example in our modeled distribution.\n", "The less bits we need, the more likely the example in our distribution.\n", "When we test for the bits per dimension of our test dataset, we can judge whether our model generalizes to new samples of the dataset and didn't memorize the training dataset.\n", "In order to calculate the bits per dimension score, we can rely on the negative log-likelihood and change the log base (as bits are binary while NLL is usually exponential):\n", "\n", "$$\\text{bpd} = \\text{nll} \\cdot \\log_2\\left(\\exp(1)\\right) \\cdot \\left(\\prod d_i\\right)^{-1}$$\n", "\n", "where $d_1,...,d_K$ are the dimensions of the input.\n", "For images, this would be the height, width and channel number.\n", "We divide the log likelihood by these extra dimensions to have a metric which we can compare for different image resolutions.\n", "In the original image space, MNIST examples have a bits per dimension\n", "score of 8 (we need 8 bits to encode each pixel as there are 256\n", "possible values)."]}, {"cell_type": "code", "execution_count": 6, "id": "f731caad", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:54.645154Z", "iopub.status.busy": "2021-09-16T12:41:54.634808Z", "iopub.status.idle": "2021-09-16T12:41:54.647412Z", "shell.execute_reply": "2021-09-16T12:41:54.647011Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.045385, "end_time": "2021-09-16T12:41:54.647510", "exception": false, "start_time": "2021-09-16T12:41:54.602125", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ImageFlow(pl.LightningModule):\n", " def __init__(self, flows, import_samples=8):\n", " \"\"\"\n", " Args:\n", " flows: A list of flows (each a nn.Module) that should be applied on the images.\n", " import_samples: Number of importance samples to use during testing (see explanation below). Can be changed at any time\n", " \"\"\"\n", " super().__init__()\n", " self.flows = nn.ModuleList(flows)\n", " self.import_samples = import_samples\n", " # Create prior distribution for final latent space\n", " self.prior = torch.distributions.normal.Normal(loc=0.0, scale=1.0)\n", " # Example input for visualizing the graph\n", " self.example_input_array = train_set[0][0].unsqueeze(dim=0)\n", "\n", " def forward(self, imgs):\n", " # The forward function is only used for visualizing the graph\n", " return self._get_likelihood(imgs)\n", "\n", " def encode(self, imgs):\n", " # Given a batch of images, return the latent representation z and ldj of the transformations\n", " z, ldj = imgs, torch.zeros(imgs.shape[0], device=self.device)\n", " for flow in self.flows:\n", " z, ldj = flow(z, ldj, reverse=False)\n", " return z, ldj\n", "\n", " def _get_likelihood(self, imgs, return_ll=False):\n", " \"\"\"Given a batch of images, return the likelihood of those.\n", "\n", " If return_ll is True, this function returns the log likelihood of the input. Otherwise, the ouptut metric is\n", " bits per dimension (scaled negative log likelihood)\n", " \"\"\"\n", " z, ldj = self.encode(imgs)\n", " log_pz = self.prior.log_prob(z).sum(dim=[1, 2, 3])\n", " log_px = ldj + log_pz\n", " nll = -log_px\n", " # Calculating bits per dimension\n", " bpd = nll * np.log2(np.exp(1)) / np.prod(imgs.shape[1:])\n", " return bpd.mean() if not return_ll else log_px\n", "\n", " @torch.no_grad()\n", " def sample(self, img_shape, z_init=None):\n", " \"\"\"Sample a batch of images from the flow.\"\"\"\n", " # Sample latent representation from prior\n", " if z_init is None:\n", " z = self.prior.sample(sample_shape=img_shape).to(device)\n", " else:\n", " z = z_init.to(device)\n", "\n", " # Transform z to x by inverting the flows\n", " ldj = torch.zeros(img_shape[0], device=device)\n", " for flow in reversed(self.flows):\n", " z, ldj = flow(z, ldj, reverse=True)\n", " return z\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.Adam(self.parameters(), lr=1e-3)\n", " # An scheduler is optional, but can help in flows to get the last bpd improvement\n", " scheduler = optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.99)\n", " return [optimizer], [scheduler]\n", "\n", " def training_step(self, batch, batch_idx):\n", " # Normalizing flows are trained by maximum likelihood => return bpd\n", " loss = self._get_likelihood(batch[0])\n", " self.log(\"train_bpd\", loss)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " loss = self._get_likelihood(batch[0])\n", " self.log(\"val_bpd\", loss)\n", "\n", " def test_step(self, batch, batch_idx):\n", " # Perform importance sampling during testing => estimate likelihood M times for each image\n", " samples = []\n", " for _ in range(self.import_samples):\n", " img_ll = self._get_likelihood(batch[0], return_ll=True)\n", " samples.append(img_ll)\n", " img_ll = torch.stack(samples, dim=-1)\n", "\n", " # To average the probabilities, we need to go from log-space to exp, and back to log.\n", " # Logsumexp provides us a stable implementation for this\n", " img_ll = torch.logsumexp(img_ll, dim=-1) - np.log(self.import_samples)\n", "\n", " # Calculate final bpd\n", " bpd = -img_ll * np.log2(np.exp(1)) / np.prod(batch[0].shape[1:])\n", " bpd = bpd.mean()\n", "\n", " self.log(\"test_bpd\", bpd)"]}, {"cell_type": "markdown", "id": "bde3e5a1", "metadata": {"papermill": {"duration": 0.030814, "end_time": "2021-09-16T12:41:54.709142", "exception": false, "start_time": "2021-09-16T12:41:54.678328", "status": "completed"}, "tags": []}, "source": ["The `test_step` function differs from the training and validation step in that it makes use of importance sampling.\n", "We will discuss the motiviation and details behind this after\n", "understanding how flows model discrete images in continuous space."]}, {"cell_type": "markdown", "id": "2fbe3846", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.030971, "end_time": "2021-09-16T12:41:54.770996", "exception": false, "start_time": "2021-09-16T12:41:54.740025", "status": "completed"}, "tags": []}, "source": ["### Dequantization\n", "\n", "Normalizing flows rely on the rule of change of variables, which is naturally defined in continuous space.\n", "Applying flows directly on discrete data leads to undesired density models where arbitrarly high likelihood are placed on a few, particular values.\n", "See the illustration below:\n", "\n", "
\n", "\n", "The black points represent the discrete points, and the green volume the density modeled by a normalizing flow in continuous space.\n", "The flow would continue to increase the likelihood for $x=0,1,2,3$ while having no volume on any other point.\n", "Remember that in continuous space, we have the constraint that the overall volume of the probability density must be 1 ($\\int p(x)dx=1$).\n", "Otherwise, we don't model a probability distribution anymore.\n", "However, the discrete points $x=0,1,2,3$ represent delta peaks with no width in continuous space.\n", "This is why the flow can place an infinite high likelihood on these few points while still representing a distribution in continuous space.\n", "Nonetheless, the learned density does not tell us anything about the distribution among the discrete points,\n", "as in discrete space, the likelihoods of those four points would have to sum to 1, not to infinity.\n", "\n", "To prevent such degenerated solutions, a common solution is to add a small amount of noise to each discrete value, which is also referred to as dequantization.\n", "Considering $x$ as an integer (as it is the case for images), the dequantized representation $v$ can be formulated as $v=x+u$ where $u\\in[0,1)^D$.\n", "Thus, the discrete value $1$ is modeled by a distribution over the interval $[1.0, 2.0)$, the value $2$ by an volume over $[2.0, 3.0)$, etc.\n", "Our objective of modeling $p(x)$ becomes:\n", "\n", "$$ p(x) = \\int p(x+u)du = \\int \\frac{q(u|x)}{q(u|x)}p(x+u)du = \\mathbb{E}_{u\\sim q(u|x)}\\left[\\frac{p(x+u)}{q(u|x)} \\right]$$\n", "\n", "with $q(u|x)$ being the noise distribution.\n", "For now, we assume it to be uniform, which can also be written as $p(x)=\\mathbb{E}_{u\\sim U(0,1)^D}\\left[p(x+u) \\right]$.\n", "\n", "In the following, we will implement Dequantization as a flow transformation itself.\n", "After adding noise to the discrete values, we additionally transform the volume into a Gaussian-like shape.\n", "This is done by scaling $x+u$ between $0$ and $1$, and applying the invert of the sigmoid function $\\sigma(z)^{-1} = \\log z - \\log 1-z$.\n", "If we would not do this, we would face two problems:\n", "\n", "1.\n", "The input is scaled between 0 and 256 while the prior distribution is a Gaussian with mean $0$ and standard deviation $1$.\n", "In the first iterations after initializing the parameters of the flow, we would have extremely low likelihoods for large values like $256$.\n", "This would cause the training to diverge instantaneously.\n", "2.\n", "As the output distribution is a Gaussian, it is beneficial for the flow to have a similarly shaped input distribution.\n", "This will reduce the modeling complexity that is required by the flow.\n", "\n", "Overall, we can implement dequantization as follows:"]}, {"cell_type": "code", "execution_count": 7, "id": "14292232", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:54.841782Z", "iopub.status.busy": "2021-09-16T12:41:54.841282Z", "iopub.status.idle": "2021-09-16T12:41:54.843421Z", "shell.execute_reply": "2021-09-16T12:41:54.842957Z"}, "papermill": {"duration": 0.040991, "end_time": "2021-09-16T12:41:54.843517", "exception": false, "start_time": "2021-09-16T12:41:54.802526", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Dequantization(nn.Module):\n", " def __init__(self, alpha=1e-5, quants=256):\n", " \"\"\"\n", " Args:\n", " alpha: small constant that is used to scale the original input.\n", " Prevents dealing with values very close to 0 and 1 when inverting the sigmoid\n", " quants: Number of possible discrete values (usually 256 for 8-bit image)\n", " \"\"\"\n", " super().__init__()\n", " self.alpha = alpha\n", " self.quants = quants\n", "\n", " def forward(self, z, ldj, reverse=False):\n", " if not reverse:\n", " z, ldj = self.dequant(z, ldj)\n", " z, ldj = self.sigmoid(z, ldj, reverse=True)\n", " else:\n", " z, ldj = self.sigmoid(z, ldj, reverse=False)\n", " z = z * self.quants\n", " ldj += np.log(self.quants) * np.prod(z.shape[1:])\n", " z = torch.floor(z).clamp(min=0, max=self.quants - 1).to(torch.int32)\n", " return z, ldj\n", "\n", " def sigmoid(self, z, ldj, reverse=False):\n", " # Applies an invertible sigmoid transformation\n", " if not reverse:\n", " ldj += (-z - 2 * F.softplus(-z)).sum(dim=[1, 2, 3])\n", " z = torch.sigmoid(z)\n", " else:\n", " z = z * (1 - self.alpha) + 0.5 * self.alpha # Scale to prevent boundaries 0 and 1\n", " ldj += np.log(1 - self.alpha) * np.prod(z.shape[1:])\n", " ldj += (-torch.log(z) - torch.log(1 - z)).sum(dim=[1, 2, 3])\n", " z = torch.log(z) - torch.log(1 - z)\n", " return z, ldj\n", "\n", " def dequant(self, z, ldj):\n", " # Transform discrete values to continuous volumes\n", " z = z.to(torch.float32)\n", " z = z + torch.rand_like(z).detach()\n", " z = z / self.quants\n", " ldj -= np.log(self.quants) * np.prod(z.shape[1:])\n", " return z, ldj"]}, {"cell_type": "markdown", "id": "d1d08852", "metadata": {"papermill": {"duration": 0.030723, "end_time": "2021-09-16T12:41:54.905082", "exception": false, "start_time": "2021-09-16T12:41:54.874359", "status": "completed"}, "tags": []}, "source": ["A good check whether a flow is correctly implemented or not, is to verify that it is invertible.\n", "Hence, we will dequantize a randomly chosen training image, and then quantize it again.\n", "We would expect that we would get the exact same image out:"]}, {"cell_type": "code", "execution_count": 8, "id": "728daf90", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:54.972326Z", "iopub.status.busy": "2021-09-16T12:41:54.971852Z", "iopub.status.idle": "2021-09-16T12:41:54.978174Z", "shell.execute_reply": "2021-09-16T12:41:54.977694Z"}, "papermill": {"duration": 0.042049, "end_time": "2021-09-16T12:41:54.978271", "exception": false, "start_time": "2021-09-16T12:41:54.936222", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Dequantization was not invertible.\n", "Original value: 0\n", "Reconstructed value: 1\n"]}], "source": ["# Testing invertibility of dequantization layer\n", "pl.seed_everything(42)\n", "orig_img = train_set[0][0].unsqueeze(dim=0)\n", "ldj = torch.zeros(\n", " 1,\n", ")\n", "dequant_module = Dequantization()\n", "deq_img, ldj = dequant_module(orig_img, ldj, reverse=False)\n", "reconst_img, ldj = dequant_module(deq_img, ldj, reverse=True)\n", "\n", "d1, d2 = torch.where(orig_img.squeeze() != reconst_img.squeeze())\n", "if len(d1) != 0:\n", " print(\"Dequantization was not invertible.\")\n", " for i in range(d1.shape[0]):\n", " print(\"Original value:\", orig_img[0, 0, d1[i], d2[i]].item())\n", " print(\"Reconstructed value:\", reconst_img[0, 0, d1[i], d2[i]].item())\n", "else:\n", " print(\"Successfully inverted dequantization\")\n", "\n", "# Layer is not strictly invertible due to float precision constraints\n", "# assert (orig_img == reconst_img).all().item()"]}, {"cell_type": "markdown", "id": "063694b6", "metadata": {"papermill": {"duration": 0.031526, "end_time": "2021-09-16T12:41:55.042165", "exception": false, "start_time": "2021-09-16T12:41:55.010639", "status": "completed"}, "tags": []}, "source": ["In contrast to our expectation, the test fails.\n", "However, this is no reason to doubt our implementation here as only one single value is not equal to the original.\n", "This is caused due to numerical inaccuracies in the sigmoid invert.\n", "While the input space to the inverted sigmoid is scaled between 0 and 1, the output space is between $-\\infty$ and $\\infty$.\n", "And as we use 32 bits to represent the numbers (in addition to applying logs over and over again),\n", "such inaccuries can occur and should not be worrisome.\n", "Nevertheless, it is good to be aware of them, and can be improved by using a double tensor (float64).\n", "\n", "Finally, we can take our dequantization and actually visualize the\n", "distribution it transforms the discrete values into:"]}, {"cell_type": "code", "execution_count": 9, "id": "9e42101f", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:55.118016Z", "iopub.status.busy": "2021-09-16T12:41:55.117517Z", "iopub.status.idle": "2021-09-16T12:41:55.544694Z", "shell.execute_reply": "2021-09-16T12:41:55.545080Z"}, "papermill": {"duration": 0.469437, "end_time": "2021-09-16T12:41:55.545218", "exception": false, "start_time": "2021-09-16T12:41:55.075781", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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ZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9EIDE3IDAgUiAvUCAxOCAwIFIgL2EgMTkgMCBSIC9iIDIwIDAgUiAvYyAyMSAwIFIgL2QgMjIgMCBSIC9lIDIzIDAgUgovZWlnaHQgMjQgMCBSIC9mIDI1IDAgUiAvZml2ZSAyNiAwIFIgL2ZvdXIgMjcgMCBSIC9oeXBoZW4gMjggMCBSIC9pIDI5IDAgUgovbCAzMCAwIFIgL24gMzEgMCBSIC9uaW5lIDMyIDAgUiAvbyAzMyAwIFIgL29uZSAzNCAwIFIgL3BlcmlvZCAzNSAwIFIKL3EgMzYgMCBSIC9yIDM3IDAgUiAvcyAzOCAwIFIgL3NldmVuIDM5IDAgUiAvc2l4IDQwIDAgUiAvc3BhY2UgNDEgMCBSCi90IDQyIDAgUiAvdGhyZWUgNDMgMCBSIC90d28gNDQgMCBSIC91IDQ1IDAgUiAvdiA0NiAwIFIgL3kgNDcgMCBSIC96IDQ4IDAgUgovemVybyA0OSAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDAuNSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4KL0E0IDw8IC9DQSAwLjggL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC44ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8ID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago1MCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTU1KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDUxCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDI0NDM0IDAwMDAwIG4gCjAwMDAwMjQxNTQgMDAwMDAgbiAKMDAwMDAyNDE4NiAwMDAwMCBuIAowMDAwMDI0MzcxIDAwMDAwIG4gCjAwMDAwMjQzOTIgMDAwMDAgbiAKMDAwMDAyNDQxMyAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDAgMDAwMDAgbiAKMDAwMDAxMzU4MSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMTM1NTkgMDAwMDAgbiAKMDAwMDAyMjcwNSAwMDAwMCBuIAowMDAwMDIyNTA1IDAwMDAwIG4gCjAwMDAwMjIwMzEgMDAwMDAgbiAKMDAwMDAyMzc1OCAwMDAwMCBuIAowMDAwMDEzNjAxIDAwMDAwIG4gCjAwMDAwMTM4MzggMDAwMDAgbiAKMDAwMDAxNDA4MSAwMDAwMCBuIAowMDAwMDE0NDYxIDAwMDAwIG4gCjAwMDAwMTQ3NzggMDAwMDAgbiAKMDAwMDAxNTA4MyAwMDAwMCBuIAowMDAwMDE1Mzg3IDAwMDAwIG4gCjAwMDAwMTU3MDkgMDAwMDAgbiAKMDAwMDAxNjE3NyAwMDAwMCBuIAowMDAwMDE2Mzg2IDAwMDAwIG4gCjAwMDAwMTY3MDggMDAwMDAgbiAKMDAwMDAxNjg3NCAwMDAwMCBuIAowMDAwMDE3MDAwIDAwMDAwIG4gCjAwMDAwMTcxNDQgMDAwMDAgbiAKMDAwMDAxNzI2MyAwMDAwMCBuIAowMDAwMDE3NDk5IDAwMDAwIG4gCjAwMDAwMTc4OTQgMDAwMDAgbiAKMDAwMDAxODE4NSAwMDAwMCBuIAowMDAwMDE4MzQwIDAwMDAwIG4gCjAwMDAwMTg0NjMgMDAwMDAgbiAKMDAwMDAxODc3OSAwMDAwMCBuIAowMDAwMDE5MDEyIDAwMDAwIG4gCjAwMDAwMTk0MTkgMDAwMDAgbiAKMDAwMDAxOTU2MSAwMDAwMCBuIAowMDAwMDE5OTU0IDAwMDAwIG4gCjAwMDAwMjAwNDQgMDAwMDAgbiAKMDAwMDAyMDI1MCAwMDAwMCBuIAowMDAwMDIwNjYzIDAwMDAwIG4gCjAwMDAwMjA5ODcgMDAwMDAgbiAKMDAwMDAyMTIzNCAwMDAwMCBuIAowMDAwMDIxMzgxIDAwMDAwIG4gCjAwMDAwMjE1OTUgMDAwMDAgbiAKMDAwMDAyMTc0MyAwMDAwMCBuIAowMDAwMDI0NDk0IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNTAgMCBSIC9Sb290IDEgMCBSIC9TaXplIDUxID4+CnN0YXJ0eHJlZgoyNDY1MQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:55.273989\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["\n", "\n", "def visualize_dequantization(quants, prior=None):\n", " \"\"\"Function for visualizing the dequantization values of discrete values in continuous space.\"\"\"\n", " # Prior over discrete values. If not given, a uniform is assumed\n", " if prior is None:\n", " prior = np.ones(quants, dtype=np.float32) / quants\n", " prior = prior / prior.sum() * quants # In the following, we assume 1 for each value means uniform distribution\n", "\n", " inp = torch.arange(-4, 4, 0.01).view(-1, 1, 1, 1) # Possible continuous values we want to consider\n", " ldj = torch.zeros(inp.shape[0])\n", " dequant_module = Dequantization(quants=quants)\n", " # Invert dequantization on continuous values to find corresponding discrete value\n", " out, ldj = dequant_module.forward(inp, ldj, reverse=True)\n", " inp, out, prob = inp.squeeze().numpy(), out.squeeze().numpy(), ldj.exp().numpy()\n", " prob = prob * prior[out] # Probability scaled by categorical prior\n", "\n", " # Plot volumes and continuous distribution\n", " sns.set_style(\"white\")\n", " _ = plt.figure(figsize=(6, 3))\n", " x_ticks = []\n", " for v in np.unique(out):\n", " indices = np.where(out == v)\n", " color = to_rgb(\"C%i\" % v)\n", " plt.fill_between(inp[indices], prob[indices], np.zeros(indices[0].shape[0]), color=color + (0.5,), label=str(v))\n", " plt.plot([inp[indices[0][0]]] * 2, [0, prob[indices[0][0]]], color=color)\n", " plt.plot([inp[indices[0][-1]]] * 2, [0, prob[indices[0][-1]]], color=color)\n", " x_ticks.append(inp[indices[0][0]])\n", " x_ticks.append(inp.max())\n", " plt.xticks(x_ticks, [\"%.1f\" % x for x in x_ticks])\n", " plt.plot(inp, prob, color=(0.0, 0.0, 0.0))\n", " # Set final plot properties\n", " plt.ylim(0, prob.max() * 1.1)\n", " plt.xlim(inp.min(), inp.max())\n", " plt.xlabel(\"z\")\n", " plt.ylabel(\"Probability\")\n", " plt.title(\"Dequantization distribution for %i discrete values\" % quants)\n", " plt.legend()\n", " plt.show()\n", " plt.close()\n", "\n", "\n", "visualize_dequantization(quants=8)"]}, {"cell_type": "markdown", "id": "b4e465fe", "metadata": {"papermill": {"duration": 0.034926, "end_time": "2021-09-16T12:41:55.616011", "exception": false, "start_time": "2021-09-16T12:41:55.581085", "status": "completed"}, "tags": []}, "source": ["The visualized distribution show the sub-volumes that are assigned to the different discrete values.\n", "The value $0$ has its volume between $[-\\infty, -1.9)$, the value $1$ is represented by the interval $[-1.9, -1.1)$, etc.\n", "The volume for each discrete value has the same probability mass.\n", "That's why the volumes close to the center (e.g. 3 and 4) have a smaller area on the z-axis as others\n", "($z$ is being used to denote the output of the whole dequantization flow).\n", "\n", "Effectively, the consecutive normalizing flow models discrete images by the following objective:\n", "\n", "$$\\log p(x) = \\log \\mathbb{E}_{u\\sim q(u|x)}\\left[\\frac{p(x+u)}{q(u|x)} \\right] \\geq \\mathbb{E}_{u}\\left[\\log \\frac{p(x+u)}{q(u|x)} \\right]$$\n", "\n", "Although normalizing flows are exact in likelihood, we have a lower bound.\n", "Specifically, this is an example of the Jensen inequality because we need to move the log into the expectation so we can use Monte-carlo estimates.\n", "In general, this bound is considerably smaller than the ELBO in variational autoencoders.\n", "Actually, we can reduce the bound ourselves by estimating the expectation not by one, but by $M$ samples.\n", "In other words, we can apply importance sampling which leads to the following inequality:\n", "\n", "$$\\log p(x) = \\log \\mathbb{E}_{u\\sim q(u|x)}\\left[\\frac{p(x+u)}{q(u|x)} \\right] \\geq \\mathbb{E}_{u}\\left[\\log \\frac{1}{M} \\sum_{m=1}^{M} \\frac{p(x+u_m)}{q(u_m|x)} \\right] \\geq \\mathbb{E}_{u}\\left[\\log \\frac{p(x+u)}{q(u|x)} \\right]$$\n", "\n", "The importance sampling $\\frac{1}{M} \\sum_{m=1}^{M} \\frac{p(x+u_m)}{q(u_m|x)}$ becomes\n", "$\\mathbb{E}_{u\\sim q(u|x)}\\left[\\frac{p(x+u)}{q(u|x)} \\right]$ if $M\\to \\infty$,\n", "so that the more samples we use, the tighter the bound is.\n", "During testing, we can make use of this property and have it implemented in `test_step` in `ImageFlow`.\n", "In theory, we could also use this tighter bound during training.\n", "However, related work has shown that this does not necessarily lead to\n", "an improvement given the additional computational cost, and it is more\n", "efficient to stick with a single estimate [5]."]}, {"cell_type": "markdown", "id": "decab6f5", "metadata": {"papermill": {"duration": 0.03541, "end_time": "2021-09-16T12:41:55.686454", "exception": false, "start_time": "2021-09-16T12:41:55.651044", "status": "completed"}, "tags": []}, "source": ["### Variational Dequantization\n", "\n", "Dequantization uses a uniform distribution for the noise $u$ which effectively leads to images being represented as hypercubes\n", "(cube in high dimensions) with sharp borders.\n", "However, modeling such sharp borders is not easy for a flow as it uses smooth transformations to convert it into a Gaussian distribution.\n", "\n", "Another way of looking at it is if we change the prior distribution in the previous visualization.\n", "Imagine we have independent Gaussian noise on pixels which is commonly the case for any real-world taken picture.\n", "Therefore, the flow would have to model a distribution as above, but with the individual volumes scaled as follows:"]}, {"cell_type": "code", "execution_count": 10, "id": "a67d1060", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:55.760055Z", "iopub.status.busy": "2021-09-16T12:41:55.759590Z", "iopub.status.idle": "2021-09-16T12:41:56.167204Z", "shell.execute_reply": "2021-09-16T12:41:56.167593Z"}, "papermill": {"duration": 0.446187, "end_time": "2021-09-16T12:41:56.167737", "exception": false, "start_time": "2021-09-16T12:41:55.721550", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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aWdodCAvbmluZSA2OCAvRCA4MCAvUCA5NyAvYSAvYiAvYyAvZCAvZSAvZiAxMDUgL2kgMTA4IC9sIDExMCAvbgovbyAxMTMgL3EgL3IgL3MgL3QgL3UgL3YgMTIxIC95IC96IF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNCAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxMyAwIFIgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTMgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL0QgMTcgMCBSIC9QIDE4IDAgUiAvYSAxOSAwIFIgL2IgMjAgMCBSIC9jIDIxIDAgUiAvZCAyMiAwIFIgL2UgMjMgMCBSCi9laWdodCAyNCAwIFIgL2YgMjUgMCBSIC9maXZlIDI2IDAgUiAvZm91ciAyNyAwIFIgL2h5cGhlbiAyOCAwIFIgL2kgMjkgMCBSCi9sIDMwIDAgUiAvbiAzMSAwIFIgL25pbmUgMzIgMCBSIC9vIDMzIDAgUiAvb25lIDM0IDAgUiAvcGVyaW9kIDM1IDAgUgovcSAzNiAwIFIgL3IgMzcgMCBSIC9zIDM4IDAgUiAvc2V2ZW4gMzkgMCBSIC9zaXggNDAgMCBSIC9zcGFjZSA0MSAwIFIKL3QgNDIgMCBSIC90aHJlZSA0MyAwIFIgL3R3byA0NCAwIFIgL3UgNDUgMCBSIC92IDQ2IDAgUiAveSA0NyAwIFIgL3ogNDggMCBSCi96ZXJvIDQ5IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMC41IC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuNSA+PgovQTQgPDwgL0NBIDAuOCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjggPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjUwIDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQxNTYrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNTEKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjQ3NDIgMDAwMDAgbiAKMDAwMDAyNDQ2MiAwMDAwMCBuIAowMDAwMDI0NDk0IDAwMDAwIG4gCjAwMDAwMjQ2NzkgMDAwMDAgbiAKMDAwMDAyNDcwMCAwMDAwMCBuIAowMDAwMDI0NzIxIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwMSAwMDAwMCBuIAowMDAwMDEzODg5IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAxMzg2NyAwMDAwMCBuIAowMDAwMDIzMDEzIDAwMDAwIG4gCjAwMDAwMjI4MTMgMDAwMDAgbiAKMDAwMDAyMjMzOSAwMDAwMCBuIAowMDAwMDI0MDY2IDAwMDAwIG4gCjAwMDAwMTM5MDkgMDAwMDAgbiAKMDAwMDAxNDE0NiAwMDAwMCBuIAowMDAwMDE0Mzg5IDAwMDAwIG4gCjAwMDAwMTQ3NjkgMDAwMDAgbiAKMDAwMDAxNTA4NiAwMDAwMCBuIAowMDAwMDE1MzkxIDAwMDAwIG4gCjAwMDAwMTU2OTUgMDAwMDAgbiAKMDAwMDAxNjAxNyAwMDAwMCBuIAowMDAwMDE2NDg1IDAwMDAwIG4gCjAwMDAwMTY2OTQgMDAwMDAgbiAKMDAwMDAxNzAxNiAwMDAwMCBuIAowMDAwMDE3MTgyIDAwMDAwIG4gCjAwMDAwMTczMDggMDAwMDAgbiAKMDAwMDAxNzQ1MiAwMDAwMCBuIAowMDAwMDE3NTcxIDAwMDAwIG4gCjAwMDAwMTc4MDcgMDAwMDAgbiAKMDAwMDAxODIwMiAwMDAwMCBuIAowMDAwMDE4NDkzIDAwMDAwIG4gCjAwMDAwMTg2NDggMDAwMDAgbiAKMDAwMDAxODc3MSAwMDAwMCBuIAowMDAwMDE5MDg3IDAwMDAwIG4gCjAwMDAwMTkzMjAgMDAwMDAgbiAKMDAwMDAxOTcyNyAwMDAwMCBuIAowMDAwMDE5ODY5IDAwMDAwIG4gCjAwMDAwMjAyNjIgMDAwMDAgbiAKMDAwMDAyMDM1MiAwMDAwMCBuIAowMDAwMDIwNTU4IDAwMDAwIG4gCjAwMDAwMjA5NzEgMDAwMDAgbiAKMDAwMDAyMTI5NSAwMDAwMCBuIAowMDAwMDIxNTQyIDAwMDAwIG4gCjAwMDAwMjE2ODkgMDAwMDAgbiAKMDAwMDAyMTkwMyAwMDAwMCBuIAowMDAwMDIyMDUxIDAwMDAwIG4gCjAwMDAwMjQ4MDIgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyA1MCAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNTEgPj4Kc3RhcnR4cmVmCjI0OTU5CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:55.917189\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["visualize_dequantization(quants=8, prior=np.array([0.075, 0.2, 0.4, 0.2, 0.075, 0.025, 0.0125, 0.0125]))"]}, {"cell_type": "markdown", "id": "cbe2bafd", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.039099, "end_time": "2021-09-16T12:41:56.246361", "exception": false, "start_time": "2021-09-16T12:41:56.207262", "status": "completed"}, "tags": []}, "source": ["Transforming such a probability into a Gaussian is a difficult task, especially with such hard borders.\n", "Dequantization has therefore been extended to more sophisticated, learnable distributions beyond uniform in a variational framework.\n", "In particular, if we remember the learning objective\n", "$\\log p(x) = \\log \\mathbb{E}_{u}\\left[\\frac{p(x+u)}{q(u|x)} \\right]$,\n", "the uniform distribution can be replaced by a learned distribution $q_{\\theta}(u|x)$ with support over $u\\in[0,1)^D$.\n", "This approach is called Variational Dequantization and has been proposed by Ho et al.\n", "[3].\n", "How can we learn such a distribution?\n", "We can use a second normalizing flow that takes $x$ as external input and learns a flexible distribution over $u$.\n", "To ensure a support over $[0,1)^D$, we can apply a sigmoid activation function as final flow transformation.\n", "\n", "Inheriting the original dequantization class, we can implement variational dequantization as follows:"]}, {"cell_type": "code", "execution_count": 11, "id": "5b61f162", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:56.333681Z", "iopub.status.busy": "2021-09-16T12:41:56.333192Z", "iopub.status.idle": "2021-09-16T12:41:56.335214Z", "shell.execute_reply": "2021-09-16T12:41:56.334809Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.049683, "end_time": "2021-09-16T12:41:56.335320", "exception": false, "start_time": "2021-09-16T12:41:56.285637", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class VariationalDequantization(Dequantization):\n", " def __init__(self, var_flows, alpha=1e-5):\n", " \"\"\"\n", " Args:\n", " var_flows: A list of flow transformations to use for modeling q(u|x)\n", " alpha: Small constant, see Dequantization for details\n", " \"\"\"\n", " super().__init__(alpha=alpha)\n", " self.flows = nn.ModuleList(var_flows)\n", "\n", " def dequant(self, z, ldj):\n", " z = z.to(torch.float32)\n", " img = (z / 255.0) * 2 - 1 # We condition the flows on x, i.e. the original image\n", "\n", " # Prior of u is a uniform distribution as before\n", " # As most flow transformations are defined on [-infinity,+infinity], we apply an inverse sigmoid first.\n", " deq_noise = torch.rand_like(z).detach()\n", " deq_noise, ldj = self.sigmoid(deq_noise, ldj, reverse=True)\n", " for flow in self.flows:\n", " deq_noise, ldj = flow(deq_noise, ldj, reverse=False, orig_img=img)\n", " deq_noise, ldj = self.sigmoid(deq_noise, ldj, reverse=False)\n", "\n", " # After the flows, apply u as in standard dequantization\n", " z = (z + deq_noise) / 256.0\n", " ldj -= np.log(256.0) * np.prod(z.shape[1:])\n", " return z, ldj"]}, {"cell_type": "markdown", "id": "80820d23", "metadata": {"papermill": {"duration": 0.03912, "end_time": "2021-09-16T12:41:56.414896", "exception": false, "start_time": "2021-09-16T12:41:56.375776", "status": "completed"}, "tags": []}, "source": ["Variational dequantization can be used as a substitute for dequantization.\n", "We will compare dequantization and variational dequantization in later experiments."]}, {"cell_type": "markdown", "id": "a46fc99c", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.038999, "end_time": "2021-09-16T12:41:56.493208", "exception": false, "start_time": "2021-09-16T12:41:56.454209", "status": "completed"}, "tags": []}, "source": ["### Coupling layers\n", "\n", "
\n", "\n", "Next, we look at possible transformations to apply inside the flow.\n", "A recent popular flow layer, which works well in combination with deep neural networks,\n", "is the coupling layer introduced by Dinh et al.\n", "[1].\n", "The input $z$ is arbitrarily split into two parts, $z_{1:j}$ and $z_{j+1:d}$, of which the first remains unchanged by the flow.\n", "Yet, $z_{1:j}$ is used to parameterize the transformation for the second part, $z_{j+1:d}$.\n", "Various transformations have been proposed in recent time [3,4], but here we will settle for the simplest and most efficient one: affine coupling.\n", "In this coupling layer, we apply an affine transformation by shifting the input by a bias $\\mu$ and scale it by $\\sigma$.\n", "In other words, our transformation looks as follows:\n", "\n", "$$z'_{j+1:d} = \\mu_{\\theta}(z_{1:j}) + \\sigma_{\\theta}(z_{1:j}) \\odot z_{j+1:d}$$\n", "\n", "The functions $\\mu$ and $\\sigma$ are implemented as a shared neural network,\n", "and the sum and multiplication are performed element-wise.\n", "The LDJ is thereby the sum of the logs of the scaling factors: $\\sum_i \\left[\\log \\sigma_{\\theta}(z_{1:j})\\right]_i$.\n", "Inverting the layer can as simply be done as subtracting the bias and dividing by the scale:\n", "\n", "$$z_{j+1:d} = \\left(z'_{j+1:d} - \\mu_{\\theta}(z_{1:j})\\right) / \\sigma_{\\theta}(z_{1:j})$$\n", "\n", "We can also visualize the coupling layer in form of a computation graph,\n", "where $z_1$ represents $z_{1:j}$, and $z_2$ represents $z_{j+1:d}$:\n", "\n", "
\n", "\n", "In our implementation, we will realize the splitting of variables as masking.\n", "The variables to be transformed, $z_{j+1:d}$, are masked when passing $z$ to the shared network to predict the transformation parameters.\n", "When applying the transformation, we mask the parameters for $z_{1:j}$\n", "so that we have an identity operation for those variables:"]}, {"cell_type": "code", "execution_count": 12, "id": "89c6ff25", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:56.579882Z", "iopub.status.busy": "2021-09-16T12:41:56.574501Z", "iopub.status.idle": "2021-09-16T12:41:56.581937Z", "shell.execute_reply": "2021-09-16T12:41:56.581514Z"}, "papermill": {"duration": 0.048905, "end_time": "2021-09-16T12:41:56.582037", "exception": false, "start_time": "2021-09-16T12:41:56.533132", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class CouplingLayer(nn.Module):\n", " def __init__(self, network, mask, c_in):\n", " \"\"\"Coupling layer inside a normalizing flow.\n", "\n", " Args:\n", " network: A PyTorch nn.Module constituting the deep neural network for mu and sigma.\n", " Output shape should be twice the channel size as the input.\n", " mask: Binary mask (0 or 1) where 0 denotes that the element should be transformed,\n", " while 1 means the latent will be used as input to the NN.\n", " c_in: Number of input channels\n", " \"\"\"\n", " super().__init__()\n", " self.network = network\n", " self.scaling_factor = nn.Parameter(torch.zeros(c_in))\n", " # Register mask as buffer as it is a tensor which is not a parameter,\n", " # but should be part of the modules state.\n", " self.register_buffer(\"mask\", mask)\n", "\n", " def forward(self, z, ldj, reverse=False, orig_img=None):\n", " \"\"\"\n", " Args:\n", " z: Latent input to the flow\n", " ldj: The current ldj of the previous flows.\n", " The ldj of this layer will be added to this tensor.\n", " reverse: If True, we apply the inverse of the layer.\n", " orig_img (optional): Only needed in VarDeq. Allows external\n", " input to condition the flow on (e.g. original image)\n", " \"\"\"\n", " # Apply network to masked input\n", " z_in = z * self.mask\n", " if orig_img is None:\n", " nn_out = self.network(z_in)\n", " else:\n", " nn_out = self.network(torch.cat([z_in, orig_img], dim=1))\n", " s, t = nn_out.chunk(2, dim=1)\n", "\n", " # Stabilize scaling output\n", " s_fac = self.scaling_factor.exp().view(1, -1, 1, 1)\n", " s = torch.tanh(s / s_fac) * s_fac\n", "\n", " # Mask outputs (only transform the second part)\n", " s = s * (1 - self.mask)\n", " t = t * (1 - self.mask)\n", "\n", " # Affine transformation\n", " if not reverse:\n", " # Whether we first shift and then scale, or the other way round,\n", " # is a design choice, and usually does not have a big impact\n", " z = (z + t) * torch.exp(s)\n", " ldj += s.sum(dim=[1, 2, 3])\n", " else:\n", " z = (z * torch.exp(-s)) - t\n", " ldj -= s.sum(dim=[1, 2, 3])\n", "\n", " return z, ldj"]}, {"cell_type": "markdown", "id": "42bd2031", "metadata": {"papermill": {"duration": 0.039104, "end_time": "2021-09-16T12:41:56.660313", "exception": false, "start_time": "2021-09-16T12:41:56.621209", "status": "completed"}, "tags": []}, "source": ["For stabilization purposes, we apply a $\\tanh$ activation function on the scaling output.\n", "This prevents sudden large output values for the scaling that can destabilize training.\n", "To still allow scaling factors smaller or larger than -1 and 1 respectively,\n", "we have a learnable parameter per dimension, called `scaling_factor`.\n", "This scales the tanh to different limits.\n", "Below, we visualize the effect of the scaling factor on the output activation of the scaling terms:"]}, {"cell_type": "code", "execution_count": 13, "id": "c1bed75e", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:56.769037Z", "iopub.status.busy": "2021-09-16T12:41:56.743280Z", "iopub.status.idle": "2021-09-16T12:41:57.496549Z", "shell.execute_reply": "2021-09-16T12:41:57.496937Z"}, "papermill": {"duration": 0.797388, "end_time": "2021-09-16T12:41:57.497082", "exception": false, "start_time": "2021-09-16T12:41:56.699694", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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NSAwIG9iago8PCAvQmFzZUZvbnQgL0RlamFWdVNhbnMgL0NoYXJQcm9jcyAxNiAwIFIKL0VuY29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA0NiAvcGVyaW9kIDQ4IC96ZXJvIC9vbmUgL3R3byAvdGhyZWUgNTMgL2ZpdmUgNTggL2NvbG9uIDgzIC9TCjk3IC9hIDk5IC9jIDEwMiAvZiAvZyAxMDUgL2kgMTA4IC9sIDExMCAvbiAvbyAxMTQgL3IgMTE2IC90IF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250RGVzY3JpcHRvciAxNCAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2FucwovU3VidHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxMyAwIFIgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTMgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL1MgMTcgMCBSIC9hIDE4IDAgUiAvYyAxOSAwIFIgL2NvbG9uIDIwIDAgUiAvZiAyMSAwIFIgL2ZpdmUgMjIgMCBSCi9nIDIzIDAgUiAvaSAyNCAwIFIgL2wgMjUgMCBSIC9uIDI3IDAgUiAvbyAyOCAwIFIgL29uZSAyOSAwIFIKL3BlcmlvZCAzMCAwIFIgL3IgMzEgMCBSIC9zcGFjZSAzMiAwIFIgL3QgMzMgMCBSIC90aHJlZSAzNCAwIFIgL3R3byAzNSAwIFIKL3plcm8gMzYgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNSAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMjYgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozNyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTU3KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDM4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDEwMjM4IDAwMDAwIG4gCjAwMDAwMTAwMTYgMDAwMDAgbiAKMDAwMDAxMDA0OCAwMDAwMCBuIAowMDAwMDEwMTQ3IDAwMDAwIG4gCjAwMDAwMTAxNjggMDAwMDAgbiAKMDAwMDAxMDE4OSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDYgMDAwMDAgbiAKMDAwMDAwMzA5NiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDMwNzUgMDAwMDAgbiAKMDAwMDAwODcyNCAwMDAwMCBuIAowMDAwMDA4NTI0IDAwMDAwIG4gCjAwMDAwMDgwOTkgMDAwMDAgbiAKMDAwMDAwOTc3NyAwMDAwMCBuIAowMDAwMDAzMTE2IDAwMDAwIG4gCjAwMDAwMDM1MzAgMDAwMDAgbiAKMDAwMDAwMzkxMCAwMDAwMCBuIAowMDAwMDA0MjE1IDAwMDAwIG4gCjAwMDAwMDQzNjAgMDAwMDAgbiAKMDAwMDAwNDU2OSAwMDAwMCBuIAowMDAwMDA0ODkxIDAwMDAwIG4gCjAwMDAwMDUzMDUgMDAwMDAgbiAKMDAwMDAwNTQ0OSAwMDAwMCBuIAowMDAwMDA1NTY4IDAwMDAwIG4gCjAwMDAwMDU3NDAgMDAwMDAgbiAKMDAwMDAwNTk3NiAwMDAwMCBuIAowMDAwMDA2MjY3IDAwMDAwIG4gCjAwMDAwMDY0MjIgMDAwMDAgbiAKMDAwMDAwNjU0NSAwMDAwMCBuIAowMDAwMDA2Nzc4IDAwMDAwIG4gCjAwMDAwMDY4NjggMDAwMDAgbiAKMDAwMDAwNzA3NCAwMDAwMCBuIAowMDAwMDA3NDg3IDAwMDAwIG4gCjAwMDAwMDc4MTEgMDAwMDAgbiAKMDAwMDAxMDI5OCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM3IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAzOCA+PgpzdGFydHhyZWYKMTA0NTUKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:57.058315\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["with torch.no_grad():\n", " x = torch.arange(-5, 5, 0.01)\n", " scaling_factors = [0.5, 1, 2]\n", " sns.set()\n", " fig, ax = plt.subplots(1, 3, figsize=(12, 3))\n", " for i, scale in enumerate(scaling_factors):\n", " y = torch.tanh(x / scale) * scale\n", " ax[i].plot(x.numpy(), y.numpy())\n", " ax[i].set_title(\"Scaling factor: \" + str(scale))\n", " ax[i].set_ylim(-3, 3)\n", " plt.subplots_adjust(wspace=0.4)\n", " sns.reset_orig()\n", " plt.show()"]}, {"cell_type": "markdown", "id": "12dba007", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.041344, "end_time": "2021-09-16T12:41:57.581362", "exception": false, "start_time": "2021-09-16T12:41:57.540018", "status": "completed"}, "tags": []}, "source": ["Coupling layers generalize to any masking technique we could think of.\n", "However, the most common approach for images is to split the input $z$ in half, using a checkerboard mask or channel mask.\n", "A checkerboard mask splits the variables across the height and width dimensions and assigns each other pixel to $z_{j+1:d}$.\n", "Thereby, the mask is shared across channels.\n", "In contrast, the channel mask assigns half of the channels to $z_{j+1:d}$, and the other half to $z_{1:j+1}$.\n", "Note that when we apply multiple coupling layers, we invert the masking for each other layer so that each variable is transformed a similar amount of times.\n", "\n", "Let's implement a function that creates a checkerboard mask and a channel mask for us:"]}, {"cell_type": "code", "execution_count": 14, "id": "deef0879", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:57.685569Z", "iopub.status.busy": "2021-09-16T12:41:57.685029Z", "iopub.status.idle": "2021-09-16T12:41:57.686454Z", "shell.execute_reply": "2021-09-16T12:41:57.686034Z"}, "papermill": {"duration": 0.063936, "end_time": "2021-09-16T12:41:57.686559", "exception": false, "start_time": "2021-09-16T12:41:57.622623", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def create_checkerboard_mask(h, w, invert=False):\n", " x, y = torch.arange(h, dtype=torch.int32), torch.arange(w, dtype=torch.int32)\n", " xx, yy = torch.meshgrid(x, y)\n", " mask = torch.fmod(xx + yy, 2)\n", " mask = mask.to(torch.float32).view(1, 1, h, w)\n", " if invert:\n", " mask = 1 - mask\n", " return mask\n", "\n", "\n", "def create_channel_mask(c_in, invert=False):\n", " mask = torch.cat([torch.ones(c_in // 2, dtype=torch.float32), torch.zeros(c_in - c_in // 2, dtype=torch.float32)])\n", " mask = mask.view(1, c_in, 1, 1)\n", " if invert:\n", " mask = 1 - mask\n", " return mask"]}, {"cell_type": "markdown", "id": "613e51d7", "metadata": {"papermill": {"duration": 0.041153, "end_time": "2021-09-16T12:41:57.769008", "exception": false, "start_time": "2021-09-16T12:41:57.727855", "status": "completed"}, "tags": []}, "source": ["We can also visualize the corresponding masks for an image of size $8\\times 8\\times 2$ (2 channels):"]}, {"cell_type": "code", "execution_count": 15, "id": "6b1bfc65", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:57.857339Z", "iopub.status.busy": "2021-09-16T12:41:57.856865Z", "iopub.status.idle": "2021-09-16T12:41:57.988833Z", "shell.execute_reply": "2021-09-16T12:41:57.988350Z"}, "papermill": {"duration": 0.177754, "end_time": "2021-09-16T12:41:57.988944", "exception": false, "start_time": "2021-09-16T12:41:57.811190", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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Y2UgNjcgL0MgOTcgL2EgL2IgL2MgL2QgL2UgMTA0IC9oIDEwNyAvayAxMDkgL20gMTExIC9vIDExNCAvciAvcwpdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTUgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTQgMCBSID4+CmVuZG9iagoxNSAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjE0IDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE3IDAgb2JqCjw8IC9DIDE4IDAgUiAvYSAxOSAwIFIgL2IgMjAgMCBSIC9jIDIxIDAgUiAvZCAyMiAwIFIgL2UgMjMgMCBSIC9oIDI0IDAgUgovayAyNSAwIFIgL20gMjYgMCBSIC9vIDI3IDAgUiAvciAyOCAwIFIgL3MgMjkgMCBSIC9zcGFjZSAzMCAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE2IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSID4+CmVuZG9iagoxMyAwIG9iago8PCAvQml0c1BlckNvbXBvbmVudCA4IC9Db2xvclNwYWNlIFsvSW5kZXhlZCAvRGV2aWNlUkdCIDIgKP///39/fwAAACldCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDEgL0NvbHVtbnMgMTUwIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCA4MiAvTGVuZ3RoIDMxIDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MCA+PgpzdHJlYW0KeJzt1DEKA0EQA8H1/v/RhvNERnDXgfGCWtkkTUWzXkdu/RuQJ4tMFpksMllksshkkX2x9rX12b695kiN/bSxU0OWrG4WbM2RGutpY8WGLFmyzmPB1hyp0fBOZckqY8HWHKnR8E5lySpjwdYcqdHwTmXJKmPB1hyp0fBOZckqY8HWHKnR8E5lyfoVC7bmSI2GvyVLVhnrlMkik0Umi0wWmSwyWWSHst5k4DANCmVuZHN0cmVhbQplbmRvYmoKMzEgMCBvYmoKMTcxCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozMiAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MTU3KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDMzCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDA2ODM2IDAwMDAwIG4gCjAwMDAwMDYxODcgMDAwMDAgbiAKMDAwMDAwNjIxOSAwMDAwMCBuIAowMDAwMDA2MzE4IDAwMDAwIG4gCjAwMDAwMDYzMzkgMDAwMDAgbiAKMDAwMDAwNjM2MCAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTUgMDAwMDAgbiAKMDAwMDAwMDcxMyAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA2OTMgMDAwMDAgbiAKMDAwMDAwNjM5MiAwMDAwMCBuIAowMDAwMDA0OTc4IDAwMDAwIG4gCjAwMDAwMDQ3NzggMDAwMDAgbiAKMDAwMDAwNDQxMyAwMDAwMCBuIAowMDAwMDA2MDMxIDAwMDAwIG4gCjAwMDAwMDA3MzMgMDAwMDAgbiAKMDAwMDAwMTA0MSAwMDAwMCBuIAowMDAwMDAxNDIxIDAwMDAwIG4gCjAwMDAwMDE3MzggMDAwMDAgbiAKMDAwMDAwMjA0MyAwMDAwMCBuIAowMDAwMDAyMzQ3IDAwMDAwIG4gCjAwMDAwMDI2NjkgMDAwMDAgbiAKMDAwMDAwMjkwNiAwMDAwMCBuIAowMDAwMDAzMDYxIDAwMDAwIG4gCjAwMDAwMDMzOTIgMDAwMDAgbiAKMDAwMDAwMzY4MyAwMDAwMCBuIAowMDAwMDAzOTE2IDAwMDAwIG4gCjAwMDAwMDQzMjMgMDAwMDAgbiAKMDAwMDAwNjgxNiAwMDAwMCBuIAowMDAwMDA2ODk2IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzIgMCBSIC9Sb290IDEgMCBSIC9TaXplIDMzID4+CnN0YXJ0eHJlZgo3MDUzCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:57.893557\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"data": {"application/pdf": "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dHlwZSAvVHlwZTMgL1R5cGUgL0ZvbnQgL1dpZHRocyAxNCAwIFIgPj4KZW5kb2JqCjE1IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTQgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTcgMCBvYmoKPDwgL0MgMTggMCBSIC9hIDE5IDAgUiAvZSAyMCAwIFIgL2ggMjEgMCBSIC9rIDIyIDAgUiAvbCAyMyAwIFIgL20gMjQgMCBSCi9uIDI1IDAgUiAvcyAyNiAwIFIgL3NwYWNlIDI3IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTYgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo////f39/AAAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTAgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDgyIC9MZW5ndGggMjggMCBSIC9TdWJ0eXBlIC9JbWFnZQovVHlwZSAvWE9iamVjdCAvV2lkdGggMTUwID4+CnN0cmVhbQp4nO3OsQ2AQBDAsIf9h6YGIaF8wxXOAJHXMbL1N+A9rBJWCauEVcIqYZUerLXV/XFuhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhfXNmhJWCauEVcIqYZWwSkNZF1/rMA0KZW5kc3RyZWFtCmVuZG9iagoyOCAwIG9iagoxMDIKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjI5IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQxNTcrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgMzAKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDU2MDggMDAwMDAgbiAKMDAwMDAwNTAyOCAwMDAwMCBuIAowMDAwMDA1MDYwIDAwMDAwIG4gCjAwMDAwMDUxNTkgMDAwMDAgbiAKMDAwMDAwNTE4MCAwMDAwMCBuIAowMDAwMDA1MjAxIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5NSAwMDAwMCBuIAowMDAwMDAwNjkyIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDY3MiAwMDAwMCBuIAowMDAwMDA1MjMzIDAwMDAwIG4gCjAwMDAwMDM4NDkgMDAwMDAgbiAKMDAwMDAwMzY0OSAwMDAwMCBuIAowMDAwMDAzMjk3IDAwMDAwIG4gCjAwMDAwMDQ5MDIgMDAwMDAgbiAKMDAwMDAwMDcxMiAwMDAwMCBuIAowMDAwMDAxMDIwIDAwMDAwIG4gCjAwMDAwMDE0MDAgMDAwMDAgbiAKMDAwMDAwMTcyMiAwMDAwMCBuIAowMDAwMDAxOTU5IDAwMDAwIG4gCjAwMDAwMDIxMTQgMDAwMDAgbiAKMDAwMDAwMjIzMyAwMDAwMCBuIAowMDAwMDAyNTY0IDAwMDAwIG4gCjAwMDAwMDI4MDAgMDAwMDAgbiAKMDAwMDAwMzIwNyAwMDAwMCBuIAowMDAwMDA1NTg4IDAwMDAwIG4gCjAwMDAwMDU2NjggMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAyOSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMzAgPj4Kc3RhcnR4cmVmCjU4MjUKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:41:57.958494\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["checkerboard_mask = create_checkerboard_mask(h=8, w=8).expand(-1, 2, -1, -1)\n", "channel_mask = create_channel_mask(c_in=2).expand(-1, -1, 8, 8)\n", "\n", "show_imgs(checkerboard_mask.transpose(0, 1), \"Checkerboard mask\")\n", "show_imgs(channel_mask.transpose(0, 1), \"Channel mask\")"]}, {"cell_type": "markdown", "id": "b52dfa44", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042944, "end_time": "2021-09-16T12:41:58.075778", "exception": false, "start_time": "2021-09-16T12:41:58.032834", "status": "completed"}, "tags": []}, "source": ["As a last aspect of coupling layers, we need to decide for the deep neural network we want to apply in the coupling layers.\n", "The input to the layers is an image, and hence we stick with a CNN.\n", "Because the input to a transformation depends on all transformations before,\n", "it is crucial to ensure a good gradient flow through the CNN back to the input,\n", "which can be optimally achieved by a ResNet-like architecture.\n", "Specifically, we use a Gated ResNet that adds a $\\sigma$-gate to the skip connection,\n", "similarly to the input gate in LSTMs.\n", "The details are not necessarily important here, and the network is\n", "strongly inspired from Flow++ [3] in case you are interested in building\n", "even stronger models."]}, {"cell_type": "code", "execution_count": 16, "id": "310d88c0", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:58.171587Z", "iopub.status.busy": "2021-09-16T12:41:58.169353Z", "iopub.status.idle": "2021-09-16T12:41:58.173708Z", "shell.execute_reply": "2021-09-16T12:41:58.173294Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.055251, "end_time": "2021-09-16T12:41:58.173850", "exception": false, "start_time": "2021-09-16T12:41:58.118599", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ConcatELU(nn.Module):\n", " \"\"\"Activation function that applies ELU in both direction (inverted and plain).\n", "\n", " Allows non-linearity while providing strong gradients for any input (important for final convolution)\n", " \"\"\"\n", "\n", " def forward(self, x):\n", " return torch.cat([F.elu(x), F.elu(-x)], dim=1)\n", "\n", "\n", "class LayerNormChannels(nn.Module):\n", " def __init__(self, c_in):\n", " \"\"\"This module applies layer norm across channels in an image.\n", "\n", " Has been shown to work well with ResNet connections.\n", " Args:\n", " c_in: Number of channels of the input\n", " \"\"\"\n", " super().__init__()\n", " self.layer_norm = nn.LayerNorm(c_in)\n", "\n", " def forward(self, x):\n", " x = x.permute(0, 2, 3, 1)\n", " x = self.layer_norm(x)\n", " x = x.permute(0, 3, 1, 2)\n", " return x\n", "\n", "\n", "class GatedConv(nn.Module):\n", " def __init__(self, c_in, c_hidden):\n", " \"\"\"\n", " This module applies a two-layer convolutional ResNet block with input gate\n", " Args:\n", " c_in: Number of channels of the input\n", " c_hidden: Number of hidden dimensions we want to model (usually similar to c_in)\n", " \"\"\"\n", " super().__init__()\n", " self.net = nn.Sequential(\n", " nn.Conv2d(c_in, c_hidden, kernel_size=3, padding=1),\n", " ConcatELU(),\n", " nn.Conv2d(2 * c_hidden, 2 * c_in, kernel_size=1),\n", " )\n", "\n", " def forward(self, x):\n", " out = self.net(x)\n", " val, gate = out.chunk(2, dim=1)\n", " return x + val * torch.sigmoid(gate)\n", "\n", "\n", "class GatedConvNet(nn.Module):\n", " def __init__(self, c_in, c_hidden=32, c_out=-1, num_layers=3):\n", " \"\"\"Module that summarizes the previous blocks to a full convolutional neural network.\n", "\n", " Args:\n", " c_in: Number of input channels\n", " c_hidden: Number of hidden dimensions to use within the network\n", " c_out: Number of output channels. If -1, 2 times the input channels are used (affine coupling)\n", " num_layers: Number of gated ResNet blocks to apply\n", " \"\"\"\n", " super().__init__()\n", " c_out = c_out if c_out > 0 else 2 * c_in\n", " layers = []\n", " layers += [nn.Conv2d(c_in, c_hidden, kernel_size=3, padding=1)]\n", " for layer_index in range(num_layers):\n", " layers += [GatedConv(c_hidden, c_hidden), LayerNormChannels(c_hidden)]\n", " layers += [ConcatELU(), nn.Conv2d(2 * c_hidden, c_out, kernel_size=3, padding=1)]\n", " self.nn = nn.Sequential(*layers)\n", "\n", " self.nn[-1].weight.data.zero_()\n", " self.nn[-1].bias.data.zero_()\n", "\n", " def forward(self, x):\n", " return self.nn(x)"]}, {"cell_type": "markdown", "id": "8dd3b2c4", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042657, "end_time": "2021-09-16T12:41:58.260796", "exception": false, "start_time": "2021-09-16T12:41:58.218139", "status": "completed"}, "tags": []}, "source": ["### Training loop\n", "\n", "Finally, we can add Dequantization, Variational Dequantization and Coupling Layers together to build our full normalizing flow on MNIST images.\n", "We apply 8 coupling layers in the main flow, and 4 for variational dequantization if applied.\n", "We apply a checkerboard mask throughout the network as with a single channel (black-white images),\n", "we cannot apply channel mask.\n", "The overall architecture is visualized below.\n", "\n", "\n", "
"]}, {"cell_type": "code", "execution_count": 17, "id": "27d5089b", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:58.351932Z", "iopub.status.busy": "2021-09-16T12:41:58.351458Z", "iopub.status.idle": "2021-09-16T12:41:58.353625Z", "shell.execute_reply": "2021-09-16T12:41:58.353155Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.050041, "end_time": "2021-09-16T12:41:58.353742", "exception": false, "start_time": "2021-09-16T12:41:58.303701", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def create_simple_flow(use_vardeq=True):\n", " flow_layers = []\n", " if use_vardeq:\n", " vardeq_layers = [\n", " CouplingLayer(\n", " network=GatedConvNet(c_in=2, c_out=2, c_hidden=16),\n", " mask=create_checkerboard_mask(h=28, w=28, invert=(i % 2 == 1)),\n", " c_in=1,\n", " )\n", " for i in range(4)\n", " ]\n", " flow_layers += [VariationalDequantization(var_flows=vardeq_layers)]\n", " else:\n", " flow_layers += [Dequantization()]\n", "\n", " for i in range(8):\n", " flow_layers += [\n", " CouplingLayer(\n", " network=GatedConvNet(c_in=1, c_hidden=32),\n", " mask=create_checkerboard_mask(h=28, w=28, invert=(i % 2 == 1)),\n", " c_in=1,\n", " )\n", " ]\n", "\n", " flow_model = ImageFlow(flow_layers).to(device)\n", " return flow_model"]}, {"cell_type": "markdown", "id": "5b491b9b", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042728, "end_time": "2021-09-16T12:41:58.439262", "exception": false, "start_time": "2021-09-16T12:41:58.396534", "status": "completed"}, "tags": []}, "source": ["For implementing the training loop, we use the framework of PyTorch Lightning and reduce the code overhead.\n", "If interested, you can take a look at the generated tensorboard file,\n", "in particularly the graph to see an overview of flow transformations that are applied.\n", "Note that we again provide pre-trained models (see later on in the notebook)\n", "as normalizing flows are particularly expensive to train.\n", "We have also run validation and testing as this can take some time as well with the added importance sampling."]}, {"cell_type": "code", "execution_count": 18, "id": "0447d535", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:58.532547Z", "iopub.status.busy": "2021-09-16T12:41:58.532063Z", "iopub.status.idle": "2021-09-16T12:41:58.534199Z", "shell.execute_reply": "2021-09-16T12:41:58.533718Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.052256, "end_time": "2021-09-16T12:41:58.534297", "exception": false, "start_time": "2021-09-16T12:41:58.482041", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_flow(flow, model_name=\"MNISTFlow\"):\n", " # Create a PyTorch Lightning trainer\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, model_name),\n", " gpus=1 if torch.cuda.is_available() else 0,\n", " max_epochs=200,\n", " gradient_clip_val=1.0,\n", " callbacks=[\n", " ModelCheckpoint(save_weights_only=True, mode=\"min\", monitor=\"val_bpd\"),\n", " LearningRateMonitor(\"epoch\"),\n", " ],\n", " )\n", " trainer.logger._log_graph = True\n", " trainer.logger._default_hp_metric = None # Optional logging argument that we don't need\n", "\n", " train_data_loader = data.DataLoader(\n", " train_set, batch_size=128, shuffle=True, drop_last=True, pin_memory=True, num_workers=8\n", " )\n", " result = None\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, model_name + \".ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model, loading...\")\n", " ckpt = torch.load(pretrained_filename, map_location=device)\n", " flow.load_state_dict(ckpt[\"state_dict\"])\n", " result = ckpt.get(\"result\", None)\n", " else:\n", " print(\"Start training\", model_name)\n", " trainer.fit(flow, train_data_loader, val_loader)\n", "\n", " # Test best model on validation and test set if no result has been found\n", " # Testing can be expensive due to the importance sampling.\n", " if result is None:\n", " val_result = trainer.test(flow, test_dataloaders=val_loader, verbose=False)\n", " start_time = time.time()\n", " test_result = trainer.test(flow, test_dataloaders=test_loader, verbose=False)\n", " duration = time.time() - start_time\n", " result = {\"test\": test_result, \"val\": val_result, \"time\": duration / len(test_loader) / flow.import_samples}\n", "\n", " return flow, result"]}, {"cell_type": "markdown", "id": "27f6d057", "metadata": {"papermill": {"duration": 0.050519, "end_time": "2021-09-16T12:41:58.627997", "exception": false, "start_time": "2021-09-16T12:41:58.577478", "status": "completed"}, "tags": []}, "source": ["## Multi-scale architecture\n", "\n", "
\n", "\n", "One disadvantage of normalizing flows is that they operate on the exact same dimensions as the input.\n", "If the input is high-dimensional, so is the latent space, which requires larger computational cost to learn suitable transformations.\n", "However, particularly in the image domain, many pixels contain less information in the sense\n", "that we could remove them without loosing the semantical information of the image.\n", "\n", "Based on this intuition, deep normalizing flows on images commonly apply a multi-scale architecture [1].\n", "After the first $N$ flow transformations, we split off half of the latent dimensions and directly evaluate them on the prior.\n", "The other half is run through $N$ more flow transformations, and depending on the size of the input,\n", "we split it again in half or stop overall at this position.\n", "The two operations involved in this setup is `Squeeze` and `Split` which\n", "we will review more closely and implement below."]}, {"cell_type": "markdown", "id": "207a4bd1", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.043555, "end_time": "2021-09-16T12:41:58.714567", "exception": false, "start_time": "2021-09-16T12:41:58.671012", "status": "completed"}, "tags": []}, "source": ["### Squeeze and Split\n", "\n", "When we want to remove half of the pixels in an image, we have the problem of deciding which variables to cut,\n", "and how to rearrange the image.\n", "Thus, the squeezing operation is commonly used before split, which divides the image into subsquares\n", "of shape $2\\times 2\\times C$, and reshapes them into $1\\times 1\\times 4C$ blocks.\n", "Effectively, we reduce the height and width of the image by a factor of 2 while scaling the number of channels by 4.\n", "Afterwards, we can perform the split operation over channels without the need of rearranging the pixels.\n", "The smaller scale also makes the overall architecture more efficient.\n", "Visually, the squeeze operation should transform the input as follows:\n", "\n", "
\n", "\n", "The input of $4\\times 4\\times 1$ is scaled to $2\\times 2\\times 4$ following\n", "the idea of grouping the pixels in $2\\times 2\\times 1$ subsquares.\n", "Next, let's try to implement this layer:"]}, {"cell_type": "code", "execution_count": 19, "id": "3435b3ac", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:58.805945Z", "iopub.status.busy": "2021-09-16T12:41:58.805454Z", "iopub.status.idle": "2021-09-16T12:41:58.807515Z", "shell.execute_reply": "2021-09-16T12:41:58.807040Z"}, "papermill": {"duration": 0.050124, "end_time": "2021-09-16T12:41:58.807614", "exception": false, "start_time": "2021-09-16T12:41:58.757490", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class SqueezeFlow(nn.Module):\n", " def forward(self, z, ldj, reverse=False):\n", " B, C, H, W = z.shape\n", " if not reverse:\n", " # Forward direction: H x W x C => H/2 x W/2 x 4C\n", " z = z.reshape(B, C, H // 2, 2, W // 2, 2)\n", " z = z.permute(0, 1, 3, 5, 2, 4)\n", " z = z.reshape(B, 4 * C, H // 2, W // 2)\n", " else:\n", " # Reverse direction: H/2 x W/2 x 4C => H x W x C\n", " z = z.reshape(B, C // 4, 2, 2, H, W)\n", " z = z.permute(0, 1, 4, 2, 5, 3)\n", " z = z.reshape(B, C // 4, H * 2, W * 2)\n", " return z, ldj"]}, {"cell_type": "markdown", "id": "99659504", "metadata": {"papermill": {"duration": 0.04349, "end_time": "2021-09-16T12:41:58.894090", "exception": false, "start_time": "2021-09-16T12:41:58.850600", "status": "completed"}, "tags": []}, "source": ["Before moving on, we can verify our implementation by comparing our output with the example figure above:"]}, {"cell_type": "code", "execution_count": 20, "id": "f0ffca0f", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:58.984627Z", "iopub.status.busy": "2021-09-16T12:41:58.984154Z", "iopub.status.idle": "2021-09-16T12:41:58.988910Z", "shell.execute_reply": "2021-09-16T12:41:58.988509Z"}, "papermill": {"duration": 0.051435, "end_time": "2021-09-16T12:41:58.989009", "exception": false, "start_time": "2021-09-16T12:41:58.937574", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Image (before)\n", " tensor([[[[ 1, 2, 3, 4],\n", " [ 5, 6, 7, 8],\n", " [ 9, 10, 11, 12],\n", " [13, 14, 15, 16]]]])\n", "\n", "Image (forward)\n", " tensor([[[[ 1, 2, 5, 6],\n", " [ 3, 4, 7, 8]],\n", "\n", " [[ 9, 10, 13, 14],\n", " [11, 12, 15, 16]]]])\n", "\n", "Image (reverse)\n", " tensor([[[[ 1, 2, 3, 4],\n", " [ 5, 6, 7, 8],\n", " [ 9, 10, 11, 12],\n", " [13, 14, 15, 16]]]])\n"]}], "source": ["sq_flow = SqueezeFlow()\n", "rand_img = torch.arange(1, 17).view(1, 1, 4, 4)\n", "print(\"Image (before)\\n\", rand_img)\n", "forward_img, _ = sq_flow(rand_img, ldj=None, reverse=False)\n", "print(\"\\nImage (forward)\\n\", forward_img.permute(0, 2, 3, 1)) # Permute for readability\n", "reconst_img, _ = sq_flow(forward_img, ldj=None, reverse=True)\n", "print(\"\\nImage (reverse)\\n\", reconst_img)"]}, {"cell_type": "markdown", "id": "912fb9c0", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.04387, "end_time": "2021-09-16T12:41:59.077196", "exception": false, "start_time": "2021-09-16T12:41:59.033326", "status": "completed"}, "tags": []}, "source": ["The split operation divides the input into two parts, and evaluates one part directly on the prior.\n", "So that our flow operation fits to the implementation of the previous layers,\n", "we will return the prior probability of the first part as the log determinant jacobian of the layer.\n", "It has the same effect as if we would combine all variable splits at the\n", "end of the flow, and evaluate them together on the prior."]}, {"cell_type": "code", "execution_count": 21, "id": "420c7e21", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:59.170054Z", "iopub.status.busy": "2021-09-16T12:41:59.169563Z", "iopub.status.idle": "2021-09-16T12:41:59.171697Z", "shell.execute_reply": "2021-09-16T12:41:59.171229Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.05122, "end_time": "2021-09-16T12:41:59.171798", "exception": false, "start_time": "2021-09-16T12:41:59.120578", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class SplitFlow(nn.Module):\n", " def __init__(self):\n", " super().__init__()\n", " self.prior = torch.distributions.normal.Normal(loc=0.0, scale=1.0)\n", "\n", " def forward(self, z, ldj, reverse=False):\n", " if not reverse:\n", " z, z_split = z.chunk(2, dim=1)\n", " ldj += self.prior.log_prob(z_split).sum(dim=[1, 2, 3])\n", " else:\n", " z_split = self.prior.sample(sample_shape=z.shape).to(device)\n", " z = torch.cat([z, z_split], dim=1)\n", " ldj -= self.prior.log_prob(z_split).sum(dim=[1, 2, 3])\n", " return z, ldj"]}, {"cell_type": "markdown", "id": "5d191c06", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.043739, "end_time": "2021-09-16T12:41:59.259120", "exception": false, "start_time": "2021-09-16T12:41:59.215381", "status": "completed"}, "tags": []}, "source": ["### Building a multi-scale flow\n", "\n", "After defining the squeeze and split operation, we are finally able to build our own multi-scale flow.\n", "Deep normalizing flows such as Glow and Flow++ [2,3] often apply a split operation directly after squeezing.\n", "However, with shallow flows, we need to be more thoughtful about where to place the split operation as we need at least a minimum amount of transformations on each variable.\n", "Our setup is inspired by the original RealNVP architecture [1] which is shallower than other,\n", "more recent state-of-the-art architectures.\n", "\n", "Hence, for the MNIST dataset, we will apply the first squeeze operation after two coupling layers, but don't apply a split operation yet.\n", "Because we have only used two coupling layers and each the variable has been only transformed once, a split operation would be too early.\n", "We apply two more coupling layers before finally applying a split flow and squeeze again.\n", "The last four coupling layers operate on a scale of $7\\times 7\\times 8$.\n", "The full flow architecture is shown below.\n", "\n", "
\n", "\n", "Note that while the feature maps inside the coupling layers reduce with the height and width of the input,\n", "the increased number of channels is not directly considered.\n", "To counteract this, we increase the hidden dimensions for the coupling layers on the squeezed input.\n", "The dimensions are often scaled by 2 as this approximately increases the computation cost by 4 canceling with the squeezing operation.\n", "However, we will choose the hidden dimensionalities $32, 48, 64$ for the\n", "three scales respectively to keep the number of parameters reasonable\n", "and show the efficiency of multi-scale architectures."]}, {"cell_type": "code", "execution_count": 22, "id": "f1aa3acb", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:59.353192Z", "iopub.status.busy": "2021-09-16T12:41:59.352715Z", "iopub.status.idle": "2021-09-16T12:41:59.354799Z", "shell.execute_reply": "2021-09-16T12:41:59.354387Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.052212, "end_time": "2021-09-16T12:41:59.354899", "exception": false, "start_time": "2021-09-16T12:41:59.302687", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def create_multiscale_flow():\n", " flow_layers = []\n", "\n", " vardeq_layers = [\n", " CouplingLayer(\n", " network=GatedConvNet(c_in=2, c_out=2, c_hidden=16),\n", " mask=create_checkerboard_mask(h=28, w=28, invert=(i % 2 == 1)),\n", " c_in=1,\n", " )\n", " for i in range(4)\n", " ]\n", " flow_layers += [VariationalDequantization(vardeq_layers)]\n", "\n", " flow_layers += [\n", " CouplingLayer(\n", " network=GatedConvNet(c_in=1, c_hidden=32),\n", " mask=create_checkerboard_mask(h=28, w=28, invert=(i % 2 == 1)),\n", " c_in=1,\n", " )\n", " for i in range(2)\n", " ]\n", " flow_layers += [SqueezeFlow()]\n", " for i in range(2):\n", " flow_layers += [\n", " CouplingLayer(\n", " network=GatedConvNet(c_in=4, c_hidden=48), mask=create_channel_mask(c_in=4, invert=(i % 2 == 1)), c_in=4\n", " )\n", " ]\n", " flow_layers += [SplitFlow(), SqueezeFlow()]\n", " for i in range(4):\n", " flow_layers += [\n", " CouplingLayer(\n", " network=GatedConvNet(c_in=8, c_hidden=64), mask=create_channel_mask(c_in=8, invert=(i % 2 == 1)), c_in=8\n", " )\n", " ]\n", "\n", " flow_model = ImageFlow(flow_layers).to(device)\n", " return flow_model"]}, {"cell_type": "markdown", "id": "ca021fca", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.043766, "end_time": "2021-09-16T12:41:59.442691", "exception": false, "start_time": "2021-09-16T12:41:59.398925", "status": "completed"}, "tags": []}, "source": ["We can show the difference in number of parameters below:"]}, {"cell_type": "code", "execution_count": 23, "id": "c13d7d7c", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:41:59.534720Z", "iopub.status.busy": "2021-09-16T12:41:59.534247Z", "iopub.status.idle": "2021-09-16T12:42:02.273185Z", "shell.execute_reply": "2021-09-16T12:42:02.272760Z"}, "papermill": {"duration": 2.786959, "end_time": "2021-09-16T12:42:02.273302", "exception": false, "start_time": "2021-09-16T12:41:59.486343", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Number of parameters: 335,128\n", "Number of parameters: 379,556\n", "Number of parameters: 1,062,090\n"]}], "source": ["def print_num_params(model):\n", " num_params = sum(np.prod(p.shape) for p in model.parameters())\n", " print(f\"Number of parameters: {num_params:,}\")\n", "\n", "\n", "print_num_params(create_simple_flow(use_vardeq=False))\n", "print_num_params(create_simple_flow(use_vardeq=True))\n", "print_num_params(create_multiscale_flow())"]}, {"cell_type": "markdown", "id": "949772c8", "metadata": {"papermill": {"duration": 0.04404, "end_time": "2021-09-16T12:42:02.361991", "exception": false, "start_time": "2021-09-16T12:42:02.317951", "status": "completed"}, "tags": []}, "source": ["Although the multi-scale flow has almost 3 times the parameters of the single scale flow,\n", "it is not necessarily more computationally expensive than its counterpart.\n", "We will compare the runtime in the following experiments as well."]}, {"cell_type": "markdown", "id": "a40ce763", "metadata": {"papermill": {"duration": 0.043937, "end_time": "2021-09-16T12:42:02.450029", "exception": false, "start_time": "2021-09-16T12:42:02.406092", "status": "completed"}, "tags": []}, "source": ["## Analysing the flows\n", "\n", "In the last part of the notebook, we will train all the models we have implemented above,\n", "and try to analyze the effect of the multi-scale architecture and variational dequantization.\n", "\n", "### Training flow variants\n", "\n", "Before we can analyse the flow models, we need to train them first.\n", "We provide pre-trained models that contain the validation and test performance, and run-time information.\n", "As flow models are computationally expensive, we advice you to rely on\n", "those pretrained models for a first run through the notebook."]}, {"cell_type": "code", "execution_count": 24, "id": "8b210a38", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:02.543059Z", "iopub.status.busy": "2021-09-16T12:42:02.542580Z", "iopub.status.idle": "2021-09-16T12:42:02.794090Z", "shell.execute_reply": "2021-09-16T12:42:02.793670Z"}, "papermill": {"duration": 0.300432, "end_time": "2021-09-16T12:42:02.794206", "exception": false, "start_time": "2021-09-16T12:42:02.493774", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n", "Found pretrained model, loading...\n", "Found pretrained model, loading...\n"]}], "source": ["flow_dict = {\"simple\": {}, \"vardeq\": {}, \"multiscale\": {}}\n", "flow_dict[\"simple\"][\"model\"], flow_dict[\"simple\"][\"result\"] = train_flow(\n", " create_simple_flow(use_vardeq=False), model_name=\"MNISTFlow_simple\"\n", ")\n", "flow_dict[\"vardeq\"][\"model\"], flow_dict[\"vardeq\"][\"result\"] = train_flow(\n", " create_simple_flow(use_vardeq=True), model_name=\"MNISTFlow_vardeq\"\n", ")\n", "flow_dict[\"multiscale\"][\"model\"], flow_dict[\"multiscale\"][\"result\"] = train_flow(\n", " create_multiscale_flow(), model_name=\"MNISTFlow_multiscale\"\n", ")"]}, {"cell_type": "markdown", "id": "0f554c5e", "metadata": {"papermill": {"duration": 0.046682, "end_time": "2021-09-16T12:42:02.888035", "exception": false, "start_time": "2021-09-16T12:42:02.841353", "status": "completed"}, "tags": []}, "source": ["### Density modeling and sampling\n", "\n", "Firstly, we can compare the models on their quantitative results.\n", "The following table shows all important statistics.\n", "The inference time specifies the time needed to determine the\n", "probability for a batch of 64 images for each model, and the sampling\n", "time the duration it took to sample a batch of 64 images."]}, {"cell_type": "code", "execution_count": 25, "id": "1a7fa75f", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:02.984361Z", "iopub.status.busy": "2021-09-16T12:42:02.983766Z", "iopub.status.idle": "2021-09-16T12:42:02.986662Z", "shell.execute_reply": "2021-09-16T12:42:02.987047Z"}, "papermill": {"duration": 0.052662, "end_time": "2021-09-16T12:42:02.987161", "exception": false, "start_time": "2021-09-16T12:42:02.934499", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", "\n"], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["%%html\n", "\n", ""]}, {"cell_type": "code", "execution_count": 26, "id": "30428798", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:03.098132Z", "iopub.status.busy": "2021-09-16T12:42:03.092877Z", "iopub.status.idle": "2021-09-16T12:42:03.101398Z", "shell.execute_reply": "2021-09-16T12:42:03.100990Z"}, "papermill": {"duration": 0.067197, "end_time": "2021-09-16T12:42:03.101499", "exception": false, "start_time": "2021-09-16T12:42:03.034302", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Model Validation Bpd Test Bpd Inference time Sampling time Num Parameters
simple 1.109 bpd 1.107 bpd 51 ms 50 ms 335,128
vardeq 1.068 bpd 1.066 bpd 69 ms 50 ms 379,556
multiscale1.029 bpd 1.026 bpd 40 ms 22 ms 1,062,090
"], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["\n", "table = [\n", " [\n", " key,\n", " \"%4.3f bpd\" % flow_dict[key][\"result\"][\"val\"][0][\"test_bpd\"],\n", " \"%4.3f bpd\" % flow_dict[key][\"result\"][\"test\"][0][\"test_bpd\"],\n", " \"%2.0f ms\" % (1000 * flow_dict[key][\"result\"][\"time\"]),\n", " \"%2.0f ms\" % (1000 * flow_dict[key][\"result\"].get(\"samp_time\", 0)),\n", " \"{:,}\".format(sum(np.prod(p.shape) for p in flow_dict[key][\"model\"].parameters())),\n", " ]\n", " for key in flow_dict\n", "]\n", "display(\n", " HTML(\n", " tabulate.tabulate(\n", " table,\n", " tablefmt=\"html\",\n", " headers=[\"Model\", \"Validation Bpd\", \"Test Bpd\", \"Inference time\", \"Sampling time\", \"Num Parameters\"],\n", " )\n", " )\n", ")"]}, {"cell_type": "markdown", "id": "0c8899de", "metadata": {"papermill": {"duration": 0.047332, "end_time": "2021-09-16T12:42:03.196088", "exception": false, "start_time": "2021-09-16T12:42:03.148756", "status": "completed"}, "tags": []}, "source": ["As we have intially expected, using variational dequantization improves upon standard dequantization in terms of bits per dimension.\n", "Although the difference with 0.04bpd doesn't seem impressive first, it is a considerably step for generative models\n", "(most state-of-the-art models improve upon previous models in a range of 0.02-0.1bpd on CIFAR with three times as high bpd).\n", "While it takes longer to evaluate the probability of an image due to the variational dequantization,\n", "which also leads to a longer training time, it does not have an effect on the sampling time.\n", "This is because inverting variational dequantization is the same as dequantization: finding the next lower integer.\n", "\n", "When we compare the two models to multi-scale architecture, we can see that the bits per dimension score again dropped by about 0.04bpd.\n", "Additionally, the inference time and sampling time improved notably despite having more parameters.\n", "Thus, we see that the multi-scale flow is not only stronger for density modeling, but also more efficient.\n", "\n", "Next, we can test the sampling quality of the models.\n", "We should note that the samples for variational dequantization and standard dequantization are very similar,\n", "and hence we visualize here only the ones for variational dequantization and the multi-scale model.\n", "However, feel free to also test out the `\"simple\"` model.\n", "The seeds are set to obtain reproducable generations and are not cherry picked."]}, {"cell_type": "code", "execution_count": 27, "id": "ab20301b", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:03.294176Z", "iopub.status.busy": "2021-09-16T12:42:03.293684Z", "iopub.status.idle": "2021-09-16T12:42:03.408730Z", "shell.execute_reply": "2021-09-16T12:42:03.408245Z"}, "papermill": {"duration": 0.165353, "end_time": "2021-09-16T12:42:03.408844", "exception": false, "start_time": "2021-09-16T12:42:03.243491", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 44\n"]}, {"data": {"application/pdf": "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U2luGJuY7Nq1i88QOeqoKThyq54jnAF8D5lOO4q6eZghg3DMx4+HDRsG4VSq5OTkzp07y2RQH+cYupkzJsfyhISEdu0GDRrk7+8fHCxj5OMDC3fu3Nmtm5ubG7v/qJ4fh2FZWVlgYGA9ROZLbcacnZ2d3YYNG44cEQiIHLVCjs8gx3rFxcVpe+wwHMViqlNTFU8fytD+QvrWW28RHEgwYLt27QDdTz9BFX7Pnj11b1Jdld7Scv/+/YJ+HIaJiYlyXSmeqn///qdPnxYLiBy1Qo7PMMcffxw7diyDEP5ZsmRJaalYTHV2NpwkkwxCQtG4YMEtzvkPOGNyr4qPh/p7ampqba2mnJ0xw8fHZ8KECQcPaueB5vPjMIyPj9ciZHoLQ1CofPv78z2nqG+IHOvNWoCjtbWCQSiXd+vW7cyZM+Ix1TU1Q4YM6dSp09ChSUlJ169fJz2U1ICGzq9QUFAArckuXSZNmgR1kcmTgeeFCxdIAyJHrZCjvp4RjpqZSXr3Xrp06eXLl8ljGqwm8aNuiBzpGCJHOobP5PgUjfdDjnT8kCMdP+RIxw850vFDjnT8kCMdP+RIx6+JOKIaK+RIR8iRjpAjHSFHOvofJ7UksAplbmRzdHJlYW0KZW5kb2JqCjE0IDAgb2JqCjY3MjgKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjE1IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQyMDMrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgMTYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDg2MDIgMDAwMDAgbiAKMDAwMDAwMDYzNSAwMDAwMCBuIAowMDAwMDAwNjU2IDAwMDAwIG4gCjAwMDAwMDA3NTUgMDAwMDAgbiAKMDAwMDAwMDc3NiAwMDAwMCBuIAowMDAwMDAwNzk3IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5NCAwMDAwMCBuIAowMDAwMDAwNjE1IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDU5NSAwMDAwMCBuIAowMDAwMDAwODI5IDAwMDAwIG4gCjAwMDAwMDg1ODEgMDAwMDAgbiAKMDAwMDAwODY2MiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDE1IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAxNiA+PgpzdGFydHhyZWYKODgxOQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:03.360522\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["pl.seed_everything(44)\n", "samples = flow_dict[\"vardeq\"][\"model\"].sample(img_shape=[16, 1, 28, 28])\n", "show_imgs(samples.cpu())"]}, {"cell_type": "code", "execution_count": 28, "id": "4ff785f9", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:03.509095Z", "iopub.status.busy": "2021-09-16T12:42:03.508627Z", "iopub.status.idle": "2021-09-16T12:42:03.616157Z", "shell.execute_reply": "2021-09-16T12:42:03.615677Z"}, "papermill": {"duration": 0.158469, "end_time": "2021-09-16T12:42:03.616267", "exception": false, "start_time": "2021-09-16T12:42:03.457798", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 44\n"]}, {"data": {"application/pdf": "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WyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjE1IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQyMDMrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgMTYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDk4NzQgMDAwMDAgbiAKMDAwMDAwMDYzNSAwMDAwMCBuIAowMDAwMDAwNjU2IDAwMDAwIG4gCjAwMDAwMDA3NTUgMDAwMDAgbiAKMDAwMDAwMDc3NiAwMDAwMCBuIAowMDAwMDAwNzk3IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5NCAwMDAwMCBuIAowMDAwMDAwNjE1IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDU5NSAwMDAwMCBuIAowMDAwMDAwODI5IDAwMDAwIG4gCjAwMDAwMDk4NTMgMDAwMDAgbiAKMDAwMDAwOTkzNCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDE1IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAxNiA+PgpzdGFydHhyZWYKMTAwOTEKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:03.572464\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["pl.seed_everything(44)\n", "samples = flow_dict[\"multiscale\"][\"model\"].sample(img_shape=[16, 8, 7, 7])\n", "show_imgs(samples.cpu())"]}, {"cell_type": "markdown", "id": "d070c015", "metadata": {"papermill": {"duration": 0.049106, "end_time": "2021-09-16T12:42:03.715575", "exception": false, "start_time": "2021-09-16T12:42:03.666469", "status": "completed"}, "tags": []}, "source": ["From the few samples, we can see a clear difference between the simple and the multi-scale model.\n", "The single-scale model has only learned local, small correlations while the multi-scale model was able to learn full,\n", "global relations that form digits.\n", "This show-cases another benefit of the multi-scale model.\n", "In contrast to VAEs, the outputs are sharp as normalizing flows can naturally model complex,\n", "multi-modal distributions while VAEs have the independent decoder output noise.\n", "Nevertheless, the samples from this flow are far from perfect as not all samples show true digits."]}, {"cell_type": "markdown", "id": "e16d33ec", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.049321, "end_time": "2021-09-16T12:42:03.814088", "exception": false, "start_time": "2021-09-16T12:42:03.764767", "status": "completed"}, "tags": []}, "source": ["### Interpolation in latent space\n", "\n", "Another popular test for the smoothness of the latent space of generative models is to interpolate between two training examples.\n", "As normalizing flows are strictly invertible, we can guarantee that any image is represented in the latent space.\n", "We again compare the variational dequantization model with the multi-scale model below."]}, {"cell_type": "code", "execution_count": 29, "id": "c066c621", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:03.919045Z", "iopub.status.busy": "2021-09-16T12:42:03.918562Z", "iopub.status.idle": "2021-09-16T12:42:03.949438Z", "shell.execute_reply": "2021-09-16T12:42:03.948964Z"}, "papermill": {"duration": 0.085776, "end_time": "2021-09-16T12:42:03.949542", "exception": false, "start_time": "2021-09-16T12:42:03.863766", "status": "completed"}, "tags": []}, "outputs": [], "source": ["@torch.no_grad()\n", "def interpolate(model, img1, img2, num_steps=8):\n", " \"\"\"\n", " Args:\n", " model: object of ImageFlow class that represents the (trained) flow model\n", " img1, img2: Image tensors of shape [1, 28, 28]. Images between which should be interpolated.\n", " num_steps: Number of interpolation steps. 8 interpolation steps mean 6 intermediate pictures besides img1 and img2\n", " \"\"\"\n", " imgs = torch.stack([img1, img2], dim=0).to(model.device)\n", " z, _ = model.encode(imgs)\n", " alpha = torch.linspace(0, 1, steps=num_steps, device=z.device).view(-1, 1, 1, 1)\n", " interpolations = z[0:1] * alpha + z[1:2] * (1 - alpha)\n", " interp_imgs = model.sample(interpolations.shape[:1] + imgs.shape[1:], z_init=interpolations)\n", " show_imgs(interp_imgs, row_size=8)\n", "\n", "\n", "exmp_imgs, _ = next(iter(train_loader))"]}, {"cell_type": "code", "execution_count": 30, "id": "dc209bbb", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:04.053707Z", "iopub.status.busy": "2021-09-16T12:42:04.053237Z", "iopub.status.idle": "2021-09-16T12:42:04.365802Z", "shell.execute_reply": "2021-09-16T12:42:04.366194Z"}, "papermill": {"duration": 0.36694, "end_time": "2021-09-16T12:42:04.366333", "exception": false, "start_time": "2021-09-16T12:42:03.999393", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"data": {"application/pdf": "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\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:04.133364\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"data": {"application/pdf": "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\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:04.336241\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["pl.seed_everything(42)\n", "for i in range(2):\n", " interpolate(flow_dict[\"vardeq\"][\"model\"], exmp_imgs[2 * i], exmp_imgs[2 * i + 1])"]}, {"cell_type": "code", "execution_count": 31, "id": "6a103456", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:04.481539Z", "iopub.status.busy": "2021-09-16T12:42:04.481065Z", "iopub.status.idle": "2021-09-16T12:42:04.697014Z", "shell.execute_reply": "2021-09-16T12:42:04.696379Z"}, "papermill": {"duration": 0.278454, "end_time": "2021-09-16T12:42:04.697129", "exception": false, "start_time": "2021-09-16T12:42:04.418675", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"data": {"application/pdf": "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IY2NEGlIYyNEGtLY6C/m5CTmCmVuZHN0cmVhbQplbmRvYmoKMTQgMCBvYmoKMzYwNgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDIwNCswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwNTQ1NyAwMDAwMCBuIAowMDAwMDAwNjQ2IDAwMDAwIG4gCjAwMDAwMDA2NjcgMDAwMDAgbiAKMDAwMDAwMDc2NiAwMDAwMCBuIAowMDAwMDAwNzg3IDAwMDAwIG4gCjAwMDAwMDA4MDggMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk2IDAwMDAwIG4gCjAwMDAwMDA2MjYgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNjA2IDAwMDAwIG4gCjAwMDAwMDA4NDAgMDAwMDAgbiAKMDAwMDAwNTQzNiAwMDAwMCBuIAowMDAwMDA1NTE3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMTUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDE2ID4+CnN0YXJ0eHJlZgo1Njc0CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:04.561824\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"data": {"application/pdf": "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bmRvYmoKMTQgMCBvYmoKMzU5MQplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDIwNCswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwNTQyNyAwMDAwMCBuIAowMDAwMDAwNjQ2IDAwMDAwIG4gCjAwMDAwMDA2NjcgMDAwMDAgbiAKMDAwMDAwMDc2NiAwMDAwMCBuIAowMDAwMDAwNzg3IDAwMDAwIG4gCjAwMDAwMDA4MDggMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk2IDAwMDAwIG4gCjAwMDAwMDA2MjYgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNjA2IDAwMDAwIG4gCjAwMDAwMDA4NDAgMDAwMDAgbiAKMDAwMDAwNTQwNiAwMDAwMCBuIAowMDAwMDA1NDg3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMTUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDE2ID4+CnN0YXJ0eHJlZgo1NjQ0CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:04.667333\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["pl.seed_everything(42)\n", "for i in range(2):\n", " interpolate(flow_dict[\"multiscale\"][\"model\"], exmp_imgs[2 * i], exmp_imgs[2 * i + 1])"]}, {"cell_type": "markdown", "id": "ac2dff10", "metadata": {"papermill": {"duration": 0.053429, "end_time": "2021-09-16T12:42:04.804401", "exception": false, "start_time": "2021-09-16T12:42:04.750972", "status": "completed"}, "tags": []}, "source": ["The interpolations of the multi-scale model result in more realistic digits\n", "(first row $7\\leftrightarrow 8\\leftrightarrow 6$, second row $9\\leftrightarrow 4\\leftrightarrow 6$),\n", "while the variational dequantization model focuses on local patterns that globally do not form a digit.\n", "For the multi-scale model, we actually did not do the \"true\" interpolation between the two images\n", "as we did not consider the variables that were split along the flow (they have been sampled randomly for all samples).\n", "However, as we will see in the next experiment, the early variables do not effect the overall image much."]}, {"cell_type": "markdown", "id": "e3c5cbb6", "metadata": {"papermill": {"duration": 0.052651, "end_time": "2021-09-16T12:42:04.909712", "exception": false, "start_time": "2021-09-16T12:42:04.857061", "status": "completed"}, "tags": []}, "source": ["### Visualization of latents in different levels of multi-scale\n", "\n", "In the following we will focus more on the multi-scale flow.\n", "We want to analyse what information is being stored in the variables split at early layers,\n", "and what information for the final variables.\n", "For this, we sample 8 images where each of them share the same final latent variables,\n", "but differ in the other part of the latent variables.\n", "Below we visualize three examples of this:"]}, {"cell_type": "code", "execution_count": 32, "id": "4c3c81a9", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:05.022247Z", "iopub.status.busy": "2021-09-16T12:42:05.021766Z", "iopub.status.idle": "2021-09-16T12:42:05.288274Z", "shell.execute_reply": "2021-09-16T12:42:05.287851Z"}, "papermill": {"duration": 0.323428, "end_time": "2021-09-16T12:42:05.288390", "exception": false, "start_time": "2021-09-16T12:42:04.964962", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 44\n"]}, {"data": {"application/pdf": "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\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:05.083293\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"data": {"application/pdf": "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aDcuPqu7u9s7BRbXDnCMHT16lI8jg/Ihg9h24g02NTUxxiIihHwd9XIzh+jo+/fvi8C1tLQATq+/d++eCFx9fT3ghg9XtNUEGZSJKganTJkC0B9+4I1TiMvOzgZcSoqQb/nd7nXr1gGOs2uWclx+fr4kSQUFinBkUCZkEItTwWBzc6g31dVCOlZUGAwGnU73889CcDdvwn0dcL/9JgRnt+v1ejghP/6oCEcG+4QMYnGqGKyuhuuC1WoVtJnDsmVQcPPmzbxxCnH790O7pKQkhZs58AZdugS4lJQUzu/A3GTwdVHFYEJCVFQUb36pvOPkyYMHD759+7Yg3NSpcFFobGwUhPvsM/gUV/MvWGTwdSGDWJx4g11dXTrd0v+slP3s2TO/PwTm43p7ewMCMjIy3J7VuOPi4trb2zG42tra0NCioiLvzhUmk6muzs9gPu7atWuMZWZmuj0bLC9fvryqiocjg6+GDL6VBhcsWPDRR75XZw4cgGm131W4+bjCwsKEBN+rM199pdVq/b7jw8clJydPnOj7L+enn8I05PBhP4P5OIvFEhMDGp1OZ1AQ4Py+k0MGZaKKwbq6ugEDQOOwYcO8e0N0dnZiOsJkWqPxPTdJEjyL2e12DM7hcFy9+vTp07179wYGwgn59Vc/g/m4HTt2DBoUExOTnp4eFAQ4m42HI4Ovhgy+lQa9aWlpSUtLCwvbsmWL33EKcR0dHXA77t/fwtkkWCEO7u+rV68ODeUNVoirqakZMmSIwbB27Vo+jgzKRBWDSvPu4cggFkcGsTgyiMWRQSyODGJxZBCLI4NYHBnE4sggFkcGsTgyiMWRQSyODGJxtKo8NmQQGzKIDRnEhgxi829rp63ZCmVuZHN0cmVhbQplbmRvYmoKMTQgMCBvYmoKMjQ5NwplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDIwNSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwNDI5MCAwMDAwMCBuIAowMDAwMDAwNjU5IDAwMDAwIG4gCjAwMDAwMDA2ODAgMDAwMDAgbiAKMDAwMDAwMDc3OSAwMDAwMCBuIAowMDAwMDAwODAwIDAwMDAwIG4gCjAwMDAwMDA4MjEgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDAyIDAwMDAwIG4gCjAwMDAwMDA2MzkgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNjE5IDAwMDAwIG4gCjAwMDAwMDA4NTMgMDAwMDAgbiAKMDAwMDAwNDI2OSAwMDAwMCBuIAowMDAwMDA0MzUwIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMTUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDE2ID4+CnN0YXJ0eHJlZgo0NTA3CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:05.171845\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"data": {"application/pdf": "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6oyJQeH59+HQIC0ODdLi0CAtDg3S4tAgLQ5nlacNGqQNGqQNGqQNGqTNfwHPD65oCmVuZHN0cmVhbQplbmRvYmoKMTQgMCBvYmoKMzYwNAplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMTUgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDIwNSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwNTQ4NyAwMDAwMCBuIAowMDAwMDAwNjU5IDAwMDAwIG4gCjAwMDAwMDA2ODAgMDAwMDAgbiAKMDAwMDAwMDc3OSAwMDAwMCBuIAowMDAwMDAwODAwIDAwMDAwIG4gCjAwMDAwMDA4MjEgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDAyIDAwMDAwIG4gCjAwMDAwMDA2MzkgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNjE5IDAwMDAwIG4gCjAwMDAwMDA4NTMgMDAwMDAgbiAKMDAwMDAwNTQ2NiAwMDAwMCBuIAowMDAwMDA1NTQ3IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMTUgMCBSIC9Sb290IDEgMCBSIC9TaXplIDE2ID4+CnN0YXJ0eHJlZgo1NzA0CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:05.257858\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["pl.seed_everything(44)\n", "for _ in range(3):\n", " z_init = flow_dict[\"multiscale\"][\"model\"].prior.sample(sample_shape=[1, 8, 7, 7])\n", " z_init = z_init.expand(8, -1, -1, -1)\n", " samples = flow_dict[\"multiscale\"][\"model\"].sample(img_shape=z_init.shape, z_init=z_init)\n", " show_imgs(samples.cpu())"]}, {"cell_type": "markdown", "id": "8cb113a3", "metadata": {"papermill": {"duration": 0.054683, "end_time": "2021-09-16T12:42:05.399905", "exception": false, "start_time": "2021-09-16T12:42:05.345222", "status": "completed"}, "tags": []}, "source": ["We see that the early split variables indeed have a smaller effect on the image.\n", "Still, small differences can be spot when we look carefully at the borders of the digits.\n", "For instance, the hole at the top of the 8 changes for different samples although all of them represent the same coarse structure.\n", "This shows that the flow indeed learns to separate the higher-level\n", "information in the final variables, while the early split ones contain\n", "local noise patterns."]}, {"cell_type": "markdown", "id": "256310bb", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.054446, "end_time": "2021-09-16T12:42:05.508976", "exception": false, "start_time": "2021-09-16T12:42:05.454530", "status": "completed"}, "tags": []}, "source": ["### Visualizing Dequantization\n", "\n", "As a final part of this notebook, we will look at the effect of variational dequantization.\n", "We have motivated variational dequantization by the issue of sharp edges/boarders being difficult to model,\n", "and a flow would rather prefer smooth, prior-like distributions.\n", "To check how what noise distribution $q(u|x)$ the flows in the\n", "variational dequantization module have learned, we can plot a histogram\n", "of output values from the dequantization and variational dequantization\n", "module."]}, {"cell_type": "code", "execution_count": 33, "id": "665bc626", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:05.628077Z", "iopub.status.busy": "2021-09-16T12:42:05.623860Z", "iopub.status.idle": "2021-09-16T12:42:05.658084Z", "shell.execute_reply": "2021-09-16T12:42:05.657594Z"}, "papermill": {"duration": 0.093163, "end_time": "2021-09-16T12:42:05.658187", "exception": false, "start_time": "2021-09-16T12:42:05.565024", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def visualize_dequant_distribution(model: ImageFlow, imgs: torch.Tensor, title: str = None):\n", " \"\"\"\n", " Args:\n", " model: The flow of which we want to visualize the dequantization distribution\n", " imgs: Example training images of which we want to visualize the dequantization distribution\n", " \"\"\"\n", " imgs = imgs.to(device)\n", " ldj = torch.zeros(imgs.shape[0], dtype=torch.float32).to(device)\n", " with torch.no_grad():\n", " dequant_vals = []\n", " for _ in tqdm(range(8), leave=False):\n", " d, _ = model.flows[0](imgs, ldj, reverse=False)\n", " dequant_vals.append(d)\n", " dequant_vals = torch.cat(dequant_vals, dim=0)\n", " dequant_vals = dequant_vals.view(-1).cpu().numpy()\n", " sns.set()\n", " plt.figure(figsize=(10, 3))\n", " plt.hist(dequant_vals, bins=256, color=to_rgb(\"C0\") + (0.5,), edgecolor=\"C0\", density=True)\n", " if title is not None:\n", " plt.title(title)\n", " plt.show()\n", " plt.close()\n", "\n", "\n", "sample_imgs, _ = next(iter(train_loader))"]}, {"cell_type": "code", "execution_count": 34, "id": "0e0a4cb8", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:05.770898Z", "iopub.status.busy": "2021-09-16T12:42:05.770412Z", "iopub.status.idle": "2021-09-16T12:42:06.769321Z", "shell.execute_reply": "2021-09-16T12:42:06.769715Z"}, "papermill": {"duration": 1.056909, "end_time": "2021-09-16T12:42:06.769866", "exception": false, "start_time": "2021-09-16T12:42:05.712957", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b632bd849f444d76b9cdb1ce9b458ecc", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/8 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:06.250107\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["visualize_dequant_distribution(flow_dict[\"simple\"][\"model\"], sample_imgs, title=\"Dequantization\")"]}, {"cell_type": "code", "execution_count": 35, "id": "c3deeaa6", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:06.894994Z", "iopub.status.busy": "2021-09-16T12:42:06.894523Z", "iopub.status.idle": "2021-09-16T12:42:08.443397Z", "shell.execute_reply": "2021-09-16T12:42:08.443791Z"}, "papermill": {"duration": 1.612603, "end_time": "2021-09-16T12:42:08.443933", "exception": false, "start_time": "2021-09-16T12:42:06.831330", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "e14299cd737b4a32ae911b93b2592618", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/8 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:07.870733\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["visualize_dequant_distribution(flow_dict[\"vardeq\"][\"model\"], sample_imgs, title=\"Variational dequantization\")"]}, {"cell_type": "markdown", "id": "37caab54", "metadata": {"papermill": {"duration": 0.062935, "end_time": "2021-09-16T12:42:08.571623", "exception": false, "start_time": "2021-09-16T12:42:08.508688", "status": "completed"}, "tags": []}, "source": ["The dequantization distribution in the first plot shows that the MNIST images have a strong bias towards 0 (black),\n", "and the distribution of them have a sharp border as mentioned before.\n", "The variational dequantization module has indeed learned a much smoother distribution with a Gaussian-like curve which can be modeled much better.\n", "For the other values, we would need to visualize the distribution $q(u|x)$ on a deeper level, depending on $x$.\n", "However, as all $u$'s interact and depend on each other, we would need\n", "to visualize a distribution in 784 dimensions, which is not that\n", "intuitive anymore."]}, {"cell_type": "markdown", "id": "2cc36677", "metadata": {"papermill": {"duration": 0.062538, "end_time": "2021-09-16T12:42:08.696736", "exception": false, "start_time": "2021-09-16T12:42:08.634198", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In conclusion, we have seen how to implement our own normalizing flow, and what difficulties arise if we want to apply them on images.\n", "Dequantization is a crucial step in mapping the discrete images into continuous space to prevent underisable delta-peak solutions.\n", "While dequantization creates hypercubes with hard border, variational dequantization allows us to fit a flow much better on the data.\n", "This allows us to obtain a lower bits per dimension score, while not affecting the sampling speed.\n", "The most common flow element, the coupling layer, is simple to implement, and yet effective.\n", "Furthermore, multi-scale architectures help to capture the global image context while allowing us to efficiently scale up the flow.\n", "Normalizing flows are an interesting alternative to VAEs as they allow an exact likelihood estimate in continuous space,\n", "and we have the guarantee that every possible input $x$ has a corresponding latent vector $z$.\n", "However, even beyond continuous inputs and images, flows can be applied and allow us to exploit\n", "the data structure in latent space, as e.g. on graphs for the task of molecule generation [6].\n", "Recent advances in [Neural ODEs](https://arxiv.org/pdf/1806.07366.pdf) allow a flow with infinite number of layers,\n", "called Continuous Normalizing Flows, whose potential is yet to fully explore.\n", "Overall, normalizing flows are an exciting research area which will continue over the next couple of years."]}, {"cell_type": "markdown", "id": "d5a736a3", "metadata": {"papermill": {"duration": 0.06309, "end_time": "2021-09-16T12:42:08.823219", "exception": false, "start_time": "2021-09-16T12:42:08.760129", "status": "completed"}, "tags": []}, "source": ["## References\n", "\n", "[1] Dinh, L., Sohl-Dickstein, J., and Bengio, S. (2017).\n", "\u201cDensity estimation using Real NVP,\u201d In: 5th International Conference on Learning Representations, ICLR 2017.\n", "[Link](https://arxiv.org/abs/1605.08803)\n", "\n", "[2] Kingma, D. P., and Dhariwal, P. (2018).\n", "\u201cGlow: Generative Flow with Invertible 1x1 Convolutions,\u201d In: Advances in Neural Information Processing Systems, vol.\n", "31, pp.\n", "10215--10224.\n", "[Link](http://papers.nips.cc/paper/8224-glow-generative-flow-with-invertible-1x1-convolutions.pdf)\n", "\n", "[3] Ho, J., Chen, X., Srinivas, A., Duan, Y., and Abbeel, P. (2019).\n", "\u201cFlow++: Improving Flow-Based Generative Models with Variational Dequantization and Architecture Design,\u201d\n", "in Proceedings of the 36th International Conference on Machine Learning, vol.\n", "97, pp.\n", "2722\u20132730.\n", "[Link](https://arxiv.org/abs/1902.00275)\n", "\n", "[4] Durkan, C., Bekasov, A., Murray, I., and Papamakarios, G. (2019).\n", "\u201cNeural Spline Flows,\u201d In: Advances in Neural Information Processing Systems, pp.\n", "7509\u20137520.\n", "[Link](http://papers.neurips.cc/paper/8969-neural-spline-flows.pdf)\n", "\n", "[5] Hoogeboom, E., Cohen, T. S., and Tomczak, J. M. (2020).\n", "\u201cLearning Discrete Distributions by Dequantization,\u201d arXiv preprint arXiv2001.11235v1.\n", "[Link](https://arxiv.org/abs/2001.11235)\n", "\n", "[6] Lippe, P., and Gavves, E. (2021).\n", "\u201cCategorical Normalizing Flows via Continuous Transformations,\u201d\n", "In: International Conference on Learning Representations, ICLR 2021.\n", "[Link](https://openreview.net/pdf?id=-GLNZeVDuik)"]}, {"cell_type": "markdown", "id": "d586cc6a", "metadata": {"papermill": {"duration": 0.065018, "end_time": "2021-09-16T12:42:08.951852", "exception": false, "start_time": "2021-09-16T12:42:08.886834", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 9: Normalizing Flows for Image Modeling\n", " :card_description: In this tutorial, we will take a closer look at complex, deep normalizing flows. The most popular, current application of deep normalizing flows is to model datasets of...\n", " :tags: Image,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/09-normalizing-flows.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab,id,colab_type,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 19.664801, "end_time": "2021-09-16T12:42:09.722798", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/09-normalizing-flows/NF_image_modeling.ipynb", "output_path": ".notebooks/course_UvA-DL/09-normalizing-flows.ipynb", "parameters": {}, "start_time": "2021-09-16T12:41:50.057997", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"0989ef9cef0e410d8ba8708d70a44485": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_bd437d01e4504868a0eab569e54fc653", "placeholder": "\u200b", "style": "IPY_MODEL_ddf110132d7a4b79a55b81c2445d20f3", "value": " 5/8 [00:00<00:00, 34.55it/s]"}}, "121d677b3fe64b379404376f171236bb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "20eced8f26104528a837da146da0288f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "212476674e8742c5a36d5a6a59b8d2cf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "21d9114cc8a54f54998866b9b4c3f542": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "301d6460224d44a69468ee761437b216": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_20eced8f26104528a837da146da0288f", "placeholder": "\u200b", "style": "IPY_MODEL_52759cf555ba4bb79cbb72b597401e1c", "value": " 0/8 [00:00<?, ?it/s]"}}, "3420b49f1b7245029561a3537f3672e5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "360f088f7aa34336bba895d5688f18f3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "52759cf555ba4bb79cbb72b597401e1c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5cac1945d4b94e45a8cb5128a7462916": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "63e7d0109110426fad97b7d711fa9919": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e7fb1721fcec4c4eab30b9ff09edbb4a", "max": 8.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_212476674e8742c5a36d5a6a59b8d2cf", "value": 8.0}}, "9caa927d490648dca2d1d7c54e448858": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ee2e2fb65a534444bfa51ad704a04f35", "max": 8.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_121d677b3fe64b379404376f171236bb", "value": 8.0}}, "a26ef8f75ac44f1c9a6872f079431d43": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_21d9114cc8a54f54998866b9b4c3f542", "placeholder": "\u200b", "style": "IPY_MODEL_edb3a164cc18459694b2c1dbb050b44a", "value": " 0%"}}, "b632bd849f444d76b9cdb1ce9b458ecc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_a26ef8f75ac44f1c9a6872f079431d43", "IPY_MODEL_63e7d0109110426fad97b7d711fa9919", "IPY_MODEL_301d6460224d44a69468ee761437b216"], "layout": "IPY_MODEL_3420b49f1b7245029561a3537f3672e5"}}, "bd437d01e4504868a0eab569e54fc653": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ca544fad59514bc6b35ed43510f6bb85": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ddf110132d7a4b79a55b81c2445d20f3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e14299cd737b4a32ae911b93b2592618": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_f797208673e64a9aac664d03c1b76f3a", "IPY_MODEL_9caa927d490648dca2d1d7c54e448858", "IPY_MODEL_0989ef9cef0e410d8ba8708d70a44485"], "layout": "IPY_MODEL_360f088f7aa34336bba895d5688f18f3"}}, "e7fb1721fcec4c4eab30b9ff09edbb4a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "edb3a164cc18459694b2c1dbb050b44a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ee2e2fb65a534444bfa51ad704a04f35": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f797208673e64a9aac664d03c1b76f3a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5cac1945d4b94e45a8cb5128a7462916", "placeholder": "\u200b", "style": "IPY_MODEL_ca544fad59514bc6b35ed43510f6bb85", "value": " 62%"}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/10-autoregressive-image-modeling.ipynb b/source/notebooks/course_UvA-DL/10-autoregressive-image-modeling.ipynb deleted file mode 100644 index f7164e0..0000000 --- a/source/notebooks/course_UvA-DL/10-autoregressive-image-modeling.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "10249518", "metadata": {"papermill": {"duration": 0.025611, "end_time": "2021-09-16T12:42:18.560094", "exception": false, "start_time": "2021-09-16T12:42:18.534483", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 10: Autoregressive Image Modeling\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-09-16T14:32:36.593971\n", "\n", "In this tutorial, we implement an autoregressive likelihood model for the task of image modeling.\n", "Autoregressive models are naturally strong generative models that constitute one of the current\n", "state-of-the-art architectures on likelihood-based image modeling,\n", "and are also the basis for large language generation models such as GPT3.\n", "We will focus on the PixelCNN architecture in this tutorial, and apply it to MNIST modeling.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/10-autoregressive-image-modeling.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "3109481c", "metadata": {"papermill": {"duration": 0.024038, "end_time": "2021-09-16T12:42:18.608085", "exception": false, "start_time": "2021-09-16T12:42:18.584047", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "86088e31", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-09-16T12:42:18.659637Z", "iopub.status.busy": "2021-09-16T12:42:18.659166Z", "iopub.status.idle": "2021-09-16T12:42:18.661686Z", "shell.execute_reply": "2021-09-16T12:42:18.661233Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.029126, "end_time": "2021-09-16T12:42:18.661813", "exception": false, "start_time": "2021-09-16T12:42:18.632687", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"torch>=1.6, <1.9\" \"seaborn\" \"torchvision\" \"matplotlib\" \"torchmetrics>=0.3\" \"pytorch-lightning>=1.3\""]}, {"cell_type": "markdown", "id": "e1dc997d", "metadata": {"papermill": {"duration": 0.024049, "end_time": "2021-09-16T12:42:18.710135", "exception": false, "start_time": "2021-09-16T12:42:18.686086", "status": "completed"}, "tags": []}, "source": ["
\n", "\n", "Similar to the language generation you have seen in assignment 2, autoregressive models work on images by modeling the likelihood of a pixel given all previous ones.\n", "For instance, in the picture below, we model the pixel $x_i$ as a conditional probability distribution\n", "based on all previous (here blue) pixels (figure credit - [Aaron van den Oord et al. ](https://arxiv.org/abs/1601.06759)):\n", "\n", "
\n", "\n", "Generally, autoregressive model over high-dimensional data $\\mathbf{x}$ factor the joint distribution as the following product of conditionals:\n", "\n", "$$p(\\mathbf{x})=p(x_1, ..., x_n)=\\prod_{i=1}^{n} p(x_i|x_1,...,x_{i-1})$$\n", "\n", "Learning these conditionals is often much simpler than learning the joint distribution $p(\\mathbf{x})$ all together.\n", "However, disadvantages of autoregressive models include slow sampling, especially for large images,\n", "as we need height-times-width forward passes through the model.\n", "In addition, for some applications, we require a latent space as modeled in VAEs and Normalizing Flows.\n", "For instance, in autoregressive models, we cannot interpolate between two images because of the lack of a latent representation.\n", "We will explore and discuss these benefits and drawbacks alongside with our implementation.\n", "\n", "Our implementation will focus on the [PixelCNN](https://arxiv.org/pdf/1606.05328.pdf) [2] model which has been discussed in detail in the lecture.\n", "Most current SOTA models use PixelCNN as their fundamental architecture,\n", "and various additions have been proposed to improve the performance\n", "(e.g. [PixelCNN++](https://arxiv.org/pdf/1701.05517.pdf) and [PixelSNAIL](http://proceedings.mlr.press/v80/chen18h/chen18h.pdf)).\n", "Hence, implementing PixelCNN is a good starting point for our short tutorial.\n", "\n", "First of all, we need to import our standard libraries. Similarly as in\n", "the last couple of tutorials, we will use [PyTorch\n", "Lightning](https://pytorch-lightning.readthedocs.io/en/latest/) here as\n", "well."]}, {"cell_type": "code", "execution_count": 2, "id": "6c3a3524", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:18.766239Z", "iopub.status.busy": "2021-09-16T12:42:18.759431Z", "iopub.status.idle": "2021-09-16T12:42:20.494992Z", "shell.execute_reply": "2021-09-16T12:42:20.494567Z"}, "papermill": {"duration": 1.761266, "end_time": "2021-09-16T12:42:20.495107", "exception": false, "start_time": "2021-09-16T12:42:18.733841", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_3486/3450944711.py:26: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Using device cuda:0\n"]}, {"data": {"text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["\n", "import math\n", "import os\n", "import urllib.request\n", "from urllib.error import HTTPError\n", "\n", "# Imports for plotting\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision\n", "from IPython.display import set_matplotlib_formats\n", "from matplotlib.colors import to_rgb\n", "from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint\n", "from torchvision import transforms\n", "from torchvision.datasets import MNIST\n", "from tqdm.notebook import tqdm\n", "\n", "plt.set_cmap(\"cividis\")\n", "# %matplotlib inline\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "\n", "# Path to the folder where the datasets are/should be downloaded (e.g. MNIST)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/tutorial12\")\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "# Fetching the device that will be used throughout this notebook\n", "device = torch.device(\"cpu\") if not torch.cuda.is_available() else torch.device(\"cuda:0\")\n", "print(\"Using device\", device)"]}, {"cell_type": "markdown", "id": "9527732b", "metadata": {"papermill": {"duration": 0.024895, "end_time": "2021-09-16T12:42:20.545620", "exception": false, "start_time": "2021-09-16T12:42:20.520725", "status": "completed"}, "tags": []}, "source": ["We again provide a pretrained model, which is downloaded below:"]}, {"cell_type": "code", "execution_count": 3, "id": "9b4b631a", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:20.600149Z", "iopub.status.busy": "2021-09-16T12:42:20.597718Z", "iopub.status.idle": "2021-09-16T12:42:20.772102Z", "shell.execute_reply": "2021-09-16T12:42:20.771611Z"}, "papermill": {"duration": 0.201736, "end_time": "2021-09-16T12:42:20.772216", "exception": false, "start_time": "2021-09-16T12:42:20.570480", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial12/PixelCNN.ckpt...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial12/\"\n", "# Files to download\n", "pretrained_files = [\"PixelCNN.ckpt\"]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(\"Downloading %s...\" % file_url)\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "a675f0ec", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.024998, "end_time": "2021-09-16T12:42:20.823035", "exception": false, "start_time": "2021-09-16T12:42:20.798037", "status": "completed"}, "tags": []}, "source": ["Similar to the Normalizing Flows in Tutorial 11, we will work on the\n", "MNIST dataset and use 8-bits per pixel (values between 0 and 255). The\n", "dataset is loaded below:"]}, {"cell_type": "code", "execution_count": 4, "id": "26f824ca", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:20.878881Z", "iopub.status.busy": "2021-09-16T12:42:20.878404Z", "iopub.status.idle": "2021-09-16T12:42:20.910958Z", "shell.execute_reply": "2021-09-16T12:42:20.910469Z"}, "papermill": {"duration": 0.062918, "end_time": "2021-09-16T12:42:20.911075", "exception": false, "start_time": "2021-09-16T12:42:20.848157", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}], "source": ["# Convert images from 0-1 to 0-255 (integers). We use the long datatype as we will use the images as labels as well\n", "def discretize(sample):\n", " return (sample * 255).to(torch.long)\n", "\n", "\n", "# Transformations applied on each image => only make them a tensor\n", "transform = transforms.Compose([transforms.ToTensor(), discretize])\n", "\n", "# Loading the training dataset. We need to split it into a training and validation part\n", "train_dataset = MNIST(root=DATASET_PATH, train=True, transform=transform, download=True)\n", "pl.seed_everything(42)\n", "train_set, val_set = torch.utils.data.random_split(train_dataset, [50000, 10000])\n", "\n", "# Loading the test set\n", "test_set = MNIST(root=DATASET_PATH, train=False, transform=transform, download=True)\n", "\n", "# We define a set of data loaders that we can use for various purposes later.\n", "train_loader = data.DataLoader(train_set, batch_size=128, shuffle=True, drop_last=True, pin_memory=True, num_workers=4)\n", "val_loader = data.DataLoader(val_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4)\n", "test_loader = data.DataLoader(test_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4)"]}, {"cell_type": "markdown", "id": "8392a407", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.025246, "end_time": "2021-09-16T12:42:20.962362", "exception": false, "start_time": "2021-09-16T12:42:20.937116", "status": "completed"}, "tags": []}, "source": ["A good practice is to always visualize some data examples to get an intuition of the data:"]}, {"cell_type": "code", "execution_count": 5, "id": "4c29da69", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:21.018762Z", "iopub.status.busy": "2021-09-16T12:42:21.018292Z", "iopub.status.idle": "2021-09-16T12:42:21.101884Z", "shell.execute_reply": "2021-09-16T12:42:21.102269Z"}, "papermill": {"duration": 0.114672, "end_time": "2021-09-16T12:42:21.102399", "exception": false, "start_time": "2021-09-16T12:42:20.987727", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:21.065617\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["def show_imgs(imgs):\n", " num_imgs = imgs.shape[0] if isinstance(imgs, torch.Tensor) else len(imgs)\n", " nrow = min(num_imgs, 4)\n", " ncol = int(math.ceil(num_imgs / nrow))\n", " imgs = torchvision.utils.make_grid(imgs, nrow=nrow, pad_value=128)\n", " imgs = imgs.clamp(min=0, max=255)\n", " np_imgs = imgs.cpu().numpy()\n", " plt.figure(figsize=(1.5 * nrow, 1.5 * ncol))\n", " plt.imshow(np.transpose(np_imgs, (1, 2, 0)), interpolation=\"nearest\")\n", " plt.axis(\"off\")\n", " plt.show()\n", " plt.close()\n", "\n", "\n", "show_imgs([train_set[i][0] for i in range(8)])"]}, {"cell_type": "markdown", "id": "353ee95b", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.026067, "end_time": "2021-09-16T12:42:21.155368", "exception": false, "start_time": "2021-09-16T12:42:21.129301", "status": "completed"}, "tags": []}, "source": ["## Masked autoregressive convolutions\n", "\n", "The core module of PixelCNN is its masked convolutions.\n", "In contrast to language models, we don't apply an LSTM on each pixel one-by-one.\n", "This would be inefficient because images are grids instead of sequences.\n", "Thus, it is better to rely on convolutions that have shown great success in deep CNN classification models.\n", "\n", "Nevertheless, we cannot just apply standard convolutions without any changes.\n", "Remember that during training of autoregressive models, we want to use teacher forcing which both helps the model training, and significantly reduces the time needed for training.\n", "For image modeling, teacher forcing is implemented by using a training image as input to the model, and we want to obtain as output the prediction for each pixel based on *only* its predecessors.\n", "Thus, we need to ensure that the prediction for a specific pixel can only be influenced by its predecessors and not by its own value or any \"future\" pixels.\n", "For this, we apply convolutions with a mask.\n", "\n", "Which mask we use depends on the ordering of pixels we decide on, i.e. which is the first pixel we predict,\n", "which is the second one, etc.\n", "The most commonly used ordering is to denote the upper left pixel as the start pixel,\n", "and sort the pixels row by row, as shown in the visualization at the top of the tutorial.\n", "Thus, the second pixel is on the right of the first one (first row, second column),\n", "and once we reach the end of the row, we start in the second row, first column.\n", "If we now want to apply this to our convolutions, we need to ensure that the prediction of pixel 1\n", "is not influenced by its own \"true\" input, and all pixels on its right and in any lower row.\n", "In convolutions, this means that we want to set those entries of the weight matrix to zero that take pixels on the right and below into account.\n", "As an example for a 5x5 kernel, see a mask below (figure credit - [Aaron van den Oord](https://arxiv.org/pdf/1606.05328.pdf)):\n", "\n", "
\n", "\n", "Before looking into the application of masked convolutions in PixelCNN\n", "in detail, let's first implement a module that allows us to apply an\n", "arbitrary mask to a convolution:"]}, {"cell_type": "code", "execution_count": 6, "id": "7f373c01", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:21.215016Z", "iopub.status.busy": "2021-09-16T12:42:21.214539Z", "iopub.status.idle": "2021-09-16T12:42:21.216213Z", "shell.execute_reply": "2021-09-16T12:42:21.216588Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.033688, "end_time": "2021-09-16T12:42:21.216710", "exception": false, "start_time": "2021-09-16T12:42:21.183022", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class MaskedConvolution(nn.Module):\n", " def __init__(self, c_in, c_out, mask, **kwargs):\n", " \"\"\"Implements a convolution with mask applied on its weights.\n", "\n", " Args:\n", " c_in: Number of input channels\n", " c_out: Number of output channels\n", " mask: Tensor of shape [kernel_size_H, kernel_size_W] with 0s where\n", " the convolution should be masked, and 1s otherwise.\n", " kwargs: Additional arguments for the convolution\n", " \"\"\"\n", " super().__init__()\n", " # For simplicity: calculate padding automatically\n", " kernel_size = (mask.shape[0], mask.shape[1])\n", " dilation = 1 if \"dilation\" not in kwargs else kwargs[\"dilation\"]\n", " padding = tuple(dilation * (kernel_size[i] - 1) // 2 for i in range(2))\n", " # Actual convolution\n", " self.conv = nn.Conv2d(c_in, c_out, kernel_size, padding=padding, **kwargs)\n", "\n", " # Mask as buffer => it is no parameter but still a tensor of the module\n", " # (must be moved with the devices)\n", " self.register_buffer(\"mask\", mask[None, None])\n", "\n", " def forward(self, x):\n", " self.conv.weight.data *= self.mask # Ensures zero's at masked positions\n", " return self.conv(x)"]}, {"cell_type": "markdown", "id": "ab31353f", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.026241, "end_time": "2021-09-16T12:42:21.269129", "exception": false, "start_time": "2021-09-16T12:42:21.242888", "status": "completed"}, "tags": []}, "source": ["### Vertical and horizontal convolution stacks\n", "\n", "To build our own autoregressive image model, we could simply stack a few masked convolutions on top of each other.\n", "This was actually the case for the original PixelCNN model, discussed in the paper\n", "[Pixel Recurrent Neural Networks](https://arxiv.org/pdf/1601.06759.pdf), but this leads to a considerable issue.\n", "When sequentially applying a couple of masked convolutions, the receptive field of a pixel\n", "show to have a \"blind spot\" on the right upper side, as shown in the figure below\n", "(figure credit - [Aaron van den Oord et al. ](https://arxiv.org/pdf/1606.05328.pdf)):\n", "\n", "
\n", "\n", "Although a pixel should be able to take into account all other pixels above and left of it,\n", "a stack of masked convolutions does not allow us to look to the upper pixels on the right.\n", "This is because the features of the pixels above, which we use for convolution,\n", "do not contain any information of the pixels on the right of the same row.\n", "If they would, we would be \"cheating\" and actually looking into the future.\n", "To overcome this issue, van den Oord et.\n", "al [2] proposed to split the convolutions into a vertical and a horizontal stack.\n", "The vertical stack looks at all pixels above the current one, while the horizontal takes into account all on the left.\n", "While keeping both of them separate, we can actually look at the pixels on the right with the vertical stack without breaking any of our assumptions.\n", "The two convolutions are also shown in the figure above.\n", "\n", "Let us implement them here as follows:"]}, {"cell_type": "code", "execution_count": 7, "id": "08032e0c", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:21.326679Z", "iopub.status.busy": "2021-09-16T12:42:21.323108Z", "iopub.status.idle": "2021-09-16T12:42:21.329089Z", "shell.execute_reply": "2021-09-16T12:42:21.328692Z"}, "papermill": {"duration": 0.033791, "end_time": "2021-09-16T12:42:21.329186", "exception": false, "start_time": "2021-09-16T12:42:21.295395", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class VerticalStackConvolution(MaskedConvolution):\n", " def __init__(self, c_in, c_out, kernel_size=3, mask_center=False, **kwargs):\n", " # Mask out all pixels below. For efficiency, we could also reduce the kernel\n", " # size in height, but for simplicity, we stick with masking here.\n", " mask = torch.ones(kernel_size, kernel_size)\n", " mask[kernel_size // 2 + 1 :, :] = 0\n", "\n", " # For the very first convolution, we will also mask the center row\n", " if mask_center:\n", " mask[kernel_size // 2, :] = 0\n", "\n", " super().__init__(c_in, c_out, mask, **kwargs)\n", "\n", "\n", "class HorizontalStackConvolution(MaskedConvolution):\n", " def __init__(self, c_in, c_out, kernel_size=3, mask_center=False, **kwargs):\n", " # Mask out all pixels on the left. Note that our kernel has a size of 1\n", " # in height because we only look at the pixel in the same row.\n", " mask = torch.ones(1, kernel_size)\n", " mask[0, kernel_size // 2 + 1 :] = 0\n", "\n", " # For the very first convolution, we will also mask the center pixel\n", " if mask_center:\n", " mask[0, kernel_size // 2] = 0\n", "\n", " super().__init__(c_in, c_out, mask, **kwargs)"]}, {"cell_type": "markdown", "id": "4d152ec5", "metadata": {"papermill": {"duration": 0.026372, "end_time": "2021-09-16T12:42:21.381597", "exception": false, "start_time": "2021-09-16T12:42:21.355225", "status": "completed"}, "tags": []}, "source": ["Note that we have an input argument called `mask_center`. Remember that\n", "the input to the model is the actual input image. Hence, the very first\n", "convolution we apply cannot use the center pixel as input, but must be\n", "masked. All consecutive convolutions, however, should use the center\n", "pixel as we otherwise lose the features of the previous layer. Hence,\n", "the input argument `mask_center` is True for the very first\n", "convolutions, and False for all others."]}, {"cell_type": "markdown", "id": "cd8536aa", "metadata": {"papermill": {"duration": 0.026279, "end_time": "2021-09-16T12:42:21.434188", "exception": false, "start_time": "2021-09-16T12:42:21.407909", "status": "completed"}, "tags": []}, "source": ["### Visualizing the receptive field\n", "\n", "To validate our implementation of masked convolutions, we can visualize the receptive field we obtain with such convolutions.\n", "We should see that with increasing number of convolutional layers, the receptive field grows in both vertical and horizontal direction, without the issue of a blind spot.\n", "The receptive field can be empirically measured by backpropagating an arbitrary loss for the output features of a speicifc pixel with respect to the input.\n", "We implement this idea below, and visualize the receptive field below."]}, {"cell_type": "code", "execution_count": 8, "id": "52423559", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:21.493549Z", "iopub.status.busy": "2021-09-16T12:42:21.493069Z", "iopub.status.idle": "2021-09-16T12:42:21.641737Z", "shell.execute_reply": "2021-09-16T12:42:21.641316Z"}, "papermill": {"duration": 0.181442, "end_time": "2021-09-16T12:42:21.641845", "exception": false, "start_time": "2021-09-16T12:42:21.460403", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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Nzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTYgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTUgMCBSID4+CmVuZG9iagoxNiAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjE1IDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE4IDAgb2JqCjw8IC9CIDE5IDAgUiAvVyAyMCAwIFIgL2EgMjEgMCBSIC9jIDIyIDAgUiAvZCAyMyAwIFIgL2UgMjQgMCBSIC9mIDI1IDAgUgovZyAyNiAwIFIgL2ggMjcgMCBSIC9pIDI4IDAgUiAvbCAyOSAwIFIgL24gMzAgMCBSIC9wIDMxIDAgUiAvciAzMiAwIFIKL3NwYWNlIDMzIDAgUiAvdCAzNCAwIFIgL3YgMzUgMCBSIC95IDM2IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTcgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9JMSAxMyAwIFIgL0kyIDE0IDAgUiA+PgplbmRvYmoKMTMgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOCAvQ29sb3JTcGFjZSBbL0luZGV4ZWQgL0RldmljZVJHQiAxICj95zcAIk0pXQovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAxIC9Db2x1bW5zIDE1MyAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgMTUzIC9MZW5ndGggMzcgMCBSIC9TdWJ0eXBlIC9JbWFnZQovVHlwZSAvWE9iamVjdCAvV2lkdGggMTUzID4+CnN0cmVhbQp4nO3OsQnAQAzAQGf/pdOmSSF4eBenAcTNs7W5DfiNrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWOySbb4eWhzZkfUPWN2R9Q9Y3ZH1DtiGyHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHllvr+wFBs9ayQplbmRzdHJlYW0KZW5kb2JqCjM3IDAgb2JqCjE4OAplbmRvYmoKMTQgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOCAvQ29sb3JTcGFjZSBbL0luZGV4ZWQgL0RldmljZVJHQiAxICj95zcAIk0pXQovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAxIC9Db2x1bW5zIDE1MyAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgMTUzIC9MZW5ndGggMzggMCBSIC9TdWJ0eXBlIC9JbWFnZQovVHlwZSAvWE9iamVjdCAvV2lkdGggMTUzID4+CnN0cmVhbQp4nO3OsQnAQAzAQGf/pdOmSSF4eBenAcTNs7W5DfiNrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWOySbb4eWhzZkfUPWN2R9Q9Y3ZH1DtiGyHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHllvr+wFBs9ayQplbmRzdHJlYW0KZW5kb2JqCjM4IDAgb2JqCjE4OAplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMzkgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDIyMSswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA0MAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwODE0OCAwMDAwMCBuIAowMDAwMDA3MDE0IDAwMDAwIG4gCjAwMDAwMDcwNDYgMDAwMDAgbiAKMDAwMDAwNzE0NSAwMDAwMCBuIAowMDAwMDA3MTY2IDAwMDAwIG4gCjAwMDAwMDcxODcgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDAxIDAwMDAwIG4gCjAwMDAwMDA3NjggMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNzQ4IDAwMDAwIG4gCjAwMDAwMDcyMzAgMDAwMDAgbiAKMDAwMDAwNzY4OSAwMDAwMCBuIAowMDAwMDA1NzU1IDAwMDAwIG4gCjAwMDAwMDU1NTUgMDAwMDAgbiAKMDAwMDAwNTE2MSAwMDAwMCBuIAowMDAwMDA2ODA4IDAwMDAwIG4gCjAwMDAwMDA3ODggMDAwMDAgbiAKMDAwMDAwMTEyNSAwMDAwMCBuIAowMDAwMDAxMjg5IDAwMDAwIG4gCjAwMDAwMDE2NjkgMDAwMDAgbiAKMDAwMDAwMTk3NCAwMDAwMCBuIAowMDAwMDAyMjc4IDAwMDAwIG4gCjAwMDAwMDI2MDAgMDAwMDAgbiAKMDAwMDAwMjgwOSAwMDAwMCBuIAowMDAwMDAzMjIzIDAwMDAwIG4gCjAwMDAwMDM0NjAgMDAwMDAgbiAKMDAwMDAwMzYwNCAwMDAwMCBuIAowMDAwMDAzNzIzIDAwMDAwIG4gCjAwMDAwMDM5NTkgMDAwMDAgbiAKMDAwMDAwNDI3MSAwMDAwMCBuIAowMDAwMDA0NTA0IDAwMDAwIG4gCjAwMDAwMDQ1OTQgMDAwMDAgbiAKMDAwMDAwNDgwMCAwMDAwMCBuIAowMDAwMDA0OTQ3IDAwMDAwIG4gCjAwMDAwMDc2NjkgMDAwMDAgbiAKMDAwMDAwODEyOCAwMDAwMCBuIAowMDAwMDA4MjA4IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzkgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQwID4+CnN0YXJ0eHJlZgo4MzY1CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:21.570580\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["inp_img = torch.zeros(1, 1, 11, 11)\n", "inp_img.requires_grad_()\n", "\n", "\n", "def show_center_recep_field(img, out):\n", " \"\"\"Calculates the gradients of the input with respect to the output center pixel, and visualizes the overall\n", " receptive field.\n", "\n", " Args:\n", " img: Input image for which we want to calculate the receptive field on.\n", " out: Output features/loss which is used for backpropagation, and should be\n", " the output of the network/computation graph.\n", " \"\"\"\n", " # Determine gradients\n", " loss = out[0, :, img.shape[2] // 2, img.shape[3] // 2].sum() # L1 loss for simplicity\n", " # Retain graph as we want to stack multiple layers and show the receptive field of all of them\n", " loss.backward(retain_graph=True)\n", " img_grads = img.grad.abs()\n", " img.grad.fill_(0) # Reset grads\n", "\n", " # Plot receptive field\n", " img = img_grads.squeeze().cpu().numpy()\n", " fig, ax = plt.subplots(1, 2)\n", " _ = ax[0].imshow(img)\n", " ax[1].imshow(img > 0)\n", " # Mark the center pixel in red if it doesn't have any gradients (should be\n", " # the case for standard autoregressive models)\n", " show_center = img[img.shape[0] // 2, img.shape[1] // 2] == 0\n", " if show_center:\n", " center_pixel = np.zeros(img.shape + (4,))\n", " center_pixel[center_pixel.shape[0] // 2, center_pixel.shape[1] // 2, :] = np.array([1.0, 0.0, 0.0, 1.0])\n", " for i in range(2):\n", " ax[i].axis(\"off\")\n", " if show_center:\n", " ax[i].imshow(center_pixel)\n", " ax[0].set_title(\"Weighted receptive field\")\n", " ax[1].set_title(\"Binary receptive field\")\n", " plt.show()\n", " plt.close()\n", "\n", "\n", "show_center_recep_field(inp_img, inp_img)"]}, {"cell_type": "markdown", "id": "e9b19d4a", "metadata": {"papermill": {"duration": 0.027207, "end_time": "2021-09-16T12:42:21.696690", "exception": false, "start_time": "2021-09-16T12:42:21.669483", "status": "completed"}, "tags": []}, "source": ["Let's first visualize the receptive field of a horizontal convolution\n", "without the center pixel. We use a small, arbitrary input image\n", "($11\\times 11$ pixels), and calculate the loss for the center pixel. For\n", "simplicity, we initialize all weights with 1 and the bias with 0, and\n", "use a single channel. This is sufficient for our visualization purposes."]}, {"cell_type": "code", "execution_count": 9, "id": "ad09d7c8", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:21.755609Z", "iopub.status.busy": "2021-09-16T12:42:21.755136Z", "iopub.status.idle": "2021-09-16T12:42:21.910953Z", "shell.execute_reply": "2021-09-16T12:42:21.910478Z"}, "papermill": {"duration": 0.18684, "end_time": "2021-09-16T12:42:21.911059", "exception": false, "start_time": "2021-09-16T12:42:21.724219", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM3IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJztzrEJgFAQQLHT/Ye21cLiCeIXkgky26rm68Ats86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrDPrzDqzzqx7PJuL/czMzMzMzMzsN7PXmXVmnVln1pl1Zp1ZZ9aZdWadWWfWmXVmnVln1pl1Zp1ZZ9aZdWadWWfWmXVmnVln1pl1Zp1ZZ9aZdWbdurMDCeZbZQplbmRzdHJlYW0KZW5kb2JqCjM3IDAgb2JqCjE3MgplbmRvYmoKMTQgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOCAvQ29sb3JTcGFjZSBbL0luZGV4ZWQgL0RldmljZVJHQiAyICj95zcAIk3/AAApXQovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAxIC9Db2x1bW5zIDE1MyAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgMTUzIC9MZW5ndGggMzggMCBSIC9TdWJ0eXBlIC9JbWFnZQovVHlwZSAvWE9iamVjdCAvV2lkdGggMTUzID4+CnN0cmVhbQp4nO3OsQmAUBBAsdP9h7bVwuIJ4heSCTLbqubrwC2zzqwz68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrHs8m4v9zMzMzMzMzOw3s9eZdWadWWfWmXVmnVln1pl1Zp1ZZ9aZdWadWWfWmXVmnVln1pl1Zp1ZZ9aZdWadWWfWmXVmnVln1pl1Zt26swMJ5ltlCmVuZHN0cmVhbQplbmRvYmoKMzggMCBvYmoKMTcyCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozOSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MjIxKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQwCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDA4MTIwIDAwMDAwIG4gCjAwMDAwMDcwMTIgMDAwMDAgbiAKMDAwMDAwNzA0NCAwMDAwMCBuIAowMDAwMDA3MTQzIDAwMDAwIG4gCjAwMDAwMDcxNjQgMDAwMDAgbiAKMDAwMDAwNzE4NSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDEgMDAwMDAgbiAKMDAwMDAwMDc2NiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3NDYgMDAwMDAgbiAKMDAwMDAwNzIyOCAwMDAwMCBuIAowMDAwMDA3Njc0IDAwMDAwIG4gCjAwMDAwMDU3NTMgMDAwMDAgbiAKMDAwMDAwNTU1MyAwMDAwMCBuIAowMDAwMDA1MTU5IDAwMDAwIG4gCjAwMDAwMDY4MDYgMDAwMDAgbiAKMDAwMDAwMDc4NiAwMDAwMCBuIAowMDAwMDAxMTIzIDAwMDAwIG4gCjAwMDAwMDEyODcgMDAwMDAgbiAKMDAwMDAwMTY2NyAwMDAwMCBuIAowMDAwMDAxOTcyIDAwMDAwIG4gCjAwMDAwMDIyNzYgMDAwMDAgbiAKMDAwMDAwMjU5OCAwMDAwMCBuIAowMDAwMDAyODA3IDAwMDAwIG4gCjAwMDAwMDMyMjEgMDAwMDAgbiAKMDAwMDAwMzQ1OCAwMDAwMCBuIAowMDAwMDAzNjAyIDAwMDAwIG4gCjAwMDAwMDM3MjEgMDAwMDAgbiAKMDAwMDAwMzk1NyAwMDAwMCBuIAowMDAwMDA0MjY5IDAwMDAwIG4gCjAwMDAwMDQ1MDIgMDAwMDAgbiAKMDAwMDAwNDU5MiAwMDAwMCBuIAowMDAwMDA0Nzk4IDAwMDAwIG4gCjAwMDAwMDQ5NDUgMDAwMDAgbiAKMDAwMDAwNzY1NCAwMDAwMCBuIAowMDAwMDA4MTAwIDAwMDAwIG4gCjAwMDAwMDgxODAgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAzOSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNDAgPj4Kc3RhcnR4cmVmCjgzMzcKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:21.840084\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["horiz_conv = HorizontalStackConvolution(c_in=1, c_out=1, kernel_size=3, mask_center=True)\n", "horiz_conv.conv.weight.data.fill_(1)\n", "horiz_conv.conv.bias.data.fill_(0)\n", "horiz_img = horiz_conv(inp_img)\n", "show_center_recep_field(inp_img, horiz_img)"]}, {"cell_type": "markdown", "id": "a4151321", "metadata": {"papermill": {"duration": 0.02825, "end_time": "2021-09-16T12:42:21.967945", "exception": false, "start_time": "2021-09-16T12:42:21.939695", "status": "completed"}, "tags": []}, "source": ["The receptive field is shown in yellow, the center pixel in red, and all other pixels outside of the receptive field are dark blue.\n", "As expected, the receptive field of a single horizontal convolution with the center pixel masked and a $3\\times3$ kernel is only the pixel on the left.\n", "If we use a larger kernel size, more pixels would be taken into account on the left.\n", "\n", "Next, let's take a look at the vertical convolution:"]}, {"cell_type": "code", "execution_count": 10, "id": "4d7db603", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:22.028223Z", "iopub.status.busy": "2021-09-16T12:42:22.026790Z", "iopub.status.idle": "2021-09-16T12:42:22.183299Z", "shell.execute_reply": "2021-09-16T12:42:22.182815Z"}, "papermill": {"duration": 0.186816, "end_time": "2021-09-16T12:42:22.183411", "exception": false, "start_time": "2021-09-16T12:42:21.996595", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM3IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJzt2LEJgEAABMHX/os2NRHcV1FktoBj4hvLVxtvAw4j65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHrTsnE2MjIyMjIyMrLLrfvumSTrkfXIemQ9sh5Z74e/xuOR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6G2EeWd0KZW5kc3RyZWFtCmVuZG9iagozNyAwIG9iagoxODUKZW5kb2JqCjE0IDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM4IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJzt2LEJgEAABMHX/os2NRHcV1FktoBj4hvLVxtvAw4j65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHrTsnE2MjIyMjIyMrLLrfvumSTrkfXIemQ9sh5Z74e/xuOR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6G2EeWd0KZW5kc3RyZWFtCmVuZG9iagozOCAwIG9iagoxODUKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjM5IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQyMjIrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDAKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDgxNDYgMDAwMDAgbiAKMDAwMDAwNzAxMiAwMDAwMCBuIAowMDAwMDA3MDQ0IDAwMDAwIG4gCjAwMDAwMDcxNDMgMDAwMDAgbiAKMDAwMDAwNzE2NCAwMDAwMCBuIAowMDAwMDA3MTg1IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwMSAwMDAwMCBuIAowMDAwMDAwNzY2IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDc0NiAwMDAwMCBuIAowMDAwMDA3MjI4IDAwMDAwIG4gCjAwMDAwMDc2ODcgMDAwMDAgbiAKMDAwMDAwNTc1MyAwMDAwMCBuIAowMDAwMDA1NTUzIDAwMDAwIG4gCjAwMDAwMDUxNTkgMDAwMDAgbiAKMDAwMDAwNjgwNiAwMDAwMCBuIAowMDAwMDAwNzg2IDAwMDAwIG4gCjAwMDAwMDExMjMgMDAwMDAgbiAKMDAwMDAwMTI4NyAwMDAwMCBuIAowMDAwMDAxNjY3IDAwMDAwIG4gCjAwMDAwMDE5NzIgMDAwMDAgbiAKMDAwMDAwMjI3NiAwMDAwMCBuIAowMDAwMDAyNTk4IDAwMDAwIG4gCjAwMDAwMDI4MDcgMDAwMDAgbiAKMDAwMDAwMzIyMSAwMDAwMCBuIAowMDAwMDAzNDU4IDAwMDAwIG4gCjAwMDAwMDM2MDIgMDAwMDAgbiAKMDAwMDAwMzcyMSAwMDAwMCBuIAowMDAwMDAzOTU3IDAwMDAwIG4gCjAwMDAwMDQyNjkgMDAwMDAgbiAKMDAwMDAwNDUwMiAwMDAwMCBuIAowMDAwMDA0NTkyIDAwMDAwIG4gCjAwMDAwMDQ3OTggMDAwMDAgbiAKMDAwMDAwNDk0NSAwMDAwMCBuIAowMDAwMDA3NjY3IDAwMDAwIG4gCjAwMDAwMDgxMjYgMDAwMDAgbiAKMDAwMDAwODIwNiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM5IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0MCA+PgpzdGFydHhyZWYKODM2MwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:22.111784\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["vert_conv = VerticalStackConvolution(c_in=1, c_out=1, kernel_size=3, mask_center=True)\n", "vert_conv.conv.weight.data.fill_(1)\n", "vert_conv.conv.bias.data.fill_(0)\n", "vert_img = vert_conv(inp_img)\n", "show_center_recep_field(inp_img, vert_img)"]}, {"cell_type": "markdown", "id": "f506aec8", "metadata": {"papermill": {"duration": 0.029554, "end_time": "2021-09-16T12:42:22.243059", "exception": false, "start_time": "2021-09-16T12:42:22.213505", "status": "completed"}, "tags": []}, "source": ["The vertical convolution takes all pixels above into account. Combining\n", "these two, we get the L-shaped receptive field of the original masked\n", "convolution:"]}, {"cell_type": "code", "execution_count": 11, "id": "63b459cc", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:22.305232Z", "iopub.status.busy": "2021-09-16T12:42:22.304151Z", "iopub.status.idle": "2021-09-16T12:42:22.458303Z", "shell.execute_reply": "2021-09-16T12:42:22.457833Z"}, "papermill": {"duration": 0.185539, "end_time": "2021-09-16T12:42:22.458405", "exception": false, "start_time": "2021-09-16T12:42:22.272866", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM3IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJztzrENgDAAwLDC/0ezsiCRIqCDc0Dksa3a+BtwGVmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWm5aNu5GRkZGRkZGRPZbt52aXZGRkZGRkZJ/LXo+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9ZbV3YAnghZJwplbmRzdHJlYW0KZW5kb2JqCjM3IDAgb2JqCjE4MQplbmRvYmoKMTQgMCBvYmoKPDwgL0JpdHNQZXJDb21wb25lbnQgOCAvQ29sb3JTcGFjZSBbL0luZGV4ZWQgL0RldmljZVJHQiAyICj95zcAIk3/AAApXQovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAxIC9Db2x1bW5zIDE1MyAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgMTUzIC9MZW5ndGggMzggMCBSIC9TdWJ0eXBlIC9JbWFnZQovVHlwZSAvWE9iamVjdCAvV2lkdGggMTUzID4+CnN0cmVhbQp4nO3OsQ2AMADAsML/R7OyIJEioINzQOSxrdr4G3AZWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9ablo27kZGRkZGRkZE9lu3nZpdkZGRkZGRkn8tej6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1ltXdgCeCFknCmVuZHN0cmVhbQplbmRvYmoKMzggMCBvYmoKMTgxCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozOSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MjIyKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQwCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDA4MTM4IDAwMDAwIG4gCjAwMDAwMDcwMTIgMDAwMDAgbiAKMDAwMDAwNzA0NCAwMDAwMCBuIAowMDAwMDA3MTQzIDAwMDAwIG4gCjAwMDAwMDcxNjQgMDAwMDAgbiAKMDAwMDAwNzE4NSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDEgMDAwMDAgbiAKMDAwMDAwMDc2NiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3NDYgMDAwMDAgbiAKMDAwMDAwNzIyOCAwMDAwMCBuIAowMDAwMDA3NjgzIDAwMDAwIG4gCjAwMDAwMDU3NTMgMDAwMDAgbiAKMDAwMDAwNTU1MyAwMDAwMCBuIAowMDAwMDA1MTU5IDAwMDAwIG4gCjAwMDAwMDY4MDYgMDAwMDAgbiAKMDAwMDAwMDc4NiAwMDAwMCBuIAowMDAwMDAxMTIzIDAwMDAwIG4gCjAwMDAwMDEyODcgMDAwMDAgbiAKMDAwMDAwMTY2NyAwMDAwMCBuIAowMDAwMDAxOTcyIDAwMDAwIG4gCjAwMDAwMDIyNzYgMDAwMDAgbiAKMDAwMDAwMjU5OCAwMDAwMCBuIAowMDAwMDAyODA3IDAwMDAwIG4gCjAwMDAwMDMyMjEgMDAwMDAgbiAKMDAwMDAwMzQ1OCAwMDAwMCBuIAowMDAwMDAzNjAyIDAwMDAwIG4gCjAwMDAwMDM3MjEgMDAwMDAgbiAKMDAwMDAwMzk1NyAwMDAwMCBuIAowMDAwMDA0MjY5IDAwMDAwIG4gCjAwMDAwMDQ1MDIgMDAwMDAgbiAKMDAwMDAwNDU5MiAwMDAwMCBuIAowMDAwMDA0Nzk4IDAwMDAwIG4gCjAwMDAwMDQ5NDUgMDAwMDAgbiAKMDAwMDAwNzY2MyAwMDAwMCBuIAowMDAwMDA4MTE4IDAwMDAwIG4gCjAwMDAwMDgxOTggMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAzOSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNDAgPj4Kc3RhcnR4cmVmCjgzNTUKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:22.388054\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["horiz_img = vert_img + horiz_img\n", "show_center_recep_field(inp_img, horiz_img)"]}, {"cell_type": "markdown", "id": "62da8fad", "metadata": {"papermill": {"duration": 0.030338, "end_time": "2021-09-16T12:42:22.519399", "exception": false, "start_time": "2021-09-16T12:42:22.489061", "status": "completed"}, "tags": []}, "source": ["If we stack multiple horizontal and vertical convolutions, we need to take two aspects into account:\n", "\n", "1.\n", "The center should not be masked anymore for the following convolutions as the features at the pixel's position are already independent of its actual value.\n", "If it is hard to imagine why we can do this, just change the value below to `mask_center=True` and see what happens.\n", "2.\n", "The vertical convolution is not allowed to work on features from the horizontal convolution.\n", "In the feature map of the horizontal convolutions, a pixel contains information about all of the \"true\" pixels on the left.\n", "If we apply a vertical convolution which also uses features from the right, we effectively expand our receptive field to the true input which we want to prevent.\n", "Thus, the feature maps can only be merged for the horizontal convolution.\n", "\n", "Using this, we can stack the convolutions in the following way. We have\n", "two feature streams: one for the vertical stack, and one for the\n", "horizontal stack. The horizontal convolutions can operate on the joint\n", "features of the previous horizontals and vertical convolutions, while\n", "the vertical stack only takes its own previous features as input. For a\n", "quick implementation, we can therefore sum the horizontal and vertical\n", "output features at each layer, and use those as final output features to\n", "calculate the loss on. An implementation of 4 consecutive layers is\n", "shown below. Note that we reuse the features from the other convolutions\n", "with `mask_center=True` from above."]}, {"cell_type": "code", "execution_count": 12, "id": "f6dcdf70", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:22.585305Z", "iopub.status.busy": "2021-09-16T12:42:22.584833Z", "iopub.status.idle": "2021-09-16T12:42:23.217060Z", "shell.execute_reply": "2021-09-16T12:42:23.216574Z"}, "papermill": {"duration": 0.666981, "end_time": "2021-09-16T12:42:23.217176", "exception": false, "start_time": "2021-09-16T12:42:22.550195", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Layer 2\n"]}, {"data": {"application/pdf": "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NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgNiAo/ec3yLdllI53ZmlwNUVsACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM3IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJztzjESggAMAEFE9P9PtiUFDtcAxV6bSbLL56ktdwMOI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZL0g20bv0bpvjuYaGRkZGRkZGdlB8+FrtOwbzJWMjIyMjIyM7FTbn777zp8kIyMjIyMju1x2cWQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9st5zZT9MVLaSCmVuZHN0cmVhbQplbmRvYmoKMzcgMCBvYmoKMTk1CmVuZG9iagoxNCAwIG9iago8PCAvQml0c1BlckNvbXBvbmVudCA4IC9Db2xvclNwYWNlIFsvSW5kZXhlZCAvRGV2aWNlUkdCIDIgKP3nNwAiTf8AACldCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDEgL0NvbHVtbnMgMTUzIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAxNTMgL0xlbmd0aCAzOCAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAxNTMgPj4Kc3RyZWFtCnic7c7BCYBAEMDA0/6L9utHMCDnPiYFhFnH1NbfgMfIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5ZL8jWF5GRkZGRkZGRkZGRkZGRkZENk5333i/JyMjIyMjItss2R9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHpkPbIeWY+sR9Yj682VXcPNUyMKZW5kc3RyZWFtCmVuZG9iagozOCAwIG9iagoxNzUKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjM5IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQyMjIrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDAKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDgxNTggMDAwMDAgbiAKMDAwMDAwNzAxMiAwMDAwMCBuIAowMDAwMDA3MDQ0IDAwMDAwIG4gCjAwMDAwMDcxNDMgMDAwMDAgbiAKMDAwMDAwNzE2NCAwMDAwMCBuIAowMDAwMDA3MTg1IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwMSAwMDAwMCBuIAowMDAwMDAwNzY2IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDc0NiAwMDAwMCBuIAowMDAwMDA3MjI4IDAwMDAwIG4gCjAwMDAwMDc3MDkgMDAwMDAgbiAKMDAwMDAwNTc1MyAwMDAwMCBuIAowMDAwMDA1NTUzIDAwMDAwIG4gCjAwMDAwMDUxNTkgMDAwMDAgbiAKMDAwMDAwNjgwNiAwMDAwMCBuIAowMDAwMDAwNzg2IDAwMDAwIG4gCjAwMDAwMDExMjMgMDAwMDAgbiAKMDAwMDAwMTI4NyAwMDAwMCBuIAowMDAwMDAxNjY3IDAwMDAwIG4gCjAwMDAwMDE5NzIgMDAwMDAgbiAKMDAwMDAwMjI3NiAwMDAwMCBuIAowMDAwMDAyNTk4IDAwMDAwIG4gCjAwMDAwMDI4MDcgMDAwMDAgbiAKMDAwMDAwMzIyMSAwMDAwMCBuIAowMDAwMDAzNDU4IDAwMDAwIG4gCjAwMDAwMDM2MDIgMDAwMDAgbiAKMDAwMDAwMzcyMSAwMDAwMCBuIAowMDAwMDAzOTU3IDAwMDAwIG4gCjAwMDAwMDQyNjkgMDAwMDAgbiAKMDAwMDAwNDUwMiAwMDAwMCBuIAowMDAwMDA0NTkyIDAwMDAwIG4gCjAwMDAwMDQ3OTggMDAwMDAgbiAKMDAwMDAwNDk0NSAwMDAwMCBuIAowMDAwMDA3Njg5IDAwMDAwIG4gCjAwMDAwMDgxMzggMDAwMDAgbiAKMDAwMDAwODIxOCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM5IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0MCA+PgpzdGFydHhyZWYKODM3NQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:22.673112\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 3\n"]}, {"data": {"application/pdf": "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NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMTIgKP3nN+PNUsa1Z4KAeHZ2dl9jblJZbDdGbCc9bQszcAArZAAiTf8AACldCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDEgL0NvbHVtbnMgMTUzIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAxNTMgL0xlbmd0aCAzNyAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAxNTMgPj4Kc3RyZWFtCnic7dY7bsNAEAVB6mvJ9v3Pq5QTkHADgiXD1elgHyrc5etdW14N2IysR9Yj65H1yHpkPbIeWY+sR9Yj65H1yHq7ss/RbXQdXdbN03w2J8nIyMjIyMj+tWyOnkeH0bLuOBroy31ERkZGRkZGRrYhO253Gn2MfuEXREZGRkZGRvZXZHNmfmDm7Xvd3uTPIyMjIyMjI3uS7KWR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch67yt7ALevlsIKZW5kc3RyZWFtCmVuZG9iagozNyAwIG9iagoyMjMKZW5kb2JqCjE0IDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM4IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJztzsEJgDAAwMDq/kP79aMYUFrkMkC4sa3amA24jKxH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIerey8XVkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGS/lu3n7pbPIyMjIyMjI3tJNjWyHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHllvXdkBi+JKDwplbmRzdHJlYW0KZW5kb2JqCjM4IDAgb2JqCjE3MgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMzkgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDIyMiswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA0MAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwODIwMiAwMDAwMCBuIAowMDAwMDA3MDEyIDAwMDAwIG4gCjAwMDAwMDcwNDQgMDAwMDAgbiAKMDAwMDAwNzE0MyAwMDAwMCBuIAowMDAwMDA3MTY0IDAwMDAwIG4gCjAwMDAwMDcxODUgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDAxIDAwMDAwIG4gCjAwMDAwMDA3NjYgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNzQ2IDAwMDAwIG4gCjAwMDAwMDcyMjggMDAwMDAgbiAKMDAwMDAwNzc1NiAwMDAwMCBuIAowMDAwMDA1NzUzIDAwMDAwIG4gCjAwMDAwMDU1NTMgMDAwMDAgbiAKMDAwMDAwNTE1OSAwMDAwMCBuIAowMDAwMDA2ODA2IDAwMDAwIG4gCjAwMDAwMDA3ODYgMDAwMDAgbiAKMDAwMDAwMTEyMyAwMDAwMCBuIAowMDAwMDAxMjg3IDAwMDAwIG4gCjAwMDAwMDE2NjcgMDAwMDAgbiAKMDAwMDAwMTk3MiAwMDAwMCBuIAowMDAwMDAyMjc2IDAwMDAwIG4gCjAwMDAwMDI1OTggMDAwMDAgbiAKMDAwMDAwMjgwNyAwMDAwMCBuIAowMDAwMDAzMjIxIDAwMDAwIG4gCjAwMDAwMDM0NTggMDAwMDAgbiAKMDAwMDAwMzYwMiAwMDAwMCBuIAowMDAwMDAzNzIxIDAwMDAwIG4gCjAwMDAwMDM5NTcgMDAwMDAgbiAKMDAwMDAwNDI2OSAwMDAwMCBuIAowMDAwMDA0NTAyIDAwMDAwIG4gCjAwMDAwMDQ1OTIgMDAwMDAgbiAKMDAwMDAwNDc5OCAwMDAwMCBuIAowMDAwMDA0OTQ1IDAwMDAwIG4gCjAwMDAwMDc3MzYgMDAwMDAgbiAKMDAwMDAwODE4MiAwMDAwMCBuIAowMDAwMDA4MjYyIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzkgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQwID4+CnN0YXJ0eHJlZgo4NDE5CiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:22.838845\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 4\n"]}, {"data": {"application/pdf": "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NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMjQgKP3nN+vUS7+waqacc5uTdpiRdn18eHh4dmxtcmBkblZcXG1GUGs3RmwuQWwqP20lPW0hO24RNW8ALWkALGYAKmAAXChbACNQACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM3IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJzt2ElOAzEARNEmTGGGQGjuf1K2LqREKhSJpvP+1tPzzvI0L7XprwEHI+sj6yPrI+sj6yPr+yH7jD6it+gpehzLoVyWW+ZxZGRkZGRkZKuX5cI8YhtdRpuxq+gueo32ERkZGRkZGdnqZbvoIbqOLqJpLJibvNBLREZGRkZGRnZmsmOvoHzO3ES3Y/fRc/QenejHhYyMjIyMjOy/yHLq/kg582tsPklkZGRkZGRkv5YtKLI+sj6yPrI+sj6yPrI+sj6yPrI+sj6yPrI+sj6yPrI+sj6yPrI+sj6yPrI+sj6yPrI+sj6yPrI+sj6yPrI+sj6yPrK+5cq+AdxOGLwKZW5kc3RyZWFtCmVuZG9iagozNyAwIG9iagoyNTQKZW5kb2JqCjE0IDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM4IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJztzsEJgDAAALHW/Yf2K4LgidA+kgky5q7G6sAjs86sM+vMOrPOrLvNxkpmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmavHVfzF2ZmZmZmZmafZxsx68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrDPrzLp9ZydD3j3rCmVuZHN0cmVhbQplbmRvYmoKMzggMCBvYmoKMTU5CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagozOSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MjIzKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDQwCjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDA4MjU4IDAwMDAwIG4gCjAwMDAwMDcwMTIgMDAwMDAgbiAKMDAwMDAwNzA0NCAwMDAwMCBuIAowMDAwMDA3MTQzIDAwMDAwIG4gCjAwMDAwMDcxNjQgMDAwMDAgbiAKMDAwMDAwNzE4NSAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDEgMDAwMDAgbiAKMDAwMDAwMDc2NiAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA3NDYgMDAwMDAgbiAKMDAwMDAwNzIyOCAwMDAwMCBuIAowMDAwMDA3ODI1IDAwMDAwIG4gCjAwMDAwMDU3NTMgMDAwMDAgbiAKMDAwMDAwNTU1MyAwMDAwMCBuIAowMDAwMDA1MTU5IDAwMDAwIG4gCjAwMDAwMDY4MDYgMDAwMDAgbiAKMDAwMDAwMDc4NiAwMDAwMCBuIAowMDAwMDAxMTIzIDAwMDAwIG4gCjAwMDAwMDEyODcgMDAwMDAgbiAKMDAwMDAwMTY2NyAwMDAwMCBuIAowMDAwMDAxOTcyIDAwMDAwIG4gCjAwMDAwMDIyNzYgMDAwMDAgbiAKMDAwMDAwMjU5OCAwMDAwMCBuIAowMDAwMDAyODA3IDAwMDAwIG4gCjAwMDAwMDMyMjEgMDAwMDAgbiAKMDAwMDAwMzQ1OCAwMDAwMCBuIAowMDAwMDAzNjAyIDAwMDAwIG4gCjAwMDAwMDM3MjEgMDAwMDAgbiAKMDAwMDAwMzk1NyAwMDAwMCBuIAowMDAwMDA0MjY5IDAwMDAwIG4gCjAwMDAwMDQ1MDIgMDAwMDAgbiAKMDAwMDAwNDU5MiAwMDAwMCBuIAowMDAwMDA0Nzk4IDAwMDAwIG4gCjAwMDAwMDQ5NDUgMDAwMDAgbiAKMDAwMDAwNzgwNSAwMDAwMCBuIAowMDAwMDA4MjM4IDAwMDAwIG4gCjAwMDAwMDgzMTggMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAzOSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgNDAgPj4Kc3RhcnR4cmVmCjg0NzUKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:22.991369\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Layer 5\n"]}, {"data": {"application/pdf": "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NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMzcgKP3nN+zVStTBXtK/YM28YqWbc6CXdZSOd4iFeIKAeHt7d3Z2dmZpcFhdbVddbVRabFFYbExUbERPa0FNazdGbC5BbCs/bSQ8bhY2bxQ2bwszcAAvbwAuawAsZgArZAAqYgBcKFsAJlUAJFIAI1AAIk3/AAApXQovRGVjb2RlUGFybXMgPDwgL0NvbG9ycyAxIC9Db2x1bW5zIDE1MyAvUHJlZGljdG9yIDEwID4+Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9IZWlnaHQgMTUzIC9MZW5ndGggMzcgMCBSIC9TdWJ0eXBlIC9JbWFnZQovVHlwZSAvWE9iamVjdCAvV2lkdGggMTUzID4+CnN0cmVhbQp4nO3Zt27DMBgAYaU71ek9cYrz/q+YlTdekMEW7jaCoPRpI35Na/SNPtArekaPY9ziMT6SryNlSpYsWbJkyZLNQcbNN3SPztEJWoydokv0gFaIlGTJkiVLlizZLGSf6AldoEO0g6axPXSMbtAL+kLJkiVLlixZstnLlugI7aPdsQN0hu7QOxK3oGTJkiVLlizZtsi45MSF45FrxEHK1dgt4sdyxsJ7j/i7kyxZsmTJkiXbFhmXdPIg93juZ2z9LyVLlixZsmTJ/izboJL5kvmS+ZL5kvmS+ZL5kvmS+ZL5kvmS+ZL5kvmS+ZL5kvmS+ZL5kvmS+ZL5kvmS+ZL5kvmS+ZL5kvmS+ZL5kvmS+ZL5Nlf2C5hfl6YKZW5kc3RyZWFtCmVuZG9iagozNyAwIG9iagoyNzkKZW5kb2JqCjE0IDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM4IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJztzrEJwDAQwEA7+w+d1ikFb0hxN4DQWgAAAAAAAAAAAADw8Zz2CGfOnDlz5sxZOJvJXOCsc9Y565x1zjpnnbPOWeesc9Y565x1zjpnnbPOWeesc9Y565x1zjpnnbPOWeesc9Y565x1zjpnnbPOWeesc9b99+wFOVgutwplbmRzdHJlYW0KZW5kb2JqCjM4IDAgb2JqCjEyNAplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMzkgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDIyMyswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCA0MAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAwODI4NiAwMDAwMCBuIAowMDAwMDA3MDEyIDAwMDAwIG4gCjAwMDAwMDcwNDQgMDAwMDAgbiAKMDAwMDAwNzE0MyAwMDAwMCBuIAowMDAwMDA3MTY0IDAwMDAwIG4gCjAwMDAwMDcxODUgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwNDAxIDAwMDAwIG4gCjAwMDAwMDA3NjYgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDAwNzQ2IDAwMDAwIG4gCjAwMDAwMDcyMjggMDAwMDAgbiAKMDAwMDAwNzg4OCAwMDAwMCBuIAowMDAwMDA1NzUzIDAwMDAwIG4gCjAwMDAwMDU1NTMgMDAwMDAgbiAKMDAwMDAwNTE1OSAwMDAwMCBuIAowMDAwMDA2ODA2IDAwMDAwIG4gCjAwMDAwMDA3ODYgMDAwMDAgbiAKMDAwMDAwMTEyMyAwMDAwMCBuIAowMDAwMDAxMjg3IDAwMDAwIG4gCjAwMDAwMDE2NjcgMDAwMDAgbiAKMDAwMDAwMTk3MiAwMDAwMCBuIAowMDAwMDAyMjc2IDAwMDAwIG4gCjAwMDAwMDI1OTggMDAwMDAgbiAKMDAwMDAwMjgwNyAwMDAwMCBuIAowMDAwMDAzMjIxIDAwMDAwIG4gCjAwMDAwMDM0NTggMDAwMDAgbiAKMDAwMDAwMzYwMiAwMDAwMCBuIAowMDAwMDAzNzIxIDAwMDAwIG4gCjAwMDAwMDM5NTcgMDAwMDAgbiAKMDAwMDAwNDI2OSAwMDAwMCBuIAowMDAwMDA0NTAyIDAwMDAwIG4gCjAwMDAwMDQ1OTIgMDAwMDAgbiAKMDAwMDAwNDc5OCAwMDAwMCBuIAowMDAwMDA0OTQ1IDAwMDAwIG4gCjAwMDAwMDc4NjggMDAwMDAgbiAKMDAwMDAwODI2NiAwMDAwMCBuIAowMDAwMDA4MzQ2IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMzkgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQwID4+CnN0YXJ0eHJlZgo4NTAzCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:23.145226\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["# Initialize convolutions with equal weight to all input pixels\n", "horiz_conv = HorizontalStackConvolution(c_in=1, c_out=1, kernel_size=3, mask_center=False)\n", "horiz_conv.conv.weight.data.fill_(1)\n", "horiz_conv.conv.bias.data.fill_(0)\n", "vert_conv = VerticalStackConvolution(c_in=1, c_out=1, kernel_size=3, mask_center=False)\n", "vert_conv.conv.weight.data.fill_(1)\n", "vert_conv.conv.bias.data.fill_(0)\n", "\n", "# We reuse our convolutions for the 4 layers here. Note that in a standard network,\n", "# we don't do that, and instead learn 4 separate convolution. As this cell is only for\n", "# visualization purposes, we reuse the convolutions for all layers.\n", "for l_idx in range(4):\n", " vert_img = vert_conv(vert_img)\n", " horiz_img = horiz_conv(horiz_img) + vert_img\n", " print(\"Layer %i\" % (l_idx + 2))\n", " show_center_recep_field(inp_img, horiz_img)"]}, {"cell_type": "markdown", "id": "0fb070de", "metadata": {"papermill": {"duration": 0.035948, "end_time": "2021-09-16T12:42:23.289214", "exception": false, "start_time": "2021-09-16T12:42:23.253266", "status": "completed"}, "tags": []}, "source": ["The receptive field above it visualized for the horizontal stack, which includes the features of the vertical convolutions.\n", "It grows over layers without any blind spot as we had before.\n", "The difference between \"weighted\" and \"binary\" receptive field is that for the latter, we check whether there are any gradients flowing back to this pixel.\n", "This indicates that the center pixel indeed can use information from this pixel.\n", "Nevertheless, due to the convolution weights, some pixels have a stronger effect on the prediction than others.\n", "This is visualized in the weighted receptive field by plotting the gradient magnitude for each pixel instead of a binary yes/no.\n", "\n", "\n", "Another receptive field we can check is the one for the vertical stack\n", "as the one above is for the horizontal stack. Let's visualize it below:"]}, {"cell_type": "code", "execution_count": 13, "id": "cd0487f2", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:23.363786Z", "iopub.status.busy": "2021-09-16T12:42:23.363266Z", "iopub.status.idle": "2021-09-16T12:42:23.517870Z", "shell.execute_reply": "2021-09-16T12:42:23.517438Z"}, "papermill": {"duration": 0.193071, "end_time": "2021-09-16T12:42:23.517983", "exception": false, "start_time": "2021-09-16T12:42:23.324912", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "JVBERi0xLjQKJazcIKu6CjEgMCBvYmoKPDwgL1BhZ2VzIDIgMCBSIC9UeXBlIC9DYXRhbG9nID4+CmVuZG9iago4IDAgb2JqCjw8IC9FeHRHU3RhdGUgNCAwIFIgL0ZvbnQgMyAwIFIgL1BhdHRlcm4gNSAwIFIKL1Byb2NTZXQgWyAvUERGIC9UZXh0IC9JbWFnZUIgL0ltYWdlQyAvSW1hZ2VJIF0gL1NoYWRpbmcgNiAwIFIKL1hPYmplY3QgNyAwIFIgPj4KZW5kb2JqCjExIDAgb2JqCjw8IC9Bbm5vdHMgMTAgMCBSIC9Db250ZW50cyA5IDAgUgovR3JvdXAgPDwgL0NTIC9EZXZpY2VSR0IgL1MgL1RyYW5zcGFyZW5jeSAvVHlwZSAvR3JvdXAgPj4KL01lZGlhQm94IFsgMCAwIDM0OS4yIDE4MS43MDY4MTgxODE4IF0gL1BhcmVudCAyIDAgUiAvUmVzb3VyY2VzIDggMCBSCi9UeXBlIC9QYWdlID4+CmVuZG9iago5IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTIgMCBSID4+CnN0cmVhbQp4nJ1RS08CMRC+z6/4jnCwO9NStj1CUKI3zCYcjAezFFjCrvIIxn/vrILPi9jJZN6dr18FK8oGgsUOjJXqMwRjZKN0qMp0Ox6i3BFrvibXi8aqtz56EsTk3A8SNMXfwyVRQxvk2taqeGu0fpSf4TZhigbZwLYwRGGIwmCM9QbxDu361uYqZY3sWjB6xIQm2JyGGIuvg21MGxK1FzoLYcPOcez7yBHS98Z9ri9rGhbIrrTNopi/vbaY0R060y58MD663KOTqsVyn2bYdmHZBHc6WinT0746JMyrtJ51cY/ihi4LekcoIZqPZf+mg37TIZFPhNhzCbGsn+Vt6LESIn9nZFg1D9uX8zigVzdDhxgKZW5kc3RyZWFtCmVuZG9iagoxMiAwIG9iagoyNzAKZW5kb2JqCjEwIDAgb2JqClsgXQplbmRvYmoKMTkgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyNjQgPj4Kc3RyZWFtCnicPVK5kQMxDMu3CpbAX1I9vrlxYPefHsA9OzExSxEAQfduUTkbP20ly4/82GVnSeSW96Bt8rqi6gapmyBzyXHJFWKW8rgSJBYtZSbWZ6qD8nENahW+8BMzE9MhScQSclKPIqyPr4PX9RzcXzfp/BoZH3RsUHpLkqugVMO+crQSO5bqXYPsg6ab0uoz067sgKXxhqzUpE5/HfW/IyzCpN5IRQchFr/Tyx2yLKQc1Nu5fgPhXe2CWE+tPOwQad5WeksVzObMBEwZbTusgLVZY8JUCfAWzSHUWojMFzqtiPnk3NHBNFV5GiLDUoWD7T2jNVzoGhJLw/lJxgTmv/D6rMhtf/8AcGhnugplbmRzdHJlYW0KZW5kb2JqCjIwIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggOTIgPj4Kc3RyZWFtCnicPY3BDcAwCAP/TMEIEALE+1RVH+n+3yYR6gcfBtkYYGGzNeDB2cCX0to3vaRFk9oIVrVF3VCeuxSlWF1HpUzCT5k7f1J0HO1wDtvf1uU4TePoX/fQ/QEPSh4LCmVuZHN0cmVhbQplbmRvYmoKMjEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAzMDcgPj4Kc3RyZWFtCnicPZJLbgMxDEP3PoUuEMD62Z7zpCi6mN5/2ycl6Yoc2RZFapa6TFlTHpA0k4R/6fBwsZ3yO2zPZmbgWqKXieWU59AVYu6ifNnMRl1ZJ8XqhGY6t+hRORcHNk2qn6sspd0ueA7XJp5b9hE/vNCgHtQ1Lgk3dFejZSk0Y6r7f9J7/Iwy4GpMXWxSq3sfPF5EVejoB0eJImOXF+fjQQnpSsJoWoiVd0UDQe7ytMp7Ce7b3mrIsgepmM47KWaw63RSLm4XhyEeyPKo8OWj2GtCz/iwKyX0SNiGM3In7mjG5tTI4pD+3o0ES4+uaCHz4K9u1i5gvFM6RWJkTnKsaYtVTvdQFNO5w70MEPVsRUMpc5HV6l/DzgtrlmwWeEr6BR6j3SZLDlbZ26hO76082dD3H1rXdB8KZW5kc3RyZWFtCmVuZG9iagoyMiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDIzMiA+PgpzdHJlYW0KeJw1UUluxDAMu/sV/MAA1u68J8Wgh/b/11LKFAhAJba4JWJjIwIvMfg5iNz4kjWjJn5nclf8LE+FR8Kt4EkUgZfhXnaCyxvGZT8OMx+8l1bOpMaTDMhFNj08ETLYJRA6MLsGddhm2om+IeGzI1LNRpbT1xL00ioEylO23+mCEm2r+nP7rAtt+9oTTnZ76knlE4jnlqzAZeMVk8VYBj1RuUsxfZDqbKEnobwon4NsPmqIRJcoZ+CJwcEo0A7sue1n4lUhaF3dp21jqEZKx9O/DU1Nkgj5RAlntjTuFv5/z72+1/sPTiFUEQplbmRzdHJlYW0KZW5kb2JqCjIzIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMjMxID4+CnN0cmVhbQp4nDVPOZIEIQzLeYU+MFUY20C/p6e2Ntj5f7qSmU6Q8CHJ0xMdmXiZIyOwZsfbWmQgZuBTTMW/9rQPE6r34B4ilIsLYYaRcNas426ejhf/dpXPWAfvNviKWV4Q2MJM1lcWZy7bBWNpnMQ5yW6MXROxjXWtp1NYRzChDIR0tsOUIHNUpPTJjjLm6DiRJ56L7/bbLHY5fg7rCzaNIRXn+Cp6gjaDoux57wIackH/Xd34HkW76CUgGwkW1lFi7pzlhF+9dnQetSgSc0KaQS4TIc3pKqYQmlCss6OgUlFwqT6n6Kyff+VfXC0KZW5kc3RyZWFtCmVuZG9iagoyNCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDI0OSA+PgpzdHJlYW0KeJw9UDuORCEM6zmFL/Ak8iNwHkarLWbv364DmilQTH62MyTQEYFHDDGUr+MlraCugb+LQvFu4uuDwiCrQ1IgznoPiHTspjaREzodnDM/YTdjjsBFMQac6XSmPQcmOfvCCoRzG2XsVkgniaoijuozjimeKnufeBYs7cg2WyeSPeQg4VJSicmln5TKP23KlAo6ZtEELBK54GQTTTjLu0lSjBmUMuoepnYifaw8yKM66GRNzqwjmdnTT9uZ+Bxwt1/aZE6Vx3QezPictM6DORW69+OJNgdNjdro7PcTaSovUrsdWp1+dRKV3RjnGBKXZ38Z32T/+Qf+h1oiCmVuZHN0cmVhbQplbmRvYmoKMjUgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMzYgPj4Kc3RyZWFtCnicTY9BDgMxCAPveYWfQCBAeM9WVQ/b/19L2HbTCx7JgGxRBoElh3iHG+HR2w/fRTYVZ+OcX1IpYiGYT3CfMFMcjSl38mOPgHGUaiynaHheS85NwxctdxMtpa2XkxlvuO6X90eVbZENRc8tC0LXbJL5MoEHfBiYR3XjaaXH3fZsr/b8AM5sNEkKZW5kc3RyZWFtCmVuZG9iagoyNiAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDM0MSA+PgpzdHJlYW0KeJxFUktuRDEI279TcIFI4ZeQ87Squpjef1ubTNXN4AlgbHjLU6ZkyrC5JSMk15RPfSJDrKb8NHIkIqb4SQkFdpWPx2tLrI3skagUn9rx47H0RqbZFVr17tGlzaJRzcrIOcgQoZ4VurJ71A7Z8HpcSLrvlM0hHMv/UIEsZd1yCiVBW9B37BHfDx2ugiuCYbBrLoPtZTLU//qHFlzvffdixy6AFqznvsEOAKinE7QFyBna7jYpaABVuotJwqPyem52omyjVen5HAAzDjBywIglWx2+0d4Aln1d6EWNiv0rQFFZQPzI1XbB3jHJSHAW5gaOvXA8xZlwSzjGAkCKveIYevAl2OYvV66ImvAJdbpkL7zCntrm50KTCHetAA5eZMOtq6Oolu3pPIL2Z0VyRozUizg6IZJa0jmC4tKgHlrjXDex4m0jsblX3+4f4ZwvXPbrF0vshMQKZW5kc3RyZWFtCmVuZG9iagoyNyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE2NCA+PgpzdHJlYW0KeJxFkMdxBTEMQ++qAiUwgAr1rMfzD+v+r4b000F6GEIMYk/CsFxXcWF0w4+3LTMNf0cZ7sb6MmO81VggJ+gDDJGJq9Gk+nbFGar05NVirqOiXC86IhLMkuOrQCN8OrLHk7a2M/10Xh/sIe8T/yoq525hAS6q7kD5Uh/x1I/ZUeqaoY8qK2seatpXhF0RSts+LqcyTt29A1rhvZWrPdrvPx52OvIKZW5kc3RyZWFtCmVuZG9iagoyOCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDcyID4+CnN0cmVhbQp4nDMyt1AwULA0ARKGFiYK5mYGCimGXEC+qYm5Qi4XSAzEygGzDIC0JZyCiGeAmCBtEMUgFkSxmYkZRB2cAZHL4EoDACXbFskKZW5kc3RyZWFtCmVuZG9iagoyOSAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDQ3ID4+CnN0cmVhbQp4nDMyt1AwULA0ARKGFiYK5mYGCimGXJYQVi4XTCwHzALRlnAKIp7BlQYAuWcNJwplbmRzdHJlYW0KZW5kb2JqCjMwIDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTYzID4+CnN0cmVhbQp4nEWQOxIDIQxDe06hI/gjAz7PZjIpNvdvY9hsUsDTWCCDuxOC1NqCieiCh7Yl3QXvrQRnY/zpNm41EuQEdYBWpONolFJ9ucVplXTxaDZzKwutEx1mDnqUoxmgEDoV3u2i5HKm7s75Q3D1X/W/Yt05m4mBycodCM3qU9z5NjuiurrJ/qTH3KzXfivsVWFpWUvLCbedu2ZACdxTOdqrPT8fCjr2CmVuZHN0cmVhbQplbmRvYmoKMzEgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAyMzkgPj4Kc3RyZWFtCnicTVDJbQQxDPu7CjUwwOgcux4Hizyy/X9DygmSl2hL4qHylFuWymX3IzlvybrlQ4dOlWnybtDNr7H+owwCdv9QVBCtJbFKzFzSbrE0SS/ZwziNl2u1juepe4RZo3jw49jTKYHpPTLBZrO9OTCrPc4OkE64xq/q0zuVJAOJupDzQqUK6x7UJaKPK9uYUp1OLeUYl5/oe3yOAD3F3o3c0cfLF4xGtS2o0WqVOA8wE1PRlXGrkYGUEwZDZ0dXNAulyMp6QjXCjTmhmb3DcGADy7OEpKWtUrwPZQHoAl3aOuM0SoKOAMLfKIz1+gaq/F43CmVuZHN0cmVhbQplbmRvYmoKMzIgMCBvYmoKPDwgL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxNjAgPj4Kc3RyZWFtCnicRZA5EgMxCARzvYInSFyC96zLtcH6/6kH1kei6QI0HLoWTcp6FGg+6bFGobrQa+gsSpJEwRaSHVCnY4g7KEhMSGOSSLYegyOaWLNdmJlUKrNS4bRpxcK/2VrVyESNcI38iekGVPxP6lyU8E2Dr5Ix+hhUvDuDjEn4XkXcWjHt/kQwsRn2CW9FJgWEibGp2b7PYIbM9wrXOMfzDUyCN+sKZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDE4ID4+CnN0cmVhbQp4nDM2tFAwgMMUQ640AB3mA1IKZW5kc3RyZWFtCmVuZG9iagozNCAwIG9iago8PCAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEzMyA+PgpzdHJlYW0KeJxFj0sOBCEIRPecoo7Axx/ncTLphXP/7YCdbhNjPYVUgbmCoT0uawOdFR8hGbbxt6mWjkVZPlR6UlYPyeCHrMbLIdygLPCCSSqGIVCLmBqRLWVut4DbNg2yspVTpY6wi6Mwj/a0bBUeX6JbInWSP4PEKi/c47odyKXWu96ii75/pAExCQplbmRzdHJlYW0KZW5kb2JqCjM1IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggNzUgPj4Kc3RyZWFtCnicM7U0UjBQMDYAEqZmRgqmJuYKKYZcQD6IlctlaGQKZuVwGVmaKVhYABkmZuZQIZiGHC5jU3OgAUBFxqZgGqo/hyuDKw0AlZAS7wplbmRzdHJlYW0KZW5kb2JqCjM2IDAgb2JqCjw8IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlIC9MZW5ndGggMTQxID4+CnN0cmVhbQp4nD2PwQ7DMAhD7/kK/0Ck2CmhfE+naofu/68jS7sLegJjjIXQ0BuqmsOGYJvjxdIlVGv4FMVAJTfImWAOpaTSHUeRemI4GFwetBuO4rHo+hG7kmZ90MZCuiVogHusU2ncpnETxB01Beop6pyjvBC5n6ln2DSS3TSzknO4Db97z1PX/6ervMv5Bb13Lv4KZW5kc3RyZWFtCmVuZG9iagoxNyAwIG9iago8PCAvQmFzZUZvbnQgL0RlamFWdVNhbnMgL0NoYXJQcm9jcyAxOCAwIFIKL0VuY29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA2NiAvQiA4NyAvVyA5NyAvYSA5OSAvYyAvZCAvZSAvZiAvZyAvaCAvaSAxMDggL2wgMTEwIC9uIDExMiAvcAoxMTQgL3IgMTE2IC90IDExOCAvdiAxMjEgL3kgXQovVHlwZSAvRW5jb2RpbmcgPj4KL0ZpcnN0Q2hhciAwIC9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMTYgKP3nN9/KVqWbc5GMd2Rnb05WbDREbCo/bSI7bggzcAAtaQAqYgAmVQAlVAAkUgAiTf8AACldCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDEgL0NvbHVtbnMgMTUzIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAxNTMgL0xlbmd0aCAzNyAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAxNTMgPj4Kc3RyZWFtCnic7dk5agRBEEXB0b4v9z+t3P7mExIMTTy3qMwwC+ryNX1Mb9PL9Dw9HdujvbYjd91SLmRkZGRkZGRnkH1Or9PjdD/dTbfH9miv7chdtxQyMjIyMjKyU8jep32yPEy78Ga6HNujvbYjd91SyMjIyMjIyE4hu95XEBkZGRkZGdkZZNf7u0NGRkZGRkZ2Ptlv+z72NyPJemQ9sh5Zj6xH1vsj2T9E1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrXa/sB+8QQIcKZW5kc3RyZWFtCmVuZG9iagozNyAwIG9iagoyMzUKZW5kb2JqCjE0IDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM4IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJztzsEJwDAQwLC0+w/db76GC5QgDWC8FgAAAAAAAAAAAADXeEa8u5mks85Z56xz1jnrnHVDZwc465x1zjpnnbPOWeesc9Y565x1zjpnnbPOWeesc9Y565x1zjpnnbPOWeesc9Y565x1zjpnnbPOWeesc9Y565x1/z37AGw/MkUKZW5kc3RyZWFtCmVuZG9iagozOCAwIG9iagoxMjgKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjM5IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQyMjMrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDAKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDgxODIgMDAwMDAgbiAKMDAwMDAwNzAxMiAwMDAwMCBuIAowMDAwMDA3MDQ0IDAwMDAwIG4gCjAwMDAwMDcxNDMgMDAwMDAgbiAKMDAwMDAwNzE2NCAwMDAwMCBuIAowMDAwMDA3MTg1IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwMSAwMDAwMCBuIAowMDAwMDAwNzY2IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDc0NiAwMDAwMCBuIAowMDAwMDA3MjI4IDAwMDAwIG4gCjAwMDAwMDc3ODAgMDAwMDAgbiAKMDAwMDAwNTc1MyAwMDAwMCBuIAowMDAwMDA1NTUzIDAwMDAwIG4gCjAwMDAwMDUxNTkgMDAwMDAgbiAKMDAwMDAwNjgwNiAwMDAwMCBuIAowMDAwMDAwNzg2IDAwMDAwIG4gCjAwMDAwMDExMjMgMDAwMDAgbiAKMDAwMDAwMTI4NyAwMDAwMCBuIAowMDAwMDAxNjY3IDAwMDAwIG4gCjAwMDAwMDE5NzIgMDAwMDAgbiAKMDAwMDAwMjI3NiAwMDAwMCBuIAowMDAwMDAyNTk4IDAwMDAwIG4gCjAwMDAwMDI4MDcgMDAwMDAgbiAKMDAwMDAwMzIyMSAwMDAwMCBuIAowMDAwMDAzNDU4IDAwMDAwIG4gCjAwMDAwMDM2MDIgMDAwMDAgbiAKMDAwMDAwMzcyMSAwMDAwMCBuIAowMDAwMDAzOTU3IDAwMDAwIG4gCjAwMDAwMDQyNjkgMDAwMDAgbiAKMDAwMDAwNDUwMiAwMDAwMCBuIAowMDAwMDA0NTkyIDAwMDAwIG4gCjAwMDAwMDQ3OTggMDAwMDAgbiAKMDAwMDAwNDk0NSAwMDAwMCBuIAowMDAwMDA3NzYwIDAwMDAwIG4gCjAwMDAwMDgxNjIgMDAwMDAgbiAKMDAwMDAwODI0MiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM5IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0MCA+PgpzdGFydHhyZWYKODM5OQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:23.446589\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["show_center_recep_field(inp_img, vert_img)"]}, {"cell_type": "markdown", "id": "bbe7c460", "metadata": {"papermill": {"duration": 0.036988, "end_time": "2021-09-16T12:42:23.591980", "exception": false, "start_time": "2021-09-16T12:42:23.554992", "status": "completed"}, "tags": []}, "source": ["As we have discussed before, the vertical stack only looks at pixels above the one we want to predict.\n", "Hence, we can validate that our implementation works as we initially expected it to.\n", "As a final step, let's clean up the computation graph we still had kept\n", "in memory for the visualization of the receptive field:"]}, {"cell_type": "code", "execution_count": 14, "id": "e76aa938", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:23.668813Z", "iopub.status.busy": "2021-09-16T12:42:23.668347Z", "iopub.status.idle": "2021-09-16T12:42:23.670487Z", "shell.execute_reply": "2021-09-16T12:42:23.670021Z"}, "papermill": {"duration": 0.041448, "end_time": "2021-09-16T12:42:23.670586", "exception": false, "start_time": "2021-09-16T12:42:23.629138", "status": "completed"}, "tags": []}, "outputs": [], "source": ["del inp_img, horiz_conv, vert_conv"]}, {"cell_type": "markdown", "id": "193e3fb4", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.037077, "end_time": "2021-09-16T12:42:23.744338", "exception": false, "start_time": "2021-09-16T12:42:23.707261", "status": "completed"}, "tags": []}, "source": ["## Gated PixelCNN\n", "\n", "
\n", "\n", "In the next step, we will use the masked convolutions to build a full autoregressive model, called Gated PixelCNN.\n", "The difference between the original PixelCNN and Gated PixelCNN is the use of separate horizontal and vertical stacks.\n", "However, in literature, you often see that people refer to the Gated PixelCNN simply as \"PixelCNN\".\n", "Hence, in the following, if we say \"PixelCNN\", we usually mean the gated version.\n", "What \"Gated\" refers to in the model name is explained next.\n", "\n", "### Gated Convolutions\n", "\n", "For visualizing the receptive field, we assumed a very simplified stack of vertical and horizontal convolutions.\n", "Obviously, there are more sophisticated ways of doing it, and PixelCNN uses gated convolutions for this.\n", "Specifically, the Gated Convolution block in PixelCNN looks as follows\n", "(figure credit - [Aaron van den Oord et al. ](https://arxiv.org/pdf/1606.05328.pdf)):\n", "\n", "
\n", "\n", "The left path is the vertical stack (the $N\\times N$ convolution is masked correspondingly),\n", "and the right path is the horizontal stack.\n", "Gated convolutions are implemented by having a twice as large output channel size,\n", "and combine them by a element-wise multiplication of $\\tanh$ and a sigmoid.\n", "For a linear layer, we can express a gated activation unit as follows:\n", "\n", "$$\\mathbf{y} = \\tanh\\left(\\mathbf{W}_{f}\\mathbf{x}\\right)\\odot\\sigma\\left(\\mathbf{W}_{g}\\mathbf{x}\\right)$$\n", "\n", "For simplicity, biases have been neglected and the linear layer split into two part, $\\mathbf{W}_{f}$ and $\\mathbf{W}_{g}$.\n", "This concept resembles the input and modulation gate in an LSTM, and has been used in many other architectures as well.\n", "The main motivation behind this gated activation is that it might allow to model more complex interactions and simplifies learning.\n", "But as in any other architecture, this is mostly a design choice and can be considered a hyperparameters.\n", "\n", "Besides the gated convolutions, we also see that the horizontal stack uses a residual connection while the vertical stack does not.\n", "This is because we use the output of the horizontal stack for prediction.\n", "Each convolution in the vertical stack also receives a strong gradient signal\n", "as it is only two $1\\times 1$ convolutions away from the residual connection,\n", "and does not require another residual connection to all its earleri layers.\n", "\n", "The implementation in PyTorch is fairly straight forward for this block,\n", "because the visualization above gives us a computation graph to follow:"]}, {"cell_type": "code", "execution_count": 15, "id": "cc9d393c", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:23.919095Z", "iopub.status.busy": "2021-09-16T12:42:23.918572Z", "iopub.status.idle": "2021-09-16T12:42:23.920520Z", "shell.execute_reply": "2021-09-16T12:42:23.920095Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.138888, "end_time": "2021-09-16T12:42:23.920638", "exception": false, "start_time": "2021-09-16T12:42:23.781750", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GatedMaskedConv(nn.Module):\n", " def __init__(self, c_in, **kwargs):\n", " \"\"\"Gated Convolution block implemented the computation graph shown above.\"\"\"\n", " super().__init__()\n", " self.conv_vert = VerticalStackConvolution(c_in, c_out=2 * c_in, **kwargs)\n", " self.conv_horiz = HorizontalStackConvolution(c_in, c_out=2 * c_in, **kwargs)\n", " self.conv_vert_to_horiz = nn.Conv2d(2 * c_in, 2 * c_in, kernel_size=1, padding=0)\n", " self.conv_horiz_1x1 = nn.Conv2d(c_in, c_in, kernel_size=1, padding=0)\n", "\n", " def forward(self, v_stack, h_stack):\n", " # Vertical stack (left)\n", " v_stack_feat = self.conv_vert(v_stack)\n", " v_val, v_gate = v_stack_feat.chunk(2, dim=1)\n", " v_stack_out = torch.tanh(v_val) * torch.sigmoid(v_gate)\n", "\n", " # Horizontal stack (right)\n", " h_stack_feat = self.conv_horiz(h_stack)\n", " h_stack_feat = h_stack_feat + self.conv_vert_to_horiz(v_stack_feat)\n", " h_val, h_gate = h_stack_feat.chunk(2, dim=1)\n", " h_stack_feat = torch.tanh(h_val) * torch.sigmoid(h_gate)\n", " h_stack_out = self.conv_horiz_1x1(h_stack_feat)\n", " h_stack_out = h_stack_out + h_stack\n", "\n", " return v_stack_out, h_stack_out"]}, {"cell_type": "markdown", "id": "2b8ce7cc", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.037081, "end_time": "2021-09-16T12:42:23.995828", "exception": false, "start_time": "2021-09-16T12:42:23.958747", "status": "completed"}, "tags": []}, "source": ["### Building the model\n", "\n", "Using the gated convolutions, we can now build our PixelCNN model.\n", "The architecture consists of multiple stacked GatedMaskedConv blocks, where we add an additional dilation factor to a few convolutions.\n", "This is used to increase the receptive field of the model and allows to take a larger context into accout during generation.\n", "As a reminder, dilation on a convolution works looks as follows\n", "(figure credit - [Vincent Dumoulin and Francesco Visin](https://arxiv.org/pdf/1603.07285.pdf)):\n", "\n", "
\n", "\n", "Note that the smaller output size is only because the animation assumes no padding.\n", "In our implementation, we will pad the input image correspondingly.\n", "Alternatively to dilated convolutions, we could downsample the input and use a encoder-decoder architecture as in PixelCNN++ [3].\n", "This is especially beneficial if we want to build a very deep autoregressive model.\n", "Nonetheless, as we seek to train a reasonably small model, dilated convolutions are the more efficient option to use here.\n", "\n", "Below, we implement the PixelCNN model as a PyTorch Lightning module.\n", "Besides the stack of gated convolutions, we also have the initial\n", "horizontal and vertical convolutions which mask the center pixel, and a\n", "final $1\\times 1$ convolution which maps the output features to class\n", "predictions. To determine the likelihood of a batch of images, we first\n", "create our initial features using the masked horizontal and vertical\n", "input convolution. Next, we forward the features through the stack of\n", "gated convolutions. Finally, we take the output features of the\n", "horizontal stack, and apply the $1\\times 1$ convolution for\n", "classification. We use the bits per dimension metric for the likelihood,\n", "similarly to Tutorial 11 and assignment 3."]}, {"cell_type": "code", "execution_count": 16, "id": "16fca558", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:24.082322Z", "iopub.status.busy": "2021-09-16T12:42:24.081826Z", "iopub.status.idle": "2021-09-16T12:42:24.084319Z", "shell.execute_reply": "2021-09-16T12:42:24.083832Z"}, "papermill": {"duration": 0.051889, "end_time": "2021-09-16T12:42:24.084419", "exception": false, "start_time": "2021-09-16T12:42:24.032530", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class PixelCNN(pl.LightningModule):\n", " def __init__(self, c_in, c_hidden):\n", " super().__init__()\n", " self.save_hyperparameters()\n", "\n", " # Initial convolutions skipping the center pixel\n", " self.conv_vstack = VerticalStackConvolution(c_in, c_hidden, mask_center=True)\n", " self.conv_hstack = HorizontalStackConvolution(c_in, c_hidden, mask_center=True)\n", " # Convolution block of PixelCNN. We use dilation instead of downscaling\n", " self.conv_layers = nn.ModuleList(\n", " [\n", " GatedMaskedConv(c_hidden),\n", " GatedMaskedConv(c_hidden, dilation=2),\n", " GatedMaskedConv(c_hidden),\n", " GatedMaskedConv(c_hidden, dilation=4),\n", " GatedMaskedConv(c_hidden),\n", " GatedMaskedConv(c_hidden, dilation=2),\n", " GatedMaskedConv(c_hidden),\n", " ]\n", " )\n", " # Output classification convolution (1x1)\n", " self.conv_out = nn.Conv2d(c_hidden, c_in * 256, kernel_size=1, padding=0)\n", "\n", " self.example_input_array = train_set[0][0][None]\n", "\n", " def forward(self, x):\n", " \"\"\"Forward image through model and return logits for each pixel.\n", "\n", " Args:\n", " x: Image tensor with integer values between 0 and 255.\n", " \"\"\"\n", " # Scale input from 0 to 255 back to -1 to 1\n", " x = (x.float() / 255.0) * 2 - 1\n", "\n", " # Initial convolutions\n", " v_stack = self.conv_vstack(x)\n", " h_stack = self.conv_hstack(x)\n", " # Gated Convolutions\n", " for layer in self.conv_layers:\n", " v_stack, h_stack = layer(v_stack, h_stack)\n", " # 1x1 classification convolution\n", " # Apply ELU before 1x1 convolution for non-linearity on residual connection\n", " out = self.conv_out(F.elu(h_stack))\n", "\n", " # Output dimensions: [Batch, Classes, Channels, Height, Width]\n", " out = out.reshape(out.shape[0], 256, out.shape[1] // 256, out.shape[2], out.shape[3])\n", " return out\n", "\n", " def calc_likelihood(self, x):\n", " # Forward pass with bpd likelihood calculation\n", " pred = self.forward(x)\n", " nll = F.cross_entropy(pred, x, reduction=\"none\")\n", " bpd = nll.mean(dim=[1, 2, 3]) * np.log2(np.exp(1))\n", " return bpd.mean()\n", "\n", " @torch.no_grad()\n", " def sample(self, img_shape, img=None):\n", " \"\"\"Sampling function for the autoregressive model.\n", "\n", " Args:\n", " img_shape: Shape of the image to generate (B,C,H,W)\n", " img (optional): If given, this tensor will be used as\n", " a starting image. The pixels to fill\n", " should be -1 in the input tensor.\n", " \"\"\"\n", " # Create empty image\n", " if img is None:\n", " img = torch.zeros(img_shape, dtype=torch.long).to(device) - 1\n", " # Generation loop\n", " for h in tqdm(range(img_shape[2]), leave=False):\n", " for w in range(img_shape[3]):\n", " for c in range(img_shape[1]):\n", " # Skip if not to be filled (-1)\n", " if (img[:, c, h, w] != -1).all().item():\n", " continue\n", " # For efficiency, we only have to input the upper part of the image\n", " # as all other parts will be skipped by the masked convolutions anyways\n", " pred = self.forward(img[:, :, : h + 1, :])\n", " probs = F.softmax(pred[:, :, c, h, w], dim=-1)\n", " img[:, c, h, w] = torch.multinomial(probs, num_samples=1).squeeze(dim=-1)\n", " return img\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.Adam(self.parameters(), lr=1e-3)\n", " scheduler = optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.99)\n", " return [optimizer], [scheduler]\n", "\n", " def training_step(self, batch, batch_idx):\n", " loss = self.calc_likelihood(batch[0])\n", " self.log(\"train_bpd\", loss)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " loss = self.calc_likelihood(batch[0])\n", " self.log(\"val_bpd\", loss)\n", "\n", " def test_step(self, batch, batch_idx):\n", " loss = self.calc_likelihood(batch[0])\n", " self.log(\"test_bpd\", loss)"]}, {"cell_type": "markdown", "id": "0a771ffa", "metadata": {"papermill": {"duration": 0.037059, "end_time": "2021-09-16T12:42:24.159136", "exception": false, "start_time": "2021-09-16T12:42:24.122077", "status": "completed"}, "tags": []}, "source": ["To sample from the autoregressive model, we need to iterate over all dimensions of the input.\n", "We start with an empty image, and fill the pixels one by one, starting from the upper left corner.\n", "Note that as for predicting $x_i$, all pixels below it have no influence on the prediction.\n", "Hence, we can cut the image in height without changing the prediction while increasing efficiency.\n", "Nevertheless, all the loops in the sampling function already show that it will take us quite some time to sample.\n", "A lot of computation could be reused across loop iterations as those the features on the already predicted pixels will not change over iterations.\n", "Nevertheless, this takes quite some effort to implement, and is often not done in implementations because in the end,\n", "autoregressive sampling remains sequential and slow.\n", "Hence, we settle with the default implementation here.\n", "\n", "Before training the model, we can check the full receptive field of the model on an MNIST image of size $28\\times 28$:"]}, {"cell_type": "code", "execution_count": 17, "id": "546c40c3", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:24.236461Z", "iopub.status.busy": "2021-09-16T12:42:24.236001Z", "iopub.status.idle": "2021-09-16T12:42:24.487057Z", "shell.execute_reply": "2021-09-16T12:42:24.486641Z"}, "papermill": {"duration": 0.291233, "end_time": "2021-09-16T12:42:24.487172", "exception": false, "start_time": "2021-09-16T12:42:24.195939", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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NCAxMjMzIF0gL0ZvbnREZXNjcmlwdG9yIDE2IDAgUgovRm9udE1hdHJpeCBbIDAuMDAxIDAgMCAwLjAwMSAwIDAgXSAvTGFzdENoYXIgMjU1IC9OYW1lIC9EZWphVnVTYW5zCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDIxIC00NjMgMTc5NCAxMjMzIF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zIC9JdGFsaWNBbmdsZSAwCi9NYXhXaWR0aCAxMzQyIC9TdGVtViAwIC9UeXBlIC9Gb250RGVzY3JpcHRvciAvWEhlaWdodCAwID4+CmVuZG9iagoxNSAwIG9iagpbIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwCjYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgMzE4IDQwMSA0NjAgODM4IDYzNgo5NTAgNzgwIDI3NSAzOTAgMzkwIDUwMCA4MzggMzE4IDM2MSAzMTggMzM3IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYKNjM2IDYzNiAzMzcgMzM3IDgzOCA4MzggODM4IDUzMSAxMDAwIDY4NCA2ODYgNjk4IDc3MCA2MzIgNTc1IDc3NSA3NTIgMjk1CjI5NSA2NTYgNTU3IDg2MyA3NDggNzg3IDYwMyA3ODcgNjk1IDYzNSA2MTEgNzMyIDY4NCA5ODkgNjg1IDYxMSA2ODUgMzkwIDMzNwozOTAgODM4IDUwMCA1MDAgNjEzIDYzNSA1NTAgNjM1IDYxNSAzNTIgNjM1IDYzNCAyNzggMjc4IDU3OSAyNzggOTc0IDYzNCA2MTIKNjM1IDYzNSA0MTEgNTIxIDM5MiA2MzQgNTkyIDgxOCA1OTIgNTkyIDUyNSA2MzYgMzM3IDYzNiA4MzggNjAwIDYzNiA2MDAgMzE4CjM1MiA1MTggMTAwMCA1MDAgNTAwIDUwMCAxMzQyIDYzNSA0MDAgMTA3MCA2MDAgNjg1IDYwMCA2MDAgMzE4IDMxOCA1MTggNTE4CjU5MCA1MDAgMTAwMCA1MDAgMTAwMCA1MjEgNDAwIDEwMjMgNjAwIDUyNSA2MTEgMzE4IDQwMSA2MzYgNjM2IDYzNiA2MzYgMzM3CjUwMCA1MDAgMTAwMCA0NzEgNjEyIDgzOCAzNjEgMTAwMCA1MDAgNTAwIDgzOCA0MDEgNDAxIDUwMCA2MzYgNjM2IDMxOCA1MDAKNDAxIDQ3MSA2MTIgOTY5IDk2OSA5NjkgNTMxIDY4NCA2ODQgNjg0IDY4NCA2ODQgNjg0IDk3NCA2OTggNjMyIDYzMiA2MzIgNjMyCjI5NSAyOTUgMjk1IDI5NSA3NzUgNzQ4IDc4NyA3ODcgNzg3IDc4NyA3ODcgODM4IDc4NyA3MzIgNzMyIDczMiA3MzIgNjExIDYwNQo2MzAgNjEzIDYxMyA2MTMgNjEzIDYxMyA2MTMgOTgyIDU1MCA2MTUgNjE1IDYxNSA2MTUgMjc4IDI3OCAyNzggMjc4IDYxMiA2MzQKNjEyIDYxMiA2MTIgNjEyIDYxMiA4MzggNjEyIDYzNCA2MzQgNjM0IDYzNCA1OTIgNjM1IDU5MiBdCmVuZG9iagoxOCAwIG9iago8PCAvQiAxOSAwIFIgL1cgMjAgMCBSIC9hIDIxIDAgUiAvYyAyMiAwIFIgL2QgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIKL2cgMjYgMCBSIC9oIDI3IDAgUiAvaSAyOCAwIFIgL2wgMjkgMCBSIC9uIDMwIDAgUiAvcCAzMSAwIFIgL3IgMzIgMCBSCi9zcGFjZSAzMyAwIFIgL3QgMzQgMCBSIC92IDM1IDAgUiAveSAzNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE3IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvSTEgMTMgMCBSIC9JMiAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDgKL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMTUgKP3nNyE7bh05bgQycAAxcAAsZwArZABcKFxcAFwoWwAmVwAmVQAkUgAjUAAjTwAiTf8AACldCi9EZWNvZGVQYXJtcyA8PCAvQ29sb3JzIDEgL0NvbHVtbnMgMTUzIC9QcmVkaWN0b3IgMTAgPj4KL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0hlaWdodCAxNTMgL0xlbmd0aCAzNyAwIFIgL1N1YnR5cGUgL0ltYWdlCi9UeXBlIC9YT2JqZWN0IC9XaWR0aCAxNTMgPj4Kc3RyZWFtCnic7dTLSgNBEEDR+H5G//9vpSuLDATBi0hiOHfVTcH02dTsPi613bkB30bWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+uR9ch6ZD2yHlmPrEfWI+tdn+z92Nz3q839bZr7jPZkZFclO3L2m+ePhs1o4yUjI/sT2Wb956dwOD6vHlcHw8vqBE1G9v9l08m+jeRhdT/N6GnKHycjuyDZ4fnX1WG/5ng7jeRuulntVp/TbzhkZGQ/kJ03sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6xH1iPrkfXIemQ9sh5Zj6z3BSeE7iMKZW5kc3RyZWFtCmVuZG9iagozNyAwIG9iagoyNjIKZW5kb2JqCjE0IDAgb2JqCjw8IC9CaXRzUGVyQ29tcG9uZW50IDggL0NvbG9yU3BhY2UgWy9JbmRleGVkIC9EZXZpY2VSR0IgMiAo/ec3ACJN/wAAKV0KL0RlY29kZVBhcm1zIDw8IC9Db2xvcnMgMSAvQ29sdW1ucyAxNTMgL1ByZWRpY3RvciAxMCA+PgovRmlsdGVyIC9GbGF0ZURlY29kZSAvSGVpZ2h0IDE1MyAvTGVuZ3RoIDM4IDAgUiAvU3VidHlwZSAvSW1hZ2UKL1R5cGUgL1hPYmplY3QgL1dpZHRoIDE1MyA+PgpzdHJlYW0KeJztzrEJwDAQADE7+w8dSJ/iwOAvpAm09lTrduCXWTd9tiYy68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrDPrzDqz7uTs+ewzzMzMZjLrzDqzzqwz68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPOrDPrzDqzzqwz68w6s86sM+vMOrPuBTtEMOAKZW5kc3RyZWFtCmVuZG9iagozOCAwIG9iagoxNzMKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjM5IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQyMjQrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDAKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDgyNTQgMDAwMDAgbiAKMDAwMDAwNzAxMiAwMDAwMCBuIAowMDAwMDA3MDQ0IDAwMDAwIG4gCjAwMDAwMDcxNDMgMDAwMDAgbiAKMDAwMDAwNzE2NCAwMDAwMCBuIAowMDAwMDA3MTg1IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwMSAwMDAwMCBuIAowMDAwMDAwNzY2IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDc0NiAwMDAwMCBuIAowMDAwMDA3MjI4IDAwMDAwIG4gCjAwMDAwMDc4MDcgMDAwMDAgbiAKMDAwMDAwNTc1MyAwMDAwMCBuIAowMDAwMDA1NTUzIDAwMDAwIG4gCjAwMDAwMDUxNTkgMDAwMDAgbiAKMDAwMDAwNjgwNiAwMDAwMCBuIAowMDAwMDAwNzg2IDAwMDAwIG4gCjAwMDAwMDExMjMgMDAwMDAgbiAKMDAwMDAwMTI4NyAwMDAwMCBuIAowMDAwMDAxNjY3IDAwMDAwIG4gCjAwMDAwMDE5NzIgMDAwMDAgbiAKMDAwMDAwMjI3NiAwMDAwMCBuIAowMDAwMDAyNTk4IDAwMDAwIG4gCjAwMDAwMDI4MDcgMDAwMDAgbiAKMDAwMDAwMzIyMSAwMDAwMCBuIAowMDAwMDAzNDU4IDAwMDAwIG4gCjAwMDAwMDM2MDIgMDAwMDAgbiAKMDAwMDAwMzcyMSAwMDAwMCBuIAowMDAwMDAzOTU3IDAwMDAwIG4gCjAwMDAwMDQyNjkgMDAwMDAgbiAKMDAwMDAwNDUwMiAwMDAwMCBuIAowMDAwMDA0NTkyIDAwMDAwIG4gCjAwMDAwMDQ3OTggMDAwMDAgbiAKMDAwMDAwNDk0NSAwMDAwMCBuIAowMDAwMDA3Nzg3IDAwMDAwIG4gCjAwMDAwMDgyMzQgMDAwMDAgbiAKMDAwMDAwODMxNCAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDM5IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0MCA+PgpzdGFydHhyZWYKODQ3MQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:24.416262\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["test_model = PixelCNN(c_in=1, c_hidden=64)\n", "inp = torch.zeros(1, 1, 28, 28)\n", "inp.requires_grad_()\n", "out = test_model(inp)\n", "show_center_recep_field(inp, out.squeeze(dim=2))\n", "del inp, out, test_model"]}, {"cell_type": "markdown", "id": "7b3910fe", "metadata": {"papermill": {"duration": 0.038084, "end_time": "2021-09-16T12:42:24.563987", "exception": false, "start_time": "2021-09-16T12:42:24.525903", "status": "completed"}, "tags": []}, "source": ["The visualization shows that for predicting any pixel, we can take almost half of the image into account.\n", "However, keep in mind that this is the \"theoretical\" receptive field and not necessarily\n", "the [effective receptive field](https://arxiv.org/pdf/1701.04128.pdf), which is usually much smaller.\n", "For a stronger model, we should therefore try to increase the receptive\n", "field even further. Especially, for the pixel on the bottom right, the\n", "very last pixel, we would be allowed to take into account the whole\n", "image. However, our current receptive field only spans across 1/4 of the\n", "image. An encoder-decoder architecture can help with this, but it also\n", "shows that we require a much deeper, more complex network in\n", "autoregressive models than in VAEs or energy-based models."]}, {"cell_type": "markdown", "id": "af3db43c", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.039876, "end_time": "2021-09-16T12:42:24.641944", "exception": false, "start_time": "2021-09-16T12:42:24.602068", "status": "completed"}, "tags": []}, "source": ["### Training loop\n", "\n", "To train the model, we again can rely on PyTorch Lightning and write a\n", "function below for loading the pretrained model if it exists. To reduce\n", "the computational cost, we have saved the validation and test score in\n", "the checkpoint already:"]}, {"cell_type": "code", "execution_count": 18, "id": "386d2b64", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:24.724459Z", "iopub.status.busy": "2021-09-16T12:42:24.723984Z", "iopub.status.idle": "2021-09-16T12:42:24.726084Z", "shell.execute_reply": "2021-09-16T12:42:24.725601Z"}, "papermill": {"duration": 0.046099, "end_time": "2021-09-16T12:42:24.726183", "exception": false, "start_time": "2021-09-16T12:42:24.680084", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_model(**kwargs):\n", " # Create a PyTorch Lightning trainer with the generation callback\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, \"PixelCNN\"),\n", " gpus=1 if str(device).startswith(\"cuda\") else 0,\n", " max_epochs=150,\n", " callbacks=[\n", " ModelCheckpoint(save_weights_only=True, mode=\"min\", monitor=\"val_bpd\"),\n", " LearningRateMonitor(\"epoch\"),\n", " ],\n", " )\n", " result = None\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"PixelCNN.ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model, loading...\")\n", " model = PixelCNN.load_from_checkpoint(pretrained_filename)\n", " ckpt = torch.load(pretrained_filename, map_location=device)\n", " result = ckpt.get(\"result\", None)\n", " else:\n", " model = PixelCNN(**kwargs)\n", " trainer.fit(model, train_loader, val_loader)\n", " model = model.to(device)\n", "\n", " if result is None:\n", " # Test best model on validation and test set\n", " val_result = trainer.test(model, test_dataloaders=val_loader, verbose=False)\n", " test_result = trainer.test(model, test_dataloaders=test_loader, verbose=False)\n", " result = {\"test\": test_result, \"val\": val_result}\n", " return model, result"]}, {"cell_type": "markdown", "id": "245b1ba8", "metadata": {"papermill": {"duration": 0.037966, "end_time": "2021-09-16T12:42:24.802271", "exception": false, "start_time": "2021-09-16T12:42:24.764305", "status": "completed"}, "tags": []}, "source": ["Training the model is time consuming and we recommend using the provided pre-trained model for going through this notebook.\n", "However, feel free to play around with the hyperparameter like number of layers etc.\n", "if you want to get a feeling for those.\n", "\n", "When calling the training function with a pre-trained model, we automatically load it and print its test performance:"]}, {"cell_type": "code", "execution_count": 19, "id": "4328bb62", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:24.884095Z", "iopub.status.busy": "2021-09-16T12:42:24.883627Z", "iopub.status.idle": "2021-09-16T12:42:27.511570Z", "shell.execute_reply": "2021-09-16T12:42:27.511143Z"}, "papermill": {"duration": 2.671446, "end_time": "2021-09-16T12:42:27.511686", "exception": false, "start_time": "2021-09-16T12:42:24.840240", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model, loading...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Test bits per dimension: 0.808bpd\n"]}], "source": ["model, result = train_model(c_in=1, c_hidden=64)\n", "test_res = result[\"test\"][0]\n", "print(\n", " \"Test bits per dimension: %4.3fbpd\" % (test_res[\"test_loss\"] if \"test_loss\" in test_res else test_res[\"test_bpd\"])\n", ")"]}, {"cell_type": "markdown", "id": "c4e1ed4e", "metadata": {"papermill": {"duration": 0.038901, "end_time": "2021-09-16T12:42:27.590698", "exception": false, "start_time": "2021-09-16T12:42:27.551797", "status": "completed"}, "tags": []}, "source": ["With a test performance of 0.809bpd, the PixelCNN significantly outperforms the normalizing flows we have seen in Tutorial 11.\n", "Considering image modeling as an autoregressive problem simplifies the learning process as predicting\n", "one pixel given the ground truth of all others is much easier than predicting all pixels at once.\n", "In addition, PixelCNN can explicitly predict the pixel values by a discrete softmax while\n", "Normalizing Flows have to learn transformations in continuous latent space.\n", "These two aspects allow the PixelCNN to achieve a notably better performance.\n", "\n", "To fully compare the models, let's also measure the number of parameters of the PixelCNN:"]}, {"cell_type": "code", "execution_count": 20, "id": "f8c9acf4", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:27.674832Z", "iopub.status.busy": "2021-09-16T12:42:27.674359Z", "iopub.status.idle": "2021-09-16T12:42:27.676436Z", "shell.execute_reply": "2021-09-16T12:42:27.676819Z"}, "papermill": {"duration": 0.046548, "end_time": "2021-09-16T12:42:27.676936", "exception": false, "start_time": "2021-09-16T12:42:27.630388", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Number of parameters: 852,160\n"]}], "source": ["num_params = sum(np.prod(param.shape) for param in model.parameters())\n", "print(f\"Number of parameters: {num_params:,}\")"]}, {"cell_type": "markdown", "id": "d3ee86a3", "metadata": {"papermill": {"duration": 0.039335, "end_time": "2021-09-16T12:42:27.755406", "exception": false, "start_time": "2021-09-16T12:42:27.716071", "status": "completed"}, "tags": []}, "source": ["Compared to the multi-scale normalizing flows, the PixelCNN has considerably less parameters.\n", "Of course, the number of parameters depend on our hyperparameter choices.\n", "Nevertheless, in general, it can be said that autoregressive models\n", "require considerably less parameters than normalizing flows to reach\n", "good performance, based on the reasons stated above. Still,\n", "autoregressive models are much slower in sampling than normalizing\n", "flows, which limits their possible applications."]}, {"cell_type": "markdown", "id": "77208025", "metadata": {"papermill": {"duration": 0.046301, "end_time": "2021-09-16T12:42:27.842873", "exception": false, "start_time": "2021-09-16T12:42:27.796572", "status": "completed"}, "tags": []}, "source": ["## Sampling\n", "\n", "One way of qualitatively analysing generative models is by looking at the actual samples.\n", "Let's therefore use our sampling function to generate a few digits:"]}, {"cell_type": "code", "execution_count": 21, "id": "27bb57d8", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:27.932413Z", "iopub.status.busy": "2021-09-16T12:42:27.931873Z", "iopub.status.idle": "2021-09-16T12:42:32.123905Z", "shell.execute_reply": "2021-09-16T12:42:32.123484Z"}, "papermill": {"duration": 4.238655, "end_time": "2021-09-16T12:42:32.124020", "exception": false, "start_time": "2021-09-16T12:42:27.885365", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 1\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "8961cfdd02c24f6dbc1c42cf6db817fd", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/28 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:42:32.079950\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["pl.seed_everything(1)\n", "samples = model.sample(img_shape=(16, 1, 28, 28))\n", "show_imgs(samples.cpu())"]}, {"cell_type": "markdown", "id": "8fb41b9f", "metadata": {"papermill": {"duration": 0.041107, "end_time": "2021-09-16T12:42:32.207277", "exception": false, "start_time": "2021-09-16T12:42:32.166170", "status": "completed"}, "tags": []}, "source": ["Most of the samples can be identified as digits, and overall we achieve a better quality than we had in normalizing flows.\n", "This goes along with the lower likelihood we achieved with autoregressive models.\n", "Nevertheless, we also see that there is still place for improvement\n", "as a considerable amount of samples cannot be identified (for example the first row).\n", "Deeper autoregressive models are expected to achieve better quality,\n", "as they can take more context into account for generating the pixels.\n", "\n", "Note that on Google Colab, you might see different results, specifically with a white line at the top.\n", "After some debugging, it seemed that the difference occurs inside the dilated convolution,\n", "as it gives different results for different batch sizes.\n", "However, it is hard to debug this further as it might be a bug of the installed PyTorch version on Google Colab.\n", "\n", "The trained model itself is not restricted to any specific image size.\n", "However, what happens if we actually sample a larger image than we had\n", "seen in our training dataset? Let's try below to sample images of size\n", "$64\\times64$ instead of $28\\times28$:"]}, {"cell_type": "code", "execution_count": 22, "id": "f55e0ed4", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:42:32.292415Z", "iopub.status.busy": "2021-09-16T12:42:32.291942Z", "iopub.status.idle": "2021-09-16T12:43:10.492028Z", "shell.execute_reply": "2021-09-16T12:43:10.492425Z"}, "papermill": {"duration": 38.244567, "end_time": "2021-09-16T12:43:10.492570", "exception": false, "start_time": "2021-09-16T12:42:32.248003", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 1\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "a04db2d75a3f476487be88f18d66ddb1", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/64 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:10.455579\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["pl.seed_everything(1)\n", "samples = model.sample(img_shape=(8, 1, 64, 64))\n", "show_imgs(samples.cpu())"]}, {"cell_type": "markdown", "id": "3876e6b8", "metadata": {"papermill": {"duration": 0.043073, "end_time": "2021-09-16T12:43:10.581847", "exception": false, "start_time": "2021-09-16T12:43:10.538774", "status": "completed"}, "tags": []}, "source": ["The larger images show that changing the size of the image during testing confuses the model\n", "and generates abstract figures (you can sometimes spot a digit in the upper left corner).\n", "In addition, sampling for images of 64x64 pixels take more than a minute on a GPU.\n", "Clearly, autoregressive models cannot be scaled to large images without changing the sampling procedure such as with [forecasting](https://arxiv.org/abs/2002.09928).\n", "Our implementation is also not the most efficient as many computations can be stored and reused throughout the sampling process.\n", "Nevertheless, the sampling procedure stays sequential which is\n", "inherently slower than parallel generation like done in normalizing\n", "flows."]}, {"cell_type": "markdown", "id": "5ef1ba66", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042826, "end_time": "2021-09-16T12:43:10.667581", "exception": false, "start_time": "2021-09-16T12:43:10.624755", "status": "completed"}, "tags": []}, "source": ["### Autocompletion\n", "\n", "One common application done with autoregressive models is\n", "auto-completing an image. As autoregressive models predict pixels one by\n", "one, we can set the first $N$ pixels to predefined values and check how\n", "the model completes the image. For implementing this, we just need to\n", "skip the iterations in the sampling loop that already have a value\n", "unequals -1. See above in our PyTorch Lightning module for the specific\n", "implementation. In the cell below, we randomly take three images from\n", "the training set, mask about the lower half of the image, and let the\n", "model autocomplete it. To see the diversity of samples, we do this 12\n", "times for each image:"]}, {"cell_type": "code", "execution_count": 23, "id": "44b3d642", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:43:10.758938Z", "iopub.status.busy": "2021-09-16T12:43:10.758463Z", "iopub.status.idle": "2021-09-16T12:43:18.484127Z", "shell.execute_reply": "2021-09-16T12:43:18.483633Z"}, "papermill": {"duration": 7.773878, "end_time": "2021-09-16T12:43:18.484244", "exception": false, "start_time": "2021-09-16T12:43:10.710366", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Original image and input image to sampling:\n"]}, {"data": {"application/pdf": "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IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjE1IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQzMTArMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgMTYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMDE3ODMgMDAwMDAgbiAKMDAwMDAwMDY0NSAwMDAwMCBuIAowMDAwMDAwNjY2IDAwMDAwIG4gCjAwMDAwMDA3NjUgMDAwMDAgbiAKMDAwMDAwMDc4NiAwMDAwMCBuIAowMDAwMDAwODA3IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5NiAwMDAwMCBuIAowMDAwMDAwNjI1IDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDYwNSAwMDAwMCBuIAowMDAwMDAwODM5IDAwMDAwIG4gCjAwMDAwMDE3NjMgMDAwMDAgbiAKMDAwMDAwMTg0MyAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDE1IDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSAxNiA+PgpzdGFydHhyZWYKMjAwMAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:10.792399\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 1\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "e76d7eb2829b4c6c9b26ff6703e0787d", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/28 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:13.309899\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Original image and input image to sampling:\n"]}, {"data": {"application/pdf": "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MTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagoxNSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MzEzKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDE2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDAxNzcyIDAwMDAwIG4gCjAwMDAwMDA2NDUgMDAwMDAgbiAKMDAwMDAwMDY2NiAwMDAwMCBuIAowMDAwMDAwNzY1IDAwMDAwIG4gCjAwMDAwMDA3ODYgMDAwMDAgbiAKMDAwMDAwMDgwNyAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTYgMDAwMDAgbiAKMDAwMDAwMDYyNSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA2MDUgMDAwMDAgbiAKMDAwMDAwMDgzOSAwMDAwMCBuIAowMDAwMDAxNzUyIDAwMDAwIG4gCjAwMDAwMDE4MzIgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAxNSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMTYgPj4Kc3RhcnR4cmVmCjE5ODkKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:13.375277\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 1\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "5ec4fe6328c247b489d2701b79eb8048", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/28 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:15.879705\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Original image and input image to sampling:\n"]}, {"data": {"application/pdf": "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SUXpdDrI+3O8rxaRJ1JEnkhxDbyNHeSJDPJE5gkEVtsWCmVuZHN0cmVhbQplbmRvYmoKMTQgMCBvYmoKNTMwCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagoxNSAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MzE1KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDE2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDAxODcxIDAwMDAwIG4gCjAwMDAwMDA2NDUgMDAwMDAgbiAKMDAwMDAwMDY2NiAwMDAwMCBuIAowMDAwMDAwNzY1IDAwMDAwIG4gCjAwMDAwMDA3ODYgMDAwMDAgbiAKMDAwMDAwMDgwNyAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTYgMDAwMDAgbiAKMDAwMDAwMDYyNSAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDA2MDUgMDAwMDAgbiAKMDAwMDAwMDgzOSAwMDAwMCBuIAowMDAwMDAxODUxIDAwMDAwIG4gCjAwMDAwMDE5MzEgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAxNSAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMTYgPj4Kc3RhcnR4cmVmCjIwODgKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:15.945419\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 1\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "ff6c8e20790c444e8f7c56fe0cf6f5bd", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/28 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:18.448685\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["def autocomplete_image(img):\n", " # Remove lower half of the image\n", " img_init = img.clone()\n", " img_init[:, 10:, :] = -1\n", " print(\"Original image and input image to sampling:\")\n", " show_imgs([img, img_init])\n", " # Generate 12 example completions\n", " img_init = img_init.unsqueeze(dim=0).expand(12, -1, -1, -1).to(device)\n", " pl.seed_everything(1)\n", " img_generated = model.sample(img_init.shape, img_init)\n", " print(\"Autocompletion samples:\")\n", " show_imgs(img_generated)\n", "\n", "\n", "for i in range(1, 4):\n", " img = train_set[i][0]\n", " autocomplete_image(img)"]}, {"cell_type": "markdown", "id": "ba91d363", "metadata": {"papermill": {"duration": 0.051443, "end_time": "2021-09-16T12:43:18.586848", "exception": false, "start_time": "2021-09-16T12:43:18.535405", "status": "completed"}, "tags": []}, "source": ["For the first two digits (7 and 6), we see that the 12 samples all\n", "result in a shape which resemble the original digit. Nevertheless, there\n", "are some style difference in writing the 7, and some deformed sixes in\n", "the samples. When autocompleting the 9 below, we see that the model can\n", "fit multiple digits to it. We obtain diverse samples from 0, 3, 8 and 9.\n", "This shows that despite having no latent space, we can still obtain\n", "diverse samples from an autoregressive model."]}, {"cell_type": "markdown", "id": "ee095fbc", "metadata": {"papermill": {"duration": 0.04956, "end_time": "2021-09-16T12:43:18.685925", "exception": false, "start_time": "2021-09-16T12:43:18.636365", "status": "completed"}, "tags": []}, "source": ["### Visualization of the predictive distribution (softmax)\n", "\n", "Autoregressive models use a softmax over 256 values to predict the next pixel.\n", "This gives the model a large flexibility as the probabilities for each pixel value can be learned independently if necessary.\n", "However, the values are actually not independent because the values 32 and 33 are much closer than 32 and 255.\n", "In the following, we visualize the softmax distribution that the model predicts to gain insights how it has learned the relationships of close-by pixels.\n", "\n", "To do this, we first run the model on a batch of images and store the output softmax distributions:"]}, {"cell_type": "code", "execution_count": 24, "id": "fd594831", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:43:18.789379Z", "iopub.status.busy": "2021-09-16T12:43:18.788903Z", "iopub.status.idle": "2021-09-16T12:43:18.897568Z", "shell.execute_reply": "2021-09-16T12:43:18.897984Z"}, "papermill": {"duration": 0.162907, "end_time": "2021-09-16T12:43:18.898132", "exception": false, "start_time": "2021-09-16T12:43:18.735225", "status": "completed"}, "tags": []}, "outputs": [], "source": ["det_loader = data.DataLoader(train_set, batch_size=128, shuffle=False, drop_last=False)\n", "imgs, _ = next(iter(det_loader))\n", "imgs = imgs.to(device)\n", "with torch.no_grad():\n", " out = model(imgs)\n", " out = F.softmax(out, dim=1)\n", " mean_out = out.mean(dim=[0, 2, 3, 4]).cpu().numpy()\n", " out = out.cpu().numpy()"]}, {"cell_type": "markdown", "id": "460b20fc", "metadata": {"papermill": {"duration": 0.049128, "end_time": "2021-09-16T12:43:18.998216", "exception": false, "start_time": "2021-09-16T12:43:18.949088", "status": "completed"}, "tags": []}, "source": ["Before diving into the model, let's visualize the distribution of the pixel values in the whole dataset:"]}, {"cell_type": "code", "execution_count": 25, "id": "cf4f889d", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:43:19.102964Z", "iopub.status.busy": "2021-09-16T12:43:19.102489Z", "iopub.status.idle": "2021-09-16T12:43:20.698247Z", "shell.execute_reply": "2021-09-16T12:43:20.698640Z"}, "papermill": {"duration": 1.650907, "end_time": "2021-09-16T12:43:20.698784", "exception": false, "start_time": "2021-09-16T12:43:19.047877", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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L0ZvbnQgL1dpZHRocyAxMyAwIFIgPj4KZW5kb2JqCjE0IDAgb2JqCjw8IC9Bc2NlbnQgOTI5IC9DYXBIZWlnaHQgMCAvRGVzY2VudCAtMjM2IC9GbGFncyAzMgovRm9udEJCb3ggWyAtMTAyMSAtNDYzIDE3OTQgMTIzMyBdIC9Gb250TmFtZSAvRGVqYVZ1U2FucyAvSXRhbGljQW5nbGUgMAovTWF4V2lkdGggMTM0MiAvU3RlbVYgMCAvVHlwZSAvRm9udERlc2NyaXB0b3IgL1hIZWlnaHQgMCA+PgplbmRvYmoKMTMgMCBvYmoKWyA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMAo2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDMxOCA0MDEgNDYwIDgzOCA2MzYKOTUwIDc4MCAyNzUgMzkwIDM5MCA1MDAgODM4IDMxOCAzNjEgMzE4IDMzNyA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2CjYzNiA2MzYgMzM3IDMzNyA4MzggODM4IDgzOCA1MzEgMTAwMCA2ODQgNjg2IDY5OCA3NzAgNjMyIDU3NSA3NzUgNzUyIDI5NQoyOTUgNjU2IDU1NyA4NjMgNzQ4IDc4NyA2MDMgNzg3IDY5NSA2MzUgNjExIDczMiA2ODQgOTg5IDY4NSA2MTEgNjg1IDM5MCAzMzcKMzkwIDgzOCA1MDAgNTAwIDYxMyA2MzUgNTUwIDYzNSA2MTUgMzUyIDYzNSA2MzQgMjc4IDI3OCA1NzkgMjc4IDk3NCA2MzQgNjEyCjYzNSA2MzUgNDExIDUyMSAzOTIgNjM0IDU5MiA4MTggNTkyIDU5MiA1MjUgNjM2IDMzNyA2MzYgODM4IDYwMCA2MzYgNjAwIDMxOAozNTIgNTE4IDEwMDAgNTAwIDUwMCA1MDAgMTM0MiA2MzUgNDAwIDEwNzAgNjAwIDY4NSA2MDAgNjAwIDMxOCAzMTggNTE4IDUxOAo1OTAgNTAwIDEwMDAgNTAwIDEwMDAgNTIxIDQwMCAxMDIzIDYwMCA1MjUgNjExIDMxOCA0MDEgNjM2IDYzNiA2MzYgNjM2IDMzNwo1MDAgNTAwIDEwMDAgNDcxIDYxMiA4MzggMzYxIDEwMDAgNTAwIDUwMCA4MzggNDAxIDQwMSA1MDAgNjM2IDYzNiAzMTggNTAwCjQwMSA0NzEgNjEyIDk2OSA5NjkgOTY5IDUzMSA2ODQgNjg0IDY4NCA2ODQgNjg0IDY4NCA5NzQgNjk4IDYzMiA2MzIgNjMyIDYzMgoyOTUgMjk1IDI5NSAyOTUgNzc1IDc0OCA3ODcgNzg3IDc4NyA3ODcgNzg3IDgzOCA3ODcgNzMyIDczMiA3MzIgNzMyIDYxMSA2MDUKNjMwIDYxMyA2MTMgNjEzIDYxMyA2MTMgNjEzIDk4MiA1NTAgNjE1IDYxNSA2MTUgNjE1IDI3OCAyNzggMjc4IDI3OCA2MTIgNjM0CjYxMiA2MTIgNjEyIDYxMiA2MTIgODM4IDYxMiA2MzQgNjM0IDYzNCA2MzQgNTkyIDYzNSA1OTIgXQplbmRvYmoKMTYgMCBvYmoKPDwgL2VpZ2h0IDE3IDAgUiAvZml2ZSAxOCAwIFIgL2ZvdXIgMTkgMCBSIC9uaW5lIDIxIDAgUiAvb25lIDIyIDAgUgovc2l4IDIzIDAgUiAvdGhyZWUgMjQgMCBSIC90d28gMjUgMCBSIC96ZXJvIDI2IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgL0YxLURlamFWdVNhbnMtbWludXMgMjAgMCBSID4+CmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iagoyNyAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjEwOTE2MTQ0MzIwKzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDI4CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDEyNDg1IDAwMDAwIG4gCjAwMDAwMTIyMjIgMDAwMDAgbiAKMDAwMDAxMjI1NCAwMDAwMCBuIAowMDAwMDEyMzk0IDAwMDAwIG4gCjAwMDAwMTI0MTUgMDAwMDAgbiAKMDAwMDAxMjQzNiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDAzOTcgMDAwMDAgbiAKMDAwMDAwNzM2MyAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDczNDIgMDAwMDAgbiAKMDAwMDAxMTAzMSAwMDAwMCBuIAowMDAwMDEwODMxIDAwMDAwIG4gCjAwMDAwMTA0NzkgMDAwMDAgbiAKMDAwMDAxMjA4NCAwMDAwMCBuIAowMDAwMDA3MzgzIDAwMDAwIG4gCjAwMDAwMDc4NTEgMDAwMDAgbiAKMDAwMDAwODE3MyAwMDAwMCBuIAowMDAwMDA4MzM5IDAwMDAwIG4gCjAwMDAwMDg1MTEgMDAwMDAgbiAKMDAwMDAwODkwNiAwMDAwMCBuIAowMDAwMDA5MDYxIDAwMDAwIG4gCjAwMDAwMDk0NTQgMDAwMDAgbiAKMDAwMDAwOTg2NyAwMDAwMCBuIAowMDAwMDEwMTkxIDAwMDAwIG4gCjAwMDAwMTI1NDUgMDAwMDAgbiAKdHJhaWxlcgo8PCAvSW5mbyAyNyAwIFIgL1Jvb3QgMSAwIFIgL1NpemUgMjggPj4Kc3RhcnR4cmVmCjEyNzAyCiUlRU9GCg==\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:19.922594\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["sns.set()\n", "plot_args = {\"color\": to_rgb(\"C0\") + (0.5,), \"edgecolor\": \"C0\", \"linewidth\": 0.5, \"width\": 1.0}\n", "plt.hist(imgs.view(-1).cpu().numpy(), bins=256, density=True, **plot_args)\n", "plt.yscale(\"log\")\n", "plt.xticks([0, 64, 128, 192, 256])\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "b8f425d1", "metadata": {"papermill": {"duration": 0.054971, "end_time": "2021-09-16T12:43:20.809613", "exception": false, "start_time": "2021-09-16T12:43:20.754642", "status": "completed"}, "tags": []}, "source": ["As we would expect from the seen images, the pixel value 0 (black) is the dominant value, followed by a batch of values between 250 and 255.\n", "Note that we use a log scale on the y-axis due to the big imbalance in the dataset.\n", "Interestingly, the pixel values 64, 128 and 191 also stand out which is likely due to the quantization used during the creation of the dataset.\n", "For RGB images, we would also see two peaks around 0 and 255,\n", "but the values in between would be much more frequent than in MNIST\n", "(see Figure 1 in the [PixelCNN++](https://arxiv.org/pdf/1701.05517.pdf) for a visualization on CIFAR10).\n", "\n", "Next, we can visualize the distribution our model predicts (in average):"]}, {"cell_type": "code", "execution_count": 26, "id": "45d72339", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:43:20.943659Z", "iopub.status.busy": "2021-09-16T12:43:20.935876Z", "iopub.status.idle": "2021-09-16T12:43:22.354374Z", "shell.execute_reply": "2021-09-16T12:43:22.354760Z"}, "papermill": {"duration": 1.486584, "end_time": "2021-09-16T12:43:22.354905", "exception": false, "start_time": "2021-09-16T12:43:20.868321", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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IDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9laWdodCAxNyAwIFIgL2ZpdmUgMTggMCBSIC9mb3VyIDE5IDAgUiAvbmluZSAyMSAwIFIgL29uZSAyMiAwIFIKL3NpeCAyMyAwIFIgL3RocmVlIDI0IDAgUiAvdHdvIDI1IDAgUiAvemVybyAyNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC41ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMS1EZWphVnVTYW5zLW1pbnVzIDIwIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMjcgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDMyMiswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAyOAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAxMjMxMiAwMDAwMCBuIAowMDAwMDEyMDQ5IDAwMDAwIG4gCjAwMDAwMTIwODEgMDAwMDAgbiAKMDAwMDAxMjIyMSAwMDAwMCBuIAowMDAwMDEyMjQyIDAwMDAwIG4gCjAwMDAwMTIyNjMgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk3IDAwMDAwIG4gCjAwMDAwMDcxOTAgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDA3MTY5IDAwMDAwIG4gCjAwMDAwMTA4NTggMDAwMDAgbiAKMDAwMDAxMDY1OCAwMDAwMCBuIAowMDAwMDEwMzA2IDAwMDAwIG4gCjAwMDAwMTE5MTEgMDAwMDAgbiAKMDAwMDAwNzIxMCAwMDAwMCBuIAowMDAwMDA3Njc4IDAwMDAwIG4gCjAwMDAwMDgwMDAgMDAwMDAgbiAKMDAwMDAwODE2NiAwMDAwMCBuIAowMDAwMDA4MzM4IDAwMDAwIG4gCjAwMDAwMDg3MzMgMDAwMDAgbiAKMDAwMDAwODg4OCAwMDAwMCBuIAowMDAwMDA5MjgxIDAwMDAwIG4gCjAwMDAwMDk2OTQgMDAwMDAgbiAKMDAwMDAxMDAxOCAwMDAwMCBuIAowMDAwMDEyMzcyIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMjcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDI4ID4+CnN0YXJ0eHJlZgoxMjUyOQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:21.574138\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["plt.bar(np.arange(mean_out.shape[0]), mean_out, **plot_args)\n", "plt.yscale(\"log\")\n", "plt.xticks([0, 64, 128, 192, 256])\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "329f3ea9", "metadata": {"papermill": {"duration": 0.05645, "end_time": "2021-09-16T12:43:22.469433", "exception": false, "start_time": "2021-09-16T12:43:22.412983", "status": "completed"}, "tags": []}, "source": ["This distribution is very close to the actual dataset distribution.\n", "This is in general a good sign, but we can see a slightly smoother histogram than above.\n", "\n", "Finally, to take a closer look at learned value relations, we can\n", "visualize the distribution for individual pixel predictions to get a\n", "better intuition. For this, we pick 4 random images and pixels, and\n", "visualize their distribution below:"]}, {"cell_type": "code", "execution_count": 27, "id": "17909488", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:43:22.589483Z", "iopub.status.busy": "2021-09-16T12:43:22.588638Z", "iopub.status.idle": "2021-09-16T12:43:27.526284Z", "shell.execute_reply": "2021-09-16T12:43:27.526674Z"}, "papermill": {"duration": 5.001478, "end_time": "2021-09-16T12:43:27.526816", "exception": false, "start_time": "2021-09-16T12:43:22.525338", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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n0dUlgplbmRzdHJlYW0KZW5kb2JqCjE1IDAgb2JqCjw8IC9CYXNlRm9udCAvRGVqYVZ1U2FucyAvQ2hhclByb2NzIDE2IDAgUgovRW5jb2RpbmcgPDwKL0RpZmZlcmVuY2VzIFsgNDggL3plcm8gL29uZSAvdHdvIC90aHJlZSAvZm91ciAvZml2ZSAvc2l4IDU2IC9laWdodCAvbmluZSBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTQgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9laWdodCAxNyAwIFIgL2ZpdmUgMTggMCBSIC9mb3VyIDE5IDAgUiAvbmluZSAyMSAwIFIgL29uZSAyMiAwIFIKL3NpeCAyMyAwIFIgL3RocmVlIDI0IDAgUiAvdHdvIDI1IDAgUiAvemVybyAyNiAwIFIgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL0YxIDE1IDAgUiA+PgplbmRvYmoKNCAwIG9iago8PCAvQTEgPDwgL0NBIDAgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTIgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgovQTMgPDwgL0NBIDEgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC41ID4+ID4+CmVuZG9iago1IDAgb2JqCjw8ID4+CmVuZG9iago2IDAgb2JqCjw8ID4+CmVuZG9iago3IDAgb2JqCjw8IC9GMS1EZWphVnVTYW5zLW1pbnVzIDIwIDAgUiA+PgplbmRvYmoKMiAwIG9iago8PCAvQ291bnQgMSAvS2lkcyBbIDExIDAgUiBdIC9UeXBlIC9QYWdlcyA+PgplbmRvYmoKMjcgMCBvYmoKPDwgL0NyZWF0aW9uRGF0ZSAoRDoyMDIxMDkxNjE0NDMyNiswMicwMCcpCi9DcmVhdG9yIChNYXRwbG90bGliIHYzLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZykKL1Byb2R1Y2VyIChNYXRwbG90bGliIHBkZiBiYWNrZW5kIHYzLjQuMykgPj4KZW5kb2JqCnhyZWYKMCAyOAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMTYgMDAwMDAgbiAKMDAwMDAzMjYyOCAwMDAwMCBuIAowMDAwMDMyMzY1IDAwMDAwIG4gCjAwMDAwMzIzOTcgMDAwMDAgbiAKMDAwMDAzMjUzNyAwMDAwMCBuIAowMDAwMDMyNTU4IDAwMDAwIG4gCjAwMDAwMzI1NzkgMDAwMDAgbiAKMDAwMDAwMDA2NSAwMDAwMCBuIAowMDAwMDAwMzk3IDAwMDAwIG4gCjAwMDAwMjc1MDYgMDAwMDAgbiAKMDAwMDAwMDIwOCAwMDAwMCBuIAowMDAwMDI3NDg0IDAwMDAwIG4gCjAwMDAwMzExNzQgMDAwMDAgbiAKMDAwMDAzMDk3NCAwMDAwMCBuIAowMDAwMDMwNjIyIDAwMDAwIG4gCjAwMDAwMzIyMjcgMDAwMDAgbiAKMDAwMDAyNzUyNiAwMDAwMCBuIAowMDAwMDI3OTk0IDAwMDAwIG4gCjAwMDAwMjgzMTYgMDAwMDAgbiAKMDAwMDAyODQ4MiAwMDAwMCBuIAowMDAwMDI4NjU0IDAwMDAwIG4gCjAwMDAwMjkwNDkgMDAwMDAgbiAKMDAwMDAyOTIwNCAwMDAwMCBuIAowMDAwMDI5NTk3IDAwMDAwIG4gCjAwMDAwMzAwMTAgMDAwMDAgbiAKMDAwMDAzMDMzNCAwMDAwMCBuIAowMDAwMDMyNjg4IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gMjcgMCBSIC9Sb290IDEgMCBSIC9TaXplIDI4ID4+CnN0YXJ0eHJlZgozMjg0NQolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:24.825070\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["fig, ax = plt.subplots(2, 2, figsize=(10, 6))\n", "for i in range(4):\n", " ax_sub = ax[i // 2][i % 2]\n", " ax_sub.bar(np.arange(out.shape[1], dtype=np.int32), out[i + 4, :, 0, 14, 14], **plot_args)\n", " ax_sub.set_yscale(\"log\")\n", " ax_sub.set_xticks([0, 64, 128, 192, 256])\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "d39faab7", "metadata": {"papermill": {"duration": 0.074666, "end_time": "2021-09-16T12:43:27.671433", "exception": false, "start_time": "2021-09-16T12:43:27.596767", "status": "completed"}, "tags": []}, "source": ["Overall we see a very diverse set of distributions, with a usual peak\n", "for 0 and close to 1. However, the distributions in the first row show a\n", "potentially undesirable behavior. For instance, the value 242 has a\n", "1000x lower likelihood than 243 although they are extremely close and\n", "can often not be distinguished. This shows that the model might have not\n", "generlized well over pixel values. The better solution to this problem\n", "is to use discrete logitics mixtures instead of a softmax distribution.\n", "A discrete logistic distribution can be imagined as discretized, binned\n", "Gaussians. Using a mixture of discrete logistics instead of a softmax\n", "introduces an inductive bias to the model to assign close-by values\n", "similar likelihoods. We can visualize a discrete logistic below:"]}, {"cell_type": "code", "execution_count": 28, "id": "41272be3", "metadata": {"execution": {"iopub.execute_input": "2021-09-16T12:43:27.818104Z", "iopub.status.busy": "2021-09-16T12:43:27.817603Z", "iopub.status.idle": "2021-09-16T12:43:28.944136Z", "shell.execute_reply": "2021-09-16T12:43:28.944525Z"}, "papermill": {"duration": 1.200149, "end_time": "2021-09-16T12:43:28.944659", "exception": false, "start_time": "2021-09-16T12:43:27.744510", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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MTMgMCBSID4+CmVuZG9iagoxNCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjEzIDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE2IDAgb2JqCjw8IC9EIDE3IDAgUiAvUCAxOCAwIFIgL2EgMTkgMCBSIC9iIDIwIDAgUiAvYyAyMSAwIFIgL2QgMjIgMCBSIC9lIDIzIDAgUgovZWlnaHQgMjQgMCBSIC9maXZlIDI1IDAgUiAvZm91ciAyNiAwIFIgL2cgMjcgMCBSIC9pIDI4IDAgUiAvbCAyOSAwIFIKL24gMzAgMCBSIC9vIDMxIDAgUiAvb25lIDMyIDAgUiAvcGVyaW9kIDMzIDAgUiAvciAzNCAwIFIgL3MgMzUgMCBSCi9zaXggMzYgMCBSIC9zcGFjZSAzNyAwIFIgL3QgMzggMCBSIC90aHJlZSAzOSAwIFIgL3R3byA0MCAwIFIgL3UgNDEgMCBSCi92IDQyIDAgUiAveCA0MyAwIFIgL3kgNDQgMCBSIC96ZXJvIDQ1IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgMTUgMCBSID4+CmVuZG9iago0IDAgb2JqCjw8IC9BMSA8PCAvQ0EgMCAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMiA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAxID4+Ci9BMyA8PCAvQ0EgMSAvVHlwZSAvRXh0R1N0YXRlIC9jYSAwLjUgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwgPj4KZW5kb2JqCjcgMCBvYmoKPDwgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjQ2IDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTA5MTYxNDQzMjgrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDcKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMTUzNDkgMDAwMDAgbiAKMDAwMDAxNTExNCAwMDAwMCBuIAowMDAwMDE1MTQ2IDAwMDAwIG4gCjAwMDAwMTUyODYgMDAwMDAgbiAKMDAwMDAxNTMwNyAwMDAwMCBuIAowMDAwMDE1MzI4IDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDM5OSAwMDAwMCBuIAowMDAwMDA1MzcxIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwNTM1MCAwMDAwMCBuIAowMDAwMDEzNzE3IDAwMDAwIG4gCjAwMDAwMTM1MTcgMDAwMDAgbiAKMDAwMDAxMzA2MCAwMDAwMCBuIAowMDAwMDE0NzcwIDAwMDAwIG4gCjAwMDAwMDUzOTEgMDAwMDAgbiAKMDAwMDAwNTYyOCAwMDAwMCBuIAowMDAwMDA1ODcxIDAwMDAwIG4gCjAwMDAwMDYyNTEgMDAwMDAgbiAKMDAwMDAwNjU2OCAwMDAwMCBuIAowMDAwMDA2ODczIDAwMDAwIG4gCjAwMDAwMDcxNzcgMDAwMDAgbiAKMDAwMDAwNzQ5OSAwMDAwMCBuIAowMDAwMDA3OTY3IDAwMDAwIG4gCjAwMDAwMDgyODkgMDAwMDAgbiAKMDAwMDAwODQ1NSAwMDAwMCBuIAowMDAwMDA4ODY5IDAwMDAwIG4gCjAwMDAwMDkwMTMgMDAwMDAgbiAKMDAwMDAwOTEzMiAwMDAwMCBuIAowMDAwMDA5MzY4IDAwMDAwIG4gCjAwMDAwMDk2NTkgMDAwMDAgbiAKMDAwMDAwOTgxNCAwMDAwMCBuIAowMDAwMDA5OTM3IDAwMDAwIG4gCjAwMDAwMTAxNzAgMDAwMDAgbiAKMDAwMDAxMDU3NyAwMDAwMCBuIAowMDAwMDEwOTcwIDAwMDAwIG4gCjAwMDAwMTEwNjAgMDAwMDAgbiAKMDAwMDAxMTI2NiAwMDAwMCBuIAowMDAwMDExNjc5IDAwMDAwIG4gCjAwMDAwMTIwMDMgMDAwMDAgbiAKMDAwMDAxMjI1MCAwMDAwMCBuIAowMDAwMDEyMzk3IDAwMDAwIG4gCjAwMDAwMTI1NTggMDAwMDAgbiAKMDAwMDAxMjc3MiAwMDAwMCBuIAowMDAwMDE1NDA5IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDYgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQ3ID4+CnN0YXJ0eHJlZgoxNTU2NgolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-09-16T14:43:28.378484\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["mu = torch.Tensor([128])\n", "sigma = torch.Tensor([2.0])\n", "\n", "\n", "def discrete_logistic(x, mu, sigma):\n", " return torch.sigmoid((x + 0.5 - mu) / sigma) - torch.sigmoid((x - 0.5 - mu) / sigma)\n", "\n", "\n", "x = torch.arange(256)\n", "p = discrete_logistic(x, mu, sigma)\n", "\n", "# Visualization\n", "plt.figure(figsize=(6, 3))\n", "plt.bar(x.numpy(), p.numpy(), **plot_args)\n", "plt.xlim(96, 160)\n", "plt.title(\"Discrete logistic distribution\")\n", "plt.xlabel(\"Pixel value\")\n", "plt.ylabel(\"Probability\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "0ee82ec6", "metadata": {"papermill": {"duration": 0.072594, "end_time": "2021-09-16T12:43:29.091439", "exception": false, "start_time": "2021-09-16T12:43:29.018845", "status": "completed"}, "tags": []}, "source": ["Instead of the softmax, the model would output mean and standard\n", "deviations for the $K$ logistics we use in the mixture. This is one of\n", "the improvements in autoregressive models that PixelCNN++ [3] has\n", "introduced compared to the original PixelCNN."]}, {"cell_type": "markdown", "id": "d30f7d84", "metadata": {"papermill": {"duration": 0.071033, "end_time": "2021-09-16T12:43:29.238966", "exception": false, "start_time": "2021-09-16T12:43:29.167933", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this tutorial, we have looked at autoregressive image modeling, and\n", "implemented the PixelCNN architecture. With the usage of masked\n", "convolutions, we are able to apply a convolutional network in which a\n", "pixel is only influenced by all its predecessors. Separating the masked\n", "convolution into a horizontal and vertical stack allowed us to remove\n", "the known blind spot on the right upper row of a pixel. In experiments,\n", "autoregressive models outperformed normalizing flows in terms of bits\n", "per dimension, but are much slower to sample from. Improvements, that we\n", "have not implemented ourselves here, are discrete logistic mixtures, a\n", "downsampling architecture, and changing the pixel order in a diagonal\n", "fashion (see PixelSNAIL). Overall, autoregressive models are another,\n", "strong family of generative models, which however are mostly used in\n", "sequence tasks because of their linear scaling in sampling time than\n", "quadratic as on images."]}, {"cell_type": "markdown", "id": "38f1dd4f", "metadata": {"papermill": {"duration": 0.071797, "end_time": "2021-09-16T12:43:29.381584", "exception": false, "start_time": "2021-09-16T12:43:29.309787", "status": "completed"}, "tags": []}, "source": ["## References\n", "[1] van den Oord, A., et al.\n", "\"Pixel Recurrent Neural Networks.\"\n", "arXiv preprint arXiv:1601.06759 (2016).\n", "[Link](https://arxiv.org/abs/1601.06759)\n", "\n", "[2] van den Oord, A., et al.\n", "\"Conditional Image Generation with PixelCNN Decoders.\"\n", "In Advances in Neural Information Processing Systems 29, pp.\n", "4790\u20134798 (2016).\n", "[Link](http://papers.nips.cc/paper/6527-conditional-image-generation-with-pixelcnn-decoders.pdf)\n", "\n", "[3] Salimans, Tim, et al.\n", "\"PixelCNN++: Improving the PixelCNN with Discretized Logistic Mixture Likelihood and Other Modifications.\"\n", "arXiv preprint arXiv:1701.05517 (2017).\n", "[Link](https://arxiv.org/abs/1701.05517)"]}, {"cell_type": "markdown", "id": "c99bfefb", "metadata": {"papermill": {"duration": 0.074235, "end_time": "2021-09-16T12:43:29.526938", "exception": false, "start_time": "2021-09-16T12:43:29.452703", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "![Pytorch Lightning](data:image/png;base64,H4sIAAAAAAACA9ycyZKjyNqm93UV0bmlSeaprPJYCyQ0IAkECIQ2xxicScyj4MZ631fWKCKzKior8++qJvQvTphFhAbnlfujz7/BAf/ll8fPb/9jKQu6paxewiZN/vXLb49/L4mdBV8+gezTi2c3NuzmSV7Bae6BL5/stsm/vpxEQdjATQjS6fXXJ1/f8Ozq9u31x+NP//rl5eW3ENje48H0MAWN/eKGdlWD5suntvFh9q1NEmW3lwok03FZDRcV8EHjhp9ewunRl09h0xT1rwgSRE3YOp/f/tn1JFJ/dvP0n0jY3dTRqv6q0dagcvOsAVnzT4XeBGA3yVvvc018tlN7zDO7/8c9evQBjlI7AD/pFfK92qQ0vZ0Bt/m7gF7cKq/rvIqCKPvbYv8vVNPPN6V38pOlZHk2pHlbf3pJgRfZ0ytJ8uklmo4NqqgZvnyqQ5vCcNiXx8NWJdSTH906wtnTZd0KCI4F29JGk8vGjWQNkitdkNex0KZMhvSieSwLYWgWWMmZLbRsSkjZ3zVA46q+XSBpdzx9+fLpbWR1MySgDgH4u5iQt4eIX9kp6PPqVsOMC1zXxTHM8zAS81mbdHyGdh1g2w4OUOezW0/DRL6a9wwWMUGcj97O3TW3nU9owJeyrawf2up2zA/4YnfQeNs7G2YjhgpBu/uDn9BSs6Lp5RpSBSjz2Ei/6xy7rTdR1soXY8MevH4+CweEdhflVQ2zPuN5JEF5OOXitMehHOMzJAcY1+ZcluD+hOK7P3PIBJHHMAsTv0c34QRWGdJsq7TeC8nWxb1LpXgLhowcvVzjhwCctqeVme2tzVG7onVFnE5kcr0dUdwduCSPwkAfh2N3uX8Ama8OgMVxhnGAi1IO7dI4h+IkBjCc8YGH4w5n/47lMVlqt4qK5qcgvMlDVJO3ePz7EYoj8DOLlRlhzRy7QD/J5yWn007vHO8BNx4tXTQx/RZltLppz/q16xiNNCoO0xeNEfKWO/hifR7posmKw11VmoVOss0rimYoHj6+KJLItZsoz5B4mv5v3f30Ulfu38YCsi6q8iyd/ARMUIABDE18jutP//oNeZP7Oldmolg6SVlQq/rg7YyD194JbGfv617Y14BIr9XF1bt1z09tTbDDufWSQBrF84f7itMXldcBVb2G2UndQNxVdXyeWt4QjT59LAo3bLMb/M6boJ7D0bbNPIGHrZ16NPUGIk63NX4vlyil7RPnDLVXM2zoneS3F1xJJZe8mGlP7NpBI52twSuF44pLtBCXS5fL5aqk4w5V8bO7btxN8AweHci8KaugOZwkUA//C4sPmCc4F8ThGu3jkJST7rjyd+VtWbirTOnkLC+I43q3Gpv2ZLNHbx+YeOKdnJRaczWISrpQgtBIvcWBuJADvz6wq71u3Cvtg2H84Vg9x2NxgiV/RGI+C/68dKrL1ZG4U16dKx+V8pVzXexCPRshvjykGOqUV0Gie2rTD4xly+qJFMnlJdzfFobe2bvwGN7v4BKkwW6voDrP6PLf8BmvCeGUOrYJgCPvy6fPX79+2+vszAXeY7Rvjf65CX3TgFGKnOaU7T1hQlGNKWkVk4aLUQtJ7U5ZqhhIfuOt71K/kTJ5bfXUMrnHm/B2Ms6qJZ4J/kZtD/L6ILWLaKcnAiFfrANxH9fuEI/tSjz9DQfzU27ZlCM2AM4zeGqaJ8ksgN+LwYD2qCm7sZ9AskYVZXJDFxkTzCW6VBQDVTjCdiplYSWIKOvbzk9DSJCRi5ArxMpTOG7ICHHfHRtyf9gyu9Tk/XVkcztRuRZVfmYX1QwLdKd6Jo2qKq/mIPxDBXYIknB9/xnsnBCMLc0PA8Nsh9Aq8hNKM0TAXvOy4nmUIBzI3g3cyEQ4CV0Y7YDcepKEIPm01lRzGEvkdrrR7A3KVG5TnV31wp70v+HJfs7uW0VYz4P3uwxMA4xxCMd5Aj0G21xbTKd2ZqkeRa6vUvx4uS5Ky+jG+5VddnlFnakE2bcr3lkL/IVitGygUTWGyvP1uuhW/FpMjvWIMdxyleEbsQ+MxRx62VQCNtE8dG8aMPApzOOewk3BuTOaHNPwHBMUYVSCWdDu6IXH4tCIDHo3JJ9cT2gMScjLdXMv0HroFwcWIWkoEtCbJ0Zqd8CIFd8ts5LOB1Pto1kzNmuqyGkfB9RwXUTeVDIHlV2EM0n+RBUmfNqnyB8kJ/PZdkWD616Do40sqU2lmo1s2GqE93y+sP3kuD6r9L09BWcEtXr/3jn2WKICHoZS30YC69ALFiOCyzqFWD9Gj8Iqrx1rRlxx82KYR7EYYAdwDkOTz4jDduUnB9E1RmNh2QqeMSkT3BJFCM3aqIVloyp2hwsO7ZR7sPPZInDy2NwRqxpIl+1e2Jb+PUE62q54r71y2j4tDqvrDFv0QGIPU+qR5LYXZQEMEvCodeYg/IkkTNsOAygCfQJVOkZyTYRujgkNoDkq3ZZmSWNctFwxtgFtxHgIES2294RWDVUyztpoc/XBSUpqIru7OqIRGyjfNozYRMEKsUEeQ8GMuOJVdjD9yYtZHL+JwMAmfMCS7BPIGZrmQdYY4dFBg4rIsehwiZjeUNwXKy93KBFXD9K9ldzFRsNXhWjtFKBfcuPeoMdLgBKNH+PezcbTVbPRoC4uLM1ezrBH4EUNHOb5Da6BW4HmIwzyZ5owReEk5/ncE7iuqvIqJpNFjVtnn183Xe+X3DXOqTGHLGxBYmgGlOW6kyDT0A0FQ6Oj7uelJ8va9agNWNDspa253G4j2SDQpKLH806YEasfDGYzxHDH5jiMfoZfjMrDdr2+rnN2cTrEbr5NJeWg11fDUBM6bATNYvhCTYu1skYWImSWkILIMeI1oXmStzfOFY4HZWXXjIyPC5XOywxt5vBK8ziCi8i9TVH0I2zwB3qTR8Rt1sWf4RHLU7lOOAOTk7B0q4uoiqRzb04dxVR+6KTKQuba/eLSWYudZp2IqGNXTrDWK7zsJ69oEqlGWsgaNR2CyMotr6yzLXmeEZd9O0qcfBbCrxKwzaE2S3PPWHaqB1Tix4TYVC3oobzaYciKdIf2HN0bA201lNC0TWGpY7gAhQniNhiUmlj0m+IErVJqYx98DzsoI3IgofvKFp2TSwUzrNAHdtNWAHbtJMnbD3GGP5Gcqj7cQ230GVRDdq+We9/rPAlHbpoUsztroOOB9xJ96O97Mil7g5YN2SM0QVGD845EKkLaiKer0lo9umYWphnuLGSbR4217xamCKg5thglzeMkVVa081C+04FZxsU9zH5GjI74273ak5VPSpkEZC61jXsX3kRO93pFOi9RrNTw7YiE2g2J9zjXI41zrHhkLWJUYYbLprGvnjey9AKxw2t143eMvpxhlcEUQitQ5B9hjt9rwSyLcQRmu0/gyIuYf3XDpj0JK0zPi3Spaq686xRX2hyYmPe3w96uR0kO3dWOF2nfE+zbDU2j0ZExahAO0eUGkcZ2qR/GYJ1ytIzshBl2GGX142xzDZctqGZVLX9WglGS8iifpp5xoiLiyMEV66PT9LRFjXjtbijkUm2QlBV2ZUst1MU1lqsTHflMq+3D+5E9nPl86y2RqtZD7M7wPdL640nId4V19cbVjp3FsIua1+azVnDeycAowH2GdZ+RFXKccYw2K2tvIt41cc3t9WofFxdraY7a3T22Q7UgTrVfF8iFCLc3NhfEIYgSos28usZodc2XkJaqEpax1C6wqTTmr9iMmRy3aQE3+RxyXyVgn/Eo0mOfUqM0nt0fEFmrW8ijO5y6WvTZPdnIer1faI220uK69Fb9HQIDy66P1vp2wskc2oliTiYWxPT7g+oztlDt77cFpZUtnx9n2Fxit5kbvi6ZfoQL/IEcTNEeQ9nuMzJDVaXON0hacQdEqMu9mwYqEEKNuS7jlA3urpwtLdO8HTyg11VhIvdlstucVsHZb1cCvdld/EE5uiaU8/qmPGStO/HW59CMsikbCe1qHsTfVWCSwgBFcs84TVsYtb/wTmEa3ZBlMV7rUsdzRQrprbxsOfVmub3e+inEblFDZ0ZcNZo4XlfQ+TiW2MI8bPTqpJ1ZbMkOtKx7uj+ihTJj/qagqSJ3KmsT4D4O+ghz/JkmbFOUg/ko9oyV7fCKElgWRCJB9+vad/NhEEy97K17fIFKoO8bwWCEHbSVoX68jUKKqblKmKPpjr6Z9vtjWUGblrid5ODG2+uMzBpzhk1meRP5Xw+YTKtuYD9321kR5ieSMAAYzZCe/wSq66yJ177cHy718VGI3NNRJ1DaE2hiL6GsXFKbympHHlRbE5cRHhnZcuPEy20qnnnyOrbsrZMM7H7m+PTMCPZ+D45gxqpYUYE0alO4arMMVLNoficFYzbj0AT2jJjdX5IOOR1Iub5Yw7nVfDG+E5dSSzt/XzZxx9/pXkzWx3QzevTyllbY5qZfa6wsBEsX4mjosTGlZNUmZTIwF5ILuZIyY4VsGnoNMhfA3y5om4fxT1qwizEc7fvPWPlObVSWByKLURdzS/6O8jc33Yr1vnS1K3TYtrfTfViJU5w39fFarKHTIZBzU1lsriNJHGh/iBfnNRZvaBeh90oUSYowzOKYTwUcgIspef4Iv/kjvSnhsz0UEM84qxUGuSbt9WTPbomtWSH6Xj4KZ+hI3jyh8aqNUFWtRx6lMFCTfaX36OWshCVvL73rORK1cXlSeyQTT9itxw2gp2jAQcmMWPR1/B+BkKVRG8PxZ+SSquwUVd+JKh72ayVfQtZKWPDn1Cx753yXiMO5d1RLbp2Oijjs6sZrG4yrUlyyyvVK7/B7appXh0MiSLTMgSC8yoeCGZGmaJN6mn5tEz6unXk7XfcRxvhzWZgkAQ04+xkR5yiMuqrp3V2IWhe9VyDARgznq6QhfI2DzCaz1mfXWy2XxcWJETSqDDek9YpW48kh+IclsHdxT9zO/qrOtM1JO+WXcUbEqYDtpWAqT9yPgPpXNZggcdcjyKdU2uPoqaGeD1qr3jc3K9G3JunvLwQyWLSRGLcAAhU+SEN21pxoX+pqHEFNgnNnMfJdUTOcm3lsyjOkjDvKOS1Iww4Os1j6X5PCeVeavNeBUZ9wPY98xhkF3yIsixVp3TQ3mGllVFuL1NlkN/LIN1bskuesWVGLSORW/Ha7Ellvx4B0rB256WQ26RSBiqsjI1mcgB5jeSHeDHsxI95MGUveTDVKEhVOblfe7FPVPxSEGY/1PPYpF6lumWjLofgSW9A126bCEg8dkczRUDKxpCqOvrrDN0zb0753MFSE3XlNhnEOsUaWy61OJsVFPuv59aKnjjUFnoU15aT2LIusizyrow7AbeaB6lEDZnY3j+kPJWGccFiW9Z+xOq5NYYTWcHZXQ4g11p0VpAW0XXkCWjYHl/fp2gov+sqNMVtUhL0i63ImS+UoRsdhg+dHY+lBLXWKWFBa26O2bXF2L86ISG8p9RQz8raYlVz+SQgmOdp1GPCMDF0zPAmVaE7JTqQohF6R0BQqq1BXJJZ5KT3vfryt1lJ48unmBsKyk27iKqD2J+0aCIbU4I3vckJm7pJUEnpH16NiFc+wyxpUEajhxnbmpUPvdSZ+FENOyeQzViUXpX48rdBaOHJIW+Do2Pmee2x2tGgZbblWdDoufINVO0pTbe1SMZt7tRbQy+riXlG7SwS/tXZHlI8vEnPVOdQy0N0sfh14dPBxjsptE/sRKD4gfP8XsrBPojZHEM+I46GvgzE6rsIIq7YlQfKurEku5ldouyrPoeQYUrhDRIfsDt3ZI8OgVXTWPuqutO4cNK36tO54pjAMkO6OrLpV3Tw0ZsShaQxvFvXtku1ZTL8Xg1nKJz3ggieQbEDkVxsoofJtVmcx12bbcap/Lv2aH8+XeIFVtXA1kvNuu2sM3GnHKzk22kjYKh2up/juNAdlHO8ANKpDhl6b7v0rNKPiqRvbvQEPtqfEcP4K5l/VYIcCLOc/5VwY2maM5Wvk8sAzVy5VefR+2+/jo3hJDqaZefGIjP3V2J33dmny3R7BRstU9ukqk0k5xynpmq0J3+Uvd2TJqAsu2Y3dckbUeR19/fVc6uOi0CiAuwj0s4n+QBP2cMD5Dv2Mlcxdcdyt2eJ+Pm9wYkzjWMlXF6jzdisnqrQM3V8lJ7+emtsJNAFtiUTAa4YULQX84jQub2PMSbph10OJImvG2cQr3HfbOb70jcFUWQdTblN/FNM/6cE4zREkhz1jlSOTbOLsLXCZMQC5y3AysHkcHc7+rtOYPsk89sQKUGQu2QhvLvvAlkC6NG97AO7g5FU1ZGccKwoJ5cvRgO6gaMygZoadNmEFwOe3N+ZwfK8Dc65NOwz1DHtky0aXEyPi013RJsWOEBCIWTgEoTEHhWoWq62/ue6ZHZlCRBQ0xnqxIitmCAas4tUYcd01u22xQpC2Urfw15YxakUyh18065rI6XDYx22HIohn3J9gSqxx6YnEP58QQzXRMQkkJXQP53KDtnrGI9IIlLPiXUMhRTbF3b9cNpK+7mJKQGVwZ6DR2R7wlTCe906jCzrZBsQwY/Y2uV3PCiqvAjDF2j5GUc9Y8TloqsZqVpCJ3UnbSrFSskRTk+OpbDTGkq9TRrgkLz6qgQtPbpRmBDVqgeVCPAHhVtxbtoAEV8bPzUBWi0GxrfEkrOYQ66fwXude1KazuP0hAxMYTlK4/4zZ6XeuLFt2IdyV21I9mSNunsaq3NOVGl5YdtiYIsv4DnfJBlwsyaSKUPloG+KeuSw1PdKHlpT1AmLNfPAPRhGsz7ZtzKDXZh2IZt2K9aYAM8BnUEA8o9pr/GXetX5cFsuDcknLc93a9vEoy4h3iVhbFfST1gq7Vidxi79DZnvrAJlANo3yIdNzVnZgz+NZrBbKHrEgKI4Pi+WciPC64cEUFpu2huvWSeddZ/tXtSkL9FHAOs/IqF2pRaALE+iQaWzI+iI1aECsGcbxT74b9G2lys4B8hr2MiRKyoGVuYUQ25RCw3Zoo7barmu3FK9XkXQJL/6un6pDbwbLHjhBAvd2NStI/KECM6jtOP4PrvD+gNtyN1vqKrAUdL8BEB6DRq6ZPe0q1EYkXCyClFUodXRTHblDahNOes9psUgGylGCbBky6WINePsuCy1vHQ66xzNx50V/J4X+J0Ael9bV0VTpPhYSMJcFFId+n6bNLiXANQVcOmp8jq7qgTxuQc9qUm0BRhukxmR3bR+NWxJQcbPQ+kL3T0hm1ZsrD6G6rB01x4gZs11qneUuoWUMdtap+eDbk73I92vYwwBN08z3AH75tpVLZj+2enmktMVU1356+bpByJdPfeQ14RcPdJEL4Ncnn74ZURM1CfhXMUyM3fBtL5ksygIkyYP8c5EFL3bzkk5BGFQv/+d/vyiD/mi3/73ZXw58tFpHzaZ1fkPetN/tNfPWQQ+89XwC8q6PegheXlV68Pj77aNe+moCOH26n1cv4fQOPD2ZHqePW45fFtuXxylge2r4+UVz7QS8DHlbvTzuZUzq//mS5c1LMwk7eZSAqkjsBnx+gV8+brif3u818raDxWtvfvTl5wXI3t59RwC6p8m3rS7etfj8+vIrwC+f3nh+v++Mn7SRB0fug+IPt8p42M9ro0ebx+B+KPj6xUz10IT1MSl859epz/+OvHdfDUaiGMmyjyqJoCmcfXfg2zf6GOXkCZs2m6bp9OTdsdOzh4vESIYhGJpDqT/2Z3kn0PRRMzH/9XXjnV+nmfFO4duofvcFw3+9Pw/GsDROMziKMBzmcSSLPm78ADCGAQdmKZuBOZZhGQfFMAb1Ht35UU+mTwLvOvG/3j7rZ61du3oPrG7T1K6Gfyd2FYB/v/b2Z0e+fiPvDv1A0/zJJ/5HTb8/G9MfZpwHv37F/t9uR3/txK920vxHoP7BAB/z5N8PA3s3wK/u5ccHPPziu7a5E79udvXjtv8Nk+NPH9hWyQ8s5p0//f9W/o+bda/7jU05xJ+D0lu+8nf3bnrLQqbf/wsAAP//AGQCm/08bWV0YSBuYW1lPSJyZXF1ZXN0LWlkIiBjb250ZW50PSIwODAzOjU2MDk6NUJERjkyOjk3MTBDOTo2MTQzMzk2NCIgZGF0YS1wamF4LXRyYW5zaWVudD0idHJ1ZSIvPjxtZXRhIG5hbWU9Imh0bWwtc2FmZS1ub25jZSIgY29udGVudD0iNmE1NjI3OWJlZGE1NTgxMGU0NDI4MTA4MGYyNTFhY2JiM2Y2NjYwNmJhNzRlOWQxZDI5YTE3NWNhMzNmMTU0ZSIgZGF0YS1wamF4LXRyYW5zaWVudD0idHJ1ZSIvPjxtZXRhIG5hbWU9InZpc2l0b3ItcGF5bG9hZCIgY29udGVudD0iZXlKeVpXWmxjbkpsY2lJNklpSXNJbkpsY1hWbGMzUmZhV1FpT2lJd09EQXpPalUyTURrNk5VSkVSamt5T2prM01UQkRPVG8yTVRRek16azJOQ0lzSW5acGMybDBiM0pmYVdRaU9pSXlNVE16T1RRd09EUTRPVFE0TXpZeE5UY3lJaXdpY21WbmFXOXVYMlZrWjJVaU9pSnBZV1FpTENKeVpXZHBiMjVmY21WdVpHVnlJam9pYVdGa0luMD0iIGRhdGEtcGpheC10cmFuc2llbnQ9InRydWUiLz48bWV0YSBuYW1lPSJ2aXNpdG9yLWhtYWMiIGNvbnRlbnQ9IjViNjg3NGU5NTE5NjcwZDI4N2IyYTljYmYzYjY2MzYyZjU3ZTE0MjliMjhlNmVkOGJmNTExZTdhY2NmNzUwZDIiIGRhdGEtcGpheC10cmFuc2llbnQ9InRydWUiLz7sPWl32ziS3/MruOrt3t3XocT7SGLPcyfO0XGOiZ1Od8/M44IkJDGmCDYP28q8/u9bBZAiRcmmZMk5Zmc6Y5IggCoU6iYA3bsnwf8ezWhBpITM6MFgyi5oFpAslPPS/0iDQi7IZCAFLCloUhwMMpqyPCpYNn+g2o6lWbamDKSQFEROP5IruchIkkdQ9fDevXvLXU+iYlr68jmd+4wDmLKsCMoiX9v9/ZyVWUDlgIV0LYCDQZGV8Gp0iIC6wHIaA/I0lOMoOR9IFyQuqejeEx1fh3SXIBPGJjGVASkqA2micRSQImJJC+lAPS+fyC+15y9+eWrpZjxPg/zDW/bEOI9iZsofj7zLn57Oz4zy1W+Dwy5R+rt/eWZOcmeqXF4QMjl5efTLhz8c36evL5Pff3+pZuqvf53nv+pXZXby/jbd//5p+sv8+Omlb1/qVJHLN2dxMXN+zoNz7al5WvwSTZ8o9OpSG+dHt+n+2a+5+ZK9f3/++jEjR78n9uXb13KhqG/nl6n7Ss+PPyaFp3u/f3gbQPf32r2zoGDxvIiCXJ6yvGhTnMU4vSwbCqYiaToM2AyZ4ZoOoIYcha0uRMMbWtALqCeXWdxqNC2KNH8wGl0Df1SxOL0qaJaQeORn7DKnmcf7ugHWbaDUr2sJaHdNALjoOWYrMzL6IS4eloCWjJV/mBQPeQnKRlPix8wfgYRebiR7XfgsLaJZ9InGcxlbj6OYthD45w9/lKx4CAyTA2bi4YEkroa43K8eMxgjAxVRV/rbP+o3xTyl4VEZAjoBXfOeJCyZIw4v3tYvEef6dZox1G4vwmXwqmXrtm0pqq0sI3JBsoj48TpIY0qKMqNPYzJZ85ZepSAYMxh487IiQF5Aw3wZgXdlkkTJZBk4qYb5IlwDgGOGU7zS/w04R51xa4qhO5YO2ktbBg3aerkmziLMinj48750K1iO4Zq27to9sIoMSIukq6Dd1KXtapptaEZPlxOQyGLqTaY08FjCDRGQ2xOzhDRebh+TOc26TALQLNsxHNM1l2uDfIxBBR7FtdR1ZwSGEhXzNf2tJwhNwjfjdySZ0LqBqihKQ/Rru1s/l6vdmfvtztqsu00H6+y3O6Sd0vDQmIEXEP6yIj3//LOB+VVJqKk4mq6Zqv4ZJNTUdRAmxTF7YG0hoaaqmbrquOpmEpqWcexl9I+S5oVHAk4+D7T2LC02ElFTcx1N05QO095aRK+hyG1l6prJ/BfkWkM3bU03+jhpL1yrKo5t2eYe7YqpOopua2Yf+hXXRnle0tuxq2ZqhtaVjtuz63pS3Jpd18/iLuz6jy4XrvAZsEQYLTUT17/9XVxZ9vcanX9WdzNSBNO/19WrG3oFE7KoWt2go9qt+J/gt3phOZvNPVIUWeSXBe22Q7+z2y4o84LNrm/Do88bgLXf//mPZRJ3WbKNY0W45QbchV9q8qbxxp/RhGYE4mKpdp0lmCLpJxKcX4IrlEuPgWNhqvwoBj7oigcweZmukcQ8PH/ZFYgPZ4FFLpNn5W9Pwmeu8/i3dz5JutxzEWUsQUFcaQ6iE5ZBw/ELZqlpvMItXUKpFuh/wwbp7ZNdjIW8unkjCysd2qqhQ2igmX1OZp4SmJ/e/hzVNi1L0RW3pz+WTUgSfeIi5KVxTcebunZUXVFUsxvGrHQd5V7MJhMaelF/r65i6w5oRK0P4QllfZ1pEGOZhqXpZp9DUDkBNHxcZhmw7Apb+qx4GsUQci8M3ANpTOJ8EeyRIGBlsnmwx8P1FQZrIrm19nKVXVwVplg1jL4BTudhxrwgjoLzYUjyKY9KhgUlwRQYs2As9tmVFxSkS9NtMXJUzVBh1EqfC5mX/iwqhm2+yzH1kMa0oF4eTRKvTHfHxjAdV9OdPnOdMG9GQeyDIZjD4Bx4FVMCUUg9NvaaVMPu+NiObZqm5fTgI2YKiNOOIGGugvM4yoshCUMPkym74+MopqlrvfxTzVaTO/WiWcqyAqYMHR26B0QsFb2gzQgzpXE6jCnJEm/GMvCGfJgur4Udx2qh2XfCyzF1RTX6lFyBMQQX6mHIvIQVHqh8UHhe18rsgouruhAlG322QdCIgl8495CFeEoaZHvIk2tjEPiGUntAyrRUzXY2m7g1SAlG2idKmqq4hmm4fSakR8gi8B1AFc3ozKdZvgesVMUCUbs1oYSk7ZdQ4HEDh+9IqDINETHgdMzC7o6Vhj6Q3ZsY6MEqjPIZxEq7o6Mbjq5pjrW9QeNR2gW/XZi23fExNR1N2uYGtsUy+1LXmglmTDX1PrvaQkLIkcelqiLJvnBxIbTVrD4VvW6CKp6tWHhnVEzLAGfb0vpY9yKil8MgJnmeMYjqxJdEr43ZHlABiwoecB/bCilq4YKeF9gtXr47FjbMDkQzfXPTIUgTYO4E29AUCCY20yMN8ErP7nc2HEzvGeq2uNwJZziuYelAm9vRZVGwOyKuqms6ODS3Q2QcZeBz7RUdC3Sr1ucLd9GZgFeF+eOA7sHagOFTddXYVoNs5++Km5UvWjeg5eiqBaFCr9EpU6BGiOlJHg6vzSRsSxNXsQz4Z24oPKjAdo8cXcUG66ZbvZGjAIrTsRi0H8Ux+iIpmeysxQAPWzdNpzdCq2J74fR4gghL2eGdcHCBK119M9cV0+zg+Hg5WeTUd4ANNgyCHqU3y1TDnqUkW+a/PeBgqpar6OpmvNCVgT1kVVyQUdXV1d6sWOUTpzRZgM8vowKzPDvjYII11RWnVzXVEgEeZzMP+4kNXPAkXBvCqA1VAR96MxG4OuRqd3GwdUV1Hac3iVM5nFM2o6gKvD1pJnAlHF2xtM24cd/QYeCmrSrKhlZSmGronmZpFuXUK7KIrHyC2xILTQEn29Qdzd3QdVj16LyCkl1dBkADDJOpaxtmGqIkLYsVWnhjlu0BEwukU1F6066Nio6SknogINM9kcICt9KGkPDWMzLO6K4KAtBwLEu3LHMzU1WZyZocQ/xMs4fYA9AAX9Jw3A2zYl00KupUemtnZFSQFt22tvKfGmTojES7C6yqqhiPbcgeXRxS8HMvWbby/Wx7NFBmDUCmBw3uXoOQBnvy4wAymE8LM5ObGY19swBIBDCl27f0roJeuzApmSOQ/ago1UYa6Op2FNhj6KspmCYyTMvezGxkFNxJ6D2kIXckMS0iOBP97KAEP3vOysxLKA13dTIRN9swwKxtmFLja6Hn4FhFM5LNPf6BeT+iqukGRqG9+bReIhWAV2Pt9oAXfs22nZ0nby/2TtNdx7A1tXcxax82C1u8F/unmaBibIgVetDqIrQzYFzBCf65vtns1Ko1IHFQxgQXoKtVhnh3TEzLcl2rN1dxPSbmnj76iFW8gIm+mbe8Dhdlf7jYuJfG0DfjWOGbhmSeexnqlWZp376zSxwz3bStDTUO6l7cQuRVq+L3QBhXc2x3w5BmzSRVi7P2NlGOqiq2siED36BaSJrGc69e5eHThI6jYh/46Yrj6r3OxLX02lK+bsFQjqE5urvhl/LW58OC5Lt+9BDLxV1Lczbjp975y4syRCdsu/m7DdEsW9VtbbMvRh2ieT64a2GQlTP/zvDDPKTa7z8uZ+EKtrUPchvUcKciaLDNVCuJzxGv8u6m0jVUw1A2TF7XSePbZWlug51rQ1WzdyKrT+ahR+KYc9mdEcxUwOOFQLlXYSxQ6vD/3WEGHG8plr2pn0lCL2TBdth09k3ciA04ecDofdjUy9NpgivrN4zbb0Mcx3UUt3dtxjKfC9fGJ0myqbfZgtdeqn8TZrppgPO34cfEdWv6F+hu8xHnFoiatqLgKubbI5rToky9S5adj2N2eWes55rgFGkbpr2X9vbcBsEbMAE32lIUXd3Mu8jBS809nvvdHbKh6aqiK5tl8kKKORTQ5gKFpNxLeGVZmusqSv9umyrnfT0OCyAZxa9F3X26i3Vpf/JdwJL06D9kWTpjUprxhZYS5uWkcUzyKWjh+1IxpVKzVFf6+VTiiRmpYJJPpZiBNxBKUYL1xBb8KSjLQ6kgE3g/ZhnlPTx580rK0BvLckmWOdg8yKK0kIKM5TnLokmU4PZn3PvLynwghXRMs4MBvwwAQEEnWVTMDwb5lAB7yzYxsl989uPo5On79ER7Nc0/Pvvt0/vzX1j44VzV/8jJ89+OkqcfP72JX7OXz85+zt+w55QVx6Pgr1f+rz+RJ5f5h1+jy9J2Ez26SH4/vWLp5cHBQMKNIoAKeInVpvjRR3JBBLoDKc+CZqO32NBd7/LOQSL4auuRuB21dlPTkFDNN83hx3xw+GgkejsUG7DF2QXd4xzyAu9WNr7z7fKHK6cd8G3hMQMqtloMxFkO3brInPyIhT4gq4cqVJZHroLE9gkQP707ev34uff23Zuz48dnL9689t69Pzn2Phz/9PzNm5f3Xx29e3l89vbk6PGx9/b49ZMXr595L16fnh2dnBxh7dNBazM80lcGNRNdHAyuxBb2atN5C6DiQDTi+4GvanhjqYEb6qaqmGEYjlXXdyHgUwNNH49NzSCq4YahaxPbIgoEpo6htg5FWAMvyNM1MG1KLDClYagSh8AlMFxqjxWTgm84JiHofV8lZmhYjjGmTqhRywdrhSs9wrGtm34fzHwNTG1smJrt24Zt2sR1QzM0LUMlJHQU8K1MxQwoOFqBQQwH7btt+MQc65YPow+d0HVuhvlxHUjf11RLd2zbGAcaDcaur/lAQYhiAssIA6pr9pjothM4Ci51Jw5VxyoxTAJuFVBHDFNwz8rBE7JYmryW50Zv52csC6Yn0WRaYG5klM4LLJDjukSC2lJH/jZriWI6WDlxoXWYQ4gKGykhVxuLWjiaju5YrqnecBZEp3lXGLsIbtRTe2tAG53WES7b9ZJcshtwWiXatv2npQ9qswWiPm9iq6Hm+G3kvNUL3xW09Vhpgd6JlzFW7It87S5vQ8t7tVLF423AJMYHgwCtHpiaeCBNMzpesS6bcbc4+GNGcog2RxCr5CNxXM7Iw13FUTCKZmDY8xGwJRumiMxGZ/9UB6HI2Ml1x52QNBq2cMWvJxekoPUhKiPedlXy6q5plrHstn2Lxtd33jLB4DpB17uBa7rzRHfLCDSTChNxLkcBatW1k7riMqQRBE6hjAwIXscwv+CHN8UMfKDvFP4/occbECTmh9UUVBJw+LrFgwEodH6sz5hciHLhz/DpH/GJ3wyhqn1e33Cm6aCwOWAYz49Xs+tYvB840qNzyhD4lTM8Zgpo1JrJ71SqgXmCyu26vJacB9ikVZlLDwgC6ppRxfztOUyiMcXziwTSo7oAnDg+bnRd31SuK+h8GZxyzClCHJx3ka0dp3rhkJC5mPuXckySSYnS2fHdAJsRd6f5rc/CeU1rsfVTZmUh0eRCbrZDcfcdNFaeAvWiCyqeUTeA61rMY0AFP/nLlxlJH0iYXjyXseBh5VNW2imMLmpQXPcJNYjoQpcw0YgVSBf2AoGOVDWGhqSi1Xcg9Fkhs7FcjWjBJumVrEnpXDYEe8v+RI6SMbgFCfohtCrFJWXy5RT4ScLDjGSAP2ZBmSPw/DxK5YItej48hQIMSKqCRyOywCdPSSI0HS4+kDHTxJKWYaqRytgEiJYLfcijmgxBtR5ln2TS26qetKDKOLqCAOgyCoupPIbwuPbNOeCK4j4JznELdhIKdn0gfWe7vjMeP+TtHkjK9w8HG2BZQ5eBLDNpHc6IJI8vADpIgLgKUlQUWUQEi5Lq4ZGY0xrWczHDLA6larJbPAeUCYG3ozjnk0CiBBo+ESXSKsOMDZxwbSBlTJADc0RCl7QYbdGTfBVLoRxP5HFMryT8wwcMsHguVUpb/F2Tu9VP2Gr3scyLaDyXfbDblCarnS04l/Nu1cMsk40bjPFAwmMRQGp91BLPK4muJnBCZB6jHwz++4QTTAKC/Y8kyHlfmjBk1FoL3Oeq+wHaZC6FM9REh615eQR6D+iPaupgoGsV5GkUgp6pmQNZ5id2BQGRpEiqBf+gTLj04OkMwWXlXCba9zMZWCBESqquMuIki9GvyGZN/ZQUU2kcxbGclTjDmElgIXhc4cHglSMpj/WhCRf4r7pxAn5rSNpQcyVraOqSOTRsyR6a7tAYKnA15aEqLjq0gAdXHiqqPHQ0flWHhitrcDfUbbiauiwKLKjmGlAFrhoWOuIZmgEtoECDAhV6teBqir6g9dDS8VZSh4qDKKrQGNoMbQ1vsdyx4R/gq0NdCzDE1rwzeGEiPlCkDm0o0GSogiBdcaMPXRNHABV1rAhDBGw0joXC6w91CzvQ8BHKNU6qoQUwADRAQUywRxUR06G+BpjgXxgFTjq0hwFLmoQvVCQG9MaJiwOvr0PDgJfwH/DI0EVgEgID7IemhfVU7JHXV00+V3xIMGp9aJs4FlPCscCUDTUTZm9o8zmEqRBYIFE4DV1BTICL49AAJMwuzLIJc3nkwCtdEn9hnAqgA+wgG0MDCWc6Mv/vE6ox5Cuuxi4mLTlF9d6WkSXZB7UB7jSYEVCMwNdJgA5S+0EGwzShRa2yIZpMQTUdLivDdvcj6L8F8DpVc41W6djFkfgI+Rd4wCXsB6fw+GOZ/kBm6UMsi1lwIPTtj0Lf/gjqY/EWdcbB99rT7/XHi3MAv9ePRcniHEBRgjYfLmg9eXsRDlSd85hm0EFSaoYVJeAGcb8hOJcaoo7NVfOcMLnExB42kHxQYmhDhGXnD1A3A5com0vcAoIFAfNf+QCqBKpWxz/5TDZX8eHaip+yUCvVOr+KqUoPHcwqu1l9ICnBKQQHLKi2Qja77e6LS0rmaCirRlVn9RE9fGP5YlVi1SW6tJUFXOppKTKvWiRg/+83mKzid/ri2Wvv/duljkTuExBIJh4EJUv1P0skuITN0pEqfEB//jlYmQd5OiMBRCCEarZGNd8nNtXUUDOoGRg2pb5r2cFYoVbojDXFpoFvmdQhvqlS23RMkxiWqQRhd8K7UiNJKB4/JH6ePizTrkh11YBfFgXYrbZtPgMRioFFIYiYVKdp8tf0CvwiYMVFYkGEKqKHTYxkywGq1IlfJPzI2pa0zApZJBnvdXwtbtXXWPLa0GvGFlYdK29v1QtQRxS9xfx2Rh2Vu20ewf/hH6pxVcUbbYr2hCyKVRXtgvlc7VbmzT/NFGm1D3vjPmzs4xVvpGqtBgpv0O1GUeS6m1XjwvOVj0aCAw47ur9rBtomQDh2r2hSSs2t3HaWO6FCwVJZkTKcabj6DADO4EbMvbBHdQNgpIUP3XGIex1bUQJqiDXB2arlaqn2dq80CStVLgsEm1gtp3i6F6rzVNbb3vPXIX5r5G1radPAczL2Km1XFf34w4KCGwmaiS4o/iFLcqJYknICjqKqgHcbg0umyfinLTW8Ev45UXV+q/F6Uree3KoIHWLdWF5TUxE1ZVET4UKXHDVw4G2nXVHhlTaTshX/CthlEYihiICzoOMf4FRFmvmyiX/wYTkOewZWj8TL0VMZLzmGnOXxGAuZB+ac8VddtThqGrUcoEb2uMhg6mPxuCR4y4JTPUGlhaiDu8P/QJku0XBCeXtUD+tiZwTRAOvK/XUeqLQyLpT/KkJfUV21NEm1VOFK0JjMF88QcDcuc5Pl6PSflzNcNr+m//pNq4gbS5hWBR1BvaaTgv4kH+4S8dv+6FrYkvRhOpeeRcXz0v/L2vdcDaCEp1cDaV5dl62sITkD6WoWP8hTAq7yIMVhZxcg1yidBwPOL/XouCwHU3oBl5BdQqB8XkyaCSR+zuISfMd12vyaIdS6gBvY+2psDbX7KL331UaS1rZaDpCWXgjCr33ZNgcZS8Ug0JAtTEftsot5EtLoywaofhiMsYZb24NdUCCmY2zJLyg7xjXDb4Kk1uflKj83h4AhnmK2CTR4Dpa1jIuMLJjkBJhJlqttG8shydSUfipnKec3WeY/k7BNquZpjcphfVfl9irMmr7z+cxnMRCPkULm9l3iTlXCshmJ227WIiJKM7ShP2Qkyx5WybpW8rJDnUaZdVQYhmUwLXrrc//aDhrFtqR0gLcWhJ8xXxz5vg3ZG5dgifDjNYQ/fMUB3D0JH43i6Doh25QYNReOqpVZd0iWIwHhG6ML/sQHV5V3SZrHCyDfGHUA5/Pqo85d0eZtBeIbowwMscTFXHdImdMKxDdGGZQoGRfu0cvRHcuUJMB8YwTiS2Xzu6TNCw7hWyMLXx9J7tpMvWiB+VwUWu/SPBqV8eE1r653lCBwVFoRWcFSKeU+qX69R77BNPCPkbgKZFPKL1H67nzX0xqvQxEYLQq+LHdvQlKx+Y8vPGJZtIUR/UykfVzhJ9X4HXZLviCJhXCsK1+kMzvFIsxfSYWsgdCavK0SHevyEYvZxr3jixnu5giWpo3nCq5PDWwxg2cI8xD/rgm5PtvIm91hn3v8xw3kw+b+Wlr8O0v2L5ElO75KQcHQf2fImhd1hmwfKTLwJjTMxXyGZNmNXs5u/gwVTPIVZN8qdgUNJW6qDO+X9F9u8jynRrOYbRVUGxV+P2MJQ0cbJky7zi09wfP4JZKEfHWj+AmVR6Op8fm5AtCKgjuMLrax3QKVQ3H90rHaRv6s+M3Pu43PtvFfW/gcth6+/sigwH2EfDfFV0DGsxqZw/ruqydgvaYoJv5waVHrV0BOruyQiIBcpfrw6YR8UY2/FVnxlFix3mo4KaPwDr+lbEHXN4CUJLCSOFYg9Ktl/4+s6mOWJKDxJDypU2LFlGb5bY2qsotRXbPADw+bmN3lJ7izKZXeAZBXx7idAX9M+ZsRrhaZxE99fRXSdSxQORTXb5CYszK5068xW/klFTb484blDCV1qeCboS4Ny2oVcmtL/ddA4SqCOq7xWySHFyXfDI1xj1l+LX15GP5liXyKCIpNY5hsXCpNRemXN7rrym+TJ/4c6VLcjESLNCbBZ8+XvmqBPmw9/Dtj+q+dMX0rTlr8djOm30jK1KhSpne8tLA6OLMxFf6Xym1WjDU4fIs/p/IZVhV+9oxhRevvqmUKsvgdmShvHRrwhX1N/Fkbif+cDfc0m8dvxgdqPiC2sznVrwV9JWTmuEin+MtFPPRuHr+89/N1LCHZPGro82p3Wv+wTdDbxA9fReBwV2selvt9NEpIs1llZefxdQcWtHew8HFWL/g9lKL9Wt65srbnWZTIwlfiG2H0xUaY7u7le/faHVRbfnNKsmDaMsP1gS3Vi2t8xJzGYxCAjBZ141koCnknNR5Q2Hig8KBLecBSGta9C0hLRQD/I/JIwQb3uodBrCBTb5bC0wr/a/C/eIogv380QiKC3kbmuJqlh/wNP6iHIc/gj2wcPsK/rc1jrWHL+Ko+mUIULe8rOsUtkUIwOPqy2Kz2brH/d+llFC6dLtW8qUeNBw+BadxgG2+NDO8DvCicwjU9sWySr3a31LhM1jVcDJZ/+dkOJ/xdRlB7wRQiWVocDN6fPZXBz53RYsqAAhPasPMjTsea+EhtfmYIEFzix3fK+Uxa4tDFqTK4TxImK5hmbEZjfu4Jb9AcObK2Xc1Uaxj61keDcMDVNkXkt/bO5S1GJrqp8AMTQOOwJQVNwTJ/1sfetAt5zQhQxd9rx6OF2hiJ7dqsOKfzg/z+qPVGHEb0x0pl/KF1WfwsKp44BUpuCeV2fR70Tlkc4vGcp4Ifut0tGG5N5So3stLm+hbtqqhzApJGBYmjT3hg23jcfi3EGIymz3x2tdSQbwAlecrSMhVmfV2N7tbU7nsOvzquWvSyUqXiAdQ01bxCyAdGOl+pWWuY9USsW+cl2OCcf52VMVwDScVfR02nf8SjZ7Q4Fa9p+HqxzfYJVk+q1cCtbvOUxjH/Le91w1sa2RJdG0H4PwAAAP//ALcASP88aW5wdXQgdHlwZT0iaGlkZGVuIiBkYXRhLWNzcmY9InRydWUiIGNsYXNzPSJqcy1kYXRhLWp1bXAtdG8tc3VnZ2VzdGlvbnMtcGF0aC1jc3JmIiB2YWx1ZT0ialhGTWg1NXBrcHRnUFJpTDYyVWhoRjZqWmxucGxvS1lIK29wYzZaUDdpM0QybFZGbGNyZkhSL3hhbzIwRmtvajhIckFIdEQraWhLZlN3YWFUM0FYT2c9PSIgLz7sfdt23EaS4Du/Ird82tN92kDhfqFF9sryRZ6lZJ+WrNntF27iUiyYQKEaQBVJ+czr/NH80H7JRmQmgEwARVZJlCW5S0dEAXm/REZGREZGnBDx70m2Wm8a0tyt07PZMkuSdDUjcU7r+mz2a63VWZNqdUqreKlhGm2RpXkyIytaQHoMmZHzEyL9e1Jvr8htka+ggGXTrE/n85ubG/3G1svqam4ZhjGHFDNykyXN8mxmWTOyTLOrZQPvxozQKqMabweUX23SrjVFpZmQlCZp1bboOr3Taohdzs6frGmzJIssz89mq3IF2eqmKq+hkV+Efvg0fDYj5ZrGWXN3NtOdGUnOZi9s3dXdpWnFpu4Tg9jE1G142lvTjg348DUWoMHfUoNkGkun2RgMT3urYUIWykIw6VtoyhzborSoawNWa5p6QLyLgJiubi41PbwwDQxZmlJmHKTzkxNpaJ8k2bYdi2/KW7IuYXKycqXRqC7zTZOScptWi7y8EeNHEg1Hgvy6KdZaU2r15uoqrTFLTWBqJ4K1uFw1NFul1Uyd1JMnm7ytuy11uoQmLdY5BaBRizo5eZJnfQmLPL0l+IAyIOMCprGhVcODAOQKaEq6atKKrDWDLFyAt212RVl3MRprnwgaN2hGqjIHICjX7Aub8oSShkbZKklvz2aa2cHXqtQ2EFjl0GgiN5FumnKiZVO9Z1M+2ZAuSmp2uYZJWmu4Aqp0cTYD8KANZbVofEFKHeETIgNBW0kZNxmMtlxvG8RaXS+rbHUN4wgryCJNetu0XeBT2c01W7rtYjS9bo3iq6izLVj8alW6Lge1jJvBU4nKSJM1OCN/TxkAl9WdWPQ5jdJcDd9m6Q2A+tnMgOVpegQbAjBew3hAq3Sznd2suJIxgFZtMDTdpqsySfiis4ilu0/hD3+hNMN08HcZ6L5L4Q/+s1D+ujUhlRys8felpoSbJq5+QCGYRbOWWkBNYmIGAzKYDuIQuRBTN3wLAg1siRNiW/CJcRAMJW218G0B2MDVzDchFBdruu16gHN0L3QgswMoULeM4A02Q1RlQrnL4O0Ll0CjLXdrw4PCH/xnLdEd3cpN3XGx+sCXohC7GReB7iEm8tU8mm5ttWFREOji3xJiRhHwf4S/3hmu1lX5axo3D4NWm3AAXT/zYBW0usBHhCuTTe5T9mOKiTbY5sGB6JmBg2uZnu4HDquLpQRkj/CoZsNYmCz3DYYM4xw21s8x4C3UyqOUyRRzw0FXCUewGAaLKXs+LKad4zeiJtYCW1oLhlgLW19ZIga2ydhqg1BMjH9vCy3AWtTVxvO4g7UmSoLgty+C6brtyboh9KkUCnkfDyI5yfEwQIp0A3h8xUJVcGzDHhMacSB86gBaCQl/IpaCoQ/DAMBUjYDxZ+lhbvTAgmDfoR7xWBbAFR4iDC+3dcMh+JDxHosiLF7DOPaYHOwnc9iyzjkh8wS60I4v3cJeV/E9aeegSvunSN+OK80b3DDl8Xyd0gLIvirGiJa8DCTyMpi17ZjYRaWqkLjd0QoW1VMFQ4qLba55umgIUBM3FV2TuAaaqNqsYqCJlA8NenOVNrOJUZJaF5UVkCQAAEiaJID91aGKy7ystOgKyK6qyWh1R9a3kIgHs8Z0EUWOuT0yJt8imly1RL5EC6zpSmIEphLzGpJ0QTd5j4HlKclgSJZZTap+U+8Jyx9HkW3dc6z88JZc5WVE810NoXlOygX5IWuebyKpGU8hnAfeU/1utiTRshWSjFqUl/E1H+atRvPsaqUVkCEH+ur//dd/y4WOJnt36R9s/hONQfNWW5Txph6DA351g/Tv8EGacmpC3r37+ELPT57M8wzG4sl8k+PPfXwGrKkqrQHWdrIZDzAZguwd0/LAYdzH1jAiXTRe7rw0znUKLUkoAvjLkoisacK4BBg6IhrejwPvt9TtLOmRkUjdYv48q5uovO2gApmigmNKiZ3ohmKSNWlHTko1zcGNEn4MBi4G3igZ7qWfNyvHunSpoNkjN3fk5o7c3JGbO3JzR27uyM0dubkjN3fk5v4Q3NzvzS+UNys8FfzjcQ2sY5dH3uHIOxx5hyPvcOQdjrzDkXc48g6fMe9QVld0lb2lkk6LzD3I0Uf+4cg//E78A4eVPxTnwLt05BmOPMORZzjyDEee4cgzHHmGI8/wWfIMx/OGI79wP7/Q6lGR7l+XjH+wicTki7IqzrtoeSyVfF2IvJy6GzdVmgMZvk0R/9ikiDQHH/kVjKnaI+UaDXAPnFif5+VVtvpblTabanXZlGd4T6r+k/30T9b38P8qa5abSI/LAj5+vntdAoheIBJaZasrCFrfNRik5VIY1BbBT0FrmDJ4Scq4hp+63FRxCi+XwC7BDgNvWUEBHcELNKLU16urmXLTR3T1Obtn9SJdbaCa1fUAcGR+R83OuJDlXVKVWpxn8fXZ7Lcv/7kpm69xo2kukTXh36f8B/DgEiKymLE1OsvDY77iP2t6l5c0EZlEYTC0LP1ltrpcQ3eUIvHKmrgnRgrogFJcjy0us7bQ1SbPv+qbM27kqx9/eHn5y89KQWWVwRxCK1ZXl5sqV9Kz6Tydz/uJnA+ncT6axDlO4ZxP4Bynb84nby6mbs4nbt5Om9KaTZ1Waof+8z9no8nQlgWF7S22g4Vj+TE+TBoaaRrakbeIUurB0zYSxw9jNwqpH/lpFPiJFSRJEoa+b4deHHgTM35F2+n+80UJ211Cyk3zF8Kh6CvC4iDwFaxrkq2+YjvdaY2rPFspa4S0aZQlSfsk4xUrLywscrOGlbW4jBt6hmX9dbP+khbrrzEM4OaMQ8Zfc9bMv0Izu1iEpDNcgvYzHE+2W//J/o6HIOBIIWK91cvyhuXncyUKZxyrFHyJ32f7rGR1aPdcjQMkqggjxA4gMD376JG62BcQ61tkfaeZw+qPq/lfbTW/w5yvaH4HPap1luDh6Uby9QqmTJ0UXPWwUuXcNEaoUJKxFqE6dc3TE9jOCY1jAGS1ZrbfK1nbFX46/zLnQ8yW85dXzdcspFvgLIRNH67urwUuORUtbPHIqYDJvEN3fxSAMjwrNBybxmkQWKnvBbHpW77lGjQK7TheWKbl2YvESGPDiI3YWThRmlIaGIGZGm6QqAB1PgAvMYwq/u4RvES2SYSeoNX4kLekb7cRMBINteeZSFgrF0x1HWCxo4NxIpFiZdQqspk8K/vHysIS2HCsf6W3iL5z6D7XyK+1Bd4wV9ThWa3tFQSJueAp24gJoStLQHiyBUwDIb/9xuNeImsIs3J+ouZgyLm9Y/8k2jRNuVJKg7kra8aRSZ8zcaGfp1eZmW+zusjqmnNPRVrXADeqEHeCoZClHQ9LHCR5CBtVzAEDWKyBd1g1AyZlKCq5bcftXlmFrfsWwYcqEDI8lEkSTw+d3NYtC4WQlix66CQPFyG+BSwVGabqBRRQWCjkFKN0XCbLBR0XWCUJWJNQFhLIyQyWpBdwnCgSDj5J/KudBgRQAA0xPQAYErgrrAt+twDX8zO8rBNm3iHON0mqLSp6BftnI0HrqmyyhdirgZpI84U2TC3mL6I18MooGpnAXXIx9TxKGzpnhWFvh+Wdt2sOO3giUTh0vc7bphSwzAjHIqx2qKTIGm2J4pKYVkmtpSsa5WnSJ0myGlY2QuC9yQDsN6lGV4m2rqYTSoQmwyp4aoPKV+xNGMngRi3qeJkWlBm1eFUumhtapa8YRn5WJj1wt7DM+iTwCdtpGKLpcYqEffoLOm1jTnrQ6JDWiYL5ekKoz65xdNk1ZXnL9j8RDGw/WTfAvcI6Txku0uq4StMVcrLubIwNO5pbxk7irAuy2Iio2KNIgB2GX2CH3dkOTrqXgRXZSmPoAghZ/rvIGsZXD/iCJ0uzw4nyERuTk42P2G4iLapSeq3dANFLFjYXrq2A+6d5d5Xro+M6drTFiXMh/hW3yJhkcy9EeDya+l2PpnrUrQiykNUoxc3CGjFp3VRpg/J5hMp1Va7bNO2k0jYrkIdkgYZKcHMWaQRt1mKoVhlU1p0YJoFigAGGJPWYmuxStcfFoySz82GIIMqkC4tyf4vbTpoo9ZZMX4icy4U0Vbm6kkYFSW7Zxo81MYjdkHVY6Wz2xS5MuqOHY5J6dj4K6jvNmjnq9gWSUIQ9NWmxqiJLAoO5iWA7a7sNlKsp2/KZkDr2114RRyOK1jj7U4+YfSGALVq2Hy0d3SFaWNMkgT6cEmt9S4yvOe59gvJRUUkHck1Z5k22XgPj0r9qNYmaFf5pdaGSjP+n3JBiUzckShnnBflQNg+oa0lXQJ7IJACp0wbZnlpA9KpclHle3oz5jY8gTVAbuolgy8uYegeTLZAbCtD2/hKGi59+uPzx5R+FH0yi1HRdmvgpcH2RGxuRm7qOaUZOmvh2HFihnbimY6e2bSyMKIn9xKG+ufBd07WoZ892Sb33lG5/Ors1rIVc2ZNx/w2gFgqbsDhJDgPcjnw31g3T13TThocR+mKDsftdJsbIgLAdxtJN02bR8PG0LQxLnjInNkUFQFqgAqBYYnMqwEAqwN1awJX4sUF02/GgQsPSvQBrgk039HNsqWEj4eB6FBoc4J/Y9wzDhq9cx04ZhodlGJCd/znij+Xy8Y/vrhCCMZjLx5xL2PJDj39jkSam9/CvS6/xotRQB0M9Vjm++Wo1rHEato53AGkX16GKyoPJOqlBL9+o48JG6u0Lm7gUBojTGfDoR8pwoWT4C2E0HJgZO8z7YULFCtdkZfCRgonTnQCIHPu5hWF0GA9NtDxsou3lUmufSkSGTXys/M0kmUHIS5nBOum2KKEoJyF4Fcn3yJz93AB20ZigjJAd+H9PpI9iFkLlU9lPENOzRnLO+kGEbvqBZ3m+ZfwLYfWAJot0EfteEBrUiOzISNLYT2NqwV8cLyDQTTzPts0oMtw4Dt3Qju1oYflJahtO9EhY/ZPA6wxWWhqOEa9RCYBTIBNm7sWEBYQxFBK35fmAF8wAlnwQoNwoMF0C/ApEmIoECRKhoMnyQ64K4wGaCYG3MlHRxgxH0qYA+LrQROORgFEcHzP5HpYQBuOkwKuFue5bGhblXEAjUDRm+86wBYikghzbBwyZaV74um35uucFkkKUyTr5tjCQuXPcCw9SAgYdspOu50CJrDPQDwdZQcuBhxmotaJSmwdbA+SwUGxmBJjK90INWTpPHUxUP8ohFg1tQiTksTWWZVikxsrEKjWskqUKIdQZtxICcRwt5EC3uNNMY9+WA9gNVj36fQWAdCKo/k6o3WPkuowzmgskjMZVpe8D+JY5wusVfQsLoJW1y3jbdGGvJ4gMaoaqK8TyO9RoYJ+zr4UYkgrp4tS+ss+u8n6byqKsrj/5TYVrIqfAaeY1b/Fxh9l9MGmmkUdDz3DtMImShLpW6plJbFpGmga+ZZlWnDihm6QpJDFiYCWoa1PL88w4Muji0XaYT2SXYcIKBJo02WtPQZLVcgenEvgy1IDlKNm0LIpiMmJxspJL8ICw1QM/eCrHGAbLHzD5HrIIphWwrCbhT8zM8r4RaaiaHV80JpLTsPDd1U4KGh1FzRa2EK8r6W1hYzDsPnLeezpua6qY0zRY9ZIWr8HI/R2YHf99DzNyIp9A0gMw8SptbiD/vEiLCNHxFKaXzkMVNB24gcDSHCju0XWE3f26OyCVyH9F602VKklHAzWAZ51tU61I2WnKxJlDJ+NXDpDw38mTFd3uJXfbdQ3j4ZXSlog1MZvjsEDpVjrJwKoIS9X1pVNxwUxDRdpf2siXELn7XAKlbg+3Ti4MCMPkjqA5No0J3QY3YPJsj/I6dR0U7LXXinCm4jJBvd7oEBEmrw8ysdOPs1lm9KWwqDrN07iBjRgVh2p+UnTJUT6DtcukvFnhNshB75IftokP2IFTWqfiq6FX4i2q6CpetuFrGl/jniHKA1Aq7/Cwryb792BZNtfp3dnsisSzoUpZD0xfkZfdFSiuVPYVweM20vdXACmDJ6EGMA2p8QYoI5wc3OX3gVIFDtgFsuXtpQyGPLBV3d4Nrt3ts25uhjfT2oiZcigs//sE9jMENGWldffGOhl1XbQy6n02PIcf6VvjI312wG615/Wjg3/lvF53fNza7PH5O/I3ruYwJsbT8fRJ3jikIkybneUz3mpUl9HfXejKky+TiLq0vrJ7Nh+JtRDAejZj58fn+FRVyeUc4l7Iy7IhdAvUKB5f7zOHz3BDQhWWtmgOX7ip8B3lURAZO2avD0Rlc55rjNFMubjdOI2n4jiI7ULivcjytAYaPd0LHamt6LFSdiBW+pG35jC89CnhoE8a9XA9DrwDC2TTu6AgLrMJ8ayZ3YVraUeb9N9IO06685gWABk0IAEvBrr7lH+guNngF+4CCjiHeLpQQ7Lhpw9AgQ2EHIoqOJTNzvnvg+jC8p2DkASk/6B4Yo1aJ1X6T2h7czC6wMwT2MKaKHQ30mCFECBl4uv90INca48d1gdih59R9a9t4xFJfBgkcZXB4EnA8Ki0iq+bvo9SS9++ADTi27rn+9KZDp4p6YHrbIGoQSGufEPWsVjui7YM+JYTGKga47x9wVhhVSuHc7MDYgMvx1ricqzCiZs2FwtssWhvyOELpvqNC8y2pcoHGHeNwge8wItNWGrmG2dp9oo5xIRCvZ1iA0UfyeBlvC0glzGQaIz4et4o0XnTerjvB2JMZeWhnob0+SD+DN2D0GfoflDs2es8How7paxjDGqPCt6NP6Wke6HPccU9Er06EIl+K9V9RKEfiMUrkJGX1GsfFYmaDElM2yEIJs0QuJNWCITWnnzP30U+8gLP/AHHOM6bcMCltYYFALNNmS8Q5gO4RsHIPAO2GZvyzAASsjPOYAjjDMZzaws9c2yKZ1X8xMoXclMnwG/DBtxveIgPl7ZuBuHQWoNFgrYRwyjEoMRcagFrHyp5OOoISrqMUxqduhvrocdu6bO4wCFdDdtRfSY3HGFaz01nulOmJvUqRB15VNYMVCTfGgOwMB4fWzydCwfqqWw+TEVbU56NAzG9hB5m59LH58pUC3XAQ1G9yDZG845S4G4U36ohHl5Zj9rpgaj9qajziNY/CFpf5/Tu0RH5XvztA3wy6iaEBJa/JdPShgdBjoN0rGmq2tyQHBCwg5I4z2GKVaEcbyCxHVxIxR6KRQQkzs7Fy+eKPYSho8O5bJFvjD9ctch7eGyRjqzSm0s5hCgf79CaHsFEhzLgbZv6th8Rxx6IQ8zV4+KOowmsz8IE1p4MdrtEz9u3B1HmYVz1h2Wq6zTeVFlzdyiebPON8aSnFjmJJ9sk7CAbG05onlaAntYl4Ks70pTX6eqyjukKKyN48tZ/vUPzesRZH4g4X7UtPZJmH0YVdYn+1R9ZVOl4vm7a9kBBHjky4ANzhrhM3QuGrB/KAJ3gjc98obuep+k2noaagYUO0A0bOE8P3vUwwGMTJwyQAAuQpQ9MG5CPwZIHLh6yIIPot8jJBlIwRr7RQQ/rhoP6qYYToo686/OMGsv4zMQqgec1UcMej3VR1ZX4b7Bho+5YJhbneT7rEr4Gbwsd2WygAkOVRTZdaEOudX0fxPqwWdht120XEC+TxXpM09ZwUWobiA0JNWuZnBWlnLYDg2JDKlu3QiRVdX6VA+pzoM+olwrIPHQxgQtjB/kMkQXvJIbBMxMFFy4JeV0owAgglr+Jfg+bihdHfN4X3ukXIY5Ze4/SBBYdf4Ts1kK9KbZHqXtJp7hlT+0Lh24DLZ6Ynbdvk9vA6HY6u2R+CEabI8qkeadFS+M4XeMqS2+beVvsX5dNke+4j/4hdpEM0BI09F3OtOp0vIX4anm7Se0rQI3LTpFn1VRZtIE6apKk6xRQyiq+42lEAI3K5nKzhgJT2GuwcrJOy3WeEpT8bVaI6A9t994n5KJPRxL8sFMtNn2PzbybKlnZogIkefFKlAPEs217PAGT0jn6SOkFKWFB9BZIbKNqvWsPSm2tZZgGu/MED0DEwN37o3TEuGAmNwLVxkevfnPhE58LFR0fryT4o1R8b9N2XeC+D3+10Dk7b98+AufP1TtZRajNKa75brM6i7Ic1uYph8KvD14+rUzvvlXS6lSSzuohjeoy3zSAI9DqITy4RmXFNCpJhaOkGbNzMTStmvweaJSnZDWiZKr9hpalDRkZXeSItt4UBbdhlkt2bu65pCHNmmSIYoxVikozhNk07M1gvj/6+r9OI9gYlmWVvUXyeuo+avigUgtb8w8nK1Bmt7+GzHhlDW7P15VWrnKgCV6U1YRyW2fYRsxtZwppz1kVYMNucnOwwNd9BjmpyjXqwfK8ypdW33D/x0qv2nWpBLY0A8s2VsflIDPOx6iGvr1sB5I0ox9UEu3by7Pu1G799FWBDzDdMDGMhOkCM/w5GuG5ekv2nhlT1A0fe84GU/W7aTMerIU5Pb5cz+x9R3hKReujDfS7aoAdqqg2PaCK/sn7jutYceOjjep76YW8mx7L9ABLB7/vO7zqgelHG9p3Oo89/Lx4ejjFKdj7DuXg7OjjLf4PcTT1DodrO5CDiH7f0R5IoD/GaP+OAu53kNlPD38ruHrf4R9Ibz4asH9k4dChUrAdFIgYyz0nRVyQBM5CYg7OewuaPLQPgMGVLM6iXwGJV4yBAq4W2S1iC9niZJMVKTO23V9YvM13XTscGG3ceWIi6hwYdkQRKRqD5ZICyS4k+8H/T4RV0NayLe2Fla3njnWKRhDRnFgNfGQTb5rBsdBBq4jdJ48Cx3VMx3MSz4hiaqdGkDixayaLgPqRsfDcyLe9IN3nxvns/Oe2ga1RAujH/9A0glURAboEWnrKQhRgPt1a1mlIXTtK0nRhLJwkXixS0w78JIhMx04iJzS9hWOmie1gS33fMalheakfpFESxTGqK2vahDMV2egkNzUpuaDr7LOt0RTmwDBlgWPff7Ye7KK0uUGDm20axbNdB/8n93qgEBAlZC0DgQoXoAzFK0zAUUTwmDEw5BycwBlsgXQWt4SMRTL9IPugmfL8ICDoRo1r3RjdZGgksGMZy4ogI/kpmYLBSwO8ffvJTs3HUtJ/4yk2PE0DT7uee73ZTdS5N6eu6iuK/IPChRhXD2yPGea0CH9iFR7xl05XPpSy1VClf3AVIOyuAghN0Id7+sLWx7YLdmn632tzpUW8E46PpH1W2E/mNi3u8f7T7ZQxrdB1kupVphM6Ta27V2xxvGjXxnSUVpRJL5AT9uHHqYRZ3skmSsnYmpmdD9bMHBfMoI+qUXCpCGYK/BshGlUNgvMjpvLqKk/RJsU0FujXo7jW/4yZGudSNWWdLijb43tPY0eD4R/CYLhqK3xoED9brTfoUKvY3PJR5LwcjB6NJGMK3GbnqZT6ixq2zvRHDCBTaRMlMScJWeoeiJVsUztmXOabgnkZbUFZQ1BGmrjuLJiiMWgOOKfcgGlHx00vOJjrRlpK/Si0R6iIKvA4tmu9zmmPodML+MctMKy4g1Mm+czQSD+rAX7SPJnING4Srx69TLHxgBUxkY2tHcBEjMBbaMyeRDssE8m7oykoDXf1heAvkv2yo51t5vlgOgqXWZ42aF94sdjVWIEAvmfDoWKjiSzMmMiyzAE4987D8RMOvxwpgcA9jrlktMngiSMieMexme2GhnaKFDcVuxEqY+BURNrVJDBiy3q1BxTfiG6rS/e9Kjp/zfaAYXmTA9Tlgg0jzWdkGmb4AAlYf3jy7lnfE84N8Wsm+U1Wlys2ho9ZWak0I+89p8XGlCafzXqwuJHdnUjcIsIB1PGszF484Di5JV+wqO94zCAXorp2ltVMEPNKRAzysOXHBl3NwcK/z6q6uYC4F8yKkJJVbfI/NykwgsCbr8sMt8V9+DOobzAkykdMYV41RrRvjVPTs00/tC3b0y3HCx1r4ByOnyoJ/IgIEmboP75/m/zw5m4w7NytowCxXanQ9jc3IQfIEMHtl+f5L5H977/S727X/7CWRlTkm3/chTfp//77Ni5elhf/IYXfnA1mdA19RXvh6OqlKDd1ijCoIBNVONB5cFHwgzxBOivve+Cf0uS16tZFKmcHNShcq/DSBfD/9hthb8zVy7NykydoPJvgqVu3yBTXhaIKycfHsPY9erEqX6Bl69e7HdNMtPv8ZdksUf6GJl+X5U0nJJGaojYSefTXP33706nwLUNvxW5OMhSxxlgwsJPA9qUoTWqWqegzs6hEbsrVvzWkjgFn5TohN8u7vzFOfDTWuzuK5TzrhSjyKfC4o6zSnvboWntKbNtY3+6v3ronQOAk43gytxtrYMBVm1mYBgdKJJsQgDF6XDQ3Km+1OnvL7L23zYKwr7kJ/lOypdWfNU1yLbGuMuRv/vJ1R23bVk+m4/sUmY7UNBphXO2l0UJXWaFVZdPC2JM4q2IUGEKhAaS6Yz+wzn0c9qq8ho4InPIMW9qGauWaxlkDyfEKYBfaeuVF7gGnW0sXC3hh7dPqGEVGVxpPOyNz1oBOLcLEG0pok9oi/MmYal97qCV9nSIAJX0xXQPgofe8fZsyoRpByORCH7s33G+No0hKWeCdIbv3sBMJ6GpT5d+tUCCf/D1dCC9Ve4nxpohjJqJWhdwVQHs5MvbJlRjwVBZJKcSa9TM+PaQzOTmFUk+6dfKRb05j04ncdYwdfGs82X6SJuATA7QgrLCThnCi7aNsxh/YIG7V6Nit21C5lGuYEh+KysLA/XpoKNibZJB3eK6SJReM8DPvcVjd6ZOxyeR0JcDZM8zOpg7Cqw7CFNkMq6edSpYdtqZvheNoBIf7vXPI/kSYZFUQJ5JXEu5KeNfGBgkWZdlMSnN4BIzPIWYjO8L7/A0/C8ulzR+1/3ipqnKUvNrPdzsT2Un2M3Zb0oiaJvdXCaEolX1fIn/inOshWh9r/ePS7gdxLY9D6P9xaPtPnHLnQtn9qfZ3p9kfoe3TZP1e7T5SIkdK5EiJfERK5LHZ3yP3e+R+Pz3ud5DucWlvrnHQ0d18696P5p5sJixW+eyrPZqTCDxJo0jx4iuUj05OBpbblxbXy4BtT8MZ7AAMHZomcbUpIokE73GK8Hwqe1SVnJ6S4lazWherqHbMX4tEKobfejE7jRT+zfSGCly28IBQ+VaMgnlbO+4VjCQRfnTyBM+R1UTYKWgyv/HZXaKE/sJG1fltpehrbeDy8V6NqQMuD7AGTXl/7Dy3dK/8R7nFkq5pRRv05Dmfiv/4HZw3VZrKPj7aHuP7RCc/894J8q/tJP/6w3azpXLb7orPP3x/BVXfdpt/vUuvuQPalljMVjTnWPa85Rdk768cmS+t7v2QrQYKT0SPBrx2P+hsuOi62VSp0B1ANTvmB1d29cqmYUKggPl3q+E1Eqb+oWRuloAf7rpF+z2n1SDsNp6irJAQ4TceROxeV+l+j8uckkbixOW8odYS3jYkoisfS0fpM7i2ieP4zpcve//Fe9yllPLlWS9n5Kgm4eqyqwmJBx3VwTXgR4q5Ea1T7hqZL4f9nYb1+tb7OgzjjJhSyPc/vvz28vsfL767/OaX169/enmwe7DP2OtX6KaG5wRWmFiG4dAEXQf70WLhpyb1k9Q3o8A30thKDTeMncC1o9iPF2GS2L6fBka7uKatSCBCZSjsK9L6aTtlPtnaayWzIf6TNyohenkn9D3Fjg5lDkwGft7j2SmLAaOc3Fd6gazIpPP0CenR+evesbh6CWLiAkR7I0XR9dyJ4EdLTmgojYr4dVOsm5Kxg21mLckowM+kpul+t6/Zakavgqj73yL2+4fvnrU/ecXm3knDbLsm7RGm7WInQPT2HxADC8Wuhyd2OH6ArrOkF/xwJDqZdTTPsNjWUUmrBGZpfXcIyKgKwpiZ86xbmm+gEXvJZw+FkXhT1TDm7HCkW5yjYe0ag4OqdvDhwf2wI9RecOmG6T2w+qPevflAU/Eh1qw6kg+urYNAgN8bG90T41Ht3bCREhXQj2TXcWl3QaiINLu/3DMqQFwPIN8KgvzXDo4mRK73X0/i8qBdwppBq3JtZSLbgc+GPSN4qrqalJ+NSPet2vuG19la2WTFLgy7cozjzXcEpCNGUUDewG7M/CDOX6V09ZJW6WrexYv0QEZ3BFyfl91hY6FysiQDWpPd+D6bYQpG5J2ihKzb/LuKAD0CJ9AJcLeUuTZnPxpvLjNI1q5OHlPrfJViAsH2sgW7mXu+AUvN/lt95gRf0mL99fbM6RgHy+k5DnynObz8z74pZH4uuaHsIbf7liZbnHHsmnOcTlsWBKrzKN9pfPCUZEixU2ULRAEfucB5aEXss11A8lmAQ/cqc8eT6wf6es3QoLioM8Y8dEx8iotwTxPUsgB8Sxj+xesCkLa6JgX61Ks363VZNeTPX4SuGfylP1RiwyzRGPuTsvxQf25HthMHoW/YtpFGnr0AstuKkySwqOs4NLW8hW+GHuCnPVvIBC8dRHC3XDjIiLb6LzEdaVUJQulsxg/mGdHFDtDZGIrDVGCHw9AzAye0WnYA0Xwmbtuy/K8xA54XAdRt8cCFp2Pg8257KTd8Msch3wGwaMjjUhjr2AG4e5oDYbXIUP0+x+NdgbNzBjBiTj5f2PuLggankaG0D+9an7lmDXHXyNzkPdYi9zdFeWAH56jfvFn/LcmqlOs2jS1KTtuq3D0cg4MzGcOzrUDeKLo9f7xtKNeTU+C1p3ib1sTiwL+2QuD1gkvFEKGS+vwCLQs0hA/fpHk9OsFusdeiXJXkd4TJfjWdizwjGOUjgMO5BgIZBgF696JMskWGflyftAJPZi0Ai0vxBSgBwzI1I9RM57XpnVrBqWH+owOHbqRfpWtiOl8RTI2nklJZ51NLQ7InON6MDh6n+gDJ0UyBu9Yap0IhYGhnavDTs7C3zPA65N1+ujC659hoE9mxL9CPG3rqVn1XMNfshvMG9VjQlKZpB7ppWjwFWpu3oQhPcWph+j5zEndh6b6J/n0s01Wca2jQSdTAcdxANd/rhDazNBzohmGKJ/fK5mmu7gXO2xc+JncGnnfQILylh6GVW7phBXpgqpdWdZe5/LEhAdp41kzZgbsvnBWptuNNXtEB1gF3mi+V0/ODmed8ioYHNdO4kZABWa0c3kvn8e3bgCFjLr93Utr9QbywGRmVTVMWLXply5e0920OMdMw+NbYHp0vNaEyJKPmdl0BuU4bLU8XDXJy9qw7zVFMZFxG5W3PQQ5sPKikvMw6fvR1yY3P7LUsXWaAwKUWsYSdAofwd7RRAKvAIugygdsp4GvK1YOQWax10FOMa/jiiaBs6wYz7G0Ejnrl2wlgIdu4RHGlOWK9oWaNHo6sHThoK92yvafj4k0D7dwazOB6yB/cVDf3QmbKC1ZYXZDPc0xmLNzSA6dvH/N9Y3q651tb3XYVE7q660NCOzbRPnkQIobyLCgMrUO4zEOXPXR57dgOlOdAg6CL7MGqgPRouBya+hTQIBHeHu9Z9e0KtsZrt3uRgbVbp9KxlLyWhNC5z9tLYnDt9qtpLJZh6loLmuC5K1nABtcfr8oCu1+QFyU3y5Is6TbtG8e5F3bRDYX9feYD6Ma+n0wb76Btdg1UALBO00RfL0hSqTT1wjHDbs2qMyAtv3Ncwoz6DiX6Y4sQreSfX1THIDEvn9zm/q9jK0LZ/ybuni9tGQymhBh7Qr5S0dLezZTcy9B86sqdxR1K1MRW355VHzU+99T43M3N9qbZZIwhaUe28CFRiu0apqrgVpY1HSxs/AaoN/qhBY0fhOP6G4DksqzOWA96wm4s12ZKRe8h3DaAjAicQ4TbvEkAD6Q7qn7USfvdJMQfdOIk8fOHm7z3PJngE3giSRYk7C4difFjsf5kbHBXgSZFqjVlrIvWzWSCjakYjzTEWFnDAzP2NT40W9/BIK257twugVeRAMa+mTgna08uuUxRpqZ6kdPCkxg+5gZB0pu2BnrTpjSf7+znBu87sQt0Bwlhdru6sUI9IP/rm50crxg4GEkTH6juPVQ2n9YSF70H5v0KxrfNNbR1CIRfCmn4b5HMJuwsftOsfoC9ppcvHqRzWdGbg8hoZhCH3iBamb2L/iF2BjU221b3HmTg/7fCoL5Q7KDqcmk7PjhjlcpuqdemLPMmW68Z5dW+aqsb7r+jKJnvjjyta7zN26DxqcF1WS77E3FQ+E22Ssqb+quCxoOU6klEktbXTbnW+xOJQXKZF/hpDRPckYUEuKofsub5JiLf8lKm2jSt51ViSXh3lIgGTEiCPjorkaTbLEbtJ7mFe/i6BJ55l4tJ/zAPk8JX5MiDJXMMjsYan0/4wRTuy33uWdPsPWtawrOmtUQ2w481HT2nGQ6K/zAu8NHvm8VsSPquL/uJdHTuktNTvQLproceySzHhsJ8yK4HAQaYoQ1P3/R4eRorj9c64dDTaHs66dDTfPsCWCgzQB/lwCWh5B6ZJPbgY+V7fMxt9B43FYtSjPvZJ/UomNm5+bfZ/0XjNOwd7+vdwiaXohb6bbE+ZzFzrgfEbhZVxfkTfHYnpcJvGK5U0t5w38vifYqm2g7CcPyMC035VTXuwr+8/l4DrqRIgfhImEnZZnb+/wEAAP//AKMAXP88aW5wdXQgdHlwZT0iaGlkZGVuIiBkYXRhLWNzcmY9InRydWUiIG5hbWU9ImF1dGhlbnRpY2l0eV90b2tlbiIgdmFsdWU9ImppZWg3V01GRlVIUjUrSFlGN1Q5bS9yVkhFaXk2N1hzV3d4WG9QWGFndlhkNHl1NUw2TnluSU5hd0FpRzNnZGJnZEVuSzlDSEJML2R2dk9ZcERFUllnPT0iIC8+7BvLkty28a6vgHlwLgKHAN/S7rpsuWI5JVdUjqOqnBQQBIf08hWSs7ujlKvyG/m9fEm6AXKG5MxKK9uxveWsRkOw0QAajUY/gJ4nZPZ3keyGoamJLEXfX1rJUNNGDoUE0KxMU1FvVUeGpimHom1VOivS+tYiw75Vl1a/S6pisJ6QxZ/oCkFLkajy0vpbsyPVrh9IokhfbGvoqaihM1KJa0WajrRd0za9IjLHIXuLpGIQNC16kZSK3hZDfrXq/qK/2Zox8iJNVX1pDd1OWSRXxTYfLi0WWOSmULdfNHeXlkMcwgKiYarriwbQmc0sclukQ26Q9YjYgsqmapta1cPU58imiUUTe4ZO9LllCLtoxZCTrChL2u1KYIq6UXWTptDvpfVNYPuE2aEvbO7DB6hxGDwp/M+5vQbD542bU/cNNnlXedDYAQAHuAAIfDQmTMn2X3Ld7QxKEerrti98YoeRR3z97ZBAI+GIL5hjc+AI9MEMDjz1cN94thcHgBrM+9XdejG3mV/aga7+HNF1GxzX8W3PgVc/920WSzvGRnbgc2oHke7aY0hZGGB7Cu1nRI+dU5t5cUnH/udMAUZ5sc2BWxS7P6miWKdbYs/vrKuLDa7H1ZOLDYjJUnQuNkb4sTJruurKANPiBvHGgllTKE5rn9KsVHcEZE3RcmuedyVBIG26VHWUm5dt19xSx5rGvEjVIIqyP/QDgp42tzUZ4bRTvRoObw2IZyn2JKVFXRa1oknZyGvrOIOLfldVotuf2brWuB1E3zbtrp2Ed74PYY/1BewoIqBJU/fWnDW/gR11rRKR0LzpindNPYhysblwI0UkFkxvJiOVDnXJ8d1xHOK++wZfPoxWMZc4H8S6V5Tg1azEKCwativX60wrVe/I4o32oDn7YY9aQnPrGWGh395ZKzkti7XOA6A4GaAYVHWviibfo4RVIFZ0V5eq72lbigHFfq2u9Z9esQmjR/JqGKN/Wgl5Fj/vVHZp5cPQ9s82m1T110PT2ltQ17vEhkW/f5CtoLIs5PWl9a0CqSyGpts/JU2raoLanox9WacsIOTPB6wvDdYpmzZivemX3Dxh7j2MtcYpbl7vv2s6mb/CrVAX9XbT7gcE0PIA6cTtphL9oLpN2sh+0ze7TqrN234QIN2bohJg2TZls23stt6ezOxLGLNsRLqiezERM42ZDO7Kg6LZjDpk3DJGj03qbER6Mio1nBqaXNiO6m44bEfY2TRp0j1pqUNA8yT0thMgSZ1eM23tKTYgZFv0A6BqxYfK7qDtjhoTEamEPQ/NW+quVE0rjt6H6QaHgt1WVFvSd/JhLEcaP4Lnn8ESXY5asQRddFwL2M1A0YyXZ6zBkZVPnpxR7O/X58t38Ky6a4sUoNK+31Xt0FCt7CestBBA2qT8Jo2v903eDNdqD6QvFfufoBf0p7AbPZtJN5lVWfQ7W22CKz4zOtrGaTMmm3JX1UTURUUzAcYO/LUMGK1HQJrfN/7Exk8oJX+w/k4ovdLliw0KheiUABLvqvZK12yaFo3R1WiOL/B7ohHUFw5GJw7puoOYGoKt0ZpdWliSqgWxy0UHy3Bp/fW7P9LIIpUa8gaYvVXD3JYWdbsbpqGwa5h2PXTNaNfFDj3UDqzCNGlqWpxQVagynbxhs6NAiUqVNyVI9pI7n+aqLIv2+Xv4R3DgrJG7fkbr6LFrIZAl+MrjauKsF274A8wuOA2aDfD5qtHyfJ9TtBCccUdMaubJUbs8OVE4pn0linq5j+b1o06aK42saVBf4DJAU9AK4GNpG0mzXVmCHoHtBVPqixuYDSwUTBtxYYpFnTV6UvPutGUB0YBmYNSg0ULG0VPrFLovygDKLcJMGdXRAVo3x9fvIY4psv2k2iaUCZyo4VaBfQIpDEibgFtYYSkLYFJl02n9SXsFVKe4qUflB0ZsrB8BR4xJDRx9ixKVr3YggLB6OSdNJ/REGdcPoMz/KLorTXKCVTMvtiymwfV+gC9T/6ls2v1zwh3OyFfF8HKXPCVf19Je2tt7m4PVXbkQoMRn/sNG1RvztoGFVLRtwGfYjyBgJTgptMmAWd1NIdUo+vkeTPjkXfzz03/smuE5RmPDW9wn5v2ZeYhalHswFL2tEQzwqXm0Yo/meMQf+5FiUFtwVRa9/FEL7aKx0UgLrG2DG1zTvEDVGmCBiSLy7BQRvOJtUYMY19u3u27ZZOLfjHU/v/lcULMDpr8tJvbUsDt/+OF0AWgOruOlFfoR5zKVcZQGUZSxyGWBFEmaqYSFmefzIBOSqyDO0ohlQcjiLOFJJpXyoyyRICnfITvQG/pFJKvtihsh9xT5oMBnHx6DaI1EP0S4zqE+YvEKROhzJ+FB6Huhk/mey91IAshx/CiM3CAMlB8mjgyzVPnSzxwpZZQpT7ppkvHIunptGPJxAvbbFwmwIruuGB4kE2dxH7FQxF7AA5UyNxCBZEw5QigZpnGUecJT0kmjOAlk7LluKDI/kEkcezFPfHB2vZAlmbVSILNpTqyyrv4yln6KZrq9vR0VE7Jg1+shHoPCMeQ+SLZOMR+xZHmoYUTsuyJynTR0My9RYeqrlDMRcinTwE+YH4OVc0PHYzII0lTFXgTi5nMuMhAbzY+zQnNQLMcTErMmT4nhOsQP7VOi2folzHItpis7Z10h0nIkc2xgimIRhrxsKtUClyCkKAZ0ro1PZy3d88NdQTr5nyDZ+qCSVNqn9O7fO0eX8gOnjdz7iNNGRP7400aIkK+pIe1Bp/gRcV64th/h4SAZC5HURY9wm8cksH2X+LYXktD2Y9uzHXj61Gbm4UILeImpjefWEddPPPqmHEq2G8LTd6kBBIAWe4CCp9sIjMw7NIOpA4ADgPl4vk5t3/QFre3AxSJhthMhiQwaQxs75FhEeBTCB+h1ATcACrG17gwqfKQHQMwOAcApoOCQsSm4duzjDADR1af5MVLDNRWOxrfdADvg+ApwrlllBzAGDA2jICXYI0PCXMDnQAl+wyz0jYELFMWEE6xgyAzoTTMXJz49bc+DSvgHImHHOBjBwYB62w8Qj2GPGp/5eq30lGDWrh36OBef4FxgyfAmwffsUK8hLIWhApmieRgbZsK4OA8OQ8Lqwir7sJafR1DlEvON58VADogD9WwPGedHVP87PUI+HOr9qjHdx5iofte2TTfM9Mpng9j2l2kzQJkatfAYTJY+WJDDQ2zWOdRHbLSCIA64EE7mBDyOIQDjXLHUZ1JmUrh+mkTMz7xUqszxndB1Qh6xwIviMHMj0M8gFS8MQ8Y4/6d4PDMWQSQigQ+PQXZeG1IfIjvnUB+x7KRpoOKMxZ6MnFhlSZh4keckfhbziIcyZCLIMplFWej7fpy5iYd+kAtOt2SArXR8hQxZC82PDt8fg7yItniIrKzRHrGcMB4zEAvhoHJxuAxY4idxIF0vCzJ0l73M8yIQHRCZ2EtcnoZSepkKYs4j5XPr6vPXX/94GRmPAR+bnAydKOoHKpazuI9YYjIwMWmasczxZOoyx3EC1xfCcwLBElemCkIn7sRRKhiLmQyl56gQtApYM6ZcJqyr70aO/Az2KMFLt0cgMEjnQ4TlBO8RC0oUJk6QoPGJHZ75LPOkG2Wp57oQZTMGUsOyMEiV8DiE48J1Yskc7qTckwEPeWxdfQFE/LiIWyTNbhhDbl1+z9GQqb/6HB/nou755fJpetE5p75NaHC4Cp5dmy9vlABzKES3n19lr3MADrf/EMyK78UdVV0H7SvV9zrmHzs+rQK6RJ+bb1NjERO1T3T96plDogQGPCyKtznXaWveLI0MglqIKZ1XDMJuCE1dCLD5vBraQGga5RBkgle8qsGoN3p17PddhfEzxuGhE0oINmOKUTiGsR4PxzLEpFFAnBLiXQxYMfqPlil1jHmmzn8JgSiLxLIW42/PxROD0H8V2J6PAbPjhe++iaE7AdGrzu6jGLmaFwfjcSAOMw/9ZTKhzhokzo1ORjxCDRDHOJ8INd1Kmwto83I8LNLiou+q8cZ8JlZp0VdF3y+vwL80QGLk66HHRL+AaN09SKxcPGHBr3kyJiwIrPGriAR27JUuygp+zfIqDQp+vYqxFGksssaiR7SIIGJJz+A5Bk9/vcIhSaRJIp4dRuskUeeePMljKgC+YeauFPV//vXvgbSq0/kXQy6GMeeCQGnIi54MRaXs5U3/TL3B8oNBKBXtQaHgffxMoYxq5VZ09VhMRF2rg4rRZPxfz/y+9czc8J0XJmryymlRH2wTyu4x21wnDIq6GXJMaRcJpp2b/EabHDxC6+pbhR4bmm40/QDsFMjoHtwaMo5nz5PFPoIy4zickAZgJO9npuzgZUARk/1KcDq14de33WcSzX56Phspc+g4E7tymHsmbVfo9LX87m3XQ0CGOZ6rzDaTyDOZj7lReKGtx0Tm/zKvzZA3dTW+VU16SEI++dWETkOb/XKiwqTJ2fsh+UgkPYwK/O9QTQHW0LQQh6ys5r3TPs35mqeD/+oa8XdmHtcGcmnogCkNxHe0bwu0YaTa0wCdn6WkTtU4wHGbvj/bDnMnzTY+8+uE102Lwo4j5ViQokvpmBh3KoeH7HPww6DH/TOCVw7PURNh/uH4aqEqAi2kUHJOMuvGEQ8Rwuqd0qQBHlW0VNlwWlmKDtrgRjWKos8FaDsDXuXGu4HT3j23zsQzZ3RbjXnvA0ZxbdIYHrR7Ou6w9RTeqa6ZY44phx/ct+NyLQdZbl8AHPbLiwnvC6M9QEXgOq2aV2A/Wz3EMXu/AbGB5ZkUAE4lUypNBIaoMEah0k/GyrEZiE6nxmTY29+SD93iEcFq2lCCGY2qkz9Ii+gdyhb+hCnfuNIhtgeejOsGBgSf3Fu6GXj5h8jUXXeB/3MK6O8qBLtvwBXJ3TdeTl1wZLgdhR54bkG4bod3sTxeOVSO1nT+TWz7SFUcBOYXVgcc/Moj2181Yx56hBzog5ZLL0xfunroPDF//qMsJIFoEuY/IWM8sDkLkYDVT6WwnNPoFE4P4646wpvb8F5V+OtLlsyVvF5JFoKMYM1zbndSgv6ZkhYeKnKwImEEFmJpSPQlMViZEFkVrn6YR0e7xtEExTaP1jZvtECwgGj3QDo4Dc6axvsitKXyWOjGmZUY87IxVR5r8qHCtI//AgAA//8BAAD//7J6XNJVEQIA){height=\"60px\" width=\"240px\"}"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 10: Autoregressive Image Modeling\n", " :card_description: In this tutorial, we implement an autoregressive likelihood model for the task of image modeling. Autoregressive models are naturally strong generative models that constitute...\n", " :tags: Image,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/10-autoregressive-image-modeling.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab_type,colab,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 73.044444, "end_time": "2021-09-16T12:43:30.306539", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/10-autoregressive-image-modeling/Autoregressive_Image_Modeling.ipynb", "output_path": ".notebooks/course_UvA-DL/10-autoregressive-image-modeling.ipynb", "parameters": {}, "start_time": "2021-09-16T12:42:17.262095", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"01769afc187444bfaf2aec03b4f08496": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0640c19d24424bdd9409f8216335ba18": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0bb5668432394fb5befb04fa0f5d4605": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1006aae5c5444b3583ff98233ccb55d2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "14f0fe5dfbfb47b5bdb2c070ef6a31ee": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1c756dfde210415c809395bc6f9bc1f8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1f6f6bfc18304ecab2062b2bf6933f8f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1fa667b5112343df9c5e37e6d9ca6b1f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0bb5668432394fb5befb04fa0f5d4605", "max": 28.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_5dbc98f3633e4245acdc824506aebf18", "value": 28.0}}, "1ffdceee837a4a4d9b92dc9983c6feb7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "21e083e4f6784332a4e4be80c9719372": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "288775f9620a426f835527c2a20267f5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "2990971999c9429baf82e5715967d2e7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3542c1b421d74a9c9673ec4a04fceba9", "placeholder": "\u200b", "style": "IPY_MODEL_f3ca5bf492e2434080c0eb2ee7d333f0", "value": " 28/28 [00:02<00:00, 8.81it/s]"}}, "353657f372f84344b42e3792af9f53b5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3542c1b421d74a9c9673ec4a04fceba9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3e194c3fa88d4f7796e12faf5a612eaa": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "443488f69eb4484d8d014daa1df90910": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_01769afc187444bfaf2aec03b4f08496", "placeholder": "\u200b", "style": "IPY_MODEL_3e194c3fa88d4f7796e12faf5a612eaa", "value": " 28/28 [00:04<00:00, 5.26it/s]"}}, "4588a9d7ce4c4420b31d66495762c51d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "47dbee09f7744686a6f2811eabac4a47": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4a41e2c8c24f4501ad3f27256fe2dd00": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_afe9d9293fc54046b1a23a652de9156c", "max": 28.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_e5e0bffb840a4e6bb1718c6edc3478c0", "value": 28.0}}, "580c4ca4d0104b1fb3d44477534e266c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5df1358fcace453c90d53fc8bf0c6852", "placeholder": "\u200b", "style": "IPY_MODEL_14f0fe5dfbfb47b5bdb2c070ef6a31ee", "value": "100%"}}, "58c7fa2a2b544e4995676502aab755f1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5dbc98f3633e4245acdc824506aebf18": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "5df1358fcace453c90d53fc8bf0c6852": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5ec4fe6328c247b489d2701b79eb8048": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_f06e46b2cceb47d6bce7e6fb0deece49", "IPY_MODEL_4a41e2c8c24f4501ad3f27256fe2dd00", "IPY_MODEL_7b6d55f1c3e94df5a3c638a6963f180f"], "layout": "IPY_MODEL_e0e996dd55e54974abbba367a08b200d"}}, "609d34d968c74b98bfde90b5c8853bb2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1c756dfde210415c809395bc6f9bc1f8", "placeholder": "\u200b", "style": "IPY_MODEL_47dbee09f7744686a6f2811eabac4a47", "value": "100%"}}, "6678217ddd654f39bdbc8fba2cd3788f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "6ddeeb369e0b46dd9e9a13c20c943fe4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "71addce685174d8c9ed888c0e5e10e93": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7b6d55f1c3e94df5a3c638a6963f180f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0640c19d24424bdd9409f8216335ba18", "placeholder": "\u200b", "style": "IPY_MODEL_4588a9d7ce4c4420b31d66495762c51d", "value": " 28/28 [00:02<00:00, 8.84it/s]"}}, "8961cfdd02c24f6dbc1c42cf6db817fd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_d80e197eb464480d9f8d2b14d7d47169", "IPY_MODEL_1fa667b5112343df9c5e37e6d9ca6b1f", "IPY_MODEL_443488f69eb4484d8d014daa1df90910"], "layout": "IPY_MODEL_aa387ed113584652801c05449d0576f4"}}, "9162e93ab9e348f48b2f46cce2c72155": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a04db2d75a3f476487be88f18d66ddb1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_609d34d968c74b98bfde90b5c8853bb2", "IPY_MODEL_a70e1a44ecb440e8a79c4735752761c2", "IPY_MODEL_a8f4af858d4d457b8d2c2c59506c257c"], "layout": "IPY_MODEL_c5d74f5e252d446b838c3a099f2534f4"}}, "a70e1a44ecb440e8a79c4735752761c2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b91e999fdf704ceeb051bf05e4d0728f", "max": 64.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_f63d7ff436734e8dbe1e7ea69988a2e1", "value": 64.0}}, "a8f4af858d4d457b8d2c2c59506c257c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_353657f372f84344b42e3792af9f53b5", "placeholder": "\u200b", "style": "IPY_MODEL_c142ac0ce8e6411ea84868b82e3112d3", "value": " 64/64 [00:38<00:00, 1.02s/it]"}}, "aa387ed113584652801c05449d0576f4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "aa4ad04083b642e4b289e7350fbef888": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1006aae5c5444b3583ff98233ccb55d2", "max": 28.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_d320a03105ae4a43a4d3ee1ec4c2cf75", "value": 28.0}}, "ad3bbb05cc344a59a95a7a6ca63461f5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "afe9d9293fc54046b1a23a652de9156c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b91e999fdf704ceeb051bf05e4d0728f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ba1f0b2b45fd4c3ca18dfe2954a6747f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1f6f6bfc18304ecab2062b2bf6933f8f", "placeholder": "\u200b", "style": "IPY_MODEL_6678217ddd654f39bdbc8fba2cd3788f", "value": "100%"}}, "c142ac0ce8e6411ea84868b82e3112d3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c1e3f5021d764025a1e5dfc1a7acb318": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c5d74f5e252d446b838c3a099f2534f4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d320a03105ae4a43a4d3ee1ec4c2cf75": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "d80e197eb464480d9f8d2b14d7d47169": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6ddeeb369e0b46dd9e9a13c20c943fe4", "placeholder": "\u200b", "style": "IPY_MODEL_71addce685174d8c9ed888c0e5e10e93", "value": "100%"}}, "e0e996dd55e54974abbba367a08b200d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e1cd7fe63e8646ccb242422e29b75572": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e5e0bffb840a4e6bb1718c6edc3478c0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "e76d7eb2829b4c6c9b26ff6703e0787d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_ba1f0b2b45fd4c3ca18dfe2954a6747f", "IPY_MODEL_ebd8b932d6ce4c76b14940b50d2d2569", "IPY_MODEL_2990971999c9429baf82e5715967d2e7"], "layout": "IPY_MODEL_1ffdceee837a4a4d9b92dc9983c6feb7"}}, "e973aa8423374ae9895e0f38003d662f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ad3bbb05cc344a59a95a7a6ca63461f5", "placeholder": "\u200b", "style": "IPY_MODEL_9162e93ab9e348f48b2f46cce2c72155", "value": " 28/28 [00:02<00:00, 8.85it/s]"}}, "ebd8b932d6ce4c76b14940b50d2d2569": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e1cd7fe63e8646ccb242422e29b75572", "max": 28.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_288775f9620a426f835527c2a20267f5", "value": 28.0}}, "f06e46b2cceb47d6bce7e6fb0deece49": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_58c7fa2a2b544e4995676502aab755f1", "placeholder": "\u200b", "style": "IPY_MODEL_c1e3f5021d764025a1e5dfc1a7acb318", "value": "100%"}}, "f3ca5bf492e2434080c0eb2ee7d333f0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f63d7ff436734e8dbe1e7ea69988a2e1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "ff6c8e20790c444e8f7c56fe0cf6f5bd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_580c4ca4d0104b1fb3d44477534e266c", "IPY_MODEL_aa4ad04083b642e4b289e7350fbef888", "IPY_MODEL_e973aa8423374ae9895e0f38003d662f"], "layout": "IPY_MODEL_21e083e4f6784332a4e4be80c9719372"}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/11-vision-transformer.ipynb b/source/notebooks/course_UvA-DL/11-vision-transformer.ipynb deleted file mode 100644 index ee96f08..0000000 --- a/source/notebooks/course_UvA-DL/11-vision-transformer.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "aa14bc73", "metadata": {"papermill": {"duration": 0.013536, "end_time": "2021-10-10T16:36:01.853136", "exception": false, "start_time": "2021-10-10T16:36:01.839600", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 11: Vision Transformers\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-10-10T18:35:49.064490\n", "\n", "In this tutorial, we will take a closer look at a recent new trend: Transformers for Computer Vision.\n", "Since [Alexey Dosovitskiy et al.](https://openreview.net/pdf?id=YicbFdNTTy) successfully applied a Transformer on a variety of image recognition benchmarks, there have been an incredible amount of follow-up works showing that CNNs might not be optimal architecture for Computer Vision anymore.\n", "But how do Vision Transformers work exactly, and what benefits and drawbacks do they offer in contrast to CNNs?\n", "We will answer these questions by implementing a Vision Transformer ourselves, and train it on the popular, small dataset CIFAR10.\n", "We will compare these results to popular convolutional architectures such as Inception, ResNet and DenseNet.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/11-vision-transformer.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "9f2931ec", "metadata": {"papermill": {"duration": 0.011652, "end_time": "2021-10-10T16:36:01.876879", "exception": false, "start_time": "2021-10-10T16:36:01.865227", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "1d59c918", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-10-10T16:36:01.905223Z", "iopub.status.busy": "2021-10-10T16:36:01.904752Z", "iopub.status.idle": "2021-10-10T16:36:01.907322Z", "shell.execute_reply": "2021-10-10T16:36:01.906861Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.018985, "end_time": "2021-10-10T16:36:01.907434", "exception": false, "start_time": "2021-10-10T16:36:01.888449", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"torchmetrics>=0.3\" \"matplotlib\" \"torch>=1.6, <1.9\" \"pytorch-lightning>=1.3\" \"torchvision\" \"seaborn\""]}, {"cell_type": "markdown", "id": "cef420ac", "metadata": {"papermill": {"duration": 0.0116, "end_time": "2021-10-10T16:36:01.931606", "exception": false, "start_time": "2021-10-10T16:36:01.920006", "status": "completed"}, "tags": []}, "source": ["
\n", "Let's start with importing our standard set of libraries."]}, {"cell_type": "code", "execution_count": 2, "id": "d1953eb9", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:01.963459Z", "iopub.status.busy": "2021-10-10T16:36:01.962980Z", "iopub.status.idle": "2021-10-10T16:36:03.909605Z", "shell.execute_reply": "2021-10-10T16:36:03.909182Z"}, "papermill": {"duration": 1.966591, "end_time": "2021-10-10T16:36:03.909720", "exception": false, "start_time": "2021-10-10T16:36:01.943129", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_493/3416006740.py:22: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Device: cuda:0\n"]}, {"data": {"text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["import os\n", "import urllib.request\n", "from urllib.error import HTTPError\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision\n", "from IPython.display import set_matplotlib_formats\n", "from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint\n", "from torchvision import transforms\n", "from torchvision.datasets import CIFAR10\n", "\n", "plt.set_cmap(\"cividis\")\n", "# %matplotlib inline\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "matplotlib.rcParams[\"lines.linewidth\"] = 2.0\n", "sns.reset_orig()\n", "\n", "# %load_ext tensorboard\n", "\n", "# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data/\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/VisionTransformers/\")\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", "print(\"Device:\", device)"]}, {"cell_type": "markdown", "id": "7b4c9cda", "metadata": {"papermill": {"duration": 0.012503, "end_time": "2021-10-10T16:36:03.935539", "exception": false, "start_time": "2021-10-10T16:36:03.923036", "status": "completed"}, "tags": []}, "source": ["We provide a pre-trained Vision Transformer which we download in the next cell.\n", "However, Vision Transformers can be relatively quickly trained on CIFAR10 with an overall training time of less than an hour on an NVIDIA TitanRTX.\n", "Feel free to experiment with training your own Transformer once you went through the whole notebook."]}, {"cell_type": "code", "execution_count": 3, "id": "6284d05a", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:03.966290Z", "iopub.status.busy": "2021-10-10T16:36:03.965811Z", "iopub.status.idle": "2021-10-10T16:36:04.460168Z", "shell.execute_reply": "2021-10-10T16:36:04.460557Z"}, "papermill": {"duration": 0.512509, "end_time": "2021-10-10T16:36:04.460702", "exception": false, "start_time": "2021-10-10T16:36:03.948193", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial15/ViT.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial15/tensorboards/ViT/events.out.tfevents.ViT...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial5/tensorboards/ResNet/events.out.tfevents.resnet...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/\"\n", "# Files to download\n", "pretrained_files = [\n", " \"tutorial15/ViT.ckpt\",\n", " \"tutorial15/tensorboards/ViT/events.out.tfevents.ViT\",\n", " \"tutorial5/tensorboards/ResNet/events.out.tfevents.resnet\",\n", "]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name.split(\"/\", 1)[1])\n", " if \"/\" in file_name.split(\"/\", 1)[1]:\n", " os.makedirs(file_path.rsplit(\"/\", 1)[0], exist_ok=True)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(\"Downloading %s...\" % file_url)\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "c96b3099", "metadata": {"papermill": {"duration": 0.012919, "end_time": "2021-10-10T16:36:04.487227", "exception": false, "start_time": "2021-10-10T16:36:04.474308", "status": "completed"}, "tags": []}, "source": ["We load the CIFAR10 dataset below.\n", "We use the same setup of the datasets and data augmentations as for the CNNs in Tutorial 5 to keep a fair comparison.\n", "The constants in the `transforms.Normalize` correspond to the values\n", "that scale and shift the data to a zero mean and standard deviation of\n", "one."]}, {"cell_type": "code", "execution_count": 4, "id": "bb8b01bd", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:04.522789Z", "iopub.status.busy": "2021-10-10T16:36:04.522299Z", "iopub.status.idle": "2021-10-10T16:36:06.982772Z", "shell.execute_reply": "2021-10-10T16:36:06.983222Z"}, "papermill": {"duration": 2.483202, "end_time": "2021-10-10T16:36:06.983367", "exception": false, "start_time": "2021-10-10T16:36:04.500165", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"data": {"application/pdf": "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Rm9udERlc2NyaXB0b3IgMTUgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTQgMCBSID4+CmVuZG9iagoxNSAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjE0IDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjE3IDAgb2JqCjw8IC9BIDE4IDAgUiAvQyAxOSAwIFIgL0YgMjAgMCBSIC9JIDIxIDAgUiAvUiAyMiAwIFIgL2EgMjMgMCBSIC9kIDI0IDAgUgovZSAyNSAwIFIgL2YgMjYgMCBSIC9nIDI3IDAgUiAvaCAyOCAwIFIgL2wgMjkgMCBSIC9tIDMwIDAgUiAvbyAzMSAwIFIKL29uZSAzMiAwIFIgL3AgMzMgMCBSIC9zIDM0IDAgUiAvc3BhY2UgMzUgMCBSIC90IDM2IDAgUiAveCAzNyAwIFIKL3plcm8gMzggMCBSID4+CmVuZG9iagozIDAgb2JqCjw8IC9GMSAxNiAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4gPj4KZW5kb2JqCjUgMCBvYmoKPDwgPj4KZW5kb2JqCjYgMCBvYmoKPDwg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zeUgoKU1vwgVLl+Trx2aty64M5fzNZaLK5cqGUBNasFU9xEqRUehTPj8PG3NI3xGIhC2tp5wtzb8Smo1uN4gmyibl+cSb51nwesjeMh2hZGbHqxgPQ+ZRZjK3kDCDAowFikVOOmOzhWE8iMdhb57KhQKxTTQp6dCoVBMA2cbhQ8VCoVC8fND3z0VCoViGujTU6FQKKaBPj0VCoViGujTU6FQKKaBPj0VCoViGujTU6FQKKbB/wZ/Q/tTCmVuZHN0cmVhbQplbmRvYmoKMzkgMCBvYmoKMTQzMzMKZW5kb2JqCjIgMCBvYmoKPDwgL0NvdW50IDEgL0tpZHMgWyAxMSAwIFIgXSAvVHlwZSAvUGFnZXMgPj4KZW5kb2JqCjQwIDAgb2JqCjw8IC9DcmVhdGlvbkRhdGUgKEQ6MjAyMTEwMTAxODM2MDYrMDInMDAnKQovQ3JlYXRvciAoTWF0cGxvdGxpYiB2My40LjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcpCi9Qcm9kdWNlciAoTWF0cGxvdGxpYiBwZGYgYmFja2VuZCB2My40LjMpID4+CmVuZG9iagp4cmVmCjAgNDEKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDE2IDAwMDAwIG4gCjAwMDAwMjI3MjYgMDAwMDAgbiAKMDAwMDAwNzkzNyAwMDAwMCBuIAowMDAwMDA3OTY5IDAwMDAwIG4gCjAwMDAwMDgwNjggMDAwMDAgbiAKMDAwMDAwODA4OSAwMDAwMCBuIAowMDAwMDA4MTEwIDAwMDAwIG4gCjAwMDAwMDAwNjUgMDAwMDAgbiAKMDAwMDAwMDQwMCAwMDAwMCBuIAowMDAwMDAwNzQxIDAwMDAwIG4gCjAwMDAwMDAyMDggMDAwMDAgbiAKMDAwMDAwMDcyMSAwMDAwMCBuIAowMDAwMDA4MTQyIDAwMDAwIG4gCjAwMDAwMDY2NDMgMDAwMDAgbiAKMDAwMDAwNjQ0MyAwMDAwMCBuIAowMDAwMDA2MDM0IDAwMDAwIG4gCjAwMDAwMDc2OTYgMDAwMDAgbiAKMDAwMDAwMDc2MSAwMDAwMCBuIAowMDAwMDAwOTI0IDAwMDAwIG4gCjAwMDAwMDEyMzIgMDAwMDAgbiAKMDAwMDAwMTM4MCAwMDAwMCBuIAowMDAwMDAxNTAzIDAwMDAwIG4gCjAwMDAwMDE4MDggMDAwMDAgbiAKMDAwMDAwMjE4OCAwMDAwMCBuIAowMDAwMDAyNDkyIDAwMDAwIG4gCjAwMDAwMDI4MTQgMDAwMDAgbiAKMDAwMDAwMzAyMyAwMDAwMCBuIAowMDAwMDAzNDM3IDAwMDAwIG4gCjAwMDAwMDM2NzQgMDAwMDAgbiAKMDAwMDAwMzc5MyAwMDAwMCBuIAowMDAwMDA0MTI0IDAwMDAwIG4gCjAwMDAwMDQ0MTUgMDAwMDAgbiAKMDAwMDAwNDU3MCAwMDAwMCBuIAowMDAwMDA0ODgyIDAwMDAwIG4gCjAwMDAwMDUyODkgMDAwMDAgbiAKMDAwMDAwNTM3OSAwMDAwMCBuIAowMDAwMDA1NTg1IDAwMDAwIG4gCjAwMDAwMDU3NDYgMDAwMDAgbiAKMDAwMDAyMjcwNCAwMDAwMCBuIAowMDAwMDIyNzg2IDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNDAgMCBSIC9Sb290IDEgMCBSIC9TaXplIDQxID4+CnN0YXJ0eHJlZgoyMjk0MwolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:36:06.917884\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["test_transform = transforms.Compose(\n", " [\n", " transforms.ToTensor(),\n", " transforms.Normalize([0.49139968, 0.48215841, 0.44653091], [0.24703223, 0.24348513, 0.26158784]),\n", " ]\n", ")\n", "# For training, we add some augmentation. Networks are too powerful and would overfit.\n", "train_transform = transforms.Compose(\n", " [\n", " transforms.RandomHorizontalFlip(),\n", " transforms.RandomResizedCrop((32, 32), scale=(0.8, 1.0), ratio=(0.9, 1.1)),\n", " transforms.ToTensor(),\n", " transforms.Normalize([0.49139968, 0.48215841, 0.44653091], [0.24703223, 0.24348513, 0.26158784]),\n", " ]\n", ")\n", "# Loading the training dataset. We need to split it into a training and validation part\n", "# We need to do a little trick because the validation set should not use the augmentation.\n", "train_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=train_transform, download=True)\n", "val_dataset = CIFAR10(root=DATASET_PATH, train=True, transform=test_transform, download=True)\n", "pl.seed_everything(42)\n", "train_set, _ = torch.utils.data.random_split(train_dataset, [45000, 5000])\n", "pl.seed_everything(42)\n", "_, val_set = torch.utils.data.random_split(val_dataset, [45000, 5000])\n", "\n", "# Loading the test set\n", "test_set = CIFAR10(root=DATASET_PATH, train=False, transform=test_transform, download=True)\n", "\n", "# We define a set of data loaders that we can use for various purposes later.\n", "train_loader = data.DataLoader(train_set, batch_size=128, shuffle=True, drop_last=True, pin_memory=True, num_workers=4)\n", "val_loader = data.DataLoader(val_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4)\n", "test_loader = data.DataLoader(test_set, batch_size=128, shuffle=False, drop_last=False, num_workers=4)\n", "\n", "# Visualize some examples\n", "NUM_IMAGES = 4\n", "CIFAR_images = torch.stack([val_set[idx][0] for idx in range(NUM_IMAGES)], dim=0)\n", "img_grid = torchvision.utils.make_grid(CIFAR_images, nrow=4, normalize=True, pad_value=0.9)\n", "img_grid = img_grid.permute(1, 2, 0)\n", "\n", "plt.figure(figsize=(8, 8))\n", "plt.title(\"Image examples of the CIFAR10 dataset\")\n", "plt.imshow(img_grid)\n", "plt.axis(\"off\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "de31824d", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.01573, "end_time": "2021-10-10T16:36:07.015490", "exception": false, "start_time": "2021-10-10T16:36:06.999760", "status": "completed"}, "tags": []}, "source": ["## Transformers for image classification\n", "\n", "Transformers have been originally proposed to process sets since it is a permutation-equivariant architecture, i.e., producing the same output permuted if the input is permuted.\n", "To apply Transformers to sequences, we have simply added a positional encoding to the input feature vectors, and the model learned by itself what to do with it.\n", "So, why not do the same thing on images?\n", "This is exactly what [Alexey Dosovitskiy et al. ](https://openreview.net/pdf?id=YicbFdNTTy) proposed in their paper \"An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale\".\n", "Specifically, the Vision Transformer is a model for image classification that views images as sequences of smaller patches.\n", "As a preprocessing step, we split an image of, for example, $48\\times 48$ pixels into 9 $16\\times 16$ patches.\n", "Each of those patches is considered to be a \"word\"/\"token\", and projected to a feature space.\n", "With adding positional encodings and a token for classification on top, we can apply a Transformer as usual to this sequence and start training it for our task.\n", "A nice GIF visualization of the architecture is shown below (figure credit - [Phil Wang](https://github.com/lucidrains/vit-pytorch/blob/main/images/vit.gif)):\n", "\n", "
\n", "\n", "We will walk step by step through the Vision Transformer, and implement all parts by ourselves.\n", "First, let's implement the image preprocessing: an image of size $N\\times N$ has to be split into $(N/M)^2$ patches of size $M\\times M$.\n", "These represent the input words to the Transformer."]}, {"cell_type": "code", "execution_count": 5, "id": "ae492f17", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:07.050794Z", "iopub.status.busy": "2021-10-10T16:36:07.048305Z", "iopub.status.idle": "2021-10-10T16:36:07.053076Z", "shell.execute_reply": "2021-10-10T16:36:07.052610Z"}, "papermill": {"duration": 0.022148, "end_time": "2021-10-10T16:36:07.053172", "exception": false, "start_time": "2021-10-10T16:36:07.031024", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def img_to_patch(x, patch_size, flatten_channels=True):\n", " \"\"\"\n", " Inputs:\n", " x - torch.Tensor representing the image of shape [B, C, H, W]\n", " patch_size - Number of pixels per dimension of the patches (integer)\n", " flatten_channels - If True, the patches will be returned in a flattened format\n", " as a feature vector instead of a image grid.\n", " \"\"\"\n", " B, C, H, W = x.shape\n", " x = x.reshape(B, C, H // patch_size, patch_size, W // patch_size, patch_size)\n", " x = x.permute(0, 2, 4, 1, 3, 5) # [B, H', W', C, p_H, p_W]\n", " x = x.flatten(1, 2) # [B, H'*W', C, p_H, p_W]\n", " if flatten_channels:\n", " x = x.flatten(2, 4) # [B, H'*W', C*p_H*p_W]\n", " return x"]}, {"cell_type": "markdown", "id": "1577af41", "metadata": {"papermill": {"duration": 0.016461, "end_time": "2021-10-10T16:36:07.085363", "exception": false, "start_time": "2021-10-10T16:36:07.068902", "status": "completed"}, "tags": []}, "source": ["Let's take a look at how that works for our CIFAR examples above.\n", "For our images of size $32\\times 32$, we choose a patch size of 4.\n", "Hence, we obtain sequences of 64 patches of size $4\\times 4$.\n", "We visualize them below:"]}, {"cell_type": "code", "execution_count": 6, "id": "bc8f2072", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:07.121184Z", "iopub.status.busy": "2021-10-10T16:36:07.120716Z", "iopub.status.idle": "2021-10-10T16:36:07.353313Z", "shell.execute_reply": "2021-10-10T16:36:07.353705Z"}, "papermill": {"duration": 0.252758, "end_time": "2021-10-10T16:36:07.353849", "exception": false, "start_time": "2021-10-10T16:36:07.101091", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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bnMgL0NoYXJQcm9jcyAyMCAwIFIKL0VuY29kaW5nIDw8Ci9EaWZmZXJlbmNlcyBbIDMyIC9zcGFjZSA3MyAvSSA5NyAvYSA5OSAvYyAxMDEgL2UgL2YgL2cgL2ggL2kgMTA5IC9tIC9uIC9vIC9wIC9xIDExNSAvcwovdCAvdSBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgMTggMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgMTcgMCBSID4+CmVuZG9iagoxOCAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjE3IDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjIwIDAgb2JqCjw8IC9JIDIxIDAgUiAvYSAyMiAwIFIgL2MgMjMgMCBSIC9lIDI0IDAgUiAvZiAyNSAwIFIgL2cgMjYgMCBSIC9oIDI3IDAgUgovaSAyOCAwIFIgL20gMjkgMCBSIC9uIDMwIDAgUiAvbyAzMSAwIFIgL3AgMzIgMCBSIC9xIDMzIDAgUiAvcyAzNCAwIFIKL3NwYWNlIDM1IDAgUiAvdCAzNiAwIFIgL3UgMzcgMCBSID4+CmVuZG9iagozIDAgb2JqCjw8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MDAwMDAgbiAKMDAwMDAzMzM1MyAwMDAwMCBuIAowMDAwMDQ2NDYzIDAwMDAwIG4gCjAwMDAwMDU5NDEgMDAwMDAgbiAKMDAwMDAwNTc0MSAwMDAwMCBuIAowMDAwMDA1MzY5IDAwMDAwIG4gCjAwMDAwMDY5OTQgMDAwMDAgbiAKMDAwMDAwMDc5OSAwMDAwMCBuIAowMDAwMDAwOTIyIDAwMDAwIG4gCjAwMDAwMDEzMDIgMDAwMDAgbiAKMDAwMDAwMTYwNyAwMDAwMCBuIAowMDAwMDAxOTI5IDAwMDAwIG4gCjAwMDAwMDIxMzggMDAwMDAgbiAKMDAwMDAwMjU1MiAwMDAwMCBuIAowMDAwMDAyNzg5IDAwMDAwIG4gCjAwMDAwMDI5MzMgMDAwMDAgbiAKMDAwMDAwMzI2NCAwMDAwMCBuIAowMDAwMDAzNTAwIDAwMDAwIG4gCjAwMDAwMDM3OTEgMDAwMDAgbiAKMDAwMDAwNDEwMyAwMDAwMCBuIAowMDAwMDA0NDE5IDAwMDAwIG4gCjAwMDAwMDQ4MjYgMDAwMDAgbiAKMDAwMDAwNDkxNiAwMDAwMCBuIAowMDAwMDA1MTIyIDAwMDAwIG4gCjAwMDAwMjAxODEgMDAwMDAgbiAKMDAwMDAzMzMzMSAwMDAwMCBuIAowMDAwMDQ2NDQxIDAwMDAwIG4gCjAwMDAwNTk3NjAgMDAwMDAgbiAKMDAwMDA1OTg0MiAwMDAwMCBuIAp0cmFpbGVyCjw8IC9JbmZvIDQyIDAgUiAvUm9vdCAxIDAgUiAvU2l6ZSA0MyA+PgpzdGFydHhyZWYKNTk5OTkKJSVFT0YK\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:36:07.261617\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["img_patches = img_to_patch(CIFAR_images, patch_size=4, flatten_channels=False)\n", "\n", "fig, ax = plt.subplots(CIFAR_images.shape[0], 1, figsize=(14, 3))\n", "fig.suptitle(\"Images as input sequences of patches\")\n", "for i in range(CIFAR_images.shape[0]):\n", " img_grid = torchvision.utils.make_grid(img_patches[i], nrow=64, normalize=True, pad_value=0.9)\n", " img_grid = img_grid.permute(1, 2, 0)\n", " ax[i].imshow(img_grid)\n", " ax[i].axis(\"off\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "cce53f7d", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.018572, "end_time": "2021-10-10T16:36:07.393026", "exception": false, "start_time": "2021-10-10T16:36:07.374454", "status": "completed"}, "tags": []}, "source": ["Compared to the original images, it is much harder to recognize the objects from those patch lists now.\n", "Still, this is the input we provide to the Transformer for classifying the images.\n", "The model has to learn itself how it has to combine the patches to recognize the objects.\n", "The inductive bias in CNNs that an image is grid of pixels, is lost in this input format.\n", "\n", "After we have looked at the preprocessing, we can now start building the Transformer model.\n", "Since we have discussed the fundamentals of Multi-Head Attention in [Tutorial 6](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial6/Transformers_and_MHAttention.html), we will use the PyTorch module `nn.MultiheadAttention` ([docs](https://pytorch.org/docs/stable/generated/torch.nn.MultiheadAttention.html?highlight=multihead#torch.nn.MultiheadAttention)) here.\n", "Further, we use the Pre-Layer Normalization version of the Transformer blocks proposed by [Ruibin Xiong et al. ](http://proceedings.mlr.press/v119/xiong20b/xiong20b.pdf) in 2020.\n", "The idea is to apply Layer Normalization not in between residual blocks, but instead as a first layer in the residual blocks.\n", "This reorganization of the layers supports better gradient flow and removes the necessity of a warm-up stage.\n", "A visualization of the difference between the standard Post-LN and the Pre-LN version is shown below.\n", "\n", "
\n", "\n", "The implementation of the Pre-LN attention block looks as follows:"]}, {"cell_type": "code", "execution_count": 7, "id": "0f69e05b", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:07.437581Z", "iopub.status.busy": "2021-10-10T16:36:07.437105Z", "iopub.status.idle": "2021-10-10T16:36:07.439187Z", "shell.execute_reply": "2021-10-10T16:36:07.438722Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.027401, "end_time": "2021-10-10T16:36:07.439284", "exception": false, "start_time": "2021-10-10T16:36:07.411883", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class AttentionBlock(nn.Module):\n", " def __init__(self, embed_dim, hidden_dim, num_heads, dropout=0.0):\n", " \"\"\"\n", " Inputs:\n", " embed_dim - Dimensionality of input and attention feature vectors\n", " hidden_dim - Dimensionality of hidden layer in feed-forward network\n", " (usually 2-4x larger than embed_dim)\n", " num_heads - Number of heads to use in the Multi-Head Attention block\n", " dropout - Amount of dropout to apply in the feed-forward network\n", " \"\"\"\n", " super().__init__()\n", "\n", " self.layer_norm_1 = nn.LayerNorm(embed_dim)\n", " self.attn = nn.MultiheadAttention(embed_dim, num_heads)\n", " self.layer_norm_2 = nn.LayerNorm(embed_dim)\n", " self.linear = nn.Sequential(\n", " nn.Linear(embed_dim, hidden_dim),\n", " nn.GELU(),\n", " nn.Dropout(dropout),\n", " nn.Linear(hidden_dim, embed_dim),\n", " nn.Dropout(dropout),\n", " )\n", "\n", " def forward(self, x):\n", " inp_x = self.layer_norm_1(x)\n", " x = x + self.attn(inp_x, inp_x, inp_x)[0]\n", " x = x + self.linear(self.layer_norm_2(x))\n", " return x"]}, {"cell_type": "markdown", "id": "b5f96bdf", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.0187, "end_time": "2021-10-10T16:36:07.476786", "exception": false, "start_time": "2021-10-10T16:36:07.458086", "status": "completed"}, "tags": []}, "source": ["Now we have all modules ready to build our own Vision Transformer.\n", "Besides the Transformer encoder, we need the following modules:\n", "\n", "* A **linear projection** layer that maps the input patches to a feature vector of larger size.\n", "It is implemented by a simple linear layer that takes each $M\\times M$ patch independently as input.\n", "* A **classification token** that is added to the input sequence.\n", "We will use the output feature vector of the classification token (CLS token in short) for determining the classification prediction.\n", "* Learnable **positional encodings** that are added to the tokens before being processed by the Transformer.\n", "Those are needed to learn position-dependent information, and convert the set to a sequence.\n", "Since we usually work with a fixed resolution, we can learn the positional encodings instead of having the pattern of sine and cosine functions.\n", "* A **MLP head** that takes the output feature vector of the CLS token, and maps it to a classification prediction.\n", "This is usually implemented by a small feed-forward network or even a single linear layer.\n", "\n", "With those components in mind, let's implement the full Vision Transformer below:"]}, {"cell_type": "code", "execution_count": 8, "id": "0228d483", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:07.522084Z", "iopub.status.busy": "2021-10-10T16:36:07.521601Z", "iopub.status.idle": "2021-10-10T16:36:07.523699Z", "shell.execute_reply": "2021-10-10T16:36:07.523301Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.02827, "end_time": "2021-10-10T16:36:07.523823", "exception": false, "start_time": "2021-10-10T16:36:07.495553", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class VisionTransformer(nn.Module):\n", " def __init__(\n", " self,\n", " embed_dim,\n", " hidden_dim,\n", " num_channels,\n", " num_heads,\n", " num_layers,\n", " num_classes,\n", " patch_size,\n", " num_patches,\n", " dropout=0.0,\n", " ):\n", " \"\"\"\n", " Inputs:\n", " embed_dim - Dimensionality of the input feature vectors to the Transformer\n", " hidden_dim - Dimensionality of the hidden layer in the feed-forward networks\n", " within the Transformer\n", " num_channels - Number of channels of the input (3 for RGB)\n", " num_heads - Number of heads to use in the Multi-Head Attention block\n", " num_layers - Number of layers to use in the Transformer\n", " num_classes - Number of classes to predict\n", " patch_size - Number of pixels that the patches have per dimension\n", " num_patches - Maximum number of patches an image can have\n", " dropout - Amount of dropout to apply in the feed-forward network and\n", " on the input encoding\n", " \"\"\"\n", " super().__init__()\n", "\n", " self.patch_size = patch_size\n", "\n", " # Layers/Networks\n", " self.input_layer = nn.Linear(num_channels * (patch_size ** 2), embed_dim)\n", " self.transformer = nn.Sequential(\n", " *(AttentionBlock(embed_dim, hidden_dim, num_heads, dropout=dropout) for _ in range(num_layers))\n", " )\n", " self.mlp_head = nn.Sequential(nn.LayerNorm(embed_dim), nn.Linear(embed_dim, num_classes))\n", " self.dropout = nn.Dropout(dropout)\n", "\n", " # Parameters/Embeddings\n", " self.cls_token = nn.Parameter(torch.randn(1, 1, embed_dim))\n", " self.pos_embedding = nn.Parameter(torch.randn(1, 1 + num_patches, embed_dim))\n", "\n", " def forward(self, x):\n", " # Preprocess input\n", " x = img_to_patch(x, self.patch_size)\n", " B, T, _ = x.shape\n", " x = self.input_layer(x)\n", "\n", " # Add CLS token and positional encoding\n", " cls_token = self.cls_token.repeat(B, 1, 1)\n", " x = torch.cat([cls_token, x], dim=1)\n", " x = x + self.pos_embedding[:, : T + 1]\n", "\n", " # Apply Transforrmer\n", " x = self.dropout(x)\n", " x = x.transpose(0, 1)\n", " x = self.transformer(x)\n", "\n", " # Perform classification prediction\n", " cls = x[0]\n", " out = self.mlp_head(cls)\n", " return out"]}, {"cell_type": "markdown", "id": "0fde39b8", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.018994, "end_time": "2021-10-10T16:36:07.561842", "exception": false, "start_time": "2021-10-10T16:36:07.542848", "status": "completed"}, "tags": []}, "source": ["Finally, we can put everything into a PyTorch Lightning Module as usual.\n", "We use `torch.optim.AdamW` as the optimizer, which is Adam with a corrected weight decay implementation.\n", "Since we use the Pre-LN Transformer version, we do not need to use a learning rate warmup stage anymore.\n", "Instead, we use the same learning rate scheduler as the CNNs in our previous tutorial on image classification."]}, {"cell_type": "code", "execution_count": 9, "id": "99bcb238", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:07.606617Z", "iopub.status.busy": "2021-10-10T16:36:07.606128Z", "iopub.status.idle": "2021-10-10T16:36:07.608256Z", "shell.execute_reply": "2021-10-10T16:36:07.607820Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.027621, "end_time": "2021-10-10T16:36:07.608351", "exception": false, "start_time": "2021-10-10T16:36:07.580730", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ViT(pl.LightningModule):\n", " def __init__(self, model_kwargs, lr):\n", " super().__init__()\n", " self.save_hyperparameters()\n", " self.model = VisionTransformer(**model_kwargs)\n", " self.example_input_array = next(iter(train_loader))[0]\n", "\n", " def forward(self, x):\n", " return self.model(x)\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr)\n", " lr_scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[100, 150], gamma=0.1)\n", " return [optimizer], [lr_scheduler]\n", "\n", " def _calculate_loss(self, batch, mode=\"train\"):\n", " imgs, labels = batch\n", " preds = self.model(imgs)\n", " loss = F.cross_entropy(preds, labels)\n", " acc = (preds.argmax(dim=-1) == labels).float().mean()\n", "\n", " self.log(\"%s_loss\" % mode, loss)\n", " self.log(\"%s_acc\" % mode, acc)\n", " return loss\n", "\n", " def training_step(self, batch, batch_idx):\n", " loss = self._calculate_loss(batch, mode=\"train\")\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " self._calculate_loss(batch, mode=\"val\")\n", "\n", " def test_step(self, batch, batch_idx):\n", " self._calculate_loss(batch, mode=\"test\")"]}, {"cell_type": "markdown", "id": "db79a63a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.018896, "end_time": "2021-10-10T16:36:07.646134", "exception": false, "start_time": "2021-10-10T16:36:07.627238", "status": "completed"}, "tags": []}, "source": ["## Experiments\n", "\n", "Commonly, Vision Transformers are applied to large-scale image classification benchmarks such as ImageNet to leverage their full potential.\n", "However, here we take a step back and ask: can Vision Transformer also succeed on classical, small benchmarks such as CIFAR10?\n", "To find this out, we train a Vision Transformer from scratch on the CIFAR10 dataset.\n", "Let's first create a training function for our PyTorch Lightning module\n", "which also loads the pre-trained model if you have downloaded it above."]}, {"cell_type": "code", "execution_count": 10, "id": "8c1f2286", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:07.692103Z", "iopub.status.busy": "2021-10-10T16:36:07.691595Z", "iopub.status.idle": "2021-10-10T16:36:07.693685Z", "shell.execute_reply": "2021-10-10T16:36:07.693221Z"}, "papermill": {"duration": 0.028441, "end_time": "2021-10-10T16:36:07.693783", "exception": false, "start_time": "2021-10-10T16:36:07.665342", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_model(**kwargs):\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, \"ViT\"),\n", " gpus=1 if str(device) == \"cuda:0\" else 0,\n", " max_epochs=180,\n", " callbacks=[\n", " ModelCheckpoint(save_weights_only=True, mode=\"max\", monitor=\"val_acc\"),\n", " LearningRateMonitor(\"epoch\"),\n", " ],\n", " progress_bar_refresh_rate=1,\n", " )\n", " trainer.logger._log_graph = True # If True, we plot the computation graph in tensorboard\n", " trainer.logger._default_hp_metric = None # Optional logging argument that we don't need\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"ViT.ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model at %s, loading...\" % pretrained_filename)\n", " # Automatically loads the model with the saved hyperparameters\n", " model = ViT.load_from_checkpoint(pretrained_filename)\n", " else:\n", " pl.seed_everything(42) # To be reproducable\n", " model = ViT(**kwargs)\n", " trainer.fit(model, train_loader, val_loader)\n", " # Load best checkpoint after training\n", " model = ViT.load_from_checkpoint(trainer.checkpoint_callback.best_model_path)\n", "\n", " # Test best model on validation and test set\n", " val_result = trainer.test(model, test_dataloaders=val_loader, verbose=False)\n", " test_result = trainer.test(model, test_dataloaders=test_loader, verbose=False)\n", " result = {\"test\": test_result[0][\"test_acc\"], \"val\": val_result[0][\"test_acc\"]}\n", "\n", " return model, result"]}, {"cell_type": "markdown", "id": "e359c353", "metadata": {"papermill": {"duration": 0.019033, "end_time": "2021-10-10T16:36:07.732020", "exception": false, "start_time": "2021-10-10T16:36:07.712987", "status": "completed"}, "tags": []}, "source": ["Now, we can already start training our model.\n", "As seen in our implementation, we have couple of hyperparameter that we have to choose.\n", "When creating this notebook, we have performed a small grid search over hyperparameters and listed the best hyperparameters in the cell below.\n", "Nevertheless, it is worth to discuss the influence that each hyperparameter has, and what intuition we have for choosing its value.\n", "\n", "First, let's consider the patch size.\n", "The smaller we make the patches, the longer the input sequences to the Transformer become.\n", "While in general, this allows the Transformer to model more complex functions, it requires a longer computation time due to its quadratic memory usage in the attention layer.\n", "Furthermore, small patches can make the task more difficult since the Transformer has to learn which patches are close-by, and which are far away.\n", "We experimented with patch sizes of 2, 4 and 8 which gives us the input sequence lengths of 256, 64, and 16 respectively.\n", "We found 4 to result in the best performance, and hence pick it below.\n", "\n", "Next, the embedding and hidden dimensionality have a similar impact to a Transformer as to an MLP.\n", "The larger the sizes, the more complex the model becomes, and the longer it takes to train.\n", "In Transformer however, we have one more aspect to consider: the query-key sizes in the Multi-Head Attention layers.\n", "Each key has the feature dimensionality of `embed_dim/num_heads`.\n", "Considering that we have an input sequence length of 64, a minimum reasonable size for the key vectors is 16 or 32.\n", "Lower dimensionalities can restrain the possible attention maps too much.\n", "We observed that more than 8 heads are not necessary for the Transformer, and therefore pick a embedding dimensionality of `256`.\n", "The hidden dimensionality in the feed-forward networks is usually 2-4x larger than the embedding dimensionality, and thus we pick `512`.\n", "\n", "Finally, the learning rate for Transformers is usually relatively small, and in papers, a common value to use is 3e-5.\n", "However, since we work with a smaller dataset and have a potentially easier task, we found that we are able to increase the learning rate to 3e-4 without any problems.\n", "To reduce overfitting, we use a dropout value of 0.2.\n", "Remember that we also use small image augmentations as regularization during training.\n", "\n", "Feel free to explore the hyperparameters yourself by changing the values below.\n", "In general, the Vision Transformer did not show to be too sensitive to\n", "the hyperparameter choices on the CIFAR10 dataset."]}, {"cell_type": "code", "execution_count": 11, "id": "8aacc01b", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:07.775505Z", "iopub.status.busy": "2021-10-10T16:36:07.775020Z", "iopub.status.idle": "2021-10-10T16:36:15.614815Z", "shell.execute_reply": "2021-10-10T16:36:15.615209Z"}, "papermill": {"duration": 7.864306, "end_time": "2021-10-10T16:36:15.615357", "exception": false, "start_time": "2021-10-10T16:36:07.751051", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/VisionTransformers/ViT.ckpt, loading...\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:678: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/usr/local/lib/python3.9/dist-packages/torch/_jit_internal.py:603: LightningDeprecationWarning: The `LightningModule.datamodule` property is deprecated in v1.3 and will be removed in v1.5. Access the datamodule through using `self.trainer.datamodule` instead.\n", " if hasattr(mod, name):\n", "/usr/local/lib/python3.9/dist-packages/torch/_jit_internal.py:603: LightningDeprecationWarning: The `LightningModule.loaded_optimizer_states_dict` property is deprecated in v1.4 and will be removed in v1.6.\n", " if hasattr(mod, name):\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "2824556005cc42f79c31cbebe248b150", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "06ecaa193abb4c6599f94a9a7e64de8f", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["ViT results {'test': 0.7559000253677368, 'val': 0.7563999891281128}\n"]}], "source": ["model, results = train_model(\n", " model_kwargs={\n", " \"embed_dim\": 256,\n", " \"hidden_dim\": 512,\n", " \"num_heads\": 8,\n", " \"num_layers\": 6,\n", " \"patch_size\": 4,\n", " \"num_channels\": 3,\n", " \"num_patches\": 64,\n", " \"num_classes\": 10,\n", " \"dropout\": 0.2,\n", " },\n", " lr=3e-4,\n", ")\n", "print(\"ViT results\", results)"]}, {"cell_type": "markdown", "id": "c305d6d0", "metadata": {"papermill": {"duration": 0.327633, "end_time": "2021-10-10T16:36:16.015814", "exception": false, "start_time": "2021-10-10T16:36:15.688181", "status": "completed"}, "tags": []}, "source": ["The Vision Transformer achieves a validation and test performance of about 75%.\n", "In comparison, almost all CNN architectures that we have tested in [Tutorial 5](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial5/Inception_ResNet_DenseNet.html) obtained a classification performance of around 90%.\n", "This is a considerable gap and shows that although Vision Transformers perform strongly on ImageNet with potential pretraining, they cannot come close to simple CNNs on CIFAR10 when being trained from scratch.\n", "The differences between a CNN and Transformer can be well observed in the training curves.\n", "Let's look at them in a tensorboard below:"]}, {"cell_type": "code", "execution_count": 12, "id": "88cc38e7", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:16.703879Z", "iopub.status.busy": "2021-10-10T16:36:16.703404Z", "iopub.status.idle": "2021-10-10T16:36:16.705449Z", "shell.execute_reply": "2021-10-10T16:36:16.705045Z"}, "papermill": {"duration": 0.027779, "end_time": "2021-10-10T16:36:16.705553", "exception": false, "start_time": "2021-10-10T16:36:16.677774", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH!\n", "# %tensorboard --logdir ../saved_models/tutorial15/tensorboards/"]}, {"cell_type": "markdown", "id": "1b7c6344", "metadata": {"papermill": {"duration": 0.021202, "end_time": "2021-10-10T16:36:16.748363", "exception": false, "start_time": "2021-10-10T16:36:16.727161", "status": "completed"}, "tags": []}, "source": ["
"]}, {"cell_type": "markdown", "id": "26c6ac67", "metadata": {"papermill": {"duration": 0.02121, "end_time": "2021-10-10T16:36:16.790843", "exception": false, "start_time": "2021-10-10T16:36:16.769633", "status": "completed"}, "tags": []}, "source": ["The tensorboard compares the Vision Transformer to a ResNet trained on CIFAR10.\n", "When looking at the training losses, we see that the ResNet learns much more quickly in the first iterations.\n", "While the learning rate might have an influence on the initial learning speed, we see the same trend in the validation accuracy.\n", "The ResNet achieves the best performance of the Vision Transformer after just 5 epochs (2000 iterations).\n", "Further, while the ResNet training loss and validation accuracy have a similar trend, the validation performance of the Vision Transformers only marginally changes after 10k iterations while the training loss has almost just started going down.\n", "Yet, the Vision Transformer is also able to achieve a close-to 100% accuracy on the training set.\n", "\n", "All those observed phenomenons can be explained with a concept that we have visited before: inductive biases.\n", "Convolutional Neural Networks have been designed with the assumption that images are translation invariant.\n", "Hence, we apply convolutions with shared filters across the image.\n", "Furthermore, a CNN architecture integrates the concept of distance in an image: two pixels that are close to each other are more related than two distant pixels.\n", "Local patterns are combined into larger patterns, until we perform our classification prediction.\n", "All those aspects are inductive biases of a CNN.\n", "In contrast, a Vision Transformer does not know which two pixels are close to each other, and which are far apart.\n", "It has to learn this information solely from the sparse learning signal of the classification task.\n", "This is a huge disadvantage when we have a small dataset since such information is crucial for generalizing to an unseen test dataset.\n", "With large enough datasets and/or good pre-training, a Transformer can learn this information without the need of inductive biases, and instead is more flexible than a CNN.\n", "Especially long-distance relations between local patterns can be difficult to process in CNNs, while in Transformers, all patches have the distance of one.\n", "This is why Vision Transformers are so strong on large-scale datasets\n", "such as ImageNet, but underperform a lot when being applied to a small\n", "dataset such as CIFAR10."]}, {"cell_type": "markdown", "id": "5df010c9", "metadata": {"papermill": {"duration": 0.021723, "end_time": "2021-10-10T16:36:16.834866", "exception": false, "start_time": "2021-10-10T16:36:16.813143", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this tutorial, we have implemented our own Vision Transformer from scratch and applied it on the task of image classification.\n", "Vision Transformers work by splitting an image into a sequence of smaller patches, use those as input to a standard Transformer encoder.\n", "While Vision Transformers achieved outstanding results on large-scale image recognition benchmarks such as ImageNet, they considerably underperform when being trained from scratch on small-scale datasets like CIFAR10.\n", "The reason is that in contrast to CNNs, Transformers do not have the inductive biases of translation invariance and the feature hierachy (i.e. larger patterns consist of many smaller patterns).\n", "However, these aspects can be learned when enough data is provided, or the model has been pre-trained on other large-scale tasks.\n", "Considering that Vision Transformers have just been proposed end of 2020, there is likely a lot more to come on Transformers for Computer Vision.\n", "\n", "\n", "### References\n", "\n", "Dosovitskiy, Alexey, et al.\n", "\"An image is worth 16x16 words: Transformers for image recognition at scale.\"\n", "International Conference on Representation Learning (2021).\n", "[link](https://arxiv.org/pdf/2010.11929.pdf)\n", "\n", "Chen, Xiangning, et al.\n", "\"When Vision Transformers Outperform ResNets without Pretraining or Strong Data Augmentations.\"\n", "arXiv preprint arXiv:2106.01548 (2021).\n", "[link](https://arxiv.org/abs/2106.01548)\n", "\n", "Tolstikhin, Ilya, et al.\n", "\"MLP-mixer: An all-MLP Architecture for Vision.\"\n", "arXiv preprint arXiv:2105.01601 (2021).\n", "[link](https://arxiv.org/abs/2105.01601)\n", "\n", "Xiong, Ruibin, et al.\n", "\"On layer normalization in the transformer architecture.\"\n", "International Conference on Machine Learning.\n", "PMLR, 2020.\n", "[link](http://proceedings.mlr.press/v119/xiong20b/xiong20b.pdf)"]}, {"cell_type": "markdown", "id": "18e70eed", "metadata": {"papermill": {"duration": 0.021621, "end_time": "2021-10-10T16:36:16.878164", "exception": false, "start_time": "2021-10-10T16:36:16.856543", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "![Pytorch Lightning](data:image/png;base64,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NENFOjYxNjMxNjY1IiBkYXRhLXBqYXgtdHJhbnNpZW50PSJ0cnVlIi8+PG1ldGEgbmFtZT0iaHRtbC1zYWZlLW5vbmNlIiBjb250ZW50PSJkZjBjZTEzZWMwOGZiNGVhYmZhZTY2ZGZkMzc0ZTdmMjdkODU2MGJlNWIzYzBkZDk1OGQyYmY4MjQ5ZjQ2ODk2IiBkYXRhLXBqYXgtdHJhbnNpZW50PSJ0cnVlIi8+PG1ldGEgbmFtZT0idmlzaXRvci1wYXlsb2FkIiBjb250ZW50PSJleUp5WldabGNuSmxjaUk2SWlJc0luSmxjWFZsYzNSZmFXUWlPaUl3UXpBek9qUTNRemM2TVRFMU1URkJRem94UVRCRE5FTkZPall4TmpNeE5qWTFJaXdpZG1semFYUnZjbDlwWkNJNklqTTFNekU0TXpNNU9UazBPVEl6TWpJNU1UY2lMQ0p5WldkcGIyNWZaV1JuWlNJNkltbGhaQ0lzSW5KbFoybHZibDl5Wlc1a1pYSWlPaUpwWVdRaWZRPT0iIGRhdGEtcGpheC10cmFuc2llbnQ9InRydWUiLz48bWV0YSBuYW1lPSJ2aXNpdG9yLWhtYWMiIGNvbnRlbnQ9IjYyOGRhNDc0YjJkZDBlYWRmN2JiNjMzOTk3NjVjYTViNmYwMDhmOTVjYTZiZDEwYzFmMWMyOWI4YTUwMzYxODEiIGRhdGEtcGpheC10cmFuc2llbnQ9InRydWUiLz7sPWl32ziS3/MruOrt3t3XocT7SGzPcxznmDixJ3bSx8w8LkRCEmOKZPOwrZ7X/32rAFKkKMmkDufonemMSYJAoVComwD06JEA/zuY0owIIZnSw94kuqGJSxJPTPPhJ+pmYkbGPcGNwoyG2WEvoXGU+lmUzJ7IpmUohqlIPcEjGRHjT+ROzBISpj5UPXr06NEi6LGfTfKheE1nw4h1MImSzM2zdCX4x2mUJy4V3cijKzs47GVJDq8GR9hRs7OUBoA89cTAD697wg0JcsrBOxzwOqSbBBlH0TigIiBFRSCNP/JdkvlRWEPala/z5+Ib5dXrjy8MVQ9msZv+dBE91679INLFT8fO7bMXsystf/tL76hJlHbwb670cWpNpNsbQsZnb44//vSbNRzSd7fhr7++kRP557/N0p/Vuzw5+7AN+F9/n3ycnb64HZq3KpXE/Pwq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){height=\"60px\" width=\"240px\"}"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 11: Vision Transformers\n", " :card_description: In this tutorial, we will take a closer look at a recent new trend: Transformers for Computer Vision. Since [Alexey Dosovitskiy et...\n", " :tags: Image,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/11-vision-transformer.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab,colab_type,id,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 17.019243, "end_time": "2021-10-10T16:36:17.607886", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/11-vision-transformer/Vision_Transformer.ipynb", "output_path": ".notebooks/course_UvA-DL/11-vision-transformer.ipynb", "parameters": {}, "start_time": "2021-10-10T16:36:00.588643", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"06ecaa193abb4c6599f94a9a7e64de8f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_262db12adb4a4eb8a01537b1fbbac6a7", "IPY_MODEL_de04ee4e69b34edbb4623993191f18ff", "IPY_MODEL_34d1be73a2424261976f78e8ebd2f3b9"], "layout": "IPY_MODEL_3cf6ceecdcc04e9ea8214064fe78c0cc"}}, "07ce532be9e048b4a36844396612f0d6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "112f161d8d5e4d6892247a723f5c42d7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_88a70622b99e4603992ebe4a8310647a", "placeholder": "\u200b", "style": "IPY_MODEL_07ce532be9e048b4a36844396612f0d6", "value": "Testing: 100%"}}, "21c8daba50c54686b687787528812f0a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "24c3fc2871254a43a96cb796a4246134": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_be78b541f4774bcba63ff910d8f1ce22", "placeholder": "\u200b", "style": "IPY_MODEL_21c8daba50c54686b687787528812f0a", "value": " 40/40 [00:00<00:00, 47.98it/s]"}}, "262db12adb4a4eb8a01537b1fbbac6a7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d1ce9a66c073476d9c101cd69731611d", "placeholder": "\u200b", "style": "IPY_MODEL_654debaf094149e09b6c3423310b596c", "value": "Testing: 100%"}}, "2824556005cc42f79c31cbebe248b150": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_112f161d8d5e4d6892247a723f5c42d7", "IPY_MODEL_b29fce5508ba479589e3f4b0d0c593ae", "IPY_MODEL_24c3fc2871254a43a96cb796a4246134"], "layout": "IPY_MODEL_d1dd5a2d9d904ef3949b431c81e9f5ad"}}, "34d1be73a2424261976f78e8ebd2f3b9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8511d9a6c88540a893dd44dd16362e6b", "placeholder": "\u200b", "style": "IPY_MODEL_ce9b4471f190492182eb11d93560a81b", "value": " 79/79 [00:01<00:00, 49.85it/s]"}}, "3cf6ceecdcc04e9ea8214064fe78c0cc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "654debaf094149e09b6c3423310b596c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "688aa138fc5148bf85f1c48377198169": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8511d9a6c88540a893dd44dd16362e6b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "88a70622b99e4603992ebe4a8310647a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a5c301fa41b34ea29fc9e3745f950ce2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b29fce5508ba479589e3f4b0d0c593ae": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a5c301fa41b34ea29fc9e3745f950ce2", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_d1bd9770a9c04cc687a41b9da1507b7c", "value": 1.0}}, "b4d671d57ae7430b9f8b46f5dd95be19": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "be78b541f4774bcba63ff910d8f1ce22": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ce9b4471f190492182eb11d93560a81b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d1bd9770a9c04cc687a41b9da1507b7c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "d1ce9a66c073476d9c101cd69731611d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d1dd5a2d9d904ef3949b431c81e9f5ad": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "de04ee4e69b34edbb4623993191f18ff": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_688aa138fc5148bf85f1c48377198169", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_b4d671d57ae7430b9f8b46f5dd95be19", "value": 1.0}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/12-meta-learning.ipynb b/source/notebooks/course_UvA-DL/12-meta-learning.ipynb deleted file mode 100644 index 5e56e5e..0000000 --- a/source/notebooks/course_UvA-DL/12-meta-learning.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "e97dbf01", "metadata": {"papermill": {"duration": 0.035787, "end_time": "2021-10-10T16:36:26.406495", "exception": false, "start_time": "2021-10-10T16:36:26.370708", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 12: Meta-Learning - Learning to Learn\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-10-10T18:35:50.818431\n", "\n", "In this tutorial, we will discuss algorithms that learn models which can quickly adapt to new classes and/or tasks with few samples.\n", "This area of machine learning is called _Meta-Learning_ aiming at \"learning to learn\".\n", "Learning from very few examples is a natural task for humans. In contrast to current deep learning models, we need to see only a few examples of a police car or firetruck to recognize them in daily traffic.\n", "This is crucial ability since in real-world application, it is rarely the case that the data stays static and does not change over time.\n", "For example, an object detection system for mobile phones trained on data from 2000 will have troubles detecting today's common mobile phones, and thus, needs to adapt to new data without excessive label effort.\n", "The optimization techniques we have discussed so far struggle with this because they only aim at obtaining a good performance on a test set that had similar data.\n", "However, what if the test set has classes that we do not have in the training set?\n", "Or what if we want to test the model on a completely different task?\n", "We will discuss and implement three common Meta-Learning algorithms for such situations.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/12-meta-learning.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "89d6c708", "metadata": {"papermill": {"duration": 0.034571, "end_time": "2021-10-10T16:36:26.475148", "exception": false, "start_time": "2021-10-10T16:36:26.440577", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "2350fea0", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-10-10T16:36:26.546320Z", "iopub.status.busy": "2021-10-10T16:36:26.545801Z", "iopub.status.idle": "2021-10-10T16:36:26.547964Z", "shell.execute_reply": "2021-10-10T16:36:26.548348Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.039795, "end_time": "2021-10-10T16:36:26.548525", "exception": false, "start_time": "2021-10-10T16:36:26.508730", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"torch>=1.6, <1.9\" \"matplotlib\" \"torchmetrics>=0.3\" \"seaborn\" \"torchvision\" \"pytorch-lightning>=1.3\""]}, {"cell_type": "markdown", "id": "5bc75be2", "metadata": {"papermill": {"duration": 0.033967, "end_time": "2021-10-10T16:36:26.617046", "exception": false, "start_time": "2021-10-10T16:36:26.583079", "status": "completed"}, "tags": []}, "source": ["
\n", "Meta-Learning offers solutions to these situations, and we will discuss three popular algorithms: __Prototypical Networks__ ([Snell et al., 2017](https://arxiv.org/pdf/1703.05175.pdf)), __Model-Agnostic Meta-Learning / MAML__ ([Finn et al., 2017](http://proceedings.mlr.press/v70/finn17a.html)), and __Proto-MAML__ ([Triantafillou et al., 2020](https://openreview.net/pdf?id=rkgAGAVKPr)).\n", "We will focus on the task of few-shot classification where the training and test set have distinct sets of classes.\n", "For instance, we would train the model on the binary classifications of cats-birds and flowers-bikes, but during test time, the model would need to learn from 4 examples each the difference between dogs and otters, two classes we have not seen during training (Figure credit - [Lilian Weng](https://lilianweng.github.io/lil-log/2018/11/30/meta-learning.html)).\n", "\n", "
\n", "\n", "A different setup, which is very common in Reinforcement Learning and recently Natural Language Processing, is to aim at few-shot learning of a completely new task.\n", "For example, an robot agent that learned to run, jump and pick up boxes, should quickly adapt to collecting and stacking boxes.\n", "In NLP, we can think of a model which was trained sentiment classification, hatespeech detection and sarcasm classification, to adapt to classifying the emotion of a text.\n", "All methods we will discuss in this notebook can be easily applied to these settings since we only use a different definition of a 'task'.\n", "For few-shot classification, we consider a task to distinguish between $M$ novel classes.\n", "Here, we would not only have novel classes, but also a completely different dataset.\n", "\n", "First of all, let's start with importing our standard libraries. We will again be using PyTorch Lightning."]}, {"cell_type": "code", "execution_count": 2, "id": "a414f0fb", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:26.695087Z", "iopub.status.busy": "2021-10-10T16:36:26.694610Z", "iopub.status.idle": "2021-10-10T16:36:28.445235Z", "shell.execute_reply": "2021-10-10T16:36:28.444817Z"}, "papermill": {"duration": 1.794133, "end_time": "2021-10-10T16:36:28.445346", "exception": false, "start_time": "2021-10-10T16:36:26.651213", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_739/3072189054.py:29: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Device: cuda:0\n"]}, {"data": {"text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["import json\n", "import os\n", "import random\n", "import urllib.request\n", "from collections import defaultdict\n", "from copy import deepcopy\n", "from statistics import mean, stdev\n", "from urllib.error import HTTPError\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "import torch\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision\n", "from IPython.display import set_matplotlib_formats\n", "from PIL import Image\n", "from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint\n", "from torchvision import transforms\n", "from torchvision.datasets import CIFAR100, SVHN\n", "from tqdm.auto import tqdm\n", "\n", "plt.set_cmap(\"cividis\")\n", "# %matplotlib inline\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "matplotlib.rcParams[\"lines.linewidth\"] = 2.0\n", "sns.reset_orig()\n", "\n", "# Import tensorboard\n", "# %load_ext tensorboard\n", "\n", "# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data/\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/MetaLearning/\")\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", "print(\"Device:\", device)"]}, {"cell_type": "markdown", "id": "ce51286e", "metadata": {"papermill": {"duration": 0.034776, "end_time": "2021-10-10T16:36:28.518803", "exception": false, "start_time": "2021-10-10T16:36:28.484027", "status": "completed"}, "tags": []}, "source": ["Training the models in this notebook can take between 2 and 8 hours, and the evaluation time of some algorithms is in the span of couples of minutes.\n", "Hence, we download pre-trained models and results below."]}, {"cell_type": "code", "execution_count": 3, "id": "fddcdeaa", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:28.594883Z", "iopub.status.busy": "2021-10-10T16:36:28.594389Z", "iopub.status.idle": "2021-10-10T16:36:29.453405Z", "shell.execute_reply": "2021-10-10T16:36:29.452919Z"}, "papermill": {"duration": 0.899529, "end_time": "2021-10-10T16:36:29.453517", "exception": false, "start_time": "2021-10-10T16:36:28.553988", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial16/ProtoNet.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial16/ProtoMAML.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial16/tensorboards/ProtoNet/events.out.tfevents.ProtoNet...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial16/tensorboards/ProtoMAML/events.out.tfevents.ProtoMAML...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial16/protomaml_fewshot.json...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial16/protomaml_svhn_fewshot.json...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial16/\"\n", "# Files to download\n", "pretrained_files = [\n", " \"ProtoNet.ckpt\",\n", " \"ProtoMAML.ckpt\",\n", " \"tensorboards/ProtoNet/events.out.tfevents.ProtoNet\",\n", " \"tensorboards/ProtoMAML/events.out.tfevents.ProtoMAML\",\n", " \"protomaml_fewshot.json\",\n", " \"protomaml_svhn_fewshot.json\",\n", "]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if \"/\" in file_name:\n", " os.makedirs(file_path.rsplit(\"/\", 1)[0], exist_ok=True)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(\"Downloading %s...\" % file_url)\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "96d108b7", "metadata": {"papermill": {"duration": 0.038624, "end_time": "2021-10-10T16:36:29.528397", "exception": false, "start_time": "2021-10-10T16:36:29.489773", "status": "completed"}, "tags": []}, "source": ["## Few-shot classification\n", "\n", "We start our implementation by discussing the dataset setup.\n", "In this notebook, we will use CIFAR100 which we have already seen in Tutorial 6.\n", "CIFAR100 has 100 classes each with 600 images of size $32\\times 32$ pixels.\n", "Instead of splitting the training, validation and test set over examples, we will split them over classes: we will use 80 classes for training, and 10 for validation and 10 for testing.\n", "Our overall goal is to obtain a model that can distinguish between the 10 test classes with seeing very little examples.\n", "First, let's load the dataset and visualize some examples."]}, {"cell_type": "code", "execution_count": 4, "id": "2a4a4c43", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:29.603641Z", "iopub.status.busy": "2021-10-10T16:36:29.603170Z", "iopub.status.idle": "2021-10-10T16:36:34.357455Z", "shell.execute_reply": "2021-10-10T16:36:34.357029Z"}, "papermill": {"duration": 4.793304, "end_time": "2021-10-10T16:36:34.357578", "exception": false, "start_time": "2021-10-10T16:36:29.564274", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz to /__w/1/s/.datasets/cifar-100-python.tar.gz\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "f523d40c013f4da7bc639dc9291509e8", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/169001437 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:36:34.496593\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["# Visualize some examples\n", "NUM_IMAGES = 12\n", "cifar_images = [cifar_train_set[np.random.randint(len(cifar_train_set))][0] for idx in range(NUM_IMAGES)]\n", "cifar_images = torch.stack(cifar_images, dim=0)\n", "img_grid = torchvision.utils.make_grid(cifar_images, nrow=6, normalize=True, pad_value=0.9)\n", "img_grid = img_grid.permute(1, 2, 0)\n", "\n", "plt.figure(figsize=(8, 8))\n", "plt.title(\"Image examples of the CIFAR100 dataset\")\n", "plt.imshow(img_grid)\n", "plt.axis(\"off\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "6393f96b", "metadata": {"papermill": {"duration": 0.041466, "end_time": "2021-10-10T16:36:34.711913", "exception": false, "start_time": "2021-10-10T16:36:34.670447", "status": "completed"}, "tags": []}, "source": ["### Data preprocessing\n", "\n", "Next, we need to prepare the dataset in the training, validation and test split as mentioned before.\n", "The torchvision package gives us the training and test set as two separate dataset objects.\n", "The next code cells will merge the original training and test set, and then create the new train-val-test split."]}, {"cell_type": "code", "execution_count": 6, "id": "ce60f407", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:34.798013Z", "iopub.status.busy": "2021-10-10T16:36:34.797539Z", "iopub.status.idle": "2021-10-10T16:36:34.875295Z", "shell.execute_reply": "2021-10-10T16:36:34.875698Z"}, "papermill": {"duration": 0.122697, "end_time": "2021-10-10T16:36:34.875846", "exception": false, "start_time": "2021-10-10T16:36:34.753149", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Merging original training and test set\n", "cifar_all_images = np.concatenate([cifar_train_set.data, cifar_test_set.data], axis=0)\n", "cifar_all_targets = torch.LongTensor(cifar_train_set.targets + cifar_test_set.targets)"]}, {"cell_type": "markdown", "id": "3f939d72", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.040873, "end_time": "2021-10-10T16:36:34.959229", "exception": false, "start_time": "2021-10-10T16:36:34.918356", "status": "completed"}, "tags": []}, "source": ["To have an easier time handling the dataset, we define our own, simple dataset class below.\n", "It takes a set of images, labels/targets, and image transformations, and\n", "returns the corresponding images and labels element-wise."]}, {"cell_type": "code", "execution_count": 7, "id": "83d1fcac", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:35.051943Z", "iopub.status.busy": "2021-10-10T16:36:35.051455Z", "iopub.status.idle": "2021-10-10T16:36:35.053594Z", "shell.execute_reply": "2021-10-10T16:36:35.053130Z"}, "papermill": {"duration": 0.053299, "end_time": "2021-10-10T16:36:35.053692", "exception": false, "start_time": "2021-10-10T16:36:35.000393", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ImageDataset(data.Dataset):\n", " def __init__(self, imgs, targets, img_transform=None):\n", " \"\"\"\n", " Inputs:\n", " imgs - Numpy array of shape [N,32,32,3] containing all images.\n", " targets - PyTorch array of shape [N] containing all labels.\n", " img_transform - A torchvision transformation that should be applied\n", " to the images before returning. If none, no transformation\n", " is applied.\n", " \"\"\"\n", " super().__init__()\n", " self.img_transform = img_transform\n", " self.imgs = imgs\n", " self.targets = targets\n", "\n", " def __getitem__(self, idx):\n", " img, target = self.imgs[idx], self.targets[idx]\n", " img = Image.fromarray(img)\n", "\n", " if self.img_transform is not None:\n", " img = self.img_transform(img)\n", "\n", " return img, target\n", "\n", " def __len__(self):\n", " return self.imgs.shape[0]"]}, {"cell_type": "markdown", "id": "8afc6b05", "metadata": {"papermill": {"duration": 0.041697, "end_time": "2021-10-10T16:36:35.136827", "exception": false, "start_time": "2021-10-10T16:36:35.095130", "status": "completed"}, "tags": []}, "source": ["Now, we can create the class splits.\n", "We will assign the classes randomly to training, validation and test, and use a 80%-10%-10% split."]}, {"cell_type": "code", "execution_count": 8, "id": "5ff3abd6", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:35.223236Z", "iopub.status.busy": "2021-10-10T16:36:35.222768Z", "iopub.status.idle": "2021-10-10T16:36:35.226508Z", "shell.execute_reply": "2021-10-10T16:36:35.226037Z"}, "papermill": {"duration": 0.048464, "end_time": "2021-10-10T16:36:35.226609", "exception": false, "start_time": "2021-10-10T16:36:35.178145", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 0\n"]}], "source": ["pl.seed_everything(0) # Set seed for reproducibility\n", "classes = torch.randperm(100) # Returns random permutation of numbers 0 to 99\n", "train_classes, val_classes, test_classes = classes[:80], classes[80:90], classes[90:]"]}, {"cell_type": "markdown", "id": "32cdc6d7", "metadata": {"papermill": {"duration": 0.042958, "end_time": "2021-10-10T16:36:35.311335", "exception": false, "start_time": "2021-10-10T16:36:35.268377", "status": "completed"}, "tags": []}, "source": ["To get an intuition of the validation and test classes, we print the class names below:"]}, {"cell_type": "code", "execution_count": 9, "id": "3ba456f9", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:35.405497Z", "iopub.status.busy": "2021-10-10T16:36:35.405025Z", "iopub.status.idle": "2021-10-10T16:36:35.408515Z", "shell.execute_reply": "2021-10-10T16:36:35.408107Z"}, "papermill": {"duration": 0.052713, "end_time": "2021-10-10T16:36:35.408617", "exception": false, "start_time": "2021-10-10T16:36:35.355904", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Validation classes: ['caterpillar', 'castle', 'skunk', 'ray', 'bus', 'motorcycle', 'keyboard', 'chimpanzee', 'possum', 'tiger']\n", "Test classes: ['kangaroo', 'crocodile', 'butterfly', 'shark', 'forest', 'pickup_truck', 'telephone', 'lion', 'worm', 'mushroom']\n"]}], "source": ["# Printing validation and test classes\n", "idx_to_class = {val: key for key, val in cifar_train_set.class_to_idx.items()}\n", "print(\"Validation classes:\", [idx_to_class[c.item()] for c in val_classes])\n", "print(\"Test classes:\", [idx_to_class[c.item()] for c in test_classes])"]}, {"cell_type": "markdown", "id": "ce1f7d09", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.041936, "end_time": "2021-10-10T16:36:35.493337", "exception": false, "start_time": "2021-10-10T16:36:35.451401", "status": "completed"}, "tags": []}, "source": ["As we can see, the classes have quite some variety and some classes might be easier to distinguish than others.\n", "For instance, in the test classes, 'pickup_truck' is the only vehicle while the classes 'mushroom', 'worm' and 'forest' might be harder to keep apart.\n", "Remember that we want to learn the classification of those ten classes from 80 other classes in our training set, and few examples from the actual test classes.\n", "We will experiment with the number of examples per class.\n", "\n", "Finally, we can create the training, validation and test dataset according to our split above.\n", "For this, we create dataset objects of our previously defined class `ImageDataset`."]}, {"cell_type": "code", "execution_count": 10, "id": "32f068d1", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:35.581555Z", "iopub.status.busy": "2021-10-10T16:36:35.581087Z", "iopub.status.idle": "2021-10-10T16:36:35.583171Z", "shell.execute_reply": "2021-10-10T16:36:35.582711Z"}, "papermill": {"duration": 0.04763, "end_time": "2021-10-10T16:36:35.583269", "exception": false, "start_time": "2021-10-10T16:36:35.535639", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def dataset_from_labels(imgs, targets, class_set, **kwargs):\n", " class_mask = (targets[:, None] == class_set[None, :]).any(dim=-1)\n", " return ImageDataset(imgs=imgs[class_mask], targets=targets[class_mask], **kwargs)"]}, {"cell_type": "markdown", "id": "e8d4fb26", "metadata": {"papermill": {"duration": 0.041782, "end_time": "2021-10-10T16:36:35.667114", "exception": false, "start_time": "2021-10-10T16:36:35.625332", "status": "completed"}, "tags": []}, "source": ["As in our experiments before on CIFAR in Tutorial 5, 6 and 9, we normalize the dataset.\n", "Additionally, we use small augmentations during training to prevent overfitting."]}, {"cell_type": "code", "execution_count": 11, "id": "25bb398a", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:35.757452Z", "iopub.status.busy": "2021-10-10T16:36:35.756975Z", "iopub.status.idle": "2021-10-10T16:36:38.059891Z", "shell.execute_reply": "2021-10-10T16:36:38.059376Z"}, "papermill": {"duration": 2.350192, "end_time": "2021-10-10T16:36:38.060028", "exception": false, "start_time": "2021-10-10T16:36:35.709836", "status": "completed"}, "tags": []}, "outputs": [], "source": ["DATA_MEANS = (cifar_train_set.data / 255.0).mean(axis=(0, 1, 2))\n", "DATA_STD = (cifar_train_set.data / 255.0).std(axis=(0, 1, 2))\n", "\n", "test_transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize(DATA_MEANS, DATA_STD)])\n", "# For training, we add some augmentation.\n", "train_transform = transforms.Compose(\n", " [\n", " transforms.RandomHorizontalFlip(),\n", " transforms.RandomResizedCrop((32, 32), scale=(0.8, 1.0), ratio=(0.9, 1.1)),\n", " transforms.ToTensor(),\n", " transforms.Normalize(DATA_MEANS, DATA_STD),\n", " ]\n", ")\n", "\n", "train_set = dataset_from_labels(cifar_all_images, cifar_all_targets, train_classes, img_transform=train_transform)\n", "val_set = dataset_from_labels(cifar_all_images, cifar_all_targets, val_classes, img_transform=test_transform)\n", "test_set = dataset_from_labels(cifar_all_images, cifar_all_targets, test_classes, img_transform=test_transform)"]}, {"cell_type": "markdown", "id": "3456a8f9", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042069, "end_time": "2021-10-10T16:36:38.145286", "exception": false, "start_time": "2021-10-10T16:36:38.103217", "status": "completed"}, "tags": []}, "source": ["### Data sampling\n", "\n", "The strategy of how to use the available training data for learning few-shot adaptation is crucial in meta-learning.\n", "All three algorithms that we discuss here have a similar idea: simulate few-shot learning during training.\n", "Specifically, at each training step, we randomly select a small number of classes, and sample a small number of examples for each class.\n", "This represents our few-shot training batch, which we also refer to as **support set**.\n", "Additionally, we sample a second set of examples from the same classes, and refer to this batch as **query set**.\n", "Our training objective is to classify the query set correctly from seeing the support set and its corresponding labels.\n", "The main difference between our three methods (ProtoNet, MAML, and Proto-MAML) is in how they use the support set to adapt to the training classes.\n", "\n", "This subsection summarizes the code that is needed to create such training batches.\n", "In PyTorch, we can specify the data sampling procedure by so-called `Sampler` ([documentation](https://pytorch.org/docs/stable/data.html#data-loading-order-and-sampler)).\n", "Samplers are iteratable objects that return indices in the order in which the data elements should be sampled.\n", "In our previous notebooks, we usually used the option `shuffle=True` in the `data.DataLoader` objects which creates a sampler returning the data indices in a random order.\n", "Here, we focus on samplers that return batches of indices that correspond to support and query set batches.\n", "Below, we implement such a sampler."]}, {"cell_type": "code", "execution_count": 12, "id": "c0bad2d9", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:38.237449Z", "iopub.status.busy": "2021-10-10T16:36:38.236947Z", "iopub.status.idle": "2021-10-10T16:36:38.243012Z", "shell.execute_reply": "2021-10-10T16:36:38.242609Z"}, "papermill": {"duration": 0.055637, "end_time": "2021-10-10T16:36:38.243112", "exception": false, "start_time": "2021-10-10T16:36:38.187475", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class FewShotBatchSampler:\n", " def __init__(self, dataset_targets, N_way, K_shot, include_query=False, shuffle=True, shuffle_once=False):\n", " \"\"\"\n", " Inputs:\n", " dataset_targets - PyTorch tensor of the labels of the data elements.\n", " N_way - Number of classes to sample per batch.\n", " K_shot - Number of examples to sample per class in the batch.\n", " include_query - If True, returns batch of size N_way*K_shot*2, which\n", " can be split into support and query set. Simplifies\n", " the implementation of sampling the same classes but\n", " distinct examples for support and query set.\n", " shuffle - If True, examples and classes are newly shuffled in each\n", " iteration (for training)\n", " shuffle_once - If True, examples and classes are shuffled once in\n", " the beginning, but kept constant across iterations\n", " (for validation)\n", " \"\"\"\n", " super().__init__()\n", " self.dataset_targets = dataset_targets\n", " self.N_way = N_way\n", " self.K_shot = K_shot\n", " self.shuffle = shuffle\n", " self.include_query = include_query\n", " if self.include_query:\n", " self.K_shot *= 2\n", " self.batch_size = self.N_way * self.K_shot # Number of overall images per batch\n", "\n", " # Organize examples by class\n", " self.classes = torch.unique(self.dataset_targets).tolist()\n", " self.num_classes = len(self.classes)\n", " self.indices_per_class = {}\n", " self.batches_per_class = {} # Number of K-shot batches that each class can provide\n", " for c in self.classes:\n", " self.indices_per_class[c] = torch.where(self.dataset_targets == c)[0]\n", " self.batches_per_class[c] = self.indices_per_class[c].shape[0] // self.K_shot\n", "\n", " # Create a list of classes from which we select the N classes per batch\n", " self.iterations = sum(self.batches_per_class.values()) // self.N_way\n", " self.class_list = [c for c in self.classes for _ in range(self.batches_per_class[c])]\n", " if shuffle_once or self.shuffle:\n", " self.shuffle_data()\n", " else:\n", " # For testing, we iterate over classes instead of shuffling them\n", " sort_idxs = [\n", " i + p * self.num_classes for i, c in enumerate(self.classes) for p in range(self.batches_per_class[c])\n", " ]\n", " self.class_list = np.array(self.class_list)[np.argsort(sort_idxs)].tolist()\n", "\n", " def shuffle_data(self):\n", " # Shuffle the examples per class\n", " for c in self.classes:\n", " perm = torch.randperm(self.indices_per_class[c].shape[0])\n", " self.indices_per_class[c] = self.indices_per_class[c][perm]\n", " # Shuffle the class list from which we sample. Note that this way of shuffling\n", " # does not prevent to choose the same class twice in a batch. However, for\n", " # training and validation, this is not a problem.\n", " random.shuffle(self.class_list)\n", "\n", " def __iter__(self):\n", " # Shuffle data\n", " if self.shuffle:\n", " self.shuffle_data()\n", "\n", " # Sample few-shot batches\n", " start_index = defaultdict(int)\n", " for it in range(self.iterations):\n", " class_batch = self.class_list[it * self.N_way : (it + 1) * self.N_way] # Select N classes for the batch\n", " index_batch = []\n", " for c in class_batch: # For each class, select the next K examples and add them to the batch\n", " index_batch.extend(self.indices_per_class[c][start_index[c] : start_index[c] + self.K_shot])\n", " start_index[c] += self.K_shot\n", " if self.include_query: # If we return support+query set, sort them so that they are easy to split\n", " index_batch = index_batch[::2] + index_batch[1::2]\n", " yield index_batch\n", "\n", " def __len__(self):\n", " return self.iterations"]}, {"cell_type": "markdown", "id": "966baf43", "metadata": {"papermill": {"duration": 0.041981, "end_time": "2021-10-10T16:36:38.327241", "exception": false, "start_time": "2021-10-10T16:36:38.285260", "status": "completed"}, "tags": []}, "source": ["Now, we can create our intended data loaders by passing an object of `FewShotBatchSampler` as `batch_sampler=...` input to the PyTorch data loader object.\n", "For our experiments, we will use a 5-class 4-shot training setting.\n", "This means that each support set contains 5 classes with 4 examples each, i.e., 20 images overall.\n", "Usually, it is good to keep the number of shots equal to the number that you aim to test on.\n", "However, we will experiment later with different number of shots, and hence, we pick 4 as a compromise for now.\n", "To get the best performing model, it is recommended to consider the\n", "number of training shots as hyperparameter in a grid search."]}, {"cell_type": "code", "execution_count": 13, "id": "6e5eee4a", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:38.415592Z", "iopub.status.busy": "2021-10-10T16:36:38.415120Z", "iopub.status.idle": "2021-10-10T16:36:38.437251Z", "shell.execute_reply": "2021-10-10T16:36:38.436842Z"}, "papermill": {"duration": 0.068099, "end_time": "2021-10-10T16:36:38.437354", "exception": false, "start_time": "2021-10-10T16:36:38.369255", "status": "completed"}, "tags": []}, "outputs": [], "source": ["N_WAY = 5\n", "K_SHOT = 4\n", "train_data_loader = data.DataLoader(\n", " train_set,\n", " batch_sampler=FewShotBatchSampler(train_set.targets, include_query=True, N_way=N_WAY, K_shot=K_SHOT, shuffle=True),\n", " num_workers=4,\n", ")\n", "val_data_loader = data.DataLoader(\n", " val_set,\n", " batch_sampler=FewShotBatchSampler(\n", " val_set.targets, include_query=True, N_way=N_WAY, K_shot=K_SHOT, shuffle=False, shuffle_once=True\n", " ),\n", " num_workers=4,\n", ")"]}, {"cell_type": "markdown", "id": "3105a10e", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042422, "end_time": "2021-10-10T16:36:38.521966", "exception": false, "start_time": "2021-10-10T16:36:38.479544", "status": "completed"}, "tags": []}, "source": ["For simplicity, we implemented the sampling of a support and query set as sampling a support set with twice the number of examples.\n", "After sampling a batch from the data loader, we need to split it into a support and query set.\n", "We can summarize this step in the following function:"]}, {"cell_type": "code", "execution_count": 14, "id": "928a254e", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:38.609800Z", "iopub.status.busy": "2021-10-10T16:36:38.608312Z", "iopub.status.idle": "2021-10-10T16:36:38.611864Z", "shell.execute_reply": "2021-10-10T16:36:38.611397Z"}, "papermill": {"duration": 0.048021, "end_time": "2021-10-10T16:36:38.611980", "exception": false, "start_time": "2021-10-10T16:36:38.563959", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def split_batch(imgs, targets):\n", " support_imgs, query_imgs = imgs.chunk(2, dim=0)\n", " support_targets, query_targets = targets.chunk(2, dim=0)\n", " return support_imgs, query_imgs, support_targets, query_targets"]}, {"cell_type": "markdown", "id": "ac8356fb", "metadata": {"papermill": {"duration": 0.041927, "end_time": "2021-10-10T16:36:38.696240", "exception": false, "start_time": "2021-10-10T16:36:38.654313", "status": "completed"}, "tags": []}, "source": ["Finally, to ensure that our implementation of the data sampling process is correct, we can sample a batch and visualize its support and query set.\n", "What we would like to see is that the support and query set have the same classes, but distinct examples."]}, {"cell_type": "code", "execution_count": 15, "id": "18340de4", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:38.786640Z", "iopub.status.busy": "2021-10-10T16:36:38.786168Z", "iopub.status.idle": "2021-10-10T16:36:39.045016Z", "shell.execute_reply": "2021-10-10T16:36:39.044567Z"}, "papermill": {"duration": 0.306369, "end_time": "2021-10-10T16:36:39.045134", "exception": false, "start_time": "2021-10-10T16:36:38.738765", "status": "completed"}, "tags": []}, "outputs": [], "source": ["imgs, targets = next(iter(val_data_loader)) # We use the validation set since it does not apply augmentations\n", "support_imgs, query_imgs, _, _ = split_batch(imgs, targets)\n", "support_grid = torchvision.utils.make_grid(support_imgs, nrow=K_SHOT, normalize=True, pad_value=0.9)\n", "support_grid = support_grid.permute(1, 2, 0)\n", "query_grid = torchvision.utils.make_grid(query_imgs, nrow=K_SHOT, normalize=True, pad_value=0.9)\n", "query_grid = query_grid.permute(1, 2, 0)\n", "\n", "fig, ax = plt.subplots(1, 2, figsize=(8, 5))\n", "ax[0].imshow(support_grid)\n", "ax[0].set_title(\"Support set\")\n", "ax[0].axis(\"off\")\n", "ax[1].imshow(query_grid)\n", "ax[1].set_title(\"Query set\")\n", "ax[1].axis(\"off\")\n", "fig.suptitle(\"Few Shot Batch\", weight=\"bold\")\n", "fig.show()\n", "plt.close(fig)"]}, {"cell_type": "markdown", "id": "06b51e8e", "metadata": {"papermill": {"duration": 0.042585, "end_time": "2021-10-10T16:36:39.130534", "exception": false, "start_time": "2021-10-10T16:36:39.087949", "status": "completed"}, "tags": []}, "source": ["As we can see, the support and query set have the same five classes, but different examples.\n", "The models will be tasked to classify the examples in the query set by learning from the support set and its labels.\n", "With the data sampling in place, we can now start to implement our first meta-learning model: Prototypical Networks."]}, {"cell_type": "markdown", "id": "40315428", "metadata": {"papermill": {"duration": 0.042341, "end_time": "2021-10-10T16:36:39.214888", "exception": false, "start_time": "2021-10-10T16:36:39.172547", "status": "completed"}, "tags": []}, "source": ["## Prototypical Networks\n", "\n", "
"]}, {"cell_type": "markdown", "id": "e32fdcd0", "metadata": {"papermill": {"duration": 0.042438, "end_time": "2021-10-10T16:36:39.299755", "exception": false, "start_time": "2021-10-10T16:36:39.257317", "status": "completed"}, "tags": []}, "source": ["The Prototypical Network, or ProtoNet for short, is a metric-based meta-learning algorithm which operates similar to a nearest neighbor classification.\n", "Metric-based meta-learning methods classify a new example $\\mathbf{x}$ based on some distance function $d_{\\varphi}$ between $x$ and all elements in the support set.\n", "ProtoNets implements this idea with the concept of prototypes in a learned feature space.\n", "First, ProtoNet uses an embedding function $f_{\\theta}$ to encode each input in the support set into a $L$-dimensional feature vector.\n", "Next, for each class $c$, we collect the feature vectors of all examples with label $c$, and average their feature vectors.\n", "Formally, we can define this as:\n", "\n", "$$\\mathbf{v}_c=\\frac{1}{|S_c|}\\sum_{(\\mathbf{x}_i,y_i)\\in S_c}f_{\\theta}(\\mathbf{x}_i)$$\n", "\n", "where $S_c$ is the part of the support set $S$ for which $y_i=c$, and $\\mathbf{v}_c$ represents the _prototype_ of class $c$.\n", "The prototype calculation is visualized below for a 2-dimensional feature space and 3 classes (Figure credit - [Snell et al.](https://arxiv.org/pdf/1703.05175.pdf)).\n", "The colored dots represent encoded support elements with color-corresponding class label, and the black dots next to the class label are the averaged prototypes.\n", "\n", "
\n", "\n", "Based on these prototypes, we want to classify a new example.\n", "Remember that since we want to learn the encoding function $f_{\\theta}$, this classification must be differentiable and hence, we need to define a probability distribution across classes.\n", "For this, we will make use of the distance function $d_{\\varphi}$: the closer a new example $\\mathbf{x}$ is to a prototype $\\mathbf{v}_c$, the higher the probability for $\\mathbf{x}$ belonging to class $c$.\n", "Formally, we can simply use a softmax over the distances of $\\mathbf{x}$ to all class prototypes:\n", "\n", "$$p(y=c\\vert\\mathbf{x})=\\text{softmax}(-d_{\\varphi}(f_{\\theta}(\\mathbf{x}), \\mathbf{v}_c))=\\frac{\\exp\\left(-d_{\\varphi}(f_{\\theta}(\\mathbf{x}), \\mathbf{v}_c)\\right)}{\\sum_{c'\\in \\mathcal{C}}\\exp\\left(-d_{\\varphi}(f_{\\theta}(\\mathbf{x}), \\mathbf{v}_{c'})\\right)}$$\n", "\n", "Note that the negative sign is necessary since we want to increase the probability for close-by vectors and have a low probability for distant vectors.\n", "We train the network $f_{\\theta}$ based on the cross entropy error of the training query set examples.\n", "Thereby, the gradient flows through both the prototypes $\\mathbf{v}_c$ and the query set encodings $f_{\\theta}(\\mathbf{x})$.\n", "For the distance function $d_{\\varphi}$, we can choose any function as long as it is differentiable with respect to both of its inputs.\n", "The most common function, which we also use here, is the squared\n", "euclidean distance, but there has been several works on different\n", "distance functions as well."]}, {"cell_type": "markdown", "id": "d400bfc2", "metadata": {"papermill": {"duration": 0.042184, "end_time": "2021-10-10T16:36:39.384014", "exception": false, "start_time": "2021-10-10T16:36:39.341830", "status": "completed"}, "tags": []}, "source": ["### ProtoNet implementation"]}, {"cell_type": "markdown", "id": "a10ef367", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042386, "end_time": "2021-10-10T16:36:39.468678", "exception": false, "start_time": "2021-10-10T16:36:39.426292", "status": "completed"}, "tags": []}, "source": ["Now that we know how a ProtoNet works in principle, let's look at how we can apply to our specific problem of few-shot image classification, and implement it below.\n", "First, we need to define the encoder function $f_{\\theta}$.\n", "Since we work with CIFAR images, we can take a look back at Tutorial 5 where we compared common Computer Vision architectures, and choose one of the best performing ones.\n", "Here, we go with a DenseNet since it is in general more parameter efficient than ResNet.\n", "Luckily, we do not need to implement DenseNet ourselves again and can rely on torchvision's model package instead.\n", "We use common hyperparameters of 64 initial feature channels, add 32 per block, and use a bottleneck size of 64 (i.e. 2 times the growth rate).\n", "We use 4 stages of 6 layers each, which results in overall about 1 million parameters.\n", "Note that the torchvision package assumes that the last layer is used for classification and hence calls its output size `num_classes`.\n", "However, we can instead just use it as the feature space of ProtoNet, and choose an arbitrary dimensionality.\n", "We will use the same network for other algorithms in this notebook to ensure a fair comparison."]}, {"cell_type": "code", "execution_count": 16, "id": "2ba373cc", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:39.567463Z", "iopub.status.busy": "2021-10-10T16:36:39.566989Z", "iopub.status.idle": "2021-10-10T16:36:39.569062Z", "shell.execute_reply": "2021-10-10T16:36:39.568593Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.056702, "end_time": "2021-10-10T16:36:39.569165", "exception": false, "start_time": "2021-10-10T16:36:39.512463", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def get_convnet(output_size):\n", " convnet = torchvision.models.DenseNet(\n", " growth_rate=32,\n", " block_config=(6, 6, 6, 6),\n", " bn_size=2,\n", " num_init_features=64,\n", " num_classes=output_size, # Output dimensionality\n", " )\n", " return convnet"]}, {"cell_type": "markdown", "id": "50da6f0b", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042572, "end_time": "2021-10-10T16:36:39.654722", "exception": false, "start_time": "2021-10-10T16:36:39.612150", "status": "completed"}, "tags": []}, "source": ["Next, we can look at implementing ProtoNet.\n", "We will define it as PyTorch Lightning module to use all functionalities of PyTorch Lightning.\n", "The first step during training is to encode all images in a batch with our network.\n", "Next, we calculate the class prototypes from the support set (function `calculate_prototypes`), and classify the query set examples according to the prototypes (function `classify_feats`).\n", "Keep in mind that we use the data sampling described before, such that the support and query set are stacked together in the batch.\n", "Thus, we use our previously defined function `split_batch` to split them apart.\n", "The full code can be found below."]}, {"cell_type": "code", "execution_count": 17, "id": "08c3adca", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:39.751136Z", "iopub.status.busy": "2021-10-10T16:36:39.750647Z", "iopub.status.idle": "2021-10-10T16:36:39.752752Z", "shell.execute_reply": "2021-10-10T16:36:39.752285Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.055843, "end_time": "2021-10-10T16:36:39.752853", "exception": false, "start_time": "2021-10-10T16:36:39.697010", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ProtoNet(pl.LightningModule):\n", " def __init__(self, proto_dim, lr):\n", " \"\"\"Inputs.\n", "\n", " proto_dim - Dimensionality of prototype feature space\n", " lr - Learning rate of Adam optimizer\n", " \"\"\"\n", " super().__init__()\n", " self.save_hyperparameters()\n", " self.model = get_convnet(output_size=self.hparams.proto_dim)\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr)\n", " scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[140, 180], gamma=0.1)\n", " return [optimizer], [scheduler]\n", "\n", " @staticmethod\n", " def calculate_prototypes(features, targets):\n", " # Given a stack of features vectors and labels, return class prototypes\n", " # features - shape [N, proto_dim], targets - shape [N]\n", " classes, _ = torch.unique(targets).sort() # Determine which classes we have\n", " prototypes = []\n", " for c in classes:\n", " p = features[torch.where(targets == c)[0]].mean(dim=0) # Average class feature vectors\n", " prototypes.append(p)\n", " prototypes = torch.stack(prototypes, dim=0)\n", " # Return the 'classes' tensor to know which prototype belongs to which class\n", " return prototypes, classes\n", "\n", " def classify_feats(self, prototypes, classes, feats, targets):\n", " # Classify new examples with prototypes and return classification error\n", " dist = torch.pow(prototypes[None, :] - feats[:, None], 2).sum(dim=2) # Squared euclidean distance\n", " preds = F.log_softmax(-dist, dim=1)\n", " labels = (classes[None, :] == targets[:, None]).long().argmax(dim=-1)\n", " acc = (preds.argmax(dim=1) == labels).float().mean()\n", " return preds, labels, acc\n", "\n", " def calculate_loss(self, batch, mode):\n", " # Determine training loss for a given support and query set\n", " imgs, targets = batch\n", " features = self.model(imgs) # Encode all images of support and query set\n", " support_feats, query_feats, support_targets, query_targets = split_batch(features, targets)\n", " prototypes, classes = ProtoNet.calculate_prototypes(support_feats, support_targets)\n", " preds, labels, acc = self.classify_feats(prototypes, classes, query_feats, query_targets)\n", " loss = F.cross_entropy(preds, labels)\n", "\n", " self.log(\"%s_loss\" % mode, loss)\n", " self.log(\"%s_acc\" % mode, acc)\n", " return loss\n", "\n", " def training_step(self, batch, batch_idx):\n", " return self.calculate_loss(batch, mode=\"train\")\n", "\n", " def validation_step(self, batch, batch_idx):\n", " self.calculate_loss(batch, mode=\"val\")"]}, {"cell_type": "markdown", "id": "1d28cde2", "metadata": {"papermill": {"duration": 0.042403, "end_time": "2021-10-10T16:36:39.837555", "exception": false, "start_time": "2021-10-10T16:36:39.795152", "status": "completed"}, "tags": []}, "source": ["For validation, we use the same principle as training and sample support and query sets from the hold-out 10 classes.\n", "However, this gives us noisy scores depending on which query sets are chosen to which support sets.\n", "This is why we will use a different strategy during testing.\n", "For validation, our training strategy is sufficient since it is much\n", "faster than testing, and gives a good estimate of the training\n", "generalization as long as we keep the support-query sets constant across\n", "validation iterations."]}, {"cell_type": "markdown", "id": "02222c05", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.042841, "end_time": "2021-10-10T16:36:39.923166", "exception": false, "start_time": "2021-10-10T16:36:39.880325", "status": "completed"}, "tags": []}, "source": ["### Training\n", "\n", "After implementing the model, we can already start training it.\n", "We use our common PyTorch Lightning training function, and train the model for 200 epochs.\n", "The training function takes `model_class` as input argument, i.e. the\n", "PyTorch Lightning module class that should be trained, since we will\n", "reuse this function for other algorithms as well."]}, {"cell_type": "code", "execution_count": 18, "id": "76c40dce", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:40.014200Z", "iopub.status.busy": "2021-10-10T16:36:40.013719Z", "iopub.status.idle": "2021-10-10T16:36:40.015817Z", "shell.execute_reply": "2021-10-10T16:36:40.015348Z"}, "papermill": {"duration": 0.050208, "end_time": "2021-10-10T16:36:40.015917", "exception": false, "start_time": "2021-10-10T16:36:39.965709", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_model(model_class, train_loader, val_loader, **kwargs):\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, model_class.__name__),\n", " gpus=1 if str(device) == \"cuda:0\" else 0,\n", " max_epochs=200,\n", " callbacks=[\n", " ModelCheckpoint(save_weights_only=True, mode=\"max\", monitor=\"val_acc\"),\n", " LearningRateMonitor(\"epoch\"),\n", " ],\n", " progress_bar_refresh_rate=0,\n", " )\n", " trainer.logger._default_hp_metric = None\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, model_class.__name__ + \".ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model at %s, loading...\" % pretrained_filename)\n", " # Automatically loads the model with the saved hyperparameters\n", " model = model_class.load_from_checkpoint(pretrained_filename)\n", " else:\n", " pl.seed_everything(42) # To be reproducable\n", " model = model_class(**kwargs)\n", " trainer.fit(model, train_loader, val_loader)\n", " model = model_class.load_from_checkpoint(\n", " trainer.checkpoint_callback.best_model_path\n", " ) # Load best checkpoint after training\n", "\n", " return model"]}, {"cell_type": "markdown", "id": "6bd74652", "metadata": {"papermill": {"duration": 0.043123, "end_time": "2021-10-10T16:36:40.102077", "exception": false, "start_time": "2021-10-10T16:36:40.058954", "status": "completed"}, "tags": []}, "source": ["Below is the training call for our ProtoNet.\n", "We use a 64-dimensional feature space.\n", "Larger feature spaces showed to give noisier results since the squared euclidean distance becomes proportionally larger in expectation, and smaller feature spaces might not allow for enough flexibility.\n", "We recommend to load the pre-trained model here at first, but feel free\n", "to play around with the hyperparameters yourself."]}, {"cell_type": "code", "execution_count": 19, "id": "91cae9d3", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:40.192927Z", "iopub.status.busy": "2021-10-10T16:36:40.192456Z", "iopub.status.idle": "2021-10-10T16:36:40.257180Z", "shell.execute_reply": "2021-10-10T16:36:40.256698Z"}, "papermill": {"duration": 0.1128, "end_time": "2021-10-10T16:36:40.257291", "exception": false, "start_time": "2021-10-10T16:36:40.144491", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/MetaLearning/ProtoNet.ckpt, loading...\n"]}], "source": ["protonet_model = train_model(\n", " ProtoNet, proto_dim=64, lr=2e-4, train_loader=train_data_loader, val_loader=val_data_loader\n", ")"]}, {"cell_type": "markdown", "id": "92207f47", "metadata": {"papermill": {"duration": 0.043516, "end_time": "2021-10-10T16:36:40.344935", "exception": false, "start_time": "2021-10-10T16:36:40.301419", "status": "completed"}, "tags": []}, "source": ["We can also take a closer look at the TensorBoard below."]}, {"cell_type": "code", "execution_count": 20, "id": "7b2883ee", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:40.435675Z", "iopub.status.busy": "2021-10-10T16:36:40.435210Z", "iopub.status.idle": "2021-10-10T16:36:40.437301Z", "shell.execute_reply": "2021-10-10T16:36:40.436898Z"}, "papermill": {"duration": 0.048812, "end_time": "2021-10-10T16:36:40.437398", "exception": false, "start_time": "2021-10-10T16:36:40.388586", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH if needed\n", "# # %tensorboard --logdir ../saved_models/tutorial16/tensorboards/ProtoNet/"]}, {"cell_type": "markdown", "id": "a96395e2", "metadata": {"papermill": {"duration": 0.043172, "end_time": "2021-10-10T16:36:40.524550", "exception": false, "start_time": "2021-10-10T16:36:40.481378", "status": "completed"}, "tags": []}, "source": ["
\n", "\n", "In contrast to standard supervised learning, we see that ProtoNet does not overfit as much as we would expect.\n", "The validation accuracy is of course lower than the average training, but the training loss does not stick close to zero.\n", "This is because no training batch is as the other, and we also mix new examples in the support set and query set.\n", "This gives us slightly different prototypes in every iteration, and makes it harder for the network to fully overfit."]}, {"cell_type": "markdown", "id": "fe724d6e", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.043726, "end_time": "2021-10-10T16:36:40.611913", "exception": false, "start_time": "2021-10-10T16:36:40.568187", "status": "completed"}, "tags": []}, "source": ["### Testing\n", "\n", "Our goal of meta-learning is to obtain a model that can quickly adapt to a new task, or in this case, new classes to distinguish between.\n", "To test this, we will use our trained ProtoNet and adapt it to the 10 test classes.\n", "Thereby, we pick $k$ examples per class from which we determine the prototypes, and test the classification accuracy on all other examples.\n", "This can be seen as using the $k$ examples per class as support set, and the rest of the dataset as a query set.\n", "We iterate through the dataset such that each example has been once included in a support set.\n", "The average performance over all support sets tells us how well we can expect ProtoNet to perform when seeing only $k$ examples per class.\n", "During training, we used $k=4$.\n", "In testing, we will experiment with $k=\\{2,4,8,16,32\\}$ to get a better sense of how $k$ influences the results.\n", "We would expect that we achieve higher accuracies the more examples we have in the support set, but we don't know how it scales.\n", "Hence, let's first implement a function that executes the testing procedure for a given $k$:"]}, {"cell_type": "code", "execution_count": 21, "id": "9e43469f", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:40.708839Z", "iopub.status.busy": "2021-10-10T16:36:40.706609Z", "iopub.status.idle": "2021-10-10T16:36:40.710916Z", "shell.execute_reply": "2021-10-10T16:36:40.710471Z"}, "papermill": {"duration": 0.055241, "end_time": "2021-10-10T16:36:40.711017", "exception": false, "start_time": "2021-10-10T16:36:40.655776", "status": "completed"}, "tags": []}, "outputs": [], "source": ["@torch.no_grad()\n", "def test_proto_net(model, dataset, data_feats=None, k_shot=4):\n", " \"\"\"Inputs.\n", "\n", " model - Pretrained ProtoNet model\n", " dataset - The dataset on which the test should be performed.\n", " Should be instance of ImageDataset\n", " data_feats - The encoded features of all images in the dataset.\n", " If None, they will be newly calculated, and returned\n", " for later usage.\n", " k_shot - Number of examples per class in the support set.\n", " \"\"\"\n", " model = model.to(device)\n", " model.eval()\n", " num_classes = dataset.targets.unique().shape[0]\n", " exmps_per_class = dataset.targets.shape[0] // num_classes # We assume uniform example distribution here\n", "\n", " # The encoder network remains unchanged across k-shot settings. Hence, we only need\n", " # to extract the features for all images once.\n", " if data_feats is None:\n", " # Dataset preparation\n", " dataloader = data.DataLoader(dataset, batch_size=128, num_workers=4, shuffle=False, drop_last=False)\n", "\n", " img_features = []\n", " img_targets = []\n", " for imgs, targets in tqdm(dataloader, \"Extracting image features\", leave=False):\n", " imgs = imgs.to(device)\n", " feats = model.model(imgs)\n", " img_features.append(feats.detach().cpu())\n", " img_targets.append(targets)\n", " img_features = torch.cat(img_features, dim=0)\n", " img_targets = torch.cat(img_targets, dim=0)\n", " # Sort by classes, so that we obtain tensors of shape [num_classes, exmps_per_class, ...]\n", " # Makes it easier to process later\n", " img_targets, sort_idx = img_targets.sort()\n", " img_targets = img_targets.reshape(num_classes, exmps_per_class).transpose(0, 1)\n", " img_features = img_features[sort_idx].reshape(num_classes, exmps_per_class, -1).transpose(0, 1)\n", " else:\n", " img_features, img_targets = data_feats\n", "\n", " # We iterate through the full dataset in two manners. First, to select the k-shot batch.\n", " # Second, the evaluate the model on all other examples\n", " accuracies = []\n", " for k_idx in tqdm(range(0, img_features.shape[0], k_shot), \"Evaluating prototype classification\", leave=False):\n", " # Select support set and calculate prototypes\n", " k_img_feats = img_features[k_idx : k_idx + k_shot].flatten(0, 1)\n", " k_targets = img_targets[k_idx : k_idx + k_shot].flatten(0, 1)\n", " prototypes, proto_classes = model.calculate_prototypes(k_img_feats, k_targets)\n", " # Evaluate accuracy on the rest of the dataset\n", " batch_acc = 0\n", " for e_idx in range(0, img_features.shape[0], k_shot):\n", " if k_idx == e_idx: # Do not evaluate on the support set examples\n", " continue\n", " e_img_feats = img_features[e_idx : e_idx + k_shot].flatten(0, 1)\n", " e_targets = img_targets[e_idx : e_idx + k_shot].flatten(0, 1)\n", " _, _, acc = model.classify_feats(prototypes, proto_classes, e_img_feats, e_targets)\n", " batch_acc += acc.item()\n", " batch_acc /= img_features.shape[0] // k_shot - 1\n", " accuracies.append(batch_acc)\n", "\n", " return (mean(accuracies), stdev(accuracies)), (img_features, img_targets)"]}, {"cell_type": "markdown", "id": "e8adff5f", "metadata": {"papermill": {"duration": 0.043398, "end_time": "2021-10-10T16:36:40.798077", "exception": false, "start_time": "2021-10-10T16:36:40.754679", "status": "completed"}, "tags": []}, "source": ["Testing ProtoNet is relatively quick if we have processed all images once. Hence, we can do in this notebook:"]}, {"cell_type": "code", "execution_count": 22, "id": "6b522e34", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:36:40.891051Z", "iopub.status.busy": "2021-10-10T16:36:40.890582Z", "iopub.status.idle": "2021-10-10T16:37:25.334685Z", "shell.execute_reply": "2021-10-10T16:37:25.334200Z"}, "papermill": {"duration": 44.492916, "end_time": "2021-10-10T16:37:25.334799", "exception": false, "start_time": "2021-10-10T16:36:40.841883", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "59b3b0d90a7548ae8ec79d1fb1aa783e", "version_major": 2, "version_minor": 0}, "text/plain": ["Extracting image features: 0%| | 0/47 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:37:25.755286\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["ax = plot_few_shot(protonet_accuracies, name=\"ProtoNet\", color=\"C1\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "d99265a0", "metadata": {"papermill": {"duration": 0.04916, "end_time": "2021-10-10T16:37:26.050508", "exception": false, "start_time": "2021-10-10T16:37:26.001348", "status": "completed"}, "tags": []}, "source": ["As we initially expected, the performance of ProtoNet indeed increases the more samples we have.\n", "However, even with just two samples per class, we classify almost half of the images correctly, which is well above random accuracy (10%).\n", "The curve shows an exponentially dampend trend, meaning that adding 2 extra examples to $k=2$ has a much higher impact than adding 2 extra samples if we already have $k=16$.\n", "Nonetheless, we can say that ProtoNet adapts fairly well to new classes."]}, {"cell_type": "markdown", "id": "41e5a54a", "metadata": {"papermill": {"duration": 0.05058, "end_time": "2021-10-10T16:37:26.152476", "exception": false, "start_time": "2021-10-10T16:37:26.101896", "status": "completed"}, "tags": []}, "source": ["## MAML and ProtoMAML\n", "\n", "
"]}, {"cell_type": "markdown", "id": "209a64ec", "metadata": {"papermill": {"duration": 0.051648, "end_time": "2021-10-10T16:37:26.254669", "exception": false, "start_time": "2021-10-10T16:37:26.203021", "status": "completed"}, "tags": []}, "source": ["The second meta-learning algorithm we will look at is MAML, short for Model-Agnostic Meta-Learning.\n", "MAML is an optimization-based meta-learning algorithm, which means that it tries to adjust the standard optimization procedure to a few-shot setting.\n", "The idea of MAML is relatively simple: given a model, support and query set during training, we optimize the model for $m$ steps on the support set, and evaluate the gradients of the query loss with respect to the original model's parameters.\n", "For the same model, we do it for a few different support-query sets and accumulate the gradients.\n", "This results in learning a model that provides a good initialization for being quickly adapted to the training tasks.\n", "If we denote the model parameters with $\\theta$, we can visualize the procedure as follows (Figure credit - [Finn et al. ](http://proceedings.mlr.press/v70/finn17a.html)).\n", "\n", "
"]}, {"cell_type": "markdown", "id": "d0f6ca26", "metadata": {"papermill": {"duration": 0.049624, "end_time": "2021-10-10T16:37:26.354505", "exception": false, "start_time": "2021-10-10T16:37:26.304881", "status": "completed"}, "tags": []}, "source": ["The full algorithm of MAML is therefore as follows.\n", "At each training step, we sample a batch of tasks, i.e., a batch of support-query set pairs.\n", "For each task $\\mathcal{T}_i$, we optimize a model $f_{\\theta}$ on the support set via SGD, and denote this model as $f_{\\theta_i'}$.\n", "We refer to this optimization as _inner loop_.\n", "Using this new model, we calculate the gradients of the original parameters, $\\theta$, with respect to the query loss on $f_{\\theta_i'}$.\n", "These gradients are accumulated over all tasks, and used to update $\\theta$.\n", "This is called _outer loop_ since we iterate over tasks.\n", "The full MAML algorithm is summarized below (Figure credit - [Finn et al. ](http://proceedings.mlr.press/v70/finn17a.html)).\n", "\n", "
"]}, {"cell_type": "markdown", "id": "1555184d", "metadata": {"papermill": {"duration": 0.049633, "end_time": "2021-10-10T16:37:26.455355", "exception": false, "start_time": "2021-10-10T16:37:26.405722", "status": "completed"}, "tags": []}, "source": ["To obtain gradients for the initial parameters $\\theta$ from the optimized model $f_{\\theta_i'}$, we actually need second-order gradients, i.e. gradients of gradients, as the support set gradients depend on $\\theta$ as well.\n", "This makes MAML computationally expensive, especially when using mulitple inner loop steps.\n", "A simpler, yet almost equally well performing alternative is First-Order MAML (FOMAML) which only uses first-order gradients.\n", "This means that the second-order gradients are ignored, and we can calculate the outer loop gradients (line 10 in algorithm 2) simply by calculating the gradients with respect to $\\theta_i'$, and use those as update to $\\theta$.\n", "Hence, the new update rule becomes:\n", "$$\\theta\\leftarrow\\theta-\\beta\\sum_{\\mathcal{T}_i\\sim p(\\mathcal{T})}\\nabla_{\\theta_i'}\\mathcal{L}_{\\mathcal{T}_i}(f_{\\theta_i'})$$\n", "Note the change of $\\theta$ to $\\theta_i'$ for $\\nabla$."]}, {"cell_type": "markdown", "id": "fcef41fa", "metadata": {"papermill": {"duration": 0.050818, "end_time": "2021-10-10T16:37:26.556682", "exception": false, "start_time": "2021-10-10T16:37:26.505864", "status": "completed"}, "tags": []}, "source": ["### ProtoMAML\n", "\n", "A problem of MAML is how to design the output classification layer.\n", "In case all tasks have different number of classes, we need to initialize the output layer with zeros or randomly in every iteration.\n", "Even if we always have the same number of classes, we just start from random predictions.\n", "This requires several inner loop steps to reach a reasonable classification result.\n", "To overcome this problem, Triantafillou et al.\n", "(2020) propose to combine the merits of Prototypical Networks and MAML.\n", "Specifically, we can use prototypes to initialize our output layer to have a strong initialization.\n", "Thereby, it can be shown that the softmax over euclidean distances can be reformulated as a linear layer with softmax.\n", "To see this, let's first write out the negative euclidean distance between a feature vector $f_{\\theta}(\\mathbf{x}^{*})$ of a new data point $\\mathbf{x}^{*}$ to a prototype $\\mathbf{v}_c$ of class $c$:\n", "$$\n", "-||f_{\\theta}(\\mathbf{x}^{*})-\\mathbf{v}_c||^2=-f_{\\theta}(\\mathbf{x}^{*})^Tf_{\\theta}(\\mathbf{x}^{*})+2\\mathbf{v}_c^{T}f_{\\theta}(\\mathbf{x}^{*})-\\mathbf{v}_c^T\\mathbf{v}_c\n", "$$\n", "\n", "We perform the classification across all classes $c\\in\\mathcal{C}$ and take a softmax on the distance.\n", "Hence, any term that is same for all classes can be removed without changing the output probabilities.\n", "In the equation above, this is true for $-f_{\\theta}(\\mathbf{x}^{*})^Tf_{\\theta}(\\mathbf{x}^{*})$ since it is independent of any class prototype.\n", "Thus, we can write:\n", "\n", "$$\n", "-||f_{\\theta}(\\mathbf{x}^{*})-\\mathbf{v}_c||^2=2\\mathbf{v}_c^{T}f_{\\theta}(\\mathbf{x}^{*})-||\\mathbf{v}_c||^2+\\text{constant}\n", "$$\n", "\n", "Taking a second look at the equation above, it looks a lot like a linear layer.\n", "For this, we use $\\mathbf{W}_{c,\\cdot}=2\\mathbf{v}_c$ and $b_c=-||\\mathbf{v}_c||^2$ which gives us the linear layer $\\mathbf{W}f_{\\theta}(\\mathbf{x}^{*})+\\mathbf{b}$.\n", "Hence, if we initialize the output weight with twice the prototypes, and the biases by the negative squared L2 norm of the prototypes, we start with a Prototypical Network.\n", "MAML allows us to adapt this layer and the rest of the network further.\n", "\n", "In the following, we will implement First-Order ProtoMAML for few-shot classification.\n", "The implementation of MAML would be the same except the output layer initialization."]}, {"cell_type": "markdown", "id": "352f15a0", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.050087, "end_time": "2021-10-10T16:37:26.657875", "exception": false, "start_time": "2021-10-10T16:37:26.607788", "status": "completed"}, "tags": []}, "source": ["### ProtoMAML implementation\n", "\n", "For implementing ProtoMAML, we can follow Algorithm 2 with minor modifications.\n", "At each training step, we first sample a batch of tasks, and a support and query set for each task.\n", "In our case of few-shot classification, this means that we simply sample multiple support-query set pairs from our sampler.\n", "For each task, we finetune our current model on the support set.\n", "However, since we need to remember the original parameters for the other tasks, the outer loop gradient update and future training steps, we need to create a copy of our model, and finetune only the copy.\n", "We can copy a model by using standard Python functions like `deepcopy`.\n", "The inner loop is implemented in the function `adapt_few_shot` in the PyTorch Lightning module below.\n", "\n", "After finetuning the model, we apply it on the query set and calculate the first-order gradients with respect to the original parameters $\\theta$.\n", "In contrast to simple MAML, we also have to consider the gradients with respect to the output layer initialization, i.e. the prototypes, since they directly rely on $\\theta$.\n", "To realize this efficiently, we take two steps.\n", "First, we calculate the prototypes by applying the original model, i.e. not the copied model, on the support elements.\n", "When initializing the output layer, we detach the prototypes to stop the gradients.\n", "This is because in the inner loop itself, we do not want to consider gradients through the prototypes back to the original model.\n", "However, after the inner loop is finished, we re-attach the computation graph of the prototypes by writing `output_weight = (output_weight - init_weight).detach() + init_weight`.\n", "While this line does not change the value of the variable `output_weight`, it adds its dependency on the prototype initialization `init_weight`.\n", "Thus, if we call `.backward` on `output_weight`, we will automatically calculate the first-order gradients with respect to the prototype initialization in the original model.\n", "\n", "After calculating all gradients and summing them together in the original model, we can take a standard optimizer step.\n", "PyTorch Lightning's method is however designed to return a loss-tensor on which we call `.backward` first.\n", "Since this is not possible here, we need to perform the optimization step ourselves.\n", "All details can be found in the code below.\n", "\n", "For implementing (Proto-)MAML with second-order gradients, it is recommended to use libraries such as [$\\nabla$higher](https://github.com/facebookresearch/higher) from Facebook AI Research.\n", "For simplicity, we stick with first-order methods here."]}, {"cell_type": "code", "execution_count": 25, "id": "7fb5c1c7", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:26.777531Z", "iopub.status.busy": "2021-10-10T16:37:26.774558Z", "iopub.status.idle": "2021-10-10T16:37:26.779554Z", "shell.execute_reply": "2021-10-10T16:37:26.779118Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.070015, "end_time": "2021-10-10T16:37:26.779661", "exception": false, "start_time": "2021-10-10T16:37:26.709646", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ProtoMAML(pl.LightningModule):\n", " def __init__(self, proto_dim, lr, lr_inner, lr_output, num_inner_steps):\n", " \"\"\"Inputs.\n", "\n", " proto_dim - Dimensionality of prototype feature space\n", " lr - Learning rate of the outer loop Adam optimizer\n", " lr_inner - Learning rate of the inner loop SGD optimizer\n", " lr_output - Learning rate for the output layer in the inner loop\n", " num_inner_steps - Number of inner loop updates to perform\n", " \"\"\"\n", " super().__init__()\n", " self.save_hyperparameters()\n", " self.model = get_convnet(output_size=self.hparams.proto_dim)\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr)\n", " scheduler = optim.lr_scheduler.MultiStepLR(optimizer, milestones=[140, 180], gamma=0.1)\n", " return [optimizer], [scheduler]\n", "\n", " def run_model(self, local_model, output_weight, output_bias, imgs, labels):\n", " # Execute a model with given output layer weights and inputs\n", " feats = local_model(imgs)\n", " preds = F.linear(feats, output_weight, output_bias)\n", " loss = F.cross_entropy(preds, labels)\n", " acc = (preds.argmax(dim=1) == labels).float()\n", " return loss, preds, acc\n", "\n", " def adapt_few_shot(self, support_imgs, support_targets):\n", " # Determine prototype initialization\n", " support_feats = self.model(support_imgs)\n", " prototypes, classes = ProtoNet.calculate_prototypes(support_feats, support_targets)\n", " support_labels = (classes[None, :] == support_targets[:, None]).long().argmax(dim=-1)\n", " # Create inner-loop model and optimizer\n", " local_model = deepcopy(self.model)\n", " local_model.train()\n", " local_optim = optim.SGD(local_model.parameters(), lr=self.hparams.lr_inner)\n", " local_optim.zero_grad()\n", " # Create output layer weights with prototype-based initialization\n", " init_weight = 2 * prototypes\n", " init_bias = -torch.norm(prototypes, dim=1) ** 2\n", " output_weight = init_weight.detach().requires_grad_()\n", " output_bias = init_bias.detach().requires_grad_()\n", "\n", " # Optimize inner loop model on support set\n", " for _ in range(self.hparams.num_inner_steps):\n", " # Determine loss on the support set\n", " loss, _, _ = self.run_model(local_model, output_weight, output_bias, support_imgs, support_labels)\n", " # Calculate gradients and perform inner loop update\n", " loss.backward()\n", " local_optim.step()\n", " # Update output layer via SGD\n", " output_weight.data -= self.hparams.lr_output * output_weight.grad\n", " output_bias.data -= self.hparams.lr_output * output_bias.grad\n", " # Reset gradients\n", " local_optim.zero_grad()\n", " output_weight.grad.fill_(0)\n", " output_bias.grad.fill_(0)\n", "\n", " # Re-attach computation graph of prototypes\n", " output_weight = (output_weight - init_weight).detach() + init_weight\n", " output_bias = (output_bias - init_bias).detach() + init_bias\n", "\n", " return local_model, output_weight, output_bias, classes\n", "\n", " def outer_loop(self, batch, mode=\"train\"):\n", " accuracies = []\n", " losses = []\n", " self.model.zero_grad()\n", "\n", " # Determine gradients for batch of tasks\n", " for task_batch in batch:\n", " imgs, targets = task_batch\n", " support_imgs, query_imgs, support_targets, query_targets = split_batch(imgs, targets)\n", " # Perform inner loop adaptation\n", " local_model, output_weight, output_bias, classes = self.adapt_few_shot(support_imgs, support_targets)\n", " # Determine loss of query set\n", " query_labels = (classes[None, :] == query_targets[:, None]).long().argmax(dim=-1)\n", " loss, preds, acc = self.run_model(local_model, output_weight, output_bias, query_imgs, query_labels)\n", " # Calculate gradients for query set loss\n", " if mode == \"train\":\n", " loss.backward()\n", "\n", " for p_global, p_local in zip(self.model.parameters(), local_model.parameters()):\n", " p_global.grad += p_local.grad # First-order approx. -> add gradients of finetuned and base model\n", "\n", " accuracies.append(acc.mean().detach())\n", " losses.append(loss.detach())\n", "\n", " # Perform update of base model\n", " if mode == \"train\":\n", " opt = self.optimizers()\n", " opt.step()\n", " opt.zero_grad()\n", "\n", " self.log(\"%s_loss\" % mode, sum(losses) / len(losses))\n", " self.log(\"%s_acc\" % mode, sum(accuracies) / len(accuracies))\n", "\n", " def training_step(self, batch, batch_idx):\n", " self.outer_loop(batch, mode=\"train\")\n", " return None # Returning None means we skip the default training optimizer steps by PyTorch Lightning\n", "\n", " def validation_step(self, batch, batch_idx):\n", " # Validation requires to finetune a model, hence we need to enable gradients\n", " torch.set_grad_enabled(True)\n", " self.outer_loop(batch, mode=\"val\")\n", " torch.set_grad_enabled(False)"]}, {"cell_type": "markdown", "id": "175f6c04", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.049445, "end_time": "2021-10-10T16:37:26.879819", "exception": false, "start_time": "2021-10-10T16:37:26.830374", "status": "completed"}, "tags": []}, "source": ["### Training\n", "\n", "To train ProtoMAML, we need to change our sampling slightly.\n", "Instead of a single support-query set batch, we need to sample multiple.\n", "To implement this, we yet use another Sampler which combines multiple batches from a `FewShotBatchSampler`, and returns it afterwards.\n", "Additionally, we define a `collate_fn` for our data loader which takes the stack of support-query set images, and returns the tasks as a list.\n", "This makes it easier to process in our PyTorch Lightning module before.\n", "The implementation of the sampler can be found below."]}, {"cell_type": "code", "execution_count": 26, "id": "a64ff4e8", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:26.989336Z", "iopub.status.busy": "2021-10-10T16:37:26.988844Z", "iopub.status.idle": "2021-10-10T16:37:26.990910Z", "shell.execute_reply": "2021-10-10T16:37:26.990506Z"}, "papermill": {"duration": 0.059649, "end_time": "2021-10-10T16:37:26.991013", "exception": false, "start_time": "2021-10-10T16:37:26.931364", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class TaskBatchSampler:\n", " def __init__(self, dataset_targets, batch_size, N_way, K_shot, include_query=False, shuffle=True):\n", " \"\"\"\n", " Inputs:\n", " dataset_targets - PyTorch tensor of the labels of the data elements.\n", " batch_size - Number of tasks to aggregate in a batch\n", " N_way - Number of classes to sample per batch.\n", " K_shot - Number of examples to sample per class in the batch.\n", " include_query - If True, returns batch of size N_way*K_shot*2, which\n", " can be split into support and query set. Simplifies\n", " the implementation of sampling the same classes but\n", " distinct examples for support and query set.\n", " shuffle - If True, examples and classes are newly shuffled in each\n", " iteration (for training)\n", " \"\"\"\n", " super().__init__()\n", " self.batch_sampler = FewShotBatchSampler(dataset_targets, N_way, K_shot, include_query, shuffle)\n", " self.task_batch_size = batch_size\n", " self.local_batch_size = self.batch_sampler.batch_size\n", "\n", " def __iter__(self):\n", " # Aggregate multiple batches before returning the indices\n", " batch_list = []\n", " for batch_idx, batch in enumerate(self.batch_sampler):\n", " batch_list.extend(batch)\n", " if (batch_idx + 1) % self.task_batch_size == 0:\n", " yield batch_list\n", " batch_list = []\n", "\n", " def __len__(self):\n", " return len(self.batch_sampler) // self.task_batch_size\n", "\n", " def get_collate_fn(self):\n", " # Returns a collate function that converts one big tensor into a list of task-specific tensors\n", " def collate_fn(item_list):\n", " imgs = torch.stack([img for img, target in item_list], dim=0)\n", " targets = torch.stack([target for img, target in item_list], dim=0)\n", " imgs = imgs.chunk(self.task_batch_size, dim=0)\n", " targets = targets.chunk(self.task_batch_size, dim=0)\n", " return list(zip(imgs, targets))\n", "\n", " return collate_fn"]}, {"cell_type": "markdown", "id": "a8e833ad", "metadata": {"papermill": {"duration": 0.050761, "end_time": "2021-10-10T16:37:27.091732", "exception": false, "start_time": "2021-10-10T16:37:27.040971", "status": "completed"}, "tags": []}, "source": ["The creation of the data loaders is with this sampler straight-forward.\n", "Note that since many images need to loaded for a training batch, it is recommended to use less workers than usual."]}, {"cell_type": "code", "execution_count": 27, "id": "579696ce", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:27.195877Z", "iopub.status.busy": "2021-10-10T16:37:27.195402Z", "iopub.status.idle": "2021-10-10T16:37:27.234232Z", "shell.execute_reply": "2021-10-10T16:37:27.233821Z"}, "papermill": {"duration": 0.092572, "end_time": "2021-10-10T16:37:27.234347", "exception": false, "start_time": "2021-10-10T16:37:27.141775", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Training constant (same as for ProtoNet)\n", "N_WAY = 5\n", "K_SHOT = 4\n", "\n", "# Training set\n", "train_protomaml_sampler = TaskBatchSampler(\n", " train_set.targets, include_query=True, N_way=N_WAY, K_shot=K_SHOT, batch_size=16\n", ")\n", "train_protomaml_loader = data.DataLoader(\n", " train_set, batch_sampler=train_protomaml_sampler, collate_fn=train_protomaml_sampler.get_collate_fn(), num_workers=2\n", ")\n", "\n", "# Validation set\n", "val_protomaml_sampler = TaskBatchSampler(\n", " val_set.targets,\n", " include_query=True,\n", " N_way=N_WAY,\n", " K_shot=K_SHOT,\n", " batch_size=1, # We do not update the parameters, hence the batch size is irrelevant here\n", " shuffle=False,\n", ")\n", "val_protomaml_loader = data.DataLoader(\n", " val_set, batch_sampler=val_protomaml_sampler, collate_fn=val_protomaml_sampler.get_collate_fn(), num_workers=2\n", ")"]}, {"cell_type": "markdown", "id": "483d09a8", "metadata": {"papermill": {"duration": 0.04969, "end_time": "2021-10-10T16:37:27.335586", "exception": false, "start_time": "2021-10-10T16:37:27.285896", "status": "completed"}, "tags": []}, "source": ["Now, we are ready to train our ProtoMAML.\n", "We use the same feature space size as for ProtoNet, but can use a higher learning rate since the outer loop gradients are accumulated over 16 batches.\n", "The inner loop learning rate is set to 0.1, which is much higher than the outer loop lr because we use SGD in the inner loop instead of Adam.\n", "Commonly, the learning rate for the output layer is higher than the base model is the base model is very deep or pre-trained.\n", "However, for our setup, we observed no noticable impact of using a different learning rate than the base model.\n", "The number of inner loop updates is another crucial hyperparmaeter, and depends on the similarity of our training tasks.\n", "Since all tasks are on images from the same dataset, we notice that a single inner loop update achieves similar performance as 3 or 5 while training considerably faster.\n", "However, especially in RL and NLP, larger number of inner loop steps are often needed."]}, {"cell_type": "code", "execution_count": 28, "id": "f65be6c5", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:27.441153Z", "iopub.status.busy": "2021-10-10T16:37:27.440678Z", "iopub.status.idle": "2021-10-10T16:37:27.503563Z", "shell.execute_reply": "2021-10-10T16:37:27.503082Z"}, "papermill": {"duration": 0.118168, "end_time": "2021-10-10T16:37:27.503674", "exception": false, "start_time": "2021-10-10T16:37:27.385506", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/MetaLearning/ProtoMAML.ckpt, loading...\n"]}], "source": ["protomaml_model = train_model(\n", " ProtoMAML,\n", " proto_dim=64,\n", " lr=1e-3,\n", " lr_inner=0.1,\n", " lr_output=0.1,\n", " num_inner_steps=1, # Often values between 1 and 10\n", " train_loader=train_protomaml_loader,\n", " val_loader=val_protomaml_loader,\n", ")"]}, {"cell_type": "markdown", "id": "8cc7c232", "metadata": {"papermill": {"duration": 0.049852, "end_time": "2021-10-10T16:37:27.604342", "exception": false, "start_time": "2021-10-10T16:37:27.554490", "status": "completed"}, "tags": []}, "source": ["Let's have a look at the training TensorBoard."]}, {"cell_type": "code", "execution_count": 29, "id": "8bdec8ea", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:27.711240Z", "iopub.status.busy": "2021-10-10T16:37:27.710762Z", "iopub.status.idle": "2021-10-10T16:37:27.712848Z", "shell.execute_reply": "2021-10-10T16:37:27.712444Z"}, "papermill": {"duration": 0.05552, "end_time": "2021-10-10T16:37:27.712949", "exception": false, "start_time": "2021-10-10T16:37:27.657429", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Opens tensorboard in notebook. Adjust the path to your CHECKPOINT_PATH if needed\n", "# # %tensorboard --logdir ../saved_models/tutorial16/tensorboards/ProtoMAML/"]}, {"cell_type": "markdown", "id": "ffa8d4ab", "metadata": {"papermill": {"duration": 0.050609, "end_time": "2021-10-10T16:37:27.813989", "exception": false, "start_time": "2021-10-10T16:37:27.763380", "status": "completed"}, "tags": []}, "source": ["
\n", "\n", "One obvious difference to ProtoNet is that the loss curves look much less noisy.\n", "This is because we average the outer loop gradients over multiple tasks, and thus have a smoother training curve.\n", "Additionally, we only have 15k training iterations after 200 epochs.\n", "This is again because of the task batches, which cause 16 times less iterations.\n", "However, each iteration has seen 16 times more data in this experiment.\n", "Thus, we still have a fair comparison between ProtoMAML and ProtoNet.\n", "At first sight on the validation accuracy, one would assume that\n", "ProtoNet performs superior to ProtoMAML, but we have to verify that with\n", "proper testing below."]}, {"cell_type": "markdown", "id": "1ef9ae57", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.051235, "end_time": "2021-10-10T16:37:27.917147", "exception": false, "start_time": "2021-10-10T16:37:27.865912", "status": "completed"}, "tags": []}, "source": ["### Testing\n", "\n", "We test ProtoMAML in the same manner as ProtoNet, namely by picking random examples in the test set as support sets and use the rest of the dataset as query set.\n", "Instead of just calculating the prototypes for all examples, we need to finetune a separate model for each support set.\n", "This is why this process is more expensive than ProtoNet, and in our case, testing $k=\\{2,4,8,16,32\\}$ can take almost an hour.\n", "Hence, we provide evaluation files besides the pretrained models."]}, {"cell_type": "code", "execution_count": 30, "id": "24b90577", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:28.039732Z", "iopub.status.busy": "2021-10-10T16:37:28.039190Z", "iopub.status.idle": "2021-10-10T16:37:28.041339Z", "shell.execute_reply": "2021-10-10T16:37:28.040864Z"}, "papermill": {"duration": 0.070613, "end_time": "2021-10-10T16:37:28.041443", "exception": false, "start_time": "2021-10-10T16:37:27.970830", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def test_protomaml(model, dataset, k_shot=4):\n", " pl.seed_everything(42)\n", " model = model.to(device)\n", " num_classes = dataset.targets.unique().shape[0]\n", "\n", " # Data loader for full test set as query set\n", " full_dataloader = data.DataLoader(dataset, batch_size=128, num_workers=4, shuffle=False, drop_last=False)\n", " # Data loader for sampling support sets\n", " sampler = FewShotBatchSampler(\n", " dataset.targets, include_query=False, N_way=num_classes, K_shot=k_shot, shuffle=False, shuffle_once=False\n", " )\n", " sample_dataloader = data.DataLoader(dataset, batch_sampler=sampler, num_workers=2)\n", "\n", " # We iterate through the full dataset in two manners. First, to select the k-shot batch.\n", " # Second, the evaluate the model on all other examples\n", " accuracies = []\n", " for (support_imgs, support_targets), support_indices in tqdm(\n", " zip(sample_dataloader, sampler), \"Performing few-shot finetuning\"\n", " ):\n", " support_imgs = support_imgs.to(device)\n", " support_targets = support_targets.to(device)\n", " # Finetune new model on support set\n", " local_model, output_weight, output_bias, classes = model.adapt_few_shot(support_imgs, support_targets)\n", " with torch.no_grad(): # No gradients for query set needed\n", " local_model.eval()\n", " batch_acc = torch.zeros((0,), dtype=torch.float32, device=device)\n", " # Evaluate all examples in test dataset\n", " for query_imgs, query_targets in full_dataloader:\n", " query_imgs = query_imgs.to(device)\n", " query_targets = query_targets.to(device)\n", " query_labels = (classes[None, :] == query_targets[:, None]).long().argmax(dim=-1)\n", " _, _, acc = model.run_model(local_model, output_weight, output_bias, query_imgs, query_labels)\n", " batch_acc = torch.cat([batch_acc, acc.detach()], dim=0)\n", " # Exclude support set elements\n", " for s_idx in support_indices:\n", " batch_acc[s_idx] = 0\n", " batch_acc = batch_acc.sum().item() / (batch_acc.shape[0] - len(support_indices))\n", " accuracies.append(batch_acc)\n", " return mean(accuracies), stdev(accuracies)"]}, {"cell_type": "markdown", "id": "d580df69", "metadata": {"papermill": {"duration": 0.051584, "end_time": "2021-10-10T16:37:28.144161", "exception": false, "start_time": "2021-10-10T16:37:28.092577", "status": "completed"}, "tags": []}, "source": ["In contrast to training, it is recommended to use many more inner loop updates during testing.\n", "During training, we are not interested in getting the best model from the inner loop, but the model which can provide the best gradients.\n", "Hence, one update might be already sufficient in training, but for testing, it was often observed that larger number of updates can give a considerable performance boost.\n", "Thus, we change the inner loop updates to 200 before testing."]}, {"cell_type": "code", "execution_count": 31, "id": "a3a77c03", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:28.249335Z", "iopub.status.busy": "2021-10-10T16:37:28.248861Z", "iopub.status.idle": "2021-10-10T16:37:28.251118Z", "shell.execute_reply": "2021-10-10T16:37:28.250650Z"}, "papermill": {"duration": 0.056293, "end_time": "2021-10-10T16:37:28.251218", "exception": false, "start_time": "2021-10-10T16:37:28.194925", "status": "completed"}, "tags": []}, "outputs": [], "source": ["protomaml_model.hparams.num_inner_steps = 200"]}, {"cell_type": "markdown", "id": "5c8e75e8", "metadata": {"papermill": {"duration": 0.050389, "end_time": "2021-10-10T16:37:28.352727", "exception": false, "start_time": "2021-10-10T16:37:28.302338", "status": "completed"}, "tags": []}, "source": ["Now, we can test our model.\n", "For the pre-trained models, we provide a json file with the results to reduce evaluation time."]}, {"cell_type": "code", "execution_count": 32, "id": "a2ec75b4", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:28.464792Z", "iopub.status.busy": "2021-10-10T16:37:28.464316Z", "iopub.status.idle": "2021-10-10T16:37:28.467735Z", "shell.execute_reply": "2021-10-10T16:37:28.467269Z"}, "papermill": {"duration": 0.059702, "end_time": "2021-10-10T16:37:28.467837", "exception": false, "start_time": "2021-10-10T16:37:28.408135", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Accuracy for k=2: 42.89% (+-3.82%)\n", "Accuracy for k=4: 52.27% (+-2.72%)\n", "Accuracy for k=8: 59.23% (+-1.50%)\n", "Accuracy for k=16: 63.94% (+-1.24%)\n", "Accuracy for k=32: 67.57% (+-0.90%)\n"]}], "source": ["protomaml_result_file = os.path.join(CHECKPOINT_PATH, \"protomaml_fewshot.json\")\n", "\n", "if os.path.isfile(protomaml_result_file):\n", " # Load pre-computed results\n", " with open(protomaml_result_file) as f:\n", " protomaml_accuracies = json.load(f)\n", " protomaml_accuracies = {int(k): v for k, v in protomaml_accuracies.items()}\n", "else:\n", " # Perform same experiments as for ProtoNet\n", " protomaml_accuracies = dict()\n", " for k in [2, 4, 8, 16, 32]:\n", " protomaml_accuracies[k] = test_protomaml(protomaml_model, test_set, k_shot=k)\n", " # Export results\n", " with open(protomaml_result_file, \"w\") as f:\n", " json.dump(protomaml_accuracies, f, indent=4)\n", "\n", "for k in protomaml_accuracies:\n", " print(\n", " \"Accuracy for k=%i: %4.2f%% (+-%4.2f%%)\"\n", " % (k, 100.0 * protomaml_accuracies[k][0], 100.0 * protomaml_accuracies[k][1])\n", " )"]}, {"cell_type": "markdown", "id": "a3b76b70", "metadata": {"papermill": {"duration": 0.051095, "end_time": "2021-10-10T16:37:28.569910", "exception": false, "start_time": "2021-10-10T16:37:28.518815", "status": "completed"}, "tags": []}, "source": ["Again, let's plot the results in our plot from before."]}, {"cell_type": "code", "execution_count": 33, "id": "c19a1cd0", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:28.700295Z", "iopub.status.busy": "2021-10-10T16:37:28.688544Z", "iopub.status.idle": "2021-10-10T16:37:28.982037Z", "shell.execute_reply": "2021-10-10T16:37:28.981554Z"}, "papermill": {"duration": 0.36056, "end_time": "2021-10-10T16:37:28.982151", "exception": false, "start_time": "2021-10-10T16:37:28.621591", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/pdf": "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IDExNCAvciAvcyAvdCAvdSAxMTkgL3cgMTIxIC95IF0KL1R5cGUgL0VuY29kaW5nID4+Ci9GaXJzdENoYXIgMCAvRm9udEJCb3ggWyAtMTA3MCAtNDE2IDE5NzYgMTE3NSBdIC9Gb250RGVzY3JpcHRvciAxNiAwIFIKL0ZvbnRNYXRyaXggWyAwLjAwMSAwIDAgMC4wMDEgMCAwIF0gL0xhc3RDaGFyIDI1NSAvTmFtZSAvRGVqYVZ1U2Fucy1Cb2xkCi9TdWJ0eXBlIC9UeXBlMyAvVHlwZSAvRm9udCAvV2lkdGhzIDE1IDAgUiA+PgplbmRvYmoKMTYgMCBvYmoKPDwgL0FzY2VudCA5MjkgL0NhcEhlaWdodCAwIC9EZXNjZW50IC0yMzYgL0ZsYWdzIDMyCi9Gb250QkJveCBbIC0xMDcwIC00MTYgMTk3NiAxMTc1IF0gL0ZvbnROYW1lIC9EZWphVnVTYW5zLUJvbGQKL0l0YWxpY0FuZ2xlIDAgL01heFdpZHRoIDE0NDAgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjE1IDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzNDggNDU2IDUyMSA4MzggNjk2CjEwMDIgODcyIDMwNiA0NTcgNDU3IDUyMyA4MzggMzgwIDQxNSAzODAgMzY1IDY5NiA2OTYgNjk2IDY5NiA2OTYgNjk2IDY5Ngo2OTYgNjk2IDY5NiA0MDAgNDAwIDgzOCA4MzggODM4IDU4MCAxMDAwIDc3NCA3NjIgNzM0IDgzMCA2ODMgNjgzIDgyMSA4MzcKMzcyIDM3MiA3NzUgNjM3IDk5NSA4MzcgODUwIDczMyA4NTAgNzcwIDcyMCA2ODIgODEyIDc3NCAxMTAzIDc3MSA3MjQgNzI1CjQ1NyAzNjUgNDU3IDgzOCA1MDAgNTAwIDY3NSA3MTYgNTkzIDcxNiA2NzggNDM1IDcxNiA3MTIgMzQzIDM0MyA2NjUgMzQzCjEwNDIgNzEyIDY4NyA3MTYgNzE2IDQ5MyA1OTUgNDc4IDcxMiA2NTIgOTI0IDY0NSA2NTIgNTgyIDcxMiAzNjUgNzEyIDgzOAo2MDAgNjk2IDYwMCAzODAgNDM1IDY1NyAxMDAwIDUwMCA1MDAgNTAwIDE0NDAgNzIwIDQxMiAxMTY3IDYwMCA3MjUgNjAwIDYwMAozODAgMzgwIDY1NyA2NTcgNjM5IDUwMCAxMDAwIDUwMCAxMDAwIDU5NSA0MTIgMTA5NCA2MDAgNTgyIDcyNCAzNDggNDU2IDY5Ngo2OTYgNjM2IDY5NiAzNjUgNTAwIDUwMCAxMDAwIDU2NCA2NDYgODM4IDQxNSAxMDAwIDUwMCA1MDAgODM4IDQzOCA0MzggNTAwCjczNiA2MzYgMzgwIDUwMCA0MzggNTY0IDY0NiAxMDM1IDEwMzUgMTAzNSA1ODAgNzc0IDc3NCA3NzQgNzc0IDc3NCA3NzQgMTA4NQo3MzQgNjgzIDY4MyA2ODMgNjgzIDM3MiAzNzIgMzcyIDM3MiA4MzggODM3IDg1MCA4NTAgODUwIDg1MCA4NTAgODM4IDg1MCA4MTIKODEyIDgxMiA4MTIgNzI0IDczOCA3MTkgNjc1IDY3NSA2NzUgNjc1IDY3NSA2NzUgMTA0OCA1OTMgNjc4IDY3OCA2NzggNjc4CjM0MyAzNDMgMzQzIDM0MyA2ODcgNzEyIDY4NyA2ODcgNjg3IDY4NyA2ODcgODM4IDY4NyA3MTIgNzEyIDcxMiA3MTIgNjUyIDcxNgo2NTIgXQplbmRvYmoKMTggMCBvYmoKPDwgL0EgMTkgMCBSIC9GIDIwIDAgUiAvTCAyMSAwIFIgL00gMjIgMCBSIC9OIDIzIDAgUiAvUCAyNCAwIFIgL1MgMjUgMCBSCi9hIDI2IDAgUiAvYiAyNyAwIFIgL2MgMjggMCBSIC9kIDI5IDAgUiAvZSAzMCAwIFIgL2YgMzEgMCBSIC9oIDMyIDAgUgovaHlwaGVuIDMzIDAgUiAvbCAzNCAwIFIgL20gMzUgMCBSIC9uIDM2IDAgUiAvbyAzNyAwIFIgL3AgMzggMCBSIC9yIDM5IDAgUgovcyA0MCAwIFIgL3NwYWNlIDQxIDAgUiAvdCA0MiAwIFIgL3UgNDMgMCBSIC93IDQ0IDAgUiAveSA0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E0UiPPmOptUT61o00jIS1MYRrGoDvDv9ME4AABNxywJkn0qUs+TEb7H0swZX+v4Bn0dUlgplbmRzdHJlYW0KZW5kb2JqCjQ4IDAgb2JqCjw8IC9CYXNlRm9udCAvRGVqYVZ1U2FucyAvQ2hhclByb2NzIDQ5IDAgUgovRW5jb2RpbmcgPDwKL0RpZmZlcmVuY2VzIFsgNDYgL3BlcmlvZCA0OCAvemVybyAvb25lIC90d28gL3RocmVlIC9mb3VyIC9maXZlIC9zaXggNTYgL2VpZ2h0IDY1IC9BIDc2Ci9MIC9NIC9OIDgwIC9QIDEwMSAvZSAxMTEgL28gMTE0IC9yIDExNiAvdCBdCi9UeXBlIC9FbmNvZGluZyA+PgovRmlyc3RDaGFyIDAgL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udERlc2NyaXB0b3IgNDcgMCBSCi9Gb250TWF0cml4IFsgMC4wMDEgMCAwIDAuMDAxIDAgMCBdIC9MYXN0Q2hhciAyNTUgL05hbWUgL0RlamFWdVNhbnMKL1N1YnR5cGUgL1R5cGUzIC9UeXBlIC9Gb250IC9XaWR0aHMgNDYgMCBSID4+CmVuZG9iago0NyAwIG9iago8PCAvQXNjZW50IDkyOSAvQ2FwSGVpZ2h0IDAgL0Rlc2NlbnQgLTIzNiAvRmxhZ3MgMzIKL0ZvbnRCQm94IFsgLTEwMjEgLTQ2MyAxNzk0IDEyMzMgXSAvRm9udE5hbWUgL0RlamFWdVNhbnMgL0l0YWxpY0FuZ2xlIDAKL01heFdpZHRoIDEzNDIgL1N0ZW1WIDAgL1R5cGUgL0ZvbnREZXNjcmlwdG9yIC9YSGVpZ2h0IDAgPj4KZW5kb2JqCjQ2IDAgb2JqClsgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAKNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCA2MDAgNjAwIDYwMCAzMTggNDAxIDQ2MCA4MzggNjM2Cjk1MCA3ODAgMjc1IDM5MCAzOTAgNTAwIDgzOCAzMTggMzYxIDMxOCAzMzcgNjM2IDYzNiA2MzYgNjM2IDYzNiA2MzYgNjM2IDYzNgo2MzYgNjM2IDMzNyAzMzcgODM4IDgzOCA4MzggNTMxIDEwMDAgNjg0IDY4NiA2OTggNzcwIDYzMiA1NzUgNzc1IDc1MiAyOTUKMjk1IDY1NiA1NTcgODYzIDc0OCA3ODcgNjAzIDc4NyA2OTUgNjM1IDYxMSA3MzIgNjg0IDk4OSA2ODUgNjExIDY4NSAzOTAgMzM3CjM5MCA4MzggNTAwIDUwMCA2MTMgNjM1IDU1MCA2MzUgNjE1IDM1MiA2MzUgNjM0IDI3OCAyNzggNTc5IDI3OCA5NzQgNjM0IDYxMgo2MzUgNjM1IDQxMSA1MjEgMzkyIDYzNCA1OTIgODE4IDU5MiA1OTIgNTI1IDYzNiAzMzcgNjM2IDgzOCA2MDAgNjM2IDYwMCAzMTgKMzUyIDUxOCAxMDAwIDUwMCA1MDAgNTAwIDEzNDIgNjM1IDQwMCAxMDcwIDYwMCA2ODUgNjAwIDYwMCAzMTggMzE4IDUxOCA1MTgKNTkwIDUwMCAxMDAwIDUwMCAxMDAwIDUyMSA0MDAgMTAyMyA2MDAgNTI1IDYxMSAzMTggNDAxIDYzNiA2MzYgNjM2IDYzNiAzMzcKNTAwIDUwMCAxMDAwIDQ3MSA2MTIgODM4IDM2MSAxMDAwIDUwMCA1MDAgODM4IDQwMSA0MDEgNTAwIDYzNiA2MzYgMzE4IDUwMAo0MDEgNDcxIDYxMiA5NjkgOTY5IDk2OSA1MzEgNjg0IDY4NCA2ODQgNjg0IDY4NCA2ODQgOTc0IDY5OCA2MzIgNjMyIDYzMiA2MzIKMjk1IDI5NSAyOTUgMjk1IDc3NSA3NDggNzg3IDc4NyA3ODcgNzg3IDc4NyA4MzggNzg3IDczMiA3MzIgNzMyIDczMiA2MTEgNjA1CjYzMCA2MTMgNjEzIDYxMyA2MTMgNjEzIDYxMyA5ODIgNTUwIDYxNSA2MTUgNjE1IDYxNSAyNzggMjc4IDI3OCAyNzggNjEyIDYzNAo2MTIgNjEyIDYxMiA2MTIgNjEyIDgzOCA2MTIgNjM0IDYzNCA2MzQgNjM0IDU5MiA2MzUgNTkyIF0KZW5kb2JqCjQ5IDAgb2JqCjw8IC9BIDUwIDAgUiAvTCA1MSAwIFIgL00gNTIgMCBSIC9OIDUzIDAgUiAvUCA1NCAwIFIgL2UgNTUgMCBSCi9laWdodCA1NiAwIFIgL2ZpdmUgNTcgMCBSIC9mb3VyIDU4IDAgUiAvbyA1OSAwIFIgL29uZSA2MCAwIFIKL3BlcmlvZCA2MSAwIFIgL3IgNjIgMCBSIC9zaXggNjMgMCBSIC90IDY0IDAgUiAvdGhyZWUgNjUgMCBSIC90d28gNjYgMCBSCi96ZXJvIDY3IDAgUiA+PgplbmRvYmoKMyAwIG9iago8PCAvRjEgNDggMCBSIC9GMiAxNyAwIFIgPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0ExIDw8IC9DQSAwIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EyIDw8IC9DQSAxIC9UeXBlIC9FeHRHU3RhdGUgL2NhIDEgPj4KL0EzIDw8IC9DQSAwLjIgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMC4yID4+Ci9BNCA8PCAvQ0EgMC44IC9UeXBlIC9FeHRHU3RhdGUgL2NhIDAuOCA+PiA+PgplbmRvYmoKNSAwIG9iago8PCA+PgplbmRvYmoKNiAwIG9iago8PCA+PgplbmRvYmoKNyAwIG9iago8PCAvTTAgMTMgMCBSIC9NMSAxNCAwIFIgPj4KZW5kb2JqCjEzIDAgb2JqCjw8IC9CQm94IFsgLTggLTggOCA4IF0gL0ZpbHRlciAvRmxhdGVEZWNvZGUgL0xlbmd0aCAxMzEgL1N1YnR5cGUgL0Zvcm0KL1R5cGUgL1hPYmplY3QgPj4Kc3RyZWFtCnicbZBBDoQgDEX3PUUv8ElLRWXr0mu4mUzi/bcDcUBM3TTQvjx+Uf6S8E6lwPgkCUtOs+R605DSukyMGObVsijHoFEt1s51OKjP0HBjdIuxFKbU1uh4o5vpNt6TP/qwWSFGPxwOr4R7FkMmXCkxBoffCy/bw/8Rnl7UwB+ijX5jWkP9CmVuZHN0cmVhbQplbmRvYmoKMTQgMCBvYmoKPDwgL0JCb3ggWyAtOCAtOCA4IDggXSAvRmlsdGVyIC9GbGF0ZURlY29kZSAvTGVuZ3RoIDEzMSAvU3VidHlwZSAvRm9ybQovVHlwZSAvWE9iamVjdCA+PgpzdHJlYW0KeJxtkEEOhCAMRfc9RS/wSUtFZevSa7iZTOL9twNxQEzdNNC+PH5R/pLwTqXA+CQJS06z5HrTkNK6TIwY5tWyKMegUS3WznU4qM/QcGN0i7EUptTW6Hijm+k23pM/+rBZIUY/HA6vhHsWQyZcKTEGh98LL9vD/xGeXtTAH6KNfmNaQ/0KZW5kc3RyZWFtCmVuZG9iagoyIDAgb2JqCjw8IC9Db3VudCAxIC9LaWRzIFsgMTEgMCBSIF0gL1R5cGUgL1BhZ2VzID4+CmVuZG9iago2OCAwIG9iago8PCAvQ3JlYXRpb25EYXRlIChEOjIwMjExMDEwMTgzNzI4KzAyJzAwJykKL0NyZWF0b3IgKE1hdHBsb3RsaWIgdjMuNC4zLCBodHRwczovL21hdHBsb3RsaWIub3JnKQovUHJvZHVjZXIgKE1hdHBsb3RsaWIgcGRmIGJhY2tlbmQgdjMuNC4zKSA+PgplbmRvYmoKeHJlZgowIDY5CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNiAwMDAwMCBuIAowMDAwMDE4MDgzIDAwMDAwIG4gCjAwMDAwMTcyNjIgMDAwMDAgbiAKMDAwMDAxNzMwNSAwMDAwMCBuIAowMDAwMDE3NDkwIDAwMDAwIG4gCjAwMDAwMTc1MTEgMDAwMDAgbiAKMDAwMDAxNzUzMiAwMDAwMCBuIAowMDAwMDAwMDY1IDAwMDAwIG4gCjAwMDAwMDA0MDIgMDAwMDAgbiAKMDAwMDAwMjE5NCAwMDAwMCBuIAowMDAwMDAwMjA4IDAwMDAwIG4gCjAwMDAwMDIxNzMgMDAwMDAgbiAKMDAwMDAxNzU3NSAwMDAwMCBuIAowMDAwMDE3ODI5IDAwMDAwIG4gCjAwMDAwMDk0NTQgMDAwMDAgbiAKMDAwMDAwOTI0OSAwMDAwMCBuIAowMDAwMDA4ODEyIDAwMDAwIG4gCjAwMDAwMTA1MTUgMDAwMDAgbiAKMDAwMDAwMjIxNCAwMDAwMCBuIAowMDAwMDAyMzc3IDAwMDAwIG4gCjAwMDAwMDI1MjMgMDAwMDAgbiAKMDAwMDAwMjY1NyAwMDAwMCBuIAowMDAwMDAyODE5IDAwMDAwIG4gCjAwMDAwMDI5NjcgMDAwMDAgbiAKMDAwMDAwMzIwNSAwMDAwMCBuIAowMDAwMDAzNjIxIDAwMDAwIG4gCjAwMDAwMDQwMTAgMDAwMDAgbiAKMDAwMDAwNDMyOCAwMDAwMCBuIAowMDAwMDA0NjM4IDAwMDAwIG4gCjAwMDAwMDQ5NDkgMDAwMDAgbiAKMDAwMDAwNTI3MyAwMDAwMCBuIAowMDAwMDA1NDgyIDAwMDAwIG4gCjAwMDAwMDU3NDYgMDAwMDAgbiAKMDAwMDAwNTg3NCAwMDAwMCBuIAowMDAwMDA1OTk2IDAwMDAwIG4gCjAwMDAwMDYzNTAgMDAwMDAgbiAKMDAwMDAwNjYxMyAwMDAwMCBuIAowMDAwMDA2ODk5IDAwMDAwIG4gCjAwMDAwMDcyMTMgMDAwMDAgbiAKMDAwMDAwNzQ0NCAwMDAwMCBuIAowMDAwMDA3ODYzIDAwMDAwIG4gCjAwMDAwMDc5NTMgMDAwMDAgbiAKMDAwMDAwODE1OCAwMDAwMCBuIAowMDAwMDA4NDM1IDAwMDAwIG4gCjAwMDAwMDg2MDAgMDAwMDAgbiAKMDAwMDAxNTk3OSAwMDAwMCBuIAowMDAwMDE1Nzc5IDAwMDAwIG4gCjAwMDAwMTUzNzAgMDAwMDAgbiAKMDAwMDAxNzAzMiAwMDAwMCBuIAowMDAwMDEwODE2IDAwMDAwIG4gCjAwMDAwMTA5NzkgMDAwMDAgbiAKMDAwMDAxMTExMiAwMDAwMCBuIAowMDAwMDExMjc0IDAwMDAwIG4gCjAwMDAwMTE0MjMgMDAwMDAgbiAKMDAwMDAxMTY2NiAwMDAwMCBuIAowMDAwMDExOTg4IDAwMDAwIG4gCjAwMDAwMTI0NTYgMDAwMDAgbiAKMDAwMDAxMjc3OCAwMDAwMCBuIAowMDAwMDEyOTQ0IDAwMDAwIG4gCjAwMDAwMTMyMzUgMDAwMDAgbiAKMDAwMDAxMzM5MCAwMDAwMCBuIAowMDAwMDEzNTEzIDAwMDAwIG4gCjAwMDAwMTM3NDYgMDAwMDAgbiAKMDAwMDAxNDEzOSAwMDAwMCBuIAowMDAwMDE0MzQ1IDAwMDAwIG4gCjAwMDAwMTQ3NTggMDAwMDAgbiAKMDAwMDAxNTA4MiAwMDAwMCBuIAowMDAwMDE4MTQzIDAwMDAwIG4gCnRyYWlsZXIKPDwgL0luZm8gNjggMCBSIC9Sb290IDEgMCBSIC9TaXplIDY5ID4+CnN0YXJ0eHJlZgoxODMwMAolJUVPRgo=\n", "image/svg+xml": ["\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:37:28.787735\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["ax = plot_few_shot(protonet_accuracies, name=\"ProtoNet\", color=\"C1\")\n", "plot_few_shot(protomaml_accuracies, name=\"ProtoMAML\", color=\"C2\", ax=ax)\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "b631ffc3", "metadata": {"papermill": {"duration": 0.053734, "end_time": "2021-10-10T16:37:29.090435", "exception": false, "start_time": "2021-10-10T16:37:29.036701", "status": "completed"}, "tags": []}, "source": ["We can observe that ProtoMAML is indeed able to outperform ProtoNet for $k>4$.\n", "This is because with more samples, it becomes more relevant to also adapt the base model's parameters.\n", "Meanwhile, for $k=2$, ProtoMAML achieves lower performance than ProtoNet.\n", "This is likely also related to choosing 200 inner loop updates since with more updates, there exists the risk of overfitting.\n", "Nonetheless, the high standard deviation for $k=2$ makes it hard to take any statistically valid conclusion.\n", "\n", "Overall, we can conclude that ProtoMAML slightly outperforms ProtoNet for larger shot counts.\n", "However, one disadvantage of ProtoMAML is its much longer training and testing time.\n", "ProtoNet provides a simple, efficient, yet strong baseline for\n", "ProtoMAML, and might be the better solution in situations where limited\n", "resources are available."]}, {"cell_type": "markdown", "id": "6d158afa", "metadata": {"papermill": {"duration": 0.055343, "end_time": "2021-10-10T16:37:29.202908", "exception": false, "start_time": "2021-10-10T16:37:29.147565", "status": "completed"}, "tags": []}, "source": ["## Domain adaptation\n", "\n", "So far, we have evaluated our meta-learning algorithms on the same dataset on which we have trained them.\n", "However, meta-learning algorithms are especially interesting when we want to move from one to another dataset.\n", "So, what happens if we apply them on a quite different dataset than CIFAR?\n", "This is what we try out below, and evaluate ProtoNet and ProtoMAML on the SVHN dataset."]}, {"cell_type": "markdown", "id": "71c4f38f", "metadata": {"papermill": {"duration": 0.054154, "end_time": "2021-10-10T16:37:29.316416", "exception": false, "start_time": "2021-10-10T16:37:29.262262", "status": "completed"}, "tags": []}, "source": ["### SVHN dataset\n", "\n", "The Street View House Numbers (SVHN) dataset is a real-world image dataset for house number detection.\n", "It is similar to MNIST by having the classes 0 to 9, but is more difficult due to its real-world setting and possible distracting numbers left and right.\n", "Let's first load the dataset, and visualize some images to get an impression of the dataset."]}, {"cell_type": "code", "execution_count": 34, "id": "74228693", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:29.435411Z", "iopub.status.busy": "2021-10-10T16:37:29.434935Z", "iopub.status.idle": "2021-10-10T16:37:34.722837Z", "shell.execute_reply": "2021-10-10T16:37:34.722322Z"}, "papermill": {"duration": 5.346857, "end_time": "2021-10-10T16:37:34.722956", "exception": false, "start_time": "2021-10-10T16:37:29.376099", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading http://ufldl.stanford.edu/housenumbers/test_32x32.mat to /__w/1/s/.datasets/test_32x32.mat\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "04c241d2abd34caa8d271a853e8f6a74", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/64275384 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:37:34.921312\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Visualize some examples\n", "NUM_IMAGES = 12\n", "SVHN_images = [SVHN_test_dataset[np.random.randint(len(SVHN_test_dataset))][0] for idx in range(NUM_IMAGES)]\n", "SVHN_images = torch.stack(SVHN_images, dim=0)\n", "img_grid = torchvision.utils.make_grid(SVHN_images, nrow=6, normalize=True, pad_value=0.9)\n", "img_grid = img_grid.permute(1, 2, 0)\n", "\n", "plt.figure(figsize=(8, 8))\n", "plt.title(\"Image examples of the SVHN dataset\")\n", "plt.imshow(img_grid)\n", "plt.axis(\"off\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "65604d6b", "metadata": {"papermill": {"duration": 0.057271, "end_time": "2021-10-10T16:37:35.195031", "exception": false, "start_time": "2021-10-10T16:37:35.137760", "status": "completed"}, "tags": []}, "source": ["Each image is labeled with one class between 0 and 9 representing the main digit in the image.\n", "Can our ProtoNet and ProtoMAML learn to classify the digits from only a few examples?\n", "This is what we will test out below.\n", "The images have the same size as CIFAR, so that we can use the images without changes.\n", "We first prepare the dataset, for which we take the first 500 images per class.\n", "For this dataset, we use our test functions as before to get an estimated performance for different number of shots."]}, {"cell_type": "code", "execution_count": 36, "id": "0de00f6d", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:35.315332Z", "iopub.status.busy": "2021-10-10T16:37:35.314823Z", "iopub.status.idle": "2021-10-10T16:37:35.326321Z", "shell.execute_reply": "2021-10-10T16:37:35.326709Z"}, "papermill": {"duration": 0.07457, "end_time": "2021-10-10T16:37:35.326849", "exception": false, "start_time": "2021-10-10T16:37:35.252279", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/plain": ["(5000, 32, 32, 3)"]}, "execution_count": 36, "metadata": {}, "output_type": "execute_result"}], "source": ["imgs = np.transpose(SVHN_test_dataset.data, (0, 2, 3, 1))\n", "targets = SVHN_test_dataset.labels\n", "# Limit number of examples to 500 to reduce test time\n", "min_label_count = min(500, np.bincount(SVHN_test_dataset.labels).min())\n", "\n", "idxs = np.concatenate([np.where(targets == c)[0][:min_label_count] for c in range(1 + targets.max())], axis=0)\n", "imgs = imgs[idxs]\n", "targets = torch.from_numpy(targets[idxs]).long()\n", "\n", "svhn_fewshot_dataset = ImageDataset(imgs, targets, img_transform=test_transform)\n", "svhn_fewshot_dataset.imgs.shape"]}, {"cell_type": "markdown", "id": "761db618", "metadata": {"papermill": {"duration": 0.059836, "end_time": "2021-10-10T16:37:35.446022", "exception": false, "start_time": "2021-10-10T16:37:35.386186", "status": "completed"}, "tags": []}, "source": ["### Experiments\n", "\n", "First, we can apply ProtoNet to the SVHN dataset:"]}, {"cell_type": "code", "execution_count": 37, "id": "443d295e", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:37:35.566197Z", "iopub.status.busy": "2021-10-10T16:37:35.565609Z", "iopub.status.idle": "2021-10-10T16:37:49.932486Z", "shell.execute_reply": "2021-10-10T16:37:49.932000Z"}, "papermill": {"duration": 14.429039, "end_time": "2021-10-10T16:37:49.932629", "exception": false, "start_time": "2021-10-10T16:37:35.503590", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "6ae81549f8a343a2985e8fbdd3e6cfce", "version_major": 2, "version_minor": 0}, "text/plain": ["Extracting image features: 0%| | 0/40 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:37:50.563371\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["ax = plot_few_shot(protonet_svhn_accuracies, name=\"ProtoNet\", color=\"C1\")\n", "plot_few_shot(protomaml_svhn_accuracies, name=\"ProtoMAML\", color=\"C2\", ax=ax)\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "81b31ea2", "metadata": {"papermill": {"duration": 0.064936, "end_time": "2021-10-10T16:38:10.896748", "exception": false, "start_time": "2021-10-10T16:38:10.831812", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this notebook, we have discussed meta-learning algorithms that learn to adapt to new classes and/or tasks with just a few samples.\n", "We have discussed three popular algorithms, namely ProtoNet, MAML and ProtoMAML.\n", "On the few-shot image classification task of CIFAR100, ProtoNet and ProtoMAML showed to perform similarly well, with slight benefits of ProtoMAML for larger shot sizes.\n", "However, for out-of-distribution data (SVHN), the ability to optimize the base model showed to be crucial and gave ProtoMAML considerable performance gains over ProtoNet.\n", "Nonetheless, ProtoNet offers other advantages compared to ProtoMAML, namely a very cheap training and test cost as well as a simpler implementation.\n", "Hence, it is recommended to consider whether the additionally complexity\n", "of ProtoMAML is worth the extra training computation cost, or whether\n", "ProtoNet is already sufficient for the task at hand."]}, {"cell_type": "markdown", "id": "e546b5d0", "metadata": {"papermill": {"duration": 0.064218, "end_time": "2021-10-10T16:38:11.027954", "exception": false, "start_time": "2021-10-10T16:38:10.963736", "status": "completed"}, "tags": []}, "source": ["### References\n", "\n", "[1] Snell, Jake, Kevin Swersky, and Richard S. Zemel.\n", "\"Prototypical networks for few-shot learning.\"\n", "NeurIPS 2017.\n", "([link](https://arxiv.org/pdf/1703.05175.pdf))\n", "\n", "[2] Chelsea Finn, Pieter Abbeel, Sergey Levine.\n", "\"Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks.\"\n", "ICML 2017.\n", "([link](http://proceedings.mlr.press/v70/finn17a.html))\n", "\n", "[3] Triantafillou, Eleni, Tyler Zhu, Vincent Dumoulin, Pascal Lamblin, Utku Evci, Kelvin Xu, Ross Goroshin et al.\n", "\"Meta-dataset: A dataset of datasets for learning to learn from few examples.\"\n", "ICLR 2020.\n", "([link](https://openreview.net/pdf?id=rkgAGAVKPr))"]}, {"cell_type": "markdown", "id": "468b2212", "metadata": {"papermill": {"duration": 0.062817, "end_time": "2021-10-10T16:38:11.155742", "exception": false, "start_time": "2021-10-10T16:38:11.092925", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "![Pytorch Lightning](data:image/png;base64,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MzA6NjE2MzE2NjYiIGRhdGEtcGpheC10cmFuc2llbnQ9InRydWUiLz48bWV0YSBuYW1lPSJodG1sLXNhZmUtbm9uY2UiIGNvbnRlbnQ9IjQzMWM4N2NjNTk1ZDhlMjA5OWM0MTU1MGZkZmM5YjdkNmQxZDAyMmM5NmM3ZDgxYmViNjhkMTA4ZjIxNDlkOTIiIGRhdGEtcGpheC10cmFuc2llbnQ9InRydWUiLz48bWV0YSBuYW1lPSJ2aXNpdG9yLXBheWxvYWQiIGNvbnRlbnQ9ImV5SnlaV1psY25KbGNpSTZJaUlzSW5KbGNYVmxjM1JmYVdRaU9pSXdRekEwT2pFMVJEWTZRa1JDTnpZd09qRXpOa1V6TXpBNk5qRTJNekUyTmpZaUxDSjJhWE5wZEc5eVgybGtJam9pTkRVd05qZ3lNVEE1TkRrNU5EVTBPRE15TmlJc0luSmxaMmx2Ymw5bFpHZGxJam9pYVdGa0lpd2ljbVZuYVc5dVgzSmxibVJsY2lJNkltbGhaQ0o5IiBkYXRhLXBqYXgtdHJhbnNpZW50PSJ0cnVlIi8+PG1ldGEgbmFtZT0idmlzaXRvci1obWFjIiBjb250ZW50PSIyYjIzNWE3NTU2NjQzOTk1MTc0M2E5MGZkMzJiMWU3ZTk4YzViZTdkNTI4YTE1Y2I3YWM3Y2RlZDI2MjljMDI2IiBkYXRhLXBqYXgtdHJhbnNpZW50PSJ0cnVlIi8+7D1pd9s4kt/zK7jq7d7d16HE+0hsz3Mc55g4sSd20sfMPC5EQhJjimTzsK2e1/99qwBSpCjJpA7n6J3pjEmCQKFQqJsA9OiRAP87mNKMCCGZ0sPeJLqhiUsST0zz4SfqZmJGxj3BjcKMhtlhL6FxlPpZlMyeyKZlKIapSD3BIxkR40/kTswSEqY+VD169OjRIuixn03yoXhNZ8OIdTCJkszNs3Ql+MdplCcuFd3Ioys7OOxlSQ6vBkfYUbOzlAaAPPXEwA+ve8INCXLKwTsc8DqkmwQZR9E4oCIgRUUgjT/yXZL5UVhD2pWv8+fiG+XV648vDFUPZrGb/nQRPdeu/SDSxU/Hzu2zF7MrLX/7S++oSZR28G+u9HFqTaTbG0LGZ2+OP/70mzUc0ne34a+/vpET+ee/zdKf1bs8OfuwDfhff598nJ2+uB2atyqVxPz8Ksim1l9T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){height=\"60px\" width=\"240px\"}"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 12: Meta-Learning - Learning to Learn\n", " :card_description: In this tutorial, we will discuss algorithms that learn models which can quickly adapt to new classes and/or tasks with few samples. This area of machine learning is called...\n", " :tags: Few-shot-learning,MAML,ProtoNet,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/12-meta-learning.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab,colab_type,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 106.820185, "end_time": "2021-10-10T16:38:11.928740", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/12-meta-learning/Meta_Learning.ipynb", "output_path": ".notebooks/course_UvA-DL/12-meta-learning.ipynb", "parameters": {}, "start_time": "2021-10-10T16:36:25.108555", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"02efcb1a6aa4448eb2be5597b7229117": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "04c241d2abd34caa8d271a853e8f6a74": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_94a0798591274c8b8b0011603eb7638f", "IPY_MODEL_b0685ba39080452fa095a152855ce3f5", "IPY_MODEL_df741ff3bf654a9b8b7a024c14f54f61"], "layout": "IPY_MODEL_5643ce9402954952881a5eb9119ff556"}}, "05eeda38820a4700b6808a22c087f854": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "06dabf4f2d724f2e855a18452a421901": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0748cb2c03654ebebcdbf1bec54ef2a6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_72ff32b019be4252b3a9736e42d05257", "placeholder": "\u200b", "style": "IPY_MODEL_693e0990c811418d8be6f20a3c613f3e", "value": " 42/47 [00:00<00:00, 83.87it/s]"}}, "074a0b76522b4e2ab81b2abed09f23a3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_bffa3c9c26424621a044f35fc72dc03f", "IPY_MODEL_65f3907fd55c4b4dbd91642a08fd39d9", "IPY_MODEL_4ea0e80530d34d4f8d8485c83df9da16"], "layout": "IPY_MODEL_cb0c7957cd344fd5b8e229b443f1640d"}}, "08d9ec62524244f1872cdb20191f3be7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "0a0c945ddb6546be814dab292cf61c4e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "0a2fabd9ce614e4ab1700634bc17e28e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1357795e20b8457abe5ee3078efe3ca7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "136db4bc997541c5b3123895b1746e5e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "16b537a4cb21479fa2564472a850f609": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "18aca4955a7a4c64a8eb12925d3704d7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b380f61e129140fcb2daf6c273570e16", "IPY_MODEL_b7d2cb428c5c4272a094846477794af9", "IPY_MODEL_509d1c763a634f83b7fba1ad9494dc02"], "layout": "IPY_MODEL_74c3e37ba7ac43019562374aae3f8c4e"}}, "1984df6019f34935a0d89f8f0cc9c549": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "19f3640a3b6f411984df19dd9b9f2536": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1b10c53ce772446d99285a37607ef5e3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f6e7ccff3f3f4b87983f1603bf0b3547", "placeholder": "\u200b", "style": "IPY_MODEL_241f1955c2624d60b29151d36487ef5e", "value": " 298/300 [00:13<00:00, 22.45it/s]"}}, "219937af24194529826e86cbeaa92dc1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_45c4fd2d576f4ecaa4df4aca60ed4377", "IPY_MODEL_5fa0132632af4e80b186723f1a5c9667", "IPY_MODEL_43bf706a87a74c8c903431b130a3b7cd"], "layout": "IPY_MODEL_314d69183852488f8d2526db24356295"}}, "241f1955c2624d60b29151d36487ef5e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "25be42641c3c4d3abe06f325f7e5707c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "26bde6db982d430988ad5224841c46b6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "29ac64c53e904692b4e1bcacd235cb15": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2cc8415ac50d463e9eb6eba8bd7441dd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2e1ac83c3a2b4bda89b392a3b2795d7d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_842078486c2c449786473d055756c9f3", "placeholder": "\u200b", "style": "IPY_MODEL_fca8ada280ae405ab7fc84a435653757", "value": " 124/125 [00:02<00:00, 44.61it/s]"}}, "314d69183852488f8d2526db24356295": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "33082c5ed1d04d52bc8bf407f24b6cd2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3545a43fc2ba428b8b8c312ecdb91469": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_36c6f2b6dcba4d0386076ed767d9c503", "max": 300.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_441a767165c641359c9955258a379f90", "value": 300.0}}, "36c6f2b6dcba4d0386076ed767d9c503": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3ae54407f35e4135929978d9d2889764": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e08469460c584a789dd3516b7d78ae77", "placeholder": "\u200b", "style": "IPY_MODEL_e46fb7aeb83b4002bdd245d9c6f731e3", "value": "Evaluating prototype classification: 99%"}}, "3c728764f18c47bc8b94ff75bfdde4f4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3d2ebb08ff2f4bdbacdb096b84f1ec09": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "3e6e0061bdc745beaac7f83f84327df4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3e99426778d245cbbafb04d6f52c153e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "3ec9165e19ed46f8b78dfdfd0adc8ccf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4281b262e4a544969139ae166f292c37": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cb1d437a4c354907bc26e5c54ca76168", "max": 47.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_ea8059402deb40659f77d6c63d7f8d09", "value": 47.0}}, "43bbd6d914404eb7be1ccc7417e0c882": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "43bf706a87a74c8c903431b130a3b7cd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1357795e20b8457abe5ee3078efe3ca7", "placeholder": "\u200b", "style": "IPY_MODEL_4b795244ba454b47bca4061ab2fc69a7", "value": " 34/38 [00:00<00:00, 52.34it/s]"}}, "441a767165c641359c9955258a379f90": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "447d64a260fa4e579069160a3d9460de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "45c4fd2d576f4ecaa4df4aca60ed4377": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cc4167da1b794828ab4eb0679922efe1", "placeholder": "\u200b", "style": "IPY_MODEL_48a737ec864a4e288e6cf338e313bf74", "value": "Evaluating prototype classification: 89%"}}, "461f881689924b8e82af3e96a920be27": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3c728764f18c47bc8b94ff75bfdde4f4", "placeholder": "\u200b", "style": "IPY_MODEL_d939ed1df36b4c5db18f4b0496ee48de", "value": " 72/75 [00:22<00:00, 17.74it/s]"}}, "467e71f262cf432fba62ed8d75effd3a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "48a737ec864a4e288e6cf338e313bf74": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "495f557ce24948f0968a8e77cc121b55": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d2c2b819979a476a8482e9d67ebab1a7", "placeholder": "\u200b", "style": "IPY_MODEL_557662d2d3e3487cbedd294325df6f31", "value": ""}}, "4b795244ba454b47bca4061ab2fc69a7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4c13f424d1bb4cf0a44284e678229cf7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4d5211d9d6e24a2cb1baa906ffa8dac2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "4ea0e80530d34d4f8d8485c83df9da16": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_29ac64c53e904692b4e1bcacd235cb15", "placeholder": "\u200b", "style": "IPY_MODEL_a37e0600abe64891ba7bad38e42cae86", "value": " 12/16 [00:00<00:00, 117.22it/s]"}}, "4fd996b6dd534694b6413f376e6be22c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "509d1c763a634f83b7fba1ad9494dc02": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b19b954922464bc3ae7a287ae8e7ba36", "placeholder": "\u200b", "style": "IPY_MODEL_f4334aa28f094684884cba73f70d9539", "value": " 148/150 [00:04<00:00, 37.03it/s]"}}, "557662d2d3e3487cbedd294325df6f31": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5643ce9402954952881a5eb9119ff556": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "59b3b0d90a7548ae8ec79d1fb1aa783e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_fc0cd3a5fceb46438c8831f7ace96e8e", "IPY_MODEL_4281b262e4a544969139ae166f292c37", "IPY_MODEL_0748cb2c03654ebebcdbf1bec54ef2a6"], "layout": "IPY_MODEL_5c60626bcc1c4a56a49881ed8afd804c"}}, "5b9c06753fca42ca89e480bfe3de1b06": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_eae30ee4b8e54cfbafa7a73ac8c3720d", "max": 250.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_02efcb1a6aa4448eb2be5597b7229117", "value": 250.0}}, "5c60626bcc1c4a56a49881ed8afd804c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5fa0132632af4e80b186723f1a5c9667": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2cc8415ac50d463e9eb6eba8bd7441dd", "max": 38.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_b4923b5857b447e2aca7a73524cbb3bb", "value": 38.0}}, "6051618a7e8943ce91d14f6a31a0f679": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "65f3907fd55c4b4dbd91642a08fd39d9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c39a5cb98e1644529c1364704681c37d", "max": 16.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_7b48347de2bd47e88549b165c7af8d90", "value": 16.0}}, "66330b6ebefe44c7906d3e6e5fd221c6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "693e0990c811418d8be6f20a3c613f3e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "69f9bf1e8e3c43d0a2225f3c3d6cf91d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6ae81549f8a343a2985e8fbdd3e6cfce": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_9643addbcabd45b48bba6f84b24900f2", "IPY_MODEL_d022e70ba7ff4133a278315f4cabdec8", "IPY_MODEL_f69a45ea7e884b48bc7e22af83113f6d"], "layout": "IPY_MODEL_3ec9165e19ed46f8b78dfdfd0adc8ccf"}}, "6b6ae741f4c442b5bcccfbb60ea48963": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6e439485ca4e47de8597c3af51ac4d8a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_dee61b262f264abe9c8f19123bb21078", "max": 32.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_90ce08a097804618b4b4aa29e00c2221", "value": 32.0}}, "72ff32b019be4252b3a9736e42d05257": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "738ae6a5004b4fcd8e1acf8973c7fcf6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7427ffde194a471bb68404b98bba5521": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6b6ae741f4c442b5bcccfbb60ea48963", "placeholder": "\u200b", "style": "IPY_MODEL_4c13f424d1bb4cf0a44284e678229cf7", "value": " 169001984/? [00:01<00:00, 116158853.76it/s]"}}, "74c3e37ba7ac43019562374aae3f8c4e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "77ca484b49b447dc9c081a459a84ab3b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "783a9fed2b1a4153869348f1b4cd9bdf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7b48347de2bd47e88549b165c7af8d90": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "7f28b92e80274d7f9b778bfb634ac8db": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3e6e0061bdc745beaac7f83f84327df4", "max": 75.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3e99426778d245cbbafb04d6f52c153e", "value": 75.0}}, "8058b34966e64a54a236152f5de30268": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "820542f219d34da6af0d83ed20f6756d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_a38734b949f0443fae617d76a196b40f", "IPY_MODEL_6e439485ca4e47de8597c3af51ac4d8a", "IPY_MODEL_84f66ce16b0a435eb9746909f90b69b5"], "layout": "IPY_MODEL_cac735df1f3544b0a8a2c89d88e27d1c"}}, "8352a72f638049be98819a832027cb10": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "842078486c2c449786473d055756c9f3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "84a6abc3c3d54dd7a08fbc7b8dfd07f3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_97e410e1d6a245009d4f410fbf407f2a", "IPY_MODEL_f5f64e7b652046608b09e1865fc8929c", "IPY_MODEL_ae07625fe7bd4a2cbbbf1d0890d6df36"], "layout": "IPY_MODEL_cc5e5ce694504583a2d4dcaf311d80c2"}}, "84f66ce16b0a435eb9746909f90b69b5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_738ae6a5004b4fcd8e1acf8973c7fcf6", "placeholder": "\u200b", "style": "IPY_MODEL_0a0c945ddb6546be814dab292cf61c4e", "value": " 31/32 [00:00<00:00, 97.75it/s]"}}, "8a82349f3b624433926ed46218343ef9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "90ce08a097804618b4b4aa29e00c2221": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "93c64b6a35e441bf9a79602a829a41d7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b35a4714ac784c49970726e49084c08b", "IPY_MODEL_5b9c06753fca42ca89e480bfe3de1b06", "IPY_MODEL_b81a79f5481446ad9a9ef8597cc47731"], "layout": "IPY_MODEL_b6c8207aebc64e519f57ea458016b8d8"}}, "94a0798591274c8b8b0011603eb7638f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b8d61566453f40a8a10086d9159efd3f", "placeholder": "\u200b", "style": "IPY_MODEL_467e71f262cf432fba62ed8d75effd3a", "value": ""}}, "9643addbcabd45b48bba6f84b24900f2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_efed4fdf79514ceabb9b9762cc24d4a2", "placeholder": "\u200b", "style": "IPY_MODEL_c0993cf20a304d07b41e2a11fa17f1ef", "value": "Extracting image features: 78%"}}, "97e410e1d6a245009d4f410fbf407f2a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_136db4bc997541c5b3123895b1746e5e", "placeholder": "\u200b", "style": "IPY_MODEL_783a9fed2b1a4153869348f1b4cd9bdf", "value": "Evaluating prototype classification: 97%"}}, "9a243670d2374f4ca046908284044ab5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9dc8c432361347f482dc1fbac96f1ec5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a09bea6c4bec4d14b6ac5fc81cf9461a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "a0ee70498fb94ecc9c39238ad029ae29": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_26bde6db982d430988ad5224841c46b6", "max": 125.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_4d5211d9d6e24a2cb1baa906ffa8dac2", "value": 125.0}}, "a28f68eaf131448ebb0bc0fb801bfd0b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ef2a48d284cd4a8b99863c678d1eff1f", "placeholder": "\u200b", "style": "IPY_MODEL_e67ab28b10924b64a5d18ea090aa87e8", "value": "Evaluating prototype classification: 53%"}}, "a37e0600abe64891ba7bad38e42cae86": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a38734b949f0443fae617d76a196b40f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_69f9bf1e8e3c43d0a2225f3c3d6cf91d", "placeholder": "\u200b", "style": "IPY_MODEL_3d2ebb08ff2f4bdbacdb096b84f1ec09", "value": "Evaluating prototype classification: 97%"}}, "a4f9318e26d847d394ca5e252d5bbaf4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8058b34966e64a54a236152f5de30268", "max": 19.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_08d9ec62524244f1872cdb20191f3be7", "value": 19.0}}, "a878ed48e3e745238604dc0491a31f1e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "acb6f2dd23f94af091e7ed4aa1b7a3de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8a82349f3b624433926ed46218343ef9", "placeholder": "\u200b", "style": "IPY_MODEL_fb4936f8301947a686a8c8fe1df1eb27", "value": "Evaluating prototype classification: 99%"}}, "ae07625fe7bd4a2cbbbf1d0890d6df36": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_eda1ed0aecca4420a13f2ac4cc2475a3", "placeholder": "\u200b", "style": "IPY_MODEL_d36132a1ac6044e08ce39355ab0e24c0", "value": " 61/63 [00:00<00:00, 64.84it/s]"}}, "b0685ba39080452fa095a152855ce3f5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d388d078f57a4be6a40dfb1d09fcdb68", "max": 64275384.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_a09bea6c4bec4d14b6ac5fc81cf9461a", "value": 64275384.0}}, "b19b954922464bc3ae7a287ae8e7ba36": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b35a4714ac784c49970726e49084c08b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9a243670d2374f4ca046908284044ab5", "placeholder": "\u200b", "style": "IPY_MODEL_e79716d59af843ae8291b7d25d7afa97", "value": "Evaluating prototype classification: 100%"}}, "b380f61e129140fcb2daf6c273570e16": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a878ed48e3e745238604dc0491a31f1e", "placeholder": "\u200b", "style": "IPY_MODEL_f449bfa0562f46dd9d166a6667df8ce2", "value": "Evaluating prototype classification: 99%"}}, "b46d974e72914b52bd20ced66ed6fcd3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b4923b5857b447e2aca7a73524cbb3bb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "b6767d1462de4f8b8eb018e9f51ef402": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_e8f08f9a11444d0fbdfeaeebcdc3ac7a", "IPY_MODEL_7f28b92e80274d7f9b778bfb634ac8db", "IPY_MODEL_461f881689924b8e82af3e96a920be27"], "layout": "IPY_MODEL_c0922c095d624b36adfc21be13d915e9"}}, "b6c8207aebc64e519f57ea458016b8d8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b7d2cb428c5c4272a094846477794af9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_16b537a4cb21479fa2564472a850f609", "max": 150.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_1984df6019f34935a0d89f8f0cc9c549", "value": 150.0}}, "b81a79f5481446ad9a9ef8597cc47731": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_be1415cbc6e14d888ea2e0f649648c2a", "placeholder": "\u200b", "style": "IPY_MODEL_447d64a260fa4e579069160a3d9460de", "value": " 249/250 [00:09<00:00, 26.83it/s]"}}, "b8d61566453f40a8a10086d9159efd3f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bcd5770b13c540db929b606eb7518b21": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "be1415cbc6e14d888ea2e0f649648c2a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bffa3c9c26424621a044f35fc72dc03f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_66330b6ebefe44c7906d3e6e5fd221c6", "placeholder": "\u200b", "style": "IPY_MODEL_c3f25997fc9b49649de9d4690d00ba4d", "value": "Evaluating prototype classification: 75%"}}, "c0922c095d624b36adfc21be13d915e9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c0993cf20a304d07b41e2a11fa17f1ef": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c2ce51ed3ad643b2b521e696f82af495": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_3ae54407f35e4135929978d9d2889764", "IPY_MODEL_3545a43fc2ba428b8b8c312ecdb91469", "IPY_MODEL_1b10c53ce772446d99285a37607ef5e3"], "layout": "IPY_MODEL_8352a72f638049be98819a832027cb10"}}, "c39a5cb98e1644529c1364704681c37d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c3f25997fc9b49649de9d4690d00ba4d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c40fd99740dc4dd5980ca6537e42c5d0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "cac735df1f3544b0a8a2c89d88e27d1c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cb0c7957cd344fd5b8e229b443f1640d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cb1d437a4c354907bc26e5c54ca76168": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cc4167da1b794828ab4eb0679922efe1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cc5e5ce694504583a2d4dcaf311d80c2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cd0b594802d34dd58532e00bad4a7397": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ceabb6b1df1d4a1284de89bb635533c0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4fd996b6dd534694b6413f376e6be22c", "max": 169001437.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_e7e09bfea2384e90951c090fc2a680e7", "value": 169001437.0}}, "d022e70ba7ff4133a278315f4cabdec8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_05eeda38820a4700b6808a22c087f854", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_dfd6e04d1360446d9ef1db11256ebe69", "value": 40.0}}, "d2c2b819979a476a8482e9d67ebab1a7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d36132a1ac6044e08ce39355ab0e24c0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d388d078f57a4be6a40dfb1d09fcdb68": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d939ed1df36b4c5db18f4b0496ee48de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "db308fcc8f994e4aabcd079e27a47da6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "de8827ff3a9b4e519927fb64aa069b4d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "dee61b262f264abe9c8f19123bb21078": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "df741ff3bf654a9b8b7a024c14f54f61": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_77ca484b49b447dc9c081a459a84ab3b", "placeholder": "\u200b", "style": "IPY_MODEL_b46d974e72914b52bd20ced66ed6fcd3", "value": " 64275456/? [00:04<00:00, 21648157.34it/s]"}}, "dfd6e04d1360446d9ef1db11256ebe69": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "e08469460c584a789dd3516b7d78ae77": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e46fb7aeb83b4002bdd245d9c6f731e3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e67ab28b10924b64a5d18ea090aa87e8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e79716d59af843ae8291b7d25d7afa97": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e7e09bfea2384e90951c090fc2a680e7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "e8f08f9a11444d0fbdfeaeebcdc3ac7a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9dc8c432361347f482dc1fbac96f1ec5", "placeholder": "\u200b", "style": "IPY_MODEL_bcd5770b13c540db929b606eb7518b21", "value": "Evaluating prototype classification: 96%"}}, "e9c2476de61b41c78b739b3465029786": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_acb6f2dd23f94af091e7ed4aa1b7a3de", "IPY_MODEL_a0ee70498fb94ecc9c39238ad029ae29", "IPY_MODEL_2e1ac83c3a2b4bda89b392a3b2795d7d"], "layout": "IPY_MODEL_6051618a7e8943ce91d14f6a31a0f679"}}, "ea8059402deb40659f77d6c63d7f8d09": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "eae30ee4b8e54cfbafa7a73ac8c3720d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ed46b8de7055431dafc7c8dbf5aede81": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_43bbd6d914404eb7be1ccc7417e0c882", "placeholder": "\u200b", "style": "IPY_MODEL_19f3640a3b6f411984df19dd9b9f2536", "value": " 10/19 [00:00<00:00, 97.06it/s]"}}, "eda1ed0aecca4420a13f2ac4cc2475a3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ef2a48d284cd4a8b99863c678d1eff1f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "efed4fdf79514ceabb9b9762cc24d4a2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f4334aa28f094684884cba73f70d9539": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f449bfa0562f46dd9d166a6667df8ce2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f523d40c013f4da7bc639dc9291509e8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_495f557ce24948f0968a8e77cc121b55", "IPY_MODEL_ceabb6b1df1d4a1284de89bb635533c0", "IPY_MODEL_7427ffde194a471bb68404b98bba5521"], "layout": "IPY_MODEL_33082c5ed1d04d52bc8bf407f24b6cd2"}}, "f5f64e7b652046608b09e1865fc8929c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cd0b594802d34dd58532e00bad4a7397", "max": 63.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_25be42641c3c4d3abe06f325f7e5707c", "value": 63.0}}, "f69a45ea7e884b48bc7e22af83113f6d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0a2fabd9ce614e4ab1700634bc17e28e", "placeholder": "\u200b", "style": "IPY_MODEL_c40fd99740dc4dd5980ca6537e42c5d0", "value": " 31/40 [00:00<00:00, 74.38it/s]"}}, "f6e7ccff3f3f4b87983f1603bf0b3547": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fb4936f8301947a686a8c8fe1df1eb27": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fc0cd3a5fceb46438c8831f7ace96e8e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_db308fcc8f994e4aabcd079e27a47da6", "placeholder": "\u200b", "style": "IPY_MODEL_de8827ff3a9b4e519927fb64aa069b4d", "value": "Extracting image features: 89%"}}, "fca8ada280ae405ab7fc84a435653757": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ffaf8189884a44548efd49c252a85a7a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_a28f68eaf131448ebb0bc0fb801bfd0b", "IPY_MODEL_a4f9318e26d847d394ca5e252d5bbaf4", "IPY_MODEL_ed46b8de7055431dafc7c8dbf5aede81"], "layout": "IPY_MODEL_06dabf4f2d724f2e855a18452a421901"}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/course_UvA-DL/13-contrastive-learning.ipynb b/source/notebooks/course_UvA-DL/13-contrastive-learning.ipynb deleted file mode 100644 index 44f1eef..0000000 --- a/source/notebooks/course_UvA-DL/13-contrastive-learning.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "6000551a", "metadata": {"papermill": {"duration": 0.030399, "end_time": "2021-10-10T16:38:20.779892", "exception": false, "start_time": "2021-10-10T16:38:20.749493", "status": "completed"}, "tags": []}, "source": ["\n", "# Tutorial 13: Self-Supervised Contrastive Learning with SimCLR\n", "\n", "* **Author:** Phillip Lippe\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-10-10T18:35:52.598167\n", "\n", "In this tutorial, we will take a closer look at self-supervised contrastive learning.\n", "Self-supervised learning, or also sometimes called unsupervised learning, describes the scenario where we have given input data, but no accompanying labels to train in a classical supervised way.\n", "However, this data still contains a lot of information from which we can learn: how are the images different from each other?\n", "What patterns are descriptive for certain images?\n", "Can we cluster the images?\n", "To get an insight into these questions, we will implement a popular, simple contrastive learning method, SimCLR, and apply it to the STL10 dataset.\n", "This notebook is part of a lecture series on Deep Learning at the University of Amsterdam.\n", "The full list of tutorials can be found at https://uvadlc-notebooks.rtfd.io.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/course_UvA-DL/13-contrastive-learning.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "c075e657", "metadata": {"papermill": {"duration": 0.028259, "end_time": "2021-10-10T16:38:20.837183", "exception": false, "start_time": "2021-10-10T16:38:20.808924", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "132c3c36", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-10-10T16:38:20.897899Z", "iopub.status.busy": "2021-10-10T16:38:20.897428Z", "iopub.status.idle": "2021-10-10T16:38:20.900024Z", "shell.execute_reply": "2021-10-10T16:38:20.899479Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.034648, "end_time": "2021-10-10T16:38:20.900141", "exception": false, "start_time": "2021-10-10T16:38:20.865493", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"torch>=1.6, <1.9\" \"matplotlib\" \"pytorch-lightning>=1.3\" \"seaborn\" \"torchvision\" \"torchmetrics>=0.3\""]}, {"cell_type": "markdown", "id": "15410d3c", "metadata": {"papermill": {"duration": 0.02886, "end_time": "2021-10-10T16:38:20.957863", "exception": false, "start_time": "2021-10-10T16:38:20.929003", "status": "completed"}, "tags": []}, "source": ["
\n", "Methods for self-supervised learning try to learn as much as possible from the data alone, so it can quickly be finetuned for a specific classification task.\n", "The benefit of self-supervised learning is that a large dataset can often easily be obtained.\n", "For instance, if we want to train a vision model on semantic segmentation for autonomous driving, we can collect large amounts of data by simply installing a camera in a car, and driving through a city for an hour.\n", "In contrast, if we would want to do supervised learning, we would have to manually label all those images before training a model.\n", "This is extremely expensive, and would likely take a couple of months to manually label the same amount of data.\n", "Further, self-supervised learning can provide an alternative to transfer learning from models pretrained on ImageNet since we could pretrain a model on a specific dataset/situation, e.g. traffic scenarios for autonomous driving.\n", "\n", "Within the last two years, a lot of new approaches have been proposed for self-supervised learning, in particular for images, that have resulted in great improvements over supervised models when few labels are available.\n", "The subfield that we will focus on in this tutorial is contrastive learning.\n", "Contrastive learning is motivated by the question mentioned above: how are images different from each other?\n", "Specifically, contrastive learning methods train a model to cluster an image and its slightly augmented version in latent space, while the distance to other images should be maximized.\n", "A very recent and simple method for this is [SimCLR](https://arxiv.org/abs/2006.10029), which is visualized below (figure credit - [Ting Chen et al. ](https://simclr.github.io/)).\n", "\n", "
![simclr contrastive learning](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtIAAALnCAIAAAArvzfjAACAAElEQVR4XuydB5gUxdaGJZkDJoxEkSiYEcVAFjBeFJEgSZAsiKggiiL8YiAJildQQJBLRoJIkAwSdPMuu4TNObE5x/+bKWxnq3tme2ZnZyd871PyrD1VHeqcOvVVd3X1ZeWEEEIIIQ7hMnkDIYQQQkj1QNlBCCGEEAdB2UEIIYQQB0HZQQghhBAHQdlBCCGEEAdB2UEIIYQQB0HZQQghhBAHQdlBCCGEEAdB2UEIIYQQB0HZQYgnUpycXBgRgZQfEpJ16JBIF3/5JXXVKiklzpsX/+mn5lLi/PnqIhfXrlX2mX/2rDhQcUqKfBKEEM+DsoMQd6AwJiYvIADdfOrPP6Pjj589G5ogcuTIiGHDzvfsea5z56CWLQOaNPG56iqvyy6r2eRz9dU4k6BWrXBW5599FmcYOWqUQcTMmWNQLatX4yryAgOLYmPliySEuD6UHYS4AMVJSbne3hk7dqQsW4YeOmrs2PDXXxdiwu/WW9Vdu9skvwYNcI240vABA6LGjcO1pyxfjnrI9fFBncjVRAhxeig7CHEWStLScr280rdtS/7229hp0yLeeAPdbWDTpt6XX67uj5mQUDOBzZqhllBXsdOnJ3/3HWoPdViSni5XLiHEOaDsIKQGKIyKyjpwAAN3yIuw114LfuQRv5tuUner1Zf8brkloEkTw8MO470EkcIHDIgYNkxKMVOnqqd0KCnm3XfVRbAfZZ9BLVqIA/ndfLP6NKovoT5Rq6hbyBHUM2q7MDpaNgMhxOFQdhBSvZSkpeWcOoWeDz30heefRzfvXa+eupusYvK/7TYxW0JIB/S10ATJ33+fumpVxu+/G2ZLBAUVRkSUZmfL5+dwSrKycCY4H5xVxq5dOEOcJ87WcIPnH8mCa/Fr0EB9mVVMqHnUP6wAW8AisAvvixDiYCg7CLEnRQkJmXv3Ji1eHDV+/LkuXezVd/rWr3+mbVsxARM9dNI33/w79TIuTj4JN6IoNhbXaJgqu3p10qJFcZ98EmGcJIva8L3hBnVF2ZCg2M517Qp7wWqwHSwonwQhxH5QdhBiO2VFRbm+vhivR0+ejK6rig9KfK66Cr3phd69I0eOhLbAbrP2788PCXGGWxTOCWoG9ZP5xx+Gl3eMb+6c79ULdehz5ZXq6tWfYEdYM+add7Bb2BdWlg9MCLEVyg5CrKAkLQ2dXOKCBRFvvHHm/vttflzid+utIR07hg8cGPfRR+jbso8dc++bFo4H9YlaTV25EjWMeg557DGbX/mBlWHriCFDYHcIQfiAfDBCiG4oOwixRGleXvaffyYtXBjWv39g8+bqPqnS5H3FFWfatw/r1y/uk0/SNmzI8/MrycqSD0McAmo+19cXVoibORMWgV1gHbXJKk3wBPgDvAK+AQ+RD0MIMQ9lByEVKSvL8/dPWb48asyY4Ace8K5bV93rWEi+118f0qFDxLBhiV99lb59e/7Zs/L+iZMBG8FSsBesBtv5Xned2qwWEjwEfgJvgc/Ac+A/8gEIISZQdhBimCKQuW8fRsDnunb1ufZadddiIQU0bhz60ktxH3+cvnVrwYUL8q6JCwI7wpqwKSwb0KiR2ugWEvwHXgRfgkdxUg4haig7iIdSnJycvmVL9OTJwQ8/7F2njrr/0Eze9eoFP/hgxPDhSYsWZR06xNcvPQFYGbaGxWF3WF//hB74FbwLPgZP45qqhAgoO4gHURgTk7pqVeSbbwa1bKnuJMyloBYtwgcORK+Tc/JkaX6+vFPiYcAH4AnwB3gFfEPtMOYSMkeOGAEP5MJlxJOh7CBuDoaqaZs3R40dq7+H8GvQIPTFF+Nnz87cvZv3M4hl4CHwE3gLfEb/Mi3wRvgkPJPvxRBPg7KDuCGlubmZ+/bFfvBB8COPeNWqpQ766hTUunXkqFGpP/9cEB4u744Q3cB/Ulevhi/Bo9RuppFq1YKXxr7/fubevfBbeXeEuB2UHcR9yAsISJg791znznreivSuVy+kQ4eYKVPSf/21ODVV3pfbAR0mf1Hls89SV63CSN3BvV3CF1/g6Onbtsk/uB3wK3gXfAyepmdGCPwW3gsfhifL+yLEXaDsIK5NaU4OOjAMLgMaNlTHcTmsX3752aefRp+XdeCAg/vaGifmvffUFSKS/+23Zx8/LheoNnzr18dBI4YNk39wa+Bv8Dr4HjxQzyeF4c/waqgWeLi8L0JcGcoO4pLknzuXOH/+uW7d9NzYCH7wwZipUw3Deg+O4IrswMgbQ+qQxx8PaNJEKAAk/zvuKCsokMtUD54pO0yBH8Ib4ZPwTLW7SslwC6RbN3g714Ah7gFlB3EZykpKsg4dip40KbBZM3V0llJg8+ZRo0enbdjgCQ9Q9KDIjvzgYGVjWVHR+V69xPbMfftMslcjlB2mwD/TNm6Er+pZAzewadPot99GK0BbkHdEiItA2UGcndL8/IwdOyKGD/e75RZ1IDZNPtdcc+GFF1J++KEwMlLei8ejKTtA8rffiu0X164VW9ClJS5YcKZdO+86dQIaNYocObIwJkbJHzVmzLnOnRPnzYNMCXn8cYzFg1q0wFjcdHXOkvR0qMOAJk28atcOatMmbuZM00dalB3mgN/Ce0NffBGerHZv04S2gBaBdsE3uonLQdlBnJSSzMyLa9aE9etXaQgOvPfe6MmT0Qs67DGBK6IpO1Bj57t3F9vz/PzExvABA6Qa9r/jDkV5hHTogC0QE9IaaxiFiwwlWVln7rtP2sPZJ59UvuNK2VEpsEvmH3/Aqyt96xutI+zVV9FSSjIy5L0Q4pRQdhDnojg1NWXZsvM9eliedudz1VXnn302acmSgtBQeRdEC2luB1Jg8+bK3I7Qvn1FtoydO8WW2A8/xEgaWkSsDh4+eLDIIGSHYcugQUWJiflnzxreUr7M8CKoeP8idto0L+OLQmkbNpSXlaVv3SpMmbx0qdgDZYdVwMPh5/B2+Lzi/+qESkarQdvhU0Xi5FB2EKegJDMzddWq8716WX7P0O+mmyKGDk3fts3T3kOpOubeZAls2jR+1qyywkKRDfLCUM+33FJeWiq2JM6f72UcVYv5BEJ2QDoot/dzvbzEruJnz8b/ipk3F55/XvwKwl55BVvOde0q/peywzZK8/Lg+ag3tAK1HZWEFoR2hNaENiXvghAngLKD1CRQDxfXrQv9z398rrxSHUCVFNCwYfSECVkHD5YVF8u7IPpQZEfC55+nLF8OEWD439q1Y6ZOVRRG+T+qwufqq8UdEaSgNm1EwYKwMCXD2U6d/t11aan4Tm/kiBFlRUVifTb/O+5Q9hBw992GLbffLrJTdlQRtAK0BbQIyy+No02Fvvwy2hc1OnEqKDtIDVBWUIBxW/iAAeje1OFSSUGtWsXNmJFz+rRcnliPNLejrKTkXJcuYkvU6NFKtuAHHsAW3+uuU0SDkkxlR0jHjkoRIO5RQXaghxP7hNSQil/o3VtkpuywIzl//YU2YnlFVLSy8NdfT//1V05+Is4AZQdxKDknTkSNGeN7443q4KikM/fdFz97dv65c3JhUgXUU0qLYmMVQ6Rv2SI2hr74opfx9pJSsCQtTZkNWq48ZLn+euUhCywldhL/6af4X/HCUVj//kqRosRE5e9yyo7qoeD8ebQa9WRe04Sah8REG5QLE+JAKDuIIyiMjo6fM8fytPzA5s3jPv44LyhILkzsgVp2gNTVq8VG/zvuEK9CpCxfLrakLFtWbnx7Ofihh7xq1z73zDPiFVllSmnkqFEYPZfm5IS+9JLYgpE3MkBPeBlH2OJ/C6OioFHwvzHvvCMOStlRraAFoR1ZXgUELRHtkR/CJTUCZQepRkpzc1N//vlct24WvscW0KgResRcLy+5MLErmrIDXHjhBbE9auzYcuMCYoqwQOfkd+ut4u+Un34S+S9N/rjmGtgUYkKZlBM+cKDIUBgZKUp516t3pm1b8f6zz1VX5fr6igyUHY4BbSr2/ffFi0jaqVatc127ooVy8gdxJJQdpFrIOnwY/YqFJTf8b7steuJER34KxMNJ+PzzgCZNkKRXjovi44Nat8b2wGbN8s+cKTcu9mV4EHbddcJSgU2bXlyzRsl/aUrpE09go5AXEB/RkyYp78KUGx+7XHjuOeUV6JCOHXNOnlR+PdO+PQ5nmMpKHAJaGdoaWpy6GYqEdorWijYrlySkGqDsIPakOCUlcd68oJYt1aFNJO8rrgh79dWMnTv5TorzUxQXV5yUJG1UZIf438KICFPBUYHSUvzK1zidBLQ4tDu0PgufMULLRftFK5YLE2I/KDuIfcg6dCh8wAALES3ksceSly4tvnhRLklcCkl2EJejJC0NLRHtUd1IRUIrRltGi5ZLEmIPKDtIlbh0e8P8XNGAu++OnT49PyRELklcE8oOtyH/7Fm0TbGqimYyfG2HNz+IvaHsIDaSfeRI+Ouvm7u9YXiY0r9/5h9/mC5FRdyA+FmzIoYNi58zR/6BuChlZWinaK0W2jJaOtq7XJAQm6DsINZRmp+fsnz5mfvvV4cnkQwjpK+/Lk5OlksSQpwYtFm0XAt3LtHq0fb5zVtSRSg7iF4Ko6Nj33/f7+ab1fHI658hUdbBg3IxQohLgVZs4UYmIgDiANf8IDZD2UEqJ+vw4dC+fcV3N9SJD4AJcT8sT9tCNEBM4Du3xAYoO4hZLj1Pad9eHXRE3Al75RXe3iDEvUEbR0s3N+pAfOCTF2IVlB1Eg+LU1PhZs5QVKqVkuMv6wQe8y0qI52B4xvrBB+aesfo1aBD/2WeIG3IxQlRQdpAKFISFRU+Y4HPVVerI4iXmlP34I0c2hHgmlmeUG9arnTBBfKmYEHNQdpBL5P79d1i/ft516qijiXiewjfoCCECRANzT14QQxBJEE/kMoQYoewg5Rm//Xauc2d1+PAyfinbA2etl2RkFEZEIBWcP5916NC/af/+1FWr/k0rV8Z/+qlpwhbTDFkHDpgWL7hwQexWfOuV1BS0r70Qb7eJb/upE6IKYotchng8lB2eS1lJycVffjnTtq06XiAFNGyYuGBBSVaWXMyVKYyMzA8ORg+Rtnkzuo34zz6LnTYtYtiw0BdfRIgMbN7c/6671FVRTQnHCrz3Xhw39KWXcA6x06fHz56Ns0rfsgVnmB8SgrOVL4BYhPatERAlEufPR8RQVwISIgziDKKNXIx4KpQdnkhZUVHKTz8hJqpjhCFM3H+/IUwUFcnFXITS3Nz8M2cyfv89eenS2A8+COvfP+Sxx/xvv119pS6RcOYhHTviKnAtuCJcF67Ok79UTvs6J4gYF9esMTftA9EmdcUK140qxI5QdngWZYWFyd9/H9CkiTouIJ3v3j1z7165jLOCEJYXEJCxc2fSkiUxU6eGvfpqyKOPmnv7xv2SX4MGuN6wfv1w7cnffot6yAsMdKewTvu6qH0z9+xBJFFfERIiD+KP2U8WE8+AssNTKM3PT1q0yP/OO9WxwLtu3fABA3J9feUyTkbBhQvp27YlfP45YvGZdu00p7PZJflcey3iI1JQixbnOnf+N3XrFjFs2L9p+HDp2T+2mGY417WrafHA5s3Fbn2uuUZ9ULsk1AlqJuy111BLqKuC0FC5Ep0Y2rfS5EL2RTwxfJJay4j+d92FWMQX4jwWyg73pzQ7O/GrrzB4Urd/78svjxw1yjlfeCtOSsrcuzdp4cLIN98MfuQRdBXq87c2Bdx9dyWP26Oi5POoNsxORHjpJUMfdu+9dpmIgHrDoDly5EjUJOrTeb6VQ/u6t30FiC2IMIgz6jP3v+02xCVEJ7kMcXcoO9wZjCcSv/7a75Zb1G3e58oroydMcKpXVHAyaRs3Rk+efK5LF3OrEulJ3nXqBN5zD0aikSNGxM+alfrzz1mHD7vu9D2cOc4fV2H49Ovw4biuwGbNNN9z1plQt6hh1DNq25EOQPtq4jb2tQBOw7Aa0JVXapztLbcgRvHOh0dB2eGeGOZwfPed/x13qNu5zzXXxEyZUpSQIJdxODjJnNOnMUoL69cP41T1qVaaAho1Ot+jB4ZT8XPmXPzll+xjxwpjYuTDuCkI5bjei2vW4Nox2EU9oDbUVVRpQs2j/mEF2MK+D91p36rg/Pa1FsQcRB6fq69WnyQiFeJVzZ4ecRiUHe5GWXFxyk8/aU4a9b3++tjp02v2m21FiYnpv/4aM3Xq2U6dNEc/FpJhGNe5c/TbbycvXZpz6lRJZqa8d48HdZJz8iTqJ3riRNSVtTcVYBHYBdaBjWApee86oH2rlRq3b9VB/EEUQixSnx6iFmIXIphchrgXlB1uRFnZxf/9T/OLkb433BD3ySclaWlyEYeQHxKStGRJ+MCBgc2aqc/NXPK55prgRx6JfPNNw0PrPXuKk5Lk/RIdGKZQ7Nnz7xQKa2Y7wl6wGmyXHxws79cE2rcGcYB9qwPEIkQkxCX1WSGCIY4hmslliLtA2eEmYPhy5r771G0YYSh22rTiixflAtVMSXp62oYNCIXmFhFSJ5+rrz771FOx779vmKJ/4YK8R2InCs6fRw2jnlHbmne8NRPsCGvCprBsOe3rxNjFvo4BcQnRSVMqIZohpskFiFtA2eHyZB8/fvaJJ9Tt1vuKK6InTXLkILKspAQng0FMyOOPe9WurT4ldQpo1Cisf/+kRYty//7bJdYkcDNQ56h51D+soHfqQK1aPtddh3/l7VqJ9q1ZbLFv7dpov2jFaMuOWVoUMSr67bcRr+QzuewyRDachlyAuDiUHS5M/rlzoX37qtuqd716kW+95bBJ7IWRkSnLloW9+qrmLVP53K64IuSxx2LeeSdt8+ai2Fh5X6RGKYqLg11gnZCOHTW7gUoT7evMWGtf3+uvR7tG63bAi0KIV4haiF3q00CUQ6yTCxCXhbLDJSlOTo4aN07dRL3r1Il44w0HLCKEUVTmvn0YowS1bKkOE1LyueaaC717J86bl/3nn2UFBfK+iPMB+2bs2hXWr5/mci+aye/WW9FFZfz2G+9quARoiTknTiTOn3+hTx/NxxxSQkuPnjgxc+/earUvYhcimPr9YcQ6RDxnW5WE2AZlh4tRmpsbP3u25jzw0Jdfru6pYWWFhRm//x4xfLjfTTepT0BKZ+6/P/aDD7IOHKDUcBWssq+55HvjjRHDhmE/fB/SVUALRTtFaw1+4AG1QR1vX8QxRDONQ19/PaKfK36whphC2eEylJWUpPz0k+bq5iEdO2YfOyYXsB+IShk7d0YMHWruC9dKwuA4fNCg1J9/doZ1QYhOrLJvaN++kW+9FfrKK5XeCMHesE/smbrThUDLTV29Onzw4Bq3L2IaIpv6uIiBhvdsHTLvhFQHlB2uQdahQ2fat1e3wMDmzdM2bpRz24nS/Pz0bdsi3nhD8+aKkrwvv/xc584Jn3+e6+Ul74I4MVW3L7ZgO37VXP1aSdg/joJjcTFK18IZ7Iv4hiinPijiIaKinJu4ApQdzk5hZGRYv37qVud3yy1JixdXx3PW0ry89C1bwgcNsvyZDL9bb40cORJjHX5VwbWoDvuW5uQgZ+SoUZaHyDhi+MCBODrOQd4FcWJq1r6Icoh1mh95QGx0wHRXYl8oO5yX0tzcuJkzfa66Sm7bV10VO316SUaGXKCKlJVl7tuHqGF5fpn/bbdFjRljmLHBm5yuhUPsi1JZBw9iD/63367euZJwDuEDBuB8uCqUa1GD9kXEQ9zTjIeIk5zw4UJQdjgpF9et01iIqVatiCFD7P5ZisKoqPhPPw1o3Fg+nEnyv+OOqPHjDXc1S0vl8sS5qRn7lpZmHT6MfWrORlISzgrn5sgPwxL7UEP2RfRDDFQvG4NoiZgp5yZOCWWH05Hr43P2qafUDTikQ4eckyfl3FWgrKAgbf368z17Wljay/+uu6InTsw+csSOoxbiGJzFvmVl2H/0229b+s577do4T5xtNU1OJNVITdg359QpxEP5KJddhsiJ+CnnJk4GZYcTUZKWFjV6tPqddf/bb09dudKOHUOenx/ChIXvSGHoED15crW+HUOqD6e1L44Y8847Grfx/kk4Z5w5zl8uSVwBh9q3rAxRUf2RbcTPyLfeqqnvTxE9UHY4C6mrVvndeqvchC6/PPaDD0qysuTcNlGSnp68dGnwww+rw4FIPldfHTFkiGHsS1wQF7IvzgFnYuGLIbgKXIsjvw9C7IjD7IvYiAipftEGsRQRVc5NnAPKjpon/8yZs08/rW6ZF55/3l5fzEIUMLy5oJqNpaSQDh2S//tffmrcRXFR++J8Un74QfNuuUi4IlyXM+gkYgMOsy/i5IUXXlDvH3EV0VXOTWoayo6apDQ3N3b6dPUa50GtWmXu2SPntp7S/PyUH388c//96gYpEsYEMe+8kxcYKJckroDb2BdniPNU3+1TEq4RV1ody0IQB+AY+yJmInJKe0Z0jZ02je+5OBWUHTVGxs6dAU2aSI0E8j/h//6v6qtxFMbEoLFpvuluaIp16lzo3Ttt06bqW96YVCtuaV+cLc4ZZ66e3iQSrhdXzS/MuSgOsC8iJ+Kn+uEOIi3irZyb1BCUHTVAYVSU5hcHLjz3XEF4uJzbSnJOngzr18+7bl31/pECmzaNnzPH7q/gEofhCfbF+eMqcC3qC0TCtaMG7PtWF3Ek1W1fRFHEUvWeEXXt+CovsRnKDsdSWpq0eLF6dciAhg3Tt2yRM1tJxo4dmm/eGlLt2mhyWfv3y2WI6+CB9s06cCC0b19zGgu1gTqRyxDXoVrti4iqfqcGsRcR2G6L0xCboOxwHPkhIWc7dZKaAZpczLvv6ll/2hxlhYWpK1eeadtW3W4Nzezqq6MnTLDX1FTieGhfjFBj33/f3EdxUTOon6o/lyQ1RfXZF3EV0VUta84+8QSisZybOArKDkdQVlyc8H//533FFWrvz/P3l3PrBo0qcd48c6v0+N95Z8IXX/D9ddeF9jWlNC8v5YcfzrRrp64KQ23cdRfqqiryndQs1WdfxFhEWmmHiMaGWXTFxXJuUv1QdlQ7ud7ewQ88IDm9b/36KcuW2bwCWElmJtqMuRmFwQ8+eHHNGtvGB8QZoH0tkLV///levdTV4mWck4h6s9c6N6RGqBb7lpUh3iLqSjtEZEZ8ljOTaoayoxopzc83LGWjusUX+tJLRXFxcm59lGRkxH/2mfYNyVq1LrzwQtbBg3IZ4jrQvjrBEDZ80CD1y+dIqD3Uof2/lUgcSHXYF1EXsVfaG+IzonQV390lVkHZUV1kHzsW1KKF3GAaNEhbv17Oqo+StLS4Tz5RC3Yv42u3UWPH5p89K5chrgPtawOFkZHRkyZpflMXNYn69JyHUG5Jddg3bcMGxGFpb4jVjv9WgMdC2WF/ygoLY957T/39rfDBg4svXpRz66A0Ozt+9mzfG27QaHvXXx83c2ZxaqpchrgOtG8VQbNCLaGuNCqwfn3UrW1zAoiTYHf7YoeIxvLeatdG3HatpW5cFMoOO5MXGKheNTKgUaOM33+Xs+qgrKAgadEitTb3Mr4JFjt9um06hjgJtK8dQV2hxtRvp3sZ7zKinu31+VNSI9jdvojJiMzSrhC9nX9VX1eHssN+lJUlLljgc+WVFfy4du2oceNsmANVVlyc8uOP6lbhZXxnEqq8OCVFLkNcB9q3mkC9ofbU61R6GdU/6pwvL7g09rUvIjPis3RnGjEckdzm+f6kUig77ENhTMy5bt3kZtC4cdbhw3JWHaRv3RrUsqW6XaE9RE+eXJSYKBcgLgXtW92gDlGT8hjAmFDzaZs3ywWIS2Ff+yJKI1ZL+0E8d/XVfp0Wyg47cHHdOvVMwPDBg22Ya53n53euc2d1W/K+4oqo8eNtfv+FOAm0ryNBfaJW1evlIJ198smcU6fkAsSlsKN9EavVsz18b7wRsV3OSqoMZUeVKElPDx84UO2saRs3ylkrozg5OXLUKPVHkrwvvzxq9OjC6Gi5AHEpaN+aAnWLGkY9SzWPFD5gQNW/gkRqFjvaF3Eb0VveycCBiPNyVlIFKDtsJ+f0afWtufM9elj7BcWywsKEL79Uv8jgXbdu5IgRhRERcgHiUtC+zgDqGbWtXkQHY+WY995jv+Lq2Mu+iN6I4dJOEOcR7eWsxFYoO2wkadEiaSkbnyuvxEZrJyKl//pr4D33SF6OdL5XL341wA2gfZ0K1LnmCph+t9yS8sMP/EKYq2Mf+5aVIZJLE0cQ7Q3hndgDyg6rKcnICO3bV3Lr4AcfzD9zRs5qkTx//3NduqhbSFCrVra9bUucChe1b2F0NGJ31qFDSBfXrEldtUpJ8bNmxX/66aU0a5bpT8gpiqCs8z8wQv3DCmrTBD/0UPbx43Ju4mrYxb6I54jq0h4Q+W2YsUckKDusI9fbO7BZM8kXY955x6oPZJh7zO93001Jixdb9QIYcUKc2r6lpQVhYVkHDqT89FPcxx9HDBt2vnv3kMcfD2jSRHM5JpsT9oZ9Ys/YP46CY+GIOC6OrnfQWZ3ACrCF5iL0EW+8wbm9ro5d7IuojtguFUf852dcqghlhxUkf/edNGvat3799G3b5HzmgR8nfv215mP+6AkTuDaUq+Ns9i2MisrYuTPhyy8jhg49+/TTmsuE1EjCmeB8cFY4N5whzlM+dYcAi8Au6gkBvtddl/DFF1aNJYgTYhf7IsJLLyqiF0BfIOcjuqHs0EVJVlbYa69Jvhv8yCNWzQfMOXHiTNu20k6Qzj/7bH5wsJybuBo1bl94afaxY8nffhs1fvzZJ55Qqx9nTjhbnDPOHOePq7BhhT2bgXVgI/UpBbVunX3kiJybuBpVty/iPKK9VBw9giO91J2g7KicvICAwHvvlXwueuJE/av3l2RmqtfCM/h9y5YZu3bJuYmrUYP2LQgNTV29OmrsWMOS/LVqSSdgQ/K/8054+7nOnZHCBw+OGDZMSXGffKLM7cDfpj8hpyiCstiDerdWp1q1cEW4LlwdrlG+7GrAMCFAaw03XB2/ieMGVNG+iPaI+VJZeDt6BzkrqQzKjkpIW79e+v4hhmVpmzbJ+cyT/uuvAXffLfmr7403Gj4ioO8uH3FmHGzfspKSnBMnEufNC335Zc2PuVSa/O+662ynTuGDBsXNmJGyfHnm7t3ZR49iPGffZ0DYG/aJPWP/OAqOhSPiuDi6+pQqTbhSXC+uGteOGpAPZidgL1hNvXKD3803p/z0k5ybuBpVty8iv3QT0efaa63qDkg5ZYcFEN1ipk6VHDT4wQf1j72K4uLU77xgGBf55pv2DfGkRnCkfQujo1N+/DHstdesenTiW78+enrx5CJz7978c+eqQwlZC84BZ4LzufQ8qFMn9SK/FhJqAPWA2qimV2ZgO1hQfevo7FNPOeZhGalWqmhfxH/5DZdataCqnWGitKtA2aFNcUrKua5dJb+MGj1a70cOy8qSv/9e3UME3nuvbV9pIc6FQ+xbmp+PvjlmypSg1q2lA5lLZ9q3Dx8wIPGrrwzzNKunV64mcLY4Z5w5zh9Xob40zYSaQf2gllBX8h6rBuyofrTqffnl8bNnO4N0I1WkKvZFL4C+QCp7oU8fvlurE8oODXK9vaU5/3DHlOXL5XxmgGTGAE526Hr1oIj1qhbixFS3fUtzc9M2bMCAXvMzm1Lyve668927x82cmbl7d0lmprwvlwXXgivCdeHqcI3qC5cS6go1hnpD7cn7shVYEzaVVgX0Mmq7XC8vOTdxNapo39QVK6QlxYJatNBzv4RQdsikrlrlc9VVps7kf+edOSdOyPm0KCssjPvkE/XXAUI6dswLDJRzE1ejWu1bmpNzcd26sFdfrVRt+N16a/iAAcnffpvr62vtqrguSVkZrhTXi6vGtasrxDQZ9Merr6ImUZ/yfmwCloV9paN4160b+/77dr/FQhxPVeyb+/ffAQ0bmhb0vf56q5ZU8EwoO/6lrKgoavx4yf/gkTrXlsk+dky9NB68MGnJEj72cwOqyb4YcqWtXx/at68kdqWEMdnZp5+OnzMn56+/5F14GKgB1ANqQz1ONU2oT9Qq6tYOt6BKS2Fl9XJqgffeC6+QMxOXowr2LU5Olr8pXbt2/KefesR4wFYoOy5RnJR09qmnJLeLfPNNPW/JIq7FvPuu+v3J0BdfLIyJkXMTV6Oa7IsRfPTEiZoLKSopoFGjqNGj07du5QoBalAnqBnUj+Vl0FDDqGfDnaGqAVvD4vL+a9eGb9hB2ZCaxmb7lhUXqxczDX3pJbZZc1B2GMj18ZEnc9Srl7x0qZxPC4SzM/fdJ/mc/x13pG3eLGclLojd7VuSnp783XfBDz8s7dM0BTRpEvPeezmnTsmFiRlQV6gx1Ju6MpWEOkfN6/8YqSawO6wv7RkeUnVZQ5wBm+17ce1a6fFoUOvW+efOyfkIZUe5WJmjorv433Zb9tGjcj4VZSUlCZ9/Lj/pr1ULwy9OaXYD7G7f7CNHwgcNsvAwJbBp09j33+cntqsCag91iJpUV69IqH9YQefylJrA+oYXGSq+gQk/gbdU35oixGHYbN88Pz/J8XxvuKG6Fwx0RTxbdpSWxn74oamXeIklz3XcOS8IDQ15/HGpLAZbdnx/ktQgdrRvWUFB6ooVwQ88IO1NSX633hozZUru33/LJUkVQH2iVi1MQYVFYBfL988tAE9Q31yBz+hf14c4M7bZt/jixfM9elQoVbt2wv/9n5zPs/Fc2VGSmXnh+eclr4oYMqTS2cvl4m2Xa6+Vyw4f7k5vMHoy9rJvUXx83IwZ5no+7zp1LvTpk7Z5s575Q8Q2ULeoYdSz+oPAIsE6sBEsJZfUAfwBXiHtEJ4D/5GzEhfENvuWlZRET54slQp79VV7vVrlBnio7Cg4f15agsm7bt2kRYvkfCpKs7PDBw+WXMqvQQO+NOUe2Mu+OadPhw8YID+g+ScFNm0aP2dOUWysXIxUG6ht1Lm5hy+wFOxl2+Mt+IZ6lXp4EXxJzkpcENvsC2kifa78TLt2BWFhcj6PxBNlR+bevdKy/H633JJ18KCcT0Wut7d6YbvQl14qTkqSs1YnZeVlqTlJ0WlhZxP9T0YcEGlfyNZdQetEwt/KduRBTuRHKXlHpCJ2sS+869wzz0g7EQkD7tC+fbMOHJDLEAeC+ocVzN38gO1gQblMZcBD1C9BwJfgUXJWooWTxzTb7Jtz8qQ0O9Xvppsy9+2T83keHic7kr/91rtuXVNXCH7ggcLISDmfiqRFiyT16nPNNfqXLrWNvKLcsJTgP8P2bfNf/eOfX83dO+XdLQPHrX/JtoSy2AP2g71hn9gz9i8f0lOpun3Tf/015NFHTfegJMjcmKlT9bgZcQywBSyi/iqYSLAjrCmXqQx4i/TZSHiUnnuoHoXrxjQb7FsUFxfSoUOFInXrog+S83kYHiQ7DI/c3n7b1AOQwgcMqHQ15ZK0tAsvvCAVPNO+fXWsg5tfnBcU773nzKZlx7+Y+dtodTOze8JRcCwcEcfF0eUT8gCqaF/41cW1a9Uv2YoU1Lp18tKllfoYqRFgF1jH3CdvYFNY1vLLCxLwGfUHZeBd8DE5q8fgTjHNBvuW5udHDBkiFUFPZJVfuRmeIjtKs7MvPPecqeG969RJ/OorOZ8Kw/dZVPOZo8aO1TPzVCd5RTl+sac2+/70xd4p4ze8rG5FDks4Os4BZ4LzwVnJJ+qOVMm+paWpK1cGNm8uFRfp/LPPZu7ZIxchTgksBXupjYgE+8LK+heihefAf6SdwMcs35B3M9w4ptlm38R586TneuiPLM8OcWM8QnYUxsScuf9+U5P7Xnddxu+/y/lUpCxbJn3sx7d+/fQtW+R8NhGXEbkneNOCA9MnbPiPurVYTh/uGDFn99sLD85YdWrh6tPfIP3qt0p5Doq/xUb8ijzIifzqnVhOOCucG84Q5ymfurtQFftm7NqlfYejVq3Q//xHz6ekiLMBq8F26k+iexnvfFi1AAO8CL5kugd4GvxNzudeeE5Ms8G+6HGkjxqiV9KzWIP74f6yAyLU/847TY0d0LBhXkCAnK8ipXl5EcOGmZbyMr60XfXH82EpIRt9ln+0Y6S6VagT2sn/7Zm04sS8HQFrT0YcQNmsfNvXWERZ7AH7wd6wT+xZZ3TA2eKcUVbeo8tSFfvmnD4tf4XBmLzr1g0fNCgvKEguQFwKWBB2lGaAiQS763/bBb6kXvoFXgffk7O6OJ4Z02ywL/od6dNx6Jss3yZxS9xcdqRv2yZNAgp+5JFK39EvCA2V7o4gRU+aVFZUJGfVTXRaKAT7Rzsrb5lzdk9c5/X9sdA9UWmhRSXVu6ID9o+j4Fg4Io6rPhkp4fxxFbgWeUcuhc32LbhwIey116SCXsbXLyPfesvyOkLEtYA1YVPNV6DhA/AEuYAW8Cj4lVQcvucersKYZoN90fugDzLNjx7K2vfzXR13lh2J8+ZJn+8K7du30sl9Gbt2+d5wg2kp3+uuS1u/Xs6nj9zC7EPnf4MGV7u7aUIG8fQxp7Amn/bh6OKJrJ4TxnXl1ujZ2oZt9i3JyIieOFHjk6e1a0cMHVoYFSUXIG4BLAv7qr8CCE+AP+hcIx/eJd1dN6yZ/dtvcj4XgTFNwlr7og9CT1TBo2rXRm8l53Nf3FR2lJaqX1qJee+9SueFxc+ZI4WYoDZtdL7RIHEh+czKk/Mnbeqn9m+RJm/u/99j/3c8bG96bqpc2AnAWeHccIY4T/XJi4SrwzXiSuXCzopt9k3bsEH9dSikC717V/q0jrgBsDJsrXYAwxcBN2yQc2sBH4OnVSheuza8Uc7n3DCmmcNq+5aWoj+S3Al9VqU9lHvghrKjrKBAuhOOoUmlCzCUZmeHvfKK5Afhr79u7WTj4tLi05GH5+6donboSy1z02s/nZjnF3OyuLSSW/pOAs4TZ4tzxpmrL0ckXC+uGtcuF3YabLNvQVjY+V69pFJexnUd9KwvR9wJWFxzXRZ4iJ7VJw0L4L7+ulQWPmnZA50BxjQ92GBf9ErSDVT0XDZ/JMiFcDfZUZKRIU33861fP2v/fjlfRQwP+9u1My2lc610U/KKcvac2TR9+zC1B48zTKTqC5XtFXW0sMRVvQpnjvPHVeBa1BeIhGtHDdjlLTX7YoN9y4qKEj7/XP212MB77qn0iQxxY2B9+IDkFfATw+dJK5sbVC5Wpas4WdWwZrb5qQA1C2OatTHNWvuib5LeiEH/pfPhneviVrKjKC5OWsvF/6678gID5XwVydy71++mm0xL+d16q1Vj2dzC7N8C/2durb2Zv43eE7wpI8/sejIuB64FV2Ru5R/UA2rDhkek1YQN9s0+fvxM27amRbyMKxLGffKJJ4xFiGXgA/AEaU1bJPgMPEfOrQK+J30d0LBmtvUrslcrjGk2xzRr7YseCv1UBUdq3x59mZzPjXA92ZEfEqL5PBXbAxo3NjVeUJs2hdHRcr6KpPz4o7SKS/CDDypvUZaUVrKQHBxxR8Dad7cMUHvqhA3/+eH43LOJ/nIZNwJXh2vUfGMNdYKaqbShVlrDVcSyfdWUFRbGTJ2qnkJ4rkuX/HPn5NzEg4E/wCskP4HnwH8q/aQwPBB+aFoQQ2TDomTVT6UtjjGt6jHNWvuin5KmhqAvQ48m5zPebNMjbZ0c15MdoS+/DBNKN7pzTpzwu/lmU7Od7dSp+OJFw29lZj8XlPztt9LSQKZrpafnpi48OMNcK8X2g+d2vLd1kNo7p2x+fZPPjynZiXIZNwVXiuvFVaurAvWDWrJQh6jhqs4+s9W+avICAtQrH2Pgkvrzz3JWQozAN6ShrZdxtFrpXGP4IbyxQsFatSx9rcO8n+uHMU0nVY9p1tq3ODUVfZZpfvRo6NdM86DXQ993vnt3042uiIvJDgg9YRJT5ZGxY4fP1VebGgzSRKzZkn3sWPTkyRV28Q+IF6aDWvVa6StPzoeTbfNfbbpR4Bdz8tNdY9Ue+f6vb+wN3pJfbd8icmZw1bh21IC6WlBXqDG5QHk56ha/op7lH3RTFftKJC1cKN82r1Ur8s03L4lXQswAD4GfSALX8JGwhQvlrCrgkxXuxtWubU7jws/h7fJWK2FMs4qqxzSr7Ju5fz96LlMvQr+G3k1kEJpDbK+6J9QsLiY7TFeFgw0SPvvs7JNPmtoJKWr0aOUrOxCG2KLWmOnbt5tO/PG56qqMnTtNM4SmBI9fb/iUwPgNL59P+nd2SFJW3KKDH6m98INtQ/4I2VpYrONDHirCU8+KBYCDE3zk3/5BPUW8rKystExbcdsX9aEtgBpAPaA21FWEekPtKTlRq+JjDahn1LbJPqzAZvuaUpySIn2vx8t4kzPr0CE5KyFmgLdID3m9jN/dgHfJWStiWNLQZOYy/BbeK+Ux3Le77LIqDnMZ0xTUh7ZAFWOaVfZFz4X+S/Ii9HHo6UwDWhU9ocZxJdmRvmWLZA91ip81S8kPSSg2Qm+afoElfetW03Et/s784w/l13Kj95u+LfbhjhG5hdnFpcW/B214e+Orkue9u2XA3uDNVZnLvfjQTLGrT34bU1aucSv1r8jDc3ZPNN0SlhL8xd4pph5fHaTmJC47/sXJiAPyD5WB2kCdqB8Po/ZQh6hJ1KfpJxVQ22XW30O2yr75ISExU6ealL5E1sGD0tr5SBFDhpRkZspZCbEIfEb9rVF4l+b8ZXij8vA+c98+yWPhw0pO+LYyYrZ5mMuYJqiRmGatfdGLSV6kTjZ7gjPgMrKjrLg4qEULde0rCWJQWpxDDIVF8r3uujw/v9Ls7Jj33jO964W/TT1AcDxsr+Rbiw7O+Oz38dLGCRv6rvP6PrugSv0TmoHpFxrV07U2+fw4zjiVWtnyd9RRkblam2hiVqxYF8iGJipAzaB+1G+moSZRn9JG1LlcvjKssq+4eyndF1FPOPW98ca0zZtN8xBiFfAfeJGpUxlC008/meYRo1v4pLLFoJUr+i08Gf4MrzZdAdPmYS5jWnmNxjRr7WtY0kPrq0DqnK6Iy8iO5KVL1VWvpKBWraTZN8pQWEn+DRoESGtN1q6tnl2cV5Sj+TBPSksOf5qYaYePB+4IWDvOOEVcjDl+/FOef7Dq5IJxFZvoofO/iXOo1iYadfGCOIrNTVSAWkJdqStQSqhz/S/Hl1tp33+nBJncF7n4yy/SGytnO3XiMuek6sCLpOmB8DT4m/i1wujW5K0E+KrkkPBneHWF/dg0zGVME9RsTLPWvujR0K9Jv2rmdDlcQ3ZAFfrfdpu63pWkfrfFdCismVDk4po1pkUEQolbSNO2DfOKOioXs4nSslJxX27J4U/QOMcZbtm9kpX/71oxv/z17Qfbho4zjkIWHpyB9rz/7DZl5tdXf7y37PgXyOYbcwK/Lj06Ozz1LH5Fe97ub7i0jLyLa04v+XC74RCQ+Z/vmXzkwr8PI8DFnGRkEJ+OnL59+Nq/vsvMN7yLH5zg88U/t2Rn/T4eOxfbbQY1hnpTV6ZpQs3LxcxjlX1NpwSJ+yLp27aZrg+IbiDuo4+UKUGEVBH4EjyqwgC3Xj14nTS6hWealoLHWh7jetk0zGVMK3eOmGaVfU3nkGomGzzBSXAN2RE3c6a60qVkqjzUQ2EpBTRqpDlhMD4zeuJG+QaaSXr5l9PfWjUot0xQvJfY8+nIw8rfe4O3KBnQqExP4Idjn//v7+9Mt6BdIduhczvHGRuhsp7gnuBNRSWFmlPTj17YLXaOgYV6DPTxzrdyC7MxGpC2p+YkKWdlG6g31N4446Q2zYSaR/3LxbSwyr7qKUE+Fd9Y8b7iCgvfbSLEZjJ27pRej5J8Dwn+aVrEMDW1USMpj5SsGuYypjlVTNNp30o1h5LTdOeuggvIjqKEBJ9rr1XXuDopysPyUDj40UfNLd6w+PAnatcxTZrvntnMD8fnYp/vbO5fWJyPUYJoYDN/G61Mwoq6eEGcEvKcTwqMy4hCu9rovUycDBo2hgLl/zTRcYbJ50OPhe7ZfWYDJP+ZeO93twwYv/7lwLi/kedibrJokBhAiJ0vMe55ypYBQfHe+cV5f4b9ISa67wj4BeOAY6GXHgbjknFou3yuWrxdZiHhYuUyWui3b6VTgrxq107buLHi7gmxG/Au9epzpgn+CS81LQLvDdb6+IuSrBrmMqaNc7KYVql9z7Rrp0dzeFnpCc6DC8gO9QtF5tKZtm2T//vfSofCXqqphQK/2FNqp5GS9O5ZVcjKTxejkFWnLr3fv9VvpThKSIKvkk3Pc1CliSqqXwFHEX8kZMbMMs4gm7d/Wrnxe9Bi2tcW3xVKZhRHJaAxl9vvOaiC8naZ5YQTkEtWxCr7Wp4ShLadumpVxd0TYmfgY5Z7EXipaX4x4dRy0jnMZUxzwpimx77BDz+s3qiZdHqCU+HssiM/JMRyi/UyPjQN69dPualueSh8qUjFVy7LjW9yz/ztLbXHqJN498y0rG3sC9kqdrjO63s0A6Tt/mvEluV/fqlks6qJRlw8r2QrN867PnB2+3dHPjNdeXDe/g/wE3KK/zX3Col9m6j0dpmFBCtYfqtev30rnRIU1r+/vHdCqgF4mtr9lAQvVb5Tajrh1ELSM8xlTJNwhpim377QE+e6dVP/pM5Z8bxcAGeXHdKqbVLyv/PO+E8/LYqPV/LrGQqLJKYWKgV3n9modhfNNHv3xAvJQUpBm5m1a5x65yJhxKAoequaaGJWrJJNecw5eXP/pUdn7w3eIp6qipFBeOpZUeRY6B6liCn2baKoMdSbdJnmEmwhl/8Hq+yr5z6Z5n0vQuyIntFt3MyZyClNOLWcKh3mMqZJ1HhMs8G+esRHpZ7gbDi17FDee1Snc507p23aJD0TLdc3FBbpTNu2ykP99NxU+LHaV0wTms2Pf35lr7uRF5LPiN2iFX28c5SSlKnRe4I3iZwWmqjSIJUmavrRhLV/GSZqTd06SJlGjsaJLfONTTQjL00U2ei9TCmywXvZ//7+7lSEYXWjqLRQkeFkuB2aqAC1hzq0OMHNkGALcx9q0W/foJYtva+8Ur1dSur7XoTYEZ2jW59rry1KSEBEUn/62FyyPMxlTBM4VUyz2b6WxYdlT3BCnFp2mL73KJJhCDt+fF6QtjDXMxSWnsgIxKcKzKXp24fvClpn3688rz71jdh5XEaFhSLyinLEgjYzf3tLTMJac3rxOONLaNkFmel5hs4YWl6UDUnwFVuUJpqa828TxWhgnOHDRYMLjMsbx2VEij1/9cd7IsMX+94dZ/zI07kkw2erAuL+EsvgrPf6L/43Nj1C7BPXnlOYZZfpVwLUJPaJWhX710yaH2qxyr56bnWIJN33IsReWDW6hceKUvBe+LDpC97mkoVhLmNaubPGNJvta0F8WPAEJ8R5ZYf03qNhuuh335VkZcn5TLA8FFY/kREonypQp4UHZ/hE/2n3zwTkFeVO3vTaOMMCuu/Iv/0zFEAKNk7C+j1ow7/nc+DDcuML6MoWtLpyM0304LkdYuNHO0cuPjQTjVxIcow/RAZcODYquxIJTVrcaUCwMP36c3RamLJnu4BaRd0uVC3tJ5Lmh1r021fPlCAl+d18MwpKxyKk6sCvpC9jW0jwWNNvncOTUVy9cr9pMjfMZUwzTc4Z02y2r6b40MzptDip7FDee9S8OaGJhaGwuScy5apPFYgEsbzB+4f4iprdjnhHHxe3HzWfQV5IDhK/bvQxrPWekXdROUPRROHcy45/Iba8/+sbEP4nww+IImm5/353qrSsFHsQCwW+s7n/Zt+flOMqL6yHp56dv3+aiFBokEsOf5pgskzhrqD1ojgCSljKvwHRvqCeUdvqb0xLH2qxyr6WpwSJhP4g8s03M/ft03QMQuwCvAs+Bk/Toz9Ml0tXisO34eHqzCKph7mMaa4S08ptsq9ALT7M5XRCnFR2JC9dau7mhDnUQ2HLT2QE0qcKZu+eeOTC7/nFeXK+miYjLy2n4lRz/K+ee6RFJYVokGiu8g8mFBbnI4/mLUfxk7y1GkCdo+alKVqmM9L129fClCAvqg1SQ+jUH6bLpZsCP4e3qx/ZqIe5jGnlLhLTTNFvX1NMxYflnE6Fk8qO7CNHrOoVpKGwnicy5SafKrDv1CpSFUynaCkfNbDKvuopQV5UG8RpsKw/pOXSJeDz8HxpWqLpMJcxzQnRjGmaVGpfTRTxUWlOJ8FJZYe1iKGw/icygk0+P1bH1CpSdZQpWuKjBvrtK00JotogTos5/SEtl66J6bRE02EuY5rTIsU0y5izrwWgORLmzpW3OiXuIDtQ3dY+kSk33qnziz1l96lVxI7AOrBR+pGDOu2rTAmi2iAuhKQ/1Mulm0OZliiGuYxpzo+IaZpPf9RI9nUb3EF2FFy4oLOVEldEv33TNmyg2iCui6I/4Mnyb+ZBKbQReStxF9zPvu4gOwghhBDiElB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVSv7MjIyAgLCwslJkRERJSUlMg15WpkZmaGh4fL1+bZxMbGFhYWyjVF3IWioqKEhAS6vSkI7wgFpaWlcmW5CNnZ2VFRUfJVeTAwaFxcXH5+fllZmVxZ9qMaZQfO+9dff33ppZdeJCYMGDAgPT1drixXY+/evf3795evzbOZPn16TEyM64ZgYhlojrlz5/br1082vAeD8L5v3z70UnJluQjHjh0bPXq0fFWezYcffhgUFFRcXCxXlv2oXtnxww8/NG7cuGHDho2sRBRp2rTpPbpp0qRJQyPyvipDlMJ5NqmMxkbwh1WlFERVtG3bNjk5uVq1pAP43//+17p1a1EJd+tG5EftWVVvSm2LgjoRpfAv9tBUN43/cVeUaqaTps1EKXRIFy5coOxwVyIjI996660WLVrA3LLfmOdfB77b6pbS0Bhh5D2aRzlWY2P7kn82g2kp2bfNI0qhyLp163JycuTKchF27tz51FNP4dqtaO/NLrX3hlb2NYpNbThWI1t7Q+EJOhEn+dprr506daqoqEiuLPvhCNmB+nrkkUee0E379u1x/fj366+/XrFixUod/Pjjj8OHDxe2eeyxx+Q9muf+++9HRTdv3nzEiBFv62PixIm9e/fGse69994xY8bIP5vn+eefR224k+y466676tevf+21116jjwYNGiCktmvXbsiQIahGuYLM0KNHD5gV4b579+4v6+bRRx9FQ3r44YenTp361VdfzdMBsr3wwgs4FspC8n/zzTdL9LB4ycCBA+FCaK6UHW6MkB3wXsSKzz//XPYeLRDB4OcIZQgyN910k/6WgmZ15513whUR1vQfa8L4Cffddx/cftCgQTNmzJg1a9anlYE848ePf+CBB1Bq5MiR8+fPl93bDO+++y6CJ8Lg2rVrXV12tGrVasqUKXrb+5IlAwYMQBFY56GHHpJ7FPPAc1DJKAiXWLRokbxTM/Tv379ly5aIY4hIy5cvl3s+M8CU6GgQojEWmjBhghxSzdCrVy90aohjp0+fdnnZAfHVtWvXPjrp3Qe6Ad6Mf3HxCfEJeoiJiZk7d65Q6+il5H2a5/HHH8exYJ4vvvgC2uUni/xoZNmyZYg7QkAsXry40lICeAxiFtzOnWQHIuOVV15Zu3btWvpA5IXseKzDY5999hkqRK4jFaJuBw8ejE4d9YbeHU1oog6QDS6HuNClSxeExZMnT/6lA2QbPXo0joWyW7ZsCQgIOKOPjz76CNHE1WUHfLKkpKSwsBARR+dVoAgyo4jO6UrIX1xcXFBQgH9drglERESgCT/44INffvnl0aNHZe/RAhEM4aJjx46QHRATderUkZuEGdCsbr/9drgiwtqRI0fk/WqBY/33+/9CMSMGzpw5c/v27Xv08f3336NTRCloDi8vL9m5zfDLL7+I4Ik/srOz5cpyEYTsgH5avXq1Ve0dUhL92tNPP40hqNypaPHcc8916NABlYxjoVv09fWVd6pFUFDQtGnToFc6d+68devW6OhoueczA2QolArU5Hvvvfff//5XDqwqRNc2fNjwNm3auI/swDj1eZ0897yQHfBp+EFmRqYeLl68iDYjjtWzZ095n+ZBe8OxUNcLFixYs2YNnG+NRZBh1apViD44FgYWsKicwww///zzuHHj3Ex23HHHHZdffvllurnxxhvvuusuRGGIPFSIXEcqhDmGDh2K+IvafuONNyZPnvyODpANLgfZ0a1bt82bN6ORB+jAz88PIz8cC2V/++03aIhwfWDIiNpwadkBh0TngUj3xx9/HDt2DJGr0gtBBrQ7aDUUCQkJqfRhMA6RlZWFjm337t34F2VdqxUI2fHQgw8tXLgQ3bzsPWZAuECQgey4+uqroSfkJmGGK6644rbbbsPQE2ENfYC8Uy38/f3RfwjZMXv2bOiJQ/pAf9OpUyeUwiAK/Zzs3FqEhYVt3LhRBE83kB3onjds2KCzvePa0d6hHtDXPPPMM9ATcqdSkRdeeEH8gX4NlfzA/Q+sXLny7Nmz8n61wLEgcSA7MBDatWtXSkqK3POZAaZ85JFHIJGnT5++YsUKObCqMETa1WtGjhzphrLjBZigsiTLDn1IskO9W830fEXZsdqIbBBTVlN2XMJm2XH33XfDsjplxxqjNZxcdogwJJqr68qOvLy8HTt2IERCFyLMoTmgWcmZTIADw42XLl0qHlPi2kNDQy17dWFhIQTHk08+iXE8+rnvvvsOzVbO5MRckh0POUh2oJZskB1iSD1nzhz9sgOlnuz0JGXHhfNWtHdJdqg7F9NeRigP22THxx993L5de2tlxzfffGO17Fjj2bIDo2HHyw6EkuqWHWPHjqXsoOxwQuCN8fHxCKD16tWDmfAv6i04OFjOZ4LQEDCKsGz9+vW//vrrgoICOZ8JWVlZEydOrF27NvLXrVsXgXvfvn0u1BAcKTvEQxbHyY4nDbIDfRVlR6VUVXY8YKXs+Pjj9u0pO3RD2WEKZcdllB3OCs4ZIgOdHGyErrFOnTqo7WPHjsn5TMjNzV26dOlNN90kLIvROS4fYVHOZ0JaWhpitKkzLFmyxIWqi7JDgbKDsqMqUHZQdtgCZYeCG8gOeGNMTEyvXr3E3Q7Y9Omnn0Y3JuczIT8/f/369fBnYdn69esjPkKLyPlMQDsdM2ZM3bp1LzPe7UBDQNB3oYZA2aFA2UHZURUoOyg7bIGyQ8ENZEe5cW7Hli1bOnfuDJvCsui3LC9qh8sMDQ19++23kf+uu+7q06fPmTNnLHt1YWHhjh07Hn30UXSo4vV4NAQ5kxND2aFA2UHZURUoOyg7bIGyQ8E9ZEf5P4t/e3l5oe/Jycmp1EXL/nn55e+//0bBSvOXG5UHOu+TJ0/iX8sTQZwQyg4Fyg7KjqpA2UHZYQuUHQpuIzuIZSg7FCg7KDuqAmUHZYctUHYoUHZ4CJQdCpQdlB1VgbKDssMWKDsUKDs8BMoOBcoOyo6q4FDZoQt7yA55n+axTnYYzUPZUV5F2dHx8S/m6pUdqHAnlx3hxlVKKTvcHttlx+NPNLzbOtnh6OXCKDt0r1Iabmzv+lcpVVBkx6qVq6ySHQ5YpVTgVrIDdQ237q6Tbt0ffvhheDP+3b59u5c+Tp06NW3aNJTCseAK6Gzk3Zrh0UcfRamWLVt++OGHX+vjyy+/HPD6AFwX+t3PPvtM/tkMX3311aBBgxq716fg7rzzTgTTerq5+eabITsefujS59nkOjLDK6+8ghbeqlWrvn37Dh8+fIQOkA3RBLKjU6dOS5YsgSPt1AGyDRkyBMdC2WXLlu3bu2+/PiZNmgQXouxwb4TsQLeBWAEtK3uPGeDn6ADg9tdffz00utwktKhbt+4111wDTd/8nuYIa5s2bZJ3qsWOHTvmzp2LngZuP2XKFIgJuVcxA0pBrKDURx99BLUtO7cZoFFE8Fy7dq2ryw7E5EWLFu3du1e+SC0O7D8w6e1JKIK+BmNj/X0NBCsqGcMn9CC7d++W96vFH3/8gYEQpADiGCLSyZMn5Z7PDBArcFTolTFjxsC+ckg1Q//+/cW3pdxBdgBcTFvdQOM3bNgQ/6KbwQB3iA4GDx789NNPNzKC7lDeo3nEsZo0aYLiPXTSvQccqJHxa7cQofKv5hFyqq0byQ4E0wYNGtyiG0RSYVkhQ+UKMgPaT2Pjd4zxByJdB33A5YSg7NWr16uvvtqvMpAH+gY9hHDXPn36oPn11weGfXAhyg73RsgOuD1iBUKT7EBawKngwCLI3H777XJ7MAPUOZrVXXfdBVdEp/if//xH3q8WOBbaVPPmzeH26KUwvH5RH+g4W7RogVIYsGEnsnOboWfPnqIUQoGrf4EWseXZZ5/V2d5ff/31J4xfzoN1EF6gCeROxQxwA9FrwEywl7xfM3Ts2BFFcCBEJHRzcs9nBpgSwyecJIrrj7RCGLm87Fi5ciXEHWr8PmtAfmFLdDMP6QMav3379m2MyLuziHIsFL9fN1CRtpVCEfhBamqqXFmuBkZgUACi9lrrRhjI2noTZgLtrEEUAdjDgzp54EGloBWljL6HIkOHDg0PD6fscFeio6MnTZqEwQPMjdAkO4EZ4Ehtjc3EqpbS2thY2hpbig3HEqV0IpWSd2oGUQptc/PmzZaXiXNmdu/e3bt3b2vbu2l4kXsU8yhFUM/yHs1jGpHkbs88IiK1NcbMfyNpZYhjDRs2zMvLq9IvO1aFapQd5cbxwb59+/YSEw4ePOhyKxaoiYmJOXDggHxtns1ff/2lZ7kL4qKgc/X19d2/f79seA8G4R2hoKSkRK4sFyEhIeHYsWPyVXk2f//9d1paWrXGseqVHRj5FZOKuG4TNaWstAwXIl+bZ4MKqda2SmqcEiOy4T0bl769hwZLg0o4II5Vr+wghBBCCFGg7CCEEEKIg6DsIIQQQoiDoOwghBBCiIOoXtnBKaWayNXkgtCyahwwFYvULJx+qMbVp5TK1+PxOCCOVa/sIIQQQghRoOwghBBCiIOg7CCEEEKIg6DsIIQQQoiDoOwghBBCiIOg7CCEEEKIg6DsIIQQQoiDoOwghBBCiIOoedmRm5sbGxsb7RbExMTk5OTIV+ipoCrcxrK4EH7UnlRKcXFxSkoK4oDsQK5JcnJysVssb1gV0EPFx8fLVeOawDOzs7NrfIW3mpcdf/7558iRIwe5BSNGjDh8+HCNG9VJOHr06KhRo+Q6ck3GjBlz/PjxoqIi+SIJMSExMfGbb75BHJAdyAUZPHjwl19+mZCQIF+kh3Hy5Ml3331Xrh0XZPCgwcOGDdu/f39eXp58kY6l5mXHmjVrbrjhhtpuwbXXXrtixQqODwSrVq268cYb5TpyTW655RY4an5+vnyRhJgQGhrar18/xAHZgVyTZ5999ty5c/JFehjr169v0aKFXDWuyZVXXrl06dKsrCz5Ih1LzcuO1atXX3/99Zdddtnll19+jcuCk8clXH311T/99BNlh2DlypX169d3dcvWq1cPl3DzzTfDUSk7iGUgO1599VXEAYT4q666SnYmFwFnXqdOnVq1avXo0YOy43//+1/z5s0RBBAKYFm5slwEBGEYFP9+9913lB2XZAdq5LbbbrvfNWnfvv3tt9+OS6DsMGXVqlVCdtx5553t2rWTa81FaNCgAboQyg6iB0V2gHvuuQeRQfYnVwCDe3RUcHvKjnIT2YFQ0KZNG7myXAT0UHXr1qXsuIQiOxo3bty1S9cuLkjnzp2bNm1K2SGhyA402meeeUauNRehUaNGGPlRdhA9KLIDMe2hhx5CZJD9yRV49NFHcf6UHQJFdqCH6tSpk1xZLgJ6qMuNUHYYMJUdclW5CJQdmlB2EE+DssP9oOywO5QddoCyQxPKDuJpUHa4H5Qddoeyww5QdmhC2UE8DcoO94Oyw+5QdtgByg5NKDuIp0HZ4X5Qdtgdyg47QNmhCWUH8TQoO9wPyg67Q9lhByg7NKHsIJ4GZYf7Qdlhdyg77ABlhyaUHcTToOxwPyg77A5lhx2g7NCEsoN4GpQd7gdlh92h7LADlB2aUHYQT4Oyw/2g7LA7lB12gLJDE8oO4mlQdrgflB12h7LDDlB2aELZQTwNyg73g7LD7lB22AHKDk0oO4inQdnhflB22B3KDjtA2aEJZQfxNCg73A/KDrtD2WEHKDs0oewgngZlh/tB2WF3KDvsAGWHJpQdxNOg7HA/KDvsDmWHHaDs0ISyg3galB3uB2WH3aHssAOUHZpQdhBPg7LD/aDssDuUHXaAskMTyg7iaVB2uB+UHXaHssMOUHZoQtlBPA3KDveDssPuUHbYAcoOTSg7iKdB2eF+UHbYHdeTHV27du3xD/JvRkwz4G+xsXv37vhf/Fsx7yVMiwjM5dSEskMTa2WHpuFM6datmzqDDZbV3Lk5KDuIfmyQHcKBzXkmtigZ4P9iI/4QG5UtEkoRBfWeLUDZYYpVsgMW1zSZKZbjmKalTPcpMJfTHJQdFbBWdrz88svjxo2bYOT555+XfoUl0OzHjx+PX8eOHfvCCy9gI4w0bNgwbBk6dKhm/9SzZ08UmThhogA5R40ahf306dNHj2kpOzSxVna8+OKLwrIw3H/+8x+p5vG/AwYMUCzbq1cvsf3NN9/ElhEjRsCIpvkFL7300pgxY4S3CEaOHPnaa6/ptGwXyg5iDTbIjiFDhgivHj58+LPPPiv9CkcVHg5ef/31HsaxFlrHW2+9hcbSr18/dUCDYw8ePFgJkgD7xxZETlG8Uig7TLFKdgD0SggyE4xhStNAiD/4CRlGjx793HPPdTFqjjfeeANmglnFFgnsU3EDpYeCPyCzprJRQ9lRAWtlx5QpU3x8fM4YmTNnjtR5oDf69ttvAwMD8evJkydhSGRAf7Zx48agoKB169aplQro27evn59f8JlglAoONvzr7e29e/fuL774Qt3/qaHs0MRa2fH22297eXmh8mHfzz//XBEWAvzvypUr/f39kQHZ+vfv39UIzARz79yx85VXXlFb6p133jlx4gRMLxxGlN27d++XX3wJo+tpsZQdRD/Wyg70N7/88guCDzxz3759EAeSD0M9HDhwAA4cEBCwYMEChDJsnDZt2pEjR+DJaCbqgAb9vWLFCiVIAjSQo0ePovtEE5OalSaUHaZYKzswzkGEgcl8fX3nz58PtWf6K2IOeigR6I4fPw710MUoLhHckP/gwYMYQZnmF2Cfv/32G3xAGBQ7Rw8Fh4FLDBw4UK1s1FB2VMBa2fHpp5/m5eWJsuiEYDDTXyEAw8LCysrK8OvFixc/+OADNGN0MKdOncLGY8eOYfhrml8A+Sl6lKKioiwjubm5UA/p6enLly+XDqGGskMTa2XHRx99lJOTg4KlpaVofjClaQjGWBDmLikpQYbs7GwMDoTsOHv2LPIjsEr5BTNnzkxJSYHpCwsLUUpYFlZOTk5etmwZIoK6iARlB9GPtbID/frhw4fhnCibmZmJ4GYqC/DrokWL4MD4FZ4PgYJRELZjOBQXF4fG8sMPP6gD2rPPPotuD0ESbo888Hl4fkFBAbwXLQhj5UrveVB2mGKt7Hj33XfPnz9fZgTq8M033zQd3vTr1w8bhcURhSZNmoSNL7zwAjQEDBQVFTVhwoR/9/UPU6dOhdqAD5jGMfyNPaxduxbKo9I4RtlRgarIDjRI6HfTGv/www+FRcvNyA4xXJBQZAeiBqyI5r19+3Z4AIogFsAzLBuVskMTm2UHSEhIgO2U+Ig/oOuhAoWgzMnOsUp2oLmi0a5fvx6W3blzZ2RkJLbExsa+//77lYZgyg6in6rIDvjk7t27+/fvr/yKXWFYLOKJDbIDPdOuXbtQat26dSdPnkTzwU6wBS1FKiJB2WGKzbKj3CglZ82apTw7g/746quvYmJixK/Wyg6Ml/Dv1q1bRQ+Fo0BNYidz5szp3bu3XKYilB0VqIrsQIPcuHGjYtTu3bsfP35cyMxym2THnj17xA0rWBFNGgeCpVeuXGn5LhZlhyZVkR2odtSkcp/p5ZdfRiTFRttkBwpCc4iHpvh32bJlGRkZiPXz5s2r9J4zZQfRT1VkB3wbnQrGUYoURgcGcSx83gbZAc9/6623uhhne4wdO/bEiRNwYBxi5MiR6pZiCmWHKVWRHQhNa9aseeWVV8RPMNaGDRsQfGyTHRAZKI7eqovxuczXX38N98B+ECqlRzlqKDsqYJvsQCOElofxfHx8Bg0aJFrRwIEDYUhYGhnwb1VkB0pNnz4de8OBfv31V8oOG7BZduBfGAvxTlESY8aMwRABNk1LS8O/VZEdGHDMmDEjKSkJZlq4cCFlB7EjNssOuBYcEp4vnv11MaoHjKmys7PFY0H8arPs6GK8t4/xMfYWEhyCjZZnNVF2mGKb7EDHAZvi34iICEhJVDii08SJE9FhQT2Ih/hVkR3YIcriQDjDn3/+GX2cXKYilB0VsE12ACHe0fbmzp3b3Qi6fDRgqA1/f3/xh27Z0V/0KAcPHkL7RP7Bgwdv2rQJrR3blyxZYrmVUnZoYrPsOH78OOwLCyoPQbArGAJt9Y8//kDbs0F2bNu2DfIUlh0yZMjmzZtzjCB8q98dkKDsIPqxWXagd09ISEDoOHDgAFwUAWfEiBF+fn74KSAgAD/hD2tlBwIguiv4PEbbH3/8sYiKvr6+w4cPV7cUUyg7TLFNdqD+EZowTEIfP3/+/Oeffx52Wbx4cWJiIgwHm6anp1srO2A+aMdRo0bBpgiA6GvE8On777/X7NdMoeyogG2yAwaAGoAJsYfff/8dA1k0rdOnT0Nb4F9xQ95K2VGAXcXExO7evXvXrl1Hjx7NzMxEfxYZGVlpK6Xs0MRm2YEBH6odlQ/LornCuIiYqFUMFESMtlZ2IENYWBi8Apb9888/EQuwNxRBC7d8H6sLZQexBptlh5eXF2IO+gO464wZM/r06fPVV19BWyCIQWqjN7JBdsBdoeDh83v27IF6EMIdblzpyJiywxTbZAeq+tChQ8HBwfhj//79w4YNg2PAlEKOwNawrLWyA1ErPDz84MGDCIzoy9D9wStwrOnTp1c6fKLsqIBtsqOkpGT27NloUehRYLyhQ4d++OGHMAOGwitWrNi4caOVsuPSQxZxZwzgEJAdcIK5c+dWOuuQskMTm2XHvHnzfvvtN1gBbQwjP0Th1NRUWGThwoVowDbIDpgeRhGWzc/LR6u7cOECLFvpO0pdKDuINdgsO3x9fb/55hsEHDgzGs6bb76JJpCdnY3t6CfQtdggO8qML3AJr0fnh4aAHgv9n+V7t10oOypis+zYvHkzjIhuCNHmQyNwD/GwbNu2bRj8WCs7SoyIl5JgXxHHFi9erLl2gARlRwVslh34Y/To0WiZ5cY5NVu2bEEzS0hIgASBo9gmO5KSkk6cOIEhAro3nBg8oFLN0YWywww2y47PPvts1qxZ8fHx+N+VK1dirAbroIHBHLt377ZBdiADwjR8AJbFgAPNXs9rhALKDqKfqsiOd95558CBAwhu6LSWLFki7k9s2LABzQFOboPsQBE/P78///wTY+vt27fPnz+/f//+lWqOLpQdFbFZdiBifP311/gbndTatWsRuzIyMmA4GBT7tEF2oGdB8b///lv0UNgntmsuL6aGsqMCVZEdaOHe3t7QExEREWK5DjQwtDrYw5zsQCtVd06K7Dhy5AiG1wMGDNCzSpgCZYcmNssOaA5ox9OnT4ubijExMeKh5ssvv/z777/bIDvgDBh2jBgxApaFA+iJvAqUHUQ/VZEd48aNQy8lbtnCn9FXpaamog9D12Kb7EAAxAh74MCBUBu9e/dWtw5zUHaYYrPs+Pnnn4cPH37w4EEYFI6BMS2MiEA0atQoBBMbZAeK79q1CxlED6Vz4CSg7KhAVWRH9+7dFyxYkGd8b6W0pBS9FEYMaF3mZAf+FYsEK0AqIoP6TRaroOzQpCqyA1Hyxx9/xP+K+4poonPmzOnZsydanabsCAkJQWNGUzS1LOSF+k0Wa6HsIPqpguzwGz169NixY318fPC/iCHwaoxrBw0aNHnyZHOyAx0bGoLi85Ag0By9evVSv8liFZQdplRFdsAZli5dKt6IhEHT09PR60MFIjZqyg4MsaZNm6YYFDz//PPoktRvslgLZUcFbJYdsz6dhV4HQwRlWVKMjMVsKXOyIzo6euPGjb+YIDoz+AFlh92piuyAURBPlUUL/P39xWroQnYoq5RCWAjZgQb866+/iqXeBGLd6E8++YSygziMKsoOqO0VK1agQ8Ku4Gzz5s2D05qTHeiEoFE2bdqk+Pzy5cvFh6goO+xIVWTHK6+8gsDl5eWFDguhDH+gw0JcMic7MjMzYTvTOAYfgEdRdtgZG2QHOid0JEJ2QNrv2LGjyAjGx2JCL6wFC6Wmpiqy48SJE0JvSqDNwwngGfAS/O/u3bspO+yFtbJjxowZaA+wAkzc04iIyNgCy4oFNnbu3InGiWzK3Y7g4GAxNJQ4fvw4muvMmTPRtuEM69at0zOBVA1lB9GPDbLj0KFD8CsICMgO/C/Et5h4iK5LLB0G2QEnR541a9YI2TF37lwMi9UBDRFvwYIF6OoQxxDQ4Pnikx/WQtlhig2yA5WGoRECIGyB/mXz5s0ZGRmwIDSieOC1cuVKGCsxMREm7mKUHVAbog+S8PPzgxGxz8DAQEhJDJ8oO+yAtbIDBvD19Q0KCoIA7Gp8WomuBbaBkFRWMV+0aBGMdOrUqQkTJnQ1fgoO9g7WAi6F3gi6BONp/C+GGpQd9sJa2QHzIfjCCjCxeGz5/vvvYwtsrSxwBP0REBDg7e0tbn5g4549e84YP+AngWEBYvSUKVNOnjwJZ1i8eHGl6wdrQtlB9GOt7EC0wdAWwQdjp6FDh8Kfn3vuOXQt2IJY1K9fPzg5nB9ODrdfuHCh8im4o0ePyh4fHHz69GkEw5deegm9GkIiPH/IkCHyIXVA2WGKtbJj7Nix+/btE9+BEzPJEIUOHjz4999/I6CJp2Dz5s2DsTAYFroQRke0NHyOVAUGWjAi9rlr1y74wLffflvp+8+aUHZUwFrZ4YRQdmhirexwTig7iH6slR3OCWWHKdbKDueEsqMClB3uCmUH8TQoO9wPyg67Q9lhByg7NKHsIJ4GZYf7Qdlhdyg77ABlhyaUHcTToOxwPyg77A5lhx2g7NCEsoN4GpQd7gdlh92h7LADlB2aUHYQT4Oyw/2g7LA7lB12gLJDE8oO4mlQdrgflB12h7LDDlB2aELZQTwNyg73g7LD7lB22AHKDk0oO4inQdnhflB22B3KDjtA2aEJZQfxNCg73A/KDrtD2WEHKDs0oewgngZlh/tB2WF3KDvsAGWHJpQdxNOg7HA/KDvsDmWHHaDs0ISyg3galB3uB2WH3aHssAOUHZpQdhBPg7LD/aDssDuUHXaAskMTyg7iaVB2uB+UHXaHssMOUHZoQtlBPA3KDveDssPuUHbYAcoOTSg7iKdB2eF+UHbYHcoOO0DZoQllB/E0KDvcD8oOu0PZYQcoOzSh7CCeBmWH+0HZYXcoO+wAZYcmlB3E06DscD8oO+wOZYcdoOzQhLKDeBqUHe4HZYfdoeywA5QdmlB2EE+DssP9oOywO5QddoCyQxPKDuJpSLLDRd2essMUyg6740Syo2HDhp1ckyeeeAKdE2WHhCI70Fw7duwo15qLcPfdd1N2EJ0osuO6665r164dIoPsT67AAw88gPOn7BAosgOhoEOHDnJluQjooerVq0fZcQlFdlxzzTW33Xbb7a7JtddeC7+k7DBFkR0IYa5rWbglnJOyg+hBkR0I8TfddJOLuj28Hf0T3J6yo9xEdiAU3HrrrXJluQjooaAjKTsusWbNGiE73AD4JWWHgpAdch25JpQdRA9Cdgip6uqgl+rZsydlB2THvffeK9eOawLZsXTpUsqO8s2bN7dq1aqhW9CiRYt169aVlJTIF+mRrF+/vk2bNnIduSbt2rWDo1J2EMtERESMGjUKcUB2IBekUcNGAwcODAsLky/Sw9i6deuTTz4p145r0qxZs59//jk7O1u+SMdS87IjISHhyJEjB90CXEhcXFxZWZl8kR5JbGzs0aNH5TpyTXAhcNTS0lL5IgkxIScnx9/f//Dhw7IDuSCHDh7y8/PLzc2VL9LDiI+PP3HihFw7rgk8E2G5xgfGNS87CCGEEOIhUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRBUHYQQgghxEFQdhBCCCHEQVB2EEIIIcRB2EF2JCUl/fnnn4dJlTl16lRJSYlcvzXHxYsXjx8/Lp8lsR4/Pz+5colTUlhYGBIScuzYMdmExHoSExOdIaClpqb+9ddf8skR6/H398/MzCwrK5Or2ErsIDuOHj06aNCgF0mVGT16dEFBgVy/NYeXl1f//v3lsyTWM2PGDLlyiVOSlpa2YMGC1157TTYhsZ6DBw86Q0BDHBs3bpx8csR6PvnkE4jyqkvJqsoOCJ/t27e3b9++UeOmLds/2LbD47rSox3vbduuYaNGzZo1e7pz5159nuv13HOGf82l50SGPo917NioUaMmTZp2eKLTMz166krdezz0aIdGjRvfjf9atm7cpp3O1Kh5i7sb4lhNnunaFYeWT0k6t0tn+NwTnZ5sbKDJY08++UyPHvLJVEg9nune05h6PPjIow0bNX7qqafy8vLkKq45Dh061KpVq8aitrtbvpZ/Uvee7R96CDUNyz719DPP9jZfb//U3rPGPx597LHGTZogPdLx8actH0tUmvGP++5/EMdq2LhJ4xatG7eWLaiZmhgta/S9ezo9+VTlZ3gp9Xn4kUfgDI2bNmt1/4NwYNmlNdOjHe9p3RaWhXqTK5c4JUlJSZMmTYLbt7///u49e6rcQDt17tatdZs2aPat2973ZOcuOhvLE08/07JVawS0hs3uadyqrdpXtVOrNg2bNIUD4wy79ehRSeQU6bnnunTr1qZtW0MIbNP2iWc6X2pBhgBl6VQff+rpe1u0wBk2a9mqzcMdZPc2k1o/9EiT5vfiDLds2eIMAQ3qp0uXLogtTz71tO72/txjjz/e7J57cBWPPNaxkoikpO49H3y0Q9Nmze5Gld3bUmdEanwpIjVu2rTpE506Pdu7t9KbaCSTnzpeOsPGLe6734qI1Oa+Ro2bNLun2RNPPt6rT88+z/fSkzo+/tg999wzePBgb2/v/2fvKsCiytqwq6uC3UV3d4mi7lir2Iq6dusaa665dq/K2gpiYgc25g8mCirYohigKCgiZcfq/3K/9e7sHWKYAmbO+5xnHIc7d849X73fved85/Pnz8IhziMURjsMzSzrdezeYujv3kPHeme+5tiGjHFv3kbPwMjWzn6e71+Bu3Zv3r0n97Zr92+jxxgYGZpbWf82aco8v7XStLmrA3oNHmpobKxjZGJSr7FZkzZmTdvm3pq0MXStA6ZiaWm1YMky6Xs4fvIUqIKxmdnIqdPx05L9kWw4rMegwfqGRgWTdphb2fw28Y85q9dI9jyrFuDTqw9G2s7BYca8eRt37BQOkUQL5F6HjBgBB2dqaTVozNg5q/wlTptVWx3Q5pfuxiameiZmJnUbSilZ8yZtDFzqwHHbOzj+MWPWhu07JLuURdu1u9+vv5pbWJhY2zXo2N178GihSmfVWgwZ49TYW9/YhNGOwgKiHdbW1h06dV61br1QDbJpvstXNP75Z6hiQ+9Wkxf8BSsQ6mpWbdyc+fUaNoZr0rd1NBU1l9TVLJtJ/aZ6lrb6RkYdOndeGbBWsjNZtiWrVjVt3tzY1KTBz80mzl8oZQ9/nznX3aseArZDfVHTvkMlNTzL1rjXQAsXdyQDBYp2mFlYTJ01e5MUHonaiLHj4CKQZvQfMXr2Suk8kl9Az8HDrO3sa0GoteubNW4tKb4sm6FbXV1jU3DWCVOmbNi2XbIzWbZR48Y7OjnpGZt6tvZp9usoSUFIthZDxzg3a21oZuHgaD9x2vjAXeu379+ca9uxf8uo8SMcnBy6de9WsGiHkZVt8yG/d5+/oltmW5lLm7f8p659dQ2NnF3d9h87cSs2Tqr2KHbhsmUQqJWd/aJ1m4LCLu29cDnXFhQWMWHufGNTMx0Tc4vmHaw69rXq2C/35tPHuF5THX0DW1u7w/8LufVIojNZt1j/jRtNzc1NLC2XBG4T62EOXb0UdD5i3Kx5BkYmXgWSdlg7OC5Yu2HPuXCJnmfR9l24NHjiHybm5i7u7tv37rvx4KHEEP2n3f7+Zv6SJZbW1pZ2DjNXrN599qLkmSXapb1hEX1H/m5mbqFnbmXetK2lTx+hEP/bLLlXa58+hl5NdI1MXN09Nmzfee3+A8leZdEexU6dMxf5opmDS8uhY7vOXSZU6axa93nLPX26GpiadWK0o5CAaIeNjc2vv/12LjJKqAbZtKOnz4AEwPDbduu54dDRHO393+a3Z3/zdh2MTM30nWtbtOmag2uy/K69aOYtOuvZORsYm/z62/Czl69IdibLduLcuU5du6KHLX/psnb/YSl6mHnAyh1BPzXzNjQydvdu22nKvO4SGp5l6zBhpo3XT2D2BYp2WNnYBO7clatH4tviVavd3D0MjE2n/rVs15kLEuOTZbs0Yd4CB1e3msampg1bWHXoLSlKcWladuxLb4zr/6xramHv5LxmY+DVezGSnZFs8JxL/fw9PD31zSwa9R7cZfYSSUFIth7zV9T9pY+RlY17bbe1W/xvxV6NSbyda7ufeGeZ32L32u4FkXZ4DxvXc+HqHgv9cm8LVom699c1NHZxczv8v9B7T59J1eKf/rVyZSbtsHdYvGnrgUtXpWoRkX/MX2gC2mFsbuntY9Wpv1WnAbk3ThV09A1t7eyOnT5zN/6psDNZt6drN2/maIfV8q0790dECTuTVdsfHjlh7oICSztsHJ18NwTuC78i2XPJdvBS1LA/pmAEENR3HTx05/ETiSHKui1cvtwKtMPecfbqNbBeyTNLtKgDEVH9R48zJdrxczurjlJJ1qZjP8N6TXWNTdxq10bGAOuV7IxkgwLMmP+ntW0m7Wj12/juf64UqnSWbcGqOh27gXawux2FBTztGDJi5MXrNyQ1Ict28nyYzy9doPbtevQOPHoyUzmF6ppFC9h/2LtDR2RE+i6elm27S+maLFr9omcP2mE6ZOSoC9euS3YmyxZ68eIv3bujh626dNtw+KhkZ7JsIEYNvVvA5bq3aN952gKhemfTOk6aY1NPVHBoRwjRDlvbbUFB0VJ7pGX+azKf/BqbTF+6Agmk5OBINni/SQsWObq61zQ2M23U0sqnr6T4smzGDZqBdjg4u6zbshUZjmRnsmwr1q71qFNHz8yycd+hXeculxSEZOu5YLVXl34I1u513DdsX3v36c3Y5JhcW1zy/ZUBSz081YV2uKqCdkQR7ahlYm7ZPC+0o8HPtTJph/1RRjtkoB0e8tCOy5Jnlmiqpx3zrWxs80g7Vtfp2N3AjNGOQgPZaMcJ+WiHgUttjnYIdTXLJk47wmSiHesPH/vHgiS6JGj/pR0LheqdTeNpx549ewqCQ6O7Hda2dnmiHUs52qFvbDJNetoRETXpz0zaUcvYzKxRK6XSjphM2rEOtEPfzLJJ36HdlEw7VjDaYe0gD+0Qijzrlkk7mn2nHWcZ7WC0Q4x2TGC0Q10B2jFyxEhbtaMdp/6lHd3zQDuC5KIdBeRuh3y0w1TZtMOaox06MtGO2jLSDpu80Y6XjHYw2qEqMNrBN0Y7NASMdog3RjsY7WC0g9EOlYLRDr4x2qEhYLRDvDHawWgHox2MdqgUjHbwjdEODQGjHeKN0Q5GOxjtYLRDpWC0g2+MdmgIGO0Qb4x2MNrBaAejHSoFox18Y7RDQ8Boh3iTk3aoxUoWRjsY7WC0Q4VgtINvjHZoCBjtEG+MdjDawWgHox0qBaMdfGO0Q0PAaId4k5N2sIcskuKTbIx25BlC2rEgZ9rx/a9/gnb0g14S7bgbn+nWc2zcAU/iQTuM/ikXtgVBXaoW/r1KqYm5RZ6qlDb4p0rp0VOZ5cKkaJmdFKcd+8IjhZ3JquGwCXP+LOC0Y+/FK5I9l2wwvGGTxKqUxj2WGKUsGgjlwmVEOxxAO4IuXJY8c1Yt8r+0o59QiFm0/uK0I3D3nluxcZL9kWgS5cLmS087WJXSwgSxcmEjMmmHUBOybkQ7zHjaEZG74YM0B+w75N3Bh6tSWtuyjdS0oyVoh0sm7RgxKuzqtdx6+IxaZrmwbt/LhR06dkCiP5LdQ5O1SulsWy+RfsGjHVuDgu48lsojoVGV0kzasSSTdkgOkWQ7kEk7fB3+oR3SVim17tRfnHbcfPhIsjOSDXJfEbCWaAdXpXSZpCAk27+0w/Mf2vHo5b1cW+zLmAJKOwzNrRp06dN61KTWo/7IubXC68hJtVt10DUwtHdwWLLKLyj4iDRtz+HgMRMnGhoZmVtb/z5j9l+btkrVNm7uP2KkUeZWcKamDX42b9bBrLlPrs28WXtDd6/MreCsrJb7r9kTHCzZn//07fubqbNnG5uaGpmZj589z3fjFmFnsmo4rO9vIw0MjQvmVnAW1rajp8/y3bBZsueSbcmmLb/0H2RsYmrv6Dj/r8VgHpJjlWUbNW68uYWFmbU1WMui9YGSZ86ydejZG6OtZ2puWq+xWbP2/4pPQqDfWweLZu0N3Lx0jYwcnJxn/blg54GDkp2RbNC9wcNHWFhamtjYi7r0bTVigqRiZ9UmuTRrhQjB9mQpLOC3guvcrfuG7TskNSHLtnLdup+9W5iYmjZp3Xbm8lWSiirZFm/aOsV3cYOmPxsZm+jbOZs2ammWjWv6rswdqJn+5K1nZW9gZAwasX7rNsnOZNn8Nmz0btkKxiJq0XL6kuWS/ZHsHtqkBb6eDX4yMDJy/KlJ819HSqh3Fg3u/ef+wyzdausZFqyt4GC8cxcs3H3osOTgCBo583GTpzg6OekbGf86duLC9Zskh0iyYcT6jxht4+CoY2hiXEdk9nM7SWlKtA7mzTsYedTXNTYDMUL42LH/APWBjykSffsnGE2YMtXJxUXPxKxO+y4tfxtPgy8pEXHRtBk1ybVFe0NzS0dnx+lzp+4J3nHwZJAUbe+EqWOdnB0LHO0At7V0crHz9LLzrJdj86Jmbuegl7nhs3GjJk1btW0rZavj5aWnj58yqlO/QaPm3o2at8i5Ncx89UZeq2egn7nxvaWNoY2DgRQNh+mbWejp6RsZGTVp+nNLiZ5k1+rDSg0M9A0N6zb4qWHuPfSm5lq7tn5B3fje0MjYU7rRpitydHXDCJiYmIgaNWrZuo3kEGXZPOvW5baVN/LwqtewWa6/RePWwsHF5Z+N7y2tDWzsxcUnKVM6AH/SM7PQ1dc3MTFtIBK1bCNdD9u0ca9dm+uhiZWTq13tulKoeqa2m9rYoYfsbkdhAb/xvaOzc/MWLYRqkE1r0qyZja0t1N7azr5+46YS6pp1q9ewkaV15mb0esammRugC9VVUpk5Dbey1TMyhrtwdHJu5i1tD5s2a25rZ4ceWtna1m/cWEpz9hI1NLe01IexWFnbuntKp/P1bNw8DM3MC87G9yEhmbQDxvuTqGErKe09M9bUg4vQ0zdwr+vVsFlzycHJqiHWZG5Gj1ijb24l7pFyaFyssdQ1AJM0rtegQcvWrfHrLbkm2SvxD+vWq2eKpMvA0NzByTbTIwkF8d9GkbeuqS2CtaGJqUn9n+q1atuiTftW0rS69ergKwWFdgBnz57t1q1bmzatuZYTJP7cuvU/TfJPOSAPh/OH0q8UPPznWgYNGvThwwfh+OYfrly58kvnzmK9lQqtOXz/j/SyyhOyPO8/upQdSNm+v8/i+zki8xt5/9Y/mDx5snBwGQokUlJSFi9e3Dnvak+QTT1yUdzvkE39ZPuWQhAaGloQHNrly1eGDBmap2HIw6FZQZqvi7uj/+JfVyP5Z8kPJT/JDXk8/F+0njZtWnR09JcvX4RDnEcogHYkJSVduHDh3FnC939zQzbHncvyL9xH//wp6yOkQl6/yh987lzuX/z34P/+h340y6//50P85/Lly3///bdwfPMPr169CgsLy7K3UkO2b+UB/xn3Aolz587duHFDOLgMBRIfP368e/eumNrLAjlUMeev5vDXHP70D7I/Ivu/yI7Mcz5//lz+ECU/4MeQQf3Tp4IKVfUNsUz237p582Z6evrXr1+FQ5xHKIB2MDAwMDAwMDBIA0Y7GBgYGBgYGFQERjsYGBgYGBgYVARGOxgYGBgYGBhUBEY7GBgYGBgYGFQERjsYGBgYGBgYVARGOxgYGBgYGBhUBEY7GBgYGBgYGFQERjsYGBgYGBgYVARGOxgYGBgYGBhUBEY7GBgYGBgYGFQERjsYGBgYGBgYVARGOxgYGBgYGBhUBEY7GBgYGBgYGFSEfKYdX79+fffuXYY64vXr1wVqF3vV482bN8JBUQvguuTf+plBLfHp0yd1VfuPHz9qoNpThIIzFw5H4cfbt28/f/4svGCVIJ9pR3p6elBQ0Ep1hJ+f3/PnzzXQUAnwv4GBgatWrRKOS+HHxo0bU1NTNVayDNkBacbly5fXrVsn1Bi1QFhYGAKV8JrVHWlpafv27VNLP7Z169YHDx7kC/PIZ9oRFRX1888/m6kjzM3NT548qbE3PO7evVu7dm3hoKgFXF1dQ0NDwauE18yg2UAGOWvWLGdnZ6HGqAWGDRv25MkT4TWrO25cv9GuXTvhWKgFvLy8tmzZAl4lvGblIz9px5cvXwICAsqVK1ekSJFixYoVVxf8+OOPRThMnz5dY4PT6tWrSbIYDeEAFVpAS3/44YcSJUqMHTs2X8yVoSAjJiamffv2pUqVKlq0qDqpPS4HhmxkZBQeHq5pedTWrVtNTU3VLEKRH4Oijhw5MjY2VvU3bvOTdrx+/Xr48OEk0WrVqhmoC3R1dclQGzVqpIG3JYHPnz8PGDCgZMmSGAd1kmyNGjXAOSDZ1q1bP3v2TPXmylBgAWUIDQ11dnYG4ShdujScgFB7Ci3Kly8PQ0a42rVr1/v374VXrr5AYowEo1KlSjD5qlWrCsel0KJmzZra2tpgHq1atbpy5QouU3jlSkZ+0g44bgRmSFRLS8vJyamOusDNzQ0RF9elo6Pz8OFDDQxOjx8/bty4MfwvlNvW1tbT01M4RoUT0FK4YJgrrigyMjJfHosyFEx8/PjR399fT08PERr0FE5AqD2FFiYmJnBoSA5nz5798uVL4ZWrL54/f962bVtcOyiXlZWV2vgxV1fXKlWqQFHh0IKDgz98+CC8ciUjP2nHxYsXQSERnsuWLVu/fn2RugDXAjqJ4ISkZ8eOHarnkvkL0Kx9+/YZGhpCraHcsFXhABVaeHl5IaLQdW3YsEEzb2UxZIm0tLSBAweWK1cObBshSp0cmoODQ6lSpeDQWrdufefOHc3Jo06dOgX6CL5VuXJlvPnpp5+EQ1M40aBBA6TEUNSKFSuuWLEiNTVVeOVKRn7SjuXLl0OV6a6AcGAKOeB3oKyQ65gxY1TPJfMXoFmLFi4ClUR4BvlQJ/8Lv2NsbIzUB3o7ceLEV69eaY4LZsgZ8fHxUHW6w4dsUqg6hRmIuIhPMGdra+sTJ05oiEP7+++/V61aBXvHhRsYGNSpU0c4LoUZZmZmWlpa8GPDhg2Li4sTXrySkZ+0o3PnzuAcuHInJyfhqBRyODo6lixZEpfWokWLxMREjQpOCMY0sQPhGX4KzFo4OoUZkCw9Fu3YseOTJ080bYYdQ3YIDQ01NzeHYlSqVKl27dpCvSnM8PLy0tfXhznXqlUrICBAQyZTw4+NHj26SpUquHBa9yEcl8IMMOPy5cvTBMSoqCjhxSsZ+UY7UlJSTExMaKVD3bp1haNSyAG/A+8DHwRPdOrUKY0KTtevX7exsUGKULp0aXd3d7W5M0mArpYtWxaSRQIUERHBpncwEKZOnVq5cmU4NOTHahaiYMLwY0gkQLjHjBkTHx+vCXnU1atXW7ZsiUuGvTs4OKhZ+lSvXj0wKvgxPT29I0eOqHgmQL7RjpMnT5YqVQpWivCsTvfhCdBRmlxWpkyZdevWaVRwOnjwIAyV5n5DuYVDU8gBF1y9enVIFha7bds2jZrYz5AdPn361KFDBy0tLSRR9vb2ahaiADs7O7gyun2L5FjtHRp4VXBwsJOTE62y9PDwEI5IIQdFKKgrlPavv/5KSUkRDoEykW+0Y8aMGfSM3NTUVM0SYgLyAwgVWjtx4sTXr18Lr199MX36dKQIoB1mZmbq539F3MQdKs0yYcKE9PR04fUzaB6io6Pr1q0Lh4bY7OLion4OzdPTE1kEvJm1tTWSY7Vn2+/evVu6dKmBgQEuGeFZzSZ2iLj0CaJE5g9X1r9//wcPHgiHQJnIH9oBZ92+fXtwDlyzg4ODcEjUAvA+IJIITrDYR48eacJtSQD+CCZK5WjU0v8C7u7uVL3Dy8tLQ244M+QAKMCaNWto6VbNmjXVbGIHAYZMyXG5cuX8/f3Vnm0nJiZ269YNJBKWrmbrknjAP1eoUAFKi/cqLgSXP7QjIiICVAuOG0oMJy4cD7UANJVqPJQuXfry5cuqFGo+Aqy5WrVqkCyuWv2m7BBwXdBbSLZ69ernz59X+xvODDkDVHvs2LEVK1YE2zY1NVWziR08aHoH1H7okKFPnz4VjoJ6ITo6GukTPSVHeBaOhVoA/BgeDEprZma2bds2Vd6Szx/asXnzZnLcSA7UkkgSdHR0cI3QXc2p3rFz504waFoUra6SxXXR3deyZcsi82PVOzQcjx8/7tKlC+ITFcdTV7V3c3PDNcK0PTw8bt26JRwF9cL+/fstLS1xsQjMann7SsT5MSMjI1BJpMfTpk1LSEgQjoLSkA+048OHD1OmTCnGQV0ndhDgg4pyVdJ9fHzU/mnoNy7tGzx4MC0Hh9Gq5cQOgo2NTYkSJYoXL47rTU5OFg4Eg8bg69evJ0+edHR0pOJLrq6u6urQYM6VK1eGaeP17Nmzanz79vPnzyNHjoQ0aeqh+s2L5wE/Vrp0aZreERMTo7LnxflAO+Lj46HBCMbgWepXsUMcoMk0CaBGjRqaUFQ4Li4OPpcqJsERq6v/BaC3VLcR14jMT0NuZTFI4tOnT/7+/lSVWEdHR/3mHvLgp3cAK1euVOPpHSkpKS1btkRSAe9tZ2enxumTi4sLFYLDNZ4+ffrjx4/CsVAO8oF2XLlyhfYmLVu2rLo+ByXUr1+fqneUL18eV63G+QEhJCTEysoKSly1alV1qokuCZrYjys1NjY+dOiQhtRtZJBERkbG6NGjK1SogGBsYWGhrk9YCNbW1sgooPbdunV79OiRcCzUBZcuXaJ58QjJ6jovnlC3bl2kxLjSKlWqrFu7DsosHAvlIB9ox5YtW37gaqLr6uqqsURFXH5gZGSEiy1ZsuT8+fNVxiXzBUj7lixZApoFr4SsSL39LyRrYGCASFOmTJnly5ercjYWQ4FCbGwsMkVkxrBxZ2dn9XZoVNryB24rxKioKLXMoz5//rxy5Ur4bfgxHR0d9U6ffuJ2eyhRogQudubMmUlJScLhUA5UTTugqVQTHbCzsxMOg9rB1tYWwQmG2rZt21evXgmHQ42QnJzcp08ffrdGNb4zSbC0tMTFwlwHDBiQkJCgsseiDAUKoaGhcNxFuLKH6jr3kAdiMK19wCXv2bNHLSdTp6amjhw5EtJUy5rokkAULl26NBS4S5cud+/eFQ6HcqBq2pGWlqarq4uLhO6qN5EkuLm5UclODw8P9d688f79+3C7EGuZMmVwseqd9ok4ycJcQSgdHR1v3rzJpndoJhYvXoxIXEQda6JLgtY+IDmGjU+ePDkxMVE4HIUf0dHRHTt2hGnDb6tlwVkB4LRp7SE4VkhIiGruYKmadoSHh6txTXRJ1KtXD14JwalWrVpBQUFqXOPh+PHjNLGucuXKajz3mwdN7C9atCjc05kzZz59+iQcEQZ1x5s3b7p27UqTi21tbdU+RIm4tQ+4XpreERMTo5oopTLgcg4fPuzi4vLjjz+q2Wb32QFRGLEJ6SKYVmBg4Lt374SDogSolHZAqHPmzClevDhoh4mJidpLVMQ9PKPyhdDjadOmwU8JB0Vd8Mcff1BNdORDmuB/AaregZCzdOlSNZYsQ3a4desWkkWYNly2es895OHq6kprH7y8vM6dO6dm89UQdBcvXqynp6euNdEl8RO3z5+WlhbiMny4aqp3qJR2pKent2nTBm4aQkVyIBwANYW9vX3JkiURjzt37vz8+XN1fc6CK6VZLBrif0WcZGmBdLNmzV68eCEcEQZ1x549e+Cy4c2QL2rCI2MAbIOmd1SrVg3JsZpNpk5OTh48eHCFChVg19bW1ppwPx5wcHAoU6YMqGSrVq2uXr2qggilUtpx48YNXGERta6JLgkYKlX3s7S0RHqkZrclCYmJiUj9wTm0tbXVtSa6JBBpaFtOfX199bvhzJAz3r9/P336dERfxGATExNNeLAoElv7gKueNm2aytY+qAZ37txp2LAh8v5SpUo5OzsLL15NgVhcpUoVCNTOzu7w4cMqKAegOtoBp7x9+3ZcHgKwetdElwRV9ytZsuS+ffvUcu7h1q1by5cvX4QrjKY5kqWJO8gSKlaseOjQITW74cyQM2JjYzt37ly6dGktLS0bGxvNUXuqlQdj79Chg4p3LlU2goKCzM3NcWlqudl9dkBijKQRVFJHR2fFihUqWHGpOtrx7t27SZMm/fjjj3DTRkZGGnIfnmBmZkalSiZOnKh+wQmEkq+JbmFhoSETO0TcrFKkuVBpEMrx48enpqYKh4ZBfREWFla7dm1kxiCdmjD3kEfdunVpdZ6xsbGKdy5VNuj2FS3r0JDbVyLuDhauFw4cbHLkyJHg08p+zqI62pGYmNi8efMiXE10zbl/RQBxRnDCtSNQqd/cwxcvXjRr1gz+F4qr3jXRJWFnZ0d8q2XLlnFxcerkghlywOfPnzdv3kxFpWrWrKkJcw95wInRTb6SJUpu3bpVbap3pKWlde7cWVtbG74adq1Rfsze3r5cuXKQaYsWLSIiIpS94lJ1tOPatWtUsQNMWXMSYgKIM92WrFGjxt27d5XNJVWM48ePW1hYQGU1oWKSACCUtGUUmPSFCxfYMloNQXp6+ogRI6gmukZlxgTYO9XKmzhx4vPnz4WjUzhx8eJFqjxUvnx5zZkXTwBvppnC1tbW+/btU/YyWtXRji1bttDSWVye8KLVHaBZVNMCVHrTpk3K5pKqxJcvXxYvXkwlk0ErNc3/4nohWfjfKlWqbNiwQW0yP4acgVhLcw+1tLScnJw0KkSJuOkdVNqyZcuW0dHR6pFHrV+/3tTUVBNqokvip+/7/IFJr1ixQtnPi1VHO3r16gU1pbo6wovWAFhZWRXjMGzYsPfv3wtHp9AiLS0NV0Q10dV7s/ssAXM1MTHBtUOy48aNe/nypXq4YIacceXKFVg0vBnctKbd4RNxN/lo9yUkx8eOHVPB2gdl4/Xr15MmTapWrRpCL8iH2heclYSFhQU4NGQ6ePDg2O5Ca6wAAIAASURBVNhY4QApFCqiHQi0uKoiXE10DZSoiMsPaBJA06ZNnz17pjbB6e7du3Xq1IGtlipVSqMm1vFwdnamOpWtWrV6+PAhm96h9oDxLliwgOYe6uvra6BDq1evnoGBAdh21apVly1blpKSIhyjwobbt2+3b9++NAckxpqzLokHvDetRvT09IyIiBAOkEKhItoRGRnJb3avaQkxASkRTQIwNjY+efKk2iyjPX78OFwPrgtXp4H+V8S5YOg2RsDQ0FAFs7EY8h2fPn3q2LEjbQGvOUWlBKDkGBg9enR8fLxwjAob4MdcXFyQFWtITXRJQI3Jk9esWfPIkSNKTZ9URDvmzZtHJSzBkTVQoiJueoeenh78FLzVypUr1WbuIdI+mi0LOqWZhBL6TBP7kfxt2rRJ2bOxGPIdjx8/BtekmujOzpo195CHg4MDckiovbe3N7LKQs22P378uHbtWngwzamJLgl4b319fTgxRKhFixYptXqHKmgHhNqpUydwDhiqvb298HI1BpaWlhAqxgH5QXp6unCYCiHga5o3b05rg52dnTXT/wJIeWm69LBhw5Q9G4shf/H169ddu3aZm5sj4oJuauDEDoKnpyeSY6rQun///kLNtpOSkgYNGlShQgWaoKZp8+IJ8N42NjZg0pBply5doqOjhcOkOKiCdty9e9fNzQ1OGZeEN8LL1Ri4urrSZmnu7u73799Xg+kdz549Q3IAIlWieAlNm/stDsQeLS0tSBZ5kmr2UmLIL3z48GHy5MlVqlShsoea+WBRJLb2AV7d39+/UOdRiFD169dHuC1VqpQGrkvigQgF7gXFBvEKDw9XXoRSOu1A17ds2VKpUiVaOquxViriHp7R9A6EqDNnzij14ZlqcPjw4X9rotfTxCfcBGg1LSHGOJw7d65Q33BmyBmglT179ixTpoym1USXhKWlJU2THzx4cKGe3nH8+HGIEn6satWqmlMTXRJIHeHBaC3P1q1blbfPn9Jpx8ePH6dNmwbVBIcyMTERXqiGgfZLg36vXbu2sAcn9H/ChAna2tq4Ig1cOisOXDskS9nS4sWL1a8QLQMBSVRYWFidOnVKlCiBvBDZocZmxiJuCzGqkm5tbX316lXhYBUSIP2bMWMGUmKaoKbhiTFiNKhk6dKlf//998ePHwsHS0FQOu1ISkry8fGBRGGoDg4OwgvVMNjb24N+YTQ6depU2Kt3PH/+3NvbuzgHXJcm+18A2RI0HInCwIEDlTobiyEfAaodGBgIikkTOzT5waKIY9v0sAls+/Tp04X09m1GRkbHjh0Ra5E2wIo1OX0ScX6MdtXu1q3bnTt3lPScRem0IyoqqlatWgi0uBjN2RI9O8BPIThhNHR1dRMSEpQkVNXg3Llztra2Rbma6B4eHhpOO5ydnclcMQ43b95UmwXSDOKgolJQeM2siS4Jqt6BgL1ixYq0tDTheBUG3LhxA3LUzJroknB1da1cuTJN7wgNDVXSxqVKpx27du0qwqFGjRrCS9Q81K9fH/kBghNCVKGe3oGeBwQE0DpvPT09TX7CTahTp061atVgrhiNPXv2FPZbWQxZ4tmzZy1atEBmjORBk+ce8kByTNPk27VrFxMTU+jyKPixwMBACwsL+DGkxxp++0rETVPDOFD9Ej8/PyVRSaXTjhEjRhDtsLKyEl6i5uEnrpZ25tKPEiXmzp1beKt30FZY8DhQUKR9Gn5nUsRJ1tDQEEkwYtLSpUvZ9A61xMWLF6kmetmyZTV57iEPJMc0mdrR0TEiIqLQ3eR7nfF64oSJSBjgx4yNjdn9eIpQJUuWhEwnT56spH3+lEs7Xr9+bW1tDc6BLFBjF7gLYGtrW7x4cQxImzZtCu8WHg8ePKhfvz7VRGd3JglQddqWc+DAgYX9CRpDlli9anXNGjXh0AwMDDR57iEPxOmaNWvCD4Bz79y5s9CxbfixLl26lOFgZ2fH7tqKuEJwGA0oeceOHe/cuSMcMkVAubTj+vXrVMKyXLlyCpFow4YNW7Vq5cOhZcuWjRo1Eh5R4OHu7k75gZOT09WrVwvpc5bz58/r6OjgKipVqqSQJ9wQJSTbgUOLFi0KqWRpeoe5uXlkZGShy/wYcsanT5/69+9PIkaIUsgdvp9//rldu3bwZnjFe/g34REFGxgESo5Lly49fvz4Z8+eCUetAAOJQUhISJ06dZAHKqomeuPGjRGYyI/hDf4rPKLAo45nHSp4YWRkdOLECWVEKOXSDn9/f5pBCS4sp0Qhwt9++23WrFk455YtWzZv3rxq1aqZM2cOGTIE5io4uHnz5hMnTpzE4ffff2/btq3gAAApKR0zevRoPsg1adLk119/5b8LLyPpCHA2nJOO6datm+QBOQP0C/kBPFf16tV3795dSJfRLlmyhHa+1tfXl9P/tm7deujQoXPmzIFkIdbAwEBIFoIeNGgQ5Cg4uFOnTuPGjaPBHzt2rLe3t+AAiLJHjx7wgDgACgMqwwsIv0JfxCskC1n/96uiNm3aQBnoGPxQXqkPP7EfXgzmqqTZWAz5hdjYWDgxCBdRVs6JHVCt7t27Q0t9fX03bNgAh4ZXvMcnSL4Fmon/QpNJLSdMmNCrV6+mTZuKHyDiot3w4cPpgJ49e/IHQI15lYbhQMMF/gpfxAlhSjhg2LBhMMa8OjQqbQmHht9CcqyMKKUkfPjwAT7H2NgYNgufLOf9eEQoxA6EJLivQA5+fn6zZ88ePHgwRlXgTCDlMWPGUJTBmywjFH/MqFGjwEpJLpDXgAEDIGXej0k6SXhFKAyvLZLxMWcgQtWqmTm9Q0tLa926dW/fvhUOnNxQIu1Ad2FaRTlANYUXJzUwiLAHBKTLly8/fvw4OTk5jcPLly/hCMLDw9esWdOvXz9xU4QB3L59+y6HGzduIIZJxpgtm7dER0fjgHPnzvF/RZTauXMnfQ4cO3YMpxLYIYgOfpQOQLCUgc+CRVJwmjp1qvJKsigPyBKgyrTJjoODg8z+F5KFRUGyGE9esqmpqZBsXFzc+fPnYbdgh+JmA1uKioqiwb9169aUKVMERgU1WLhw4fXr10l8vXv35g3+4MGDvFbgPdiJwBeA6OBHSfowaUnnnitoWwewMYSQwihZhhxw4MABS0tLyLdatWoyL92Cu0A4mTt37tGjR6GNCQkJKSkpUHu84j0+OXz48LRp03x8fHjH0qJFCxxMaonXffv2QasFbgex5/jx4zgAZ1i8eDHiHH2OwAP/Rt+9efMmoiCio/gXYYPLli2LjIzEAcHBwQhjeXVo7u7utPbB09Pz9OnThYhtv3r1auTIkcjs4YotLCxkvmtLESogICAsLOzRo0dJSUmpHODQ4McuXrwITjls6DAcxoeSGTNmXLhwgXzR1atX//zzTzDC/55VBAaD7+IAjCpoBAUpCBqEBqKk70JkoDWC6Na1a1d8fpfzkEuXLgVlEf9rroBiYzS0tbXh4cGDnz59Khw4uaFE2nHv3j0XF5ciXE10V1dX4cVJB/AAiASj//79e/5h+VcO/Hv4d5jc77//zo8+6M6nT5/oMACBCpL474lFoSGhIOb4a3x8PB9gICEEHvqczgwbFtBJ/BDNyQBAbCUJTa5AqEbChJGBd4BQvxa2SQAZGRlUEx1XIfO2SSD4cIKgkqCnWWZIGBaY7qlTp0D5+UGeP38+fv0fuX79CsUQJxYi7q71+vXr3717h7/CBSP/4N0ozPvz58/0RXic5cuXC1wwfNCzZ89I+vihvGYJgKOjI93ew4+yKunqBGjOvHnzatSogfgqc1EpaAWo7datW0GyyUHRycUdAH4InhNZJlwWKTa8BDSZd0owipUrV/LEggBqgjCDv+LrW7Zs4SMN+gwPQ9/FK+xl6NCh4i4LDnb79u3gPTgAwYwPb9KDbt+CjVWtWhWmB/P892IKNh4+fAjfDoMtVaqUs7Oz8MKkAwZw+vTp4HaQC+/HSFL8+/T0dBwwefJk/uYr2MDz58/psC9fvly5cgUJmMDhwEG9ePECB0Bb+CwILuvkyZMfPnyg7+LMGPMOHTqIf7FPnz4QJf4KHdu2bVvHjh3F/yoN4Mdonz+MDyhplv5ZHiiRdoC26+joFOGWzspGJCGhRYsWgT/yj8nfvHkDCZ04cQJDf+PGDYw+fY4ws2nTpmbNmtEXQTvEH15g9NesWSMwp9DQUNIMmKUk7aAvfuVMccCAAeI3PHjagQNWr16dVysVfd8qHSNjYmJCDoXvaqFASEhIhQoV0H9kObJJFl7S19f3zp07vJjgraDf/+OAMQcXoRGmzSF5yYKDit9FwLfggnE2/swwXeQWtH5VknaIz7fArwwcOFA8tyPaQb8rG+1Awkc3nGvVqlUYJcuQHRITE/v27QuzRZSysrKSbaYa4sHBgwfhPUgxoI2IKAhI8GZ4BU8l/YQGRkdHQxvJL4FhiBduwhu4PjglcbZNtOMbx1okaQf/XdALRCkfHx/eoSEQ7ti+g3ZUgUUMHz48rw4NybGRkRGGBVFq0qRJSlr7oAwgWbWzs4O1VqxY0d3dXXhhUgDDDs6BkMQvmIdHwjAiuMCPXbt2DQNLg48ItWPHjl9++YVGftmyZeIDhcMQvwR31ol2fON2PBanHTgzH/gA6AZCkvitWajZrVu3vnGBTzbawd/BsrGxOXDggMLLASiLdiBazJ07l9bhyDaxA0YF6s1vmQbBgBAEBATgw+4cMNaBgYFw7hgU2CEEw4cQAe34xj2XHTZsmLihSkM7AJx8165d4rmF/LQDoIoXGJ99+/YVrrmH6C0cIu3FANokw8QOmNa4cePgJeneAwwVY+7v7z9ixIieHMaOHYtsDwfgT+AKOJgfZAHt+Mrd0kB/+AOkpx0wXeSdbdu25U1dftoBEkYJcfny5SFZce9QKIBrh39MSkqCv6M7RsIjJABjQTCDD8WrNDQLx0CCiOKvXr2Cl5DmJwoCwsPDvby8EFxlLioFTUMMSE5O/soluMim4NDnzJnTv39/+Cu8Lly48NixYyAf8Ejbt2/Hh/zdDkG9SKj35s2bxaOUlLQDg3/v3r0JEybwPD6TduyQi3aIuFp5NNMLXY2JieH7WcCBwYe10txJGW5fwbGMHj3m4sWLZCnIlCIiIuC44L569+4NPzZ69GikTHA7UHW8zpgxA6NN36W7HXxP8HVEuqlTp4rfWZeSdkAZgoKCeG0RKYJ2wI8hakPbMT5LliyB0vI/pxAoi3ZgvKD6Rbia6GCUwsuSAqDkMEJiDxhlOPFBgwZh6HlLwxtvb2/wa5BWGJI43eNpB8RJEoIADh06hHPyx+RKOygo4g1Ixh9//MELVSG0gzbOxvjAzgtXcEpJSaGJHVTtXgb/Cxd56tQpqlkCf4f8D+RAfBo/3sAtIo3Ys2fP+PHjeRcpEqMdnzl84yR7+PDhDh060NeloR18tIPooT88vZCfdmA0zMzMaHDGjBlT6KqkwxXC0DDy8IBHjhyhe+/Cg8SA8Xzw4IGfnx+GcdWqVfHx8bkyD3gGxEW4UYzw6dOn+XSwIAMXBVUEyaa5hzI8WIQSTpkyBVSDnncgNvv6+oI3iN9sw/sePXqAf69cuRJv+D+J0w5ySsCTJ09mzpzJ3+fLlXZ84fCVu/F+9OjRLl26kL0ohHbUr1+fbn/q6emFhYXlqgMFAfC6CBOlSpUqVqwYIpQMfgyDjHhPQ4cEKSQkZNSoURCEIEJB1cER6QkL/12edkAoNFx4PXnyJBgDH2hypR2QNakT7A6OkT+//LQDsLKywuBoa2sjXX/48KFijVRZtAM2YGpqCkUsW7asbDVYEN2REtHZLl++jKEUvwHFA8bZt29fganwtAPhB+ye5AoRzp49mz8yV9oBH8E/pzxz5gz//EwhtMPDwwPBCePj5uamjKnCygNk4ejoCFuFo3F3d5fBXJENIB7TAMJJZTeLDVEfzlGcc4jEaAfOAM9LUgYZnzZtGglRGtqBL+Ik5IJPnDjBzy2Vn3aIuG13aHs89BzmWihcMAGDg4gFK6tatWrlypURFK9duya4aygALHTu3Lk2NjZQBjDpjRs35roBenBwMDLLihUr6ujo4CfOnTtX8Gk3LBRUrEqVKlRUSobMGAnPgQMHSHVTU1NB0cRvs4kDfgZeSNwieNoBXYJ+gvdDUqDOCD/winRkrrQDcoGwaL4n7AWfE2VRCO2AE6hevTo4GXwaQmyhcGgY0gYNGqDDpUuXlm1dEgIBxhxC+cJNzsB/Bc6KgA8R+Js3by4ubv4hC1QCMn337t03bv8y8enAOdMO/C7+CjcIicOPwY6GDBlCslMI7UBKWa5cOZrecfHixZz9QF6hLNqBFJaK5sKFySBRDPH69espIX7z5s3ChQuzjEzZgacdSNcCAwMhNjJahEz8icSfK+0Ae0VMoj6Af/BzSxVCO+rVq0clWapVqwZipFguqTzAwJDa0hOiWrVqyeB/MdRIHCnSgBwgqxa/TZUreNpBN6ITEhJIWBcuXOjVqxfYgzS0A2oA4UKvMOwQ5ZIlS8gFK4R2gFBWqlQJ5gr+AbZK+lMogAHp1q0b1aWgO8/79u0jh5gd4Ozo0QOOx7fg4JDH58y0EL+pKgAA8oFxLvj3hKAV0NKSJUvSHT4ZHiwOHToU2kjp6enTp8WT2lzB0w5oL2g6vp6WmnkXCuO2YsUKilK50g4cDGnGxcVReg17oRUQCqEdgKWlJT14hXssFJOpERfolnONGjVkWzoLPwMG+Y2LMvAhgkm+OYOnHZAL+ChCAAQH0URERMALkefJlXYgK4CHAVmBfNGHtWvXUm6sENrh6emJkQHPtrCw2L17t2KppFJoB0YBZkaeBaIVXpAUgNnAMOhsGMHevXsLj8gRPO2AOc2YMWPTpk3kPSEtyIbsKlfacezYsTFjxjx48OArB1g+6CSchUJoBzwXwjasFLbq7+evWC6pPIB+0cQOZAnmZuYy+F8YJyRL1xsbGwuTyDLnyw487YDRgrIEBQWRZBEy/f39cXLkFtLQjkmTJkGv6JY13tDcUoXQjvr16yOPp00NAgICClHdRggXKTiES5Zbs2ZNBKQc3A0G6ujRo2BXdN+uCLd+B4NJg5wdxo8fX758eToevzVlyhRYk/CgAgb4d4TVIlzZQ9lqok8YP+FpfGb4h7qCuOcpRInTjuPHj8+ePfvmjZtkQRhtWnuSK+2AHKHSu3btohuNcIwbN26E9eHkCqEdrq6uRFhhNegVGVGBxaePn6ZNm0Y10U1MTGRIn4AjR46Qn4EfQ7wT/jlH8LQDdAFJ9ebNm4k9wAZBFOgRWM604yu3jg/fxStlcVAAGBfGXyG046effqJ9/iBWpNwpKSn/GUH5oCzaYWdnBystKmtN9F9++QXxns4G1yZZESVn8LQDIWrWrFnIg2l12TdORcAeIFTku/RJdrTj1KlTnTp2Ak0hm6S5pUgOQDtIRb7JQTtE3MMzKD2GqH+//gqfKqwkwAAgC4QZbW1tZ2dnGe5j+fj4IOpTZAL/yLJOTg7gaQfyDESsYcOGUZyDOCBi8Ab4X1pA+y172hF9JxppPWgKzV3AhzB7SHbEiBHy0w4RV72DtuUcPXo0ryoFH3BSixYtorkp6HzdunXPnj2bcxkGBCo4Nb4SMSg+LQsUHicGZJn4CZwfQ4QQvnfv3hyYTQEBiCwtysOrbI+MJ/8xOTEh85Ex9NbX11d87VWuEH/IAq8Fb7bGf03Kq8wwAL8BkgERIBrlTDug5FBpKCTyaYpSMTExEyZMgD1u375dftrB3+QDP+PjcYFFXFxcz549y5YtCz9mY2Mjw7okRBCEf7IOWu0oPCJH8LQDaQl4JPwYbI2mpj58+BBJEdInHJMD7fjG7e4O0rlkyRKajYDO0NzSvn37okvf5KMdgIWFBd3BGjRo0KNHj8QHUE4ohXYgtNMMIxClBvXznBCLuAJtGGs624EDB/L0hEUkRjsgVBhbo0aN/vrrrzRuMz1IAifs0KED2AzRi+xox/lz5/FfaGdYWBidLSEh4Y8//kBsi4+Pl592uLi4wF9DqI0bNSbX8H38Ci5Onz6tr6+PPiNhlWFiHQAbuHLlCoV/nE2ySE7O4GkHXqdPnw6uwK9uhylCsl27ds2VdoDRQrJQEiQK9BAEAoWpQ7K8IOShHZAsLaPFGXJ96FCggJFZsWLFQA7IhjGwOXce9gU7GjVqFGIh3UDK9aESnAPIDTwjXC0CHjxmzj+R74A3x9WRQBFTZQtRoMj8TfUFCxbITDvOnDkD39ijRw+4qQ/vM1NeRCkYAswqZ9qBP8F22rdvD9Lz5MkTnArXBX6AeLl161byjfLQjnr16lFyXLlyZThbha99UCAwGidOnHB3dwe9rlixomzpE2IKKCBp+7Vr1/r37y88IkfwtAOeCrQDbhCSgijhoD5xc36hcvBsdEx2tOPGjRsgBPBjx48fp0fGoFPQrqFDhyqEdtA+f4jjeANX+Z9BlA+Kpx24eIwX9A/d1dXV/alBniUq4sqM8pvQwNLymhOL044/5/8Js4cdhoaGkpbAmULG+/fvz5l2gG3gd6FecBkU2L5xe5FAA/gtnuWhHQjbVapU+YFbYHwk+EiheM4Ca6GVcjJvdg/Hh3yLLhZsnfePUkJAOyA4eGGqn/ON8+mQLHLTnGkHzLsdV2x41qxZNDsE/YETmTp1Kr8WQx7a0aBBA7gzSBajBIUpFJIlfOXuxsdzyK6MmwC4uqSkJPg7DH7Oj1cIf3MLbnE8XCrdKxYeUcAADUFMpb2FZZt7KOKq69KMh/T09KVLlwrq1OUMSdoBlUasesQVNIJPg2cDh8uhXBgNMgKSt7d3586dDx06RM/+IDJ0Zvfu3TRHQR7aIeJu32pra5coUQJhj6bT/XcgCwowYuvWrTPiSkXr6OjIvNk9vwFCdHQ0wr/wzzlCQDvganx8fPbu3Uu3nSCXJUuWrF27jm5j5EA74N8gL0QoqoGES0PYgluT/yGLiHteTDOFK1WqdPDgQWmsW0oonnbgUn/55Rf4XHTX2tpaNitFFnvs2DFSXHBzGJXwiBzxH9rxZybtgKHC8nGqrxwiIyNzvdtBtEPE1b7dt28fnTAjIwMpGuKZ/LQDwQlhCaMEQ120cFHBn88PwOXR/EFHR0fZJIs87+zZszSYIHM0D1R4UPaQpB34+vjx4/kdX8PDw+FVc57bQbRDxHGg4OBg4ijIzyBZ/hmBPLQDoOodIN/8rReGQgp4eVtb22LFiiFJkG1iBzBu3DhyPghUoAX8em9pIEk7RNxdQ4pSX7npogEBAWBy0tAO2As6ExsbSye8dOkSPB4t2ZOTdsAn8Gsfrly5UmDZNjjWhAkTqlatCippZmYm28QOEbfZAj0ffPbs2e+//56nW/KStEPELVaAy/rCAX4MtIYe0WZHOyAv+DcoEijLjh07aNYOru7AgQNQhm9y0w5+ekfJkiV9fX0VOL1D8bQDeRLyeESmUqVKyVwTHSIE3SOhYuyQv+YwvaNZs2aCLcEkaYeIs97NmzdTQMIrGd43KWgHAOnS2mX8CRfI3/yQh3aIuPyAbgsNGTIEQqVzFlggMNPcb7hg2Z6wiLibk3CRJFlYFwZQfDm7ABArhCvuoCVph4jbpwBWRwkcXmGlRC9ypR3ozJgxY/glBrBVvkaWnLTDxsaGJNu/f38FmiuDigHnM4+riY48Ci5YtokdANQAAZ4KxiBJHTFiBO9zBGjMbWGKV17ts6QdOGDUqFHXr1//xBVZv3//Pm0akCvtEHGhC56QohS+hdhGt4HlpB3wCTRJ09DQUOFrHxSImJgYeBWkT9ra2vb29jLMiyfw1AFXijHnK5BKArnWz//dXjhL2gFPuG7dOgouaWlpSKUoY8mVdsCPDR06lOaW4rv4IjlDOWmH6Ps+f/D5OAndQVEIFEw7cM379++vWbMmHC6SA5mtVMRFen5lKeIBxj1L5kEl8REkxGcJZEk7gJ49e/KzrPkYLw3twAF+fn70BFT8u3LSDnd3d5qOhzcFfxltSEgIPeqTc7P7Xr16QaA0yA/uP4D4YEuSFovBnzt37rRp08Q5ZZa0A+jbt69kkYkcaEf79u3pQygVJIsU4SsH/rty0g5PT09aQO7s7Exp7r/dYig8QAyAapUtW1aemugijkAjolCkB/lAotyvXz9J1wFdHThw4OLFi0FTeN3OknaIuHRr1apVtA6IVzBpaAeAX79w4cJ7bqMr/rty0g4+OcZYLV26lB7cFEDAj9na2hbh9naQrSY6AWMIKkm0D0kspAavIvBj+C9tPjV58mTxv2ZJO/DXAQMGnD59+i23NQQvl1xph4jTHF9fX8EcQflph5ubG7w9ODf8GDqmqOcsCqYduM45c+b8+OOP4EdQQZmJpIhjiGvXruUn0l+5cgWRQLwEbGOuUBjkB5sE2Rffsy072oHv4jDBmkZpaAedk1/5yUNO2kHV/SBUMEook6KEqiTAPCiUmpmZySNZSAHGwPNxsIG//vqrW7du4pLt06cPXCqoGIQ7a9YsXrLZ0Q644OXLlwuWjUhDO4AePXpcvnxZIFk5aQdoGUm2evXqZ8+epWySodABXBb6o6WlBcIt29xDHkOGDIEGkiaAf+zdu3fs2LGtv280T/Fp3LhxR44cAU89cOAAFWUWZU87RFweBack/nxWStqBN/CEgqqyctIOwNramtY+DB48GCfnz1ygAEqkq6sLP2ZoaChPYowx9Pf3p8lhECtkBBfUu3dv3m9Ac/Bf/Bxy3evXr8PLgQGQl8uSdoi4LAguDiRGXC7S0A4RF6FOnjwpfpNJftqB9KlWrVoI6CbGJtAr8r3yQ8G0A+YESygiR010cSAUwcw+vM8cYpgTUuRjx46tWLFiAQeE/NDQUJI6wkl0dDR+mr6YHe0Qcc9ET32vzE2QknbAGhHnBOsD5aQdAFT/B646ExSxIAcn5C4dOnRAKgMVlK0mujjAKiC7199LhcLPBgcHr1y5ciEHPz8/ZCS0MxZkERERwZdXyo52iDht4eeWEqSkHQ25uaUCycpJO0DLQLtppTF0VVHmyqBKQFt27doFko0kqmrVqrLVAuABZgzdhu5RfpyWlgaFRFRA+IeG4HXHjh1gORkZGdD5lJQUhDS6g5sD7YDzAbGguaXUZylph4hbLQijE8/B5Kcd7u7uZcuWhTezsLAAlRc3qAICRGWkGfTgQJ7bVyIufYJf2r9/f3Jy8t/ctHT4MbigNWvWLFq0CLJG4nTixAl8iOQZf42MjATRpEqm2dEOoHPnziCdfIHsb1LTDopQNLeUvig/7cD4mJiYlCxZskyZMuDEj78vL5UTCqYdGAiqqwO5yjxDmAcGFCnCqdBT/D1wSBd28pIDBEb8AJ9D8LTRAH0xB9oBXYHsafoV9VlK2iHiRM7PLSXITzucnJxgABgxBMIC+zQUQOwH2yjKbXLm5uYmJ+0AFfjtt9+OHz/O7/oBvwlhJXMQlyz8L8acv8uVA+2AlMePH0+LA6nPUtIOEefZDx48KO6C5aQdAGg3zLVYsWJwc0lJSfyZGQoL3nI10StXrgwhGsm0W5gAoBHImhAYKGx85R64wATgzfBKdOQbV9UQSRRUnRa85EA7RFwetWfPHt6OpKcdIm6bgnv37vGURX7aAbZdpUqVohwOHz4suINYEIDrRSchUJlroosDvmXQoEFgCTDwv7/8/e1r5kItuK9XHOBPaGzJj4HkIZyRy8qBdsCPjRkzBrLgR09K2iHiKuvzlce+KYJ2ALa2tlQIzsfHB7RY/DaMzFAw7YAAaKUDlE+e+/A8YAODfx2MoaQsQfh73MjevXt348aN/fv35+/S50A7RNx0kK1bt/IFbaSnHSKuyDHNLaXvyk876tSpQyOmp6fHlwMpgIAIaGKdjo6O/P5XxE2Xgc0gm3zw4EGWNanI+cKH9uvXj5dsDrRDxD2YgwvmJSs97cD5R40ahZ/jTV1+2uHi4oLMDyMGQ8i1didDAQTcd6dOnUqVKgULlXnpljgacosOQAVCQ0NTXqVIenC6zxESEjJ37lx+KkDOtAOqO3r0aJoW/S2PtAMxDGGJn4QhP+0QcbdvixcvDrVfunQpPxmu4AA5jI2NTVE5aqILQMxj06ZNd27foTkZgl+Ec7t///7OnTsRO/hNW3KgHSJOLhs2bOBrYUtPOyidu3z5MnlUhdAOJJlg3hixunXr8hXS5IQiaQfG6I8//kAEhc5ZWFgIuy8rYFcgcTNmzABXOH369KNHj15wgCxhn7CxKVOmwET5yCTiZiPi4O3btwcGBgoEQ+jTpw+YynYO/v7+fGSCWcJQISp8jsAjOYkVegMtoQOAESNGiP+uDEBMoi1OwL5PnDhRMIMTzGnSpEnoIbIEU1NThRBKESfZX375BZKFLM6ePQvJPucAycIvw5KhTvzTUAKkic8x8ngFHeEFx2PAgAF0AADHJ745uK+vLwlu5cqVAv8r4ux5zpw5UCf6LlyJ5MnzBFgprXqvVasWfpctoy10QG5Hux5C82VeOisJkOOBAweuWLFi//79UVFRtDIO5ADvkbZBOaHD4huZwudAk6GT0CLQEcnC6uS1SHXh98aPH8+rN9QY/o1UGrYjoOn4CWRra9asoQOWLFkCTiOnQ7Ozs6OtEEGw+PpGBQSwwZkzZ9asWRNWKefEDnFgxOCm4KzWr18Px3Xv3r3ExET4MaQ3oImQyLRp07p27SrO50AO1q5dizFHOgdxC1wN5NK3b19oAskFyW2vXr3oGBCUWbNmkawXL14M/yb+RRE3OwT5GAIfrwx0z0xmIMmk6R3VqlUTX1chDxRJO0DB3N3diXYo0EoJjbhdvjD6GFNfDmAb3bt3b8pVbhAc3JAr1EGQ/KvggMZiIm/ILUbK4Ysirif8F7M7RnoghaJVqUgREIALZvWO2NhY2AxVTJJ/YocANJ4wM0iWnolCsj169BAsOSOIC07yr4IDBMeIC07sG/9C/IAsT54nYJSMjIyo0DicnULMlUGVQBSBwy3CbU+jqBBFIC1FdoS8BYwB3gyvw4cPb9euXZY3G3i1zM7hZOeUpLQX/GgOx+QJSI5pMrWNjQ2CboF6zhIXFwfHQjXRra2t5ZnYIQkMO4JRz549wT8WLFgAPwa2gfwWPEBSarm6Gmn8GN5k+d3slEE2wI/R9I4fuH3+wI+Fw5p3KJJ2PHjwoFy5ckW4HZ7kWWApDbIc7kIK5AcIThAq3FDB3MLjypUrenp66CEciswVOzQQ8Lw0sb9fv378vW6GwoJhw4bBoUF8Cg9RAqiTNwM/o+RYX18f2XaBmkwdFhaGHpYoUaJSpUqurq6KTZ/UGI6OjjRTGBFKIdU7FEk7oGT0VE9HR4dJVHq4u7uXL18e42Zvb3/p0iXJJ775ji1btpDaQbJK9b9qBg8PD5reYWVlJblGl6EgAwkAktcSHOSfe6g5aNCggYmJKa19mDhxIr+3Yr7j06dPmzdvtrCwKFasmKImdmgIQCVB1OD/TUxMjh8/Ln+EUhjt+Pjx46+//kpzmG1tbYUdZ8geMFRdXd2i3CI9ULeCFpygZLR0FmoHyTL/Kz0g2WrVqhXl6rru3bu3YD5BY8gSJ06coLmHcLhIDJjaSw9a+4Cha9++/Y0bNwrIfLWUlJRx48ZVqVIlswqFrJvdaybgx2rVqgUnBja5evVq+e9gKYx2PH78GIJEZELPnJ2dhR1nyBHGxsbFuK3Ap06dKr9QFYu3b9+CFSFlh7myFCGvMDU1xbjBLubPn1/QJMuQAxYvXgxXW5Qre6jYiR1qD7A0WvuANyEhIQWEbcfHx7dt21ZLS0tbW1vhE9TUGxgrS0tLjBuC1OjRo588eSIc3DxCYbTj+PHj+vr6tHSWWWleATOAPWD0vL29C1ot7cjISKqJXrFiRWVP2VE/ODk50QLpJk2a0H6SDAUfaWlpw4YNg9pDdrJtdq/JwHDR9I6qVauuX7+edlXNd1y4cMHV1RVkqEKFCm5ubsJOM+QIZ2dnmufUrFmzK1euyBmhFEM7vnz+smTJElo3ZWhoyIhkXoFwTtM7dHV1r169Kv/DM0UBPZk5cybVRDcyMlLU0lnNQZ06dWh6R7Vq1fjCIQwFHJcvX4bsSpYsCdmBODK1zytMTEzA2KD2Y8aMSUhIEI5vfmDRokW0LklPT4/Ni88rPDw8QCJB2iwsLIKCguQsB6AY2pGSktK7d2+6D29lZSXsMoMUqF69OgYQnm7fvn0FJzhlZGS0bt26ePHixYoVYxM7ZABlfhg9BLBdu3bxpcwYCiyQye3duxeBE04WZJE9WJQBLi4uZcqUQYxv2rTp3bt3hUOcH0CEKlWqFGRqbW3NeGRe4eXlZWhoCCoJi1i4cKGcZZcVQzvu3btnY2NThKuJrvCKHRoCsEiYBJjHr7/+WkCehgK3bt1yc3ND1CxXrpz8NdE1EBgxSBZ0HBaLzO/Vq1fCIWYoYAA19PX1pVJvcLVs7qEMQFyvWLEirWrk6z7nIxISEkCAaI8kti5JBmDErKyswNu0tLSGDBny4MEDeZ6zKIZ2nDp1CoSDJnYwicoGJFW0WsTS0rKAbM4CZ7F582Y4DriPGjVqsCk7ssHBwYGePzZq1AgEPd9dMEPOSExM9PHxoZrotra2LDOWDfAYSFdA3davX5+/k6kRIHfv3o2oSc865dnsXpPh6OhYvnx5CLRJkyagklnuVSIlFEM7pk+f/gO3jaqRkZGwswzSoX79+jQJoFKlSo8ePZKHSyoKYD8TJ06E/y3GbYXFJtbJBhBKmthvY2MTGhqqkE0NGJSHmzdvgm3AEsuUKcPu8MkMCwsLmiY/bNgwhZS2lBmfP3+eNm0aLWVXYE10TYOnp2f16tURC4yNjbdt2ya+cWZeoQDaAaE6OTlBvSBUFxcXYWcZpAO8G61TLVmy5MqVK+XhkooCnEW7du2KFy/OlpzJA6TLenp6MFcQyjVr1shjrgwqQFBQEORVRAk10TUKrq6utPahUaNGt27dysc86vnz5/3790emDhok52b3mgz4f5regUTU19c3JSVFONBSQwG048WLFzR7CMGJSVRmQKjW1tZUJR3BPn9vSxIiIiJovxi4D+Z/5YGZmRnMFSM5fPhwOMF8dMEMOQNJ1G+//UYrxpGvM4cmM2rXrk1rH6D8hw4dyq/J1LC1c+fOgfrDACtUqODs7MzSJ5mBCAXOgQgFGvfw4UPhWEsNeWkHhIrkgCoT1KhRgz0HlQcuLi40RcbLy+vx48f5HpzWr19PNdEhWeZ/5QGcHSQLc8X76OhoNr2jwCI5Oblhw4bFOTg4ODCHJjPq1atnYGCAYaxYseK8efNevnwpHGuVADwyMDAQ1Kcot9k9W/EgD9zd3UHdinDlqs+ePSsca6khL+34+PFjnz59inJAcsCIpDyoW7dulSpVfuBqnwQHB+d7lfR+/foRobSzs2OSlQcgbZUqVYJka9WqBXMtCE/QGLLExYsX7e3t6YkYq4kuD2jtg7a2NpjHoEGD4uLi8iWPysjImDFjBggHq4kuP+DHaIUX0tE9e/bIHKHkpR2JiYkIlkW4XWfZxA45gdQK+QGEimA/d+5cOUuyyAn8OthGUW4/EWar8kNHR6cYBz8/vwKyUolBgC9fvixbtoyWbunr67MHi3LCycmJ1j40bdo0IiIiX9h2bGxs+/btwX4QoeDQ2O0reQAqaWxsjPAEKjlv3vzk5GThcEsHeWnH6dOn0Q/QjsqVK7PSb/LDxsaGbjAMGDAAQs2X/IBw5cqVihUroidlypRhNdHlByRLC6S7desms7kyKBUpKSm//vpruXLlkBmbmZkxtZcTiAi09gFMbuvWrfkyX+38+fNgP+CREKurq6uwiwx5BKgb7fPXrl27GzduyBah5KIdYK+LFi2imgR6enqMSMoPDw8Pmt7h5uaWv5MAfH19qSa6oaEhk6z88PT0LFWqFMYT/EP+vZQYlIFbt241b94caTGria4Q0NoHqnE8b968fGHbAQEBBgYGRVhNdAUBgalSpUqgHQ4ODidPnpTtOYtctCM1NbVfv36QKBSL1URXCODpaBIAQlRwcHB+VUl///49kvISJUrAX9jb27Mn3PIDqTNV78Dr6dOn8+WGM0MOQN526NAhkELofNWqVUEThSJkyDtsbW0pe+nTp09cXJxw0JWMjx8/0u0rdIDVRFcIQN10dXUR8UHmZN7nTy7a8eDBA2TnkChiJJshrCgYGRlR7bUFCxbkV2mp27dvQ6Dwv6VLl3Z1dWW0Q37A5SHzw5CWLFly5syZspkrg/IAqg2LoxlzyIzZxA6FoHbt2rQaztLS8vLly7Ldk5cZT548adSo0Y8//gijc3R0ZH5MftSvX9/U1FRLS6tMmTJjx459/PixcNClgOy0Awp0/PhxKghToUIFtsBSUaDdmWGorVu3zq+5h9u3b9fR0aElZ8z/KgrI/OD+MKo+Pj6JiYkqdsEMOePly5c9evQAz2Y10RUIDCNV70B+vH//fhXnUceOHbO3ty/KbenHEmNFwc7WDlQSo9q+ffuoqCgZbsnLRTtmzZr1AwekccKuMcgKLy8vmlVaq1at2NhY1QenT58+zZ49G/4XqbmJiQnzv4oCX7fR09Pz2rVrMpgrg/Jw7949d3d3usPHQpQCAR9CDu3PP/9U5VaInz9/Xrhwoa6uLgKksbExW46nKMA6iEq6ubkdPXpUho1LZacdf//9d9OmTaFMcKP4eWHXGGRF/fr1IVSMqra29uHDh1UfnBISEjp27IjsBKk5q9ihQNStW5fu4desWXPLli35u0CaQYCgoCB9fX04NDaxQ7Gwt7enydQ+Pj7gdirLoxITEwcMGFC+fHmQHgsLC7YuSVFAhAKZ+/HHH6tVq+bn55eWliYc+twgO+149uwZ7JPmk7InLAoEwrylpSXdlpwyZYrqiwqHh4cbGhrSkjOW9ikQkKyRkdGPHCBZeTY1YFA4Ro8ejRAFtWdLZxULJKUVKlTAwCKHOXXqlMomU0dERDRp0kRLSwtiZeuSFAj4MVNTUySlxYoVGzVqlAzr8mSkHWCse/fupToEoDwsIVYsHB0dwdBhqC1btlT9Fh47duygRbxIyhmhVCzgeWnBedeuXePi4vJxgTSDON68eePt7Q1PCp+GEMUcmgJRt25dSo51dHRkXvuQV3z58mXPnj22trb43Ro1atSuXVvYLQY54OzsTOuDmjVrdvXqVeHo5wYZaQfc5ZgxY5CRw4GypbMKh4eHByVe1tbWFy5cUHFwGjp0KD2LhWRZiqBYeHp60vQOjO2lS5dkW/XOoHAgMwbXR/YGu2Ob3SsW8CFmZmagdEhmJkyY8OzZM+HoKwEZGRkzZ85E4gTawTa7Vzjq1atXpUqVIlxVpxMnTuQ1QslIO5KSkpo0aQLvScmBsFMM8qF+/fq0kKRSpUobN25U2W1J4O3btxYWFkU5uLu7C3vGIB/ggqtVq1aUq3+/c+dO1T9BY5DE169flyxZUqtWLcgFeTkrKqVw2NragnMU5UpbXr9+XQXz1eLj4318fEqVKqWlpcXWJSkc4OW02wOGd9WqVSB5QgHkCBlpR2hoKNVEr1ChArt/pQyYmprSJICJEyfmVajy4Pbt23T3rGzZsuwJtzJgbm4OsWKEJ0+erJobzgw5A1R7xIgRcGVIothm98oAYkSVKlWLFi3m4OBw5MgRFbDta9eueXh4gOjQ7SthhxjkhpWVFVgdmMfgwYMfPXokFECOkIV2IDnw8/MrU6bMD6wmutLg6OgIIong1KRJE1Uuo12zZg1NO9fX12eSVQZcXV3pGVbjxo0TEhKEAmBQOe7evduqVSttbW1k5Gyze2UAyTFN76hSpUpAQIAK2PaGDRuMjIyKcGUI2LokZcDFxYVmCiNC5bUQnCy04/Xr10gOQCShRkgOhN1hUAS8vLxoekf16tUvXbqU14dnsgFpX5cuXZDzQbjW1tbsCbcyULduXaLsNWvWvHLligpuODPkALjL/fv3W1paIm+rXLky2+xeSeDXPowePVrZ0zs+f/7ct29fkEhYGVs6qyR4eHjQ82LYzp49e/JUDkAW2hETEwOmAyKJ/IDt6ac8gKfDbMDtdu/erZq5h/fu3WM10ZUNJNP0WBSDvHHjxjyZK4PC8eHDh6VLl1I9FQMDAzb3UElAyADbRtQACbh586ZQDAoFTT2kmQesJrqSADkaGhqWKFGiUqVKs2bNSkxMFIohe8hCO8LCwug+fMWKFdlzUOXBysqqKFclvW+fvip4GgocPHiQKnbACzP/qzxAssU5jBw5klXvyF+8evWqX79+ZcuWhTisra2ZQ1MSwLYRL+BbkCKfP38+T/fk84qQkBCwjaLctotsXrySADIHe6HNBAYOHHj//n2hGLJHnmkH1GX16tU/cHuVITkQ9oVBcfDw8KBJALq6uiooKoy0b+bMmVRsH5Jl/ld5gE8Eccc4N27c+N69e6p5gsaQJe7cuYNEHJkxJII3LDNWHmrVqlWMw4YNG968eSOUhILw6dOnuXPn0rokfX19ti5JeXBycqpQoQLGuWnTpmFhYdKvuMwz7cCpwVuLcDXR4T2FHWFQHBD4ac4OGOWNGzeUHZwSExM7depENdFtbW2Z/1UeateuTZsamJmZHTlyRIZNDRgUhYMHD1JNdGThLEQpFVZWVjRNvl+/fnFxcUJJKAgvX74cNGgQPCf8mKWlJZvYoTzAXqgyChJjUEnpV1zmmXYg7aYSlkjE2eY6SgUCPxwiaAeGesWKFdJzSdkAZuPg4EBLztjcb6UCkjUwMIC5wjmuXr1aeZkfQ874+vXrtGnTKlWqBIfGaqIrG/xWiAgc8DZKes4SFXXV29tbW1sbv4XEmK1LUh7gx4yNjRGekKzOnTMXhE8ojGyQZ9px6tQpqomOdI1JVKmgh2cITjBUHx+f1NRUoTAUit27d8P/0sNX9oRF2TA3N4e5FitWbPjw4aqvf89ASE9Pb9myJa2wsLe3Zw5NqUAyA9+CoYbyHz58WBnz1T5//rJz5y4bGxv8CiIUm9ihbFAhOESNvn37Sj+9I2+0Awn36NGji3I10ZEcsPvwyoabmxsttvTw8FDq5o0487Bhw2gqCZwC87/KBk3sh2Thi5H5sWW0+YLIyEhWE11l4Nc+lCtXburUqXla+yAlXr9+PXfuXLrzb2RkxObFKxsITBUrVkTUsLCwCAkJkTJC5Y12JCcn8xM7DAwM7BmUDGtra6reoa+vf+jQIeUFp7///htuF/73B26lu7AfDIoGEjKiHZUrVz5z5oyyn6AxZImNGzfS0i1IwcrKSigkBoXCzs5OV1e3ePHiGPDu3bsjOZYySkmPhISEXr160bokRCjk4sJOMCgUGGGKUGCTW7duldKP5Y12XL58GcZJtENbW7ssg5KByESPtCDU2bNnv3379puC7fQfPHr0qGrVqiRZ/KiwHwxKAJVIB9ULCAjIlCyDavHmzZsxY8bQxA7YF1N7FUBLS4uKAjRq1Cg8PFzKKCU9EKE8PT1hU/gVRCgmU2WDj1AYc0QoKad35IF2gJkePXq0Q4cOzRjyA4sWLUpNTVV4fkA4cuRImzZthD/JoBLMmTNHBeWiGQR49uzZjBkzmNrnC7p37x4SEqLwWnn/+9//evfuLfwxBuWjefPms2bNQvoqTYTKA+1gYGBgYGBgYJAHjHYwMDAwMDAwqAiMdjAwMDAwMDCoCIx2MDAwMDAwMKgIjHYwMDAwMDAwqAiMdjAwMDAwMDCoCIx2MDAwMDAwMKgIjHYwMDAwMDAwqAiMdjAwMDAwMDCoCIx2MDAwMDAwMKgIjHYwMDAwMDAwqAiy0I709PSkpKTk5ORXYkjOEeJH8hA/4OXLl+L/FR76HeLHZIkUDsJPxSDNSQiCI18lZ/FFdPsFB/H+430SB8mLojf4E75CY5gsdhJx0Nc/f/4sHH3l4eu3l0+TkuNfvopPfvWUa/GZLeVpcmrCK66lpCamZL5mtleZ7/9pqfgk7XlqRlJaxsvvLTmzpSelpr5ISXuRgk/evMp4k5Lx5lX6m+SM10npGS/SMhJSU58kv3r8MiU+OR1ff5maeUBqxrvXbz+8fffh7dv3b96+y3jzLu0NPnyTkv42LeNdxmt8iL/Sn96mvX6bmvEav5WUmvEis6W/SMXPvX6Vgc8zW9prHPY+84TvM9ub9x9eo737+Ob9x7fvP7398Ondh88fPn7+8Pnzh0+f3n/Efz++/fDhzbv3+N301+/S3+BgHIa/fvn0Ge1dekZKfELyo7hXcU9S45+lPkV7mhr/NOVJ/KsnT1PiM9+nxsdn/vcxDohPS0hIT0jEK3dYfGZ7jO/GfcjIkGbzAgZlA1YWHR0dExPz6NGj+/fv4/3t27fxnoz3IYe0tLS3b9+++Y63Enj37t2HbPD+/XvhR9/x8b8Q/vX73z9xwHnevnmbmpoKz5CYmJggAXwIvwHvBz/zkkMqh4yMDL6f/CWI4/Xr13DpOBJff/bs2ZMnT+Li4jACsbGxcRxoEPAmPj6efuj58+cYqOscMFz0J3wRr/gr+oBOPn36lD7BwfgE3cAlqEDnv/z9OTn1eUJSbMLL2MTkxy+SnyS9evoy5VlyppNKhHfgPEUStbTXcFVJ6ZmvmQ1v0l8nS7aMzPYKLZ17zWycL8v4fkBapg9Ce/nPmdNfpKQ/f5WGlkgNv56c+uxlZkNnnialxKO9eIX2hGuP0Z4nxyVmtlj4l4SXD58lPXya2R6gxb+4H/8i5klizOOEe3EJ0bHPoh/G337w+Nb9uJsxj65HP7h+J+ba7XtXb0VH3bwddeNm1LXrUVFRkXi9eScKn6Ph81t3cECW7Rrfbme262h30O7euBtzK+Y+1x7cvv/wzoNH0Q9jo+MeP0h6+eJNRsa7t28/ffwkg0zzTDvwG2FhYXv27Dl06NCxY8dOnDhx/PhxvDly5Ag+OXz4cHBw8AEO+O/Bgwfx36NHjx77Dhx29Dvw/jAHHLl///6DHPAeX6GD+SP54wk4gM5DZ6DfxSv+i/7gi/TTeEU36A1/GP8tnIReCXxnCNRtuig6M98HnJA/7d69e3ft2rVz5859+/ZRx+hadu/ejSHat3cf/9P0E9SloKAgfAXH0Cjhu3i/Y8eO7du37+CAv+IYfA7HJ4NQZcTf346sOXhyZXCI39HQNcdPrTkRsvpIqP+x0DXHzmw4eWbj/85uQjt5euMJvIZtDgnbEhK29dS5zSFnN4Wc2XgybOvpy0HnIw9diDocfjU4/NqRCLxePnA+fO8ZtKjg8FsnIm+dvHzreMSt4MvX90ZEbb9wcV1oyOLgkGXB5zeGRB64cO14xJ3z129duPbo5r34mIdPYx7G3Y55EHn7fvitu+euRZ+Nirl47UHUzbhbd+PuxDy6fe/BtTv3r9y6ez7qxvGLkQdOX95zKnLvuch9YeG7T10/En4nNDL6XFRM+PUHkbce37yXcDcu8d6ThDtxCTcfPbvxIPH2oxfRcS/vP015lJj65EU6fAIoV1wimfmz27GPr96NvXz70aXbT67FPL/7JOXxi4wXyW+TU55cvXE2YPOJ2b6nFiy96Lf+4rpNYes2XFi78cyqtWdWrzvrv/6s39ozK/z/99eyE76LT69efXHjhvDAwAvrN5718z+1YuXZ5StOL1oUuuDPZ5GRfyt6+00GGQArGz58+OzZs+fPnz9z5swxY8aMGjXqr7/+IjOcMGHC1KlT4fHu3r2LQHvnzh284v29e/fuiwFBGiEWMRtRmX+luEuhF++fcaA3fPAGkl5kJiEA3vMfvniO/2d+QsD5w8PD4WnhbTZu2Lg2YC2wbt269Rw2bty4bds2dBhuhPcnAHwUnE9oaCj6f4FDGAd6f/47Tp06Ra4M5/Hz81uyZMnChQv//PNPvC5atGjBggUzZszAyCxevHjNmjX0i4GBgf3792/LoVu3bnPmzFm2bNny5ctXrlyJv8J34VRLly7FMOJb6Ce6ce7cOQwdmIdQAIpGxtuU4+e3r987c8O+mVsO/7nr2OKgEyv3/8/v8Kl1J85vDrmw7XTErrOXg85d2RsWtT/8+sGL1w6EXzsYfu1QxPXDXAuOuBYccf3IpetHL18/dvn68SvXT0TeOBl1MwQt8kZmi7oZevXWqas3QyNvnrxy48SVG8cv3TiKr4RfC7547VDY1QPnIveegT+K2BUasSM0fHtI+LaTF7ccC9t45NyG4LPrDp8JOHh6zf5Tq/eFrAz63/KgELRle/63ZOfJv7afWLTtxIItx+ZtCp694dCMdQemrTswNWDfH/5BE1buHrts+6glW4b7bhry5/pBc/z7Tl3WY6Jvl9/ndfptaruB41v3HdWy19DmXfs17ditcev2DZv8LGrm3bBjj6Y9hzTH5516N+3Sr3m3Qc27DmzedUCzbpkNbzJbtwHe3Qe2QOsxsGXPga16DWqD1ndw+0G//fLb771GTug5amKPMZN7j5vWd9LMgVPmDFmw+I/g4L23rl2/f/d+0vOkz3n3Y7LQDigo1HHFihVQ/Q0coGR4D5X19/fPfLPab/Xq1XiPT0hTcQAOg/7hw4CAgHUccCT+u4bDqlWr/DjQAfgKjs+0re/H4EM6OYD3ZHUAfR1/538LH9KpAJgBekLv8QYH0BdxBrznz8x/KIbMT+jXAfotnByf4MJhY3j19/PHOfF+yeIl6D8djx/Cn2C6+By/Tp2jvuE8OAzGCVOESeMYOjkOg30u5LCIg6+vL4wWn8M9qY52fPm2ecq6neM37Zq4eefEzXsmb9s9afOeP7bs+iMwaNrWPdO37J4WGDQjcN+sLQfmbDs4b8fh+bsOzd+9b9b/2XvvoDiSLPE/1t1YjdPIe++QRwaBBMiDEEICJAHCCCG899C0w3tvu+nGNO0tjffeg/AgJARCXqOZWX+/3b29uN+reqJPp4iLi+/GhGL+mKeKUlZWVmZWNZXvky9fZgkltFIJvVQVK65OldfnlTeyKpsLq1o41c3syiZ2VQOroj6/vIlV0c6tbedUtRVUdOTVtKRWVjOl0qBioQ9HRRc051X2ipv6K9tHmvsGGrsneoZmhkYeDo1Odd8brO+8V9c9VNU1WNUxXN850to11tk73jMw1jc01j041jEwVNfeJappKpDXZ4gbs+BFVtekSRvzlJ2ltb3ShsHyluG6jumuoUeDk3MD04+6J2Y7RmGb6xp73Dv5/N7Dl2Nzr6cW3jx8Dn2JZ5OzT0YfPB15ONc7OdN2b7qpf6qxb7p1aLZ38sXk4x8eP//z6+/mugfKmcmldp5iZ9+qMHoNPaomKq4mMkEVGqkMZaojoivC6OrAcJl3oMjHX0Wh1ERF1TAiKyl0VWiELDBY7h+o8PEVu3s8bG7559///v5P8It8cIGXGhSnvb399evXr1y5cvbsWQhERERQKBTQrAYGBlevXgU92rcoA/0Dg4ODQ0ND0J0cIQV6/OPj40AkyCIPSJkhRQMos7Ozjx49mntH0JawsLDwBP4tLEAMJIashoeHoRSADCCD9vZ20NbADcAQQEXAQL6+vgBJsA8KCgohJTw8HJgAWw9ABGg94uLiYkmBAFwFrQ20SNCYwB4aH2yXsOFCVoB4uArSR0VFMRiMsLCw4OBgyDmMFHgO/v7+QGNQOsTDIZ1Oh/3u3buXkrJp06YbN264ubpBrRDa4FofHx9PUry8vOAqyBnqBmAE/aj3f4CfWn744yuOLCokzSw0/Qo91yqu0DGR65zEdU0v9c4Xh3LldF55tKgqUV6XrmrMrmjOha2yJa+6jVXTXljTxq5uZde0curaixo6ShugU9UpaOoUNneJWrrFLd2Sli5Jc5cY9q3dxNbSJWruEjZ3Cxq7yho6S+s6imvauVVthRUt0OTlKBqyoFWS16dJ61LFNUmCqjheRUypOqpIyeDIqWwZpUAali8JKZCG5kmCs0X+mUKfNIFXqsAjhe+WUHI3tsghmmMXVWhDL7gRkXstNOtyYJqRb9IZj7hTLlEnHGlHrIP3mnvvNHXdes5uve61ldrG3+47/dX2o59t1Pp45Ybfff7lrz/65FfL1v7b1kOf7jz2+YY9n2w58Pn2I19s1f58y6HPthz6fOshCMC2ZOuhL7Yf/mrH4a93Hf5mt/a3e44u33ts5WG99brnthsY7zS8vOOs2a4L5lpGlvtMrQ9fs9Nx87ku5Bf1dfYMDww/W3j69//3duxfwQ54CeEPGlQjoXpJlQxUoVH2oHpBlSMfkGFC0yN2QBj+4uEShAPUx2ySQgg9TwqbBALNtZhGAzGo/jGNhgOQEjCNJgfU6AUkxLBIrEGyYZNMg6WjvFc01gf+x7IwDSbAopEqYI91YC1SBYfEDqgGFAS3qYlkk8CUTzIQ9gCQLeCwkMQaSA9NAzxP2EOCLFLwQUHv50Nih4BRLAsvU9CEMqpQQROr6CIFTSCjlsnowBw8UQRHRi9WxwoqEsSVSZLqNEV1sqI8TiKmloioxXImXxknqEyTVGVI63JVDfnlQBvNnOomDpCHui5X0ZgLr7myIUXemKSqjpIpwktl4cUVsZKmgsoBVftoY89Uz/B03+hE78hk39Bk78BM3/Bk573+OmCR3sG6rqH6zrHWnrGO7pHOnvH+wfGh4UngksGxsc7+gcrmdl5lUy70HJQN2eU16dKaDMhW1V5S0S2sGapsmeoYfNA7+rB3/GHX2Gz7KLER5DH+tH/m+b3ZF2Pzr+8/eTXz+On4zON7U/P90w87R2dah+43Dcy0DM20Dz/qmXo5ufD9wos/v3nzeGC4PDazyNGP5+KnDIkoj6BXRwJ2JKvCY+QhDHVEVGUYrTqEUh1GKw+jVTEj66Nj6qjM6hBGRURMJTOhkhlfQYuRhdNnO3t+wY6fgwiFQldX14sXL544ceLAgQM7duzQ19e3s7O7efMmBHR0dIBFSkpKQP33dPf09vT29/UjebyLHWOkTExMTJIyQQqACGIHUIiGPEAgAIdwClPChZBDZ2dnVVVVWVkZtAnYAgATgKqGZgEaWxqNBrof9DoBBMEhoN0RC0D9M5nMBFKio6PppFCpVEgPAAE4wiQFtD7wB+QGAWoEFdJAANJASkgDrRl2lrD1gwA0RIgvUDq0TtAoQRrgEigXroqJiQH02bx585dffvn111/v3LnT0dERniGgBtQK6gnkAfWEzCNIAUKCs3fu3IGynjx58v4P8FPL979/nisI900475NwJjDFODTDLDzjakSmBSPHOp5zN73MJ0cYyJFH8CtjxTXJ0tpURX0GNExqAj7yK5vzK5vyq5rZta3c+vbSxo4yoAoCOHokxEaghpSADzLQ2gMbyR89QCSipi5BQ1dZXXtJbVtRTRt0sNiVLQXq5jxVY46yIUtRnymrTRNXJwsqE3jqmNLyqBJVZJGSCQjCVTIK5bQCaXi+JDRXHJIjDsoWBaQLfFLKPJJKXUn+uB3JtqXn3aBkXwtKN/ZNPusZr+8ao+tIP2ITsv9mwJ6rHtsvOGw4abnqqMm3WgZfbjvy6YbdHy1b/dtPPvvVp0t+vWbrR1sPAYt8unn/ZwR2HP58M4kdJHks2Xb4i22Hv9yp/fUu7aV7jizTOrZiv85q7VMb9M5tNzTZbWi648yVnefNtS5YaBldB+zQvmZ7wifQoVwpHeobHBsae/70+d///kGsHSqVKo4UtHloFDlqXPJvtxBi4F0F1ECli4gApzQmB43GRRSAQ0gMl3BIiwgKam7WOwyBKIAgApcQ2MEm6ARywEiSfAjBMJYFZxEFsHSsDFYAYQXL0gheosGOfNIGowELyAp7DHizmuoVkpyEfIOn8DYxARREYEd6BrzSaHvE2ykgHwK+2Jitpv5w4QfGDj69SBJWqqALlQyRgiFSMUSEnYMO2MGT0EoF4YUiCkcRyatMklanyWszVTXpyopEiZhRLKAUiqhcKbNEmcBXJwurMqU1OfLaXHlDYUUjp6KOpazOBhSQ1abKa+JkFQyxIpynjhbVZyraims7pY3DDT3jnQMzQ+NT/SPTA6OTfYMTPQPTvYMTnQPDbcSpgbr2nqqmkbbuse6+ke6+iaGRieHR6bGJBxPT04MjY219AxVtnaXVrayK5jx1Xaa8Jk1anyVvYinbS4FpmkYbuydb+++3D892jj3qGCcMHp0Ediz0TT0bfPB89NHL6ccv788/G3/4eGj6Uc/4bNcY0MZMyyBgByDIwsDMy6kn3z95+adXr+d6BiriM0s9w0q9Q8ShNHkEQ0mNVNPjlJRoeTBNGRSh8Akq9w1S+QQr/MMUIRFqCr08mKbwo6rCY+sTshtT8qrjMyoT0hcGR/75/26c/EV+chEIBC4uLhYWFiYmJoaGhhs2bPjmm2/WrFmzf//+48ePA4sYGxsDBLS0tHSToxN9fX3vMgcIMgfI+Pg4AgfwxBQpcBYS9/f39/b2dnV1wR7CkElbWxuOBSuVSolEIhKJoKGARgAaVdDToaGhCASADsAHGIA9KH5ocoEwIhcFUANgAi6E9gTOoh0C1HxgYCDsw8PCQYBOgBUgGbQwsAf+wAGUhPgECKPZFW0e0PhgoweCJljClJueDrVC+y5aU6Dc27dvr127dsmSJV988cWxY8eANiKZkUwGE/UCFBcVSYAOVA9uBygEuMTBwQHu4sNgR0ZJkHuUvmuUrmfsKZ94Q+84A7+ks0Fpl6g5ltFs+/gip7QyzwJpCEdBKymPElUnSmtTZHVpsrp0gANlfXZ5Q15FY0FNa1F9G6+hvayx478NHmjkIJjjLXZI24A8SChp7hY3dYkaOvh17aW1AB/tRdVt3MoWdkVzQXlTvqoxV1mfJa1NF1eniqqSRdVJwqokQJAydVxpeUyxMqpIGclV0IE/WLKIfElYjigoQ+Cfxvcm4IPnFlfkFMW2o+fdDM8yCybI44xngr5L9AkH6mHbsAM3AvZccd1y1nadnsVKbaOlu08u2XLwkzVb/+3Lpb/5t49+teSb36zd/vGmvZ9t0Pp06+ElBHAc/J92Du2vdh0B5iCMHAd01x4z3Hzqws7TJrtOm+4E7Dh7dTdgB2zG1/cT2HFLNyDIWSLkN9c3dbZ2zs/O/+1vf3v/N/i/5F/BDnhbkhKT4M8L/mTh7xWJQUMDCAFwCAxRXFzMIlED0eQd4wQhqHHzF+FAgx15i6KBEsSCgnzCdIGEgXCAueFADaZHGli8niAPNsk0SA9IAFgcVhjrjNfmkZVHgNC8fhiDd6ShK3xLQYhaLeIRZ3FQ5l3EwXIxBl9sDZcUktiE6fFUHvmg8I6w0A+MHWU0rji0REoplVJ5ABwKhkBG50sJ5igRRnAFFK4ooljKKFPFitSJkup0eU2GvCJFLI8tFTO4IlqhBMA9tkyVwK/OkFRnSqqyxLXwrhWoanKl6hRhebyoIlZSESmWR/BUTEFzflWvpLlf3TpQ2z7eMTDZOzQ1ODLaMzDRNzTeOzDROzDa1TPW3T8Gge7evtrGZnl5X0Nzf1vnYFfvSN/g6ODQ+Mjw/cmJ6eHRid6h0fb+/oq2LmFdexF0V9TVKdLKRHjBJc1sdbeoblDdOlLTOVHfN9Ny70Hb8MOOkbmusfme8ce9E08Hpp+PPgTmeP1g4fnU/JPhB3O9E/N9U/O9k7MdIw/a7s10jDzqm3o+Mff9kxd/ePZspqmlKiFNGREto0TKqFHq6HgllaEE+AinKwLClL7BMnc/iYu34Lar8K6nwDNA5Bcq8acIfcJlIZHVMWk18elVMSl1SZnP7o398x8f0F/4F/lfBCAAWh40KoCy19LS+uSTTz7++OM9e/YAc+jq6urr64O+rK6ubm9vB3TQYMfw8LDG1DG+KBhGAwbkDE2lUCjk8/nYBysrKwPCgDC83UQbkkXYFRAFoC3FIQ9gAgAIpIRscjwXdT+wBah8xAhQ55AMdTykxAthD3cB1yK4BJMCATiE9FAQZAIkkUGKpoeDJlgNjkAqaIWwKeZyiI4ipIS65SyOF0NjBU/D3Nx82bJlgB2fffaZgYEBxEACqB5WDOoAl0CJTCYTEARJKCAgACrw4sWL93+An1re/Pgsme3jSDnmQDnkRD/iwjwGm3uMnle8QUDKeVDb9Pwb0exb8VzH5BLXTL4vW07hKunFSmapKhogQFRFdKoUtZnqxoKqpsLqZk5tS0l9q4Y/RM1dYgI4SLMHCR+E8QM2wI53yINX214M4FLdyoGtqqWwspmlbsxX1uco6rLldVmyukzYpDXp4qoUYUVimRr4I55XHluiigb+4CjoLGlEnjg0SxgI8JHO903heSUUu8QUOjDyrShZV4PSjX2Sz7rG6jkyjthHHLIK1jLz2HreYf2pG6uPmy7ff+ar7cc/36D18fJ1v/t0ya9/99Gvvl7+u/U7P1u785PNBwA1AEo+33oQmePLnYe/3qX9zZ6jy/cdX3VId53O6a36RrsMTXYZXt5x2pTYADsuWGhdtNh76caBy1aHzW1OhoR6ggrkFBSWcEoG+gb++pe/vv8b/F/yr2AHvIHwBwR/4vBHnEv6NGjGUFgkc6AgZ2BHv5AcZwHRkAdhHyAVNiZmkSgAZ9mkZUJDA0gJeIiZIxNo8IW1aGzA9HmLBg+MxMsRUPByTM9+O5JCCFYDL8E8uaQzCibQHMIN4h3hu4qggEXgXSCmQADisbHAyiBVgGioAsvFWuUt0lIBiTv4fLBWsH/16tWHxI7SiEJhEFcUWiyllSoYfCVDoGQKFJECObNMEM4pC2Xzw7iC8CIRrVgeU1aZQhgwqtMk6iSBIqZUwuTCJo8uVcbDKVEFcEYSXxUP75S4JkWqjhWqIoWQmzpGVBEvaSmo6pe3jdb1jjb3jrX3T/YMTfYPjfb0D3V0j/T0DQNYdPf1trTd6+4d6e0baGvvqKiuEohaKqs6Gxq7m1tgG+jsvNfXMzlyb2rk3sTQ0OTQvdGu/qH69j5VU5ewqS5LpYgqk1GLquKFLQXqXlHjkKptuKJzvKZ3qqF/pvXeXOfY4+7xxz2AHfdfjM2+fvjk+/kXr2dfPJ98/HhoZmHw/uO+KRI7hu+3Dz/oHH0yMvPm0ZPfzz+eqK6piU+pjUyopEUpwqhqKl0dTqkIp5YHUxRegUoPf4Wbn9TFW+TkLnL2ELj7Cr2DpQEUsW+4PIRRTo+VURiykAg1jfmkf/AXa8fPQQAj4JWEpgwaNFCTe/fu/ZyULVu2HDx48NChQ/v377e2tgY9Cu8jNAI8Hk8gEKBLuEKh0PjCo485+ptDPCSAdx90cFRUVExMDPIBFIEtJ+h4hIbIyEg8q7EcY/sABZWWlsIemyZsEiHx3bt3b9265evrCxXWtDM4JpJJGiTQbwxvByIRJtBugWfRYoGuY5AtnEKCgb2GfrAxzCMNtPmLlmMkG4iBO7p06dJXX30FzAGIZmxsDLcA9wXwAdwGGWK7jZdAQZAzFAosArcGDdr7P8BPLd/98DQ2x83Kf991v523QvbaUw44RBxwpB12pGu7Rp/wTToTnG4UmmkSmmEC+puRZx3PdUoqdk3neWfx/XOFwSwJhSOllypjhBXJ0soMRU12RX1BTROnprmotrWkvq2sqVOg8fMg3DvIAEkeBHY094gbu4QNHYK6Nl5NS1FNS3FtWzEx7AII0sKtai6saGKrm1iqhjxVQy5SiKwGemnpkqo0UWUqFMpXJ5SWxxUrojkyBktCyxOFZwtDMvmBaWW+icWuUWx7au71kIzLvsnnADvuMI/aRxy8Gbj7ivvWCw7r9W+u1jFbceji0t0nv9h88NM12wiDx29/96uPPvn1ivUfr9n2yaa9S7Yd+oIkjyXbDn6x4/BXyBz7dVYfPrle5wwwx059450GJtsNL28H5jhjtvPctd0XLfcaXd936cZBk+uHr986x6CFZaVmZSRnpiWmt7W0//lPf37/N/i/5F/BjpqaGk13H1WpxlCBNIBa810liqzwrk5FREDlyiYpBJUxa3FQAwXTo24uILHgLYmTgIKJ302PmWNAE8aXueB/4gVqfTylERZZVcgf33msm+bu2CSCkIVCVoQraD5p58A9eS1BHlDVTKI7kYlWDTRjYBq8HU0d2Iv4hc0KFocJ8H4/MHYUU9iAHaS1oxTIA/ZAHqookTJSIIzglAYXlAaxSoNZAkqhlFmiihNUJosqkoXliXxFbJk8qljC5MiiS2TRxXJiXyJmcIWhHGl4STlDqKILlHS+OlrYkF3eXlw3qO4ca+qbaB8Y6xgY7x4c6x4Y7ekbau8caOsY7OjqbWnva+tor2voaW4daO/sqKmrlykkhdxyvrBeqWoqr2yuqGqtqelpbR7q6hwb6J0eG569P/VwcmLm3r3Jrr7R+t5OflNlkkQcwhIHs6riBK2sqj5h86CsbbSie6K2d7px4GHr8KP20bnO8YWeqecjD14/AOx4+Wb+1euHz4E8no08ejp4f65r7GH7CDDHw66xpyMz383O//Do0VhFZWV0QkU4QxkULvcPkvsFlAeHqIKC5V7+EmcvqbOXzM1X5OIt8/CVegFz+Il8g2WBFKlfmCKYWk6LlIRRhH4BYt+A+c7uX3w7fg4CbzcoTv1T+oaGhkePHl21atUXpCxdunTFihUrV66E/datW48fP3769Onz58+DxjU1Nb169aqlpeWNGzdu3rxpTYoNKbdIgcPr16+bmZmZkgKJr127ZmFhAYnhrK2tLewhsRUpdnZ2Tk5Obm5u7u7uHh4esIewt7c3OnOghIeHg14PCQ6B9FAByC0iIgJigJMgmZeXF6YPCAhAb9CgoCC0c4SFhcEhROKwCySAZBADp9AIAZnj5eimCgVBPOzhcgzgnk6noxEF9pDSwMDgm2+++eijj5YsWQI3CNlC/pgzVAnwBSADIQaIJIIUuBYq/Pz58/d/gJ9aXr95Qku+Y+a608x1y3XfnaCSrYL32IRq2VL2OzKAPHTcY/W8EvR9Eg19Ek8HpFwMyzCLyLKg51pF5dvFsBzjC50TC13Ti31ZoohSRaxInSyrylTV5pbX51c0sKqaOHVtpQ0d/MYOQWOHkDB+dL4dfCHIo0fS3Ctt6hLDqYZ2fl0Lr66VB/xR1w770tq20prWkuqWoqpmTkUjW93AUjcWlDcUqOrzlHW5itoceU2WlLASpwN88FQJJYo46N6xJYxcYXgWPyi9zD+51DOm8DY153pQuol34mmXGN3b9CO3wvab++4wcdl8zn4dYoe28bdaBl9tPfLZul0ffbPyt//28a9+/etfLfnmd6s2f7J+56eb9y/ZenDJlgOwJ7Bjz5FlpDPHRmCOkxe3nTTaesp4K2AHyRy7gDnOW+y5ANhhuc/I8oCxhbaLu01aUgo7h8PKLszPLOju6PnLn//y/m/wf8m/gh21tbUao0LeordEESlcchgCdSdGaswbqFNR0SJG5JLjGhqdjQqYgANyIgzGI8polHfBogcr2lE0FcAiUJ1jJAoWoYnEQ6xJwSKaoJUCC8on7RZo20AuQVrCs5iYvBBuh61x/Hy3tm/NIdmEOURDZlhDFCyaTVpHMH84m006mWtyw34M7D/wIAs7OJcfWAjYIaGWiKklwrAiCaVEESmQMXgiKrcshFXsn8fxyS4OyBFQ2FJmsTKOp4grlccSkCGPKpIyOVImVxjBFoQXCCmFkJ7vXyD0L5SF8mThPFWkoD5D2cVvHKzoGG3pnewdnOodGusZHOnuH2rv6m9t621q6mls7Kitb1RXtlbV1MiUdYryBkVFlUBWXiIsSsspTs/lpGSLC4pVpaJyvqhWrmwor2ivrRnp7X44PfFwenJ2anx2fPR+3/C9mu42bnVFDF8SWqigFdckSjvYNf2C5mFlxwgQT1X3ZH3//cahB8335jvGnw5Ov5x89N3Dp989evH64YtX95++GJ9/NvRgoXdqrmt8rndyvn/62fjsq9nHb2YfTVbXqqPjhb4hUr/Q8sBwuXcAwIfUx1/k7ClwdBPf9RS7+5a5ecsCgiT+AXxff2FAiDyUKgsIha2cxlAwGJLQUHFg4FxX1y/Y8XMQeMuOHjm6Y8eOnTt3btiwASADO/EgEPj0009h//nnn4OWRQpZvXr12rVr169fv3Hjxs2kbNmyZeuWrRqBw02bNkFW69atg5RrSFm9KJowBODsukVZTwpctZGUTYsC+W/fvh0QJzws3PmuM9AMsMudO3fQpdTFxeXcuXMHDhzYTQrcwq5du/aQoqWltW/fvr2k7CPl4AHCeINy+PBhbW1tDGgOjxw5cmxRALN0dHQwgHtgMkiD8ehPCo/o66+/hvwh5gQpurq6p06dAigxJAVATV9f/+TJkxjj7Ow8Ozv7/g/wU8urN0+oiU4mjjsu2m+44rLlqvtWc+8dlv47rYO1boXts484eJt6+A5d2yVKxzVa1z32pHfCab+kc/4pF4NSL4WkXaFkWkRkXmfm3EriuOUKQzgyepEsqlgWU6ZKFFWkAhmoGwqAGwgzRktJHeH8wW/qFJKbiACOLlF9h6C+nd/QLmhoE9S38SFc3wH7Mkhc21Ja01Ja3Vxc1cStbOLivqKhsLyeparLV9XlwR4QRFqdJaxILVMllSriubIolpiWIwjJ4PknFbtHsewp2eb+qRc9EwwI3w6atnXI3qve242cNp62XXvq5qrjZssBO/YafrX96Ofrd320bO1vP13y69/89lcAH9+u+WjN1k/Wbv90o9YSgI8tBwA7lu7TWX1Ef5Puue2nLm7XM9qiZ7T51KUtBqSp4yxp6jhnvge289f2nL+619RCNzTMPycjl5NbzM0r4eQX9/V8qEGWhoYG1NOgO1G7oxJF8gCdjZCBWhZ1Ns41RQIoWPR+eMsEpKkC+/c5pOSS/g+oy9mkWQIT4LUscpZs4eLwRMGinylmi/ng5ZriMExiEiGYCcZgbigYWUCaWCCQTdo8EQsKFkEB7468QTayBeaGd4qYhTVB5oB7wbOaJ4aiicFyM8nhWyAPAllycghHdpJCPvAE2vyAzGLvPEFgoTCEKwrj8kM4/JBCYA4Zo1RM4wpC2byA/ELPDLZHerFftiCcJWFypVFFsuhiWWSRlMGRMAoFlPzSwOzSwFx+UEGpf16ZT57Ar1AewVdFCmvT5V2l9YOqjrHmvsnegamBwYne/pGevsGOrs66huaK6kZleZ1Urirhiwq40sJifg67LJtdmJDFicvipRRkRiSkh8dGuocm+NIzKQms+Kzi9AJediEkrhbL+trahvv7pkbvPZgYfTgyOtExMKBsaymorIgTymmlclpJXbK8k1M3IG65p2ofq+qZqO6dqOmbrOmbbRmZ7xp7dm/69czj1zNPXkwvPJ+Yez46+2xo5nHPxFzn2FzPxOPB+0/HZ188nP/+8cJcZ3d1UnqZd7AiiFYZFqkIoEgDwyX+YWLPAImbr9I3RBVKU4TTKxmR8pBQUVCwNIyiZkTJgikCT38llV6REF8RH1MRG7Mw+Msgy89CmpqaIiMjg4ODPTw8PD09r169Cmzx8ccfA2oAFiBYgIDuPH/+/MWLF42NjY2MjC5dunTlyhWNGQPNHlZWVhCAGFNTU0gGQAB6F9QtaGLQx6CwQbuD+kc+ANABRgHCANpAQHmXQgBKVq1aBTWBPWCHg4ODm5vbbYfbPj4+DAYDJ8f6+/vb29tDlfbv3w+ZQEoAo+XLly8j5VtSli5dCsAEcAABjNGI5tQ378jXpGDgq6+++pKUL774YsmiQDzUCmjst7/97W9+8xvYA3wgnGECHKJCwcPPSIEAPISxsbH3f4CfWl599yQi4e4l+x1nrNYZOWy6dGfzZeetV923m3vtuOa1/brfLqtArRsBu62CtOzCD9pHHHKkH3GO0nGL1YPNM07fJ+Gsb8L54JTLwB/RBfYJHOdEjntKkXdmWWC+kMKVMstUCeLKNHlNNlCCuoFd1cSpbi4CkgCeqG3lwVbdXALhOgCONkFdq4DYt/GJUy282ubSmqYSciuuIZPVNBdXNxZV1nMr6gsrGjiwQZ4AH7LqbKE6rUyZVCKPg85criA0tcQnttCJlnszJNMUTR1OkcftIg7dDNK66rX94p2NhrfW6F1fedR0+cHz32jpf7ntyOcb9ny8esu/fbXsN598Rkym/XLZ79Zs/XT1lk/X7fh8w+4lm/Z+sePgtwdOrD1+ZrPuhS0nSebQYIch6dhx5srOM2a7Tl/ZYXB5x3nTA453LOOio/Oy8vMy87JSs7PTcj+ctQNeVCQMDjmgkEN6G6Ee5ZJjH6jmEQKwf5+XS/Tgc0iLheZsNvpR5r61fLxlDiAG0hUUiQGVPRIMe9FSwlo0V+S946yKhxqw0AQwDHDDIuV/RC4SA94O4kUBWShWDxNg9QpIHMHxnbeYUkCcYpFGDvRcwXzwCeCt5ee9teUgarDfcUZZhA/iieG4KYEpi0CGRX9ga0eef3r+3bRC1wyue1aJT16Jf15JALxrHGAOMb1IEMICmOB6ZbDdUzke6SX+2SIqG8hDEVMqiyoSUVkiGosfBldl8/xyS31zuF6ZpZ654qCiylhZY25FZ2ntoLxluKZzoqN/sq9/tLtnpLNrqKOjq6GhTqZSFYsk+UW81Nw8elJ6SFRaSGRKMDMlOCrSNYR+2z/FL5LuGBB+yzvkhrunkZ23yW2KrR/TOSzag5oYwMimJ8gLebVyVW9r69hA//TQvameodH63h5hU322sjxGIIsorooRteZW9gkahys6xmv7xqv7JmCr7Jmu6Z9pHpzvG3s9Pf/64dMXU/NPxh48HXnwZGh6rmf8YfvIbOfY4/7pp6MPnk3P/rDw9MXIeFM2S+wXrgqiq8MiyylRanqsCjYKszyAUh3GrItOqo9Pq42OV4dTKyOoVXRmVXSsnMIs9QxUMSIbMtLqM9IbsnOfjU3880MuQfuL/C/y5MmTvr6+rq4utVrd2NgIzQJ060FfgrrV0tLCHjx09319fXHmCEhUVBQofnTUyCbnnWKDgFbYXHJtnnRy0a14UgARmEwmlUoNCAhwd3d3JuX27duAKYAsgCm47palpaW1tTWyCwANxoDcuXPH29vbzs4OjRw4vSUmJgb2Xl5et27dgpRXSDExMQEeAuK5cOHCmTNn9PX19fT04Bag/rDX2CFOkwIBSIDWCzgLXAV3qvGihUO4Cq0ggDV79uw5dOgQWkQOHjwI1PK73/3u17/+NWAHwBNQFCQ4QAokRp8YnAcEOcBjhEi4Fu5uZmbm/R/gp5aXr58EMm31zdfrXVt52mr9Gev1Z2+tN7q92fjOZmNHoJBNxnc2XXbectlly1WPbRY+O24G7rELP+BABf7Qdo485hKl48LU8Yoz9E08H5hyKTTdLCzDnJptFZVvH8d2Si7yyCoLyOGHsEQRJfIYoTpFUpUprcySV+Uoa/JUdQXldezyOlZFQyGwRW0zj9haeDUkbQBz1DaVViN2NBKHda1ltc0lgB1VDUWVDdyqxiKSPArV9WxFTZ6kMlOgSgbsYIvpmbygBI47I9cmOM3UJ/msCzqT0g5bh+6z8Nt52W3L+dvr9a1WnzBfoW2ybN/pr3frfbn96JLNBz7bsOeT1Vs/Wrbuo29Xf7Ryw6frtn++cc+STXu+2Kz15fb9X+/RXnZIb+2x0xt1zm3UvbAJyOOU8RbADn2Trfom2wwvEx4eEDhlvPWU0Q5Lq3OR9IgSdgm/WFjK4RWxisuKygb7B//ylw+CHfByolUD9qjpNRQCyjJ70fs6IyMDySP/nVEPHEBB5YrYoWEOzArVLYe0nWiAQ2MqQFwAfY+IU8j+75muGo2ObJG/aPbQsA5WQ6PREQWwRCyucNHxQhPADDXAVECuOFJEenuBcEhDCFKLxluWu+h0ApVE2MJCMbd3c8bEiCn4NN5y2CJLQf4f2LeDHZxT4JyefyeV7ZxZ5J5T5JNbElBQFsIShLOFEYW8oNxinyy2R2q+S1KBazLXJ5NPKZBEcgk3DiZHQC0Q0tiC0HxgDp5PbrF3VpFvpiiEWxktacqt7hI0DajaBtStQ7XtI63dABwDLW39za1ddfV1UoUkv6Q4ITc7PCHBg0K/5RNq4RZ+wzPI3DXY0i3wqovbGetQc3cf49uup62dT1k6HDNzOGp296Sli6GV29lbvqaOodYeyf5MVnyGjMNrqagZaOkYbu0ZaegZUra3F9XUpskrokVVMeLGdFUPr36ksnOirn+qYWCqfmCiuneqto/Ajv7xV9Pzb2afvZpZeDE1B9vzidknwzPElJa+6afDD19Ozz2bevhmbuH1xP3WgmKhT6jcP0IZTC+nRFdFJVXFJAN/KLxDK4JoNZGJNTEpFYyYyghGLY1RHUFT0xjltBhpCKMqKq4uMaEqLq4mOf3Z6Dhgx4f6aX+R/1UWFha6u7tbWlpw1WCBQAAcsHbt2hUrVuzevRvHF/bu3QsKHmdn4OxTEFy4CN9ifPexZSA6JItNkKaJQ2c4nOsB3ECj0UJDQ9F7A5DC09MT9oAUVHLJDQqFAmFIgMuDAl7A2bt370Id0P0CckAvVJCwsDBHR0cnJ6fg4OCIiAi4HPaQs6urK8QDrNja2trY2Dg4OEAOLi4uwD2QDy7n5UeKPym4FBi6g6DPx11S3NzcIAcgISgCDmEPz2fz5s0ff/wxkAfwGYARFAQUBVSEAcgcKokmGcgNsgoMDITMP4xvB2BHANPupPm641eW65gtP3FthZ75qlPX1xharTtjte6s9boz1uvO2aw7b7ve+PZGU5ct1zy2X/fdeSNgt3WQln34QUfqYSf6UZeoE65Rum7RJz1i9T3jCQQJSDIOTjUNzzSn51jTs62j8m+nFHnnCcOACdgiBlcSWSqP4yuThOVpYnWGrDJbXccGjKhuLIatigALLuIFbBhJ8EdzCdAGaergqus5sBHUUstS1uRLK7NF5WllioRCMTO7LDS5yCsyzz4k9YpX/BnXaD1H5lF76iGbsH2W/jtN3bdccNxw+tZaPctVR68sP3h+qZb+17t1v9px7Itt2sS8la0Hv9h28MvtB7/edejbHYeWbj/w9U5ibGXFoZNrjhquP35m47GzG46f3aB7cRMBHJe2nDTe/BY+yLDexc0nL243MdeNoARxC4oUIpWYJ5UKFDKhQi5WjNwb/etfP8ggC2IH+l0i48O7yufzIRKoIp0UJA9U2Kh00VqA+h4JQMMZiAhoAECMwPxRhS8q6beeELiHQjWGB5JM3o7mIChogAPVuSag8Z/IXxy+wdYB61a46CCC1WAtOni+W1VN9RB08PaROTAxh2Qv8no2lJyR8T8eAnuRnArJqT04x7hw0XkFrR1IJMhVHxQ7/vO/iqgsrk8exyOn2DufH8DhB3MEoVx+WKEgvJAXks8LzuP6ZOS5JOa7JrE9U7m+mWXh+UI6S0BnC+lsAY0liGDxg/NLvLKLvbJL/HNEtMLqVHlLfnUnr6lH3jJY3dFf3dpX3dxf19Rb29BeUdMgr1Bx+LykvKzg+BTvSLqtX5DpXW/DWx561l4Gdq561m4nrd1PWjtpX3PXs7p7zMLhoKmNlpHtIRO7w5dvHTS+feSyw5HLzictPS/YBlm4RrmEpARFlqTmV5ZJWuRVnYr6PllLD7+pJb+qJkmqjhZUJ0o6uTUj6s77TQOEr2jb8P2WwYfEYh7DjwcnX03P/TD3/A3x9YOF17NPic8gzDx5PjH/fAwQZB4OX83Mv3m08GZ6trNEyPcLE3oFy/0pQB4qSnQ5LU7qEypy8lb4hpVHRKtoMbBV0qNraIzKMIqaQq1hxlUx4uqi4tRUqjIsXB0Z/WRo+JdBlp+DjI2NQdsFLzjyBLyhoDtBra5cuXLbtm3o9wD8AdoUGjRN9wZaEpwPguOhOJcEjRyAI5BVTEwMwAE6ZuKaXehimZiYCCSBE1zRxxOdMUE3oz8mCK7UDnuc7QIq3NnZWQMBkBjgANf9hHKhUEgJCQAsQN8DuEC5kCekBESASHt7ewACdFkF1IA9wAccQm5AM5AeLkfjDWFozs2FbKEmgCaQBqEE8oFnAjHo9Hr16tVNmzahBwwE4C7iyXXlISuc3wuPAp6SgBRoynIWp8PAc/7uu+/e/wF+ann53ZOgKHv96xt0zVecuLpCBzazFTpXlp8wW3HCbPkpi9UGlmv0LVefvrnurM16o9ubYLt0d5OZx1YLr+2WPjtvBuy2DdtvRyHGX25TtWG7wzh6l6njzDzhEWPgnXDWL/GCf/LF4LQr1ByrGPadONbduIK7SRz3zNKAXH54gYDKEUUWS+ME5amy6mxlbV55PQs2IAlFTb6ypqC8lo2buq4QNlUNS1XDhg1OyavyZZV5kopsoSqtRJZQLI0tFDEI5uB6MnNtg5Ive8YY3GUcd6QftSOYY//1gN1XPLYa3dl45tY6/RvoTLrswNmle/W/2X3iq13Hv9qt842W7lLY9p9cdkh/1aGTq/efWKl1dBnstQ3WHTtD0MbxsxuPn9t44vxGvYubEDj0jDYRGwCH8WaCRYy2X71hEBjoVcwulgkUEr4M4KOUw+MXC0Q88b2B4Q/n24FcD39VZWVl8MeEAVCT8BeWTk4xzyZdKXMXR1JQ6aJex/cWlTqX8z9cUJEqULUjT+STvg4sckaZZhSjaHEuK4Y5xBohpZoPmmBNCknEKVh0X8U65C66jOQtjtRgGgxrKoaRmvj3EuApvLVC9ts1S1gkS7FJd1QNweSS64Bh0fjuaXJDcsJb4C6OEL1NtuiFApl8YOwoZrKLAvNLAgtKA1llIYXC8CIRpYgfxuaHs4qDs4sDs4r8Mgs8klleqYW+6YU+adzATIjnheWKaGwhjV0anFvim8N1zyj2yRFROTWZsrYiYuGcTmFdt7Kxv7a9v7a1q7y6Q65qFkrreGJhOjs3NCHBiUK56h1+xcvvvKO77g3nQ9ecD1q4HLZ0PXrD7dhNlyOWdw6aOR64Yr/XxHa3kc2ui7YHjG0PGtsdNL5z7Iqzrrm74U3vC7aBZndptj5Rd0PSgqJKU3Lk+SWVbFFLSWV3WWNncX1TdnlFrKgqTtTOqhxRdc60DM71js33TywMTj25Nz3fPz7XP/Zyau73j1/+OP+C2BZefP/4+evZJy+nH7+YnHs28ei7BwtvHjwG7Pjx0cJIZa2SmSD0CRV7hch8w6VBVHkoQ+IdIrzrLfMNVUVES0MZshCGKpxZHhquDAqqotKqmdGVtKgqKkMWGKiOiKhNSHg6/At2/CyktbUV9C4usQXqFtgCCGDHjh3Lly9fv3494Yl58ODevXvNzc1xyU7sG8A+NTUV1C2o2FyyywR7uBayAtUL+UBuoKddXV1Bx3uTEhgQCIoZZ7SSa5cTi5EDZGBTCc0mvPvYf+PxeNAsAE9AnpAYlD0OvsDe1tYWcAHntuDao6mk0Ol0IINbNreAP+Be4BANHnAtUXRg4FvDhp+/h7sHUAjkgzYSIBVcCAQHeSGrSPLDNFhzBBc7OztIjHcBN3XhwoW1a9d+/vnnn376qba2No1G00zQRaMO2QyWSKVSiUQCPSs4hU26QqH4AIujA3YER9kb3gDsWHni2grYdK+R/GG2nLR/rDhuCgiy8pTFGr1rq09brTe4vua01Rojhw2X7mw0cd58xW2LufeO6/67bwZp2YTsswnZa01s+26F7HekHnGiHyf4I07fO+GMX9KFgORLgckmAARh6eaMbJuYgjtxBS7JhV7pRf5ZpSFsEaNEEcdXJ/PLk/nKZIEyVazOkFRkicszpepsWUWOrDJXWpEjrcgVl2eJVBlCZTpfkVosjWcJmDml4VklIenFAclcL2aOLZQC0OMcqeNIP+JA075FMoeZ5zbju5vO22/Qv75Gx3TFUePlh85/u99w6d6TS7VOfKN1Amhj+UH9FQcI5lihbbha22DN4VOrAT60DdYeO7OeZI4NBHNc2KR7cSNuyBwniA0OCTvHlesGoaH++Vn5Mr5CSjJHXkYeK5udn1XAymF1tnf9+U9/ev83+L/kX8GO6upq+DOCvydcCQdeEtSgLLITgGvR4LuUt7iyFipdjVJHLU6YDbhvJ7+gGkadjSlReWtsA4gdmJJLLoEKryj8HZeXl1dWVjY1EZ87Ir6YMDAAgZaWFmCjurq6mpoaXAoQcASHRRAaULAaGNZQhUawaBRMSSQg/TnySNcNuDVcwSyftNYgyuC6QGjJYJMrDb9LPOxFZ1LEJnwIiB0sElzI0Sni6eET+8DYwYvnlEaw+FS2IKKwjMKCvYhWCDwhZrAFtDxeWE5pSA7HP53tl8YNyOT6Z3B8Uwt9UooD0nlBWcX+GYVeaVyvrGKvHHEYtzJZ0lpU3VFW28araCtTd0iquirqO9U1TWWiusKi6jx2ZS6HQ0uOsg4IPu/sq+fgo3fb84SNq7aF074rd/aZOR24BtjheuyG8xGLO4euOuw3td1jdGuPkd3eS/YHLzloX3Y6Ye5qaOV+7pa3ka3/FYewG26Rt/0T3Ckp/gxObJoogyXN4Fblilu5VZ0lde2FNYTBI4ZfnyHvFzdPNfbN9489G595dX/u9YP5Z2MzgB3PJ2aROQA+/vDk5Y8Lz7+bXYDIheH7j4emno89eDXx8LuHj//w7MXC4EhTHhdQQ+YTJgfsCIyQhtAl/uF8N39JAEXNiJcEUfleoRBWBASrgoJqGPQqZmQllaEOpyqCQqoiI+vT056M/LJK6c9CADuAAEJDQ3HZCXj/YmJiDhw4sGLFilWrVmlpaUEY4OP06dOg5pPJb5fg0hfYv9KASEZ6BkAAWhpw0ASVNChvQAHQ3xAGdY4rd0Ep0D7wSamvr4c6NDY2NjU1tbe3Q1itVkOekA9odMjkxo0b6L6KHqzAH4ACwBM44AK1iouNi2QSFpS7Tndv3rwJXAI1iYiIgBwAKXA2rIeHB9TElRQIQxrIAdAEFztHfNEsQQbkAeQECaBo4C0LCwsgHqgJhUIB4tHX18f5PoAdZ86cAXKKI5eORNsP0QwWsDiFHLg1aAaB1aAIXJRZqVT98MMP7/8AP7W8fP0kKNLO4PoG3Wsrda6uOGG+Qs9iJSCIZiPI48ryY5eXHzVZpmO2EhS2ntlKfYvVhjfWnLddf852nZEj8McWUOoW3jssfXZa+u2y8N1p7gMssusG4EjgHjvKQQeqtgP18O0IbSfasbt0HTemnnfsmcDkS6GpZpR0S1qmDTPbIZHtkVbsD+iQURKUwwtnCRhFkpgiSSxXFFssjiuVJvBkSTxZYok0kSOKYQsiCwSMXF5EWlFgEssrJtclMtsRMqFmWvklGLtF6d9l6jgxj92ma9tFHAQkgvpcdt18wWHDaet1umarjhgtP3x+2X6DpVq63+zT+xa2/SeXAXMcMlh54NRyAjtOr9Y2XHPYYA3sgTmOnd1A7M9s0CGwY6PuhQ0nYDu/AeGDiLm42fDSnuu3LkZQQ/Iyc0sLy8Q8qbBYVMIuLWYVlxaWcvO5nHxOb3ffB3IpBUUOrxkoSwAO0OUaVsDOOrwwuBCeppePShf1N7IIanHca2wDaLpgkS6cBaS/BepmPIsp4RD+mgEjgCr6+/unp6cfPXq0sLDw4sUL0ND4vfhX5Icc8WPQ+MnH+fn5qakpwBEAJgBwHBvSVABFgxq4RxBhLS4ZgtXGe8wi55tgejZJLTnvOHAQleQQX2iEh8MhlzTNJiWHnBObS66uxlk08Lx712wSX4A50E8Nn9sHdSn9z//ipxQJojiiKK6QUVhGzedF5AnpLGkMV5FQIo/jiiPZcFgcksUJzCgCzgjK4vim5LvFF7glFLgm5jsn5DklFnlm8f0LlJFldVmKZq66XVDdJqhoKJU3lEgaSoT1HF51Rm5lUlp1cmplaiaPnkgz9/TRs/fWsfU+buutc8vzmNXd/eZ39po5HTJ31bFy1bV207NyOnLt9qErBHDsM3E8bOaobeakY+FqaO150d7T2CHgmlPoTReag3ecW3BaACOPGi/NKawqEak5AlVOWW2+rIVT0cGpbsxUqmPL1PH8dk7VRHX3fN84MMd3cwvfzz97PTO/MEw4jb6env9+9imBHU9fwfb93LNn4w/n+ibmescfD04/H374evrxD49ffHd/tpsnVlCiZAERiiC6IpguCYiAje8VIAyIUDISJWGRpV7BEv8wZUCI0s+/PCSkPJyiptAqqJGqiEgVPaYmLevpGOHb8f5P8It8cAE1jyMmIGiNAB154sQJnBiyZ8+e/fv379279+DBg6DIIQ0o6QRS0shPrIEAiIBmDQslXCL8/PyQNnx8fCCMe29yVYzg4GCAAFDDABxisbi2thaKRs4A5sA+UlVVFTRu0CBAWXZ2dpCPra0t0Mbly5cvXLhw/vx5IyMjU1NT4AA0ewBAQBHBQcHorAqHBKxQadCGJJNflEwmPyELNWSSC4ZqXDegVmgUgdtBuwvcO1yLn3yDlJAeeAWKAOAwv2YOrAOZI1SdOnXq22+//ZSUS5cu4SgSXA4BdHdlMpiQLa6KBtWDCyEBHELf782bN+//AD+1EC6lDHt98w06V1bqXCHsHHrmK09arDx5HbZVgCAnrhEjL8dNlx8lyePopWXHTJYRJhBIeXXlCbMVJy1WGVqtOWe7/qLDBqPbGy47b77ivsXUA0Bky1WvrWaeW819d1j47TD33Q44AlByM1CLtIVoOzN13KJOecYY+sZfDEq5Qsm8Qc2yoWZYM7LsYvKcElgeKYU+yWzvZJZ3SqFvelFgJjSmxSHQmKZw/JMKfeMLPKJznemZ9pRUq+Ak84AEU/8EE4/o03doOg4RRxwitO0ph2xC91kFa0GhV722X7q7+eyt9SfNVx81XnHo3LIDp5ft1Vu658Q3+08tO6C//OCpZYf0Vx4yXAXwcVh/5ZHTa4+cXqd9eq22IWHqAOA4epqAD8QOAA6dcxt0ADsuIIVsOXN5v43DlfjYaFGZSFImEfHE/CJBGadMJpSL+RIet4zH4YlKxcMfbJAF3g3suIN+1dgk0ACQR5o30sgPm+FQS+47Hy7JyyU+e4j9eI2exrMAyGgGQEXOWvQqxTEIDmkLEQqFlZWVPT099+/fB6T4/e9//+c///kvf/kL7P9Eyp9JwRiUdyN//PHHp0+fTk5Odnd3wxsOb75G32vQB4tGyEAcwfvSCDY0eJazuNRHTvZbtEJAKVhcXp1FLshBdooICxBOkdVki3SFdhFkLLgqNyc3K5OwD2FuH3QC7X/+lySDJ4opEkdzBXRWWURuGS1fwGRJornyhBJFfIk0miNkFPDCs4tDMrmBGWyf1Hz3hAx7ZqoNI8s+NtchMd8xucQrWxjKlkcVV6bza/LFjSXKZr66kiNQ53KVydnKmGQFNaqCHtmYkFCTmCCPSUy4Hexr6OCta+OlY+2ja+dx7JbzgevOByxcj1l5nLLzPnPby9DWWee601GCPO5oX3XWsbx7wtJZ74bbGRufS7f9zZwoNp7MO/4xrsFJfpQcWhw/Pa9JquhvaOqtb6wpkypzeFW5omZWeSurojZNWh7Lb8hQ3pO0Peoafzkz//rxkzePn76eXXg6/nDh3vSTe9Mvp+d+BOx48d0fXrz507M33z94RqxY2n//ydDs06FHL4YfvZqcezU50yuSK2mx0mCaMiJaHkjjufgT66B7+8lC6KrIVHlkkiAkQhpMUQSFyfwDlSEhqnCKIowqC4uUhMaKQmIq47Oejkz+gh0/B4GmAJsveElppMB7eu7cudWrVy9btmznzp2IHbt27fL09ERXUHS9RA8MNBJAPFAFsIW/vz+oc0yAmhgO4RT6bEJ6eM2h2wac/WeXMAAAgABJREFUAR0naEhVKpVUKhUIBLg+IXaHABRcXV3t7e0dHR2vXbsGqHHmzBlQ9qdPnwb4MDExgcibN29ev34dJ7xAACgEq4emC7Q65JJ9JGi1oIUBmoFyodMFTU0GuRp6XFwcJEafDGARAA4vUqCqaK3BtdXh0MvTy8/XD8KQHlgEqrF06VJc18TY2BihCkeUELOQsQBBILGTk5OLiwtUDJ7SB/LtQOy4tlHHdOVx0xUnrqzUvbpS99oKXfOVwBx65sSwCznmQtg8iGEX2JsS/HEEEMRk2bHLBILAWYAVg+urDW6sOX1zDWkC2XDZZfNVz23XvLaZAXx4bLnivvmKKzEoAzGWfuS6IKH7bcMO2ocfcqLruMca+hBeIJcCky6HpJiFp18PTbEITbYIT7EMS7YIS7GkplsDYTCzHJjZDvRMO2qGTXjazeCka76xxp6R5zwiz7pHnnZj6jvRdOzCDtmE7L8VdsAqaC8AB2zXvHZcdtl63m6jwY21umarjxit2G/47d6TS/eQzhz7TxFjK4f0lx8yWKltuPqwwSptg9XAHEfOrIf9UTR14PDK+Y0EZ5zfSDAHmj0ubta9uPX0pX0OTuZxMZE8Lk/KlytF5XKhUsqXyQRyuVAhLhMLSoWCEoFUIBsZHPlA2FFfX48vat47i2HkLpo60NkbewP4R19AjlYQZ7MIQ0gOSSfZ5EBgHrluOoILoobGGMAm/STgnYS3BboC0C0YHh6em5sDevj3f//3v/3tb/8fKRD+66L8hZC/QgzE/42Ud8/+4x//+Pvf//Hvf/135I+RkZGmpqby8nJ87Yv/e0GOt9iBIIJvr0Y0t8wmYSVvEUowEhNrAAVQA3tRGgMGXliw6PQK6TV0hbNj2Cw2kAeSDcgHxg5ljlAWVyKNKRYxWDxKTmlEDo+ax4vIEzBYoshC2AvoBbyw7CL/tAK3hFQbavINSsK1sDizsHTrqBz7+HynpCLfTF5ojohZIEssVGUWKTO56pwSRVqBND5DxEiU0uJEITQ1PbIhPr46NrYyIYUTHBt8ydnn5C3v4zZeJ+zcjli7at90PXrT85QtMIffRSefc/Zu+lbOupZ3jl1z0rFw1r3ufPKmq761xzlb38uOwZZujDsBsZ5hSf7UTEoMLy23qkzUV18/0dM11d/bW12rLiyTZ5RUZosbC8qb8yqrEqXVibKuovr7jUNPxmZePpx/NTv/6sH8i6m5J6Mzc4MTC8PTr2Ye/7Dw8g/Pv//Ly9//9dmP3808ezoy+2To4ULfzJO++y9HHz69N9FeKpLT4xWUaBU9TuQXXuzkI/Twk/n4KUOpSlq8MjJeSmUqaFFqZowigl4RG6eOiReHUkt8wot96GWBMTJG2vzg6C+DLD8HgXcf0AE7CQnkV9bg7QOlvn79esCOzZs346JbgB2g3dGt0tuLWBIUrQJRpKAKh0BSUhK+vDnkp9RiY2MBO3DOCIShCHSGwxHYvMVOGjQRcGECuTw50ExgYCD6XlhZWV28eBGns4KAvodDnFh748YNCwsLc3Nza2vr27dvo5fonTt37OzsnJ2d6XQ6tLTYzqSQn5sBgbBIJJLL5QqFAvbQ74J7xz5PLgkoxAgv6YcB4QjyE25QeYASYBT0IIF2DG4Z0Ofbb78F7FiyZAkgkRspgBdQDXR9hTBci56z6M0KucHtQ/v2YbAjgGF36hpaO1aeIDfgD4CJ41dI91KzRVfTqyuOmxGzXY6ZkmMupPHjyCUCPsjEhBeq3rVVumYrT5qvQvgwubvZ1GWLqdtmwA7YTF02mdzddNl1s5nn1mve2wBKrnptJ4ZjAvbcCj3oQD3iEnXSM/a0d9xZr5izHpGnPaNOe0UTAUAK39iLAQkm/vEm/gkmPnFGnlHn3BiGztSTjuHH7UOP3A47djv8qAPlCDCHddC+G/57LP12W/jsMnMnjBzn7TactVlneGPdKfM1OpdXHT63nGAO3W/2nCCwY9/JZTiwcthw1WHADmJbQ1g7zqw/Srh0bNQ5S8yYPXFhk97FzXoXNwFtHD+3gTzcctJoxznTw3aO15IT4/nFZWKeWMyTAHDwi/hlxXxBiVBSJpUJ5RAu5fL4JYLB/qEPhB2grQvIibI5iwMoqGszF78OkED6hGcufqAIk+FQKKJGPjm1DBFEo79BQOlqBlbgfaisrBwdHX38+PHLly/fvHnzhz/8AbACkAIAQoMUCB/v8gcyxz9I+fvf/w7xaP+AyP8A+cd/YDykhDwfzz+emJjo6+uDnodm8OU9sNDcIwBB3uIIEdJDFunLkr34MQV8zwn3DPID9zhxH1si1uL8FA2sYM4F5GgOl/RWwcYInyQ+3g88yCJOKxExC4X0gjJKbklIFjcoA7aioMzi0Gw+taAkLKcoOIPtk5zjFJ1yMzzaxC/SyDfaJCDqcmDyTWq6LTPzbnSBTwIrIKGEms6PzhQn5vKjUsoYiSJGkpCaIKDEymmJklBmZWRcbVx8OZ1ZHZesTMhm3PD2PWXndfSWxzEb16NW7rq33E/e8jkHzOEYcMnJ96KD5xkbN8ObzicBOCzv6lm6Glp7nLXzvOjge8UpzNor2iM0JZiZRYsrSsmuLhN3VdUONTeNtjSNtTSO1Fa38EXKLI48latKLWvILa9OUVQlypvzq4bLOx90jyyMTD2bmHk+Ofvy/vzzqUePhiZn+8cIT46JudcPn/1h4fVfX/7452c/vHnw9Onww7nuifmuiRfDD5/cm+oUKVVx6RWRyRXRacIgusAnTOofInB3F3j7y8KjlJFx5fFJNSlZVTHJ4qAINTVaFRHND4zgeocX+TD5QXHCiOTZvl98O34WwuPxQDuySeMlvHSgnqGlAk0JwLF8+fI1a9ZokQLYASoWevNAHqBTcZXx4KC3i46Dmk8iv5fGIT+BCQKvOeQDp2xsbEAfg95NIKe34IfpKRQKzmTBOSwgmjyhCFDbt2xuAXZcvXrV0NBQlxS0dpw/fx6wA907QECpQ2IgD2NjY0QTExMTVPM4zIHNSFRkFHAJlAJMAzFc0pMdWh5oZ+B+s0jfVYChtra2zs7O2trarq4ulUoVQ37gHoEJ7oW0YadCnaEOiB1fffWVqakprkQCuGNvb3/r1i2oG9QcHinkHBISApUJCwtDMoNCX79+/f4P8FMLYoc+iR0AHDqmxHb88grSmWMFsAXAx4lF+Dix6OdBbAgfaPMwJQ4BPo4BiBh/S8yCIa0mBjfWGN5YfcF+vQmx+MdmU1egEGJv5rH1sssmI8cNRnc2AIVcdgUo2Wbhu9MmdN/tiCOw2Ycdtg897Eg5eocKPHHEIfSIE0XHmabnTNdzpOjYhxyxCThk5b/fOmC/VcA+K/99bwOBWgAcQBtXCNrYYuy05YL9Jn3LNSdMV+qartI1XX3MaOWhs8v2G3yrpfcNYseeEwR2oGPHInOs1j695ujZRSPH2Y0nzm3SJdxIicXBYNM1IsInjbbpG++6aHbsrqtNWkqyqEysECkBMgTFQmGJsKyoTFAClCoU8cRSvpQ4BAop5g/2DXygdTvq6+uxK4/aEQGC/MAhITjCggOfCCKIF5ieVLTkoAqps5FCQFLJbxdlZWaxyLV34A8UmOPBgweICzhcAvJHUnDc5Pe//z2ACFo+NBSiOUS2eBc7IACRBHmQAmEAFLjkj3/4I/DH9PR0RUVFCTkpV2PLQYzAe8xe9NMuyCfGUBC8EC+wY0HwRG5eGvldew1z4FBx3qJVg0N+9hYZhsCORWxDMkMcSSMFH9cHtnbw4tk8AjgyOf6psLF9Ugu8U1g+EE4vCspieSdnO0el2VGTrodGX/aLMvYjyOOyL8PMN846JNGBkuJKzfaLyg2I5EQk8KLTJEk5gsjkklCmkBIjoSVJ6CmSiARJEKOcFq2OjJZTGQp6tDolO8U51M/A3uOIjfsxa9fjVu76dt5nHX0vOAZecgo0cfK/5OB90c7z/C2301YuBpbO+tcJ5rjg4GPiFGDhSnMISApiZjETWPHpck5pd03DcHNbf0VVr1TWwy/rKCpqZnOrMvJlCdmCqCxpHEceyytPlNRmKrv49aO1Xfc7Bh71jz4ZvQ/M8eL+PGDHdNfgTMfgo56xhcHpV1Pzv1949ZdXP/zx2XcvJuce9Yw/aBt93D/9amJuqrm7Mbe4KiZDHZ0upsSp6IlqRkyxu3uJh68oNFLGiFMlpJYnpknCojh3fPhuwQLPUH4AVRAaI6KkSChpssjsucHxX7Dj5yBlZWWgWYVCoVqtlkgk+DKGh4fv2rlrBSm40PjOnTsNDAzQMRPIABABEQQHVoAhQKNDO4D4goZSaNAsLS0BBWCP80EAQW6RYmtLrKVhY/32syzADcAZQCe47gXEgAqHNAA6enp6uJwXYse5c+eAMC5fvoyLg8EhnDp48OC+ffuOHj0K8UAAoOYjSGEymdAKRUZGQiSof5w4A+2PxtkT6g9kA2UBTEAM9L6APAA4ADs6OjogGWQFaeBpADSQjVKWq6urvr4+DrIsW7bs+vXr8PQgDc7ghVu7e/cujUYjGvOsrJDgEE9PT6gAtIeJiYmF5NS893+An1qIQZZIewOLjSeurEKDB2LHcdMV6O2h2QAmgCRwkOUtdpCjLW/JgxxtIewfRt8evURYQSCTE5DJ5eX6lmsNb6w9b7P+ot16I4cNJk6bLt/dZOy48eLtDcZ3iLEYAkdIFrH03UkYKnx2ATrA3tJ393W/3Tf9tW7677UO2GcdSLCFpc8ec8/dV913XvPcaeG928J7p7nXjqvuO8zctpm5bzN132bktPm8/UZDq3WnLAjgOGa8QvvCcqCNg4bL955aqnXym31o6tAlTB2IHQdOLj90agUxvGK4Rvv02qNn1h0/hyMpxGCK7oUtehe36hltPWn8djtlvN3w0p5L13Td3B3SkpPFZWKVtEIlUcv4CkmZTFwqkQpkUoFUKVZVKKokZRJBCfwNlUn44nuD9z6QtaOqqoqwW2S+nbQC+/TFD9JmkhYOjEHsQBBBnZq/uAAXdv3zFue5wB79nnCKGuj+8vLyyclJYIV//sc/AR2AML777rvnz5/fv39/YmLi4cOHoI8BnCEeUUNj7UBTh8b+QTLHX9HDA/dIJP/85z+BPCAMlwC7/Pjjj0Ae9+7dwxlfaHLIRnMOOeSBldSQE3txcizeaTo5+Y0AqUziRtCZC9cUgmeCaFVAOsmyyMk+GMMip65kkXP90fMLR2SgV5RKfl4STsEtf0jsKKRnFQamcQPTCv1S2D5JBZ5JBe5J+e6JOa5xua5xGXcYSdahMWZ+0aa+DGOv6Cv+UVd9Iy18o2z8k53DM3zoecFRHEpcETWeH5MqScwqTy+oSMuTRyXLaPGq6IyKhDwlPYXvFSIPYygZMTJalDCcIY1JLgyOC73o4nHUxuXwDddjNz30bX3P3yFMHSZ3Ai87Bpre8b1k72Ns73HO2tnwOmyeF+x9TO4EXHUJtfJi3glOp8QWJKQXZ+TWieX3mttHm9q6BNKmHFZdUmpVbEJVXHJ1Qpo6NpUfHlvoH10SlqlMElRmyJoKK/rkjSPVbZOtvbP9Y0/GZoA85gA7OgamWvpmWgcftA8/7p98PT3/x6cEefz/7L1ncFxHku/7YWdnZOitSFE0okiKoAVhSBCOoAU9KXrRewPf6EZ779GNhvfeu0YbdKPhQe9A761EUtKM9GZ2Zv3beC929r7/qSQwuooXsREbCl59UEVFReF0HYtzKn+VmZX1l6//8PrW02cX7311/fH3j159deXO2aJat9rWorTUSwytuvQ2Y1qjSNooVjQqzC26NJc5vVllrBeoyk6nVMXL6vjqBrG+SWaxKzJcmtw2W9m3957+6tvxS0gQrvjczp49e+PGjStXrvT09LS3t6MT2LN7D1Djo48+ohCcs2bNgvgHFkC+xrG102jNNsAHsEMqleL7RZ9AGs18ZkQGmixevHjhwoUoAR8UJBSgEM3SmjVrUK6NXguMAECQoygFWacAXCjBGaCKEJbAH6TPCA0NBWEQatCSK3QKHOfYsWMACKFQSFoT1MENNI8G4x9cWFUVF2gAIyv0M7iF9evX48ggDzATulyAFyAMo69z5851dHSgpY1NzSXjUS6zEQOegoKCRo4cCeyYNGkS/qTVYUQiEXmx6PV6NKNRllKhxHadVkedXnV19TvRdnwrUB8i7CALy9u85W0m5mBmF6q8tbCgXPLWzsJxBsFHEAccYwZABL+OC2b8sWTD+LCtEyK3TVi2c+KKLydF7f545d5P1hycsvbIlPXHp248NW0jZ4j5DAABdFgHKDk+DTiy8eT0DSc+3Xpm5o4Ev128uTuT5myL+3zLmZnIm07P4PKZGWiz/ti0tUemrTk0ZfWhKasOTlm2Z1I4A46g6HEBq8YGrBw7n0Xm+HzJiNlLR84N42bMgjnmhY2eHzZ2XtiY+aFjUPHnsGNiIPMk5VxHV09FXsrlaaGcMWVGxDrkmRHrZ0aun7Vi47zNOyITk07nZeVUllTWlNWAOVrtXneTx9XYiuxubgV2OBpdbY52gEhdRV1tZW1jbdOdW3f+/d2EC8PbiXeIRuqkzyDBCVlL2MFJaDYLdFBaFwzE2uIUCWxeCGkUqDES3m8ri7eDTwJY8+DBAyDFIE+AGL7//nts7Orqamlp8Xq9gBKAM+kwOIcOlqj9IILQdpSkJgFbYJc///nP2AKUIXUIuZqCOf7hH/7h9evXfX19+DYGjT70pXFqiYEAJHRTg6qOXJZwg7k5XEs8B7KDDmosSI1BvELHIVLB9hIWWWTw3k0siDLYC9hB6pP8d+7bUSTLKIi3FCWAOaxF8al5J/XZR9VZx9TphxS2w3LTPpF2V7J6W4J2R5J6e7xxX0rqYaH1lCQjQV4oUFWrjc0mW5PGYtda23OKuwoq2jMLu3NLutPz2gxpvtTsTltRhyGzOVns0Zo9xnS7ytykMDapLY3abNMBUWz4gRMLt58O3BkX+mXC8gO86CO8dWCOY4ItJ5I2gDyOxkUfOr1y75nVe+M3HEredkK4+4x8f4LuuChbbqzMzGssKjvn8vZ7Oy83tPTklrSnZno0plaFrlWua1XoW8SaqjPC3P1xhTEqV3qdL8/RWei6VNd+w933oPfqsyt3Xt18BA54ffvR8yt3nl26/fzSPWDHi3O33/Q/+uPT1//45od/+uaPPzx98+bui2/uPv/h8asXl29351XYpcZmqaFOoGoSa5xqg1tn9JgsLmO625LjTcuzq01NEo1LlerSprv0WS2qtHqhsZZz7LB15Jb//tGzv/6KHb+ABCmLDufq1av9SNf7r127hvqFCxcgdMEBtCyLn5/fzJkzId2//PLLmJgYYg6M4xMTE5PZoq/4bPG1VnAT59/GbsZHDW4AE9DyKygHY4QPrq8WsiQEDBHOVkpbuXLl2rVrN2/evGXLFlqoluwmwIIlLA1WsOPgsm0oAwICcBDsu2fPHhBGLEvgDFyhVCIFIYEMJBIJOKC6ugY9MPoZXP8XX3yB3XFfOBTaoNcFcKDPQe/a092DB1JXV8f1ctlvVbx5bGYAeq1t27bNnj2b1oHDTeFcg94tNGqigRaNu2g7N0ZlLizv0qV02bapS7dMYOTB4GPLR6FbmJMHs7ZweQNjCMpMmUFgwTiDgGNMwLoxgevGoBLEOZyOWbyRM74AUILWjQ2MHhMQPRo7Lt08PpQ7Eef/sWzXxOV7Pl6175M1hyavOzZ18+nPvoiZBYyIPjIlmsORaRs4uwxY5PPd/Hl7BPO2J3K+GixzzdYf+xS0sfrglJX7uFCqy3Z/HLnz4/BtExevHxcYPTZg9ZiFy0YviARhjJgZPHRm8LDPQ0b4hY70Y0qOuUtHs3mzYxeEjZ0finLcokhO1fHWh5SbKDuFA441n4I5wrhlZmdErp+5bP2sqA2zV26ct2lHOI93urSwtLm2pb6yobKkqqa8trnG3ubo8Dk7252dnZ5ue11LbUU9F6K0tqWhuqmWc++o6r96/R0ZWQg7cpl3Al4p64A/VBpbpICMDsQfhB0oyVuCxvfgFU5+44XO4t5OEt4FLMK63W6H4AdSAIrBCkAEMAGI4Z/+6Z++++479AhA8mxmibx79y5A4f9mJhWyoRB5EHwMYgf9hEMBYsAWNJ8WlUG1B5rhONj+5s2bJ0+e4BTobsgUwl3qj+b90odXyKbLEoUQfxSxGb+4HcIO+uqymBKIVCM5zM02k7mAZDH1D6mFOFMTm+pCnyW2m9iC0XieuSxCGg74jrGjRJFVmGBFLoiz5J42Zh5Vph2SAjish2W2owrzYWnqUZn5iMh6XGQ9mZKbIC8XGysVqRUyfb1C7zJy3gxuvc2ltV4oqb5S3dyRXtBhzeox23qMli5rZgdQQKbzSBTthjSPPr1JYnRorC5kY3ZJikmw7vjp4D0xgXtiFu+Kj9jHW32EF32Yt/4ob8OxJJYT1h0BecStO5Sw+bBoz2nZwXjNMb7pjLRQk1abV+yuqr/obDtbWd9qzvDq07w6q1tp8qhMLpmuiS8vPZ6Ytf2wYdWOzP1J9boCd3aTL9/RV+G97ux7eLb/xbV7X9948Ob2Y2RUvr7x8Ksbj55fuvuk9/rzc7de33j8+4ev/vTiuz9/9QeKHvZ/PXn99MKNnvwKlzK1Raa3gzmk2jaDpdNq60rP8lqzfJmFbRkFDl1qi1Lbk5bdZs5yGbNcuvQGsb4mWeVQpXozcr/5dQLtLyM9fPgQnEG0cfXK1evXriNduXKlt7eXz+dPnz79k08+gaAFOkBIAztoigpKEUsgD5Q25pRaywInUnwgfMXR0dG03hstSAt8mTFjBg4C/iAtBRJABDQD2b906VLShaxZswZnAfGsWLECPEGqjsUDKTg4mNY6IeUHVXAiIA5ZcA4PJPAQLpLChaF/RmdisVhxqfv376dFa2d8NgMHxC7oc3KZj53L5ero6EAfW1lZie4IxIBendTV6KOoowMMTZ48eciQIe+9996iRYsANNTJUw+GQRdQQ6PRCAQCMkWhTqMvlOha390EWm4my4QQxhxvnTw4a8v4EA47xgM1wBaB67jMKTZYJuAYzAFrARZjUBKFBHMqkDHBG8dyehFmfOGIhEEJ54K6kTO+hGwaF7blo4htE7kQZLsnrd4/Zd3haWsOTll9YDLymkNT1h2dtunkZ1tjZm2L99sWN3vTyRnRh9Bg6uoDU1ftm7KCC9w+OWrXpIgdH4dunYCrDQJtrBwzP3LkvIiRc8NH+oWMmBk0dEbg0BlgjiXDP186AuSBzHmSAjtCxywIBXOMWxA63j98QkDkx4HLPwlazrmRcp6knA8pLfkG4JhFwLF8o9+qzQvWbFm0c98qi1nfVAOkcCCXF1WCPJpr7a7G1pZ6Z0u9y+dqdzW7m+s4f1JHo7Ol0dlU01RdVn310pV//Zd//en/4L9L/xPswHuZMRD+HC80eXFDZGYww4qNeWnQq0avLN5IEs8ktkkDiTY0iYsMKwAOjDDu3bv38uXLr7/++vff/Z6sKmRM+eGHH54/f+7z+fA904uOZoAJIgxy9QBADFpSqEK8AuAAvuAIg2E8kIAa/87mtvzHf/wH2pD5Bt3OuXPnmpubabySy8wceczrkxCkiNmG6E+6EdpSxCa/oEJfIAdVA4RBCJI+MMU/i3nDEJTQ7rSdjjD46DgyY5Ny3zF2FMozCpOsRUlp+fHmrNN623Fl2nGF7YQy87Q6O0afGaPLTTRmJ6iLUrRVUm2DTNOqs3o0Fo/a7NGYvRpTm9bq1ad7DRnnCqrOFde0mbO9Sl2HTNWpUvdY09p05oY4vkcga5XpXBJDI0/pUpi8apNXb3EaM0yHkuMjD8SF7D8duCs2dG/SSmDHUd66Y4nrjiSuP5q44ShhR8KGo0nbjssOJujOiFKTFMjFpsyGogpfnf2Kw9ebX+GQm1qVZrtE2yxWu5Qmp8LYyJeXn+RlbD9iWLOz4KigXGRpNFd4cuzdpa2Xm7vv915/ef3h1zcfvbr18PVtlI+/vv341e3HL6/ee9zX/6j7+pOzt7+69ujbOy/++OSbPz3/9g+PXv/w+M2jvqvdOWWtqlScyK0weFXGTqO1y5Lek5HbnVnQlV3kS8/1mK2+VMu5nLx2a5bXktWbXdhpzfZoLd3WnM6MnG/u/jqT5ReRvv/+e3Q7N2/eBG309/ffvMFVzp8/7/V6VSoVKGHixImfswSAgGinuB2QpmKxOCkpiaKCQnJnZWaVl5XXsASxjW8Z0h2iHbQB7KA1ZiGwwTEzZ84Ef8ycwcqZM0kR4u/vD6QARoA2du/evWnTJlRI2/Fj5ggKCkJJ3h7kZ0pOpjt37tzBEqjiwIEDx44dAzPRxF2abkPWlu3btmMXsA54aM3qNfxkPjph9FG4ZjCHw+FABZ0bmAn3LpVKsS9NEkabEhbmfOPGjRMmTPjggw/ef/99XINarc5lc3YsLO47aW2xLzftNi4uNjYWD4dCoOayGKzvRNvxLV95KHLr1JCNzJ+UlUuZYynHH+ReymHHOMKOt1Tx1p7CZdoO4Ahk5AGwYNoRTv/B5XVjUGf8QWYXbuYttqNxMCBm3Rgu/thWNv/liwmR2z9GPWwrpwuJ2D4BOAKqiNo5KWrXJ8t2TkLGr9wlrR8XsmH80o0fhW6esHj9+MDosaTemL9s5NywEbNDuHXdPgv48FP/D6cvGvJZwNDPAofODB4+c/HwWcHII2YvGTknZNS8pcCO8QvDPloYPmFRxMSAZZMYc0xB5jxJV09bylw6ODeODbOjNvot3zgHzLFqs3/4mtnRWxcpVMm1FTWOere9xllRVF1TVldf1dhQxU1jQaWp1t5Y29TS0OJsdDkanPZ6B/ijobrh2pVr7w47yIKAkgbooAe8eTlsQkfOgPUBgjmTeVzilS36W2Cut86YeEHJpoCD4F3HBw+S+Oqrrx49eoQu4MWLF2/evHnF0uvXr7/55htshGDGhwR4R48Aah5Qafz7v3FROThLyj+yBIz4M0tgCzQDcIAqvv32Wxzk64EE8gCUkDkG+4Jsnjx5cvHixZ6eHtwdxiuDeohBxUP+gG8KJXIcI5wigy7FBxtUkwxCBlVoIx0tj01Xy2P+LhxksNhreDREcvQrOcS845ksBYq0QkFqHs+YGau2nVGmnZZnxCqz4lX5PD1yQbK+Up5Wo7ba9VaHSusUij1CiSeO1y6QdWhMbrHazpO5JdpWmd5nzHDr0pwyvUMgdvNT2lXKbqvFJVdVn4hzJIkaE0QNZwT2MyltEk23ztRtNHfZMqokWtGm43Hh+88E74kJ2Zu4/Ah/3cmUTad4G45z2o6NnMIjft2RpE3HU3aeUZ9IsQo0WUpjhkJfZMlqLKrsbHBddbSfLahuVXOqDodC3yxVN0s1DoWhRaRpTlHX8xT1cdKWFF1ViqFKkdtsrmrPd5yvbb/tu/z86v1Xt5+8vsvynaccdtx5/PXNh0/O37rfeeVe+9XHvTdfXLz75saTPz35Bszxw6M3D7ou+qw5LsCNQO4QKpxCsJTKozZ6DWk9GQV92UUX8ovOZmefz82+VJDfm57dbcu+VFRyIb+kNz3/Qnbx2ZyC737Fjl9GwrePPufOnTvk29HV1YUhUAULuww5unDhQkhZspIAILZt2yaTcuuhiESi06dPnzx58ujRoylsDTbgSCZzKS1j6zShvmbNGlJ1EHZMZInUHsQiSKjQCq7gGwrEjr1AD+vXrx/EjuD/PRF5LFmyJCwsbPny5evWrQN2bN26dfPmzTvYGrZIuKqEhITExETQBsgAeLRr1y4cGUfDWXBTaI+um0Y7uF90eo2NjflsYRpS5CQnJ2N3oVBI94VODJ0bxlE4xfjx42mx+6ioKLlcjm5cPrCCHUqCD5yUprFQtBLaWMYWmfrpP+DnTsCOZMWhiC1TQzZMCNk4Ycl6zg8D8LGU8Qdzyxi3eD0zsqz/m0mFWVu4ksOOtZySg7Qdi9aM5nhiw1hGKgQio0nJwcX52PgWWQaphWu8fiyOH8T4I3zrRPAE/ly6ZXzEtokRX0zExQSuHrNoxWj/laMD1rC8erT/8tELl48KWDk6KHqc/4oxc8KGzw0fOX/ZaL9QDjimc8DxwaeLPkDls4AhlGcEDpsZOHxW0PDZwSP9loyaEzIa2DE/dJw/Y45FkR8HRn0SyLBj8cqpIaunhXDmlc/C1s6IWD+LmGPlpvkrNi4IWfHZvCXjF6+YfCp+R1F+jrPR3VLvqiyuqa2or6tqAHA01zpqKxoaa+00hwXMwc1tAYjUNDfVNl27eu1f3k1w9NbWVqLXPDYp3MIWZqQ/i5j9j4RrGVuiBS3zmSfHoMAuGogCTtoRvO6XLl16/vz5gwcPrl27BsHf0dGB+jOWHj9+TJXe3l68x/jswc5Pnz4FNJBFhcMOZkkhFw2ije9ZAm0ANb4aSDjUQ5ZQx3Y0A6CQagR1bLx169bZs2fR79TW1nJ+YQPrx5JLCmk4qCweiDFayBKwo7y8HHuBVwhB6HumRMAxqDIhL3dijgIWzgR/VlZws+dJC0K4g0da8M6Doxdp0/JF+oxEhTVGbD0jykqSFwo1BXxlkUBdJtLVKIyt1uyOrLx2i80uEDaeONV85Ejztp3Ooyc71IZWma45LsUZn9Ick+wSK1uE8uYEQRMvpYnHb9Nqu6xpTqmq7kySXSCuSxDUHI1tOR7XLpB0qLRdBmO7ydCi0ZkPJfBWHohbui8x/BBvxbGU9afFW2OFW0/zN5/kbTzO23giccMx/tbT0n2JhhhplsJcmJpRkp5VlV3UUlrXZ/ddbmnvLax1amze1PQ2q81rtngMplaNnvNglerdylSnWOfkqxpS9OUptkpJnjO1uqfIdd3R9+TCbdDGdw9ffvvw5TcPXry59+zVvSdf33748tr9Z+duP+q88bD92uPu/ucX7n53++Xv73OLwz3qON+RmuVOUTiSJfZEQUsi3yWUOqUatyq1IzX7Ym7xjeKy64UFV4ryLuZk9Vlt3ea03vTM3oy8blt+b0Zhb3YRtwLt//urkeX/fMKABN1OZ2cnhvv4BiEj5Ww1NTIcBAYG0ppwhB3R0dFxsVyMc5rmevjwYch41GkVt3Q2Z426QQy31q5dix0JLyZPnvwxS6hMnTp1ykBCHYedOZBmz569cuVKMAFggqbOAi8INQJZIvJYvHgxACIiIgJoQnFLiTy2swQyOHjwIJlXJGxttn379uFoOMjcuXNxkEOHDqHTppFPXV1dfX19ZWUluh3SUmBHiqGOhDtVKpUYHJJLKegBhyJtx5AhQwA9aI+9QCpU4lwU4RSJ1oUhajGxeCTo0NAt//Qf8HOnQexYunECyGPxekj98SEb3mLHYgYBweuAHeOBIH8jjx/pPALX/Y05Fq0ejQqnFGEmFWzHRpBH4NrRAWupxMbRTBHCzC4U82PjuKC1aD92CeemykKgcsE/JoJCgteO45gjatSCZSPnR46YHzFiXvjwuaHD50WMWIiNzFeUKTY+mB44ZEbQ0M8Ch0wPGMIBRyDlIcicnSVwOLDj8+ARc5aMmhsyZv7SsQvCxiNz2BE5kQuCvmwSyCNo+eTFq6aCOUJWTw+NnhG+bmbk+s+Xb5i7apP/2q2Ll66YOSd4rN/i0YFRE7/YF2q2KOurgRRNFUVV1WW1II+asjp7rZM0H1ysjtKqlvqWuoqGhupmFsDDfuNa/zvSdgA76NMi8Ulj+vwBqwRZFji1BlPK5TDHjnxmWMke8E4i4V1dXe3z+W7evAkUuHfv3vnz59va2hoaGpqamrARWzAEAQrcv38fdafTiY8c2OFwOPDuEjGAOYgbQCFAhx9Y+u6770hBApJ4whJFRkfPQuu2YAv6Ghzkj3/8I8UC+ed/+mfshWYY7ly8eLGlpaV0IH45ARPpMOhPSjnMw5TQoZDNUqEREmHHIFgMpkHeKmLaERoVVbJYPeVsuV1sJG1HNpsKRMbUd4od//W/Ss2ZBXJjFl+RzZPkJImLxcoqpa5arq4UK+rkmhalttNkOZed4zOYGhOSag8crtzyRemyqLpdezxSebvW5BFIXXG85jMJrQJJa4rYncR3S5QuuabLauuyZnhkWgdP4pFrnGJ5SzzPeTrel5zilcq9ak2bQe/U6ot5UvWO0/zlR5OjjievPClYe0q05Yx0e5xw6xn+xhO8DRx2pGw/IzvEMyUoM+WmEmtObUGpq7qho9F10d11rt7TmV/jNGX7MnI6MjK7s7N7s7N9RqNbrvZprV6dzS7WNSYpavma0mRTUVJajbSgNa3+fLXvTvvll9cfADu+e/TVt4++Anl88/DFm/tPwSKvrj98ef7u467+R139z87e/urKo29uPn9z4+n91t42bZo9PqUlUdAcz2uOS3KL5F6l0aOxtultXanZ52x559Ozz2Wk91ktXXqDW64CAPmMaS51qkdr8Rht39659yt2/BISehh8vxqNBoN1jPJJSQDywAeIrxUyHoP76dOnAztQLl269DCLzQWRfPTo0ePHjqMEc0DEQrLmMGevfDZQAYJs3LgRJEG6DRDGJ598AuwgU8sklrAFdfxEaAIEAXaAJNavX79q1SpQBU5H2AFWCAgIWMRSEEvYHh4eDphYvXo1TYTZwhIqmzdv3rVr1/79+4EdQAFcKtpgdwr0juuniBq4Zeq7gBS4fhAD7v3IkSNAlgMHDgBNUE9gS9jjsUjYQvZotnfv3okTJwI7hg8fDuyIjY2lmOjkEQ/IAF5otVoiFfJUw6MwsoQe793MZElWHCTsWMppOz5azDLIYwmYg7CDaTs4awuHIEAKlsnIwrmRckaWRWvGgDk47FjDOZYCNUgFwrEIV+dKrr5mtP/qURyIMCgJZP4fNBeXwp5SxumYfmVsIPZaNdp/5aiFy0cuiBo5j/PbGDEvcsQ8UMiyEZ+HDANbTF/0wfRFHGHMCB42M3gY4GNG8FCu5DK3nP2MwGED2DFyzpLRc5eMmUfYwbw6OAtL5McBHHZwdham7fiUWViAHbOWrfdbsyXoy4Objp7aE756jl/QaBzBP2J81OZZAukJCLDm2pbmGkdTTUtdZWN5YWVjjb2qpKa8qLKEW4SljAsdVtEAIqnldB4N/Vf7IX9/+j/479L/HDtI+hJt0J/5zGRA9heSr9kDk06R0tgCAeRNWcDmjp87dw5Icfv2bQh7MqY2NjZCDIO+Mf4AIqABIAD9Av6EUMeHhLcfLIJ39y9/+QvDjr95jJL3BtHGixcvnj9//pilZ8+e3bxx026319TUtLe3d3R0gDy+/vprtAR80CxcUMuf/vSnly9f3r17F792dXWRX1gZS+S9Uci0GoQOOVzIc85thRiLQIpun1gkm81Pwa+ZGW8jpJWyRLsXs8n9uB4wFq1rQ9gxSCp5zByDZu8YOypSs0pkhgK+tFworRRKahUqh97QqjM0S2V2ZH4KYKJdrbGLxHVxCfXHT5Zt2ZYVFl6xY4c9Ocktk7n5KS4ev0UgsicJ3HyhRyz2qnSdlsxujPKtWT6F3sNXtAkUPoG0LTmlnS/sFEvbkFXqTrO13Zrh0FmKExXKL2KTV53grzopWHNStOGkdGusaEusYNMp/qZTvE0nUnYw7IhXpKVoC/Tp1TnF7rrGHmfrJVdHb6WjO7+2I6u0Pb3AZ8nqzirozinwma0+lb5Ln9ZhyHQq05qlphqhpkpiLOaZShJt9YrSjgLndUffw77+r248fvPgxZsHz1/fffLto5dc5iKmP/766r3nF26DOV6cv/viwt3X1x6/uvLwoedcmy6j9lRC3Zm4iuOnyo+cbOKJPepUt9rSIjc1CnUOqdkh0TtFcpdQ7BZK7EKJU6l2yDV1SaJmvtSl1H57+9cVaH8RCb0ZxvfoWyBoARwCljBkJ+yAgB83bhyYgHw7gBFbt25F4y+//BLi+cTxE0jkAEH6AxqModNAF4Ex0qxZswg7gBTgDAhskAf5eVCdEv6kCvACiADyiIyMJH9SDjuCggMDAgEcCxcuBDf4+/sTfJCdhWbBrFmzZt26deAVWrolOnot0r593IK0qKD9bJZw8WazGayAawNV8FkCRVGwL1Le7GJp75d7sZGcM+Li4k6fPo2WPB5v9+7dhB0jR44EG+HRqVRqg+FtnHUkE4tUlMXc/tRqNVlekIAm6ELfjbaDJz8YvnnKEg41CDs4hccSVi5exywsZGRh1pa/Ycc6TvPBkQGwYy1hxxj/VaNAHoGgjWhOyfFWBcLIYxA7Fq4etXDVyLc4whQhgetGgz8CN4wJAoJsHsflQXPMeoIYjmkWrhq1cOWoBStGcuXKUfOXj5wTPnzWEkDGkIE8bNZibBk+c/EwLnMIMmwmM6/MCBg+M2jE7MWjGHaMBnaQM+lbxw5gB2dnGcCOVZ+GrvksfO3MyLV+azYHHzq6U6dVpdksuw6sWxT+ydyQ0QvCxi1eNWnfieiMDHNNWV1jVYu9zgm8KMkvr69uqi6vK8otLc7jclVpTX5WYbYtpyi3qKyw/PLFK+/OyEKyNo95IVB90HbAnBk4DQepBPD1sgAWFisLTUGzQyFugRSQ8eCJ3t5eWgnJ6XRCBmMvIAKYg9YswE9og8YQw3j16+rqXn396o9//CNAYXCiCup//vOfyary+vVrYAcAAuRBq8ShDpJoaWkBzaAE7pw9e/bJkydkuwF/EHxgd7TEdlDOpUuXcDFEA7RQAiXihhw2M8XGEhmY6JbJkYWsJNiIm6URAG48hzmNDipLCDtwsxScmM5SzIIlFzJbDMFHybte+P6/KtTG8iRRaUxiLU/QIBQ6tOp2i7lDr3eJxC4wB0/g4aW0SeUOoaiFx2+Kia85frJk5676o4cb4+Ma4uKb4+K8crlLrqznCRwiSVdqKhc8w5zebs06l13YY0rvkOl8CaKuOF77yZj2+KR2oaRDruo1pvZa03tsmQAUly4t65Q4Zd3RpKgj/BXHRWtOSzfGSrbGizhrS0zy5pP87aek+xP0p0SWBHmW1FCcmtVcVtXR5DjX7O0ra+rKLGs35ztVGXYZOKawHQhiyfPK9B6x2i012CWGeqGqTqyy61LLU/R5Z7QlSRlOS92luu67HVeeXbn36jbn2PHVrUev7z178/AFsOPbe89e33z48tLdlxfuvbr88KuL919feYzK046rPWmFdWeSqk+cLj14pOjLQ+XH45pFqiaJtoavKI0R1iYra5OkTTxRXWxiQ0yCPTmlWSKrSxZVxwkak4QOqeJN/81fseOXkJqbm48ePYpBPORuTEwMxf6CvMxkTt/BwcFjx46dMmUKAGLGjBkgBsjajRs30mJsHHSw8Fxqtgrr4LAqh7lz7dnDRf4gTcagtgMymyBjAkvk8PHRRx8BbiZPnkxxSEES4AmaK0tWFdJ2ADgWDiSQBLYEM/fSwSm4uDZUIiIisDv2JbfTefPmgZbATDggbi0pMSk6OhotATfYhYKPUfwxstF88cUXgJLdu3bv37cfXS6PJeAFsCMpKWnHjh1kZMFjAcSIuJV7ObZARSgU0jIu+JOCsfJY+BDshZ9EQhEeKUaGP/0H/NwJ2JEkOxC2cfLideP/lhlzBK8bH7x2HJeZwoNpOBh2rH2bCUHAHJxHJ9N2ADv8V3E6D3/kAd0GtxEl4wz/NRxzLFw1AlveEskaVEZxJpgNY4I3MezYxLmdMnXIW17BXovYccAc85ePmBfF5bnLRviFDZ+1eOjM4CEzFw9FBcAxe+mI2WzGCvhjZvDwGYHDPls0dEbAMGDHrKARnFcHw475b7HjI/9wzrFjADs+CV4xdcmqaUtXTw9bMyNirV/05pDDR3dpNar83IK83HxeSkzU+gXzl45fEDo2YNlH63YuUmoEJQUl1aV1dRVNVSW15UVVNeUNVaV1pQWVqBfmFpUXVYA2SvJLy4sqK4qrrly8+i/vxsgCGiD1RgmLb0GfGclUGq8P2iOIS9JY0FK8czR7paKior+//+HDhzdv3gRbdHR09PX1oQQT4Cfs4nA4zp8/397e7nK5cC7U0QzfM97jixcvklvGYAQwmq5CFhZy5gBGfPfddxSiA4AC/gDEgDnIfIMKDvLgwYN79+7dvn0b6PPo0SPSjuCSgB0P7j/A5QF3cD0gg2KWyBRSypa+y2We2xQebRA7yIuFvFXortN/FCeeMIWIhNiCTC1FzL2UnhUdgcxSucwhFyd9t9jx11qNrkEgbEjmOxUKr1nfmWM7m5/Vm2Zpk8md8YnOmHhnXFK3UtOh0rTJVW6prFWj9UilbeKUFj6vKSHRkZzcZdC16fV2ibxVrTtfWOizZXhAFRk5/SWV1wpLr2Tl9cnVvckp7WdiW2MTPCniLpXurMnSZUztMpk7DWaf3tysMGp2nkwI/zIp4rBwJbAjXr6dJ9+ZJN4el7wF2HFSvCdGc4RnOJli5SnyNJbK7IKWspruWse58maPPtMtMznFxia+2q1O85izvXqbiy/zJOMidS6JpkEgbZTIHRpdtUhVmqTNjzHUK0u7i1v7Xece9d14fuXey/4HX918CPjg8q1Hr0AhNx48u3Dr6dlbz87eeXb29suL95Gf9964VFzrEMob4hObE3nVx0+XHjlVGcev5kurBfIqvrxerG6WqT0ao1OmakgWVMbG1yTz61IkDSnyRr6kWSR7dfXar9jxS0iNjY0Y5QM7Dh06RHEvIF+BEaTIhLwHEIAYgB0Q3qhAZkMwc6qOEydOnTpF8hijfPKRR/+Wy2bw4Vs+duwY9prKErCDbCvEGR9PfIsdE9g6t2PGjBk5cuScOXPWrl27Zs2ayMhIEMMgc5BVBYnsLIPw8WO1B2k+Bme+YCMa+Pn5zZrJzeAlx9VNmzYBAgAcAJGARQGEMrRjVFQUEAS3Bv5AM/DEzp07SVmSmJho0Bto/k5ycvL69etxwcAOkBMegkwmIxxBs/j4eAqeRvFb8STxYPEQ8GBpVRo8H3TOP/0H/NzpDz98myDZv3T95KA144NWjwtcNQ6V4LWMOQawIwiZzRbhaGMAO97+yVXGcGE5yMiy6m+ZAwuOQkZxJhLkVRxn+DNVB/LfsIOrMOxYNyaYRfvgQoBwqhFOccK153bhjrOQ03AAOIbPjRw+J2K4Xzggg01aCfxwRtAQVGYtGfYWO5Zw2DGLsINjDk7h8XkwYccohh0ccywM/Yj8STnmWDYpePmUJSunhaz+NHT1jPA1fuu2hMXGHTfpDdl4tTNy0iw2jVbxxZcrAiI/AXYsihy/fPOsOP7+zAxbUW5RSX5ZSX55dVldbXlTHZcbW+pdTTX25tqWplp7TXl9WSFApPLKpavvyLcDiFDA4tCRPoBEZgFLFAqM/iRpSuYGEqg0gABDPH78+NatWxD/nZ2dPSwBL2pra9EArybIAFu8Xq/T6XS73V1dXUAQiHkcEGQAsABP/BOX/vkf//Ef/2Eg/elPf6Kptqj/5S9/ocCm4I/bt+8AX8hVgguG09MD4CDjzvXr169du3bjxg0AEErQCba/fPny/r37dG24kkJmPCLLSEkJBx/ABTIeESIQSRQw51BSzJawRHqLAuZGiieA+yL+QJdEhhjqnmwsIGkOm1FMOiEkulqc8d3OZPlra3qGT6/36XS92dmXqysuVpVcLC24mJvTrdW7YnmOkzGOM3Ecdmh0HUZzd2ZGT25Ol9nUpVW5FdJWpdKnVPYYdX1Wi0+l67GkXywtb7Olt6Gl2XIpK/dGWeXNsvJeg6FbrexSyHxSmUcs88nU7UqNWyr3qVTtSqVHLPEqNHnHkoQr9ydFHkxecUK8MU66LVGxO1m2KzFl25mUnWdk++KVBxOVhxMNseJMpbHIlF5iznIV1V63t/ssOQ6Rqk1u8Eq1TrHKqdC6xHJHXFJbsqRdonGlyFtSRK0KZatW69KampWWonhtmSDTk9l0qaH7lufi3fYrj87deH7t/ssbD17033t27c7z6/dQeXrx1qOeG/c7rt3zXUHl2bk7z/puXSytd8q1bqWq02RyiaS1MYn1yeIGiapBrmvRmB0qnVsNAkvttKQ71ZqKpKQ6sdihNdhVRrtcY5erXl3v/xU7fgkJ2AG5+OWXX9ISJxjfQ0BSeG8wBEQ7hvUAhVksTZs2Dcxx/Phx0EZMTAywA3XIXUjlDJbQM6BjpCGZQCCA4Ce/UaQfY8fECRMBHKTkGDVq1LBhw3CW0NBQCH6I/4iICNAA+ZBSCmKzVwhBBtUeFPMDf6IBKT+oQgFMKaA76TlwdjDNFyyRb+yMz2bM/nw22mCvIBZ5DGdftmwZoAdgQavNAcUADYAJYincFJAiiIUoBXbgdnDvYrEYG4k54tmitVKpVCgUkkXm6BE80aP0bEEeYJTXr1//9B/wcydgR5xw/5LoTwJWjkNetGJswIpx4I+gaC4Hsxy0hpueumjVGJTcbNUB7KC8aDXHHBx2MNpAm4DVbL7JKg47OC3FSiKPkUwXwhBk5Uj6k/uVy5y1hZxPQRtMQcI1oLxgxcgFrJw/oOqYG8npOWYvHfZ5yPAZQUOnL/qQZW7GysygYTM5f463zhxMzzGIHSP9mJFlXsjY+aHjFywdz7CDVB2TAqMmL14xNWTlp0vBHKv91m4Ki4s9VZhbVFfZkJ9dkGZKM2oMOo3qyMldoatnLQwb5x8xbsmaSTsOL5MpkrPTs/IyC3Iy8pCL88pryuobq+2gjbqKhsri6rKC8qLcktyMvKL84ovnL/7LP78TIwuwg7Qd5P1A2EGitIjNZCEDBLVByZn6WNBS/ASS4KIB9vdfunSJbCs4WitbDaFwwLELKAPOAIhgO7CDHDLwSQMCnj17Bl7+E0ugCnDGH/7wB8hm8vagEB00pQXv9927d0ESHo8HUhwfBl766urq8+fPP3r0CHgB1MCvV65cQUkKFYAO/vz666+fP38OHMEWu91OliPS4qArIVeMEuY3SpaR4oH5LPnMbRZPgCIVEnzQJH78iusnpLCyYKx5zHsjk81YIXaxsWULaFIxurxc5jHzbuN2/LXNZuvW6bq0+p6MzPPFRWdzs89lpvelWrxChfNEkvN4vCs2ySdTelRqrym1KyfvbF5+p07fpVZ2mvQ9Nlun0dChVpw3mzplyi6t6WJhydncvC6zpVOj8yk15zKzLuflt2vVbTpVd6qhXanwCsQeocwllLbwU9wisVcsdiYk2mMTq07zTdtO8lcd5K05nrLhjHDTGcWuRPmeROGOGMneBN1Jkf6USHNSYEySZWstpZbsdLGuwpR3u+38taqmFrHKK9X5gB18qV0gaY7nN5/heYUqr1jjEsqdQjH4xqcFEJidakut2FyYYGwylnWXtl6q775q773VdvHJxTvPrt59dvX2s6t3nl2/++LG/RfX7j89f/th9/VbngsPu64/7O5/1HXtbGl9q97aabP1ZGR0GsxOIcBL59KZWk1prYZUh1gGFmlT6bvNNp/e1KrTtRqMLkOqXWN2alPdBsubW7/6dvwiEnoVjNp37dpFfpQQtBCQkKb4GAETkN9jxozB+J4ms6AkP0oM6CFluXXi4+K0Wi0NHtjI5G3P0NDQgFEEQGFwruygnYVsKyjBHKNHjx46dCik+PTp01esWLF69WocPzw8HBww6ENKxhRSaZAKhPBikDwGVSDz5s2jheuAO7NnzybmwAXgvDjIunXrADSD02c+ZTNoKHAqnWLp0qW0yC0ZXPbs2QNowM3qdDq6QQAZnsCQIUM+/PBD3BGeFdgChEGhWhVsdXvctV6nJ1ePFEHKyZMngR3gMzxStMGg7qf/gJ87ff/Dt0niQ0ujpwSsHA/gWLR8rH/UGA4+VoFCgCBjAleNDVw9dtHKMf4rxqDkqGINgw/kQRz5MXas4bCD/EApc9sZhTDgYHkFhxELV4zgNB9MEUJE4g/OoJ9YZX7UQF4+csFyRh7LR86L4lw6/JYy7FgyHKjx6cIPuLzgQ+TpC4e8zf5DP1v0ljk47AgY/nnQSPLtmLd03MKwjzjHjtAJHHYsmxS4jM1hWTEtZOV0MMfGL5adOHFYrzaUFlbUVTVkpWUbVAazzqxVaZKSYtduCQlcNsk/8qNFUeOjdy7kiY9bLOa01HSzPlWj0Bo0RltqRpYtOzczL9uWYzOl56bn5WcVFOYUFuQUctjxbnw7AASc3cTGxQHLGFiNxcY8KMnmksoWWElngcLItpLGApji176+vhs3bpw7d46cOfBx1tbW1lTXQFTjtcabCjhARwDO8Pl8aGBvttPC9zhUS0vLkydP3rx5Q0FFaVrsq1evUOdij/6Rmz0LFvnhhx/QDBdZwuJ3QcwfOXJk7dq1+IRwSYAY4Mjjx49BHhQRmXxH0N7hcOCMQJBbt27hJ7L7EFcVMrMI+YHiggEfNG+FrCTZLEQYKTCITmjVN3RDg4ELi1lYDlJvoD0pQuj5kNmFjpPJUgbzV8XFv2Nth10ub41PaI1LbBWKW+Vyj1TSoVR6JPLa04kVB07XHo2DFHeI5S6FxqUxtpvSuw2WthSxl8/v0qh702ydltROg+aCxdQuEreJZL2p6WdtGT16Q7dS1aFQdhpNaNNtMrTr1D61olUo9AiE7hRZzenEupjEZp7Ap1S3iaWNR081n4ovOxKn2XKEH31MtCFWuPG0dGeMfF+CdG+86qggNUlpE+lsYm2m0liclt1QWJGeostINl5x9D5q6/EotK1JEmdcijNR4JOqHMkye7LKo0zzqGytSnOrSufT4QKM7dpUj8bq1GSU8PQlfEuzsaIjz3musuNyc+/d7msPz/c/vXzrBafq4DQfL1Fevf/i0t37XVcBHw86r99ru3C2tMmXltebmdeRmtau1XvlwCljhzW9Jyu73WxxSZRuqapDb+rSGb0qrU9vwK8tuB6ZzqdP96Zmv7l1/9eZLL+EhN4GwLFz505yq0QvERMTIxKJ9Gwhe0j0cSxRdA3IaUj0kydOQoJiTI/OiqJw4utGh4DPHP1DFUs4LD5wSHES/AQfEPmTPuYUHqTqANAMGzbs/fffhyCH+CdNw08UHv4sERb8OGgY/UTkQYmWaJnDEoVV5bBjOqfqwOmAFFFRUWg2OH2GStwU2WtwChyc4o9t3LiRc+/Yvfvw4cOxLPx5JoukDMjALmCOoUOG4nSk4aBQpHhcNJNFrVbjsQxOo8WDAqIplUrUgWgYN/70H/Bzp+9/+I4vORYePSN45ceBKyYELB+/cNlYZP8olGMWRI5eGEVxMrg8SB4ccDAf0kUruYgaKAMGzSvgEpp7soKbfoKMBv6rRpM3KOiBA4vlI5A57GCQQRs5/QdK9hNtnB81Yt6yEXMjh6Ocv4y5dHB/jvALG+a3dJhf6PDZIcOn+384bQHHHNPmf/DpfA47WB0IAhwh+GD8sWj4rEDOpZTNnuU8SReGcZnpOT4JipocvHzqkpXTI9bM3b47ms9PMGj1Zr05My2zKK84Jz3ParTlpOfbzOkGjeHwsd1h0Z8HRk0MWD5h+ZZZJxK+UKpEYBSTNtWkMeuVhlS9NcuWm5dVkGHNSjNlZKXl5mVCehWCPC6ee1fY0draajKZzCYuGjrY1jSQQBsZbHV7WuJ5cHBPzpUo8R1CqN+5c6enp4cUCW/9Qgo5RwfIXdoRsh9wAOxobm4GkRB2QKK7XK579+49f/4cyPz40WPQA/58wBI2vn79GvABBMGf+ObxMZDW9Pjx48uWLcN3tXfvXrz6AB0gxXOWgBd0kCtXrgAycF4Ie6A9epDe3l5cAECE8KKMLYqLVM0SKTCIG2xsBRZUctj0liw2T4f2wg2iZVVlFSk/igdm5NKQqHhgwTn8Oeivijp2QSWLxTt5p9jxn3+tjIuv2bO3DvnQ4caYM80JsS5+spPHb+Kl1PJEVTHJNbHJdpHcKdO45TqvTOsRSFti4ppOnXLx+R06fUeapSvDej7D2qWU+2TKTo2pS21oF0nbBSk9MnmHQtXGonT0GE0+maJVJHEKxU3Joto4QWNiil0o7TSleuSq6kPHnLHxLr6wPD5FuydWsjlWsjVWtjtOeZCnOiLQnZZa+epcjaXInFGakVuTX+Ksa8qRpaoOClx5DQ86z/dkZDsS+M1nEpxJyd1avU9rsosNdnFqi8zaIjO2akytWpNHZ4Hsb1VaXKq0ZnVGEd9UKc1xpNZ2FLjP13Zddfbdaj//8Oy1Z5dvv7x2/ysuVjoyFzT96YVbD7quPeruv+M+11fU4DZle/VpHqXWLVb4lPo2laFNa/LqjR690aUyuHTmvqycsxZru0bXYTC1ypSN8bymWL5PYXbr0l/d/HUC7S8ioa/Yt28fxfdEwhAF2AFhSdE7AAEff/zx2LFjaTILZDmkNdpjiI9fabiF772yohI9FTeCYtrNwSnxmzdvHsSOTwfms5DCY/z48SNHjoQI/+1vf4vjAzIwNKLwG5s2bdqwYQNOjY4rJCSElBmDmo9BywupQwg4iDnmzp07iB0ESTgpWXVCWcIWFi7kraMJroTsL9id1B5os2LFStDPF198sWvXLnAYwALdci5bGTsxMRHthw4dCloCoOARSSQSmsgzGKsDW1AKBAI8IjFb8p6CuqJDRm/89MnTn/4Dfu7E+XakHMJAf8DcMNE/cvzCiLELI8YgLwgfvSACmYuQgUzkEbCKy5z+gzHHwhVc9qfMtvgTdjDy4JBiOafe4HLUiPksL2CZgxJCENpCKg0uc9gxfwA75oQPR54XyQHH3AiuPieMy3PDR/gRdsz/AORB2AHmmDrvwylzP0A5jaMQjjmQmbZjhB+HHWPZvNmJlAMiB5hjxWfLohfs2rtRq1bmZ+cX5RYX55dwObekpqy+tryhudZZWVKdn1Wo16k374oIWj5p0bKPQqKn7D4WJVMmpVvS8zMK8jPys9KybOYMMEpmWrbFmJaqt5i0ZlBLti2nMKeIw453Y2SBMB6MTErKjNSB9c9opE4zVuibpF9J54Y/Ic6fPn128eLFxsZGtMRLiQYFA9NeUlmkXm4tRHer1+sFmuBLBoJw2JGT29TUdO3aNVDFkydPHj54CHy5efMmt37T9eu3b98GPTx8yG0EneClxzezmiWMIQLZ8kvoUNBTABouXLhAc1hwqPv374M/sFc7W3YSIxh8+eh02tragB04FFlMkKhSxubTFjKfFSSCqkwWzBS3Dwgj7cVg+3JmiCEPjzIWvhBnIWUJkUcxm25XzrQjpFCpq6vDXdOJ3qlL6X/+tfRMXMm2neU7dtQcOuDgJbglgtYUniMx3iFMcRsNzXJVTTy/Lllkl8g9So1XrvKKJPa4eBeP55VK2nTaNpu1I8fWk2Fp16jccoVdrGgBl8Qn22PifEJRq0DUKuKieXZpjR6J0iWUNicLaxMEbpnWJde6VLqO1DSnUlUbG++Rytr1Oq/JXK8yGQ4mi7bGSHclqg4IdMck+pMyK0+dq7aW2fIaSiuctfU+d2tpaq54Z1yeyHbV3XO9xdWqNTQLRM1CsUen70rPbNWmNaTo7DKTQ2H0Agt0qW1pBW3mvOYUbZNQ47Pk1qrTK5U5zZYqb25Ld7n3XJ3vQmNbf2vfve4rzy6CPB4AOzhX0/4HLy7ffdh9/VFX/x1HX09uXZ3EVJ0osfMlDoHMKzc6BerGeEkDT+LWGL0mm8+WfS6/8KzV1q4zdBrNPpXanpDUcDLGK1S4lIZX/bd+NbL8EhK+tT179kDYY3CPCs1PSWDLt0KCYrgP4Bg3bhxENakQgBFffvnlyZMnIYOtzA0rly3rODg+KWGu4lRCbFOgUtJ2QGbT7Flgx5gxY4YPH/7BBx/87ne/w6+RkZEk7AFAO3fupFms27dvx0b8RC6iZFIhq0oAS0Qkg6oOzr4yhyMPPz8/MgnhssE006dPJ3whjQuYY3ASL0qK1467w6HQLCoqCiclr1IM1UAP6Syhw4+Li8MRcNkjRoxAvwq8QD+Ph0DdOy1Ui2bo60jPQdihZmFSDxw4gCf8+PHjn/4Dfu4E7Ijh75u3dPz0BUNnzB85a+GYkOUzw1bPWhQxcUHY2AXhHHnMDxs1L2zU/IhR80EeTPlBmVQghB1giwUsqBdK0MZbf44Vg5lpMgaw423mFBsj5y3jlBlvM7fxrVUFJek2BiDjbeYsLGFcOTeCi4P+6cIPp87/YOoCLpOeA8wx2e/9yXM48sBNfeY/oOoIZtNYQsYuDGO2lYhJLCD6FMYc06Oi/fcf2q7XaSpKKmsr6lGWF5VXlVblZeSVFVRUldQ2Vdkbqprqqxrrq+vjeUcj1n6+KGpC4PIJq7fNTZGdLMjNyc/KL8jKz8nIAbUU5hblZublZuTlZxZYDVaD2qBT6KxG69nes+8IOyD+6SUD5FrZZA28dha2yDtZB1AnywupQ/Aioj3QGCVIgqwbTqcTjSm2bi6bxJHKEr5e/OR0OCHy0Rh0AvHf0dGRbuOidvb19d26dQt48ejRI3AGhfS4fPkySOL8+fOXLl1CBWIbr/uxY8cweggPDw8ODsY3iQ8J2I7rRC8D6Hn69Onr169fvnwJ/nj16hVKYAeu/NSpUxs3bsRV+VjChRAZkINn0UB8VfoO09ksWdwgeZVa2ZJ42WwBGrLvUk9ESpFsFkKNtldVVRF/0MCIsKOMmWzQhuw4aFD8jl1K//O/qlPENceO1xw7Unf6RAs/yS1J8UpS7LyEen5iq1nvMZkaxdI6kdil0fgMhi6DvlOlbpPI+nSms5a0TnNqV05mX2FOm1nXIhQ0JCdXxPHq+PJ6vqQ+WdAqkzuFEi6nSNoVGrdIZk9KqTsd35iQ4lWbIKfbTBZfqrVVq28SS7x6fbvV0maxnCsoqpGaVLtiUzadkO6M1xwW6k5IzPHKTKmp2JrdUF7pcznPdnc6KuqNJ+WG44rW0pZb3efPllU5tMYWpc6Jw1rSO9NynFqrQ2/1mNO9RotXn9qVXeI159Yly2sTRT5jWpMqtUxkrtUVttjqWnObfIXNbUUNXRUtl+ydt9svPeq78fjCzWdX7ry8du+rK/cf9dx41H3jlr2vI6O6PFlbHiusi0upi+W3CJT2JFXlCUFtvLjdmNZhyfSlZXbY0gEcXo2uzWDuTbV6xNKmM/GtArFLoX51/cav2PFLSBgDQL6SWWEnS/v27QNVoLuAxI2NjQU3jB8/HqKaVAgoIT4hgDF8JzcssiCTvpM82LJZqB5I38SERJrM8mPs+Pjjjz/66CPy6njvvfdAHuCG5cuXD14DAQd5gCJhO37FwGmQMOazAOdkHPn/xQ4iJFJ1gBJAFRh6ocGPI5WRtoPIAyyC28RBcJZly5aRVynZWUAPWSyGIW4Wd41jgjlGjRyFKyQtCHWJGSyRt19DQ0M+i7NuY670QhYlnUKZoev+6T/g505/+P7bM7y9c5eMmzLng4mf/m7itPe3bl997MTebbtXRa2dH7Rsin/4R/NDx8zlVoofNS+cg48Fy5BHowSFILM/uTx/GbcGGzLg40fAMcqfyyM5v43loI3hpNsgi8lgJk0G2VM4kwrjEtJt+IUO8wsbRjoPUnXM5iwsw+aGj5gVPIyhxvvTFn4wzf/DaRyCcNiBe0E5bd6Hn84fQgoPwg6/xaPnLOGwg7OtLGN6jqipISs+W7520cHDu0wGQ2lRaVVJdV1FY2VJVWl+aUVxRXFeSW1ZXU0pF37U5+pqd3c6GtwQ0Rt2LA1e8Unwyo8jNk4/ErvRatXlZ+WVFZYX5RSVFpaXFpZVcuBSUVZYlpeRCxwpzi0qzi+5dOHSOzKyAAVUKpVSoVQoFBTeDl+gSCTCnwALI1u3MI0FB7OwZGKRcbldlEp8ljR7xeFwQDyTao7a6FkYXYqugWRnCW+wx+MBAZCvJeCg/3r/3bt3wS4U2guccfbsWRyzu7sb5AEEwcEhudEeiI1vmGLp0NIJ+DxwZDTD209GmW+++ebNmzcogS/4fnAXMTEx6C8AOjgOzouviNw/ySBCWo1UtsQA3WM6i4iMRKSFxsXMs4zUGOTWnsNm1VJPhJ/I+ktaDVKKVLBUzmwxpPkoZI6679al9L+atFq7MKUxmVeXlFiXmNAQF+uRS1vVSoda7lLIHDJJo0TUopC5lQqXRNwuk3UpVD6pqkNl7jVndqbaurMyujKsDckJZYcPlh09VnE6vkVudKhTW1Q6t0bdIhY38ZKdKSkdKuyi8UlkjkSeSyB1y7QtcjWHHebUbqutIzW1Mz2jNyev05bVl5HrM2aUJshlW44KNh6V703QnhAZY+SWZE2uwVZRUNTe6r7Q29XV0lphLtYeU1YaSy67e277ersLq7ymHJfS4tGldVoyOywZPltmb05+tzWDC1qqt7jVhiaJolks6zKYHRJ1aay0MElfocitN5W6Mqtbc+s8BQ1d5c7L9u5bvku3Oi7d7bny8Gz/0/PclJbHPTduNPf15DXaNVktilSHSN0Qx29KELYINRWx4pokqVuhaRJIa/jiZrnCq9a4lGqX3tRhTe/Qmxx8oUMgcqs0r/p/xY5fRAJ20BooFLsC0hFSH9iRkJCA3oDH40GUT5gwAbL584GE8Qx6PHR36NbQKaEHk0gk5OdBE9bSWMiANGsaP5kPAhjUdpBX6cQJE8eNGzdoYQF/QNgDLMjIAnG+devW6OhobImIiKA4pPhzxYoVYWFhGEQNOpOSZeTHzEEWFiQwB62di4O///77M2bMAJ1gO7hnIgtTNsgcbA7vhEFrS1BQEGEHHgXZWfAcqItDrw56wF3gykeNGnXgwAHcKU0PRFdWysI005gK3Ro5vRWyZblI84Gj4QbRb//0H/BzJ2DHycQvZwePmez3wcTp702aPuTkqYMmvSHVaBam8A4d3bl5+7KIVX6LwifNW/o3+CD+QH1u2Ii5YdwCbPMiRsyPHEmrv85DhYsi+pY/CDvIzkI2FM56EjWc03MMaD7mRRJVDANqzIvkPDkIO+Zyuo1hcxh2+IVxCDI7ZNisxUM+XzLUL5TzJ+UsLPOZSymYY8DCMmBkGUJ5uv+wmYEjZgWO/DxoFLBjQegETtXBmVemhCyfEb0p5MSJ/RaTuaSwtLy4HKBQXVpbUVRZkFlQnFvcWNXkqHfXVzS6mjwdrd0eexugJNVo2n9kc8TamYtXTgqJnrxpb7BSm1yUV1iSX1KaX4ZcmFNYWVJRlFeUm5lTkFNQW17HLQVX0/juloIDDRj0BiIJqVSKLxCiGiBMSwfJ5XIABCkDSAMJYW9j0zSwC+Q03k6yXwAO8OKivVarJV8Q/NTU1EQBNpDAN6i73W6v14t98Z2jAtQAcNDSTWAOwEFXVxeY49KlS0AHlDQ/pburGx8Ajo83HqMWdAogG3wPbW1taHzr1q2vv/76hx9+oEBhkO6gEBwNoACuBxL19vZevHixv78fZ/+xoYRuJ52F4QNMUJnHgqSR1xV9bEUDa9KWMDcOcunIZVNXUOIs6OwoiMigbnbQLkP7ZjM31XeLHX9tNpmaxKJmkahFImkSpNTHJUBqeg1GbsapWNjAS2iSCJwaqV2W0iJIciYkOJP4Tr6sJUXrkJpcytQ2g6lFKircvzdr+47cL/eXHjnVJJC3KPUOtcEpU7SIxI28pKakBJcwpUOp6FQpO5Qqn0rbqtS1yJQ+vdlnMPdaM/rSs7ozcnoycjtT09tUBi7iuMJUdJyv3HRYsvOU9qTUEKM0JqlsCkOBLbPV3nyxp+usx+eusNuSTVkCS0eF8073xeuOdl9aYYvQ4JaY3AqDT63v1Bl7Uy04XWN8UgsvxSWSerTazrS0Do2+4XRi4f5T2UeTM2OURaL0RlOZN7fRW9DkKWjsrfZcc5294T1/o+38Dc+5O76Lj/tuPD9/50ZT38USV2dGRZs5q11rdgtlDp7IKVQ2ChROhcatVNYlCysTBXaJwq1Q2eWqJpXeZbD6jFanWN6cnOJUal7d+DVc2C8ioRfC4AQjE4j2devWQdyeOHEC3Rp6DIp5BdlP9giazAJxDko4c+YMWWHQgIvBxQJXYAt2pNDgpOKNj48n7MBe09jKLGRhGTt27LBhw957773f/OY3kPdL2ar3AAu6gE2bNuF6cF5ABnl14IxhLIWGhqIe+KOZLMQcg3NY5s7hprHgUnGu8ePHDx069He/+91nn32GlqAKXAZhxyBzfMQS6miPuwsICBjEDtAPsAMdO8V4RP958OBBNCNtB54A6ArdFPVmNHAqYKEOaaBVzOIf5rHVM9ASDxn8dO/evZ/+A37uRNjxecCYT2a9P/HT96bNGpWUeCbdkl6Yi/F+bXF+qVlvjI87tXPPupXrA5ZEfTpv6bg5IaPmhIycG8rlOaEjGHlweU74CC54OeWIEXPDuUwMsSBq5IIoBhPLyHTCFBsDfh4ceSwb8NvgrCfDWcu32EEbB2bMcmHBZgYPAXmAP6b7c8wx6NsxdR6XB7GDMmHHjEUjZgaMmB00at6ScczIMikw8pPFUZ+t3RQaF3s8w2oDMZQXlTMlR1ldRUN9RUNFcWV5YTkQpLHG3ljd7Ghwue0e0APqJfmlMjl/w47FS1ZNDl718bJNMxOFB9Jt5qy0zBxbTn5mfo4tu7SglDtCUTmOXFlcWVtRV1dV/+6Co4MYyEsUrEDkQaKddB60dBAxB0lZEqJ48yhQPzYCXAAQzc3NeEHxleITxa94TSGGAc5kZWgcSKBpNMbpgB3gFWDHTZZonfqzZ8+eP38edYrGQYuq0BrWfX19OBGpECD7cToACjAFSAFqefny5bfffgvyoMkv4A+QB6gFbYhdcIQXL17gRNgC+CDVC/mTAmgGZ9IWF3Mx07IHJqeQwYUUj6gUsSXwyEBDqkjcPj7FUhYDHjdLGhFSThJ8FLFJyJksBMg7NbL89b8ajakNQolbImuTK1xiiVMqbdMZ3Gptq1rdrlO3adU+s8GbZvDZ9J2pOodQ0JCQ2MyX2kX6Wp66IlZqlyhdEkn58RMlp+PKE/gVp+IaEnjNMrldqbULZV6VFvK4KvZ0XexptyjFq1K0m02daTaf0dym0bXrDC6Fzq00eNVGj9oAFnHLVK0ShUei9gpVTacEGduPKbed0BwRGc4ozUlqi1ibqTM3VlRe6Oq62tfX4/JW2gqsPG1DemV/5/nHl69frbV7ZSa3QOsQqn0SZbtI4pVIHEJRQ2yiI1ngFUvdMrnPbHHL1NWHjhfv2V96KjHjpDA7wVClLGxJq2nNaXDn1neUOS40dYI8brZe6Hedve7oedhz/avLD2429F4scHiMBe2mjC6D2SOV23kp9WcS7UkpPrW2y2rxWi0tgC251mcwuvWGFpXRoTa3aoxukdSRyG/ki15evf4rdvwSEnqbHTt2UHDxbdu2HT16FCMcfIDokSBu0WVB9JK1AjKbtAgQ4fv37485E4Mej3S9AA6QB/pAdIAY3JOvAxI6RvKoGPQnxXFAA6NGjfrwww///u///u/+7u/wK/AiPDw8MjJy1apVGzZsgLwn2wpAJCQkxJ/F4SDdBhlWBkvSdvyYOcAWn7PQqDgRzkK+I6AQ7A4AwsX/WNUxkQVIxfWQOgfNcExcBh4FLfKyZ88e3JSRhUFDV49Lwi4jR44cPXo07vqtpZmNwawsFjMeGlBMz6KYoCtDR0dmd/yKA+J23oG24/fff3MyYQ+wYxKw47P3Zs+fKBQkF+WWlLE4m5UlNTVl9XmZBWqFSpjCO3X60Jbty8NX+fmHTpwbMsYvZKTf0hFzGXlw80pCOTggOwuAY07ocGSCj7kRlDmHjDks2NccTqvBKTxIsfHWkyPirQPp/EhO58G8OpiqI2y4HzddlpiDK1GfvYSw4/1PF3wwdf77U+a8P9nvPZQMOz6cNm8IMcenC8m9Y8SsgJFzgscuCPnIP2xiQMQnS6I+W7cpnJcUl5ORU1bEKTmQwRO49/KiitryuvqK+rKCsoLsgrLCCgBETVldU60d8IHcVG3Pz8k5eGJr+NoZIdGfRGyYdihmncWqrigpL8svL8krLckrqS6rqa9qqCqtpiOXFpSVFZVduXj5X9+NkcXpdBoGkpEl0jTSpCnUTWyFAnK3/JvKkfl50GsKaU2WFEhuMosWFxXXVNfQ6L+C+T3Us7iiVLa2ttpsNhwf4h9AAMggxQYIA4SA+u3bt2nJNzK7INEcXRBDR0dHG0vYiB3JBQRv/6NHj8jD4/e///0f/vAHkAdk/LNnz4Avd+/cBXPgmK9evQJ5oPGtW7fAH9gRlIMj4yAoQTAejweXBKDBdZKehi4eJU11KcjnwogRiJDpl/w88EGWs8Q5fJRwzDFIHuSMRsqSd+pS+tf/5UzLcil1HrnSo5B5lXKfVtWu14MVPGp1l8XQZTJ61XqHQukzGTpSIUQ1LVKZV21wyYwNyeqaWHEdT9SuN3g1BofO5DJaPDpdm0blVClr+CJgR5cxtTs1tYGXUHvmpJPPa1XJvRZjd3ZmuzW1zaDzaDRNEnm9QFKXkNwsELdIuTXkPAqtPSHFGSeoP5ZQfDDWtjdWse2k9kiyOV5ukxgyNOaKnPzuVm//hQuXu3taaxoLNOlZUktPU9uT67efnb/cY81pPMFvPMVr5QlbBSkOsdAplTUn8n1yZYdK45Iq203WNrWu6VRM04lTDqGsSqTPTzaWSLMr1QX2tEpPXmNnmbO3xnOpubvfea7f0XutpeduxxUOOxp7z+c2u9RZHlVqu0rnkSpr45LLj5xujE1yS+WdFktXVpZbZ7YLlZ2m1A6L1aky2qUaB24qWeCM5WKlf3Xl6n/+P79ix//5BOwAWNBKK4mJiejNyNCZw0LpoLOKj4+HIIechiynSBgQ+bt378bQPzY2FlIWbEG2Zkho1EEt9Cf6K7lcDqAhPQe5dFBA0iFDhvz2t78Fc/zmN7/BRgBBUFDQ0qWhNH+V1nLbu3cv+bpiS1hYWGBgIAhj7kBMDiQQBv78iXmF9DGk6hgxYsT7778PvgEtATuwI+iHHDsGfTtoTg2uAXUiKlxDdHT0+vXrAQqHDh0ifgJzCIVC/DRu3DgcFrucOnUKI8Z0NokPD4qa4d4TEhIwEMV26gNpYgG2L1++PCoq6t1gx/G4XbMWjf5k1vuTZr4fEjEHV19WWFmQVZSdngsxXF/VWJxbkpGanpOeU5BVaDWk8nlxu79cvzx6UVDktLlLxvotGTGHiw06/HOWOdQI41ADFbDCXDblhGVu2VjOaBLO+WqQxwYzqXCaj7deHUCN8GFkauH0HKzZnFCunB0ybCZbeIWpOgawYyEHHFPnvT9l3vuTB7GDaGPB0LeqjoVDZ/gPY6qO0XMXj58fMmFR6KTQFZ9v3b6Sz0ssziutragHEwAsKkuquLheLMwGQKG8sByVvKy8guxCtqW8rqLBXutsqrFXFleXFJRIZInRXyxaGj05bMPUbYeWao38+uqaxuqm5lpHS73T1dTqam4FeWBLQ3Ujjl+SX3L18tV/+9d/++n/4L9L/xPsgIjFd0W6RCtbhgAlKT/0LIZuqplzDkWF/DzI25nDi6y3s0zxUkLKtrDo43lsHTUyCtKbCklczqaqokfAuRobG4EdGP3jC4doh+AHQFwaSNev9wMUwBDADpTADjBBX1+fz+cDEIA8qD0tKUfoAHC5c+cOPgDs8vz5c1q6hcKOgUIogipa/n/svXd0XFWW7z/TM9NgwGR+DNEG2zhgjG1sjO0GEwwYDI1xtrJUpco555yDVFXKqVQqqaKkUpZt2cYBR2yCyRm6abp7ftP9pnvyW7/XPb/vvdvW68U/b71ePV78wVlXd926de6555bu2ftz9tlnH+SZDZpOE3dxQBNnsEeGt9iEzOfPn8cZ2tOydhR8DHyGypOvxixYEFXgkS8Nf7Z3dHcxE2jx7GTjwTMODQ3hxxkdHf3Nb35zJbFjuqVz2h8ed7gmXY4JYAEDH/Yxi3XK6Twc8k273EWlcUCkyin0ozYXs6y813swGJ2w+kf17hGdvajQjhsth32RKXfwYDByLB470hieCQZGTLaSyTrp9uB43Goa1aknjHomaFhT47HOtqlIaDLgHXc7Sg5nyWovqDQjZuu421+ye/J6e6peXJCqB3W2nN6ZlBn9e7j2vfXuenWTJdTkjHSEGkv92VOHj5w/efLwxGSxKx0zBzPx5JtHTn351sUzA/l+jqS/ip/lCAaVqlGHvWS19YvkwwbbpDsyagtNuWNDetdAvWSwXjhpsI65oz36QLM60GFoyHg7SrH+ifbC/p7Bw/0TJwszZwYPnR0+dH7y+Ccn3nlr6NiJ9tKEt3XI4CnpbSWjLaPQpUXKtEhR1BjG3Z4D4fCoxV2QGSZtzimvb9TuGzY5RoyWMZV6mC8cN5i/+sHa8f1IEDIVFRUU9QtyCa0SZ9AYGfNknIlErNVqobOhp+fPn0/YsXLlSmBHXV0diITULQQdNKvFYkG/C/IQRTGrkLBp9+7dZGMgIweA45prrgENYH8dm+bOnQsWARCsWPEwmT1AKqCN2cCp2ON2gIBnn3121r0DVXqQnbHynYQakgMH3euqq67CnrAD+QFAxByEHWR9oaEWMucQdgB0XnjhhZdffpnP5xM3QLArlUqgzy233ALsQE61Wo2eJMR1gg0ZgGPkgdjHI+MXw28COU/f4lfFL4Nr8WhXCDskuxc/csu9S+bcu/gaKONoOJLqTnc0dzaGYiAPKOa2BOO10NnS2dLY0hpv7WjqiPjDeo2qrm7vy9sff/y5ZSs2/P2SR29cvO56Njz5Dcs2MhtZO5gDdioKTXklF9GlG+YuXc9sD7LksYxGUi4NqVz/4BMsdmxiGIWxc2y8AfnBGQCOhWuvZciDWX5lLjPIAuxYDuC4+p5lV9+z9GoGO5bNmX/ZsYOxeTDYQTNZbmIWj11324rH7lj/1LKyyu0uu70l1pbtKzCrtcXb8VDsmEgqFoknIom2eGtzQxMOOlu6Opo7WmItHS0d4BJm/KUv39+TGUhmEvHo7qrNG7bMX7/lns07lin0Vcmujnx6cKQwMT40NT40MVoYKw4MprvTfczsmOxA78AbZ9/413+9ItiB3ryRXf3I52WwgwZcZrGDzBuz/qQ0rEAmjdYWxgeikZ3qgjMACyhaGpvoYBOBc5wNDY6WPzCQgQ4ulUrADlyFloz85D0KsABPACYAB7Su26fskrOgAXIynZiYgNY/cOAAPp49exZwACgBJZCxBFfhI0XvwLXAC4o5hvQpG8+DQnrgq88//5ymzCAPHZOhBXvc67333qN1bj/++GN8++WXX6IcspGgVmhjABGyu4BCaIkZJDwOHiqfzzNclWXMJKjqFLvuHZ6Iakss9W//9m/f/Qf896U//PFAe+dkIDhsd0x7PBM2+7BWP2G2jRut+y2OQ3bHfqttSGVM1Yr6udJxq2sqFBrzuCec3lG9bdLoHDfaimLpqFRx0OyY1Fpfc/nPtbUfbmLig824mCBd037/tN+73+eadljHjboxq/FAOHCkJTHq9Ux6GWsHE0Y9Epvy+Kd8gelgZMTlLxjteZ1h1OkpeUJD7vCQ3Z+S66I1IneVNKEPtroaW1zBvqbWmfHxc6dOAfcOlMb7Yl3tjviB/pEPTp377MyZAw3xglKX5vAKUtmI1QJOKhqtGbW5ZI+OWmIj6lAfz9BdUZ+r5k4odFO2QMbga5bYm2TublMs6+ssxdPjbTnAx0xy9PXc9BsjR96cOvHJ6xff3//G6b7JQ7G+UXt0xOobNDqKBntea8lrzcNmG7YJp2tYbRrgyIZV2iGjqWR2DRntJaNlRKkscLmjOsPXZ3/Aju9FgkjhcrnoQZGdlayV1AVqY5ewB09s2LBhHhvcc9GiRRQ3bMeOHeRzCrkHkRW+7DhPIQCwZ5zeVGqFQgE6ASUAO8AWIAxAAGst+HsQAEVbpwEXfAX1jzOPPPII2AKEAc7Yw8ZOrWMT4KO8vHznzp2gAWABeX4AgMj+sexyWFJUEnxzww03gGz+7u/+7m//9m+vvvpqUMIKNlw6DfRQ+lODxx1s4FQWfVbQ3bds2fLSSy+Bq/BoNDqsUqnwLWp700034dY4n+pN4WHx1JDYyIPuJZQCgAP9Q2Ac8kfYCcZQB/it8NNt2rQJAvO7/4C/dAJ2cMXAjpvvXTpn/tK5NZxdrc0t/b2Zgd5sa6KtNd6e6uzvbuuB0m1uhBqOtzA2j/bWWFtTtCkaiNqtFrGY88r2p37yzIMPb7xz6bqbl6y7Afyx5LHrl224FEuU2dazdgsWPsjUsWQ9SOI68ATzkd1oPGU5mTpou4Qd17OMwgyyMKu+sdYOxuCx7npmeGX5HDDH3UuuwsaSB4sdl3077mcm0N7wwOobF6++aemaWx569I71Ty4tr9rh9/n72Bkrw3mQwXCyvbenLYkz/T0D3a097U3tYKyulq7edvTs+0AYjM9HR7KzpaOrrXugt384PzKUHelo6xArqp7+6bLHnr97w4vzdtdsamz0ZVL5QrpU7B8ezpXAHLm+XLIj2dfV19Pe093WdeqKzWRBmwQB2G12r4eJTOdko8Sg3bouT92myVQEH8TClMjUwZhD2Jkg+EhB/QAZxCIetweFoChc1dvbm8vloZ5H2YT8YB3kp4gatIDcbBgPBjs++RQH4AkaSdm/fz80/czMDNk2gAXgCWAHLgEK4CpagZawg0r44osvvvnmG3ADPoI5kBk8QeE9CEFwDBS4wEZVBx+cPn36HXYlOWQjQAFwzK5F9ys24QxujRJQ2iymULyQS4vCMONFzCARPQjBDWpCBPOf//mf3/0H/PelP/zxYGfHiMeTMRhHzLYhjSkj0+QU+rxYO6owTEhV03r9AZu9JFUOS5STVvt0yD/mdo6abEWpZlxrHjNaCkLBYD2vJFVkueJpg+N8d+pgvGHUaBjR6ic9nv2h0Ew0fLSpccJlz8okOZlkxGw6HG2c8Pon3d4p4IvDe6ih5WA0MRWITIcaDoYbRm2OKb//SFPTdCQ25otM+MIjDk9aa4mLDRGFK2ENJ2z+Fl9oqD/z2uFDb5w7c+rosenCWNLf0ettOT4y9ek7b3908vWZtracWlNSqkp63XQoeKChsWR1TrrC46ZwgWdM1cj6agVD9aJJqWZK5yio7B0ic1xoa1UHk9ZEIdQz0jQw1pqb7hw6NjB5rnTk7f0nPz393henP3xz+NjZ9PhUoHXMHSnZQUiOjNpYNFhHrK68zpTXGDICxUC1MC+SZpXqjMqYVhlyBtOgWpXj1RdUqi/OnPkBO74PKZfLiUQiSKTBwUEa9qVR0dbWVkgwUqhbt26F5qZZphSHA1pZLpPrtDqIQfICobFmJDL0QhlD74rFYmhc0AOw4B526izKASts2LDxqaeeoskpNK+VjBPQ6LgLzqxfv37z5s3btm3bu3dvZWUl4KOioqKsrAzYQQ6nUOGPPfYYgAYwQdFE5rPRSGnBlKuuuupHP/rRX/3VX/31X/81ea3SWAwKJ9+OWZvHLHbgANiBPGAsVAy3eOWVV8BVeCKyZ4Mk8BQoH9iEyofZaSzoeep0OjIUabVa4BF+DRCYRCLBRxyTwVsgEODW4BgIw+/+A/7S6dtf/gzY8cCqm+9ZMmfh8ptr6/Y2NSbSPf29nX0tsdbu1mQhPZTuGWhvAnY0R/0RbG3xtpbGtmigIR6Jh30Rt81lt1rVakV55fYXXtn42NOLHnrs9sVrbnxg7VxsDB8wxMBwBsMfNGKykWGOxeuuXQzyYM7/723ZRsYEsowhlUtnlrCmDmAHuZTimL32ukvY8eCce5cypo5L2PEggx33XZ7GsuDh6xnsWAXsuHn52js2PrWionKn2+lsa25rbWrtbOnqT2Zyfcx02a7Wrvbmjo7mLiAIuYJ2tXanuwfSPRmQxEAym2xPtSXaEw1NXW1dKQYjUl1tSa/Pvr3sifXP3/vYlrue3rZEa+Z1d3T0daV7O/oYf46OJBP8ozudSWU7mtvbm9quXHB09A/wOlosFjQt8ioFdtCIJo7xzjWyC56RMwde2WZ2pRK0ZBrkQx7siU6SySS6/uj3IzOAw2qxgi3QUUAh5HQ5MjIyMTEB+MB7b7VacQbYAeY4ePDgsWPHCCCgrfE2k2/HbDCPkydPgkuQh8ZKoMJBGOfPnyfsgNYnIwcUPJlJZjnj66++BivggFCALBwohLCDzoNsTpw4QW6n+IjSUAEarAF2fP3119h/++23wA7wBz7iRsiAa3HwJZsILPARxb7Ppll7yWeXEz7++7//+3f/Af996Q9/mGpuKlisGZ1+UGMsyrUFjaVPrOnhSvNizWC9eFKhOhXwHzabp3SGKattf8i/P+AfNdoyfGVRrClp9AWRMM/hpjn89ipuXm0+mugomm39Enlaohh2OMf9gf3h8HQ0PGizpGXSXp6gpNYf8IZAGPuD0TGrO6M2TQSih5vbpvzhcVdgJhg96PEdCgZPNDcfbwaOxCdDsSF3cMgTynkbIgprWOtucjU0+SJ9Hd1TY2OnXj/+5pkzp48cH0sNt9gaSq2Zt46e+fLd9y7OHBoPBXNqZdGgH/P5UIERm23c7CiItcny+j6OOCOQF+ulJZG6JDdmRNpuga5VbE3IPR3GWL+7Ix/uGWnJTrYX93cNH89Nv3ng5OcXPvzFe199eOT8e+PH9kc6By0AJv+QwZaXafIydclo71ea+mT6Pp48XcXL8UUDUmW3SJXSWvIuT0Gnz/H4owbDzy/8MJPle5FAG2q1GqwAQXTkyBHs0UGC/KFeO/pU4I99+/ZRfE/CDmjxp59+mgJ7QBIiG7ph2Bv0BoCIhl2QFoIRX4E5cB4Sb/v27Rs3XnLdmI3PAaQgt5Jnn312zZo1NDd17ty5t91226JFi9atWwe4oTAe5GEKFIC+RyEgAzDHo48yi7OsWrUKrIDqgV1w4Q033IASsP/xj38M5gB5/M3f/A2+msUOihX2Hewg3w5UAHlAPMAO3Lq8vByimGKC4fcBOixZsgQ1ROGosJ1N4CogEX5A/GK0kC+Nv9PUHjKE49fAM95+++3AJsi97/4D/tKJwQ7R7gdW3nzP4quXrbqTz69LNDS3Jtobgg1Rf7SrpbunNQn46GrtaY23dTR1tMU7mhpa4pEmn8sX8Ph9Dm/QE2wI4VRrY6jRhWeUcLfvev6JzQ+v3HDPolVMFNGFa65btPY62tPc18XEHMx2HXv8Jx4bLG0ALB549FpsTAZmY9e1Z69lHUspP4Md85YxYytsfDDG7DEbrmM+uzHYsRLYcdPytXc+s2Udl1vjtDmjgWg8EvO5PHazDZWnMRSfy+u0OoLuUEdTF3CkLdHGUFdbb7KDWbme8fxgF3XraO5khp+CjbFgDL9MorGRK9z1xIuLHn3uzg0v3FvJ39LQEOhp7+5p78FG7AKmKfQXUp29nS0drx8/cYWwo7+/Hy2Kwn+RJylF7KADsjSSqYM6AfhIZjp8hQvRRNEOgR1Niaa+vj6QAVR4b28vsBrtFu8xegkoDdhRKBQAHPv37x8aGkI5IBL0Raanpw8dOjQzMwMZAcVPcEAGCaj/i5cTPoIJgAt40Sk+B/KwLqjM+Ai+BaaQ7gd2ID+KQoH4lsZfgAgoDTRATqZvnLvEN+ReCnw5ffo0MmNPMVL/FCmIKpCAL7THGQpNRh/pDO1xhmwh77Eh3pENe8IOHFxZ7PjjSKShX63NafUAgrxAnpHp0zJdUqLOKHW5OkGxmnvMZj+gN4zr9ONWy6jLOWq3ZSSKVI04zZWO6k1DcnmGy+up48fL69r48iGbP6cyZVWGfrV+IhAaDwSnI9GJUHjY7QHc9DNLxmtGTPZBm3s6GJm0uvMq/XQ4ciLZdSAUmnT7pr2BA073pMmy3+2ZaYjOxOPTDYmC3TvsC000trQbnQGZOWGPNvsauxJtpWz+2MGZC6dPvX3u3ImDR1PR7l5v59HsgTdnTn544uwpvEh+36DDPeLxTQaDJaN5UKbu5wh7a/l9MlVOpslxxAO1gqxQXlDqmLESW6DT5E9oA93mWNrdNhjvH2srTHYUjwyMn5089skb7//6k2++OvfBZ0fOH23PDOgsw2bbqNE8JFUUhZJxna1fZkwKVH08aaqGm+WL+sXyXrG6aPOOh2NDemN/VU1Jqfr5Gz+sQPu9SOjYQGqha8TEBGI9tCBb0E2CvoTShZSD4IJCXbp0KWEHgGDBggVPPvkk1CoyGNhIP1DJUqkUB/X19bRAGs7rdDoIum52/UjAR3V1NbRyZWXl7KINfD4fFwqFQh6Ph/Nbt259ZPUjuMvNN9984403gg+AFJs2baLJLDR7hfU83YCTwJcXX3wRLEJh1AEKAJFl7IK3NFnm6quvJuz40Y9+dPfdd7MTXZhBFiIPSsCO2cksOAB2gE5QPqhi27ZteByaRUxWHNR58eLFBEagHzwvoIqWs0FOKIK6ujo8CJ6IyyY8HE3zgcBHhW+99VY88tf//SvQAjs4ol2LHr7pniVzVq9fIBHzQ75wQ6jR7w4kok09bb3gjObG1uaGFnxsRqe4oTURbQl6Qlajxaw3Wgxmt93tsrl8Tm/A7Y8GwtFg1ONwK+Wyiqpdz21dv2r9/KWP3LZo9Q33rbz2/pXXLFx9LbtOPYMUDzD7S8yxiHXaYG0Yl8Bi0RomJ23Mt2uum/248BFmW8QG7WAmsPzJNhuuYz4zk+W6BSuux6OtfGzeiy9vksvEQW8oEW3GBm6I+MNgjhB69P5wNBDxu30Osw0U1R7vwFMDF9qYg1RvR19zI5490RxjXE3xVaqLcXwBqXS3dHc0t+t00md/unLt5jsf23L31r2r3X59Z2trc2NTR0tHb1eqp6O3tzPV3d7T1NgUj8ZOHDtxheJ2ABFA9GhyaK5mdpljNDDyn8J7hteUPDxoyJOmupCfByiYriJTZKwxBuw4fvw4WvvBgweTySRaKRohXmgUAuzI5/PDw8MHDhwAf6BknO/q6pqcnAR5QEaAEo4ePQrFT64Yb7DpwnlmIIMGUJDwltMqtThGztdee41m2xJ2QMd/xjph4HIUNckmFE5zaEEb5KYKJiC8eJN1NaVE03dZn9Zz5KBKHh6EDrMDJZ+xBhViEeKSn7EJzIG6YY/8qAw5qKLmH7DpU9bRFfsr7NsxGmnM60wDUtWwVJWtEw4IlRmlIWe0FY2W1O6K5Es7RniSokAypFCOmIzDVlNOIe2t5fTV8dM88aTdPmEx5ySytFjWWieMVwv75OZhA9DEkzNaJoKhEY93KhwZDzWMAkH8wWGrZVCnHbFaU2rtsNU+YbQMyVUHgsFDHS1jPs+YyzXu8UzbHCWVrmS2TTZED3e2H4g1Dlttow7XmMefs3kiIk1YYYvZAu2RplxP34GxiTPHj79z/vybZ87uL4ynfF3Djbn9nWNnRo+dnzy4v6V1yB0o2j2TvsCw3pwTKTN8ybBGP2K2FiTSTHl1urImL5GVzOYJn28q0pD1Bpt0jiaVq9uWyEZ6S02Zqc7iTHr0WHH/W4fOfvPu5z9/8+PPj791oThVBAnZ7GMWS1Ei6a+py/MkGZ4yXSfMC8UFmTwnUwyIFX0CWU5nHPf48jJFqqwyxa3/9MTr/9///J/f/Rf8kK54QpOHaEJ/ZtYPjFo3ujroOEEiQXyBD6CPobABHLTQCQgA9EA6VSKRQGqJRCJoXGahdw4HRIILgTIoHGVCpLS0tED0kQ0Ae1q4FUmr1UJ46tmE0qCtX3nlFbAFmAAKnoKy43YL2RCiAIunn36aQozQPNudO3dSZNVdu3YBQX7yk58sY4N23HbbbbPWDuzvuOMOmvYyn03z2KhlZOog7CCXUvLt2LhxI4AGhIR6GtggkEgQvxXlFagGanX99ddv3rwZqIFnwVMjG5gDcEZdSjx+9aUV9ZhpPvhxkG316tW4BQT4z3/28+/+A/7SaRY77l1yzYZNy1UKqcvmDHqDPqc/Fk4AMmLBeCyUiPiiUX+0Ab38UKIxGPc7Ay6r0+fyRoORoC9oNZjkYolcItap1Sa90WP32E1Oh8Wh12j27Nq29adPPf7MioceveuBlTfdv4JduW3lNQtWXbNg9TULHrnmMkxcw7AFgxcMiwBKGOx4hMlz/6rLmZmYYHPYxWavue/hOQtWMqHQmUAdD7IGDxpeoRBhD2E/9/4VNyxeeeuaxxbu3v2y3WSJhxMtsVbGXNHUiYdqT3T0dvZ1tfYAocK+cGMwGvYGOxIdPa3Jvq7+vq50qjOdbOsrpId62/ta0HNLdNCITCdDGx1tjONLa3tTu9Nm/emOTaueuGPNM3c88dIChbamCao6HOtu6+5m4pziFt3xaALY0RBsOHbk6D9fGexIpVIUG4c4gyJ2UEOatYJE2OXdm9kgWqhzY2NjAxsxLHg5gClNskWvAsBx6tSpQ4cOjY6O4iNeceol4DiTyQwODk5NTYFO0LDRqltbWmnYBe2ZvEpJUiDRrFqQAdHGN9988+2334I5fv3rX0OF4zwF+aAgYO+9y6DJZ+zYx/vvvw9BAxyB3CmVSjTEi5uCJ5AHGS6w81+QYdZYgoT7An0OHz5MHh4X2aghNKGGiIcIg+wZBCKfX3b+QCJDCI3vkPkEHa232JVlCD5QAuNS+q9XFDsGrM6ixpiTqHM8aX81Py9UDKqMQyZb0WDq2bWvZ+uOgT3Vyb2VGWZ8RD1uNGTquZlaTqZekBVJJm32UZMxr1QWdfq0QpMUq7IqMzN9w+HOW6wlj2fQ4ZgMR8ZC0RFfYNTtnQBY2G2Tbk/WbBnQ6otKTVGmOBAIjEfCRbt1xO0acblGTLYhvWXUFzrQ3nmgs3OmrWXC6xmx2IYMpqLZ0qbQBYS6qN7d7I0mm9pHssWjBw9dOHv2vbffOnfs9fHe4WwoXYrkD/dOv14Yn4y3FOzevMU56vEOWuw5jbGo0o5odWNqTX9lVWrn7lRZ+YBQULKap4KBiYBvyOvttbriEmObPpT2d2TCnaXm/qmeof2pkRPFg5+euvj1+Y8+O/H2hwdPjoZiBYu1qNcNiAXJqorO3ftSlZyePfsG6mqHVaqiUpWTyNNCUZ9MOmg0DEgkWZG0UyD64PiJH7Dj+5DQhCGpyGkMPRmaJQcRgX2xWEQ/p6OjA/0oqHwKv0GDLFDhUK5Qqy52cimklsVioaEWCEOQSjabRWkQShBuEHEoP8YuQWW32ZntcsK10Nk0K9DHusdBhIJyZuHj5ptvBkOQKeIBdv1bEM+6desef/zxZ555BuqfYptu374dlzz55JMr2LjpgIMf/ehHf80mkAdKWLp0KepM4UMIO8iflGay3MHG7VjITtJByQAayFsdu6gK5DnqiUfbs3sPDQPdeOON4BI9GxnS4/FQgCL8hiTkXew0WjwXKQgk6AXcHc+iVCgh+r77D/hLp1nsmLd07ubnH7MYjazdgllJ1YMescMXcofcFrdVZ7XoLfgI8nDbPfjKZXW57S7Ah91k0yrUcnCVRKyWK9VylUahVkhwSq2SKXl1XIVUVs+t3bZtyxNPr169/v4lq2+7/6G58xnLxNWgB1AFAxarWf5gOeOyeeNaQMn9q6+5b+Wc+SvmMBHQ2e3+h+cwwcEIOx5ivEcBHCCPSy4dzAjL3PsemrtgxQ0rH5v/zPOPVVfuc5odTZHmziZmvCObZqaitMc7Ul3pYmYo3dOfiDbFgrHWWGt3S3cuVUh1pBmXju6BfB973Nnf3dIDBMFXg4zzaaox1NgUTSQiicZwLOKP2Ey2V7e9uPThOx9YefPqn8yrrtuVaIyBWrC1N3Uwdo6GZlySiCbAKKdPnr5CE2jz+TxeR2pjOKDX608XO8ZrB7YA4Hey0TkJPsJsJA8CTA45AACAAElEQVS0vWZ2MXq0ZB/rOgrljcY5za43iz5Hc3ML0QkuxEe0/NGR0fb2djRyHo8HggEZkLcH9gAF8ArxBIXlAAFAowM1fvvb3/7mN7/BAZQ9TqLxk6sHeZh+9CEzdZa0PhAEUgZIgW8heoYGh8bGxlAfFMgYRT5k3Evp8qNsep1NoBBkBv0cP3acsAPlkJcGeZjOYgcNl5Cpg9w+CDtQMTAKTcoFx4A8wB/EHNhTOf/+b1dwkOV//bGJL+2tE+WFqr4aUYYnL4jVQ0JFQaIsqjXdFVWd23d37drXVV6d5ouzEvmISpfnCDLV3IFaXl8NtyCWZUSSnEw+bNCXbJYhi3nUZh1zuid8/hGvdzIUKrldY8HgdCw27vcPGsxFjX7MZJ32BEZ8wazFVtCZhvRmJpBGMFKyO0Zc7iGHKy3TFUyu6WjzZKS55ItORBrHfYEhMxud3e3ss9hDEkNI7Ug4Qm3BeCE5cGjywBtnz3z40bvvv/vO+WOnp5IjQ5GB/W3DryXzY4FoRm/tU+lGPa5xr6dktY2YzQNicUEk6dm9r2fHnr6q2oxYMu5yTQeD427PiNOTt7jaJPq41NFpbexxx7LRrsmewenu4SPpifePvPH1hY+/PPfBp8fPH+pIoqp5nW5AIU2LeF3VFcnq6rbdO5MVZTlufYEvLMokObUso5KUdKqcWDyk1WeNls/P/TDI8r1IaNFQmZBUkD+QRa+xiZZZOMckpm2CG1566SXobOjOxYsXr1mzBsoeEiyXy5XYRCs5IIE2qL80NclADAX4mZmZQR6KBRJll8aksALU9YqyYY3Id5Vc4kAtkKUymWzXrl0gDHIruZeN/EF8AHQAIqAmDz74IEDh0Ucf3bhx46ZNmzZs2EBxS6+77jpgx9+wCeQBVkAhyEzYce/llVlmyQN7erTVq1eDZiorK4EUkNLRCOOfhwOQB+AGsIKiADGomJNd1IK+TbCJPHDxjBS1Gc+Cq2hWC+67aNEik9H0i1/84rv/gL90AnbUCXYueOjGBQ/e+PJPN9vN1pA3FI80YQv7og3BWCyU8Ni8Fp3FZrC5bG6Pw+u0Ot02t8PsMGrQjTbolDoJH8whMxvNNpPDrDcbNHqNQqNTabFJBWKWP1QivqimqnLf3h0vbH1y/RMPrXps/uKVt9y3/DrgwryH5sxn+GPOfYwxYw5j27hs58BHEMa8h66etxxgwW6AlRUEItfMWzbn3qVz7lnK7NkNZ65d+NCNy9fctX7T8j17t2mUap8z0BiMAzvi4URTQ1NzrLWrracxFMejNTW0gDbiEajbeEtja09bMtXZD7Do7UxjD9Toau7qau5uT3SwNpKuntbe5mhrxBsJeYIui8tusmvkmrrKus1PPcXEf1l419pHH+bU1oZ9IRBMZ3MnE+mkuT3REG8INcTC8b7uvgvnLlwh7EATonnqAFsaZME7OuvCjdaI5hS9vDwS4CPGrklLs7oJjRvYJYLoDJo0dDZabHd3N2VGhh52dVY0VCZMab6AYnfs2AHyQMnU1LFH22aghJ2VSkuoABSguaHjKQjYN998A2WPMxS6lKCBvDc+YiN20IwV6Hucp/DqzGIxwyWURoBCfhuMbwc7DxZyBBlwAMFEK7YAiQ7sP0AzcoEdNEQyO7GF7Byz2EGjLUQeNM5CfqY0v4bGWS4RzOXpOVfYt6NDqu7jSIoSXXeVsI+nSHFlw1J1UarIKRQtleXN+/Y27d3bLRBldKa0Ut/DEQ0K5AW+NFsv7C2v7i2rygpEJbV6WK2adDsm/e4xh61gNA/ZHGNe78FIZNTtmgiFDre2TAEddKZUvbSfKxtU6EZd3lF/cMTtHXP7mFXpHf4Rk33IaM2oDO214qzOPuYIDWqcGYWlYHKPuPzDdsekzzMR8BX8wbjeFVBYG4yeJk+kr7V7Ynjs1MmTH3z0Lnjy0w8+uHD49HBDen9L/njXwLg3kNdbe5WaUZdjxueetFnG7baUXD5oMPVU1/VV1qZq6zNy9bjHh2qULM5Bg3VAqe0VaZoEhrjS2WkPp0NtI22Z6a6hI/2T7x4+9/Wbn3zz9udfnL54cWrmUFNrUW/qk0r7JKKu+rpeQX1PPSfDrx+orM5UVg1KRWMmzahGNiwRpOpqMgrFoM3+9fkf1mT5XiQ0apq0n0wmh9joybTkNRo4oAENE0IAZ3bu3AndCb27du1ayKKqqiqwArLR8Cv1GSAlxsfHQR407Z8Wk0IeyJNJNihzf38/oIRiBmJPU2Y62LUU2HjHTCzjzstrI5ADPo287N2795VXXiEHjoceeggqHDWB+r/11ltvv/12HINCli9fDuZYtWoVwILCrl911VU//vGPwR/XXnstKk9h2udfXgWXxlkIPsAcCxYsIH/SF198sba2FtwD4UyTA5AcDgfuTjNl5s+bX19fT9LezgZGs7ILYmCPbG43s+Qs9jT/Bd1RyG3cAndHB/WKWDt+zhHsuv/BGx5YcVttXVljuBG989Z4e2dLdzza3NLY1tncnYg2R/zguwa/0+e0OD0On9/FjMLYTQ6H2Wkz2sAfBo3RYXbZjA6rwQrysJsdLpuLGWdR63Fs1lsAHxKhRC6Rgz+4dXU11WUvbN20dsPih9bc9cDKm+9bfi3AAtu9D14FyACFMEHAlmO7+p4Hr7pn2VX3LGW2e5cx2+WDOfcsvvruB66+a9HVdy7C/pp5S29Y/PDfr/vJ8ld3vCgS8A0ag9vuc9l8QU8kBs4INDaGY43heCySQOWtevwznH5nAAjltDi8Di94KxpoxJOGmRGlBlwSdIXCngg2v8NvN9qBGl67N+gOOS0us86slCiry6pffXnbs08/89SmJ57b/MyuHTu0So3b5gy5g4wfTGNzLByLR+MtMfBNW7IjdebUlVqTZWRkhDgX6GBnly1wsYvak0nDx8YQI2b3s1HP8e6SSzPOo6V1sOsDRdiEtocWCwWMNomXO8JG+0DDA2ykmMVKGOwgV5KtW7eWlZWBcnA5mAMNGGhC7lq4dmxsDKKB3ERozIIcPnBwkk0QHyAPEiU4SUYFcjsFrFCwUUiH4eFhlAZJRKIECTRARZEzx1E2Fju6L5NsNDDkxx75UQ5wgYCDmQf7KTMPhQwqn7HTVT5l5uoyCedpki0QhHxH3mHXtEMJOCD3WKYE9qorjB1ZhzenNOYUxiRPmZWbBkS6YaO15LCVXNaUuD4l5iWF/AGNbswfLfnC3TxRUSIfVKqH1Jp0TU3X7t1ZkQA9+yyPO2I0DlmsaYkiLVUNKDQ5pWrMYhkyGCb9gUOxxLjDM2K0t1eKIs/vaX9557BCMe5xjXpcE3ZHSa0flGrzQkWWL+kuq+ncU52VG3MSQ6pSnOKrBs3OUZdvzMuEKRvzusei8aSnMaiwBjXWmDPQGWvLJAcOTOx/58Kbn3zw3mcffvj+2bfHEtmDzdnX23tngpExd3DQ7hu2WCes5mmH9VA4OBWJHIw2jGh0I3Jlmi9OipVDdte42z9qdgzqTBmpop8v6eArGoS6Zp27N9Ccj/eOd+aP5Kbeee2NL8599Iu3v/rizAdfnn3nXGawaLD0SZWdXEFrDTej0Q8oFFmRMF1VlS6vTNfWDnC5OU5tlluXlgjzRl3Javny9On/9cMgy/cgoS1DZUJSQRZB5pA/GeiBXNfJXAEigfCByt+wYQMOBAKBWCyGZk2n0yAVZKZ4PLiWlHQrux4khVqm0pDQQQKOZAYyOE/xiCHZZlda+NOEM/gKYhDwAZHoY2NAQ8yShyaPxysvLweFPPHEEytXrlzGrnFPYygUnOOWW26hhW2vu+46it4B+ABhUFSPWceOP3UsxUl8C2ShBVkofhooAdBA83SAPmvWrAHioHBAD7ADkt/MLsilUCiwB3CQkwr25MCHOkPs44cFsd11110oHF9dIewQ7rp/+Y0PrrpTLOJGA9HGUByowY4RMF389kQneuphbyjg8nvtHo/NA1Xtc/jD3kjE3xD2RQKugMvqthkcZq1Fp9TpVQaWRfDADsYiojWCQsAiQBOdSm/UmiRCqYDD18g1UqG0fO+eV1998eln161et3DpqjsWrrhx/oPXsDNTLkUAu3vJVXct/vHd7HbXInZb+OM7F+LgKmZbePWdzDZn3pKbVqxd8PhTa156+dmqynKTzuSwOLE36kzgIavBhioF3CFUOBpoCHpDICQwh9fuj/iYR/A4PCFfKMBMyYnFI4mgNwy2aAw24mHNWrNNbzWpjAqRXMTlCzl8hUgh4YklfGzSmvKavTv37Nm5q2zP3pqqKplYajVYrAazzWDDj9YYinns7og/0tTQ1J5o72hqP/7asd///vff/R/8n9Kfgx0A+QCb0B7c7IoqjZdXYSXCiLKJLCJhNiA6nUGDpOicNOwCBEE7f/vtt6GAofKBEbTWGppckV0mrZ+NJ4ar0M7RzMjLCXxDsT5b2ISr+tll1ZjxDrZ3Aj4AHLz22ms0x5WYA/QA7Jid/wKMwH2JSyBWGLvF6CiKRa0gOyBukI1ijGKPkmnKLhlLUP4RdqIdY4hhV61DBppTQzNfAB9kq/j444/Jg4S8PWaxgwZfyM5BcENjwDgAeaAQmmXz6RWfyVL0N4w6/CMWT1ZpyCsMObG2oDEUbKaJgGvIoBo2qEtG/ZjFPhWITIWiw0bzoFo3arNPOZ2Zem7nrl09NdV99bze6tqiQj0gk6d5wrxEOazSFiTiUZ2mZDSMOh2jDkdWoSpojF18tefZPYH1TyV3781KxH1iSZrDz3B4eb44xxel6+p7q+syAklGou2rVaTK+DmRatziGLbYD0QbD0ajE37faLhhIJCIqB0BlS1q9beFW3rinYXe3MnDx988ffrDi++8/8bbh5Jjh5ozh0KxMaNl3OEfsvszat2gUT/hsB6Jhg8nYuNu97hWN6pU5RWqAaVu2GwfMVpKau2QTjdsMhZU6h6hNCFUxdSOLn9TvrlvvLtwuDD1xv6THxx7++s3Pvny7IefnXr7w4PHphsTBbOtTyRLi2SDVjyjOscT9JZVpspreirruitrU1VV/bU1ebm0qNNkZNJPjx79wbfj+5DQtKHRoV8huGixJJInEAizfuvZbJa8I6E4qVsFKURDM8ALyCISHSgEChh9JDIAQwBSHnw7MDBAsqJYYFa4pNWnc2xiDLpswpfo8zB5WOlHy1OTtKQuGYlcQhADOzsX6h8YBFB4ctOTGzduXLt2LZhg1kWUApmDP0Aet912G8VonzV1zGIHEnAE7EIL4W7ZsgXYYWQDQoIt8MhgLJxEBhR466234haVlZU0yI6HlbMJ2cjDD7+STCbDnuYkopyXXnoJt3jmmWeCweAvf/nL7/4D/tIJ2FHH37lg+Y2r1s5XSMUhTwjKsj3R0d2abIu3tyU6EpFmgIXb5rKZ7FDDAA58hKpORBIhXzgaZIYPoKTBInajQyPXGjVmt83jsnr8riDgQ6/SmfUW8IfFYDXrLBad1aA2KsQKqUCqlqrBKOAShUQKnb13346fbtv8/NYNjz+14uE18x546Nb7l91439Ib5i+7/r5l189bMpexaiz4MbY7sF941Z0Lrrp74TVLVty5dv2yZ7dsqqjYJxVJ9GqdTqWzABR0ZoAO42IiU9N9Q94IyMPr9LvtHtTBpDHbTS7wBypmMzl8roDfFQh6gmF/JOjB4/jDvrDP4TWghmqjWqJGbWUCqVKsEHKFdVW11WWV3GpOxZ6yna9u37trT9mefbWVVXKxVKPQGPBYUhVjBNIaZCKJUQMOcwQ9gZZY85WL2wF+x9uPLoKHTQALNIx2du1BNDO0ELRJ4gxCEDqDhAOyK5KZEY0cDfI4uww99mNjY9TwUD6NmOIYGXAjvNZ1dXUcDgcvNJnv4myi4Rg0afQ5yBvjFJuOswk8QWO059mw5ccur9ICIjnFhtwAdkDxXxpbYUODoKVRHwXVEwqFaNJoTngKMhgCL8gJAyVAKkEuoF+Cu6Pmx9ggIhffYQweeJzvuJcSfBBJfMYm8mPFrU+wgdspzY7poJDP2Qm3Vwo72Pjrf/ivHp1tUO8s6R1FqSZVxUvXivIyVU6rnvDYi1pVQSEfUanH1ZpRvWHS4RgHdmgNwzZHyWTOCQTZel5OIs8rdH18aVYkzQpFQ1LZoEQ2rlSWZOJxg2rCYZ70OEpmQ04mHTKaxjyRNo4q/Pz2xCs7ums5XXX1zXvKeut5ea1qyGIsGvQlp2MiFOqTaHtrpAN10gxHNCQH+pgPNcSOxBOTPv+YPzToizYz4yy2gNbVaI80uxq7Iq2jA4OHxifeeePc+2+8dXrw0JFEekxvzfNFY0bHqN2f0+oHUbhWM27Qjxj0aV59ob4+x+XkZfIRo3XMYM4LxLn6+oJMPGIzjdgtA3JFh1DRqnd1B5uyLanRZP5QfuLkyOEL0yc+O/3uzy98/O7B01+cfPOdif1j3mBOKMvViwpKdV4sz9cJ+vbVJPfVtZdxuqvre/eWJ7fvSlVV52Wyfqnsk2PHf8CO70OampoiBzWa1U+2ChrenWTjDZKNE2cgcPAVOAAiAtyQZ1Pu8tJR0KlcLreMTVDbkFfklUlzUEEMhCCz15Ksg+AavpRK6OFAkqA/gz3ECwlAZAYGQQx2sQu6JtjVvMnS7HK5yOQA3S+TyoAgu3btevbZZzdt2vQTdg0XgALw4pabb5kzZ851111HPhzkz0GJ4APMsWDBAopRhmuff/55iFlU4PjlVSZQW/T3Hn30UdAMyAN8A+5pZte8pCoREtG4OX5GssrMTth56qmnFi5cuHfvXuT81a9+9d1/wF86/eKXP6/l71y4/Mb1P1mmUcobQ40t8dZEtCnoCUf8DY3BWMjDdP0D7oDNZPM6fFF/Y9gbwYHP5fO7A+jNe+wednjFzih7rVWvNllNdma0RWfTyDRQwFqFxqK3+N1Bj91n1oIAQCF2tUxl0poCriBKhr53WQA0AY/D63V69Ep9dXnF9m0vv7T1uRdfeHrLC0+9sPWpzc89tvyRO+ctueauB66+ezGz3bvk2iUrbt+xfatKpsStdUqDRW9zWtxehx+cYdKiTh6NQof6qGRatVzLYJDJiTyoqkHDYIdBZRTWC6vKqiR8KSrssfkYPxWeWC6SK8RynUpj0hh1Cp1cqEAGtVSjk+v1Cr2YJ+bX8ar3VdaUV5Xv2Vexb19NRVXFvrLaqiqxQAD0sRjMWoXaqNEDp7g1NXq11mq04IeKBiJHDh3+/e9+993/wf8p/TnYgeYRZNdmBAG42KCiNM7nZ+P04RitIsTGIQ1fDpSOPbFII7t0EFk1kAArExMTUP94v9HtmGbDj86OhqIFosnhQrQuvMECgYA6HCQgCDvQjNF00S8htU3WDlAF1PnR1xibBxEGzuP40KFDNGqLzMh2+vRpGjrBfdG8UWfGyzWewENBaqxYsWLevHk//elPxWySSqWQUDTaQpgC4MDdUUNUlbDjvXeZ8REAB9ABYEGeoWS9+PRPwqF+xMb/AL4QG5FcQ4UJO2iQBVcxM1mu5ARaxqVUkeIp0wJVqkrQsa08VcbLCRV5uWrCbu+XytMCSVEoGarnF4XCg3b7uFZfUGjyWkNOrsoLRCMqzbjFPu70D5ocWalyUCIf1xtHtfoJnXZCrx4zqMZtxhGrEZoe+n7a5ZzwBbI6W69Y2SdXplWqAa0mr9cO24xDVv2gXj2s04zbbDmDsamS014ryMo03RV1OZ6koNROefxHorEJp2fc6SkYLN16R1Bq8srNQYM7ZPQ02kO9ic6hdOb86VOfXHz/dOnQgXDHoFiZqanLyzSjTv+k1zdqNmd5/HRVdZbH69yzq3/f3r59u3Mi0bDWMChVZGs5ubq6gZqqolQ0AgDSaHv58i6NPRmIDTR3l5LZQ8XxU6OHzk6+9uGpt371wVcXD5z56Mj5by+8f6Krv58n695R1ldek63jpfZV9eyq6N5b07qvrrOK27u3ouulbenyyhGtbshq++zU6R+w4/uQIGQgW9C6SbD42FWyIaDQo0AbhyAiz1CICJyhwZFUKkXLRSHhAHAAxYzLt27dCq0PtU0Lx2/fvr28vLympobH46F8g4FZnZsKB8TQAkxEHiU2FjMkISTMNJsg0HCMMzgPJEH51MnB3Ts6OiA5If3IihxkI6LSHFcoe0iq2tra3bt3v/jiixQClQkEctPNII+bbroJ3HDP5XmzhB3knfrggw8CUygiyJYtWyAAIS3JEEuz9iC0n3zySVx+2223vfrqq6g5xOCb7AoSs9GS6ABn3mTdXCBdyWa8Z88eIAvKxHP90z/903f/AX/pxGLHroUrbn5uy2NWk7E13grsiEebGkKxxlCMMWZE4qCQxlAcIMI4RoQToASghs/F2AOcFqdRYzLrLSadxWlx4VgqkqnkzEwWmUAqF8nUcrwuSo0cPGIDiFj1dp1Cr1VoFWKFWqbBsVKs1Mq1RpXRprPbASt6q1lttursXnvA7wy6LT6n2Y276DWqZ1945IGVN8x7cM79D1+74OHrFq28fu1PFsokAp/T7zS5zRqbw8SM9QRcYRCGw+Ryml16lUEpVSslapPWAgqx6B3gG4PaxNKP1W3zapV6Yb1YzJOadTaHxQ1YAWHgo4AjUqBiCi0gCfUU8cSCOqGUL5cLFMI6obheVF/D5VTXATh4dfUinlApUejVOrVCbdIZGcuQ0eww28w6o0Iss5msfpcvGohG/JH9U/v/jP/pn4MdgHEaQyErHN4n8AfZ/ZBcbIQ+djqYD00CzOFn4+M2sBNokcjmgURDLXiDoW7RqyDHi9mEVxaIgLcWXQRcjk4DzY+nQUcbG/wDRUHxo+mikZCRgF56Ztji9JnX2fmuwA5ikWPsmrE0yELMQUMwOIP2QBYOwiY0YDQVGijdvHkzTY5Hr+IsG2eMmhPEAUgFQgd71PN1dl4uRe/4iE2ABnykoRxy2sBH8imh83hqgNEMu0Yu0lE2BgnOv8Omt9n0Z6yy8+enP/yxTaFLy7Dpe+pknXt5PWWivhpJiiPpF6taqvndAnlRoRmRyQblkimHdVirz0nkA2L5AF+S4QqGNbohoyWrt+RM9n6JsihVjZqs41bHlMU6btANquRFtTKjAMQo8xIJqGLQqM9rtSNOV8nrzltMYz7XeMAz5neOuqwFlbykVI3pzX1SVRtX0K1QDtlcnTW83lpRRqYZsTjHbS7WMmHJiWV9alODzOCTG0IGZ9TqTbij3Q2tmY7kqaPHPn3vg7Njh0qOaH8dIKMmK1UNO7yTgWDJYk1xeF37ytJ8Xm99XapsT9e2l/s5nJxE2ltTlyyvyHDqk7t2DZSXF+XyIa0hI9akVJaUO5xt6R7tKxwZnjo9ceTcgWPvn3nr//3smy/PfvT+4Qu/eOfzL15/c8LX2Ly9PLm7MssRdOwp6y6rSlZxknWClEiWFog7d+7Nc/gTeuOE3fnlmTM/+HZ8HxIkDEQKyABdfL2OGRwBfNCAC1o3FP9BdhEoNHno/k52ah72PT09gA90ObAnOYDLn376afL/eOKJJ9DFpxgb6Lfs2LFj3759oAHcRaFQkLcmpF93dzcVQg4lkKvowIA2wBwkE/ZPX4IPVBJ0UiwWcS/y/CCn+9kVJ8ipDgIZEhiPQ+QB+kFlgBR333333LlzQR633HLLnZcTmIMm5QJN1q5du379+scffxzVfmHLCygNNDbDJlSGBD6+uv3222+99VbIQ1SYrMhvsyGLqI/0ERst+uOPmcHljy7HjMZT7N2794UXXoCAxZn/+I//+O4/4C+dgB01/J2LVty8fedzLqutwd+QaGhua+psb+oM+yI2k91hcfrdfr874HUy5o2IvzHoDoW82MJ+p99lcRs1ZpVUpVFogR1Q8zjQqfTYKyWgDY1CqpSJZDjQKnQGsIXRCUDBV6J6kVQg0yn1MiHUvEghUuqBI3KtUqTQSrU6ud6ksZg0ZmYoxGAHi2iUiudeXLN41Y3zll3NMMdqZl3ZRzbeLxJyHSaHTW+3aK1uixcHTovHacbmtoAkzG6Uo5ZqlWI1+EYpVhlUYA6rVq5Ty9Q2o10j10n4MlG9BJtEIFNIVHKxUsgViThimVAO8sATqeUaMU8i5Ii0cr1KquFUcWrKq2sqq2qqqrHVVtXUVlQDPtQyFZ5ULpapZeAVlV6lMWgMRo3RaXEARPwufyQQOTxz+Pe/uyK+HWgh5LeBNDvpnGxrJpPJfnlxWvIhBW0wc8XAH2Fm0bhZFqGpVolEAg3vGDvH7AjryEmu43ihz7FL2OOtxSsORsFVoA2wPJou4AONHA0MfINyQB7Q+njXKQ4HzSWhkQ6892+/xSj+N9mI6WTkuHDhAo7RbMjacZINow6MoGWfyL8MTffll1+mHgPaG4fDgWRByTTflbCD7KuAHkglsqngduTD8QkbdWM2pOl51sX1rbfeIph4kw07xoQvO3JppVzsUQfUhB4ctXqDDZj4L//yf+0k/OenP/yxy2DNaM1ZtTktMfXWGzorlK07ePFt1ZEdldEyTke9NC/X5IS8rJiXkYh664UprgBbZwWnbV9Nd72wSyjuFIha6+q7OcLuan5WocsrdBmRbIAvTvOFvXxhv1SZlii6Odx+kTAjl+SU8qloeNjvSWuUQ1ZLyeMdb2iYjIYPRiNTHt+ENzTk9A97fKPBYMnp6+RIuzjSgt4xZHX3K/QFpb6k1g3JlWmlrkltCWtsIb2z0eprC8R74+2Z9u7Dk9MXz104XZrO6hydFXXtZVUDUnVebxt1+YomS1osTXK5SaGwT6lMCfjdFft6qisHRMK0UJgWiwcU8t7a2r6KyrxYOmyy5jTGrM6Wsnoy8bbp/MixsZkz+4+9eezMB2+884uPvvrtF7/+6vwnX5776B8+/PrjQyd7pdr2srp+viTJ5Rfkyn6BqI9bn5UrMypdH1c0KFLkRJIhje7LUz+4lH4vEnR5fX19VVUVQx5SJt650WBEJwdyAGIH35K7KNo41H8fs05cH0QWFD/2zIe+vkwmA+GgUqkgKNasWfPII4+gc7+OTdD6Tz755OwSJ1DY5eXlZPzQ6XQQnuR0D0mYTCaBFBAmFJeIzB7ADjJ+AAJwvlQqQfYy/iGFAhlaCEEguABJ5NEP2YuS+Xx+WVkZiAcAtGrVqgULFgAXaDVaHNAKLLQWHTpXqC2YY5aWcBUKoZ5hY2Mj9RshbLdt27Z69WqUVllZiXvh7kV2wg551M66vtEwNMkxiLhksve5554HA+HR0Ju6AubbX3z78xrezsUP31pVs8vv9oW94Ri0TSQeC8c9Ni+0ps1oheK06s1GjcGsM3vtPrvZYTUw7houZkKHBSocGlougk42MJvaqFMazFoLNLRGrsVHg9qoVxmRzW50mHVWQAkQxKg2GZBZCfWvYs0eBj1r+RBzxVJwAFckE8qEXKFUIEU2k8akUSiff3HdktU33bd8zqKV1z3AYsean9wvlQmAO06zi5mZYnC4zO6gK+y2+IAgRrUZlOO2+WwGB+PDobdZdFa7wemx+nQKHepmM9iNKrNCpAJPSPhyTjWPU8XlVHPrquoEHCFr8FCbNVa90qiRaVUSVMKCAz6Hz6mqq6/lcKprRTyhgMuv2FdeVV4pFUrEfJGQJ5AIRSoZsEtj0ppsRht+PZbeHCFf6MihI1cIO/DSU5SbIBurn9w4cEy0QbaNGDt7luZz02hLKMgMxPguD80ASvBVc1Mzeg8UdwsNG8qYFDOZ7KCeoc7xbYZdqBbX6vV6tFgRm2hNBAgIdD7wipP/5pdstAxa0e3LyzEzyPxwkV08ltQ5DcRQhNO32dXjcBeUg74O2VTQbtH8Nm7ciK4AyAMVxlUUUQMHwA6wAg3QohcCNkJpqDwy0KTZTy6vtELWkVm3VtwLl5OVBVCFcnBfEAyenYlTxFhnTr7OzPVlvFJw8Pvf/x4/+Hf/B/9N6Q//lbZ6hvS2vFLXLzZ01mrju2SNL3OiL1UmKgWdIkW/WDnAFfbs3dO9Z0fn7j3tuytStdyBemHr7urGV8tbKrm9YllKIu+qrQeIxHeUt1fUd9YK2qp5bbWCtFwLPhi1OEtaU0YsGxBJe/mClFg6GgjmLNYuHr9XKOnXW0u+yFQwfKKlbSYWP5Bo3t/UeiAen/AEsgpjn1iblmhHHP7paHPR5slq9DmVKqtSDOiMHSZX3OJvNAVilkCbP5Zqast29uwvjV84dup4ZnhAYWot58T21TZX1ieqeAM6y4Da1MOX9AnEnVw+niunN6elkl4+N8WvL+r0QzZHv1abVasGRKIBiWTQaMjp9VmtPm2wpAPRyYHBY6MzZ2aOv3vmzY/ffO+j8+/9j2/+4X989euvL3z8s7c++dm5izPxzhRPlhXIi0rthMU2qFL113PT9UAQfVGkSdXwO6pq8moNY+34YQLt9yBB2VdUVEAvgjwEAgGkCs3IQCcKoqynpwf6Ei2UzA+5LKPsM2wiP1B8SweQh88+++zatWuBHav+JOEMmUAIPl566aXt27fv2bMH/FFXVwc5plAocEdQDnncAyMgWCBVcFNo9Ek2kcPH2Oglnw9yBqFbE3+AWnAtSqC12WjOLXpNkGArV64kP9Nrr70W5MGuf3s7sGPhwoUQbuAkYAfVEIJu06ZNqFWEnXIYY5ftJCMKBCB+E9QW4hG/DE2U9V9e+IJm7uA3Qa0onBINEqHauBC3wG+LGgJN/gyD/P9t+sW3P6up30HTWBrCjYmG5ng00RiK+V0+s9ZkVBl0ci0owcQ4Q5jMOpMFnKEATGgNar1BbdAqNGK+RCKQ8mp5Ao5ALVMrxCoZXybhidH1F7ImDWzgCXZURc0MWDCDGhJWkauUIgUbEcQO7NDIdBKeBNghFyhUEjV5nuJALlSopWqdSrv1p48vfeTmhSuvXbTqWma/cu4jG+8TCuuAHQALg9oMznBbPEF3xGsLBFwhj83nsnqdFg++DbrDHrvParA7kEVvM2rAKZ5YKBELNTnNXp8jaDc6dQxe6MQ8Kb+OL+XJtHKD3eAKuRuCrggyeO0Bp9lt1FiMWgtYSqPQSAUSuViulCp4HJ6IJ1DJlDIx/hQykVQjR/31gDOXzcXEVWPm6Doj/vDRw0ev0EwWvO702pGjBl67Rnb9WHoLGxoa6Dx4YtbhiLiEHD4ibKwwZEZOfNvS3IKGBFLGSwmV/C6bZqNmQU9D++INRgOj0RabzabVaslrmrGL6vVoIa+99hrxClDjH/7hH/7xH//x17/+NeBjdgEU0ADFGwV2nGMTaAAoQEYIHIB7epO9gHr0ErZs2fL4448vW7bsLnZVRggLyCbAxMV3Lq0zd+zYMcgCavmDbBxVVBKFf8TOQEH6jI25ToM+s9hBoy1kxQFV4JH/N3bMMFFDTrKxUMnNBednZg6hlV5R7LB4RozOtFCWrJf08jRNe2SxV/mxPYJ+raNfY4Y27a2o6961u2fPruS+st7KmoJQkuNLWvfWxndUJrmSIZ0pp9AMiBQ9tYKuakFaqEprLWmjvVdrGXYHh1zBSYevyBVnOcLOqvqWan57vaxHbugVq9sr6pJcQcFkmwhExty+A5GGQ02JA83Y4iM2W0aqyMg1gzpLTqUfNtumg9GixZrT6vIadUYp79fous3uhCXQZIs0O6LtwURfS8dQqv/g6MT5104eSQ0OKCwdNcKWWmFLjaCZK8yaHX1KfePeis5aTieH28kT9iu0PXxxWizpE0tSUkVSouysq89KZTmlIqPEXjIgE2UV8rRK3Wv3DLb2zuQmTu0/+t65tz67+NG7Zy78/KPP//mXv/ntV7/+6u1P3p95/WhTV1GoGCiry9bwS3LVqFY/KFcUpcpRtTnPkXbsruqs4RT0hq/Onv0BO74PCcpy3759QAHo6draWvQ3IFtoXga6NLFGZrlKcAC0KRk80N5xCc12KbBz2ZBwAIkHpICKXX05Qd+vWLGCYmkgQbUDAmggg9ZaQ8dm165d5P+Bro5SqYRGh56GLG1h5+jN+p/iphCSswaPYdbpHmhC8gcZkBPyFmLWbDYDDlDmq6++unnz5nXr1oEtQBgQZbfeeisFXL/zzjvnzZu3ZMkS1BAVBhghG1XvlVdegXCmmEmQt0AZ1ARSuokNQgrR3cgmEAlOUkTH2eGhadYzbwZii3WhwzF+LvySy5cvx6PRYPSVwY7q+h2rHr1fLKwPeAJNsea2prZIIOJ1eBxmu9VgNSgZIGA8M7AZLEboU6lKLVPKRVK1TCUFcDBKV8St5tZV1gm5QiFXwKutr6uqra/l1lVxgCO15bVioEa9GMdKGsJgTRpykZxXU68Sq8AfChEzAsKt4vJrBTgwqkA5Rr1Sr1cYQAAKkUIjVwE7Hnz01sVr5oI57ntwzqKH5659/H6JlOdifCkcepXJoDa5ABlGJzjDY/N67cAOZk4NOMPn9OPAbmJsHmAOm9Huc/gjvmjAHTHr7BadXa80An3UEq1RZdErTRat3WMNADiaIm0NgUTIE/E5A0FPBBzjdQZC3qjL6jaqjWbGcdVlM9lMWiP2Bq3BYmDMQkaN0awzO82glYDb7vbYPCFvKBaOHT0C7LgiM1nwuoM5PB4P3j+8fOTcNIsdsctL3jexIXHoI/b0sgJE8Con2JB2hB3IhjcSOhiK9hQbQnQWO6C2cUB6Gq8s2jauJbcSVABCgZZcwplBdqQDhXzyyScAjl/96lcUj2s2QhfSl+wS9iAbIMIJNuEANEBYgPJTqRSaB8QB8B9tdf78+egW4ADNGEiBy2l0hgZl0NKot1Fk59qBWvAVyqdopF9eXvsNz4L64xLchebO4CNZO0AYZPAAtcwcnDnOTrp5nQ2Wimd5jYmpevx3v/vdFcCOP16eydJrdhf09k6uuLmqPiXWdnH1zeXSWLUkpbPldZasUD7AESYrqpLlFT2VFf08XkYqTYmkXQJ5WqYvmZxpnqSngjMgkPdJNANaa97qzVo8w65Q0eLNau05o2tQbW57aVfnrqrmMk5rvaxLqk+p7RmdrYsn6hGKR72+6cZ4weUZC4dnmuJHYg2HvN5hhSotEI06nRN+X0GrHVJpxgzmgkKZEUsKMvmQXJGWKrsMroTJl7CG23yJZKKzv6NnNJs/dmDmzaOnZjoKKYktKdJ2idW9cm3WYJ4KRwsmY3tdZU4u7BNye3icos6Q15gKGmNBZ+wUSFpq6ruruVmpvGQxjdnNI1pVQS4palR5nSFt8/aHWyeSgycmDr179vxXH3z8ydvvYvvNN7/813/83S8/+urtydfG7IGeXeUtz7zQsXV7vl40qjNNmOwjOktBou7dV9dVVpsWydIqzcevn/zBpfT7kKCwd+7cCXW7e/fuiooKEAA69DQRA90byBnIKBAAhB7aOHQq2ju5dJBDBtQ/RAEOwCLosTzyyCMPs4lQA3qdPq5gE51fwybypQB/vPjii9u2bduxY8csgvB4PNwdAEGhL0hsop5kaIG0nJ0+QwAEMGpjYzBCJAoEAmIOdJaAEbjd0qVLFyxYcB+bFi1a9ACbZmOLEXaQSQY/gt1uB0mQOQfkgZLx7DQBmJ56NqYI9nhqtnfEJIgscgShdICdGQAWQS8R9BMIBKampiH6rsCoMYMd3B0bnlgO7LAYTHazPRwI2c1Wq8Fk1BoNaoNOodUzwxwWpUipEis1UrVOoQMQKMRylVTJq+MJuAJONYdbxakury7fU15TUVNVVsWt4Zbt3le+p6y6rKpqbxV4QlAn4NXwAB9CjkhQJ8Qm4UtwXlgn4NfivEjMFddVcvh1Ao1cgzsaVMyYi5QnAYUoJUoJX/jiyz9Zsf7/WbL2+kUsdjywau66JxaiTk6by6yzmrRmYIdF72DtGQ6zxmrW2rABRCwMMNnsJodFawXHOBguYebaMAiixlVmp8VjNzo9Nr9ObgSy2JjLHV57yGP1x8PNEV/MbfN57L6AO2zQmHEjNgyJwaK3AF/cdq/P5Q+4guRpG/FHQt5wwBNkHHL9DcxKtvHW5sbWZEdvZ0v3qROn/+XKRCmFunWyiWwbhA4EH+ygSiIYZOL+xtlwv9hfsmq0tND0M5pqS44dNJMWrzWaNN5UcsyEboa2JgclMhi8wQYvP7D/ANp8HxtsBzzexMbSAXPQRFy0B6j/i+9cBGqQpYFmqwI+vvnmG2AHzuPkeTZQBwAFd4Rqf51N+IieBCpTX1+/YcMGCAuylC5evPjpp58G/n/ErhtHDhm4HFVF/jzrXl5gY3vQOMvFixdp4RWiHNx99o5EHuTNijuSVYOGWqgmJ9lV5ah8+nj69Jl//ud/vgLYcSn94b/6HP6U2top1SfVgAb/oCXUIzUmNeas1ZGTKYpC6aBE1VvH666B7hQUNIoBhQzKOM8svOIvai3d1byOck4PX5Y1OtNGZ5tU185X9gjUGYm+l6/KqCxpiTb+yt72Km5rvThrAHBY0lpz0eYctFiGrebpYGAyHJqMRA4mmg42xkbNtgJf0lfLa6qpHWkITbfGBs36gkhcFMnyEnmKyx8QSjN8SZIv6zG4W22hJnu03d+Ubusd7M+VMtnjB2feOnpmf1O+R+Ls0zj6dLY+lSGjM0wGA+M+Z0EnH9ErUzxOSiAc1FoyCkNGqR1Qq7uEog4uv6te0MUTDOpN+52eEbW+qNJmlLqMxppxhPtDbaPdRWDHO6fPffH+hz/76JPPLr7/7edf/etvf//bL395cfJYSqqLPPVcYN2GhmeeH+CJswrtiNU9aLQPSJU9VdxejnBArknK1R+f+AE7vhcJ6hNaH7qfHD9BHhACTCQK2f/P3nuG13Gc+Z7f71hhPLY89nhsySOLlCxbokmCIImc8zk4OeeccwAOMkCCIAESOecMgpkUc04SsyiJpIKpYEXba3tnv+zdnevZf9drnKurvffZZ/R4Ofqg0lE/ffpUd1eDXW/96k3lpTVmIeUgstDZye19lnlyYAzGqL97JQ6FFJ9WqxWjOEZ0zFUwqCeZI0keyZ1fsZVTyAQD/sjIyAB/5Ofn0xpvAoEADITGaDQau92OZsTj8RaW0zkpb/tYgcjCkRaWIRSQhJbjLDwIHicrKwuNeeGFFwAZoA2IstWrV9OCcMnmkVYGsg7MgT9CfX09wQ25rFJWksHBQbKhEIJgh5gj6XOKQl63hB3JYBwU/GXQVGYHHz506BBE36PBDp1ZmF+U6nZYQ95AyOuPhcJ+j8diNDqtTrfN5TBzphMu3DQYD/vCIW8YNIAjHrsHv9pNdofZYTXazHqzUWvgtmAMuVYlU2vBhEAQOcccFp0ZbAHycFncXpsP8GEGLuqteqUevGLWmQwqPfaNapNVb8UHUMJ5cZocxCu4l9/lEUoKXt78w1Vrv/PcWk7hsXrtE+vTn7FYddXRajAEuCERqeUcUSM1II+mmhYAB2ijOlpbFU5UhxN1VajAOXPUVzXWxYEddY2JpsZES22soSZSl2CAAnCJB6u21m9rTGxpqWttrGnesaWja0fvru09Hds6t2/p4Fxiwwmvw+u0OqLBaCKWaKzhzChADQBHSz2n1eBSqDW17trR2d7a0d3e3b2zB5+h3qGJkcnrr934GguHfR3sQB8ja99fSbyvv4/7cDGx2JJhBTsjIyN4ZcnXGj9RkDcqE470skgW0uCh8gkWH59MvIGh+k2WegvoAPgAhVCWC7zcZDJEG8ioOTPNiQD0B9wLO7jC7du3aZgHBBB2UDJycAAueOvWrbOsnFnJJwYIOMrcR7Zs2SKTydADIQvQabGDLop5A7oZoQx5hOBctGEvl6Cdww6KvwfX4zpvvPEGauJeyYVXcEe0HO25eIFz3KAgnTMstzrF8V66yIXSnGZrUFF8DSljsMW9HrFvx9yWjhF/oscRmWvoOLB94EDTrtlwYndt/XJNYtHhWrI6l73hBV9oMRRejAQWg77doeBiMLK/rmVPVdO4zTvpCEz5q6ZiddOxuvFAfNwbG7EHBg2uaVtwzh2fC9dNBapnfPH5WGI6WnW4tW1Ponbc7Z2PRw801x9uqT9QW7OvKnGwpv5Q05b9Dc2DRluXUDGst/Q7HMsd2w717ZqJhiat9kmrczEYngtGZvyxKW903BOZbe6Y2jk41NYzvL13om907+zS/vmFUwcPv/bK+f3bpoedjWPBhunqpjFfaMLrW6quno9Fp32uWbdz0GDeKTf2axyTdu+MxzfudIzarKN2x0woMmRzjdjdC4HoqNk1ZPUOuSJT4brJmq3TbX37RhdP7z9648KlB7fufPjgnYdv3f/onXf/9Nnv/vzR5w9OX5nwRrZl57Ru2thRXDJmtvXrzWMO76Q7MOH0TFpx0+B8KDbhj7xz6dsA2m9EwWhaXl6OIR9bsVisUCj0er2bFZfLFQqFIOsgoyBpMI6iC6PjT6+UhfkFQhBSQkQiETDEi6xgaE9qOJKoQYM9dvDrl6uRsYNcUDMzM4EgeXl5+Xn5RYVFPB4PGAEYAtOgSUCQQCAQDAZjsVhVVRXuiCP4yWAwqNVq1KyoqCguLs7Ozob4wu3AHLR6y/PPPw8Eodu9xPQcSebATUUiUQNLWQS6woOQEmWSOc9SvO4QS9+OvxVZc3AcMpAc8gg7yKqCPxHnf8vIA9tltuQF5DyuduDAAci0r5Fa6j9agB16s6SCn+t22iOBcJRbSCXkc3nMeqPb7rKZrA6rnUXAhoAXZr0FnGHVm11WDjiMGiM+Jq3JBKowWk06k8PiBCvo1Qb1CnYARPQqvUFpMKqNeoXeorV5rD670cGww4aDWoUO2KFTag0q/KsYrDqLw+Rw27wuq8dj9znMTtyC8y21OyXy4jVpP3r213+3ev3jz6c88fz6J9em/USjkwR8/hinHakC0HisnoA7FA8lQCGAhlggHvFFKSIGRFIbbWioagJ2cJhS1YCdmmg9toCP+qom/BRnNTmnECALi/WtDtdsa96xnctW0tHW0r6lvhUnhrxBLrGHP8KlUKtr3tKwdWtjK1BjS8MWfIAgTXVNLfUt21ra2re279q+a2fbzo5tHaMDo9devf41wi2/DnaAW7dv376NxcTijRweGu7v59iihyUNG2KF4sqwxWtH6g2wxUD/QF9vX9K8knSWBojgrb3GVn3EqHyKrXvCpcF44w1yFL137x55mJKq4CxbQuUVFgPChv950Df5QKHDnGbB4njROb6+cpUMH7QSSlLhQfoGMnacOnkKEIO+VBWvwiSDph3ADswM0Dkx4di/bz+Q5c6dO6hMDhngHqDGEivk1YUjuCAaScvBfPbZZ59++illQAf9kDGFeikKaWvpMc+vpO64tLK2LalAOFJ55C6le3f0LlY1z8ca927pOtg2sBCsn3F494VCi07ngtG2aHbO2fyTNt9SNLYcDy84HHuCoXGray5QNeNPTAaqxrxVo/7aPVt2LlXXL8dqDjRumYvieAx8MO+JLgSrl6oal2ua56trFhOJ421thxqa5gPBhXBwIQ6OCc25fEue0G5/ZDlWvRSPD1mt/VrdnN+/nKiejUXm4vERh2fSHRy1u5bCoblAcMITXUy0LNa37u8fXZ6YneofGd3VN94ztDQ+e3hh9+k9B46N7JuIdPaaq3YZ/COBxHQsPhXwjzq9457QtN8/Gw5MRaNtalObSDlqNI2bTaNGw5jZNGIyj1qcozbPiMM1FQyNuL1DzuBEuGaxrmU6UTfftuvA2MLx5UOXj5+6ffnqu3fffPjWvYf3H/z27fc+f+f9h9duLze0dBaV7MrO6hOJ+1S6bSX8Dp5kWG2Y0JqnjI4ZR2AOf5Bw/L0rr/7bf/3Wt+M/v2AoLSkpycjIwEgP8sAAjPGbAvUdDofT6cRkHSILogx9HF0SsoXWT6HwV0x4IHympqYwHre2tmK8J574MlKQ2oNoI1mS1UgDQfVRjfQf5AWCVoFCcM2CggLwB6gIzcMWYgp4pFQqsUNrz6LllDUE9XE6BBduB9R4lhVgB5jjRZY9nZQcoA2yreAuuA6EOc2gmO2GI48pVihMhuwsxBx4ZLKY4+9AwEGOaChfYQ7yg8Ep4yz32v79+yHWvob74X+0ADvMNoVcwa+OxWqraqqjVeEA5yxpN9vC/rDP5ccn6A0GPQGXzQW24Pw21HqLzmzk2EKplCrwUcvVGpY+S6vQADXkIqmYLxLxxZXlldiRCCRKsUrKl4p5Io1MY9FZWcCIGeShUxp0AFelTiNT6xRanK6SKpViJVjEoDLiY9FbTRoTyMZqsMgVFesy/vm5td9ZnfL4L1KffH79E7/e/E9SRbnDanWY7W6r22nmPFLDvmjEH4/64/FgtcfuZUExXr8zwOX78kUBEwAL1KmN1zUkmqKBeNAdDnki1eHaphpOwwGwaKnb2lDdhJpBT5h8SGvjDQ0s7+qOLe1/XQyvrrmZy5nGJYZvTHDuHdWxRAPnPdrc2tzWVNuUiCbw09ambVubWrc1t7Vv6xjqHbpy6cojWpMFbxgIg3CBSvsOjiR62XorRBjYIRMg6ThAHqQVJM4gjyTSdmAfV8A1wQdXWEoMepuxg05+nYWGUx6LmyuLup1j3g+H2Dq06CG415YtW0D9QH5MUHA7vOW72PJFaBX6EgZy0AYpIbh1X+/dx73u3OYyfJxniYDAKKgfCUcwqyguLgF5YDJA6zEajcYTx0+Afsgng1xfqUcts0I+ZaTtePPNN4Ean7PyGSsff/zx/fv3L6ws+0KOV8dZstQkdqCQCYYcTVD5LFv5Bbd7xEaW+fpti8Ha3ZH6PbVtu6tbJy2BOavrgMc7q9bMyDQzasuEztWnso86vAuR8ILLN23zDOvtU67YpB+DeuNUtGEu3nywafuSP7rgCu5NNC1W1S1UJUZsznGdedrsnHJ457yhhXB0T03N0dathxqb9tfVLoaDYybDkEI1ojXPOPzTLu+s3z8XDc/GgtNh395E7GBt9UIosBiOTrp9s77olM272+1ZdHnm3ZF9VY27a5pfGZ7cNzU3MzQ60Ts4PTDKsGPpzO6Dcy1D/c7mEXfNoDM2Vd10cMeO+UTVuDc0FYjN+kNz0dBSc+Nkdc24NzDusE067TMe16jF1K9W94pVYzrbtMO7pyqxr6Z+f6JpLly1EKuaDARm65v2D08e273v3KFXXjt97s3Xbty/9fq9G3fuvXr7jQvX3jx9+fD2roFKaXd+yYBM16207ihX9UiMAzLDiFgzrrSMG9zT7vBMuPrha98uBfeNKBg+dTpdYWEhBmzSeUilUrPZDElCOo/q6mrMsiDNMHCeOXMGAyrZiMn6QHIM8g0UAmkDCMCQTxiRVG/8v508ktjxi5Xywkoh/iD9RzLGhKwwKDk5ObTYPSCjqKgI+ziIORLoAaSCU3Au0ca/rBSysOAWdNkkc+DKuKxWq8XTQYruZSnL9q6kRp1i+ZexnWeLVJBdG8+IZ8cfBFiGp4ZAoxBfog0CjiSCHGBZp0kPBDmJr5BpjwQ7PnK49XqdvDZRs6WhpTZei/G4KlRVFeacGCgatiZWGw9zi5v4XX6nxalTcZYRsIJBbQAWOCxcui2OJDjPDKtBa5AIRJUVAiFfxCvl8UoqJJViuVjOL+WXFpTwy/hKqUouVgh5QolAWlkqkFRKZAKZhC9hgCIWlPNLC0t4JXyVRCXiiXFcI9fgRkqJQiwpS8n86Qsbnli1/vEXUp94PoXDDom8zG62slBbr9PiMmnNXs47lXNQ9Tn9lHvDDvKweQAlVr017GV2IlcoykXKBAKuoN/h99p9YU+kKlgN4GjkwnEb6uL1iUit1+FDNb+Lc3qNBWOcXaa2iS34Ut+2ZceO1p0gDxz3O33xcDwRSzTVNQE1Wpu2tbVsr4nXUq6Ouqq6unhtU23jQM/Ao9N24JXaxUp3d3dbWxtGd8q+R+YSbIkkepg/KSCDuiV9JRcQIo9OtiwL3mPQCUbit99++9q1a+SORCoB2qctvpLbKcZvvMSAbtwdkF5TU4NJCagfPRC9CB0S8oIuG4lEyEwLcYAedevWLTLZUGjr7Vu3MczjWdA3iFFwHZlMVlpahgIxhN773HPPoVsCO27e4IgHcECZNsjKs48V8u1Cl7t69SrgBthBqg6Qxxeff4GvZJ2hsw6xiHzy90YhvEhal1Dt1atccpEkiDxS7Pi3fx/xV03bQ1PW4KIvseCpGlFZp3SWeb15XCwbrRD3l0t3Vqh3isxDFs+41z9m8Qyr7aN6jKPxSW/1mK96PJiYCSfAHJNa65TeNeup3h2t3VOdGLdZx1XKKY12ymCaNJgXvP69iepXWltOdGx/ZcfW5VhoWCrtKCjplWrHre5Jm33CZpn1eeZC3qmA82BN7EhN1VLAtxyJznj8k87AJBee6lhweXYHqg7VNO1LNB7tHzs4vbAwNjk3PIbtnumF43v3n91zeLahd8hWO+6MLiRa9rXtOt7Xu1BXMx2KT/ujsy7fjNs9X1M9U1MzE4ksREPzsfByomra75lyOEdUhkmDY94ZWApVLUUTR2pb9oZiiz7/hN0x4gsu7Og8PLNwct/Bi0dPXj978fWr1+9cunbz1JVLe46/uvfEoe19gzJ9V1Fln8ywU2bqlBgmzb4xrW1Eoh1XWKctod3Buj01Le9fu/ktdnwTyo0bN0hWYNIP6YGxHOgAEKFgWnLthJTDuIvx+BjLngxpRv7ykDMdrJAoQzVIGwgijO5J7UVS2/FlnceXgeMr2JHcT4ICuYCAKjZu3JSaunHz5rQsVoAaIBLQAzAC1VAfhPHMM8/89Kc//clPfoIt9knVsXr1asKgNS+vobQilKgDmNXY2Di94qdCMTI0jyL9DX4CNGAWR+SBI0ODQ+FwmMfjYY4HMb60uASZRlG+R1iiM9J/YB8iN+l0T463jwY7Pvnko0DQabeZEvEqWnilqYZL1smCYyMhbyTsjUT88bA/GgnEXCxW1mbE/26X1e0wuzw2X4DLxBXAoG7WW11Wj9lgJeUH2EJQLhTzJWqpWiFWqqUamQi0IZIJZSI+uKQSPwkrRBK+WMwTAUoqy/i84oqKojJeabmwQqCRaZQSJWesUegAEzKxXCAsScl8+hepT65a+9iqdY+tXv/4Gk7bUWE1WQAWTovHZnJoVXo0z2awAY/cNk/QEwY2YQetBY6YtRaHmUtNFvHFo4Eqj8PrtXMfMsHE/PGmmubqSE11pBrbunhD1B+LBqJ+t9/n8nFhw4FoNBDxOj0+lxecEQngj4PrR4KeUF11XVW0Cp8axm1NtS2JaG1zXcu25m3RYJQyhnW2d165dPURLXyP4ZOiYcmpEwM/5d1rb+dynxNwJANrMaL3Mp+PTpbNhgJoUbm1tZVcQ/qYvxJe1nfffffOnTt4ZXF9fD3PEpWCP2hoJwdywpfm5uZAIGA0GlUqlUgkwgQFkkJQKSBXcKvVWlVVhSujZkNDg8ViQU00EvB+giUlIxdr6hXoSGgALphIJHw+n5vlMEbD0LXI0wry6CTLBkYWGXLsIOBAAQBRhBsOAmKAHR988AFQ45NPPvniiy9AHh999BE9FzETCvVPmhmcZNE3ST+PK6zQg5//T8jb8Zduq3dAaesVG2cADVrHkFg/qjD3Vip3FVV2Fws6CgXN2YIdpZpxq2/a6x/S2fsk5hGda8IWGLX6ejS2Xr19yOgYUhr6ZbpRg3vc4F9wxfbGE/MB37RJN63XzZhMwwrFtNOxFIvuaaw5unPr6f72/bWxIam8o5jfrzDOukNzLves1Tpvt01bjBMm3W63c7/PP221caDgCc54Y9POwKzbsxgKL8Vq54NVM6GqVwbGjy7u2T+7uHtieu/0/MGF5bNHjp7fd3R3S3+fxjussRyq37q/pe1o+/a9dXUzYA5vdNbinrM6liKRxer4QiR8uKXx0NaWfY11swHvotc7Z3XOWb0ztsCUMzIbShypaTkWr9vrDc6Ynb0a40ikeql3+Oj8nnMHj106dvq1Mxevn7189ZVzp2YOvrbv5Im+iRGDo6tM2CNWdCl0PXLNjM0xabFOWZyL7uicp2o50rAUb3jv8rdGlm9Eeeutt/azjMOAj2AwSMG0lFTD6XQCIyAKWlpaIHkwcUeXxwhNRhbyuIRwg6wj4zIJPYiR3NxcIg8yahBtfEXV8QtmW0lCxvOsJPeT2yR/fPkiX1aWoALpNp5++ukf//jHlIGU1rKn0BUwxwtMiYJzf73m12TEyc7O3rBhA/gjGo1OsoxnpJZIbsm3g7YUQUMuLOPj45CumJhBNkLw4vTR0dE9bJVdUuUmpRz+qqQPphnaAbZw1SPBjt/GY0Gf21Ud5lZqbaxtrq9qAHC4rE6TjouJxfiNgZx8OLRKHQ5ih8vUqdKRrQRMgGo4qJSqdEo94EOn1GmBHSI5aEMp1Vj1dnxsertBZVTLtHqlWSkBiCikAplJazHrLGALqVAmqZQKygQyoVSj0OgUOhFPpBDLcRelRIWPRCDmC4rWZvzk+ZTHgR3/8tJ/eW7NYy9v+lFRebqAz1dIlQqJSqPQapRatBDNc9u9fncw7Iu6bV58LHqrzWjXq/UGrRF3BC35XEFAFQDL7wqEvGEu2iVa21TbTB9asi4aiHG5QzwBEEYsFKsKc2GxYZbiLB6OhXyhoJvTguBITbymJp6oq66tjlbFQ/H6RAO3bE3DlsaaRvBKS31Le2tHX+cAF8nyaIwseI3QDzFUkzKDMoABRLicYMzXmnPjWAn4JlUHdU4CDopzoTSmIBXUQYc/dPAQZfY8eeIk3leQwYULF8jugIF5efcyXm5cv66uDljgcDhMJhNmJOCJZLw7SnUVt+ByPStbt27F7dBC3AWMgmoACxIQZCHqY2E1Y2yFhQmWdnCSRcnvZ2sg4RlxI5yCznaWZd+7yApahU61h4WqA+HJAwtEj4OYNuERKHCGtB3Yklnn7t27OPHY0b8uNHOEGT5xCjlkkf6DRcxy/hzk7nqehdc+Yuzoc/j75KYeoX5ErO8rFg8JjANS25YCydYCURdP0SfRd/F1u8rUkwbnlNneJ9Xu5Gn6VZZxs3PK6po0Oyct3jGdY0xvG7I4B03uAal5XOXcF6tbDIcXPM4xvX5QrR4xaGe87rmQf74qdLSj6XTvtsW4v1+j7ZKqB7W2hUDsQE39cii26PQs2h17PO69Lvei1TGut047AlOuyHwgMWXzjJmMc77AhCs8YHJPhmtOzSye3H/o4OLy3um5w4vLx/YeOH/0xOmlQ7sbe3vFpmGxcn8ovjsSX45FJ6z2KYt31hGc0dmXDPbdDte8077gcR2oS7yybctSdXzSYZvQG6bU+jm9Y0JhHVRYJ+2hfaHEQX9st9U3prBsKxF1m9zTrZ37RudO733lyvFzr526ePP8qzdOXT67eOTW0XOvLR1ciCbGtOZdIkWHTA3sGDcYh4yGcY93whvqN7tG7L5JX/jNM2f/7//zW5fS//wCmYO+TAMkhlWIC8gKYAemNBAytA4U5ADEGiVEhwChcXqB1kmZ5NZJoXjaeVbAIpQoHcDxS+beQVvaSRLDlxUeL7DF68nxE4X0E6tYIWgg+HiRaVCwQ4pYIAUBBy2z8uOVklxsBT+hGionFSc0lSooKMDTATtwWZlMBuEMKUfBsXgW7I+z0Fk6Ms0c9idWlqHBVwh/TPZwKVx53bp1+FtB6kKGLy0uQbiRzeUgS6tK6hPs7Gepzx6RtuPjj4MBPyjByXwgQp6Iz+7HOG012pQSpVQgVYgABGJppUQhwdCu4JhAqZMIpMIKoYgvFvFF/FKekCcEN8iEcnyAGoAGo9po0lkMGqNKptarOAcOtVSjkWmxAwTRKvTc1URKvcqokqg1cg13ZblGJVVpFVrU18q1QA1ADOedil/FcoWE03asy/jJqnWPPb/hiZ+//HfP/fqxlzf/qKgsraysuKSouCivsKKEh4bJxHKlVG03OW0mO4DJarQ7WMoQi8GqU4GKLC4L53bq4zKSVddV1Uf8saAnXBuvb0w0Nde1NNZw4FUbqwtza8dEPA5v2B9OxBJVkaqIPwzIAI5wAbThOD4hL8gmHPQEgRo42Fjb2FzfDNrY2tja1rJ9S+OWaDDitDmjwdi25rahvtHXrj4qIwuGW8o0CmjAQN7B0p+DCTrYGrMU3EWoQZqPJHbsWlmclrQdwIKkYymuiTEbIzQm+tjHu4t39DWWJR1D/sEDBzHDQH2832DtmpqahoaGZEQZ3RH36uJCZLiMIPipkS3PSPUhOBQKBaQJ9imJKrADXQv9AT2EgmguX75M6bxu3bqFnYssvAXNOMVWirnACueKce48kGgPFy/HySkygi7Mc8F1OPc3v/kNOOOTTz4hC8tnn32Gr++88w5+wgVxqcOHDpOHB80MTrCQM9ohn1MyuCRv92jydvy1/Le/9Fg9nQLNoNw2orR0lkr7+eb2ckNrmaZLZu1XWIeUth6hsZevG5QaeyWqHqmmU2LoV1uGdaZJs3Xe6Z11R2ac4QVfeCoYHHW6u3myIalh1hWasLsnzfYeiaKDL5iwWScs1jGNZsHvOlgX2Z8Izbjtg1rdoNE0bDSPGm0z3sikKzxqdE4Z7Ysm2z6Xd8nlnfdFptyRMXtkwhmdtHvHTZYJk21QY+lVWw60d53ff/goOHBh+cDc4okDB88ePXbx+Okjo0uz0W0DMtOIWDmjNc9anGMG844K4bDSNKGxTMq1M3L1tFw5LpPOmPTLkcByNDhpt/Qr5V2Vgo6Cip6CysGCylGeckrrmLf6ZrWWSZlhR7FkK0+5y+Aar9++PDgN7Lh6/OyNs5duX772xtWbN45fuHPi4q19x5biTYCbDpGytVLaJZL3y5T9BuO43z/i8faZLX0W20Qo8tb5C99GsnwTyptvvskpPpc4D3H0aGwhjjCT0Wq1er3ebDaDPCKRCGUxZzJnO2QRM0xwxgiQB2kFyAeTxunBwUGv11tSUoKh/aWXXiJc+J8CRxI7QBhABEINSq1BYEEHCT4IOKgCAcfPfvazZ555htayJ8MKtklVByqsWnEmxd3XrFmTkpJSVFQESQgBLhQKQUJpaWmYAZLHKEXlTDFPUjwj6Tk4wwrT6xBs4VcIT4vFsnnzZjQDd8RdgCAVFRU+nw9SHZVxFumnd7NC8AGpfub0mT//x1cr/Y8WiF+/1w+GMKj0LqvLaXZhqLYYbAa1Sc2N/UqVRAkU4KBBTooHpVwk55fweKW8ynJBaWFpQU5BYW4hv4wzmgBEMPYDPnCuVqlHfZCEXKRQSYEOSpVULamUqmVamVghEYBjFDKRrLyoXFghEvPFABSQh05t0HPhLVrsm3Vmo9YEVtAquVwggJr1Gc+sWv84sGP1+sdXrX/s5bR/zCveUFFegv8qSip4pRWlhdgv45fxJUIZR0gqzgHFpDXLhTK5SCbkCTguEcnNOovVYMMHDwvY4vKYuQMRf6Q6moiHgRfRkDcc8AQjgajP5Qd2NNQ0NiSagp6Q2+7hlrWL1US4kJ9w0Mv9GglEauO1ddV1zXVNW5u2bmnYigoglXgk7nW6XXZndbS6pWFL966+K5euPqK8HRit8dZi4AdqYMgnrUbSXEIaSHS8bpbMAz91d3G6R+zvYp6knAsqIw/scF6mzM90cXHx7t03Hj58iDF+geXhwYz/xo0bIA8M2MdWVmbBWbgpyHpkJZqLFBV0O/QHXJkyicViMQgLTDvUarVYLJbL5RAltFId6uAKIHeiiossaRhQg3KUATuAIBReS+qNpJ7j7Flu5TbSdiQlzhhbTAH1cTraj5f+008//R0rlCn13Xffff3118lX9Pix42ReIT8PCmwhR5bTpzjsuMB0PBcvcDqPUydPPdIspf/2ly0yY2uxfEjtGzEFe5X27gpDR7m+U2EbMPv69K4BnbtLau4RGzrK5F0CdY/M1Kt2j1lCQzrHoIZz8phwRGZ8sTmffyHin7Cau0v5QzJ1r0zbI9X3y02dfNnW/JJBnaFXquguKp2xmmbdlnGtatZgmtDqhvXafoW8i88f0hoGTc4+na1XpplQaOatjimnb8IbHXHGpn0N446qSXdoWG/tEyv75doBg/X48OiZg4cPLezZP7f74NLy2WPHLpw+efnEmQM9UyOO6gGVtU+iHRSqxlTmLpm+rqBiR7lkUKoaV2kGhOJuPn9Uo5yw6KcclgmjbkAi6igvayutqE8vrvrlxo7UjPFyAZfjS2UaFKsmleZ2gXrAFe5xhobjLQs9Yyf3HL50+OS1MxduXnoV2PHWxWtvnnn19v7T04HGEaO3V2PuEMq6yit7hKJRh30uGlmMhueDvrlwcKm+/uH1Gyu+HY/q3/fb8j8r6LZkxoUIWl5JwoGxk9JgoGCIDQQCiWpOpNTX15OidJjl8YQEoDB+7EOkUHIL7OMIroBpj9FoLC0tpfSDhBe0feH5/25DwQ7pNpKoQWoMKvQVx6kO9slRNMkcKLSiLNFGEkHIsePLFhY0o7CwEGKwneVvBBhRPjHMytrZujCjo6Pk0gGhSg9Iqo6hQc5+1MvCaCFpURPTOVDLyy+/jBv98Ic//Md//Edsn376aVytrKyMIpDx54IoJpU2CuT22OgYJmNf/Qf4WxdIYK/HW1ZcplVofE4fi4C1Y5hXgDaUeo1ci2Fbq9DpVQZOCaHQq2UaUYUY0CCuFIsrJeXFFUV5RflZecWcu2hlZXllfnY+r5RfXswrKyovLSwryS+uKK7gl1aWF5eXFeNIuYgvYcdL+OV83BenF+eXAD6ALFKBTFAhxHWwDxBRy9QmrUkqkHJKF4lCJOStTXv6ec6l9LEXNj65OuXxX236fnbhWoGgQiVT4SPCeeV8ER9X4JcUlKAxQp5QIZGDKtRSlUqikArEAp5QyBMpGUtpFFqFWGE1WLnkInqTVqnRq3VWo9Xj8DgsDrvZEfAEgr5g2B+Kh+LxUJXT4tKr9VwWk0jc5/Z4Xe5YOFpfUw+qqInX1HKfRE28mi18H8K50UA04g8HvAHUaa5v6eCylJ7/3x9NltI9LG9HTU0N2B+dcGAlPnaILcyYDGbZxfKTcrTBOXhwpZ05f3R0dFD8LalGCEdw7lmWYxQEQNBNKbPOsdRewA6IA6AGXn2qTNFr6CHDK3nDwBPVrEBegDbQlzBNUalUfD5fKpXSlIXsJmjD6Ai31MLhw4cx0tO6a6ANUnXgK+XVoIhWQAAo5NWrr5LLBbmeAORJ1qAZaAxED9pJSTs+/vhjvPdgji++4LQdwA4cvHv3LjltUGg7OaZhB6gBjsH+saPHTp7g4APYcfnS5QvnuYBb/PSIsaNOYNhSqhsyRifcdeOO2IDY0Cc3d+mdA+5Il9UzYPd166yjJmeXSNMj0A3IHH3q4KAhNm6Lj+gC/SLbgNw+aXVPWa0zLvuYTtdbwptUGoeV5j6FaczsGdbbxg2WYb2hV65oLyoZtVsmfPYBpWJCqZ1Q6/rk8n65rF8qGjXr5kLBMY+/S8NlTB8x2Pp1jl6Dt0fv7zdGx2zxCYu3V6LeWS7slKr7LI4zc/OnDx46vLT34NLeI/v2Xzhz6urFc6+ePHOga3TQFhkweXcpLV0Sw7g1MOKM9Jk9HWJNj0Q1iDtqtQNW81JD7UJVZNxqnjaZBsSSbl5la0FpSyG/dnNuR15xf3lld6WsV8kpdSbN/jlf7VykYcQdHY02znT0H5nde/HQyWunL9y58tqbV67fu3z9/pWbd45dXKjr6DcHBwyeQaluSCAZUinno6G5GJjDuxzyLgd9sz7ve5cuf7sU3DehEHYsskIKD8zMIeIg1oLBoNVqBXZAetjt9lAoRPpd0tFC0JElYpwl1yJRQD4fZJIgFmlubhaJRJQtdDUrTKHxVwNK8khSw5EEjiReoHz5yM+e4WjjZ6wkseOHXypkZMHx575kYVm/fn15eTn4CY0neoD0BjqgYTgOjKDE59wscaXgGUdY1g2S0r3MT5+bJfZwC3NCluK5cGUwxz/8wz9897vfxfb73//+9773Pew/9dRTaB64ZOPGjZmZmVlZWWlpaZgBvvPOO1/9B/hbl08//SzgD5YUlgh4Ap1Kj5HYYrBKBDKwgowzr0iklVLOLVSmAYKIeBJBuagkv7SyjM+wQ0zajtzM3Oz0rKz0rNKCEnwFauRn5advTN+UsglbUAW+Zm3OzM3MyUrPLsgpzN6cXZCVX15cVphXUJxfnJORU5hbWFZUxj7lBTlFoBPcBW2QC+VSoVRQLqgsqywqzF2z6SfPpz753LrvcNqOdY//ctP3c4rWiYRADQGvjFdUUMgpPgq5eBlcFswhE8r45ZV4EKlAIhfLjFqTXKwAUWmUXEIzjUKnECv1XE4zlUahVstUWiVbS9Yb8HsCBo3RoOVyprnsLqfN6XcHgt6Q2+52WZ0Bj89pd9jMNtSMh+N+t89td4U5E0w0Ea+uqUpwHqZhzoJTHakO+YK1VTWcY2lN4/FXTvz5T/9hDdbXwQ70TFA/xm/ALMXN9rCUo2RSIZXDjpX17vHK9nBO35zXdyfLWErkS8SNsoMtFIfT9+/fz8W13rlz4MABYAe+kvflKZblAqKBtAvdzKxD98IWp4OB4vE4UIMy/LicLlpSUskK3nV8pRWeUA1TENxreopLX0N+16ABIg/Kwo57kafnq2zxtuvXuaUU6adjrAA7IKFI9wjZhA6MqRLq3759+7333vvwww9BHgAOYMenn36Kr8AOig0GzQA1yPGbtsfYsgV40kNs/aQTK1G1Z1l2EDTj0WLHv++yBPq0rkl3dDZYPe2PjVldi7HahdqtCzVbhm2eCat7SGOcdXqmHL5Bg6dPG9wpdfRoPag/onUM8tV9FYoRlWFYpR3WGQblqoFy8ZTKNmcLjtt8C+GqGW9oxurslym7hLLt+aVTHu90wDditffJ1YMa/YDBNOP3zXnd0273lCc47o4NWUNcaK7NP2n1jJrcvWrbgNE3bPL1SjSdPHF7ubhLZ56qrX/t6LEzhw8f3Xfo6L7Dxw+/cvXShds3rt44fXpPW+eYIzLqivbbI10qx7gzOhNtmInU9mut7TxJt0w1aDZPRUL721oX4tFRg3HCYBpSqIfkmm6BrF9h6Ffo+6XKKZNl0eWf9QTHnMG5QM1yuGHKGhwxu0YD8eltnftG5s/uOXb1xLlbF6/cvcyRxzu333zzwrXdrb399tioNTSutk1qTWMm02zIPx8JTdhNU4Aqi2FUr3v77LlvjSzfhEJGFsrBk8SOffv2gzwgo8jPQ6FQgD8w0SL3NWxphKa5FqEG5MAQK0Qk5B5BXzH5eZ5lKE9hBRxAWo0kanxZvZFEDQzbyZ1kSWIHlaeffhrbH/3oRwQc/8jKlxa15+Jmf/WrX4E5QBhoP6mW0eCpqSmgEkCKUoNgqobH6e/jMh0kA4MpIAACdhtbEI5EPYEI/jK0MCf+OOvWrfvpT38K5njyySf//u///oknnnj88cdp/0lW/n6lAD7w1/7qP8DfsnCiElLX7/NnpWfmZedhqK4o4YnBGZUSfIR8YWU5n7lcqKVCOb9MwCutxJbTWxSVVZYJhDxRfnY+oAGokZ+Tn52eDXrAkbKi0qK8oqy0bDBHZlomDqJOxsZ00AnbyUjbwLEIgKAgt6AwtwjHcVZuBi6BNhQV5Rbj1+KCEpzIK+WJ8F8JH9ghqKxIyfj58xueXLX+sZ/96r88+9J3Xtr0VGb+mpLi/ILc/ML8wuxMME0e4KaCU6uUop3CCiEeqry4orSorCivEJwEBAF2AGVELKGIVCTXKDUmLbd2jM1oM+tMwA6HxY4tZ6DRGNi+xaDR20w2u9mOrUGtV8mVOo3OYrJYTVaHxWHWg04cAY8fhBEJhsKBUHW0uiHR2FTf3FjbUMtRCLAjkogljhw68jXW2fk62IEOCYQn7wpSoKEr0jop5C6K49jHAE9c0sNi3MdGuU5IqpEkqSQ1HwAI9HmM3BihMejOz8+jV8wy1/EkdkAiYLwfGhzCLYhpGhsbARMgDcxIDAaDnopOj35OvugQFuSODsyvrq5Gs9Fa9Dpch2wcpFQgkwoFm4AGcPwSW4P+JluhHkRCJhKysKDC7t270W+HWUE/xFewAhpPSVEpQcgnn3wC/qCk7OhsuNp5liOEQmDwN1xmsezYp4j5oyvRwuRn+gpL7/GIsaPPGx11+Kb8wUmvb9Tu5pZp9VdNhhpnI43zrsCs0T6pM886XbP+0Kg32msJ75QaR6zuperqKbtjVCjZmV86pDIOqs3DRlef0jzI1/QLzQNa57DdO+72DZosPRLV9jLRTpGmOZc37olMeMPDDm+vxjCoN88Ew8t1tYux+JgrsEvj6NUFBgyRfo1/xOAZVBq7hKoBtWU+mBi1+trLRZ18SYdUNVFVc3738s0L508BO/YfPn7o6JkTp65fu/rWmzdvnj45Ha0eUFonnLFRd6JX6x4yeKb98dlAdFhn6ZQo+zTGQbN12OmcCgX7dboekbRPpmrnS3ZUStsrJL2V8lGlfkilA2MterxzLs98KL4cb9jjr5rSmAeV6mGXZ7a1Y7Fn8tjMgQuHT7165sLti1fu3bj90TsP379zb/e27n5baNwSmLMHl4LxYZN1SG+Y9QfGzKYhpXxQKh3S6h6cPf8tdnwTyl99O1bS8JAXArlAzs3NATukUinES4StU9/CCkZcSC1S1kKaQbiNsKSIJBDIstzLwvewhZgqKyvDwAxoAL44HA6RSIQBeO3atS+88MJXjCbJ/eSRZHnmSxqOp1khTw4QxpeZg+wdSex48cUXU1NTJRJJy8oCbwMsxQgeDfIW7RQIBBs3bjQajYAMkmbDzFREz0LkRNoO8tIbZ9mYeplhHUIYfxPM6HD99PT0VatWgXi++93vAjseY+U7/2NBHYj3r/4D/K3Lp5986nQ4Utat35iSmrZx8+bUtNysXDBECRv1gRdysbyUKSHwqSwXSEUymUjO1AklFSUVqIAxHh9wBj6FOYWAjzKcl1eck5EL7MAnY1NG6toNm1I2pW1Iy83Iy9qcvTk1PSczh06pLBeWFJQW5hYUZOcX5RaBPzh9SUFJSWFJUUFRTlZu5uas8qIKYJCgkp+aufr5lL9/fsMTz675u5//+rGXNv8gK39Nbm5mxuZ0EAyumZuVg8ZzHh7FZcCOfHZNtCQPWJOZk5edW1xQJGIJRYQ8oaBCgPaLKsUm0Ibeajc7bCY7MMJpcXgcHrPeDPgwag34VavScmnQxHIwBzgMO2qlymwwATtcdrfdYseOzWxzWHGi2+NwuW2usC8cC8Vi4Xg1F1WbqK+ua6prOnH0xCNa+B4jJV645NiPLYZzdEW82RQfi/1mVpKMjDd1fIzDf3qbUfpZyp1OVtp3cLaYmZmZy5cv05KtuAUq4AgGZpr3QxYssmWmgS+4NS5OgS1gDq1Wq9PpwBvoPEAN2oekoIQ/Pp8PUE+GRkiKwZVVE48cOQLauHrlKoiBomohaCBucBxHrrCV24g5XmNruaHyCRZ1cujgIZxOzlYUBYOvQIpbt249ePDgfbYgCyVF/YjlRcXB119/nXKwEj/tWd6zvJtbPRK3I/LADhd79gqnfaHnJXPMH//4x0eJHR0276Q/Oub2jbs8I2bnhMk76Yz3WcJj3uphnXVAqps0uuY8/oVgZDYQ61Hbt1dqulTmuWj1qMU+wBdtzysZ0pjHrf4hU3BQH+wTWtvL9e0S0061sUOl3i6R7xApWioVO7WOhjJ5ty3cbQnskGp3CuT9Ct2YxTFud0y6vX0WV4/R3aNzd2vc7VLL9kpdXXZla4lqQOebDTfMhWpGDfZBrWnQ5T08MHjv+vVbr145/corp44cPXPsxLlTZ27dvvHuw3u3z5wYc3p2lYg6hYYulbNPYeuXGkcNjimbe8Jg6ddZxnyhQYeny2gBQAwZjL0iWY9M1VJWGcnIq8sq3paeP8SXTOrNUxbrlNk0rtONGUzjJvseb2jObF10OSd9vt3bO5cHpk4tvnLxyOlXz5y/cfHS/Tuvf/LBR588eG9vW/eA2Ttmcs97oocat47b3QMq3ZjdOWKxdghFPVLFiNnx4Ny3LqXfiJLEjhXHR26hV8IO9E3IGUiParYqWxNb/3L7SoFk28mSEvWzdIikzSUQ+avyg6k/IAPz8vJ+/OMfgwAw+SGhl0gkTCZTeXk5hvxf/OIX/8KWavsyZCQJ48u0QYWYA8BB0SvYAjV+sFKS2IFTVq9ejetDPJKnJwWkDDPHO9DDAlu31u/3p6WloSWQ3pyUZhlXk8KNAmiJV8hblrzZSO0xwAoeB9evqqqC7MV1Nm3ahPuSzwfa873vfe/JJ5/EFvv5+flvvfXWV/8B/tbl008/dbtcmzZs3LBuA7ZZaVmcviH/rwxRnAegKC/MKSjIyefUBiW8ksIy7IAMOEUF4wYM8zlMjZGdnsMpPzI55QdndsnITt+Ujgtu3rAZ2/ycwqz0nM0b0vAV27TUzekb0zas3QBMwVnFeUWghIoSHnilrLC8vLiCyKMgtxDgkp2WjRsV5OduyFj1i9Tvrkp5bNWGx1elPP5S2g9yi9YBT/KzC5h1poDTlxQUAyb4ZZX4gI3ANFnc6cCg7LSNmwAoFaUVleWV/IrKMi5HCA/3lQokSolcLVermLVFq9QYtXq1QqWUcavZ4aBaoZaJZGK+SCbijDUysVQplZt0BrvZHvSFwoGI0+YCdgBWsOOyOz1Ol8fpCXgCfrfPZbM7rXbsJ+I1Rw4feURGFvRG0jQSc6Cga1G0Kqkx6Cfy5OhjGUvJKDjA1p4lJSTp8ciUuLOD01jiLcdAe5OtLI9JP76CxzHMU4gHBxzM1xpb3BGdBLeDIEBPtrKCaQQgw+l0YoLicrrwFdhBthV08vr6erQKDcBl5+fmIVwgVkirQdk4SA+BRyOzC9oA2gArXL92/dWVVWdJCUGeGbvZ+tfoieiNmCThlNu3b9+/f/8hK6TwwPadd9554y637C04hrQp5LNG5eABLtidbC6474njnH3nFFsVjyJcHi12/KVBphswuQb01nGLE0PjrDu6O1I3HU7MxhuGtM6OcmWPzDRtC4wbHENyQ59It0tibhOa+vX+TrG+o4DXWcaftDimHIEBnadX5e0UOXeKHR0y23aJdrtY0SqUtYrVO9TmHou3U+/sMvnaFNY2vnwnXzqoMg+qTSMa/ZjB0qszDxpt407fqDMwYAvtVLqay/TtMk+XKTIWbjiwrWOptmGupu7k+MTb1699/OH7d2/fvHT61KWTJ6+eO3vlwoW7b9798JOHd8+cGDVb2rOKm9JKGgqEnSLdjnxRV6Vm3OQaUhk7pZohq2fQ6d+uNvWYHLMe/4hGO6jR9msNiZziqk25W9Pzh0XyBYdrwe1acjmmTYYBiaRPJJ20OccdjkWfZ8LhWG7beXBy4dzBE1dPnL1x/uLNi5fu3bnz0cOHH7314NCugZlw3VygetIeWAxXTzg8vRpdj14/bHf26C1TntC0L/Lg/MVvseObUCiAdjeLuiDuSGIHOib6eE1NDUXPUYIiSB6adBF8cEZkRh7k80EjOmQC5MwYWzsNojIzMxNwsGHDBrAL6Qz62YKxuBRGfbFYnJ6eDij5+c+5ZF9EFWQ9IX1G8kiy4DgBB22feuqp77NC2EHr2uNqqampZrMZDRtnhZQZg2yNFdJboJFoc1lZGVqIZ+QE7PTMHCuTrNC0apQVnE4+K5S5ZJqDkL9CCcEHHgcXoYxKEomEki5mZGSkpKRkZ2eXlpZiBgjZ+NV/gL91+fzzz+OxeGFeIXCBNA2FuYWV5YLKCmF5MQ9jNgigAp8SLl4EwIGBPC8rLy8zF0RSkFMIGsjJyAU6ZGzOXPvyus0buHGd+XBkZaZlpm1MW7dm3dqX1gI7MPZnbMrcsC41df3GdWvWr3t57ebUzcAOHMR9USE3M4/Ti6zfgHNBLXlZublZOWhVxuaM9E0ZgI/Nmzau3fjsC6nffW7dd37OfR57Ke2pgtINwkpeaWEp2pablUd6mtLCkgpOnyEiB1W0sCC3KD8XSJSNB2QxL3x+Ob+koFgmlokrARMymUgqEYgVUoXZYLEYLIAPlVwpl0jlEhn4Qy6R88r4XJywSCIWCPGTVqUxaPSo6bQ5nTaH1WTRqbVOq9NistisVub5YfVgjAWF2JxWowUc47DZ9iwvY5D66r/B/1f5OtiBDklggR5Fig1s8ZWMnXgFyXuUXDe4SJW+vs5dnFcHvqIaAIX8lbpX1lEk+MCrDM64zgpQA/uQAuj5ZATB0E7dFUhOyT/ANOj/oAp0XQAHWVJo3+P24CstXU2SAqfgjuhp6DPJzHq4MnlUHGE59ZI+FufOnbt69WoSO15jC8NeZBk1TrFkqYAD/BFwqbm5+bnZOQisU6dOo9kQYeCM3/zmNx988MGHrODr66+//hqLxznN1rgnoXaIlcOHOGdScvg4yspxtpYjOZHg66PGDrF2R4W8T6CYNdoGDeYhq2vS5Zvw+ke9oSGzt0OobylRdIoN7WWy9iJ+v1g5bHJurVS1lMmb8iu3c3k5FYvB4EIwhOODBtdOqaVNYOqQmDsk+h61eZdC1y7XD1g8XJJTe6hX69zCU3WIMNg7RizBAZ13QGkZ1tmH9M4Ro3PeF5oLxcb8sW5LoMsSHott2d3ef2Zh+fUzp+9dufT2jdd+/+lHf/7z7z/+4rfvvP3W9UuXXj1z5sbFi9cvX757986HH7x9ff9yr1jckZa7Lb24MZ/fJTO2Vyg7RLphu69DrtnBl/UAm7S2Xq2tW26YMNhGlJopq3PS6d8hUjcVVvZItIMqI47MOV3zVutuj3vSbB7VGsZtjkmHfdpiHNXqZmsbj0wvnHvlxI2Ll9+8fv3O1atv3b71wXvvPbz75oXppX3NHXPRxkGzt1dn7zfaO1XaIb1xxOqcDlYdbGpbrmt+7+pr//Zf/69H9U/7bflflrfe5NKFQSaAPJJGFoDIIZZQGF8xYyFf9RgrZGTBlkRZO3MyTTqcQUBRhB35mUJYNTY2AjgABzk5OZBCNA2jcZo0HxBiEFMKhaKgoGDt2rWrV69+mikzkmEpKNhJxscmj/8TK4AMYMf3vvc9YAepOnDw2WefBXPo9foOlm6EnFtJeBJDUNsGmCeHyWRCZTSgmy0GDrAAdgyziNlR5io7zLKi0axyhAXWEo5ghwCLroZHI8c+IioI3poEty4u5n7BYBBfcS6Y4Kv/AH/rgltUxeJ52XngBtADhnyM3JUVlRhlmZ+HRClVivlizqpSUASMyM7gRu6i3CJgBwAFY3zGxgygA4b2VCDFutTMTRkZG9MLc4vysgAT2S+9+DI+mzemb0zZlL4pPS01LX1jxvq1KWvXrMvNysWRFI48MlLWpqz/9fpfvvDL559bDRZBHZAHgCOTtYq0KVnpGRvSnv9F6j+sSnnsX9b83bNrv/NS+lO5xetKSwrRZqBJVnp2cUFxaVFpXnZueUkF58Mhkgn54vyswpL8Mn6ZoLJcKBMr1XKNTCTXqw0SgVQlV8nEUgGvkiJcNAqNxWi1GKxalRY/6dU6nUoDwgBtSIQSqVCiVqiBHRqVGjs4qFPrgB1gDrPehJpuh9PtdNksVovRhCMADrfN6WAmm6AvEItE0VP+9MdH4ttxiC0FRxYWIg8qBBn0/nGmE+Y62sFWfSOw6FsJb6EpAs0S+pm1BUfwcmNugQH+jTfeuHTpEoZnkAdEAFlDIBrw1raz9GI9LM86Cq4PQQDaAEfjzQaCgDY4r1K2dCTQu66ujkw/OAWno5PQnObIYS6PLwqZOcjSkdR2XGCLwXKpyld0HvT1r5E1Z7gw2kNsWZY9nIsGJ6coIub27dsgj/v377/33nvvP3yfVp67cxtD0tWzLPiWsAMF0ENuItQM0nkcYdmFSbvzn2Fk+csWhXVHkXRHRtGoRDFqse6UKQekqkG5ZlBvGbG5Bgyu5jJFc4mkKb98J188rNbM+f09Wn0bT4Qj28pFO+WquUhoPhqaDfjHHa6WckFVVsWWUuU2nqpXbelWGTsU2j6zY8Dk6te7OhWWLqV5p8zYo/f2maO9hmhDsWqnxDJi9o+ZPNMO/0KkajpePRCMj9U2H5ucfe/W7T//7vP/41//+Kc//+F/+9Pv/vCnLz7/wyeffvHBBw/fvnnlyvlXTlw+cebWlav3Xr/9/r3XTw/27ijI35Ga0Vsk7JfoBpTmHoV5h1Q/YHO3K7UNJZXbBYqWEtGWUlFrqaijTNRZLpyyevs09h0izQ6hZswR7NM6+lXGWYtzXK2bc7tng6GFUGQ5EJ7SaadU8hGZbDwUPTI1d+H4qbs3br537637d27fv3Pnkw8++Ow37989cmpPw/ZRd3TME+01eXZqbW1S9YDe0qO1jLpCM8H4pD/y9rcL338zypddSgk7UEgsYGqBI5ByZF5pYdmACDXaWIblLpY4gIws2GljmZqTYq2LeWVi3F2zZg1QANP91tbWpDxEfQhJXBxXpjRIIBuDwVBWVrZu3bpVq1aRGYWyjqKQbiP59cvlKVZ+8IMfJJkjKyvLZrNBJrezQuIaNyWNBSiB/FE6WXwK5GRmZmZGRkZDQwNENKlGelmhaWQXc8LrXPFZ6Weqa+InzozECtnNydI0wFbgSup16HR8XVxc/P3vf//Vf4C/dQF2VMerCvMKARyc4WNTegmX+qKiMK+otLCUX8qrLKvMzcwDQ2D4T12XkpmWnrU5Iz8rD8xRXlyGobokv5RcOrLSsjalbNq4LnXzhk3Mb6MY9PD8cy+8sOqFlF+nUAVOBZKatjmVcyLBLZiOZH3m5qy0jembNmzesC5l9c9XrfnVGjAE6gNxUDNjU3oO522an5udvTHjly+mfn/1hsefW/8YZ2TJeCq3ZC2wo6KUV5hXXM6pZMpysrIBMaCWihJOT1OYXZifVcDnFn+RVZYJRTypQqwEZGiUWjWn0lBLBOLyEi64V1wpAliAPDhri0wpF8sMGj329Rq9kn2ViaRGrUEukQE7wCJKuRJogjrkcMqt1muxOmw2tVIpEYr0aq3b7ooEwmFfKOQLVkWrWlu2nTxx6mvkYvk62IEOSX6j1It2rCQ771jJT4ptN1uuhQ4SZAyw/MEDLEkoKiTf3d4vpf0Aa2OuT8kzjrLF6BfZ0oUYg7HFr7gaLkvdBvXxFX3G6XQSc2DrZgUUgj6MzozOtp2FyeDWXCqfqSlccGFhAcQAViAfVfIgIxoATGDUB14Qc1y/zllYgB3EH2QoIRdXUAKdSEIKZ+HXGzduvP766xBkDx48eOedd95++23y6rh48SLI6ThLyr6P+cbuY5nKcLtXWFI/srNghzO1sELHHyl2/Ld/77AEO8XGho15rflluxTq+pyibZlF2/MqOoXKXoVmUGfpkOpa+dKGIsAEv1OunAoGRpz2HqVyS2lFTX5pfYVwwOmaCmFkDY7YrFt4/GhGcVUWr6VCtQsDv87cb7X32Z0Ddv+AJTBo8k44g2M2X7fG0an19ptizaXaNr5uxOwd1bsmLb7ZQGypueXIyOjZvfveff32H7/45Pe/+/TDD99/+8GD+2+98Zv33v780w//8Lvfvv/eg+uXrpzc98rx3Yeunbnw9ut33rnx6oFtW3fk5ndsyu0vl48ItX18RYdQvY0nGzU7Rm2uHoOtz+zapTa3Kwz9Znef2jiqNc27o70q2/YK6S6RcsDoAn90lEtGgAsi+YTFOhMIT3r8Cw73uFy622qYUKumo7HjcwuXT55+4+bNh+88eHj/3ntvvvX5R7/9w0ef3D12ZswXHba6ubSkNk+32d1rdQ97I/3OwLA7Mmz1DpscD85861L6jSiEHX+NnmV+pUlTC3XVbrb2Eylrd7JCigoauWnQJXHUw+L1kqMvRYL4/f5f/epX//zP/yyTycABJBUJNcglE1OjmpqaGFvIHlvU1+v1PB6P3D5+9rOf/dP/GB/7Q+Y9mnTmQCELyw9Z5oyXXnqpuLgYMzE0GC0hVmhjSZIG2YL1kysheChoIX6C2MzOzk5JScFUDdWGVjxG25mVnBQ5kKIEIqQg6Wa5H0l6J5+aDhKHJbGDKuxi7rcQvI8GO2qqE7wyHueSmZFbnF/MK+UJKgTMqlLBYl8L8nMKCzjtBeAjt6SgBHU4T4uc/MJcDOf8sqJyEEZBNoXR5jBzSU52elbm5systOyUX28AYeSk5wBcgBcbUzZuWp+auTmDIUgWcOTlX67ZvGEz+APEk5OZk/Lr9WtfXpuyDmdtzsvO4+wv6VnYAYJkcs6pL/0i9furUh5bnfoEPi9n/iC3ZE1paUFpUWlJQSlz6eCDIXI5A00uGswr5QvLRcIKESUCEVaI5SKlVCDjl/FKC/GkFRIhp8YQVYoqyiokIinnK8rcOLADztAo1djKRXJBuYBlYpVSqla5RA4uMWiNarlGJeNMMOAPk97stDnMeoPFYDLpTS67y+fyAjiC3qDb4Qn5I011zadOnPrXR5O3g7QdZFghlRrpNtpYztBdLBXpLhZ5RXOCfubsTcCR1HPQy0qY0ss0eLvYKiro/Hfu3Ll37x5G92m2CgCOkDaCGBydlpyeAB/oG8AL9FK73f5la0s8HifSp24zwJy3cSnSH5JikJgGF9/HrB60Beskc3aBM8gng7xKQR6XL18+d+4cKoCNwARoFWEHZkWgiusri+WivPHGGxBn9966Rx6y+DWp7SCJdpAVIg+aV2F7+BCn8DjMUnrg+P59+//whz88SuzYZvXvUNkiGSXxnLL6ckldAS+WkhvakNdYKtpWLugQiAb0ph6dqZkvrsoraeJLem2uAatt3G7vUqqbhfImiWanwT7iDfaZbLtU6g65cqtAHc6oqM6XtEr0g3bPVCg84nIPWTxDRt+w3jVt8+z2h0bNrn6De8QWGzNHOiX6HrluJ1/WKzP2mz17OzrfuHL1o4e/+ezjDz5478EbN65fPnX+2J7Dx/YeeuP6zY/ee/ejh+/ef+POtQuXTu49sjw4c3zxwN0rV2+dOjUWDG/LKWrPKuviqYfFxn6hukthbOVJ++SaEYt93BOe9MdnAvEJX3QmXj8fqZpx+abs/mG9c0xrGVVph3TmLQUVO/MrBgBSEtmgUj2gMfSq9f1i6aCIv+x3jBm187U1p3cvXzl15u61aw/ffvDhu++8f//BZx999PvffvLm6QsjTu+I3jhqtfcarZ16y7DTPxFKjIZqBr3xQWdg1Bu8920kyzej/K+wg/opvkKGtLKcV6QboClTL7MR00jctzLvH2GFfB2SmgCIoxdffPGnP/2p1WqFSExyAFlqKJlyFSvxGFvhoboa24CfW89ByVanAxC88MIL4A+wy49+9CMwB6XH+IeVAvLA9cEoOTk5RqMxKf3I2EGqaGo/kRNJb3APcAeoodVqCwoKioqKwB/bWDqlXUyRQ1oKwqxky2lWSdchtOphMYm7mPKGszYxH5cupunpWsmqQHUgcn/3u9999R/gb1P+u5DksCNRU1xQwg3VmbkYwblkX0UVGMVLC8tyM/PACtlpOWkb0jalbMrjxvKCzE1ZGRszABMY15kTRj7opDC3CFCQk5GDU3I431LASkFBbiGF0XJhLKlpqetSN6zbgE9WWmYOxyjsJ07JsQG/cpCRU5iVkb1pw6aUtSk4zilFNqalbUzLzc5D/byc3M2Zv34x5alVKY+vSn181YbHX8p8Kqfk5YoKNJfzQq2sYOvZihSlReVoD1olqBDKRQpgR0UJj/PzKOVLhTKxgIsN5lfw5VK5jPmHCiuFwA4cwU/ACCmHHZxuAwiilqu4pXErpVKhnFfCxx9HzN1CBvgwqA24HReIK5RpVXpuMV6rw6g1WI3YcdpYtK1JZ+SiXWyugDvUUNN04tjJRxTJghGxg6XfSL6UeKuwX19fX8PSlqNsYzGx9C7StABbvO5JNKE3sostyELaEaoMtsAAf//+fQzwSywrFxiZ1AMghhGWIh0HIRTwEuNc3BHYoVKpyLHD6/GGQ2F0ZtKyJPEcjMJ5QE1P41zy9sJOUr5wwoaVQ8y3I6nwQAFqvMYK7VPSMHIKOX6cSzm6j5lmACJo9q2VQvCBcuPGDZxFp5ACg+7IeXYcOkTwQfOqg2w5A/JX3c8KLvuIsaPLH68TavzZvGihOJDDTxSL3ClZno351UWVNdmFNek5uySqCU+gy2hrVejbFKbtcn1rpXTM4hgwWvss3k6Lr9vq67N6+iyOnRinzY4ug6e2TNFUod4uNeD4sMM9pNEOyjR9Uv2A3Dim0M+YLDM216DW2quwtpUo2krErSW85vzibWXCHQrjyfG5u1euv3v/wb27d65dvHh8z4H5nom+pq6lobnrZ6/cuvTa66/euHHlVWDdoem5wcT2yS195/e+cnZxuVWuqUrNaMgobCkSdYv1fXLDDrFqa7loW4WoVSDbrjAOWXwTFu+Q0T7qjUz4w90qXadM2yXTjemtMxb7sNbUtDmvPbtgZ1kFYKutpKK9rLJXpmorKd7BLx13mvsN6pFo8NTuhVdPn379tVffe/AAbPThe+9+8enHf/ri83sXLg/Y3bsqxf0KzZDZ2mMwDVod/Wb7iCvYY/cPhuJj1bX3Ll35Fju+CYXDjn2ckSXp25HUduxnES5f1uzStH6Q88rkPNJIgpEQA22MslARCjolBEFNcMCqVaueffZZDOo0s+pktpWkayoIgNIgRaPRZHrlWDSWqE5EwhGINZvNJpfLy8rKsrKy1q1bR4vMPfPMM8nEXDgCaNBoNGAIzqOipgYXJKYhrxSASD1bJ5zkM7a4LGZoIBvAEKU4MhgM5AmHamRAJzDCFl8htyF4SXSTYKfZZhczeUPStjNnPk4XxLCmg2nBaXTYxZxCUBMi9/837Pjv5YsvvqirqcvPzsNgn5uVV5hXXJwP4CjnlwkKcgpZEGxmQU5RemrGpvWbQBvpqenpGzM2paRt3pAO4EhjX8EWYIjMzZyHR0F2AR3Jy8xHBeBLUV4xsGNzyubUdRs5/tiYnr6JU1zgk74pIzMta/3alNT1qYAMtCF9c3pWBpd5LDszh3Mj3bAZP21Yn5oBUsnOSd300i/Wf//5DU88t/6x51Ieeyn9+1lFvywtz+eX8zkNTSkP5MEvqyzlwnp5leXcqjHAkfISLi6mKK8oLyunOK+oIDc/LyevpLBYwKvklVbwyipKi0srSsv55RW4jlKqUis0MrGcZU2VqqQqcIZaocVHyAOhyFFBJVNyxhe1ERUkbLUalUwjFym5RGQKrVapU8s1uAg+SpnKoDGwlOqctuP40eOPKJIF3ZJerJ1Mi0gvIvoPOXvXs4KvPcyYQrOBXqZ1bGUxt5z/9/btnWzqQHWoJ5OaDpQA4Hj77bcxzGNgBmrQAksEB5OTkzgRwIGxGTiCs9CL0C3RbcjUgo6Hzobr05WT044JtqwReVeRtQUX2cOWKVpmphby3ybVxdGjR8+xVVqIPEjtce3aNRwhf1IwBLaogy2pK0AMFy5cAGTcvHmTKpNvLE6/ePEiqpEOg7xJCDv+quE4zK3SQi0hVw8y3xBpPVrs+MtwpKqZL9si0NSVyBIFklahqqFU0FwhbhPIQynp7hfXN+bzRiyufouzz+btNnq28JW1eWXdCt1OubZL62hX2zp01l16zpjSbbQO2Hwj/tpOk7dTZ+sxOnrM7l1KfVtBSXtBSWelpF+h7RXKR1XaUa2xT67rk5ki6/MbssqbC8qaCkvaeELc/VDP2NmFQ5cPnDh/4NjxpYPzPZPtodY2T/Pe3tlT8weOTi4en14+Mb//1PKeiW3bG5WeGpF7OLFjONEUKyyLpmWH03MShbx2saZLomkpqGjMLx4/hjgAAIAASURBVKnOKaoD0Cgtg0Z3t1i1U6IYcAcGPIGWSnFTuaCxlLdTphow2YbMzi35ZdsLS7aUlTaXlm0tLNtZLuoSy3YI+Ft45T1azU6pqNttOb44fe3c6devXXv7/v2P3n/4yUfv/+EPn/7rn//w8OatqWjt9jJpN1/eo9D0Wyz/D3vvGSVXdeb9fr13PE7zzsy1x4BAodU5V3dXd3XlnHPOuXNSq3POudVSK0tEkUR+MdlkMAYMxthgbIMZTJQIxgST5/73flAN1x/uu4Y1S8MHtkpn7dpn1zmnqqv28ztPPJpt2h+KHIwmD7Z2Xjwxc8Xy2h+ffOqzTz79+z/Bt+2sN2AHfmjMpZSn7viqqoO0IFjTILlJAJM7PAFHTu5ucB0ABaaiUYe0AlghbTbbVp6vEzyBp6vcYIGVEMvUDG8Q86ANkMFu3nDvBCbAUxAAztu3u6+bNyr1AIgBKKRSKbVavWXLlh/96Efnn3++3+8HMWDVxTHp3m9sdAydwcFBjGNVxNqIhRFHphUSg62trR0dHXSoBG9YRbELZ8FeXBIORckV0clRCC6bCl2RtehL1FhlUoBuKed4VCNpwckyRZ4lq9w4jkUYTPD3f4D/7nbqjVPDg8NKqbK6QgCpL5cqZWKFXmXg8agMO0R1YpCHoFxQU1UjFUmFgnoghUKiFIIkakXC6nrsZVaSeolCqhLXSymStr6mQdogrxeATpiapKSgtLq8moWxVAsxXy5RgFEYpqAjkpYWl5UUlVSUVZSXlldXVotF4gahqL6uobqimoX11tVXVVTVVNdgvK6+vKjm/ymo+z7F0JY2/otMX2Y0qwEQXocX5GExWYw6o4xZeWQMOHQsyEWjwH+Dy+YyaY0mngsVFMJTkrAwFhdLqW5xWG3YkpmGFbqzuvAJmLRmUIXNZGMpTQNRu9kBgnFYHS67M+ABYURD3hAmY5zZcSwsF1nQG2TJT93BZDSViqV5bRpGIYlwcmp06u677vka5f2+Dnbg9znH67vSl4kcS+mrSWG09GvcPJPDnxSSmEM/XYIPfB1pAhphB9oerqa76667XnjhhSeffBLSHWxBlED+m5deeukBns8DIhxrBH7hOCB+TqFQCLSOHxV+26Ro+Sp2kFc2GTWPck9sCv26nNesJ+PLZZdedgkPhb2Bp9C460ytFgKIx3l7+OEvq9JTvAn5eRA34CV33303S6N+Jp/6gw8+iC1YBC8h2gBngDbwLnBrRfoM0pRg5ApeV4p0HsQltPCdVez47PN97R3LLt+GLzqtts5r7Ljp3/QH9wVDh1KZJY+/r1E53KhZUJsW9ZZVd2jRlxi3BkcNznmnb8HpW3T4Z82eWZt/xulf8IXwdM0ZuXTX2KVDk5ePTRxsad0Ix9c94UWjfUFn2RsI748nVp2uPTbnot60YLDu98fHVZZhqW7WYFtx+VecgXVf4tiu8YsHFi7qnb98Yv3E1N593dMTgZ5Jb+ee7MDRXWMHO3cf7hk4tnv08pmFmXRLssGQEVr7rbE+q79bou6TqbtkmmGja9bi2ePyzWoNw1JFu6Buwmw/lGpZd/kXDOY1f+B4X/+R3t71ZHI5HOpVyIZUygW3Z2+2dSORPhiP7w0EVuz2ZbNzyerb44usewKLVuc+X3BRq9vMJO664tLH77vn6cce+8PvfgfseP3Vl975y6n33nvn5d8+e83M+qIptKpxzWltq97QoXTzxe3dh5Mtxzt6T4zNnJhe/MNjT3z6ZU2Wb9v/ZPsSO66mumVM5flV7MASgVUFkpiUuNPT0ySAaUvqCjIlr/K2wl1K928yn3qMgyfEYvG5554rlUohsNd5KB/dmIEJsKValaAKCsTr5t7xjA+62UnRxwiVesBlYJxwAbs8Hs+OHTt+/OMfb9+2ncprEyJgS/oMHLavrw8kwUw2XLeB4+ApEQwOhS2lG6AE8OSJ38WreRPBUN4BdKampvBmiWnIhQ5z6L1P8QJYuWk4NQZJNOCdAjhIUUTLPm75zgZ2nDo1NjIOUc1iRsQyhUxVXytCv7aqFsQgqZepZVqQBwiADB/lxeWCStapLKuqqaqtLK0S1TaCM4AjKpmmvkaE8cb6xqqy6roqYUVJdWlhWUVJBZ7iaDKxXC5RSkQsMgUEw0J25WqlTAUWqSirFFQJgBeAD5yopLCkuLCkpKhUUFWjUWlwQOYUUiusrC4srCbfju8VCH9Q1vgvKnOVxaozaBlGaJU6g8YICDBojUqpCiI/GoyDDKwGq9lgCXiCHocH8OFz+aOBaDgQbkplI4Gwx+nx4x938nDZ3W67y2F28EAeM7bk2IEjWAxWvdqgkqkxqFVprSYrCCPkDQfcATfLvO4HW8TDCY/DG2IVXlLZVFMsFLOZHQFvyOvC8b1DvYN333X3X/96Vnw78IMkBRq+W/Nz7CtFWjgS9pD0FHaF/ua+L02hkPd7eGA3MT7ZSje4pydmkpEFT+lmAnL9xRdffOaZZyD4IZIpFA2gQDaX48ePgw/INxPjOCy5d0QiEfxm8DNe4LG162dq4e7ndZsoGOwwjx+jgHWcNxcehqekVrmGZw697bbbQBWUNJ0iWX7JG5laQBLYezevUw/sAElcz/1Db7/t9rt5HRmqG3c/ryGXQ5Prz7RrTl5z1ZUs7xk1vClCHzQ8pTmk7cD2rGLH518sNXcuh9N7wtlJtX28Ubthd236fUeSiYOp1NG2thV/eKhRNSGU7jHY9nnDK4HUpDMy4QishGNr/tCy1TVYJxuV6aeMtkWnd0JpmFWaN4LpA80dV46MXNLZccgf3vSENwLxJYdvXyK1v611I5lYcnhW7F7I8j2+0Jo3uOjwTFtc69HshN61YAsuB9Iroea9kdY9geS6L46Rca2vX+YcUTmmtdZ5o30ZgGJ3740mBmzOhFA8ZHbP+JM9amu3RDNssPebPWOu+IIzvOJwr/sCMxbbiFY/qNXtS6cXPZ5xrW7aaF6PRDfSqc2WpuN93XMh97BWNaLRTjk8G6n0xbu6DzdnjrW3rkYTs87gij+xN5TeDCaORVIrWv2hbOaeq6/45QP3PfWLX/zhmWdfeuH5N1576a2/nHr/w3ffeP5Pt2xetOpKLymdQzWK/nrFrMN/sLX7SHPnwWTLgXTrhV27n73/oW+NLN+ElksXRo1+d/QzxDgWh+bmZiwsZHrAltQJU7yNc8sFKQAg5od4VjFaDMmMApFcU1Pzb//2bwaDgY6AOcyG0t8P4d3N7cKggWw2m0wm07yhAw4ADYAJMM7TEbF0AGiYCRDBFi/0er15eXk/+tGPCDtwdqpWgQV2hOcXId0zBid4ojOiEJyXyICWYkyjSEBcEjm04vJwnaTMwJzcQdgbZ2+d0RIaHYRAhGhshmemxggpOdjiv7m5vsYwi4w1eO9nBztef/2NocFhnVrHwlZ5DCq4AZBRXlJRUljWKBRzL1FZQ51IUCEAZJQBO8oF1eWCovxiRgYFpdVl1ZgmqZeKakXYu3N7fllRWXWFoK66HhTCLDKC+prKGoYdjXJxvUQikorqRTw7WX1FaaVI2ChiIbW1dTX1gIxaAUvsgYekQapWaEx6s9VsUzCXUnmDsKG6prRI8OM8wffyar6XX/f9EtE/S7QlOr1CKVNqVSxwBq9SyTUapU4mVhh1LLepxWABJfDU71qNQi0RibF1WB1uHg0b8PjBHPFIIhaGUIxHQzGwQsAbYD4fTq/T6mLmEm/IwY01NrMdQOO2e1lGEFZkzg8o8bl84I9MgjlwZBLZGKugy2reRgJRoIZRa3KxFOxejUIbDcauPXndWcrbAUFLEh2ifXGBqdpI68jdiZiV5CJeHgmDqyssWowGDx06xDBlfn5p8cu8wps8bxhQAN9RAAf9GDB+7z33Ajt+//vfP/LIIxDYlJGGMtVAQhN53HgDc+eEqMbL8Sr8OPFzxU0ADkLXxryY/tOCw4wshC9olCyV/ML28Wg37L3qqqvI0kHuHXfx2ig5C0suXSkFswCMgBToYw65azC3jFtZChCMAD4wgRKuY0uqDlrRcAqc6AQvXElebGRCIr8T8m/FHEzGXsw/u9jxHwtNXcvR5nlPvK9B3SeQzJssG8Hgms83Z7fvjUSX3f4xpX7RZDsYjh2MpVb98TlHcM4dWPQHZq2OCammr1I0JJQuaI0HPL55vWnR4ho3OPsU+jV/8EAoss/p3ePyb4TjK97AnnhyT2vrRmvbSjg9ZfFNWzwr3tCeYBQAsRrLrKY7B7SuCb17NZDYAAa5gwtq7YJcMSOWzmvNyxbfrNwwJmiYFisWDfZFjX7Z4pp2BpZDkYMt7ftaegZN3gG1dTGUnk92jXuapqzRGbN3yReedrhWo5HlSOj47m5QxajeMmGwTpttK/7gZjp92XDfRQPde+KRMZ1h0mxbi8cu2t19rKP5ov5d+7t75kPxxWBqwQvAih1PZJZ0xj3J1F0nr37ikYd/9cgjv//Nb/4M7Hj1z2//9fSHH7//zmuv33XxyVlbbEZiXlTZptXmGbNr3RfZ9ISOxtOHm1suGRz83cPf+nZ8I9r/D3agQY7G43HIdchjCGmsMBDezIpxBjUgknMyHk9JSNMEiORdu3aVl5f/+Mc/tlqtOTUD5U3GNuddgbWL6jqEQqFAIBAOh3FSIAh2xVlLYEIikUilUmARSr7s8/koGfm2bduwC1eFg+MicWrySyU+oEaXNMIdO8hoMsvzLRF2jHCKIlU0KXKWecaBvdxjY2lpaZmXzSJbyQpvbFXf2IvlfZnX3iKwwMHHuXkdgxvMpZRFFGMQZ8FJcTqsvWcBO3CK/r4BqVhWXlpRVSEQVNbgAUogGwq5bjTUNqCDp7VVdeVcdVFdXl1RUoE++KO2shZgUVclrK2oBYJUllaCS+prGoSMNuowuaSwpLy4XMjm1OEI5EYK4ACjlBaVsdNV1dJ5y0vKK3kaMWmjFKxg0Bp0ahbNi4fZYNFrDFJZQ0X9loLaH2yv+kc8iuv/V6OmWKWWcCWHU6tiWU2x1an0MrFMLpazLKus8q2Rl4yRKiSyxvoGcUOjXqMzGYxWs9Vpc3gcLpfd5XF6ACLhQMTvCQA7mCaDJUH3h/0sFTq3pNiDPqa3CPsjAAtMw15gDdgCIzyAtinFEn5EeQ2XZDQUD3pDNhYC4+Lp5O14yWWXXgYh9fd/g/9T+zrYcdNNN21wTyIycJJp88CZOHVsyZ2KDH5c/cj8jIgAiAawF7KfXK728epxmElBZTgmRP6f/vSnZ595Fthx5513QgCTfgL8QboBQAM5YZzkqUvxMyBrJb7fOBrhBV3SGfPNYRyARDupPYiZVrh3N1ltcswBSgA94Bruu/c+cu/IVWYh9w4KSyF7yoMPPnj77bdff92XL0SfmIPiYIEg2N7Ao11ynHEpryWNy8DI5ZdfTghyCa83TYE2aOic4CE8p0+fPovY8cVMMLbg8M1bXZNi5bRYueL1LgUCQ2rNbrFkQq0dEMl66yV9cuWYxTrrcq0AOKyuZZdnweOZMlpGRIpxkXJKqpyXK9eNpr0+7954oldlCOYV7apvXLK7lly+/Zmmfcnkste7Eo6sZJr3de5ajDaPO6KjBve02bni9h3KtlzUP7bZPTITaJq2hTYj2T3u0IxENVRWMVlXN9EonjdZ1x3eOZlyqLyyp7i8t0o0WN0w0qAZUzqXneE9sZbNlqEpdxKvXYu2zkfbx5yZMV1gRGmdMDuH9KY5r2cjHj3W0T7r8PQ0Kmds3jmnZz0YXXA4l0PeC3s7LurpWvQF5v3Bg12dlw0PHG9v3d/StLej43BP70W7h/cmWtaC0SPJ1KLRsprK/OyG6598/JGnH3/suad//fKfXjj1+ivvfPDOh5/97cP3/vr4HffO+1NLWscRf+J4qvlQInMgmjzoj1yUzl7c0XHl6PALj/7iTOH7b9v/ZAN23HjjjX/HHPjNYhBLDcQ81YGjJIRgBcpD2NPTQ9YQCHuyZZAOA/KebCgkxTGhqKjonHPOAUwQGRBtYLyZN5AEB4s40AHYgWngCT9vVMYyzFswGMQItrEoU+tistfrpRzk27dvxy5KHIDDkg9HF3cQ6eMNa+NA/wBhB1ZIQEmOP8iPdeSMQ14OSnLaaLqTZMs3Dwncz730vurIklv5mZMHX8bXebjAPh40S1BCCiE03CKehXRhr732Wk/3rlpBbTHgAChQyhwsqsqrgAU1lbUABVFto7QBIlyhkKgozSh2CZgyQyjAhOr6hhrm4SFgzMFoQyqSNtaLsaukqLQovwiHLcwvrOEZwBi74HjVwqryap5GrAwTykrY6YQ1LDt7dUU1HmAOuUShkCmpzIpMLFXJVBaDBWxRW1tZIvi3gpofbCv/Dh5Fdf8k0ZYolGJwBsumarAadWan1R1wBwEKFoPVrLd6Hcw51GFxGLWADyWOhodSpjTpTaANh9WBXSad0WpieUujoWjAG3TzQFlAg9fhjQQiwA7GIh7mtAHIiAajsXAMe31Ov0VvdVpcmIBXAVnikYTX5UtEk6x6bRh0nA54Alz/kfA4fPFw/Korrnrrrbf+/m/wf2pfBzvwyyT/DALh3PeMsIP8Nja4xSTnVUSGj73ckrLGaw4RGezlNeuJr2e5RzcA+eabb2Y5xXnSMIh/srCSSGam12uuwWuxRtzC05lDYAMdQPT4SeOqjvECcuQ9StiBc4FvwCIk4MnDg34hZNMBuOBoN59p5LcBdAA0PPDAA7iGnHsHYQfG77vvvocffpiwA5NvuJ55adzKq7jdxRvFymLklp/ech2vJkMkgbdAb/wwL39A6ZOP80ZodZyX0iYHWFztqVOnzh52fPb5oM40KlOOy9XDNaKRWtF6JLQUCXY01mcrStsrKntr6hl5yBQdEkmPVDJrNs+ZzON6w5jROKzSDDfIxhtks2r9rNa0YLUfzmQOZdMTWn1HVc2AVDHncC/4Q+vpzIH29r2p5FokNu8Pr6ea1xKtS9GmWXtgRmOa0xv2RmIH23v3NO1eCLZMGH2LNu+83jJS19BbWDhaUzuh0IzpbTNm16xGN9AoaqsVpouqe8vrR+rkQ3WKPqFqwhBcCXROWmLjes+MJTCqd47q7bNay5xSO2cwL7m8I2r9tMm+Fo5Nmp2DMsOy3bcvHNrw+2cNhnG9Zm8iuplN7c1kVrOZQ7t6DnV1HWxt39fafqB714mR8cv6hi5s6zqaaTqWzuz1+g92dj54682//tXjzz791O+f/c2/v/j8qTdf++vf/vq3zz/65NOPX3ruj8f7x/e5wgeD8cPx9OFY6ngTq6x7LNt2vLn1sp7u53/+8LfY8U1ov/3tb2/iacFIGUnYcSOPaceaBiCAUE+n0+ADqsCAbc7YQW4W5IbZyRvxB3k/YDXLZrM7duzYunUrQGGQW2EwAa8i5viqqiPKW5A3kIfH43HhltVudzqdIAxcg5M39LHFXofDUVBQcO65527btg3TcJ04ThNvZJqh68SV4HTY0tkJfcirFJCEi/ySS3jLGYlYPS2eezrnpbHIIxD3b+5f51EEG1y9Tdpi8s/L6a33n6nKSyNYezGNwAX3UV/jzvi/2l5/7fW+3f0NQpGgqqa6sjo/r6CyrFJYUw9QENWJmAcGKzCrkYrkjXXi2so6oaC+sqSyvLgCUAKMwLSaihpBBV4gktRLWTlZKfM2LdxZXFpUWlJYgkdRfnFlWVVdjRBHJuDI276zvKRCUCkoKymrramrrhSgX8e8N+oppIV7kjaIRawuLphDJVPrWXk5vaCqorD8x3mV3wNzbK/8xyLhPzVqitUaGZWMsZrsWqXOZXX7XAGH2alXG3Uqg9sOfAj73UGHxem0Oq1GG7O8GK16jd6gNbI4XrFcr9aDOUAhiWgiGUtFg7F0PJOKpoOeUJA7jYZ8IZAEBoO+UCQYZaEuTn8smMBhvwypdfnwSEZTTpuL8UcoQbhjNzvAK5kEvrjZztaua09ed5awA79PfCmJi3Pfzr08IQe+i6RwQyP3KzK+kIJuD4//BhyABvB1pHFyOKIOmSEBAVgLnnrqKQj422+7HUsAzoivLMWYQJxDeEOQ38Zr0KOPLzq9EIelXdhSGBt970miX8qjZ4/zOkbkA4tfAn4bmIyFhgXE3nZ7TtXxM97uuecegAW5d5CRhblr3H0PFW0BiPz85z/HZFzGdWeKvOBVd/JGsbW48pNXn8QpCDLQSOVziNdu+OpNA6EYdzhhHidHuevrG2+8cTaxo0Mi3VVT2yeS9ElV/SrtjN+zEA326dXZOkG6qnJAIh+WKMd1pkGtflirW3a7Vr3uMYO+SyLpapCMKQwjEvWsxbUSSuxLtxzMNK/5fJNK9W6haFCumjBZp2zOWa9vT1N2ORKZtjqG5Jo5m2fZE17yR6dNjgmJakyqnLG7l6PZlXDLrCM6ZwlOqkzjKt3uWmF/taCvunZ3vWzIYB+zeSZt9tlwoNdhj9dKuoWK/jpZZ3ldR6WoT2wa0/iH1e5RjXNEZRmUa0c0+hGxdFIinlRr5qyOUYVhWGFYCMTXEs2L/vhqKHqoKXMwndiMhmfttqWAbzHo35NJr6bS+1vb9ze1HWhu35NuPrpr94nhkSNtHcda2o6km46mMvtC4YtGhx+9967nnn36D88988Lzv//zyy++8fbr7338/kdffPzpf3z63jvv/OzCyy/Mdm5GkuvgLbv7gDdyLNmyGU4fybSe6Ot//pFHvsWOb0KjmiyEHeRMSqoOLDgQ1aR7IBsHtq2trRQxN9DPAk+6urowh6Q4mINcMkkjQu7tgUDgvPPOA3ngOOQfChoIhUJEEuhgC7ywWCzYYjKYA4PYog+2wKtcvAE13G432V/wEsKOwsLCn/zkJxdccIFGowF5YAQTyFhDFbmBIOQUglPjYkAYuAByTSVPVYpzwV5S1YyMjGDRxlo6xHMtksIGS+UUT9LK7iFXWAwB3XCu82BgggxW9o43UpBQmDEpQmj8EEcQrIdfQ0T9VxuwY/eu3TVM08AiWQryCqsrqhuFjaJ6UaNQJBQImVajoqa0sBTEUCfg0SsCYXlxOcijulxQUlhWUVpZygJVBOJ6iYjl82gEmuTvKCwtKispKC4uKC7cWcTJo7K8lGk4qsurcZbKUiBI1Y5tO6oqqnDe6rIqHFbcIAZ21FTVAEfKS8oxLpPI1Qq1DtTBsqYaJY2ikqpzgR07qr6bV/29orp/Eoi31grLcbUsdEVr0in1DosLqIHJLLOqSA4SAjd4nT5gh8/pc9k8XoefJoCQQFQKicqstzCPUY0B8MEcQqOpRDjBgMPmYVoNnk897AsHuOepx0nKj1AymmnNticjqUwim001p3gN22goFgd1+MJAFpvJ7rS6Q95QU7Kpq627p2PXyauueevN//Lf9Otgx/W8Ai2+f/jykfcytmTP28ND0gkg8K1d47YV0i6sclsLmQb38wQ7hB05hyO0ce6uBUR48sknf/nLX0Lq384Lw5Kd9S6eyAuDGMGicAcvQ4/FAvKbABzSnVk0rmEWDebMwWvekv6DLCyXnUkyRorEnKoDByQlB8sPegcLjr2DZ+YgH45HeHv44YcBGQAOXAauAQjyxBNPYHv33Xfz+BR2kwTUIIUHpfS47lqWXAT0cMkll+AiD50pgLeH5w8m7CDNx0GeM41+qzmPE1zb2cSOLz7/rFkm662rGxJJRvTGEYdryGaZ9rmnfd5xr7fPahs12/saleN667jZMePw7AlF9sfC+xLRUYt1yulb9CbWwtmjnX2HO3rXo5lZq2dEru1tkLfXNo5ozdM214zVMe/2LIRCE2bboFjRV9PYW924u1YC5pizugZFsgmdbdzqm7D5J0zeOXNgyeKfNzhGVKbW6oYhubq3QdohlA/qbRM2x5TDvpyILqZSu22eTpmxq1HXLdZ11qs6a+TddapxrWvW5ps024cNhnG7bbdM3i6sj1VVZxsku2SaYaVx3hsFdswGYguR+Eo6vZHJHO/q3JdOrUbDa/HYRjK1FIytBOKb0fSheHp/LHFhZ8eJwYGjnZ2bmezBbMv+VHY1Eb98Y+XXT/zihT/94fkXfv/iS8+/9MqLr5x++d2/vfvRFx99+h+ffPK3D5+4856jPcN7Es0zDteURregMq6b3etO/75w7Ehr++8e+DY5+jei/fY3vBTc/9fCgobfIFAD8j4SiZBaIpVKoUPxIAwvOpi5hDwtMIhd5A3azlsXjzoxGo3kfmEwGKLRKFAgZy4hewpwAbvUajVmAj7wFFvqm81mq9VqMpmMvNlsNjdveDnhSElJyZYtW3BwuVxOr9Lr9ZgG/sBebAE3RCF4CzlVDSVxxlvAU3JTJZ0NpUklj1c08hIl8qBkHjm/UVr8yT9vhadk3OCZSzZ4NDHzJOW7jvBKLqTtpptPrM9nIW/Hq6++2t7WDrFdXSmor6kHE0gbZUqZkqUwZ4TB/DOAFMUFJVUV1cKa+qpyQUVZZUkRM50ALDiL1AERwBN11XUlhaUVJZV4gDMqSioqSsuLC4qK85nmo5LFqtSAUerZQaoxUlxYvO2CbQV5+UJBHSiEj7DDCioFgioBA5SKap6rVMXcO3RmrVKvVqlqxQX51f+UJ/jezprvF9T+sKLhvMrqoprqmprKWqmIkQeww2qw2c0OrVKnYVTBMqs6Qao2D1jBY/d6nX5MYDVpGxU2kwMzPQ4vHrw+nD3oC0YCEZaN1MMsNX53MICHhz1cLDmYy2Zm1VsigVjIF8kkmtqbOptSLel4lo+E4mFWljYRSSQiKRBJPJzsau3GCPAFgycuvfzN0/9lDdbXwQ4I6eUzkSw5+CAfZrKnYEsKDBKfX375ltkXdJ2H12Icc+Z4zO0Cd/km7QgpITEHwv7RRx8l8X/zzV/m7oTIxyAI4B5eypUyjmPXVVddRT6nWDUwjdw2L+O1myn4lvQfZOOALCfLztrqGuQ9ZQS59SuVUKiBGwg77ucZSwEc2FKtWub2cd99hB2P8ap1WKRozcrhy095bTlcyeUnLifTyXHuy5L7Ha7z1MKk+djPG90lYIvPB3spIOj1118/e9jxxeedZnOvSDwqko5K1aM607jdOev2zHv8a4nMAh6x1KDWMmpyjRhdM47QtM0363AuhQJTXv9yLLvgS27EWtaj6T2RxKTeOizX9wjlHQ3qbK183BFYDMRn7J45p2fW4xvVmXcLpYP1iv4aeUtRbU+jesxkHVJpp6y+MVugT2HsFWnGlJZprW3Z5ZuyeHplugGlvl+h6ZVru0Ty3gZxv1Q2adCPabR9al2XTLdbbes3eDI1inRxbaagurNGhlPMez2zPveEwzak1/doNVGpJCaXtkqlkwbLgt07a3ONm62TNsc4fxzp6joxNLQ3lTrY1Hy4uXUz1XQ403o803YgEj+USF3c3nastfloe9vRnp4jPX172zoPj43cfcuNL7z0h5de/fcX//ynF1958U+vvPDi6386/ddTH376wadffPT5Zx+//Ps/Xjy7ure990B757LXPypWzyoNLBma1gAIe/L2Oz/56KO//xt82856I+z4qoUFv9wrr7wSCxFEODgAtAGpvKuHGUdI20GEkU6ns9kshDo5e9IWYIFBoAgeGNFqtYWFhQUFBfX19ejbrDaQhJ03ggOcQqfTaTQa9ZkmkUhEIhG2ctYU+K9UKjEB0wAWOAjYwsxbcXHxeeedt3XrVkzGXuzCNEAM+pgDXsFZPB5PzkeVFDa4YDIY4SlzXE19GTpLqMTe6a5dZCQiFQg6GBzhrrJ9fX24M/yqhptEABq5dyxzX1RShORWs5UziSVx43cWsOO1117raO9obGARqoIKQWlhCcsiyuqnsKKyvFSKiArWgzkkIik4ADOrylm0S01VDZ4CWaoqq8AQTJ9RUg7IKCsuB74AJkAYZWdMLXjgVZgAWCkrrqhiL6+trqgCZ5QUl5aXllcw/YcARIKTShuloBnuYlLRUCdSyFRqhUbJtspaUUF+1Q/zqr+bX/v9/JofCMQX1NaVNtRR1IxEKVVplFojizdxgydsRptOxRKFGbUmm9EO4LCbHMAFp81lNdoAKNFADIQBzgj7IpSBw+fyOSwOVgbP7jbrmXcI01iwJOhhvDDsj4JC0I+FEsyHNJZNRjMhf9Tn8vPjsE7YH07FkulYOhlJpmLpVCzVnG5Ox9Nd7d033nC2SsFBtK/zOPU1noRuD88DNsmTdJE+g76OxCWrPF0dKTNWuF80GVwWeGDVMs+5i5dPTzGUBnZ0d3dPTEzccsstkOhgDhL/BAQPPfQQTwH6xMO8YQJGIOOxalzEk53jIyDTxtVXX808M7lHCIWKoGECaTsOUl2Yzf3gEtzl3MByojPCwKHIiIN2G4+hJQ+PXN4wYAd5jD5wP6MiygyGCaSSPckzi+DDwQtx2bR44ZdGQTTHefjMXp6/hBo+jWWeP54+MbpRoM4G99I9ygs2njXs+I/Pv5hKpod05n6pZleDckhtmXH65l3+Oatn0RVeiWX3tfUsxLPz0cyIPTBk8OySmSbs7sVYYtTtX4k3LQeSM0bXsFQ5q9ZMKdWTGtNuqa5bYetSOWcjzYvRpnG7Z0Bn2q3Q9jTIumvEnVWiboGsuVTaq3aMu/3jVvu0wzdh9eFV/Y0MO2as7tVoYsrh75MbdjXKB1SaPqWmW6LYJdf0afVDGvVukbBPp9ylUI0ZnNOueEujvqmisaW8oV+imzBZ5/2u1Yhv3mFbsNsWvO6V5vRSS3olGtoMBje8vnW/f28svBEJL7m9sy7P3kzTJbv79iVSG+HYvkj8wraOEz29V/UOXNzec6y183Ams+ZjvqjH+/uPj0+eWFn7+W23vPTn50+/9+Ybf3njlTdefvk0Hi+98vbLp98/9f4nf/308799/vnHf3nzrSv3H5uIZfe0ta9nMn0yTU+daEQim5QrJ7TGx2++9Vvs+Ca0rxpZiDzQsEBBYEOQgwzAGX27+yB9KfwETynGpLm5GYRBUa80CCkeiUR4EEoG0h1HAEY0NDQIBIK6ujrAAQ4IdMiRAfrogCrAFlKpVCaTKRQKdPASXpuN8YdYLMYIwYdKpcIBMQ19dIAd5557LmEHnmIvjo+DG3nFeTxFhygHVxIMBqPRaIwndA7w5vP5vF4vLpj4CSzF9B/tzCJD3qmkxSGNDoAj5/wxxdN4LHOvvlWenHpsbIxuIGk9J7lAihC61VzkEcW4AzwL2PHqK6/g7VSUgwOqBJVMk8Hzc9Q3CsW11XUAkdqqOmGtqL6W0QaVTWHl7CurgR2Yj8mV5ZXVVdWV5VWVZVVlJYwe8GARKziGgPlzlBeXlxaWCjim1LH42DoBXsHARQDEKSspKyosLC0qqcbRqlkQLzgDr+XRvFKe3gPEoZVJ5MAOiaixomb7zqof7qj6bn7N9/MF368Wn19dU4xjCgUNtVUsCyogA6hh0BhYQnST3aQzW/GHNbIas8yrQ88yoAM7mHNoIJaJZ2PheIr5foYsXEfisnkwGa8FlFiM9mgwHmTJOVhgC6Zlk83pWDbsiwI7mlKtYA48ZY94ltQbIUYnrIxtLBSPhxPJaIo9jWdaMq3tzR3XXH3N22+drQBa5lbE9RZkwCOeJUvHXh4uS8mDl3jlQwolJy4h/ljmbYHnk9nLTS0YxFd5apKFwqNdddVVwAsyspCnBRAEqPEkbxD52PvrX/8a/fvvvx/XcyGvaP+/eSPsoLCRE7zwCuk5QAAUMEIEgAYKOclTZYAVyLxyG6/7ejPPFkpOHpSKg7QdOBeZYB568KHHH3+cLEEYxPyrzjTydSXLCy6JlBy0Jdsn6YfQch26OSDNEH2em2dCi0HuZxM7dttcvXJtr0I3aHAOW3zj9sCsK7rqSy5aAyuOwMFs22q2eSoaH/dH+rT29nr1rD96oKN7JhBei6TXfIkJuWGyUb6oUK8ZLQsG24TOPhfILKW6V5t611MdC6HEkMmWraxprW4YkBl6ROpBpa1HaJvxNC/Em2a8/hmra0pnm9Y55ozuKaNr3OJcSaRximGlcUCinLTZZ9yeSad7PpmZjEYnfZ45n3spEZ6xOyZ01lVfYtoaHDf4RnWuRU9o0RdYCnhXvM5Fo2HVYlr3uY50th7saD2STR+NRQ/G4it+34LbuScU3BeOrIVj6/H0eiSx4PKOqjVDjY1LZvM+n+94OnOiu/doc/uBZHIjHMTpDvX1Xr1/8xf3/OzUq//+9runT7/75ql3Tp168/XT77xx+t033vrg9F8+fufDzxh2fPbZxx9+8P4d194wEklOR6PrTdlxi21QqRxsbJyUSOYNpqduu+PTb7HjG9ByLqU5bQcWk2QySbqHUCiEG6F+3ihCBGKY5DRZKNp5Li9qTTzZBrapVCocDrvdbpPJpNUysCBQMPEG1KCDE0ZgC6oAYTQ2QihJwRC1tbV4CvhARygUNpxpYBeaQBqRoqIi0nbgCIAYOizpOdAHf5AXas4FFZxBAbrkUILLA3aAQsgQg2tOJr40EhGFkFGJ+kAuUnWQnwdWLaz5i7yyDAUVky8qrWMkCOiGkxY6WvYvOitZSrFmtrW2sfSgwAiAR2kls3FUCAScJypKymvOZPEqKy6tKq8Ec9SBFYrLCncWlRSxpF6lxaVAB0wAeQAdqnCcihoQADCFKTwEQh7DIhI3iEVCnvWrpr6upq5RKJKL5ZIGMQ5eUlxSmF9QmF9UXFSKIwB3QDk1VbXieolSptYqdWaDxQBE1BrAmxWCHTsrf7i94h93cjtLnXy7VCEEDElFCjHLHdLImcNk1Jr0aj2vL2NixWl1ZvTJnkJ6Di/+wk5/0Me8TSOBWNgX/tL/wxVgadEdXoBI0BdpSbcFPWG/K5hNNLdm29ubu3o6+loy7eCMbLKlOd0KcMHeoDfkdwViwXjAE/S7A8AO0EYkGKVo22wy297S0drUdvWVV38Nf52vgx0Qq/t4VDfdlEM60g066TZIZGJ8D+UHm2B1AShNDb6FZE/BdzGnAtnD/UyJTmZnZklTByagKJJcVi7I+4ceeuhXv/oVhP1jjz0G5njuued+85vfgAZws4KVgrJ8UoPsB3NgKcEgUIC8OiiSJZeWFPxx1Znysz/96U9zmpVbeMvhDnmVkm/HfffdR3YfXMnjvNgsGq4TF0DKFTLl4Lw4DrAD5zrGg3XJufUgr469yrMj4+eKt7zK3WzxSXK3LPah5WbSyNnFjs9b5Mr+RtmwRDWis/bpbLs0til3Yt6TGtfYF7TmdY9vTzo1GwsvppJL4fiAwrQWz2ykM4te/7IzOKlx9DdoJpWmUbF63uic0duHFIalaHq1tWcmlJ1zRBbd4eVQpF+pHTfYZ2zBRU9iJdw0og5OmRNz7viE0TUi1w/WKSZl5imtc8LkHjbY5gKR1Xh6weSYlGkX/aF9TS2b2dbFWHrE6hq1gmlCUzbvhNo0JFWNqU2jGsuAwjKgskxa3SuhOOBjzmxfNpoWzIa1oP9oZ/fRjp5jzZ0H45k9kcyUxb1bJO8Xyca1hj65Fu8F7DJtc4yaDAON9f2lxWOC6nm18qJ08ngqsT8RPdbdtpZJHBkb/OWDd7/51mtvv4P18+U3Tr9++q1Tb735xttvn/rL+2+//8m7H3z214+++OCTLz7+6NOP3nvvL7+6/95j42ML0fhKODprt8+ZLQsmy6LesM8XeOauu7/17fgmtDPYcS1hB5YICFGIbQh4vU4PoUtRHj09PWSJaOPJNpqbm0EYEMxAEFJ7UMAquZRiEOIckh4QoOCNdBXksaHVakmrQbs0Gg1GMEHGG5ACeAHIAIXkaANbcAYopIErQjANLy8tLT3//POBHXhqMVtAGzgIDvhVCwvAAlv0cV7yOSVtB4XJYFc0GiUn1hRvhB3kNkvROqTzGOKNonAHBwfJwwMUgkHySMXqjUG6HQVn0F0lGdyJP9DHqn4WAmhxit5dvQJe97W2upapOmrqmY9nZXWNoKa0qKSspLSosHD7tm0lxcXlJWVcLVEF/igvBWdUFBcVgxi2XrB1y7nn7tyxo6igCDjCgmzLmH0E0+qq6xRiBY4JoCkvqSguKK6pFFSXVwnKq8X1Yuwt2FmwY/uOLeedt33bjrLSsp15+ZhWy+wvzODSwFKgKlQylVFjUMmUSrmitqGksOqft5d/J6/quzsF329Q7dTopHoWHKtVSlUKiVIpVRs0ZrPeqpareRSMSqvS20x2LS+iazFazKzELuMPEInFYHFYXT63L+AJxMMJ8AcLfI2mwr6Iy+YOeMPJaDoeSmaTze3NnZ0t3a3ZjqZUK8OOWDYWTHS0dCUiKafNnYiwoNnWbFtTsplhRzyTjKVjPHUHOnjEIwk8LrvksrPk2wHRmxOTaOQcSt8tfOdws86yYqytky/S6MiXuWKWuG2FxO0Sj1vJYQe+ppM84R2mrXN3JEAMZPmDvJEPByCAsIM8KoAdzz77LEW7XMUTe1x5pmIcqUyZ5uHKLyutUBgLJoBFsLhg70mej+t6noycTCpot/J2My+eQl4d1HBeMus8wKurgEhwVYQdFOULcLn4Kw3kQV5pdGEsipcrMAg7lrlhZZZnC845uxzi1fKO8Fjfwzxb/NnHji+AHWrtgFgxKlGDDEZt3nGnb8YZHFZZ+yXaeYd3PZlea25eTibn/MEph79fbZvzhiZtjj6lalRrHpAZexq0M47QLrlxzBGY9sYGTZ6lWHY6nBi2+qdswRG9Y8YdmLK55xz+aYt3LZCcdQanbJF+uX23SNNVJelt0AxIDEMN2mGpcVhlmTA5Zx3+BY9vQW+clsqXXe6NaHxvJDVlC0xb/DPO0EI4sxBITxscY2rDqMk2H4gOaYx9EtWAwjjjiMx5ElMW/7zVPWO2LflCm5n2zXT3aqB51hadtoen7MEhlaldINotVXeLtZ116iGZccLinPX7hzXK7qKdfSWFw8KaZat5xWZetllWfJ71luzJw5uvvvzCO++e/tMLz7/+2qvvvveX995/9+3Tp/787y+eOn3qg4/f//CzDz7+/KNPv/j0g48/PH3q9Udvu/3E1PxaPL0eTix6ArNWluxkxenbF07+9mf3fosd34RGeTtI24H1Ab9KyGPSKDgdznbuQArs6Od5RSn6NKfeSPDWyr1KaQLLntHWFolE/H4/aTv0vJF6g7BDfcaBQywWE0YQcxCLNPKGcVJs0Jx6yM3aWoFAUFNTQ0SCvWVlZYQdmEOep6TkoLOgoWPjDX1ySgVtAEQoOoacT3GpFFaT5C3N06Tm6rOg4b1T/lMQBnXQFnhI7STPxY51e45HBZLCe5XnhMwFJ6LROPjjwrOSpfSN198Y6B8Q1gqZJqOuoa6mrqFehKcVZRD7gvy8fDDBzry8bVu3ggkAHEWFReCPsuJScAkm4CkeBfmYth0IUlRQuHPHzsryyqIC4EhRUX4RGAIoI6hgacSqua+GUFDHVCaCOuZNwsw6FSVFJYCP/J35jGMKS3gOMUFlWWVZcRkVp5U0iFVyFTr1NcIqQWF++f/aXvadHZXfzav6Xr1yp1wpUspVOrVBrzboVHqVTE1pPFRSNYiHeXsodFaj1cSyp2u1Kq1OrTNqTU6ri4fasrTlABG1ArBi8IE0nP6QLwKM8Dh94UAU2BFjIJLOJJramjrAH6loJhXLZJLNiQizuSRjmZA/Eg3FKGlYR3NHa6Ylm8hiTjQU9zi9QJlYKMFTp8dPXHriawRFfx3sgEimCBF8jSg0A985SNMVXo+APCdWz6TrmOU1l3PjK9yreYnn6p/jLqVEx+SUirZ4psABjg/agHSHXAdb4KRkZAF2YPAJXuX1kUceueOOOxhgXHnlDbyUCd2yYBG57DJWY+XCM40UHlhWruNl2K7nyUBv5JXYKM0XYUdO50FeHffdd9+9vD3A853fc889t3FvU4AI0AcXgDUL13DjjTeR98bFPBcZOmRtIeIhu8lBXhpqk4ewc9cO1tZ5jp29PA8Pzcnx3CFeG+9sYgf+tJPx9JTVPSDRDGksYw7frD80a/fMmtzTZs9qNHmwu3e5qXkuGB4xWnvlhrZG7bDOPGYyDWg1vQrNmNE1rHMuBFKzwfRUKDPujfcZPeOueJ/B2ya1DFtjI+bojCMx7wrP232zZuei3TuqMY1rrG210kBBVXOdclhpm1Y7xqSGcZVlXGcfkOt76xUzJuuiwTinUM7pdGsu17rbt2z3Llk9a8HEUiy9r7l9yeGZMtnmgrGVTMuEzTFtso6qbavBlrVY17w/veCJLngiwzrHpDUw54iPa4O7Gix9SgfIY84RmrJ6VsPJ2UB61Bbukxon9LYlt3dSpe6vruyrrupvaOisFXRWVw43iAbEktGA74YTF7/22p9fefml3/z66bfeeuujT/72wQfvvfDc83f99K7f/PqZ9z/84JPPP/nsPz797IvPPvzbBy89/8Kdl5081jd5rL3veFvXsbaOY23tF7Z1XNLVfbyj+3f3fxvJ8o1oOezADxa/R4/HA6EOJtBoNOFwuKmpqZOnBcO2hzdyfUAjJcffha5gJJVKQZaHQiEybaAZefFYMW9yuRxboVBIGFFdXQ2SAFXgjDSIpxhHh5iDjCwYqaysJOzAZFwh9paXlxN24CnIBsCh1WrBHxTVYuThMB43y/8BAHI5GWqQwQVbCnWxWq3Y5eTpQChxCBrpSPC+Wlpa8B7beOQLeZgO8rToFNIyzZOcjvO0pEQhWNXJbo4t3VvmohTRwZJ+xRVXnD59+u//AP897T8XSayZXZ1d9cKGWkGtoFpQXlZRI6gV1gnrausqyitKwQAFhcVFeBSUlhRXVlSywWKGF8UFRYX5II6i0uKS8pKyslKmFCksKNy+bXtBPqCjmMw0ZazMWzF4BccHc7DI2NJyoAYmFOYXoA/CEAqYRQYQgznAEYphEdbUi+obxQ1iYU2dVCTRqnUSkQT96pqi/IovtR14cCNLLZhJwNOHKCVKDUtUqsNDrzFYDFZWjVZvsZnsQA3giF6t18g1Ro3RZrSBMPzuIJiDMQqDFZ3DwqJUHFaWVzQSiCYiqeZUWzKaCfoiYJGmZAs4Ix5OxkJJZpfxR/EAUkSCsTjXdmQYbaTScTBKNhFOMn9VHnzLvVBZ5o8rL7/yLBlZIJIhZSm3BHlmgDlWebpSohC6g9/kDU+JOS7kEaQrPIhjgddDImsLIQtGWCzu+AQ6pDjBEUAGEOo/f5iVQaHEoOTSQWlDnzgTv3rTTazEGgWPkFcHKATy/quZuE7wdvLqk8AOchq9kTeW1Py22+64/ctC9uThgUapSO89k+mc+uiQXoSSeQA7nn76aVwJDnIhT42Kk1JMLDrkTYKn5NSNTi5iBT9R+q2StoMUPPt5UDHpPAAfR3j69ldfffXsYcfnX8w3ty8nmiZcwWlvhGksPOF5b2wtmJmz+pcc3s1Eei2ZmvR654KhSVegW6obUenGDfpxh23IYp1w+GftwVlHcMYbW4g3TXljuxSmSVdy3pfdrfUOO1Kz3s5xXXRC55rRW5esjo0AmMbd16joEqvSEm2TRN8rVMwqjDM6+7jONqazN5UKk3kV/RL1nNU1rTEOCeunFIoFi2nF6RyRKefc/uVkZl9ry7hO21Er7NNa50OZBX9sPRRbdIf2JTsWgplRq2feHVgJpkaNnjGdc9Ea6BfpmsskXULVsNI8qbPPWz0H0i2HuwfXUp1jKsusxjSn0Q0LhX11Db2N0l65qqm2rqWmblJrnLW5ZlPpu2+68dWX//yH537//PN/fP/D9z7+7OO33nzryYd/fd2lN/7myWfe/+CDTz//9PMvPvn880/e/+u7v330l9esHF5P9u5P91zUvftYR+fR9tYD2eyF3d1XjI4+/9hj3+bt+CY0cinFuoGfLXABkr6urg4QANFLyS26u7p7e3tz9VMgiYEXkMSk6kgmkySeARxADYhzEtvo+3w+yHWKj8UxBbzl/DYAEFW8gTxqecMI+hCEpaWlmAm8AHOALQAcORbB5ZFdRqVSVVRUbNmy5YILLgB2aM80nU4H8sBJKTqGrC24KlxMLnaXMn+Q/QXYgS2FukR4I12Iz+uLx+LcPZY5slDMLcW59PGsrJSqhGJewB9kdiH+IJU2Glb43C0WdmEpPgtGFqyZzdlmQVU1iGFn3s68HXnlZeV4ACzyduzI37kTMFFSjFaUn7+TPXbm4QHyqCgtL+cuHUCKshIARXlxYdGOHdsLCwpKi0sBJSWFTIexcwcOmscBpTBv+w4cv7iouJiTDJgG5FFVXlVXXcsUIZxFcGoqRQtkEdYKGxsagUQioUglV8klcrlEJmwoL6j8l21M2/GPwI4a6dZGaRX+3pWlzEdVJpYrpUq5WKGWqS0Gi9vudlidbrvHZXVR8i67yc4CZY12l82NcbAIuMRpc9ktToy4bGwOz/NBOo90Ot7EXUfTAAgwisfB4m+9Dj/zPDXZbRZHiGVM9wV9ISoCl4gko8FYMprKJpqwZYYbfxSvDXiC6Xjm5FUnz5JLKeQu3bvv28u0GuReRN7LgAwSqAQi+3kNWOKPIzyOA19E2ku6DUwmGYwtJe0g7MAxcbSLL74Ykv6xxx4jd84HHngAMh53J5D3IA+ytmCQVBSQ/Vg+ANTkt0EGi4t4SnWIf/LquP46ZoK59dZbb+M+HICV227ltHE7SwFCwEH8cScv9nbPVxoxB5lgKJkHLuCpp57CFrvANMd5glGc9ABPkgMQwanx9le5A0fO1EJepTnPrFUe8kNkRgqPgzyGBUfDQc4ydqy0dB7o6ltu7piNZUasviGTdy6UnXBGBhXGWZV+QWdc8noW4pGlTHLC69+t1A8r1fNu90ZTZi2bXQgn512hGbt3ygUgaJrxRTrF6lGTa80bnXP6l5Ity/HOEb1/UGqYUOlXnK79ieSk09Ol1I26glO+5IDaPiRWzWhNgwrtgFI3rLV21inbqyQ9jboRg3tIoe8urxqoF04YtYtu765GVa/a2m/1TvpCQ1pNp7A+W1m/S2oc0TsWvaGNaJK5iCabl4LRtUBwwRMeMbiGZNolo7WvtqFb0DgoVo/LdZNK3YzOdCCWOtbWsz/WPKMwzMjVo2JxT01Vn1w1anHO+iJjNveEw7MRTR1p7bxkcu6Xd9//p+f++Nxvf3fq9Bvv/e2vH378wb+/8NI9Nz98x3X3vvTHl99///2PPvvbJ5//7W8fvvvq88/fdcX1R3rnFgMd6/HOjXTn4fbe/S1d+1t7ju0avHBo7A+P/vJb7PgmNKrJAtBvamqCUIdoByJAfuN2n0QsFqWhIVbihDJuQdAyFuE1XVtaWsklglw9YjzZKEQ1JRmDXLfZbDgUDptTbJB/BhlNwBNAB4zjjCAMDGJaSUlJUVFRNW+AEiBIWVkZ6TnqeAO1UEgtXn7eeecBO3BAjOh50g6j0UjmFTKsgD/I29TpdJIzKQgj5+FBTqakm8E4hbrgsinUBSOAKry1nKano6MDCELKHlLw0DgaPiUWE8DTOIFCyLYyxzN8EHNgtccqhwXt7/8A/92NAmiBHVUVlcCLc8/5CaiiID8fQLCTd0By1dUCdLZv25aPtnMnppUUgSxKSwqLiwuKgQgsD1hpWWlxSXFhYVFhEXAE2AHmKGXWE3YgbLdt3bb1/POBIBQ1gz8cXgJeqSyvYEoO/LlqhEw7UsaAA/yxY/sORjAAmvKKGkGNWCQRN4gb60W1dWUFFf+6tfQ7W8u/s4NpO7ZJlTVSiRh7xQ0SuVShkCqkYlbk1syyfZiMOpPNZOPBLMyl1GX1uO0+Vqre4rTxOBeb2RH0R3zuoM/l9zn5w+UPuFkq9GyyJRlJAztS8YzXwSrD+d0BXtU24nH4LHpbrtJbwBdEx+cOYJyn6GCZ0WMhzAyHfREwh9vmTkZS11597VkKoIXc3eQprchit8obwQd4YoqntyNVGwQqmRVI+YHv4hgvw0j6N/L5QAfAgS8rftL4eS/MM+wgZIEUx4qQw44HH3wQ/aeffpqEPS8K+0vade+994IbbrrpJrAFhD3pDCiqNudJSs4c5D16J29EGGRVydEGjZDlhTKD3ccb+rewdOwsgyEGKYIXAAQSwtOTJ08SZ+QCdLGQob+PV7nD2wFybW6yAnWk3aG3nzOC5rCDPlgAx/+AtuOzL+Yiyf3ZlvXmpnF/cNITGjF5FiIp9MfsjimjZd5sWfC4VpqSc8lot1Y9aDGPmCzzgdBme/v+js6NVNOSO7ho98xaXbN2b59MmykWdFfWDdUJB0UNkw7bdCA84fIP68yTVvtKOLwnnRkwW1sU6j6bZ9QKHNGPqw3TVvtuiXxIqZphBwkNqy3tYm1WqNot03bX1g1pFCMW/YzLNaK3jZj9/Tr3gM4+YbVNmkyDctWIUjem0c9YrHvCkfVY6nBH70a6ZcUfHjc5hpTGYZF0WacbEtZPaHULVsecwTCjx3zNujuwP5jY54vOaUyzWtOcxTJutUwHQpNO34onPG92rrh8S073ajB6+czqAzf87LF7H3vqF7/+87//+fSbb5w+9cYLzzx/9/X333ny3t8+9uzLL7365ltvvv3mqRd/98zPb77txPzmcqp/PbV7Pblr0pFai3evpXuO9k0dH5ja3zP8zMOPfvrxt9jxP9+AHZdccgmgQSKRkO4BlAB0oCQWvJLaIGXzpG0LLwwLuZvlSTsgqskegS2FhCQSCRYw4g94PV673Q6RD54gjMg5hxJSVPCGDulCaKScN+pjiz6wg5CFdB5ouEKVSoWn559/wfnnnw/+ALWAb0Ab5OQB5sCpgRpWq5U8PJw8zynZU0jn4eJ+HnTxlMMjxhOLYRB9QhAKZsFH0cEr0QC2WltbyXkWEDY8PEzWFnIBQYdcQNAnc/kyz0mN8Tmez/rsYMfrr7/e39cvaZRA9oMDtpx3blFhQXFREQOLEmBEEQQ/EOGCLedj145t27du3VpcXIzPGTRAOomCvHxmc2EKESBIaV7ejm2YU1RUkM+goZxpQYrLSsp2cFUH/jz4WzJjDfvbMb1KUQHTiHDQKcRMHLaiguk5BJXVgBKcGyhZUlwC8hDViwRVgpLSnTtK/3lryXcuKP0HkEdV43nADq1WpVapjQajSqFSKdUGnYFVkgNzgDwMFqPOaDFaTFqjUWv0e4LxSAqoYWbjZpPewiDDGwZSYMTt8Po9IbfDQ7VnM4kmsEJLujWTbI6FE6k4EKQlEUknIszn1Gl120wOpw3Y4Q/5w0FfmMqyJKMp5kMaSoBdEuEkJge9QbfdnUlkr7/2hrOEHZDchBGQi6vcIEJWkkWet5++bZs8BQUBBBlWMILOCC87BLagV5HxD19fUmDi2zw1OTXPfS3J0fLyyy9/lFdCuZNXoifsoBhaNIh87CLHC3ADqOIEr0l7gKf4xGuv5I2wg2JlCTt+xhulEyXsIBDBCJEHaURoGpiGyAYvJKcQTMBJf/7zn+MCgD64MBDPpZdeSqYWXAC5cRBGLPFGFigiEvqUZnmxx5xvKecS9qHRZ0XxLGcLO/gpPvti0h1YdLrXYpGZYGDS4x3TW+c9/sVIZCEcBCssOFwrgeBqOrUQCo5ZLDMB/1wgNOsLLUbic/7QuMk2rjKMi5VjjYpBoaS9tCq1o6Bpa15z3s6+xoY+tWLQaBi1WIf1xpVQZDkSXU9nVtPpaZ9v2O4cMVnHlNp5kxlnH5SpFm32ZY9/1hEcN9paZap4bWOrUNQrFU+7rSN246jVNm51z3kia+HMoju4EgzOuRxLTtea1w82GpApp832BU94M9NxtH33ejAxpbNMyDQLCtWyRjfcKJ00WRc93hmTYdFtm7AaFu12VhfXH9/jje4LxPeFk3Pu4EwgOmn3LlqcUzLNpFo/pdePaPR7m3ound289uDlPz1x/b233P7Mk08+9+RTT93/+L0n777x4I03HLrh3hvu/fkdDz508z23HLvqyvnNS0fWluO7NzK7jnWPrkY6VoKtS6HmI10jhzuHDncPPvfwL77Fjm9Cw5KCZQeCvJI3yHKb1Yb7e5Ksu3gJlc4z+c4hcQk7KHo254AJCsmkM4FAgHlRcItGgGc3h/jHkXOEAbyo5+0/TSqVlbQXbEH2FPL5yI0Tr2AvvRyN3D7kcrlAUHPBBVvPO+88zMRZSOGR8ycFcBB24GLC4TC2eAoiAY4AOwBGAAuMU3wsaIPSrtM4WASDgA8WVZtMYotp7TzChd77bl53Bm2Ip1HHzSTdbU7yXAnkZ0raXADH6Ogo7iqx0GFZOwvY8corr7Q0N4vqGyrwmTEzRxH72OuEFfzjBXxA9jNVx/btW847rzC/AATAFBj5gIV84AJIYgvXIRUVFe3Yvj2PTztvy7l5O5iuIi8vD1vm/1FYUlnGHDuAEYUFhWAJxiUFTHHCRgoLigoLAT3s7DV1YhHL2CGqY3G2NdX4X4u/YnUlg5Vt7C/44/Pzfril4B+2FP7f5xX+Q2Hlv9WLqliokliikMmlEqlMKmPRthq9WqE1G21up8eg1dvMdq/LB+ywmmwuh8fCCr/Z7Ba7Rqlx2VwWg1Wn1jODi8ag1xiMOhMrGOsOAjKa0y3xcCIaZLXfIv5oJBCLBOJNqdZssjngDjksroAnDJSJBKLgEnQAMZFgFA9ARjqWbU61Nmdao6Gox+nuaOm86Yb/ffawY4lnm8gJUaKEdR7Gspc38oikwNoNng6c3BqmecMIGRSWeFQ35C6lxgN/4AtKahLSlOBGBNL9kUceITh4+OGHKVUGUQilDb2HO17cfPPNFLCKs+DgeCH5eQBE0MeWnElvueUWct0g7Djjy/Flig4c5x6ehxTHx2Du4DgRTo2X4CA4S87O8otf/ILQB9dGDiXkmXGApx9ltqXNzT28oUPYgV3kM7vAHa9I7YFfLD4EsrmQ1+0h3s6qS+lnn4/aXdMm87TNOuNxT7k8AwrdqME66fTMeH1jJsugXDWqNc24/RMm+4zJvugJzLt9c07PhNnWL1f11Dd2VdZ2FVf2lFZ3lpT31db11tZk8/LSxUUTdtu4yzGk0w3IFH0i6ahKN221r8Zi+9vb1lLpAZN1QGseV+oXDKYFs71fpFi02FY8gUmze8Ji69ZodhmN3QrZkF69FPbOBDwT7sCo3TfrCa1GYqvB4Ho0tB6LLLi8s1bvkNreIzZMGN0TBteMNXA023Ugml40mOfkigWlatlkW3AFJt3BpUhyM5k93NZ2oK11IxrZ4wuueqNr3vi6Jz5r8g+rHANq27DSuABeaVSMSdXzdueQWjuotcwGMgf7JvYPTx6amrv5+MV3njh5cu+x69YvvnT8wJFdy4d6F1ZbhpYz/cux3rVY7+GWiaVA51IwfbRz155E65QjNO2OrCdbD7X3Hu7pe+ahbwvffyMaftEQxmTvgPxWq9XZTBZitY8nyAJwNPNG+gwKWmnmacXbeCRtlmcMo0ha8u0wGAyU3lSj0YAPCDKIHogngBdkOkEHd9QlTEqxhn4l9xsl31Lm91FVRRoOmgYpRXsJO7B3y5Yt5557LiZTPjEDb3g7FEGDvpnHzVL0CqVU9/KM6YlEgjJz0JUDNTBCGg7AR5ynNCUESfDSuEme952eZlkaVhZLjA8BHEacAbYYHBykPjr49CbGJxZ5xg4scVSB9uxoO3CKttZWcAaYoKqyWlhbL6zDB8pcSmtraoEF+TvBFztBD9iWFrN42vy8PGzBgOWlZQCGIvYoBDfk5+3csW379m3bWBL6rVvxWmzxwC5wIn+U8xQd+XTYnTvz8GBKkiLml1pcWFywk0XB1NfVN9azjGENwgZcCXl41Apq8J1j0JOft2Xrj/71nO/863n/14/P/+72vHMqykuxlxWqVajAHBq1xqQ32cw20IPFZPO4fGaDFZzBPDyYHcQPBDHpzXqNnmU6N9mcVqfdzBw+bGaH0+IEjui1RpvJ6XEwr9JULM0cRHg+D0rCEfJF4uFUPJLGFoOYEw5EeYrSWCwYZwVc+LRoKJ5JsDSmrU1tqXg66Atlk03Xnrzu7bODHbfeeivZU3I36CRTyaywyd0XNnjeMPItxbcNfTLKQNZCvpIm4PCZioVHjhylLyg5V5LA3uC+qEePHoWAB2pArmOB+BmPa7322msvvvjia6655qYbWfgrGUcwePz48f08JATin3JygDzAHBRjgv71Z0rb4zhEHoQXpNWgg+N0ABrspXGCG0If9HHMyy+/HAfBtF/w9uijj4JIQCo5XcvBrxSGPsTrsBBjkfKD8AtQRdhBSsgJXnKaNB9L3M12gyctPcvYMWA2DUmlPfUNw1rNvMc7YXePOwOTvghzobDYuiTyVqGkX2feLdWMSLVLDt96IDxrs8+aLH0Nol119W3l1ZmdRXhk8wsHGhpH5fIeYV1PY+O8378Sjiy43eMaTb+osUdQO6JQzwFZQpFpf2y32detdnYIFAN1iimlcUgkn1Copk22OVd4LZZaicWWY7FZr2fcapoLuKa83oVYdjaUnPUHl0KB5ZB/zu9bjsaXAvEJS7BP6R3Sh0YMvkGlfVBqWbQG9rgDC3r9gkY1bzDuj6cPte9aa+pcTrbuSbYvhLNLqdbFQHTZ4x/XGqf0jmVXfFjjHVB6+yTW4Ubdqtq8LNPMS3ULBvuUwTyut867I3uzXZsdu/e09ezt7Ds6MLXW2re3Y/BA+8CBpu5Ff2xYb520+kZ1nnF9YM6WmXekVvzRvfHEUii+EMnMhVMLscxac9vent5fP/jQt9jxTWj4wVIwKiCgsbER9/1kT6HU4BCrFLrSwrN0ZHkjoZvlQSuQ0JT3ghwmXLyuG7ADzCESiUhjQZYRwgg8BUMQZDBlewnzbiR/Dtw0E45gS9oRkApT0J/RfJDxBYMgD7FYjM75W84/5yfn4OA4l0wmw0nJk4NMLeiT2oNiaMnOEuDl4vA2cfFkJCL3DjKpoOFdMCNRIEAqEMq+ig650NJMIEhOCdTO6/H28kbKIXxoIDZwBtmRscoRi9CC9vd/gP/uxiJZurrqhfX4HMtKmYsoMIApHoqKC9DyCwAc+Chra2rwwKcPLBBUC0Akgurqqooq8smorhQU7MzfvnUbsOOC87dsveD8c875yY4dO7ZvQ5chSP7OvMKC/OKiQq7twAFZZtILLjj/3HPPOR/zOZqAa/J2bAeCFOYXkM8HOvk78y+4YOv27TtwLQxQgDLbLvjJOT/6wQ+/+/0ffOec836M74Kwrk5ULwJzaDU6qUSmVCh1Gh3Iw2wwGbQGm8VuNliAHUANsIibpTw3g0h0aj3LW8qDVpgPqcvPPDOcHjzsLJgFqOEKekOsFIvdxerT2twhf4i5jjp9fncwGowDOOLhZDrBAmXD/mjIH40GY4lIknueYk4AOAIWyaaa8ACOJMKJK05ccZYCaCG2IRfJdEKgcJBHfkLWAjsovGWeN/IeXedhomhk4cvZUEgtgfmXXHzJBk9piqcXXnjhYd5Y6McmiOQICOCJJ57ASQEWN9xwA+kVlnm8LiaTDoNSgmIy8AJYACLBTHAA4OM4L0WLC6MAWkoOdhdPBZbTdpAZ5Z577iEPEjRSqOBc99x9Dyk20DBICYVuuukmvISMLLg2kAeeYvDyE5cf5Yk3yI/1Ql7sLdcO8cSjeI97eP5gcrbCG1k6k8WEPHOJ1Uhp9Morr5w97Pj88xGXfUQuH5LIh5WqcYNx3OKYcAanvLEpFtji6tcbOpXqYadrQK3bVdswbTAvutyzNtu8yTwgauwXS3aJJV0NjT2N0kxZdUtF7S6hZEyvnzQY5832RZtzPRBY9mO+aVqvnTWYh3XmHo15l97e5wh16lz+reWZ7aUTUs20SjOtUQ2rdNOO8Eo0uxKIzNldSx7fjPP/Ze9M4Gra3v/vXveah2hQMlMpUyJJqVAZK00aFBVFA4UiQoMmqTSQIVPGKEJSImMkSSUN0jzPGhDi+n/OXl/n79ed3Ik6rc+r12mfPZ2991rP87yfvddaW8tJffFmlcWeJqu9Vph5Ll+229TE33Sli66Bo5aBk8ZyN61V9kqGdnOW2inrbMafgrqt1GyHmbNdFBRcZs50VFD01TEMNFkVYLra28jYXQfnZeRpbO6pa+ihouY0R9F90WKfpWY2c7TWzFqyUVFnx3xtnwWLfZQWuUgrOkjNdlZU8dQyDDC1Anb4mazZbWLpa7LGd8U6LyMrb2OLvast9q5c4auv46Gu5qe/bJf2cnc1Q+eFBs4L9Lw0dPz1lwauMN9nbe+32ibQcv0+m40HHbY/e/CQPmRpD4LvQghH4EfwBjFYWVqts2G1lCTtJcldjbWMSFfSdUxnWsy3sLBAkEbYRiQmj1fIQw3SrlNJSUlGRobc2yCfLLD4fFcDn0iNyTThjHGsW/TIslnCIlaAFBWdyEh8Eqt5B3nOQiiEtEsFdiALFxQUBIvMmDFDjumdS56zkCYdGhoamCDAQUQ6rZgyg6iCLkirUtKlRV9fHyfyv4YpDEKRGyGkkw5BLkyTOyUrV7JeyUtmAshIN2Myzge5PrhWZOQP0skWn2ARX19fOLS2BfBvq7KyEkcwY7r0NMlprAHLJaZOGM+COxYzjB7F6rzC6r0iwmrtAeAbzyKSKRJTcIknYnr8hGlTpklLzZACh0yVHC82fiyr6ego1g2PYdDQ4cNZf8OGDhk5YsSYMaOFxowSEWaBxSRWA43/UQlQg0U3IoCScfhFVhMSsAjT5whbYW8QsAOfWCosPGbIkMECgwcN4ufl5+cdMnQwagdKc87sOQvmL5g9a46yorKSorKCnILibEUFuVnKc1j9WRarqKstWswaKGyRGmtioRqgBNjBjICuBT4wZPVD0dFcrKmtga/6hDb0tPV1tXT1WSyiq6sNjNDX11mKddRV1FkIorcckLFU12CpngE+9XUMtFhcsgwzSbNTbM76amiywtjUxGiFnrbecv3lJ0O+1XBhiPGEOcgjFXLHggjBFbEWM5G+b9u2DZ8kfSfx1YMZinTn5zfGeTMvrWVhx/HjCLTI9cnr2YKYrqQHmN622Dl+DqH92rVrCPmnWIOds4b8wk+gKvv7+QMCWOOSRkSEh7HGJGUP3UEGPCZdWA8wLTSBHViE+SAYIAXBDjIgKYADbEHG52CGBYtn3/C4GXcT02TEMKyAPQBlrly5QlqVEuwgHWqwq7OhZ48xwpGcZ0YfwqmBeIKZbjX7vxi6g9wu8mZamxKEIpeU3OMhzIFrUlpa+i2xw8PEyHn+ArcFas5zF2xXnmsrN2e93HxruflbVbSdNbS2qizatHCho46Ow6KFa6ZMdp4333H+gu3z521WkN88S8FZdZHLYlWHefPsFJXMp8uajJ9qKT5jy6zZDnLyLvKK3otUfXW1fI10PXXUdsyf66w0b+tcVZvZC+wXa7gZm9jMVTEUmbBSSNRhpoLb/IU7F6u7LFJ3XLhk2yJtTw3dHXNVHRSUnBaosF51q7fMZ7X1zpVmXiuMd68yddUzdNIy3LbYwEnTZOfStW5aFi6LV21VNdqkpLNVUXv9FHlzkYlbpkq7z1J2nDl7x5y5XgsX+Wpp+Bku8VBf5KWj52+6OsB4pYeqquv8eb5L9HYZrLSeq2W9UN9ukZ6rpsGO+aqbpRVWi0qYiUy1kZzlsljf32ytv+laV03DnUtNPfRXu+tauOuYe2ib7DZc4b98ub/B0kBDA7+lRn6Gq/0NrXZqmTku0HNT1dq9RNd/mfEeUyt/E/OAFRb712wItt+WlfColfZkaQeCE5OVlSXMgbhrzbzvDYGTtG8gMRUhE6k86buBpQjGiL7srh+I02AOBPj5zNvagB0LFy5UUFCYPn06qzHBhAnkvgW5oQKRextgC3ySux2iTKMBQhtM1woWkbCghEnQWQNYIbBNmkRudZCHQRAWDR48mI+PD4vkmFe3kFHScSL4VFFRISNzkGHBwBz4JNhB2MLQcBkOntytIV1XSD8XZhFLWA1nup4RMIK0bgF5kHZ4mDBnXsxrxYwKjzm4ROTNNdaMwBzbGG3ZsgXksWnTJvi9b4Ad5eXlOCQ5WTmZ6TJgiGlTpZjerWITJ05g7k+MZ3VmmTRpuhRrMA8gHWtkjjFjxooIAwZBD2RY9Cm4xpNZyDIecMICxnHjxoqBS4AUwI6RI4YBRFjYITQaVAGSEBMVBXAAQfCHOcOHDxsxYhgwRIilMUOHDiUjrOCHhIUAHCNZLUuGDwdwjBo1QojZD/5YHWNGDBcREZaaNk1uptzsWbNlZVj9bGWkWYOe4WgxMV95/lzFeSoLVDUXa6kCOFTUVRaqLlBeoDxbGfOVZikvYgYqXayyWENNA0SyYO5C1uMSA2NdbX3M0VissURTBwBBbmOYrVgNvFi21AhLWVwCXtFfprdEX1NdUxvEgj0sVAOjMMOe6utq62qpay1W1dDWXKKrraO1WNPMZFX42W/1TpZLly55MKOLAhHYATIwgDXyBGEIZPWIpq7MqDLADhJN9wWxmnp4Mn1ldzEvrcXEHqbHByEV9srezGsMwQq+zFggYWFhjx49QphHsMfOEcJJu05yEyX0TChIAotiPg8zisMjcwAfJOrjJ44wL7jHoihmTFIwx63PfWIBH/cYkQFJExMTwRiYJh1oSasO8h440nQU+8HOsZQMX0a61WAF4AuOijRiJXdlmJ4vrCcvx5i3wZFOLnuZviqBzJMpIvJQaR8jglzkfs/Bb/1Olo9e5qs9dfW9tQ3dF6q7zF9grzB3o9LijQs0XXSX7TRY5qGv66Kt7aq/1N0A5DFn+7y5GxVm2SspbVSas01tkbuB7ua5iutnTLedreCweLGt0vwNM+asmzrNdto059lznObM3jJHxmGenIOy/BYFBVtpeTctg53LV/iarzq00dZVZ6m98sKt81VcNXU9dJZ7Gph4LDH0Yr2ITtd3qTGOx0NF099g+V4Ly6B1m/ytN++2QuTeHGSzboe+iZ+Jhb+Jxa4VlrtMNwSssvczsdtpsHa72vKtC/Q2yS3YPHOu+zytPUtNd2lo75w/111Bduf82a7zZ22RnbZ19mxXbV0/E3NfQ2Mfg2V7TFf7ma7ZvXrDLrP1nsarvA1NPNV1HRQX2c5ZYKu8eMsiXdclxgGma4NWWe9aauJttNpJ09hTf423/nqXhSs81Ve4L9bfrWu4z9h091IT32Wrg8zs/E3sAlZsCFxhdcjSKmil8b4VJoctLYOt1hxeZ7t//caMeNq2o10I/gS4gNiMyLqeef0KaWeGsEqeraxiXhaPWEv60yKgkoYOBozInQPybIV0WFVnBgOdN2+ePDPM+VRmWA4ZGRky8BcQh91vRZLpFkual5IeK4QzAByEUZCIYxpRjzxnmcaMFfZltxdEMkFBQUywmYP0YQFtkMYcpJUokAKfpAOLIfNIBRM4bEyQFh7k0Ql55oJP0k6FtP8gdy82fB6hFRPkEuErGbfUgXn3BdgCzOHEiAyjTkZmIuRBWrDBoX2bcTvAQDLSMxCkZ0yfIS09Y6bMzCkSk6dJTpWWnj5tmqSUpCSu+BQJCRnSw2jKlPHjx+HfNMlp8nLyTH+hqZPFWeN8SE6dOnHiRKaApGbJz5qrpCw/c6bEZHH8SU6RQPGwSoj5lGRhJT7BhRJiTPNUMbGxrMcsY8diPpgShUjuVbFuWzE3tyZOmDBpIqtFyBTW7StJyakS06Wmsf5JSJBHZvJMY9KZM2RxFqCoeXPnKc1RAhAsmr9wnuI8HU0dLQ1tHW09EAZYRHm2kuZiTQ01TbVFamoqasCLxWrqoJPFizRYQ3vpLVuiqYv1l+ouNV5mor/EYPVKFLy5taXN6pUWq1aa6y7RN1y6fLmhiQmLRVbp67CakRoZGC1bunypnoGhwTIToxWmJitNlq/QW6K3YO4CbQ0tA10Dy9VWYWfD6mq/yXBhCLenT58mUZw1BjmjiM8DjUcz44tj+iwzUic+EYMjI1nDeeEzPDz8DPMyWAABuVFBEIHsCrslW5FHIVgf8wEHTxkBCK4z75sFHABEEOaxzt07d0m7TvJitqSkpIcPH2IpPgEK2Ja0RSXtNkgTDdIF5ssBT9PT058xyszMzMrKIn10yftfyBwoIyODHANp6oFFmPPixYu8vLz8/Pznz5/j1+OZAVVBJ2RUDwjrYw5pQQKRwUII+kR/ftstueNCMAVL2QOnYv2XL19+O+z45ZewvXvP7vI+vcP9xCaHE5s2nd3hfnKH52lP39CdPpf8/C8HBpzz9jnruztij/9JD+fjzo4nXVxO7nA5vNXhmLNjiIuz35o1e2xs9m/cdMrN44zHzhBHl/0bbYM32h2ysw1aY73P2nq/zZp91muD1q0P2rD5pLvPESe3E24e0QeCL+zyP+O6M8In4IJPQJh3QOS+w+e8fM66eVzyD7jgF3hup0+oq2dUYFDsocPXjh6PDA6JDD5268yZ2CPHzu/eE7V3f+yhI5EHD4ftORixL+Ty/hMXg0LO7T4Q4up9ZNuOE05uZxw9Inb6XfD0Ouew5ZiVxUlbm9NbNoastwmxswt19Qj18InY5Xd5t3+Yp8/F3Qci95y4vOd4xO6g87t8z+HcHd1C3b3PePqG+QSe3bn7kt/eCB//cE+f8zt3n3TyCHXHVkGhLgFn3QNOOO48ud3t4k7fMFevcM/dEd57L+wKurrnaPSeQ9f3H4rx97/q4x292+/GgQPXg4OvHDhQlJHxobW1bRFQfXPB1sjzzUDmmS/BfTLMIJKoPZ+bzGNRMNNa3J8ZZJk8MiYjBWBbd7f/DVnh6OhImmohMK9jepySsE063wJZwDH4asWItMpkP7ghbVRNmc4jq5g33Foxw56SPq7k1gJ55IEJM+YtuORhxwamR8lWRiTkk64lJNize8yRzobk2TeOmUwjryP3ocmdV3LblSwld6PJzWxcCpI1kefpSI3IJSIDI508eRJ++9y5c8i44NhDmddQnGZeAI4VTpw4Qd5TARcHh9a2AP5t1dXVBR8MBiACjHD9LczNbVmPzKztbDfYM9S4ds1a/IGwcB3XMbK0sFi7Zg1m2lhbb2CNEcfCTVajFRTJGivsASvgKwrJztZu0yZ7FOw6a5u1a6wwx5ZFqKwSIdiKjaysLFeZmZqZmVpaogRZHa1RosuXs+4tkcZAKGFswNrhOhtLC0sr1nBz5masTVDuZubmWGiGwwTjbbbfjD8cNg5y08ZN27Zuc9zu6LDZYfPGzVs3b3Xa7rzVYdsmu42Y4+ri6u3lvWunt/N2Z8et210cnfGHdVydUTU9nB1ddjjvcHN13+mx093V3cfLd29AUKB/oP/uAC8Pb6+d3qxbBK5uXh47dzi7urt6+Pr4Bfrv9fPx3+0TgHV2+/jtDQzaE7DX28vHd5evh5u7u6ubzy7ffXv3I/42Nja2LYM/01/GDqihoaGSUXV1NZmoqqrCdE1NDYq8tra2mhGZST4xs4YRmUO2InsgYi9i74rMIdvixF69eoVP7B/zGxnVMUI9xnTTFyJLISyqr6/H+mACEEBRURGOnL208YutXn2h169f45PMb25uZs+B8BUzGxixF735LLLoJSOyc6yDCRwDOdQ2Ys+vZcSeINP1jFq/bWSqLi2tLimuKCgoz3lRkZtbU1RcXVJSXVxSVVRSU1JSW1aGr1Wsv+LKosLKwoLKwsLy/Hz8VRTia2FxzovS3NyyvLyyz3PKCwvK8vPKcnOLn+eUvnhR9uJFcfZzrFaSm1deWFxRWFxeUFhTUoqfqCwoqiosZv1WMX6ooor1o0XVxTgAHFIpltaWltVXVLysrKotr6guKaspK8OvpCQ8THuYiN+qLS+vraisraiqLi2vLCmrLMZfSWVRUVlefkV+QVUhdl5YlZ9XkZNT8SKnKi8PZ1eRl1dZUFBRUFiN0ywqqsjHBI6koqa4orq4lDWTtRWOoQQ/XVVcWllYVIOjwhzWH5YWV7H+SqsKSyoLS8oLmD2w5rNOBH/4WlNSVltSVocjLymtw5UsKKwtLq4pKa4pLX376tW3A0qq3xfMDVWp5LOKi4vhKMpKy8oZYbqYUWlpKb6WMmqzMj4LGBV9FqZzc3NffCF8RX7Cns5hlJ2dDdeEOZjOZUSmyXxMPP8szCFf8YmMiNypRXaEr2SHEJKfvM/CARQyIgeGRWQOjo18sk8Bp0MOGPPJ6RDhK1nKPn1cJfKVzCciM/HJduYkHBD3XsEIE8SfwzF+A4eGn0BJkYvMunRZWf+77J9TREw/z2ZdUzKffcHxifQykxGmWcVByom1QlY2s5gURw7rLyePVaQssXbIFM0XZcfaG5nJ2pARds4uR7KUlB32fe8ua9BLYBnyVfLrWErqzOf9s2oIDp51XqyfZk0XMoXMrJDLqn6FTDkWFKIeoMhRhMVFKNBizM9nnXc+ihVlhRUwp5ip1yXFKEpW5WdVElycvLxCZg/YCvNLS0rJHrAUE1gNiwryC1iVo7CQtcPCIgSpDx8+tC2DP9PfwY6OJVR6wDuSA+A24KDtYqo/VJvA+Bfj5F9c/a8IziUhIYEMHJmSkvL+G7WT+Htn9Pe2omqX+q6FiWofHx/v4OBgZ2f3+PHjd/Q53b+m71OuHz9+fPbsmbu7O+v2iJnZ0aNHf/1g/fsc2X8pDscORKOQkBBBQcEuXbpISUk9efIExdx2JaoOKCQ0lpaW/fv379Onz8aNG5Fy0ZKl4nghYV2xYgUXF1evXr22bdtW+i2bnFP9B6qpqQFzjBw5En6sR48eEhISERERr169arseZ4mTsQNxCHmwrKxs9+7d+zBCcoxipoba0fXhw4ezZ8+OGT0Gzrdbt25iYmLh4eH0VhYVZ6upqWn//v2jRo1CfEK1l5aWjomJodW+46q1tfXatWsKCgo8PDzKysqSkpLc3NxGRkapqanf6vbt9xHHYgfYAqnA6tWre/fuPX78eG1t7QEDBoiIiFy4cIHemezQQsnm5eWpqKj07dNXXk4e+QHgQ0NDIysr6288ZaSi6hB6+/ZtbGwskiiEKDk5ubFjx2Ji/fr1BQUF9D5fB1VJSYmVlRVQY8qUKefPn/fx8REWFh4+fPjOnTs5+/Ytx2LHq1evgoKCBAUFEZOcnZ3T09NnzpzZtWtXxKfCwkJ6w6PjqrGxEfYJnzty5MjQ0NAjR46glLm4uHx9fRsaGmjJUnGeUKszMzONjY2RO02cODEsLMzFxUVAQEBMTAx51OvXr9tuQNXuBY48c+YMSvCHH35YtmzZixcvEhISFBQUunXrNn369MuXL3PwoxbOxA5wYnx8vKSk5I8//giWvHLlSmtrq4WFBb4iPh04cKClpaXtNlQdQR8+fLh165aMjAyMU0lJKSMjIycnR0pK6qeffpo2bdrdu3fprSwqztPLly+RAYOzkTjNmjXr6dOn8G8TJkzo3r37ypUr8/PzKW13LCFCgSP19PT69u3bpUsXAGVeXt6TJ08UFRXh2Xr27GlmZoYVOPX2LQdiB3m8YmRkhFCEEh00aNCDBw9QzGvXrgV2sNuWcmqJcrbKyspWrVoFW0VRLl68GLZaUlIye/bsn3/+Gba6evVquGAOvjlJ1QkFkkbiNGXKFFRyVHtVVdXs7GwANzgbFDJ48OBz5841Nze33YyqHauurs7Hx2f06NEkJGlpaaFMCXYAJTFz2LBhgYGBVVVVHAmUHIgdTU1NKLABAwbASmGWAgICqampra2tiEkoTsxBfNq0aROnligHC6R44sSJ4cOH/8hIfbE6wY45c+agrGG9Q4cODQ4Opi6YimMEhgZh6Orq9u/f/ydGy5cvB1unpKRMmDDhhx9+wJxly5ZhnW8wJAbVvyKUVFxcHAiDi4sLGRRKECiJEkScUlJS6tevHx8fH+Yjm7p69SpHPmrhNOxgleiNOOQBvXv3FhER6dOnj6Cg4PPnz5ExGBkZIVYhMmGmqKjoxYsX6Q35DiTSkhSEgeLr1asX8JF9t4Ngx8CBA/GJaUKZbbenouqAQnbk6uoK1Ga9iGzoULg1Kyur4uLixMTEsWPHdmU0atQo0HZ9fT3NozqEKioqbG1tBw8eLC4urqCgAMdFsCM7O3vBggUoZQ0NjcmTJ/Pz86OsObKlPKdhR25uro6OTs+ePWVlZW1sbAYMGDBkyJCCgoLXr19raWkBO5ArzJo1q0ePHpqamvSZaAcSebzNzc09btw4YCWKuA12LFmyZMwYVpdaJyen6upqWrJUHV0tLS1nzpyRkJBABDIzM0NMQv23s7MrLy+/e/eusLAwvoJIAOJqampJSUmc3euSM4RcNywsDMAhICBA3pCHCKWiogLsIB30Ro8e7eXl5ejoiPIVEhLav39/bW0th3kzjsIOsIWnp2f//v1RkCEhIXv37kVyALMsKytrbGycP3/+Dz/84OHhcfr0aaQOWC0wMPDt27dt90LV/tTa2hobGztlyhSUmr29vbW1NRcXVxvs8Pf3hw3z8vKKiYlFRkbS8QyoOrQ+fvyYmpq6cOFC1HlQRVRUFFImVG8EpKqqqri4OMQkIPiaNWswgRiG+l9X95dfykX1LQV6eP78uZ6eHspRQ0Pj3r17IIyhQ4eilNPT0wl2jBgxAkWZmJi4YsUKHh4eRUVFzmspzznYASu9c+eOqKhov379LC0tkRD4+Ph07doVcyorK2GQM2bMAHb4+vqSZokgEiTNaWlptAVi+xcZggX+V1paGgbp4uIyYMCANthx5MiRBw8eAC6xmpGRUU5ODufdnKTqPEKOC8Lg5+cHWyBTQljS19dHHHJzc6upqbly5cqoUaOmTp0aERGB2j5w4MC5c+cmJyfTx4vtWe/fvz937hzizsSJE4ODg+HWEI+AHSg7ICa8maqqKr7u2rULiy5evDh79mwJCYkzZ85wWHs1zsGOt2/forRGjhyJwAPzw9edO3f++OOP8vLyYA6Qx/jx40Ehe/fuBTmCH6dPnw4b9vb2pr3e27+uX78uLi7Ox8d38ODBV69eubu7/xo7jh071tTUdPz4cYCmnJzczZs3OSxFoOpUys7OXrZsGZgDkI1KjiwZ+THwAtlUfX19WFgY0mLUc6A2yENKSgrh6vDhw41//XWgVN9MSISQNW3evNnDwwMFitATGBg4fPhwZWXllJSUwsJC+LRhw4Z5eXlVVVUBLk+cOIFsKj8/n8MyKM7Bjl9++eXp06dAjUuXLiHeADucnZ2BHSoqKjDF4uLiIUOG9O7d++jRo0gIUN5+fn5qamonT55ErGq7L6p2pvj4ePJacOAjCvr3sAOF/vLlSxjtwYPBsGEOs1WqTqWWlhbQNjgb8QkuKz09HTkxsGPfvn1waMiVBQUFkQ0nJSUhMwaa6Ovrh4aGNjQ0tN0RVTvTmzdvmpuaUaaIUyDFUaNGEeyoqKjQ0tJiY8cnJqi13ZgjxDnYQQRbJc01MAGoBHYYGBgAMhCfYKW8vLzIEkg0Ki8vT01NpQlxh9DHjx/hW0H95B7yH2DHJ6bx6bt3tG0dVYcXog78GHkKTHpXwoMhA3716tXu3bv5+fkRrp48eYJq/+LFi4yMDI7sbMnBev/+PbzWmDFjFBUVUY7IqbS1tZEee3h4YLrt2hwkTsMOtmCBNjY2wA4zMzOYZVZWloCAwNChQ69cuULM+BdGbTej6gj6Y+ygouIwwVMlJSXNmjWLj4/v/PnzSJednZ0xraqqmpaWhjyKurKOKDZ2zJw5MzExsaKiAtiBYuX4dwtzLHY0NTWtXLkS2GFtbY0UOTk5edCgQUJCQrdu3aJtSDu6KHZQdSohAt2Pvy8rI8s/iP/atWstLS2ITIhPS5cuzc7Opg6tgwrYERISAuyQlpZ+8OABwQ4eHp7NmzfDrVHs6Hh6+fKljo7ODz/8YGdnh2wgPj4eVjpu3Lj79+9zcHF2ElHsoOpUAljcuHFDUlKSn5//9u3br1+/tre3Rx61YsWK3Nxcih0dVMiHT506JSwsTLCjqqoKMYuXl5diR0dVTU3N3LlzgR2wT2DH1atXubm5J0yY8PDhQw4uzk4iih1UnUoAi8uXL8N9ATsQn5qbm21sbJBHrVmzpqioiDq0DioEpvDw8LFjxxLsqK2t1dXVpdjRgVVdXa2kpMTGjsjIyIEDB1Ls4AxR7KDqVGLHJ2DHo0ePGhoazM3NgR22trac3QiAs/Vr7CAjiVHs6Kii2MHBothB1akED3b8+PERI0YICgqmpKQgPhkZGSE+OTg4cHaXB84WxQ5OE8UODhbFDqpOpdbWVjJQx7hx49LT08vLy3V0dLi5uR0dHeHo2q5N1UFEsYPTRLGDg0Wxg6pT6f379/v27Rs8ePCMGTOysrIKCwtVVFSAHTt27KipqWm7NlUHEcUOThPFDg4WxQ6qTiVgR0BAgICAgKKiYk5OzosXL+Dc+Pj4vL296evfOq4odnCaKHZwsCh2UHUqvXv3bteuXYMGDSJ1Pjs7G3V+6NCh+/bto6Ohd1xR7OA0UezgYFHsoOpUQsXesWMHApKurm5hYWF6evqsWbNGjhx55MgR+u63jis2dkyfPv3+/fsEO3h4eBCziouLOThOUeyg6nii2EHVqdTS0rJlyxZubm5jY2NU+JSUFDk5OWFh4dOnT3PYK9E7ldjYISEhcefOnZqaGmAHFxeXjY1NQUEBB8cpih1UHU8UO6g6ld68ebN27VoEJDMzs9LS0oSEhBkzZiBchYWF0de/dVyxsQO6du0aYhawo1evXqampi9evODgOEWxg6rjiWIHVacS2MLExKRv376Wlpbl5eW3bt2SlJQcP3785cuXQSRt16bqIGJjh4iICBs7evbsuWLFCoodHVIUOzhYFDuoOpWampqWLFnSu3fv9evXV1RUXL16VVxcHNgBt0axo+OKYgen6d/CDli1EyMvL6+2yzq+3r1713bWH+r9+/fsq4crSa7My5cv/+9a/7kodlB1KjU0NGhpafXt29fZ2bmmpubChQvjxo2bNGlSTExMS0tL27X/TNSnfanv6NModnCa/i3sOH78eJfPSkhIaLu4w6qystLY2NjHx6ftgt8XDAMW8vr1a/LV39+fXJb8/Pz/u+J/LoodVJ1KtbW16ovV+/XrB1Cor68nb0ufMmVKXFzc36jz1Kex9X19Ghs7xo8fj6IkTUp79+69atWq3NzcvxSnOpYodvyJ5OTk2CaKOt12cccU7BMuDGfk7e3ddtnvCMZMLgLbRI8ePTqCUXFx8f9d9z8XxQ6qTqWqqqpFixahzgcFBSERP3LkyOjRo2VlZe/fv49kve3afybq04ja+DTEhW/s0xCYQkNDhYWFpaSk4uPjEbN0dXVRyhs2bODsFwtT7PgjPX/+HHtAvezZsyc+e/XqxRmD8xQUFBB7+3oT3bhx45cm+n1FsYOqUwlRUFlZGXX+8OHDcEEHDhwYNWoUqv2jR49aW1vbrv2Hoj6NrS99Gi5jS0sLPr8+OvxzARkBOiBIgGBiYmJFRYW2tjYfH9+2bds4+8XCFDv+SJs2bUKl7Nq1K5IMUkH37Nnz5Qpnz55VYFT9+YVMsGoyJy4ujr3awYMHJ02a1KdPn6lTp4aHhwcHB2MFPT09stTMzAxfseeoqChpaem+ffuKi4ufO3fu48ePu3fvFhUVBcXLy8snJSWxdwgdOnQIe4PXAJsbGRl9eVdwx44d2KGtrS0OBrGZh4dHQEBg5cqV5PUNDx48mD59Ojkd1HismZ2djflYamFhISQk9OOPP+JQJ0+eHBgYSK4VrsOwYcPIJrCQ1atXYyZOhJwprIX909HR0QsXLhQUFOzfv/+0adNwUrj47KU4ZawPS4uNjZ05cybOVERExNHR8ct1vkYUO6g6lZD7wpvBkE+fPt3U1ESwQ1VV9enTp3/VdqhP+02fhl3V1tbi8pIz/TY+7d27d/v37x85ciQKNyUlpaysTFNTc8iQIZ6enpWVlV8fpzqcKHb8rkC+/Pz8qJTYD7AUEIpp7OHLddi36dg35R4/fkzmnDlzhszZunUrmUOEQxITE8MEHAdZYcqUKfgqLCwM22CvhmlEU/ZXCIGW/dqndevWfbkIgh0SS4P09fUxB8aGTb5cByeCpVeuXPlyJgTjx8nCL7SZD/n6+mKTBQsWfDkTtvfpt56DOjk5fbka0fz589n3gWFgmIPzhdf7cp3t27eTFb5SFDuoOpXY2AGvAuxArEJwVVNTe/bs2deEN7aoT+vyOz5NUlIS3gNIRL5+G5/W0tKCgxk6dCh2CILEjwIlAUMoAjbzcaQodvyuIiIiSAUKCQnBVxsbG/IVZM1e509NtLCwkFRHCQkJTL9584Z9Z6+NiUJgZNRmtgnh4I8cOYKDZxs5YPwT09yafDU1NW1sbMzIyABf4ysMieyQmCikpaUFqwY4jx8/nuywvr4eUB8aGkpWMDc3v3XrFhzZzZs3yZNR8hM4HdA9vs6ePRtfYRK6urpkk5iYmOTk5E+/wg7YObl5CwNOSEjIzMw0MTEhK7i4uJADIyYKOTs7w+QA+EgO8BXZD1nhK0Wxg6pTiY0dsNx/gh3Up3VpTz4Nl87NzW3w4MEoSuz8+fPnOF9cw6CgIM5+wx/Fjt/VokWLUHt69+5Nhh9+8uQJqV5GRkbsdf7URNl3Mtn3J/HrqFhdfmWiyELYB9atWzfMmTp1KvkK50J2EhAQ8OlzWoB12F32EXG7MDdOyQsaiIni3Nl118PDg+wBNfvT7z8HZZ8FwvnkyZOxAtIFMufXbTvamKiZmRmmEf5LS0vJCjgdkm0ICAiQUyMmOnbsWLICBLrHHG5ubvacrxHFDqpOpd/EDtT/v4od1Kd1aU8+DaWwefNm8qodnAWQRVFRESV78ODBb9OD93uJYsdvC/Xsp59+Qu2ZOHHisc/CHrowjbDYdeLXJgpAJnOIiZInqRD7XiKkqanZ5VcmKiUlxV6hf//+mIO6SL7CBshOYBX4On/+/C6MMZDHkNCkSZPIComJiZ8+myiOlr1D+CmyAtKIT79jog0NDXAo8GXkNiwR9kyW/qmJTps2DdNIQdg7hDZs2EDWQXby6bOJLly4kL3C8uXLMQfn+/+3+QoR7FBXV6fYQdUZ9K9gB/VpRO3Hp4Go7OzsUKyrVq3C8SckJODEhYWFjx49ytlv+KPY8dtydXUldes3RQj90xcmWlhYSOY8ePCAzCEmilpFvn7ZRklNTa3Lr0x05syZ7BWIibJTkDYmCiLGNDwI6ev1pe7fv//ps4mCx9k7BD6TPfyeieL4yR3IHj164Lq5uLggL+nyVzID4iaQT5ClRGwPVVZW9umziWpoaLBXMDY27vJ1JsoWStPZ2RnYoaWlhROBR8alg8M6fvw4xQ4qjlRxUfE/xw7q09qbT6urq7O0tIQrwyUFFF67dg2gNm7cuLNnz3L2G/44FjtA4srKymzsiIqK4ubmBrd+DXaw7xmivraxAZIusPnXz8+PVMHMzEwy58KFC2QOMVE2kmM+WaGlpYW05PpTE2V3qW9joqRa9+3bl+1xgMZfdt8nJjp48GD2nN8z0V27dpEVyFPe3r17I4qTObh6XZjHt+Qr20TZr55qY6KAgC5Mr7wvOR3w3oXJUT5+/Pjps4kiMWKv8PUmyhYOAEfLxcVlYmICx/c8+zkMFb8bHh7+VwcopKLqECLYAQ9GerIQ7FBVVU1PT/9K7KA+7VP782nl5eUGBgZ9+vQBEsGV4XoCcSQlJaOjozk7g+JY7ABIIhsAdtja2ra2tt69e5eXl1dYWPj27dt/ih1xcXGk8gFC2ywiFRGKj4/H11OnTpGvZGg8eARYGplDTBRE3L17d3wdNmxYbGxsSkoKebza5R+Y6Pnz58nXnTt3fmKyf+QKXbt2BcsTQ/1TE8VRka+wzFeMlixZ0oUxe9KJ/8WLF6Q1FruVO7sJWHZ2Nrm72MZEQejkK8C/trYWBODl5UXmrFu3juzkn5goW/h1HR0dbIK0A+4g4UHC8OHD+fn5792991fHMKCi6hAqKSlByESoO3LkCCw0ODgY2PGXxu2gPq0d+jSgEi4dLqa3tzcO4NixY2JiYjIyMohWX1msHVQcix2odpaWlj/++CMqwZs3b1CxBAUFQR5hYWF/mh/o6emRuoVkos0icChZtGzZsk8MrpJRd7owRogqjuyBfGV3NvP09CRziHr16iUkJNTlH5gosIndDw27Yvc+d3d3J+v/qYnCknEYZA5048YNdgaDvS1cuBAnRXIgJDFkD/B07PXxo59+ZaI4KnKjtY3ExcXZNwz/iYmylZOTg9NH5oe0DCUL18DDwyMqKorkjOQfVFQcppqaGiRRiJqwccS/c+fOocLDsr7+nSzUp7VDnwaXBcjAge3du7eiosLX1xeXevbs2UlJSZztyjgWO0CmO3bsAC/PmjWrrq4OLAnIRbVDxfrj4YTBK2PHjkXxI71ou+zTJ9QGADiWwuzJE8HLly8PGTKkC9MrHeiam5tLbl1iPnurkJAQshX2+fjxY9LUWVhYmCxVUVHBIm1tbfb6OFTMYeclxcXFZJ9Hjx4lc1Dpra2tiSV3YR55EuslwiKs/GV7rtOnT5M9APnJHFTxHj16dGFaj+NQcV7gd9LaHGZgZmaG/INskpWV9YlpnCUrK0t+jjiXo78aSBiEjmyA3MuF4CXXrl2LbOnzUXyCG8L6ZLQxIpwj5rQZOeCPhQxPUlISDghHiFLetWsXPKO8vDwO40/vY1FRdUTBKZmamsLeLSwskNbHx8dPnz4dDiQ0NJT9gOAPRH1a+/RpuG64hnBlOBfQnoODA85aXV0daMjZroxjsePDhw/79u0D/44ZM6akpAR1Gtz6ww8/bN269b94VTR+4su6yBZwB1Xq/v37VVVV7JngWdRgaWnpL1b8myoqKmJ37vqrgjMqKCj4MlvClcGcPxj+HEj+NT/38uVL7Kft3H9DsMYrV66MHj0ajuDOnTsgSBsbG/gUQ0NDXGHOtlWqTqu3b9+6uLjw8vIiJsGykCXPmTNHUFAwMDDwv+tpSX3al/ovfFpMTMz48ePFxMTg0wA6gCQgCMgyPz+fs10Zx2IHUJcwOwoyLS0N0GplZQV4X7Zs2X9nqL9WTk4OwWT4iMTERFTcY8eOEQBnPx2k+nqBM0CT8L/I9pCywB0vWLAAKc7GjRvr6+vbrk1FxRGC+9q/fz+8mbi4+LNnzxAmtbS0uLm5t2/fTholfEtRn/avCGBx6NChkSNHzpw58969e8+fP1++fDk/P7+Tk9OXXYQ4UhyLHSjUhw8fTpgwgYeHJyoqChTi5eXVtWtXBQWF8vLytmv/l2KPhfelBg0a9DWITdVGSL9AGH369NHW1q6urq6pqRk/bjwXF1dAQMDX3G2mouqI+vDhQ3hY+FiRsfAb9+/fB2GvXr0a8G1jY/Ndni1Sn/bPRZoBgDNAkEiMk5OTVVRU8HXPnj3fMjH+LuJY7PjEvMFIRkamX79+yI+RLly6dOnnn38WEhLKzMz8loYKl3H06FF1dXVJSUkRERElJSUHBweO59n/SOTd0N27d7e3t29uboatIl0YMWJEREQEZ3c5o+rMgr+6d/eezAwZoMaFCxdgBVZWVpjG53d5Qzr1af9cYItVq1YNGDAAnzk5OTdv3pSXlx86dGhYWNhXNhPuuOJk7EBOAMPo2bOnra0tCVE8PDzdunWLi4v79oZK9a+opKREQUHhp59+Onz48Js3b0JDQ5FjSUhIIAXk7C5nVJ1c2dnZ8Gbc3NxeXl6FhYVWlt8TO6j+ufLy8lRUVFCIzs7OxcXFcGUTJkwAw4E/OLsbyyfOxo73799bW1uDM9TU1EDisE8pKakffvjh4MGDHF+unCo43OnTpwM7kBO8evXKrgZiAQAAgABJREFUxcWFi4tr/vz5SBdomVJxsKqrqy0sLMhA2hkZGeRux+rVqwsKCmjN73ACKd65c0dGRgaccezYscrKSh8fn6FDhyKnSklJabs2x4mTsQPWuG/fvr59+8rKyj5//rympmbRokXAjo0bN/7p0B1U7VPADrAjsCM8PByOeNmyZSjflStXAitpzkfFwWpubt6+ffugQYPmzp2bmpq6ZcsWPj4+LS2tvzQ+OlU7EVJiMjiYtLR0bGwssGPt2rXc3NwrVqzIy8truzbHiZOxA7p+/TqMU0hI6NatW7W1taQPraWlJTXUDqqcnJyJEyf+/PPPERER6enps2fPhq3u3r0bTpliBxUHq6WlxdfXFwmxvLz806dPQ0JChg0bNnXq1Lt379J3AnQ41dfXOzg4DBkyhIAj3BpSYi4uLnd3d2RTbdfmOHE4dpSUlIwdO3bAgAHBwYeqqqrU1dV//PFHc3Nzih0dVDdu3ABH8vLy3oy7GRkZKSoqKigoGBUV9cdDwFFRdXS9ffvWz88P2CEnJwfsePTokbi4OA8Pz6lTp2gfrg4ncMbSpUv5+fm3bt1aXl4eExODbGr48OFnz579gyFGOEYcjh2IRvPmzfvpp5/s7OzKysr09PTIcOm010NH1MePH93c3Pr06TNz5sysrKzdu3fD7U6YMCEjI4M+3qbibMFlBQQEDBs2TFZWNiUlBQmVmppa7969t2zZ8uWwXVTtX0h6r1+/jnIERB5l3nEfGBgI5pCXl4+Pj+8MTeM5HDsge3t7MkpYZWXltm3bunXrhtL99mPsUP1zNTQ0KCsr9+zZc926dbm5uWZmZj169EDJVldX0ycsVJwtZFDHjx8XFhaWkJCIi4uDLTg4OAC7FRUVKXZ3LL1588bX1xecIS0t/eDBg5cvX1pYWPDy8i5fvryTvFiK87HD29u7a9eu+vr69XX14eHhgwYN4ufnv3//fmcoXU4SwCIxMXHIkCEC/AInTpxAwicjI/Pzzz/v27fvvxjtnoqqXQn1PyEhAYFq2LBhqPO1tbXnz58XExODQ4uMjKS3bzuQysvLkSxxcXFZWloWFhbClc2dO1dQUNDDw6OT5MOcjx0+Pj7AjiVLlgA70tPTYbd9+/b18/Pj+CFZOExwrCDIAQMGyMrKJicnX7hwYfDgwfjaSW5LUlFVVFRoaGigzq9duxbhKi0tTUVFBdHL2dkZ4Yre8Osoun3rtqSkpICAwKFDh4CPISEhYmLjJk2aBI7sJM10OB87goKCkBMjVpWWljY1NZmbm/fo0QPW+10GFab62yooKICTBTJaWVqVlZbZ2tr26tVLUVER/peWI1Vn0Lt378gL4ebMmfPo0aPq6uqtW7cieiFXTkpKoq2qO4SQ7rq5uQ0ZMkRBQeHevXtwX2vWrEEh6urqpqSkdJK+DpyPHTdu3IChcnNz3759G/EpPDych4dHSEgoNja2k5QxB6i1tTU0NHT06NHDhg27dOlSbm4uOLJ3797wwo2NjW3XpqLiUN25c0dUVHTw4MHHjx9vaGiIjIwUFxcfOXIk8maOf5EHBwgBKC0tbd68eXx8fJs2bcrPz4+Li5OTkxs0aFBAQEBdXV3bDThUnI8dpaWlMjIyXbt23bFjB6JXUVHRtGnTkChv27atk9zR4gBVVlaampr26dMHiR1sFewIzztq1KgrV67QQQuoOo9gCIsWLYIhIEUuZYSv/fv3x1d626/9CxHH399/zJgxU6ZMiYiIKCsrc3d3Jy8WvnH9xvt3neV+FedjB0p648aNPXv2BFRWVVV9+PDBwcGhe/fuUlJSWVlZ1FDbvz5+/BgVFSUsLAz3evTo0fLy8rVr12JaXV2djolO1alEWjgJCgpOnjz5/v37zc3NS5cuRRKlo6MDb0Zv37ZnwVM9efJES0uLj4/P0tISvuvhw4eysrIIRqtXr87Ly+s8wYjzsQOFjZx45MiRKOzIyEh8TUxMHD16NAo7ICCAPhBt/6qtrTU2NoZvlZeXLygouHv3LnIFbm5uHx+fpqamtmtTUXGuEJni4+NnzJgxYMAAX1/fyspKe3t72IK0tHRcXBztz9KeVV9f7+7uPnToUFFR0fDwcHw9ePDgqFGjhg8ffvr0aRBk2w04V5yPHZ+Yd/3NnTu3Z8+e1tbWDYxAl/gqIyOTm5tL0+X2LHAhTBSWyc/Pv2fPnqqqKg8PDx4eHnFx8YSEBNqHhaqzqby83MrKCkmUhoZGSkrKjRs3QOGgEAcHh7Kyss6TMXcsffjw4datW0icyBMxxJ3MzMwlS5ZwcXHp6uqmpaV1qjtVnQI7WlpavL29+/XrR+5MIlZdv3594sSJmOPp6QkKobbaPgUifPr0qaqqKkqK3EaGfcrJyfXq1cvW1hbpAi04qs4muK9z587BfY0ZM+bQoUMlJSUADkFBwXHjxkVERNCXE7VDwY8VFBRYWloid4L7io2Nraio8Pf3FxYWRkLl5+fXGd7D8qU6BXZAiF4wy549ezo5Ob169QqosXnz5j59+kyaNAnpAn3U0j4F49y0aRM3N7eYmNjly5ebmpo8PDyAIEJCQnS4DqpOq9LSUmNjY9iFlpZWampqcnIyJgYMGIDPxMRE+qilXQkUWFlZibwXkAFSDAwMLC8vv3nzprKy8sCBAw0MDBCbOtWtjk+dBzvevHljbW39008/SUlJZWZkophhrlOmTEHevGzZMjqGRztUS0sLkrlRo0aBM7Zu3VpVVZWRkSEpKdm7d28rKys6IDpVpxXcV2hoqIiIiICAwP79+xHGTp06NXnyZCTTdnZ2OTk5lMjbieCjGhsbQ0JCpk2bNmjQIEtLy2fPnhUUFNjY2KDsxMXFUXCdcAiAzoIdEPLj4cOHd+/enYxBCxCBxSJj6NOnT3BwMDXU9iPYanNz8/Xr18eNG4fS0dDQyM7OBnZs374dKR3ca2xsLO03S9WZBdQwMTGBOcyZMycxMRFfXVxchg0bRsbYrqiooFD+3fXx48f6+vrz58/PmDGDi4tLTU3twYMHNTU1e/fuJcjo7u6OguuEJdWJsOPVq1ebNm3q1asXjNPNzQ05ASLZvHnzfvzxR2Vl5dLSUtq2tJ0I+H/p0iXYKphDQUHh7t27KLvw8HAxMTH4WScnp7q6uk5oq1RUbMFZRUdHT58+HTZib2+PBLq4uNjY2Lh///7wb1euXKEvKvq++vDhQ0lJydGjR8XFxeG1EGJiYmIQcSIiIqSkpHh5eU1NTTMyMjrb4xWiToQdCFQIYFOnTu3atStKfffu3QBPV1dXbm5ugKePjw8lj+8ulBEhDFlZ2b59+8KrIlcAhTx79kxTU7N3797I7Tpbq28qqt8U3BcQnNUhc6zo4cOHEdKioqLIU0gLC4vk5OTXr19TOv/2QhAB8wEpkNxOnDiRh4dHVVUVRVNZWXnt2rUFCxYg4iDdxTQKqO3GnUOdCDugly9f+vr6jhw5skePHpMmTbp9+3ZmZiaoExSCFMHFxSU/P58+bfkugn98+/YtyO/MmTOkQyCY49KlSxUVFXCg1tbWsNUhQ4aEhYXRd/hRUX1iwhtwXEdHZ+DAgbNnz46Ojs7Jydm2bZuIiMjgwYONjY3j4uLq6urg0Ch8fBvhOgM4CgsL4bgMDQ3JMy9dXd1bt25VV1fjU11dHYUF/3b27NnO3IOyc2HHJ+aNYu7u7hISEt26dUMleP78+YsXL8zMzBDVUCHWrVv39OlTVJ1OWyG+i969e4dUAGa5adOmESNGoCAWLlyI/ACWCQNWVlbu27cvbBhL6eMVKiq2QB5ImmVkZGAg2traSY+S4N+8vb2RZHNxcYFFgoOD4dBgR/Q+7n8qdtYUExODHGns2LEIKIgyW7ZsSU9Pr6qqAhSqqqpi5pgxY/bt29d5Xr/ym+p02AHza2pqQsEjvMFW7e3tUVeys7PNzc0FBARgqwYGBgh4ZBj1thtT/auCreIiV1RU3L5928nJSVZWFsDBx8eHROHOnTuNjY0JCQnz5s3r2bPnpEmTPDw8UEz0XhQV1Zd6/fp1YGCgmJgYDw/P+vXrMzMyy8rK4N/k5ORgTQhyOjo6x44dy8nJQTZF4eNfFy5pS0sLgsiNGzccHBykpaXBFoKCgqDAM2fOFDPC9UfuhAKaOnWql5cX0LCTB5dOhx1EoAo7OzuY5eDBg318fCorKouLihH5QKn9+vUTERHZtm1bampqfX09vfPxXwi22tzcXFRU9ODBA1tb2wkTJoA2UBbTp09HKTx//hyuE4vU1NRQHCNHjoQB04yNiurXgncqLy93dnYWEhJCtIM1JSUllZSUXLt2zcjISFRUFNFuyJAhy5cvj4iIyM/Ph9118pj3rwjXEKGhpqYmIyPj8uXLyFrHjx/Py8s7dOhQRUVFPz8/zIcTe/Lkyfbt21EKYJEZM2acOHGiurqa+rFOih2fmCF3YIp9+vSBrQYG7qmsrETecPLkyVmzZg0aNKhHjx5KSkqoPVeuXMGa4FkKH/9c5FZkbW1tVlYWLvXq1atHjRpFHqDMmzfPw8MDwEEakLq7u0+ePLlXr14Awb1792Imvf5UVL8pQh7IlGBHwHRVVdXDhw+/ePECPi08PHzFihWIiP379x8xYoSpqenp06fT09MR/N69e0dt6q+KNBcFbTx9+vTixYtwU3BciBdcXFzIjhYsWODl5YXLixWys7ODgoLk5ORw5RFidHR0bty4Qd95TtR5sQMmh/CmoaGBsDd69Ohdu3bhK6z3/v37bm5uyL8HDBgARMWEtbV1ZGRkTk4O4iX4A5xLzfXrRZ6k4LrV1dXl5eXFxMTAVrW1tWGlMEjkYWqqaoEBgUgLGhoaysvKwXmGhoZ8fHy4/tLS0oGBgXQQAiqqPxYMJD8/38bGRkxMDA4NNG9ra3vv3j3Ev6KiotDQ0CVLlsDLweKEhITU1dV37NiBKJibmwv+QCx8//49dWu/KeK+kCzBOyH/TE1NjYiIcHFxWbx4saio6MCBA3l5eXHNkcEeOHAATgzXMy0t7eDBg8uWLcOlhhPDag4ODikpKbRLM1udFzs+MeianJwMDgVeoPbMmjUrICAACTfg4/Lly+vXr589ezaiI5YijQDJorahzqHmFRcX19fXI5S2trZiJ9RcvxSuBq4Jcqnm5uaqqip4Q1zkCxcugDZUFqnA9yE54OfnRwamq6uLtAwswgKO8vLr16+vW7cONgxjxmoWFhaPHz/GTug9SSqqPxXMBIAOxwWmh7/i4eGZMWMGEm4k35iflZUVEhKir68/btw4AQEBGKCIiAhi5+bNm48fP37z5k2sBreGzIpQSGd2azhxOHZcB1wNQBscPnIhhAYzM7OpU6eS11KSl+AsXLjQ2dkZjqugoKCsrCwjI+Ps2bPImhA14MSEhYXh4uD66IOVNurU2EH07Nkzc3Nz1CFkCf369YNlwg4BH69fv4YdIi5qaWlNnjx58ODBvXr1Qrowc+ZMrO/n53f16tWkpCSCKU1NTSDiTl63iK3Cx2VmZiLTOn369NatW0H90tLSvRkBOMTFxTU1Nbdt2xYbGwurBlXAYklrLFgpGcxNQ0MDlx1m3MmvJxXV3xCCn4eHh4KCAg8jNTU1Hx8f2CPcVGVlZVRUlJOTE4ADHg8+DQ6tZ8+eI0aMUFRUhFvz9PQMCwtLSEiAW8PKMM/O0/8W3gY+nNyUhWMnmRJQQ05ODpcRrgmfSIemT58OmHBzc4uOjikvr0DKhDBx584db29vXGqkr3B0QkJCgL+TJ0/imtOWNL8WxQ4W2yLChYaGAjhGjRrVp08f1C0QKxgW9Qk8kZ2dfe3aNdQq1KRJkyYNGTIEdELevT5r1ixUNVtbW4TJmJgYcHFhYSHYFuZK7ltycNJA7mrgHIEaOGXQw8OHD8+cOYP8CddERkYGlxG2Su4V4Vrp6OjAG+IqIfGCbeOaP378+NSpUytWrJg2bRrcH3KIuXPnBgYGwm/Ssc+pqP62Ghsbb926tWrVKlFRUWJZoBDQxu3bt0tLS6uqqkjTBF9fX6CGvLw8GeoDa3JxcWEac7S1tJEJIHDev3+fNBMhPo2THBrxYDgpXC64o/T09OjoaFwTZEpz5swRExPjYgSHP3HiRFVVVXt7+xMnTuAa4oK8fPkSTg+XER7MyspKVlYWFxm+DhEEawYHB8PL0RGGfk8UO/4nVD7UvLNnzyIPIM0OYLHGxsaYk5aWRmwVkTU5OfnYsWNA4JkzZ5J3T/Px8SGJHzp0KKYnTJiAVH7Tpk2odnFxcU+ePAGyYLeoo69fv0Yo5QDyRfYDcwKN4bxgWomJifBNQI1FixbBUJE24YLAAoWFhadMmbJw4UJHR8fw8HBcQwJkuJLE5cEnguFw3QBww4cPnzp1KsAOO6Rdh6io/rngapBqR0ZGrlmzBgm6gIAADBMOytLSMiQk5MGDB7m5uTDGoqIiUD6SdSRO1tbWysrKEhIScGXkSSgMc+zYsYqKiuvWrTty+MjNmzdhvHCD5FlMR3Ro5AEKnAx8cklJCVADjjooKMjU1BRXCScO/MKFgh9DpoQsyMbG5vjx40iogBq4nhUVFfn5+XDsERERwDI5OTlcIl5eXqwP8kD+efXq1by8POrE/lgUO/6PUFdQsY4ePQpiBbcS/J8xYwapfAB/WGlDQwPAH1UQdRFQ4uzsvHLlSuT3ABFirtikX79+qLuTJ09G3N2wYUNAQABCLzAZtZxNIQCdDlQ1cbRADSQ9qampyAkOHTq0fv36+fPn45QHDBiAC4XzHTdu3Lx585AruLq6Xr58OTMzs76+HlvhfOGq4OlCQ0Phv2CfQA3SnhTT4A9QGnZLkwMqqn9X8DBwNQiTTk5OSkpKSKjgnWB6yBCWLl26c+fOc+fO3b179/nz5/B7dXV1+ESGgJnbt29H0oW4i+RKUFCQbAV2gcEaGRl5eHjA9ZFRnrEJbLyd94vBsREPRpqFXrlyJTAw0MLCAk6b3OYZOHAg0AHkgbwRHhsh4PHjxzg1+C4knLg+8fHxcF/u7u7wb0iogBpsD7Z8+fKDBw5mZmTS0ei/UhQ7fkOIf4D6I0eOmJubo4aB+mF1w4YNA9sCivft2wd7y87OJpUSFIIkHmk6oOTMmTNI2a2srBYvXoykAfUYtRNRGVFWSEhISkoKNIO6vnv3bhj2vXv3cnJyEMgbGxvbYevUXz73QAE6APBxyjDFjRs3AqQmTZqEa0KG9ho9ejQyJKCDl5fXxYsXYdIAMlwTbIVkIjk5mdzLhQuTl5eH1+vbty+2xR5MTExg+bgIyJzgEdr+PBUV1b8k2DI81aPER4cPH7a0tIQlIkeH/cIvIW1AWrVkyRJ7e/tjx47dvHkrIyMTJgyfBvsFryB/gJ2uXbt20aJF5BEzGdAZhg+HpqGhYWdnh7Th+vXrBEHgzchdkO/uytgeDB7mxYsXd+7cwXFaW1vDg02YMAH5ITwziAonhaRx06ZNSKXi4uJAGOCMmpoaOD2SKTk6Ourp6QEvkIhyM8LVmzZtmqGhoZ+fH6ANmeTbt2+/+/l2IFHs+G2hDsF4UF/B/gEBAfr6+pKSkjA5VFbQMRhCRkbG0sJy796958+fR4UmD1OqGMH2ioqKAC5RUVGg45UrV86fPx+bI+hiW9IKGhAjLCwMjoE9w6ojIiJQfbETGDxyjjdv3nwvuyUnDmjAKSQmJoaHh+MU4HFwtOT0kfGIiIjACEESu3btunnzZm5uLg4bJw6EwlbIEoAaQCsDAwOkSjBRnC95DgVrX7Bggb+//6NHj7AyHQ2FiuqbiWT8cC9wTSCMNWvWwK7hl5AdwSnBrmHgxLPZ2toiQl+9epU8Gy0tLcUnIndSUhI2XL9+PeI04i7CMOkUgw1BIfAJ2GdQUBA2BK9gEwRv0iLkW5o5+8YGnFJKSsqFCxecnZ3hgceOHcv2YGJiYoqKivDMOM2HDx/CgxUXF+M08/LygBphYWHwbOrq6uPGjYOjJueIq4QUFCe+YcMGJI24MvDzdODXvyeKHV8lQEBWVtaePXtgkKivJAbz8vL27NmzW7duXFxcIGgbGxvU73379iESA/xhdYjE5AkohAp94sQJ1GZkFUuXLp0+fTrZCWkj/fPPP2MCIXndunXubu6XLl3KyMhACIePwLbfpma3trbCTeA479275+XlBZuEK8GBkR4o8EezZ882NzfHIriVmuoaAD7ODvQAo42Pjz948CDSAmwFk+7Rowdp+A3mmDx5sq6O7pYtW4BosHDaeZ2Kqj2IJBhI7o8ePeri4mJhYTFr1iwSmwcMGAAT7t69u7S09KpVq2DXhw4dglsgwwc3Nja+fv0a02fOnPHw8LC0tJw7d+748eOx4cCBA+ESYfvi4uJGRkZOTk4hISEJCQn5+flwZf9pXz9yOsSDwc8cOXIEfAA26tu3b58+fcAN8GDKysoAIxzzjRs3qqurCZoAp3Bq8Ntbt241NDSEy8KJE6cHrsIVQH6FE8FVglenD4L/FVHs+GtCzSbtiYitLl68GACBCj148GDAB0wOFosEQkVFBWxhZ2cHy7x//z4YoqSk5OXLlwi6sD1M37lzBxgOY96+fTt2AvPATmAbsBDsBJEbiYiBgYG7u3tsbGx6ejqi+3/Unx47xJ5B+rBV+BcdHZ0ZM2bAccBccVKABpyIj48PMoBHjx7hFEi/FaAGvMnJkycBW9ra2siQABm9e/XmHsg9fBirfaimpqarq+upU6dIh6AO1/SMiqrzCC4FMJGUlHTx4sXDhw8jYM+bN2/ixIlDhw6FQ4M3gGuCW9DQ0ACdHDhwgGRWQJCGhgZE+pSUlCtXrsCbbdq0Ca4PGw4bNoybmxvBGywiJycHV+bp6Yl0BckbvMe/ezf3I/NWFDjV27dvI/HT09NTUFCA7+rXr5+AgAAAaNmyZeS59uPHj/HrtbW1wKDExMTTp0/DfcFT4dRY7qt3b3wCNeCN9fX1kSKGhoaCSOioG/+6KHb8HZGnhgjApDfH3bt3SRNL1GApKSlAA6yOj4+Pl5cX7IyvqNYI3t7e3mfPnkU9fvbsWWFhIUiivr4eRotpgAV2EhQUBLJGzkHsFjZAiBubA9KRN5CnOVVVVcg2/nl/euwBGEHGKTc1NYV94oBJ33RZWVnMQcYA1CgoKMChwqrx0w8ePMDKcC7wSuQOJJwLbBvMNGXKFC0tLWQMwKm0tDRcGRwkNVcqqg6kX5iOHqQNOII0siZ7e3u4NURiYWFhQUFBHuYNL5ieOXPmypUr/f39o6OjkYkhD4HJI3vJyclBAkMapaqrq8MtwIOR3m1wLHPmzNmyZQtyGJDKP39I8ZF5tRMOFdBjZmY2adIk8kMjR46UkJCAy4VHjY+PJ89Q2KNxuLm5IdPDyqQTCgAFp4PsERnXjh07oq5EkQHW/uGxUf2BKHb8a/rIvNsWHE16o8G6QNkIzzAAmAHMtW/fvlxcXKSr1fLly2GWR48ehdEitMMwABPYHFZEbjwcPHgQOQfMY/Lkycg5sCE2x7bYoaOj4/nz52G3dXV1f++5KZiJtG+HWSI76d+/PxlQee7cuYCnc2fP5ebmISfAkSQnJ0dFRe3fv9/KygpLgRdYk7Rph90qKSkZGhru3LkzMjISUILjp4ZKRcVJgq9obGyEdcMP+Pn5rVq1atGiRaAQ+KKBAwf27t0bYRsuTltbe9u2bXBo169fR9aBLAU5FdIVuKnQ0NCNGzdqaGggtJNBj5CrSEtLb968+erVqwCCvxHgyT1auE3sHKkaGfIAHmz+/Plwj0iN4LjgUcvLy+HlkOy5uLjo6+vDZcEP4wCQzo0fP15RURHbenl5kXdf0JfkfTNR7PhPRDAcyPzs2bPY2NgjR444OzsDNQD748aNI09PIfDExIkTFRQUsMjBwQGr3bp1C5uQTrbV1dWZmZngkj179tjY2MBIREVFSdcYERER8Ievr+/9+/dhXV9/54PcTSV95JCLgB4ANDgkExOT4OBgmCiOubKiEkZ76tQpW1tbgAXsk9yxhK3id3EYFhYWu3btQoaRlspqV/W25W3bn6GiouIssRtPZGVl3bx5E+7C3t5eS0sLCELSKlDIsGHDgCDgkjVr1vj7+8P1gVfgx0gfVDi33bt3GxkZYRM+Pj5sgpXhZOBJkK19/VgXcHeFhYWACTCQmJgY6cgKb4YkCh4MnhMurqCgAJ4TCIKDQbIE0CEPUJDyIRt0c3O7cOECcXe0E8q3F8WO/1wfmReUkCYRebl5pJ8tuXUJGyAPUAET/Pz8ZMwxOTm5lStXHjx4EKnAw4cPYWCwDYAIeBx26+HhAaIXFhaGIYEDwO/gFVBCXV0drLHtb/9fwbqQfyAjAf3AQcBNwCDxWzdu3MCvIDMAjgD8N9tvlpKSwsHANYBLJkyYIC8vD2cB+omLi8vLyyN9bf5qgkJFRcUZIgiC6A6nkZqaeunSJVdXV21tbTg0cAD8ElwH6fFHXmUFr/L06VOsDGopLytPSkpCGgZHJygoiDXhzZD2REVFlZaW/jEEwOc0NDRgc6RhSIHgA+EwARZwTcjQyLgjjx49AhItXboUSRoZ9Ix0kd2+ffvFixeR1JF2cvTGxncUxY7vpvfv3wNEUlJSQBjr1683MzNbvHgxrIifGWS3Z8+effr0GT58OAySNAqBXVUzr6sGziM/sLOzAw1gzW7dusnIyCCNgGH/7zHHb5ktGYYV3qFfv369e/eePHmysbFxeHg4NkEucv369Z07d86YMQNLScNvZCRADR8fH5hxbW0ttVIqKqrfE1gBxICIfvv2bTgZS0tLdXV18AR5Rwkz6KLMxo0bkXGRBvLNzc3wV76+vlpaWuQtCmAU8tZcgMXvpTQAl8OHDysoKPTv3x95ERzmqVOn4NbgxJA4AW7s7TcjkYMHwy+Sd+06OjoiecPP/QHNUH1jUez4/vrlc0fzFy9egAP279+P/AAIP3bsWDKqT9++fTFBxrQBHyAnaGaUkJCwdetWCQkJrMDDw6OqqnrkyBHs5NeD5cGMHz9+DKvGfgAWMPXLly8jMyANsjw8PCQlJbETJAfwFEgU9u7de+vWLUobVFRUf0mkuT1cUG5uLrzZrl279PT0xo0bx8fHBw8zcuRIuCmkTEAEeKr6+vrs7OxDhw5hpoCAwIABAxQVFUESebl5gJgv94kdAlbgG8XExLAfcXFxwA0IBpkY9gCaWb169fjx40Eb2M/06dPXrVt3+vRp/AQdHKgdimJH+9IvTEty2Bh7EN/NmzcrKSlNmDAB5kReNWRoaHj8+PGUlJSampq6ujrAh5GRkaioKIx28ODBMOALFy4AYtjGBi+Qn58P8CdNQTU1NR88ePDy5Uv4hbNnz2poaPAxkpaWRvIRHx8PHPl7LVWpqKio2PrIdG0FLsDhwLfA1YAM4IJIdznkUcHBwYAG+LHMzEwnJ6dJkyaBG5AamZubJycns8kDO4mOjl67di2SIjixBQsWREVFATiw54MHD86bN4+MBw3fiOmgoKC0tDQAzZ8+dKb6XqLY0d71CzOw8cOHDz09PWG3wAtYZp8+fUAJXl5esOeqqqra2loAiqmpqYiISPfu3ZFbRERENDQ0/MKopKTE1taWvN56x44d+FpQUAA00dHRga0idZg6daq9vf2zZ89+794mFRUV1T8RHBE8Emmdpq+vLy4uTkZn19PTQxIF54Ns58yZM0uWLCEvYwNkZGRkkMbygJKVK1ci6QJzKCsrx8TEkPGj4QCBL/CH4BWkXidPniwuLqa00f5FsaPD6N27dy9evAAuWFpaSklJkTcjyMnJgSQIfBQVFR09ehQ4AsKQkZEJCwsj70bx8/MjowJv3boVtv38+fONGzeKiYmBXcaMGWNoaIhMAgkHZQ4qKqr/Wm/fvoWnunz5MvzYxIkTAQ3wQrq6uuHh4UiHwB/r16+Hv+Ln53dycsrLy4Nnc3R0HD58OOYYGhhGRkZizp07d8AZxK1pa2vD1wE46KudOooodnQwffjwoa6uLjk5edu2bRISEqTROGDf1dUVPNHU1HTx4kUwB5Bi8uTJp06dOnHihKioKL4iXcjOzoZhW1tb8/LyEnM9f/58WVkZbcBBRUX1LdXa2gp6AEPo6OgICwkhgxIXFw8KCgKRZGRkmJubCwoKjho1ytnZ2cPDAx6Mh4dn1apVrKcndfXIstTU1HiY92va2dmlpKTQMcs7lih2fK0+fGx9+/bV23evmb9XLfhjfWXPYf8x83970eu3b1//n0Vv2Tthb/KbX/+3Mmv9lqaWt82YeP268WFi/Nat9nPmyPPyDhAcMmjdujXPczKaX728EnVx2rTJXAP6iIoKTZaYOJC7n7y8TGpacn7BC0dHBwEB3pGjhtltXF9eUcz8etuDfPf57/Mc9jptVv7/8784ZWaCnBfz2dLS/Bt/b/HZ9Abn0tL8hvXHmn7zhvlknWDbJrFUVFT/UG/ftrysr29gDWvR0NCISeQvtY0NLzEfamxsbG5qev/ubWvr+9b371mfn/8+/N+/jx9af/Pvw+e/Xy/6+PHDxw/MH3vi8yZ1NTXnw8I0NTRGDB82XUrqwIH9FeVlqalPLMxXDR8+BH9Dhw7m4+PW1Fx8+/bNmpoqfC7R1hQUFJg6VSI4+EB5ecnbltfv37W0vn/7ofXd51/EQbY9bPLX+v7d+/ct8MNv3jS/asYpNzQ1NjQ1NTQ3NTY14Ro04K+5ufH1q+bXr+FjX715/brhZX1dbQ3+6utrm181Nr9qwuer1/BXr969a3nbglWaX71qev2qqQVz3ra8f/8O50U92B+IYsfXqv5lUUra1bSs2NTs608yYh4/i05+Gp2SEZOaeS0183pqxvW0DHzGPnkWk5x+9ckzLLqWlnU9JfN6CjMffynPrqU8wwrRqZn4Y319kh6dnB6VgpVZf9dSM2LSMmKeZlzDX1p6zNPMa+lZMU+fRT/NiMXOU55GpaZHPUm7nJx2Oe1ZTMqzqKSUS/cfnj951td6g86K1fNXrVl4+tyuRykXE5LCvf1tTMyUjFYqmVmomK9VP3hkB37rSsyhLdtNVq/RcHSzuH73NM4lLeva0+wb6c9vpmfFpT5jHSRmPsOc7OvPsq+nZ8WmZ8emZsakZeJc8BmD00zPvvk06zrrOrC2jWWdflbMk2c4tqs4/pSMq09w+unXcF5PUq8kp155lHzxYdL5h48jHj7GxMXEZPxdSHxyIeFx2P3EsITHEfcfhWMi/uG5+AR8ht9/GPYkNa61laYvVFT/ppIfPwr08z18YN+pk4ePHd8fELjLz9fj9Inghw/v3b9/9/jRw+FnTuVkPi0vzi0vYv6KcytK8vBXWZpfXZpfVZZfXZZfW1H0sqasoba8obaiEX91FZh4WVtRX11aX1WCz5fVZQ015Y015axPZp2musqm+uqm+qrm+urml9XNdVWYwyyqxERzXSU2jL8Vu3f3Li93p5DDQU9THuRkJV04f8TSSk9bZ46W9mzDZQv8/LZFR5+6FhPq671tpYmmibGmz67tcTfOJyVde5Icm5Vx90X2g5KC1MrSrMqSrIrirPKizNL8Z6X5GaX5mMgsyc/AX1H+0xfPEzMz7iY9ir51M/zK5RMXzh09dzo4PPTwhXPHzoUeCzlxKORE8IULJ69dOx8Xd/nG9St34mL2+Xu7bbd33bbR28sp7GLIxZhTEdHHI6+fvhl/KSXlTsKDazFXz0ZFnoi5ejr+TmR6Snze87Ta6tL37+kgir8rih1fq8KSx2cubDt/dcf5a17noj3OXnULj/a4EO15IcbjYqz3+aseF2M8MH02ijX//FVPLIq45hkW7RGGOVHuETGeF656RMTsvMDayu38VXf8hV1xDYtyDb+6IyxqB1aOuOZxMdot8prH5RjPy9GekTGeUTGekVEekdFel6O9wi5tP3/FJSLK9cKVHZewE2wVuT0Ce45y+3/svXV0m0e47rvWbcPsMDMzQ9OkaZumadowgxNzzCiymBktWWa2xcySOXbMduLEdsxOTInDTG32nc/d+9y79rq9J+ec/qlnPWvWaDSSPtnJmp/feef9lEZcnhaRrYxS6sAcokwXm6tF5qiQeWqM1kIxOBjWQq61kGdyMPV2mtpKNbg4piK+wcXSOxnmQp6lUGh2CjUmplJPVJkIOjuYwLAUsK0FbNDqnODiweURdA6S3sEw5/NM+RxTAVvvohnywXen6VwU8JTBRtHbyRobTm3B6uw0jYWi1OOVBmyeFp2jRuVqkLlqRK4GJTNgM1XwTBUsUxGZLovKVCLSFbBMeXSmHJalQGTkwTJkMIMl7sPHV//9F+CWW279H0iel3P+1PHggKvBgZf8vE9cOHXQ58pxIjqUTEIgEGFnjh3297xUYNE21ZXfrbtxt7b0bl1pc0NZy63ye7cr2hor2+9Udtyt6mqufdB2u6/zbn93EzBY13s77z5ov9PTegsa77jT39k00PU/3DzQ3fKwp/Vhd+vDnrahB+2PIbcOdjf1toPJjaDtbb91v+1WZ3NNR1Nl652y6pvmPBlfIELQmSFEsi+R7EOm+NHp1zmcMA4rUsBFxMfhpMDxxJRkalIiITkVl5IRm5GNys6NVaopOh1bq+Kq5TxVHl+ZJVCmCVRpIkWqQJEmVGSJ8vI4yemxCanRInE4mxVKwl3HIvywsb44jC8W6YeEBwSHXAsOu4YmBNLZEWwunEmPZZHRh77bunq+x8q5k7ZtWnQt8EQ08Vos0wfHCyAJgthx0URqIBxxFYm4ikF5s2jh6YkUrVx8q7bw9atn7oDHP8mNHV+r7gd1WUpEjgapMjEUZprCTNTYqDo7G6CDBrCFmaa2QAwhMxHAuq6zsQBqKM1EuZmgtJBUw5NVZrAq/z2NpDThFcBmMJkEmAN0NDYSWO8BWGjNRK2JaDCTIeCwMPRGqsFEM1jpKiNOqcNqoQmAPKiADwB2gIeAM5QmrMocqzDE6qxUjYkg12NURqLaRAYzdXaKwUU3F7KMLrrBQTUBVnDRAG0YC9hGF8PgpIHW6GKZXGyNmao2EmR6uMyI0oKZ+SyLC6APYA6K1k7W2sGlYlQWjN4GRqg6B1llw6usWA1o7ViVBa02YTUWcBlIhQkuNyAVBrTMgMw1IGRGTJ4OmwMwSAPP1sTILNhMfWyGFpWlw2RpsRkaZIYqOkMZlaWA56gRGYroLGWMySF2Y4dbbv27MmpUiGDfgNOHL/+06+S2VSd2rfQ9/ysGFRwV7n3kl70Hdm89e/SITpZZX1Fwq7LwdlXRnZriu7UlTfU3WhrK2m7fbG+EsKPjbnVnU013S13PvXrgbsgNwD2tDffbAEM09gMiAeTR3QzYYhB0OpvByCBAkM6m/vbG3taGzrtV4D1LCowOu8pgyNHpspTK5OwcYV6eID2bwY6DRcVeC4m8FBbhGRF5GYG8hsb64AgBOFwgiRDOpaOFLDwH0AALzhMi+HEwviQqLilSmBAqTAgDHXFKTFwSkh+HZrNj6cQYOjaSjY9h4WPYRISAgZGIyfFJBGkqTprKlCYLRCKakIdJTMQkJKKkEkxKIkkiwokEOLYwhsDyY4ki+XwcLDxg+5qli6aOXeAxetuWJVcDTvhFnQ1BXYCRrkXjL6FoV0nsACothE6JZNJjqLRwDM4Hgbyclxf38GGvGzv+SW7s+Frd72+Q6fFyPU5npetsDLUNogeNlaGCwh400FGaKUoTYAjS39EL0FEYIeZQWggKMx5ghxpMBsxhJg+bAFhENUwkf89XW0haK0VrIWstUEhDb6EZbWy9la63Mgw2ht5OV5mISgNObybpTWQ9oBwjWaFHq41YlQmvMeN1dqLWSgTYAUBEZcRrLGSNiaw2EvV2qt5B0TtpJifd5GSAVmMlaKwkvYNqdDIs+Syjk25wUiwFDIuLpXfQNDaC3kU1F/It+Vyzi2kBr3LRjPkMMKgyY5QGtN5G1dsoGisRoIkGwhGy2kaQG1EKAwAytNoKaAOWq4XJdEi5GZVngiltWKUFrzTjFUacwohR2YhKGyXPRACWW8g5RkymJjJHHZOjjMlURqTKQ7PUMSanxI0dbrn1L+nvxe+LRZEb8sfBcxtXH1259NCqxduXzd60eu76lfO+37l+/87NP+zafPzwwaxEcfUNJ8COW1WFjdXFTXWlgDlaIeaoaIcCHlUdd4bJ425NZ1Nt593ajru1wwjSACFIS11Xc21nc21Xc01PS01Pc3VnY0Vr/Y3mmqKmmsI71QV15baSfLVem5qVJWFwSQQ6msTAUFg4ChuHpcSgyRGxlFAsIwxHCyfTEBQqhk7DUilIOg3F4hLYfApPwOBxqTwWmcnE0rjRJP51LNsbx/alCoPocaEUQSSOGUVgwyk8LJ1HZLAIdAKKhoYzMXA2HkXHI5k0rDCeJU0XxKfzUmTJOTp5hiw9PUOckEATCDDJCeSMJJI0LjYtmR4vJrOZKJEQL+TivS6eXL1w1pzJo2ZPGvnTgQ0wtDcM54elBvLFMXxxNFMQwhNHioRIkQDF4cHQOP+g8As+gaelSZy+vvtfvrjPBv5/y40dX6v7/Y0aC0sFhRCoejtTYwcMARZdutbOUAPIMJOUJqLCgFebiAApFCacwgTWWqLGCkGG0kxQ26BQhwJwCcQcVKWZpDARoJeAFpoP9QGCQHsidjogG7WRAsVRzBS9jQYGdTaa0kRRGohaI8lgphosDI2FojAADsCoAW3YKXo7CdCA0crUGqlKHVZtxCn1WKUBCzjD6KRpbWSji2EuYBtcTB14fzvd6GKZXSyAGuZ8pimfZilgWfN5JhcXTDAWMiyFHIg5oAlMg4MI7bDYiQYHxeyk24vYYL7OQdY7GHoHQBbordQQgQG0wmpsOIU5VmFGQ6hhweYaYWobBowrrGi1DQ/eQQv9HMgKM0ZujFWawU8Jk6eDa0w4uR6Zo47OUkfnaJGW/MQPH1//91+AW2659b+s//qD+8uXIouFDUMgvH2DLlwO8/LZun7tpAljx4wcsWPT2p++23Zg95bDP3wvYJDK8k11N10Nlfl3aoqb6yHmaLtd0Qrts1S03xkmj79jHn+TR1Ntd3MtIJLb1YU3iowuu0qvzdSq0/PtypuFOqc+R5Yal57AzEjmpCbz4hM5AilDmMiKS+EzRXQCA4OnoDAkOJVD4Enognh6XBIzPpWVksJJFzJEGISUSRbzSBIJnS+iswRUtogukLIS04XJWUJJGoMpgWMYAVFYrxicfxTGD4YNQlNgZC6BEUflxDP5Ela8kJEkYKSLOWkJvNSMuPg0IS+BxRBRiGwsiYsTprCTc+NS8yRpOZLMTElGCp/LRBMJkVIJMztdkpshSU9mMymoPw79MH/apKljR04Z++0fR3bxBJEyGUul5OTlkdLSYkVx4QnJiPQsvCQRDviDxgmLQnoFR3imZUkGBt3Rjn+UGzu+Vg8Gm7Q2ocrEUBnpajNbbiLLjFiFGauyEJUmvAoyQaHHK40AIPDDJqqtVJUFyuHQ2qlq298PqRooNEKDwh7DoQ4ltAsDAAUKfqitJK2VDJhDawbYQVYDRjES1eApI0FtoWutLDUYB8xhZxrsDK2NCsaVRui1YNXXWaHwht7E0JlpagMOAAd4VmelGh3Q3orGStA5aAYXA7TmQq65gGct5FkLOLYCDmhNgEhcbHuh0FrA19qJKhta5yQYXRSTi2W0szQWvM6ON7noEKbks4wuqtZB0tgAhZCN4D3tdJOTZXAy9Y6/WYSid1I0NvB1CFooYQWKtQDa0NnAV8Mb7CQjlPYBbQxpLQSlASXXIQAegZkKI0amRSsM4BuRXCWZHz69+e+/ALfccut/X196OtuKnUaTOkcnS1dmJ+3cumHs6FGjR43avG71gT1bf9i15Ydd22LCArXy9CK7rqzAVFFsqy511pbl192Etl0aq0vu1JbeqS25W1vSXFfaVFd6p6bw1k1ndYnZostKSxaI4lgCMTBbkiSQpggTU0XSeJ6AS+PxqRwxgy4gk7lEHBNN4uCESdzknIS0nIT0LIlMmaTSpMoViWlpwuRUfkIyJyuBmQ0P4B3Zh/n9ZyYsUsKmxAtpUikzKYUTn8gQSSjSZHZ8MksYT+SKkFwBhssjcNh4Bg3FYmN5QrJQyohP4cYnseOkDIGEGp9MS8nmpOYKJKksroTEkeD5CUSOGCFMQCRkktNkvGxlXLYsLi1HlJojSc4Sp2SJpSm8xFRBSgqfQUUf2Ld7wvhxY0ePGTNq9B+HDwjYsdnpTJEIzuGFJCUh0xIJmWnU5CQil49iCaO50oi4dJggNcZckPH4Wb8bO/5Jbuz4Wt0fuKO1CzV2vsYu0Ng4eXpcjh4lN6GBVWYcWFY1FoAIJLWVorKS1Vaa2kLV2ZkqMwNK6bAzNHa61sE2uHhGl8BeJHEWS60FIpOTrXcwAJRA7AKww0zUWSkaM1lnoUIJHBYKIAmNiQT4Qwk6Fig7BEpitVG1NvAsXWOmy/VEuR6nNBKhNd5CBZSgt9HBSxSAPExEjZWihwIhFJ2doLUN763YadYCrqWAAwU5nFA8wwwZ6tjz+RYXR28nqy1YjZVocDIsBQJbocRSyDPnM6yFdEs+HXT0Troxn2kuYJgAc9ho4BN1FqbRwQHkAX4IRidtGE0AjlCNVobJxTY6ASTR9cO7Pzoz2WChGqBvR9aZiDoLTmfGgz74pioTTm3Ga6AO2VWS/uGTO9rhllv/ppx2W0RwoK/nRV/PS1fOn1m2eIHHxIlTJk5cMHv2ikULVixeuHLJou9377x87nSQr1dkcAAsIgQVE4VFwghoJAWHphGxDDKeScGzqQQuDZjIoREZFDwJj0bCoyMjw4GjYdExiBh4LDwWF4sj4bA4TCwaPEBGoeDASDwaTyfQuFSOiMkRMpl8BpNLF0k40kSeJJ4jFnPixGxxHFPFI6VdPc7Yux6xbVX0kZ8ExNiUBAEY5/FIHBaexyUKhTRJPEuayJYmsOIldKmImSTiSHlMMZ8mFtJFQppIROPxiDw+UQRhB12aQouTErkCNJOLoLFjWHyYSIqSpmGl6QRJClkkJYNpfDFNEM/ki5nxiVyRhMUV0YUSNpGI+uH73ZMmThg1ctTo0aPPnDpCY8DpXBiFHckRwZJTyVkZ3MwMTk42Pyc7LjmdJUzCCFNRLGmU1p489LTPvcnyT3Jjx9eqp/+WxsbSOXlQGqmFJDPG5hkRgDlkBqzCANZOgs4CoIEMGEIDZVwy1AAarNS/j7QYXFxzYVxRRXbdHcu9jht9Aw0PH94ZGLjd21vX3VPZ1JpfVp3nKI43u7h6K0NnpQPCGAYOyCoDUW0kQQarspmosUAdiG8sDJ2NrTZSFDqcSg8ugKK3UXVWGoQjFrrSAHAEqzRidVYwEzAHeAlGacTobWR7IcAOQAPD0QsXC8CHtYBtcjCg4zOgBSRho5kdgEU4pnyOrUhsK4kzFTD1TrzWgYfSUV0sazHfVsS2FnIANplcHKOTbymIA5O1NqLeRgHXoLPT9DYG+C5qCCmooKM1Uw02ptHGMFpoJitdB67WQjbayAYrWW8iGSw0NRS5wahNeIUBay9Icm+yuOXWv6v01JRdmzatXDh/7bLFi+fMmgqW0zGjJ44ZPX70qPGgHTMaPJwyftxMj8nzZkxbOHPG4tmzls2bu2LB/BUL569ctGDV4oWrFy9ctXjR6iWL1wx79eJFKxYuWLZgwZL58xbPm7t43rzF8+cvnr9g8QLghUsWLVq6cNGShUuWLl66bOnyFctXrly5es2atevXr9+wYeOmjZs2b9q8ZcvW7dt27Nixc+eOXbt37dm/Z6/f8WNJPhf5P21HrpkfsWJO9KYVxNO/sxCwiKCwc6fP/374998O//bbb0eOHPn9j6PHjh07cfzY8RPHTpw8fhryidOnTp49fercmbPnz567cO78+fMXzp+/eOH8pQvnLg53Llw4d+HChUsXL1+9fNXb86r3FU8vz6teV695e13z9r7mBdnb2we0lz2vXr5y+czpE9u3bpk7e87UKR4L5i8An+zpdfUqeN7P2/e6X2BwYHBwcEhocFhYSEhYSGBIUEBwYFBYUFB4UHJ6Sv+AO9rxj3Jjx9fq/sAtlYWmspJUJqLKiJcZkDm6qDwdSm4ggDVeqcVqjODvdYzKggdz1FayEqCDnWEvSSivljfcNnX31L59//jzp5efP734/On550/P/vzz5Z+fXn/+8OLPzy8+fX755FlPS9uNm9UqZ3GyycmHYhsGEnRixUBU6AkKHR5gjd5K0RhJquFPVBjxShNGrkcotEiVHjccFCGoTUSNGYAOW22iybU4hR6ntVIU0DkXrAZcmAkHIMNZLIA4w8UwOZgWJ0Qe1ny22cEE1KI2YfUWosUFBUK0EECQzC4mABRLPt1gJ+shnKKDb6dzUIwuqtFFN7o4BgfXVMA3FwiNLp7RyYTiN3aOuUAERvR2lsJIVhop4GL0NsA3HIONpjMTAWroTGSAGpBNZKOJAjpaI0mhR6vAz9BEKijJ/OiOdrjl1r8maP1z2W2R1wOunT116cTR80eP/LBrx/TJk8aNGjFp7Jg5M6YumDV93sxpC+fMWrdy+eb1azevX7Np7eptG9ZuWb9m24Z1UGfdmr+9ed2qzWtXbVqzcsPK5euWLVm9BMDH/OUL5i5bMHfJvNkL58ycP2v63BlTZ033mD518nSPSTOmTpo+ddJUj4kekydMBZ4yYdqUCdOnTJgx7GmTxk2ZOG7K+LEeE8Yu9ZgQfXCv9OgB0f5N0gOblKcOWK8dM147Rv31+8Ajv/763d4lc+ZMHjt6wqgR40d9Ow54JOSxI78dM+Kb0cDffjPm22/HfAs6344eAQYh/2dn5DdjR3w7dsQIaDL0khFjgIefHTXim1FQ+//MH/nttyPAm4waOX3KZEBjI7/9ZsQ334DB//II4BFQO3LkCMijRowaBXVGQIY06uLFi+5bXP3/yI0dX6sHA7eVZhIU3tDFQrUoNMgsDSxPE5ujASMYuQ6nMYJVH6c0YaEjo8ZYjY3mKEnqelDz4kXv2zcD79/0f3z78NP7xwAy/vrr1V9/vvjy1+svf7396/PLPz8+/fPzq7++vPvrrzevXg30DzTW3zIYHVwVtGaTAXDI9QAg8BozBSCITINR6gmASOQ6VK4aliWPkGtjtQBxzBS1cXhcj1daCCoTXqGNVeigDSAlYA4bQW0haKwEg51myeeYXSwjVB2EDvACgIUVkIeTbXWxjVaa2U4HFDI8h2PN59oL+X9vwZih6AhAEKYR8ArouJgGF9tcIACENEwbTGh+AXd4AtdcEAfVMrFRlNCWE1Nj42psDJ2drrGQwUVC/GQmG8wMg5WpNVENFigKogN4ZMAqTTi1hVRwI+ujO7fDLbf+VQ30dteW5Ze5jFZ1jkMvEzFJG1YtHztqBFj1t6xbsW/Hpr3bN36/c5u356WI0KCIiKDQsICYmNDo6FAEIjIWFYVARCBRkRgsDIeHE0hIHAGOwUahMVEIZFh0TFBU5HUYLDgGFhQW7hsU5OXnf9nL79wFz6Pnrx654PXbuSuHT5z7+Y9TB34/eeDoqQMnz/x46cLBKxd/vXz+0Knj+4/+tuf3Q7uOHtzl+8se1u/7mXvWcnavzvz9O+35Q8Yrvxs9j+ae/YX48+7AQz+d/uXgrk3rN65evG7VvDUr56xePnvVstkrlsxasnD6onnTF86ZNn/W1AVzpi6cN23h/KnAi+ZPAwYP58+ZOmfmlLkzpsyb5TFv9tTZMzxmTZ8ye/rkubOmzJ45Zcb0ydOmTpzmMXHqlIlTJo0HeOThMXHGdI+5s6aPHwdw5dtv/q9vAGhMHD9u0sTxkydPmAIQaupEMGfatEnTAFpNmzpj+tRpU6d4TAHvMGmaxxQfb6/m5mY3dvyT3NjxtbrfVy/Xk4aZA56tgg+TBzpPg83WoOUGgspIUkEZGDi5HpWnQwLysBdJO3sq379/9v7ds2cPb7fWKu43579/+eDTh6fDzPHirz9f/vX5xZ+fngIcef+y/+P7x3/9+ebPTy8+vBl48qip+EaKxkIFNKMyEEAr1+GVeqJMi5PpsAodTqHD5qpj8jQIOfh0KKoBZbOCaWojBZBQtiYqWxUhUyFUBvBCpNKA0VqJw6knOL2NanaxAXwY7FSzkwXtqjigumRmO8vq4uptdKOTAaDEVshzFoucRQJnoTi/WFJyM628KquiJq+yTn27yXbrrq32tqnmlrG8Oje/KN5RILS52HozyWgl660EKChipUEFSCxYpRmvtJLlFmqemai0kLQ2mhqMQ4kpFJ2Vpbf/fayGobVShzNXSHIDRmHEOAqSP7o3Wdxy69/Ul6HB3ub6G41VBSV2TblLq8lN+OPnvYtnTlo8c8KOdUsO7Nr0w+4tuzavDwnwoZEwXDaZQkaz2SSBkMETMsRSTkqGOFeeotRmqHQZGmOW2pCu0CXJNPG5KnGeOj5HGZchE6Tn8RMzmNJUFl9CZIpQjDgYUxKNYwUiyL7ReK8YgjcwguTN5AWJJVECQSSLGUohBTAogUJ2VAI9MjPymvSPfdw9axJ/2ZZ7fL/y9M/aC79arh1zBpwx+Zzi/rYv5OD3ML9LZMJ1Kt2XyvShMLyxhMsw5LnQsBPBwcev+x/zvvqbv/+RsMjjkfCTMbGnEdizsYQLKPxleOylkLBTwSGnIiMvIeA+0WFeEUGXEZHn8djLyNhLwWGn/Px/9/M9du3a0TOnD3pdPXo94HTQ9Ysnjx1csWzRmFGjAHbMmDH59yM/nDt35Pzl368FnPAJOu4TdDQ8+nx0lHcsIgKHjomJ9A30Ox/ocyEqxDczTTrQ3+vO7fgnubHja9XdW5enJQHUyJSDRR2Wq0XmagGC4HJ0WLmRrDAA43M1iDw1wurk1dTK73eUvXza8epZ5+vnnT23zbZkeI0r993roU/QPsvTPz89+fzx6acPQ5/fD3x42f3myb2XT+59fPcIYMfbF/f7OytrK3P0VoYSCnJAlUO1VpLSiAcfobNBCR8KPVGhB/CBUenwMiVGocXLtdg8KBBCUmgBA+FzlTFQXAQ6GAIGMTorEbwQwAd08MTFgWgDquHBNDlZwIA/zHbAInStjQiVIrWTDA6atZBTVBJ/q17b3Vny9HHLm1cDb988fvf26cf3rz59fPPp07tPn99/+PDq+bPeBw8am1ryKytyXC6+0UrSmvBaI9lgoWjMWJUJfDRFb6VprWSNjaJz0LQ2qg5KeqWqTRSlETrtAnhIY6FAqbIW8H0JSj3WUZDkxg633PpX9aXvfntZgd6uz8xJ5eZl8NUySZDPmY1LZiyfOW7TstnfbVmzd/uGDasWBwZ6SRK5qRni5BRBYhIfyq8U0BNThRnZ8SlpwgQpRxRHS0rlpGZxk7NY8alkUSJeIMVx41B8KVqYjBEkoSTpeFEKlpuAovAjMdQgFPE6khQAJ/pGYq6Go67EEq4xmH5MeiCTGMQhhAkI4QJciIQYlo4MyPQ8Hvfj1viftiT8vCX54Lbso99rzh0yev5u9zuVH3iuMPhi6umDiJ92ob3PsmlhYmmMKCECkAcMcSYs4kRo6PGQkGPh4SdhyLMIzDkU7hyadAFDvkhkXEPhL1I5/uKEGEk8QsCFizjo1DhGXionJw0XJwrFk/2ikFdi8f4YfGB41FVf/9MIhC8q1heLDb58/vc1KxZNHDt6zIhv169eGhPmSyHC8bhILC4EjQnA4gIEPHSqlK/MTldkp6UnCBOF7GQxNyWe67LrnzwZcud2/JPc2PG16uqty9ERM9WodAUsW4PN1uFyAGcYiNk6jExPlgMbiHkarNnCuXtb13vP0VmjHegsfXy/eqinolhOT8Zde9BW9/7dy48fXnx8P/Thbf+7lz1vn7e/Gmp63lc91F365H7Vqycdn94NfXjVN9BZ0VApszkFarAwW/FaG05rx0O2QS2UsOlg6IYP0GoMuP9K7CBBERcDWWOkGG10lQ6j0EBxEa2FBJ6FzrlYKTorWWdjGF0CA5SBQdfbhs++2hlGO9Pq4EMl2J0MgxOMkw0ORlFpfPMt1f27tq7btp5G14OW0r6Oyv6euqGBludPel69GPwbQT5/fPP587uPH9+8evlwYOBO/W2Ds1BisrOHy6di9A6SJZ9lzedoTRS5Dqs0QrtFKgNJacBDxc1sUOasGuCUkaiCMnBp4BsBlnK4U0rdcutf1pf62op4AZlOisGjQwm4iPgERkjQpU3LZy+bMXbdoum7Nq7YvWXt5nXLfX0vixLZqTmStJz45AxxfAKXyyNzeGQunyxJYEkTmZJEakI6jR2HonJiiPQIDDkYjvGLRFyDYXzQ1OsEZiiBFcKWwsUZeH48lslE0ShIMiUKTwolkMLptGgBCyZmxEipsEwuTiGiyPiETAZMwUapon0S/9jH2bOWuWMFa8cK/ndrEn7emvXHXvW5X6w+J1zXzxYGXcgPPJ977lfk3s3Rp38jowJYrDA6K5BAvIpCX4iGnYpBnEFiAW1cJDI9SRxPDPViJOJkJPxUdOwZOPYcjn6FLbouTUZkpdPy0lh56YTE+DAqxSsadjEC5jlcb+Oyf/BF34BzUXDfaLgPChV09uShlYtmzxg/atqYET/s2ExBR6fGM5MlzHg+XcShxHGpqfH8zCSpOi9bK8/NSUtOkvAzksUZiaICh+npk8du7PgnubHja9XTVy830fMMpBwDAA5cqiY2C2CHjpSrxeaoMdnqWJWBWlSU1HHXfL9Rf8cpqdRyO2p0nXX67gZjJiNEl8Z5/qT/HcCO98/fvRl4/azjxaPG5331Q/drWqu1dc7kznrTs76GV0/bP7zqfTbY1HrHUVqSYrIxoYIcUD0PksaCUZngKjMSOs9iJmrNZJUWq9bHqs0YrZ0IrHNQTU6Oxck22RkaA1ZtwGr+PkPrhGqZ62xEtQUP3fOlgGvMhwIeJge0sWJ0MCHscPJNFra9UGQvFDgK+IUl8VXlKWUWjisr1p4a68jAl6jY5ab4Kmfm3WpT6y1XZ1Ppg87qgfuNTwZbXzzuefv68ccPLz9/fv3u/ZPBRy21DTqLg6HWwTQmtMYGyIks12Kzlag8DW6YOYhQTREbGap4psVrwIiekKeD52gjclSRcjXSWZDijna45da/qi9VFaUMEgIDD0YjgjGoEImUjieE7926Yvms8avne2xfv3TXltXbNq46dfI3OhcvlNJ5YipXRBXFM/kCCpOJ54uoSRmcDAU3Tc6QZhJZcTAULiAa4R0OvxIdey0adS046kJQ1MXAyCvhCF8MNYwuhIvi8cnxzBQBKzuer0yRqJIlLo2symWuLbTVOI3VDl25MdeQQE3G+skwASnnDlG2L0evmYtbN4+wYQF54yLOrlXi/RvTDu9Snf3ZcOWIzeeE1fu49vyhtN/2UPZvDdu/HedzWULFSGixNFQgJvoKKuo8BnURh7tMJF+JxZ2Dx56OhB0PDvkjBnkGR75M5/ryJUGSpIiMLExuLj4vD5uaGiPgBeFwV8Nizl/1P3rR64+L1457ep+MQfrjSZGxsaF/HP5hwSwPjzHfTh3z7cG9O8jYyHgRPllKTUlkpaXw0tP4WekiWU6SVp2tUWampQmEEnJSGjc7O/FGScHz5+57svyj3Njxterpb5Dp6DIdRabDy03kbB0xV0+UG4gAONLl8Bw1qqg4safJ2lye2VKSXCEnFWdSbhdk3MpPbSzMUIhiWxpKXr14+O7ts3dvH7950fvqceubxy0vH9171FNfacvJlwnqC3P7Wov62ksAiAx21/S2lTfVGYoKpXrbcPaDjao0oBV6uFwLhyIZ2lilHqtQYmQKuEKLUBlRAEp0VpIeOv5K1ZvJGiNaa4kdriZC0jtIBhdNbcMroBLmsXo7yTx8jGWYUXi2fJ6lkGct5FtcfGuB0AhYpIDnLBYUFPENqlhZ/PU8vm8m82oG00suCNUlxjrknCK9pNScUl0kb7hpaKpzdDaV9HZUP+pvevXi/qcPzz5/fvvh44uOzkpXvlimQSlNWKURPVwylag0UnRWhkJHkGlxw1tF+Dx1rFJLBJZr8LlqRJYiRq7Duooy3HU73HLrX9WX+tqb8UIqn41NlNJYDERiIk0iJv66f+uK2ROXz56wZc2inZtWbd+0asf2DSQ6PCGTwU8gcOLwbBGWLyUlpQuTMoX8BBJTDGOIIgnsIAw9IJbkF4m6GoPxQhH90GQ/JNE3JOZyaJQPGgsnEGIpBFiqmGlTZJsyU4q0yoaSwlulRbWFrtoCe02BtdqhLzPkOrNEqbgAwtm9wtP7mXvXYtfNQ6+Zg1w5C75iJmgxa+ZSNi/h7loVv39D1u/fqc4eNFw+YrzyR/Yf3wv3rGHvXss7uCfx2jkZDs0LCcZfu0zwukQJ8OTGBHIQQcTwq4jrJ6P8jqDCTnFo11OSkJmZ6IxMdEoqKjERLhFHCAUh4vjwlHREXGIkhu7jHXLi/LU/Tp49fPHy8YgoXzozFo0KO3zw+znTJk0e/Q0gj8MHv6Myo+JSY/mp0ZyUMEFmtDAzhgfAJRUjTqeJ0ykoWlAExo/MRYmSaM5C/dNn7k2Wf5QbO75WDwZuKQ00nYWmNOLkRlyODp+mxuQa8NladJYGbXZw2xr1HVWyRmd8g0XoTIy2JCAanGl19oQaS4Irl9vbVvXqRd+L570vn/e8eXH/zfP7b55197bX1hbqU1gomZh8u8z84F5xR4Ops9bUWKpqv5XfdstZVZahNVNUZpLOQTfYaVorSWWEipaqjDioEoaJZTSzdEaqTAXhCLS067FyHVquxuQpoRE1dLsWMsQiTo7WwVRbqWqoYhjF5GJai/i2YpGjMM5ZJDQVcIHthSKLiwelerg41kKuvYTnKAZQwtbocDnZUZlpwemJAVnSoLz4sDxxuDIRacpmuNTiUlNSVX5OY4W+vbGwv6v66aOWNy8f/PkJyv8YfHivoDTN5ASXjQUApHNQNFaSxkxR6KFK8EoDHhCGykRQ6vBKLUFnomnBoC42T4OyudwppW659e/qS0NteU6mKDWFlRBPoRAiOEykREw+8tPOlXMmLZ0xbtPKBTs2rty2YcWi+TOQuOu8JBRDHMVJRLCl8Lh0nDSTlJBN5UoxBHYYhn4dRvCOwXkh8H6xxOswrA+S4Ich+6NJ/hhCEB4TToBHsNExOUKmLS+9ymmtLXJVOi03TNoircKpyLTlJluzpLYsiSWNp+LF8q4fxR3ewjm0jbRlCUCNiCXTghdMCV7gEbl0OmzZDOzaefRty9g7VjC3L2NuX8HZtZq5dTl/5yr5yQN2/zMlEV7l8IDiaD9z9HUDBacXi5xymUsl1yZLk6kEISqKBw/hIUPYyCBqtDch/AIZdpmJ9WMRgggIL0TEOWT0OSzmEoXlTxEF47jXw1FXr/mc9ve7FBHuR6OgUPAQgB2zp06cPOqbaeNGHj6yE065iuZ5IVhXIqjnUFxPnNALzfXGckLwbBiGGR6IuBQE88IyEFwpxVagffLskRs7/klu7Pha3e+rzVEic5QxWYroDGVMpjo2JScmQ4lMVcRkqKJdBQkttbpqIzc/FVmQijHww8wJ6HpH2k29sMoYX6ISdbfcGOxr7O2u7u+uftx/5+nDtrtVrjh8JCUm+NzP+2hR/hV2eX/Hze5GV0etucqSqk1klpuza8ryjFaW1kIzuFhGFwMs3go9WqGPVegxaiPeaGeaHCxzPktnI6jN+OE0DqLKQNCCJVxP1ejJCrCuG6H0Dui8az5UEdXgYFlcbLOTbc3nuUriHIV8ez7HnM+2FQnyS+OBXSUSe1Gc84bYdSOuoAz0BbZCocHOlhkx2Vp4nj5WrYlVy5HyzOichAiZFKZJwVly6AW6uApXdmOlvvNu0aMHt96/evj509vPf37sG2gqLE0yO8F10k35FJ0Dr7WRoNvIQTeQI6qtBK2dpLVCpU6tDo4BAJYOI9fG5henu+t2uOXWv6ovxU59TipbLhdnZvG4HBSbiZRKaaf+2L96/pQl08duWD5nx8YV2zYsX7Zw1vWQi3CSXxjmcjThKpruTxaEsKSRgjSEMB3FTUSQuRE4SgidARcJCQlJ1IQ0sjgFL0rAEkkhxNgAhP85WoRvnpDulKcXaHIcikxLToohTaxJ5CnFTJmAlMfH53HRMg4yjxGVjLxMOrWT+OMGyu7VsJWzguZP8Z8zKWDupLBFHvAVM3Br5xI3LCBtWIBZMxexYjZi+RzC2gVx+zZlHz+Qe/KnlMN7kg7t1F35vTjcsxJxvTjGVxl4JSnEX8bjWrKzXCpFoVZVoFE4lbmW3HRtakKehJ/KJgvxcEZ0ECnMl42OEDFQFEIQHOGJJHsT+EEMcQxHEMvnEUQCqlTKRqPCjhzaN3vqpMmjvp05Zcyho7tCsRdDCRcDMee84ceDcWfDiOfC8OejCL5EDgJBCfaKOOcT7okiwyk8nN6ufPzUjR3/KDd2fK2671el50Rl5UWlZ4en50ZlyOHZMliOApEhj07NDTZYWE31+mIZRUn1MvBCNZzwgixGhQEAB6/SFG/PojXVmnrab3Y1F7besrQ2WO7dcsqkjOiLR89/v/nYjg1sWNANQ/rDzqpHPbWPOitv6JNI18/LRMTacoXJxtLbaHobWWVCy7QIwD0KHTpPjVDrccMHUugaO05tw0FVyV1sk5OlHz4Ta7DRrdCJFbraBMgDq7aQzAUscwHHks+zuABq8G0uTmGJqKCI5yxgFZfFl1dl1darG24bmu/Zm+85W1qdLW2O5nuW23d0Dbe11fWq4soMV1m8rVhghiqMsc35DK0Vp1CjczMQuYnR8gSkMZtZakq5U67rulv6/FHHh3dP//rr3afPb7u6qwpuJBrsNL2VpDWDK8ForOCCSRo7Xm3DqK04HRSMYZmcDOjMrRmntWIKy9I+ubHDLbf+Tf3l0OdIWLDsTE5uniBRSomLI6ZlcYKDz21cPnPpjLFrFk3fvmHZ1vXL1q1cdNHzKILsD6d4RRM84WQvNMOPyA8kcK8TeAHcpJjEbFJ6NkueJ9KopBqjJE/DTUwnczgxsaHn4VePCFEBchE5V0hKZ8amMZBJ5GgJNlSMCRIh/QVwb2HMVTHsajzcMz76giTsBNfrR+Jvm7i/bMNvXhy6aKrfnEnA1+dNDl04JWrJNPTq2dRNi9jDGaaCvesSft4u/WUnY8/66JVzA+d5RC2dwdu7Tn7moNX3VH7IpaLQKyUR1ywB51m/7Wd7XUkjkeQigVoq1iXH65OluqREdVKiITPdmJXiUObcdFiKjboCg6qy0CbLiMOTg8MxnkhaMD+ZmJLLlKbTxEmMmOiAQz99B7Bjyqhv58yY9MeZH4JQF/zgZ7yiTvoiTgF7Rh69En4iEh9AFWLglMDA2EvRxEA8C0UXEgx21ZAbO/5Zbuz4WnV21SWlxiQkBcXFecZJPBPSAjLyonMVqFwlKkMWmSVHlhQmlunYSoa3gu5jFMeUyjk3VIISBbdMI1SK4HUl8q6m4rZb9ruVusab+jtVtpoirVLKoAVfIgVezhOSq+05g20VLwbuvRy8d6/aJosjFutTmhts+YVi6O7zVpJaj1VBFTtwSj1BpsWojdANXKC6pVaCwoxTmXFg5bYVCYxQ0gbT5KIbHESNFWtwUKEzLBaytYDtKBHmlyUU30wru5l5szyzplbZ0mTr7iwdHLz9ZKjtxdPO1y96Przp//Ru8OO7gU/vB6DO2/53r3tfvex5MtTS31fT3uGqrMwqLol3FQtN+VS9g2S0UVVqdHZaVHZ8tCGDVm7NaKlzDPXeff9q8M9Pr758+fT27ZPa23pTAVvvoGjMuOEKZtCNdlVGtMIAUxpQgJ+UOozagM1TouRquNqILihJc0c73HLrX9VfNk0WGeaVmcZWKqQ5OXFxYkp8Eg1HCNm2buHSmWNXzpu8bd2SreuXblq79NjJn6Nx3tFEz3D0pSicJ5ruF0P0hJGvohk+eI4fLxmenEeWablKg0Bp4INOajoZHnza8+AG9LXDrPDz9JDTZP+jRJ/fib5HSD5H8FcPYa8cxF76CXvxAO7CD6RLBxhXf2Zd/ZF1eR/5+BbaLxsYe9cgVs/xnzvJa+YEv9kTA+dPDl/kAVs6HbtmDnXLYvau1fRdq/FblkeunHdt9qSzHmPPTR0TuMCDvmNFzvF9qnO/6C4dMXufKAq74gg4l/zLTvbuNSmnfkn2PJOHiDLRyUV8dgGbqkZEcq9epAb782IjFVJeXXH+nYryQoO6/kZBXYlNlslFYvzCkd4ETowolRKfwhBLGOHB3j/u2/F3tGPx/OlnPQ+HYjz94ecvhx67HHb8XODvF4KPheF8KUI0N4EKIweG4a6QRXC2lEQTEtQm+aPHD93Y8U9yY8fXqrOnNjkLJk0Ll6YEi5P8QZueA8+WI2VKdI4SlSmD6wzUMrvAkRxjEYUWZRHzs6iFuawSGbdEKVDwETX5Oa0NtpY6U3ODvb3pZktj+e1qZ80NfYk5w6lJrSnUNN/UD7SVPu9rfP2o9cWjtrZbJS01zr62yp72ont3DQ018qry7PLyjKLiRJsrzuYSWZx8i4NtgnJIyRozQWXCaixEawH371IcJhdLbwdYQHeVSvNL4gqKheUVqXV1itZ7rv6+2qeP77142vHiWfvrZ10fXvW+f9n77kXv2+cPQPvm2YN3z3vfPO9+//LBu2F/fPvww7uHH173f3zd//7F/ZdP2h4NNnZ3ldXU5LoKeCYrWW2MlauiZTkxeSkohyauudY+1N/y7u3jP/989x//8RmQx+BQq6NUanDRtZbhEu92qs5OU5lwMgNSocdoTHiVAac2ENVashpKL8WYbXEfP778778At9xy639fX2w6GZ+CKXTa6+tqqqsr8l12lSY7IZl1+Mcdq+d7LJs1Yevaxds2LNu4dsme7zYERJ6Dk71hRK8YglcU7koI8nwE9hKSdo3A8+clwVJl5Bw1K0/DzZKxM3IYLHLQ6X2rT2yb77l/uc/Pq31+Wun/86qQXzeEHt4Y9tvG0MMbQn9ZF3ZwTcTBNdG/rIEdWos+spFwdDPxj42Yn1fyj2wnb18evniaz+yJ3jMn+MyaEDBvEiCP4AWTQes7Z+LlmRNOeYw9Pnn08cmjTk4edWHq2OAFHuxdK7OP7pWfOpB7fH/mH99nHd2nPPdLyi87+btWKs4eLIn0LkcG1lJQ1RRUGSZC432WtndTwIq5vvt3sGJCHIqs6kKHMSfNIsuoKrRWufROpVTCQcRirhPo0ZIEWnqyICNJFBpwZe+ODbOmjJ88+pvlS+Z4+p1CUgLRrGA4xT8IeSUg2hNDj4nP4GcoUlJypbG0yEi8H54FFySxhMkck1P/2F2345/lxo6vVUdXeUJ6SFJ2RFJuZEpuWGpWdEYOIiM3OkcGz1VhclXYLDnSoCPny0mutFiTBKHiIUwJxPw8zg2dWCaMKTUm3CrX3Kk2d92r6OlsaG+prL9pvWGXVZcaG2uLO+/c6Kyz9rcUDnVXPe6tff6w6VFPQ3dTSV/HzUf3q4Z6Kvrbb/TctnTfsfa3lwz11jwZbHzU19DeVninUXuzPPVGefKN8vTS8rTissTi0oTymxk11fLaamXjLWNPx42ng7dfQadmWl48bH75qPnl47a3z7peP+l4NdT+aqjj7eOu5/33ng+2vHrc+WKoHfKj9tdPWt88aX0xCOa3vBpqeznU/uZJJ/SSJ+0QkbzqBbDy4WXP80d37t7WOewsvR5vMJH1RrpZL7hb73rxrO/zx9df/vzw15fPX/7jrz//el9Unm2AMlEYRgBDNqrRxR4+x8u0FPJM0C1qaRAq2VgWG8di5zgL09wppW659a/qS6HLbtKqK8rKa2vramtqqyoqKm/evFlWJhWJfjuwZ9nsyRtWzNu6bsmmtYs3rFty0euPEOTlQNhFn7BTgfCzkThPJM0bw/Rhx0ely6kKPU+hE8jUgrRMZlISOfTKrwfXzPhp+aSDKyb9unrK7+umHt8w7cyWWRe2zb60fc6lbbMvb5vttX2uz/a513ctCN69MHLfUuRPy9EHVxB+XkXfuwq5Zs71eZO9Z00A2OH1//KV6eMvTB1zburYsx5/e4zn9HERCzyoGxdKf9iY+sv2tF93Sg9sluzfkHP0+9RDO9hbl6Ye2lUW43uLFNNAhhXDrys8j7H3bQpaOvP4tHGH5kyJPHtUlRLvUOVli1jaVEmZTV9u17lUKcZ0pjKBnBFPyUzlZqaIMqWiRD7ruteFbeuXT584dvLoEetWL42I8qHzYQRWOIkdTebAuPGkxExRQkZcXBJXnMJli4lkLoIhwvPiaVwJzeTQPn7qxo5/lBs7vlbtnWUJadeTs4OkGf6JWdeTskKzFYisXGSeAivXENNzYSk5YZkyhN0osGZTsih+ibHXNMLYUnVcuV4sE0QX6cTVJfLb1ZbO1uquzlvtrTVN9YV1N0yNNYU97bcetJR237I87Ch90lv7qKuir7XkYUd1X1tFb1v5UE/VQHvpw/ayntvmjlrd/bv2R11lzwfqXwzeefGw6fnDO0MPqga7Sx89qHj4oHKgq6y3vXCwu+RJb9XT3pqnvfXP+m8962t43tfw+H7d4/u1jx/UPRtofDZw+0lf/dP+2y8Gm57133020Pysvwly3+2nvbef9d950lv35EHt4/s1Q/drIXfXgjd51tvwtLfh9RAAkRboAPCTjrdPO98+a+3vLrtVK6+pyiq7mVZUnHqvpezjh9dfvvz55a/PX4axA/Rv3bHbHDybk21yMiD4cNKNLqrKPJySAmWccKC7ukCFU6kGJ7GgLPXjp1f//Rfglltu/R+opamprLSs8mZFdWVVJWCOisrKm5XlN8rzHQ5YeND6JXPXLJ65ee2iTWsWbViz5JLnKRg2FEkMRRAD4WQ/NPN6LN2XwA1KyCQojUKTI0Frlih0cTlyYYKEcPm3nT8s89i/eML3C8ftnT/uh0Xjf1464beVk46unnJstcex1ZOPr5p8evWUs6s9Lq2d6rVxxvVtc8P3LIjeu5D64xrC5kVhizx8h5kD+NqM/+HxV2eMvzZrgt+ciX6zJ4KHXjPGhy2YQlg7l7N1KX/nCuF3awR71vB2rUw/skt55ifpvvXx+9ZbvU9UooJKYH4Gn9PSw7tR6xd6zp7486TR+2ZNPrN7owADyxCy0ng0Q1ZSuU1fZtXYFcmqRGo2H5HNR2XGUZOE9HgePUMqTBKy/T3PrFs2f9r40ZNGf7ttwyoCJjQxkRKfSJGmMMXJDI6IxOASGTwyngZHEUMJ9CiumJiSzUvL5SYkMx0O/VM3dvyz3NjxtQLYEZ/iL033FadclaR6SdP8M/IiM3MRmTmwbBkiOTU4KS04XYZwFaZUF+fJ+FHCyJMKblSJSliqFiRTApwqYVWhrLbc0NRY0tJU0dJ0s6u1uqetfuBBy8O+prZ6S0e9ob+95GlfXX9rafNN1d1ieVuVubflBnh4/46tr8nx4Lapo1rVWacdaC180lMBqOJZf93TwYZnA/VP+2qf9dc/HwB4UTfUeWOg1fm4q/RxT8WTnprH3ZWPOm8+6igfbC972FXxqLvqWW/9457qoR4wXj7UeXOouxJyZwUgm4F7xQP3Sh923RxsvwHmD3beHOgsH2gvGWwrHYImlw91gc+FiOTl4B1AHu9edEN+3vnqSfOzRw1DfdUP+2tev+j689MbiDmGgQP8mfXlP750dFda7GyTnWZ2MQ12qs6C19mwSgNSacQM3+EWyn4F1ttpSgvWWZr8wY0dbrn1r+rhw0e3b92prqqpKK+suFkJOtVVtRVl5YUuJxEds3XN4lULpm1es2jjmkVbNq7wD7pE5aHYEjQ7HkGLi8Iwr4ciL6OpgZIkQq6Sp7NIjfYkjUkqU8clSYmnf9y0a8GE7+aN3TNvzI5Zo4H3zBlzYOG4g4vGHQTtgrEH54/9dcG4IwvGHVs0/uzySVfWTPXeMCN061zKdyvQa+eGLIAOsHjPmnBtJkCNcVenj/ecPu7K9LEAO8AgYA7oeMvsSYA5MCtnMTYvpG9aSN2wgL1tGXv7csGe1dnH9spO7Es/uFV5Yr/N+4Th2om0o9+z9qyNWDbrwvTxP00ac2DxXP/jv/KxsHgqNi+e51BmFenlTmW6MUOYJyYm0cIlBP8EcnASCy1lUxJ4rMwEcVIcN9D7wspFs6eMGQGwY/fmtSR0ZHoyNyWNJ0lmChMY/Hg6lY3BkKMjkP6BkZ6RSB8cLYInwSen05OTGU679ql7k+Wf5caOr1V7Z4UwwV+Y4MWXXJGkeCekBmTkRsvVhFwVPlOOBM7IQ6Vmw6z2+PoKTb6Kk8cN1YphhXKWJQXPjDihzaCUWtMr8gF5GOuq7E2NN/ruNz953POov62zqaS5Ut11yzLYAZFBV6Prbpn6pj6xypjWXu3qbSrtqNW0V8k6a1TNJen3bmQM3rM/bCt83FX29EH188H6JwA+AHMM3n7ef+vJ/aqB1oIHd02Dba7B9uLBjvKHHWUDrTcATAxjR/lgZ8lAW9EgcGthf4vrwV3b/bv2+422+w3GzhpNR52+s8Hcddvaedt2vyn/QUthX1txb0v+gyY7hD4tBQOtJQ8BiHSWDvVUPxtofAlFPtrePO1696zz7bP2l4/uvnzY+O5Z68c3fZ8/vQDk8fePDmBH/8Mmq5OrNmKhe8XZ8GpzrFIPJZOqDXi1hWy0QUdv9Faa0cHUWqn5penucmFuufXv6vHjp3fvttbXA/Kor62ur7h5s7jAZdarFblpZEzMns2rVsyfumn1ok1rFm9Yu+Ty1aMMSbQgDUHih0QTvENRnsGwyxhiCJUaw+NhkpIZuXKRQiNRG+LT0ymnfty4a9747+aPBQbMsWXayK3TR+6ZPXr/nDH75ozeN2vUvpmj9s8c9dPs0b/OHfv7/HEnF088t2xy6Ma5hC2L4MtnAp64Pnei9yyINgBzXJsxAbRXpo+7PG0sgI9rM8b5z54YtnAKcvkM/OrZxLVzcKtnkdbPY21dytq2VPjd6vTfdmUf2a04tld//hfZyR+4362JXTs3YP6UM1PH/uwx/tDaVYTwoBwxP1PIMmYnF+rynKpMXZowW4BPoobHE/z5CE92zEVBrFcCE5HAp0EFzhMlIhY12Pvi8vkzp4z5dvKYkfv3bCOT4fw4EpEBI7ORFC6GHUeOS2RxxWQcLRqGCQyDeyHwgVQeQpRESkhn2PO1T9wnWf5Zbuz4WnX21KbmwuLTA+NTr2fI4Gl50RkyWKYckaWITc2LyZKjc+SEjDy03ZnSUGUwpBMMKVhbNq1QxsjjBmN9f8wQxFgVIrNM4NInlRWqGuvz2+9Vd3fUtd0pba6xNVVo2urNfW1l95tKGsv11S55gTyt2qCqt+mbS53NZfqWcnlrRe5tV9wtG+9+g3qwNX+o8waADOieL/erHz+oedpX//RBw+OeCvBU710zwI5HAA66qx513XwIgGPYg+2lffcAQ9h6m6x9Tdb7jabuBkNXvaGzVtteqWgqzWopz2ur1tyr0nTUGzsbLB23TF2Nlp5Gc0edtrNe13Pb0nPH0dPs7L2XP9hx40lvLfjQ5/2NLwebXz9uffe8Cxjaf3l09+2ztg9vej9/ev3ly1/g/96nPz/39N42WtlyLVJpQP19iziVAavSETR6vM5MsTg4Zgcbgg87Q20mu0rc2OGWW/+yhoYe377dVFNdV3bjht1szEqViHlkIROTnsDl0jA/7t64fN7UDasWbFq7eO2KBb/+tjcS44lh+kXhrlyPORsEv+QXeg6OCsBiQ+g0mFhCSk5lZ2TxcxWC1BTSmZ8375w3Yfc8CDt2zv5P7Ng5c9TeWaP3gnbGyO+mjfx+6sgD00f9MmvMoVljDs8Ze3Te+ODVs3Hr58OXzwBI4T9n4rWZw9gxA2qBrw13rk4f5zVj/PU5kyIWeSCXTcesnIlbNetv7KBsWEDZMF+we1XKL9vSDm5VnPheffanuP0bYpbP9Jkz8YzHmF+nTTi/ewcXG5sh5KbzGYaMRJcq2y5L0yTzUhgIAcqHEX6GEX6KGnKCHHycDfcU06MFPJIgjpOSImXTCYHXzi2bN3Py6BFTxo0+cGBXTGwwkQHHUqPQlEgkIRxNjqSxMSwhjiGMxVHDI5HeRGYUP4HASyAyhGi1MfvR0IAbO/5Jbuz4n+o//+l03a/NyItJywnNUcBlGnymEpWlgGfKojMU8DQZIkeJk6mIwEVFGXcbnIWmhAKdqMSYUKQVJBKv4gMOigneuRKMLAlvzOMXu+QNtY7b9a7b1dY7NbaWemdjmfLOTWX7LXv7rcKGG0ZTljSTTrFIhKKIoGwSWi9m3ClWdlTJb5nZVWrCvbL0vmb7UPcN4Mc9Nx91VQ51Vz+5XzPUefPBXXtXnbarXtvbbHvYUfLkfvXDjtLB9hIo8tFW2Nfi6m129jTauhtMPQ2mrgZte42mq07XVadvq9Y231S0AFeo2qu1bVXq1mpNa62uvc7QWadvrVS0VMruVctba1Tt9YauW+beJsfD9tJHXeVPeqqe9FQ+7617+bDxzZP21086nj9sfvaw8cXT1jev+z5+evfxz0+v37+921omU+NyVTFKA1YNlSjFqgxQxTOtEW80Ucx2tsXJNdkZOitJbcEX3MhwH6B1y61/Vz3dXTaTPis1XswlM4kx6EgfTLQ3Ex+RImFK+JRD+7Yvn+uxfsW8TWuXrFw697u9mwPCL4TGXgzDXghGngXk4RtyJgbhh8WEstnIpFR6aiY7PYuTnsWWiDFnft6ya8GEPQA75o3dNXv0thkjt00fuWP6yF0zRu2cOmKXx4jdHiP3eIz83mPkj9NGHfAY+YPHyF9njP2/2bvvoLayPV/0Tc4ZTMY2TjjnHLrdbrudcw44ATY5IxRQRCByzggQQjnnLCGBCBIiZ2wDBmzjDuecmblVd6ZqXG/L3XPfranX53bd13/qW7/atdmIUIVd+tRav7VW7CY/6NbAjA2+8aGeUYFuz7/OsDzxdXno43zfx+mReagD8Id5niU2xCN5nXfWBl9YhD9iawBiWyBie1DO1kDEtqCiQxurTm5r+fEA4/Zp/IXD4G0hT/3dbng6nvdyjjx+qAoNL0OCq9FgSm0xG19JqytsK8lpwCQXZzxGxlyEPT9rrpc/Ql+ez0u9X4yIw6DTc3LBJZWFKCT46f3r64P8PJ3sfNxdLlw6kwlNQuJAyPwMbBE4vxSGKgChcJkoXDqmJL2oCgJUNT63vr2wtrWgvB7LFdNWf/poYccfxcKOP5t37wfaKJmtxDgiKaGl7VVre2xHRwqxI5PQkdXWkdlOASwCpbBzRNLyAQNnoJ83ZBAO9nL7Ne2MBlA1/EFJ1o0a1DNCebqYXjk8oJya6J8Y14+YlOMm2cyQfKyPM9bLmTCIRg3S/k4es6WiBpaGefHw7LbNMeeOlyQ9m9CxZntoQ4KiPgZ6Wk9YnpJ/XtR/eqf9MKdZmVavzGlX33StzmnmB9lT3YQ3RvLyhPjDjGplGnCGbGVavjIl/zAtWxwTLo6LFsdE88P8NwPM2V7qXB/93QBr3sR9Y+LNAPgwcWaM7Jk+5mQP1awQ4EZPneqmjmnbhzUto7q2CX3HVC99uo/xdoi3MC5dHJctT6mWJ5UfZjp/WTL8/cPov/0y9+un8Q/zvR+XBlZXJ3/5Zfnnv68urb43jcr58nLzYfdiDF2MpPJzeDKcRF0uVBRxhbkCSZFAWsQV53ElWJYEpeq27FJqiSV/bb50aeSwtJiEZ9eTo27D06PQoFfo7JgiVGpzdX59Rf7F04c3BXtt2xC4Z1v4tk0hhw/tePTyahzo3uvM23GgOzHpN5PAj7AlaTWNyAZ8bmt7AYVVQedWMbhVzfjcJ1dPHFvneTzM5WSYy/EQp0P+Dgd87YE6AuDDy+6Ah91hT7vDHrZH3G2PutsddbM77Gp71d81cYNfWrhvQqjnq2B3gB0vzKMdzo98nB8A7PB2uufjeN/HEcBHpJ9LFCCPYI/kMK+McJ/sTX6QLWsgm/1BG32zNvnBAIXsCK45vaftyknkvo2RAW7XPBx+dHe8vyuiHgHOT49NfnAOl/6sMT+7FpOWn/IIHnUJEX0RGX0B9Pi79Psn0u+fynz8PSjyLDrxdj44CpTxKjk9DgzLSEt9fefauXUBPl5Odn5ebpeunItLeonKBxVXIYurkUVV8KJqZGVTXj2hsJmU10jEFNWA4YUpyOJMZCEorzSHK6Fbtgv7J7Gw48/m7YIB35HWTEpupaQ2t8c1tyW0EzPb2xHtHZA2UmYLJRVPSSayMoD3Tr2+Y2RQBqhialI/ZpKr+XWU6vQq2L3i9BtV8MiOOtjosPbN2/H5xemFxcn5t0Pzs/1zo+rpYeXYgGLMpB3sVwvohIL0uMz7128e3nPz6K6OmsK3o6rZftaQqMzILZjpJS1Pqz6/N/602PvhjWpxHMCE9P247P0If7afMqpsnNET3ppYb4zMeRNjYYS7MMJbHOEvmY/G5S+M8pcmZIuj4neD3Nl+5psB9tsBxhxADQNn2siZ7GNM6KljOtKYjjyqJY7qiCOatkF5s1HaYJDWGiQ1g8rm8a6Oie6OmX7a22HeuxHR0oR8cUz8fkL+6W3XL8umv62O/311bHFa82ZCOTfbPTXdPzSuGxhTqnqJIk2FuXVDmgvAgiGA8xQ4oaqYY95HFcMR44TyYrYEy5ZhWVKESm9ZyWKJJX9tvqikvKy4R/GRV5Jf3IAmRaKzX2Oh8cWY9CaAHeV5l04f3hjkuXX9mr3b1u/cHLZ356YrN89ce3ju5tMLj19feZ15NxH8EF2QXFyeXVYBrW/MbW4raO0o7qCWNeOxUXfOnNzofSzU+USo87Fgx9/Z4WN3xNf+kLfdfg/bA+62B1xtgNrvYrvX2faQs+2L9b4pgDlCPF8FmcHxdd2Ki5kdvk7m0Q5vp7teDne9He55Oz7wcXri6wy8IDrQLS7EI3mtV9p6n/T1vsBN4lrPuFDPmGD3lI0BKVtCHvi7X3Z3OOtqdzl0Der53TJwQtyN76LO74q6sCvu+uGUu6dSbh9PvH4w5dbhjPvHU+8eTbp1OOnW0dQH32ZG/oCMv5mb8TQj6UV09NO0tITkhKgbF0+v9ff2drJf4+V+8/YVCCK1oAyKLQYhCzIRBRk5+anIwsy8cnBFI7ysDowqTAbnxkOwSWBMCgiRQmK0flq1TLL8YSzs+LMB2NHYkY6ngVqZ4NavcyvtdAiJgWmn5hDoYAITTGCB2lkZVB5co2seMoqmJvtmZgdnpwcG+yRqfhOtDtKUF1sFf44vTutSscbG+pc+zL9fnltamll5P734dnBiSDNsUA4NaAf6tTwmKfnJrStHtu8O93t09VyPRvxmVDPdSxuUVBp4heOdeODN/qelgc/Lg5/edc2P8BZGBAvDgvkB5mwfeayzdVpPemNkTPd0zPYQ5430+UH24jB/cUy0MC55P6X4MKtdmdEsTsjfjojeDgtmB9jTRta0gTXVz5rQ00Y7iYOqFpOsqV9UOyBr7BPVdXMrurgl3ZzSHl75gLR+RN08pm2d6iPPAF4xsedHBPPDvPkRISChT/M9P68M/m11bGW+x9RP1/fStD0Uma5Nom0QqIp5SkAVGI4slyNHsWVIthzLUeQxxVgyB0EXYviyQp4Ux5HlceV5aj3BMtphiSV/ab7IReyMV/fjn1xKeXkzK/YBPO1FLiS2DJvZVJVXW5Z7/rtDGwI9Itb5mVtKI9bt2LL+zA8nLt069zT2dmzmg7is+ymwJzm5cUUl2RWViLJyoJA1tdiaOkxTMzYl6vp3W/yPhgDmcDoa5HAk0OHQGvuDgDl87A54Aeyw2edms9/VZq+L7Q5H620O1me8XVM2B2Rs8IkP9ogKAMxhbub42thhvgLIeOjjdN/b8a634x0zPhzveZstAnzqpb9rTKB7fIhnQqgXcPPS/LWud72drnk4XHSzP+did8bZ7pSb/fPjO4tTI2NvnLx2cO294+vvHAu7eTjkzrG1j77b9OzM1hfntsVc2h17dX/ctQNx1w8l3TmR8fgMPPZabuZTNCwFDE5DIbKz02Kv/XgibI2nt7N9oJ/ni+iHSFwmLDcpExGXAY9Py4nNQsaC0HEZiOhsZDQI/jIV9DQ5+3l85tNUWEwWIo5Ib/ywumxhxx/Fwo4/m3eLxuaOjGZqVhsD0c5EdLCQBBqMwsUQOQgiG0nmoMhcOJmbQ+LA5ZoGo4E/NKgZm+ifnhubmDKZTGqdmqbgNwvJ5VxCMaOtRCGiDBh0RkP37Mz40vu5xfnxiaHOfr2st0epUokba8ruXTzzw7Fdx/dHQNNTJ4Z6p03SUW17n6Csh1NoktXMj4p/Whn+ZXUaeJt/NyJ4a6K/MdDe9JJmeoCiTvfS3w3x5oz0dwP0xUEOIJKlMdnKtGp5Rrk8owLY8elt96c33e+nlO9GJHNG3uwAZ7KfMdoFgAM/IK/rFVZ2sco7aYVaZqmOXa6mFcqIKBkRqSLndjELe7mlRmnNmK51Qt8xO8CcM3HeDvMBvixOyJZnNKsLfb9+GP7l87hpmCfSVAu1xaLOUpGqlCMFkIHmKtA8BYYlRTDECKY0lyVFsyV5HAmWLcGwRUi+BCtWlonV5bo+6r//x7/99z+AJZZY8n+fL3IhKy36bhzAjqgb6TF3IUlPUFnRRajUhgpMcW72mWN7wgPcN4f57olYt3vr+s3hQZevnImMvhub/igdGpWU/TQx6wkCm1RamVNdm1tdnVdTjcO3lLe1l5LIFbC0yNNbg48EOR8LcjoW5HA0wOGQn/0Bb7sDnraAOfa62ex2sdnpbLPNyWaDPcAO23uhPhmb1qSt844Ldn++xvXJ1zaOr50cX9fNfpXHIx+nBz5OZnl4O9wB5PG12+Mx8Bx48RqXZ/6uwCu/vsDphqfDJXf7H13tfnC2Pelo8/0a97Rrx6CRZy/sCT61yf10hPuPO33O7/a9vD/g+qHgW0fC7p9Y/+T0lhc/7oy+uDf2mpkdaY/PwF5dwYKe1lShmvEVLU0VOFTWuZP7grxcAHYEr/F+Hf8UXZANgCMV+joZHB2b8TQh+wUIEwvGvM6CRyWmP4mKvRMd/zAy+k508qMUaFQrqXb5w/v//PKf//2PYMnXWNjxZ7O4MkhgQPH0rDZWdgcHQuYhAXMwJXkkAeLrfQ6RlU0yP4fJOhtGRhT9PcK+XvnQcM/ouGF4tMdkVHR3MmUCPJNYUpOfUgh7TSPUtTbWsBkUY1/327fT05ODg4P6PkMXn88owaEfX79w/vi+E3s2l+Shpoa7Jvr4g0p8v6C8j1tsklUvTkh/XZ36x6/zf/t5ZmlaMdHVOqFrntI1z3QT53opcwaAHdy3JuYbA31+kLswKlocly1NKt5PKZamlYA2lqY1H2Z1yzOd8yOyGSNvso85picPKpsN4jo9t1zLKFIQsfI2lJyYKyFg+E0wbh2IVZ3KrU2VtkJ19HyDqGq0Ez/WRZg1MudHzKtd3o2a+zzeTyo+mVe4GH9ZHZ2aVkg6KzkKFEUAYorMB89y5Bi2DM1X4rjSPJY4jy3BsSW5ImWhSIHjSXO5EhSHj5Ary6WKkt5++n9Y2GGJJX9lvsgEzNSo268fnU9+cS016lZW/IOctGc4eGJtKQoLTzt5aMc6f7fwIM9dW8L2bF2/ZUPQpcvfJoEepcIi06BRr1MexSQ9xORllpWhamtxRGItmVRHItd2kCp4XHwxOvnHveHHQl1PBDsfD3L8X+zY72mz3912j+tXczjahNtbB9taHXJ3ilnvl7beOynUIybQ7ekal4e+gDCcH/oAqvh9Dcuzr2tYgA+B5/e8AVuY5XH/K0TM+5Z6ARZxvO3leMvL4Zqn/WV3+wuu9oA5TjvbHHGwvrLBL+H8rshTG06Eu5/Y4HpqI1BuZ7Z6Xtzjf2V/IFA3DoXcPbb+8fdbn/+4+/XVgwm3j6c+PA2NvpSb9biiFFpRndvQWIzLzT62f5ufm6MPwA5/7xfRD8HI1GxUciokNhEUBUInwAtSC6shZXWQkmowKj8lLevlq4RHj1/cfhH3AJab0k5pWF5Z/M//tLDj/zsWdvzZzC+b2hmQNnpmY8crEjuNzAOR2FASJ5vMzwao0cHKIjLTCcwMMi9Hqm2cmu7Wa7l6nbirW6yU0xWiDj6tmlADLYE/g7y+kBr5bdLj0wWwV3VlSERmbD40RSNlG/SKLo1EKWG3N5UXojNgmS8zYh9e+3ZvSzV6wiCc7GUNyBqB9/tebtGgomZhUvnL5+l/+dvCP35982mhe7K7bUxTN65pmNS2zPQQZ/tpbwbYM/3UWQPj3ZBgYUwyPyZ7Nyp5N2aeZFmYkC+MmxfBLk2r5oZE0yb+RC9zrItskjV20gvV1HxJG5zXkMGpSWFWpTAqUmjlyeTiODIuilkeK2uFGgVVYzriVB99vIfyZoDzbtQ8zrE4oQK+5+K4/OOc9qeF3s9LA29mlGJ1KUMCZ4ihLDESAAdbhuLI8/jKIpYolyHAMARolgjNFaF5Yixfks+T5PHFOKW6UiYv7u0D2PE/LAOUlljy1+WLVMBMeXnr1YNzSc+uJr+8kRZzG5L0GAuLqy6G43KzTh3btT7Qfb2/285NIXu2rd++OeTUt3tToI/TEJEp2c8zcmLh2KyqWlxzY0kHoZpBbaKT66iUGga9XsRvayiHXTux/cRa9xMhTieCHY8HOhz1N8+w7Pe03eduu9vF9rdxDn+bb/xtrM76uKSE+2aF+6Su9YoP8XgZ4PbYPG7h+Fsbx2NzG4d50Sxw/W3OBXhy//fZlt8nXID7m54O1z3sr7rbX3Kzu+Bm99tQxylHmwMO1g/3rXv53YaLO3wPhbocDnM+HOYEXI+vd/1hu8/FvQEX9vhf3h90/VDYvZObIn/YEX15f9zNYykPvgNHXUClPSwqAJVVYmrqcPkY0MFdW9a4Ovo5229eH/Qi6kFKZmwGJB6ByyyoyiltQBVUQwsqwfnlYFwFDLhiS7PTsl9Hxz6C5SYXVkJaidWL799Z2PFHsbDjz+btUn8LLY1Iz+hgpJNY6RROFpuPpPKhFBGUxIO0M9PbmSmtjAQCB8SSl0/P6Xt1bL1OYBzo1qq5lJa8alx8CTKqAhtfV5zRXA5uKgPVFafVFiVV573GpN8sz3nRWpJBrsfI+C3dGoaxTzg+rBwx8DX8huEu2rSBM9nDGJA29nBL9SzcoLJ+aa7759WZv/+6+OtPb1cX+sxbeqjqRlV1E534UVXzpI4w20uZ7qMBNTvAfDcqWpxQAsKYGeQB9+8nlYtAma0gfzsqmR0STvTSjYrmLk6ZjIgStsAY1WmU0jhSUUxHYUxr7vMm1JMG+IMW+ANuZaKOmj+hI04baDMG+pyJNT8qmB+Tvp9SL0xp3o3LAdAAP+jTW/3nxYGVhf7O7maKEEIVgJhCOFeG5pkLy5XmiZWlYmUJUCLgqijmijAieZFYXgjcS5TFQim2s5tgOfjeEkv+0vzGjpsAOxKfXU16fj095g40JRILjSvGZpaUgq5eObYp1Gudv+uOjUEAO3ZvXbd754ZXqXcRxUlFNdDSOmRFHaqttZhBquHQ6/mcRj63gc2uY7CqWZxqIiH/ydVjJ8M9j4c6nwxxPhnsdDzA4ZCv3QEvgB02u1xsNjlaB9tZeVh/E+5g+yDEM2ujH3yLP1Dgjb6Z4T4JoZ4v/d0emQc2HAFhPPyKjye+5gmX39gBPLn3VR63PR1ueznc8nS44elwDWCHh8MVd3NXx3nzs/VjAACAAElEQVQ3uzMutocdbfc7297fF/LkWOiZzR6HzOZwPhTqdCDYcX+w09Fw17M7fM7vWnN+t/+V/cG3j214fGbHi4v7Y28eS77/LejFj+iMx+VlOY2tZQ1Npcic1P3bN/q7O65xczj97QEYPCULkghGppTUInGVEExxFqogA5mfAkUnguDxUEwyujCjoAJWWoMsqYVDcxNLKrBv3sxa2PFHsbDjz+bNcl8TNYFATWWw4XQujC1CMvgIEgfUwQMRgSs7i8hOJwDFhZD4ecOTyv5uVqeS0ter0OmEfE4DEY8l4XF0YikFn4evAsyRWJ77HAe+nRN7FhJzpgwW2VqaKqSW9XVxhgeUk2P6mVHVhJE1N8wHWDBt5IzqyP3iWi09T0WEDSqblt/0/fL5zT/+tvz3n999WuifMzLGtc0jqoYxdfOYqmVc0zKtJ8/2Mca7SdP9tLkh3tsRydywCPg+U/2s2UHe21Hx21HRuzEJ8Hx6gDfaTekR16sZxVIiWtgK5zaAGFWptLIEamkcpeQ1gA88KpKY+0zcmN3LLR/Vto11ESZ7iOY9PIYE7yfVyzPd76c7FydV70aBbyhenFB9Xhj4eWV0fFJCF8PZUhRfkceVodgSBEeMESkKZJpSsapIrC4QqUqE8kKeCCWQ5MmV5RJ5sVheIJBjNd1tlpUslljylwZgByPx2bWoO9/HPb4YF3k5+eVNcOJjZFY0FpGQX5z65PmFrRv8AHZs3xDwlR3rt20JexZ9KxX2FI6Lr2pE1TXnEoiFTFYFh1/JElSwRBU0XimVWwIUmV4U9+zCqY3ex0OczOwIcjweYH9kjd1BH7u97rbbnK3DHazX2Fq5Wn9z0NU+KsQje4MPMsI/d3tg7vag3B1BmO3BsC3+SWHez9e4fhXG1/JxeuD7ez/HQ1/zTh6/yePW13Nob3jY/xc77M3NpK52J51sdttbH1/jcm9f4O29fifWOgPsAOpgqPOBEMc9gQ77gd9tk8f3W73PbPO5sCfw5pF1D09ve3Fh/+ubRxPvn8x4dhaT/bS6GlXTWFhRhc1KjdkTsT7Q0znAw+nqldOY/MyiClhZLaqgApJXlllcmVVUnpVfklnbgszBJqSCX4IRcfll2YVVMEhu4vO4+znI7JmZKQs7/igWdvzz/L8j/W+X+ppJSa3kZBI9q4ORRmSmEWjJLZTEDjaYykdSeHASF0LkgvDMdDwzu9tEHxmUKITNCklHl44vExOZlEp8VU5rdXY1Njo/+xY6/Soi6QL01Tlk7AVc+u06bAytEabgNwDsMPYIRwfEw3rKZB/93YT03bhsxsQb0XX0i2s6qWgVETqkxi/O9q5+nP1pdf7n1TcfF42zJs6IutEkrR6QVJukNcPKxjEdYVTbMaRpn+ihTvYzAVtMm7jjvbSxbjLwZHaQPzcimh0CTCOaMnIHO4ndvBoVrURGzpe0o0StOQI8lN8M5tZlcGrT6RUJ7blPGSWxqg60QVg1IKsdVjeZ+0lNnPkJ+ftZ7cqbnuW57qWZzoUJhZkyY7LVBcMvq1OfV0f5yiKOEs2Q57AUKI4cK5AXSVTFfHkeXZhD5qYzRBC+HCtWFguV+dLOIpGyUKIuAkrb024Z7bDEkr80X8RcWuyTy8+un4q6cyb6wY+vH19KfnkrO/EJFBSdjXgVk3Bnx5bA9f6uW9etMbNjW/jWjaGRz27Fpz/Kgj8rrsmsaoE0khAd7HwSp6CDVdBO/60KifRiEq0Ukvbouy1rjgU7nQx1PhlsZsdRMzvs97jbRjhbh9lbe9tY+VpbXfRzTQzzgm32Q28LyNsZhNsdmr8rBLc7DLcnFL0jKDPcLzrQ/YGP0x0vx9vmfo7fZlXMXR2/1V1zS4fDDU/730c73O0vuztccLM/42J7yMF6i631d2t97uz1v7rd60iw48EQwBxOB0Oc9oc47g1y3BPktC/E+fA6txObPM7t9L92aN39b7e+uLDv9Y0j8XdOpD45k5cT09hUUNtUlF8Aff3y7s6NoUFeLoFeTk8jL1VUgVrb0URqXmMbvL4FWlWb1t6BJZILhNLa+rYcXFlSUUVycWU6EpuYmPbszsOrGZkpk5MTFnb8USzs+LNZWDISaKAWShKeHEtgJJE4WR30FCIrlcgFkwUIqhBBE8HJQmiHEETkg6S6+tExpVLcohC16LU8maCVSypvLAc3lqbiMm+iki7nJl9FxV/KS7lRAnpQh4kh1+cI6RWdsg5DN29QzzVq2oY68dMGNmCON2OSKSMHYMegorGXV9TFyB3uJMzP6D+sTH76MPdpZer9rH7GxB5WNfSLKgyiaqO0blDeMKIljHQSTcpWk7ptqIs83EUd7qYOaTtMqlagxvX0N0PCuWHx3DDADvaAhtDJrpCRCxQUnIKMlZHQKnq+kpqnpGAVpFxhE5haGMMqjVO0I3TMIoO0ZrSrfXqAMzcqm59SL85qPyz0flzsX3nbC+Dj/Yx2YUr74Z3h7z/N/us/FgdHeUwpkiqBMWVonhIn6SyTqMu4chxbnk8Xw5kiBEeWJ1CVcWS5DDGYLgYLVLlcOUautRwFZ4klf22+CDm0V48uP7l64umN757d+uHp7bNRDy6mvr4PAUej8pNAOdF7d4QB7Ngc4rVn27o9282bhj2KvIQuiS2oT6pqy64igEub0/NrEksbM2tac6qa4PWtmCZifgupsKkVh4LGnN4WdDT46yTLb6MdfnYHvO12utlucrQOtLNyt/5mg73NfX+35DAv8EZfMzvM4xyBkI1+Geu809Z6mTch3RIA2ewPuOS5v+tD83n3X/3hae4nvev1dT2tt7mNFGCHubHDw/6Kh3mo46yL3Qknmx121qE2VifWed/Y5Xdxq8fhIMd9QY77Q5wOhDjvC3HaHeS4K8BhV4Dj3mCnQ+vcvt++5vLBtfe+jXhxcV/MtcOvbx5NfnwaDY1qbi2uw5eg0JmP717aFh4Y7O0S7OuUnnaTw0P09Vd39VYz+RiWII/Jy+PwC+TKMm13rUyNFclyqSxsRU02Oi8BnPPq0ZPrqSkJE+PjFnb8USzs+LNZXDESWZkEemILJbGNnk7m5VB52VRhOlUIJvHBZD6IwgeReWAqH0LhQ7mKktFxWZ+OrpMRujuZYnYdtQlNrEO0VGaXw58UZt0tyrhTmHazAvq4Ie81sRrE7SiScho6ZSS9ktwjb+uTNwx1EaZN3Ldj8rdjstlh8WQ/c6qHPCir6ecXD6haZic07xeHPq5MrSwOvh2Xj+s7BhT1BlmdSdE0pGweVDQPdRKGOtuBq1HRYtIQTZ0kg4pgULT0y5oG5PhBFWGijzVp4E0ZeCN6Sr+yRcuv6WSWKSkFSlq+hlWo45Z08Su6eWWdtHw5Ac4oiaUXvRQ3ZwEf9svqRrs6pk38uXHlwqxuZb7v46JxdWnww/zA0tvexdnu97O9y+9Mv6zO/Mvfl5ZXhnjyQroEyRCjGKIcnjxXqCnhKPNpEgRFAGYIkHQBmi3FsiXmg2cp/CyWNIcmzBarqv/nv//jfx9qssQSS/7/5YuIS3/16MqDy8fuXz5x/8qpB9e/j3lyHZL5CoVIweSl4YpA3x7fuSnYY1OQx+6IsN3b1m/fEvrjxUMgTCSqPCavKjG/OgVVFg/Jj4bkxSCKE9AlSYU1maUN0IqGnPySLFDWi5Pbg46FOp8KdT4R5HjM3/6wr90+L9vtrrbhjtZ+tt+4W32zz83+WZBbUphn+jrzgAcqIgC2yS91rVd0gNtvDRzP1rjEBnukrPVKCvV8HeQO4OOR728rWcyDHLc8HW79VzPp1xkWgB0O593sv3Wy3W9vvc7Gyt/G6liY+7Ud3gA7gN9kb4BZHvvM4xwOOwMdd/g77gxw3B3otD/U5VSEz8X9Ife+3fr8wv7oa4df3Tia+Ph7SGZkYWlOZT0OVwi9f/PHiDD/EB+XED/n9My7LaQsrgSl6SnX9ZZounECGYonBut08K4utFiewRXBqWxsBz23pR3R0JSTmPQkPS1xfHzMwo4/ioUd/+d8+fr+t7hsbGNmtjKSO5jpJGYahZtF4WeQ+Uk0EYghgjLFwJsomMXPYfMRdB6UIUH0mVijA2I1r75T0ibjNJAbzKexUJsxReD7xZm3SkE3K2B320oSKXVQRmuuiF4pYVUpefUaYYNe2tgLsEPXPjXAfTMimxuRvhmVTg9wx/XkIXWTQVozpCXMjKnezvXOvzUsvOmZMglGu0kD8gaDtH5AiR9Q4A2SxgFl64C6zajE90oajEqCSU0cULX3Spt6xQ390iaDvGVA3T7QSRrR0we1JL20QSuoVtCKZOR8NbOoS1jZK6nvE9V1sQvVRIScAOVUJTBLX4nwoG5e5VAXedzAmR6Svp3SvJvpWp43fFgcXl0e//njxOrSyIfFwY8LQysLw59WJv7x9+XVn2bVvU1MOZytzGPJ83hqHL+zmKPCAR9yFTiOtIAtLuLLi4SqYpG6SKguFGtLWDKUusdyJosllvy1AdjBiH545db5o7cvHr9/7fvoJ9fzkKlNtdi6Wkx1FaKqGnHz6rfb1vttCvHctSV099b1O7asPXp0e0zqjVTUAyguCoqLzURHgTAxGYio7NzXiKI4ACLFDZklTaD86jR0QcK3e9ceAdgR5nw8yOGIn725scPTdqurTYiDtbvNNx5WVsfcHJ8HusUHuyeGeGSu94ZvXoPcGpCzxT9tnU9UgNsDHyfzmlhPh7te5iUtX89k+drYYd4u3SyP38xxw+N3dgB1yd3+tIvtQQfrCFvrQGsrf1uAHW5Xtnme3+J+Isx5byBgDoe9QQ67gxx2BTrs8HfYvsYBkMfeEJfjm73O7w2+dyrixcX90VcPxQDsePIDEhZbWpVbXpOXnw+6ffX7TSF+wV7OawOcMZjnNDaSzIK2M8FsCYIryQbMwRYkMjhP+aJXAlmCuhsjViLpPJj52CkaKjLyQnxczLhltOOPY2HHn83CkrGdDmmnZ1E4WXhKfF17DJ4WTxeB6IA5JNlEbkIHK4XJgzC5CCbPvGpUpcdPjndqxHglv1YlaOR2FMg4dUJ6eQXiKeb1DxXZt1uKXtObcliEXA6pSEgvE1JLRdRSraAOEICOV25QNo/20GcGRTPDYjM7TLyxHvKwjjSgajVpCJOD4plxzexk58yEYszAHlDhjbJ6g7ma+iQNPYJ6owzfK2vqlTb2SZv65S1AGRRtJhVhQNEyqCYA7OgRN2pFdXpZU4+sSSes6RLVdglr9aLaHnF1n6KuT1LVzS3qpGJU7TlKEkzamiXBZ8na4V28iuEe5syoam5C925Wv/i2f2Vh8POHyV9WZ//x67tfP7/5tDz+aWl0ZXF4eXH415/n/+1fP45OicTaYr4mjynP4arzOMpcmgRCl0KF6mKJpkLaWSXVlks7y2WdlTJtiaSziKfAavs6LNuFWWLJX5ovQg795YMrdy+dirx9ISn6QSk2k4Qv5FKriS04fH1ufQ064dX9vRHBm4I9d2wK3r113c4taw/u3/r01fXojJvp6Cc5BbGw/FhUcSI0Nw6GS8gpeF1Qk1ZQk4osi8NUJOKqUi59v+NQiOuxIKfD/vb7vGx3udtsc7VZ72jtbWtlb/WNq7XVXme72z5OL/xdXwW4JYd6gDf6oLYF4PaEFuwJQ+0ITgv3fRno/sDX+aan4zV3hytu9r/VNTeAGo7Aw69l/1szKVCX3e3PuNgdc7TZY2+9ycbK18rK38H65DrXyxEeP25yOx7mtC/Yab+5zBMru4PMQx0AOwB87Al2OrLB/eyewNsnt0T+uPfFpQNR144kPbuQi0nGFoDzcOA8TMb18yc2BHoHeTlvDPWAgO/VNycRyFmAPNroqfXtz6nspNaOFyXVlyisSKkqWW9AqnRQBi+Vyk4mUpMvXdod/TLSwo5/Egs7/mwWFgfIVBiJnNVBTW+jpbezwFQ+gi6EMYQwqgDUwozD0xNJ3Gy2EMUU5jBEWUp93ewbQ6+WLmWWKjiVEnqpjFMrZtfgS1JyX5+rgT6gVKczWuEsAoZHLuRTiwSkQhG5QEYrUNBwnaziPmndUBdpyiR4MyZ/N6GYGRSO99CBGtSSDMoW4L1/zCQeG5SMmQSmLnKfrKFXXN0jquwRVekFVRpGqZ5fqxfW60UNPdJGPQARSUO3oK5HZB7qMCrb+uT4LnGdkl3eCbxM2tQtbeyVNfYrmg0qvEHZYJBXdzJyVSS4jpGvY+E6mVgdB6emoRUUlF5UM2YUzI7r5ib183OG9/MmgBefP07/8tPc33+d//svi6sfpz8sjX1cGl2aN61+nPq3f/nw9p1epq3kyBEsKZQrR7OkcAo/jcrP4EmxfAlOrCwUKwvE6lK5rlqsLubJUTwFWqO3jHZYYslfmy9CLuNV5K24Z3fgma8rC8C01kIJo0ZEr6ITClpq0S11ueC05we2h23+eiDcroi1ADuOHNz+/PXNp4nXEqH38iqS8suSyhuyiqrT8yqSC6tSqpsgBVUpsMJoRMlrTHn8w3vf7g/z3O/nsMfLbqurTbijdZiDta+dtYuNtZONlauNVaCd9UFnu2tejk/8nKP8XZJCAHn45u0MKd0fXnlkU/mRjbh966BbgxLWej/1d7vn7Qxo44qbwyU3+0uu5rpsVoid2SLmTlL78672p51tDzvY7LC1Xmdt5WVlFexi+12429VtHhci3E6sdTpgbuxwBArwx3/JA2CH4+6g39lx88TmR2d3P724/8XVQxlxtysqkWXVubn52Rhk+qUzx9b7ewV5OkeEr0mIv1nXkMLkwEQylFCew+SnEiivOmhxTYRH+PYnLH68SguVqeB8STaLn9rY/OrIkXVPnzywsOOfxMKOP5v3S4MsDpLOAjE5EDoXRuHCmMK8DjaYws2m8aEdfDAJIIjYbA4KL7WdE6vorn77zjRokEjZlRJGmYpbI2FXq8R4Bh5VDrpNKIjltWFYbSh2G4YPgINeKiLjeC0wbnO2lIzpFlQZVa3DXdRJo2BuXPF2XDFjEo72MGYGRSM9zF5Fy4COOtTHGezjDAEfypu13Aotu0RNL9BxS3XsEhW1oJNRpmGVd3Iq1fxKDbdCx6vR8Wt+k0eXqF4va9bLm7vFDX3ytkEtxaBu71c29ysbgOoVV+lYedK2bBUZ0y+tNyga++UNw10dvbI6JaNQK6gZN0onhzWTw+qF2f7FdwML70yfP03/+vPbf/xt4V/+tvT54+zHlYmVxaH5uZ6V94P/+o/lhUWDQlfGkeewZQi2HE2X5dBkEIYcypYiJeoSpbZSqS6XqUtFSpxAkc+XIQVSlFrXZhntsMSSvyL/q0Hqi1jATnn1pCIfzGgrpTbl8zqKlOwqBbNKQComNWDITfnFmJRvD22JCPXeum7Nri1hADv27dp07/H5NMRDVMnL2uYsfCukoTm7qiGzsiGzsQXWSsRUNUIQhbGQ/ChoQXRM3LVD24J2rXGMcLcNdTSDw8ve2s/RJtzDfm+A03Yfh0BHG28bqw32Nt+52d/2dny+xiXBvJjWF7U1sHDv2srDG2uOR1Qfj6g4srlg33rEdvP4R3SQ+yNf59tejte/rpU1y+Pr9aKb/fcudoA5ttlah1pbeVpZOVtbBbrYnljrCrDjYoT7sdDf2GEe8/jKDmdAGzsAdgQ47gp0OhzucXZ3EMCOhz/sfHJh39Orh8DpTytqcmuaixvwxQX52edOHQjz8wj0dD55NKIIFyURYZQKDJOVyeJm8YUgCi2JTE8mkF5V1z8urblXg3/WRk5kcLKJlEwM9nlERPCTRw8sLaX/JBZ2/NnMvzcQGelttGQCPZXKyyIyk1vIKc2kNCIDTGBktrEyKHwIQwQjcZMJnNgWegxPnjs+0WnsF3bKCWpxs17ZrpE0datJAlppW2kCvx0r5dRwKMVsUiGfVi5lVQrb0fSqZF4TWMOpNGiIA9qOoS7aWC9nekg8N6qYHRSN9XOnR+RjRoFe3mbQkE3dDIOO0qNo1fCr5NQCcRtSgM+REjEiAlJMQIvbsaL2XDEJJ6UWikn5ckqRilGmZlUoGWUyRrGaXwN8YZ+K0K8mmbSMfiWhV1bbIyzrYhUoO+BSQk4nu6JfSRztYZl0xOFe1uSw2KSn6cT1Sl5Fr5I4MSSbHFG8ndW/nzctLY58Wpn4+fMsII+//7rwy+c3nz9NrbwfejfbPT+r/9sv88srIzJtCU2cRpdnsZQwtgLOVaL4agxbniNUYeXqUpWmWiQv4ElRAoV5i3SWANXVR//3//gf//0PYIkllvzf54uYz8xMeFFXgpKzm9ScOgm1SEDMFZNxEloxowXDaMESapC3zh/dud5/69o1OzeH7dgctn1T6NWrJ7IQjzAlUdV1aXRqLpEAa2mD1DdnN7XktJPyGprhuLLUVEhkIuhhGjTy2tUT29f6bfJ0XOdmt9Hb/liYy/XtXo8PrnlyOOD6Tp+jwS7hrrY+dtY+NlY7nWzPezgApIgPcgeFeyMjAvJ3hZbsX196YANQxfvW43aForcFQjetSV/v/TrY/bGf801Ph8vu5h7SH1ztjznbbrWzDrK2creysv3mGyugrKzc7a0Ph7pc2e5xydxSap5h+WoOx30hzvtCnXcHOu3wN7Njd7DzwXUep3cEXju66f73Ox+d2xt59RgKnlBejS2rza+sxaLgKacO7w7xdQvwdLp59Ri5A6KQF7A58Ka2hHZKGpmRXdMYh8q/j2/PaGhJhObewBTfrcfHtbQns/jwopLYbRGhz589npqatLDjj2Jhx5/NuyUjkQlqo6bgicmtZEAbGQRaqrnVgwsjstMI3FSSIJPASW1jJ+CZr+pJL9sZaf0GrrGP360mG7pZYyaxqYdl7OGqRXgBKV8jbJKL8FxmJZ9RKWZXSxhlgnYEoy5NQsb2yPCDXTRTF8XUSR7S0SaMgplh6dSAYLSPPTumHDeJu6QtXeIWQye1R0nsFNbJGcVCYi6zFkSrTOc1QjmNEHZdNqcexqqH8FvRYlKemISVkHAycrGUUqJklKu5NTpxc4+8rVPUqJe3GztpRlWbXlzZycyVtGZLiYhuUcNQr2h0QDZmEAz10CZG5LNT+lGTtF/boZM1aAQ1hk7KmEk0M6lZeGdcXhgyt5GujH7+OPHrT29+WZ37tDyx+NYwO6Wdn9P//NObj6tTiu4yhiSVowTTxVkUXgZXiuBKYAwhmK9AC+RYngzDlEHYSrCgM1fSVSzU4NQ97ZZJFkss+UvzRS7hw9JjKwsRcj65W06T0CtJDRByA7i1KqOxNKm5PIXSiIh+eH7v5mCAHWZzbA7bEh507odDWeDHiLxnRWWxjQ1pdbVJNXXJlbUpldXpVdUZ9fXg6rpsVN7rTNhTCOZVRvbzi6f3/LDN79oO78jD/nGnQ9POh2de2Zx5JSLlwpaob8Ov7fA7HOiywc1ujZ11mJ31IRe7uz7OCSHusM1rCveurTi8qeLI5uKDG3N3hUG3BKSt940N9nwe4PbAz+WKp+MZszbsDjra7rK3CbexXmNt5QWUg529tTXADiButlYHg52ubPW4FOFx9Gtjx4GvtT/UeW+IebRjp7/jdvMki/OBde7fbg+8fGTz3e93P/zxQNSD82h0Gq4UVlyFKq/GpCY+P7Bzc5C3S4Cn471b3+MbIUQCFIt7AYbfK6uOr8NnpGbfi0m8UlmX1NaRVVQRVVb9mkTNwROyGvApsfGX1q31ffXqxdzczBfLUXB/EAs7/mwWlgepnBwqF0Jiggm0LDITSmMhSIysNlpSMymumZHUwk5uoCe0MlPxjMQGUkwjMVGnpxj6hAY9e7BfND2umxzXTk3o+7QMGauiS0FUiFsl3Hopt1bGrZazS6X0PCEZreZW9ClbTTrKgJZkVBOHtNSxPs7UoGS0lw3czI5pJgdlADs0/LpuWUufkqhkl8sp+aI2JLM6jVaeCBS9Ko1Vl82uh7LrIbwWJK8VLSBiJRSchFLIbcOKSYWd/PpuKb5L0qIRNHVJ8H0qQp+8UcMo4OPBKmbZgJY51C+bme4HftWxQeWQQTg+pp2c7Bsf6RoyirrVhE5xbaeovl9LGhmUzE3rF98OvH/btzTf/2lp5KePMz+vzn5cHgfYMTfV+Xame/XD1OpPs0pdJVOULlAh+QoUSwhmS2FUQSZVBOMqc5kSGFUEbhcktPGiycIkmiiDJkzv7MVb2GGJJX9pvmgUYiw8s7a8QCERDhn7hvo7JRx8cwWoHPu6DBNVgY0iN0ATX1zdFxFmZsemkB2b10ZsCLl9/UwBLq6sMqGo9FVu7iME/HYe9iGu4CkS8RCowoKY6qoUHO41BPaivALWUo9Dxd/JuLE7+/o22K3tqPu78x4fyH96OP/Z0fznJ/KenUQ+PJp8afed/aGHA13XuZjxscXR5qKXY3SwO2izf0r4mqgQr0f+7nd8Xa57O1/3drnj5xYZ7PVqnW9CuG/8et+YUK/IQLcbPs7fudjtc7DZ7mi70dPZ3c7G5is7XGytDgT9xg73o79vF+Z8INTZzI5gp12Bjtv9zItZdgU6HVgLsCMIYMed7/c8unw8Nf4xGpuJLQaXVCPLK1HPn1zftiE4wNPF39Ppzo1TOOwrNCoqNu7mk6cXMrMfYQviouJuxsTfhKOew9GRUPjjjKy7uMJXZZVJJZXJFy8fDgz0SkmNezf/xnLw/R/Fwo7/Y37/p7O8Msbi4ehcFJXz9eA3DpTGgbdTUvGE2Oa2ZDw1Dc9IaqQlNtGSm+gJTZTXRDpIoqgxGkWD/fyRAeHsVPf0VO/srLG/iy3n1qolrQpJm4xbJ+dUy5jlal51p7BWyavukjT3q9sHtGSDhmTSksd6WOP93EmTaLSHPaznzI13TQ1rNMJ6ObNEzizVS5vl1GI+Hs5vhjJrMkmlieTyZFY9iN8M5+OR/BYkpxnBbIYB7JDRSuX0Mi4By25Fi6jFwA9V8xs0gmYVt0bFqVAycDJKfqegfsQom57on540mp0x2j02ojMZ5aOj3aNjhomJwYnxvkGDoFvRohbUaoS1fTryxIhybqprwbykpWdlwbi6Mrb6cWp5cWR+rg8wx/xsz+ry5OpPbzRdDTQeoA2UQJnLEkE5cihbBmbL4HwFQihHSVQFXBmaKcqm89KpnHQyN02pa/yfFnZYYslfmS9albSsAEVqa1JKxb3dXUMDhrFhY7dGTG2rrCqElKBTG8sR8c9v7du6dkvYmu0bg3dsXrd1Y9jlcyeg4MjCotfY/FfZ4Edg0CMkLDIX+TQr4xYk6w4a/qi04HV9JZRBauhWSNQcGrEEUpl2oyz+h7KE7ysTfqhMOleVfL465VJV6pWq1GtVaTfKk67lR59Pu3bg7oG1R0M9wlzsQuytD7nan/NwOONi952z3Y/uDnf9XGJDvcBbApDbgtDbg/J2hBTsDgMqf1cIansgaKPfU3+38+72x71cNrg7Otv8PtrhaG21K8AJMMfFLe5HQxzNR8GFOgP42BfsvCfIzI5ta35bQ+t0cJ3HdzuCLx2JuHv2UNSDS2BwPCIvE1mQjSmGIdHpN65+vzFkTYCHc6CXy5ULJzIzn0EgL9PSHiUmP0Bj41va8qDw2PSsZ1Gvbz2Nuvwq9goE/CQfm5AJepYNe3ngUERQsF8OAvR+acHCjj+KhR1/NssfxnnCUhYXTWWAyCwQiQvpYGeRmFlkBvB+iezggvGMlDrKazwzCU9PbCLFtjNTKWyYvpc5bBSPDIjnpvVT4/rJMV2vlqGVEdSSFoWoQUgvFlMLZdQiDb+6R47vkbf2KNuNnRSgTFrqUDd9tJcz0ssaN3CGuqimLtb0mG5mvEsrapRQ8gVEjIJRLOnI4zTmAPJg1mR1lCRRKlKZdVmcRqh5nKMFxWtF0+uhzEaEgJgvZ5YLKIVcYh6vI19ML5Ewy4AnMnqxhJQvpxf1qTqGjdLJ8Z6xsd6R0b7h4Z6hoe4BU6dSzdb3KPoMusERw9jk4PhE/6BB0qPqkDFK1PyqAT1jYljyZkoDsGN5oX/1w/hPn2Y/vB9femdcfNP7bqZnZWl8+eOYpqeRA8BCguTIkQwxiCZKJfETyZw0Gg/ElcB50ly+HMOTIZkCCEuEYMswiq6Wr7uUWv7TWmLJX5UvPV2ahqoSOpkol4i1KmWPrtPY1z1i6tdr1Wwqpa2pubGmNisl+fi+nRFrA7aFBwPmWB8auHNL+PVLP0Q9uxcT9eh1TGRackx2Vhw0Oy4HnICExpfjMpiEMq2IadSqBjpVOj6D31xCKkhsgT/EQ27joXdacu615NzH5zzEwx41wR43wZ81w182waNqsh7jXl1JvXb4+p61e/3d1jnZhTvY7HayPe1qf83TfMxspJ9LtHmTD4/UrxuYQjb5ISL8MTuCsTtDENsC40K9rns5/hDqF+TqZGtlZe7t+OYbO6tvIvwcz0eYRzuOmdnhDNSBYMd9QU57g5x2BzoB4NjpD/jD+VC493c7Qy8d3f7g0qm4l/ezwPEgeDIUkwFDZ4LBidcvfbch2M/fwznE1/3W1e/RqNj8/GQcLrOkHF7XlF/XlFdWCa+qQRcWQ3LzU6E5rzLSn+flpsLhianp0Tt2bl63PiQXh1haeW9hxx/Fwo4/m6WVETonl8aE0pnZFCa4hZKOJ6cR6SA6F0rjQVuZSS2MpLqOuDpiQgMhqaYpuo2RSubCxIqGsSHFsFEyO6WfGuueGtUNGsQGHVMjbhYzS4UUnIxZomSWacX1Bk2HUUs1aMmmLhpwM9hFB+Qx2M0c6+eM9bHMi000tKlR7exkd6+iXU4rEBHRvDYEvxXBqDU7g1kLIpUlE4riKRUprPpsThOc24zgtaIYDTBKDYiJhwsphQJyAZ+UzyXmstpQjCYopwkibEer2OXGbtb4iHZ8tGd4WG8wdur1cm2nWK3iy+QMCqWOzmjm8ohyJa+rVzU2PTQ5YwJerJM0CzownYJqUxdlEpDHpHphVvdpefjn1bmfPs59fD+yPG9YfNP/YWlscWVY2V3PFEMYUghNDCLzksi8ZIowi8TLogPOEMPoYiRJAKYIIUCRBWCmDKH6fQGt5T+tJZb8Vfky0K9vri1vxzcIOEylVKJVKXQaRV+3ZrC/e3jA0KXpUkg7ye20m5cvbN8QvHV90LYNa3dsDj938mhOZia+ronYSuwgkNpb2lubWvD1zXWVNZUF+YzmKi2fZlBKBjRKo0qmF7IU5AZePZpRmkotiKUWxFBwr0j50QTMSwImhpgX345L7ihIIxVmEHFprblJDbDo4sS7mXdO3Tywfm+Ae7irQ6ij3XpH293Otqdc7X90d7ji6Xjb2+m+r/OTNS4xwR4p4b7Zm/2Beh3iedPP9eyGID9XJxurb4Cy/VobvB3ObnS7tNXjeJjToTDzaMeBIMf9QU6/DXgA8tgV6LQnxPXwRt/Tu9ZfObn35d1LEHA8Ki8rvyQnryQntxAKR6TdvnbWPNrh6bI2wPvOjbPwnNiKSnh1fV5VQ35ja0k9vrCyDlvbWNSEL8MTzFVegUEgUpCo9ITkmK1bN23aHF5clv/h47KFHX8UCzv+bJaWB6ksGJ0HoTIzaWZ5QGmsHDoPSmdn0dkwAiWlmZRIYkObyWlVja9q61+2UtLamWAGDzc8LO/v5kyOqAZ7hYO9gqF+Ub+WLmVX8kj5Mna5hlepZJd2SZoBcAzqmaZuhqmbNtjDHOrjAPdmdhi4kyaBsZPUJW8fNkqnxrQDOqaKXSYkofltcE4LjNYAYjXD2M0IgBeEkiRiaQK5IplWm0Wvy2bj4cxmWEdlakdVKrMVwSFi2e0YOh5Grc+i1KRzmqFKZvlAF218VDc63mc0aZVKDo/b2tZa2FiHriqHlhdllBWkoWFRSMjLUlxmTSWSy2vv6VePT/SNDEjVvCoRCdMlqh0fEMyMymbH5EvzvZ8/Tq5+mP64PLq8MLD0zvBhaeLT52ldXxtHnMMRw5giKEsCFigxfFU+XYxgSVFkXjZHUchRFrJkWJYijy7GchQ4VVerpbfDEkv+unwxs6Ovu7GqqLGqhNbeIuZxlFKxWi7WqqTdnfK+Lo2pv9/YP6yQdT65d3f7hpBNoX57tm66evb75/fv1JZXalTdwGdNxrEh08SwaWLENKZXSOW0VhUV38kidvEYehGnV8ztETK1rHYFsVLchOXVQFhlacyyVGZZGqMsk1kBZlbAGJUIZhWKUYlmVKHpFQhKKYxUlE3IS6uDvES/uBB7fs/9Qxsu7Qj9dlPA3lCfcC/nEGf7tY62Gx1ttjra7HKxP+jq8J2r/TUPB8AiV4K89gb5ujnY21t/42z9jZO1ld033wS72p1a53J+s9uxUPNoh/kcOPOp9857zexw3g1UsOu+dV5HI4LPHNh6++yx1Je3igtBACnq8EV1LUXAtbgUcefG+Q3BZnZsCQ+Kirpb3ZBb3YgpqoBii7MLKhAl1eiCcnhJFbqqHldejS6vRjW3FVdWoxCo9GfPH27csH5LxMaS8oIVCzv+OBZ2/Nm8XzFReRAmP5vDy+EJ0DxRLpWdRWKlUFiZVDq4g5JCZqbReNkMMZzGh5Po2Qwems5DsIXYAZOoW0PtVVOkjAohpVAnJ3TK2kTMUgmjolPYoGKVSmg4taBOr+4w9XKM3YwBPXPYwBsdFI8YBAN6xriJPzUiNXZRtdKWYaN4clQ73C/SSRpE1DwprUBIyue2YzgENKsFQa2DEMvS2ktT2osTSBWpgELoDVBKPai9MrmtIonSAGER0Mw2FL0JQqlJAzii5lT1axmDBtGASdHTK5FKyYSmvHJcEizjFijhUmb8paSo74GKfnT05cMjsc++TXl9MQ8R09ZcoFTQjT2Cfg0R+OU13PJxk+CNuYFUu/DWzI5fPr/5+fPs6srEh/cjnz/N/PzLXK+RwhRCOWK4QI5mi8F8OZIrQ3OVaI4cxZYg2fJcniJPoMiXaEpEqgJRZ2FnD8myb4cllvyl+WLo0daU5AJFaKyik9p4LJqIx1SIuWq5oEstBeTRpelsacTfunJx5+a1gDzOnTr25PbNqMcPcRgslyNSq3u6dYY+vcnYN6hXyDnNJdyGPCG+WNxWJSM1KqmtnayOLh6li9OhoTcD8pDgccJ6FK8WyatD8usx/IY8QSNO0FTIbyriNRbxG4v5jUXc+kJ2TR6tHNFRmNWCjm+AvqjJeliReqc4/ir62dmUqweffrft+v71R8N9InydgdoX6PZdkPslP5fLfq5n1wWE+7i72dl42lj52Vp52QLy+Mbb3vpwsNMPG1yPBDkcCnY6+L+tZNkX4rIH8Eeo+6GNfid3rLt4bO+jiydTnl3LRafmFUGLK9FlNdiSSkxBAfzG5bPhwb6BXi67tq1PTH1WUAFDFqZB0HEgRGw2KgldBAYhEsCIODg6EZWXnFeUUVIFrmlC5BeBbt+9HBYaFBGxAYtDvV9a/E/LSpY/iIUdfzbzywYSO5PJBzFZ2UxODtfMjkx8RzSRlkhhZBGpSQRaXAsthi4EMwQ5BHISlQlmsnOoLKium9yro4qZ5RJGGaURruDW6BREMz6EeC2/UUrBCclYJb++S0U26Nn9OuZANwcAh5kdRuGgeTdSwfiQ2KCl6qQtQ/2CyfGu0UFlt7JNzi3XSfEaUYOQWsLtyKMD7GiAUGoh5BoIpQ7CaIQym3NYrQhKE6S9JoNQnQ5cyY3ZlMZsUnUaswmi4tX2aDr6ulk9eo5KSxKKG2srMnJSroFizyY9OxofeSjx6bHYJ4de3tvz8v7e6EeH41+cTou/CMv6f9i76/BGrjxf+Ls7M2kmNzMzY5qZGdJp5m63mUmyJAssy5Yty7aYqapUXKUSo9nN3B3oYHeSSTKYmR2e3dxbcu++77Pz3GT6uTc7/6y++T31HJfKbcmOXR+dU3XOyUZ5vsshD3iMXVFHh18XJRoe3SJefnzzq58++fLzx1//9Nmvf/nxb755+ctffPTl50+/+vLpL3754bvPE56ghPBycC8H8ZSRfgHGcPEgn4iy+KhGAzxPTOaNyP0xRSBR74vLWrudKXakksoPmm97OuKKao5cWKZT1tiNLbDLQsBOhnCHvWRr2NfdGu5qjbpslsLc7POnT2SlX+NzueIqIb+yks/lScU1bCnkSpVSrVHU6ySFet5FE/+iRZjuqClAlHxKV+uzKkMuTQwyJGBjAtLHAHXU2RS2KYKW+qBNEbIrw86WsEsVdqlDTrY0QYc6aFf5rc1eo4LS1aDNfEBe6pAV2KtzrKLrZsEVPeeMqvi4MveQ9Mr20uOrL2+be3Dp+B3zxuyaP3bXgolLJ48cO6jPhP4/nj7gR9MG/Gh8n38Z8qN/Gvgv/7RoVN/NUwasGvvGinF9VyTvZ+m3YuKAZRMHLJ0wcMmEAUsnDV41c9SWpbOObF55ZveajBN7sq6cy8m6np+XlZOTfv3a+X27t65evmjquBHjhg9cPHdqRsZZkbSCLy4t5eUVcbLYbZW0nFNVwJMUcqpyBdLiKmmhoLqgRlFRq+CcOLl/6pQJW7euNVs1X33901Rvx3clxY7Xzccvb9ugQpc7z+UuQKlKjBICSKkTLnBhxS6s0ArnGIEMnfWyGcgCsBKnOw9AikGU44CLaZ+8pwMkHNUM3IC76gKUtjPu6o7Z2xh1HGsMueVBtDHmNSWCtvaIs5vFRwd+/wb98DZz/yZ1twt/fM/38I73dgfSFXfeu+V99qz7yZOu9jgQZlTtIUtHxBYiVV64ngHrGKjOA9QSjhrCIUVtVahdREO1pEvqtvCd2hJLU45Bfs0kvwqqCxlAGg8YW+POQNDgDTZ7ovVMRKmQXy29vqXk6vq886tyLqwpuLot59LG9DOrMs6vzb60qShrP6/sVG11pskg9gecra14xG9uDxqjpLIrbP7kedfPvv7g5z/7gKXGL372/m9+/fKbX7/48rPHn39y/+ufPv3si/u+iJzy891UiZsuJ3wiN8XFggI0KCCCQsovZII1wWhjgD0mUEX6eIl2619SgyyppPJD5tuu9qhcWFLNyVVUV+iUUpNG4TBrYJfJS0CxoKcjFuhpj0aCPoNOp6hvVLVo9DqDyWQDQdTtxgEX7HK6QQBB3KihUSrLPlJ9aZPkwnrp5U0NWXtYHJiqrtilOc7aIreC69HLAjZlxKmKgeoooGa3cUgbg3Rx2BB3G2KQMQabE4g1gdrYisGWCKhnIeKzNtKGWlIrwVVVqJLrbih11eY5pFk20TUT77yu4qS27HhzwQHx5S15h5YdXzt914Kxu+aO3Dc/bdfswRsm9V80/CcT+/3LkB//0+RBP35zfN8Vo99YMqYvW0vH9l0yrv+S8f0Xjx+weMLApZOHrpkzfteqeYc3Lj65fUXJtXMui53EaNABGbXGOmnNqWNHVi2eP3Xs8Imjhq5YNP/axYsVJWV8Do/HFRTlF2ddz87OzC7IKyhk/ysoKCouKiktKS0rKSkrLC7J375ty9Qpk48fP+wEbF9+9WWKHd+VFDteN599/hAhqwC8DKI5sKeS9EoRohLEy2xwiQMpNgEFOke+0Z5ndOYAJBciBRBZ5cKEVFhBBhq6OhDYLDQpC8wqDmyXMZA8hNVHsfoIqgijihCujDH6qNeQCJg6wvYbCehuZ3Ko5XYXdqebvHuDut2N32x3d7eC9+9Gnr1z996D9mjIGfToQrQm5jNEvLog0eyDGzxgHQ3UUq4azCaCzTxAz3Wb+G5z0hx6+ZVGwXEF/22V5IJDUww7RBShoD0KjJASHhEdkXrjDSDEL8/eXnBpbd7FdSXpO0qz9hZl7sq6tCHn8ua8qzvK8o7WiDJVzVwPY71xO3jvSTyecPmolijVEiOan971ff7y4ddfvvfl508+S67S8v5vv3n5m19+8tVnj3/64s7LF7dCsWbaJ4LIEoAuo4IS0i+jgzWYV4BQPASpoGmh1ytlfEKEKkM9vO6byF9SvR2ppPJD5tuOREjCyeEXXa3h5StruKp6kb65zm5oJiBbiMFbw96uROhGZ9xDkUaT1WC0sebQ6S1WG+ByIWzZ7KDThcBupFFQUnB0dd7euYV7Z5cenFdxdDH/5ErWH/KMXYqcA81Fb5ur0ll/oE0CxlgXtCtZecQhXcJtiMPGBGJpRe2tuLODAjsoqINOVjvhasPsccQUBbUhZ7PP0sAYa2mtGG/hI8oKd30JUFfgrMmyS65Zqi7quae0ZceUeftqrmxiv27RwXnXtkx+e/nIbdMHLhn5xoT+/zLijX+ZPeyNhSN+snhM3/+osSw7BiyZOGjZlGGrZozevGjq3lVzjqxfcHL7SklZUSQYvXXz3s0b97q77iRiHS0Niq1rV00ZkzZ5zPDjhw9DLrijtftG152bPfd6Om93tPW0xTvikbZIMB7wRhgqSOE+AmVgEFc16w7uO7J4wRJOWWUkFPvlL36VYsd3JcWO183nXzzGPVIQZ0lRidACf6gBp4VOpNiJcwh/LWsLkJJgjAyhJRAtgWkpRIlATESHWnxRbfct2keoQSNPLc/RNhQ41CWQgZOUB6UK4soA1hBldK0Bazxgi/lN7WHbzVb3vR7iVid6sxO/10P2JMBbHUhPB3LvXuuTdx509oTDIXeAsfppXcRnCnl0PqyJcdfj9mq3WQAauIC+nKWGtbnA0pRnVGSqas7XVR6R84/Xi842yzOtZi4ESlhzUIzMG6ilfVW4T4B5RaRHxi/en39pXeHVLaXZeyvyD5Xm7s9L3557dUfh9X28kjPK+jK7TdFz03/jDhXvMgXjLZSnzu0U0K7azrD92ePIyxf3v/jsyUcf3Pzkw56ffv7ot79+8auff/jph7e++Oxhd4+d8UsDUTn7rSO8EtJXC5M8EC7EKZ6HFvoZadBfFwzL/XGFL67ovIWl2JFKKj9ovm2LBfjF6WXZZ6pK0mWCgnpxeaOsUtckc5rVNOKKBaj2qK+rNdzZGjcaLc1N2sZGTVOzzmC02+yQE0AgmIARCnJBooJrF7bOO7du0oW149M3Tc7cNjV7+7SCPTMrjizin1ghPLeuNn2HIuegpuy0Q5qDNVd6DDXJzg+XOuzSxtymVszeTkFdDNxbSLcX7fIibLuDAlpxG4uPCKQNOlsCdqXXLKf1UpL1h0qANHHdyjKwocgpy7FJrhl551qKjsivbxOdW1N2ZGHWjmlvrxi1dfrAhcN/MrLPP4/p+6PZQ3+yZEy/JWP7LR7Tb/G4/klzTE1bPWvsliXT9qycdXD13KMbFp7c8aaqXhaLtnZ33e7qvNXedjMWadM2qzavWTlx5NDJY0eePXUaQ2mWI7dvPbp/79mjh+8/efT82ZPn7zz98N1nH773zkdssY2nj58/vPeOlw6fPnF+2+adoAv96IMXf/j9n1Lq+K6k2PG6+fLLp8GwkmCqYUKAEyKCktihIifGI0KNWEhORuQALUID9XS0hQ42EkwD6q1l4ipPtJmJKcOd1rZOJOAx2vWCesGFBv5pozzDosz2AtUhtDFMtCSCttYI0BoD2+NgW8SZCNq7ElBXAgh5tB1Re0fMdaMdu9Hj7b4ZvXGvPRBCgkEwHAYDflvQa/aRGgKSow6p2yp06Dh2dYmtpdDUlKdruK6qu9YkvdxYc7m5IdOoK7Q7uBgtY4INsTZdpFUdaW8KtzcHWmu9kRraL2F8MqMmv/DqhsIrG8qyd1fkHSzLO1SSfaAwcz+3+JRMlKFTVxG4sasHibSq2E9hwiKYLNEbz2OgNOLR3+2hP3re89mLBy8/uffJhzc/+aD7ixf3f/H186+/eOfrnz7r7LZQPiHhE3qj9b6I0h9ppoO1oUSjL1hLUAJfqMEbYvcrAnEV4a9p74FS7PjH59/+7d/+1Bu28bePfUf+8pe//PGPf2S3r/nG7q9//St7/J///OfUchX/8HwbDzEVuRcKrh4rzz7DK7oi4eRIeQW1wjK1QuKyqP0UHA/QiSDNysNDUVar02JxWWyg0ezU6SxGk4P1h90OuhxAVcH10xtnH1k8/PjSEadXjT25YvSZVWMvrBl3fdOk7G1TC/bOrji6iHdimeTCusacfZrSt02Cy6C8CFFWYi0iSl/3H5egIvbW5C0wUKfnFT6S1elxtxKOOGqNIeYoZAwBmqBTFbA3e00Kj6GO1tcQGhHcxAUbSh21eWbhFXXp8br0bYLTK4oPzrm0YfzhRcM2TOo/ffCPh/z4n8f0//HsEX0Xjem3iDXHpMErpg9fO2f8tmUz9q6adeDN2YfXzTu6cdGZvRtrqgQGndlstBsNNqPBqlXpq7i8LevXTZkwfvbUqZfPXbBZnIwnEAom4rHO9raejvYbPZ23b9+4f+/Ok0cP3nnymIXI+48fvnv/7lMYJA/uO3L08HEcpT94/skffv/H1AwA35UUO143n//0MR2oIwNSlH2/TgtIfxXi5VIhGRmUIV4RE22gQg1EqB4JVmN+KeatIYK1VIwVSQMVlvtiLZFW640bNA4pG4UXBdm76jgntHUZsFUUIpujHk17xN4RBztb3T0dWFcr3B4F2mNAe9QRZXQxn7GrDb3Rzdy8GQpHSYJ2gm4NiuoI0kiTegrXoFAjYKsGLEKHsdJu4Fq15RZViUaRq1HkGFoKLPoyl1MAYlVEQEBHquiQyBsRhxIKb1hKRcX+9gbMzyF8HCrAZWJS3FMtrnyr8OrGssydFTkHynIOleYcLM05LOFfa1FWWkzyYMDV0WXzhmtJv5gKiNxEqR3IgAG+x93Qk4A/fI9lx8PPXzx6+en9D9/rfu9p4sVHt77+/OnXXzxp6zKxX5oKi/xxeaRdFe3UhTo1kU51olsfblVGOpqCrfXxdmVru9oTqOm86U6x4x8cVgNPnz4le/Pee++xMvjbI/5rWGf89re/7ejoQFG0q6uLbf9defz+97+/c+cODMPBYPDFixcsVv72iFT+G/NtyEfkX3v7+pl9BVePl+ec4xVdFVdkizm5sqpibWM16jIGaTjixdrCTDTkdzgAixWw2SCrDXS5UAgi3G4CgjA3iIrLCs5snn906cijS9KOLB52dHHa0SXD31o64tTK0WdXj724blzWdhYfM0sPzeOfXC46t0Z6eXNDzv6Wkre1nPPGqgywgYNrahhLU9CliyHWVtzVToKdtLuTZQcDt1FgAnclMGcCc7CPRmFTBDKwRwYcGp+tmbEoSJ0MUQkBRbldmq2tOFV3fTv/1PL8fTPPrx93ZEnatukD56e9kfaTfx78xo8mD3lj/ui+LDuWT0lbN3f81iVTdy6bvm/ljIOrZ7HsOLJh0dUTh2TVspYWY4NCLamuq6qSVHIFXI4g83r2yeMnTp88VZBXWMUXScSyutrGhvpmpVKtatEb9TaHE4ERhqRCXm/My8QYT4xi//rLVRvWbT598qzdBty98/C3v/nXv/sb8T82KXa8bl5+8QgPSLEgHyCLIbwY93LxoIgM1OEBCcoIMEqIkFUIIwO9UsArBigB7JfgkToy1sTEG73RhlCb/vb9UMhr1cmzeRnbuNd3KkXX3Pa6WMAR9ZqiXn3UZ2qNAr3mgOJBR3cr3Bl3tYcsiYD1Zrfv3v1EZ3cAgHR6o9xmVwKA2g1rYUiFuFUQoAQddaBd5rJInRax3Vpl0Jfr9KVWB88B8nCmxp9ogn2VFvqqy5dNhnlEqAIPcokQn33+gTYFFRKg/nIsVEonRGREpjfm8Qr3lWfu4hccrSx4qyz3UEXB8TpJrtkoRxFTeyvR0WH3hRt9iRYmWseEJbSfh6JcwMQNkS3PHkY/f/nop58/++KzJ8k+j/c7P3iWePHhrS8+vRtvb8H9HMRfwUQkobi8tUcdvakOdCr8rQp/TB6IN7D/mi8m88frqbAkmND8+c+//dsfQCr/bfn3f//3SCSyd+/eSZMmTZgw4fLly6w8vr/P4ze/+Y1Op1u6dOnYsWMXLVzk9/n/8Ic//O1B/zWsUbZv3z5u3LipU6emp6c/fvz4r3/9698elMp/V74NMFj2xSOX396Rdf5g4bUT5TnnxeVZYk6OkJMjl5RbNPW4y+gnwHiAbI0GUQRzumCnE3a6EAQm2QIBlN1iCFEvEZ7dufLwstFHl444tnT4kUVphxYOOzh/6KEFw44tGX5ixehza8Zd3jDh2uZJ2Tum5e6eUbR/Dvf4UuGZN6svbpBd29FU+Jau8pJVkgs0VOCaaq9FyaoiDJmiLDJQezwJDhcrD7YRhe1R2BZD7BG3NQSZQ5CRPZKxKHGtDFBwjMJryoJD4gvrKo4tyt417cyaMQcXDd0yfeCSUX0mDvzxsD4/mjikz+yR/RaMG7hyxqjNCyftWDp117JpLDsO9HZ4HNu6ujQ3p6G+qaXFoNaYG5WaOnmTvL5ZXt9ULZWLRDVicY28vrFRqWL3yBtalM1GkxUBkQBCRBEyjlKtuKedYDpwTxvhacfIBKdStmTxqqzsQrsT6ey+8+tf/ybFju9Kih2vm08+v29Hy8xguhPPgelS3CtAfMkpOtwBPuSrsqOlNqwI8lYhwVo4WIsmh10amNYmOt4YaG/2tTWFusztPWQry3ijQFJ8mJe9m5d7qLE6B4fVEZ/FR6j9tKGrHetpJ7sSWFsE6k4gN9qx+7fo2zc8Dx7Gn71zKxwlHU6Vy6WGETNNuxgGoj0gjtsQtw5wNjhttQ57rdUmcgBCG1gJ0mIyqsBCYiTIwUI8yFthwTOMeAbgr0BDfNTPp6LSQKIx2tEcbJMwrRy6tZJpFYc6lf6oQtWcUZGzm5e/X1B0lFtwrKLghEycpVMLcczU3oa1thnDscZQTOmLsFyQeoNimBA47RVuM+9ma2+Hx6cPP3vx+POXjz/79M7HzztffNj98qPu1g61kyg0u7MIL0u0Sozi4j4e4qtEfEKILCNCQiYupUIiJlZHxWSJHtufk5Ojp/IPyu9///sDBw706dOnd2mLfxo/fjyGYd/DCPbvaWtr67Jly3784x+/+pRDhw598MEH3z90wuPxhg4d+ur4/v37V1dX/+pXv/rbg1L578q3Pgq5fvbAhWNb0k/vzb54OP/q25WFV0QV2byS62JObpOMZ9MpUKfBT0Jt0UAkHIEgzGS067QmlwPCUIokvQTG4AhpNRiuv73n4LLxBxYNP7x4+JEl7DYpj0MLhx5cMKS3hh5dMvzokhHHl404sWLU2dVjr22alLNjWtG+3vGXkysEZ1dLLm+uy9yrLDiqr7xskxY45eXuZjGmrfNYmgMufQg0BZPFUsMcdltCoDkAmvwuI2NTE4YGd7PIJMlR5B8RX9hQcXxR3t4ZlzaMP7Z85M65Q9dNGbBiwoCl4wcsnTRk6ZRhy6ekrZk5evOCSdsWTdq5dMquXnnsWjb9wPqlBRnXDHozCLK6Qh0OONm1Y3cDAGZ3uPV6q1pjbFHpm1t0bENnsDtBD8sL0tfNFuVnt1294OjAmU7C20X5e1C6rbhUvPrNLVWiBtLbdvv++9/85vcpdnxXUux43bz86QPEI3EiRQ40B6KKUB8X8XIAshzycpxkuQMts0AFIM2BfAI4WA37JVhQREZqyIjM7ReSsRom3tB+A+654aGxFo0su5ZzVlTydun1vYKiEyhQHw264mHodg9zq4u50UHd7qYe3gk+vhd590nr++92v//ejQ8/ehhL0AhiRBADgppgxEAQFpKwYIje5VAYTBKzTQKi1QDBw4ISPCL1d6gC3VqmrQYPl5CxJDI88TosIoRDVXRbjbe1Ltqt98Ua4z2axC010yH0tAkCHTWBdrm3tcZNVVZxDnCyd1cVHasqPVlZfLJaeFVZX2ozywM+czjW7AkKPQEh4efj/nLMx0G8IowWu6zlEVr76Lb/+bOOjz+489GHdz7+8PYH77Z/8E7bx+93dN+0434h+72CPZUsU2C6yhOpI4LVbkaEUHwskPwWIX4BEZZ4oqnpwv7R+eabb3bt2vX/sWP06NEgCLIW+dvj/jMsLxiGmT9//o/+Y+3Pf9qyZcuTJ0++v4OksLBw8ODBr45nvVJSUvKzn/3sbw9K5b8r3zK4+9KJPWcObrx6cnfmuYM5F46UZJ7hF6dzC9P5pZm1whKdUgpaVAzqbI/6Y+Gw0wE6HZDDAcBujCK9Xm+YYYI05YMBqPDKyYMrJu6en7Z3wbCDi9IOLR5+cGHawYXD2Dq0iK2hLEEOLRh6OLmH3Z/G+uPUytGX1o3P2DI5d9eMgn2zC/fPKT2ykPv28qpz6ySXttSk71IWvq2pvGyS5IJKHqKWIBoZpmvADQrSpCSNSkyvgLV1zka+qaZIV5UhzzlYdXYt9+2lhftnX90y6e2Vo/YsSNs8c+jaaUPWTB+6dmbahjmjN8wdu3HeuC0LJrDm2L540o4lk5O1dOqeNQtzr17UqHUuJ2KzglYrYLG4DEabTmc2GGxs22x2mK1Oo8luNDtMFheEBghfJ2sLijWHr5tONnpwTydKtWN0O+bpYAtAoxKZNjO7rKHJjJCJW/ee/+a3f0ip47uSYsfr5rOvHnjCNYSf50YLEbIc9ZYD3kKQLofIctjDBWkBe8oHKQ77Dh718lCaTwaEqI8HMRx3QIBHqn0Jec8d7PadkJcy2/VinaJCIc4sy2J/+bfUCi5HAs5bPf4Hd6N3b/jv9vge34s9f7fn4w9uffLRbfYU/u7Tzufv3mxtpXDcRBAmFNUDLqXFLNWoKzUars0pdhFSOCAmomJ3gIPHBWSiytsuZ9rlMFMK0rnsUyKC0kBbPZ2Q+Doa6DaZr7Mm1NnoY49prQt2NRPxGjLOIomP+DlkrAoLlWsNV/glBysLDkkqz1XzrynqijQtAoux1m6W3bwNMhEhGa6kQjxfWOiPVHsTcn+ijqREiEPUFrTc7abee9bx4fObH39456P3u99/knj6IHTzBhBMKH1tciZW54srqIiUibMUq2XicjpYzYRriUAdHa71RKojHc2JzlfThaV+a/9B+ctf/iISiSZPnty3b18WH6tWrWpra/v+yzsePHiwZ88elhFvvPHGoEGDeDze119//f1v78xm89SpU9l/v1+/fgsXLkRR9He/S+HyH5ZvaQw8/9bO47tXnz+y9erJPRnnDhZcO8ktvFaed6U8/6qQk9dYw7fqFARkaQ0zXW1xD+UBXG6XE4JcbjcIuyGUbbtBhGUHNzf90KqpO+cN2zV/GHu+37cwbf/CtH0Lhu1fmKwDSXkMO7wo2f+xf/6QvXOTtW8eq5C0Y0uGs/44v27c5Y0Trm2ekLltStb2Kdk7ey9EPbZUcGZt9bUdisJjzWVnVNzLmsrrGn6mXpxnrC7QCXPVvPSGwlOyrEOiy9s5J1aWHV1UfHBe5o5pp9eM3b9o+NbZw9ZNG7puRtq6mcM3zB61ae7YzfPHs+bYsnDi1kWTti2emJTHsqkHNi4rys7QqQ0AgAEutlCXC7Hb3SaTQ6sxqVt0Op3JanWaLU4WInq9xWJDUKqVpQbtv0H6ewhfF+ntxpkujO5AyFaEakXpdrbhdAebNK7aBr3OjEBYtOf2u6neju9Jih2vmxc/vev28SFPGYAVWsFcG5xpxdKdaJ7KfNUCFcFeMRGsYd/Qw95SG5zlRAoBvMRFliB+LuCpRAJiT6y28xbaczPEkDbIrnDblboGrrjsfFnG/vwr26xG6b17bU+f3Lh/K/T4fvT5s55PPnnw8uWjz14+fHw3EvXoEwEr4zHDSJPLJbPaJDaXxIGKIUqMMMl1TOCQ2B2soBNCOFhuJtKN2FWbJx8IVLDiASiu28MlwyI8XOkOlFMJGZVQUG21RLiKjPGIOI+MinztMiIsgD0VVEjijde5vRwiXG0yF0sqT1fzL9ZLcnQqIQTqCMzWIC1sSwC+WCMRFAbi9d5ANROs9kXq/BG5NyBF3SICqI17rbe6qXcexz98/8YnH97+8L3OZw+DXR0W2icmAjw6KCD8QipYE2hr8rc1kBEx5Cmmg0IWH/6ohAzwiVBVvDu1Jss/Oj/72c8MBsPJkyePHz8OAMCvfvV3Zh1gpdLZ2Xn16tX9+/eXl5d/+umn3z/C8r+S94J9WVdXd+TIkQsXLjAM880333z/l0jlB823JAqcPrTt4NZlx/euO3tk65UTe3IuHi3PvViWe6Uo+2JZ/tWaqlJdkwxxGqJ+4mZnwu/xOO2A1eywmR0QgPR2foCgC2YVIqviHVk3b9uctB1zh+2eP2zvgmFJeSxK4qN3OyxJkAXDDiwYdnDB0APzh+yfl5TH7jmD98wdsn/+UHb/kcXDT60afWHtuMvrx19eN/bqhvHpmydmbJ2ct2dW+VtL+GfW8M6sqzy9tvL0OsH5zcJLW/nnNnJOrC4+srTk8OLiQwty98y6vn3ahQ0Tjq8cfXDJyB3z0tZN7+3nmDFsw5xRm+eN3bJg/JaFE7ay1Fg6ZcfSyduXTN65YubbuzdWVZRbTHa3mwABDAJxpxO22UCrxWUyWnVao4F1htlhZl+yFXA4YLXWojWACJmgAz2Uv4f0dRNMJ850suZAqTY0aY42hGp3E60OyK81wWoDZHV6QDTadfPZr7/5Xep/7+9Kih2vm48/u2PHy12eUsTPsaP5dqzAhhTAnioXUQUSPPbUjvoEVEiM+XkwU4F4+YhHSPjFVFQIMgLYJ3GT/K6bxM2bEQqzkIjZ74HspvoGUXZl3rG8a9sMpsqb9yPP3rn16H7bo3vx95/d+Pij+x9/dOfDD2/FaKtdUYg4+TAhdXtq3F4J4q/GwzIiIuldTS05egKHq9Awn45WOelSB11so4qcTJk7JAD9QtArdHkESEjEJCT+Nqk3UY9H6yxEiR0rQpNPTOD2c6mYiAjzkyYI1fhblVS0PtiuDsSbzKYCMfdErfB6c30FCBkjEdqkrkZdTe0dYDCq9EZqfdF6f1zpCSfn/mJfLMlIXDYeCTaEGVNXwv34XnLhuufP2t97HO5MGCmPEKZLaX8l6uESAQkZqqHDMl9rPRrgYkE+4q0kg0KM4cGeylDc/Kc/p9jxj86f/vSnn//856w/2Mbr/MVknfHrX/+axcS//uu//l1z/K/eK0J+97vfffXVV6xpUheT/sPzLUMiZ4/t3rtp2YGtK47tXnv26LbrZw8UZ54ty7tSknulNO+qVFCibpRCNl3Yg9zoiPloSq81aFq07NagNWrVOp1ab9SZLEaLSqE4tWvN9vkjt88ZtnNe2u4FaXsXDt+/6FUl2ZGsXnawW9YZyQ6PeUP2JOXxajt4z5wh++YOPbhg2FtLem+BeXPM2dWsQsZc2Tg+c/vU7J3TM3dMz9o5I3fP7Pz983L2zsncOYPdc33btGtbJl/ePOns2nHHlo3aOz9t26yhG2cM6TXHK3Ykh1c2Lxi3ddGEbUsmbV86eeeyqbuWT9u9avbJfVuFXK7DDsEwhaIeBKHdbrKXHQCLDLuNLdBmdVnMTqsVMCdvG7YKqmSl5WKDhcA87aw28OR4SnsvNdrYbbI87Qjd5sZjDtBvtBEmG8k2ICzadeNpih3fkxQ7XjcvP78LojyA5MI+LogVuvEyAC1zkywyxC6iAqBKAbIUZbguvBD2lEMeDuGX0oEa1Mez4xVMuAEEubdu+bu7wpjbHPAisTAF2loaxNmcvEOlxfvsMD/W5X76Tvfjh+3PHne+97Tn6cP2J/ejd2947IpylfiKG2HPzSIsJMYjScrgUR4e5pMRqbe1nmmVwaFKt68CDVRCjAD0CGC/AAkKsLAED0uxSLU7JEWj1eyjlJ+H0VyQ4WidmVY4H2GqyGA16OW7fBVka423vd4Tq2Xidb64nInVeqISOiRoablQU3WlQVpo1NdHo0wkhJu1kp5OqrvH6QsnnwmdkAKeMjwoYKI1TrJQa7xuVBV74KYgbexpwx7c9j2+F/zgaexmpzUYlbMmQ30VVFhCBGVJeQRrmLCcCFWx31IyUkcE6hBaSPhk8Tbgz3/5zgsLUkkllf+LPH70oLFOevHUkcO71h3cseqtvesvvr079+rJ4mRXx7XygutSQalawbJDH6Cg9qg/4KG0Ko1S0dxYr1Q3axrrG5WKpiZFs6ZFp23RFFw7v//N2dvnjdwxn2XH8L0LR7C1jzXH4uEH2FqYdqB32GXP/KFs7Z43dHevNlh27Jo9eDdbSXkMTnaBzB68b95Q9uDkRCDLR555c/TlDZMurh9/Yd34SxsnZmyfxmrj2pYplzZMvLB+wqk3x7y1fNShxcN3zhm6ecbgtVMGrpo84M0pA1dPG7xm+jDWHGtnjtgwN8mObcl+jqk7WXCsmLFn1exjO9aJKitZT9gdMAgRKMagmIdtuADMbodMJgfgghGERFEaAnGH3e10oVqdtaiYe/jwycISAUsKNxFDqFaESqBs0a2Et5P0dhHJno821hmsNqxOj83ldbmDKXb83aTY8br55OUdAKvEPELMw0PQUjdaCqDJBVkwWgCyJvAI3TjXARbaoByQLrQQ2URI7AnXYF6uN1rjY9/WR+puPwy3dwZJzBaP0ez522VtrBVcK8/ZX6c478TLYh22d9+/9e6zm+887Xp8P367y3ezg7zdjjZwLjTXX0e8lUS0Co8I0SAHD1cA/iIyJmQSMk9cTEZ5gL8ESI7mCOhwvTdaT4QkRLSajEupeA2LEiIk9ERlVEjKRMQoU0FHJKwtgh3NRFSGB8VUos7prYBDEqaN3SOHvSLIw2NNQASEVIhnc+U1N19V1Obo1BKaBju6IqjbSGGmJ08SnrAY8ZcC3krYx6PDkkBUBnkKqSDfaec4DQIGbYkH7Tc78Pu3mY/ebetpN3lZZwQELrwYC1Qx8XrcL2CVQwRYS3FhfzkTqaf89Z6gMhTT9dym/vLX36eu7UgllR8wz58/9zJe0OWSiQUZF0+eOLj17NEd188dzUs/U5afzi3KElcWNsuFLrOKdNtCDIZALnWLRtnYomxo1qj0apWud6EWo0FnZPFRJ61JP31s36qZO+aP3Dl/+J6kOVh5sP5IY+vVmMveBcN6wcGyY8iuuUN2zRm8c/bgHTMH7ZyV9EcSH7MH7Z4ziPUHa5Fdswa9ugrk1RDMkSUjjiwecXjxyEOLR+6ZN2zHrKG75qTtmD100/RBayb1XzG+X+96K/2WT+i/YtLAlVMGr54+bM3MEetmjd40f/yWRRO3LZm8I9nJMWvf2gW7V887uvXNGgHf4XDb7MlygSiLDBgmIYhFBmgy2dmH/nMaeNRmBV6xo7CIs2f3wTVrN168lNWgNDvdfjceg8kE64/e/o8OtjCqFUIjTjDgAPwuKAjAYQhl2fEkxY7vSYodr5tPP78PoDyY5AFwGYzzQLISICvcHi6IFrngYtJf5QlIYIIH4GWwr8KEZFvRYgda4ESvYZ4SCC9209z7T2OtHQGCsCdavT4GtOhrpNxL3ILDOls2FuQGwsonT9vfeXrz8f3WezcDt7o8928FH94JAHqhC2BP7dzkxRnBMtBbiARKXd5iPMonolw8xGFP20hYgLAICFTTkTom1gAzIjwkdHsryEilJ1HFbul4NRWtwYM8JFAOB0vxUEW0p4mMyYhwcrwGDYshP59pVbAaYGLJGcrpcB0ZrvEnlMl+GpynVuWrm3gOe0uiLRyP+xBA093JdN3BIYbnoDlYQEp4BThVYkfS6UgFRnKMmiLQKqaxlpjfereH+vi97vs30USixRuS4r4q1MsngkKALIQ8FU6s3O2vcPvLqXAN6ROhVKUnKE502P6SvIE29UubSio/WF6xg8AItixGA7ck79Kpw1dOHcy88FZBxvninMvcoow6UblRJXfb9RQKOO32ZPeAEwZAzOFE7A538uYOo90FIA6n2+mCmxqbrpw4tHv51O3zR+6aP2LPgt5aOGL3guF7FgzfuyBt97xedrDbpDyGsvLYOWfw9lmDXslj58xBO2YMYrcsOHaxEJk1aPcrfMwbfJBVy/zhO2YN2Txt4KapAzdOGbhu0oC1kwa+ySJjXN9l4/osHctWv2VsTRiwfNKglVOGrJ6etnbWqPVzx26YP27r4knbl07ZuWLG/nULT+zeuGXpjA3zxudfOWPUmcxWwGR2OVwI+6KSC83YkneyJC8dtbpsdshiYeWBsi/WBeAWK8jhig4femv5shULFyzasWNfQbFAa3QDSAjunbcjOchCtcJ4HETCLneQrV5zRFiapNjx/Umx43Xz8osHpFdqBYvNrjIEFyNMFUhXeuNyJ5pvQ3IIfyUTFFFBCe6tosNSG1pmcuUaTOkGyyUXXGhw5rhI7tPnXfE4w3gglh0UaTe2VFVzL0lFZx1YMeKrhFDh3QeRR/fb790I3enx3unx3L8dfPQgcaubpgJ1bl8FEalCQzyAKWaNggUqiYgIi1Qhfj4eEhPxGiouQzxCMlSDBSSQhwfSpQ6k0EmwRinwdoqIqAgJCh14Puwvt5N5RiCT8LInfg5EC6loI+SrdlIVeEDE9HaK+FtbmFidJy4LdCh9iWbCJ4exmqb6Mr26jqbhGzfbA16ERgz370XD7Ra3V0JG5IiPY4GzXESu21PgpstYJ1k0HNgm85P6jhj8wTs3Pn7e2dlpZmGB+Lmwp8yFFlqQTCuSDxFVbm8l6C2B2S1eanRkwFR5W5cjOUtp6pc2lVR+uPSyg8FRHEeT8oAAQCauyrp8Ov38W9lXT+VdP1ucfVHEzW+SC83aRk2Toqmx2Wx2gW72WC/kxh1O2GwBHL0TiNmdbpsDZkWibGg6f3Tv7pUzty8cu2P+iF3zh+9ekGTHbrYxb9jOV+aYN3TnnCG75vayY/bg7TMHbZ+RlAdrju3Tk+3tMwdunzFwx4yBrwjyn4MvQ3bOSj60dTorj/5srZ/cf2XSHH1XsPiY2H/5hP5Lx/dfmmTH4JVTh66eMTzJjjlJdmxh2bFs2r51Cy+9faSKW3Fs1/r1c8e8vXNtY52cfQlavc1odhpMdoPRloSUycHutDncFiug01msdghIDr6w7IBqa5WnTp1bt3b97Jmzpk2esmDO/J079+UXclu0LquLccFBEA2DaISFyH9hR3KQ5XGKHd+TFDteNy+/uEt4qtxEOYTzQZSLUBy3p8KGZZvgbLdXRATFbqoEYXiEV4JSQrenCg2UE4EylClj38fDjMATanz3/buhIB0Ne2IxhkAt+maBTHhd2XDN5S5003yEqX3wJP7gXuud7sCdHt+tTs+9W8Enj9ru3KLooDw5GhKqIcM1RESK+ARkSEQEqvFQNRoUYQE+kxBRkSoAL6ZDfDosBKlikMqFqFKAKtNBpx1ktospt1MFZiQTDwtcdLkVKQaJcswncRACItiAB+qwQDUakDrwciQgQAMyLCBF/UI8IKXCLUS4Dg9K9aay5kaO065qb4/eu9cT8DgDuD4ecxDe5OdCNAcJ8KxEthnPhHyshOqtDr5Vz8dBZYix3u72vvz49v37FB2SW8kiI5xuQq47PcV2stDp4VjxQoOb/TbmmvA8B11gxTL9cXXqktJUUvlhw7KD8TAIjKEIjmMERdIkQbU0NhRlX8m+cio3/UxBxjl+WY68ulLCr+CUV1RL65KTZelteqPD4UR6Ozxgq9Wl1ZosFqfD6WbxAYCIRqMvysk6uXfrnpWzts0fvW3e8G1zhrG1fc6wHXOGbp89hK0dc4bsmJ2sneyHMwdvmzGIxcS26QO3TB2wddoAtsFuk3tYjswYuGt2Eh/bpyfB0bu//6Yp/ddP6rd6Qr/lvexYObH/ikkDlk1ILmefZMfkwaumDVs9Y+S62WPWzxu7acGEbUun7Vm3OOPSeVm1rKGhMfPyue3LZ+xcNo1XlNPcpNLqk7awOxFWTjY7pDfa9cnldh0Gg02rszQ36xqVWo3OotVb6xvVBUXlW7ZsnTNr9pSJEyeOHTt+9Kjxo0fPmzNv755DGZlFfGG9XGFq1rg0BlhnQg0W3GghDGY8HLvxy1+mZin9zqTY8bp58dkdB1hsd+U7gQoHVGF2ZVtcGQjBscNFiEcEYWV2MNOBFmOMyI3x3HSFk2Tf+he5veVoWAAHOEyk+dm7d8MhT3siFAqSKKjXNXIba3OstiLKL8aCEirS9OTd7kf32+/0BO4my/foXvTp447ubsQXq6ej1URA4G2VMm01yYESbyHh53liVZC/3EUUEiE+FRPBDN8TlaLechee58RyQbrQThVrwGtGKNuBFzupPCuWYcWzAG8Z6ONDPgHql8J+KRVRkuFakC5DfTzEKyZDNVSo2u3hObFS9omhtAAP1OAhmcMtblKU6dTSgJ+6//Deo4c3Q4wpGtUR3mrIJ0bDEpeHa8Gy2II8PDwgAfFKvaHQYammMW17FP7k+e0P3u+ItBuMcKEazFCD6RYyy+YpsNCFZjzfQZYb0XwNlG6ArtjRTE+w6U+pydFTSeUHzfP3n3soDwwhKIzhKE7iJEVQFEE21dcWZ1/OTT9dkHm+ojCjJD+zpKCoVtagUhlaWnQ6g83Yuwhtsmyg2ezQ6806ndFoSvYZmK0u9hRuNNkVDU3lBXnHd67btnDCptnDN80ctnnW0C0zh2yeOXjLjMFbZ7L4GLxtZrK2z2AryY4tU/+DHVum9t88pf/mqQO2TEt++GoPW5sm99/I1pT+Gyb3XzOx36re6zkWjk4u8LZ4LFv9k0vLThi4fMqQVdPT1vR2dWycP37Lkil71y+9cuYkaw6FQlVb28gpKT2ydfXWhROuvr1fIhTW1jY0KFo0WhNrC53Oyr7MpiatQW/V6ywajUmlNrLyYHeyBzQ166ql9dk5+Zs3bZ46efLYkSPZGjNixJgRw9li/TFz2ozlS1du3LB1584D+/cfO3z45JEjpw8efFsmU3zyyYt///cUO/7PSbHjdfPJi9tmR4HdVQgAJXZ7oVp7Ra2/YrUW2By5Fkd6g+K4SnsBwAuN9nSHqwBCys1QlhG8anRnuQNcC5qf6AbuP+iKhJnWeCDoQ2GHSt/IbWrMBohyJMiH/DwyonjyTvfD+603Oj03Ozx3bngf3A0/etje1gl6ojXehBT3c7wJSbBbBtAFLjwf9ZZ6ElVEhD3HV3oSYiJa5UDzEabMExYhNAciSkCqHAqI7VSZ21uF+6VubyXk42jAK2Y8104W2fAit78S8HFgvxj2CgyO9CbTCRtRaCcLQW8RCxQDcMWFZDrRXCpc44k1YkyDRl3ZpBAADkNHR/Tps3sdbXAkoaSC1ZBHgEWknkQDFqoCmXI3w0MCAsDDMbsKdRoOCqlaI/Dzp10vP7519wEFEDwLwv77RQb4ihFON6AZdrrESuZbyWwrkW9D8xxoDu6p+9OfvvnbH0AqqaTyf59vn7//PkMzrDkQGEURDMcIkqBYiGBuuFbMK869knvt3LmTxy6eO19RXllf36RsVCkULaoWrUZt0OutVitgMtlZc6hV2ubkomhalUrHfmgwWFl26A1WjUqXfvr45gVT1s0evXHumI2zR26YkbZu2uD10wdtns7iYwjrj83TBm2eNpDdbpk+kHUG22bxsWlKkhfsNlmTk5VUSFIb/dZO6r9uUv+1E/utmdj/zYn9F7PsGNVnwei+80b1SS7zNraXHZOHvDljxNrZYzbMG7d58eR9G1cU52Q3KVu0WpPeYLPZYa3GVHj9yt5Vc97auqKaz1UqWuR1yrraRmlNg1RaLxbXikW1LFA0amO9vIl9iGUH+3JUKr1UKpdIavkCcU52/u5du6dMnjx0yOARaWkj09JGDB06kq0hQ0cMHjJiyNCRw4aNTBs+aviIkcNHjEgbceXS5Xffeed17ir/n5kUO143H3162wKWWYBSvfl6Xf3pZvW1Ju3VFn2GyVlgcFwz2HMsUJEZzGg2nNZbMh1Ort6WY3Bdd2JFmE9CBpuePOvqaAsEfHg8wgQYyO1o1jVX6IzZDrzARhZADMcTbn78tOfB/bbuTqqrnbjV5bl7K/jkSXswYfAlaqhwVaC1homJEF8JSBehXi4TkXhbqxF/md2dg/iLmPYqKsZFfOxpOxsiC63uqw48Fw0KnWQxFChFw5Wgp8LJEsRXjnorbWg+5C91B8udgQowUIVFRERArLNnttjSNUC2GSuHA1IXzrcA2Q4sC0v2sjRQoVqLnVcvK25pFLoc2u7uWE83jngqYKYCD0oAb7mdLjQTGQ463+UpshBZVoq1RYnNyYchdSwCP7gX+eh5z2cvbic6DQBVYMWvG+GLRuCKDryogy6ZkRyl6ZQWuGxGr0O+XNRb/ac//Tp1SWkqqfyA6R1k8eAohqH/f28Hyw4f7XVazMV5GUcP7Dt3+mxxYYmwSiKrqa+prquWyOR1jWqV3my2s8WeiTVqvVZjUKsNZpM9eV7Xm5WNyRM8i4/Ghoazh3cunZQ2b/yIo7u3nz60e9+6xRvnjV87Y/i6qUPWTxuUrCmDNkwZsH7KgI2sNlh29BZrjg2T+q2f1G/DpP7rJ/ZbP5H9MLln3cT+ayb0Wzux/+qJ/dhaOb7/wlFvLBj1xvxRfeaO6DNvVN+FY/svnjBg2ZQhb84cyZpjy5Jph7au4ZQU63RmhwsxW1wGk9PpwmDE06xQntm/9cCaucXXLzTI5Hq9haWSJTk/h6u5iVWURqMyaNVGVlpNSk1DfXNLi85gtKvUprr6ZqFYJhRJOVz+hYsXN23aOGvmzBHDRwwZNCht6JAhgwYO7NdvcP/+wwYNTBsyePiQIcOHDGXr8oUL7zxLseM7k2LH6+ajF7dNQJkT4dndFTa4wo5WmKEiE5RrR4vNcK6dKHQSBVrrhWb9aZs73wmWQ0iFyZHtwircHhEV1jx43BYNY14ajARJPw1CjiatqswOFFvcmTb8Oh4QMGHts/fvPHjQ3pHAO+PorU7q3q3go0cx0i+F6SI7nIX6uLCfa0Vy7XgRW7CnDA/z0QAXIjl2PMvtz3V78xxIthPJB9ACm/OKxX5Rb7tqg/MddIGNKrAg2SYow44VWmH2i+YBZDFAF5vQPBtW4qBKHWQ5GpQ5aSHsS468ECEJkrzmtMruLoBpDhEQwT6uFapoUpaqlVVWc2Mi4e3sRmxwnhXOY4+04kU6MNuI5hixbCtRwJYZz2GhA6I8BG1JxIiH91o/++Txy4/uvfi0m/SLrXCmBc02w9kWOBugC5xUsRbMaLFfbLScVAGnAFr4hyQ7UkkllR8sz99/7k2yIwkOkmCLokmaoRiWHSRKVJRV5OUWcCr4Ar5IJKyWiGX19UqZrKFW1qCob1KpdFqtkT2dJ0trYv2hatGx+NDrTKoWTVOTurlZWyMWH9m2ZtH4IUunTSzOK1Cp9aIqcealCyf2btuyeBqLjzVTBq+ZPHDt5AFrpwxYx9bkAesnD9iQNEdSGxt62cHWul55rO/t5Fg7od/q8f1WjuuzYlyfZWP6LmLNMeKNucPfYNkxf1TfBWP6LWLZMXnI6pkjNy6c8tbuLbzyCvZZsWIwWwCTyWk0OV5N1GE22nMvnzuwZt6143vqq6s1aqOhd/AoOTOYyWE02KwWF9tgX5rRYGUb7IfG5OToNqVSq9NZGns7fqqlch5fmJOdd2D/gZUrVsyYPm3U8LTBAwcO6Nd/4IABAwf0H9S//+ABA4cMHHTh3Pl3nj1LseO7kmLH6+bTL+5b3RV2uMIKleidORao2AgUa2xXtLZLZnemAcwwAZkq/UVl0xkAKHaDpQ5HocGa7SIrbBjPFzXcvhUMeMAA4w75cBK2OYz1Jk0p4i53uPPM7isOLIcKNjx7v+fenVh7FOmIYTfaCZYd9+75Ma8AoctcaIEnIvLEZFa4wNzbiUKGJEy8FvOLyWCNg8g3QBetyFUHexZHCkGi2AllG43n7UC+CczSQVe00BULkmVwXjMBWVYk3+C6bgGzLECRC+VSQaGTyNe7sm1IqRkuhL1CJqbwt6mpcCNE8yCyHPcK3R6hg+LYkEqdhqdtETvt6nCYSLTaTc5sjSXdCpXpXTlqe4bJXahz5diwUiOUbyGKrGSpDSsH3LJo2P34YdcnHz/5xc8+fvnp3dYuPUDmQd5iJ12E+ssscIYFK9DBOQrj2UbzaWHzbsgj/eOff/O3P4BUUknl/yGvbqDFMZxIsoPqZQfFUB4vzfg8PpPBXMkVcLlVIpGUNYdEXFNXq5DLlfK6RrbRUK9sbtao1XqDwaLR6BsbWzQag8nssFp7F04zO4wme6NcfnDzqvljBq6eO51XwWEhotNb1WpjfZ2SV845/9ah7cvnrJs1as3UIasnD1wzacDqif3XJG3xagyl35rxfVlksOZIVm9jzYS+qyckzbF8TJ8VY/suG9NnAWuOtJ/MSXtj3og+C0b1W9g7yLJiatraeRPe2rutWig29A4Gmc0ug96m1Ziam1gbmZJzddigWpHo7e2rT2xfVVvFs1sBAMRAEAcA1OmALGaHxZycn9RkYhtOW+8EHs7e+4S1GqPF4jT1vkCtzqxs1tbVNVZWVuXk5F+8cHH/vn2bNmxcsWz5iqVLF86dPXPq5AVzZi9fuqS8rPTvLsj8PzkpdrxuPvnsTovuutaYoXNkqGzXWHOYXeUOuNSFFprsmRag0AqXgjgfcvNBgIvClQBQYrBlG4AcnKnp6IA6IrgXd4Z8mI+G3HaNRSeFHFUgUGoDik2uXCdS5Akqn7zTef9O7EY71dNK3unyPLoX7eiEYKYc9pRjDNdNFlNBAYAVg3ghRFcgPgHirUQ8XE9YCFEcgCizAtfsUBaElVmchQBcbnMVsCAA6SIrlqVHsqx4CUBwUKbS6LoOoGUAXAkTXDdeZodz9Y5rVrDIhXBdJA/x12D+OjrWAPu5LCOQQB3AVDjpEgdR5EQ5Og3Xoq+HIUtbqy/s1zucJU6kAvFLLVS51s0aq8RFlOucuUYwD6A5oJfrJMogWOKlLA8fdn700ZOvvn7x1ZfPPEGZXH/Igl6HPRUuPN/izjSC2UrL+Qbz2Qbj2SbbZZhh2ZEaZEkllR8yr26gxRAUR1l5EMkRFpL2UklzBLwBGqer+KKigtKyEg6fJ+RXCgU8kUQkY6tGWs/ig6WGoqG5uUlTL1eyDYPBarY4XS7YnrzUNDnRZ41QuHPlvPkj+21fvaxeJlerDCqVUdWib1So2FIoVDJp3fWL5w5tWbN10dQ109NW9c7D8eb4vmsm9GO3K8f2eXNcn9Xj2UruYWv1hL6rxiX3r2BrTJ+lo/rOS3tj9pCfzBmWZMf8Uf0WjB2wdPLQtfOnnDi0r04mZ7Vhtrj0BptKZWC5o9NaDQa7Xm/VaEzs0zMZLNdOHd6zclbRtXMGndHpRJxO2NG7zL3NBlosLvYYo9GuURualBqN2si+LheQXCjO4YJ7pyqB7SxQrE6VWt+i0snljTU1dRKxlMOprKjgFhYUXb1y9eL5cxnp14uLSiEQ/uqrv7My4v/kpNjxuvngk5tqQ47BVqhz5KkcWQZ3sREssEFlVrDE7i53Ihz2FGvHi51omcFeqDZmW4BSnS3f5Cwh6bqbnXjM4wxQrqAX9tIuBFBbjCKTtcwGsHApgalKCOUEI8anz3oe3I3f6vL2tFG3Osn7t4KBsB71c+xYjgPLQ7wVCFOGe7kuvBj1VzrhfAeQjzMCJiYGiRKE4jpcWRZLphsph3Gex1eDMjwnVQJ58lxkkcNT5qRKWXO4kCRKQLwSxLgAxjqp2IWWQpQA9VcDHj7oq3L7Kt1eHujh64BsPVgEMtU2tJiVB3sYQlQDQKPd0oLCllgUxwCp3VZiR7lWpMxBckzuIpUl3QDl6sAcA5BrhYsdaJndXQYgYoo03roVe/78wYsX73722WMyUCvTHW62nm8ynjdBGS68wI4WmN2ZejDdiGZb8FwqXP/HP3+TYkcqqfyAeXUDLYr0XtuBEQRO0kSyt8NHe/0en5dm5HUNrDZYc4jFNRKJLDm8omhukCsbG5qTpWhpadaypWxUNTaqku3keq0WrdbUnNyprijI3bRo2pwR/d7et7tGUsPuaW42tLQYGhXqhvpmqbReIpaJRTXiKklJXt7JA3u2LJ2zatqIZeMHLB/bdzkLi9FvrBjzxoqxyUqOqvRS4z/rjeVj+iwZ1SfJjrSfzBn+k/mj+s4f03/h+EHrF864dOakRFQtr1WwuGlq0tSxjUa1Vmdh/WEw2F6x41VxCvIOrF1wavc6ebVEqzEajMll7tUtehYZLDhe3UPbUN/UpFSzYNLprOyrY/cYjFajyaY3WNgGW9rkjTxmlUqnaGxpbtaw1dSsUSpVdXUNtbJ6mUxeUyNnmMDPf/7LFDu+Kyl2/G/23js4juvK9//nrf289q5z0DrtPq8SMxGJnDMwA2AGmJxzDj05557Qk3Me5ESABCmKEkUFSqIkUrQSFalkOciSFSxbtiT7eX/1uw1q91W9KnlZ9eN/v/nWUev27Z6e7hFU59PnnnvujeoXv7myvG2rbVgra+bUojy5KMgt81MVQbYqzy9oiiv6woo6v6xI53mxBDeRlSeKsnhOmK1ITuwiD51bPr2Rvff0KsCOk8er68vRUsGYysirS/rqirS6Iqksie85n7969eKzVx54+tK9jz54/LGHNi8/dmr3dHTzlHVj11Bdk26eMq0cV6/vaMqr0rW79OUVSbbIWt2CTt3rrC4JVzagpXV5ocpd3pBv7erXd6Dapqi4yS6vMgoL3OKKrLwiWlxTbO5Ylza0y9uq1W1o/bSluCEuboiWdw075wNrd7ty6+JEhV5YkxU2oYW7bOvnHKunzbUNRXVLunpCe/f5yInd4vpaYWd78Z6zK9UitLxq2D4bKq9qFo7rKlum1IK8tKVdPGkorAgWt5Vru8aNu8zVLcPGduz8AztPP/3wi88/ceXS3WfPxXILzESeminRs1XK8pYUzXJdly8eF1e3FYVVxfpO4LPP6oMsddV1M/U5dqxvfo4dW9s7gDx2Tu6e2AXwsbuzm05mgeP0w2EkHAfAEQxEgE/NZgqlYq1cXgDbfK68FwxIxSIpABMALEKhuNvldzp8XrdPJeR07f/p7d/+R+rstN3sANcJhRKBQMwfiASDMbfbD7DDbLJp1TpIqYFUWolAQicQuo/cceTH3zp8y9eO/OArR77/5SPf/9LR73+p4ftfBnbku186vGeHvvtlYAe/++V93wbY8aU7v/M/D/zgq4d/8s2Og7cqJdJEIpfL11LpYiZTiUbTgDkKBXQht3xhIVeoodt8LQnoB0k6zfbpvrahxltNSlkskkRRo7QImCMaTiABQFeJoD8SRhL5XCWfq1aqK+jRDPq8mUwhky0mUvl4MhdLZAB2ANIC7JVM5lKZYiyRTYJDiUw0lopEU6l0/p5773//g/9mDef/P6uOHTeqN351qbqmLtTkixv2zLI6kudlF9SldWNpw5qoKjOLUHXDVFnSFkqqZEacqSiLS1C6KM6UhKfPpB6+b/3MVvGhczundpZ3jlcXl/zJtDie5GfKksV1eXVVmC3T7jmfee7ph579OcCOux99YP3KYycvXtg4dSa4vmNYOq7ZOGnYPmPevce+dcq8uq1fPaGubfIra+zaKh9QS3mJX1sXVkHPKjtfo2fK1OoqM18hI8npSGYmmSOUlgW5Kiu/yE+VWYUlQbbGLS/z13a1CyfV6RVecUu5eNq+fh7OrIoyC9wlgCw78soJGZqLuiFbPg6VNhSLW7qNHfjM3cunTq2dO3fi3LmVyoJk8bh89ZSltiFfOC6t7siWTps3znpKW+qFbenScdHWKf3ueXNplV+oqE+dKl985MzPL99/8eGdCw9XiquSSIGeBre9pa6tgW+BcgvSyqa0elySX5GcOJv6tF4urK66bqr+M9qxcX2c5XqSx4ntnRM7J4Dt7pyMRxOwP4QA2EhkE4lMKo2OJgDPuhfVKBYKZbDN5UvpDBonQOeJ5KvAx6fShb2i6RWtlN95549v/c5XuTS6xxMERBIKJpwO2G73Ai4xm5zAlHItpNZrIYMW0qkVGqUMkkoUTDpjrLer8+AdDT/93qEf/NOh733l8Hf/56Hvfungd760/1v/cOc3/uGOb/zDvm9+6Y5vfum2b375ju98Zf8t/9x+4GfzmEmT0QbDkWAoEU/kgqE4uBmAHelMKV9cTGfLiWQ+HssiSAr2hR02n9Fg53GEPV09bS0tSjmEjqSkUaQA8JROFwF5xGLpaCTt84bARcDReCwdBjgSigVDMQAT4HEAhCF7p2VzJfDLROPgu4qJZA6dcxvLoF+XyAFLpnLg1aoe7fg7qmPHjeq1Ny8WFsS1NfXqtr22qS8sKCvr1tyKJlqW+DP8ZEVeWlIurKqrq5pcVZksKxJlWSzPyS9K7r9/8eL57QfPbFw4f2p3e2lztVCpuYs1eaEiiWeZhRqvuiLKlpgPPbLwwtVHXnjmoSuPnXrkvuVLF7YunF/Y3nWun9CtntRvnDIubSl37rFV18RLm4pchVlc4CxvqhbXAdmwy0vChVXJIvDfC4JUllSoMosVeiKFT+fYhRqAG2qqRC5U2JUVYW1dsrilKG8qk1V6aVWQX+IlqszUIje7IS5sK9LLgniVU1qXF5fF5Q1hZUNaXZIurMjzS4LcouDEmfjZs5tn7t6+8Mg9p0/nq+CcNR7gmHiRmlthxxfphePy/JYsXKZ54xORAqawTK+u80qL3HxJvn0889ijdz/79CMvPP/w44+vLm9oInlatMzMragK6+r8qiq7JE8tcfPr/Pya+NT92XqV0rrqurlCy4WdRAdZrud27OwZWjEMMMeJkwA7wkEEhgM+GAFeFrAFeMXP5YrJZAYdR4ilEvFUPJZKpXLgXX9vqshSvljLokMPVbTKeL4sZZLbbrvltu/9MyRXRiLpWDwbi2UDcMTjDng8IRgOe70hq9VtMNh0OovZ5DCb7EaDVQMZIMioUEASkZQ6T5js7+k9eqD5Zz88eMs39n33a3d8+yu3fv1LP/un//Gzr/2Pn339y/tu+XbnkQPTI0MiHl+vNZktTofD5/NH3J6g0egwm50Wi8vtgr2eoM+HeNxBu82n01iUMq1cCgn4UjKJPocnMOlsSKVzWN1uJxyAw2EkEQrGYE8wACNg1w8jYBfQBoAtQFTRGIoaYHvdwC8DdtGJLZFEJJoMhqIhJA6IBwknY/EMMPCRWCxz6vTZ3733fh07vkh17Phv9F9/OC+/+kCywCuvqotr6sKyNJJl5ZeMxWVdsiBK5EX5RXW6KissqzJVRaIii1cVkao0VOakFiV331189L7ti/ftXrj/9Lm7tzZWs+WqNVcSVpcUiSwtW2SWFwSlkvDRRzZefuHSc0898PiF7YfvWXziwbVzZ7NrW4a1Le3arm5lW72wKgUIUluT1laVmRK3tCha3FCkAVLkaIUSv7IoKC1ws3lGKkupLUkW11W1NeXKprmwJMkv8YsLvHyBDb6oWGPml9jpZV5mkVJdExQWRYVVEZInpxZ5mSVhcpGbWOQX1xWVVVV5WVpeleTL/NKCKFGipauS0/eUz9134tx9Zy5defjUmUxlUVZeEFWXlKkSJ1Zi5Veh2klTdlWQqNHCOUK8Qilvimvr8o0dXW0V2tgIP/TAzlNPPfTSS49fuby7umYqrwrz65LchiqzKclvSrKrksySqLShrG0bT91XqGNHXXXdVKHlwnZP7m7+Z0rp9Wm0Jz6fRrt7cnvH43Q57U6vLxgIRoBPBX43nUbhA5AHOkU2kY7vhUByuXKxtFAsLQLL5yt75cIWcuDNCTfZ9NPvNPyvH2lVEIKgsYdgMHa94pbbFXA5/Xab22Jxmox2vc5iMjm0GpNKqQMUYja5DHqrVmvW66ygEzCBXCxn01gk3BxucrLr8J13fvtrt33jywd++J15LEYuVQF2sdt9ZosLXMRkdlhtHoPRrtGY1GqDQqEBTKNU6oBBkEGlNMikkEKuE4mUPI6ISefSKQwui6eUqQ0ak0FrArfkdPiMeqtJb3HZ3Ch8+CJ+OBIMRGBv0OdFaSkUiqHkEU8nU3nwg4BHC4cToVDU70dgPxKJpcPgSUPxEJK4zh8ARE6dOvPee+/VseOLVMeOG9WLrzyKpHmpmii5zM+ty/NrUG4RSi8ow6jTZSxs6otLUKamyK3oUouqzLIqu6pJ1NTJqv7u06V7j5fOrGbPnV69/96t9ZV4saJMZBmVJWWxJs5VJNUlqLygunT5zAtXn7jyxLnHHzrxxEPbjz2wfmoHWViUVRYktTV5cYFfqvKKFXEmzy3WpJEkGTj76rpycVOTrwhLy4r0giBVEWcr4lSRXV5TF5dllWXF8rpm+bi0timvrCqSRW55XZld4WQWmJUlYWWBmc4zIjlOMEmOpRnpPC9dFmQWhLUtXbokyBW4xRInkiQUa8KVLW1+QXry7twTTz74wANnH3r4wWdefHJlx12ssbMldmlZVFqVZGuieEmUrPLDeaobwYYyuPwKt7Ahqm4plk4oixuS1c3A2TMrTzx+/0svPf3ccw9uHLevHIdqGyqAONEKO73IzVTFlRXV+q5x87Rl42To03puR1113VTtRTt290ZYttB1aPfKhf1XbsfW+oZRp9Np9Sbgi60uj9sfCIRhX8Dj9vq8fiQUQZBoNJpEi2vF0qlkDrzxR9HUy3w8ngkjCb/XP9PfduSHXx/vPGY1WcF7v98fcdg8Xk8QrQTqDrhcfoALGo0RAgYZARYo5BpgKpVOozWBXaFQIZWoZTKNSKiQiFVqlVEDmQCOEKYmmn/y3UPf+0rrbT+WCoRms9MAaMPiMhjsKrVBJteoIXDjVggyaXUW0BaJlTyehMsVi8UqqVQjEMhlMggwB4vB5rB5Ar5YJlWhE3Ygg9Fgddi9aDAGzXWFXQ6fw+r2OGG71QkMPIVBazQZLG4nmgnrdHgddvA4AYAd4NG83gCwSDgej2d9PsTnRcAWMMoeeSRO7Jx+993f1bHji1THjhvVa288GS8qklV5aVNfWNMXVvTZZSi1AkUXFeGKKL2gAhSSXVTFitLsEpRdgWIlcXoRdOofefD42a3SqZUMwI4zJxbXFsPFsjyaoVWW5aVFYWFRvLhhWFy1XLp017NX0HXgLj96+snHTj16fv3ENlJdUhQWhQsbimSBkatwSwvCdIGZqwjSWVoqRy4sSCorkmKVH03T4iV2riaNZ5iAFdJVVnldWtuAFjb0+QonnqMky9RshZPIUWMFcnFNUluXZwvMZIYeSdOQFCNbFqVy7EiSUlwQFBf52Sqvuq4uLSmzOdGJE8gTV05efvbeV3/x4vMvPff45QtPPHnx6itXKluGcGYmU6anS4xYnhnL0sMpWmEFrV+CZMiJErWyJantqgobktyqOL8hWT8ReOih3aeeufj8Cz9/6eXH7rkvUV1RZqrCzIIovySvbgCAk5RXpNVVUXFFePJs/NP6mix11XVTtVccHcWOrb1Cpdtbx09s/5+U0lqprFbIjXqTy+l1uXyAPKwWh8UEPLDLYXcZdCa91uhyesBRh83tcniBnwaOFrzcJxLZaCQZ8PnHO47s/8HXiFNjbqcnFIw57V6DzmI2OoBpIKNGrVcpdQK+lC+QikQKHlfM5YpEIrlCoVEotFKpWrhHGwqFTiRUCgUogkilkFyqmZ+cbP237x/6zv9s+fcfSflCo9FusritNp/F4jaaHEaT02h06PQ2QCF2B2x3+iGtRSxRiyUq0DBbvWarx2RGoylqtU6rNSrkaplEqVRoILXebLRbzQ67FQ14mM0OE7iywQbManGajVatSmfUmcFTuF0Btwu2mO32vUJqwLwev9+P+ANhJJJIAAKLpoPBGGARP+AQJBkKxXdQ7KhHO75Qdez4b/X5n84vf311ZcubWdTk17T5VU3tuKOwbMiuKtIr+kTZHMlA0bw8v6pLVOSpijKeEyQynNKiqrRov4zUmTwAADltSURBVPrsI5cfvRfNJz1eWqsg6+Ug7KW6/bhUiZdeFMZKrHRFuLXjffLx05cfOf34hd1Lj5z++eNnH7xndWsnWF2TlZdF5UVpusSurkmqK4JwCpfKU+JJcrbIKi1Lc8uSZIUfAvSQpmQr/HSek8nTg3GsPznvi0/Hi7RMlesJz3rjc5klWTBDswemUzVpZUsfStN9wZlEjpUoCvKgZ0laqIoyWVY0SshXeUtbmrWT3rvuqbz+1ou//PUv3njr9V+98+trb7z8wINnLj350JPP3Zde5CcruGyNGs1SUhVhFHwww0xXuQsb0liOmqnwkmVGbpVX29YW0KwR4cKW45HHzr587blXX38B/JIn7kIqK+p0UVxcVFWWodyiKLcoKyxLM2Vuusw7fjpSx4666rq5QrHj8+LogDw2t49/jh0nd3YBeURCQRGPq5IrPW7Y5YINBrQchdPm8nj8Pl/QbgPwAdyzFz0KyMPuNhltOo1RC5nMRpsFtNVQf8Pt+37wNcrMlEFnNJusBr0JUhvMAAsMNrkMkoiVQr6UyeAx6FwGnUMmMygUBmjzOIA/xDyehMkUMBh80ObzpCwGn8USSiRqCDJR8fi2f7/l4B52yARCq9Vls3stVg/KExYXoBCLFd2aTE6rDe23O2GT1e1wBbxwxB+Ku70hmwMwikuvtxqNNjvACLcfDcA4/V5PEAnFUql8JluORFOwPxwMxUKhqNPpdTp9TifsdQeDwWggEAX9oMdkcqDfbnXq9SaL1eH2+JFwIpUuJJL5YCjucvnBlQP+SCAYPbl75ne/q2PHF6qOHTeqN3/1bHXTHi4IgzlqrMZIryoWTrhrW554WZVZ1OeWtImyMFnmRYvsRE0arUljNXZ+UXLh4sarL19++sn7Hzq/vVoN+sxcOXvcbSf7AgRHYCaUpUULlHSRsbZqffLxU489tPP4wyevPHb28sV77j27snUisLAiqSzwc2V+viYKJ5lmx5TVg4Wj+ESe5QvPupBJewhj8Y0Hk5R4kV1YECbStFiKaXNjDdZRo33EAc9Y/JPRAscbJiMpdiTPDcTpsTQ/luMnCnxvYD6aZGUW5ABNCku80oY4kafFs+Rgcj6Spe2eSb740pOXrzx8amfx5PbC0888duWpR0/vLm2sJk6cSsHRuXAalywxfDF8JMfM1YSlZWVlXZsscTILnHSN6U9PI0UikiPBmTmkxqxsui5eOvfK68+/9es33n73tY1dOFnlJcqCTEWSr6jSRUWqJCkuy6MFmjsymSxIPvnkg3rdjrrquom6Xi7sODp7dgswxw5gjhNoVgew4xubBg3EolIEXJ5apTXozTqNQaPWAQNsAan1Sjmk0xr1OhPYGo0Wu91lt7qcDh/wtbAPCcCITqnsOvi/DvzLN4lYjEalQ/MzpEqxUKZS6kQiuYAn5XJEHLaAxeLzuKAhBMxBozLpdDaNBozLYgrAlkrlkElMKpXNZAl4PBkLbLli6hyu4/YfNv7LV4/degudSFAotGKxClIb9XqbRmPSQGaVyqjRWvUGu05vMxodFqtbp7cawK7OYrW63Z6g3eHT6ixarQlAA7hhrzcIgMPlhGFfGJ0tjCRSqUIyU0qki5FYOhRORmKZVLqYzaIlOtD6pKWFTLaUTOWTqVwohM4H9ngDdofHCyPoJJdYJoCmsIRsVjSmotOatVrzyurmO/VBli9WHTtuVC+//kRmQZmqyZE8J5ghR4u89AKUrELxsiycAQ5bkipIPQGiBcaGC9zUijpUkpTWHNdev/LKi08+c+XhB8+fTMWMGgVWKR8IAAhI0M2eyVCGkq7yMkXB1nH/lSfOXHp096nL9/z88rmfXz5/4cKpnbsj5RVpNs9IpMmpHMsTZuocsybPbDDJiaTZLhjnCRCs7hm9fdwTxscK9EyZlcgxwilaOEn1hGaQDDWcY5jco3AMF8+yglEyHCVEMvRgnBpMMJJ5bnkJyi+qYgVWID0fLbLiFWa8SAtniK4QFnBMbcl4+lR8cz2M+MTFjH5l1bOxE0aicotp3uuj62zjNu8MHJsD0GODZyNZBpJhJctyW3DeEZ5HU0aydF+SFMqQkBwtXGQkappHLt378rWrv3z7V+/9/u3TZxOJAgXJ0lMVUb6mWlyz11b0qTLbG8d6opOLG5ZP6yvQ1lXXTdWr11696/Tp7evMcXx7rzL6ydNoubBTpVxOIuAyKCQBT8jnigQ8kZAvlghlcpkKNalSIVOplRqd1qBUqNUqjQbS63Qmo8Gm1aBZmS6XT8bntd724yM/vYVDpRrRSIlFpzWpVHq5HBKLFUKR/Hq+BZslYNC5NCqHSKAS5im4WeIsjjg7S5yZnsfhSPPzVCKRjt/bncOTCfNUMokBzui8/UeNt3z12M9+QMXjeGhoRArIQyxWCoRysAUmkaiV4LsUWoVSpzfYLBaPXo8mmQLaMBgAi9j1eovR5ACHzGZ0Bo3Z5LCYnYA83IBCfKFgMBZCEsACATS2gYSTqVQRnUabyCXTxb05sYVkupDOFNElaXJlgCPxZDaRzAEcyeUr2Xw5nUVn0oaQuD8Yhf1hNNpRn8nyxapjx43qhdcfjhYYwI8avGMW73gwTnQFpwNJaqTAzS3K8zXl4oo1khDp7RPeOCVeVaQXrRceP/32O7945eWnrz576eLDZ2NRlcdH9ARm4ATwzThXGO9PEuJ5QjLHPHEy8dwzDz731INPXT731JPnn3/u8Sev3H/ynlQsxzc7xgFDRDJMb4xtD1IdISoc5QRidH8Eh6RI/hjZFyEmitwwQIckDgFgkWQGwkSLdzSUp8NpRiBJgRP4eJHpC5O8UZI9OGXxjfiTc4EkIRCnZ5clsRIrnGOHcyIkw/HHSOEcK5Rguf3zkSQjnmf64jhXYModmvLGZpAszRaYC8Q4ds+820/2eEnBOM2JEH1xFpITOJH5QIYdLskccZorSolVZNllbSjBjqZZ6QVOqgpdvHzfy68+//a7b3/08QcPPLiEJHAuZCqcoeUXJZmKNJ7hhNN0rWPE4Z9d3/LWy4XVVdfNFcCOM3fdhYY6trZOHN/e3Zs3e/rkLmj73S4mhUQlEtgMFpPOEvJEIr5EIkKxQ6mA1EqtQWc26M1aSA92tRp0vqtMqhKL5DKpGlLpjQYrg0w49NPvN9z6bywKHVIbIcgk4EloVDYwdFSFyafTAJCwKWQm2klDoxokEoNK5VD3ghzzBBqBSAc2P0/B40i4WRJgEUAhgDzmp6c7bvvR0e/9Y8u/fnd2YpREoJBINHAFHl8KDJAHOi7Dl8kVOhVkVGtMOr0VzfkwOsA9QGq9Wq2XySGlUgcISa3S6XVmNHXDZLfbPA6712Z1w3A4EIg4nT6bzWU22dBZtSh8xDzekA8OR+OZPcsmUvlCEV19Jr/HGal0PomSRzadAThSSKFQUsrmK7lCNV+s3Xf+oQ8+rJcL+0LVseNG9fy1B83uSbm2W2nqs3onYYTgCc5ZA7O+NC1S4JVXdKWaPpFUJIsGJKnOVW33PLD+y7fffPs3v7r63OMXH7n3xE7NZJtzhcjuKN0RpRi9OG+EBJy6OzQNSGVtM/r8c48C7Pj5pfuuXL7/6ScfevLSvaUlhyvMVdkmnWGiL0bW2kcdoblgmukI4u0+vA3GOSIEiWFMrBl0BwneIM4XxofSZEcEa0OmnNFZIzwr1HWrrf0qy7DePeWJk0wwxuwdhQGdFKhIjiPW9tiC+EhBAqdYgQQ3FOU4YRwcI2dLynCUFYzN6z29Om+f1Y+xIRipvk1tbLe6hrwwPhymwQGS3Tnj8GEsvllPlGTzz3rCRHeU7IpRQjlBICXMLGrDeUlxxVRcgZJVQapiuvzMoy+9+sJv3n3n408+vfr8ozk0n5SfromK67LylipZ4aUXROioU4a9ugbXZ7LUVdfNFTrIsntqe3NrZ3PrJBrt2Dl14sTpEyerxZKExyXNTjPIFB6bx+cKuGy+kC8BzKGBdColwA5IpzUB+ADYIRXL+TwRk87m88RymVouh9SQXiSWDQ+P/uB73/vJj34yNDROobAAYZDIDAAWaGCDSMfjyZgp3OTk9NgYZmJiZnwcOzMzPzM9j52em96Lc1zfBYbB4MH5BAJtdpYALsJi8cnzxO59/9r4L19t/bdvjfZ0YKawU1MzwObmyOCLCPM0EpFOJNLQ4Zu9gIpEotJqzZDaAMBIozE4HF6VWq9QalQqLTp+ZLDYrC7ASSajTQPpIUjv8QSQcByGEUAhZpPDbvcEQ1E01QMO+3yIH5AHOnk4u5c8m0uli1F0xmwSLaSWyoX3SndEIslMpgjYxe0OwP4IEk7snqrndvw91bHjRvX8K49ZPXiVod/gHNM7R2wBvC9MNvtmfSm6O07xx5mZgube+1YvX3nwqWcv/vrtN//06Z/++OeP33zz1SceP7+9USrlHHrjpAMG5EExOrAGM2gTXFGKPUSW6jC7p6vPP/fYkxfvfuzC7s8vn7/44M7dp8uuAB9y4jnqHrlpSm2Z0tmmDA6szT/vQsi2ANEKEw2+WZl5SuOcFWl7lZY+R2ha4xrhaY6pHYM6N9YIE7ROvNk7q3eOW+FZd5KqdU65g3jg1+N5ni9K0DtHLfCMA6FY/HMG16jRNWaDscD8ESocwnuQGa1jUO8eMbowDoRkchPUhiGdZRDSd5sdI1YvRuucFGh6+Koes3vKG6J4wlQ4zUYKAleMEUhykmVxsiytbdlzS+pESby8nXj+ledeffONX7797p8/++sfPn5vcweO5NiRPBnJUmIFdihFDqaooRQtmecvb/g+qWNHXXXdVP0f7Ng6fmLr+MntndNolbAdj91GmZudw07y2ByVAuJzhSwmh0HncLlCAV8sFIiVCjVAEA6bD4zF4NKpLAFPLOSJpVIlwA6hSE6jscZGJ/bdcSewjo4eQBVz8xSABXg8CQAEfo6MweKw0/iRkYn+/qH+vqGB/uGBgZHOjp6enoHu7r7e3sHunoHBwbHr/aMjU+Nj2MGBUbAFOIKfnuna99OG7/9j80++NdJ5DDMxNTmJnZjAToxjJsaxs+AEHHF+jkwkUGlUFp3OoQFYYfL28kh44LYhyCAQSMGziEUypVIDOEMLGVQKDcAOs9EGcEqvR8eDDHoL6NFqjDqdyWxxWCxOrzfk9YVcLj8CICOVR9CyYFFgADti8QxayDWZA7wSDEZjaBW1fDicAEdBTzyRuevue+tVSv+O6thxo3rh1YueMM3mnnf4SDbfnNo6rrAOWEN4jXtcZup1h1gXHjv10Z8+/OSvf/nsb3/763/87ZO/fPL+h++9/sYrj126sL1ZsNlIak2vyTGlMA5oLP0qfa/dRzC6cWo7Rm6YvvrSpTdeu3rl8XMPnlu//OjuI/evrK8E9Ta82joiNw9qbBMG04TJPGl3zTlhkso0prFNGV1zes+0yjZlC5IdQYrZM2P1Yp0I2eKb19qGDY5xR2A+mGBF0xLwEacH7wySPQgdjlAdgWkXMmOwjYALeoJUCzynsQ0r9G1iRbPFPWP14MzOSZcf640QTc5JnWsSfLvWMamyjUGWcYcHxxM1qoz9ttC8LULSe+c0DoIvSQ2k6XCS4YnQ40VpJM/PLcqqG5riiipWEWdqhoWNwNMvXvn1b9/55W/f/dW77//h08/+8rfPTp+uBBMirWPE6Bs1OId1hj6PHxsv0LPL/K27kE//8of6/7J11XUT9eq1a3f9J3aguR3baLRjeWGBTadgxobnprESoVil1KhVeqVSe30MhYemeqDG4wiYdDaXzVfIVEK+BGAHQBAuVyQUyZhMLg5H7O8daGpoPHTgUMPR5vb2nqGhsZHhicmJ6fExDGZqdnJqZmJiug9QRf9wf/9oXy9ojPb1DXW09+wxx2BXd39v71BXZ19//8jY2NToKCAPzODg6ODQ6NTkVOe+nzTc8o/NP/3WUMexgd6BocGR4aFxQB6AY4aGxgf6hicnAH/MAZubI1EoTCqVyWRwGAwOlcKg0ZhkMh1ACYPOFgqk4KHEYrkMDeQYIJVOIYcMRpvRaFWrtABK5HI1Sh5ao0FvtlhcdrsXYIffHwHMEQhEHA6P3eHxB8JoMTFfaK90ejyExMLoEjboKnQAOHx+BNjOydP1aMffUR07blQvvfpYIEaLZtmZijqUEiFJAZLmBbJscwjjixHOP7j6+z++/877v/3t+2//4ZM/vffhh2/96s1rr169cuXi7u6G3cpn8FolUB9D1MJStPJ1HXxVh848obdh1ObJZN7+znu//uVbL1196sKjDxx/6N7lnaVgMCiUm0a1zkGDZ1xp6tOYB5RQt1zRB+lGmaIWuvyIzNIB8EJtGXUCvIgzYllpMMHROsZVliGNdcTinIFDpECMEM1zPWGuzTvvC9OQFNsTmnWHpzyRaad/JhLlhWMsd4hidk14Azi3CxsM05z+aXdwyhXAeRCqC8G5I6RQSuAI0FzgUGjOG55z+WZ8CMkfp7sT86E8K1kRJ8o8T5ToQPBuhOCNkvXeSZ1/JJCnRMvcUFGUW3Zdvfb0O++9++7777/z3nsfffLZn//2t0//+teLl8+FkjJ7kOCK40NZYjhJCcep4TQjWuIvHg/Wox111XVzBbDjzO6pnevRjj3s2FhZVUkkmLGhieE+OpkslyhNJrTEOAQZ0JQImZrPE4uEMqUCuu6hRQIZ2OVyBAw6h83ic9hCFovPZvJIJBoWixsdnezvHwIwAWwMcMMEdnh4YmBgbKB/BMDEwOAo2HZ397e2dhw71tnZ2dvR0dPc3Aa2nR09TU3HQH9nZ19HR29XV19bW1dXVy84GZzW3dXTeuu/NN7yj63/+q3xvs7R4dGhodHh4fGxcczk5DRoDA6OTUzMYDD4mZn5+Xny3ByZQKASiVTCPIk4T8bjwL+oJAKZTmVyOHwWi8fjitjoFjCIQsAT8XlCDosLTMAVioUSrUZv0FusVpfb7ff5EDTDFEkAsAiFIhaz3WJ2eDyBYDCKlk5P5QBqXF+SF7CI1xeC/RGwBdhRTyn9+6pjx43q2rWLwE8HI9REnueLUN0wxeagOn1MV4DqjzDP3b/yxlsvPXP1iRevPfX6W9deefX5Jy7de/p0bXkxZjcLhgbvmCXs54k6KPQWkWJAbhwWKHukqn65dkBrmrvy9MMf/uGDX//6tVdeevLxh08tFdwG1TRP0iM29igsnUJ9q9jQKYK6RPIOmbJLqR1gCFrYilaJsVPvGJYZ+ux+vD9KcQaJOseEwjhodE9bvFibF+9DyJ4QLpqTeqNci2vW7J6GbOMm5xQcJLlhst1NcHqIaMADJsARAhwlOn0zEmWnwTpk943ZvJMW35QrOu2IYP1Jsi9KDCVYAClcQaINxnsRQigCnpoMR8ixJMPtxbphnM03bXPPWN0Yewgnt3UrHb1a75gtTNy6q/LqG9d+997vPvzwgytXLn/0x4//+rf/+PjPf3rk4olCDYpkuZECN1nhZ8vCQJxtgXFmeCaWV//5k9//3/8B6qqrrv8PQrHj1OnrIyy72zugEfB45mcmR/s7J4YHWXSWXAbJZZrPTaqWiBQCgRQYmsMhg1RKrUqhBZ1SiQpSG9QqPWiwmHzAHDgcAYvBjY9jhofH+noH+/uGhobGhocmAHwALNgDEXT0BLTBtq9/pLdvCLAFIIx2lDl629u7W1vaAXY0Nx9raWlvaWlraGhubW0H5+xxzEDr7T/+z9yOtuEhFDsG+oeHh8YmxqcnJ2cnp3AzaP4pGRgeT8DjieCWiEQajcoik2hgl4BmoVKpVAYwgCNMBgcYm80Ti+VCoVSrMaoUkJAv5nMFgEUAYxkMVrPZAbZGo83h8Ho9AZvVCam0MqkCUmkAfPi8AXQZ3lgahsOxWCYaTfkAc8AIYA4AKy4XvLl1ol6l9O+ojh03qpevPYpEmS4vTm+bsMDAv87b7CSLCe/wUL0IIxBmVResK0u+zY3QyZPpe87XltfdZsu8GpqUSLuZ7EaBtJPJaeGLOsXKPgk0KFR0sIRNasPU+mb2/Y8++PDjP779zq+uvfLU5SdOp1NKoaKXKe8QaLtU5n6xplVtG1KYR6XQoEjaodQO0QRNLGWjQNcIWbrFujajY9LknFYah8W6Lr1rJJrlBaI8h5fgDVKc/mk4QXJHWRYvweSZcUXoriDZEyB6Q3STAxuI0PwBuss94w7NBFIMk3vW6sZZPZMWz4TZOaE2dNu8U3JzlzUEOAZj8xLMXhxkHnEFCOE0O5rhBZNMh3/WG8GpDJ0W75g3Np8pi0MpkhEeUduHLF6c2TOTLErvvX/zlVee/+gPH731+usPnT3zwfvvffKXT9/+7ZvHN5FMTlxYUKfKivKqcWnTmq0qwxlBMMXLVe1//rQe7airrpupz7Fj83pix3Yhk2ZRicN9HYM97bhpLHjvVyi0ur0Zp0qlViJRgV1gSoVWJJKz2QIhX6qQQyqFRq8zGw1W0BYKpHQam0xmzM2RsVj86OjU4OBIT89A/8Do4NDYwOBod+9gZ1cfOnrS3Q8Io6Ojp7dvuKt7oGdvVKWvfxggCDi/rb2rs7MXWFtbV0dHd3c3IJLujg603dXV29vT13Lbjxt/+LVj//bN0a7WyfHJaSwOg8VPTs1iMPjx8emJiZnJyRkMZgaHm5uZwc3O4AFnANujEBJ+jozDo1ACQAQ3SyAQqEwmj8XkMZkoefB4YvDIFqtLi9YjsYGG0wU7nF67w2Ozuex2N2AIQBIGvQWAiMvp9XoDPm/I4w6gZcECEZ8PCQaj4UjS6w36fGhtMb8/DBBke+fUu/VBli9WHTv+W33+p/PiyxfdXpLONG6wY+Ek15uUmZy0KCKIRYXAwQdTTMgypjYMRWJMt4/qQwCgkEwOPAAUs3fE6BzUmnsFwiYm7QiFdETA66YzGtmizoVV/2/feevTv/z1Dx//4Z3f/ea1156/7/ya2TXPlDbTRU1S4wBf3y3VdEugLpNn1uDEKg2DHEmbHBqx2PESdQ+Lf4DO2CdWdSqMIxLdEGQeswemHaFpb5xh9hGcYbonzvMBOAjTFDaM1U+N5mXOENEenDJ6BoWqoyJ9i9Y9pbZjHEGaN8LVWGaNjmlHcNbknXRH5q3eWb6sS+2YMPoJzhAJSTLswXlvguoIz7sjpEJNiQBS8Y14ErOWwLQnQTIFsNE8HWAHkmYG4iwXMuOKYv0J+snjqYfO777w4lNPXXrwnp3lS4/c98Tj957azeTTikSCHw4zSiUoXZCXFtXxtCCVURTL+vWN5Gef/aleLqyuum6iPs/t2Njc3txaqlZ5TNpgT1t/R8voYC+DzpBKVQqlVq7QqlQGYAA7JGKlQq5Fa2/I1Oh4BF8iEsrQoRahTCySS0QKoVAuEiuAF6fTORQaaxZPHBoab2vrRjmjq7+js/dYW9exY6gByGhp7WhsOgbAorOj99ixzubmNtDf2d3f2NTa2toJTgaEATijubG1q6Ons7O7s7Onq6uvpeVYY2Nz4+172PGv3xhqaxgbGZuamhkdmUSzOgaG+geGQWNkeHJ8DDMzM4fHo+mleBxxehqPweDm5snApjAolBDJNPwcCTuNp1CZbI6QyeKRKAwyic7liiBIr9EaVJBBDRn1eovF4gSQAcNIKBQP+MOhYMzl9DkcXgAcwQAKFqAfdEYiqesWDicRJO5weACjBEPxZCp39p773nu/nlL6hapjx43q5VefCCJsf5AMByluhGQLEoNpcSjBgkMUX4gCxxmeKM0ZxKdKfCQn8idFVjvZYqfq7DMiTQdH0SyG2rmcZkg1bjLhYwnN2kbi3Pmtt9/5xf/+298+++vf/vzZZx989MEbb11b34hyeG1z1DtJ7EMs0TGGuE2i65ebBqw+vEI7IFZ2scXtCmjQ7sJCpkGJpkeq7JMoexn8JrV1zOCe1rindL5Jgw9rhfF2mOhGmAbXtMGFscIYZ3jeGpiVW3oFmmaJoU1m7lZah2whos49YYanvODmA3SNeczsGTX5Rj1RgiNAMHnnDfCcHaG6glR/igPMF6E4XFiLbdwNY8NZsj0yiWRpcIQZSnDCGUEgSUOS5CAyZ3dNuMNT7vCEPTATjDKTafXCknd9zbe4YE2mpT4fJRqhRWP0aJaHxDnxnDSWk0aS7FRRlC1IkTCzuuj59NN6cfS66rqZ+q+U0tWlJZ1aOTrQ1dPW1NfRgsNiRUKpUChDi5RzJUKBHPCEQCATi5VyhQawCJ8r4nGFe7Sh4PMldBqbRKRhpmZxOAKFyqIzOMBwwLtjcWPjU61tnYAwmpqPNbW0XQ9gNDW3NTS0HDp09OCho4A8GhpaDx9uamxsbWo6dvhIU2NTW0dHbwd6ZmfrsY7G5mNHjzYdbQC9XX39Q/39g11dPa37f9b4o39q+enXR7tbJyenxsYxI6NTo2MYdFhnZLy/f2hwYASAxfw8iUigUABMkOkEAhncHkAQ7DQO4AhoAyIB/Wi2KQvlJAaTRyBSZ2fmcXgSncFlMLkcjgAgiFQGgUdWQwazxWm1uQ3osi8OSGPSaM0ms9PphP3+CAyHwdbnQ9yegNcbgv1hj8cPzG7z2O1ewCW7u3fVU0r/jurY8d/q8z+dV958EskIfUEikmD54ixvnBvKyvwZjjNAtnvmzY5pvRNrDMxYAngkKwrGBE4XyeYgOgMUsaGXr+kWKXtE4h6lYrJQ9L3169f//MmfP0WnvPzv//iPv332l7/8/uM/vPvhu6+8ftVkZUwT9s3Rj+IZB6jcIyzxMaqkka/t0fsIXHkPjdskVvfzZR1qfb/ZPaUwDTthksuL15iHNZYRk3PS6MEY/VgjPGXyTums41bPnNY5C9mmFdZB296kG61nSm0b56vaZfouk3/KlyK60zQrMmNyY3TWGRk0xFO0mPwTjiDOFZi3+2dMvhlnkOoJc+AEz+kneX2EkJ/odmBsjjGlqd3oG3BHp70pijtGQdJcd5QQSFJNZqzJjHGGsCpzu8k+HEmwkRgrGuNEoqxkXojk2EiGEUtS/SG8P0mJ5QWxrCiZl4XjvGxFm8op4QilsGT+5NN6bkdddd1MXceO9ZUVp8U0NTrQ097U3dbY39XG4wjUagOkNgL/ara4DEa7VmcBjnYvzqGBIKNMqpKIFXyumI8u66qQSJRMOpdKYVIpLDyeTCRS5+ZIWCxueHi8q6uvqakVcENjU0tLazs6sNKJxjaOHm0+crS5oRENbBxr6wbW3NJ28OCRO+/cD+Cjce8jgEsOHARc0tLccgzwR3dPf09v/9Dw+NDI+LFDdxy+5Z8af/z1kc7WsZHx4ZEJLHZufGIag8VPYWZHRyZGhseBjY9Njo9NAchg7JEQlcYikxkEIoVCZQLD4Yn4OdJeqTEWjcamUJgEAnWeQJmbJxNAJ4MjFMllaBkSg0qtlyk0ApFcKlMLhDK+AC1KJhQpRGKlWm20Wj0ul9/p9DucsNPlDwTRSbNuj98Ph4PBGGARi8W1vLzx29++U8eOL1IdO25UV689FswLgkWWL01Fcvx4BQpmBf4MM16UxXISZ4DgDBIcQbItwHCGGA4Pwe4m6W0zFv+81jWptk3q7NN2N/XkbvXD33/wh08+ee+j3//+4z9+9KeP/vin33/w+/dff+2ll196+sJjuzjKARwV2BEM/lYa+5DBjWHJW0i8BoGyX2WZ5Eq6BIp+pqSTJW0R67tE6h61fkwk75Sp+nT6cZN5SmuasPrmbP5ZjWtA7xi3++adCMUWIENurNoxITOOKMwTHHmXzDAiNw5LzYNyaz9P3y7R9RisGHeQ6o/RXf45f5jujzLdYYoHIcaL/EReFsvI4gUpAItQgp9MSSMRjss9Z/NMOYNYg39Y7euTO7pN3klPiBiM0aIRRhChugKz6PIxIQoSZwYTDF9wHgkSYBjvDsyEU5RonJLJcuIFDlKgxyvCWFGQX5RnSkI4RLQ6Z3Jl658/rRdHr6uum6lr166d3N4GzDE+0NPRdBhYb0cLjUwVCoC71QDUMJrsWr11Dzh0AESkYqVIIJNIVCKhnMMRsph8DkvAR0tyidhsATAOVwjcOXDk6OAFwI6h8bZjXQAjWlraADfs5Ye2HW1oaWxsPXK4CZAHaDQ2HgNbwB+HDjceOHBk3/5DRxuajxxpBNbQ0Azgo2HvfNA4cPAw4A9AHiOjE51HDzX86BtNP/7ntoO3t7UcGxoaA7QxNoYZHp4A8DExOTODI0xhcKNjmNFxDDiEx5PQaqdzaGIHmcIgkehEEpXNEYjEciaLB+6czxdRqUwikcZEkzxAj5DDFYnECqlUBVADmESq4gtkoEep0kNas9Xm8QKesHoUCp1WawbYAdgC/FDA7A4fglbsiMF78Y9QKAZYZHll8+06dnyx6thxo7r68gVbcNYcGDf6J2zheStMcPiJLmQODpH9CDOU5vsSdKufHEnrvGGh2U+yR2i+DMeVoWvdGI0Da/QSikue137xwkd/+vgXb//q2Veee/Wt195+793fffjuW79589rrV3/+9IVUycQQtdJFHRjiQRKjiS9tF6o7uLI2CusIk9MkV/dJ1f1C1QBHPKDWYUSyXpGinyvuZgibFOpunXZAbxjV6iecforGPc7SNknN/WrToDOAcwVnTD4MZJmQ60YkqhGlflqiHJFqJgweEmQfV5kHLU6cy0GMRbkeeE6p6XfC84ClbEGazUvwBGecyKzBM2FDph2xeX+aE82IAnEuHGE4/fMG15TKPmD0jtvDGKtn1GgaNlkGbbYhp3tKbx92+WbCSWYowQyn2e4ADolREzkRjMxH0/Q46EcI0SQZSROjOXa6IksVBaEEBZ2aG+OWl32f1FNK66rrpurll15MRsNTIwNtjQdbj+5vbzqMGRuTSBQqtVENmVRq9C1fozXtve4b0LVORAoJOs6ilcshFoPHZqKruImEMg5HgA5kAF9OpAKPTiBQ5uZIGMxsZ2dPQ0NTU1NLa2t7e3tXe3t3S2tHc0v7MXT0pL3haFPjHmEcPHj08OHGI0fRcZYDBw7v23fwaANot4BD6EDMwSOHDzeg4NKKfnBgcGRwcLSr8UjjT77Z9KN/aj+EYkdHR+/A4BjAjpGRKcAcGCx+ZpaAnZmbI1CodPb0zNwsjjALerD4uXnyPJFKZ3JpdA6NzgbkwWILGEwencEBbRSeOHwGepQNtlyeGHTSGABPZDKZGjCHXKGBNEaFUguAzGx1GkwOnd7mcMJ7COI2mZ0Go93u8Lo9Ad/eNBavN+jxBF1u//rG9jvvvFvHji9SHTtuVFeeuU8o71FCw5Bh1Oaft3qpZvu8xTrrtJNMBpw7xPAB/5qVIGl5ICmGM0JrjGGN0zwFgT5EkNonzAFSPK89ebr27HOXLz318H0PHL/4xLlnX3zm5ddevHrtmbvv30jkzGzJ4CztgEjbQxc3MyWNYnWHVNvFkxyjMQ/zBS1m+5TEMKyyYxWGCY1xymjCKLUTfPmwUDWsNozrTRNG+6RI0am1j2g8gyxti8w8oNEN+wMkm3sCjlD15gk7TIZjgmhObbWTLPZZk29G6hgSGYedPhLsY6RTSl+AAa7s8OMjBa41RLR6yVYfXuea1NmxzggtmOIHozwnQoGzglBeAkdZwRTPEaDBIbY7SFDq+hSqPr15zOKdtPgxGueYPTALR0kwQvb45uJJTiBCi+aFAF+QFCOV4seD9GyMkc3w0zn5QtWYzytiWWGmpM6Xdbmi9ZNP6tGOuuq6mbrw4P0MEr69CWUOYD3trTQKXSqDBELgX3VSmUYm18jlGoVCCzr5fJlIqJTLILVap9WZAIXweeK9ihcCDltIp3Pm91I1pzCzwMePj08N9A8da2tvaGhobmoB5NHcfKwZTeloOnK0sRHtaT1yuOHAgUPA9h84dPgIYJP2trauY62djY3N4HyAHW3HOpoam++8cz845+hRcELbsfaunt6Bvr6h7pbGhp98q+GHX+1rPtTT1dPe2ds/ODo8Mjk6OoWdxo+NYyenZgBtEIhUQA/zBApADRyeCJiDQmWy2Hy+UAaMyeZfD88AyADnAPIgkxlMFprVgceTiCQ6k8Vnc0U0BpcL6Eqk4PEkHI4IDX5I1QaD1WJxOV1+p9tvs3t1eqvZ4nR7gjqdVauzOBw+NL3DG4ThsMsdcLrg48dP1ifQ/h3VseNGdenJ8zR29xzxsEo3CVlmldZpe4gViPKRuCCWlLgQht5LCOSFFj8OhZIQ0ZYkW5NMpGLwppUGF0XnImg8c1rPnDcuiJcU3hDd4aXXVr1nz1dKqy49jBfrByW6UYa0iQ8185VNAqhdYexVG/uEsnaRrFMkaZdD3SrbsNwyoDIPC9WdEqhXZuyXW0d1vjlbmOiOEQ2uaT44U91p8GAhJwaO8UpVY7qotvnwHoRksI1bHWMeeBr4frufAplHdN5xTQBjDOE8YQIcpiXysnBKiGZ3ZjhwkupK0oz+eV+G4QCggJYjk8XzylheEYizIhm+J0r1JMneJD2Q5C4umdNZQSBKDSe50QzPFphWOvr4xmNCUwdkGfaFya7ArDdENDkmDZ4ppX3AEpz2IUSvdzbgno/6OVsn/RvHPdUFU7Gqqq5pCyuapfX6mix11XUzBRzg2sriOPDgR/e1HNnf0dJAJpLBezzwmnqjXaM1A+AAL/diiYrLk6AOmC0EJpGoQD+6cixbAPw3lcYiEemEeTTIQSTRJiemgbMfG53q6xtsbW1rbm5ta0PHVoA1NbcC4Ljzjv233Xrbvjv33377nQf2Hzp06MjBg4cPHTq6f68NeoAB1Ghv7+jo6GpvQxHkwP6Dt992x0GAJocbGo42ARbp6Ohpb2w48uNvH77lq/2tR/v7+oeGx8YmsCOjk4A8JjEzo2NTg0OjY+OYmdk5DBY3PoEFd4XGOQgUMoVBZ/KEYgWHJ2JzBUwWj8bgcHkivkACEIRGY4MHoVKYFHQghsbhCKhUFlrPlMEFZ4IfQSiSKxQand5sNDnAT6SGTEqVAdKYhCKFWmM2mp2g3+X2u9HZsyHQsNt9RqPDYnWvb+zUF77/O6pjx43qpVcuuwNck3sOsk+qrFiNg2BHWO4kLVYQhpICX5xnDlI8STbw6G6E6UnRvVmaPcGAs5JAUhLOyiNFRSDDd4aJ8ZI4lOXAKbI5MG0JzLqjdEMIZ02QIHiGp+vjato0jiGDfcLgnLTA054g0WjGGMyTFjvG5SNYfDitZ1LrmdW4pyHHhNE7obaPmHxEA4zRBSaFxkGpdkRrw9jAmV5iICUK5STxksrmJ+jhMXNgxhsmu4PEYJbvjNJU1lGxfgDyzuh8MybPuEDVItP3aizjwSTdE561hqcsqXlHhuaIE60w3hOgWf04X4wEJxi+CDkQpXuiFH+WFiqykkVZqahDItxQhJEryFJJXgShma0TIkW7QNWh0g1ZbNN2+1QAoToCJDtM1tjHTb4Jd2g+FGelc4pMRpavyQoL6uqSsbpsXFi3VNbMSxuhTz+rz2Spq66bqP9nfXVxdLCr6dDtLUcPjI+OSmUqNZqdYEYXboWMCpVOLFUJhAouT8oXyMBbvlKhVaJTajVikZzLFdIZHOCn6XQOgUjF4YjTWDwwDGZ2ZGS8u6unsaHx6JGGo0cb93jiIMCLvbjFwQP7D9x5xx0HAGgcPHz48BFg+/cf3L/vwIH9hwFz7LtzHzjtjjv2HQQccujw0aPgCk0HDx4BFwENwDGdnd1d3b0dLc1Hf/odgB1dR/a1NjV3dPb09g2OjExMTc1cL7uO1u3A4qYwszOz8zMz89PTcwQClUpnszhCEplBJjOJJDp+jkQkUecJaAgEPAuLxQePQyRSAT8ByODxxUKxnM0RUalsHlfM40nQcRnwvAx08IXPlwmFir2sUoVKbYD2fjGpHFKo9OB3AwagBCCI0+U3mR0qtb5WW6mnlP4d1bHjRvX6m88m8gY4LrL6GMGULJQS2QLzzjCuvKzP13SpogqOCzNVQ6FmTlc0gTw3VGBHCgJ/ihvPSRMFdaqgcweYoTQvXJD4sxx/ku6O4c3ItDNFg8LTiuCkOjjLNwwavLOWwBy4sh3GG6xjziDR4MYpDYM2z4w/zIwVpKEczx6l2MNkcGVfjOGCSSbTjNYEaGPM4SUq1GNS1YDDM2fzkTxRjj/HjxSFsTzPEh43w0MOz4QPoaaqUKwscUYpxiDRGmMZAzSFZURp69f5xo1erAeZ03uHNPCgI0OwpkhwhoVk2P4My5WgOWNEix8bztCSRU64wPSl58JZar6mXVj2lJasyZIsmucHY2QEIccjLD9MRRCu102JhQWhANMVIAMMShX1njDDFSMieU44LYinJHCQ6YuSMhUoWzFlKtpgkhvOcsvLrvoE2rrqurlaXVkc7utoOnznYF/P/DyZy5dx99aLF4lVQpFSBEysBH5Uo7VIZRqpVC0G/UIZB00gFXI4QiaTtzc3hD4zOzc5NQ1cPloQfWS8p6e3paWlYY85ACgAOwiw48Ch/fv2A8K48479t/77rXfesW8foJD9B/ft23/7bbfffuttd95x5759B8DudQPMcQiwygHwz6EjR442NDQ1NjQdBs3DR5uamg/ecfu+H3x9/3e/0t2wf6Cvt6e7t79/cGh4bHIKOzmJnZqaxs8TsdOz17EDi53D4Ulz82RwtwA7AHzszY/lUSho9isBDYHQgTGZ6PAKmcwA/WyOgC+QSOVqpUqHRjhQ2IIAiEgkSrALWAQYk8UH/AFMIlVpdWa9waZU6cFPB0yu0BoMduPeVFuL1QUQpFpdfvs3v61jxxepjh03qpdfeyqS0cVz+mhKFcurEmU5HKXp7TPZClSsQasbzvKCKVlQp4vyQIQSTNBCGbYDIfoi3FhKnato43FhKMaOFgSRihjJoKvM+5NkpMz8f9s7s6e2rjOA/wmdaWfy1umDl3HHMWatbRoHG+IlXthBICRAIIEAIQksJFkSEdq40hWLNoQkMNikduLUT/0vOqxm0b5e6UrGvGQ6k8nUnWT6XclJHTtxPa2rvny/+Tg69zvn6p57BKMfWs41ONgKa6NIf0NqbBjX31YR7Yrppgn9DQPRop78VKm7KdFel07d1BAtU5Z2jaVVaWrUEPUq060pS4fW0GEydkzr2+QTV9SaT8n5rimCJVPXi6R1Kl2LRt+ps3DIhW7bvT7PoyGjrVNrbjYvdumdrGkwCW8fsdRHLAuJJRHhEaisLXdnGyw+LrnE0zrZn811TLs42vkWvZVlmes029kmO1s736aebTLYWGYbm3CwJs31OkuTzsqa8Qjnlkbsq+I535CWbFfpbplJrs05srAsJeZ4Tu/oysO7rvvDpJtrme+fXxh1r95xrozN+8adPqVnRUM6+M4l2YxbSjgHCEe/+/OJr/7i+vbvf8M/WQR5j3zxpwfXai9ev1I3MDAMYjEuU4FkDI+M9fQMDg5KhEJpb+8AmMfI6J1R8cSoWDY8LOnlCTgcXnNTW0szc2UTFot9+xbzTH/16o1bt5ryS3I11tVd+WP1R+fPVVeUV1WCeFSey38g9EJFeQXzsseHZ0rOnAV7KC8DjSgD1QAdOXHs+PFjxyBOnjhZUlICjgK6cfL4iVMnT5WAr5z+sKy0/Ny581WV5ysr/lBeXnH61KnfffCb3/76VyW/P1XzcU1d7Sef1F25fv1mfT2oBmM/4EA3btwG7WgAA2lnNze31ze0tDDLonOY67N0ckFBuN19EOyu3i4OD06q8MoNXyBkd/VA9PULmW+y5L/GIpXKx8bkEqlMNqGCEEtkoCNj4wqxdAKcY3T0TuHrLdJxhWRMqVBo1RqD0WhRKCflCg2YBxjJoy+e4JssbwG1410JBP5qmZXY3Ur7oty6MOK8L3b6RN4HY+6VMe/KuO+enLQLCBvPtSyyuQX2Rb5nRWxfErl8Yw8fmte+JDzLSodzxO4ZmvHyzc7u+cUhwtlruy8k3L2kd5BYEFgWhkiHYNY+ZJ3lkXOcaZJlMrL06haC7LW6hmccI+SMQE/2GOa6TQ4O6emzeoadHpmZ4N3VNk6SbTqy1WLj2n2jDt/4vGPU4Rbb3ZIZm0BnaZ3x8uxrQtuayOzuB3UwuNnm5QH7I7HB2y0jGpQkV2cfMLj6PgOfcHdbPYMWt5BcHDTaOCYXx0T2uBYlc84Rla5NMdUg012TGa9MWhtNLhCmbpONO+cd8dxXLK9Nrnw55V5VOJcnHF6xb2Vs7ZHS5hm0Lgrc9yXu1WHHssC5OrTwQLRwb3TtsXppTeVZVftWtd7VyXuPNatPDO4V2cLSsGtJ/PlX+pU1/be4bgeCvD/gCfDJnx93d3Xy+QPwnDo0JL0jU00oNDK5WiSW5f9rl0qk8jsytVw5qVLr5JAXSVmsLg6nl9XOZucvZQK+wuH2trZ2tLay2to6wULa2zsv11yuYZY6rwMbqL1U+/HFSxDVF6qZT4pWVYGR1NRcZlY6r/7o4sWaq1ev1dbWnT93vry0vLTkbOmZkrLS0sryiuoLF6oqq5i3WCoqIEBZIMrL4KeiqqKyrOQsmMeZ06cvXbrU0NDY0tza0NAExgNH72Rz2V1cVkdXczOzRGl+TQ5eT08fr08A0cvj8/mDPB6/r39AIBDyeIJ+/iCoBugUsziYYEgoFBUqw3nnGB9XyGR3ISbkKrlcI5OplDAbKm3eJ7Q6PWEwWSzkHGGZnTbPGkyk3mgxTlunCavFwlwEzmC0EOZZvd6cvyYLLhf2i6B2/Bt+/MX55puv0+loOhOls4l0Nkxlg5lsOHsYSdMhOhvJ5uKxxEGS8meyTIZKByCZyUE+enSUfnaYonNxigrRdDidDaYyAYoOJSimjKf9KTqQzoboXIzORWk6mkr6M5lgivKnUv5UzJ+lY3Q2lqEj6XQomQqkMsFkJgC7UNnw86N0Jh2NxnZjqf0EtU+l/Wk6TGeZO4H+hZGkqAAkU9lQ5jCSooPx1B5FB6n8ZoIOxKj9RDr4MjL+VNpPZUIU9M9Axp+EnnT42WESBh+N70eie5HYTji+Had2YQxJ5iyCaebcE4fPkrlncToHBw1n4FyyoWwukkwdJChmSBnoRgcyuXAmF4LIPoPhwThjsCNNx7KHsdxhgpmu/GjhfrLZxHff/ePVRwFBkP+So6OjcDgcDIaCwbA/EAyHI5FILBqNwy1kIKAejcVjsUQikYQyEonu7x8EAkG/PwABwL4Hfj8k/fltKA4ODnaA7e2tra2d7Z2nO08hdnaebm3lU1Bub0M71DY3N6G+u7u3u7ubz2xvbmxurG8wsbGxucmUTAWqm1vM7cZGYS+mtr4O3dbX1/P3sHuwf7C3t/f06W5hJPnhMaN6dbShEHOmsFmoBENhqASYUb/sUGjNz0YImmA28hMSLURhHmBCYkwlHokyExVPJFMpiqLShTKZohKJVDJJQQUy+c0klUpD5vnz5y9evHj9AUB+ALXj3fmfq+ubB/j+Ze7Nln+Rb2OKH936J73fZtz5vV5PvsZb2n9y0Fd4M/Vm5md5x24Igrwfvmd4PfkDv9jw/+Y/Hljxd0R+BtQOBEEQBEGKBGoHgiAIgiBFArUDQRAEQZAigdqBIAiCIEiRQO1AEARBEKRIoHYgCIIgCFIkUDsQBEEQBCkSqB0IgiAIghQJ1A4EQRAEQYoEageCIAiCIEXin8e6mC/X8wsnAAAAAElFTkSuQmCC){width=\"500px\"}
\n", "\n", "The general setup is that we are given a dataset of images without any labels, and want to train a model on this data such that it can quickly adapt to any image recognition task afterward.\n", "During each training iteration, we sample a batch of images as usual.\n", "For each image, we create two versions by applying data augmentation techniques like cropping, Gaussian noise, blurring, etc.\n", "An example of such is shown on the left with the image of the dog.\n", "We will go into the details and effects of the chosen augmentation techniques later.\n", "On those images, we apply a CNN like ResNet and obtain as output a 1D feature vector on which we apply a small MLP.\n", "The output features of the two augmented images are then trained to be close to each other, while all other images in that batch should be as different as possible.\n", "This way, the model has to learn to recognize the content of the image that remains unchanged under the data augmentations, such as objects which we usually care about in supervised tasks.\n", "\n", "We will now implement this framework ourselves and discuss further details along the way.\n", "Let's first start with importing our standard libraries below:"]}, {"cell_type": "code", "execution_count": 2, "id": "e2627246", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:38:21.024058Z", "iopub.status.busy": "2021-10-10T16:38:21.022126Z", "iopub.status.idle": "2021-10-10T16:38:22.765560Z", "shell.execute_reply": "2021-10-10T16:38:22.765143Z"}, "papermill": {"duration": 1.779594, "end_time": "2021-10-10T16:38:22.765675", "exception": false, "start_time": "2021-10-10T16:38:20.986081", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_1189/3845858059.py:24: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`\n", " set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "Global seed set to 42\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Device: cuda:0\n", "Number of workers: 12\n"]}, {"data": {"text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["import os\n", "import urllib.request\n", "from copy import deepcopy\n", "from urllib.error import HTTPError\n", "\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import pytorch_lightning as pl\n", "import seaborn as sns\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torch.optim as optim\n", "import torch.utils.data as data\n", "import torchvision\n", "from IPython.display import set_matplotlib_formats\n", "from pytorch_lightning.callbacks import LearningRateMonitor, ModelCheckpoint\n", "from torchvision import transforms\n", "from torchvision.datasets import STL10\n", "from tqdm.notebook import tqdm\n", "\n", "plt.set_cmap(\"cividis\")\n", "# %matplotlib inline\n", "set_matplotlib_formats(\"svg\", \"pdf\") # For export\n", "matplotlib.rcParams[\"lines.linewidth\"] = 2.0\n", "sns.set()\n", "\n", "# Import tensorboard\n", "# %load_ext tensorboard\n", "\n", "# Path to the folder where the datasets are/should be downloaded (e.g. CIFAR10)\n", "DATASET_PATH = os.environ.get(\"PATH_DATASETS\", \"data/\")\n", "# Path to the folder where the pretrained models are saved\n", "CHECKPOINT_PATH = os.environ.get(\"PATH_CHECKPOINT\", \"saved_models/ContrastiveLearning/\")\n", "# In this notebook, we use data loaders with heavier computational processing. It is recommended to use as many\n", "# workers as possible in a data loader, which corresponds to the number of CPU cores\n", "NUM_WORKERS = os.cpu_count()\n", "\n", "# Setting the seed\n", "pl.seed_everything(42)\n", "\n", "# Ensure that all operations are deterministic on GPU (if used) for reproducibility\n", "torch.backends.cudnn.determinstic = True\n", "torch.backends.cudnn.benchmark = False\n", "\n", "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n", "print(\"Device:\", device)\n", "print(\"Number of workers:\", NUM_WORKERS)"]}, {"cell_type": "markdown", "id": "ae59fd9f", "metadata": {"papermill": {"duration": 0.029754, "end_time": "2021-10-10T16:38:22.826902", "exception": false, "start_time": "2021-10-10T16:38:22.797148", "status": "completed"}, "tags": []}, "source": ["As in many tutorials before, we provide pre-trained models.\n", "Note that those models are slightly larger as normal (~100MB overall) since we use the default ResNet-18 architecture.\n", "If you are running this notebook locally, make sure to have sufficient disk space available."]}, {"cell_type": "code", "execution_count": 3, "id": "482bf0ff", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:38:22.890766Z", "iopub.status.busy": "2021-10-10T16:38:22.887393Z", "iopub.status.idle": "2021-10-10T16:38:25.058940Z", "shell.execute_reply": "2021-10-10T16:38:25.058450Z"}, "papermill": {"duration": 2.20285, "end_time": "2021-10-10T16:38:25.059060", "exception": false, "start_time": "2021-10-10T16:38:22.856210", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/SimCLR.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/ResNet.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/tensorboards/SimCLR/events.out.tfevents.SimCLR...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/tensorboards/classification/ResNet/events.out.tfevents.ResNet...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/LogisticRegression_10.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/LogisticRegression_20.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/LogisticRegression_50.ckpt...\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/LogisticRegression_100.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/LogisticRegression_200.ckpt...\n", "Downloading https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/LogisticRegression_500.ckpt...\n"]}], "source": ["# Github URL where saved models are stored for this tutorial\n", "base_url = \"https://raw.githubusercontent.com/phlippe/saved_models/main/tutorial17/\"\n", "# Files to download\n", "pretrained_files = [\n", " \"SimCLR.ckpt\",\n", " \"ResNet.ckpt\",\n", " \"tensorboards/SimCLR/events.out.tfevents.SimCLR\",\n", " \"tensorboards/classification/ResNet/events.out.tfevents.ResNet\",\n", "]\n", "pretrained_files += [f\"LogisticRegression_{size}.ckpt\" for size in [10, 20, 50, 100, 200, 500]]\n", "# Create checkpoint path if it doesn't exist yet\n", "os.makedirs(CHECKPOINT_PATH, exist_ok=True)\n", "\n", "# For each file, check whether it already exists. If not, try downloading it.\n", "for file_name in pretrained_files:\n", " file_path = os.path.join(CHECKPOINT_PATH, file_name)\n", " if \"/\" in file_name:\n", " os.makedirs(file_path.rsplit(\"/\", 1)[0], exist_ok=True)\n", " if not os.path.isfile(file_path):\n", " file_url = base_url + file_name\n", " print(f\"Downloading {file_url}...\")\n", " try:\n", " urllib.request.urlretrieve(file_url, file_path)\n", " except HTTPError as e:\n", " print(\n", " \"Something went wrong. Please try to download the file from the GDrive folder, or contact the author with the full output including the following error:\\n\",\n", " e,\n", " )"]}, {"cell_type": "markdown", "id": "f7e66a12", "metadata": {"papermill": {"duration": 0.030275, "end_time": "2021-10-10T16:38:25.125477", "exception": false, "start_time": "2021-10-10T16:38:25.095202", "status": "completed"}, "tags": []}, "source": ["## SimCLR\n", "\n", "We will start our exploration of contrastive learning by discussing the effect of different data augmentation techniques, and how we can implement an efficient data loader for such.\n", "Next, we implement SimCLR with PyTorch Lightning, and finally train it on a large, unlabeled dataset."]}, {"cell_type": "markdown", "id": "f78a4c91", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.030063, "end_time": "2021-10-10T16:38:25.185749", "exception": false, "start_time": "2021-10-10T16:38:25.155686", "status": "completed"}, "tags": []}, "source": ["### Data Augmentation for Contrastive Learning\n", "\n", "To allow efficient training, we need to prepare the data loading such that we sample two different, random augmentations for each image in the batch.\n", "The easiest way to do this is by creating a transformation that, when being called, applies a set of data augmentations to an image twice.\n", "This is implemented in the class `ContrastiveTransformations` below:"]}, {"cell_type": "code", "execution_count": 4, "id": "c7868578", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:38:25.253314Z", "iopub.status.busy": "2021-10-10T16:38:25.252845Z", "iopub.status.idle": "2021-10-10T16:38:25.254844Z", "shell.execute_reply": "2021-10-10T16:38:25.254446Z"}, "papermill": {"duration": 0.036682, "end_time": "2021-10-10T16:38:25.254947", "exception": false, "start_time": "2021-10-10T16:38:25.218265", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ContrastiveTransformations:\n", " def __init__(self, base_transforms, n_views=2):\n", " self.base_transforms = base_transforms\n", " self.n_views = n_views\n", "\n", " def __call__(self, x):\n", " return [self.base_transforms(x) for i in range(self.n_views)]"]}, {"cell_type": "markdown", "id": "ebf730fc", "metadata": {"papermill": {"duration": 0.030955, "end_time": "2021-10-10T16:38:25.316735", "exception": false, "start_time": "2021-10-10T16:38:25.285780", "status": "completed"}, "tags": []}, "source": ["The contrastive learning framework can easily be extended to have more _positive_ examples by sampling more than two augmentations of the same image.\n", "However, the most efficient training is usually obtained by using only two.\n", "\n", "Next, we can look at the specific augmentations we want to apply.\n", "The choice of the data augmentation to use is the most crucial hyperparameter in SimCLR since it directly affects how the latent space is structured, and what patterns might be learned from the data.\n", "Let's first take a look at some of the most popular data augmentations (figure credit - [Ting Chen and Geoffrey Hinton](https://ai.googleblog.com/2020/04/advancing-self-supervised-and-semi.html)):\n", "\n", "
\n", "\n", "All of them can be used, but it turns out that two augmentations stand out in their importance: crop-and-resize, and color distortion.\n", "Interestingly, however, they only lead to strong performance if they have been used together as discussed by [Ting Chen et al. ](https://arxiv.org/abs/2006.10029) in their SimCLR paper.\n", "When performing randomly cropping and resizing, we can distinguish between two situations: (a) cropped image A provides a local view of cropped image B, or (b) cropped images C and D show neighboring views of the same image (figure credit - [Ting Chen and Geoffrey Hinton](https://ai.googleblog.com/2020/04/advancing-self-supervised-and-semi.html)).\n", "\n", "
\n", "\n", "While situation (a) requires the model to learn some sort of scale invariance to make crops A and B similar in latent space, situation (b) is more challenging since the model needs to recognize an object beyond its limited view.\n", "However, without color distortion, there is a loophole that the model can exploit, namely that different crops of the same image usually look very similar in color space.\n", "Consider the picture of the dog above.\n", "Simply from the color of the fur and the green color tone of the background, you can reason that two patches belong to the same image without actually recognizing the dog in the picture.\n", "In this case, the model might end up focusing only on the color histograms of the images, and ignore other more generalizable features.\n", "If, however, we distort the colors in the two patches randomly and independently of each other, the model cannot rely on this simple feature anymore.\n", "Hence, by combining random cropping and color distortions, the model can only match two patches by learning generalizable representations.\n", "\n", "Overall, for our experiments, we apply a set of 5 transformations following the original SimCLR setup: random horizontal flip, crop-and-resize, color distortion, random grayscale, and gaussian blur.\n", "In comparison to the [original implementation](https://github.com/google-research/simclr), we reduce the effect of the color jitter slightly (0.5 instead of 0.8 for brightness, contrast, and saturation, and 0.1 instead of 0.2 for hue).\n", "In our experiments, this setting obtained better performance and was faster and more stable to train.\n", "If, for instance, the brightness scale highly varies in a dataset, the\n", "original settings can be more beneficial since the model can't rely on\n", "this information anymore to distinguish between images."]}, {"cell_type": "code", "execution_count": 5, "id": "cfb65837", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:38:25.382410Z", "iopub.status.busy": "2021-10-10T16:38:25.381935Z", "iopub.status.idle": "2021-10-10T16:38:25.383969Z", "shell.execute_reply": "2021-10-10T16:38:25.383557Z"}, "papermill": {"duration": 0.036481, "end_time": "2021-10-10T16:38:25.384069", "exception": false, "start_time": "2021-10-10T16:38:25.347588", "status": "completed"}, "tags": []}, "outputs": [], "source": ["contrast_transforms = transforms.Compose(\n", " [\n", " transforms.RandomHorizontalFlip(),\n", " transforms.RandomResizedCrop(size=96),\n", " transforms.RandomApply([transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.1)], p=0.8),\n", " transforms.RandomGrayscale(p=0.2),\n", " transforms.GaussianBlur(kernel_size=9),\n", " transforms.ToTensor(),\n", " transforms.Normalize((0.5,), (0.5,)),\n", " ]\n", ")"]}, {"cell_type": "markdown", "id": "64514ea2", "metadata": {"papermill": {"duration": 0.030802, "end_time": "2021-10-10T16:38:25.444902", "exception": false, "start_time": "2021-10-10T16:38:25.414100", "status": "completed"}, "tags": []}, "source": ["After discussing the data augmentation techniques, we can now focus on the dataset.\n", "In this tutorial, we will use the [STL10 dataset](https://cs.stanford.edu/~acoates/stl10/), which, similarly to CIFAR10, contains images of 10 classes: airplane, bird, car, cat, deer, dog, horse, monkey, ship, truck.\n", "However, the images have a higher resolution, namely $96\\times 96$ pixels, and we are only provided with 500 labeled images per class.\n", "Additionally, we have a much larger set of $100,000$ unlabeled images which are similar to the training images but are sampled from a wider range of animals and vehicles.\n", "This makes the dataset ideal to showcase the benefits that self-supervised learning offers.\n", "\n", "Luckily, the STL10 dataset is provided through torchvision.\n", "Keep in mind, however, that since this dataset is relatively large and has a considerably higher resolution than CIFAR10, it requires more disk space (~3GB) and takes a bit of time to download.\n", "For our initial discussion of self-supervised learning and SimCLR, we\n", "will create two data loaders with our contrastive transformations above:\n", "the `unlabeled_data` will be used to train our model via contrastive\n", "learning, and `train_data_contrast` will be used as a validation set in\n", "contrastive learning."]}, {"cell_type": "code", "execution_count": 6, "id": "5893d109", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:38:25.510013Z", "iopub.status.busy": "2021-10-10T16:38:25.509537Z", "iopub.status.idle": "2021-10-10T16:41:52.516922Z", "shell.execute_reply": "2021-10-10T16:41:52.517324Z"}, "papermill": {"duration": 207.042247, "end_time": "2021-10-10T16:41:52.517484", "exception": false, "start_time": "2021-10-10T16:38:25.475237", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading http://ai.stanford.edu/~acoates/stl10/stl10_binary.tar.gz to /__w/1/s/.datasets/stl10_binary.tar.gz\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "c111f74da90d4a8f831df4733df12b94", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/2640397119 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:41:53.106628\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Visualize some examples\n", "pl.seed_everything(42)\n", "NUM_IMAGES = 6\n", "imgs = torch.stack([img for idx in range(NUM_IMAGES) for img in unlabeled_data[idx][0]], dim=0)\n", "img_grid = torchvision.utils.make_grid(imgs, nrow=6, normalize=True, pad_value=0.9)\n", "img_grid = img_grid.permute(1, 2, 0)\n", "\n", "plt.figure(figsize=(10, 5))\n", "plt.title(\"Augmented image examples of the STL10 dataset\")\n", "plt.imshow(img_grid)\n", "plt.axis(\"off\")\n", "plt.show()\n", "plt.close()"]}, {"cell_type": "markdown", "id": "d9b44c08", "metadata": {"papermill": {"duration": 0.079471, "end_time": "2021-10-10T16:41:53.515584", "exception": false, "start_time": "2021-10-10T16:41:53.436113", "status": "completed"}, "tags": []}, "source": ["We see the wide variety of our data augmentation, including randomly cropping, grayscaling, gaussian blur, and color distortion.\n", "Thus, it remains a challenging task for the model to match two, independently augmented patches of the same image."]}, {"cell_type": "markdown", "id": "525c3967", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.047654, "end_time": "2021-10-10T16:41:53.615813", "exception": false, "start_time": "2021-10-10T16:41:53.568159", "status": "completed"}, "tags": []}, "source": ["### SimCLR implementation\n", "\n", "Using the data loader pipeline above, we can now implement SimCLR.\n", "At each iteration, we get for every image $x$ two differently augmented versions, which we refer to as $\\tilde{x}_i$ and $\\tilde{x}_j$.\n", "Both of these images are encoded into a one-dimensional feature vector, between which we want to maximize similarity which minimizes it to all other images in the batch.\n", "The encoder network is split into two parts: a base encoder network $f(\\cdot)$, and a projection head $g(\\cdot)$.\n", "The base network is usually a deep CNN as we have seen in e.g. [Tutorial 5](https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial5/Inception_ResNet_DenseNet.html) before, and is responsible for extracting a representation vector from the augmented data examples.\n", "In our experiments, we will use the common ResNet-18 architecture as $f(\\cdot)$, and refer to the output as $f(\\tilde{x}_i)=h_i$.\n", "The projection head $g(\\cdot)$ maps the representation $h$ into a space where we apply the contrastive loss, i.e., compare similarities between vectors.\n", "It is often chosen to be a small MLP with non-linearities, and for simplicity, we follow the original SimCLR paper setup by defining it as a two-layer MLP with ReLU activation in the hidden layer.\n", "Note that in the follow-up paper, [SimCLRv2](https://arxiv.org/abs/2006.10029), the authors mention that larger/wider MLPs can boost the performance considerably.\n", "This is why we apply an MLP with four times larger hidden dimensions, but deeper MLPs showed to overfit on the given dataset.\n", "The general setup is visualized below (figure credit - [Ting Chen et al. ](https://arxiv.org/abs/2006.10029)):\n", "\n", "
\n", "\n", "After finishing the training with contrastive learning, we will remove the projection head $g(\\cdot)$, and use $f(\\cdot)$ as a pretrained feature extractor.\n", "The representations $z$ that come out of the projection head $g(\\cdot)$ have been shown to perform worse than those of the base network $f(\\cdot)$ when finetuning the network for a new task.\n", "This is likely because the representations $z$ are trained to become invariant to many features like the color that can be important for downstream tasks.\n", "Thus, $g(\\cdot)$ is only needed for the contrastive learning stage.\n", "\n", "Now that the architecture is described, let's take a closer look at how we train the model.\n", "As mentioned before, we want to maximize the similarity between the representations of the two augmented versions of the same image, i.e., $z_i$ and $z_j$ in the figure above, while minimizing it to all other examples in the batch.\n", "SimCLR thereby applies the InfoNCE loss, originally proposed by [Aaron van den Oord et al. ](https://arxiv.org/abs/1807.03748) for contrastive learning.\n", "In short, the InfoNCE loss compares the similarity of $z_i$ and $z_j$ to the similarity of $z_i$ to any other representation in the batch by performing a softmax over the similarity values.\n", "The loss can be formally written as:\n", "$$\n", "\\ell_{i,j}=-\\log \\frac{\\exp(\\text{sim}(z_i,z_j)/\\tau)}{\\sum_{k=1}^{2N}\\mathbb{1}_{[k\\neq i]}\\exp(\\text{sim}(z_i,z_k)/\\tau)}=-\\text{sim}(z_i,z_j)/\\tau+\\log\\left[\\sum_{k=1}^{2N}\\mathbb{1}_{[k\\neq i]}\\exp(\\text{sim}(z_i,z_k)/\\tau)\\right]\n", "$$\n", "The function $\\text{sim}$ is a similarity metric, and the hyperparameter $\\tau$ is called temperature determining how peaked the distribution is.\n", "Since many similarity metrics are bounded, the temperature parameter allows us to balance the influence of many dissimilar image patches versus one similar patch.\n", "The similarity metric that is used in SimCLR is cosine similarity, as defined below:\n", "$$\n", "\\text{sim}(z_i,z_j) = \\frac{z_i^\\top \\cdot z_j}{||z_i||\\cdot||z_j||}\n", "$$\n", "The maximum cosine similarity possible is $1$, while the minimum is $-1$.\n", "In general, we will see that the features of two different images will converge to a cosine similarity around zero since the minimum, $-1$, would require $z_i$ and $z_j$ to be in the exact opposite direction in all feature dimensions, which does not allow for great flexibility.\n", "\n", "Finally, now that we have discussed all details, let's implement SimCLR below as a PyTorch Lightning module:"]}, {"cell_type": "code", "execution_count": 8, "id": "a94c063a", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:41:53.767396Z", "iopub.status.busy": "2021-10-10T16:41:53.764007Z", "iopub.status.idle": "2021-10-10T16:41:53.769425Z", "shell.execute_reply": "2021-10-10T16:41:53.769027Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.09737, "end_time": "2021-10-10T16:41:53.769531", "exception": false, "start_time": "2021-10-10T16:41:53.672161", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class SimCLR(pl.LightningModule):\n", " def __init__(self, hidden_dim, lr, temperature, weight_decay, max_epochs=500):\n", " super().__init__()\n", " self.save_hyperparameters()\n", " assert self.hparams.temperature > 0.0, \"The temperature must be a positive float!\"\n", " # Base model f(.)\n", " self.convnet = torchvision.models.resnet18(\n", " pretrained=False, num_classes=4 * hidden_dim\n", " ) # num_classes is the output size of the last linear layer\n", " # The MLP for g(.) consists of Linear->ReLU->Linear\n", " self.convnet.fc = nn.Sequential(\n", " self.convnet.fc, # Linear(ResNet output, 4*hidden_dim)\n", " nn.ReLU(inplace=True),\n", " nn.Linear(4 * hidden_dim, hidden_dim),\n", " )\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr, weight_decay=self.hparams.weight_decay)\n", " lr_scheduler = optim.lr_scheduler.CosineAnnealingLR(\n", " optimizer, T_max=self.hparams.max_epochs, eta_min=self.hparams.lr / 50\n", " )\n", " return [optimizer], [lr_scheduler]\n", "\n", " def info_nce_loss(self, batch, mode=\"train\"):\n", " imgs, _ = batch\n", " imgs = torch.cat(imgs, dim=0)\n", "\n", " # Encode all images\n", " feats = self.convnet(imgs)\n", " # Calculate cosine similarity\n", " cos_sim = F.cosine_similarity(feats[:, None, :], feats[None, :, :], dim=-1)\n", " # Mask out cosine similarity to itself\n", " self_mask = torch.eye(cos_sim.shape[0], dtype=torch.bool, device=cos_sim.device)\n", " cos_sim.masked_fill_(self_mask, -9e15)\n", " # Find positive example -> batch_size//2 away from the original example\n", " pos_mask = self_mask.roll(shifts=cos_sim.shape[0] // 2, dims=0)\n", " # InfoNCE loss\n", " cos_sim = cos_sim / self.hparams.temperature\n", " nll = -cos_sim[pos_mask] + torch.logsumexp(cos_sim, dim=-1)\n", " nll = nll.mean()\n", "\n", " # Logging loss\n", " self.log(mode + \"_loss\", nll)\n", " # Get ranking position of positive example\n", " comb_sim = torch.cat(\n", " [cos_sim[pos_mask][:, None], cos_sim.masked_fill(pos_mask, -9e15)], # First position positive example\n", " dim=-1,\n", " )\n", " sim_argsort = comb_sim.argsort(dim=-1, descending=True).argmin(dim=-1)\n", " # Logging ranking metrics\n", " self.log(mode + \"_acc_top1\", (sim_argsort == 0).float().mean())\n", " self.log(mode + \"_acc_top5\", (sim_argsort < 5).float().mean())\n", " self.log(mode + \"_acc_mean_pos\", 1 + sim_argsort.float().mean())\n", "\n", " return nll\n", "\n", " def training_step(self, batch, batch_idx):\n", " return self.info_nce_loss(batch, mode=\"train\")\n", "\n", " def validation_step(self, batch, batch_idx):\n", " self.info_nce_loss(batch, mode=\"val\")"]}, {"cell_type": "markdown", "id": "5930dd21", "metadata": {"papermill": {"duration": 0.339364, "end_time": "2021-10-10T16:41:54.219681", "exception": false, "start_time": "2021-10-10T16:41:53.880317", "status": "completed"}, "tags": []}, "source": ["Alternatively to performing the validation on the contrastive learning loss as well, we could also take a simple, small downstream task, and track the performance of the base network $f(\\cdot)$ on that.\n", "However, in this tutorial, we will restrict ourselves to the STL10\n", "dataset where we use the task of image classification on STL10 as our\n", "test task."]}, {"cell_type": "markdown", "id": "371aa9b6", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.041653, "end_time": "2021-10-10T16:41:54.360538", "exception": false, "start_time": "2021-10-10T16:41:54.318885", "status": "completed"}, "tags": []}, "source": ["### Training\n", "\n", "Now that we have implemented SimCLR and the data loading pipeline, we are ready to train the model.\n", "We will use the same training function setup as usual.\n", "For saving the best model checkpoint, we track the metric `val_acc_top5`, which describes how often the correct image patch is within the top-5 most similar examples in the batch.\n", "This is usually less noisy than the top-1 metric, making it a better metric to choose the best model from."]}, {"cell_type": "code", "execution_count": 9, "id": "7ae8797a", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:41:54.466376Z", "iopub.status.busy": "2021-10-10T16:41:54.465889Z", "iopub.status.idle": "2021-10-10T16:41:54.467539Z", "shell.execute_reply": "2021-10-10T16:41:54.467915Z"}, "papermill": {"duration": 0.056039, "end_time": "2021-10-10T16:41:54.468066", "exception": false, "start_time": "2021-10-10T16:41:54.412027", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_simclr(batch_size, max_epochs=500, **kwargs):\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, \"SimCLR\"),\n", " gpus=1 if str(device) == \"cuda:0\" else 0,\n", " max_epochs=max_epochs,\n", " callbacks=[\n", " ModelCheckpoint(save_weights_only=True, mode=\"max\", monitor=\"val_acc_top5\"),\n", " LearningRateMonitor(\"epoch\"),\n", " ],\n", " progress_bar_refresh_rate=1,\n", " )\n", " trainer.logger._default_hp_metric = None # Optional logging argument that we don't need\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"SimCLR.ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(f\"Found pretrained model at {pretrained_filename}, loading...\")\n", " # Automatically loads the model with the saved hyperparameters\n", " model = SimCLR.load_from_checkpoint(pretrained_filename)\n", " else:\n", " train_loader = data.DataLoader(\n", " unlabeled_data,\n", " batch_size=batch_size,\n", " shuffle=True,\n", " drop_last=True,\n", " pin_memory=True,\n", " num_workers=NUM_WORKERS,\n", " )\n", " val_loader = data.DataLoader(\n", " train_data_contrast,\n", " batch_size=batch_size,\n", " shuffle=False,\n", " drop_last=False,\n", " pin_memory=True,\n", " num_workers=NUM_WORKERS,\n", " )\n", " pl.seed_everything(42) # To be reproducable\n", " model = SimCLR(max_epochs=max_epochs, **kwargs)\n", " trainer.fit(model, train_loader, val_loader)\n", " # Load best checkpoint after training\n", " model = SimCLR.load_from_checkpoint(trainer.checkpoint_callback.best_model_path)\n", "\n", " return model"]}, {"cell_type": "markdown", "id": "30619702", "metadata": {"papermill": {"duration": 0.043291, "end_time": "2021-10-10T16:41:54.559443", "exception": false, "start_time": "2021-10-10T16:41:54.516152", "status": "completed"}, "tags": []}, "source": ["A common observation in contrastive learning is that the larger the batch size, the better the models perform.\n", "A larger batch size allows us to compare each image to more negative examples, leading to overall smoother loss gradients.\n", "However, in our case, we experienced that a batch size of 256 was sufficient to get good results."]}, {"cell_type": "code", "execution_count": 10, "id": "204e88a8", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:41:54.726388Z", "iopub.status.busy": "2021-10-10T16:41:54.725918Z", "iopub.status.idle": "2021-10-10T16:41:54.977674Z", "shell.execute_reply": "2021-10-10T16:41:54.977166Z"}, "papermill": {"duration": 0.357848, "end_time": "2021-10-10T16:41:54.977825", "exception": false, "start_time": "2021-10-10T16:41:54.619977", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/ContrastiveLearning/SimCLR.ckpt, loading...\n"]}], "source": ["simclr_model = train_simclr(\n", " batch_size=256, hidden_dim=128, lr=5e-4, temperature=0.07, weight_decay=1e-4, max_epochs=500\n", ")"]}, {"cell_type": "markdown", "id": "6e791b30", "metadata": {"papermill": {"duration": 0.041235, "end_time": "2021-10-10T16:41:55.168558", "exception": false, "start_time": "2021-10-10T16:41:55.127323", "status": "completed"}, "tags": []}, "source": ["To get an intuition of how training with contrastive learning behaves, we can take a look at the TensorBoard below:"]}, {"cell_type": "code", "execution_count": 11, "id": "4faca6fd", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:41:55.269774Z", "iopub.status.busy": "2021-10-10T16:41:55.269307Z", "iopub.status.idle": "2021-10-10T16:41:55.271319Z", "shell.execute_reply": "2021-10-10T16:41:55.270919Z"}, "papermill": {"duration": 0.045346, "end_time": "2021-10-10T16:41:55.271426", "exception": false, "start_time": "2021-10-10T16:41:55.226080", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# %tensorboard --logdir ../saved_models/tutorial17/tensorboards/SimCLR/"]}, {"cell_type": "markdown", "id": "6defaecc", "metadata": {"papermill": {"duration": 0.040274, "end_time": "2021-10-10T16:41:55.366743", "exception": false, "start_time": "2021-10-10T16:41:55.326469", "status": "completed"}, "tags": []}, "source": ["
![tensorboard simclr](data:image/png;base64,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Ro0a6fXXX9eQIUO0fPnyZE+x2pk1a5YmT56sAwcOJHsqqGLhcFh5eXnauXOnbNtO9nQAAAAAAAAAIOmOmZ4w1c3q1av1z3/+U02bNtW//vUvNWjQIPrc559/rhtvvFF33XWX3n//fVkWL3PErFmz9MUXX2j48OGqVatWsqeDKpSWlqa5c+fK4/HINInvAgAAAAAAAABXSg/TwoULJUmXXHJJTABGknr27Kn+/ftr69atWrJkSTKmByRFWlqaUlJSkj0NAAAAAAAAAKgWCMIcpsid/uWV1Ro9erTuu+8+NW7cuMxzM2bM0MUXX6ycnBxdeOGFeuGFF+KWb/r00081duxYdevWTWeccYbGjBmjBQsWxIy55ZZb4vYReeihh5SVlaX169fHLH/qqaeUlZWlRYsWVXh8O3fu1L333quePXuqS5cuGjVqlL766qu4Yw8cOKDJkydr8ODBysnJUZ8+fXTbbbfF7DvSZ+SLL76QJPXu3VtZWVnavXt3dMyXX36pW265RT169FDXrl01dOhQvfzyywqFQhXONWLNmjW69tpr1bVrV3Xr1k3jx4/X1q1b4461bVvTpk3TsGHD1KVLF5111lkaP358zJxt21aPHj1i+p5EXHjhherVq1eZ9+3KK69UTk6OCgoKJEn9+/fXgAEDtGXLFt15553q1q2bunfvruuvv165ubmVOq69e/fqySef1Pnnn6+cnBz169dPjz/+uPbs2VNmbGXei4hQKKRnnnlG/fv3V+fOnTV48GDNmDGjUnMqT+k+MZFeSLNmzdIbb7yhiy666KCf+9JeeOEFZWVl6brrrlMwGJTklj6bPn26hg0bptNPP13nnnuu7rjjjrjHCQAAAAAAAADJQhDmMJ1zzjkyDEOvvPKKPvnkkzLPd+rUScOHD1dmZmbM8mnTpunRRx9V69atdd5552nHjh166qmn9Prrr8eMmzp1qm688UZt3LhRgwcP1uDBg7VhwwbdfPPNevnll6PjzjrrLEkqk3ETCdbMmzcvZvmSJUuUlpamnJycco+toKBAY8eO1TvvvKOGDRvqwgsvlGmauu6667R3796YsX6/X6NGjdIzzzyjxo0b66qrrlL37t312WefaeTIkdqyZYskadiwYfrjH/+oli1bSpJuu+02/fGPf1R6erok6d///rfGjh2rlStXasCAAbriiivk8Xj0+OOPa+LEieXONeK7777T6NGjtWjRInXu3Fn9+/fXunXrNG7cuDJjHcfR7bffrr/85S+SpCuuuEI9evTQvHnzdMUVV2jp0qWS3EBb9+7d9cMPP8QEczZt2qTc3Fzt3LlTX3/9dXT5gQMHtHr1auXk5CgtLS26fNeuXRo5cqQ2bNigfv36KTMzU1988YWuvfZaBQKBCo9r586duvLKKzVlyhS1bNlSI0aMUIsWLfTyyy9rxIgRMUGsyr4XEffdd58mT56sQCCggQMH6uSTT9Zf/vIX/ec//zno632onn32WT322GNq06ZNhZ/70t5880099dRTOuWUUzRp0iR5vV5J0p/+9Cf9+c9/lmVZuuyyy5Sdna25c+fq8ssv1zfffFPl8wfK849//EOvvfZasqdx1DiOk7R9T506VZ999lnS9g8AAAAAAHA4aFZymFq1aqV77rlHjz76qG699VZ16NBBgwYNUr9+/dS0adNy1yssLNRbb72lFi1aSJLWrVunYcOG6Y033tAVV1whSfr+++/11FNPqXnz5nr11VdVt25dSdJNN92kK6+8Uk8++aTOOusstWzZUmeffbYkafHixRo8eLAkNyNk+/btSk1N1bx583TddddJkvLz87V69WqdddZZFZaMeumll7RhwwYNGjRIjz76aDTr5+2339Z9990XM3b+/PnasWOHfvvb3+rWW2+NLu/Tp49uv/12vfrqqxo/frx69OihHj16aP78+dq4caOGDh0aU8bt5ZdfVqNGjTR9+nTVr19fkpvtMHz4cL3yyiu65ZZbKuwh8/jjjys/P1/333+/Lr/8ckluJsv999+vmTNnxox944039PHHH+v888/XE088ET2+5cuXa9SoUbr77rs1e/ZsWZals846S3PmzNGSJUt00UUXRY/Z4/FIcoNc2dnZ0fXD4XA0MBZx4MABXXbZZbrzzjujy8aPH685c+bos88+U9++fcs9rscee0ybNm3SPffco5EjR0aXv/jii5o4caIee+wxPf7444f0XkjSV199pVmzZikzM1PTp09X7dq1JbnBrKuuuqrc+Ryuffv2HfRzX9rcuXP14IMPqlmzZnrmmWei7/+2bds0a9YsdejQQdOnT4++f//+97/1hz/8QS+88EKlAndAVRg3bpzat2+v4cOHJ3sqCXXgwAE9+OCDateuna655pqjvv8333xTY8aM0QMPPFDmdywAAAAAAEB1RibMERgxYoReffVV9erVS2vXro2WjBozZoyWLVtW7jqRC9GS1KZNGzVu3Fg//PBDdNmbb76pcDisG2+8MRqAkaT69evr5ptvVjgc1ptvvilJatq0qTIzM/Xf//43Om7+/Pny+Xz6zW9+o5UrV2rHjh2SpGXLlikcDqtnz54VHtfs2bNlGIZuvfXWmAbrQ4cO1fHHHx8z9oILLtCCBQuiF/0DgYD279+v1q1bS3IvtlfG9OnT9dFHH6l+/fpyHEcHDhxQfn6+2rZtK8dxtGHDhnLX3bVrlxYvXqxmzZrp0ksvjS43TVNXX311mfGvvfaaTNPUHXfcEXN82dnZGjx4sLZs2aLPP/9cUnGm0eLFi6Pj5s+hrt/jAAAgAElEQVSfr5ycHHXp0iUmCyryHpS+QJiSklImIycSPNu8eXO5x5WXl6cPP/xQmZmZuvLKK2OeGzVqlJo3b64PPvhA+/fvl3Ro78UHH3wgSbr++uujARhJatu2bXRuVakyn/uSlixZot///vfKyMjQs88+Gw3MSW7GjySlp6fHvH/9+/fXxIkTa/zFcCAZZs2apSeeeCJaEvBoeu211zRixIijvl8AAAAAAICqQBDmCGVlZWny5MmaN2+e7r33XmVnZ+vLL7/UqFGj9MYbb5QZn5qaWmZZWlqa8vPzoz+vWrVKktStW7cyYyPLImMkqWfPntqyZYt++uknSdInn3yibt26aeDAgZLcoIFUXLKsoruIw+GwNmzYoMaNG+ukk04q87xhGGWW7dy5U3/+85/Vt29f5eTkqFu3btGsnEO5YDdnzhyNGjVKXbp00Zlnnqnu3bvr3XfflaQKy3ZF+oB07tw55qJ8vPkGg0F9++23at68edyMpe7du0tStKTVCSecoLZt20Zfu3379mnp0qXq3bu3+vbtq40bN0Z7u3z55Zdq2LBhNOgR4fF4ZFmxSWeRcmXl9RSS3IymcDisrl27ljkuj8ej008/XeFwWGvWrIkur+x7EQnIdOnSpcx+473HR6oyn/uI7du364477lAgENCjjz6q5s3/P3v3HVdl/f9//MF0Q65EciAuNKhcJI5wDz6OnInbUitHVqiVmqO0zJXlQEvNkYIC4iZUlJJw5Sq3GLgINVRcKOv8/vB7zs8TqKAgjuf9duuW57re1/t6Xdc5iLfred7vdxmz/U5OTtSpU4c9e/bQtWtXFi9eTFRUFDY2NjRv3hx3d/dsr19EHr8LFy7Qr18/unTpct/RmyIiIiIiIiIiTzJNR5ZNihcvjre3N97e3vz+++8MHjyYCRMm8MYbb1CiRIks9XXp0iUsLCzMvv1vZNx27do107Z69eqxdOlSdu3aRd26dTl8+DBvvfUWlSpVolSpUmzdupWOHTuye/duypQpk+6h9t1u3bqFwWAwmyrsfhISEvD29ubixYu0b9+emjVrUqRIEa5evcpHH32U6WteuHAhU6ZMoVKlSvj4+FCqVCny58/PokWLTCHSvRiDjOLFi2eqXuCe15fR/a1bty4//fQTZ8+e5cCBA6SmptKgQQNsbGyYOHEi4eHhprVI2rZtm5nLzRTjei8vvvhipmrNynuRlXv2uB07dsz05yVLllCnTp10wdDMmTP5+eefWblyJZMmTQKgVKlSvP3226bp6ETuNn36dJydnU3TCt7t119/Zd++fXTq1MkUzl65cgV/f3/27NnDlStXsLe3p06dOnh7e5ut+ZQdoqKiCAgI4OjRoyQmJuLg4EDz5s3x8vJK99lPSUlh9erVhIWFkZCQQPny5enVqxfly5d/qHaZERoaSkhICHBnCsZbt27RrVs3098fSUlJBAQEEB4eztWrVylbtixvvfVWupB3/fr1nD59mnfffZclS5YQFhaGra0t9evXp2vXrqY1n4z+97//8ccff9C1a1fatWtHp06dsly7iIiIiIiIiOSMEydOcPjwYVq2bKkvTz6AQpiHFBgYSEpKSobrWdStW5f27dvj7+/Pzp07M3zodz92dnYYDAYSEhIoXLiw2b4LFy4A8MILL5i21apVC1tbW3bt2kVKSgoAnp6eADRq1Ah/f38uXbrEoUOHHviAOl++fFhYWJiFEPfj7+9PbGwso0aNMrsXxinQMiMpKYnZs2fj6OjI0qVLzR5wbtiw4YHHG9cKMU7LdT/GqbfuXtD+bhcvXgTM72/9+vX56aef2LVrFzt27KBcuXKmqbVcXFzYunUrlStXJiUl5YFTvWWFnZ3dfWs1fhaMU9Zl5b0w3rNr165lGPblJktLSyZPnszy5cuJiIjg559/pkePHmZtbG1tefvtt3n77beJjY1l+/btLFq0iC+++IKrV6/St2/fXKpenlTTpk3j+vXrxMXFpfuHwcCBAzl79izvvfceANu3b+d///sfly9fpnz58uTPn5+TJ08yf/58pk2bxt69e7PtHxezZs1iyJAhWFlZUbFiRZKTkwkODmbGjBn07duXH3/80dT24sWLtG7dmp07d1KoUCHKlClDYGAgEydOZPHixaaf+8y2y6yVK1cSGBgIwLp169i4cSONGzemePHixMbG0qJFC/766y/s7OxwdHQkKCiIyZMnM3z4cNOaVXBnLauNGzeyadMm1q1bh4eHB6dPn2b+/Pn4+voSEhJi9jvvjTfe4Ntvv6VevXps3rz5UW6ziIiIiIiIiGSjEydOEBERAUBISIiCmAfQdGQPacmSJYwfP/6ea3oYg4SHmT/f1dUVwGydFyPjtFhVqlQxbcubNy81atRg165dhIeH4+bmZhrp0bBhQ1PIkZmQwNLSkgoVKnDu3LlMhRrGtVqMi9MbJSYmPvBYo3/++YebN29SuXLldN8wN67/cT/G6b/uHkFxL3nz5qV8+fKcPn3aFGLczbj2y933t1q1auTPn5/t27ezbds2GjRoYNrXqFEj9u3bR2hoKFZWVtSpU+eBNWSWsYbdu3djMBjM9qWmprJnzx5sbGyoVKkSkLX3wnjM8ePHs63e7FKnTh2aN2/ON998Q+HChfn222/N3ttdu3Yxd+5c0zRwjo6OdOjQgSVLlmBlZcWqVatyqXJ5kvXq1YvLly+zfv16s+179+7l0KFDeHt7kzdvXgwGAz179uTWrVtEREQQFRXFn3/+SWxsLC1btuTQoUMsX748W2qKjo7mww8/pEKFCsTExHDw4EGOHTvGkSNHKF26NAsWLDB9zgHef/99du7cyciRI7l8+TIHDx7kyJEjODg40KdPH1Pgmtl2mTV37lzmz58P3Amzrly5gpubGwCdOnXir7/+4osvvuDy5cscOXKEU6dO8frrrzNp0iQWLFhg1te1a9eIiIhg7969/Prrr0RHRzNq1Ch27tzJsGHDzNpOnTr1vtNnioiIiIiIiMjjd3cAA3dmdQoJCbnvchLPO4UwD6l9+/YAjB492jTFlVFsbCzr1q3DwsIiwzU3HqRDhw5YWFgwc+ZMsxEpCQkJzJkzB2tra9q1a2d2TL169Th//ny6kKB69erY29sTEBCAra1tptbL8PLyIikpiaVLl5pt379/f7pRGcZ1Y3799VfTtps3bzJhwgTgTlhwt7tHYBiVLFkSKysr/vzzT65cuWLavm3bNtMC8mlpafest3Dhwnh4eLB//3727Nljtm/r1q3p2nfu3Jnk5GSmTp1qFm4cOnSI9evX4+joaFobBsDGxgZ3d3dCQ0O5du2a2f1t3LgxaWlpBAcH8+qrr5otcv+oihYtSpMmTYiKijJ9C93Iz8+PM2fO0KxZMwoVKgRk7b0wrhe0YMECs3uQkJDAvn37su0aHoZx/ZvixYvz1VdfkZSUhI+PjylMOn/+PDNmzOCHH34wO+769eukpaWZPmMid+vTpw8WFhbp/l5bvHixaT/c+YdDnTp1+Oyzz8xCa3t7e9NImewKL8+fP0+7du345ptvKFmypGl7hQoV6NixI2lpaaY1r86dO8fKlSvx9PRk/PjxWFlZAeDs7Mz48eN57bXXOHLkSKbbZYfff/+dyMhI2rdvz+eff2762X3ppZdYtmwZNjY2fPXVV+mOGzVqlOnLBgBffvkl1apVY/HixVy9ejVbahMRERERERGR7PffAMb4HE5BzP1pOrKH1KNHD44dO8aaNWto1qwZ7u7uFC9enAsXLhAZGUlSUhIDBgzAyckpy327uLgwZMgQpk+fTps2bWjatCkGg4GNGzcSHx/PsGHD0s3rX79+fSZPnkxaWppZSGBlZYWnpydr1qyhVq1amVrLoHv37qxfv56ZM2cSGxvLq6++SkxMDP7+/unadujQgZ9//pkZM2awfft2ihYtys6dO00P9i9dumTW3tXVlU2bNuHj40OVKlX4+OOPKVq0KF26dGHp0qW0bdsWd3d34uLi2LdvH/b29iQkJKTr578++eQTunbtysCBA+nevTslS5bkjz/+yHA6sy5duhAREcH69euJioqidu3aXLp0idDQUGxtbfn666/TrU1Qr149wsPDsbe3NxtpUrlyZRwdHYmNjc3WqciMRo4cyeHDhxk3bhxbtmyhfPnyREVFERERQdmyZfnkk09MbbPyXrz22mu8+eabrFq1iv79+9OsWTOuXbvGihUrTFOyPQnq169Pr169WLRoERMnTmTcuHE0b96c5cuXs2bNGqKjo6lWrRo3btwgLCwMg8FAr169crtseQI5OzvzxhtvsG7dOhISErC3tyclJQU/Pz+qVq1qCqiLFi3KokWLTMelpKSYRqkEBQWZtmWH2rVrs2LFCtPrmzdvEhUVxYEDB9ixY4fZubZt24bBYKBly5bp+unZsyc9e/YE7kxLmJl22WHbtm3A//9Swt2cnZ2pVasWkZGRxMbG4ujoaNqX0douzZs3Z9++fezYsYNmzZplW40iIiIiIiIikj3+G8DUq1ePsmXLsmHDBi5fvmwKYjQ1WXoaCfOQLC0t+eqrr/D19eX111/nzz//JCgoiP379+Pu7s7s2bMZMGDAQ/fft29fvvvuO0qXLk1wcDBr167F2dkZX1/fDB8yOzs74+joiIODA5UrVzbb17hxY4BMT+uSL18+FixYQJs2bdi4cSPjx49n+/btjB07Nl3fjo6OLFmyhLp163Ls2DF+//13PDw8CAgIoHbt2hQoUMBsSrEuXbrQvHlzzp49y/bt200jXIYOHcqQIUMoWLCgaSHpsWPHMnr0aKpWrfrA6XMqVKjA4sWLcXV1ZeHChXzzzTdcunTJbD0FIysrK77//nt8fHxITk7Gz8+PyMhImjRpgr+/f4ajl+rXrw/cWaPA+M1yo0aNGgGZv79ZUbx4cfz9/enatSvHjx9n6dKlREdH07t3b/z8/ChSpIipbVbfi7FjxzJo0CBiYmKYMGECK1asoE2bNlleLyKnDRkyhJdffpmgoCA2btyIra0t8+bNY+DAgVy/fh1/f3+2bt1K1apVWbBggWmUj8h/vf3229y+fds0siw0NJQLFy6YRsEY/fvvv4waNQoXFxfy5ctHxYoVadeuHZs2bQJINz3gozh8+DB9+vShVKlSFCxYkFdffZU+ffpw4sQJs3PFxcUBUKJEifv2l9l22cE4peNLL72U4X7jlxDu/vvb1tYWBweHdG2N9WY0TaSIiIiIiIiI5K6MApiKFStia2uLl5eXaY1XjYjJmIXh/57w3Lx5M7drua+xY8ea/f9p6VtERNIzGAwYDAZSU1NJTU0lJSWFlJQUkpOTKVu2bI6c8+bNm5QsWZIaNWqwZcsWunTpQlBQEGfOnDEFA9euXaNatWqcPHmSBg0a0LhxY9zc3KhWrRonTpygSZMmfPLJJ0ycOBG4s86Ui4sL+/fvz3I9e/fupV69eiQnJ9OxY0fc3d1xc3OjevXqfPvtt4wfP56QkBBatGjBjBkz+OCDD/jhhx/o16/fPfvMbLus8vf3x9vbG19fX9O0bJ9++inffPMNQUFBGY6Geeutt1ixYgXR0dE4OTnRsWNH1q5dy+3bt9O1/frrrxkxYgTBwcG8+eab6fZv3ryZpk2bMmbMGP2uFhEREREREXmM7hXA3C0pKck0IgagSJEiT9yImFOnTmFjY4O1tTXW1tZYWVlhZWWFhYVFuiU1HoWdnV26bRoJIyIiz4X8+fPz1ltv8euvv3Ly5EnWrFlDy5YtzUZmLF++nJMnT9KrVy+2bt3KqFGjaNu2LWXKlCE2NjZb65k2bRqJiYn8+OOP+Pn58dFHH9GkSROKFCmS7lwVKlQAyHA9lwMHDtCoUSP8/Pwy3S47VKpUCbiznlZGDhw4QJ48eczub1JSEn///Xe6tn/++ScAVapUyZbaREREREREROTRZSaAATQi5gGeuhDm7umUnsT+RETkydWnTx/S0tJ47733SExMTDcVmfEbG9WqVTPbnpqayg8//ACQbf+AuNe54uLiCA4ONjvXG2+8gb29Pf7+/ly5csWs/fz589m6dSsvvPBCpttllXEaxruv3cvLC1tbW2bOnGm6FqPg4GCOHTuGl5cXefPmNds3c+ZMs9dRUVGsXr2aV155Jd2UlyIiIiIiIiKSe4oWLWpaO/teAYzRf4OYIkWKPFEjYXKTdW4XkFlOTk7ExMSYpoDJif5FROTZ5uHhQZUqVdi8eTPFihWjVatWZvsbN26MpaUl33zzDQ4ODri4uBAdHc23337LwYMHAdIFDg+radOmbNiwgffee48vv/ySokWLsnfvXsaPH29aC8Z4rgIFCjBhwgQGDRqEu7s7I0eOxNHRkQ0bNjBz5kyaNm1K8+bNsbS0zFS7rCpevDgA3333HWfOnOH999/H2dmZMWPGMHLkSGrVqsVnn31G6dKlCQ8PZ8qUKdjb2zN58uR0fU2fPp2rV6/SsWNHTp06xejRo0lOTub7779/hLspIiIiIiIiItmtSJEieHl5ER8ff98AxsgYxBw6dCjdl06fZ09NCNOiRQt++eUXYmJisr1vJycnWrRoke39iojIk6dPnz4MHz6cbt26mb7NYVS9enV++OEHhg4dSpcuXQCwsbHhrbfeYvny5dSqVYvQ0FBSU1NNo0Me1gcffMDJkyfx9fWladOmALzwwgsMGTKEzp078/LLL7NhwwZ69eoFwMCBA8mbNy8jR46kd+/eAFhYWNCpUyfmzJmDpaVlltplRd26dWnZsiUhISFMmTIFNzc3nJ2dGTFiBEWKFGHs2LH07dvXdK6mTZsyY8YMypcvn66vxYsX4+Pjw/z58wGoXLkyS5cuxdPTM8t1iYiIiIiIiEjOKlKkCEWKFMl0e1tbWwUw/2Fh+L+v2968eTO3axERkeeEwWDAYDCQmppKamoqKSkppKSkkJycTNmyZXO7PJKSkjh58iSJiYlUqlSJggUL5ti5rl27xsmTJ7G2tsbFxQVr6/t/PyItLY1jx45x7do1ypcvT9GiRR+pXVb8+++/pKSkUKJECSwsLNKd6+bNm5QtW5ZixYqlO7Zjx44EBQXxzz//ULhwYQ4fPkyhQoUoX768WV8iIiIiIiIiItnt1KlT2NjYYG1tjbW1NVZWVlhZWWFhYUFqamq2ncfOzi7dtqdmJIyIiMjjYmtr+9gWiS9UqBCvvfZapttbWlpmqrbMtsuKjMKVhzlXnjx59K0YEREREREREXkuKIQRERF5RCkpKWzevDnT7R0cHLIUvGSnCxcusHfv3ky3r1q1KmXKlMnBikREREREREREnl0KYURERB7R9evXadmyZabbd+jQgcDAwBys6N4iIyNp165dptvPmDGDQYMG5WBFIiIiIiIiIiLPLq0JIyIij92TviaMiIiIiIiIiIg8O3JzTRjLbOtdRERERERERERERERETBTCiIiIiIiIiIiIiIiI5ACFMCIiIiIiIiIiIiIiIjlAIYyIiIiIiIiIiIiIiEgOUAgjIiIiIiIiIiIiIiKSAxTCiIiIiIiIiIiIiIiI5ACFMCIiIiIiIiIiIiIiIjlAIYyIiIiIiIiIiIiIiEgOUAgjIiIiIiIiIiIiIiKSA6xzu4DMiouLIyQkhFOnTmV732XLlqVly5Y4ODhke98iIiIiIiIiIiIiIvJ8empGwvzyyy85EsAAnDp1ipCQkBzpW0REREREREREREREnk9PTQgTExMDgMFgyNZ+jf3lVMAjIiIiIiIiIiIiIiLPp6cmhDGysLB4ovsTEZHMs7CwMPtPREREREREREQkJ+TWc6inLoTJLm3atMHR0fGR+qhVqxa1a9fOporkXubNm4erqytr1641bQsKCqJWrVps3LgxFytLb8SIEdSvX5+zZ88+9nOvWrUKV1dXFi5c+NjP/bR4mj5LzysFMSIiIiIiIiIikt1y85nTcxnCdO7cmXr16vHuu+8+chAj2WfPnj3MmjWLEydOPLBtQkICiYmJXLt27TFUlnmXLl3i+vXr3L59O7dLeWyy8r49iZ7Uz9LzSKNhREREREREREQkJ+TmcyfrXDlrLurcuTM1a9YEIF++fNSsWZM1a9bkclUCsG/fPnx9fSlXrhwVK1a8b9u3336bjh07Ymdn95iqy5yZM2dy+/ZtChQokNulPDZZed+eRE/qZ+l5oinJREREREREREQkJ+Xm86fnaiTM3QEMwB9//PHcBDAGg4G0tLTcLiNbZedD89TU1Gzpx9ra+rkIYFJSUnK7hGylACb3GH/pGX8BWlo+V7+WRERERERERETkMbC0tDQLYLQmTBY4Oztnql1GAcyKFStypKbDhw/zzjvvULNmTWrXro2Pjw+nT5/G1dWVwYMHm9qNGDECV1dXYmJimDp1Ko0bN6ZmzZp06tSJX3/9NcO+t23bRp8+fahduzbu7u707t07w7bNmjWjQYMGbNq0iVatWvHqq6+yc+dO0/6IiAh69+6Nu7s7tWvXZvDgwURFRWXq+tLS0li2bBnt27enRo0a1KtXDx8fH06ePGnWLjo6GldXV4YOHZquj9GjR+Pq6sqBAwdYvnw5rq6uTJ8+HYDhw4fj6upKaGjoPWvIaG0PgGvXrjF58mSaNm1K9erVadWqFT/99JNZAHXu3DlcXV0ZNmwYs2fPxtPTk1dffdUULFy9epUpU6bg5eVFjRo1+N///sekSZO4cuXKA+9N//79cXV1JS4uzuxco0eP5o8//qBXr17UrFmTRo0aMWbMGK5fv/7APgH++ecfxowZQ+PGjalWrRpeXl7MmTOHpKSkDNsHBgbStm1b0z1YsGBBuhDuxo0bzJo1i9atW1O9enUaNmzIkCFD0r2PxrVm5s+fzyeffIK7uzt9+vR5qPcNsnZ/9+/fz4ABA6hXrx7Vq1fnrbfeIiQkJF27c+fOme5P9erVadmyJV999VWm3rOMPkvNmjWjZcuWxMbGMnToUGrXro2Hhwf9+/cnJiYmXR/x8fGMGDGCOnXqUKNGDXr06MHu3bvp0aOH1o26j/8GMAphREREREREREQkJ9wdwjzuIOapno6sZs2adO7c+YGByuMMYI4dO0avXr1ITEykdu3aODo6cuDAAbp163bPYwYNGsSNGzfw8PDg6tWr/PrrrwwaNIiVK1eaTe+0aNEiJk+eTLFixWjdujVpaWls2rSJgQMH8sknn9CjRw+zfi9fvszQoUOpVq0ar732GkWLFgXAz8+PCRMm4ODgQJs2bbh69SobN25k165d+Pv7U65cuXvWajAY+OijjwgLC6NSpUp06dKFixcvsmnTJn777Tfmzp1L9erVs3TPatSowYgRI4iMjCQ8PJw333yTqlWrUqVKlSz1k5CQQPfu3YmOjsbT05NSpUqxY8cOpk6dyqlTpxg7dqxZ+7CwMCwsLKhbty4FChTA0tKSxMREunXrZuqjYcOGHDp0iMWLF7N161YCAgIoWLBgluoC2L17N2vXrqVGjRo0b96cPXv2EBQUxPXr15k6dep9jz19+jQ9evQgPj6eN954gzJlyrBnzx5mzpzJvn37mDNnjtlfGMuWLSM+Pp4GDRpQuXJlfvvtN6ZNm0b+/Pnp0qULALdu3aJnz54cO3aMOnXq0LBhQ/79919CQ0PZuXMnK1euTLde0qxZsyhUqBANGzakXLlyD/W+ZeX+hoWF4ePjQ548efD09MTOzo4tW7YwbNgwzp07R9++fQE4efIk3bt3JykpiSZNmlCiRAmioqJYtmwZf/31F4sXL8bGxibL79mlS5fo1q0bhQsXpmnTpkRFRREZGUnfvn3ZsGEDtra2wJ0wq1evXsTExFCxYkVeeeUVTp8+zbvvvmtqI+lZWFhgMBjMwheDwYDBYMjt0kRERERERERE5BljaWlp+u9xBzFPbQjj7OxM586dAUwBS0bByuMMYAAmTpxIYmIin3/+OW+99RZwJ7gYN24cgYGBGR5TrFgxVqxYQf78+QHw9/dn/PjxrFy5kk8++QSAqKgopk2bRpkyZfDz88Pe3h6AAQMG4O3tzZQpU6hXr55ZgJKamsq4cePo0KGDaVtMTAyTJk2iYsWKLFmyxPTAu127dvTr149p06YxY8aMe15fQEAAYWFhNG/enMmTJ5u+tb5//3569uzJp59+yoYNG7C2zvxHq0KFClSoUIGbN28SHh5OnTp18PLyyvTxRpMnTyY6Opovv/ySdu3aAZCcnEz//v0JDAykS5cuuLi4mNpbWFjg7+9vFnSFhYURHR1N9+7d+fTTT03bx44dS2BgIGvXrsXb2zvLtZ09e5bvv/+ehg0bAndCkDZt2hAaGsrYsWMpVKjQPY8dP3488fHxTJ48mZYtWwJ3RiN98sknhISEEBYWRpMmTUztb9++zcqVKylbtiwAJ06coH379gQEBJhCmPDwcP7991/effdds9FZDRs25KOPPsLPzw8fHx+zOkqXLs3SpUvNQqisvm+Zvb83btxgzJgx5M2bF39/f5ycnAD48MMP6dKlC7NmzaJdu3YULVqUgIAAbG1tmTRpEvXr1zf1+dVXX7Fs2TJ27Nhhtj2zbty4QadOncxGcvn4+BAaGkpERASNGjUCYPHixcTExODl5cXEiRNNPxPr1q3j008/fajQ7nnz3yDmzJkzz9yUdyIiIiIiIiIikjusra3Jly9fugDmsZ3/sZ4tG/3999/88ccfpoAloyDmcQcw8fHx7N69m9KlS9OpUyfTdgsLC/r06XPPEOaDDz4wBTCA6YHxmTNnTNuCgoJITU3l/fffNwUwAEWLFmXgwIGMHDmSoKAgswfGefPmNQtgAFavXk1ycjKDBw82ezjs4eGBq6srERERJCUl3fMb/MuXL8fS0pKPP/7YbNqg1157jdatW7N69Wp+//13PD0973uvstuNGzfYsGEDlStXNgUwADY2NvTp04fdu3ezZcsWsxCmVq1a6UCuZiAAACAASURBVBaSv3XrFkC6dV0GDBiAh4dHpqe/+6/XX3/dFMDAnfemZs2arFmzhjNnzlC1atUMj/v333+JjIzklVdeMQUwcCe57d+/PwcPHuTkyZNmIUzXrl1NAQxAxYoVKVmypNnnqUWLFrRo0cL0OikpiaSkJCpUqADcCW7+q23bto8cKGT2/v72229cuXKFd955xxTAABQsWJA+ffowb948jh8/joeHB59++qlZoHPr1i2Sk5OpXLmy6VoeJoSxtbXlww8/NNtWv359QkNDOX36tGlbSEgIFhYWfPDBB2Y/E61atWLatGncvHkzy+d+ntz9rQPj/UtJSbnviDwREREREREREZHMio6OxsrKKsMpyR6HpzaEgf8fuGQUxDzuAAbuBENwJ5DIyroG/w08jIHM3euFHDx4ECDD9SWM24xtjDL6IP35558A7Ny5M137xMREkpOTiYuLo0yZMumOTU5O5tixYzg5OfHSSy+l2+/h4cHq1as5dOjQYw9hjh49alof5fvvvzfbl5CQAGD24BzI8D1q3Lgxs2fP5ocffuDkyZM0atSI2rVr8+KLL9K8efOHri+jUMsYRNy4ceOexxnDEDc3t3T7KlasmOH6KHny5Em3LV++fMTGxppti4+Px9fXly1btnDhwgWzfcnJyen6yI61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6pkMYYyLwUdFRaXbZ9xmHJVRpkwZzp07x40bN9K1PXHiBIUKFeKFF17IsVpv3rxJXFwcJUuWvGebf//9l+joaOzt7enQoQOTJk0iLCyM2rVr4+vry6lTp56Ia7mXUqVKARAdHf3Yzy05IzY2ltDQUOzs7Fi3bh0LFy7kww8/zLV6xo0bh6urK2vXrs21GkRERERERERERET+65kMYVq0aIGFhQVz5841TYkFdx4cr1q1CmdnZypXrgyAl5cXycnJ/Pjjj2Z9bNq0iaioKP73v/+ZRs1kxNbWFrgzmiUzjGvRGPn7+5OUlES9evXueczy5ctp3bq12boxefLkoUqVKgAkJCRky7X895qMo2ceVZUqVShatCirVq0iPj7etP3q1avp7oc8HYyBWu3atR/LCBgRERERERERERGRp9EzOT9UuXLleO+99/D19aVnz540b96ca9euERgYSFpaGuPGjTO1bd++Pb/88gvz5s3j9OnT1KhRg5iYGAICAihVqhSDBw++77mM685MnDgRNzc3+vXrd9/2kydP5sSJE7i4uHDo0CGCg4MpUaIEPXr0uOcxnTp1YsWKFQwbNozOnTtTqlQp/v77b5YvX0758uWpWrVqtlzLf69p4cKFXLhwAS8vLxwdHTN1bEasra0ZOnQon332GW+99RZeXl7Y2Niwdu1aLly48ND9Su65fv06AHnz5s3lSkRERERERERERESeXM/kSBiAgQMH8vXXX5Oamsr333+Pv78/r776KkuXLqVatWqmdlZWVvj6+vL+++9z5MgRpkyZwpYtW+jUqRN+fn7Y29vf9zxvvvkmzZs3Z8eOHQQGBj6wrs8//5yLFy8ybdo0QkJC8PT05KeffqJQoUL3PObFF1/Ez8+Phg0bsnr1aiZOnEh4eDje3t4sXLjQtNbKo16LUfny5Rk8eDDnz59n4cKFppE2j6J169Z8/fXX5M2bl8WLF7N27Vo6depkGgGUmRE6kjVXrlxh3LhxvPHGG1SvXp2OHTuyfv36e7Y3GAysXLmSzp07U7NmTerVq8ewYcNM090BnDt3DldXV3x8fABYs2YNrq6uuLq6mk2Dd/LkST755BM8PT2pUaMGrVq1YsqUKabwxig0NBRXV1dGjBiRrp6wsLB77jMaOnQorq6uBAQEAPDZZ5/h6urKvHnzMneTRERERERERERERHKQheH/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tLW1aaolEREREREREREREZFDwCEZwvz4xz/myiuv7Hhus9l49dVXmTx5Mo8++mjH9szMTB588EFWrlzJqaeeCsCvf/1r0tPTeemllzoWGj/33HM5//zzeeyxx3j44YeZMmVKRxXKiBEjmDJlSpcx/OpXv2Lq1KkAzJw5k+nTp/Puu+92hDBvvPEGH374IVdffTWzZ8/u6Ddu3DhuvfVWHnroIe655x4AHn74YZqamnjmmWcYP358xzFvvvlm3nvvvY6+2dnZfPzxx9/49RMRERERERERERERkb53SE1HFjRq1Khun+fl5YVtHzFiBADbtm0DYMOGDRQVFXHGGWfQ1NREZWUllZWV2Gw2hg8fzooVK3p1fovF0hHABA0bNoympiZqa2sBePPNN7FarVx77bVh7c466yyGDh3KO++8QyAQoKWlhY8//piRI0d2BDBgFjg//vjjezUeERERERERERERERE5+BySlTC7s1rNZdhstm63BwIBALZs2QLAs88+y7PPPtvtsVpaWjr6fZ0xtLS0APDVV1+RkZHRMQ1aZ8OGDWPz5s3U1NQQCATwer0MGjRon88pIiIiIiIiIiIiIiIHr29FCNNbwYDkqquuYsKECd22sVj2T3FQIBDA4XDscRwWi4W2tjYA7Hb7fjmviIiIiIiIiIiIiIgcHA6rECYrKwswU32ddNJJfXqunJwcvvjiCxobG4mJiQnbl5+fT1xcHImJiQQCAZxOJ6WlpX06HhERERERERERERERObAOyTVhvq5Ro0aRmprKyy+/3CX0eOmll8LWhAlWpgSrVvbVOeecg9/v56mnngrbvmjRIgoKCjj33HOJiooiOjqak08+mRUrVlBcXBzWtrCw8GudW0REREREREREREREIu+wqoSx2+385je/4ZZbbmHGjBlccsklZGZm8tlnn/H2229z1VVXMW7cOAByc3MBmDNnDuXl5ZxzzjkcccQRvT7XxRdfzMKFC3n66afZunUrxx9/PMXFxcybN49+/foxa9asjra33HILH3/8MVdffTXXX3896enpLF68mFdffTXsmJWVlUyfPp0pU6Zwzz337IdXREREDoTW1lYWLVrCpk35JCUmcvY5Z5CWlrrXfqtXf8lHHy3DYrEwYcJYRo8+pkubqqpq3nlnMeU7K+jfP5tzz8vD6XT2xWWIiIiIiIiIiMg+OqwqYQCmTJnC3//+d4455hjmzp3Ln/70J7Zs2cI999zDz372s452gwcPZtasWezcuZM5c+ZQV1e3T+eJjo7m8ccf54YbbmDDhg088MADLF68mBkzZjB37lwSEhI62g4YMIA5c+bQv39//vCHP3D77bfT0NDA7Nmzw44ZCATw+/34/f5v9iKIiMgB4/f7uemm2/jj/Q+xY3sZr746n+9Mv4r8zXuudvzbE8/xw5k/ZuWKVRRv2cqPb7qN++57MKzNhvWbmH7xlbz+2gJqamp58sk5XHrp1dTX7+rLSxIRERERERERkV6KamtfGb6pqSnSYxGR/eDXv/51pIcgB7l777030kPYo507dzJw4MBID2O/eenF//Dww0/w4kvPkpPTD6/Xx4033ArAM88+0m2fDes3cfnl13HlDy7jlluuB2Dz5gKuuPw6fvu7O8nLO5VAIMCM71xFdnYWD/75PqzWaKqra5h20RVcetk0brzx6n0ea1FREaWlpZx88slf/4L3gxdffJH6+nqmT59OSkpKxMZRV1dHUVERo0aNwmKJ3OdWvF4v69evZ9iwYbjd7oiNA2DNmjVkZ2dH9OsCZn09l8tFv379IjqObdu24fF4GDJkSETHUVVVRWlpKccee2xEx9HU1MSmTZs46qijcDgcERtHIBBg9erVDBo0KOyDT5Gwfv16kpOTyczMjOg4iouLGTBgQETHICIiIiISKVu2bCEjI6PH/V93SZLuxMfHd9l2WE1HJnI4ONjfYBc53Cxc+D9OOWUSOTnmzWKHw87lV1zCz269i7KycjIz07v0+bB9CrLrrpvZsW3o0FwmThrPW28tIi/vVD7/fA3FxVt58M+/x2qNBiA5OYn7/3g3LpemIxMRERERERERORgohBEREelDmzblc9JJE8K2Dckd1LGvuxCmubmZ+Pg4nM7wT5InxMexcuUqANasWUdqWgo5Of1Y+skK8guKyM7OYvLkkzpCGRERERERERERiSyFMCIiIn3E4/Hi9fpISkoM256YZKbHqa3pfr2xAUfmUFtbR3HxVgYMyAGgtbWVtWvXU1/fAEDZjnISE+K5+ebbWfflBlJTU9iy5StGjBjKk089hNOpahgRERERERERkUhTCCMiItJH/H4/ANHR4ZUpVqv557enOUfPzDuVJ554jlt/eie33HI9iUkJvDj3P9TU1Ha0afY0U1Cw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ERE5CCiEOcxcd911DBo0iLfffptly5ZhtVo56qijuPzyyxk8eHCkhyciIiIHQmunEKbzui690HTWTNzb1pv1WgBefqhro+xcMxVZtDU0rVhw7Re7M/R490XtLdHha8Qkppuqk86hUXQ0JRfNxuFwkJGevk9jFxERERERETnQFMIcZqKiosjLyyMvLy/SQxEREZFIqSqD9cugvBRKNu25bbDapLIUT1wKzWPOwF23s+e2laWQkALJmVBX2bVNTLy59UawXecQxubovu3uHG5IaH9stfWuj4iIiIiIiMh+phBGRERE5NuqoQaios00X8HworHerAOz9K3eHSO9HzhcUFlK3eDjsUZbwdLDYvQnXgBvzzFryuxPdgckp0Nra+/7ON3hVTUiIiIiIiIiEaAQRkRERORQ4fdCIGDWO+muIqS0wAQVsQmQmAbV5Wa7zQZZg0wAU7UDykv2fq68K0xFS3wy1FfDumU0ZA4mEcyxdmd3wfizYHshHH3iN7nKrizREJtkHve0noyIiIiIiIjIQUghjIiISB9rbW1l0aIlbNqUT1JiImefcwZpaal77ZefX8RHHy2lqamZkSOHM2XKSV+rjXyLVJaC3x8KVTrze0OVIs0N4O405Zffb9Z+qW2fHmxv68Bk58LICeax3QnxKZA7ilZnrNnm6KbC5PxrwBULky829weKql1ERERERETkIGaJ9ABERES+zfx+PzfddBt/vP8h/j97dx4fVXn3//+dmSSzJCRkIQSyCCEssoMWV8TWKlS93VCsVO/6q1irFWut3rYudWmtW1t3ae+CtWJdbrVVVCiiVqt+cUERXNjXJCQkmYRMMntm5vfHkAlDJguYySHM6/l45DHnXOe6rvM5cyZEz2eu66reVaN//vM1nT/7Um3auKXLdq+9tlw/mHu5Vv6/T1S9q0Y33/QbXffzmxUOhw+oDg4zgUDs675amtq3g0HJt9+IkT37rM/SFCcJU1QeeR17jHT8me3ltgypoES65GZ5c4ZEyrLzpGNPj/yMmBh5HXdcez+5hQd2XQAAAAAAHKYYCQMAQAL946VXtXbNl3ru+SdUWlosn8+vq678he6550EteuKRuG1CoZDuv+9hnX3293TzLddLklZ/tlbz5l2jd9/9QCeffGKP6iCJeN1Sc2NsWdVmyeOS/J7IVGGDimOP7W/eb6SnfitN/U5s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gUCg+wYJEgqFor8zRv7uSorGYfS98Xg8hp4fAAAASGb9KgljJJvNZuj/TO7ro48+0ooVK/S9732vT5IwXq9X7777riRp+fLl3ygJcyi9jwCQrEpLS+OWb926VUOGDFF5ubHrp6xatUper1elpaXKy8s7sMbpQenp30klo6JFtrIjD+qampqaFA6HVV5ebuiDXJ/PJ5/Pp7KyMtntdsPikCJJh6KiogO/L70sHA7LZrOpuLjY0DgsFou8Xq/hvzMOh0Pp6emGx+F2uxUIBDRixAhZLBbD4mhL0A0fPlzZ2dmGxSFFkqi5ubkqLCw0NI4DTWgDAAAA6D3813g3gsGgzGazbrrpJqNDMcwHH3wgt9utk08+We+884527dqloUOHHlRfyfw+AgASzOWMrANTVxX5aTNyqnExAQAAAACApJY0SZgtW7bo2Wef1ddffy2fz6dhw4bp3HPP1Yknnhits379et1www0677zztGvXLn322Wc69dRT9ZOf/ETz58+X2+3WokWLJEmXX365ampq4p7r+OOP169+9avo/gcffKCXX35Z27dvl8lk0vjx4zV37lyNGDEiWufFF1/U3/72N91777166623tHLlSrW2tmrUqFG64oorVFJSotdee01//vOfo23++7//W3a7Xc8//3y07O2339brr7+uHTt2yGKxqLy8XJdccsk3+mbk8uXLNWLECF1++eV6//33tWLFCv3whz+MqfPb3/5WH330ke6++26NHz9eUmQu/SuuuEKpqalasGCBMjIyOryPklRRUaHFixdr3bp18nq9Kikp0dlnn/2NRtwAAJKM1y05qiVnQ8djo0nCAAAAAAAAYxg7SXIf+eKLL3TDDTdo8+bNOuOMMzR37lz5/X7de++9+uc//9mh/j//+U81NTXpggsu0Le+9a24ff7whz/U/PnzY37aFrucMGFCtN6LL76oe+65R6FQSHPnztWZZ56p9evX63/+53/05Zdfduj3rrvuUm1trWbPnq0ZM2boiy++0F133aVgMKhvfetbuvXWWzV58mRJ0nXXXacbb7wx2vb555/XAw88oOzsbP34xz/Weeedp61bt+qmm27qNGHUnYqKCq1bt06nnnqqsrKydOyxx+rNN99UMBiMqXfVVVfJbrdrwYIF0WNPPvmkmpubddVVVykjIyNu/7t27dJ1112nLVu26Oyzz9Yll1yiUCik3//+93rllVcOKmYAQBKr2Bi7n18k2YxdNB0AAAAAACSvfjkSxuPxqL6+Pu4xv98fsx8KhfTwww8rPT1dDzzwgHJyciRJZ555pn75y1/qySef1AknnKCCgoJom0mTJumOO+7ocv73fUfQSNInn3yi7du365hjjtGZZ54pKZJgWLx4scaPH6/f/OY30bmYTz31VM2fP1+PPPKIFixYEHOeE044QVdddVV0Pz09XUuWLNG2bdtUXl6uwYMH68MPP5QkTZ48OXo94XBY7777rqZNm6Zbb7012n748OG67bbb9MEHH2j27NmdXk9nli9frrS0NJ188smSpNNOO03vv/++Pv74Yx133HHRerm5ubr88sv10EMPacmSJRozZozeeOMNzZgxQ8cee2yn/b/33nvyer267bbboiNoTjvtNM2bN0/Lli3T2WeffcAxAwCSmNMhKSwpJbJfOtrIaAAAAAAAQJLrl0mYFStWaMWKFT2qu379etXU1Ojcc8+NJiykyEKqc+bM0e9+9zt98MEHOvfcc6PHxo8ff0AL8NbX1+uBBx5QQUGBrr322mj5e++9p1AopNmzZ8cshllYWKjTTjstmlzZd1qy/afgKisrkyTt3r27yynFUlJS9Pjjj3cobxuds3v37h5fT5tAIKC3335bxx9/fHQky+TJk1VQUKDly5fHJGEk6bvf/a7effddPfPMM8rPz4+OyOlK2z1ZtWqVRo8erbS0NFmtVj311FMHHC8AAGpySEqRrHZpyHBpMlNbAgAAAAAA4/TLJMy0adM0c+bMuMeee+45bdq0Kbq/a9cuSe3JiH0NHz5cklRdXX3QsbRNneVyufTrX/9amZntU55UVUUWBS4tLe3Qri2eXbt2xSRh9teWDGptbe02Fr/frzfeeENvv/22ampq1NzcHD0WDod7dD37WrlypZqbmzV16tSYJM60adP0+uuvq76+Xvn5+TFt5s+fr5/+9KeqrKzUjTfeqKysrC7P8e1vf1tr1qzRSy+9pNdff13jx4/XUUcdpRkzZmjAgAEHHDMAIEn53JHXqs2R16Jyae7/MBUZAAAAAAAwVL9MwhQWFmratGlxjy1btixmPxQKSZLMZnOHum1rlxzIqJf9Pf300/rqq6/0ox/9SGPGjIk51pb42HcUTJu2pEpKSspBn3t/v/vd77R69WqdddZZuvjii5WVlSWXy6VbbrnloPp74403JEkPPPBA3OMrVqzQRRddFFPmdruj17Znz55uz5GWlqYbbrhBc+fO1ccff6w1a9boiSee0OLFi/WLX/yi0/sMAEAHPk/7dm5h5AcAAAAAAMBA/TIJcyCGDh0qSdqxY0eHY9u3b5ckDRky5KD6XrNmjV588UUdffTROuecczo9986dO5WbmxtzbOfOnZKkoqKigzr3/ioqKvTpp5/qzDPP1GWXXRYtb2xsPKj+ampqtGbNGn3729/uMO2YJL3wwgtasWKFLrzwwmgSKxQK6cEHH5TdbtcRRxyhv/3tbzr66KNVWNj5Q7CamhpJkffh3HPP1bnnniuHw6Frr71Wjz/+OEkYAEDP1VW2b+cONi4OAAAAAACAvQ5+CEg/ceSRR2rQoEFatmyZGhoaouV+v18vvfSSUlNTdcIJJxxwv3v27NHvf/975ebm6rrrros7omX69OlKSUnR888/HzOdWG1trd58802VlJREp0Q7EGlpaZIia7a0aRvx4/V6Y+quXLlS0oFPR9Y2Cmb27Nk67rjjOvyceuqpqqur0+rVq6NtXnrpJW3ZskU/+tGPdM011ygUCunhhx/u8twPPfSQrr766pip0/Ly8lRQUCCn03lAMQMAklxtRft2+WTj4gAAAAAAANjrsB8JYzabNX/+fN1555267rrrdNppp8lqteo///mPtmzZonnz5nVY16Qn/vjHP2rPnj2aOXOmPv7445hjqampmjFjhoqLi/X9739fzz77rG688UadeOKJcrlcWr58ucLhsObPn39Q13TEEUdIkv7yl79o1KhRuuCCC6IJnTfffFOBQEBDhw7V+vXr9eWXX0qSWlpaetx/KBTSW2+9pZKSkui59nfCCSfoT3/6k9544w0dddRRqqio0LPPPqvx48frlFNOkSTNmTNHTz/9tJYtW6bTTz89bj9z5szR7bffruuvv16nnnqq7Ha71qxZo40bN2rWrFkH8rYAAJJZKBQ7HZktw7hYAAAAAAAA9jrskzCSNGXKFN1zzz165pln9M9//lOSNHz4cN10001xp9rqiQ0bNkiSli9fruXLl8ccs9vtmjFjhiRp7ty5GjJkiF555RUtXrxYVqtV48aN09y5cw9qFIwknXLKKfriiy+0atUqbdu2TRdccIFMJpNuv/12/fWvf9Xq1av14Ycf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222WsKdHjx768MMPrfaxevVq5eXl6e6777aMHunXr58++OADrVmzRldccYUcHR01bNgwJScn64cfflDnzp21bNkymc1mjR8//qxhUkREhPLz8yVJPj4+Vud15cqVys3N1ZQpUyxT6gwYMEDLli3T1q1btWvXLqtRTdV8fX3l6+srg8EgFxcXqzbPpzqcc3FxkaOjo9Vj63puq5353JxLWFiYpkyZUu8LodVMJpPGjh2rbt26SZIGDRqk9957T4mJiRo/frwkKT09XcePH9fQoUMt0/rExsZq3rx52rt3rwoKCuTl5XVB+weas6lTp+qxxx7TLbfcUmNEWO/evTV37lw98sgjuvnmmyVJzs7Ouummm7Ro0SL17dtXP/74o4xG41lH59XVfffdpwMHDuj999/XtddeK0lq2bKl7r//fv3pT39SdHS0JZSRpHvvvVdubm566qmnLCMQHRwcNHHiRH3wwQeWz9261quPQYMGafTo0VqxYoVeffVVxcTEKCwsTP/85z/l6+urmTNnWkZTOjg46Nprr9Xbb7+t8PDwGm19+umnevjhh/Xxxx9LkiIjI/X5559bfX5ef/31Wrx4sR5++GH94x//sJRHR0frvffeq3WkJQAAAAAAQFPUrEIYqSpQ2bx5s3Jzc+Xt7a2UlBR17dpVDg4O6ty5s9atW6fU1FRFRkaedT2YNm3anDWAOZ9jx44pKytLgwcPVnl5uWVqFoPBoMDAQKuA6EyBgYF66KGHapRXT6126tSp84Yw1XcJV0tMTJTRaFRMTIwKCwst5e3bt5ejo6MOHTqkAQMGKCUlRZLUv39/qwtwbdu2tUrnzGazEhIS1K5dO7Vo0cKqzbCwMG3cuFFZWVmWUUHjxo3TJ598ov/85z86evSohg4dajme+qgeyREcHGwJYKpdc8012rZtm3bv3l1rCGMrdT231c58bs6lQ4cOFxzASFUXOasDmGqBgYHKzMxUUVGRPD09LSN/Dh8+bCmTpFtuuUVms/mCLsQCl4JHH330nFNy3XHHHbr11lt14MABlZSUqHPnzpb305EjR6zqlpaWXnA/HB0d9fbbb2vOnDk6cOCAnJycFBUVZRm9WNtdGHfccYemTp2q5ORkFRQUKDw8vNa7Uepar66cnZ21fPlynThxQpWVlVaf8/fcc4/uuusuJScnq7i4WKGhofL39z9rW8OH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ERG59vnz2pN6woiIyBVBAYyIiIiIiIiIiPQGf153Uk8YERERERERERERERGRXqAQRkREREREREREREREpBcohBEREREREREREREREekFCmFERERERERERERERER6gUIYERERERERERERERGRXqAQRkREREREREREREREpBcohBEREREREREREREREekFZn8XcCmMJ4oJWLYYU0lBj+/bmTSa1jsW4hqQ3OP7FhERERERERERERGRa99V3RMmsJcCGABTSQEByxb3yr5FREREREREREREROTad3X3hPEGMG7A0IN79uyvJwIe8561GEvyMZ4owXiiBADXgCRcA5Jwpl+Hc+R1l3wMERERERERERERERG58lzVIcwZPRnA9Mz+TPu3Ebh8MYbT1e3WtQUy5j3rcMfEYr99Ic50hTEiIiIiIiIiIiIiIteSaySE6SH3fB92rIKKI5e0m8Blf8GcvRQAd3R/WrPuxpWc4Z1fxniiGGNxPgHZn2I4XU3QW/+G4/q7sd++8JJPQURERERERERERERErgwKYdos+BlMutXz9T//3O0g5uwAxn77d3FcP7ddG9eAZFwDknFcPxfzln8QuPw1zFs+Bbcb+x2PXtJpiIiIiIiIiIiIiIjIlcHo7wKuCG0BDEBIOEye3a3dmPZv8wYw1h+82GEAcy7H9XOx/uBFAMzZSzHt39atY4uIiIiIiIiIiIiIyJVFIczZAQzAztXwySvd2lXgsr8Anh4wbUOPdYVrQDL227/r2cfyxd06toiIiIiIiIiIiIiIXFmuzRBm+NiutesogHn7+W4d0rxnLYa6GtzR/bvUA+Zcjuvn4o7uj+F0tXrDiIiIiIiIiIiIiIhcA669EGbyrfDkf8A3nz5/ux4MYACMxfkAtGbd3e19tG2rEEZERERERERERERE5Op3bYUww8fCN3/m+Xny7M6DmB4OYACMJ4oBcCVndHsfbdsaK0suqRYREREREREREREREfG/ayuEOZznCVTadBTE9EIAA2CsLAW4qLlgztW2rfGEQhgRERERERERERERkavdtRXCgCdQ6SyI6aUARkRERERERERERERE5FxmfxfQK9qClbbAZfJsSM6AfgPPtOnhAMYVPxRjZSnGE8Xd7g3jHdIsfmiP1SUiIiIiIiIiIiIiIv5x7fWEaXNuj5heDGDgrKHEivO7vY+2bbsb4tTU1DBjxgzeeOONbtcgIiIiIiIiIiIiIiI949oNYcATtJQU+C4rKeiVIchcyRkABGxd2u19BGR/CoAz/bpube90OmltbcXhcHS7BhERERERERERERER6RnXdggD8N8/hpPHPT+fPO553Asc42fhju6P4XQ15i3/uOjtzVv+gaGuBnd0/26HMPHx8WzdupWFCxd2a3sREREREREREREREek5134IA/DcQ7Btued7L7Lf8SgAgctf887v0hXGE8UELn/NZx8iInL1uv3225k+fbq/yxARERERERERET8z+7uAy+a9P/X6IZzp1+HIugtz9lKCX/4x9jsW4si6+7zbmLf8wxvAOLLu6nYvGBERkd7yxhtvYDQaeeih3r2ZAcDlcpGamorT6Wy3Ljo6mj179vR6DSIiIiIiIiIiPeWrE8JcJm09WczZSwlctpiA7E9pzbobV9JoXAOSAU/PF2NJAQHZn2I4XQ14Ahj1ghERkSvRG2+8gclkuiwhTHV1NU6nkwkTJpCZmemzLiQkpNePLyIiIiIiIiLSkxTC9AL7HY/iTMogcNlfMJyuJnDZ4k7buqP7e9qrB4yIiAiVlZUAzJ8/n/vuu8/P1YiIiIiIiIiIXJprZE4Y9xW3P2f6dbQ8/Rq2B36JI/MmXPFDvetc8UNxZN6E7YFf0vL0awpgRET8bN68eWRmZtLa2uqz/J133iE5OZlVq1YBYLVaefHFF7nppptIS0vj+uuv5+c//zk1NTU9UseePXv4zne+w9ixY8nIyGDu3LksW7asXbujR4/y1FNPMXnyZEaPHn3J7Trz/e9/n+TkZMrLyykpKSE5OZnHHnvMu97hcLBo0SJuvvlm0tLSmDJlCr/61a84efKkz34efvhhpk+fzqeffsrtt99OWloaEydO5JlnnqG5udmn7YkTJwAYMmRIl+sUEREREREREblSXdU9YVxJozGWFACGHt6zwbv/S+VMv04hi4jIFe5rX/sav/71r8nOzmbmzJne5StWrCAiIoIbb7wRt9vNo48+yrZt23jggQcYNWoUhw4d4s0336SgoIBPPvmEgICAbtewdetWHn74YZKTk3nqqacwm8289957/PCHP8RoNHLbbbcBnmBl7ty5OJ1OHnjgAeLi4li1ahU//OEPKSkp4Qc/+MFFtTufJ554gnnz5vHLX/4Sk8nE7373O/r37w+A2+3mscceY/369dx22208+OCDHDlyhHfffZdNmzaxZMkS+vTp491XRUUFf/zjH3nyySfp06cPq1at4u9//zulpaX89a9/9bZrC2H+9re/8ZOf/ITTp08zZMgQHn30UebPn9/t51dERERERERExB+u6hDGfsdCApct/jKI6VmupNHY71jY4/sVEZErz5133snvfvc7li1b5g1h6uvr2bZtG/PmzSMwMJDCwkJKS0v50Y9+5BNgmM1mFi1axP79+xk7dmy3a/jkk09ISEjggw8+ICIiAoC77rqLKVOm8P7773tDmN///vfU19fz2WefMXLkSAAeeughFixYwMsvv8wjjzxCeHh4l9udT0ZGBuCZi8VkMjFr1izvuo8//pj169fzve99j5/97Gfe5VOnTuUHP/gBL7zwAr///e999vfOO++QkJAAwC233ILb7eaTTz4hOzubrKws4MxwZAcOHGD27Nk0NTXx+eef87Of/QyLxXJZ5qUREREREREREekpV3UI4xqQjHXhv/m7DBERucrFxMQwc+ZMVq9ezXPPPUdAQABr1qzB4XAwd+5cANLS0ti0aVO7bdPS0gBPz5NLCWGef/75dssiIyMZOHAgR48eBcBut7Nu3TqysrK8wQqAwWDgZz/7GVu2bMFqtRIYGNildhcKYc5nyZIlmM1mnnjiCZ/lbcONLV++nOeeew6j0TPyqdFo9AYwbR566CE++eQTNm3a5A1hHnvsMaZMmcKMGTO8PYvKysq48847+dOf/sT9999PYGBgt+sWEREREREREbmcruoQRkREpKfcc889rF27ls2bN3PjjTeyYsUK4uPjmTx5srdNTU0Nr7/+OmvXrqWyspKmpibvOrf70ucTW7NmDX/96185dOgQNTU1uFwuAJKSkgCorq7Gbrd3OF9KZmYmmZmZABw7dqxL7S5FSUkJ8fHx3l47Zxs5ciSFhYWcOnWKfv36dbqPwYMHA2d6vwD069fPp8cNeOaH+frXv84bb7zB/v37GTdu3CXXLyIiIiIiIiJyOSiEERERAWbNmkVERATLli1j0qRJbN68mUceecTbk6Ouro67776b1tZWvve97zFq1CgiIyPZtGlTh71YLtaiRYt4/vnnufXWW3n22WeJj48nICCAxx9/vF3btpoupKvtusPlchEUFNThutbW1i4d32azAV0LsNoCpVOnTl1MmSIiIiIiIiIifqUQRkREBAgKCuK2225jxYoVTJ06Fbvdzte+9jXv+s8++4yqqipeeeUV5syZ411eXFzcI8d/7bXXSEtLY9GiRT7Lzx56KzY2loCAAEpLS9ttX11dTVlZGaNGjepyu9DQ0G7XO3ToUHbv3k1zczNhYWE+64qKioiMjCQmJua8+2h77gYOHAiAw+Fg4cKFXH/99Tz66KM+bWtqagAuuE8RERERERERkStJ790iKyIicpW55557aGxs5Pnnn2fEiBHe+V4A79BgLS0t3mVOp5O1a9cClz4cmdPpxGaz4XQ6vcsKCgo4fvy493FgYCAzZ84kOzubgwcP+mz/q1/9im9961s4HI4ut+uqwMBAb++WNnfddRetra288sorPstXrlxJUVERd999NwaDwbvc5XJRWFjo8/i1114DYObMmQCYzWYaGxtZvHgxDQ0N3rZWq5V//OMfREVFMWrUqC7XLSIiIiIiIiLib+oJIyIi8qXJkyczcOBAjh8/ziOPPOKz7qabbuKPf/wjzzzzDHl5eYSHh7NmzRpvb5O6urpLOvbdd9/NX//6V775zW+SlZXF0aNHWbZsWbt9/+IXv2D79u184xvf4MEHHyQuLo41a9awefNmnn76aSIjIy+qXVeMGDGC5cuX82//9m+MHDmSe+65h2984xssW7aMV199ldLSUiZPnkxxcTHvvPMOiYmJPPXUU+32s2DBAh588EH69OnDmjVryM7O5qabbmLKlCneNv/6r//KN77xDe8xjEYjH330ERUVFTz33HM+PYNERERERERERK50pmefffZZoN0driIiIv7Q3NzstyGnDAYDtbW17Nq1i3//93/3CSoiIyPJysri0KFDrF+/nn379jFt2jR++ctfUlBQwIABA5g6dSoAb731Fna7ne985ztYLBbq6uqwWCwdfrW0tBAaGkpWVhYAO3fuZOPGjbhcLp599lmio6Opr6/nlltuISwsjJiYGObMmUNFRQXLly/niy++ICwsjJ///Od861vf8tZ7oXatra2cOnWq07osFgtBQUEYjUbS09PJzc1l3bp1BAYGMnv2bIxGI3feeSdGo5FNmzaxYsUKTpw4wdy5c/nP//xPn9dwyZIlHDt2jJdffpn333+fJUuWUF9fz/z583nuuecICAjwto2Pj2fmzJkcOHCAlStXsn79euLi4njmmWe49957e/stICIiIiIiIiLXmLq6OsLDwztd3zb6SWVl5SUdJz4+vsP5cw3uL8dPsVgsl3SAy8Fut9Pc3IzT6bzkYV9ERK5FBoMBk8lEWFjYVdtjoKqqiqSkJH+X0WNeffVVXnjhhU7XR0REkJeXdxkr8sjJyWHevHnnbfPZZ5+Rnp5+ycd6+OGH2bJlC4cPH77kfYmIiIiIiIiIXIySkhLi4uI6Xd82ZHtubu4lHWfcuHEdjjxy1QxHZrPZfMaHFxGR9txuNw6Hg/r6eqKioq7aIOZacuedd543yDCb/fOnePjw4fzv//7vedsMHjz4MlUjIiIiIiIiInJtumpCmLMnQhYRkQuzWCwKYa4AiYmJJCYm+ruMdiIiIpg5c6a/yxARERERERERuaYZ/V1AV7V1CRIRka7R56aIiIiIiIiIiIh/XTU9YTQHjIjIxdHnplwp3nzzTX+XICIiIiIiIiLiF1dNTxgREREREREREREREZGriUIYERERERERERERERGRXqAQRkREREREREREREREpBcohBEREREREREREREREekFCmFERERERERERERERER6gUIYERERERERERERERGRXqAQRkREREREREREREREpBcohBEREREREREREREREekFCmFERERERERERERERER6gUIYERERERERERERERGRXmD2dwFy5dq4cSP5+fkXbPfEE09gNHryvNOnT7Nr1y4qKiqw2WxER0czcuRIMjIyMBgM7batrKxk9+7dVFZW4nA4iIiIICkpifHjxxMUFNSufVFREXv37uXUqVMYDAaio6NJT09n5MiR3hoANm/eTF5eHt/5zncICQnpsO6SkhKWL1/e6XlNmTKFiRMnXvD8ly1bRllZGQ8//DBhYWHt1l9MLbNnz2b48OEXPOa5+25jMBgIDw+nX79+TJw4kdjYWABsNhuLFy9m7NixTJs2rd1+li5dSnV1Nd/97nd99nvjjTeSnp7erv3q1aspLi7me9/7nneZ0+mkoKCAwsJC6uvrMZlMDBgwgAkTJhAXF9flcxIRERERERERERG5ViiEkU4NGzaMqKgo7+NDhw5RVVVFVlaWT+DRFq6cOHGCzz77DIDhw4cTHBzMiRMn2LRpExUVFcyZM8cniCkpKWHlypWEhIQwcuRIgoKCOHnyJLm5uZSWljJv3jwCAwO97bOzs8nJ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itVqJSUlxa911NbWUlFRwZgxY/xah8Vi4eDBg6Snp3f55o7e4HK5yMvLIzk5maioKL/VAZ6e0H369CE+Pt6vdZSWljJ06FC/1iAiIiIi4i8lJSXExcV1ut7hcACQm5t7SccZN24ckZGR7ZZrThgREREREREREREREZFeoBBGRERERERERERERESkFyiEERERERERERERERER6QUKYURERERERERERERERHqBQhgREREREREREREREZFeoBBGRERERERERERERESkFyiEERERERERERERERER6QVmfxcgIiIil5fFYqG1tbXdcofDQUtLC42NjX6o6gyXywVAc3MzgYGBfqujqakJi8VCY2MjRqP/7lux2+1YLBaamppwOp1+qwM87x1/vy5tdbhcLr+/V5ubm7HZbFdEHW3vVX9qaWnxvlftdrvf6nC5XN46/Pm7C573alBQkN9fm+bmZr8eX0RERETkq0whjIiIyFfM8ePHqa+v73DdiRMnOHz48GWuyFfbxdujR49SW1vrtzqam5s5fvw4wcHBfr2Q29raSkVFBUajkeDgYL/VAXDs2DFsNptfX5e2OoKCgrBYLH6to7q6Grvd7g0O/aWhoYGamhqCgoL8WofVaqWiooKAgAACAgL8VofL5aKiogK3201YWJjf6gDP51h9fT0NDQ1+raOyspJRo0b5tQYRERERka8qhTAiIiJfMcnJybjd7nbLy8rKSE5OJjMz0w9VnXHw4EHsdjtpaWn07dvXb3XU19cTFhbG2LFj/RrC2Gw2AgICSE1NJTQ01G91AJhMJhISEvz6ugCEh4cTEhLCoEGD/FrH0aNHsVqtpKSk+LWO2tpaKioqGDNmjF/rsFgsBAcHk56e7tdAyOVyYTQaSU5OJioqym91AAQFBdGnTx/i4+P9Wkdpaalfjy8iIiIi8lWmEEZEROQrxt/D84iIiIiIiIiIfFVcNVdhDAaDv0sQEbmq6HNTRERERERERETEv66aEMZsVqcdEZGLoc9NERERERERERER/7pqQpiQkBB/lyAiclXR56aIiIiIiIiIiIh/XTUhTFBQEFFRUZjNZg2xIyLSCYPBgNlsJioqyq+TIouIiIiIiIiIiAhcVWPVBAYGEhgY6O8yRERERERERERERERELuiq6QkjIiIiIiIiIiIiIiJyNVEIIyIiIiIiIiIiIiIi0guuquHIRERERERELitbi+e70QgBmm9NREREREQujkIYERERERGRzlSVe74Hh0Json9rERERERGRq45CGBERkV7mdDr5/PP1HDx4iJjoaG67/Wb69+93we3y8grYvHkbRqOR666bRGZmRrs2hw4Vs3nzViyWFkaNSuOGG6b1ximIiHx1HM6D4WPbL2+1Xf5aztZQ6/luCoCwSM/Ppyrh+BGoOAIJw2DgMOgT778aRURERESkHYUwIiIivai1tZUf/vD/cLDwEFOmTGD9F5t57bW/s3jxf5EyYlin2/2/Rf/Ln//8JhkZ6cTFxfL3v73PHXfeyi9/+ZS3zWefreK3v3meceMyiI3tx9tvfcCUKRP5jz/9XwwGw+U4PRGRa0tLE6x8E37wp/brnE7Pd0crmAMub10AdSc9300mTwiz8k1Y9TffNiFhMOdhmHHv5a9PREREREQ6pBBGRESkF3380VL25hXw7nuvM3jwIGw2O99/4p/5wx9e5LXXX+pwmwP7D/LnP7/JQw/fz49+9D0AiooO860HH2fixExuvfVGXC4XLzz/38ydexu/+pefApCzZy8LF/4TGzZsUY8YEZGuapvzxdEK25bBkb3Q3HCmt8nZWprAaoGY2Avv19GK2VKPKdAI1kjPcGYdOVUJTgeYzB33YrFaYOtnMGgEBIV4wqDXnoGC7A7qa4ZPXoHaE3DPkxeuUUREREREep3R3wWIiIhcy1auXMuMGVkMHjwIgKCgQB781jfIzc2nsrK6w202fTkE2eOPf9u7bMSI4UzNmsLy5Z8DcKr2NE1NzWRmjvG2GZeZgdlsori4rBfPSETkCuZyXvw2VeWer9oTcGCnZ9nJCs93R6tv2xMlUHPMd1lnw5Q5Wum7dx2mU8fBZvGtz9EK5YWen5vqPeFJU/2Zba0Wz/Bjp6s9wU/1Mc8waQBH8joOYHCf+XHjJ2fai4iIiIiIXymEuQKVlZXx7LPPkp+f7+9SLqi7tT7//PN8+OGHvVTV1auqqopnn32WnTt3+ruUq9arr77K66+/7u8yzus3v/kNS5cu9XcZcpkcPHiIlBTfYcdShid713WkpaWFyMgIgoODfJZHRUZQ9OU2ffrGkJiYwPLln2Oz2QH49B8rcDicjBs3uqdPQ0TkwlqaMLZau7+9o9UTPnSmodbz1dzQ7ri0NHl+rj7mG5y4nJ5tOgpnTlX6trW1eHrBAFSWwqFcyNt4Zn3NMXj3j5C/+cwyqwWqj545PnhCmZpjUFdDYF01QYdzwOXyLK8qO/P9tWdgxZuebSoOwYYPz5y/pcEz/FjjabA2wdEiWP03+PTPsGNV58/R2c6uU0RERERE/OaaH45s7969LF26lJ/+9KcEBQVdeAO5arz00kuMHj2aG2+8EYAvvviCQYMGkZKSctH7ys3NxW63M3ny5B6pze128+qrr5KYmMhdd93VI/s8deoUOTk55Ofn8+Mf/9hn3dKlS9m9e3en2w4cOJDHHnus3fJzfz9KSkooLS1l2rRpBAT4Yaxz6bJz3/9yZbJabdhsdmJion2WR8dEAVB3ur6jzRg6ZDB1dfWUlpYzdOhgAJxOJ/n5+2lo8FzoMxqNvPw/L/DjH/2CObPnERkVyYnjlTz7m58zfnwHE0qLiPQmRyvGuo5793VZQy2EdjAEWJtz50RxtELxXijZB+ExMOo6sFsxVJVjdLZisDRCU41n+K6AIAgJP7OvHas8j88+3tk9R44dghOlkPMFNJzyrSP6rKHIbBbP/vdvh7RJnrrsNtiyFEZNJbChGlPTSbDe4wlWnE6orYTNn3r2u+EDzxBobcdITINRU2DfNkjO8By//yA4+WXvmyPn9m5xA53MAVZxpPPnUkRERERELptrPoTJz88nLS1NAcw1pqKigtraWjIyMrzLNmzYwOTJk7sdwlgslh4LYQwGAyEhIQQGBl7yvgoLC9m+fTslJSUAmEymdm3Gjh1LQkJCu+VOp5MVK1YwePDgDvd97u9HaWmp93lUCHPl6uj9L1em1lbPHdbn/t6azZ4/vw6Ho8Ptbrn1RhYt+l/++al/4Uc/+h7RMVG8+87HnD5d523jcDh58cVXqamp5YEH5hMZFcmK5Z/z/xa9wdixo73Dn3WktLSUhoaGdstbW1spLi4mPDy8g60uH6vVcyd9YWEhoaGdzKFwGTQ3N3P8+HFcLhdGo/86D7e2tlJaWorVaiU4ONhvdQAcOXKE06dPExl5ngvll8GxY8cICgqipqbGr3VUV1djt9tpamq6cONe1NDQQE1NDU5nN4bi6iHBdVW0WpooqbPQ6nQRZm8GwGU0YQ/vc8Htja1Wguo9r6crIAh7RB9MdisGtwtHUCgGt5ug05Xe9i3VjZjsLQze9DbBjScxOeycOFGJJSoOg91KdVUNrqp4Yqx1WCP7E71uCa0hkTTGJeEKDCF1+Z+pTRrPySYX5qbTBLQ0kLB7BW2/YZb9uzE6bASfG8AAzpJ9FG5aiz0smuD6Ggbs/ozoikLqEtNpjexPY+xQhmxZimvHaoJaGgE4sjOb1pBIYvdvILjxJEH1NZgAbFbP15daP3+byqpq4vNW0xoaSWhtBZY+CYTaOuth1EkAAzQ1NXEoJweAyspKhg4desHXQUREREREet41HcI0Nzdz5MgR7r//fn+XIj0sPz+fgQMH0q9fP3+X0o7T6cRkMvHtb3/7wo27YOPGjZhMJubOncuhQ4c4ePBguzaDBw/uMGjJzc3F5XIxadKkduuutd+Ptuf9StDbtVzJ73/xFRLiuZxns9t9llutnvkDQsM6DhhCQ0N48b9+z2+e/QM/+ckvCQgI4J577+See+9k+TLPnDCrV63li3WbeOPNV8jISAdg/vy7efDBx3n+3/+Ll//nhU7rio6O7jDcOHnyJDExMcTHdzAx9GW0b98+7HY7ffv2JSoqym91NDY2YrVaiY+P92sIY7fbaWhoIC4ujpCQEL/VAVBXV0dsbCzR0dEXbtyL2gIpf79X3W43Nput9+pwuTA6PJ8fbpMZt6njf7oHBwfjcrn89nwYm05jIgJrcCD1bhNxMdGEtLStNNHaLxYu8DtkPlVJ6L48WsbdBIArNASjxQ4YcUTHeNqYzgwd5ugThcEeRGR1CcYv52Tp21hJWGIy0UvfpDVlBlGhgSRs+Ni7jTswGHdRKK2xgwlorifKWo+hX1/6bHjTcw7NZ3onBtmafB6fzeSwMwArxvpygg7tJqTCM7dL9NH9njqKtmF02DE5znz2x9eWYLRbCSk9/zwtAZZ6EnZ/hrHVRsCXAU7oqYrzbnPGOYHM8LHe90Rnob+IiIiIiPS+azqEKSgoIDg4mOHDh3uX2Ww2NmzYwL59+7BYLPTr149p06YxatQobxun00l2djY5OTnU19cTHR1NZmYmWVlZGI1GVq1axfbt23n66ad9LoZ8/PHHFBUV8fTTT2MymXC73WzdupWdO3fS0NBA3759mTFjBqNHnxmr3+12s3HjRnbu3InVamXw4ME+tXRFYWEh7777Lvfffz+5ubkUFxdjMplIT0/ntttuo7CwkI0bN3Lq1CliYmKYMWNGuzvoDx48yMaNG6mqqiI4OJj09HRuuukmnztuu1prV867Izt37mTHjh3eO2zHjRvH9ddf3+5itsvlIj8/n2nTpgHw/vvvs3+/5z+9O3bsYMeOHSxYsICBAwfyxz/+kZtuuokZM2b4HGfZsmU88cQTlJaWsmLFCu+6Z599lqysLG699VbAM0fL2rVrKS8vx+12M3ToUG655Rafi9+vvvoqISEhxMfHk5OTQ0pKCvPnz+f5558nOTmZ+fPne9vu27ePLVu2UF1dTUhICCkpKdx8883nvdP7m9/8JhEREYDn7vWLsW3bNlJSUujbt2+7dWf/flRVVfHqq696173wwgsMGDCAxx9/HPBc7Fq7di379+/HZrORmJjI7Nmzvf+xb3sPfutb32Lv3r0cPHgQk8lEWloac+bM8elVU1JSwhdffEFVVRWBgYEMHz6cm266yXuO4JkPY+3atRQWFmK1WomLi+OGG27w6eW0cuVKtm/fzt13380XX3yBy+Xipz/9qXf9zp07yc7OpqGhgdjYWObMmcOQIUN8noOuvO8v9HlwoVoOHDjAunXrOHXqFH379mXWrFkdvlbdff/Llc1sNtOvf1+qq3zv1q+u9jyOj4/rdNvU1OG8/c5impqaCA4Oxmw28y+/+r8MHZoIwIEDRfTt28cbwLQdb+bM63nn7fPPudXZBfSmpiZiYmIYMGBAl86vt7T1FOrfv3+Hn1+XS2hoKM3NzQwYMMCvIYzNZuPUqVPEx8f7tWcQQE1NDbGxsX59XcDzXg0JCfH7e9XhcGC1Wnuvjrb5RgAiIiCq4/A9MDCQ1tZW/z0fJ1ogqB9Wq5U6p4HYvjEEbdkIo6ZCUAiEB52p3dEKbpdneLA2JQVQXwEHtxM26xueZcGhEPLlvx+O7IEBSdC3n2fulOpjEBsHx4545lf5UlhtBWGledBcz8iCVbgsGd6ABvC0ba7HfOoEACHWRkLcVqgqbXdKHQYwETGeOVqAPh//Z6dPh88x22orzT9rWLMvhxBLGA4Vh880CgwGu7XD7Tt2nqHIgsMIv+Nhwr8cgs1m6+o+RURERESkp13TIUx+fj6jR4/2uXDyySefcPjwYa677joiIyMpKCjggw8+8F4wBvj000/Zu3cvkyZNIi4ujtLSUtasWYPb7Wb69OmMGjWKrVu3cujQIcaMGQN4LoweOnSI1NRU70XT5cuXs2vXLiZOnEi/fv0oLCzkww8/xGw2e4+1efNmvvjiCwYPHkxKSgq1tbUsW7asW+f78ccfM3r0aGbOnMmRI0fYvXs3J0+epLKykokTJxIUFERubi4ff/wxsbGxxMV5Lv7l5eXxySefkJiYyKxZs6irq2Pnzp1UVFTwne98x3s+Xa21K+d9rm3btrFy5UrGjh3L5MmTOXr0KOvWrcNisTBnzhyftsXFxVgsFm+QNG3aNMaNG8fbb79NSkoKkyZNYuDAgV16zlJTU4mJiWHNmjXYbDbuuOMOYmI8d1vW1NTw+uuvExwcTFZWFk6nk927d/Paa6/x6KOP0qfPmaE1SktLaWxsJCsry/u8nis3N5clS5aQkpLCbbfdxqlTp9i2bRt1dXU89NBDndZ4djhxMUpLS6msrOTmm2/ucP3Zvx/R0dEsWLCA/Px88vPzuffee713nzudTt544w3q6+u57rrrMJvN7Nq1izfeeIMnnnjC5y71jz76iEGDBjF9+nTKy8vZvXs3YWFh3HST567W48eP8/e//52EhARmzZpFfX09O3bsoKqqioULF2I0GnE4HLzxxhvU1tYyefJkwsLCKCgo4O233+brX/866elnLji73W5Wr17NuHHjfC4qHz9+nLq6OjIzM3E4HOTk5PD3v/+dJ5980tuuq+/7C30enK+WsrIy3n//faKiopg2bRp2u50lS5bgdrt9XotLef/LlS9zXAa7d+f6LNu5I4egoEBGjhzR4TaFhYf4jz++zPMv/MY7n4zVamPr1p08+C3PBcro6CgaGhppamomPDzMu23liSq/DxUlIlcBRyuYzxl6tNUGdV+GxqGRnvlNztZQC198ADcv8F3XeqaHitFhx9Bqhw0fnZlPZfRUuOdJiO4PNUfBYALc4HJCzXHY9bnnZ5vVM6F99TEICIQhIz3zpez5Ar7+Y/jstTOhxfoOwuaKw971JocdU1Hn8+UBnnlfSvZ14cn60tQ7YfXfOl533e2QOAI+eLHj9WcPa3b/TyEl0zN3zNI/w9EisLXA8LGeuWUupF8CjJkG697rvM2Cn/nOgSMiIiIiIn5zzYYwp06d4tixY8yePdu7zGq1UlhYyPXXX++9MD1+/HhefPFFcnJySEtLw2q1UlZWxrRp07x3rE+YMIFTp06Rl5fH9OnTGTRoEFFRURQWFnpDmPLyclpaWrw9Q06cOMHOnTuZM2cO1113HQCTJk1i0aJFbNy4kbS0NGw2G9nZ2QwePJhHHnnEGxb179+fzz///KLPecaMGd6747Oysnj55ZcpKyvjwQcf9PYGSktL45VXXqGoqIi4uDgcDgcrV65k0KBBPPLII94Lz4MGDeKjjz5i165dTJkypcu1duW8O5KXl0dCQgL33HOPdxu73U5ubi6zZ8/GYDhzl9/evXtJTk72zllwduASExPDiBGei5pdGZ89Ojqa6OhosrOzAbzbAqxevRqAhQsXeoOQsWPH8sorr/D5559z3333eduGhoaycOHC8w4Ts2/fPpKTk1mwYIHP+WzZsoWmpqYen4Nh69at9O3bl2HDhrVbd+7vR1BQECNGjKCiwjPcxbBhwwgL81zUbQtJHnvsMe8dtqNHj+all15i+/bt3l5DACNHjuSuu+4C4Prrr+eVV16hsLDQG8Lk5+fjcrl44IEHvPPQREREsHLlSqqqqhgwYADbt2+nqqqKBQsWeF+PyZMns3jxYpYvX+4TdALcf//97Xq4mM1mHn30Ue/rlpGRwaJFi9i6dSu33XZbl9/3Xfk8ONu5tWzYsIGgoCAWLlzofX1HjRrF4sWLfba7lPe/XPnuu38eC7/7Q15b/DcWPDCfffsKWbz4r8ybP5fgYM/vwaGiI7zz7kfcd9+9pKYOZ9CgARQVHeGll/7M00//EJvNzrO//gNGo5Gvf30uALffcQuvv/4Wv37m9/ziFz8hIjKCVavWsnz553z/ye/685RF5Gpw8jjE+/79xGaBFs9cLhiMvkGLoxUO5cLWzyDjehiaDsazemvu3w7JYz2blh3wLGsLHgq2QnODJ0hpbQVaYdVfPesj+3gmm2+b8yRnHRzOg/6JsGMl1Hw5FNfRIt9eIz3li3OCjMwbYPg4z89BIZ7naeWbnsfjvvy7v2cdnKzw9lyhXwLc8gCYTFA9z1N/zTHPunMFhsCU2zw/tzTB7C9vxLG1eLa7UAgTEQPf/a1neLfYRFj+um/AM2wM3PN9Ty8bERERERG5IlyzIUx+fj4xMTEkJiZ6l5lMJsxmMxUVFVgsFkJDQzGbzTz11FPeNsHBwfz4xz9ut7/Y2Fjy8/O9j9PT09m9ezcOhwOz2UxRURFBQUEkJycDngvuBoOBlJQUnzAgKSmJHTt24HA4qKiooKWlhYkTJ/r01hk0qPPJlM/n7CEoDAYD8fHxNDQ0+FyE79+/P0ajkbo6z+TOJSUltLS0cN111/lc2B49ejRr167lwIEDTJkypcu1duW824aZOVvb5LrV1dXExsYCcN999+F2u30uQLe2tlJYWMgdd9zRreeoq6xWK0eOHGHChAk+PVH69OnDqFGjKCgo8DmXfv36XXCc/gceeKDdsv79+wOeMfZ78qL6qVOnKCoqYs6cOT7PX5uOfj86s2/fPuLi4oiIiPC+piaTidjYWI4dO+bT9uxeKgDx8fEUFRV5HwcGBuJ2uzly5Ii37ZQpU5g8ebK3zn379tG/f3+fQCwgIICpU6eyZMkSysvLSUpK8q7r6BxiY2N9Xrf4+HiGDBlCSUkJ0PX3fVc/Dzqqxel0UlJSQmZmps9rO2jQoHavyZX2/peelZmZwf/5+Y/5rxdf5ZVXXgNgzpxZ/NM/PeZtU1lZxT+WLGf69Kmkpg4nPDyc5194ll8/8wemLfFcrEtLS+HVRX/yvp8GDIjnv1/6A7/9zfPMnj0PgODgIP4/e/cdHWd953v8rRlNVa+WrGLZsixs4YrBBVMDxhgSIJTk3gQIKZu6qXs3OdnsbrIn2U3bkN1sCCGQZL3Uhm0AACAASURBVEPuAuGGJSwQCMQYTLAxGGO54I6LZNmSVUZlerl//DSaGc3Ilo3lsfHndY6OZn7P73me3zPFiPnO9/v99Gc+xkc+8r9P81WKyISITGAfj6DfBEZiEbANl+H0Dzdy6WoDa64JwrTtNsEImw22rjXBkh1vQGUt5Jckjvfsb7BNbsQ2ezk5h/akn+/trXDvV01w5fKbxw42bB0eb9s1anxt6n27E+qb4ciBkRJhY4qXESsYXm/y/Hjwp6AEPv7t1P2Kykw2zl+eAIfbPE4ti03GyhuroPkCk43ywb+B0kmmfNyCK82P0w27N9HR0UHpvjdxtA339JuS9IUkR9LfjpOnmfurH00N3sQDPXGVdSajyOmGwjK48GoTDNu9yaxLRERERETOOO/qIMzocj02m433vve9PPnkk/zwhz9kypQpzJgxg7lz56bUWPf5fKxbt45t27bR19dHaLjEQvKHtfGSZG+//TZNTU3s2LGD5ubmkQ/le3t7icVi/OQnP8m4Pr/fj8djak1PVF11q9WK1WpN+RA3JyeHnJwcotEoYD6sh0QwIHleRUUFR44cARj3Wsdz3ZmCDStWrOChhx7innvuYdKkSTQ1NTFv3ry0xuPbt28nGo2OmVFzqng8HqLRaMbG55WVlUQiEfr7+1NKkh1PLBZj48aNbNiwgZ6eHnw+3/F3OkmvvvoqdrudefPmZdye6f0xlt7eXoaGhvjhD3+Ytu14jeFzcnKIRCIj9xcvXsy+ffv43e9+R2FhIU1NTZx//vkpQZXe3l4aGhrSjhV/jfb29qbMH6+SkhIOHz4MjP91D+P79yATj8dDLBYb1/v7THv9y6l322038r73raCtrYPy8lKKi1ObzV9y6VI2vLE6ZWzRooU8+dQjHDrUgd1up6qqMu24F1wwjz888V+0HWwnEo1SUzOZ3NxjvzZF5CwQLwuW1Nid4b/dTqn+o/Dsg3DlB839gA/uGe6v5nDCF35iMlZaFkPjXNjTaratewouuDIRhBkOmFgP7WFa5wNYiuP/fY1BTVMig6W/Z/jn6ImvNTlwMmsRXH4rTG0xmSr3/M3YgRi7E664FZ64zwRLIiHTS2Z0WbFMAYxcO+QVwcq7YOs6yC8G74AJliy5DsqqYd5lifnOpJ5NeUVQ24QvlIu3cnIiCDNrcWKOxWr2iYRMwCscNIGgrWtNKbeAD+ZfAeueNvMvusYEi+yO9PMpACMiIiIicsZ6VwZhDh06xNGjR0dKhSWbO3cuTU1NbNu2jd27d7Nq1SpeeuklPvCBD9DQ0EA0GuU3v/kNHo+HSy+9lKqqKpxOJ6tXr2bXrsQ38uIlyd566y1KSkro7u7m6quvHtkeiUSw2+3cdtttGdeY3Pg7U2bI6RIPxmRqOByJRNK+sX+8tZ7IdSerqqri85//PDt27GDXrl1s3LiRv/zlLyxfvpwlS5aMzGttbeW8884bKWU1UeI9OzI9LvHHLFOGybE888wzrF+/niVLlnDllVfidrvZvXs3f/7zn9/5gpP4/X42btzIvHnzMj5Ox3p/ZBKJRKirq+Oyyy5L22az2TLsMTaXy8Vdd93F/v37R57rDRs20NLSwi233EJOTg6xWCxjgCMezDnRxz0uGo2OPK/jfd2P99+DTOLHGM/7+0x7/cvEcDqdTJ9+YgHE3Fwr9fXHz46sras52WWJyJkiHEpkvkQjibJgccnN2vu7U7edaMaM32t+H3obNq2B2cugohbaEtmrBPzw5ktmrG0nXHZrouxVfw+89Bjc8gVz35NYjzUcNKW6AGYuMkGKh36Q0tR+pMTYyWpZYo6Xa4PyyXD9x+HJ4VKfA72p2SMXXm0yU3a8YbJa8grTA1p2p5kzmjXXnGPupSYgYrUmAh+hQGomS/Kxcm3gSvTqiuSXmDJnG1fDzAtT57vzIf6FlVy7OeaCK2Hp9SYDZ9IUE4RpnGOCMLFYahk4ERERERE5470rgzCtra1UV1enfYvc5/MxODhIaWkpCxcuZOHChQwMDPDzn/+c559/no9//OMcOHCAjo4ObrrpJubOTXyjLFOpqZaWFlpbWykrK8Nut4/0XQEoLCwkGAyOlHHKJN7Q3OPxjNnMfaLFMzmOHj2akhUQi8Xo7u4eaVI/3rWO57pHC4fD9Pb2kp+fz6xZs5g1axbhcJiHHnqI559/ngULFuBwOBgaGmLPnj188IMfHNdx4x/kJ2dijFf8eo8eTf+mZldXF1arddzXF1/D66+/zrx581J6qHR2dp7w2o5nw4YNBINBLrrooozbx3p/jKWwsBC/35/y+j5Z3d3d2O12pkyZwpQpU1i+fDmrV69m9erVLFiwgMbGRoqLizM+LvHnIt70/kT19PSM7Dve1/2J/HswWlFRETk5OSOl/8YyUa9/ERE5ywz0JoIU7gLzoT/A0eHSn2WTTSkqgL7Uv09ycvNS7uP3QudBc7uoDIrG+G/+G6vM761rTWbJ7k2p2198NFGua3TmyF/+xwQFqqdC18HMx7/8VlOm69YvmiBPPMsGTLCibkYiuwbMvGf+8zjlxWKmpFc8AJJrgzmXmIBJZxv86UFii1eS89JjxGwOci5aYa7/zm8kGtU3JJVPtTvhti+Z0l5ggizJxwaz3+yLweZIPAehQGJ7MrvDzE8OlNgcsOwG85zmjfr70eE2/Xfi+9pspm+OwwU3fdaUcQOTiVQzHWITkBElIiIiIiITKv1r4Ge5aDTKli1bMn7L/+233+anP/0pb7311shYQUEBxcXFhMPhkf2BkftgMgsOHkz/n8uWlhYGBwd55ZVXUkqRATQ3NwOwZs2alH327t070kuitrYWl8tFa2trypx4qaTToaGhAafTybp160auHWDLli14PJ6RskfjXet4rnu0QCDAT3/605SMkNzcXKqrq4lEIiPr2rp1K06nM2OjeTBZDcnPm8vlwuFwjJSgijtw4EDGfZODNS6Xi6lTp7Jp0yYGBgZGxvv6+ti2bRtNTU0nnMEUi8VS1hfvjRK/fSpEo1HWr19PY2NjxiDLsd4fkMjaSH4tzJgxg66uLrZu3Zoyd926dWmP7fE88sgjPPjggynHj/dRiZf5mjlzJp2dnezenWi+Gw6HefXVV8nLy6O+vv645zly5MhICT2Ajo4O2traRsqYjfd1fyL/HoxmsViYNm0a27ZtS3lt9fT0pDzfp+r1LyIiZ4noGF8OSc50iUbgiZ+b4Mjvfmx+Nr98zMPaB0f9/RgP4hzelz45FjXb9wwHXTauhrs/m96nJZChsXyyB/4B/v2LsG1d+rZ5l8OM+aaEV8tik7kya1Fi+zW3w82fM03twWyrbTKBmcUrTXAETAZJcpP5umYT5HCNKm9bUWvOY3dB42z8BWWEZlyQyFwZPd/uhOW3w4e+ajJrKuvMT2mVCdoUlacGWUqrUve3jZGV6nAnzuV0E7U5iNmdJqPl8lshd9R+NkfiPDYHVE8zvW7igbO8QvMYtSzOfB0iIiIiInLGO6syYXbu3Mljjz3Ghz70oZEPbp9//nlaW1v53Oc+h91u5+2332ZoaChjv4sZM2ZQXl7Ok08+yeHDhykqKuLAgQO0tbWNlBKrra2lsLCQZ599lq6uLnJycti6dStDQ0NEIhFCodBICaaamhqKi4vp6+tj5syZKedqbGxk5syZrF+/Ho/Hw7Rp0+jr6+O1116jubmZ2bNnY7fbufjii3n++eex2+2cd955dHV1sXr16ol9IJM4HA6WL1/OE088wa9//WtmzpyJx+Phtddeo7q6mgsvNCUTxrvW8Vz3aHl5ecybN4/XX3+dSCTC5MmT6e3tZf369TQ1NY1kHbS2ttLS0jJmL47y8nK2b99OcXExzc3NTJo0iVmzZrFx40Yef/xxysrK2Lt3L/v27Uvbt6Kigr179/Lcc8+N9Aq65ppreOCBB7j//vu54IILiMVivP7669hstpTSc+NhtVppbm4eCUSVlJSwe/fukd4jfv9xPuQYp23btuHxeMZs3H6s9wckerz88Y9/ZOrUqVx44YVcfPHFbNu2jd///vfs37+fiooKDhw4wObNm1m5ciVVVVUZj5XJ0qVLefzxx/ntb39Lc3MzwWCQ1157jcLCQqZMmQKYvjGbN2/m4Ycf5sILL6SgoIAtW7bQ2dnJLbfcctxeLHG/+MUvmD9/PuFwmDfffBObzcbSpUuB8b/uT+Tfg0wuu+wyfvWrX/Hggw9y4YUXEgqFePHFF1PmnKrXv4iInIF6O6Gk0pQa6++GgNeUwqo5TnZpJGT6rCQ3pz+8H7raMk63dh4g//Ae4HITXAkHTIP3y2+Fx/4DPv/v5sP8uKDfZMGMFWRpnJOaoZJs8Uqzb9Cf6PEyLGZz4KmcSt57bsU2d5kZdLqHs0vccM0dJuCxda3pqeLKh7v+EX71rUQ5sAVXmoCKwwUv/t7Mr2hL9JWZVG/2S+6HkmszGT8Al9wADS34CyuwzbkU+1ilu6bPNYGN+PpOleTHubKO4NEBIoWlZrzoJPpAuvNh+YfNGvU3gIiIiIjIWemsCsJEIpGUb4bHx5J7PWzatImpU6dmbP6em5vLnXfeyapVq9i4cSOBQIDS0lJWrlw5UrrJbrdz++2388wzz7Bx40ZsNhsLFiygqKiI9evX09PTk1KOa+bMmbz++us0NTWlne+WW25hzZo1vPnmm+zatYvCwkIuvvhiLr300pE5F1988Uj2wqZNm6iurmblypX84Q9/ACAYDBIMBtOOHTdWj5UTES93tGbNGp5//nncbjcLFy7kyiuvTPnA93hrPZHrHu29730vJSUlbNq0idbWVvLz87nwwgtH+pD09PTQ1taWUsprtGuvvZY//OEPvPLKK1RXVzNp0iSWL19OJBJh586dRCIRpk2bxtVXX82f/vSnlH0vvvhi2tvbWb9+PVarlRkzZlBVVcXHPvYxnnvuOdasWYPVaqWhoYGrrrpqXM3WR3vf+96Hy+Vix44dhMNhmpubueqqq3j22WcZHBw84eNlsm7dOkpLSzO+HuHY7w9gJFC2Y8cOfD4fF154IS6Xi49+9KM8//zzbNmyhWAwSHl5Oe9///vH3Vcmbt68eVitVl555RWee+45HA4HDQ0NXHnllSPBBofDwV133cXzzz/Ppk2bCIVCVFVV8aEPfWjcJdEaGxuZOnUqr7zyCv39/VRWVrJixYqRMnMwvtf9if57MFp9fT0f+MAH+POf/8zvf/97SkpKuOSSS3jhhRdS5p2K17+IiJyBQgEY7DXZD4OJDE36uxPlr+L3kzNh3t6WfizfUKJPTMBnAjoDJgDial1LJAoc2JGY39UGj95t+q+sfwauSOrX5/fCxlVjLDoGC68yc9p3p29uWWwCJGufSvRdGRaZNpvDjUtpLK5M71sSD3TUNpmfeEbH5Gnwkb8Hd6EpQ1ZRa7JUll4Pb70KVVPMYxUvhzZ1dno2SK4tkTly/ceJeQfpnrGYSQUlYLOnX4PDbQJUcHr7q5RUJkqPjVd+ielrIyIiIiIiZ62c2HD0wuv1Znst71goFOIHP/gB1113XUr/hrPZyy+/zPPPPz/m9ptvvnnMrIZ3kxdffJGNGzfyxS9+MdtLOWu9G98f54pz7fV/5MiRkdJxcnrt3buX9vZ2Lrnkkqyu4+GHH6a/v5+bb775pILep4rH42Hv3r3MnTsXiyV7FVwDgQDbtm2jubkZt/sUfmP/JLS2tlJTU5PV5wVg165duFwuamtrs7qOgwcP4vf7x/zyA50HTXCltCrRjH7rOggMwY2fScw7sMOMx8t6FZamlwarqIEPf93cXv2oyVRJykLxltbgvvPrifl3fzZxe9YiuPHTpgF81RR45SkToMlk5iK48VPwwqOmITyYUmIDveY419xhxvq7zZrjc4DwkveyteEiZs5qwZ5fmH7sUMCswWpNL+fl95rAEkBekQms+AZhqB+8A6Y8255W+OJPYMrM9GMniQZ8bF/7EnV1dRRU1aZmp8TPNZ6eOafAtm3bKC0tPaHM4Ymwb98+GhoasroGEREREZFsefvtt4/5Rep4K4I333zzHZ1n3rx5FBam/7/QWZUJczxDQ0MsWbJkpJ/Du0FLS8sx/6ct2/9Dd7oUFRVx7bXXZnsZZ7V34/vjXKHXv4hIFoQCptn7O82UiESgr9Pc7mozze4rk/qbxXvEbF2byDxxZMh0jgdxwDSg70/tAePuGd6+7ilY+3TKNra9CjnAwV1p+1EzPXHe8hq44+/A22/Kda172pQmq6g1t+Mlw8Bkp7QsTgnCRCtqidqcxMbql2JzwFhVPDOVBXPlJ7JtaptM4KS4YowDpJ4nUFRJtLwmPQAjIiIiIiJymr2rgjDFxcVcccUV2V7GKVVSUkJJSUm2l5F18+bNy/YSznrvxvfHuUKvfxGRcRrsNT1XrLaT//A9GgFPtwkAeLqhfPL49guHwD9oSobFYqbJe7zEWGi4J8yjd6f3YQkOz0ku/TVWr5Y3VplAyEBP5u2P/ji1j0yyra9mHn/vx+Her5nbC640GSgFJVA3w5QFW3ClKT92cKcJxrgLzGPk95ryWuU1cNQEgGLFlZnP8U4UlZv12OympNupyFpxuqG++Z0fR0REREREZBzeVUEYERERETmH9QxnnDjdqUGY8ZafCgXMvEjEZINEIiawkz+OL8QMeUzQZus6uGA4YyQSSWx/Y1UiuNJ5IDEei5p9MomXAYt78ffmZyxjBWDGMmUmNC802S4Hd5rMltzhVBWbw9yvbTLBmMtuNuspKDHX6veax3LpdSYzB4gWl0PfGAGkd8JiheqpsFhZoSIiIiIicvbJXoFzEREREZGJEAqYzJOh/vRt3n5TFmz0Nt8g9HaawElXG7z8hBlv/Ys5Xs/h45+3bZcpN+bpzrwtLuCHfVvA02XOu+6pzMf78j0mI+VUuesfTRDF7oSLroH5l5vxlsWppcbAZLnc8Q2TMVI1xTSHL6k0QZpcu8mIyXVA41xYch0suW5iMmGSTVdPOxEREREROfsoE0ZERERE3l0iEeg7ahrA5xVCOJDYFgqZH3tS35VQAFY9DLMvMfef/Y35veBKczsahY2r4LYvm7JYAS+EwxRseJHAecsg4DNlyLauNQGWN1aBOz91Tck9XQCeewhqZ5jAwug+LQCVtab/yZxLzFr3tKbPiWewBMfIPrE74fJb4U8Pmt4vjXPhuo+Cz2uySuI9WArLTKmx8bLmmnJtTrd5jJMzfkRERERERCSFgjAiIiLnmJ6eHvz+9A9t/X4/vb29dHR0ZGFVCeFwGICuri6CwWDW1jEwMEB3dzcdHR1YLNlLHg4Gg3R3d3P48GFcLlfW1gHQ3d2NzWbL6vMCcPToUZxOJ1ardWQsJxwkt/to2tyQJQ/LYC+ubWsJ1p+HZbCPaH4xsSEfYW8IAPuBbZS8+N/0TJ6J681V5A0HTHyrHsXV30N41SPk9h5h8Pnf4Z+1hJygn6Jnf4k9x4rXVsJRnwmilO5606SZr3uayLZ1DFz6AfJe+yM5QV/aH93RXW8S7djHEDaK4mutmort8NsAeCum4OnowFrdjHPIi7v7CLl9R5KOEMPTuBC7zYVre2q/F995i3BtfxV/3UwGalsomDaXYP0svD29xOqH+4z1egAPADaLE0IxYp4BwuN9/0ej0D+EdcCLJegnas1lyB8bea3a7fbxHWcCRKNRuru7ycvLw+v1Zm0dYF6r4XCYWCyW1XV0dXXR0NCQ1TWIiIiIiJyrzrogzNDQEGvWrGHXrl14PB7cbjdNTU1cfvnlFBQUABCLxfjZz35GXV0d733ve0/ZuQ8fPsyaNWvYv38/Pp+PkpIS5syZw8UXXzzyIYDP5+N73/seF110EStXrhzzWM888wzr1iXqf+fk5FBYWEh1dTWXXnopkydnbgLb39/P3XffzR133MHUqVNP2bWJiMi5Y3BwkMHBwbTxcDiM3+9naGgoC6tKiEajgPlvam5u9v5U8Xq9I49HtoMwfr8fr9c78thki8/nY2hoCJvNdlrPa/N0kROLErXaCBeU4vP5iEajKa/VnKAfuy89uBgYGsLq9VK2/in8sRzyt66h96LrCRZPImTLxxIOYjuwE0sogOWt9eS1vjiybzy4kdtrgh/2XW/QVzOTytX/l1xPJ/6iKgKBAD6fn7x9rVhCiYwba38P7jW/x+bpzHhNllAAS+8R7FvWDI/E6JuxiFjzEgBCxZX4hoaweb0EKhrov+x/U/uHu1OO0V9QTu60BYR7OjmcP4mKgjyGps0jlmNh8p438ZbW0G/PJzzrEsKuAgKBEARCaWvJcZgwUI7FQvRE3/+5bvODCeTG3zOhUPp5TpdoNDrynsnmexcYWUe2/131+XxZPb+IiIiIyLnsrArCDAwM8Itf/AKv18vs2bMpKyujr6+P1tZWtm/fzic+8QmKi4vJycnB5XKd0m/g7dmzh4cffhiHw8Hs2bNxuVwcPHiQVatWceDAAT70oQ+Rk5Nzwse94oorRj5g6uvrY/PmzezcuZPbb789Y5Bl8+bN5Ofn65tsIiJy0urr6zOO7927l+rqaqZPn36aV5Tq9ddfx+/3U19fT1lZWdbW4fF4iMViTJ8+Pasf5AYCAQKBANOmTcPtdmdtHWACUzU1Naf/eTkwXO7KaoWa6cRiMVwuF7W1SSW0/F7oHA4OBXzgGM4amjwF+tzg7ady4zPgcFHd8RbMvQgq60z/lwOm1FfpjrXHXIbd00ntlj/DcGDFGfYxadIk6t542pQoyzD/eFyHhnvFONxUXHR5orSX023W1+kw1zZaUTl1M2aCxcpAfj74whRPn05xZZ3Z9+huSmcvo3T6eVBfa8qh1U/se9vr9RIKhWhsbMThcEzouY4lHqCbOnUqRUVFx99hAgWDQUpLS6mqqsrqOrIZ0BYREREROdedVX+Nr1+/nv7+fj70oQ/R1NQ0Mn7hhRdy33338cILL3DTTTcBcNddd52y8wYCAR5//HEKCgr42Mc+Rl5e3si2F154gRdffJEtW7Ywe/bsEz72woULU463ePFi7rnnHl5++eWMQZjW1lbOP//8kwr4iIiIiJzVIhHo7yZ3sAfbQASiQ1BUBkXlqfPWPgkVdabh/NFD0LHXjMd7r/R3m14p/d3wx19D267U7ceS1Jsld8hD3r5W2LMpsd3uNAGggd7M+9udmXu4tCyB/GLwdI997uR9G1pMkAaIdLSBr8uMx/u8XHaz6dsC5ndZ9fGvTURERERERE65syoI09/fD5CWBTJp0iSamppGtgN8//vfZ9q0adxyyy0A/OxnP6O4uJjp06ezdu1aPB4PZWVlrFixgkmTJvHss8+yc+dOLBYL06ZN49prrx0Jjmzfvp2BgQFuvPHGlIAJwNKlS1mzZg1vvfXWSQVhRisrK6OyspLe3vT/ce/s7OTIkSMjgSYRERGRc07fUazeAYhnWgz2mcBEOAj3fAUCowIcLYvBN6oUVMAPa58yDek3rj7+OQtKxgyqlGz6c+rA1BbTuH7bq+mTC0qgbkb6NrsLrrwtdSyeyeNwJTJhWhYn1tvQkn78pB45lI7KvMgrzLh+ERERERERmVhnVRBm8uTJbNq0iQ0bNrB48eKUbR/84AePu/+ePXvo6elh/vz5+P1+NmzYwCOPPEJxcTFut5tly5bR1dXFpk2biMVi3HrrrQDs378fgBkzZqQd0+Fw8JGPfCSlMew7EQqF8Hg8VFRUpG1rbW2lsrIy6+UMRERERE5YKAC2pBJV0eGyW5bj/A2VqRRXskgkEWQZHYD504PgdEFnW/p+G1elz4+rmW7KlAX90DgHrrkDfnc3HG1Pm5rcBwYwwRHP0czHrayDBVfC7k2JjJbGOXD1/zbn9KX3akpx+a2w5HrYuhYaE1/+iUxuJBBkJDNGREREREREzhxnVRBm4cKFbN26lWeeeYadO3cyb948mpubx11z2uFw8LGPfQyn0wlARUUFjz/+OHa7nTvuuGOkxFc4HGb79u3EYjFycnIYHBzEZrONWYd9rNr64zE4OEgkEiEWi9HT08OaNWvwer1pQaZYLMbmzZtZuHDhSZ9LRERE5LQb6ofuDnO7tBLyS0xAJuA12SV214mVyurKEFAB2Lou83hnmyk7NtpYARgw2SotS0wQ57qPmXJelbUZgzAj5l9ugiu10831rHs6sc3uNAGUOReboNENn4JHf2z2ufxWqKgx83LG0XvI4TKBnIralOGozZka5BIREREREZEzwlkVhLFardx555289tprrF+/nscee4zc3FxmzZrFFVdcQUlJyTH3LysrGwnAANTUmP/hbW5uTumxMmnSJLZu3crQ0BD5+fmEw2HsdvuEXNPPfvazlPv5+fm8//3vZ+bMmSnj+/fvx+PxnJKSZyIiIiInwxIOYe3pgKgPXHkmoHI84WDitnfQ7NOxLzH2+5/CFbeYoMd4PPFznPNXEKsd7g/46I8TPV3Gcqw+K7MWpZcHq5sBtU0m6FI3AywWqM1QRizZ9Z8AhxPyiuDAjtT+Lbd/3ZQ+szkg4jXHXn676WVTVJYIntgdJpsl12Z+YHhOuckcatt97OsUERERERGRM85ZFYQBE4hZvHgxixcvpqOjg82bN7NhwwZ27NjBnXfeyeTJk8d9LIvFMnLMTOPRaHRkeygUOkVXkOr973//SGDof/7nf8jPz2fOnDlp8zZv3syUKVMoLi6ekHWIiIiIHFcsYsqD+WyQexJfUBldWuyNVaapfdtOuPPvofmC9PJkkTCsewoO7oKiUujvofyV39N79Z0Q9Bw/ALN7U+YMFrsTVtwB8y6Huz+b2vOlsNT8ft9fJYIhLYvND5g1P3Ff6vzSSYn7BSXw8W/Ds7+BPa0mADNay2KTAePKT4xZrODMnHl93LJtIiIiIiIickYaR82DM1d1dTXLly/nk5/8JADPPffchJwnPz+fYDCIz+fLuP3w9dNh2QAAIABJREFU4cMcOXLkpI7d2NjIjBkzmDFjBkuWLKGjo4Pdu1O/5RiJRNi2bZuyYEREROTMMboXyonqajPBFYCAD/77pxDMcMxwENY+bYItW00miiUUoGjtH2D3m8c/T3IApnEOLF5pslA+/m2TfVNQAstuMKXB4grLwGYzGShxycGR5MAJmCyZZBaLKRv2vk/CrV9MjMcDOvHjjT7OibCr9JiIiIiIiMjZ4KwJwkSjUf793/+dp556Km1baWkpVVVVJx0IOZ54z5ddu9K/aRkIBLjvvvt48cUX3/F5Fi5ciMvl4qWXXkoZ37VrF8FgkFmzZr3jc4iIiIicFuGQCa5Ewqnj8WyYN1al9mXpaodffwvaR5Xc6sn8911u7xHYuBqIjX9Nl98CS64zWSgFJSZQkmuDpdeZgEx5DdRMNwGYkqpRJ0wKoJRWQc10gpVTGJh+Aaz8yKi5SVlC8bJpANakJPR32r9FmTEiIiIiIiJnhbMmCGOxWCgoKGDz5s10d6fW9R4aGqKzs5OCgoIJOfesWbPIy8tjzZo1+P2pTVxfffVVotHoKQmQ2O12LrroIg4cOMD+/ftHxltbW5k+fTpu9xjlKURERETONAO9cOQAHNoLD/y9yXrp74aA12S17NmUvs+ODfDDT4FvMDF2ZH/6vOOpmW6CLHHlNSbTJbksWNWUxJz8Epg+1wRnKmuhsDy9LFhhmenXUlkHFbVw25fouepOeuddbc6XzDpGxV+HG+qbzU/BOPrpiIiIiIiIyFnvrOoJs3LlSn7961/z85//nPPPP5/S0lK8Xi+bN2/G7/dzww03TMh5HQ4HN954Iw899BD33nsvs2fPJjc3l7a2Nnbt2kVTUxMtLS0p+xw5coRXX01v3lpfX091dfWY51q0aBFr167lpZde4vbbb8fv97Nz505uuummU35dIiJyekQiEZ57bjU7duyipLiYa1deRUVF+XH327RpCy+/vA6LxcLixRcyf356Wcru7h6efXYVnUe6qKur4brrl4/0GhM51SzhENiHM0KOV44sFDCZME/8HPp7TEmxtU/DnGXQ8XZqFozdabJS4n1Z3ngBlqwEbz8cPl4QJgdmLTJBkq42WHClyT753d2J47UsgituA0/qF3lSskmKyuG8hbD9dcgrTD9Nri2RDRNI6m1jyfCdJrvDBGtiUYhGE0GZ5BJiyZk1J8r2DvYVERERERGR0+qsCsJMmjSJz3zmM6xZs4adO3eyadMm8vLymDx5Mpdeeik1NTUTdu6mpiY++tGPsnr1atavX084HKasrIyrr76aJUuWkJOTkzJ///79KdkscVddddUxgzBut5sLLriAtWvXcujQIY4cOYLVaqW5ufmUX5OIiEy8UCjEX//1V9mxfReLFl3A6hde5oEHfsv99/8bTTMax9zv5/f+ivvu+09mz57FpEmV/PbB33Hd9cv5+te/PDLnrW07+PSnv8KkSRWcd94M/vjH5/jNg4/w4IP3Ulg4Mdmhco6LRRO3IxGT2QLgLjRBBb8XOg+asYAPHr3blBlL1vpy4nZRmckiqaiFuhnw238x43s3wwXvgZ5OE1g5niXXpWa5QGomizPfBFlGB2FGa2gB64kFOKKZ5lus6Zk0p0J1g3nck58HEREREREROaOdVUEYgIKCAlauXMnKlSuPOe9v//ZvU+5/+tOfTptTVlbGN7/5zbTxZcuWsWzZsrTx2tpaPvzhDx/zvC6XK+MxR1uxYgUrVqzIuO2aa67hmmuuAeC5555j5syZ5OaedU+ViIgAj/3+f2jdtIWHH/kl9fW1BAJBPvPpr/Dd7/6YB375k4z7vLVtB/fd95/ccecH+cIXPgXAzp27uf3Dn2ThwvksX34F0WiUb3zjO8yZ08KP7v5ncnOt9PT0ctONt/Pb3/6Oz3zmY6fzMuXdKhQwmRwWCwQD5Aa8FP33d6FpHjTONT9g+r/kWEzgpW2XyUR5Y1V6AGa0C66CS26EoN8EcApKTPbK3s2ml0zAl7lsWTKHOz0AAyaws6fV3K5uSN1mHaOfitMNJZXHPl822RygJBgREREREZGzylnTE+ZcFI1GaWhoYMmSJdleioiInKRnnvkzl166lPr6WgAcDjsfvv023nxzM4cPd2bcZ81wCbJPfvKukbEZM6azZOkinn76OQA2bmxl374DfOVvPkdurvlAubS0hO9+7x9ZuvSiCb4qOWd07DN9XTr2gW8Qd+c+coIB2PoqPHFfIhNm0GOCJ0E/bF1rslcyBU8KS1Pvl1ZBcYUpRWazJYIpfV3Qc9gcK162bNai1N9xk6dCaaXJqskvSoy3LIZbv2h+qhpS97E5GFNxxdjbRERERERERE6Q0ivOYBaLhcsuuyzbyxARkXdgx45dLFu2OGWsafq0kW1VVenfuvf5fBQWFuB0pn5QXFRYwGuvvQFAa+tWyivKqK+vZe0r69m1ey81NdVcdtmykaCMyCkVDqWP9fekZqFsXWdKfq19OnMWzNxLYc3jifvxrBOH22TB1M2A9t1m7EefAUdSf6Ml18H0uWbutqS+e9NmQ/5wk3u/1wSEwKwrvrZ4abDqhmMHYOCd9WoRERERERERGUVBGBERkQni9wcIBIKUlBSnjBeXmG/r9/V6Mu7XMKWevj4P+/YdoKGhHoBIJMLmzdvo7x8E4HBHJ8VFhXz+819j65a3KC8v4+239zNz5gzu+8WPcTqdGY8tclztu02fFn9S8/n+bigsw+kZlb11cKcpPQamDNmLjyYyVzJ5zwdh75ZEoCWe1RIPkowuKxY/VmWtaXRfWGbOc7KOF4AZr1z7qTmOiIiIiIiIvOspCCMiIjJBQiGTOWAd1X8i3ucrHA5n3O/q5Vdw772/4itf/gZf+MKnKC4p4uGHHqO3t29kjs/vY/fut2lomMIzz/4/7HY7W7a8xSc+/gXu/8WDfO6vPzHmusLhMNFoemPvcDiMz+djYGDghK/1VIqvbWhoCLs9ex92Dw4O4vV6GRgYwGLJXgXXYDCI1+tlcHCQSCQyoeey9BzG9f9+hPcDfwt2FzleL9buQzieuIfg0htwHkoNgMTe+DO+mYuJ2V04Vz2C9VgBGGDQ5yfn+k8CkBMJExn1WrOVVjM6fBgprSJw418TsTiweI9iDQRS5vhKqgnHjxPwYfF6GS1mdRKznKIADEDAj8Xrxe/34Y9Zsv6eGRoaGnmtZpPP5xt5rQaDwaytIxqNjqwjm+9dAK/Xi8PhyPpzMzQ0lNXzi4iIiIicyxSEERERmSAul/moODDqw0i/PwCAO8+dcT+328WP/+1f+NY3v8uXvvR1bDYbN73/em56//U8/ZTpCeMYDk586UufHglUnH/+TJYvv4KXXnrlmEGYffv24fFkzsLp6Ohg9+7dJ3CVp178w9uDBw/S3d2dtXUMDQ1x6NAhnE5nVj/IDYVCtLe3Y7FYJjbDKRal5O03aWjbxZE3/oKvrJbyXa9SsfVFcsJBcl9+HEc49bWcEwww+OrzRG0O8ro7jnn4sKuAPQcO4vB0ARB1uAkEU19rDl+MWYC/oAxrKIDNP0jbrMvx9AeI+NtxeDpxDPhoTNrnUG8//cOvWUvIP3L8lHO7PYTcPSf+mIwhfp7e3l685OLL8numv7+frq4uHI5TGGg6CX6/n/b2dmw2GzZb9sq6RaNR2tvbicVi5OXlZW0dYP4d83g89Pf3Z3Udhw8fpqWlJatrEBERERE5VykIIyIiMkFyc3Mpryij80jqh8KdneZ+VdWkMfdtbp7Ofz10P4ODgzidTnJzc/nG332bhoY6AConmV4a5RWp5ZsqKsp5+eV1x1xXXV0dkydPThtva2ujurqa6dOnH//iJtBbb71FMBikrq6OkpKSrK2jv7+fnJwcpk+fnvVMmFAoRGNjIy6Xa8LOk+M5in2P+bZ8bcxLqGEq7mf+A8tw4MUazpzZUH6glZgj87rCMy4gd+cGc/zSSUyb0UzOkHntxRwuGLWfpbeQ4NL3EZl9CZZdGwgdepuyZSspdReYCYEaLEfb4YXEPpMnT6Yyw2vWcrQdAj5zrtJqYq5T+GF8wIflqBOn08FQrov6LL9nent7cTgcWX/v+nw+otEojY2NWc1ii0aj+P1+GhoaKCwszNo6wJSSLCkpobIyvf/X6ZTN50NERERE5FynIIyIiMgEmj9vNhs2vJky9tr6jTgcdmbOnJFxn+3bd/GvP/wPvv+Db430k/H7A6xd+xofvv02ABYsmANAa+tWFiyYm7TvTqZNazjmmsb6tnxubi4ul4uCgoJxXdtEiQc88vLysrqWaDSK2+2moKAgq0GYQCCA2+0mPz8ftztz9tQ7NtQP0SAc2QeAY/dGHIXFMNiXNjU8qYHc3sMQNKXHLIN9GedRUELuBVfCcBDGarFQUFwKxaVjryMagEXXYAdYdA0UlGBz5Scds8DMSeKqrjfjo/nywJpjbhcWJvrOnAo2K3jdDDhdRG3Zf88Eg8GR12o2Wa3WkddqNrNy4u/d/Pz8rD8mbrc76/+WAVnPCBIREREROZdlt0iyiIjIu9wHPngzra1beeD+B/H5fLz++kbuv/833HzLDTid5kPKXTv38E//9H127DAljWprq9m5cw8/+cl9+Hw++vo8fO2r38RisXDrrTcAsGDBXM4/fybf+fa/snv32wSDQR5++DHWrn2N//W/bs7a9cpZyDcI8VJibTuHf++CJ+7LOD1w/jKoqD3+cZsvAGvS932mnHf8fZIb3luskByAiStMzf6iZOyMMhEREREREZFsUyaMiIjIBJo/fzZf/doX+bcf/4x77nkAgBUr3sPnP/9XI3MOHz7CHx5/mksuWUJz83Ty8/P5/g++yT/+w3dZ9vi1AJx3XhM/u/dH5OebD6VzcnL40d3f4R/+/p/5wG13AeByufjSlz/Dle+59DRfpZyRwiGwWEwwY7RoBELD5cV8g+Z3VxsE/JmPVTMd2ndztOUycqfMgn2tmecVlMBALxSWwuW3mDUsXmm21c88/pqTgza2M7h8kt0BlXWEQzlEQ5Fsr0ZERERERETOYArCiIiITLDbbruR971vBW1tHZSXl1JcXJSy/ZJLl7LhjdUpY4sWLeTJpx7h0KEO7HY7VVXp/QTKykr56T0/5GhXN57+fmpqqie2cbucXYY84BuCyloIBmCgB4orwOYw9zsPJuZuXQfbjtFL6IpbweGit7OXCjCZMHsyBGJW3AmvPAnTzjeBG89RWHKd2TaecmDDwQ0AcrPX2P24LFZwuonZXcSiYwSuRERERERERFAQRkRE5LRwOp1Mnz71hPbJzbVSX3/8sk/lFWWUV5Qdd56cg4J+6OsCd6EJyPiGIL/IBDuS7dlkSpBlYnfBlJkQjRD05RCZ3Ahtb2WY5zTBmZV3pZYVOxHDwY3j6Zv7HnKjIfKrp4w9yeYAv/fk1iEiIiIiIiJyiigIIyIiIvJu1n3YZL7EDXpMoGP1o7DkenC4Er1g4uxOE8ABmFQH5ZMBiBzuNWOVtSbTBUwZswVXmh+HC2qnZ+7lcgr1N12Iw+EgvzI9Q2yERa0PRUREREREJPsUhBERERF5N/B7IRI2ZbyS+6msfRIuvzV97rZ10N8LS1Ym9YKJQeNcE1B59MdmqGFW+rkqauG2L5nbAZ8JvoDpCTPBARgRERERERGRs4mCMCIiIiLvBn1dieyVyjoTkGnbBRtXm4yXtp0mwALQ320CL3s2QVdSb5jiSrj1ixANQ3kNHG2HxjnHPm88AJNfBCXHyEyZKKNLq8U53BBvv3Qm95cRERERERGRdzXVaRARERE5W/R2mvJfPYdTx0OBRAAmLhyCg8Nlxt5YBc/+JrGtqy1xu79n+EYM/vYXMKkenPkmGwagquHYa3K6ob4ZSqtO9GpODYs187jTDUXl5kdBGBEREREREckSBWFEREREzhShgAmQHNhhAi6jefvBN2T6urTvToxHIqnzAsMN6fdsMr/XPW0yX/q7zf3dm9KPbXcnSokVlcH8y03fF6c7fa41KfARz4TJJNc+9jYRERERERGRc4CCMCIiIiJninDIBFnA9FoZLTnYsv7Z9IyYZH4vdLWnjvX3wNZ1sO3V9PmT6hK3LVYoq4Irbs0cSLGNUQJsNGtS5Vtlo4iIiIiIiMg5SEEYERERkTNFckmxoB/+73ex7N2ceW7rGtgzxrZIGA5sz7xt3VOZxyfVp963OaB5QWogJXnbicp0HBEREREREZF3OQVhRERERM4UkXDi9tZ18Ppz5AwM92yJJmXB7NkEfUdh35b0Y3S1mUyXjr3p27auTeoBgyk5Znea2zWN6fPzSzI3vo+XKLM7T3/JMesYPWBEREREREREzkD6SqKIiIjImSIcMmXINq6CtU8DOXBwB0yrwJJceize02X/jtT9t66DFx+FuuZEWbNko8uQzVxkzrftVSiZlHlNmRrfu/Khvnncl3VKnUwWjoiIiIiIiEiWKBNGRERE5EwRCZlslbVPjwzl9HVj83pMj5e4PcNBmIEeuP/voPMgvLEK/vQgBPwmcDPQwzHZnTBzIcy8CIjBjAWn/npEREREREREznEKwoiIiIicKUIh2LYuZSin9wiWUCAxsHWdCbSAKS22dTi7pb87MSfgTy07liIGzQth6vngzIdlN8DkRpPdIiIiIiIiIiKnlMqRiYiInGPa29sZGkovVeX3++no6GD37t1ZWFVCMBgE4MCBA/T29mZtHYODg7S3t5OXl4fFcnq+t5L39haqu9pTxmKd++k+1IY9FqH2lUdxHj2Ytl/Hji2UHNyDM8Mxg0UV2D1dSSM57F90E672nfTv2w+A66Ib8Y3jeW9rayMYDI77ecmJhLH4BwGIDoaIdfWNa7/j6T1yBJvNRjAYJBDMXo+Yw4cPEwwGycnJydoaAPr6+ujs7MTtdmd1HX6/n/b2dhwOB3b7ae4VlCQajdLe3k5OTg75+dkNLra1tdHf38/g4GBW13Ho0CEaGhqyugYRERERkXOVgjAiIiLnGLvdTiQSSRsPh8M4nU7y8vKysKqEeMDD5XJldS3RaHTk8TgdQZicSBhXOJ7xEiOa68QSDmANBbDb7TidTmy+/oz7usMBciPBzMd1usGTuB+smYG9qp5IVT0jj+6M+YznkY4/Jyf0vBQWjX/uOHmHP+R3uZzkZvE14na7sVqtWX/PhEKhrL9fwLx34++ZbAdhnE4nbrc764/JmbIOl8uV1fOLiIiIiJzLFIQRERE5x1RUVGQc37t3LyUlJVRXV5/mFaXKzTV/nlRUVFBWVpa1dbjdboaGhqiurj49mTB+LwQS0RJLdT0c3AXA3M1PYXu7COvQ8Ha7E4L+kblF7Vuh90j6MR1ubOcthCP7R4bsdttJP8ddXV1UVlZm9XkBCBUX43A4KC8rhyy+XsPhMH6/P+vvGbvdTigUyvo6vF4vHo+HqqoqHA5H1tYRjUbp7Oxk0qRJFBWd+iDgiejt7aW0tJSqqqqsriMQCBx/koiIiIiITAj1hBERERE5UwR8wzdyYM6lJtgCOPuPYj20JzHv/KVQUJK4H+8LA9A4B8przPav3APFlannqJ46MWsXERERERERkTQKwoiIiIicCSJhOLgzcX/OMmhZDGToN+LOhyXXZz5ORS3c/nVYcae5XVmbut2V3R4ZIiIiIiIiIucSBWFERERETrdQALra4MAO6DlsxsJBCA5nwhSXm98VdZn3L6owAZrhTJmMps89desVERERERERkZOiIIyIiIjI6RaJgG/I3PYNmoCMtx+62s1YySTze3QWS1zRcJDms/9qSo8lq5thftuGe3KUjCpHNnnaO1u7iIiIiIiIiIxbbrYXICIiInJOiwdkRvrBYAIruTZTTszuhKDfjDfOMWNVU8xPxz5wuFKPV1kH9c2J+/GATpwzbyKuQkREREREREQyUBBGRERE5J3wexNlxNyFJngyllAA+rpM4CVZVxs8+5vE/dJJYDV/pkWmzsa64zUzvuBKqG0ygZh4psu57Fjl2ERERERERETOAArCiIiIiLwTAS94us1tu+vYQZjkMmRxezaZAEzAnxirmQ65drA7iS5awaGSeipaFuAsLE4/ZmUttO9O3D+XesFYVFlXREREREREzmwKwoiIiIhkQ383PPsgtO1K3+bKh7xCsOYSGxpgqLSGirGyPhzuEzvvu6EnjFV/woqIiIiIiMjZQV8fFBERETlVOg/CgR2mRNnxbF2XOQADiWyWY2XVxNXNSNweT3mud0FPmHBeEZH8YsgryvZSRERERERERI5JXyMUERGZYJFIhOeeW82OHbsoKS7m2pVXUVFRftz9Nm3awssvr8NisbB48YXMnz97zLnhcJgH7n+Qy6+4hObm6ady+XI8kfDJ7XdwZ+bxitrE7VybyYoZi9VqesTMWgSFZaY02Tkg4swn6nKZbCERERERERGRM5gyYURERCZQKBTis5/9P3zvuz+m49Bh/vu/n+SWmz/Crp17jrnfz+/9FR+963O8tv4N9r19gM999v/wz//8ozHn3/uzX3Hfff/Jnt17T/UlyPGEQ2Nvi0Yg4APfYHp2THIfl2STpqTcjRWX4yuvI1ZYmj63ss5kv1xzByy5Ds5bmPmYamAvIiIiIiIikhXKhBEREZlAj/3+f2jdtIWHH/kl9fW1BAJBPvPpr/Dd7/6YB375k4z7vLVtB/fd95/ccecH+cIXPgXAzp27uf3Dn2ThwvksX35FyvyNGzfzX//16IRfi5yEYMCUKAPILwL3cOZGpjJkH/snk83iHNXjxWpKksUcbnC5zFi8TJnNkdqc3jpG+bLLbzU9aArLTvJCRERERERERORkKBNGRERkAj3zzJ+59NKl1NebMlEOh50P334bb765mcOHOzPus2a4BNknP3nXyNiM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){width=\"1200px\"}
\n", "\n", "One thing to note is that contrastive learning benefits a lot from long training.\n", "The shown plot above is from a training that took approx.\n", "1 day on a NVIDIA TitanRTX.\n", "Training the model for even longer might reduce its loss further, but we did not experience any gains from it for the downstream task on image classification.\n", "In general, contrastive learning can also benefit from using larger models, if sufficient unlabeled data is available."]}, {"cell_type": "markdown", "id": "aaab8b66", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.110092, "end_time": "2021-10-10T16:41:55.567176", "exception": false, "start_time": "2021-10-10T16:41:55.457084", "status": "completed"}, "tags": []}, "source": ["## Logistic Regression\n", "\n", "
\n", "After we have trained our model via contrastive learning, we can deploy it on downstream tasks and see how well it performs with little data.\n", "A common setup, which also verifies whether the model has learned generalized representations, is to perform Logistic Regression on the features.\n", "In other words, we learn a single, linear layer that maps the representations to a class prediction.\n", "Since the base network $f(\\cdot)$ is not changed during the training process, the model can only perform well if the representations of $h$ describe all features that might be necessary for the task.\n", "Further, we do not have to worry too much about overfitting since we have very few parameters that are trained.\n", "Hence, we might expect that the model can perform well even with very little data.\n", "\n", "First, let's implement a simple Logistic Regression setup for which we assume that the images already have been encoded in their feature vectors.\n", "If very little data is available, it might be beneficial to dynamically encode the images during training so that we can also apply data augmentations.\n", "However, the way we implement it here is much more efficient and can be trained within a few seconds.\n", "Further, using data augmentations did not show any significant gain in this simple setup."]}, {"cell_type": "code", "execution_count": 12, "id": "6745071e", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:41:55.788482Z", "iopub.status.busy": "2021-10-10T16:41:55.788000Z", "iopub.status.idle": "2021-10-10T16:41:55.789951Z", "shell.execute_reply": "2021-10-10T16:41:55.789550Z"}, "papermill": {"duration": 0.117728, "end_time": "2021-10-10T16:41:55.790056", "exception": false, "start_time": "2021-10-10T16:41:55.672328", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class LogisticRegression(pl.LightningModule):\n", " def __init__(self, feature_dim, num_classes, lr, weight_decay, max_epochs=100):\n", " super().__init__()\n", " self.save_hyperparameters()\n", " # Mapping from representation h to classes\n", " self.model = nn.Linear(feature_dim, num_classes)\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr, weight_decay=self.hparams.weight_decay)\n", " lr_scheduler = optim.lr_scheduler.MultiStepLR(\n", " optimizer, milestones=[int(self.hparams.max_epochs * 0.6), int(self.hparams.max_epochs * 0.8)], gamma=0.1\n", " )\n", " return [optimizer], [lr_scheduler]\n", "\n", " def _calculate_loss(self, batch, mode=\"train\"):\n", " feats, labels = batch\n", " preds = self.model(feats)\n", " loss = F.cross_entropy(preds, labels)\n", " acc = (preds.argmax(dim=-1) == labels).float().mean()\n", "\n", " self.log(mode + \"_loss\", loss)\n", " self.log(mode + \"_acc\", acc)\n", " return loss\n", "\n", " def training_step(self, batch, batch_idx):\n", " return self._calculate_loss(batch, mode=\"train\")\n", "\n", " def validation_step(self, batch, batch_idx):\n", " self._calculate_loss(batch, mode=\"val\")\n", "\n", " def test_step(self, batch, batch_idx):\n", " self._calculate_loss(batch, mode=\"test\")"]}, {"cell_type": "markdown", "id": "55495b0b", "metadata": {"papermill": {"duration": 0.040786, "end_time": "2021-10-10T16:41:56.009838", "exception": false, "start_time": "2021-10-10T16:41:55.969052", "status": "completed"}, "tags": []}, "source": ["The data we use is the training and test set of STL10.\n", "The training contains 500 images per class, while the test set has 800 images per class."]}, {"cell_type": "code", "execution_count": 13, "id": "eeca05e6", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:41:56.197171Z", "iopub.status.busy": "2021-10-10T16:41:56.196690Z", "iopub.status.idle": "2021-10-10T16:42:07.538122Z", "shell.execute_reply": "2021-10-10T16:42:07.537640Z"}, "papermill": {"duration": 11.449982, "end_time": "2021-10-10T16:42:07.538239", "exception": false, "start_time": "2021-10-10T16:41:56.088257", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n", "Number of training examples: 5000\n", "Number of test examples: 8000\n"]}], "source": ["img_transforms = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n", "\n", "train_img_data = STL10(root=DATASET_PATH, split=\"train\", download=True, transform=img_transforms)\n", "test_img_data = STL10(root=DATASET_PATH, split=\"test\", download=True, transform=img_transforms)\n", "\n", "print(\"Number of training examples:\", len(train_img_data))\n", "print(\"Number of test examples:\", len(test_img_data))"]}, {"cell_type": "markdown", "id": "88ebb27b", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.066846, "end_time": "2021-10-10T16:42:07.663036", "exception": false, "start_time": "2021-10-10T16:42:07.596190", "status": "completed"}, "tags": []}, "source": ["Next, we implement a small function to encode all images in our datasets.\n", "The output representations are then used as inputs to the Logistic Regression model."]}, {"cell_type": "code", "execution_count": 14, "id": "08b87e56", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:42:07.810979Z", "iopub.status.busy": "2021-10-10T16:42:07.810493Z", "iopub.status.idle": "2021-10-10T16:42:07.812587Z", "shell.execute_reply": "2021-10-10T16:42:07.812116Z"}, "papermill": {"duration": 0.049258, "end_time": "2021-10-10T16:42:07.812757", "exception": false, "start_time": "2021-10-10T16:42:07.763499", "status": "completed"}, "tags": []}, "outputs": [], "source": ["@torch.no_grad()\n", "def prepare_data_features(model, dataset):\n", " # Prepare model\n", " network = deepcopy(model.convnet)\n", " network.fc = nn.Identity() # Removing projection head g(.)\n", " network.eval()\n", " network.to(device)\n", "\n", " # Encode all images\n", " data_loader = data.DataLoader(dataset, batch_size=64, num_workers=NUM_WORKERS, shuffle=False, drop_last=False)\n", " feats, labels = [], []\n", " for batch_imgs, batch_labels in tqdm(data_loader):\n", " batch_imgs = batch_imgs.to(device)\n", " batch_feats = network(batch_imgs)\n", " feats.append(batch_feats.detach().cpu())\n", " labels.append(batch_labels)\n", "\n", " feats = torch.cat(feats, dim=0)\n", " labels = torch.cat(labels, dim=0)\n", "\n", " # Sort images by labels\n", " labels, idxs = labels.sort()\n", " feats = feats[idxs]\n", "\n", " return data.TensorDataset(feats, labels)"]}, {"cell_type": "markdown", "id": "f1e62cc6", "metadata": {"papermill": {"duration": 0.10389, "end_time": "2021-10-10T16:42:08.012147", "exception": false, "start_time": "2021-10-10T16:42:07.908257", "status": "completed"}, "tags": []}, "source": ["Let's apply the function to both training and test set below."]}, {"cell_type": "code", "execution_count": 15, "id": "274643eb", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:42:08.330518Z", "iopub.status.busy": "2021-10-10T16:42:08.330044Z", "iopub.status.idle": "2021-10-10T16:42:14.259714Z", "shell.execute_reply": "2021-10-10T16:42:14.260561Z"}, "papermill": {"duration": 5.994866, "end_time": "2021-10-10T16:42:14.260713", "exception": false, "start_time": "2021-10-10T16:42:08.265847", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "32cad3ea3e3644baaa844883a715a613", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/79 [00:00\n", "\n", "\n", " \n", " \n", " \n", " \n", " 2021-10-10T18:42:16.456325\n", " image/svg+xml\n", " \n", " \n", " Matplotlib v3.4.3, https://matplotlib.org/\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", " \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", " \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", " \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", " \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", " \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"], "text/plain": ["
"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Test accuracy for 10 images per label: 62.79%\n", "Test accuracy for 20 images per label: 68.60%\n", "Test accuracy for 50 images per label: 74.44%\n", "Test accuracy for 100 images per label: 77.20%\n", "Test accuracy for 200 images per label: 79.06%\n", "Test accuracy for 500 images per label: 81.33%\n"]}], "source": ["dataset_sizes = sorted(k for k in results)\n", "test_scores = [results[k][\"test\"] for k in dataset_sizes]\n", "\n", "fig = plt.figure(figsize=(6, 4))\n", "plt.plot(\n", " dataset_sizes,\n", " test_scores,\n", " \"--\",\n", " color=\"#000\",\n", " marker=\"*\",\n", " markeredgecolor=\"#000\",\n", " markerfacecolor=\"y\",\n", " markersize=16,\n", ")\n", "plt.xscale(\"log\")\n", "plt.xticks(dataset_sizes, labels=dataset_sizes)\n", "plt.title(\"STL10 classification over dataset size\", fontsize=14)\n", "plt.xlabel(\"Number of images per class\")\n", "plt.ylabel(\"Test accuracy\")\n", "plt.minorticks_off()\n", "plt.show()\n", "\n", "for k, score in zip(dataset_sizes, test_scores):\n", " print(f\"Test accuracy for {k:3d} images per label: {100*score:4.2f}%\")"]}, {"cell_type": "markdown", "id": "e6cb0f66", "metadata": {"papermill": {"duration": 0.049197, "end_time": "2021-10-10T16:42:16.746998", "exception": false, "start_time": "2021-10-10T16:42:16.697801", "status": "completed"}, "tags": []}, "source": ["As one would expect, the classification performance improves the more data we have.\n", "However, with only 10 images per class, we can already classify more than 60% of the images correctly.\n", "This is quite impressive, considering that the images are also higher dimensional than e.g. CIFAR10.\n", "With the full dataset, we achieve an accuracy of 81%.\n", "The increase between 50 to 500 images per class might suggest a linear increase in performance with an exponentially larger dataset.\n", "However, with even more data, we could also finetune $f(\\cdot)$ in the training process, allowing for the representations to adapt more to the specific classification task given.\n", "\n", "To set the results above into perspective, we will train the base\n", "network, a ResNet-18, on the classification task from scratch."]}, {"cell_type": "markdown", "id": "237fe863", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.048695, "end_time": "2021-10-10T16:42:16.844984", "exception": false, "start_time": "2021-10-10T16:42:16.796289", "status": "completed"}, "tags": []}, "source": ["## Baseline\n", "\n", "As a baseline to our results above, we will train a standard ResNet-18 with random initialization on the labeled training set of STL10.\n", "The results will give us an indication of the advantages that contrastive learning on unlabeled data has compared to using only supervised training.\n", "The implementation of the model is straightforward since the ResNet\n", "architecture is provided in the torchvision library."]}, {"cell_type": "code", "execution_count": 20, "id": "201725a2", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:42:16.952607Z", "iopub.status.busy": "2021-10-10T16:42:16.952127Z", "iopub.status.idle": "2021-10-10T16:42:16.954345Z", "shell.execute_reply": "2021-10-10T16:42:16.953881Z"}, "papermill": {"duration": 0.059107, "end_time": "2021-10-10T16:42:16.954448", "exception": false, "start_time": "2021-10-10T16:42:16.895341", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ResNet(pl.LightningModule):\n", " def __init__(self, num_classes, lr, weight_decay, max_epochs=100):\n", " super().__init__()\n", " self.save_hyperparameters()\n", " self.model = torchvision.models.resnet18(pretrained=False, num_classes=num_classes)\n", "\n", " def configure_optimizers(self):\n", " optimizer = optim.AdamW(self.parameters(), lr=self.hparams.lr, weight_decay=self.hparams.weight_decay)\n", " lr_scheduler = optim.lr_scheduler.MultiStepLR(\n", " optimizer, milestones=[int(self.hparams.max_epochs * 0.7), int(self.hparams.max_epochs * 0.9)], gamma=0.1\n", " )\n", " return [optimizer], [lr_scheduler]\n", "\n", " def _calculate_loss(self, batch, mode=\"train\"):\n", " imgs, labels = batch\n", " preds = self.model(imgs)\n", " loss = F.cross_entropy(preds, labels)\n", " acc = (preds.argmax(dim=-1) == labels).float().mean()\n", "\n", " self.log(mode + \"_loss\", loss)\n", " self.log(mode + \"_acc\", acc)\n", " return loss\n", "\n", " def training_step(self, batch, batch_idx):\n", " return self._calculate_loss(batch, mode=\"train\")\n", "\n", " def validation_step(self, batch, batch_idx):\n", " self._calculate_loss(batch, mode=\"val\")\n", "\n", " def test_step(self, batch, batch_idx):\n", " self._calculate_loss(batch, mode=\"test\")"]}, {"cell_type": "markdown", "id": "e6fe14f2", "metadata": {"papermill": {"duration": 0.049585, "end_time": "2021-10-10T16:42:17.052720", "exception": false, "start_time": "2021-10-10T16:42:17.003135", "status": "completed"}, "tags": []}, "source": ["It is clear that the ResNet easily overfits on the training data since its parameter count is more than 1000 times larger than the dataset size.\n", "To make the comparison to the contrastive learning models fair, we apply data augmentations similar to the ones we used before: horizontal flip, crop-and-resize, grayscale, and gaussian blur.\n", "Color distortions as before are not used because the color distribution of an image showed to be an important feature for the classification.\n", "Hence, we observed no noticeable performance gains when adding color distortions to the set of augmentations.\n", "Similarly, we restrict the resizing operation before cropping to the max.\n", "125% of its original resolution, instead of 1250% as done in SimCLR.\n", "This is because, for classification, the model needs to recognize the full object, while in contrastive learning, we only want to check whether two patches belong to the same image/object.\n", "Hence, the chosen augmentations below are overall weaker than in the contrastive learning case."]}, {"cell_type": "code", "execution_count": 21, "id": "3c7c2020", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:42:17.163394Z", "iopub.status.busy": "2021-10-10T16:42:17.162926Z", "iopub.status.idle": "2021-10-10T16:42:22.652111Z", "shell.execute_reply": "2021-10-10T16:42:22.652505Z"}, "papermill": {"duration": 5.551414, "end_time": "2021-10-10T16:42:22.652657", "exception": false, "start_time": "2021-10-10T16:42:17.101243", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}], "source": ["train_transforms = transforms.Compose(\n", " [\n", " transforms.RandomHorizontalFlip(),\n", " transforms.RandomResizedCrop(size=96, scale=(0.8, 1.0)),\n", " transforms.RandomGrayscale(p=0.2),\n", " transforms.GaussianBlur(kernel_size=9, sigma=(0.1, 0.5)),\n", " transforms.ToTensor(),\n", " transforms.Normalize((0.5,), (0.5,)),\n", " ]\n", ")\n", "\n", "train_img_aug_data = STL10(root=DATASET_PATH, split=\"train\", download=True, transform=train_transforms)"]}, {"cell_type": "markdown", "id": "318c1f64", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.053189, "end_time": "2021-10-10T16:42:22.755918", "exception": false, "start_time": "2021-10-10T16:42:22.702729", "status": "completed"}, "tags": []}, "source": ["The training function for the ResNet is almost identical to the Logistic Regression setup.\n", "Note that we allow the ResNet to perform validation every 2 epochs to\n", "also check whether the model overfits strongly in the first iterations\n", "or not."]}, {"cell_type": "code", "execution_count": 22, "id": "9f4db9de", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:42:22.863515Z", "iopub.status.busy": "2021-10-10T16:42:22.862996Z", "iopub.status.idle": "2021-10-10T16:42:22.865112Z", "shell.execute_reply": "2021-10-10T16:42:22.864714Z"}, "papermill": {"duration": 0.059991, "end_time": "2021-10-10T16:42:22.865213", "exception": false, "start_time": "2021-10-10T16:42:22.805222", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def train_resnet(batch_size, max_epochs=100, **kwargs):\n", " trainer = pl.Trainer(\n", " default_root_dir=os.path.join(CHECKPOINT_PATH, \"ResNet\"),\n", " gpus=1 if str(device) == \"cuda:0\" else 0,\n", " max_epochs=max_epochs,\n", " callbacks=[\n", " ModelCheckpoint(save_weights_only=True, mode=\"max\", monitor=\"val_acc\"),\n", " LearningRateMonitor(\"epoch\"),\n", " ],\n", " progress_bar_refresh_rate=1,\n", " check_val_every_n_epoch=2,\n", " )\n", " trainer.logger._default_hp_metric = None\n", "\n", " # Data loaders\n", " train_loader = data.DataLoader(\n", " train_img_aug_data,\n", " batch_size=batch_size,\n", " shuffle=True,\n", " drop_last=True,\n", " pin_memory=True,\n", " num_workers=NUM_WORKERS,\n", " )\n", " test_loader = data.DataLoader(\n", " test_img_data, batch_size=batch_size, shuffle=False, drop_last=False, pin_memory=True, num_workers=NUM_WORKERS\n", " )\n", "\n", " # Check whether pretrained model exists. If yes, load it and skip training\n", " pretrained_filename = os.path.join(CHECKPOINT_PATH, \"ResNet.ckpt\")\n", " if os.path.isfile(pretrained_filename):\n", " print(\"Found pretrained model at %s, loading...\" % pretrained_filename)\n", " model = ResNet.load_from_checkpoint(pretrained_filename)\n", " else:\n", " pl.seed_everything(42) # To be reproducable\n", " model = ResNet(**kwargs)\n", " trainer.fit(model, train_loader, test_loader)\n", " model = ResNet.load_from_checkpoint(trainer.checkpoint_callback.best_model_path)\n", "\n", " # Test best model on validation set\n", " train_result = trainer.test(model, test_dataloaders=train_loader, verbose=False)\n", " val_result = trainer.test(model, test_dataloaders=test_loader, verbose=False)\n", " result = {\"train\": train_result[0][\"test_acc\"], \"test\": val_result[0][\"test_acc\"]}\n", "\n", " return model, result"]}, {"cell_type": "markdown", "id": "f3781014", "metadata": {"papermill": {"duration": 0.049896, "end_time": "2021-10-10T16:42:22.965127", "exception": false, "start_time": "2021-10-10T16:42:22.915231", "status": "completed"}, "tags": []}, "source": ["Finally, let's train the model and check its results:"]}, {"cell_type": "code", "execution_count": 23, "id": "4f4caab0", "metadata": {"execution": {"iopub.execute_input": "2021-10-10T16:42:23.068310Z", "iopub.status.busy": "2021-10-10T16:42:23.066889Z", "iopub.status.idle": "2021-10-10T16:42:27.618125Z", "shell.execute_reply": "2021-10-10T16:42:27.618532Z"}, "papermill": {"duration": 4.604524, "end_time": "2021-10-10T16:42:27.618678", "exception": false, "start_time": "2021-10-10T16:42:23.014154", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Found pretrained model at saved_models/ContrastiveLearning/ResNet.ckpt, loading...\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:678: LightningDeprecationWarning: `trainer.test(test_dataloaders)` is deprecated in v1.4 and will be removed in v1.6. Use `trainer.test(dataloaders)` instead.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Missing logger folder: saved_models/ContrastiveLearning/ResNet/lightning_logs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:376: UserWarning: Your test_dataloader has `shuffle=True`, it is best practice to turn this off for val/test/predict dataloaders.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "0f6b628c8d4d45f9876bc31bc99904ed", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "d901ede9b3054a479a22fda21e763fea", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Accuracy on training set: 99.76%\n", "Accuracy on test set: 73.31%\n"]}], "source": ["resnet_model, resnet_result = train_resnet(batch_size=64, num_classes=10, lr=1e-3, weight_decay=2e-4, max_epochs=100)\n", "print(f\"Accuracy on training set: {100*resnet_result['train']:4.2f}%\")\n", "print(f\"Accuracy on test set: {100*resnet_result['test']:4.2f}%\")"]}, {"cell_type": "markdown", "id": "a98707ed", "metadata": {"papermill": {"duration": 0.05204, "end_time": "2021-10-10T16:42:27.724471", "exception": false, "start_time": "2021-10-10T16:42:27.672431", "status": "completed"}, "tags": []}, "source": ["The ResNet trained from scratch achieves 73.31% on the test set.\n", "This is almost 8% less than the contrastive learning model, and even slightly less than SimCLR achieves with 1/10 of the data.\n", "This shows that self-supervised, contrastive learning provides\n", "considerable performance gains by leveraging large amounts of unlabeled\n", "data when little labeled data is available."]}, {"cell_type": "markdown", "id": "333d9e8c", "metadata": {"papermill": {"duration": 0.051935, "end_time": "2021-10-10T16:42:27.828975", "exception": false, "start_time": "2021-10-10T16:42:27.777040", "status": "completed"}, "tags": []}, "source": ["## Conclusion\n", "\n", "In this tutorial, we have discussed self-supervised contrastive learning and implemented SimCLR as an example method.\n", "We have applied it to the STL10 dataset and showed that it can learn generalizable representations that we can use to train simple classification models.\n", "With 500 images per label, it achieved an 8% higher accuracy than a similar model solely trained from supervision and performs on par with it when only using a tenth of the labeled data.\n", "Our experimental results are limited to a single dataset, but recent works such as [Ting Chen et al. ](https://arxiv.org/abs/2006.10029) showed similar trends for larger datasets like ImageNet.\n", "Besides the discussed hyperparameters, the size of the model seems to be important in contrastive learning as well.\n", "If a lot of unlabeled data is available, larger models can achieve much stronger results and come close to their supervised baselines.\n", "Further, there are also approaches for combining contrastive and supervised learning, leading to performance gains beyond supervision (see [Khosla et al.](https://arxiv.org/abs/2004.11362)).\n", "Moreover, contrastive learning is not the only approach to self-supervised learning that has come up in the last two years and showed great results.\n", "Other methods include distillation-based methods like [BYOL](https://arxiv.org/abs/2006.07733) and redundancy reduction techniques like [Barlow Twins](https://arxiv.org/abs/2103.03230).\n", "There is a lot more to explore in the self-supervised domain, and more, impressive steps ahead are to be expected.\n", "\n", "### References\n", "\n", "[1] Chen, T., Kornblith, S., Norouzi, M., and Hinton, G. (2020).\n", "A simple framework for contrastive learning of visual representations.\n", "In International conference on machine learning (pp.\n", "1597-1607).\n", "PMLR.\n", "([link](https://arxiv.org/abs/2002.05709))\n", "\n", "[2] Chen, T., Kornblith, S., Swersky, K., Norouzi, M., and Hinton, G. (2020).\n", "Big self-supervised models are strong semi-supervised learners.\n", "NeurIPS 2021 ([link](https://arxiv.org/abs/2006.10029)).\n", "\n", "[3] Oord, A. V. D., Li, Y., and Vinyals, O.\n", "(2018).\n", "Representation learning with contrastive predictive coding.\n", "arXiv preprint arXiv:1807.03748.\n", "([link](https://arxiv.org/abs/1807.03748))\n", "\n", "[4] Grill, J.B., Strub, F., Altch\u00e9, F., Tallec, C., Richemond, P.H., Buchatskaya, E., Doersch, C., Pires, B.A., Guo, Z.D., Azar, M.G.\n", "and Piot, B.\n", "(2020).\n", "Bootstrap your own latent: A new approach to self-supervised learning.\n", "arXiv preprint arXiv:2006.07733.\n", "([link](https://arxiv.org/abs/2006.07733))\n", "\n", "[5] Khosla, P., Teterwak, P., Wang, C., Sarna, A., Tian, Y., Isola, P., Maschinot, A., Liu, C. and Krishnan, D. (2020).\n", "Supervised contrastive learning.\n", "arXiv preprint arXiv:2004.11362.\n", "([link](https://arxiv.org/abs/2004.11362))\n", "\n", "[6] Zbontar, J., Jing, L., Misra, I., LeCun, Y. and Deny, S. (2021).\n", "Barlow twins: Self-supervised learning via redundancy reduction.\n", "arXiv preprint arXiv:2103.03230.\n", "([link](https://arxiv.org/abs/2103.03230))"]}, {"cell_type": "markdown", "id": "ce384bac", "metadata": {"papermill": {"duration": 0.052469, "end_time": "2021-10-10T16:42:27.933096", "exception": false, "start_time": "2021-10-10T16:42:27.880627", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "![Pytorch Lightning](data:image/png;base64,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MERFOjYxNjMxNjY4IiBkYXRhLXBqYXgtdHJhbnNpZW50PSJ0cnVlIi8+PG1ldGEgbmFtZT0iaHRtbC1zYWZlLW5vbmNlIiBjb250ZW50PSJlODU4ZjJjYzQ3YTJhNGY1NTFhZjQ3MmVhZDBhMzEwMjFiYTAwYmMyMTk5ZjYyYWE0NDJkMGJiZjFkMDNmYjAxIiBkYXRhLXBqYXgtdHJhbnNpZW50PSJ0cnVlIi8+PG1ldGEgbmFtZT0idmlzaXRvci1wYXlsb2FkIiBjb250ZW50PSJleUp5WldabGNuSmxjaUk2SWlJc0luSmxjWFZsYzNSZmFXUWlPaUl3UXpBMU9qTXdOek02TVRBM09ERTBSam94T1RFNU1FUkZPall4TmpNeE5qWTRJaXdpZG1semFYUnZjbDlwWkNJNklqRXdPREEwT0Rnd09UTTVOelEwTmpneU1EQWlMQ0p5WldkcGIyNWZaV1JuWlNJNkltbGhaQ0lzSW5KbFoybHZibDl5Wlc1a1pYSWlPaUpwWVdRaWZRPT0iIGRhdGEtcGpheC10cmFuc2llbnQ9InRydWUiLz48bWV0YSBuYW1lPSJ2aXNpdG9yLWhtYWMiIGNvbnRlbnQ9IjVlOGU0YzNkZDlmYjVmMDExMWViNTAxMzJlMjJlZjg0MDJkZmZlM2ViNGU2ZGNkOThkNmFkMWNkZDU3MWRjNzgiIGRhdGEtcGpheC10cmFuc2llbnQ9InRydWUiLz7sPWl32ziS3/MruOrt3t3XocT7SGzPcxznmDixJ3bSx8w8LkRCEmOKZPOwrZ7X/32rAFKkKMmkDufonemMSYJAoVComwD06JEA/zuY0owIIZnSw94kuqGJSxJPTPPhJ+pmYkbGPcGNwoyG2WEvoXGU+lmUzJ7IpmUohqlIPcEjGRHjT+ROzBISpj5UPXr06NEi6LGfTfKheE1nw4h1MImSzM2zdCX4x2mUJy4V3cijKzs47GVJDq8GR9hRs7OUBoA89cTAD697wg0JcsrBOxzwOqSbBBlH0TigIiBFRSCNP/JdkvlRWEPala/z5+Ib5dXrjy8MVQ9msZv+dBE91679INLFT8fO7bMXsystf/tL76hJlHbwb670cWpNpNsbQsZnb44//vSbNRzSd7fhr7++kRP557/N0p/Vuzw5+7AN+F9/n3ycnb64HZq3KpXE/Pwq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){height=\"60px\" width=\"240px\"}"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Tutorial 13: Self-Supervised Contrastive Learning with SimCLR\n", " :card_description: In this tutorial, we will take a closer look at self-supervised contrastive learning. Self-supervised learning, or also sometimes called unsupervised learning, describes the...\n", " :tags: Image,Self-Supervised,Contrastive-Learning,GPU/TPU,UvA-DL-Course\n", " :image: _static/images/course_UvA-DL/13-contrastive-learning.jpg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab_type,colab,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 249.204813, "end_time": "2021-10-10T16:42:28.695919", "environment_variables": {}, "exception": null, "input_path": "course_UvA-DL/13-contrastive-learning/SimCLR.ipynb", "output_path": ".notebooks/course_UvA-DL/13-contrastive-learning.ipynb", "parameters": {}, "start_time": "2021-10-10T16:38:19.491106", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"014eb2b9e9f84518838020b1995c1b8d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b2fc0cc8b5aa4c0c8b7c6ed61e30e1a1", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_06e3324339554e7d8653dd5ca34502c3", "value": 1.0}}, "06e3324339554e7d8653dd5ca34502c3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "0f6b628c8d4d45f9876bc31bc99904ed": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_574e12bb3361469f9592f0b1e3ea131c", "IPY_MODEL_17ae240504aa41a69aac2f89637a872f", "IPY_MODEL_3c71b91bf99a44088d998afcceba0c5a"], "layout": "IPY_MODEL_c9f6fa6ff0a4421bbbebceb0868d6f72"}}, "17ae240504aa41a69aac2f89637a872f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9815469b386b421aa09d7a7c435486d6", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_48df5e7e12f14d529ee8253b7e81c15d", "value": 1.0}}, "1ba6707c32324d89a25d94b8826ad3fb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "261190710d154e79846a4b2be71408ed": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "262c7a46de2548a1bb2c1bd654bae044": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5dc49442ae4b4da1b8883f9729335464", "placeholder": "\u200b", "style": "IPY_MODEL_6fa3b6f47f774f06911972e497cbaa80", "value": " 79/79 [00:01<00:00, 80.29it/s]"}}, "29f4053f18004b2aa3ad1439d2b93651": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "2e223ff880144417a99a792b90424c4e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "32cad3ea3e3644baaa844883a715a613": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_8ac65a9e9dbc4e9b84ae76dbf7ba131e", "IPY_MODEL_ac28c76d49c141fb834867a4cee9bb41", "IPY_MODEL_262c7a46de2548a1bb2c1bd654bae044"], "layout": "IPY_MODEL_4d296fe6da9a4f4fbc71862dd3aef6a8"}}, "3329a069de5d4624a9412647f0d80198": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_492d73828f01466f9f1aad76d13d0217", "IPY_MODEL_b7093bf46de847f79cd6d83a8bca17be", "IPY_MODEL_dad5ea58db1c4416bebfd763d88c5c70"], "layout": "IPY_MODEL_261190710d154e79846a4b2be71408ed"}}, "3c71b91bf99a44088d998afcceba0c5a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b769c6c21b5b4aeba81b70f8ac423d79", "placeholder": "\u200b", "style": "IPY_MODEL_5a9c689a271f4e35b11302006704a498", "value": " 78/78 [00:02<00:00, 35.66it/s]"}}, "3de31fdecbe4407286cc95695466de5d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d87935149db141678e50b111ed61ec82", "placeholder": "\u200b", "style": "IPY_MODEL_98e77a3d53234245ab2a62d09bd5ddef", "value": " 2640397312/? [02:43<00:00, 17274904.03it/s]"}}, "48df5e7e12f14d529ee8253b7e81c15d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "492d73828f01466f9f1aad76d13d0217": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d56f70bb6f13477e9412473f278615c1", "placeholder": "\u200b", "style": "IPY_MODEL_f8d33dc2ecbe4476bd02a6b12d40d79b", "value": "100%"}}, "4b5ff77984894ccbb8ae6abd263a4b7c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4d296fe6da9a4f4fbc71862dd3aef6a8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "57424a7cd3c7417dae1073397b010b1c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "574e12bb3361469f9592f0b1e3ea131c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1ba6707c32324d89a25d94b8826ad3fb", "placeholder": "\u200b", "style": "IPY_MODEL_57424a7cd3c7417dae1073397b010b1c", "value": "Testing: 100%"}}, "5a9c689a271f4e35b11302006704a498": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5c036d43b260499385714d1317f05efe": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5d2b7d0577694436ade46306715f91de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "5dc49442ae4b4da1b8883f9729335464": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6fa3b6f47f774f06911972e497cbaa80": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "721cee3a7bed406c8050c8433a106386": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "762e3668c79e48839269b8d565098578": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "84c4574fe0744cdf837bcc9c3032acfc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8ac65a9e9dbc4e9b84ae76dbf7ba131e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2e223ff880144417a99a792b90424c4e", "placeholder": "\u200b", "style": "IPY_MODEL_721cee3a7bed406c8050c8433a106386", "value": "100%"}}, "8c91c107ddbc4f84a5a8b5e583c69773": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "979d4939c8c04651bfd4a96e20d4d508": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "97fdda0f484c4954804bd2c4ff8d013a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_be6a5b00c73049329a585c53e6eb481a", "placeholder": "\u200b", "style": "IPY_MODEL_762e3668c79e48839269b8d565098578", "value": " 125/125 [00:01<00:00, 106.26it/s]"}}, "9815469b386b421aa09d7a7c435486d6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "98e77a3d53234245ab2a62d09bd5ddef": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ac28c76d49c141fb834867a4cee9bb41": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8c91c107ddbc4f84a5a8b5e583c69773", "max": 79.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_5d2b7d0577694436ade46306715f91de", "value": 79.0}}, "b2a75df791794c5cbf5d75ccfd368544": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d174cfe6f91a431fbedf6fe767738f26", "placeholder": "\u200b", "style": "IPY_MODEL_84c4574fe0744cdf837bcc9c3032acfc", "value": ""}}, "b2fc0cc8b5aa4c0c8b7c6ed61e30e1a1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b7093bf46de847f79cd6d83a8bca17be": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_979d4939c8c04651bfd4a96e20d4d508", "max": 125.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_c967a959d2e54813af244ce923b9bf70", "value": 125.0}}, "b769c6c21b5b4aeba81b70f8ac423d79": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "be6a5b00c73049329a585c53e6eb481a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c111f74da90d4a8f831df4733df12b94": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b2a75df791794c5cbf5d75ccfd368544", "IPY_MODEL_dd15111b76d447d783d02a4ea9a5dbb2", "IPY_MODEL_3de31fdecbe4407286cc95695466de5d"], "layout": "IPY_MODEL_ef6374a810a14d32a08759170c9d66d1"}}, "c6548cbf34cb4f5f88108f9f7ac77fb4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c967a959d2e54813af244ce923b9bf70": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "c9f6fa6ff0a4421bbbebceb0868d6f72": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "d174cfe6f91a431fbedf6fe767738f26": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d56f70bb6f13477e9412473f278615c1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d87935149db141678e50b111ed61ec82": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d901ede9b3054a479a22fda21e763fea": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_f56016fba5604d9db49374c9af2829e1", "IPY_MODEL_014eb2b9e9f84518838020b1995c1b8d", "IPY_MODEL_97fdda0f484c4954804bd2c4ff8d013a"], "layout": "IPY_MODEL_e8120e786bbe4d56aed3a534970c020d"}}, "dad5ea58db1c4416bebfd763d88c5c70": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4b5ff77984894ccbb8ae6abd263a4b7c", "placeholder": "\u200b", "style": "IPY_MODEL_ef38b8535cdf4ddfba9ced74f238ab28", "value": " 125/125 [00:01<00:00, 77.70it/s]"}}, "dd15111b76d447d783d02a4ea9a5dbb2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e2187c1fd5554b5c89ebf37667b20858", "max": 2640397119.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_29f4053f18004b2aa3ad1439d2b93651", "value": 2640397119.0}}, "e2187c1fd5554b5c89ebf37667b20858": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e8120e786bbe4d56aed3a534970c020d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "ef38b8535cdf4ddfba9ced74f238ab28": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ef6374a810a14d32a08759170c9d66d1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f56016fba5604d9db49374c9af2829e1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5c036d43b260499385714d1317f05efe", "placeholder": "\u200b", "style": "IPY_MODEL_c6548cbf34cb4f5f88108f9f7ac77fb4", "value": "Testing: 100%"}}, "f8d33dc2ecbe4476bd02a6b12d40d79b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/augmentation_kornia.ipynb b/source/notebooks/lightning_examples/augmentation_kornia.ipynb deleted file mode 100644 index e9c2340..0000000 --- a/source/notebooks/lightning_examples/augmentation_kornia.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "38906077", "metadata": {"papermill": {"duration": 0.034946, "end_time": "2021-12-04T16:20:10.519350", "exception": false, "start_time": "2021-12-04T16:20:10.484404", "status": "completed"}, "tags": []}, "source": ["\n", "# GPU and batched data augmentation with Kornia and PyTorch-Lightning\n", "\n", "* **Author:** PL/Kornia team\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:52:56.657983\n", "\n", "In this tutorial we will show how to combine both Kornia.org and PyTorch Lightning\n", "to perform efficient data augmentation to train a simpple model using the GPU in batch\n", "mode without additional effort.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/augmentation_kornia.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "8ab0f89d", "metadata": {"papermill": {"duration": 0.031285, "end_time": "2021-12-04T16:20:10.583397", "exception": false, "start_time": "2021-12-04T16:20:10.552112", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "1dde05cd", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T16:20:10.652401Z", "iopub.status.busy": "2021-12-04T16:20:10.651932Z", "iopub.status.idle": "2021-12-04T16:20:13.540277Z", "shell.execute_reply": "2021-12-04T16:20:13.539730Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.926089, "end_time": "2021-12-04T16:20:13.540428", "exception": false, "start_time": "2021-12-04T16:20:10.614339", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"pytorch-lightning\" \"pandas\" \"pytorch-lightning>=1.3\" \"torchvision\" \"matplotlib\" \"torchmetrics\" \"kornia\" \"torchmetrics>=0.3\" \"torch>=1.6, <1.9\""]}, {"cell_type": "code", "execution_count": 2, "id": "bb66b32f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:20:13.609472Z", "iopub.status.busy": "2021-12-04T16:20:13.609002Z", "iopub.status.idle": "2021-12-04T16:20:15.512338Z", "shell.execute_reply": "2021-12-04T16:20:15.511878Z"}, "papermill": {"duration": 1.939508, "end_time": "2021-12-04T16:20:15.512472", "exception": false, "start_time": "2021-12-04T16:20:13.572964", "status": "completed"}, "tags": []}, "outputs": [], "source": ["import os\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pandas as pd\n", "import torch\n", "import torch.nn as nn\n", "import torchmetrics\n", "import torchvision\n", "from kornia import image_to_tensor, tensor_to_image\n", "from kornia.augmentation import ColorJitter, RandomChannelShuffle, RandomHorizontalFlip, RandomThinPlateSpline\n", "from pytorch_lightning import LightningModule, Trainer\n", "from pytorch_lightning.loggers import CSVLogger\n", "from torch import Tensor\n", "from torch.nn import functional as F\n", "from torch.utils.data import DataLoader\n", "from torchvision.datasets import CIFAR10\n", "\n", "AVAIL_GPUS = min(1, torch.cuda.device_count())"]}, {"cell_type": "markdown", "id": "4984c138", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.033859, "end_time": "2021-12-04T16:20:15.578656", "exception": false, "start_time": "2021-12-04T16:20:15.544797", "status": "completed"}, "tags": []}, "source": ["## Define Data Augmentations module\n", "\n", "[Kornia.org](https://www.kornia.org) is low level Computer Vision library that provides a dedicated module\n", "[`kornia.augmentation`](https://kornia.readthedocs.io/en/latest/augmentation.html) module implementing\n", "en extensive set of data augmentation techniques for image and video.\n", "\n", "Similar to Lightning, in Kornia it's promoted to encapsulate functionalities inside classes for readability\n", "and efficiency purposes. In this case, we define a data augmentaton pipeline subclassing a `nn.Module`\n", "where the augmentation_kornia (also subclassing `nn.Module`) are combined with other PyTorch components\n", "such as `nn.Sequential`.\n", "\n", "Checkout the different augmentation operators in Kornia docs and experiment yourself !"]}, {"cell_type": "code", "execution_count": 3, "id": "62adfd08", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:20:15.647672Z", "iopub.status.busy": "2021-12-04T16:20:15.647178Z", "iopub.status.idle": "2021-12-04T16:20:15.649220Z", "shell.execute_reply": "2021-12-04T16:20:15.648758Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.039378, "end_time": "2021-12-04T16:20:15.649322", "exception": false, "start_time": "2021-12-04T16:20:15.609944", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class DataAugmentation(nn.Module):\n", " \"\"\"Module to perform data augmentation using Kornia on torch tensors.\"\"\"\n", "\n", " def __init__(self, apply_color_jitter: bool = False) -> None:\n", " super().__init__()\n", " self._apply_color_jitter = apply_color_jitter\n", "\n", " self.transforms = nn.Sequential(\n", " RandomHorizontalFlip(p=0.75),\n", " RandomChannelShuffle(p=0.75),\n", " RandomThinPlateSpline(p=0.75),\n", " )\n", "\n", " self.jitter = ColorJitter(0.5, 0.5, 0.5, 0.5)\n", "\n", " @torch.no_grad() # disable gradients for effiency\n", " def forward(self, x: Tensor) -> Tensor:\n", " x_out = self.transforms(x) # BxCxHxW\n", " if self._apply_color_jitter:\n", " x_out = self.jitter(x_out)\n", " return x_out"]}, {"cell_type": "markdown", "id": "c57c4fbe", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.030806, "end_time": "2021-12-04T16:20:15.711761", "exception": false, "start_time": "2021-12-04T16:20:15.680955", "status": "completed"}, "tags": []}, "source": ["## Define a Pre-processing module\n", "\n", "In addition to the `DataAugmentation` modudle that will sample random parameters during the training stage,\n", "we define a `Preprocess` class to handle the conversion of the image type to properly work with `Tensor`.\n", "\n", "For this example we use `torchvision` CIFAR10 which return samples of `PIL.Image`, however,\n", "to take all the advantages of PyTorch and Kornia we need to cast the images into tensors.\n", "\n", "To do that we will use `kornia.image_to_tensor` which casts and permutes the images in the right format."]}, {"cell_type": "code", "execution_count": 4, "id": "55e81122", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:20:15.778048Z", "iopub.status.busy": "2021-12-04T16:20:15.777585Z", "iopub.status.idle": "2021-12-04T16:20:15.779274Z", "shell.execute_reply": "2021-12-04T16:20:15.779646Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.036947, "end_time": "2021-12-04T16:20:15.779757", "exception": false, "start_time": "2021-12-04T16:20:15.742810", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Preprocess(nn.Module):\n", " \"\"\"Module to perform pre-process using Kornia on torch tensors.\"\"\"\n", "\n", " @torch.no_grad() # disable gradients for effiency\n", " def forward(self, x) -> Tensor:\n", " x_tmp: np.ndarray = np.array(x) # HxWxC\n", " x_out: Tensor = image_to_tensor(x_tmp, keepdim=True) # CxHxW\n", " return x_out.float() / 255.0"]}, {"cell_type": "markdown", "id": "d16cb9f3", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.03102, "end_time": "2021-12-04T16:20:15.841921", "exception": false, "start_time": "2021-12-04T16:20:15.810901", "status": "completed"}, "tags": []}, "source": ["## Define PyTorch Lightning model\n", "\n", "The next step is to define our `LightningModule` to have a proper organisation of our training pipeline.\n", "This is a simple example just to show how to structure your baseline to be used as a reference,\n", "do not expect a high performance.\n", "\n", "Notice that the `Preprocess` class is injected into the dataset and will be applied per sample.\n", "\n", "The interesting part in the proposed approach happens inside the `training_step` where with just a single\n", "line of code we apply the data augmentation in batch and no need to worry about the device.\n", "This means that our `DataAugmentation` pipeline will automatically executed in the GPU."]}, {"cell_type": "code", "execution_count": 5, "id": "fb8ce79c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:20:15.915783Z", "iopub.status.busy": "2021-12-04T16:20:15.915307Z", "iopub.status.idle": "2021-12-04T16:20:15.917345Z", "shell.execute_reply": "2021-12-04T16:20:15.916889Z"}, "papermill": {"duration": 0.044237, "end_time": "2021-12-04T16:20:15.917441", "exception": false, "start_time": "2021-12-04T16:20:15.873204", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class CoolSystem(LightningModule):\n", " def __init__(self):\n", " super().__init__()\n", " # not the best model: expereiment yourself\n", " self.model = torchvision.models.resnet18(pretrained=True)\n", "\n", " self.preprocess = Preprocess() # per sample transforms\n", "\n", " self.transform = DataAugmentation() # per batch augmentation_kornia\n", "\n", " self.accuracy = torchmetrics.Accuracy()\n", "\n", " def forward(self, x):\n", " return F.softmax(self.model(x))\n", "\n", " def compute_loss(self, y_hat, y):\n", " return F.cross_entropy(y_hat, y)\n", "\n", " def show_batch(self, win_size=(10, 10)):\n", " def _to_vis(data):\n", " return tensor_to_image(torchvision.utils.make_grid(data, nrow=8))\n", "\n", " # get a batch from the training set: try with `val_datlaoader` :)\n", " imgs, labels = next(iter(self.train_dataloader()))\n", " imgs_aug = self.transform(imgs) # apply transforms\n", " # use matplotlib to visualize\n", " plt.figure(figsize=win_size)\n", " plt.imshow(_to_vis(imgs))\n", " plt.figure(figsize=win_size)\n", " plt.imshow(_to_vis(imgs_aug))\n", "\n", " def training_step(self, batch, batch_idx):\n", " x, y = batch\n", " x_aug = self.transform(x) # => we perform GPU/Batched data augmentation\n", " y_hat = self(x_aug)\n", " loss = self.compute_loss(y_hat, y)\n", " self.log(\"train_loss\", loss, prog_bar=False)\n", " self.log(\"train_acc\", self.accuracy(y_hat, y), prog_bar=False)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " x, y = batch\n", " y_hat = self(x)\n", " loss = self.compute_loss(y_hat, y)\n", " self.log(\"valid_loss\", loss, prog_bar=False)\n", " self.log(\"valid_acc\", self.accuracy(y_hat, y), prog_bar=True)\n", "\n", " def configure_optimizers(self):\n", " optimizer = torch.optim.AdamW(self.model.parameters(), lr=1e-4)\n", " scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, self.trainer.max_epochs, 0)\n", " return [optimizer], [scheduler]\n", "\n", " def prepare_data(self):\n", " CIFAR10(os.getcwd(), train=True, download=True, transform=self.preprocess)\n", " CIFAR10(os.getcwd(), train=False, download=True, transform=self.preprocess)\n", "\n", " def train_dataloader(self):\n", " dataset = CIFAR10(os.getcwd(), train=True, download=True, transform=self.preprocess)\n", " loader = DataLoader(dataset, batch_size=32)\n", " return loader\n", "\n", " def val_dataloader(self):\n", " dataset = CIFAR10(os.getcwd(), train=True, download=True, transform=self.preprocess)\n", " loader = DataLoader(dataset, batch_size=32)\n", " return loader"]}, {"cell_type": "markdown", "id": "e62c886f", "metadata": {"papermill": {"duration": 0.031204, "end_time": "2021-12-04T16:20:15.980101", "exception": false, "start_time": "2021-12-04T16:20:15.948897", "status": "completed"}, "tags": []}, "source": ["## Visualize images"]}, {"cell_type": "code", "execution_count": 6, "id": "ee50f76c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:20:16.046489Z", "iopub.status.busy": "2021-12-04T16:20:16.046020Z", "iopub.status.idle": "2021-12-04T16:20:17.118504Z", "shell.execute_reply": "2021-12-04T16:20:17.117951Z"}, "papermill": {"duration": 1.107265, "end_time": "2021-12-04T16:20:17.118657", "exception": false, "start_time": "2021-12-04T16:20:16.011392", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Downloading: \"https://download.pytorch.org/models/resnet18-5c106cde.pth\" to /home/AzDevOps_azpcontainer/.cache/torch/hub/checkpoints/resnet18-5c106cde.pth\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "bb46bed597e04aa68d65fa7583f0b2fb", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0.00/44.7M [00:00"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"data": {"image/png": "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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["model.show_batch(win_size=(14, 14))"]}, {"cell_type": "markdown", "id": "2a274076", "metadata": {"papermill": {"duration": 0.056571, "end_time": "2021-12-04T16:20:22.338599", "exception": false, "start_time": "2021-12-04T16:20:22.282028", "status": "completed"}, "tags": []}, "source": ["## Run training"]}, {"cell_type": "code", "execution_count": 8, "id": "8ce9aa4c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:20:22.459678Z", "iopub.status.busy": "2021-12-04T16:20:22.459198Z", "iopub.status.idle": "2021-12-04T16:30:59.810226Z", "shell.execute_reply": "2021-12-04T16:30:59.809783Z"}, "papermill": {"duration": 637.415988, "end_time": "2021-12-04T16:30:59.810364", "exception": false, "start_time": "2021-12-04T16:20:22.394376", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=20)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "------------------------------------------------\n", "0 | model | ResNet | 11.7 M\n", "1 | preprocess | Preprocess | 0 \n", "2 | transform | DataAugmentation | 0 \n", "3 | accuracy | Accuracy | 0 \n", "------------------------------------------------\n", "11.7 M Trainable params\n", "0 Non-trainable params\n", "11.7 M Total params\n", "46.758 Total estimated model params size (MB)\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "bc56a439a8c747419a9cfce366960df9", "version_major": 2, "version_minor": 0}, "text/plain": ["Validation sanity check: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, val_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n", "/tmp/ipykernel_2475/711885801.py:14: UserWarning: Implicit dimension choice for softmax has been deprecated. Change the call to include dim=X as an argument.\n", " return F.softmax(self.model(x))\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, train_dataloader, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "d77d420abcbf47c0923ae5c54c44fa93", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b969d392aae3406f98244c05ffc71752", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "aef85224023d4c17bd6beef036b3bc95", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "0eed8fb3fe1a47308f90660740da3708", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "5ab4ced2d2af4d8b99579329b6d08851", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b76ee81572514e77b560271a3c505645", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "efee6a19f778444e987cf2f8b9c9e709", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "eb4d88a3e74c4b8286414f889c809850", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "26a0a012ab2a4accac6df52663a5c3b3", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "3e467637b02b4cfd85562b3f1c5b58e8", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "558464e277d24562a26ef9696c7bad0d", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Initialize a trainer\n", "trainer = Trainer(\n", " progress_bar_refresh_rate=20,\n", " gpus=AVAIL_GPUS,\n", " max_epochs=10,\n", " logger=CSVLogger(save_dir=\"logs/\", name=\"cifar10-resnet18\"),\n", ")\n", "\n", "# Train the model \u26a1\n", "trainer.fit(model)"]}, {"cell_type": "markdown", "id": "842de94d", "metadata": {"papermill": {"duration": 0.074854, "end_time": "2021-12-04T16:30:59.963213", "exception": false, "start_time": "2021-12-04T16:30:59.888359", "status": "completed"}, "tags": []}, "source": ["### Visualize the training results"]}, {"cell_type": "code", "execution_count": 9, "id": "6e8ce374", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:00.120257Z", "iopub.status.busy": "2021-12-04T16:31:00.119802Z", "iopub.status.idle": "2021-12-04T16:31:00.391029Z", "shell.execute_reply": "2021-12-04T16:31:00.390630Z"}, "papermill": {"duration": 0.352406, "end_time": "2021-12-04T16:31:00.391154", "exception": false, "start_time": "2021-12-04T16:31:00.038748", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": [" train_loss train_acc epoch step valid_loss valid_acc\n", "0 6.785918 0.12500 0 49 NaN NaN\n", "1 6.782912 0.12500 0 99 NaN NaN\n", "2 6.770435 0.15625 0 149 NaN NaN\n", "3 6.690282 0.21875 0 199 NaN NaN\n", "4 6.782069 0.15625 0 249 NaN NaN\n"]}, {"data": {"text/plain": [""]}, "execution_count": 9, "metadata": {}, "output_type": "execute_result"}, {"data": {"image/png": "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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}, {"data": {"image/png": "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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["metrics = pd.read_csv(f\"{trainer.logger.log_dir}/metrics.csv\")\n", "print(metrics.head())\n", "\n", "aggreg_metrics = []\n", "agg_col = \"epoch\"\n", "for i, dfg in metrics.groupby(agg_col):\n", " agg = dict(dfg.mean())\n", " agg[agg_col] = i\n", " aggreg_metrics.append(agg)\n", "\n", "df_metrics = pd.DataFrame(aggreg_metrics)\n", "df_metrics[[\"train_loss\", \"valid_loss\"]].plot(grid=True, legend=True)\n", "df_metrics[[\"valid_acc\", \"train_acc\"]].plot(grid=True, legend=True)"]}, {"cell_type": "markdown", "id": "d8c0ecdf", "metadata": {"papermill": {"duration": 0.082158, "end_time": "2021-12-04T16:31:00.555481", "exception": false, "start_time": "2021-12-04T16:31:00.473323", "status": "completed"}, "tags": []}, "source": ["## Tensorboard"]}, {"cell_type": "code", "execution_count": 10, "id": "8a67c1e5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:00.720165Z", "iopub.status.busy": "2021-12-04T16:31:00.719717Z", "iopub.status.idle": "2021-12-04T16:31:00.721655Z", "shell.execute_reply": "2021-12-04T16:31:00.721209Z"}, "papermill": {"duration": 0.086419, "end_time": "2021-12-04T16:31:00.721760", "exception": false, "start_time": "2021-12-04T16:31:00.635341", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Start tensorboard.\n", "# %load_ext tensorboard\n", "# %tensorboard --logdir lightning_logs/"]}, {"cell_type": "markdown", "id": "d0cdd5cf", "metadata": {"papermill": {"duration": 0.079729, "end_time": "2021-12-04T16:31:00.881954", "exception": false, "start_time": "2021-12-04T16:31:00.802225", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: GPU and batched data augmentation with Kornia and PyTorch-Lightning\n", " :card_description: In this tutorial we will show how to combine both Kornia.org and PyTorch Lightning to perform efficient data augmentation to train a simpple model using the GPU in batch mode...\n", " :tags: Image,GPU/TPU,Lightning-Examples\n", " :image: _static/images/lightning_examples/augmentation_kornia.svg"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab,colab_type,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 652.68493, "end_time": "2021-12-04T16:31:01.876440", "environment_variables": {}, "exception": null, "input_path": "lightning_examples/augmentation_kornia/augmentation.ipynb", "output_path": ".notebooks/lightning_examples/augmentation_kornia.ipynb", "parameters": {}, "start_time": "2021-12-04T16:20:09.191510", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"00648a26dbda4fd5a46e11eb2de65394": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_046d7b6ec7ae4b7b835b38406ef12b7b", "max": 3126.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_f91e7c90408b4d189703088295affe7e", "value": 3126.0}}, "00ae340e2c934a289981b0fa1320a63c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "00c0ff7fb0d14a24a91f72f2261fe411": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "046d7b6ec7ae4b7b835b38406ef12b7b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0963635c7a054aa6b3d78746adca575e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_dcaea45e34e741b6a90232516d5e5cc9", "placeholder": "\u200b", "style": "IPY_MODEL_f9b913913c4b4491ac3bcf7fa35384ef", "value": "Validating: 100%"}}, "0d3841271e604be7b572f89d155537fa": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_00c0ff7fb0d14a24a91f72f2261fe411", "placeholder": "\u200b", "style": "IPY_MODEL_3219d2bbd48b4a268d7a9e1c28768e37", "value": " 1560/1563 [00:17<00:00, 89.54it/s]"}}, "0ed93a7a201f484eaa6f8d7246a5038f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6af962b59cad4dba940f908fe07b961b", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_54adbc53f7f14af5974fa706733bad2e", "value": 1563.0}}, "0eed8fb3fe1a47308f90660740da3708": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_70799825c1db415f98624df354200512", "IPY_MODEL_bb97a6ac530a452899a0323aa84cb8ea", "IPY_MODEL_0d3841271e604be7b572f89d155537fa"], "layout": "IPY_MODEL_9f16f1a321e54049ad9e4341550e5642"}}, "10a200370e054b249ecf1ea16ecf383f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "15d06f0222f441e58bb27b82be5b188f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "162bc591eff249078b9cc67ef718da0a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d621bcb9284e47e28c190e6a7781924d", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_6de03aba110e4897aad75f6052017fd1", "value": 1563.0}}, "195c56daec194f64a8a6c318adef0af0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "19e4d1afe39c4b3ca0f37b8851cc8707": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1a3a9cc456654328b7a7bf78905af9b3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a113958c0fe445a0b3227b4c41bb54cf", "max": 46827520.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_d12a329b34864edea9fb3c511ca062fe", "value": 46827520.0}}, "1c89b25cd44d42d8b6eb2c8937ac96fe": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_52d64155154f40509e85bb87d133e2c2", "placeholder": "\u200b", "style": "IPY_MODEL_b1f5934bc26f4f04a3eb235a87ec8d9c", "value": "Validating: 100%"}}, "1cc3e5803d3a4657bcdd8fc1d061c12b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1e560bd4592943dabe0b4f5535a76933": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "danger", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_579307f4c11b4a02a9ad16d735305b01", "max": 2.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_aca0852b47ca4a55a2e2104bf21e8e87", "value": 0.0}}, "231f4c199a8f458e8ca9cb5987a5bedb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "243cac8083774d7993f668e7e17cdb10": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "24987c3a0e9945e3aa4184de42cd8133": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "24e727294dec4d55b83d7d9bd834590f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "24ead37777974d36bede94e54e270620": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "26a0a012ab2a4accac6df52663a5c3b3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_59aad08c7f434fd3aa35c2bee4f73a95", "IPY_MODEL_2b9cdd139e6d45a4a185da07900faf2f", "IPY_MODEL_ff1843ce802446d587aa5d666f2fffb2"], "layout": "IPY_MODEL_10a200370e054b249ecf1ea16ecf383f"}}, "2b32eddc9ca547ceace73440308036a7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e4407841504c456da355ea8b54e4a43b", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_f68bc350228c4e7397629524e9325f0d", "value": 1563.0}}, "2b9cdd139e6d45a4a185da07900faf2f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_33d08c68fa77428d9fddcdc29625e1ee", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_231f4c199a8f458e8ca9cb5987a5bedb", "value": 1563.0}}, "2c337a8fa7874b6aaa9502467932c0fe": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9b8e8dd3fd5a46d786745a1851c72bbf", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_6a66e81e0cc845e696ce7ff3c0d01b7b", "value": 1563.0}}, "3219d2bbd48b4a268d7a9e1c28768e37": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "33d08c68fa77428d9fddcdc29625e1ee": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3436c7b40e684abbb6d5d925c1e3562c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "36ed0a721f424d74b38eb5a912ab693b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c39a8ce99b7f4f0dbc2c34aa53c972d2", "placeholder": "\u200b", "style": "IPY_MODEL_86a453d61d574ac1baa007882b7a6b0f", "value": " 1560/1563 [00:17<00:00, 89.36it/s]"}}, "371384fdf3a8410191184cc20007148d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "3809e6fe848843dfaaf6b9e7f5a08dc8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6210e1a1628048b39561d7e5f684ab73", "placeholder": "\u200b", "style": "IPY_MODEL_ad50fbd1f4be41f6935d8efbafe4da05", "value": " 1560/1563 [00:17<00:00, 89.13it/s]"}}, "384a2ed4bd214b6299151e6e870fb7fc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3e467637b02b4cfd85562b3f1c5b58e8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_fb06c3f845574aeb8dc22e44961cd33f", "IPY_MODEL_2c337a8fa7874b6aaa9502467932c0fe", "IPY_MODEL_36ed0a721f424d74b38eb5a912ab693b"], "layout": "IPY_MODEL_87c36192b69043779c3db1bf5fee3872"}}, "408e2b530a9641e18ea5df659d08e4ae": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "42ea17def7cc48e59c0e0f95348555c4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_711ca9fe21654973ae92db9419db1252", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_699fae0a08bb42fb944ad9d33aab171d", "value": 1563.0}}, "44d5b7627b544985bc926220a5bc9a20": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_c4516ed461704a70afb4dc46435a3f56", "IPY_MODEL_925fcc0b20d747fd9f0d5ec004b07ff0", "IPY_MODEL_e1ffcef9fbd94b638001b8f6b09eaa24"], "layout": "IPY_MODEL_d31f5864c95a4fc5a83552e28a52d0c7"}}, "479a86133e0e40dcad3f097243ad760c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "47d19a4a9d7d40a98ec8311b15742d30": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "47eca2c448fd42cdbb4171118143b086": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "47f1a317780d4bd080de45c16617045a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4d0bbc3b172f458dbc05e627ab55422b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4eaacd5d1c9c4c2c91330d16285a3fdb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_bfd5a2987c7e48f0ba3c06565fc43e16", "placeholder": "\u200b", "style": "IPY_MODEL_7e6dd612c3094b56861bfbf839635c56", "value": " 44.7M/44.7M [00:00<00:00, 77.3MB/s]"}}, "4eb11692485a4c7aa6e6b08cf42af441": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4eecb5352ef14d36a58a439ceba50d09": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_70d3ca125efb40aabb4e2f09f0d8673a", "placeholder": "\u200b", "style": "IPY_MODEL_e01d3ee23be94b33bc2eb290319c680a", "value": "Validating: 100%"}}, "4f478fcfc9dd4785823b60704e03e896": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "4fa52e476b6a463d9fbd3fe51fba722f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "52d64155154f40509e85bb87d133e2c2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "54adbc53f7f14af5974fa706733bad2e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "555f5797cc10466da974bab15226644e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "558464e277d24562a26ef9696c7bad0d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_4eecb5352ef14d36a58a439ceba50d09", "IPY_MODEL_162bc591eff249078b9cc67ef718da0a", "IPY_MODEL_af817170ed6943f18f5d497fbfb4fbf6"], "layout": "IPY_MODEL_4f478fcfc9dd4785823b60704e03e896"}}, "579307f4c11b4a02a9ad16d735305b01": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "58a0aef8e87247a9a7251faa45b5a0e4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e82e94047cdf40649ad6d937c19b8a9c", "placeholder": "\u200b", "style": "IPY_MODEL_371384fdf3a8410191184cc20007148d", "value": "Validating: 100%"}}, "59aad08c7f434fd3aa35c2bee4f73a95": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_243cac8083774d7993f668e7e17cdb10", "placeholder": "\u200b", "style": "IPY_MODEL_bf7dfb92583341f4942256a769bb3c87", "value": "Validating: 100%"}}, "5a9c032cecf54bcc82e7e513f2df06fc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "5ab4ced2d2af4d8b99579329b6d08851": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1c89b25cd44d42d8b6eb2c8937ac96fe", "IPY_MODEL_42ea17def7cc48e59c0e0f95348555c4", "IPY_MODEL_671449dc36d847a2ad8de05053960192"], "layout": "IPY_MODEL_68a74fe8935b4009bf74be5b575d0c27"}}, "5bc08e9c52764c8eb90024a7553652f1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5edc7ec8b841473b920060032307385a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "61398a1cc2df40cb993dcbadf134d6eb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "6210e1a1628048b39561d7e5f684ab73": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "62258ba192b44f1782b24c2b3c6704da": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "671449dc36d847a2ad8de05053960192": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a88798c471b24f27b68b964b603bc4fa", "placeholder": "\u200b", "style": "IPY_MODEL_b4c14d3ba7174694bf9c2967ea5fa369", "value": " 1560/1563 [00:17<00:00, 88.36it/s]"}}, "68a74fe8935b4009bf74be5b575d0c27": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "699fae0a08bb42fb944ad9d33aab171d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "6a66e81e0cc845e696ce7ff3c0d01b7b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "6af962b59cad4dba940f908fe07b961b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6de03aba110e4897aad75f6052017fd1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "6dfddf144e2d43428755da99ae5ade36": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "70799825c1db415f98624df354200512": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_da0599f60e9a462bad72a7a78c3832b8", "placeholder": "\u200b", "style": "IPY_MODEL_d0eb14e4e0594f82890b77087740a299", "value": "Validating: 100%"}}, "70d3ca125efb40aabb4e2f09f0d8673a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "711ca9fe21654973ae92db9419db1252": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "715baf310fe648feb142396eecf38d0a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1cc3e5803d3a4657bcdd8fc1d061c12b", "placeholder": "\u200b", "style": "IPY_MODEL_4d0bbc3b172f458dbc05e627ab55422b", "value": " 1560/1563 [00:17<00:00, 87.18it/s]"}}, "772ed0b8cf8442c989145ce9f497f307": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9676b47b51cd408a93294f1744d9cea0", "placeholder": "\u200b", "style": "IPY_MODEL_f2f9d0ed7c854e2b846bf55b27d11b8a", "value": " 1560/1563 [00:17<00:00, 89.64it/s]"}}, "79c867abef4f483a806ae5fca70d480b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f2414c6fd3224202a1c15254d26a326e", "placeholder": "\u200b", "style": "IPY_MODEL_47eca2c448fd42cdbb4171118143b086", "value": "Validating: 100%"}}, "7a64b6cedb514aaba2da59fc403764de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c2138e3e88574b7dbec8aa95d9df0cd6", "placeholder": "\u200b", "style": "IPY_MODEL_c69aef2f63754111a0c7c556e6f06240", "value": " 3126/3126 [01:02<00:00, 49.85it/s, loss=6.37, v_num=0, valid_acc=0.627]"}}, "7c061e609d934ff898a6ecf8adca080c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7cfd590006c94519a366a6d9d84f5175": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c51d4d146a6d4181a06f30c9b357c634", "placeholder": "\u200b", "style": "IPY_MODEL_f1042eb12a7844d6a76a5313b589379d", "value": "Validating: 100%"}}, "7e6dd612c3094b56861bfbf839635c56": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "86a453d61d574ac1baa007882b7a6b0f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "86cc8c84003d40b58f5a92580a3dbe16": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "874502aaeb7b4cd8b7f09e3dd432b9c2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "87c36192b69043779c3db1bf5fee3872": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "8f5b3bb013e54b789487936589afaaaa": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "925fcc0b20d747fd9f0d5ec004b07ff0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_24987c3a0e9945e3aa4184de42cd8133", "max": 170498071.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3436c7b40e684abbb6d5d925c1e3562c", "value": 170498071.0}}, "9676b47b51cd408a93294f1744d9cea0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "98b44dcfc1de4e7ea715d9cae75c34c5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9a80a16ecded4902b414de0c6534eeec": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9b8e8dd3fd5a46d786745a1851c72bbf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9f16f1a321e54049ad9e4341550e5642": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "a0bcd7aa305e4919b63fee7e9a4c41cd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a113958c0fe445a0b3227b4c41bb54cf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a43601c3ae434a4e9e7c6c7bf5043e40": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "a5de7871bbd1480e949558688be41c31": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9a80a16ecded4902b414de0c6534eeec", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_c86eb064ecde400d9d1a5e0ec6919112", "value": 1563.0}}, "a714651a443a41bea533c841965a0122": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_24e727294dec4d55b83d7d9bd834590f", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_4fa52e476b6a463d9fbd3fe51fba722f", "value": 1563.0}}, "a88798c471b24f27b68b964b603bc4fa": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ab99f6a13607433c846e0c8491ba5bfb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "aba2a18665444538aa89282c2ac8df50": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5edc7ec8b841473b920060032307385a", "placeholder": "\u200b", "style": "IPY_MODEL_408e2b530a9641e18ea5df659d08e4ae", "value": "Epoch 9: 100%"}}, "ac1fbe8bcd9241359f10d2eb1865bd94": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_384a2ed4bd214b6299151e6e870fb7fc", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_479a86133e0e40dcad3f097243ad760c", "value": 1563.0}}, "aca0852b47ca4a55a2e2104bf21e8e87": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "acbddb30cb4346699a58b846a50d5c72": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ad50fbd1f4be41f6935d8efbafe4da05": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "aef85224023d4c17bd6beef036b3bc95": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_0963635c7a054aa6b3d78746adca575e", "IPY_MODEL_a5de7871bbd1480e949558688be41c31", "IPY_MODEL_715baf310fe648feb142396eecf38d0a"], "layout": "IPY_MODEL_62258ba192b44f1782b24c2b3c6704da"}}, "af817170ed6943f18f5d497fbfb4fbf6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cea2bd49df4f4445aceafe78bd628574", "placeholder": "\u200b", "style": "IPY_MODEL_ab99f6a13607433c846e0c8491ba5bfb", "value": " 1560/1563 [00:17<00:00, 92.42it/s]"}}, "afb56e9b281d41eaa676543b2a9dc32c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_dbf6e3b998a1419bbaabec661484e424", "placeholder": "\u200b", "style": "IPY_MODEL_a0bcd7aa305e4919b63fee7e9a4c41cd", "value": "Validating: 100%"}}, "b1f5934bc26f4f04a3eb235a87ec8d9c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b3cf8f790356444e811a7505b8d9418e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "b4c14d3ba7174694bf9c2967ea5fa369": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b6d3b98afbca43dda7d416208a651068": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b76ee81572514e77b560271a3c505645": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_79c867abef4f483a806ae5fca70d480b", "IPY_MODEL_a714651a443a41bea533c841965a0122", "IPY_MODEL_772ed0b8cf8442c989145ce9f497f307"], "layout": "IPY_MODEL_b3cf8f790356444e811a7505b8d9418e"}}, "b8210703bcc4451e877bf84bb63d0520": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b969d392aae3406f98244c05ffc71752": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_afb56e9b281d41eaa676543b2a9dc32c", "IPY_MODEL_0ed93a7a201f484eaa6f8d7246a5038f", "IPY_MODEL_bf1aff0040a04b83ac127f15c82ea8bb"], "layout": "IPY_MODEL_e0eceb45524d4044a25b165087aa1167"}}, "bb46bed597e04aa68d65fa7583f0b2fb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_ebef98d79fd84c7a8f9589c23a507c7f", "IPY_MODEL_1a3a9cc456654328b7a7bf78905af9b3", "IPY_MODEL_4eaacd5d1c9c4c2c91330d16285a3fdb"], "layout": "IPY_MODEL_98b44dcfc1de4e7ea715d9cae75c34c5"}}, "bb97a6ac530a452899a0323aa84cb8ea": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_be1548fdd13a40c1a50d943bdc0c6503", "max": 1563.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_8f5b3bb013e54b789487936589afaaaa", "value": 1563.0}}, "bc56a439a8c747419a9cfce366960df9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_bd92bb6a660a4fccbb217a891fdfda3e", "IPY_MODEL_1e560bd4592943dabe0b4f5535a76933", "IPY_MODEL_be0a49f27744499e89140291c9a6f48f"], "layout": "IPY_MODEL_195c56daec194f64a8a6c318adef0af0"}}, "bd92bb6a660a4fccbb217a891fdfda3e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_874502aaeb7b4cd8b7f09e3dd432b9c2", "placeholder": "\u200b", "style": "IPY_MODEL_24ead37777974d36bede94e54e270620", "value": "Validation sanity check: 0%"}}, "be0a49f27744499e89140291c9a6f48f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6dfddf144e2d43428755da99ae5ade36", "placeholder": "\u200b", "style": "IPY_MODEL_00ae340e2c934a289981b0fa1320a63c", "value": " 0/2 [00:00<?, ?it/s]"}}, "be1548fdd13a40c1a50d943bdc0c6503": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bf1aff0040a04b83ac127f15c82ea8bb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_47f1a317780d4bd080de45c16617045a", "placeholder": "\u200b", "style": "IPY_MODEL_61398a1cc2df40cb993dcbadf134d6eb", "value": " 1560/1563 [00:17<00:00, 88.37it/s]"}}, "bf7dfb92583341f4942256a769bb3c87": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "bfd5a2987c7e48f0ba3c06565fc43e16": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c2138e3e88574b7dbec8aa95d9df0cd6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c39a8ce99b7f4f0dbc2c34aa53c972d2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c4516ed461704a70afb4dc46435a3f56": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7c061e609d934ff898a6ecf8adca080c", "placeholder": "\u200b", "style": "IPY_MODEL_19e4d1afe39c4b3ca0f37b8851cc8707", "value": ""}}, "c51d4d146a6d4181a06f30c9b357c634": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c69aef2f63754111a0c7c556e6f06240": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c86eb064ecde400d9d1a5e0ec6919112": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "cea2bd49df4f4445aceafe78bd628574": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d0eb14e4e0594f82890b77087740a299": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d12a329b34864edea9fb3c511ca062fe": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "d31f5864c95a4fc5a83552e28a52d0c7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d621bcb9284e47e28c190e6a7781924d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d6dfa8b20f7c47ba8367a50953618cef": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_15d06f0222f441e58bb27b82be5b188f", "placeholder": "\u200b", "style": "IPY_MODEL_5bc08e9c52764c8eb90024a7553652f1", "value": " 1560/1563 [00:17<00:00, 89.27it/s]"}}, "d77d420abcbf47c0923ae5c54c44fa93": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_aba2a18665444538aa89282c2ac8df50", "IPY_MODEL_00648a26dbda4fd5a46e11eb2de65394", "IPY_MODEL_7a64b6cedb514aaba2da59fc403764de"], "layout": "IPY_MODEL_5a9c032cecf54bcc82e7e513f2df06fc"}}, "da0599f60e9a462bad72a7a78c3832b8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "da2c2bcab50d42a4b69305ce45610d95": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "db87d19eb9a14d00926944a0945a7e55": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "dbf6e3b998a1419bbaabec661484e424": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "dcaea45e34e741b6a90232516d5e5cc9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e01d3ee23be94b33bc2eb290319c680a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e0eceb45524d4044a25b165087aa1167": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "e1ffcef9fbd94b638001b8f6b09eaa24": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_86cc8c84003d40b58f5a92580a3dbe16", "placeholder": "\u200b", "style": "IPY_MODEL_555f5797cc10466da974bab15226644e", "value": " 170499072/? [00:01<00:00, 113379438.72it/s]"}}, "e4407841504c456da355ea8b54e4a43b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e82e94047cdf40649ad6d937c19b8a9c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "eb4d88a3e74c4b8286414f889c809850": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_7cfd590006c94519a366a6d9d84f5175", "IPY_MODEL_ac1fbe8bcd9241359f10d2eb1865bd94", "IPY_MODEL_d6dfa8b20f7c47ba8367a50953618cef"], "layout": "IPY_MODEL_a43601c3ae434a4e9e7c6c7bf5043e40"}}, "ebef98d79fd84c7a8f9589c23a507c7f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b6d3b98afbca43dda7d416208a651068", "placeholder": "\u200b", "style": "IPY_MODEL_47d19a4a9d7d40a98ec8311b15742d30", "value": "100%"}}, "efee6a19f778444e987cf2f8b9c9e709": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_58a0aef8e87247a9a7251faa45b5a0e4", "IPY_MODEL_2b32eddc9ca547ceace73440308036a7", "IPY_MODEL_3809e6fe848843dfaaf6b9e7f5a08dc8"], "layout": "IPY_MODEL_da2c2bcab50d42a4b69305ce45610d95"}}, "f1042eb12a7844d6a76a5313b589379d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f2414c6fd3224202a1c15254d26a326e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f2f9d0ed7c854e2b846bf55b27d11b8a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f68bc350228c4e7397629524e9325f0d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "f91e7c90408b4d189703088295affe7e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "f9b913913c4b4491ac3bcf7fa35384ef": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fb06c3f845574aeb8dc22e44961cd33f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_acbddb30cb4346699a58b846a50d5c72", "placeholder": "\u200b", "style": "IPY_MODEL_4eb11692485a4c7aa6e6b08cf42af441", "value": "Validating: 100%"}}, "ff1843ce802446d587aa5d666f2fffb2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b8210703bcc4451e877bf84bb63d0520", "placeholder": "\u200b", "style": "IPY_MODEL_db87d19eb9a14d00926944a0945a7e55", "value": " 1560/1563 [00:17<00:00, 89.28it/s]"}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/barlow-twins.ipynb b/source/notebooks/lightning_examples/barlow-twins.ipynb deleted file mode 100644 index cc46f85..0000000 --- a/source/notebooks/lightning_examples/barlow-twins.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "9b0d3dbf", "metadata": {"papermill": {"duration": 0.012709, "end_time": "2021-10-25T20:00:43.480755", "exception": false, "start_time": "2021-10-25T20:00:43.468046", "status": "completed"}, "tags": []}, "source": ["\n", "# Barlow Twins Tutorial\n", "\n", "* **Author:** Ananya Harsh Jha (ananya@pytorchlightning.ai)\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-10-25T22:00:34.269471\n", "\n", "This notebook describes the self-supervised learning method Barlow Twins.\n", "Barlow Twins differs from other recently proposed algorithms as it doesn't\n", "fall under the category of either contrastive learning, or methods like knowledge\n", "distillation or clustering. The simplicity of the loss function and its effectiveness\n", "in comparison to the current state of the art makes Barlow Twins an interesting\n", "case study.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/barlow-twins.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "a0f8a9cf", "metadata": {"papermill": {"duration": 0.011144, "end_time": "2021-10-25T20:00:43.503475", "exception": false, "start_time": "2021-10-25T20:00:43.492331", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "9ba1fb3e", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-10-25T20:00:43.529599Z", "iopub.status.busy": "2021-10-25T20:00:43.529097Z", "iopub.status.idle": "2021-10-25T20:00:43.531815Z", "shell.execute_reply": "2021-10-25T20:00:43.531280Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 0.017035, "end_time": "2021-10-25T20:00:43.531927", "exception": false, "start_time": "2021-10-25T20:00:43.514892", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# ! pip install --quiet \"torch>=1.6, <1.9\" \"torchmetrics>=0.3\" \"torchvision\" \"matplotlib\" \"pytorch-lightning>=1.3\""]}, {"cell_type": "markdown", "id": "23d70cf8", "metadata": {"papermill": {"duration": 0.011167, "end_time": "2021-10-25T20:00:43.554606", "exception": false, "start_time": "2021-10-25T20:00:43.543439", "status": "completed"}, "tags": []}, "source": ["## Barlow Twins\n", "\n", "Barlow Twins finds itself in unique place amongst the current state-of-the-art self-supervised learning methods. It does not fall under the existing categories of contrastive learning, knowledge distillation or clustering based methods. Instead, it creates its own category of redundancy reductionand achieves competitive performance with a simple yet effective loss function. In this tutorial, we look at coding up a small version of Barlow Twins algorithm using PyTorch Lightning."]}, {"cell_type": "code", "execution_count": 2, "id": "29341bf4", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:43.584461Z", "iopub.status.busy": "2021-10-25T20:00:43.583974Z", "iopub.status.idle": "2021-10-25T20:00:44.983203Z", "shell.execute_reply": "2021-10-25T20:00:44.982750Z"}, "papermill": {"duration": 1.417567, "end_time": "2021-10-25T20:00:44.983322", "exception": false, "start_time": "2021-10-25T20:00:43.565755", "status": "completed"}, "tags": []}, "outputs": [], "source": ["from functools import partial\n", "from typing import Sequence, Tuple, Union\n", "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pytorch_lightning as pl\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torchvision.transforms as transforms\n", "import torchvision.transforms.functional as VisionF\n", "from pytorch_lightning import Callback, LightningModule, Trainer\n", "from pytorch_lightning.callbacks import ModelCheckpoint\n", "from pytorch_lightning.metrics.functional import accuracy\n", "from torch.utils.data import DataLoader\n", "from torchvision.datasets import CIFAR10\n", "from torchvision.models.resnet import resnet18\n", "from torchvision.utils import make_grid\n", "\n", "batch_size = 32\n", "num_workers = 0 # to run notebook on CPU\n", "max_epochs = 200\n", "z_dim = 128"]}, {"cell_type": "markdown", "id": "721a96db", "metadata": {"papermill": {"duration": 0.011192, "end_time": "2021-10-25T20:00:45.006236", "exception": false, "start_time": "2021-10-25T20:00:44.995044", "status": "completed"}, "tags": []}, "source": ["### Transforms\n", "\n", "We first define the data augmentation pipeline used in Barlow Twins. Here, we use pipeline proposed in SimCLR, which generates two copies/views of an input image by applying the following transformations in a sequence.\n", "\n", "First it takes a random crop of the image and resizes it to a fixed pre-specified size. Then, it applies a left-to-right random flip with a probability of 0.5. This step is followed by a composition of color jitter, conversion to grayscale with a probability of 0.2 and the application of a Gaussian blur filter. Finally, we normalize the image and convert it to a tensor.\n", "\n", "Within this transform, we add a third view for our online finetuner, which we explain later on. But, to explain things quickly here, we add a another transform to perform perform test our encoder on a downstream classification task."]}, {"cell_type": "code", "execution_count": 3, "id": "6cb8b894", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:45.037011Z", "iopub.status.busy": "2021-10-25T20:00:45.030646Z", "iopub.status.idle": "2021-10-25T20:00:45.038691Z", "shell.execute_reply": "2021-10-25T20:00:45.039079Z"}, "papermill": {"duration": 0.021697, "end_time": "2021-10-25T20:00:45.039192", "exception": false, "start_time": "2021-10-25T20:00:45.017495", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class BarlowTwinsTransform:\n", " def __init__(self, train=True, input_height=224, gaussian_blur=True, jitter_strength=1.0, normalize=None):\n", "\n", " self.input_height = input_height\n", " self.gaussian_blur = gaussian_blur\n", " self.jitter_strength = jitter_strength\n", " self.normalize = normalize\n", " self.train = train\n", "\n", " color_jitter = transforms.ColorJitter(\n", " 0.8 * self.jitter_strength,\n", " 0.8 * self.jitter_strength,\n", " 0.8 * self.jitter_strength,\n", " 0.2 * self.jitter_strength,\n", " )\n", "\n", " color_transform = [transforms.RandomApply([color_jitter], p=0.8), transforms.RandomGrayscale(p=0.2)]\n", "\n", " if self.gaussian_blur:\n", " kernel_size = int(0.1 * self.input_height)\n", " if kernel_size % 2 == 0:\n", " kernel_size += 1\n", "\n", " color_transform.append(transforms.RandomApply([transforms.GaussianBlur(kernel_size=kernel_size)], p=0.5))\n", "\n", " self.color_transform = transforms.Compose(color_transform)\n", "\n", " if normalize is None:\n", " self.final_transform = transforms.ToTensor()\n", " else:\n", " self.final_transform = transforms.Compose([transforms.ToTensor(), normalize])\n", "\n", " self.transform = transforms.Compose(\n", " [\n", " transforms.RandomResizedCrop(self.input_height),\n", " transforms.RandomHorizontalFlip(p=0.5),\n", " self.color_transform,\n", " self.final_transform,\n", " ]\n", " )\n", "\n", " self.finetune_transform = None\n", " if self.train:\n", " self.finetune_transform = transforms.Compose(\n", " [\n", " transforms.RandomCrop(32, padding=4, padding_mode=\"reflect\"),\n", " transforms.RandomHorizontalFlip(),\n", " transforms.ToTensor(),\n", " ]\n", " )\n", " else:\n", " self.finetune_transform = transforms.ToTensor()\n", "\n", " def __call__(self, sample):\n", " return self.transform(sample), self.transform(sample), self.finetune_transform(sample)"]}, {"cell_type": "markdown", "id": "69b57f48", "metadata": {"papermill": {"duration": 0.011342, "end_time": "2021-10-25T20:00:45.061887", "exception": false, "start_time": "2021-10-25T20:00:45.050545", "status": "completed"}, "tags": []}, "source": ["### Dataset\n", "\n", "We select CIFAR10 as the dataset to demonstrate the pre-training process for Barlow Twins. CIFAR10 images are 32x32 in size and we do not apply a Gaussian blur transformation on them. In this step, we create the training and validation dataloaders for CIFAR10."]}, {"cell_type": "code", "execution_count": 4, "id": "273da819", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:45.091605Z", "iopub.status.busy": "2021-10-25T20:00:45.086769Z", "iopub.status.idle": "2021-10-25T20:00:49.752232Z", "shell.execute_reply": "2021-10-25T20:00:49.751801Z"}, "papermill": {"duration": 4.679129, "end_time": "2021-10-25T20:00:49.752355", "exception": false, "start_time": "2021-10-25T20:00:45.073226", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./cifar-10-python.tar.gz\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b13dfae7160c4c8586c5780f597cd8e4", "version_major": 2, "version_minor": 0}, "text/plain": [" 0%| | 0/170498071 [00:00"]}, "metadata": {}, "output_type": "display_data"}], "source": ["for batch in val_loader:\n", " (img1, img2, _), label = batch\n", " break\n", "\n", "img_grid = make_grid(img1, normalize=True)\n", "\n", "\n", "def show(imgs):\n", " if not isinstance(imgs, list):\n", " imgs = [imgs]\n", " fix, axs = plt.subplots(ncols=len(imgs), squeeze=False)\n", " for i, img in enumerate(imgs):\n", " img = img.detach()\n", " img = VisionF.to_pil_image(img)\n", " axs[0, i].imshow(np.asarray(img))\n", " axs[0, i].set(xticklabels=[], yticklabels=[], xticks=[], yticks=[])\n", "\n", "\n", "show(img_grid)"]}, {"cell_type": "markdown", "id": "a0233366", "metadata": {"papermill": {"duration": 0.018776, "end_time": "2021-10-25T20:00:50.033993", "exception": false, "start_time": "2021-10-25T20:00:50.015217", "status": "completed"}, "tags": []}, "source": ["### Barlow Twins Loss\n", "\n", "Here we define the loss function for Barlow Twins. It first normalizes the D dimensinonal vectors from the projection head and then computes the DxD cross-correlation matrix between the normalized vectors of the 2 views of each image.\n", "\n", "Then it splits this cross-correlation matrix into two parts. The first part, the diagonal of this matrix is brought closer to 1, which pushes up the cosine similarity between the latent vectors of two views of each image, thus making the backbone invariant to the transformations applied to the views. The second part of the loss pushes the non-diagonal elements of the cross-corrlelation matrix closes to 0. This reduces the redundancy between the different dimensions of the latent vector."]}, {"cell_type": "code", "execution_count": 6, "id": "7f3f6369", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:50.069422Z", "iopub.status.busy": "2021-10-25T20:00:50.068917Z", "iopub.status.idle": "2021-10-25T20:00:50.071108Z", "shell.execute_reply": "2021-10-25T20:00:50.070701Z"}, "papermill": {"duration": 0.02278, "end_time": "2021-10-25T20:00:50.071208", "exception": false, "start_time": "2021-10-25T20:00:50.048428", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class BarlowTwinsLoss(nn.Module):\n", " def __init__(self, batch_size, lambda_coeff=5e-3, z_dim=128):\n", " super().__init__()\n", "\n", " self.z_dim = z_dim\n", " self.batch_size = batch_size\n", " self.lambda_coeff = lambda_coeff\n", "\n", " def off_diagonal_ele(self, x):\n", " # taken from: https://github.com/facebookresearch/barlowtwins/blob/main/main.py\n", " # return a flattened view of the off-diagonal elements of a square matrix\n", " n, m = x.shape\n", " assert n == m\n", " return x.flatten()[:-1].view(n - 1, n + 1)[:, 1:].flatten()\n", "\n", " def forward(self, z1, z2):\n", " # N x D, where N is the batch size and D is output dim of projection head\n", " z1_norm = (z1 - torch.mean(z1, dim=0)) / torch.std(z1, dim=0)\n", " z2_norm = (z2 - torch.mean(z2, dim=0)) / torch.std(z2, dim=0)\n", "\n", " cross_corr = torch.matmul(z1_norm.T, z2_norm) / self.batch_size\n", "\n", " on_diag = torch.diagonal(cross_corr).add_(-1).pow_(2).sum()\n", " off_diag = self.off_diagonal_ele(cross_corr).pow_(2).sum()\n", "\n", " return on_diag + self.lambda_coeff * off_diag"]}, {"cell_type": "markdown", "id": "050c8b3c", "metadata": {"papermill": {"duration": 0.014594, "end_time": "2021-10-25T20:00:50.101352", "exception": false, "start_time": "2021-10-25T20:00:50.086758", "status": "completed"}, "tags": []}, "source": ["### Backbone\n", "\n", "This is a standard Resnet backbone that we pre-train using the Barlow Twins method. To accommodate the 32x32 CIFAR10 images, we replace the first 7x7 convolution of the Resnet backbone by a 3x3 filter. We also remove the first Maxpool layer from the network for CIFAR10 images."]}, {"cell_type": "code", "execution_count": 7, "id": "a1df9ad9", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:50.134356Z", "iopub.status.busy": "2021-10-25T20:00:50.133886Z", "iopub.status.idle": "2021-10-25T20:00:50.315984Z", "shell.execute_reply": "2021-10-25T20:00:50.315481Z"}, "papermill": {"duration": 0.200049, "end_time": "2021-10-25T20:00:50.316105", "exception": false, "start_time": "2021-10-25T20:00:50.116056", "status": "completed"}, "tags": []}, "outputs": [], "source": ["encoder = resnet18()\n", "\n", "# for CIFAR10, replace the first 7x7 conv with smaller 3x3 conv and remove the first maxpool\n", "encoder.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)\n", "encoder.maxpool = nn.MaxPool2d(kernel_size=1, stride=1)\n", "\n", "# replace classification fc layer of Resnet to obtain representations from the backbone\n", "encoder.fc = nn.Identity()"]}, {"cell_type": "markdown", "id": "c85b1d8a", "metadata": {"papermill": {"duration": 0.014604, "end_time": "2021-10-25T20:00:50.345738", "exception": false, "start_time": "2021-10-25T20:00:50.331134", "status": "completed"}, "tags": []}, "source": ["### Projection head\n", "\n", "Unlike SimCLR and BYOL, the downstream performance of Barlow Twins greatly benefits from having a larger projection head after the backbone network. The paper utilizes a 3 layer MLP with 8192 hidden dimensions and 8192 as the output dimenion of the projection head. For the purposes of the tutorial, we use a smaller projection head. But, it is imperative to mention here that in practice, Barlow Twins needs to be trained using a bigger projection head as it is highly sensitive to its architecture and output dimensionality."]}, {"cell_type": "code", "execution_count": 8, "id": "62392e1c", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:50.379426Z", "iopub.status.busy": "2021-10-25T20:00:50.378950Z", "iopub.status.idle": "2021-10-25T20:00:50.380731Z", "shell.execute_reply": "2021-10-25T20:00:50.381114Z"}, "papermill": {"duration": 0.020782, "end_time": "2021-10-25T20:00:50.381232", "exception": false, "start_time": "2021-10-25T20:00:50.360450", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ProjectionHead(nn.Module):\n", " def __init__(self, input_dim=2048, hidden_dim=2048, output_dim=128):\n", " super().__init__()\n", "\n", " self.projection_head = nn.Sequential(\n", " nn.Linear(input_dim, hidden_dim, bias=True),\n", " nn.BatchNorm1d(hidden_dim),\n", " nn.ReLU(),\n", " nn.Linear(hidden_dim, output_dim, bias=False),\n", " )\n", "\n", " def forward(self, x):\n", " return self.projection_head(x)"]}, {"cell_type": "markdown", "id": "3b69afcd", "metadata": {"papermill": {"duration": 0.014461, "end_time": "2021-10-25T20:00:50.410233", "exception": false, "start_time": "2021-10-25T20:00:50.395772", "status": "completed"}, "tags": []}, "source": ["### Learning rate warmup\n", "\n", "For the purposes of this tutorial, we keep things simple and use a linear warmup schedule with Adam optimizer. In our previous experiments we have found that linear warmup part is much more important for the final performance of a model than the cosine decay component of the schedule."]}, {"cell_type": "code", "execution_count": 9, "id": "1df8bf31", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:50.442982Z", "iopub.status.busy": "2021-10-25T20:00:50.442512Z", "iopub.status.idle": "2021-10-25T20:00:50.444620Z", "shell.execute_reply": "2021-10-25T20:00:50.444217Z"}, "papermill": {"duration": 0.019933, "end_time": "2021-10-25T20:00:50.444716", "exception": false, "start_time": "2021-10-25T20:00:50.424783", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def fn(warmup_steps, step):\n", " if step < warmup_steps:\n", " return float(step) / float(max(1, warmup_steps))\n", " else:\n", " return 1.0\n", "\n", "\n", "def linear_warmup_decay(warmup_steps):\n", " return partial(fn, warmup_steps)"]}, {"cell_type": "markdown", "id": "0f165fdf", "metadata": {"papermill": {"duration": 0.014653, "end_time": "2021-10-25T20:00:50.473995", "exception": false, "start_time": "2021-10-25T20:00:50.459342", "status": "completed"}, "tags": []}, "source": ["### Barlow Twins Lightning Module\n", "\n", "We keep the LightningModule for Barlow Twins neat and simple. It takes in an backbone encoder and initializes the projection head and the loss function. We configure the optimizer and the learning rate scheduler in the ``configure_optimizers`` method."]}, {"cell_type": "code", "execution_count": 10, "id": "ad94b021", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:50.511807Z", "iopub.status.busy": "2021-10-25T20:00:50.511324Z", "iopub.status.idle": "2021-10-25T20:00:50.513468Z", "shell.execute_reply": "2021-10-25T20:00:50.512978Z"}, "papermill": {"duration": 0.024798, "end_time": "2021-10-25T20:00:50.513565", "exception": false, "start_time": "2021-10-25T20:00:50.488767", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class BarlowTwins(LightningModule):\n", " def __init__(\n", " self,\n", " encoder,\n", " encoder_out_dim,\n", " num_training_samples,\n", " batch_size,\n", " lambda_coeff=5e-3,\n", " z_dim=128,\n", " learning_rate=1e-4,\n", " warmup_epochs=10,\n", " max_epochs=200,\n", " ):\n", " super().__init__()\n", "\n", " self.encoder = encoder\n", " self.projection_head = ProjectionHead(input_dim=encoder_out_dim, hidden_dim=encoder_out_dim, output_dim=z_dim)\n", " self.loss_fn = BarlowTwinsLoss(batch_size=batch_size, lambda_coeff=lambda_coeff, z_dim=z_dim)\n", "\n", " self.learning_rate = learning_rate\n", " self.warmup_epochs = warmup_epochs\n", " self.max_epochs = max_epochs\n", "\n", " self.train_iters_per_epoch = num_training_samples // batch_size\n", "\n", " def forward(self, x):\n", " return self.encoder(x)\n", "\n", " def shared_step(self, batch):\n", " (x1, x2, _), _ = batch\n", "\n", " z1 = self.projection_head(self.encoder(x1))\n", " z2 = self.projection_head(self.encoder(x2))\n", "\n", " return self.loss_fn(z1, z2)\n", "\n", " def training_step(self, batch, batch_idx):\n", " loss = self.shared_step(batch)\n", "\n", " self.log(\"train_loss\", loss.item(), on_step=True, on_epoch=False)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " loss = self.shared_step(batch)\n", "\n", " self.log(\"val_loss\", loss, on_step=False, on_epoch=True)\n", " return loss\n", "\n", " def configure_optimizers(self):\n", " optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)\n", "\n", " warmup_steps = self.train_iters_per_epoch * self.warmup_epochs\n", "\n", " scheduler = {\n", " \"scheduler\": torch.optim.lr_scheduler.LambdaLR(\n", " optimizer,\n", " linear_warmup_decay(warmup_steps),\n", " ),\n", " \"interval\": \"step\",\n", " \"frequency\": 1,\n", " }\n", "\n", " return [optimizer], [scheduler]"]}, {"cell_type": "markdown", "id": "5ec4d0aa", "metadata": {"papermill": {"duration": 0.014734, "end_time": "2021-10-25T20:00:50.543024", "exception": false, "start_time": "2021-10-25T20:00:50.528290", "status": "completed"}, "tags": []}, "source": ["### Evaluation\n", "\n", "We define a callback which appends a linear layer on top of the encoder and trains the classification evaluation head in an online manner. We make sure not to backpropagate the gradients back to the encoder while tuning the linear layer. This technique was used in SimCLR as well and they showed that the final downstream classification peformance is pretty much similar to the results on online finetuning as the training progresses."]}, {"cell_type": "code", "execution_count": 11, "id": "f271c5a3", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:50.582976Z", "iopub.status.busy": "2021-10-25T20:00:50.574339Z", "iopub.status.idle": "2021-10-25T20:00:50.585053Z", "shell.execute_reply": "2021-10-25T20:00:50.584582Z"}, "papermill": {"duration": 0.027403, "end_time": "2021-10-25T20:00:50.585148", "exception": false, "start_time": "2021-10-25T20:00:50.557745", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class OnlineFineTuner(Callback):\n", " def __init__(\n", " self,\n", " encoder_output_dim: int,\n", " num_classes: int,\n", " ) -> None:\n", " super().__init__()\n", "\n", " self.optimizer: torch.optim.Optimizer\n", "\n", " self.encoder_output_dim = encoder_output_dim\n", " self.num_classes = num_classes\n", "\n", " def on_pretrain_routine_start(self, trainer: pl.Trainer, pl_module: pl.LightningModule) -> None:\n", "\n", " # add linear_eval layer and optimizer\n", " pl_module.online_finetuner = nn.Linear(self.encoder_output_dim, self.num_classes).to(pl_module.device)\n", " self.optimizer = torch.optim.Adam(pl_module.online_finetuner.parameters(), lr=1e-4)\n", "\n", " def extract_online_finetuning_view(\n", " self, batch: Sequence, device: Union[str, torch.device]\n", " ) -> Tuple[torch.Tensor, torch.Tensor]:\n", " (_, _, finetune_view), y = batch\n", " finetune_view = finetune_view.to(device)\n", " y = y.to(device)\n", "\n", " return finetune_view, y\n", "\n", " def on_train_batch_end(\n", " self,\n", " trainer: pl.Trainer,\n", " pl_module: pl.LightningModule,\n", " outputs: Sequence,\n", " batch: Sequence,\n", " batch_idx: int,\n", " dataloader_idx: int,\n", " ) -> None:\n", " x, y = self.extract_online_finetuning_view(batch, pl_module.device)\n", "\n", " with torch.no_grad():\n", " feats = pl_module(x)\n", "\n", " feats = feats.detach()\n", " preds = pl_module.online_finetuner(feats)\n", " loss = F.cross_entropy(preds, y)\n", "\n", " loss.backward()\n", " self.optimizer.step()\n", " self.optimizer.zero_grad()\n", "\n", " acc = accuracy(F.softmax(preds, dim=1), y)\n", " pl_module.log(\"online_train_acc\", acc, on_step=True, on_epoch=False)\n", " pl_module.log(\"online_train_loss\", loss, on_step=True, on_epoch=False)\n", "\n", " def on_validation_batch_end(\n", " self,\n", " trainer: pl.Trainer,\n", " pl_module: pl.LightningModule,\n", " outputs: Sequence,\n", " batch: Sequence,\n", " batch_idx: int,\n", " dataloader_idx: int,\n", " ) -> None:\n", " x, y = self.extract_online_finetuning_view(batch, pl_module.device)\n", "\n", " with torch.no_grad():\n", " feats = pl_module(x)\n", "\n", " feats = feats.detach()\n", " preds = pl_module.online_finetuner(feats)\n", " loss = F.cross_entropy(preds, y)\n", "\n", " acc = accuracy(F.softmax(preds, dim=1), y)\n", " pl_module.log(\"online_val_acc\", acc, on_step=False, on_epoch=True, sync_dist=True)\n", " pl_module.log(\"online_val_loss\", loss, on_step=False, on_epoch=True, sync_dist=True)"]}, {"cell_type": "markdown", "id": "c7f6bb76", "metadata": {"papermill": {"duration": 0.014775, "end_time": "2021-10-25T20:00:50.614884", "exception": false, "start_time": "2021-10-25T20:00:50.600109", "status": "completed"}, "tags": []}, "source": ["Finally, we define the trainer for training the model. We pass in the ``train_loader`` and ``val_loader`` we had initialized earlier to the ``fit`` function."]}, {"cell_type": "code", "execution_count": 12, "id": "21965f37", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:50.649228Z", "iopub.status.busy": "2021-10-25T20:00:50.648753Z", "iopub.status.idle": "2021-10-25T20:00:50.731321Z", "shell.execute_reply": "2021-10-25T20:00:50.730897Z"}, "papermill": {"duration": 0.101688, "end_time": "2021-10-25T20:00:50.731435", "exception": false, "start_time": "2021-10-25T20:00:50.629747", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/callbacks/model_checkpoint.py:240: LightningDeprecationWarning: `ModelCheckpoint(every_n_val_epochs)` is deprecated in v1.4 and will be removed in v1.6. Please use `every_n_epochs` instead.\n", " rank_zero_deprecation(\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/callbacks/model_checkpoint.py:432: UserWarning: ModelCheckpoint(save_last=True, save_top_k=None, monitor=None) is a redundant configuration. You can save the last checkpoint with ModelCheckpoint(save_top_k=None, monitor=None).\n", " rank_zero_warn(\n", "ModelCheckpoint(save_last=True, save_top_k=-1, monitor=None) will duplicate the last checkpoint saved.\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/accelerator_connector.py:746: UserWarning: You requested multiple GPUs but did not specify a backend, e.g. `Trainer(accelerator=\"dp\"|\"ddp\"|\"ddp2\")`. Setting `accelerator=\"ddp_spawn\"` for you.\n", " rank_zero_warn(\n", "Using native 16bit precision.\n"]}, {"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}], "source": ["encoder_out_dim = 512\n", "\n", "model = BarlowTwins(\n", " encoder=encoder,\n", " encoder_out_dim=encoder_out_dim,\n", " num_training_samples=len(train_dataset),\n", " batch_size=batch_size,\n", " z_dim=z_dim,\n", ")\n", "\n", "online_finetuner = OnlineFineTuner(encoder_output_dim=encoder_out_dim, num_classes=10)\n", "checkpoint_callback = ModelCheckpoint(every_n_val_epochs=100, save_top_k=-1, save_last=True)\n", "\n", "trainer = Trainer(\n", " max_epochs=max_epochs,\n", " gpus=torch.cuda.device_count(),\n", " precision=16 if torch.cuda.device_count() > 0 else 32,\n", " callbacks=[online_finetuner, checkpoint_callback],\n", ")\n", "\n", "# uncomment this to train the model\n", "# this is done for the tutorial so that the notebook compiles\n", "# trainer.fit(model, train_loader, val_loader)"]}, {"cell_type": "markdown", "id": "9cb9b57c", "metadata": {"papermill": {"duration": 0.016005, "end_time": "2021-10-25T20:00:50.763854", "exception": false, "start_time": "2021-10-25T20:00:50.747849", "status": "completed"}, "tags": []}, "source": ["### Using the trained encoder for downstream tasks\n", "\n", "Once the encoder is pretrained on CIFAR10, we can use it to get image embeddings and use them further downstream on tasks like classification, detection, segmentation etc.\n", "\n", "In this tutorial, we did not completely train our encoder for 100s of epochs using the Barlow Twins pretraining method. So, we will load the pretrained encoder weights from a checkpoint and show the image embeddings obtained from that.\n", "\n", "To create this checkpoint, the encoder was pretrained for 200 epochs, and obtained a online finetune accuracy of x% on CIFAR-10."]}, {"cell_type": "code", "execution_count": 13, "id": "bc2dfce3", "metadata": {"execution": {"iopub.execute_input": "2021-10-25T20:00:50.800041Z", "iopub.status.busy": "2021-10-25T20:00:50.799564Z", "iopub.status.idle": "2021-10-25T20:00:51.171031Z", "shell.execute_reply": "2021-10-25T20:00:51.170535Z"}, "papermill": {"duration": 0.391254, "end_time": "2021-10-25T20:00:51.171147", "exception": false, "start_time": "2021-10-25T20:00:50.779893", "status": "completed"}, "tags": []}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["torch.Size([4, 512])\n"]}], "source": ["# ckpt_model = torch.load('') # upload checkpoint to aws\n", "# encoder = ckpt_model.encoder\n", "encoder = model.encoder\n", "\n", "downstream_dataset = CIFAR10(root=\".\", train=False, transform=transforms.ToTensor())\n", "dataloader = DataLoader(downstream_dataset, batch_size=4, shuffle=False)\n", "\n", "for batch in dataloader:\n", " img, label = batch\n", " print(encoder(img).shape)\n", " break"]}, {"cell_type": "markdown", "id": "68078df0", "metadata": {"papermill": {"duration": 0.01633, "end_time": "2021-10-25T20:00:51.204343", "exception": false, "start_time": "2021-10-25T20:00:51.188013", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Barlow Twins Tutorial\n", " :card_description: This notebook describes the self-supervised learning method Barlow Twins. Barlow Twins differs from other recently proposed algorithms as it doesn't fall under the category of...\n", " :tags: Image,Self-Supervised,GPU/TPU,Lightning-Examples"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab_type,id,colab,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 9.405997, "end_time": "2021-10-25T20:00:51.627704", "environment_variables": {}, "exception": null, "input_path": "lightning_examples/barlow-twins/barlow_twins.ipynb", "output_path": ".notebooks/lightning_examples/barlow-twins.ipynb", "parameters": {}, "start_time": "2021-10-25T20:00:42.221707", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"3365e6cc23b14f7480580fbdbd5bc019": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3882c1fab7874567af01f5ca5b6b5529": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "431aeabf10b2446bb97010d31753e44e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4bb27c7671944b6e97113a02c0341e2c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7ddd35cbfb3845b1a206ac0fcbdbfc85": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3365e6cc23b14f7480580fbdbd5bc019", "placeholder": "\u200b", "style": "IPY_MODEL_4bb27c7671944b6e97113a02c0341e2c", "value": ""}}, "7f3c22706f004367af15c8839b531c8a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "a75cd1c0f8504f44a015affaa4e54a48": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_431aeabf10b2446bb97010d31753e44e", "placeholder": "\u200b", "style": "IPY_MODEL_d8f730207f83419cb86c5c93f4f7584e", "value": " 170499072/? [00:01<00:00, 116151155.58it/s]"}}, "b13dfae7160c4c8586c5780f597cd8e4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_7ddd35cbfb3845b1a206ac0fcbdbfc85", "IPY_MODEL_eca693d1c480402d8c204656916344ad", "IPY_MODEL_a75cd1c0f8504f44a015affaa4e54a48"], "layout": "IPY_MODEL_3882c1fab7874567af01f5ca5b6b5529"}}, "bd170e01fdeb46d4aff4afb77af815fd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d8f730207f83419cb86c5c93f4f7584e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "eca693d1c480402d8c204656916344ad": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_bd170e01fdeb46d4aff4afb77af815fd", "max": 170498071.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_7f3c22706f004367af15c8839b531c8a", "value": 170498071.0}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/basic-gan.ipynb b/source/notebooks/lightning_examples/basic-gan.ipynb deleted file mode 100644 index 6562784..0000000 --- a/source/notebooks/lightning_examples/basic-gan.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "e931dfa6", "metadata": {"papermill": {"duration": 0.026978, "end_time": "2021-12-04T16:31:12.165165", "exception": false, "start_time": "2021-12-04T16:31:12.138187", "status": "completed"}, "tags": []}, "source": ["\n", "# PyTorch Lightning Basic GAN Tutorial\n", "\n", "* **Author:** PL team\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:52:58.309356\n", "\n", "How to train a GAN!\n", "\n", "Main takeaways:\n", "1. Generator and discriminator are arbitrary PyTorch modules.\n", "2. training_step does both the generator and discriminator training.\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/basic-gan.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "bb4e55df", "metadata": {"papermill": {"duration": 0.024834, "end_time": "2021-12-04T16:31:12.216179", "exception": false, "start_time": "2021-12-04T16:31:12.191345", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "194d85bf", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T16:31:12.271333Z", "iopub.status.busy": "2021-12-04T16:31:12.270849Z", "iopub.status.idle": "2021-12-04T16:31:15.189840Z", "shell.execute_reply": "2021-12-04T16:31:15.190229Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.950578, "end_time": "2021-12-04T16:31:15.190518", "exception": false, "start_time": "2021-12-04T16:31:12.239940", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"pytorch-lightning>=1.3\" \"torchmetrics>=0.3\" \"torchvision\" \"torch>=1.6, <1.9\""]}, {"cell_type": "code", "execution_count": 2, "id": "15a6b707", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:15.245721Z", "iopub.status.busy": "2021-12-04T16:31:15.245214Z", "iopub.status.idle": "2021-12-04T16:31:16.576617Z", "shell.execute_reply": "2021-12-04T16:31:16.576153Z"}, "papermill": {"duration": 1.360833, "end_time": "2021-12-04T16:31:16.576756", "exception": false, "start_time": "2021-12-04T16:31:15.215923", "status": "completed"}, "tags": []}, "outputs": [], "source": ["import os\n", "from collections import OrderedDict\n", "\n", "import numpy as np\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torchvision\n", "import torchvision.transforms as transforms\n", "from pytorch_lightning import LightningDataModule, LightningModule, Trainer\n", "from torch.utils.data import DataLoader, random_split\n", "from torchvision.datasets import MNIST\n", "\n", "PATH_DATASETS = os.environ.get(\"PATH_DATASETS\", \".\")\n", "AVAIL_GPUS = min(1, torch.cuda.device_count())\n", "BATCH_SIZE = 256 if AVAIL_GPUS else 64\n", "NUM_WORKERS = int(os.cpu_count() / 2)"]}, {"cell_type": "markdown", "id": "f96cbcf4", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.023734, "end_time": "2021-12-04T16:31:16.625285", "exception": false, "start_time": "2021-12-04T16:31:16.601551", "status": "completed"}, "tags": []}, "source": ["### MNIST DataModule\n", "\n", "Below, we define a DataModule for the MNIST Dataset. To learn more about DataModules, check out our tutorial\n", "on them or see the [latest docs](https://pytorch-lightning.readthedocs.io/en/latest/extensions/datamodules.html)."]}, {"cell_type": "code", "execution_count": 3, "id": "a42b438c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:16.681426Z", "iopub.status.busy": "2021-12-04T16:31:16.680944Z", "iopub.status.idle": "2021-12-04T16:31:16.682821Z", "shell.execute_reply": "2021-12-04T16:31:16.682406Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.034042, "end_time": "2021-12-04T16:31:16.682921", "exception": false, "start_time": "2021-12-04T16:31:16.648879", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class MNISTDataModule(LightningDataModule):\n", " def __init__(\n", " self,\n", " data_dir: str = PATH_DATASETS,\n", " batch_size: int = BATCH_SIZE,\n", " num_workers: int = NUM_WORKERS,\n", " ):\n", " super().__init__()\n", " self.data_dir = data_dir\n", " self.batch_size = batch_size\n", " self.num_workers = num_workers\n", "\n", " self.transform = transforms.Compose(\n", " [\n", " transforms.ToTensor(),\n", " transforms.Normalize((0.1307,), (0.3081,)),\n", " ]\n", " )\n", "\n", " # self.dims is returned when you call dm.size()\n", " # Setting default dims here because we know them.\n", " # Could optionally be assigned dynamically in dm.setup()\n", " self.dims = (1, 28, 28)\n", " self.num_classes = 10\n", "\n", " def prepare_data(self):\n", " # download\n", " MNIST(self.data_dir, train=True, download=True)\n", " MNIST(self.data_dir, train=False, download=True)\n", "\n", " def setup(self, stage=None):\n", " # Assign train/val datasets for use in dataloaders\n", " if stage == \"fit\" or stage is None:\n", " mnist_full = MNIST(self.data_dir, train=True, transform=self.transform)\n", " self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])\n", "\n", " # Assign test dataset for use in dataloader(s)\n", " if stage == \"test\" or stage is None:\n", " self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform)\n", "\n", " def train_dataloader(self):\n", " return DataLoader(\n", " self.mnist_train,\n", " batch_size=self.batch_size,\n", " num_workers=self.num_workers,\n", " )\n", "\n", " def val_dataloader(self):\n", " return DataLoader(self.mnist_val, batch_size=self.batch_size, num_workers=self.num_workers)\n", "\n", " def test_dataloader(self):\n", " return DataLoader(self.mnist_test, batch_size=self.batch_size, num_workers=self.num_workers)"]}, {"cell_type": "markdown", "id": "46774b1a", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.023599, "end_time": "2021-12-04T16:31:16.730307", "exception": false, "start_time": "2021-12-04T16:31:16.706708", "status": "completed"}, "tags": []}, "source": ["### A. Generator"]}, {"cell_type": "code", "execution_count": 4, "id": "fbe0fd0f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:16.783691Z", "iopub.status.busy": "2021-12-04T16:31:16.783213Z", "iopub.status.idle": "2021-12-04T16:31:16.784820Z", "shell.execute_reply": "2021-12-04T16:31:16.785196Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.031187, "end_time": "2021-12-04T16:31:16.785309", "exception": false, "start_time": "2021-12-04T16:31:16.754122", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Generator(nn.Module):\n", " def __init__(self, latent_dim, img_shape):\n", " super().__init__()\n", " self.img_shape = img_shape\n", "\n", " def block(in_feat, out_feat, normalize=True):\n", " layers = [nn.Linear(in_feat, out_feat)]\n", " if normalize:\n", " layers.append(nn.BatchNorm1d(out_feat, 0.8))\n", " layers.append(nn.LeakyReLU(0.2, inplace=True))\n", " return layers\n", "\n", " self.model = nn.Sequential(\n", " *block(latent_dim, 128, normalize=False),\n", " *block(128, 256),\n", " *block(256, 512),\n", " *block(512, 1024),\n", " nn.Linear(1024, int(np.prod(img_shape))),\n", " nn.Tanh(),\n", " )\n", "\n", " def forward(self, z):\n", " img = self.model(z)\n", " img = img.view(img.size(0), *self.img_shape)\n", " return img"]}, {"cell_type": "markdown", "id": "858d80f0", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.024118, "end_time": "2021-12-04T16:31:16.833616", "exception": false, "start_time": "2021-12-04T16:31:16.809498", "status": "completed"}, "tags": []}, "source": ["### B. Discriminator"]}, {"cell_type": "code", "execution_count": 5, "id": "08a9d3be", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:16.887890Z", "iopub.status.busy": "2021-12-04T16:31:16.887416Z", "iopub.status.idle": "2021-12-04T16:31:16.889412Z", "shell.execute_reply": "2021-12-04T16:31:16.889038Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.030604, "end_time": "2021-12-04T16:31:16.889510", "exception": false, "start_time": "2021-12-04T16:31:16.858906", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Discriminator(nn.Module):\n", " def __init__(self, img_shape):\n", " super().__init__()\n", "\n", " self.model = nn.Sequential(\n", " nn.Linear(int(np.prod(img_shape)), 512),\n", " nn.LeakyReLU(0.2, inplace=True),\n", " nn.Linear(512, 256),\n", " nn.LeakyReLU(0.2, inplace=True),\n", " nn.Linear(256, 1),\n", " nn.Sigmoid(),\n", " )\n", "\n", " def forward(self, img):\n", " img_flat = img.view(img.size(0), -1)\n", " validity = self.model(img_flat)\n", "\n", " return validity"]}, {"cell_type": "markdown", "id": "087e93db", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.023955, "end_time": "2021-12-04T16:31:16.937896", "exception": false, "start_time": "2021-12-04T16:31:16.913941", "status": "completed"}, "tags": []}, "source": ["### C. GAN\n", "\n", "#### A couple of cool features to check out in this example...\n", "\n", " - We use `some_tensor.type_as(another_tensor)` to make sure we initialize new tensors on the right device (i.e. GPU, CPU).\n", " - Lightning will put your dataloader data on the right device automatically\n", " - In this example, we pull from latent dim on the fly, so we need to dynamically add tensors to the right device.\n", " - `type_as` is the way we recommend to do this.\n", " - This example shows how to use multiple dataloaders in your `LightningModule`."]}, {"cell_type": "code", "execution_count": 6, "id": "dc2ed597", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:16.999160Z", "iopub.status.busy": "2021-12-04T16:31:16.988365Z", "iopub.status.idle": "2021-12-04T16:31:17.001905Z", "shell.execute_reply": "2021-12-04T16:31:17.001522Z"}, "papermill": {"duration": 0.040067, "end_time": "2021-12-04T16:31:17.002009", "exception": false, "start_time": "2021-12-04T16:31:16.961942", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GAN(LightningModule):\n", " def __init__(\n", " self,\n", " channels,\n", " width,\n", " height,\n", " latent_dim: int = 100,\n", " lr: float = 0.0002,\n", " b1: float = 0.5,\n", " b2: float = 0.999,\n", " batch_size: int = BATCH_SIZE,\n", " **kwargs\n", " ):\n", " super().__init__()\n", " self.save_hyperparameters()\n", "\n", " # networks\n", " data_shape = (channels, width, height)\n", " self.generator = Generator(latent_dim=self.hparams.latent_dim, img_shape=data_shape)\n", " self.discriminator = Discriminator(img_shape=data_shape)\n", "\n", " self.validation_z = torch.randn(8, self.hparams.latent_dim)\n", "\n", " self.example_input_array = torch.zeros(2, self.hparams.latent_dim)\n", "\n", " def forward(self, z):\n", " return self.generator(z)\n", "\n", " def adversarial_loss(self, y_hat, y):\n", " return F.binary_cross_entropy(y_hat, y)\n", "\n", " def training_step(self, batch, batch_idx, optimizer_idx):\n", " imgs, _ = batch\n", "\n", " # sample noise\n", " z = torch.randn(imgs.shape[0], self.hparams.latent_dim)\n", " z = z.type_as(imgs)\n", "\n", " # train generator\n", " if optimizer_idx == 0:\n", "\n", " # generate images\n", " self.generated_imgs = self(z)\n", "\n", " # log sampled images\n", " sample_imgs = self.generated_imgs[:6]\n", " grid = torchvision.utils.make_grid(sample_imgs)\n", " self.logger.experiment.add_image(\"generated_images\", grid, 0)\n", "\n", " # ground truth result (ie: all fake)\n", " # put on GPU because we created this tensor inside training_loop\n", " valid = torch.ones(imgs.size(0), 1)\n", " valid = valid.type_as(imgs)\n", "\n", " # adversarial loss is binary cross-entropy\n", " g_loss = self.adversarial_loss(self.discriminator(self(z)), valid)\n", " tqdm_dict = {\"g_loss\": g_loss}\n", " output = OrderedDict({\"loss\": g_loss, \"progress_bar\": tqdm_dict, \"log\": tqdm_dict})\n", " return output\n", "\n", " # train discriminator\n", " if optimizer_idx == 1:\n", " # Measure discriminator's ability to classify real from generated samples\n", "\n", " # how well can it label as real?\n", " valid = torch.ones(imgs.size(0), 1)\n", " valid = valid.type_as(imgs)\n", "\n", " real_loss = self.adversarial_loss(self.discriminator(imgs), valid)\n", "\n", " # how well can it label as fake?\n", " fake = torch.zeros(imgs.size(0), 1)\n", " fake = fake.type_as(imgs)\n", "\n", " fake_loss = self.adversarial_loss(self.discriminator(self(z).detach()), fake)\n", "\n", " # discriminator loss is the average of these\n", " d_loss = (real_loss + fake_loss) / 2\n", " tqdm_dict = {\"d_loss\": d_loss}\n", " output = OrderedDict({\"loss\": d_loss, \"progress_bar\": tqdm_dict, \"log\": tqdm_dict})\n", " return output\n", "\n", " def configure_optimizers(self):\n", " lr = self.hparams.lr\n", " b1 = self.hparams.b1\n", " b2 = self.hparams.b2\n", "\n", " opt_g = torch.optim.Adam(self.generator.parameters(), lr=lr, betas=(b1, b2))\n", " opt_d = torch.optim.Adam(self.discriminator.parameters(), lr=lr, betas=(b1, b2))\n", " return [opt_g, opt_d], []\n", "\n", " def on_epoch_end(self):\n", " z = self.validation_z.type_as(self.generator.model[0].weight)\n", "\n", " # log sampled images\n", " sample_imgs = self(z)\n", " grid = torchvision.utils.make_grid(sample_imgs)\n", " self.logger.experiment.add_image(\"generated_images\", grid, self.current_epoch)"]}, {"cell_type": "code", "execution_count": 7, "id": "139fbdbb", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:17.053387Z", "iopub.status.busy": "2021-12-04T16:31:17.052920Z", "iopub.status.idle": "2021-12-04T16:31:40.163711Z", "shell.execute_reply": "2021-12-04T16:31:40.164113Z"}, "papermill": {"duration": 23.13819, "end_time": "2021-12-04T16:31:40.164285", "exception": false, "start_time": "2021-12-04T16:31:17.026095", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:175: LightningDeprecationWarning: DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\"DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.\")\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:184: LightningDeprecationWarning: DataModule property `size` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\"DataModule property `size` was deprecated in v1.5 and will be removed in v1.7.\")\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:170: LightningDeprecationWarning: DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\"DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.\")\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=20)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/configuration_validator.py:116: UserWarning: You passed in a `val_dataloader` but have no `validation_step`. Skipping val loop.\n", " rank_zero_warn(\"You passed in a `val_dataloader` but have no `validation_step`. Skipping val loop.\")\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params | In sizes | Out sizes \n", "----------------------------------------------------------------------------\n", "0 | generator | Generator | 1.5 M | [2, 100] | [2, 1, 28, 28]\n", "1 | discriminator | Discriminator | 533 K | ? | ? \n", "----------------------------------------------------------------------------\n", "2.0 M Trainable params\n", "0 Non-trainable params\n", "2.0 M Total params\n", "8.174 Total estimated model params size (MB)\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "76879ff1edbb4c10a39a477697fe2c18", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/loops/optimization/closure.py:35: LightningDeprecationWarning: One of the returned values {'progress_bar', 'log'} has a `grad_fn`. We will detach it automatically but this behaviour will change in v1.6. Please detach it manually: `return {'loss': ..., 'something': something.detach()}`\n", " rank_zero_deprecation(\n"]}], "source": ["dm = MNISTDataModule()\n", "model = GAN(*dm.size())\n", "trainer = Trainer(gpus=AVAIL_GPUS, max_epochs=5, progress_bar_refresh_rate=20)\n", "trainer.fit(model, dm)"]}, {"cell_type": "code", "execution_count": 8, "id": "a1c00604", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:40.230047Z", "iopub.status.busy": "2021-12-04T16:31:40.229583Z", "iopub.status.idle": "2021-12-04T16:31:41.785539Z", "shell.execute_reply": "2021-12-04T16:31:41.785929Z"}, "papermill": {"duration": 1.590294, "end_time": "2021-12-04T16:31:41.786093", "exception": false, "start_time": "2021-12-04T16:31:40.195799", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", " \n", " \n", " "], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Start tensorboard.\n", "%load_ext tensorboard\n", "%tensorboard --logdir lightning_logs/"]}, {"cell_type": "markdown", "id": "880bb312", "metadata": {"papermill": {"duration": 0.031498, "end_time": "2021-12-04T16:31:41.850435", "exception": false, "start_time": "2021-12-04T16:31:41.818937", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: PyTorch Lightning Basic GAN Tutorial\n", " :card_description: How to train a GAN! Main takeaways: 1. Generator and discriminator are arbitrary PyTorch modules. 2. training_step does both the generator and discriminator training.\n", " :tags: Image,GPU/TPU,Lightning-Examples"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab_type,colab,id,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 31.853342, "end_time": "2021-12-04T16:31:42.690537", "environment_variables": {}, "exception": null, "input_path": "lightning_examples/basic-gan/gan.ipynb", "output_path": ".notebooks/lightning_examples/basic-gan.ipynb", "parameters": {}, "start_time": "2021-12-04T16:31:10.837195", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"0701a66e70844b08b6faf0ec350d0f73": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3280c47883334a8d92de4bc0f3dec466", "placeholder": "\u200b", "style": "IPY_MODEL_28b4d248eea6416ebb4ca9ad55fc5158", "value": " 215/215 [00:03<00:00, 56.07it/s, loss=2.9, v_num=2]"}}, "18355a5b23f843278a2ccfabdd979d9b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "28b4d248eea6416ebb4ca9ad55fc5158": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "3280c47883334a8d92de4bc0f3dec466": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4fa9eee054704cddb87d67a50d621ba0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5829933d957a462b86e52dc55b26e993": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e556860a92d8442997b62b57ce44fd2c", "max": 215.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_18355a5b23f843278a2ccfabdd979d9b", "value": 215.0}}, "610766e1db924f5ab0933c1bec342ff8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "62e2171df1994799b6987ec549a7ac47": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_610766e1db924f5ab0933c1bec342ff8", "placeholder": "\u200b", "style": "IPY_MODEL_4fa9eee054704cddb87d67a50d621ba0", "value": "Epoch 4: 100%"}}, "67121f7a826b4be2bd6356f337f40b0f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "76879ff1edbb4c10a39a477697fe2c18": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_62e2171df1994799b6987ec549a7ac47", "IPY_MODEL_5829933d957a462b86e52dc55b26e993", "IPY_MODEL_0701a66e70844b08b6faf0ec350d0f73"], "layout": "IPY_MODEL_67121f7a826b4be2bd6356f337f40b0f"}}, "e556860a92d8442997b62b57ce44fd2c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/cifar10-baseline.ipynb b/source/notebooks/lightning_examples/cifar10-baseline.ipynb deleted file mode 100644 index e87ea66..0000000 --- a/source/notebooks/lightning_examples/cifar10-baseline.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "150fd244", "metadata": {"papermill": {"duration": 0.031551, "end_time": "2021-12-04T16:31:53.611675", "exception": false, "start_time": "2021-12-04T16:31:53.580124", "status": "completed"}, "tags": []}, "source": ["\n", "# PyTorch Lightning CIFAR10 ~94% Baseline Tutorial\n", "\n", "* **Author:** PL team\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:52:59.958801\n", "\n", "Train a Resnet to 94% accuracy on Cifar10!\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/cifar10-baseline.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "50dc70a0", "metadata": {"papermill": {"duration": 0.02736, "end_time": "2021-12-04T16:31:53.668809", "exception": false, "start_time": "2021-12-04T16:31:53.641449", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "e797c5cf", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T16:31:53.738761Z", "iopub.status.busy": "2021-12-04T16:31:53.738222Z", "iopub.status.idle": "2021-12-04T16:31:56.632390Z", "shell.execute_reply": "2021-12-04T16:31:56.631747Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.935806, "end_time": "2021-12-04T16:31:56.632538", "exception": false, "start_time": "2021-12-04T16:31:53.696732", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"torch>=1.6, <1.9\" \"torchmetrics>=0.3\" \"lightning-bolts\" \"pytorch-lightning>=1.3\" \"torchvision\""]}, {"cell_type": "code", "execution_count": 2, "id": "fae10f21", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:56.692496Z", "iopub.status.busy": "2021-12-04T16:31:56.691990Z", "iopub.status.idle": "2021-12-04T16:31:56.693339Z", "shell.execute_reply": "2021-12-04T16:31:56.693734Z"}, "papermill": {"duration": 0.033333, "end_time": "2021-12-04T16:31:56.693854", "exception": false, "start_time": "2021-12-04T16:31:56.660521", "status": "completed"}, "tags": []}, "outputs": [], "source": ["# Run this if you intend to use TPUs\n", "# !pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.8-cp37-cp37m-linux_x86_64.whl"]}, {"cell_type": "code", "execution_count": 3, "id": "086fcceb", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:56.754809Z", "iopub.status.busy": "2021-12-04T16:31:56.754316Z", "iopub.status.idle": "2021-12-04T16:31:58.695537Z", "shell.execute_reply": "2021-12-04T16:31:58.695074Z"}, "papermill": {"duration": 1.974192, "end_time": "2021-12-04T16:31:58.695673", "exception": false, "start_time": "2021-12-04T16:31:56.721481", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 7\n"]}], "source": ["import os\n", "\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "import torchvision\n", "from pl_bolts.datamodules import CIFAR10DataModule\n", "from pl_bolts.transforms.dataset_normalizations import cifar10_normalization\n", "from pytorch_lightning import LightningModule, Trainer, seed_everything\n", "from pytorch_lightning.callbacks import LearningRateMonitor\n", "from pytorch_lightning.loggers import TensorBoardLogger\n", "from torch.optim.lr_scheduler import OneCycleLR\n", "from torch.optim.swa_utils import AveragedModel, update_bn\n", "from torchmetrics.functional import accuracy\n", "\n", "seed_everything(7)\n", "\n", "PATH_DATASETS = os.environ.get(\"PATH_DATASETS\", \".\")\n", "AVAIL_GPUS = min(1, torch.cuda.device_count())\n", "BATCH_SIZE = 256 if AVAIL_GPUS else 64\n", "NUM_WORKERS = int(os.cpu_count() / 2)"]}, {"cell_type": "markdown", "id": "0755d159", "metadata": {"papermill": {"duration": 0.028087, "end_time": "2021-12-04T16:31:58.755971", "exception": false, "start_time": "2021-12-04T16:31:58.727884", "status": "completed"}, "tags": []}, "source": ["### CIFAR10 Data Module\n", "\n", "Import the existing data module from `bolts` and modify the train and test transforms."]}, {"cell_type": "code", "execution_count": 4, "id": "cf38e878", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:58.820631Z", "iopub.status.busy": "2021-12-04T16:31:58.820074Z", "iopub.status.idle": "2021-12-04T16:31:58.823137Z", "shell.execute_reply": "2021-12-04T16:31:58.822662Z"}, "papermill": {"duration": 0.038494, "end_time": "2021-12-04T16:31:58.823238", "exception": false, "start_time": "2021-12-04T16:31:58.784744", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:73: LightningDeprecationWarning: DataModule property `train_transforms` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:77: LightningDeprecationWarning: DataModule property `val_transforms` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:81: LightningDeprecationWarning: DataModule property `test_transforms` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\n"]}], "source": ["\n", "train_transforms = torchvision.transforms.Compose(\n", " [\n", " torchvision.transforms.RandomCrop(32, padding=4),\n", " torchvision.transforms.RandomHorizontalFlip(),\n", " torchvision.transforms.ToTensor(),\n", " cifar10_normalization(),\n", " ]\n", ")\n", "\n", "test_transforms = torchvision.transforms.Compose(\n", " [\n", " torchvision.transforms.ToTensor(),\n", " cifar10_normalization(),\n", " ]\n", ")\n", "\n", "cifar10_dm = CIFAR10DataModule(\n", " data_dir=PATH_DATASETS,\n", " batch_size=BATCH_SIZE,\n", " num_workers=NUM_WORKERS,\n", " train_transforms=train_transforms,\n", " test_transforms=test_transforms,\n", " val_transforms=test_transforms,\n", ")"]}, {"cell_type": "markdown", "id": "eed0041b", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.028881, "end_time": "2021-12-04T16:31:58.881421", "exception": false, "start_time": "2021-12-04T16:31:58.852540", "status": "completed"}, "tags": []}, "source": ["### Resnet\n", "Modify the pre-existing Resnet architecture from TorchVision. The pre-existing architecture is based on ImageNet\n", "images (224x224) as input. So we need to modify it for CIFAR10 images (32x32)."]}, {"cell_type": "code", "execution_count": 5, "id": "3f51300e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:58.943228Z", "iopub.status.busy": "2021-12-04T16:31:58.942763Z", "iopub.status.idle": "2021-12-04T16:31:58.944731Z", "shell.execute_reply": "2021-12-04T16:31:58.944361Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.034218, "end_time": "2021-12-04T16:31:58.944829", "exception": false, "start_time": "2021-12-04T16:31:58.910611", "status": "completed"}, "tags": []}, "outputs": [], "source": ["def create_model():\n", " model = torchvision.models.resnet18(pretrained=False, num_classes=10)\n", " model.conv1 = nn.Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)\n", " model.maxpool = nn.Identity()\n", " return model"]}, {"cell_type": "markdown", "id": "e60c4b8b", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.029284, "end_time": "2021-12-04T16:31:59.003290", "exception": false, "start_time": "2021-12-04T16:31:58.974006", "status": "completed"}, "tags": []}, "source": ["### Lightning Module\n", "Check out the [`configure_optimizers`](https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#configure-optimizers)\n", "method to use custom Learning Rate schedulers. The OneCycleLR with SGD will get you to around 92-93% accuracy\n", "in 20-30 epochs and 93-94% accuracy in 40-50 epochs. Feel free to experiment with different\n", "LR schedules from https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate"]}, {"cell_type": "code", "execution_count": 6, "id": "0c7981e3", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:59.069865Z", "iopub.status.busy": "2021-12-04T16:31:59.069383Z", "iopub.status.idle": "2021-12-04T16:31:59.073065Z", "shell.execute_reply": "2021-12-04T16:31:59.073425Z"}, "papermill": {"duration": 0.041188, "end_time": "2021-12-04T16:31:59.073548", "exception": false, "start_time": "2021-12-04T16:31:59.032360", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class LitResnet(LightningModule):\n", " def __init__(self, lr=0.05):\n", " super().__init__()\n", "\n", " self.save_hyperparameters()\n", " self.model = create_model()\n", "\n", " def forward(self, x):\n", " out = self.model(x)\n", " return F.log_softmax(out, dim=1)\n", "\n", " def training_step(self, batch, batch_idx):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " self.log(\"train_loss\", loss)\n", " return loss\n", "\n", " def evaluate(self, batch, stage=None):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " preds = torch.argmax(logits, dim=1)\n", " acc = accuracy(preds, y)\n", "\n", " if stage:\n", " self.log(f\"{stage}_loss\", loss, prog_bar=True)\n", " self.log(f\"{stage}_acc\", acc, prog_bar=True)\n", "\n", " def validation_step(self, batch, batch_idx):\n", " self.evaluate(batch, \"val\")\n", "\n", " def test_step(self, batch, batch_idx):\n", " self.evaluate(batch, \"test\")\n", "\n", " def configure_optimizers(self):\n", " optimizer = torch.optim.SGD(\n", " self.parameters(),\n", " lr=self.hparams.lr,\n", " momentum=0.9,\n", " weight_decay=5e-4,\n", " )\n", " steps_per_epoch = 45000 // BATCH_SIZE\n", " scheduler_dict = {\n", " \"scheduler\": OneCycleLR(\n", " optimizer,\n", " 0.1,\n", " epochs=self.trainer.max_epochs,\n", " steps_per_epoch=steps_per_epoch,\n", " ),\n", " \"interval\": \"step\",\n", " }\n", " return {\"optimizer\": optimizer, \"lr_scheduler\": scheduler_dict}"]}, {"cell_type": "code", "execution_count": 7, "id": "0658d2df", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:31:59.139932Z", "iopub.status.busy": "2021-12-04T16:31:59.139466Z", "iopub.status.idle": "2021-12-04T16:38:59.899188Z", "shell.execute_reply": "2021-12-04T16:38:59.898678Z"}, "papermill": {"duration": 420.794323, "end_time": "2021-12-04T16:38:59.899315", "exception": false, "start_time": "2021-12-04T16:31:59.104992", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=10)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:114: LightningDeprecationWarning: DataModule property `train_transforms` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:133: LightningDeprecationWarning: DataModule property `val_transforms` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "---------------------------------\n", "0 | model | ResNet | 11.2 M\n", "---------------------------------\n", "11.2 M Trainable params\n", "0 Non-trainable params\n", "11.2 M Total params\n", "44.696 Total estimated model params size (MB)\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "c4382d4da66d4ef58c7336a37a81ff5f", "version_major": 2, "version_minor": 0}, "text/plain": ["Validation sanity check: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 7\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "962e49594a8b455f8f3f5c9501deb987", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "c590712538fa4ef7bd045c44f9aec516", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "4f4648b512ae49ef94785386a4b95c48", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "9893b65283724b288b89fd1114dc79f5", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "9438d4a6012b47c8807ebda5adce1712", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "605f32169bcd4c8ea8c309e9d7638595", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "f71b6be05b7d43f4b438780bf1f6fab1", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b9de98eebdb34b8e9c55081ebd753057", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "56b17c0aa8a64f40968f6cadbe1149ab", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "15b6d624010642f39e0ab1a2c3a49295", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "d2f8ed4e980348e5a4e806296f45ab09", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "07d0c290c34847eb9df55d1b71e1b7ec", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "29ef56d7400d4ea9a4a53c5a591d7f6e", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "729ae8a5142b414d879d0c49ca9d9742", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "638f18d72e704478b58126fee0504686", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "ef2f42b7a9cd4ad59cf933bf03cdcbc4", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "6c6a4a78c5244926b5ab585c71b22b31", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "9204f19c687749b4b9d8c58c9c5936d2", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "ea96711d4212428ba4dcc07e4ecba6bc", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "a71887abf4ff44b99939cdb9fdd47db7", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "5ed5caa95d6249e1bf43b2610b56b266", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "7a959e215a164e54aec9c5c1729fb4cd", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "78dcf8ab81484972bef98ee4808976a3", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "e2538d2d42254355a24cb5bffdd4f083", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "a7293a6474ce4e9499a1d0159c9d2034", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "ced8e9bb515746e5930f529d1c4c3f2c", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "ca42033167d84b95828523a32f20fbc7", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "3933670d051445c2a966996be4aead2d", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b8b046652eb14f2fbb40712fbe04995b", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "8aa60a1e120540efb36aefb2ea7c6158", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "c57fece658f444f18bd89f142c343bb0", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:152: LightningDeprecationWarning: DataModule property `test_transforms` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "52ca8ac78894431683ff393c47922053", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["--------------------------------------------------------------------------------\n", "DATALOADER:0 TEST RESULTS\n", "{'test_acc': 0.9203000068664551, 'test_loss': 0.27301323413848877}\n", "--------------------------------------------------------------------------------\n"]}, {"data": {"text/plain": ["[{'test_loss': 0.27301323413848877, 'test_acc': 0.9203000068664551}]"]}, "execution_count": 7, "metadata": {}, "output_type": "execute_result"}], "source": ["model = LitResnet(lr=0.05)\n", "model.datamodule = cifar10_dm\n", "\n", "trainer = Trainer(\n", " progress_bar_refresh_rate=10,\n", " max_epochs=30,\n", " gpus=AVAIL_GPUS,\n", " logger=TensorBoardLogger(\"lightning_logs/\", name=\"resnet\"),\n", " callbacks=[LearningRateMonitor(logging_interval=\"step\")],\n", ")\n", "\n", "trainer.fit(model, cifar10_dm)\n", "trainer.test(model, datamodule=cifar10_dm)"]}, {"cell_type": "markdown", "id": "47b074c0", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.080733, "end_time": "2021-12-04T16:39:00.060155", "exception": false, "start_time": "2021-12-04T16:38:59.979422", "status": "completed"}, "tags": []}, "source": ["### Bonus: Use [Stochastic Weight Averaging](https://arxiv.org/abs/1803.05407) to get a boost on performance\n", "\n", "Use SWA from torch.optim to get a quick performance boost. Also shows a couple of cool features from Lightning:\n", "- Use `training_epoch_end` to run code after the end of every epoch\n", "- Use a pretrained model directly with this wrapper for SWA"]}, {"cell_type": "code", "execution_count": 8, "id": "325d806a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:39:00.229553Z", "iopub.status.busy": "2021-12-04T16:39:00.229086Z", "iopub.status.idle": "2021-12-04T16:39:00.230724Z", "shell.execute_reply": "2021-12-04T16:39:00.231129Z"}, "papermill": {"duration": 0.090027, "end_time": "2021-12-04T16:39:00.231260", "exception": false, "start_time": "2021-12-04T16:39:00.141233", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class SWAResnet(LitResnet):\n", " def __init__(self, trained_model, lr=0.01):\n", " super().__init__()\n", "\n", " self.save_hyperparameters(\"lr\")\n", " self.model = trained_model\n", " self.swa_model = AveragedModel(self.model)\n", "\n", " def forward(self, x):\n", " out = self.swa_model(x)\n", " return F.log_softmax(out, dim=1)\n", "\n", " def training_epoch_end(self, training_step_outputs):\n", " self.swa_model.update_parameters(self.model)\n", "\n", " def validation_step(self, batch, batch_idx, stage=None):\n", " x, y = batch\n", " logits = F.log_softmax(self.model(x), dim=1)\n", " loss = F.nll_loss(logits, y)\n", " preds = torch.argmax(logits, dim=1)\n", " acc = accuracy(preds, y)\n", "\n", " self.log(\"val_loss\", loss, prog_bar=True)\n", " self.log(\"val_acc\", acc, prog_bar=True)\n", "\n", " def configure_optimizers(self):\n", " optimizer = torch.optim.SGD(self.model.parameters(), lr=self.hparams.lr, momentum=0.9, weight_decay=5e-4)\n", " return optimizer\n", "\n", " def on_train_end(self):\n", " update_bn(self.datamodule.train_dataloader(), self.swa_model, device=self.device)"]}, {"cell_type": "code", "execution_count": 9, "id": "ecdf4282", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:39:00.393861Z", "iopub.status.busy": "2021-12-04T16:39:00.391739Z", "iopub.status.idle": "2021-12-04T16:45:38.129033Z", "shell.execute_reply": "2021-12-04T16:45:38.128630Z"}, "papermill": {"duration": 397.818564, "end_time": "2021-12-04T16:45:38.129158", "exception": false, "start_time": "2021-12-04T16:39:00.310594", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=20)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:469: LightningDeprecationWarning: DataModule.setup has already been called, so it will not be called again. In v1.6 this behavior will change to always call DataModule.setup.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "--------------------------------------------\n", "0 | model | ResNet | 11.2 M\n", "1 | swa_model | AveragedModel | 11.2 M\n", "--------------------------------------------\n", "22.3 M Trainable params\n", "0 Non-trainable params\n", "22.3 M Total params\n", "89.392 Total estimated model params size (MB)\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "9ea460da748d4d009fdb4416fc5ba362", "version_major": 2, "version_minor": 0}, "text/plain": ["Validation sanity check: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["Global seed set to 7\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "d581aedbcb7f48fb8c8dbeaa079ad651", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "ff4bf4ad13c04a8791e8283c83765a05", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "f078f962d66c497b8e95231564665108", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "9ef0cc8972ec476babb87467b9ff6a00", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "664cb138f57949dfbc68e0fdcbb7144e", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "7df7c6698b064aaa92cbabaa7e6167dc", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "cc47c9cd42a140a590dddb6dc560a4d6", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "73a78a6b3957447da7f88492c1c0de36", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "a73cdacebbd14b3b9c697a62b8a758ee", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "5e17f6802c6749b0b906adc69c6ba1e9", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "2b8c7d8035ce46cc83e460afa5e96079", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "6584051a70a7443f811094bb8bb1b7ba", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "d498421af6c6458ea4fe33bf22796e86", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "4b242be8d44b4627881cdf9527e1a9b9", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "9adfc41d30154a99938b0d97fa767e21", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "efb67b6f296c44fdb4309f206eb09f4a", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "e24b7c22fbee405c8d202b376aafa909", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "3d2424c1751a42d3b71a7ee8c59e8491", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "c5a56a5d6926452cb6046dcad5b94e20", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "257e57819b96473aaf512f61d62a57d9", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "c213c26b0b8547b0bba620a692676198", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:469: LightningDeprecationWarning: DataModule.teardown has already been called, so it will not be called again. In v1.6 this behavior will change to always call DataModule.teardown.\n", " rank_zero_deprecation(\n", "LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "6d08e235cef542568fd7741e38ae73bb", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["--------------------------------------------------------------------------------\n", "DATALOADER:0 TEST RESULTS\n", "{'test_acc': 0.920199990272522, 'test_loss': 0.2513697147369385}\n", "--------------------------------------------------------------------------------\n"]}, {"data": {"text/plain": ["[{'test_loss': 0.2513697147369385, 'test_acc': 0.920199990272522}]"]}, "execution_count": 9, "metadata": {}, "output_type": "execute_result"}], "source": ["swa_model = SWAResnet(model.model, lr=0.01)\n", "swa_model.datamodule = cifar10_dm\n", "\n", "swa_trainer = Trainer(\n", " progress_bar_refresh_rate=20,\n", " max_epochs=20,\n", " gpus=AVAIL_GPUS,\n", " logger=TensorBoardLogger(\"lightning_logs/\", name=\"swa_resnet\"),\n", ")\n", "\n", "swa_trainer.fit(swa_model, cifar10_dm)\n", "swa_trainer.test(swa_model, datamodule=cifar10_dm)"]}, {"cell_type": "code", "execution_count": 10, "id": "ad6589a9", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:45:38.358787Z", "iopub.status.busy": "2021-12-04T16:45:38.358313Z", "iopub.status.idle": "2021-12-04T16:45:39.919126Z", "shell.execute_reply": "2021-12-04T16:45:39.919485Z"}, "papermill": {"duration": 1.677786, "end_time": "2021-12-04T16:45:39.919651", "exception": false, "start_time": "2021-12-04T16:45:38.241865", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", " \n", " \n", " "], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Start tensorboard.\n", "%reload_ext tensorboard\n", "%tensorboard --logdir lightning_logs/"]}, {"cell_type": "markdown", "id": "25ec73cb", "metadata": {"papermill": {"duration": 0.113883, "end_time": "2021-12-04T16:45:40.148272", "exception": false, "start_time": "2021-12-04T16:45:40.034389", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: PyTorch Lightning CIFAR10 ~94% Baseline Tutorial\n", " :card_description: Train a Resnet to 94% accuracy on Cifar10!\n", " :tags: Image,GPU/TPU,Lightning-Examples"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab,colab_type,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 829.702297, "end_time": "2021-12-04T16:45:41.984443", "environment_variables": {}, "exception": null, "input_path": "lightning_examples/cifar10-baseline/baseline.ipynb", "output_path": ".notebooks/lightning_examples/cifar10-baseline.ipynb", "parameters": {}, "start_time": "2021-12-04T16:31:52.282146", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"000e17cab12b4a16a5bb24fd4c8e6c3d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "0014f8f4f49841aaa7c1a64c57b2d49b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "0027c59a46544bef8bf57f2d9eedb383": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0046c6e7fea345d28a76745b89586893": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1caa64c8e4ed4f8bb0121571896488f3", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3a67eda0941549d88440223701afe60a", "value": 40.0}}, "00546a0bf8f647af912556b130e3909c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5fea728b72f343eeba2ea5e3750d8aed", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_da4e670e9d594e7394b38d518354f613", "value": 40.0}}, "00734065a4b748bf86e1fbb33f7a0561": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "008fa6e0a9314811a075d8ca8805709c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "014b058f5dad4a9b90029ee5c0d1941a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "01a67df105214806b42a4974f14e5792": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "023b923393b045618df10b498f0cbc13": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "024680be6f384e20a19910a8db277616": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "029ded4281f84e23a64442a8c51ad87d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "03d4fb52bb2c460b8164142c6a8b3e8a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "0425ec3a0d7849b38e947a2f4cf56349": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "0440b10cf4f441fe843002eec4c8613e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4d37c25af0834d6e8c7a53d704713ce1", "placeholder": "\u200b", "style": "IPY_MODEL_e248b9247393444b95f4e426e49403e1", "value": " 40/40 [00:01<00:00, 41.48it/s]"}}, "0447aa40a49344e79e2e683d6819d5ec": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "04c97639949842bfb3979dd8712abe8b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "05414796bb8b4c4095db6029e7c298ba": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "05a2d51877d9418b8417e35cea00aed8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "05ed6750b13e4928846c6a3626a2a2bd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "061ab79450ee4a1eadc75fc6a34c673a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "06a89e7eb1cf4eb9b31b1792b6022475": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "07728fa1b1644efdafa0f0d2934a04b1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "07c8f9ed1f6d48bbb2ccf3bc58ee548b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_731875ee32dc4c7b925239b1c3d08b9b", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_556562fbd5ed4226897e6cf42af6f527", "value": 40.0}}, "07d0c290c34847eb9df55d1b71e1b7ec": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_eceec782c1ce454dbfcadced855e791e", "IPY_MODEL_34b47bf1c5df4fa09fcc2fdc002e5722", "IPY_MODEL_77fe203b76bd43c2a5dcd03f0fa19fe9"], "layout": "IPY_MODEL_8a088cec1f3e490ca376df311ac639c3"}}, "08a677083e9a4a54a0ae80297ffbac0b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ad8f945d28464a879d7686f7d7595509", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_7223547950ec4f0abb4178506b4b5f93", "value": 40.0}}, "099f27f210e24769853bc51f75969ed5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0a6e971473a845dbaf7dd70544a4fb4e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0d45a84159db4c7ab0b5475e70ddb95a", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_241a851d435a4a9c90e8c0220fd341dc", "value": 1.0}}, "0adf97988f5d47ffb50136dfb49519e8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3f97b2d29d544bb788df0f53c6a3dba5", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_b52a63138c094b7893e021bc49c0c89e", "value": 40.0}}, "0b34e1a046f14629b409b16c1f1bd8e1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "0bb72c82108147e1a13350b851a960e5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0bed5eb371c54b7eb3fd9a73697732dc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "0ceec98503a649368d6868abd90c5ab2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "0d45a84159db4c7ab0b5475e70ddb95a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0e2a984e8cc043f5b58c6508689078ad": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a0ed6130ec664309837dff68eb1609a3", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_c5fc4d52ddb84b7082395834d134e2c4", "value": 40.0}}, "0e7532eb90c044f0b8dc0d480a9a8f98": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2dc4d71da4ae4ea1b0e5df1a7ab34dc9", "placeholder": "\u200b", "style": "IPY_MODEL_1eeb893719c04ca1ba087c7868806512", "value": " 40/40 [00:01<00:00, 41.31it/s]"}}, "0efe49958483403d970448b78c035fce": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_779ed45ace6b4434a574e897384a0f52", "placeholder": "\u200b", "style": "IPY_MODEL_de344810abe548119b3f8ddf3a1cc97a", "value": "Validating: 100%"}}, "0eff93661e2e4c6fbf791ba89304603f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0fad7f239d664ad29b3c069a3e5cf4c2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1d916e501963438fabfc53c5499facdb", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_281b4f3ac301430589f8068f75febc90", "value": 40.0}}, "0fb7a9bdc2ee4a88a7d79afdb165e0fa": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "109ca19434f2492cb50ddb48624fbef5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "10e2de30c7fa4812a7c096bfec3334ad": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "10ebfd89ac8743988d3afde159a8daa1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b131dac2a55c4445bc5f49457e59cefb", "placeholder": "\u200b", "style": "IPY_MODEL_97ca9563964441899f571f985aa19800", "value": "Validating: 100%"}}, "118e280fa5af441b80565177c090e25d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "11a171b0797a461bba2b27144b84fb35": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "11af77bd1a3c4ed48b8f79dc3bc09ee1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "122fd41a86a14cdd8d291908b9901f6b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1243b677bc9d45e5b6e28a1e33612d18": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "1259349431444f968af6416e4c991b47": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_18b9cee410e34cc98996c1c827864df3", "placeholder": "\u200b", "style": "IPY_MODEL_ffde3dbed47844968f5b0057538b78ae", "value": "Validating: 100%"}}, "125f9fd7e9d0489c86a06c1f7175f1a8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8a0481c1451549c0bc682a58facfdc39", "placeholder": "\u200b", "style": "IPY_MODEL_2d1fa20403064769858bb4cce177ff4a", "value": "Validating: 100%"}}, "12a96d79a23f49e593233c11258cc1ba": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6f9a37425e114b73801eeb7e97580200", "placeholder": "\u200b", "style": "IPY_MODEL_bfe210ee12f24249b6194fdbd5990052", "value": " 40/40 [00:01<00:00, 38.09it/s]"}}, "1346ed84515b4784be24ab3b6739983d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "13b6d5004cc64d67a46dac2c2452eb23": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_edd32f0ef84348a4b07891fb8ed6f0ab", "placeholder": "\u200b", "style": "IPY_MODEL_a85a055c9d844ad2b9bacb9583402386", "value": " 40/40 [00:01<00:00, 37.36it/s]"}}, "14086e65c6e24ccebd05a4282991f8a5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1489d138c9cd4c9bb49c6ebfed2a1317": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "14ac48af733144429e4d011377b08b9c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1593171a93284546a79ca15574055476": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "15b6d624010642f39e0ab1a2c3a49295": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_241907e611804e898fd8836588f68600", "IPY_MODEL_a25c3623d687465cae93c39b76c3ce65", "IPY_MODEL_cc0069da0700482b8bd07d5e61eccf14"], "layout": "IPY_MODEL_f669afd47abf4c0a9b3b79df62009f7e"}}, "1690182c73a74d4bb791931fe9f31bdf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "16ec8ca835ce4edbbf71ee881cd05bbf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "174cce3b873b4c2a8e173f5805d837c8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "174fb53b8ed247ebb714ec07b7abf26e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "1791c0d96d094df2ace36a97551cfff6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5566375d2f014d478f2453b1738a4831", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_558d75fe32a542b39dd8d4bdbc96518a", "value": 40.0}}, "17d2cf8733d24e43988554c1aac65490": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "18b9cee410e34cc98996c1c827864df3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "19895a4d415d4b9fb4f7c3e3ffa56ede": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_991c9442e91f483792474ebe4f09d152", "placeholder": "\u200b", "style": "IPY_MODEL_d2c31fdab421480389949e8f0af20cd0", "value": "Validating: 100%"}}, "19ef5cd49b4e4f16aa5d557bede8de6e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_40d36134e0eb4dd0bc557a6c9c47d7bd", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_a14120d2181444b3a233980b6ca6bbe4", "value": 40.0}}, "1ad088f478ef4874bd0ba0e322387932": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1b1dac267473433fa46bd05bc37db269": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_44c208c346064816aa37fbc549b50ed4", "placeholder": "\u200b", "style": "IPY_MODEL_1690182c73a74d4bb791931fe9f31bdf", "value": "Validating: 100%"}}, "1b610b37a5224b12a2bcee275a388a61": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "1ba5e824ccca492191ed59018571aa0d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1c794c38f0124a2a8100f0179717f3fb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_74553b52649d485681cc6171b8367bdb", "placeholder": "\u200b", "style": "IPY_MODEL_008fa6e0a9314811a075d8ca8805709c", "value": " 40/40 [00:01<00:00, 38.03it/s]"}}, "1caa64c8e4ed4f8bb0121571896488f3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1cf638c8ce874c81aad1d6991f6d4403": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_71e68f3d7467406ba056edd127a4b8ec", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_6faea2c255e14670a7afbcb3f1f68fc3", "value": 40.0}}, "1d0bc1bfcb6941c0b97cab36f7ab810f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1d32fa9c13af4fb69f2e7cea9a5ffab9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1d3b103a11964a219559f9f13ba9ac28": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_bd347db500c445b5914ab4c7ed5d0bd1", "placeholder": "\u200b", "style": "IPY_MODEL_57c2ff8ae9a24531bf52ee049c113cad", "value": " 40/40 [00:01<00:00, 39.80it/s]"}}, "1d916e501963438fabfc53c5499facdb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1db1255602ad4b929cd199e01fd9fa71": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_42e3454797f547f98d8e58789be26c59", "placeholder": "\u200b", "style": "IPY_MODEL_ec2f85ae2a11482b9d8967c431217991", "value": "Validating: 100%"}}, "1dcd0d34b8304b14a795e394769c5e73": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1e4437d1f3004a869ff875f935e6096e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1e5347ebd820410e88806a71d0bb8681": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f9abae85aee34c04a321d4e3410e2352", "placeholder": "\u200b", "style": "IPY_MODEL_e55684dd18f240efb5795f3d5ffdb25a", "value": " 40/40 [00:01<00:00, 40.60it/s]"}}, "1e664c703d4b4f20a8d8115990ec7248": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_556c99df63284336986c9bda4f87842b", "placeholder": "\u200b", "style": "IPY_MODEL_fb9d723beb3d4e36aaab60a344476126", "value": "Validating: 100%"}}, "1eeb893719c04ca1ba087c7868806512": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1f2fa122be4545b6af4db56d669469e9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1fabd40d482541188144c73793fb7cac": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "1fc2fe3fcf5f492c91ad6fcefe9bbbd5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4aab3345397a4b7b93504ade55c2d1ec", "placeholder": "\u200b", "style": "IPY_MODEL_f90f1336e56a4cacb8b5f8666b6c9b16", "value": "Validating: 100%"}}, "1ff9401002354c4396c387857469b5e9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_122fd41a86a14cdd8d291908b9901f6b", "placeholder": "\u200b", "style": "IPY_MODEL_77265d2f46524f789f5803628aaa8bb8", "value": "Epoch 19: 100%"}}, "2000b053e3ab49058a89a8910fee9653": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "205d0539546b41d5a349af917189d235": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "21bab32483d844888f86f71067e0f69e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "21e0696ab83b479bb28bdf8ad31a6a01": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2295acb193b2465fb3b749a6f3f9195c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "22bba232b95049b2b2d1ac3bf1ee1d09": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "22f0e92c33f040b987eea86cc152ae75": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "237abb092dc24a03977a37e02a700282": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "2394fc85d30d457d9edfc5ed0c0718e2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "23c2dfc91e574966becac4457d0b19d0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_704e66960a204403a5f4f89a9a15ead1", "placeholder": "\u200b", "style": "IPY_MODEL_959f41fc01f14eb896043296b3b9fa2e", "value": "Validating: 100%"}}, "23ddf0adf4f64bdea807fc3c6d586e40": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "241907e611804e898fd8836588f68600": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_205d0539546b41d5a349af917189d235", "placeholder": "\u200b", "style": "IPY_MODEL_11a171b0797a461bba2b27144b84fb35", "value": "Validating: 100%"}}, "241a851d435a4a9c90e8c0220fd341dc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "2541fdc2e94247aab508ae79c3def6d7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "257e57819b96473aaf512f61d62a57d9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_aa9b2cbc1307478e805f735d938a3546", "IPY_MODEL_e4cb7f9a7537486cbddfb13c57ae7c09", "IPY_MODEL_13b6d5004cc64d67a46dac2c2452eb23"], "layout": "IPY_MODEL_76d6082fd84947949437d5809e934151"}}, "258cee43a0be4850ac25f425c4b1cc3f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d73b688acdba44a2ad89c92ce6153a0c", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_e3779b041324461eb68a37851e899963", "value": 40.0}}, "25f8a6e3d77844d2804a78cedcf79e5f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "26181fafa82f430dab9d294152e60ecf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2762fd8f9f0c4c4e87ebc2ca9ea843e2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "27899444bd854cbbb68957cc37bdc15d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "281b4f3ac301430589f8068f75febc90": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "289dba33db8a44b58178c628b4c1f3da": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2954656e47e948d7b2b877e60b2f5bf3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "29d8dae79a094be8853e030d80e3d9d2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "29ef56d7400d4ea9a4a53c5a591d7f6e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_5a9423f111894a3b9907fb1f9eca1826", "IPY_MODEL_e0207d263a5f435289f3c4fd79f2f0c3", "IPY_MODEL_501bc07531894026a5e831aae8e32c18"], "layout": "IPY_MODEL_9693ff87ee62414e997ed8166a030243"}}, "2ace45c17feb45ddb2ece2fe0ba1f38e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cfd16f7563604087a407b97d5e1af29f", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_1fabd40d482541188144c73793fb7cac", "value": 40.0}}, "2b19db000c0d4da2b4aa67aebc698ce3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_80e99363f6634d5589ad8a66fe027ed1", "placeholder": "\u200b", "style": "IPY_MODEL_b1b504c2adc34ee99d363ceaefa43e70", "value": "Validation sanity check: 0%"}}, "2b7f8addc532411ba1d455956c9ef33d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "2b8c7d8035ce46cc83e460afa5e96079": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_7bab0316f29c454e8d78451bcfce3fdf", "IPY_MODEL_6e03ab437e8043268a6de9dbe99085c7", "IPY_MODEL_12a96d79a23f49e593233c11258cc1ba"], "layout": "IPY_MODEL_4ec9c16f1ec64e17bdb2740b1ab35e30"}}, "2c3052af38294165bda5283aa0fd444f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_573417c3f24e472296b7f26592a24155", "placeholder": "\u200b", "style": "IPY_MODEL_8bc8667b056f432c9256e42bb212bab5", "value": " 40/40 [00:01<00:00, 41.31it/s]"}}, "2c53cdd37f474e1e8845c3e070be0b51": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_34d66aad9af0429d928328ee7d7e3f0e", "placeholder": "\u200b", "style": "IPY_MODEL_670deef521d54465a95b84ec4fd2a173", "value": "Validating: 100%"}}, "2ca4d5329b084df0b17a5737993cfb43": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2ce48d7137ef47e79912f9a78bf1ca06": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "2d1fa20403064769858bb4cce177ff4a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "2d41f0971937454bbc65139c6f595f63": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_82df0c7e18334bb29176ed5ebdc8a6da", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_32552c3d3e0248a2a2685f1286c26895", "value": 40.0}}, "2dc4d71da4ae4ea1b0e5df1a7ab34dc9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2ecfef770cbf42c39241cba7dd6319b9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "2f8037b7d6f943b594942290d79b6130": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2ffa108ad10645a1b18413bd56b7d32a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "312a5c8115a6439e86a581a0b892399a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "31a45a8c81864c48be13eecf563fa8e6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "31a6932351aa4dfb84a2d26f4f71bf82": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "32552c3d3e0248a2a2685f1286c26895": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "32afa96f636c4f8eafd51af14244276d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c62ef3187ae842bdaee3c4292b792d6f", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_34534e54a8e44eeab86a19770decd37b", "value": 40.0}}, "3321bd2d04a74315b5b937f121591995": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "33817b1ad96043c8b531eced9f189b33": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d49067dafd874b49a1c3d0288b0edf88", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_58fd0973990d4e32ae15e8d3d2fac25e", "value": 40.0}}, "33f836df599e44f88641129b441a51fc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "340172c2cd084bbbb189896ccf76779f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "34534e54a8e44eeab86a19770decd37b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "34569022c125403fa42f989cd7a6677a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "34b47bf1c5df4fa09fcc2fdc002e5722": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3b5e104926314a50b388fae77aec2cb1", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_8e8a89ab50044f39b0bd15a946a0d30e", "value": 40.0}}, "34d66aad9af0429d928328ee7d7e3f0e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "35ac61ebf2d94417a183c5e85963e76f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "36208c417aec4e8b8cc4b4d36db804bd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2295acb193b2465fb3b749a6f3f9195c", "placeholder": "\u200b", "style": "IPY_MODEL_2954656e47e948d7b2b877e60b2f5bf3", "value": "Validating: 100%"}}, "3630871e0dbb473f929745f5c5d44b5d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "363da95944f24deaa5693756bf690a76": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "365291d5f1a94dc5a9d8da5f0d414f3e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "36c8c922093b41fcab2400f631bdc06b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "382dc007ef7b4baaaf107dcd94fc67ef": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6344a1f5d33544c4b23b797e271be18c", "placeholder": "\u200b", "style": "IPY_MODEL_462c1e01930c4ae791cdbb2e481b3638", "value": " 40/40 [00:01<00:00, 38.17it/s]"}}, "3897c2a7c1e1445fbc604c1a741c98e1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c1c99766fd58427bb5d1aa4bb932b494", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_0425ec3a0d7849b38e947a2f4cf56349", "value": 40.0}}, "38e6f6251cc247f2a70007beb7f40c80": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "392d211b8a6947759a184062f2519e0c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0eff93661e2e4c6fbf791ba89304603f", "placeholder": "\u200b", "style": "IPY_MODEL_c362daa772754d47b0063b6671836a81", "value": " 197/197 [00:13<00:00, 14.27it/s, loss=0.041, v_num=0, val_loss=0.254, val_acc=0.927]"}}, "3933670d051445c2a966996be4aead2d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_3cc22b487e6a4a709ac8bf74e290eda4", "IPY_MODEL_fe7af92e4442430d96daba437d4a374a", "IPY_MODEL_6b5ce997abf143f8b24d43f30d9999a9"], "layout": "IPY_MODEL_f0607cd375bf48418a683bc48438d6a0"}}, "3962343bfb0b44769ef42b5c98983b9c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "39772d3d7ef847248356d5afe732000c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3a67eda0941549d88440223701afe60a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "3a9384ba911144e998da472f66de9476": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "3b5e104926314a50b388fae77aec2cb1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3ba524598e40487fa22cc827d0fd284b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e7942984bfe2435b8d1a6ca6dbdbc27e", "placeholder": "\u200b", "style": "IPY_MODEL_45af9cda0f0742a0a80dabe353f66563", "value": "Validating: 100%"}}, "3c2777e8a64f4057a3384b45af8ad60e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3ca4a11a4bf24aa1a58ce41c7d3d2498": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_14086e65c6e24ccebd05a4282991f8a5", "placeholder": "\u200b", "style": "IPY_MODEL_29d8dae79a094be8853e030d80e3d9d2", "value": "Validating: 100%"}}, "3cc22b487e6a4a709ac8bf74e290eda4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d1c9ce3bf4fc44fbb4daa41d31c1ed72", "placeholder": "\u200b", "style": "IPY_MODEL_63929ac1f75e4195a41d57a8181aa23c", "value": "Validating: 100%"}}, "3d2424c1751a42d3b71a7ee8c59e8491": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_55fc4e4d0dba4756a4d99466b1fcd21a", "IPY_MODEL_1cf638c8ce874c81aad1d6991f6d4403", "IPY_MODEL_c1dd76b2378445f48f47c199006c16e9"], "layout": "IPY_MODEL_436b257a82fd48a2b685791087532d47"}}, "3d2af261942848d3a2b6c9282837ebeb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c5fad9a2cc4f42f887817c472070b776", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_fba15de3823947c997157fd19ee23ba4", "value": 40.0}}, "3d48472bd8584bd999a934e99e4534a2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8d180bcc608e40349d00ad4f02174832", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_61c8e196a838467389cee416fa5c3733", "value": 40.0}}, "3d6312bdfa8e4d7baacc6e87d7b8eebc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c3deaaeda2cb4bd3a122130339ba7bbb", "placeholder": "\u200b", "style": "IPY_MODEL_ac7a1aa262334ec094a633c7aed94a72", "value": "Validating: 100%"}}, "3de93c5568264b628ce08d92a9e2bdde": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3f1b09ad47b74af8bb89b4950d8ef999": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "3f223cafee19421782131b9f9bfcb873": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_22bba232b95049b2b2d1ac3bf1ee1d09", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_c27d2bf0ec8e4402a35653dd8da4b84d", "value": 40.0}}, "3f97b2d29d544bb788df0f53c6a3dba5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3fabbc3365a24ae98e4ddcd52b206868": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ef90020e0959424193c5298f4ab9a190", "max": 197.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_a5ac4f0fd6fe4ff0a93e849b930717a6", "value": 197.0}}, "3fcf6bf3438a429ea8d182a0f143763e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "405fe7ec85844f918ad93c8cd2daa06b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4078308e69eb4f789e37f28c5d267f85": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "40d36134e0eb4dd0bc557a6c9c47d7bd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "40ff73c7b3dc4556be5ecc1d35a6613f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "410b71b0a20941c089e536e4e128d13b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "41e40cffb2094d97afd7a8e037cf0d5b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4202db89a7a746bab07560c550105366": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "42a7109f566a424199400b71f736ec10": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_494313e50a4f4f7da351e74b310e0319", "placeholder": "\u200b", "style": "IPY_MODEL_0ceec98503a649368d6868abd90c5ab2", "value": " 40/40 [00:01<00:00, 40.97it/s]"}}, "42d5b26f1cb04b7495bb753b844e9fd6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d3f774b1b0404f198af51238375c574e", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_65ddb1327fa64c2e903b003551fc22e8", "value": 40.0}}, "42e3454797f547f98d8e58789be26c59": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "436b257a82fd48a2b685791087532d47": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "44c208c346064816aa37fbc549b50ed4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "45af9cda0f0742a0a80dabe353f66563": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "462c1e01930c4ae791cdbb2e481b3638": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4664867aff844335bcaedda48bfe3310": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_109ca19434f2492cb50ddb48624fbef5", "placeholder": "\u200b", "style": "IPY_MODEL_7cf1b98468f946d29dce76701c9dc87a", "value": " 40/40 [00:01<00:00, 41.09it/s]"}}, "46b9c07e7b1545dc96651a56d933e2a4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "471d838b49da478fa34ccc96b6ec926a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "47208c4b604b47c382c1a3f773500da7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "48bea9eace8c48a9a5aa452d5dfed96e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e9eff4cf4c714f2680f7e77dbb5a372c", "placeholder": "\u200b", "style": "IPY_MODEL_9d95159b37974aff8914b24c0d943a37", "value": "Validating: 100%"}}, "494313e50a4f4f7da351e74b310e0319": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "496688b3d51445aebf14a8ccbd529eed": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4a36ee3223aa4003bf6c9c68b41ebca9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4a86d8dd5c4d43faa9b83359c8b579fa": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4aab3345397a4b7b93504ade55c2d1ec": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4ac072f3e25e4c02b3b3e0e18e406a9c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4b242be8d44b4627881cdf9527e1a9b9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_d593afd8891e4b05ba9a03051d458764", "IPY_MODEL_e0fbc495e5d04f798a6cbfc91af1d0ed", "IPY_MODEL_b1954a59bdbf45ce91afe0766729fc53"], "layout": "IPY_MODEL_d2c757a2385045d3ab0adcf707dddba1"}}, "4b809c67a3f2448c9cfd7c34b8aa453f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4c36b11423d549cfaacf00c911dd0aa9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1d0bc1bfcb6941c0b97cab36f7ab810f", "placeholder": "\u200b", "style": "IPY_MODEL_0bed5eb371c54b7eb3fd9a73697732dc", "value": " 40/40 [00:01<00:00, 41.63it/s]"}}, "4c6b4042a5cf4501808c4c3cbd2a1ca1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4d118818f8cf4907816960bccee43c96": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "4d37c25af0834d6e8c7a53d704713ce1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4dac2f6ceaad4780b4452daf42ff31c7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "4ec9c16f1ec64e17bdb2740b1ab35e30": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "4f4648b512ae49ef94785386a4b95c48": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_afcf640856fe47e29b40125065435826", "IPY_MODEL_7be887614c9a4b02bafb06c04247b854", "IPY_MODEL_4c36b11423d549cfaacf00c911dd0aa9"], "layout": "IPY_MODEL_cacae5ca1e9d4283b9afe8e1916e8618"}}, "501bc07531894026a5e831aae8e32c18": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_79efd9bc5e2f4515acb71a95a0f1228a", "placeholder": "\u200b", "style": "IPY_MODEL_a5c6db3bbffd4e0fa2ae551c18a03b1a", "value": " 40/40 [00:01<00:00, 41.40it/s]"}}, "5107381a8622411cb492283123901aa0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5107961954384c8aaaeba31c1b5bc4c8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_312a5c8115a6439e86a581a0b892399a", "placeholder": "\u200b", "style": "IPY_MODEL_996b6c65885c4495b49d1f4e857026a2", "value": " 40/40 [00:01<00:00, 37.95it/s]"}}, "51c67493b2aa4e04af474b9d59af6a6c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "52ca8ac78894431683ff393c47922053": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_d22a5b5a07714a7b9584aad405faee89", "IPY_MODEL_573e74a43a93462ab0e0e31aa9350a52", "IPY_MODEL_afac60f3d22346c3a68852bd55ea790a"], "layout": "IPY_MODEL_11af77bd1a3c4ed48b8f79dc3bc09ee1"}}, "52eae5f9929f4a0b962a3ff4447cea26": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "530f74cfa1894309b640a8910bafcccc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "535725f3fc3d47d1834dd8c5512c4be3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "552fc188b09645f4b397b21d960daf93": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "556562fbd5ed4226897e6cf42af6f527": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "5566375d2f014d478f2453b1738a4831": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "556c99df63284336986c9bda4f87842b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "558d75fe32a542b39dd8d4bdbc96518a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "55b36c54b9f14e74aec831f953af07ea": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_61b7018ed05440359e4154848b1539d4", "placeholder": "\u200b", "style": "IPY_MODEL_46b9c07e7b1545dc96651a56d933e2a4", "value": " 40/40 [00:01<00:00, 37.80it/s]"}}, "55fc4e4d0dba4756a4d99466b1fcd21a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_de693622da2f4bb7b735600f02a60478", "placeholder": "\u200b", "style": "IPY_MODEL_47208c4b604b47c382c1a3f773500da7", "value": "Validating: 100%"}}, "56b17c0aa8a64f40968f6cadbe1149ab": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_19895a4d415d4b9fb4f7c3e3ffa56ede", "IPY_MODEL_df4ebdf10987433cb65c78dbf7e34b99", "IPY_MODEL_72a44be9f81f4117bf7754b66560ad0a"], "layout": "IPY_MODEL_ccd261d7a5764d43bcacce36b0f5db99"}}, "573417c3f24e472296b7f26592a24155": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "573e74a43a93462ab0e0e31aa9350a52": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_07728fa1b1644efdafa0f0d2934a04b1", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_eca7de566a424f17ae0b4b0d2f99f855", "value": 1.0}}, "57c2ff8ae9a24531bf52ee049c113cad": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "582fd37b71c74c259b2556b59425adee": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "58fd0973990d4e32ae15e8d3d2fac25e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "5a00a23b5f2348198bec2c1e8110482b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cd17e0a2a3524fc5a548b86c61953b77", "placeholder": "\u200b", "style": "IPY_MODEL_f6f68d546ecb45ffb73be50d80994d1f", "value": " 40/40 [00:01<00:00, 37.04it/s]"}}, "5a9423f111894a3b9907fb1f9eca1826": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_887380ee431440efa0fa779b58a9070f", "placeholder": "\u200b", "style": "IPY_MODEL_d8ff45a13d3c4930b4eac298d783b7cf", "value": "Validating: 100%"}}, "5b065d928b3a4d6eab7911a18dccc14c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5b7e893c983d44c6914b42a32154b513": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5b9dddecff0f432da833b80160ad7568": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_061ab79450ee4a1eadc75fc6a34c673a", "placeholder": "\u200b", "style": "IPY_MODEL_1d32fa9c13af4fb69f2e7cea9a5ffab9", "value": " 0/2 [00:00<?, ?it/s]"}}, "5c03b886ad694e53861e7dceebdf0268": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f5438eeef53847ba95d157f56e438488", "placeholder": "\u200b", "style": "IPY_MODEL_6ab8985820164656ad60b9fbbe1e9db6", "value": "Validating: 100%"}}, "5cffc262cf264cbb9aa5e34a41176974": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "5d3e25d0f248411da504f0a0df9c0e2d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_582fd37b71c74c259b2556b59425adee", "placeholder": "\u200b", "style": "IPY_MODEL_d407101ddcc74699a620bbd6a9628bff", "value": " 40/40 [00:01<00:00, 40.97it/s]"}}, "5d937ee650f642d1aff4dcac62eb60b4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_17d2cf8733d24e43988554c1aac65490", "placeholder": "\u200b", "style": "IPY_MODEL_f4fb223218124ea6a02efef412bac44a", "value": "Validating: 100%"}}, "5e17f6802c6749b0b906adc69c6ba1e9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_10ebfd89ac8743988d3afde159a8daa1", "IPY_MODEL_bdd9067ae2bd48aa9eff55c12ba2a8de", "IPY_MODEL_c1609608c09b4580861adb27dd251f36"], "layout": "IPY_MODEL_5cffc262cf264cbb9aa5e34a41176974"}}, "5ed5caa95d6249e1bf43b2610b56b266": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_5d937ee650f642d1aff4dcac62eb60b4", "IPY_MODEL_0e2a984e8cc043f5b58c6508689078ad", "IPY_MODEL_4664867aff844335bcaedda48bfe3310"], "layout": "IPY_MODEL_4d118818f8cf4907816960bccee43c96"}}, "5fea728b72f343eeba2ea5e3750d8aed": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "605f32169bcd4c8ea8c309e9d7638595": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_913954479b9e42789254d32050020788", "IPY_MODEL_0fad7f239d664ad29b3c069a3e5cf4c2", "IPY_MODEL_2c3052af38294165bda5283aa0fd444f"], "layout": "IPY_MODEL_1b610b37a5224b12a2bcee275a388a61"}}, "608e803ac821482db32298d68e280acd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "60cce88461724ab582baf5e4e7653386": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "61b7018ed05440359e4154848b1539d4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "61c8e196a838467389cee416fa5c3733": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "62c0abdd86f543a6860fb5b514b6028f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "62defc9448f14799a799664a6e93e99d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4c6b4042a5cf4501808c4c3cbd2a1ca1", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_75b74524c9ce4147a12ee83304575022", "value": 40.0}}, "6344a1f5d33544c4b23b797e271be18c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "638225841dcb42bdbc24ab03315c976a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_72b9b7bf3f244f3c82de65666a8a0581", "placeholder": "\u200b", "style": "IPY_MODEL_d7f9bf3cbe3f4160a09f5376dae9e096", "value": " 40/40 [00:01<00:00, 38.06it/s]"}}, "638f18d72e704478b58126fee0504686": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_78f79019e20f42fd986deb90cfeff8ab", "IPY_MODEL_08a677083e9a4a54a0ae80297ffbac0b", "IPY_MODEL_c51670dd5d7e4d54be75ebd22696d49b"], "layout": "IPY_MODEL_4078308e69eb4f789e37f28c5d267f85"}}, "63929ac1f75e4195a41d57a8181aa23c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "648c1d314aff4a5989917ea9cfb40ee4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "6584051a70a7443f811094bb8bb1b7ba": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_908454428cd14d04999314d984b9422e", "IPY_MODEL_e62c3cbcb0564497b0215b03c619f0a2", "IPY_MODEL_a7062d3276dc4584bd580ce31d23250a"], "layout": "IPY_MODEL_78d77f6a76f8468b9e2f7f42f4222213"}}, "65ddb1327fa64c2e903b003551fc22e8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "664cb138f57949dfbc68e0fdcbb7144e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1fc2fe3fcf5f492c91ad6fcefe9bbbd5", "IPY_MODEL_abcd702d4ccc492d93335618be15bf9d", "IPY_MODEL_df39f2726d03413ea1f46df9f7f4e16e"], "layout": "IPY_MODEL_6a1523a59cc84dab9155b7de57fc51be"}}, "667623f52df34426af41915a0c6857c7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1ad088f478ef4874bd0ba0e322387932", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_7015d1201f164432a3251437cca5be46", "value": 40.0}}, "668d9e48134f477fb12df091d0b7b2c3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_22f0e92c33f040b987eea86cc152ae75", "placeholder": "\u200b", "style": "IPY_MODEL_81d5b0aebde6419d9a21a71fa43b146d", "value": " 40/40 [00:01<00:00, 38.46it/s]"}}, "670deef521d54465a95b84ec4fd2a173": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "67e47d323fa6415896f68e98ed39242e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "6a1523a59cc84dab9155b7de57fc51be": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "6ab8985820164656ad60b9fbbe1e9db6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "6b3fd758e2414d97a24b249abf45b2d1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_05ed6750b13e4928846c6a3626a2a2bd", "placeholder": "\u200b", "style": "IPY_MODEL_03d4fb52bb2c460b8164142c6a8b3e8a", "value": " 40/40 [00:01<00:00, 40.41it/s]"}}, "6b5ce997abf143f8b24d43f30d9999a9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cac8b41d9a92451db93dccda6b5ccf2e", "placeholder": "\u200b", "style": "IPY_MODEL_2394fc85d30d457d9edfc5ed0c0718e2", "value": " 40/40 [00:01<00:00, 39.96it/s]"}}, "6b6968df7bb34058b2548fc8d67f10b7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "6bafb7e9a54a4abdb2b72bb7fdef79a6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "6c12cb82a52f4703a3afcbee54c82e3f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6c19c1928aad4db281729dceb27947b2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6c2d3949d97443bbadb861144c5a9403": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1489d138c9cd4c9bb49c6ebfed2a1317", "placeholder": "\u200b", "style": "IPY_MODEL_34569022c125403fa42f989cd7a6677a", "value": "Validating: 100%"}}, "6c6a4a78c5244926b5ab585c71b22b31": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_c6c19d03f7334159909e66b6419d5085", "IPY_MODEL_32afa96f636c4f8eafd51af14244276d", "IPY_MODEL_9d369d8ae62d4315b36fad5753458e06"], "layout": "IPY_MODEL_27899444bd854cbbb68957cc37bdc15d"}}, "6d08e235cef542568fd7741e38ae73bb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_9f7e30219ae345bbae61342a7d3415de", "IPY_MODEL_0a6e971473a845dbaf7dd70544a4fb4e", "IPY_MODEL_e3f10177a72948828fa102c2424cdbb1"], "layout": "IPY_MODEL_2ecfef770cbf42c39241cba7dd6319b9"}}, "6d3d89cfaac34c87bb9d4abecf5ca39c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6dcdd5a2689e4a859bacf94fa50b7b65": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1e4437d1f3004a869ff875f935e6096e", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_530f74cfa1894309b640a8910bafcccc", "value": 40.0}}, "6dda5e78cb8e4728a315e1c052bd7bc9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6ebcce28d37747fabd71debd16e24ec5", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_237abb092dc24a03977a37e02a700282", "value": 40.0}}, "6e03ab437e8043268a6de9dbe99085c7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_dceda10c727d4cce9a9976d60896b888", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3f1b09ad47b74af8bb89b4950d8ef999", "value": 40.0}}, "6eac74d845bb40709e203b62c6b0453f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_779896bca5d940b280a04b700f733cfb", "placeholder": "\u200b", "style": "IPY_MODEL_21e0696ab83b479bb28bdf8ad31a6a01", "value": " 40/40 [00:01<00:00, 40.75it/s]"}}, "6ebcce28d37747fabd71debd16e24ec5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6ebf16d170884218836f567c6624cf1c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "6f6727a7c5ce49a68d254766bd2c56f3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6f926ce2f6504e2caa5992aad503e942": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "6f9a37425e114b73801eeb7e97580200": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6faea2c255e14670a7afbcb3f1f68fc3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "7015d1201f164432a3251437cca5be46": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "704e66960a204403a5f4f89a9a15ead1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "71223c1c01744836a04d36ae0daeda23": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "71e268e4a03840a480f268ce072f5be8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f792ef8d3d3642e68f7b5f226a1dcd5e", "placeholder": "\u200b", "style": "IPY_MODEL_289dba33db8a44b58178c628b4c1f3da", "value": "Validating: 100%"}}, "71e68f3d7467406ba056edd127a4b8ec": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "721f24953e8f4d09bdcb431bb7b0d3cd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7223547950ec4f0abb4178506b4b5f93": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "727948d722bc4f8a8a8a73e8c3db95b2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_95e89ee09e5447c9b984d38c6f6715e6", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_e4c405cc7a194ecb9d121ff54b15d90e", "value": 40.0}}, "729ae8a5142b414d879d0c49ca9d9742": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_3d6312bdfa8e4d7baacc6e87d7b8eebc", "IPY_MODEL_00546a0bf8f647af912556b130e3909c", "IPY_MODEL_0440b10cf4f441fe843002eec4c8613e"], "layout": "IPY_MODEL_4dac2f6ceaad4780b4452daf42ff31c7"}}, "72a44be9f81f4117bf7754b66560ad0a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_99113cd146b44349a75e67efec13ccb8", "placeholder": "\u200b", "style": "IPY_MODEL_5b065d928b3a4d6eab7911a18dccc14c", "value": " 40/40 [00:01<00:00, 41.30it/s]"}}, "72b9b7bf3f244f3c82de65666a8a0581": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "731875ee32dc4c7b925239b1c3d08b9b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7337db267f3d419ab12ca1ecde8bec6d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b4c522f9247b4ffdb77962a600ab9135", "placeholder": "\u200b", "style": "IPY_MODEL_9920ec0a3fe946dcbf869b1e03f31e6d", "value": " 40/40 [00:01<00:00, 38.03it/s]"}}, "739bd3b55771434897638447109dadbc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ba40c658c1c645df85b0edd784e70f67", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_d04a087ecb834c029b6dbc65f40d903f", "value": 40.0}}, "73a78a6b3957447da7f88492c1c0de36": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_75e902dbc9734435b9903de6cc344530", "IPY_MODEL_19ef5cd49b4e4f16aa5d557bede8de6e", "IPY_MODEL_9199845a8a8b450595630238279489de"], "layout": "IPY_MODEL_a4901b611fe04be69811d4abec2f979a"}}, "74553b52649d485681cc6171b8367bdb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7512d436c11543f7ae1e7b77269779bc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7525800113e049808ea8b820e07125f8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4ac072f3e25e4c02b3b3e0e18e406a9c", "placeholder": "\u200b", "style": "IPY_MODEL_552fc188b09645f4b397b21d960daf93", "value": "Validating: 100%"}}, "75b74524c9ce4147a12ee83304575022": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "75e902dbc9734435b9903de6cc344530": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5107381a8622411cb492283123901aa0", "placeholder": "\u200b", "style": "IPY_MODEL_9dd7290bc6c9434da78d89ac36b662fd", "value": "Validating: 100%"}}, "7681fbb8aa304c03a9732a9c95f63ef9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "76c56c21d24541e49d001359174d8a06": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "76d6082fd84947949437d5809e934151": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "76f4c9460c2544fabd27af4240792134": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "77265d2f46524f789f5803628aaa8bb8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "779896bca5d940b280a04b700f733cfb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "779ed45ace6b4434a574e897384a0f52": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "77fe203b76bd43c2a5dcd03f0fa19fe9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1dcd0d34b8304b14a795e394769c5e73", "placeholder": "\u200b", "style": "IPY_MODEL_31a45a8c81864c48be13eecf563fa8e6", "value": " 40/40 [00:01<00:00, 40.76it/s]"}}, "78097292f6484ec1ade092c7bce9f457": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "781c2d73c32241af8536cea64f470739": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "danger", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a5c3635b42814a7982cca677d130aabf", "max": 2.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_87e730ffae1c4c54b9e8fe45d15ac50d", "value": 0.0}}, "78d77f6a76f8468b9e2f7f42f4222213": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "78dcf8ab81484972bef98ee4808976a3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_e87d5f0d76364422b001e9ba2009efd6", "IPY_MODEL_42d5b26f1cb04b7495bb753b844e9fd6", "IPY_MODEL_e14d135655704cee9e05ebdae198a92d"], "layout": "IPY_MODEL_dd99a71bd47844b69476dbc31a63e3c5"}}, "78f79019e20f42fd986deb90cfeff8ab": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_dae97dcd99ab412794e592f9eef4fcdd", "placeholder": "\u200b", "style": "IPY_MODEL_9b768c3f38414e3f88ae0c3add6475e4", "value": "Validating: 100%"}}, "79efd9bc5e2f4515acb71a95a0f1228a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7a959e215a164e54aec9c5c1729fb4cd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_3ca4a11a4bf24aa1a58ce41c7d3d2498", "IPY_MODEL_98b2037a54ef42188de9446df543151b", "IPY_MODEL_1d3b103a11964a219559f9f13ba9ac28"], "layout": "IPY_MODEL_000e17cab12b4a16a5bb24fd4c8e6c3d"}}, "7bab0316f29c454e8d78451bcfce3fdf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4b809c67a3f2448c9cfd7c34b8aa453f", "placeholder": "\u200b", "style": "IPY_MODEL_e3bdf937acbc4aec97813939d969d48c", "value": "Validating: 100%"}}, "7be887614c9a4b02bafb06c04247b854": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_be6125a2050e4c97ad54c5e536c7afdf", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_beaa1e52816c41c0967f26b2e1c78546", "value": 40.0}}, "7cbdcb186be240daa066b9a04ed3dca3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7cf1b98468f946d29dce76701c9dc87a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7d42a6086e82421a9e595b43ad729c72": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2ca4d5329b084df0b17a5737993cfb43", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_174fb53b8ed247ebb714ec07b7abf26e", "value": 40.0}}, "7df7c6698b064aaa92cbabaa7e6167dc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_8ad6c9c3073c49c1bfd83c6240832ab5", "IPY_MODEL_a0c98a98b6c84325a3ad30bcc5040c03", "IPY_MODEL_fab8109d1b334fe781851edfb02cfb44"], "layout": "IPY_MODEL_9c8adb261d2042fca1791aec070ea340"}}, "7e8eee7d4e4b4d208dc8aaee31968781": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8b2c33d6154d4557b30ef5e12200e8ee", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_9c532ce1146d4e1dbbcd2b3f782ff80c", "value": 40.0}}, "7eabd69400ff400daf651d434b6dc643": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7eed8b7bd0bd41738ad7f3cef6dabfbc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ee0b4a9189cd4eb1ad630d7d45f10283", "placeholder": "\u200b", "style": "IPY_MODEL_e442a229b0e14efbbe6bbe05d188c8d7", "value": " 40/40 [00:01<00:00, 41.20it/s]"}}, "7fca13154b964dcd891a26420f4ecc4e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "80e99363f6634d5589ad8a66fe027ed1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "810fdf5d48554b6198f4f470baffc034": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8efc1a19d4f14cd8b996a4982ffab9d6", "placeholder": "\u200b", "style": "IPY_MODEL_ab84751327534b3990319ea653ae2a62", "value": " 40/40 [00:01<00:00, 38.22it/s]"}}, "81d5b0aebde6419d9a21a71fa43b146d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8249bc65e7c14b708596bd66de402e61": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "82af5d90d4084ce9875b6e7ed0a8be8d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "82df0c7e18334bb29176ed5ebdc8a6da": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "83efcf75a4b14eb48238dfe52978f137": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "85b4d00008d84934acb2a3a0300977da": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "86557445ad984f038bc35ea22977b002": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3c2777e8a64f4057a3384b45af8ad60e", "placeholder": "\u200b", "style": "IPY_MODEL_8af8badd7aff46e1ae830a787428f307", "value": "Validating: 100%"}}, "86dbf891d6594a20a46e44de2dde8500": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "87e730ffae1c4c54b9e8fe45d15ac50d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "8822777752c042978be232ecf99811bb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "88359b1570cf4d278afcc12b52ca5cf3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_36c8c922093b41fcab2400f631bdc06b", "placeholder": "\u200b", "style": "IPY_MODEL_97c8405dea494331a4b840d715774e9e", "value": " 40/40 [00:01<00:00, 41.18it/s]"}}, "887380ee431440efa0fa779b58a9070f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "88a8d15063f74146bb9740c199a0263c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "895f50a960ef4de58e09ffaab83498a7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "8a0481c1451549c0bc682a58facfdc39": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8a088cec1f3e490ca376df311ac639c3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "8a11d75adfed4ef2a84d4b92fb8a51e3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "8a81ec850d2a4f89bfc568ec39f84e1d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d061820e73bb4ae7b1fc4384aaa911ef", "placeholder": "\u200b", "style": "IPY_MODEL_e0938103b55e4109bba1bd6a4c9c4a39", "value": " 40/40 [00:01<00:00, 41.20it/s]"}}, "8aa60a1e120540efb36aefb2ea7c6158": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_6c2d3949d97443bbadb861144c5a9403", "IPY_MODEL_2ace45c17feb45ddb2ece2fe0ba1f38e", "IPY_MODEL_6eac74d845bb40709e203b62c6b0453f"], "layout": "IPY_MODEL_f352e4d278dc4cda94f82a3846236f56"}}, "8ad6c9c3073c49c1bfd83c6240832ab5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b97c2ea5358243679d1d4e31a8b05cbc", "placeholder": "\u200b", "style": "IPY_MODEL_721f24953e8f4d09bdcb431bb7b0d3cd", "value": "Validating: 100%"}}, "8af8badd7aff46e1ae830a787428f307": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8b1a113312574d8a96b116058e4c9251": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "8b2c33d6154d4557b30ef5e12200e8ee": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8bc8667b056f432c9256e42bb212bab5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8c9494e571964db491151ef9b8b845e2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8cb60ad796bf4128af276b330260edca": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8d180bcc608e40349d00ad4f02174832": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8e8a89ab50044f39b0bd15a946a0d30e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "8ee337c0a7fd40dd87d9999965079d66": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_024680be6f384e20a19910a8db277616", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_8a11d75adfed4ef2a84d4b92fb8a51e3", "value": 40.0}}, "8ef7076c24ac4e3da9bc42adb84288d7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8efc1a19d4f14cd8b996a4982ffab9d6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8f2bc8575c9d477aae95569141884d3b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "908454428cd14d04999314d984b9422e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a50b3137d9c9433e9f6c9bfcc120cafc", "placeholder": "\u200b", "style": "IPY_MODEL_cce80a39b9d840f4a7dd7b3817da1de9", "value": "Validating: 100%"}}, "913954479b9e42789254d32050020788": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ebd6e86e6d364bfda696270bcd1285bc", "placeholder": "\u200b", "style": "IPY_MODEL_405fe7ec85844f918ad93c8cd2daa06b", "value": "Validating: 100%"}}, "9156902c48df4de4871d0325cf148752": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "9199845a8a8b450595630238279489de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1f2fa122be4545b6af4db56d669469e9", "placeholder": "\u200b", "style": "IPY_MODEL_6f926ce2f6504e2caa5992aad503e942", "value": " 40/40 [00:01<00:00, 37.06it/s]"}}, "9204f19c687749b4b9d8c58c9c5936d2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_ec1a8cd04f67429fa8fa11d2dabea42a", "IPY_MODEL_d0efda7e12494752b39085bbffd4a6d1", "IPY_MODEL_9253725ef50148e48e5d8a1a1c30847b"], "layout": "IPY_MODEL_e9d179241fa845b98f5d31c870a271d8"}}, "9253725ef50148e48e5d8a1a1c30847b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c62e8526bfe34ecda82253d53329b7da", "placeholder": "\u200b", "style": "IPY_MODEL_3962343bfb0b44769ef42b5c98983b9c", "value": " 40/40 [00:01<00:00, 41.08it/s]"}}, "92978f1abdd34477bb7ec0325a838cc7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "93b06cad51a14b71911ec76dcaa59e83": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9438d4a6012b47c8807ebda5adce1712": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_36208c417aec4e8b8cc4b4d36db804bd", "IPY_MODEL_1791c0d96d094df2ace36a97551cfff6", "IPY_MODEL_0e7532eb90c044f0b8dc0d480a9a8f98"], "layout": "IPY_MODEL_1243b677bc9d45e5b6e28a1e33612d18"}}, "953da11bc35d4b51ab1dea85dad0a8bb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6c12cb82a52f4703a3afcbee54c82e3f", "placeholder": "\u200b", "style": "IPY_MODEL_41e40cffb2094d97afd7a8e037cf0d5b", "value": "Validating: 100%"}}, "959f41fc01f14eb896043296b3b9fa2e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "95dd373c362b4f4f989389cca1bf9bba": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "danger", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_93b06cad51a14b71911ec76dcaa59e83", "max": 2.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_2762fd8f9f0c4c4e87ebc2ca9ea843e2", "value": 0.0}}, "95e821bc924e42b38ef068733a15a96e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "95e89ee09e5447c9b984d38c6f6715e6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "962e49594a8b455f8f3f5c9501deb987": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_e773d5290c3c481e80ec46e4337703ac", "IPY_MODEL_beb2d424cca549e3994971502cc2bc15", "IPY_MODEL_392d211b8a6947759a184062f2519e0c"], "layout": "IPY_MODEL_2ce48d7137ef47e79912f9a78bf1ca06"}}, "9693ff87ee62414e997ed8166a030243": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "97c8405dea494331a4b840d715774e9e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "97ca9563964441899f571f985aa19800": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "9893b65283724b288b89fd1114dc79f5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b8c893a656f44893912011b8ced212aa", "IPY_MODEL_3d2af261942848d3a2b6c9282837ebeb", "IPY_MODEL_bdca9160cad440f58fe24de81345b6fb"], "layout": "IPY_MODEL_06a89e7eb1cf4eb9b31b1792b6022475"}}, "98b2037a54ef42188de9446df543151b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c658abd81ef747f3aacf84adeabaa613", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_6b6968df7bb34058b2548fc8d67f10b7", "value": 40.0}}, "98c2bb8dfd9f42e7ba3a70833d3a9cf2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "99113cd146b44349a75e67efec13ccb8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "991c9442e91f483792474ebe4f09d152": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9920ec0a3fe946dcbf869b1e03f31e6d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "996b6c65885c4495b49d1f4e857026a2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "99ce588597484149b5d5dfe5cbd055a1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9a0dc5ee46ef43ab9610085cd998f750": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_10e2de30c7fa4812a7c096bfec3334ad", "placeholder": "\u200b", "style": "IPY_MODEL_82af5d90d4084ce9875b6e7ed0a8be8d", "value": " 0/2 [00:00<?, ?it/s]"}}, "9adfc41d30154a99938b0d97fa767e21": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_5c03b886ad694e53861e7dceebdf0268", "IPY_MODEL_33817b1ad96043c8b531eced9f189b33", "IPY_MODEL_55b36c54b9f14e74aec831f953af07ea"], "layout": "IPY_MODEL_8b1a113312574d8a96b116058e4c9251"}}, "9b768c3f38414e3f88ae0c3add6475e4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "9c0e4986ee594a8abd5a65453b5d896a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9c532ce1146d4e1dbbcd2b3f782ff80c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "9c8adb261d2042fca1791aec070ea340": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "9d369d8ae62d4315b36fad5753458e06": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8cb60ad796bf4128af276b330260edca", "placeholder": "\u200b", "style": "IPY_MODEL_535725f3fc3d47d1834dd8c5512c4be3", "value": " 40/40 [00:01<00:00, 41.09it/s]"}}, "9d95159b37974aff8914b24c0d943a37": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "9dd7290bc6c9434da78d89ac36b662fd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "9ea460da748d4d009fdb4416fc5ba362": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b00d3649a1ed4aee83941ec3499cb5b1", "IPY_MODEL_95dd373c362b4f4f989389cca1bf9bba", "IPY_MODEL_5b9dddecff0f432da833b80160ad7568"], "layout": "IPY_MODEL_98c2bb8dfd9f42e7ba3a70833d3a9cf2"}}, "9ef0cc8972ec476babb87467b9ff6a00": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_abca9201b3a7431898b41f1296e9e47b", "IPY_MODEL_a1a3aa50e30d46fbb95b70d42d8134de", "IPY_MODEL_668d9e48134f477fb12df091d0b7b2c3"], "layout": "IPY_MODEL_05414796bb8b4c4095db6029e7c298ba"}}, "9f7e30219ae345bbae61342a7d3415de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fb6196786a4c449da5bfc3b74974f5b7", "placeholder": "\u200b", "style": "IPY_MODEL_88a8d15063f74146bb9740c199a0263c", "value": "Testing: 100%"}}, "a0c98a98b6c84325a3ad30bcc5040c03": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d19b21b31c1b4251989052b2a9405736", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_0014f8f4f49841aaa7c1a64c57b2d49b", "value": 40.0}}, "a0e32c7a94a744f8af40b68c9d2f8113": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a0ed6130ec664309837dff68eb1609a3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a10691f4cc5f4264821e505be23d5c0d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "a14120d2181444b3a233980b6ca6bbe4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "a164e2d5015a442da8aaca5abe9b64ea": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_118e280fa5af441b80565177c090e25d", "placeholder": "\u200b", "style": "IPY_MODEL_cc76969b57f54e5a99a1c2ca9a23d657", "value": " 40/40 [00:01<00:00, 40.74it/s]"}}, "a1a3aa50e30d46fbb95b70d42d8134de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1346ed84515b4784be24ab3b6739983d", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_b4d729133c2b48c0a791338890f0e427", "value": 40.0}}, "a25c3623d687465cae93c39b76c3ce65": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7cbdcb186be240daa066b9a04ed3dca3", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_31a6932351aa4dfb84a2d26f4f71bf82", "value": 40.0}}, "a34763ee634d44768c767c2355e57e7a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a47d56c8623841ffba64e31bb3294bfa": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a4901b611fe04be69811d4abec2f979a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "a50b3137d9c9433e9f6c9bfcc120cafc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a56807f9e014434fb9f5ec740db1cdf4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "a5ac4f0fd6fe4ff0a93e849b930717a6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "a5c3635b42814a7982cca677d130aabf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a5c6db3bbffd4e0fa2ae551c18a03b1a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a7062d3276dc4584bd580ce31d23250a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_bd88a232244c411dbb7fd992c1027448", "placeholder": "\u200b", "style": "IPY_MODEL_d728bc80d8df41ea88685e94f199eff8", "value": " 40/40 [00:01<00:00, 38.13it/s]"}}, "a71887abf4ff44b99939cdb9fdd47db7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_3ba524598e40487fa22cc827d0fd284b", "IPY_MODEL_abdb8fc6f4ea4ee2b3e679e591afc37e", "IPY_MODEL_f187affa322744d7822bac58e3d366ee"], "layout": "IPY_MODEL_2541fdc2e94247aab508ae79c3def6d7"}}, "a7293a6474ce4e9499a1d0159c9d2034": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_2c53cdd37f474e1e8845c3e070be0b51", "IPY_MODEL_739bd3b55771434897638447109dadbc", "IPY_MODEL_a164e2d5015a442da8aaca5abe9b64ea"], "layout": "IPY_MODEL_2b7f8addc532411ba1d455956c9ef33d"}}, "a73cdacebbd14b3b9c697a62b8a758ee": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_48bea9eace8c48a9a5aa452d5dfed96e", "IPY_MODEL_0046c6e7fea345d28a76745b89586893", "IPY_MODEL_382dc007ef7b4baaaf107dcd94fc67ef"], "layout": "IPY_MODEL_51c67493b2aa4e04af474b9d59af6a6c"}}, "a85a055c9d844ad2b9bacb9583402386": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a94008efe3024367adc524556ecb73e9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ffb2fbfef1064080b54b648785376dc5", "placeholder": "\u200b", "style": "IPY_MODEL_d1e0d12030ef4e4a8b237e92e1889f0e", "value": " 40/40 [00:01<00:00, 41.11it/s]"}}, "aa81d9f10a1343688ab35aed5fa9a733": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "aa9b2cbc1307478e805f735d938a3546": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0bb72c82108147e1a13350b851a960e5", "placeholder": "\u200b", "style": "IPY_MODEL_62c0abdd86f543a6860fb5b514b6028f", "value": "Validating: 100%"}}, "ab1556a4dab6495da3a22de9eb920871": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ab84751327534b3990319ea653ae2a62": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "abca9201b3a7431898b41f1296e9e47b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b661d6f3970340209af6cdc59f9bc324", "placeholder": "\u200b", "style": "IPY_MODEL_174cce3b873b4c2a8e173f5805d837c8", "value": "Validating: 100%"}}, "abcd702d4ccc492d93335618be15bf9d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d2e50bfa91c0493d8c1e5e956f46b514", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3630871e0dbb473f929745f5c5d44b5d", "value": 40.0}}, "abdb8fc6f4ea4ee2b3e679e591afc37e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_78097292f6484ec1ade092c7bce9f457", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_8822777752c042978be232ecf99811bb", "value": 40.0}}, "abeba1de74a4420d9a46da3ee64dab2d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ac7a1aa262334ec094a633c7aed94a72": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ad8f945d28464a879d7686f7d7595509": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ae8bb373e5d7456d9627b43c3d1fa8ed": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "afac60f3d22346c3a68852bd55ea790a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0027c59a46544bef8bf57f2d9eedb383", "placeholder": "\u200b", "style": "IPY_MODEL_8f2bc8575c9d477aae95569141884d3b", "value": " 40/40 [00:01<00:00, 39.90it/s]"}}, "afcf640856fe47e29b40125065435826": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c3e1b0656e78474886a5dd89b80d99cf", "placeholder": "\u200b", "style": "IPY_MODEL_029ded4281f84e23a64442a8c51ad87d", "value": "Validating: 100%"}}, "b00d3649a1ed4aee83941ec3499cb5b1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2f8037b7d6f943b594942290d79b6130", "placeholder": "\u200b", "style": "IPY_MODEL_e5b1df60b8d64f8bbca251ea1aa45fc9", "value": "Validation sanity check: 0%"}}, "b09d28854ea24312b741c7e4bef86735": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b131dac2a55c4445bc5f49457e59cefb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b1954a59bdbf45ce91afe0766729fc53": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f5b91ccd114d43dd83abfe60d5666295", "placeholder": "\u200b", "style": "IPY_MODEL_b09d28854ea24312b741c7e4bef86735", "value": " 40/40 [00:01<00:00, 37.71it/s]"}}, "b1b504c2adc34ee99d363ceaefa43e70": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b4c522f9247b4ffdb77962a600ab9135": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b4d729133c2b48c0a791338890f0e427": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "b52a63138c094b7893e021bc49c0c89e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "b5b4aa182c534419be2c9f72ca2f62d6": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b5f729672dbe4f1784a511c5841f4e7b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_496688b3d51445aebf14a8ccbd529eed", "placeholder": "\u200b", "style": "IPY_MODEL_0b34e1a046f14629b409b16c1f1bd8e1", "value": " 40/40 [00:01<00:00, 40.96it/s]"}}, "b661d6f3970340209af6cdc59f9bc324": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b742a674c72f440aa5399890549262c4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b77a76f27de54c2183bea1ad9864dc95": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b825fe85788a46119b9da2f3f0168a69": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a34763ee634d44768c767c2355e57e7a", "placeholder": "\u200b", "style": "IPY_MODEL_c3b5ddb8b461432d9979ea0473d89c60", "value": "Validating: 100%"}}, "b8b046652eb14f2fbb40712fbe04995b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_7525800113e049808ea8b820e07125f8", "IPY_MODEL_3f223cafee19421782131b9f9bfcb873", "IPY_MODEL_b5f729672dbe4f1784a511c5841f4e7b"], "layout": "IPY_MODEL_95e821bc924e42b38ef068733a15a96e"}}, "b8c893a656f44893912011b8ced212aa": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_99ce588597484149b5d5dfe5cbd055a1", "placeholder": "\u200b", "style": "IPY_MODEL_c3d08c9ab755494196759d9dccafc132", "value": "Validating: 100%"}}, "b9415f0976ae48e1a5371a5e9f8d84bd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b97c2ea5358243679d1d4e31a8b05cbc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b9de98eebdb34b8e9c55081ebd753057": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1259349431444f968af6416e4c991b47", "IPY_MODEL_6dda5e78cb8e4728a315e1c052bd7bc9", "IPY_MODEL_e7a99e4d130f498abb222496b5671ef4"], "layout": "IPY_MODEL_25f8a6e3d77844d2804a78cedcf79e5f"}}, "ba40c658c1c645df85b0edd784e70f67": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "baeb194ab5ed4089b8cfea9c622c7485": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c72cde952f804bb3a70852ecef68bcaf", "placeholder": "\u200b", "style": "IPY_MODEL_8249bc65e7c14b708596bd66de402e61", "value": "Validating: 100%"}}, "bd347db500c445b5914ab4c7ed5d0bd1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bd7102a0c9bc45d8a32134a863be9966": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_410b71b0a20941c089e536e4e128d13b", "placeholder": "\u200b", "style": "IPY_MODEL_f5699ec6e6c847ab807ce5bf3cb03664", "value": "Validating: 100%"}}, "bd88a232244c411dbb7fd992c1027448": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bdca9160cad440f58fe24de81345b6fb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c4d7717ac5a0497785625a98c346a91c", "placeholder": "\u200b", "style": "IPY_MODEL_d54e1ea3816541bca39a1e59a9817019", "value": " 40/40 [00:01<00:00, 41.62it/s]"}}, "bdd9067ae2bd48aa9eff55c12ba2a8de": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4a36ee3223aa4003bf6c9c68b41ebca9", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_6ebf16d170884218836f567c6624cf1c", "value": 40.0}}, "be6125a2050e4c97ad54c5e536c7afdf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "beaa1e52816c41c0967f26b2e1c78546": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "beb2d424cca549e3994971502cc2bc15": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_099f27f210e24769853bc51f75969ed5", "max": 197.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_67e47d323fa6415896f68e98ed39242e", "value": 197.0}}, "bfab16490d4d401ca39e5fcbfad30dea": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "bfe210ee12f24249b6194fdbd5990052": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c1609608c09b4580861adb27dd251f36": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_4a86d8dd5c4d43faa9b83359c8b579fa", "placeholder": "\u200b", "style": "IPY_MODEL_14ac48af733144429e4d011377b08b9c", "value": " 40/40 [00:01<00:00, 37.99it/s]"}}, "c1c99766fd58427bb5d1aa4bb932b494": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c1dd76b2378445f48f47c199006c16e9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_83efcf75a4b14eb48238dfe52978f137", "placeholder": "\u200b", "style": "IPY_MODEL_363da95944f24deaa5693756bf690a76", "value": " 40/40 [00:01<00:00, 38.12it/s]"}}, "c213c26b0b8547b0bba620a692676198": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1b1dac267473433fa46bd05bc37db269", "IPY_MODEL_6dcdd5a2689e4a859bacf94fa50b7b65", "IPY_MODEL_5107961954384c8aaaeba31c1b5bc4c8"], "layout": "IPY_MODEL_33f836df599e44f88641129b441a51fc"}}, "c27d2bf0ec8e4402a35653dd8da4b84d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "c362daa772754d47b0063b6671836a81": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c3b5ddb8b461432d9979ea0473d89c60": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c3d08c9ab755494196759d9dccafc132": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c3deaaeda2cb4bd3a122130339ba7bbb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c3e1b0656e78474886a5dd89b80d99cf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c4382d4da66d4ef58c7336a37a81ff5f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_2b19db000c0d4da2b4aa67aebc698ce3", "IPY_MODEL_781c2d73c32241af8536cea64f470739", "IPY_MODEL_9a0dc5ee46ef43ab9610085cd998f750"], "layout": "IPY_MODEL_e17fb5bf5733405e8c98c36e58ffcf7e"}}, "c4d7717ac5a0497785625a98c346a91c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c51670dd5d7e4d54be75ebd22696d49b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6d3d89cfaac34c87bb9d4abecf5ca39c", "placeholder": "\u200b", "style": "IPY_MODEL_7fca13154b964dcd891a26420f4ecc4e", "value": " 40/40 [00:01<00:00, 41.35it/s]"}}, "c57fece658f444f18bd89f142c343bb0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_baeb194ab5ed4089b8cfea9c622c7485", "IPY_MODEL_8ee337c0a7fd40dd87d9999965079d66", "IPY_MODEL_5d3e25d0f248411da504f0a0df9c0e2d"], "layout": "IPY_MODEL_d2e4ed49cb994cfe85d5c254f252ef98"}}, "c590712538fa4ef7bd045c44f9aec516": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1e664c703d4b4f20a8d8115990ec7248", "IPY_MODEL_7d42a6086e82421a9e595b43ad729c72", "IPY_MODEL_db277be0c1004f0599dd4d4275351024"], "layout": "IPY_MODEL_86dbf891d6594a20a46e44de2dde8500"}}, "c5a56a5d6926452cb6046dcad5b94e20": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_de5ca57613b141ecab054a6a3bcdd20a", "IPY_MODEL_667623f52df34426af41915a0c6857c7", "IPY_MODEL_7337db267f3d419ab12ca1ecde8bec6d"], "layout": "IPY_MODEL_6bafb7e9a54a4abdb2b72bb7fdef79a6"}}, "c5fad9a2cc4f42f887817c472070b776": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c5fc4d52ddb84b7082395834d134e2c4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "c62e8526bfe34ecda82253d53329b7da": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c62ef3187ae842bdaee3c4292b792d6f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c658abd81ef747f3aacf84adeabaa613": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c6c19d03f7334159909e66b6419d5085": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a47d56c8623841ffba64e31bb3294bfa", "placeholder": "\u200b", "style": "IPY_MODEL_5b7e893c983d44c6914b42a32154b513", "value": "Validating: 100%"}}, "c72cde952f804bb3a70852ecef68bcaf": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ca42033167d84b95828523a32f20fbc7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_d898ceb905a74a9f9549aef4295429e2", "IPY_MODEL_f59bb6ed94cf497f9ddbf63eb196ab62", "IPY_MODEL_42a7109f566a424199400b71f736ec10"], "layout": "IPY_MODEL_4202db89a7a746bab07560c550105366"}}, "cac8b41d9a92451db93dccda6b5ccf2e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cacae5ca1e9d4283b9afe8e1916e8618": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "cc0069da0700482b8bd07d5e61eccf14": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ccd0f6fa912f42549bb9d30d02738ef5", "placeholder": "\u200b", "style": "IPY_MODEL_7eabd69400ff400daf651d434b6dc643", "value": " 40/40 [00:01<00:00, 40.90it/s]"}}, "cc47c9cd42a140a590dddb6dc560a4d6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1db1255602ad4b929cd199e01fd9fa71", "IPY_MODEL_e031999ce4f140bcb9f659d91587a079", "IPY_MODEL_5a00a23b5f2348198bec2c1e8110482b"], "layout": "IPY_MODEL_3a9384ba911144e998da472f66de9476"}}, "cc76969b57f54e5a99a1c2ca9a23d657": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ccd0f6fa912f42549bb9d30d02738ef5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ccd261d7a5764d43bcacce36b0f5db99": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "cce80a39b9d840f4a7dd7b3817da1de9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "cd17e0a2a3524fc5a548b86c61953b77": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ced07f39454f482a834aa01b5f977282": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ced8e9bb515746e5930f529d1c4c3f2c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_23c2dfc91e574966becac4457d0b19d0", "IPY_MODEL_3d48472bd8584bd999a934e99e4534a2", "IPY_MODEL_1e5347ebd820410e88806a71d0bb8681"], "layout": "IPY_MODEL_340172c2cd084bbbb189896ccf76779f"}}, "cfd16f7563604087a407b97d5e1af29f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d04a087ecb834c029b6dbc65f40d903f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "d061820e73bb4ae7b1fc4384aaa911ef": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d0efda7e12494752b39085bbffd4a6d1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8ef7076c24ac4e3da9bc42adb84288d7", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3321bd2d04a74315b5b937f121591995", "value": 40.0}}, "d19b21b31c1b4251989052b2a9405736": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d1a0e1f95d0b400ba9deedf188ccef73": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "d1c9ce3bf4fc44fbb4daa41d31c1ed72": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d1e0d12030ef4e4a8b237e92e1889f0e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d22a5b5a07714a7b9584aad405faee89": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_52eae5f9929f4a0b962a3ff4447cea26", "placeholder": "\u200b", "style": "IPY_MODEL_2000b053e3ab49058a89a8910fee9653", "value": "Testing: 100%"}}, "d27e62cbd5554ebc9dd552a9264f037d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d2c31fdab421480389949e8f0af20cd0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d2c757a2385045d3ab0adcf707dddba1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "d2e4ed49cb994cfe85d5c254f252ef98": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "d2e50bfa91c0493d8c1e5e956f46b514": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d2f8ed4e980348e5a4e806296f45ab09": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_bd7102a0c9bc45d8a32134a863be9966", "IPY_MODEL_2d41f0971937454bbc65139c6f595f63", "IPY_MODEL_6b3fd758e2414d97a24b249abf45b2d1"], "layout": "IPY_MODEL_bfab16490d4d401ca39e5fcbfad30dea"}}, "d369517f48604be39f83df2af26cdd15": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d3f774b1b0404f198af51238375c574e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d407101ddcc74699a620bbd6a9628bff": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d49067dafd874b49a1c3d0288b0edf88": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d498421af6c6458ea4fe33bf22796e86": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b825fe85788a46119b9da2f3f0168a69", "IPY_MODEL_0adf97988f5d47ffb50136dfb49519e8", "IPY_MODEL_1c794c38f0124a2a8100f0179717f3fb"], "layout": "IPY_MODEL_d8a0309b11494a5cae5994c0c04a818f"}}, "d4c1bc09dddd49d28af187e6be4c1caf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d54e1ea3816541bca39a1e59a9817019": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d57038b8b2184a5eaaaaf182893cc796": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fd6f831c86034ff3ab7821932b3086db", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_9156902c48df4de4871d0325cf148752", "value": 40.0}}, "d581aedbcb7f48fb8c8dbeaa079ad651": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1ff9401002354c4396c387857469b5e9", "IPY_MODEL_3fabbc3365a24ae98e4ddcd52b206868", "IPY_MODEL_f2bfe826a71142faaf708abf3fcbfce6"], "layout": "IPY_MODEL_d1a0e1f95d0b400ba9deedf188ccef73"}}, "d593afd8891e4b05ba9a03051d458764": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e6cbac4b8c974514a58a947448fc412d", "placeholder": "\u200b", "style": "IPY_MODEL_04c97639949842bfb3979dd8712abe8b", "value": "Validating: 100%"}}, "d728bc80d8df41ea88685e94f199eff8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d73b688acdba44a2ad89c92ce6153a0c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d7b5e58eb4d3438d8a5b648692171036": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d27e62cbd5554ebc9dd552a9264f037d", "placeholder": "\u200b", "style": "IPY_MODEL_21bab32483d844888f86f71067e0f69e", "value": " 40/40 [00:01<00:00, 37.88it/s]"}}, "d7f60bf8643a49eca3a16d56091b4154": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "d7f9bf3cbe3f4160a09f5376dae9e096": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d898ceb905a74a9f9549aef4295429e2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7512d436c11543f7ae1e7b77269779bc", "placeholder": "\u200b", "style": "IPY_MODEL_0fb7a9bdc2ee4a88a7d79afdb165e0fa", "value": "Validating: 100%"}}, "d8a0309b11494a5cae5994c0c04a818f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "d8ff45a13d3c4930b4eac298d783b7cf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d9bb8edb324b4f09977eac68ceb45e63": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "da4e670e9d594e7394b38d518354f613": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "dae97dcd99ab412794e592f9eef4fcdd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "db277be0c1004f0599dd4d4275351024": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f57d9339abc84a62a49d3bacd275d7c9", "placeholder": "\u200b", "style": "IPY_MODEL_16ec8ca835ce4edbbf71ee881cd05bbf", "value": " 40/40 [00:01<00:00, 41.19it/s]"}}, "dceda10c727d4cce9a9976d60896b888": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "dd99a71bd47844b69476dbc31a63e3c5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "ddde61829a984873a4bbf6613184e2ee": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fb537059a7b449fa8effe868845859bb", "placeholder": "\u200b", "style": "IPY_MODEL_365291d5f1a94dc5a9d8da5f0d414f3e", "value": "Validating: 100%"}}, "de344810abe548119b3f8ddf3a1cc97a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "de5ca57613b141ecab054a6a3bcdd20a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e1e5b90601d94304b7ef56ced388330a", "placeholder": "\u200b", "style": "IPY_MODEL_faeb509fbf3f4e5f93992b33634adab1", "value": "Validating: 100%"}}, "de693622da2f4bb7b735600f02a60478": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "df39f2726d03413ea1f46df9f7f4e16e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ced07f39454f482a834aa01b5f977282", "placeholder": "\u200b", "style": "IPY_MODEL_648c1d314aff4a5989917ea9cfb40ee4", "value": " 40/40 [00:01<00:00, 38.21it/s]"}}, "df4ebdf10987433cb65c78dbf7e34b99": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_23ddf0adf4f64bdea807fc3c6d586e40", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_76c56c21d24541e49d001359174d8a06", "value": 40.0}}, "dff17ba1b5ae45e3851d3ab860b9f529": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6f6727a7c5ce49a68d254766bd2c56f3", "placeholder": "\u200b", "style": "IPY_MODEL_1ba5e824ccca492191ed59018571aa0d", "value": "Validating: 100%"}}, "e0207d263a5f435289f3c4fd79f2f0c3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ab1556a4dab6495da3a22de9eb920871", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_a56807f9e014434fb9f5ec740db1cdf4", "value": 40.0}}, "e031999ce4f140bcb9f659d91587a079": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_85b4d00008d84934acb2a3a0300977da", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_014b058f5dad4a9b90029ee5c0d1941a", "value": 40.0}}, "e0938103b55e4109bba1bd6a4c9c4a39": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e0fbc495e5d04f798a6cbfc91af1d0ed": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7681fbb8aa304c03a9732a9c95f63ef9", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_608e803ac821482db32298d68e280acd", "value": 40.0}}, "e14d135655704cee9e05ebdae198a92d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_023b923393b045618df10b498f0cbc13", "placeholder": "\u200b", "style": "IPY_MODEL_1593171a93284546a79ca15574055476", "value": " 40/40 [00:01<00:00, 41.08it/s]"}}, "e17fb5bf5733405e8c98c36e58ffcf7e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "e1e5b90601d94304b7ef56ced388330a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e248b9247393444b95f4e426e49403e1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e24b7c22fbee405c8d202b376aafa909": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_953da11bc35d4b51ab1dea85dad0a8bb", "IPY_MODEL_f7ca4e232d884ae6bd404288c78de3fe", "IPY_MODEL_638225841dcb42bdbc24ab03315c976a"], "layout": "IPY_MODEL_40ff73c7b3dc4556be5ecc1d35a6613f"}}, "e2538d2d42254355a24cb5bffdd4f083": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_86557445ad984f038bc35ea22977b002", "IPY_MODEL_7e8eee7d4e4b4d208dc8aaee31968781", "IPY_MODEL_88359b1570cf4d278afcc12b52ca5cf3"], "layout": "IPY_MODEL_00734065a4b748bf86e1fbb33f7a0561"}}, "e28368a911464b9db767854a5f048ca3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e367433e553a477ca6a56d8a25c5f16a", "placeholder": "\u200b", "style": "IPY_MODEL_d369517f48604be39f83df2af26cdd15", "value": " 40/40 [00:01<00:00, 38.04it/s]"}}, "e367433e553a477ca6a56d8a25c5f16a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e3779b041324461eb68a37851e899963": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "e3bdf937acbc4aec97813939d969d48c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e3d7871fd58d46e28fefff6d6f2d55ef": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e3f10177a72948828fa102c2424cdbb1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e3d7871fd58d46e28fefff6d6f2d55ef", "placeholder": "\u200b", "style": "IPY_MODEL_71223c1c01744836a04d36ae0daeda23", "value": " 40/40 [00:01<00:00, 37.17it/s]"}}, "e442a229b0e14efbbe6bbe05d188c8d7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e46414f0bbc2470a9a75e3c9a77582d4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e4c405cc7a194ecb9d121ff54b15d90e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "e4cb7f9a7537486cbddfb13c57ae7c09": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_39772d3d7ef847248356d5afe732000c", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_92978f1abdd34477bb7ec0325a838cc7", "value": 40.0}}, "e55684dd18f240efb5795f3d5ffdb25a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e5b1df60b8d64f8bbca251ea1aa45fc9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e62c3cbcb0564497b0215b03c619f0a2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b5b4aa182c534419be2c9f72ca2f62d6", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_a10691f4cc5f4264821e505be23d5c0d", "value": 40.0}}, "e6cbac4b8c974514a58a947448fc412d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e773d5290c3c481e80ec46e4337703ac": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3de93c5568264b628ce08d92a9e2bdde", "placeholder": "\u200b", "style": "IPY_MODEL_d4c1bc09dddd49d28af187e6be4c1caf", "value": "Epoch 29: 100%"}}, "e7942984bfe2435b8d1a6ca6dbdbc27e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e7a99e4d130f498abb222496b5671ef4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_26181fafa82f430dab9d294152e60ecf", "placeholder": "\u200b", "style": "IPY_MODEL_b742a674c72f440aa5399890549262c4", "value": " 40/40 [00:01<00:00, 41.28it/s]"}}, "e87d5f0d76364422b001e9ba2009efd6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9c0e4986ee594a8abd5a65453b5d896a", "placeholder": "\u200b", "style": "IPY_MODEL_8c9494e571964db491151ef9b8b845e2", "value": "Validating: 100%"}}, "e9d179241fa845b98f5d31c870a271d8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "e9eff4cf4c714f2680f7e77dbb5a372c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ea96711d4212428ba4dcc07e4ecba6bc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_125f9fd7e9d0489c86a06c1f7175f1a8", "IPY_MODEL_62defc9448f14799a799664a6e93e99d", "IPY_MODEL_7eed8b7bd0bd41738ad7f3cef6dabfbc"], "layout": "IPY_MODEL_38e6f6251cc247f2a70007beb7f40c80"}}, "ebd6e86e6d364bfda696270bcd1285bc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ec1a8cd04f67429fa8fa11d2dabea42a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3fcf6bf3438a429ea8d182a0f143763e", "placeholder": "\u200b", "style": "IPY_MODEL_60cce88461724ab582baf5e4e7653386", "value": "Validating: 100%"}}, "ec2f85ae2a11482b9d8967c431217991": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "eca7de566a424f17ae0b4b0d2f99f855": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "ecb828657a224596ac79b7ca2953edc0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "eceec782c1ce454dbfcadced855e791e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_aa81d9f10a1343688ab35aed5fa9a733", "placeholder": "\u200b", "style": "IPY_MODEL_b77a76f27de54c2183bea1ad9864dc95", "value": "Validating: 100%"}}, "edd32f0ef84348a4b07891fb8ed6f0ab": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ee0b4a9189cd4eb1ad630d7d45f10283": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ef2f42b7a9cd4ad59cf933bf03cdcbc4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_f62d0978442b4f01b2c26d23d2451d4d", "IPY_MODEL_3897c2a7c1e1445fbc604c1a741c98e1", "IPY_MODEL_a94008efe3024367adc524556ecb73e9"], "layout": "IPY_MODEL_05a2d51877d9418b8417e35cea00aed8"}}, "ef90020e0959424193c5298f4ab9a190": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "efb67b6f296c44fdb4309f206eb09f4a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_dff17ba1b5ae45e3851d3ab860b9f529", "IPY_MODEL_727948d722bc4f8a8a8a73e8c3db95b2", "IPY_MODEL_e28368a911464b9db767854a5f048ca3"], "layout": "IPY_MODEL_76f4c9460c2544fabd27af4240792134"}}, "f0607cd375bf48418a683bc48438d6a0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "f078f962d66c497b8e95231564665108": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_0efe49958483403d970448b78c035fce", "IPY_MODEL_258cee43a0be4850ac25f425c4b1cc3f", "IPY_MODEL_810fdf5d48554b6198f4f470baffc034"], "layout": "IPY_MODEL_d7f60bf8643a49eca3a16d56091b4154"}}, "f187affa322744d7822bac58e3d366ee": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_35ac61ebf2d94417a183c5e85963e76f", "placeholder": "\u200b", "style": "IPY_MODEL_f96af388a83c42afb6866a6f2b22b295", "value": " 40/40 [00:01<00:00, 39.67it/s]"}}, "f2bfe826a71142faaf708abf3fcbfce6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_471d838b49da478fa34ccc96b6ec926a", "placeholder": "\u200b", "style": "IPY_MODEL_ecb828657a224596ac79b7ca2953edc0", "value": " 197/197 [00:13<00:00, 14.21it/s, loss=0.0285, v_num=0, val_loss=4.160, val_acc=0.104]"}}, "f352e4d278dc4cda94f82a3846236f56": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "f4fb223218124ea6a02efef412bac44a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f5438eeef53847ba95d157f56e438488": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f5699ec6e6c847ab807ce5bf3cb03664": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f57d9339abc84a62a49d3bacd275d7c9": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f59bb6ed94cf497f9ddbf63eb196ab62": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a0e32c7a94a744f8af40b68c9d2f8113", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_0447aa40a49344e79e2e683d6819d5ec", "value": 40.0}}, "f5b91ccd114d43dd83abfe60d5666295": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f62d0978442b4f01b2c26d23d2451d4d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_01a67df105214806b42a4974f14e5792", "placeholder": "\u200b", "style": "IPY_MODEL_e46414f0bbc2470a9a75e3c9a77582d4", "value": "Validating: 100%"}}, "f669afd47abf4c0a9b3b79df62009f7e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "f6f68d546ecb45ffb73be50d80994d1f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f71b6be05b7d43f4b438780bf1f6fab1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_ddde61829a984873a4bbf6613184e2ee", "IPY_MODEL_d57038b8b2184a5eaaaaf182893cc796", "IPY_MODEL_8a81ec850d2a4f89bfc568ec39f84e1d"], "layout": "IPY_MODEL_ae8bb373e5d7456d9627b43c3d1fa8ed"}}, "f792ef8d3d3642e68f7b5f226a1dcd5e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f7ca4e232d884ae6bd404288c78de3fe": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_abeba1de74a4420d9a46da3ee64dab2d", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_895f50a960ef4de58e09ffaab83498a7", "value": 40.0}}, "f90f1336e56a4cacb8b5f8666b6c9b16": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f96af388a83c42afb6866a6f2b22b295": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f9abae85aee34c04a321d4e3410e2352": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fab8109d1b334fe781851edfb02cfb44": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6c19c1928aad4db281729dceb27947b2", "placeholder": "\u200b", "style": "IPY_MODEL_fe5d668723a244498cb33494e5bee2f2", "value": " 40/40 [00:01<00:00, 30.04it/s]"}}, "faeb509fbf3f4e5f93992b33634adab1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fb537059a7b449fa8effe868845859bb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fb6196786a4c449da5bfc3b74974f5b7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fb9d723beb3d4e36aaab60a344476126": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fba15de3823947c997157fd19ee23ba4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "fd6f831c86034ff3ab7821932b3086db": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fe5d668723a244498cb33494e5bee2f2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fe7af92e4442430d96daba437d4a374a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b9415f0976ae48e1a5371a5e9f8d84bd", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_d9bb8edb324b4f09977eac68ceb45e63", "value": 40.0}}, "ff4bf4ad13c04a8791e8283c83765a05": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_71e268e4a03840a480f268ce072f5be8", "IPY_MODEL_07c8f9ed1f6d48bbb2ccf3bc58ee548b", "IPY_MODEL_d7b5e58eb4d3438d8a5b648692171036"], "layout": "IPY_MODEL_2ffa108ad10645a1b18413bd56b7d32a"}}, "ffb2fbfef1064080b54b648785376dc5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ffde3dbed47844968f5b0057538b78ae": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/datamodules.ipynb b/source/notebooks/lightning_examples/datamodules.ipynb deleted file mode 100644 index 6c3f0ff..0000000 --- a/source/notebooks/lightning_examples/datamodules.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "61739b4a", "metadata": {"papermill": {"duration": 0.035332, "end_time": "2021-12-04T16:45:52.526798", "exception": false, "start_time": "2021-12-04T16:45:52.491466", "status": "completed"}, "tags": []}, "source": ["\n", "# PyTorch Lightning DataModules\n", "\n", "* **Author:** PL team\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:53:01.674205\n", "\n", "This notebook will walk you through how to start using Datamodules. With the release of `pytorch-lightning` version 0.9.0, we have included a new class called `LightningDataModule` to help you decouple data related hooks from your `LightningModule`. The most up to date documentation on datamodules can be found [here](https://pytorch-lightning.readthedocs.io/en/latest/extensions/datamodules.html).\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHUAAAAUCAYAAACzrHJDAAAIuUlEQVRoQ+1ZaVRURxb+qhdolmbTUVSURpZgmLhHbQVFZIlGQBEXcMvJhKiTEzfigjQg7oNEJ9GMGidnjnNMBs2czIzajksEFRE1xklCTKJiQLRFsUGkoUWw+82pamn79etGYoKek1B/4NW99/tu3e/dquJBAGD27NkHALxKf39WY39gyrOi+i3xqGtUoePJrFmznrmgtModorbTu8YRNZk5cybXTvCtwh7o6NR2KzuZMWNGh6jtVt7nA0ymT5/eJlF9POrh7PAQl6s8bGYa3PUum//htmebVtLRqW0q01M5keTk5FZFzU0oRle3+zxwg5Hgtb+PZiL/ZVohxCI+hL5JgjmfjPxZ26+33BG3dA+ealHPM4gQAo5rU59gsI8bRvl54t3Ca62mvHyUAhtOlLd5WSQpKcluBjumnoCLs1EARkVd9E8l3p9y2i7RbQ1B6pFwu/YDgW8KbHJHMTQrwnjz2oZm9M4pavOCfo5jWrgCaaMVcMs6/pNhDr0+AMN93XlxV7R6DNpyzi7W/OE+yIrsjU6rTrbKV5cd/pNyItOmTbMp6sbBB+EqaYJY4cWE3VUciNt1TpgfcRFv71Fi54xT5kSoyLvOBEJMOMxWXkFlBeBSX4u6Zkcs+3KszYRtiapbNRqF31UgetVuc8z9vBXIv1qD+F1f83B6uDlCUyfsZGepGPpmg01OB7EITQbhS9ribKy+DmP1DUiClLz4bnIHVOqa7BY+Z1wg5g3zgUvyehiNpnJKxSLc/ts76LKm0BzX3c0RNy1yXjDcB5lWoro4iNHQxM+f1kWeWQARAWQS++trISJTp061Kep25X/MycwtjuctSC5rxo7ppi7VNUox5+PhPHtrsS2O1qJ6yx1QujQUzm9sh6hbkBlvvGcN8hYnwjUjH6kjfZEd5c/jitz5Jc5U3ENnFynKl4eB7nyEgP2UZ+Yz3/rVEbyYr27qELrtC4FIC0J7sc7xWnmccdHfRRTs0VB+cA4lt+oFcRR/wUeH8FG5w2Mbx8FQ8TXEvv1xYf4wBP3O2WyL3/UVjpXWgIqaFeUPr+wTmDvUB7njH6/bOv+HRg4SqioAg5GDe1aB3ZeMTJkyRSBqkLsWqSEm0fZVBEN94zEZnYvrdx1JL5cxe+a+AbhSJecRRHW/ikTFRTa38dtQlNZ5CRKwFvUtZU/kvBoEF9Uxni/XqIM+dwKbTw3rhcxIf7gmr2M+H6SMwx8iBzJbw5oxeG3Lv5FX9B3AGaHPS8e8z77H7v9VMpvPG5ug1enh7eGK8h0LBTwUb+GInqzInlRUK65DmTPQu4c3+uQKjwKK77zwUxBX4Tq7yR1RuiwUsqlrABCM6esHdXoy47fk4+prYKy8ZF574x4V5BnHQBuf4g9Z9ld8U36L2aktZNNplNfw7zotwWTy5MkCUft4aLEopJj5/OPHl1BQqeAVOnHgNSQOqmBzq9V9cfEm/yx5ubMGKS9cYPZ3vx2OS/c6PVHUuUO7Y1Pci3BO/1zgq18byebfGemLtNF+6JRtOvMk926ibussZqM+1mNz4TWkH7rCbM5phwGRGDAaoF8fY5OHFnlldAA8sgoEXKnDukA1NgSeNjqkJT9brbN4pC9WRweYXyLugR73c+MYvyWfu0yC6+mjzN1Isfw3FKJS98CU/zI1IHFkFPR52cHL2FJk0sB6kMTERIGo9GzcPkLNfA0cwdwi/hfEYO86ZMd9w+y1egfM2T2Eh/vesMNwljSzuZRT420SW3eqy8N6aHMmwmnFUZ7/PGVPbIoNZvNU1BURdHs0bT2+HjL8sDSM2e6vi4Lj5NW8WOLVA6RTT2azxLV+bglaFNqLieqemS/gWkw7NyoAHo+2dEsiivengjKsPFoqWOvbSh/kxPaxyW/JRzH2Fl3EzD9/xjAefJqB3usKUFn/0Gb+S/d/jy3FN2yLOmnSJJtn6oehByEiHPSeXnDxFGPRnoFoaBJjcdQlbDwcjL1zTNuQpoxD7R0OG0uUTMi0fkVwdzBdYIwcwZunxrVJVLplNm54BZp7jfDfYLoNyqQi1K6KxIdHzmN+QQ2WjFIwUT2zTGdlRXo4NFXVUO4sgX5dFC7f0aP/ZlNeUjFBuL8Xjl6uRuP6aMjSjpjzsH62FDU7JhBuGccEXIvDfJFFBc/gHw80dklfCVYnRaDfpiJcutPA4F7qJsfJeUPQI+1fqMlNhFx1FM0GDqkjFVg7NojlQ0Vt4aM5ReSqcbpaCg8nCW5lRsBvbT4T1TLfFptsfh7gItzuKTdJSEiwKSrt1vcmnEXXrsLbYnWDA1bu+z2WKy9Arq+1KRqdfKsoBo0GcdtEpS/B1bO4v0cFiUhkjskvKcMrWwtAPHuwQq8Z+4LZ1vTQANfXt4J0DwZX9gWa9qh4XDM/voC9JXfwYEMMHJcfNtusn82ihvliVUwg5KrPGVf6GH94ZJpEZBen6EC4qYTHA1dXhW0JIex8txzv//c8lhzXIi/BFxOH9jGbQhZsRalTIBZZ8KkGyZAxeRQvXkFF1TWz/Hm46jNYUnjPbt3JxIkT7f6dSj8qfJJyVvBxgaIlblOyjtysNHWN9fjjqWi7glJfW3/S0Hlj2XnA8PhKT9w6g3Qx3XiXhvuxQsuT1proxBKI/AaZqY1Xz5muvY8G8XkRRCaHsfQsRAFDH/tZPbcYuHotOG0FRIqB4HR3wNVoIPLtz8ycTguu+jpEigE218vd1YCr5m+HpHMvEI9u4LTXwNWaLjl0iPwGAmIpeHx1VeCqTJdPs1/vweweQPO3HC24NhOhnTphwoQnfv6QSY2ICbkNmdSA4h87oaLaiYfn5diIEd4att2erOwJXbPUHp953p6orQVSUVWRAXBT8c/dJ5L9xhzaJGp71GR/wFP8P5V2z10NSC9T93QM2xUg8fHxT+zU9ijeU4naHon8CjFJXFzc8/kn+dN06q9QgF98SYSo2Xen2NjYZy5sR6f+4nLSK5Iam2PH/x87a1YN/t5sBgAAAABJRU5ErkJggg==){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/datamodules.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "a3ad2cac", "metadata": {"papermill": {"duration": 0.031562, "end_time": "2021-12-04T16:45:52.591478", "exception": false, "start_time": "2021-12-04T16:45:52.559916", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "97feb35c", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T16:45:52.663442Z", "iopub.status.busy": "2021-12-04T16:45:52.662968Z", "iopub.status.idle": "2021-12-04T16:45:55.544955Z", "shell.execute_reply": "2021-12-04T16:45:55.545348Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.922242, "end_time": "2021-12-04T16:45:55.545634", "exception": false, "start_time": "2021-12-04T16:45:52.623392", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"torchvision\" \"pytorch-lightning>=1.3\" \"torchmetrics>=0.3\" \"torch>=1.6, <1.9\""]}, {"cell_type": "markdown", "id": "384d6a1b", "metadata": {"papermill": {"duration": 0.03178, "end_time": "2021-12-04T16:45:55.610463", "exception": false, "start_time": "2021-12-04T16:45:55.578683", "status": "completed"}, "tags": []}, "source": ["## Introduction\n", "\n", "First, we'll go over a regular `LightningModule` implementation without the use of a `LightningDataModule`"]}, {"cell_type": "code", "execution_count": 2, "id": "a39b56eb", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:45:55.682565Z", "iopub.status.busy": "2021-12-04T16:45:55.680296Z", "iopub.status.idle": "2021-12-04T16:45:59.522992Z", "shell.execute_reply": "2021-12-04T16:45:59.523374Z"}, "papermill": {"duration": 3.881462, "end_time": "2021-12-04T16:45:59.523593", "exception": false, "start_time": "2021-12-04T16:45:55.642131", "status": "completed"}, "tags": []}, "outputs": [], "source": ["import os\n", "\n", "import torch\n", "import torch.nn.functional as F\n", "from pytorch_lightning import LightningDataModule, LightningModule, Trainer\n", "from torch import nn\n", "from torch.utils.data import DataLoader, random_split\n", "from torchmetrics.functional import accuracy\n", "from torchvision import transforms\n", "\n", "# Note - you must have torchvision installed for this example\n", "from torchvision.datasets import CIFAR10, MNIST\n", "\n", "PATH_DATASETS = os.environ.get(\"PATH_DATASETS\", \".\")\n", "AVAIL_GPUS = min(1, torch.cuda.device_count())\n", "BATCH_SIZE = 256 if AVAIL_GPUS else 64"]}, {"cell_type": "markdown", "id": "0f3c51dc", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.031785, "end_time": "2021-12-04T16:45:59.588240", "exception": false, "start_time": "2021-12-04T16:45:59.556455", "status": "completed"}, "tags": []}, "source": ["### Defining the LitMNISTModel\n", "\n", "Below, we reuse a `LightningModule` from our hello world tutorial that classifies MNIST Handwritten Digits.\n", "\n", "Unfortunately, we have hardcoded dataset-specific items within the model,\n", "forever limiting it to working with MNIST Data. \ud83d\ude22\n", "\n", "This is fine if you don't plan on training/evaluating your model on different datasets.\n", "However, in many cases, this can become bothersome when you want to try out your architecture with different datasets."]}, {"cell_type": "code", "execution_count": 3, "id": "82a2f0d0", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:45:59.665520Z", "iopub.status.busy": "2021-12-04T16:45:59.665038Z", "iopub.status.idle": "2021-12-04T16:45:59.667349Z", "shell.execute_reply": "2021-12-04T16:45:59.666889Z"}, "papermill": {"duration": 0.04678, "end_time": "2021-12-04T16:45:59.667449", "exception": false, "start_time": "2021-12-04T16:45:59.620669", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class LitMNIST(LightningModule):\n", " def __init__(self, data_dir=PATH_DATASETS, hidden_size=64, learning_rate=2e-4):\n", "\n", " super().__init__()\n", "\n", " # We hardcode dataset specific stuff here.\n", " self.data_dir = data_dir\n", " self.num_classes = 10\n", " self.dims = (1, 28, 28)\n", " channels, width, height = self.dims\n", " self.transform = transforms.Compose(\n", " [\n", " transforms.ToTensor(),\n", " transforms.Normalize((0.1307,), (0.3081,)),\n", " ]\n", " )\n", "\n", " self.hidden_size = hidden_size\n", " self.learning_rate = learning_rate\n", "\n", " # Build model\n", " self.model = nn.Sequential(\n", " nn.Flatten(),\n", " nn.Linear(channels * width * height, hidden_size),\n", " nn.ReLU(),\n", " nn.Dropout(0.1),\n", " nn.Linear(hidden_size, hidden_size),\n", " nn.ReLU(),\n", " nn.Dropout(0.1),\n", " nn.Linear(hidden_size, self.num_classes),\n", " )\n", "\n", " def forward(self, x):\n", " x = self.model(x)\n", " return F.log_softmax(x, dim=1)\n", "\n", " def training_step(self, batch, batch_idx):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " preds = torch.argmax(logits, dim=1)\n", " acc = accuracy(preds, y)\n", " self.log(\"val_loss\", loss, prog_bar=True)\n", " self.log(\"val_acc\", acc, prog_bar=True)\n", " return loss\n", "\n", " def configure_optimizers(self):\n", " optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)\n", " return optimizer\n", "\n", " ####################\n", " # DATA RELATED HOOKS\n", " ####################\n", "\n", " def prepare_data(self):\n", " # download\n", " MNIST(self.data_dir, train=True, download=True)\n", " MNIST(self.data_dir, train=False, download=True)\n", "\n", " def setup(self, stage=None):\n", "\n", " # Assign train/val datasets for use in dataloaders\n", " if stage == \"fit\" or stage is None:\n", " mnist_full = MNIST(self.data_dir, train=True, transform=self.transform)\n", " self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])\n", "\n", " # Assign test dataset for use in dataloader(s)\n", " if stage == \"test\" or stage is None:\n", " self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform)\n", "\n", " def train_dataloader(self):\n", " return DataLoader(self.mnist_train, batch_size=128)\n", "\n", " def val_dataloader(self):\n", " return DataLoader(self.mnist_val, batch_size=128)\n", "\n", " def test_dataloader(self):\n", " return DataLoader(self.mnist_test, batch_size=128)"]}, {"cell_type": "markdown", "id": "fde0b3ad", "metadata": {"papermill": {"duration": 0.03277, "end_time": "2021-12-04T16:45:59.734857", "exception": false, "start_time": "2021-12-04T16:45:59.702087", "status": "completed"}, "tags": []}, "source": ["### Training the ListMNIST Model"]}, {"cell_type": "code", "execution_count": 4, "id": "75ebf631", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:45:59.804804Z", "iopub.status.busy": "2021-12-04T16:45:59.804311Z", "iopub.status.idle": "2021-12-04T16:46:25.412130Z", "shell.execute_reply": "2021-12-04T16:46:25.411686Z"}, "papermill": {"duration": 25.644008, "end_time": "2021-12-04T16:46:25.412258", "exception": false, "start_time": "2021-12-04T16:45:59.768250", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=20)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "-------------------------------------\n", "0 | model | Sequential | 55.1 K\n", "-------------------------------------\n", "55.1 K Trainable params\n", "0 Non-trainable params\n", "55.1 K Total params\n", "0.220 Total estimated model params size (MB)\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "768f5e115e3f4d86b97c71b44f458240", "version_major": 2, "version_minor": 0}, "text/plain": ["Validation sanity check: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, val_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, train_dataloader, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "eb61fdca27424a06a232c826ca5d695f", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "2488acd527e24363aa31b980006ae745", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "7f7924338266405fadf862414d1f02a1", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["model = LitMNIST()\n", "trainer = Trainer(\n", " max_epochs=2,\n", " gpus=AVAIL_GPUS,\n", " progress_bar_refresh_rate=20,\n", ")\n", "trainer.fit(model)"]}, {"cell_type": "markdown", "id": "81192dcb", "metadata": {"papermill": {"duration": 0.040842, "end_time": "2021-12-04T16:46:25.498462", "exception": false, "start_time": "2021-12-04T16:46:25.457620", "status": "completed"}, "tags": []}, "source": ["## Using DataModules\n", "\n", "DataModules are a way of decoupling data-related hooks from the `LightningModule\n", "` so you can develop dataset agnostic models."]}, {"cell_type": "markdown", "id": "d7703409", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.04198, "end_time": "2021-12-04T16:46:25.580985", "exception": false, "start_time": "2021-12-04T16:46:25.539005", "status": "completed"}, "tags": []}, "source": ["### Defining The MNISTDataModule\n", "\n", "Let's go over each function in the class below and talk about what they're doing:\n", "\n", "1. ```__init__```\n", " - Takes in a `data_dir` arg that points to where you have downloaded/wish to download the MNIST dataset.\n", " - Defines a transform that will be applied across train, val, and test dataset splits.\n", " - Defines default `self.dims`, which is a tuple returned from `datamodule.size()` that can help you initialize models.\n", "\n", "\n", "2. ```prepare_data```\n", " - This is where we can download the dataset. We point to our desired dataset and ask torchvision's `MNIST` dataset class to download if the dataset isn't found there.\n", " - **Note we do not make any state assignments in this function** (i.e. `self.something = ...`)\n", "\n", "3. ```setup```\n", " - Loads in data from file and prepares PyTorch tensor datasets for each split (train, val, test).\n", " - Setup expects a 'stage' arg which is used to separate logic for 'fit' and 'test'.\n", " - If you don't mind loading all your datasets at once, you can set up a condition to allow for both 'fit' related setup and 'test' related setup to run whenever `None` is passed to `stage`.\n", " - **Note this runs across all GPUs and it *is* safe to make state assignments here**\n", "\n", "\n", "4. ```x_dataloader```\n", " - `train_dataloader()`, `val_dataloader()`, and `test_dataloader()` all return PyTorch `DataLoader` instances that are created by wrapping their respective datasets that we prepared in `setup()`"]}, {"cell_type": "code", "execution_count": 5, "id": "3d9cf13e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:46:25.670687Z", "iopub.status.busy": "2021-12-04T16:46:25.670187Z", "iopub.status.idle": "2021-12-04T16:46:25.672182Z", "shell.execute_reply": "2021-12-04T16:46:25.671779Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.050689, "end_time": "2021-12-04T16:46:25.672288", "exception": false, "start_time": "2021-12-04T16:46:25.621599", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class MNISTDataModule(LightningDataModule):\n", " def __init__(self, data_dir: str = PATH_DATASETS):\n", " super().__init__()\n", " self.data_dir = data_dir\n", " self.transform = transforms.Compose(\n", " [\n", " transforms.ToTensor(),\n", " transforms.Normalize((0.1307,), (0.3081,)),\n", " ]\n", " )\n", "\n", " # self.dims is returned when you call dm.size()\n", " # Setting default dims here because we know them.\n", " # Could optionally be assigned dynamically in dm.setup()\n", " self.dims = (1, 28, 28)\n", " self.num_classes = 10\n", "\n", " def prepare_data(self):\n", " # download\n", " MNIST(self.data_dir, train=True, download=True)\n", " MNIST(self.data_dir, train=False, download=True)\n", "\n", " def setup(self, stage=None):\n", "\n", " # Assign train/val datasets for use in dataloaders\n", " if stage == \"fit\" or stage is None:\n", " mnist_full = MNIST(self.data_dir, train=True, transform=self.transform)\n", " self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])\n", "\n", " # Assign test dataset for use in dataloader(s)\n", " if stage == \"test\" or stage is None:\n", " self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform)\n", "\n", " def train_dataloader(self):\n", " return DataLoader(self.mnist_train, batch_size=BATCH_SIZE)\n", "\n", " def val_dataloader(self):\n", " return DataLoader(self.mnist_val, batch_size=BATCH_SIZE)\n", "\n", " def test_dataloader(self):\n", " return DataLoader(self.mnist_test, batch_size=BATCH_SIZE)"]}, {"cell_type": "markdown", "id": "c916523e", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.044584, "end_time": "2021-12-04T16:46:25.757859", "exception": false, "start_time": "2021-12-04T16:46:25.713275", "status": "completed"}, "tags": []}, "source": ["### Defining the dataset agnostic `LitModel`\n", "\n", "Below, we define the same model as the `LitMNIST` model we made earlier.\n", "\n", "However, this time our model has the freedom to use any input data that we'd like \ud83d\udd25."]}, {"cell_type": "code", "execution_count": 6, "id": "2d745995", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:46:25.848843Z", "iopub.status.busy": "2021-12-04T16:46:25.848373Z", "iopub.status.idle": "2021-12-04T16:46:25.850404Z", "shell.execute_reply": "2021-12-04T16:46:25.849943Z"}, "papermill": {"duration": 0.051727, "end_time": "2021-12-04T16:46:25.850508", "exception": false, "start_time": "2021-12-04T16:46:25.798781", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class LitModel(LightningModule):\n", " def __init__(self, channels, width, height, num_classes, hidden_size=64, learning_rate=2e-4):\n", "\n", " super().__init__()\n", "\n", " # We take in input dimensions as parameters and use those to dynamically build model.\n", " self.channels = channels\n", " self.width = width\n", " self.height = height\n", " self.num_classes = num_classes\n", " self.hidden_size = hidden_size\n", " self.learning_rate = learning_rate\n", "\n", " self.model = nn.Sequential(\n", " nn.Flatten(),\n", " nn.Linear(channels * width * height, hidden_size),\n", " nn.ReLU(),\n", " nn.Dropout(0.1),\n", " nn.Linear(hidden_size, hidden_size),\n", " nn.ReLU(),\n", " nn.Dropout(0.1),\n", " nn.Linear(hidden_size, num_classes),\n", " )\n", "\n", " def forward(self, x):\n", " x = self.model(x)\n", " return F.log_softmax(x, dim=1)\n", "\n", " def training_step(self, batch, batch_idx):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", "\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " preds = torch.argmax(logits, dim=1)\n", " acc = accuracy(preds, y)\n", " self.log(\"val_loss\", loss, prog_bar=True)\n", " self.log(\"val_acc\", acc, prog_bar=True)\n", " return loss\n", "\n", " def configure_optimizers(self):\n", " optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)\n", " return optimizer"]}, {"cell_type": "markdown", "id": "a835525d", "metadata": {"papermill": {"duration": 0.04143, "end_time": "2021-12-04T16:46:25.933881", "exception": false, "start_time": "2021-12-04T16:46:25.892451", "status": "completed"}, "tags": []}, "source": ["### Training the `LitModel` using the `MNISTDataModule`\n", "\n", "Now, we initialize and train the `LitModel` using the `MNISTDataModule`'s configuration settings and dataloaders."]}, {"cell_type": "code", "execution_count": 7, "id": "08a4316a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:46:26.019913Z", "iopub.status.busy": "2021-12-04T16:46:26.019449Z", "iopub.status.idle": "2021-12-04T16:46:56.702166Z", "shell.execute_reply": "2021-12-04T16:46:56.698729Z"}, "papermill": {"duration": 30.727168, "end_time": "2021-12-04T16:46:56.702286", "exception": false, "start_time": "2021-12-04T16:46:25.975118", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:175: LightningDeprecationWarning: DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\"DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.\")\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:184: LightningDeprecationWarning: DataModule property `size` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\"DataModule property `size` was deprecated in v1.5 and will be removed in v1.7.\")\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/core/datamodule.py:170: LightningDeprecationWarning: DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.\n", " rank_zero_deprecation(\"DataModule property `dims` was deprecated in v1.5 and will be removed in v1.7.\")\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "-------------------------------------\n", "0 | model | Sequential | 55.1 K\n", "-------------------------------------\n", "55.1 K Trainable params\n", "0 Non-trainable params\n", "55.1 K Total params\n", "0.220 Total estimated model params size (MB)\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "1a75cc9880ab40a8ba9ce7b57f0a0765", "version_major": 2, "version_minor": 0}, "text/plain": ["Validation sanity check: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "adae3a9829a54b508f38288825a67235", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "bf86e207916b4e409d89947baee6b2a3", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "8e77575340994f2fbd6538d157de467e", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "8e95fecb92834da89d6246cbd02d3f06", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Init DataModule\n", "dm = MNISTDataModule()\n", "# Init model from datamodule's attributes\n", "model = LitModel(*dm.size(), dm.num_classes)\n", "# Init trainer\n", "trainer = Trainer(\n", " max_epochs=3,\n", " progress_bar_refresh_rate=20,\n", " gpus=AVAIL_GPUS,\n", ")\n", "# Pass the datamodule as arg to trainer.fit to override model hooks :)\n", "trainer.fit(model, dm)"]}, {"cell_type": "markdown", "id": "34f903fa", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.051415, "end_time": "2021-12-04T16:46:56.811131", "exception": false, "start_time": "2021-12-04T16:46:56.759716", "status": "completed"}, "tags": []}, "source": ["### Defining the CIFAR10 DataModule\n", "\n", "Lets prove the `LitModel` we made earlier is dataset agnostic by defining a new datamodule for the CIFAR10 dataset."]}, {"cell_type": "code", "execution_count": 8, "id": "1b86ab59", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:46:56.920233Z", "iopub.status.busy": "2021-12-04T16:46:56.919755Z", "iopub.status.idle": "2021-12-04T16:46:56.921851Z", "shell.execute_reply": "2021-12-04T16:46:56.921254Z"}, "papermill": {"duration": 0.060616, "end_time": "2021-12-04T16:46:56.921972", "exception": false, "start_time": "2021-12-04T16:46:56.861356", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class CIFAR10DataModule(LightningDataModule):\n", " def __init__(self, data_dir: str = \"./\"):\n", " super().__init__()\n", " self.data_dir = data_dir\n", " self.transform = transforms.Compose(\n", " [\n", " transforms.ToTensor(),\n", " transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n", " ]\n", " )\n", "\n", " self.dims = (3, 32, 32)\n", " self.num_classes = 10\n", "\n", " def prepare_data(self):\n", " # download\n", " CIFAR10(self.data_dir, train=True, download=True)\n", " CIFAR10(self.data_dir, train=False, download=True)\n", "\n", " def setup(self, stage=None):\n", "\n", " # Assign train/val datasets for use in dataloaders\n", " if stage == \"fit\" or stage is None:\n", " cifar_full = CIFAR10(self.data_dir, train=True, transform=self.transform)\n", " self.cifar_train, self.cifar_val = random_split(cifar_full, [45000, 5000])\n", "\n", " # Assign test dataset for use in dataloader(s)\n", " if stage == \"test\" or stage is None:\n", " self.cifar_test = CIFAR10(self.data_dir, train=False, transform=self.transform)\n", "\n", " def train_dataloader(self):\n", " return DataLoader(self.cifar_train, batch_size=BATCH_SIZE)\n", "\n", " def val_dataloader(self):\n", " return DataLoader(self.cifar_val, batch_size=BATCH_SIZE)\n", "\n", " def test_dataloader(self):\n", " return DataLoader(self.cifar_test, batch_size=BATCH_SIZE)"]}, {"cell_type": "markdown", "id": "ac30ef93", "metadata": {"papermill": {"duration": 0.050833, "end_time": "2021-12-04T16:46:57.023402", "exception": false, "start_time": "2021-12-04T16:46:56.972569", "status": "completed"}, "tags": []}, "source": ["### Training the `LitModel` using the `CIFAR10DataModule`\n", "\n", "Our model isn't very good, so it will perform pretty badly on the CIFAR10 dataset.\n", "\n", "The point here is that we can see that our `LitModel` has no problem using a different datamodule as its input data."]}, {"cell_type": "code", "execution_count": 9, "id": "11d3054c", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:46:57.128360Z", "iopub.status.busy": "2021-12-04T16:46:57.127907Z", "iopub.status.idle": "2021-12-04T16:47:50.619126Z", "shell.execute_reply": "2021-12-04T16:47:50.618692Z"}, "papermill": {"duration": 53.545438, "end_time": "2021-12-04T16:47:50.619260", "exception": false, "start_time": "2021-12-04T16:46:57.073822", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stdout", "output_type": "stream", "text": ["Files already downloaded and verified\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "-------------------------------------\n", "0 | model | Sequential | 855 K \n", "-------------------------------------\n", "855 K Trainable params\n", "0 Non-trainable params\n", "855 K Total params\n", "3.420 Total estimated model params size (MB)\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "07d7ba996450429b80d5a056099cf110", "version_major": 2, "version_minor": 0}, "text/plain": ["Validation sanity check: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "db19a78a15954e2b8a4f99eb2de9270a", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "7f2aa9df928e4925a1193b7427380ff4", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "37a0aca2beb042d78d3680070561bfdc", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "3e91c57c5e1a446384fa939d0b3d6525", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "aa675ba935754589a5a9550795c04bee", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "1b28004c7448443a97074788b559d535", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["dm = CIFAR10DataModule()\n", "model = LitModel(*dm.size(), dm.num_classes, hidden_size=256)\n", "trainer = Trainer(\n", " max_epochs=5,\n", " progress_bar_refresh_rate=20,\n", " gpus=AVAIL_GPUS,\n", ")\n", "trainer.fit(model, dm)"]}, {"cell_type": "markdown", "id": "d7227386", "metadata": {"papermill": {"duration": 0.063624, "end_time": "2021-12-04T16:47:50.747460", "exception": false, "start_time": "2021-12-04T16:47:50.683836", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: PyTorch Lightning DataModules\n", " :card_description: This notebook will walk you through how to start using Datamodules. With the release of `pytorch-lightning` version 0.9.0, we have included a new class called...\n", " :tags: GPU/TPU,Lightning-Examples"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab_type,id,colab,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 121.040769, "end_time": "2021-12-04T16:47:52.223467", "environment_variables": {}, "exception": null, "input_path": "lightning_examples/datamodules/datamodules.ipynb", "output_path": ".notebooks/lightning_examples/datamodules.ipynb", "parameters": {}, "start_time": "2021-12-04T16:45:51.182698", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"041f10d5c9fe42f6b6101bf3ce7f53fb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_bbe528fe44644854a9bb9189dbba1160", "placeholder": "\u200b", "style": "IPY_MODEL_c18acb5a037d4945a4501d96c8fae24e", "value": " 20/20 [00:00<00:00, 20.23it/s]"}}, "046540f066864bcb84c4d37a584da370": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "05008355dc2d450ca42051e370346f18": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "055ce06d091b4f4eba52bf64158e9675": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fd49beba21cd45ccb858e36c10762a7f", "placeholder": "\u200b", "style": "IPY_MODEL_e56f1ea9380d47d0aa130d82d6e8d685", "value": "Validation sanity check: 0%"}}, "071176e5c655438588281056b551d627": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "07d7ba996450429b80d5a056099cf110": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_81027aed2f2546b99962209d53f522d9", "IPY_MODEL_65ef56c9c8eb42a598a68cacc8da5177", "IPY_MODEL_6e580744cc7e4a40a82df1730f663865"], "layout": "IPY_MODEL_549689ee50894c42a5bfcfe90b7f4565"}}, "0bb08f9ebb7d4bf89852a14acc0aa9d2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "0dc0803d41cd4c28a7a6f76b8e1ae0ec": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0e797437e8cd4d4dbaf695d6bb3082a7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "0fc92bf19ad54f50b87e243797da525b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "12948f6cb81e42c2a28c87635182f380": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fd0efe1ba0794d07801286e8c8c82151", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_e10e4a67b6bf467aa5d3d8e7fa91f1ce", "value": 20.0}}, "12f1519dc785487eb49a85ca47b5149d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1337cb79348240b58c48427043f51f85": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "1418aae647f04591870a4931b9c70781": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1969d64dc7634207ae872d28b6e425e0", "placeholder": "\u200b", "style": "IPY_MODEL_ff2fae1a3eff4dbbab900312f78a04fc", "value": " 235/235 [00:10<00:00, 23.16it/s, loss=0.331, v_num=4, val_loss=0.278, val_acc=0.918]"}}, "1476ef3e1ecd4018957f0130dfcdb383": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "15d8aa5839444bac8dbb125f268d3727": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0dc0803d41cd4c28a7a6f76b8e1ae0ec", "placeholder": "\u200b", "style": "IPY_MODEL_6df35592e449430d9f7a9ea085855234", "value": "Epoch 1: 100%"}}, "18ffda5c5a4d48ed9652bc713190efa0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "1969d64dc7634207ae872d28b6e425e0": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1a75cc9880ab40a8ba9ce7b57f0a0765": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_055ce06d091b4f4eba52bf64158e9675", "IPY_MODEL_fe6f724fbcd549d18f69f46f73cec77e", "IPY_MODEL_3608aa33ad044ec8ad3bbfbe65572fdf"], "layout": "IPY_MODEL_db6cb2d3f32b4b7b96f746b70541cdbd"}}, "1b28004c7448443a97074788b559d535": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_c09fe936a9dd4feaac58207b660e91b8", "IPY_MODEL_b50255939b1448908a80c51319aeb709", "IPY_MODEL_27d3d469dd744b83a084677b4f022b5d"], "layout": "IPY_MODEL_fb9bc72381a24c0ab759f875fe5d1d3b"}}, "1ce8afc6ca874703a5c9a25e499207f3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1e3ae51213e24a278b69f7e367bf851c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1e823fd4e7d0434982e274023a5274bc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "1efd9542e7ca4ad3a0db3d631cbee72f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9d73f752544d4bcc9f1b5b4769c7317d", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_999aa998227047dda17516eeb71aa6db", "value": 20.0}}, "1fa7d7f81f7b4420ae91e983bfb9b4ae": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "20ce657f01dd4c5d8a947124d44078ba": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "23662fea34734dd48718c50b28585645": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "23efe0a29c96456d97de2fefdd7b6806": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2aed645a39a44501b2259a5a10f806d1", "placeholder": "\u200b", "style": "IPY_MODEL_20ce657f01dd4c5d8a947124d44078ba", "value": " 470/470 [00:11<00:00, 42.59it/s, loss=0.301, v_num=3, val_loss=0.255, val_acc=0.925]"}}, "2488acd527e24363aa31b980006ae745": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b2f89c7143ae4567ae699cbee23fe747", "IPY_MODEL_2fb4cb9abeee4b53a47645473806aac2", "IPY_MODEL_3b1643d2b31745f99c51189861531ad0"], "layout": "IPY_MODEL_81c7d575b0f6457f84a3c37bbb09da67"}}, "248e123d9a48408097a33ee63175d655": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_397d40dda3864639b0263f97e6168457", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_9315c66ada0346da8c053c80b636402d", "value": 20.0}}, "27d3d469dd744b83a084677b4f022b5d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_0e797437e8cd4d4dbaf695d6bb3082a7", "placeholder": "\u200b", "style": "IPY_MODEL_4e001ded9e2142b1ab49555a49c3441c", "value": " 20/20 [00:00<00:00, 20.34it/s]"}}, "2aed645a39a44501b2259a5a10f806d1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2bacc7082a704e2d8047b2d17e27e83d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2fb4cb9abeee4b53a47645473806aac2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c6171b1b06a84e75aa7dc2fa35c36f57", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_488ea46105c04a458dc95deb2b53e609", "value": 40.0}}, "30707f52823346dab1b3a6db2b4dfa27": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "31695284f0d944f0b7a423300f908390": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1476ef3e1ecd4018957f0130dfcdb383", "placeholder": "\u200b", "style": "IPY_MODEL_3a654946c7384b10b9ec0de1c2a9683b", "value": " 0/2 [00:00<?, ?it/s]"}}, "3490c0eeca5c4fa1b2fb1540df1e0119": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3608aa33ad044ec8ad3bbfbe65572fdf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_12f1519dc785487eb49a85ca47b5149d", "placeholder": "\u200b", "style": "IPY_MODEL_b34601cdb9f34fedb83d2b3b92095d84", "value": " 0/2 [00:00<?, ?it/s]"}}, "36505e9126384458b7a2f19343d6e37b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1fa7d7f81f7b4420ae91e983bfb9b4ae", "placeholder": "\u200b", "style": "IPY_MODEL_0fc92bf19ad54f50b87e243797da525b", "value": "Validating: 100%"}}, "369f33250b7e46e6b54a31680946081c": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "37a0aca2beb042d78d3680070561bfdc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_e1e722b4ba6347d0b864de5c29e7d324", "IPY_MODEL_1efd9542e7ca4ad3a0db3d631cbee72f", "IPY_MODEL_6534be4f1769429b88eebad316c7c2ed"], "layout": "IPY_MODEL_6026cb507a9c4740b0d93a33b710c66f"}}, "397d40dda3864639b0263f97e6168457": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3a654946c7384b10b9ec0de1c2a9683b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "3b1643d2b31745f99c51189861531ad0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3490c0eeca5c4fa1b2fb1540df1e0119", "placeholder": "\u200b", "style": "IPY_MODEL_a62f633a147d475caa8490ecbb8427cf", "value": " 40/40 [00:00<00:00, 45.54it/s]"}}, "3be62b6211c54a36a9c749eebc7ba65e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_046540f066864bcb84c4d37a584da370", "max": 196.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_5828ec1d42a947628434634af513fda9", "value": 196.0}}, "3e91c57c5e1a446384fa939d0b3d6525": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_b5dfecd17bef4d9e8683f470462fa197", "IPY_MODEL_c6c1c3ec1a0341da92b182780b7a6fd0", "IPY_MODEL_cf2a40a0c5174457aea4aec7b8f7864a"], "layout": "IPY_MODEL_bdc92ba2821945acb2db16c1814d4ad1"}}, "40a0d394ad834802a994ae894ce61923": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4352932c9dc94329b785a1ae04cae96a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "488ea46105c04a458dc95deb2b53e609": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "4a042e1f9f8c47488002f5e5ef01163e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "4daeedb9efbd4bdcaee5e9e209ff85f1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "4e001ded9e2142b1ab49555a49c3441c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "50701a5262a04c9eade1571e1c112870": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "50bac340b87c4a8aa003a7cc128809ba": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "526b549d670640ebb4c66046ca783375": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "53a860195fe74155b2e3084d57f934b2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_91359b90570043e684bd0d5c807305eb", "placeholder": "\u200b", "style": "IPY_MODEL_7d14ca2343e84708889dcd5651bdadd5", "value": "Validating: 100%"}}, "53ad10603a244414a17fa3d70960844e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1e3ae51213e24a278b69f7e367bf851c", "placeholder": "\u200b", "style": "IPY_MODEL_eea47fbfb6764d2d8072c08ad33bf2bd", "value": "Validating: 100%"}}, "549689ee50894c42a5bfcfe90b7f4565": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "549a503432ca4c309730849495d09e90": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "56342bc4e2bc4fe4af37821040ac0ecd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "56d6913766104181a3d312e70a384b5d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_50bac340b87c4a8aa003a7cc128809ba", "placeholder": "\u200b", "style": "IPY_MODEL_72a9f02a5dfe46a5ad8a4f73f1feb091", "value": "Validating: 100%"}}, "5828ec1d42a947628434634af513fda9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "58658a5812d741c693aa21f0ea95f63b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a7f8bc154c9c40d2840fd03f50e2fb93", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_f2ba50047c764c5c9f8e384672d49050", "value": 20.0}}, "59f0e262344d4ea8b2800057ee40f45e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5ae4444dbb994b9eb661bae0061a0545": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "5c66fc27f7bc4809b4a91c1eb28c7be3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "6026cb507a9c4740b0d93a33b710c66f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "6218602ae56242fc8427ca200c5caa90": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "64004563a6074b9c876c648bbce6cde5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "64c91d37fcb8443e966da1b29266351f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a9877ca759754a0fa4beaaf9627608ac", "placeholder": "\u200b", "style": "IPY_MODEL_b1f67ebe8dc64b7b8cfc6649dd4217c5", "value": " 20/20 [00:00<00:00, 24.24it/s]"}}, "6534be4f1769429b88eebad316c7c2ed": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_369f33250b7e46e6b54a31680946081c", "placeholder": "\u200b", "style": "IPY_MODEL_a94711718593447395d16addb1a12cda", "value": " 20/20 [00:00<00:00, 20.07it/s]"}}, "6537a3c66b00455c9581c8a9d2d7075f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "65ef56c9c8eb42a598a68cacc8da5177": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "danger", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_6218602ae56242fc8427ca200c5caa90", "max": 2.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_071176e5c655438588281056b551d627", "value": 0.0}}, "6df35592e449430d9f7a9ea085855234": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "6e580744cc7e4a40a82df1730f663865": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fbe640bbc7d541fa9e337874f7ebb100", "placeholder": "\u200b", "style": "IPY_MODEL_85bad507a7d54408897de1439438c898", "value": " 0/2 [00:00<?, ?it/s]"}}, "72a9f02a5dfe46a5ad8a4f73f1feb091": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "768f5e115e3f4d86b97c71b44f458240": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_c5011f431d28417395dd982e6424d342", "IPY_MODEL_81b1d48c4ac047828f825d21ab953234", "IPY_MODEL_31695284f0d944f0b7a423300f908390"], "layout": "IPY_MODEL_6537a3c66b00455c9581c8a9d2d7075f"}}, "7a5ccbb2499b43e9ae6b1501456d2d86": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "7d14ca2343e84708889dcd5651bdadd5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7dad3ba253814912a31e2c6e420fc7c8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "7e228107476b4c7dadf3c70d6dab8d44": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_97cec026f48a4eeb8c5af10f22a3cfa4", "max": 235.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_9eca64b3bb7241ff8758aad3c16cf743", "value": 235.0}}, "7f2aa9df928e4925a1193b7427380ff4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_e45d95dc1d834de9a0c760552507a3e2", "IPY_MODEL_58658a5812d741c693aa21f0ea95f63b", "IPY_MODEL_041f10d5c9fe42f6b6101bf3ce7f53fb"], "layout": "IPY_MODEL_30707f52823346dab1b3a6db2b4dfa27"}}, "7f7924338266405fadf862414d1f02a1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_ca22e6df84e94eb1b72081b7566bfadf", "IPY_MODEL_b613e0efb250468f85488aad1904e785", "IPY_MODEL_d1371d179edb4b7690fb18a3751f3065"], "layout": "IPY_MODEL_4352932c9dc94329b785a1ae04cae96a"}}, "805eb023d3974ad2adeb2370206bd70d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_90625c2b1c1c4c2d82efb5ad6b7ace57", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_d08c85acb5d2474dbc5e83ced26a5be0", "value": 20.0}}, "80b13852475d438f864f8a7a1a8e6094": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "81027aed2f2546b99962209d53f522d9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ec02b811e3c9471caa5176c09991e4e3", "placeholder": "\u200b", "style": "IPY_MODEL_d49a6c09dd1c423294792b28058ad3e0", "value": "Validation sanity check: 0%"}}, "81108d80cde546739a034f085412f763": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1ce8afc6ca874703a5c9a25e499207f3", "placeholder": "\u200b", "style": "IPY_MODEL_cd49716ced8a47cd86767a8d334572f8", "value": " 20/20 [00:00<00:00, 24.59it/s]"}}, "81b1d48c4ac047828f825d21ab953234": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "danger", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fa01c038682a40668590b94a00a4a241", "max": 2.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_56342bc4e2bc4fe4af37821040ac0ecd", "value": 0.0}}, "81c7d575b0f6457f84a3c37bbb09da67": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "841ac1bf4d1f405d9af8db196992c21e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "85bad507a7d54408897de1439438c898": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "889fbb4df4784c92bd9d17bc662ddca0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d1245a33a19644fb8a69adfa1a213b2e", "placeholder": "\u200b", "style": "IPY_MODEL_4daeedb9efbd4bdcaee5e9e209ff85f1", "value": " 20/20 [00:01<00:00, 19.99it/s]"}}, "8ad9135da95e41b980ccec4cbdad6e6e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8b001407ab8447beb6aac24fe26f6e07": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8e413e2031854c979e00e11e9c931a1a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8e77575340994f2fbd6538d157de467e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_53a860195fe74155b2e3084d57f934b2", "IPY_MODEL_248e123d9a48408097a33ee63175d655", "IPY_MODEL_81108d80cde546739a034f085412f763"], "layout": "IPY_MODEL_d0a8e767be6a443e9a705ae58e413564"}}, "8e95fecb92834da89d6246cbd02d3f06": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_56d6913766104181a3d312e70a384b5d", "IPY_MODEL_805eb023d3974ad2adeb2370206bd70d", "IPY_MODEL_9c91bccae6324afbbfd226218c1a6ec3"], "layout": "IPY_MODEL_5ae4444dbb994b9eb661bae0061a0545"}}, "90625c2b1c1c4c2d82efb5ad6b7ace57": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "91359b90570043e684bd0d5c807305eb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9315c66ada0346da8c053c80b636402d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "961ec875a4bc468db541f4d17cb0c959": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_59f0e262344d4ea8b2800057ee40f45e", "max": 470.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_1337cb79348240b58c48427043f51f85", "value": 470.0}}, "97cec026f48a4eeb8c5af10f22a3cfa4": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "98056abfaec44f74b95511d322efcffb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "999aa998227047dda17516eeb71aa6db": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "9c91bccae6324afbbfd226218c1a6ec3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ce596973849e485784e0f322bd2fee72", "placeholder": "\u200b", "style": "IPY_MODEL_f0e8755a989b493791b9efcb6b675aa5", "value": " 20/20 [00:00<00:00, 24.50it/s]"}}, "9ce8d78f6d7644588c5cd03364c044cd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_be8d323ac655443da2433c4688cc3b58", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_64004563a6074b9c876c648bbce6cde5", "value": 20.0}}, "9d73f752544d4bcc9f1b5b4769c7317d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "9eca64b3bb7241ff8758aad3c16cf743": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "a066a53b41e249e2831e5980bd4003bf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a1984adb3ecb4dd1a030cf6360f62604": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a62f633a147d475caa8490ecbb8427cf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a7f8bc154c9c40d2840fd03f50e2fb93": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "a94711718593447395d16addb1a12cda": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a9877ca759754a0fa4beaaf9627608ac": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "aa675ba935754589a5a9550795c04bee": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_53ad10603a244414a17fa3d70960844e", "IPY_MODEL_12948f6cb81e42c2a28c87635182f380", "IPY_MODEL_889fbb4df4784c92bd9d17bc662ddca0"], "layout": "IPY_MODEL_e1cbc478e48c4cc2b308deca86016e48"}}, "adae3a9829a54b508f38288825a67235": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_d9cb7bfe4691411bbb36fc53a2a61d59", "IPY_MODEL_7e228107476b4c7dadf3c70d6dab8d44", "IPY_MODEL_1418aae647f04591870a4931b9c70781"], "layout": "IPY_MODEL_18ffda5c5a4d48ed9652bc713190efa0"}}, "ae0ef34d0d254bc2a5bf9ab0cb7ebcce": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "af18873d5e6c462bb6b97c59b6dda6d8": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b1f67ebe8dc64b7b8cfc6649dd4217c5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b2f89c7143ae4567ae699cbee23fe747": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5c66fc27f7bc4809b4a91c1eb28c7be3", "placeholder": "\u200b", "style": "IPY_MODEL_c7615b777eb1436984e41ded24171cab", "value": "Validating: 100%"}}, "b34601cdb9f34fedb83d2b3b92095d84": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "b50255939b1448908a80c51319aeb709": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_a1984adb3ecb4dd1a030cf6360f62604", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_ae0ef34d0d254bc2a5bf9ab0cb7ebcce", "value": 20.0}}, "b5249f51e9854f6398e334263a18f1b7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "b5dfecd17bef4d9e8683f470462fa197": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_fb18af4aa2d64f3da0e019ed601d7d7b", "placeholder": "\u200b", "style": "IPY_MODEL_f5d0c5dae3fc41848ba45e99fccc57f6", "value": "Validating: 100%"}}, "b613e0efb250468f85488aad1904e785": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_549a503432ca4c309730849495d09e90", "max": 40.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_b5249f51e9854f6398e334263a18f1b7", "value": 40.0}}, "b836287ffdbd45f484e253bf70957e90": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "bb867f5c3ba04190998f65812f6f76a2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_841ac1bf4d1f405d9af8db196992c21e", "placeholder": "\u200b", "style": "IPY_MODEL_a066a53b41e249e2831e5980bd4003bf", "value": " 196/196 [00:10<00:00, 19.16it/s, loss=1.35, v_num=5, val_loss=1.440, val_acc=0.495]"}}, "bbe528fe44644854a9bb9189dbba1160": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bdc92ba2821945acb2db16c1814d4ad1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "be8d323ac655443da2433c4688cc3b58": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bf86e207916b4e409d89947baee6b2a3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_36505e9126384458b7a2f19343d6e37b", "IPY_MODEL_9ce8d78f6d7644588c5cd03364c044cd", "IPY_MODEL_64c91d37fcb8443e966da1b29266351f"], "layout": "IPY_MODEL_23662fea34734dd48718c50b28585645"}}, "c09fe936a9dd4feaac58207b660e91b8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_cb762151a751470eb7c69722f2bd2168", "placeholder": "\u200b", "style": "IPY_MODEL_cc22e526515a48918562f074ab776ac3", "value": "Validating: 100%"}}, "c18acb5a037d4945a4501d96c8fae24e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c5011f431d28417395dd982e6424d342": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e7ad9f0b4e9742a69c0b436f5269eb0a", "placeholder": "\u200b", "style": "IPY_MODEL_1e823fd4e7d0434982e274023a5274bc", "value": "Validation sanity check: 0%"}}, "c6171b1b06a84e75aa7dc2fa35c36f57": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c6c1c3ec1a0341da92b182780b7a6fd0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_40a0d394ad834802a994ae894ce61923", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_7dad3ba253814912a31e2c6e420fc7c8", "value": 20.0}}, "c7615b777eb1436984e41ded24171cab": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ca22e6df84e94eb1b72081b7566bfadf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_80b13852475d438f864f8a7a1a8e6094", "placeholder": "\u200b", "style": "IPY_MODEL_8e413e2031854c979e00e11e9c931a1a", "value": "Validating: 100%"}}, "cb762151a751470eb7c69722f2bd2168": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cc22e526515a48918562f074ab776ac3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "cd49716ced8a47cd86767a8d334572f8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "cdd719fc342a4a57aa0c39d938d34bd9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ce596973849e485784e0f322bd2fee72": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "cf2a40a0c5174457aea4aec7b8f7864a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8b001407ab8447beb6aac24fe26f6e07", "placeholder": "\u200b", "style": "IPY_MODEL_50701a5262a04c9eade1571e1c112870", "value": " 20/20 [00:00<00:00, 20.07it/s]"}}, "d08c85acb5d2474dbc5e83ced26a5be0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "d0a8e767be6a443e9a705ae58e413564": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "d1245a33a19644fb8a69adfa1a213b2e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d1371d179edb4b7690fb18a3751f3065": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_2bacc7082a704e2d8047b2d17e27e83d", "placeholder": "\u200b", "style": "IPY_MODEL_0bb08f9ebb7d4bf89852a14acc0aa9d2", "value": " 40/40 [00:00<00:00, 44.58it/s]"}}, "d49a6c09dd1c423294792b28058ad3e0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d9cb7bfe4691411bbb36fc53a2a61d59": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_05008355dc2d450ca42051e370346f18", "placeholder": "\u200b", "style": "IPY_MODEL_cdd719fc342a4a57aa0c39d938d34bd9", "value": "Epoch 2: 100%"}}, "da7c1771e1794b5da89c02462ab3b3a9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_ee6e60f6f20b44bda4de9976ee4553e5", "placeholder": "\u200b", "style": "IPY_MODEL_b836287ffdbd45f484e253bf70957e90", "value": "Epoch 4: 100%"}}, "db19a78a15954e2b8a4f99eb2de9270a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_da7c1771e1794b5da89c02462ab3b3a9", "IPY_MODEL_3be62b6211c54a36a9c749eebc7ba65e", "IPY_MODEL_bb867f5c3ba04190998f65812f6f76a2"], "layout": "IPY_MODEL_de2baa14900e4345adbd48d2ba942b9f"}}, "db6cb2d3f32b4b7b96f746b70541cdbd": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "de2baa14900e4345adbd48d2ba942b9f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "e10e4a67b6bf467aa5d3d8e7fa91f1ce": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "e1cbc478e48c4cc2b308deca86016e48": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "e1e722b4ba6347d0b864de5c29e7d324": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_7a5ccbb2499b43e9ae6b1501456d2d86", "placeholder": "\u200b", "style": "IPY_MODEL_526b549d670640ebb4c66046ca783375", "value": "Validating: 100%"}}, "e45d95dc1d834de9a0c760552507a3e2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_98056abfaec44f74b95511d322efcffb", "placeholder": "\u200b", "style": "IPY_MODEL_8ad9135da95e41b980ccec4cbdad6e6e", "value": "Validating: 100%"}}, "e56f1ea9380d47d0aa130d82d6e8d685": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e7ad9f0b4e9742a69c0b436f5269eb0a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "eb61fdca27424a06a232c826ca5d695f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_15d8aa5839444bac8dbb125f268d3727", "IPY_MODEL_961ec875a4bc468db541f4d17cb0c959", "IPY_MODEL_23efe0a29c96456d97de2fefdd7b6806"], "layout": "IPY_MODEL_4a042e1f9f8c47488002f5e5ef01163e"}}, "ec02b811e3c9471caa5176c09991e4e3": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ee6e60f6f20b44bda4de9976ee4553e5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "ee78442ff4344ba1927dbd6e3fdb3893": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "eea47fbfb6764d2d8072c08ad33bf2bd": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f0e8755a989b493791b9efcb6b675aa5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f2ba50047c764c5c9f8e384672d49050": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "f5d0c5dae3fc41848ba45e99fccc57f6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "fa01c038682a40668590b94a00a4a241": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fb18af4aa2d64f3da0e019ed601d7d7b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fb9bc72381a24c0ab759f875fe5d1d3b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "fbe640bbc7d541fa9e337874f7ebb100": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fd0efe1ba0794d07801286e8c8c82151": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fd49beba21cd45ccb858e36c10762a7f": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "fe6f724fbcd549d18f69f46f73cec77e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "danger", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_af18873d5e6c462bb6b97c59b6dda6d8", "max": 2.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_ee78442ff4344ba1927dbd6e3fdb3893", "value": 0.0}}, "ff2fae1a3eff4dbbab900312f78a04fc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/mnist-hello-world.ipynb b/source/notebooks/lightning_examples/mnist-hello-world.ipynb deleted file mode 100644 index ea6d433..0000000 --- a/source/notebooks/lightning_examples/mnist-hello-world.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "afc54843", "metadata": {"papermill": {"duration": 0.031932, "end_time": "2021-12-04T16:48:02.533062", "exception": false, "start_time": "2021-12-04T16:48:02.501130", "status": "completed"}, "tags": []}, "source": ["\n", "# Introduction to Pytorch Lightning\n", "\n", "* **Author:** PL team\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:53:03.416116\n", "\n", "In this notebook, we'll go over the basics of lightning by preparing models to train on the [MNIST Handwritten Digits dataset](https://en.wikipedia.org/wiki/MNIST_database).\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/mnist-hello-world.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "27a845da", "metadata": {"papermill": {"duration": 0.027971, "end_time": "2021-12-04T16:48:02.591409", "exception": false, "start_time": "2021-12-04T16:48:02.563438", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "012ca993", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T16:48:02.653518Z", "iopub.status.busy": "2021-12-04T16:48:02.653036Z", "iopub.status.idle": "2021-12-04T16:48:05.576378Z", "shell.execute_reply": "2021-12-04T16:48:05.576765Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.958093, "end_time": "2021-12-04T16:48:05.577047", "exception": false, "start_time": "2021-12-04T16:48:02.618954", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"torchvision\" \"torch>=1.6, <1.9\" \"pytorch-lightning>=1.3\" \"torchmetrics>=0.3\""]}, {"cell_type": "code", "execution_count": 2, "id": "bd97d928", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:48:05.639252Z", "iopub.status.busy": "2021-12-04T16:48:05.638766Z", "iopub.status.idle": "2021-12-04T16:48:09.447461Z", "shell.execute_reply": "2021-12-04T16:48:09.446985Z"}, "papermill": {"duration": 3.841766, "end_time": "2021-12-04T16:48:09.447591", "exception": false, "start_time": "2021-12-04T16:48:05.605825", "status": "completed"}, "tags": []}, "outputs": [], "source": ["import os\n", "\n", "import torch\n", "from pytorch_lightning import LightningModule, Trainer\n", "from torch import nn\n", "from torch.nn import functional as F\n", "from torch.utils.data import DataLoader, random_split\n", "from torchmetrics import Accuracy\n", "from torchvision import transforms\n", "from torchvision.datasets import MNIST\n", "\n", "PATH_DATASETS = os.environ.get(\"PATH_DATASETS\", \".\")\n", "AVAIL_GPUS = min(1, torch.cuda.device_count())\n", "BATCH_SIZE = 256 if AVAIL_GPUS else 64"]}, {"cell_type": "markdown", "id": "5239bd0b", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.027737, "end_time": "2021-12-04T16:48:09.504088", "exception": false, "start_time": "2021-12-04T16:48:09.476351", "status": "completed"}, "tags": []}, "source": ["## Simplest example\n", "\n", "Here's the simplest most minimal example with just a training loop (no validation, no testing).\n", "\n", "**Keep in Mind** - A `LightningModule` *is* a PyTorch `nn.Module` - it just has a few more helpful features."]}, {"cell_type": "code", "execution_count": 3, "id": "6bd8a190", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:48:09.567111Z", "iopub.status.busy": "2021-12-04T16:48:09.566628Z", "iopub.status.idle": "2021-12-04T16:48:09.568711Z", "shell.execute_reply": "2021-12-04T16:48:09.568201Z"}, "papermill": {"duration": 0.035325, "end_time": "2021-12-04T16:48:09.568811", "exception": false, "start_time": "2021-12-04T16:48:09.533486", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class MNISTModel(LightningModule):\n", " def __init__(self):\n", " super().__init__()\n", " self.l1 = torch.nn.Linear(28 * 28, 10)\n", "\n", " def forward(self, x):\n", " return torch.relu(self.l1(x.view(x.size(0), -1)))\n", "\n", " def training_step(self, batch, batch_nb):\n", " x, y = batch\n", " loss = F.cross_entropy(self(x), y)\n", " return loss\n", "\n", " def configure_optimizers(self):\n", " return torch.optim.Adam(self.parameters(), lr=0.02)"]}, {"cell_type": "markdown", "id": "341c1376", "metadata": {"papermill": {"duration": 0.028061, "end_time": "2021-12-04T16:48:09.624815", "exception": false, "start_time": "2021-12-04T16:48:09.596754", "status": "completed"}, "tags": []}, "source": ["By using the `Trainer` you automatically get:\n", "1. Tensorboard logging\n", "2. Model checkpointing\n", "3. Training and validation loop\n", "4. early-stopping"]}, {"cell_type": "code", "execution_count": 4, "id": "b86e988a", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:48:09.685915Z", "iopub.status.busy": "2021-12-04T16:48:09.685422Z", "iopub.status.idle": "2021-12-04T16:48:28.879121Z", "shell.execute_reply": "2021-12-04T16:48:28.879523Z"}, "papermill": {"duration": 19.226607, "end_time": "2021-12-04T16:48:28.879683", "exception": false, "start_time": "2021-12-04T16:48:09.653076", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/connectors/callback_connector.py:90: LightningDeprecationWarning: Setting `Trainer(progress_bar_refresh_rate=20)` is deprecated in v1.5 and will be removed in v1.7. Please pass `pytorch_lightning.callbacks.progress.TQDMProgressBar` with `refresh_rate` directly to the Trainer's `callbacks` argument instead. Or, to disable the progress bar pass `enable_progress_bar = False` to the Trainer.\n", " rank_zero_deprecation(\n", "GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "--------------------------------\n", "0 | l1 | Linear | 7.9 K \n", "--------------------------------\n", "7.9 K Trainable params\n", "0 Non-trainable params\n", "7.9 K Total params\n", "0.031 Total estimated model params size (MB)\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, train_dataloader, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "5f5b35087a20496caa3f259693946413", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Init our model\n", "mnist_model = MNISTModel()\n", "\n", "# Init DataLoader from MNIST Dataset\n", "train_ds = MNIST(PATH_DATASETS, train=True, download=True, transform=transforms.ToTensor())\n", "train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE)\n", "\n", "# Initialize a trainer\n", "trainer = Trainer(\n", " gpus=AVAIL_GPUS,\n", " max_epochs=3,\n", " progress_bar_refresh_rate=20,\n", ")\n", "\n", "# Train the model \u26a1\n", "trainer.fit(mnist_model, train_loader)"]}, {"cell_type": "markdown", "id": "d3248fa1", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.033634, "end_time": "2021-12-04T16:48:28.948915", "exception": false, "start_time": "2021-12-04T16:48:28.915281", "status": "completed"}, "tags": []}, "source": ["## A more complete MNIST Lightning Module Example\n", "\n", "That wasn't so hard was it?\n", "\n", "Now that we've got our feet wet, let's dive in a bit deeper and write a more complete `LightningModule` for MNIST...\n", "\n", "This time, we'll bake in all the dataset specific pieces directly in the `LightningModule`.\n", "This way, we can avoid writing extra code at the beginning of our script every time we want to run it.\n", "\n", "---\n", "\n", "### Note what the following built-in functions are doing:\n", "\n", "1. [prepare_data()](https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#prepare-data) \ud83d\udcbe\n", " - This is where we can download the dataset. We point to our desired dataset and ask torchvision's `MNIST` dataset class to download if the dataset isn't found there.\n", " - **Note we do not make any state assignments in this function** (i.e. `self.something = ...`)\n", "\n", "2. [setup(stage)](https://pytorch-lightning.readthedocs.io/en/stable/common/lightning_module.html#setup) \u2699\ufe0f\n", " - Loads in data from file and prepares PyTorch tensor datasets for each split (train, val, test).\n", " - Setup expects a 'stage' arg which is used to separate logic for 'fit' and 'test'.\n", " - If you don't mind loading all your datasets at once, you can set up a condition to allow for both 'fit' related setup and 'test' related setup to run whenever `None` is passed to `stage` (or ignore it altogether and exclude any conditionals).\n", " - **Note this runs across all GPUs and it *is* safe to make state assignments here**\n", "\n", "3. [x_dataloader()](https://pytorch-lightning.readthedocs.io/en/stable/api/pytorch_lightning.core.hooks.html) \u267b\ufe0f\n", " - `train_dataloader()`, `val_dataloader()`, and `test_dataloader()` all return PyTorch `DataLoader` instances that are created by wrapping their respective datasets that we prepared in `setup()`"]}, {"cell_type": "code", "execution_count": 5, "id": "6a0ca038", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:48:29.028294Z", "iopub.status.busy": "2021-12-04T16:48:29.018412Z", "iopub.status.idle": "2021-12-04T16:48:29.030251Z", "shell.execute_reply": "2021-12-04T16:48:29.029861Z"}, "papermill": {"duration": 0.04784, "end_time": "2021-12-04T16:48:29.030353", "exception": false, "start_time": "2021-12-04T16:48:28.982513", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class LitMNIST(LightningModule):\n", " def __init__(self, data_dir=PATH_DATASETS, hidden_size=64, learning_rate=2e-4):\n", "\n", " super().__init__()\n", "\n", " # Set our init args as class attributes\n", " self.data_dir = data_dir\n", " self.hidden_size = hidden_size\n", " self.learning_rate = learning_rate\n", "\n", " # Hardcode some dataset specific attributes\n", " self.num_classes = 10\n", " self.dims = (1, 28, 28)\n", " channels, width, height = self.dims\n", " self.transform = transforms.Compose(\n", " [\n", " transforms.ToTensor(),\n", " transforms.Normalize((0.1307,), (0.3081,)),\n", " ]\n", " )\n", "\n", " # Define PyTorch model\n", " self.model = nn.Sequential(\n", " nn.Flatten(),\n", " nn.Linear(channels * width * height, hidden_size),\n", " nn.ReLU(),\n", " nn.Dropout(0.1),\n", " nn.Linear(hidden_size, hidden_size),\n", " nn.ReLU(),\n", " nn.Dropout(0.1),\n", " nn.Linear(hidden_size, self.num_classes),\n", " )\n", "\n", " self.accuracy = Accuracy()\n", "\n", " def forward(self, x):\n", " x = self.model(x)\n", " return F.log_softmax(x, dim=1)\n", "\n", " def training_step(self, batch, batch_idx):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " preds = torch.argmax(logits, dim=1)\n", " self.accuracy(preds, y)\n", "\n", " # Calling self.log will surface up scalars for you in TensorBoard\n", " self.log(\"val_loss\", loss, prog_bar=True)\n", " self.log(\"val_acc\", self.accuracy, prog_bar=True)\n", " return loss\n", "\n", " def test_step(self, batch, batch_idx):\n", " # Here we just reuse the validation_step for testing\n", " return self.validation_step(batch, batch_idx)\n", "\n", " def configure_optimizers(self):\n", " optimizer = torch.optim.Adam(self.parameters(), lr=self.learning_rate)\n", " return optimizer\n", "\n", " ####################\n", " # DATA RELATED HOOKS\n", " ####################\n", "\n", " def prepare_data(self):\n", " # download\n", " MNIST(self.data_dir, train=True, download=True)\n", " MNIST(self.data_dir, train=False, download=True)\n", "\n", " def setup(self, stage=None):\n", "\n", " # Assign train/val datasets for use in dataloaders\n", " if stage == \"fit\" or stage is None:\n", " mnist_full = MNIST(self.data_dir, train=True, transform=self.transform)\n", " self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])\n", "\n", " # Assign test dataset for use in dataloader(s)\n", " if stage == \"test\" or stage is None:\n", " self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform)\n", "\n", " def train_dataloader(self):\n", " return DataLoader(self.mnist_train, batch_size=BATCH_SIZE)\n", "\n", " def val_dataloader(self):\n", " return DataLoader(self.mnist_val, batch_size=BATCH_SIZE)\n", "\n", " def test_dataloader(self):\n", " return DataLoader(self.mnist_test, batch_size=BATCH_SIZE)"]}, {"cell_type": "code", "execution_count": 6, "id": "8b595285", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:48:29.104668Z", "iopub.status.busy": "2021-12-04T16:48:29.104193Z", "iopub.status.idle": "2021-12-04T16:49:00.348987Z", "shell.execute_reply": "2021-12-04T16:49:00.348528Z"}, "papermill": {"duration": 31.284667, "end_time": "2021-12-04T16:49:00.349114", "exception": false, "start_time": "2021-12-04T16:48:29.064447", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "----------------------------------------\n", "0 | model | Sequential | 55.1 K\n", "1 | accuracy | Accuracy | 0 \n", "----------------------------------------\n", "55.1 K Trainable params\n", "0 Non-trainable params\n", "55.1 K Total params\n", "0.220 Total estimated model params size (MB)\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "601373ace9d24621bdc3be090c0e9b8b", "version_major": 2, "version_minor": 0}, "text/plain": ["Validation sanity check: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, val_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "11ce7ae5de484643b6cc8ad0b77411fe", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "f7c86a8dd9c04460b3a3cf2b3056dad0", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "292a3bb25f83487db56ebc2b6e7d117b", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "6fefda9cb8654253bbd58b94764626e3", "version_major": 2, "version_minor": 0}, "text/plain": ["Validating: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["model = LitMNIST()\n", "trainer = Trainer(\n", " gpus=AVAIL_GPUS,\n", " max_epochs=3,\n", " progress_bar_refresh_rate=20,\n", ")\n", "trainer.fit(model)"]}, {"cell_type": "markdown", "id": "436f04a4", "metadata": {"papermill": {"duration": 0.043938, "end_time": "2021-12-04T16:49:00.437413", "exception": false, "start_time": "2021-12-04T16:49:00.393475", "status": "completed"}, "tags": []}, "source": ["### Testing\n", "\n", "To test a model, call `trainer.test(model)`.\n", "\n", "Or, if you've just trained a model, you can just call `trainer.test()` and Lightning will automatically\n", "test using the best saved checkpoint (conditioned on val_loss)."]}, {"cell_type": "code", "execution_count": 7, "id": "caee2e97", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:00.530200Z", "iopub.status.busy": "2021-12-04T16:49:00.529111Z", "iopub.status.idle": "2021-12-04T16:49:02.326996Z", "shell.execute_reply": "2021-12-04T16:49:02.326564Z"}, "papermill": {"duration": 1.845813, "end_time": "2021-12-04T16:49:02.327125", "exception": false, "start_time": "2021-12-04T16:49:00.481312", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/trainer.py:1393: UserWarning: `.test(ckpt_path=None)` was called without a model. The best model of the previous `fit` call will be used. You can pass `test(ckpt_path='best')` to use and best model checkpoint and avoid this warning or `ckpt_path=trainer.model_checkpoint.last_model_path` to use the last model.\n", " rank_zero_warn(\n", "Restoring states from the checkpoint path at /__w/1/s/lightning_logs/version_7/checkpoints/epoch=2-step=644.ckpt\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["Loaded model weights from checkpoint at /__w/1/s/lightning_logs/version_7/checkpoints/epoch=2-step=644.ckpt\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, test_dataloader 0, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "b2042d298d80448885957a83ec7d3c2b", "version_major": 2, "version_minor": 0}, "text/plain": ["Testing: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stdout", "output_type": "stream", "text": ["--------------------------------------------------------------------------------\n", "DATALOADER:0 TEST RESULTS\n", "{'val_acc': 0.9211000204086304, 'val_loss': 0.25840938091278076}\n", "--------------------------------------------------------------------------------\n"]}, {"data": {"text/plain": ["[{'val_loss': 0.25840938091278076, 'val_acc': 0.9211000204086304}]"]}, "execution_count": 7, "metadata": {}, "output_type": "execute_result"}], "source": ["trainer.test()"]}, {"cell_type": "markdown", "id": "5a843462", "metadata": {"papermill": {"duration": 0.049757, "end_time": "2021-12-04T16:49:02.427433", "exception": false, "start_time": "2021-12-04T16:49:02.377676", "status": "completed"}, "tags": []}, "source": ["### Bonus Tip\n", "\n", "You can keep calling `trainer.fit(model)` as many times as you'd like to continue training"]}, {"cell_type": "code", "execution_count": 8, "id": "249d097e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:02.532137Z", "iopub.status.busy": "2021-12-04T16:49:02.531667Z", "iopub.status.idle": "2021-12-04T16:49:02.760734Z", "shell.execute_reply": "2021-12-04T16:49:02.760300Z"}, "papermill": {"duration": 0.283193, "end_time": "2021-12-04T16:49:02.760859", "exception": false, "start_time": "2021-12-04T16:49:02.477666", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "----------------------------------------\n", "0 | model | Sequential | 55.1 K\n", "1 | accuracy | Accuracy | 0 \n", "----------------------------------------\n", "55.1 K Trainable params\n", "0 Non-trainable params\n", "55.1 K Total params\n", "0.220 Total estimated model params size (MB)\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/callbacks/model_checkpoint.py:623: UserWarning: Checkpoint directory /__w/1/s/lightning_logs/version_7/checkpoints exists and is not empty.\n", " rank_zero_warn(f\"Checkpoint directory {dirpath} exists and is not empty.\")\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "a123bf1c0353409dbe32f4a4a590ab6e", "version_major": 2, "version_minor": 0}, "text/plain": ["Validation sanity check: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}], "source": ["trainer.fit(model)"]}, {"cell_type": "markdown", "id": "c1c65385", "metadata": {"papermill": {"duration": 0.053219, "end_time": "2021-12-04T16:49:02.867679", "exception": false, "start_time": "2021-12-04T16:49:02.814460", "status": "completed"}, "tags": []}, "source": ["In Colab, you can use the TensorBoard magic function to view the logs that Lightning has created for you!"]}, {"cell_type": "code", "execution_count": 9, "id": "fdd68fee", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:02.978094Z", "iopub.status.busy": "2021-12-04T16:49:02.977622Z", "iopub.status.idle": "2021-12-04T16:49:04.563924Z", "shell.execute_reply": "2021-12-04T16:49:04.564319Z"}, "papermill": {"duration": 1.643849, "end_time": "2021-12-04T16:49:04.564484", "exception": false, "start_time": "2021-12-04T16:49:02.920635", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", " \n", " \n", " "], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Start tensorboard.\n", "%load_ext tensorboard\n", "%tensorboard --logdir lightning_logs/"]}, {"cell_type": "markdown", "id": "fb7c0504", "metadata": {"papermill": {"duration": 0.054861, "end_time": "2021-12-04T16:49:04.675577", "exception": false, "start_time": "2021-12-04T16:49:04.620716", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: Introduction to Pytorch Lightning\n", " :card_description: In this notebook, we'll go over the basics of lightning by preparing models to train on the [MNIST Handwritten Digits dataset](https://en.wikipedia.org/wiki/MNIST_database).\n", " :tags: Image,GPU/TPU,Lightning-Examples"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab,colab_type,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 64.932502, "end_time": "2021-12-04T16:49:06.142038", "environment_variables": {}, "exception": null, "input_path": "lightning_examples/mnist-hello-world/hello-world.ipynb", "output_path": ".notebooks/lightning_examples/mnist-hello-world.ipynb", "parameters": {}, "start_time": "2021-12-04T16:48:01.209536", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"09b7fc22ad90482a84100bbd3d5b1c53": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "09ba6f68123f47288cacec730782831d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "11ce7ae5de484643b6cc8ad0b77411fe": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_c4b7018fa4ef4a62ad60e470cfee1c9f", "IPY_MODEL_7566c4e4b4234020b638cf6c489feb47", "IPY_MODEL_b3ab34ed6d4042ae91e80bdd838ad63d"], "layout": "IPY_MODEL_7fafd99b34754f9fb3ff703cb1c762a1"}}, "14b9bf5c15264c889319160095808d4b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1c3f0a5537464548ba3655d0151b913b", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_4501abf9f43b45fdadccf542b6dc11bc", "value": 20.0}}, "19d6db216fc94fd7a8b903efbcaf8d7e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "1c3f0a5537464548ba3655d0151b913b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1e4f591b0f5c4310a2e2c3df78d41654": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "2432b8e2d34a4213a0ac1bdc1e25ff98": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "273c67245899415eadf04785d18c24fe": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "27b765a1b4d54bc1b9cb84c86b4d53fa": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "28e0a4fd04d944bba0bdd698f9f39fbb": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "danger", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_29c0045e415c420cbdd41c7d5b2fe42a", "max": 2.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_478fad99664b4688be1597372d2a8cd3", "value": 0.0}}, "292a3bb25f83487db56ebc2b6e7d117b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_bee7eb1c5f004aa09e8c8d480450ddff", "IPY_MODEL_14b9bf5c15264c889319160095808d4b", "IPY_MODEL_95d4c0d79a4440bdb504b719acbf0778"], "layout": "IPY_MODEL_273c67245899415eadf04785d18c24fe"}}, "29c0045e415c420cbdd41c7d5b2fe42a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "33522fca1b8a4402b486f5bd2ec52f16": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "36c52a31cbd84d1da6e45c5a1123c8e0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_56a2270791254a2b8f2d90010ef093de", "placeholder": "\u200b", "style": "IPY_MODEL_fee96cc5fbdc402aa2943e79a4f2adad", "value": " 0/2 [00:00<?, ?it/s]"}}, "36f68744cfbd40919535814cfe014de3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "danger", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8a457394c16d4c5da14f73ede0aa58dc", "max": 2.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_c48ac9d9272f48f0a4930157bbbc4544", "value": 0.0}}, "3945a41419174768bdd37f6346ac4800": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "3d9ac0fe691d49799e8bfd681c7a0da6": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "41a1e336782045fa8b4c51d48bec6076": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c717831da0c7486782afa3bd59a5867a", "placeholder": "\u200b", "style": "IPY_MODEL_d70e90c6cb0646769c81d4754c39c29b", "value": " 0/2 [00:00<?, ?it/s]"}}, "440b8511219849d8a2035bacc3faae8b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "4501abf9f43b45fdadccf542b6dc11bc": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "46ed279af4134635be629e5f424c178b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "478fad99664b4688be1597372d2a8cd3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "56a2270791254a2b8f2d90010ef093de": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "57326adc0f7447209791335361c9b25e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5785b759472d4738b820d3404fb97ea2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5b5fd440314f4e08b05560ea5411d6a7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "5e503ce2e5d547369325b3b3c57c91d4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_440b8511219849d8a2035bacc3faae8b", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_3d9ac0fe691d49799e8bfd681c7a0da6", "value": 20.0}}, "5f5b35087a20496caa3f259693946413": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_61750b7330d8424dba8b0236b1ea916f", "IPY_MODEL_dd0e944c91f0494e9de3e89daa1d4c3d", "IPY_MODEL_6968aa8766cd43f58e25354153ec2c89"], "layout": "IPY_MODEL_fbc62c1a049e480799d3bdb649afc778"}}, "601373ace9d24621bdc3be090c0e9b8b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_7bb333f35c324e62b30a6cf6c18ff8c4", "IPY_MODEL_28e0a4fd04d944bba0bdd698f9f39fbb", "IPY_MODEL_36c52a31cbd84d1da6e45c5a1123c8e0"], "layout": "IPY_MODEL_f1ca115efd3241449f819b4377f98007"}}, "61750b7330d8424dba8b0236b1ea916f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_aa7559605123408da3bb35c2363314a2", "placeholder": "\u200b", "style": "IPY_MODEL_962f2ab6be8e40118d4609258536dde1", "value": "Epoch 2: 100%"}}, "6968aa8766cd43f58e25354153ec2c89": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b6d778d7d9b54c099015a006d3385bcb", "placeholder": "\u200b", "style": "IPY_MODEL_46ed279af4134635be629e5f424c178b", "value": " 235/235 [00:05<00:00, 45.01it/s, loss=0.213, v_num=6]"}}, "6fefda9cb8654253bbd58b94764626e3": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_db908903174440b59181bb496ad0bd7d", "IPY_MODEL_5e503ce2e5d547369325b3b3c57c91d4", "IPY_MODEL_75c54f7791c849ae8fbe03af570c54a1"], "layout": "IPY_MODEL_33522fca1b8a4402b486f5bd2ec52f16"}}, "7566c4e4b4234020b638cf6c489feb47": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_3945a41419174768bdd37f6346ac4800", "max": 235.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_f68fa8b22e4e4c0a8ba07448efaac3d5", "value": 235.0}}, "75c54f7791c849ae8fbe03af570c54a1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_e38e27d7dd8a435792803c03f6af9982", "placeholder": "\u200b", "style": "IPY_MODEL_57326adc0f7447209791335361c9b25e", "value": " 20/20 [00:00<00:00, 23.11it/s]"}}, "775676e5b53e4a3fa26c8ea381b4e483": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "79eb4cc1040e4a9a8b971876af6fd9bf": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "7bb333f35c324e62b30a6cf6c18ff8c4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c41d39eee0404e809f007049084d30cb", "placeholder": "\u200b", "style": "IPY_MODEL_09ba6f68123f47288cacec730782831d", "value": "Validation sanity check: 0%"}}, "7fafd99b34754f9fb3ff703cb1c762a1": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "80ef770fb6a34f94a5b0cb6d4629f1c9": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_1e4f591b0f5c4310a2e2c3df78d41654", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_a6926eb7fd14423e8f9b284b6b88564c", "value": 1.0}}, "8289d840a8174725a33705d0439b9f10": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5785b759472d4738b820d3404fb97ea2", "placeholder": "\u200b", "style": "IPY_MODEL_a18c5a748a154261b6e333e87968d4a8", "value": "Validation sanity check: 0%"}}, "8406484c64fe477c9d3486292a77d2da": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "89f6945a57fa4bed9a22d6d3f329c59b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "8a457394c16d4c5da14f73ede0aa58dc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8bcd7a8fc13e46a5945906814a70721d": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "8ceaaab585ca48ac83ad55bb94e639d2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_d7ea8fa06e4549c58feb8fbf60d54f12", "placeholder": "\u200b", "style": "IPY_MODEL_8ef594a7ac2a49b4b4bb65a932f800a4", "value": "Validating: 100%"}}, "8ef594a7ac2a49b4b4bb65a932f800a4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "9071e7327b5744a1b17e70e9608e9662": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_90c9f1abf4e6487f9db8021657ad8057", "placeholder": "\u200b", "style": "IPY_MODEL_775676e5b53e4a3fa26c8ea381b4e483", "value": " 20/20 [00:00<00:00, 23.27it/s]"}}, "90c9f1abf4e6487f9db8021657ad8057": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "94477649bcc24394b6766ae40ee7a37b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "95d4c0d79a4440bdb504b719acbf0778": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8bcd7a8fc13e46a5945906814a70721d", "placeholder": "\u200b", "style": "IPY_MODEL_27b765a1b4d54bc1b9cb84c86b4d53fa", "value": " 20/20 [00:00<00:00, 23.17it/s]"}}, "962f2ab6be8e40118d4609258536dde1": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a123bf1c0353409dbe32f4a4a590ab6e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_8289d840a8174725a33705d0439b9f10", "IPY_MODEL_36f68744cfbd40919535814cfe014de3", "IPY_MODEL_41a1e336782045fa8b4c51d48bec6076"], "layout": "IPY_MODEL_ff65026a9670453ebcecb7e57edd3cec"}}, "a18c5a748a154261b6e333e87968d4a8": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "a6926eb7fd14423e8f9b284b6b88564c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "aa7559605123408da3bb35c2363314a2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b2042d298d80448885957a83ec7d3c2b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_df7beb8dbb0446d9a206713382500de7", "IPY_MODEL_80ef770fb6a34f94a5b0cb6d4629f1c9", "IPY_MODEL_f42bc9c824594b859e33d07c4246c0b5"], "layout": "IPY_MODEL_b3c049feedaf4965a787b5c4d545f32e"}}, "b294ce2c629b4c8ca6e53502f288a603": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "b3ab34ed6d4042ae91e80bdd838ad63d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_8406484c64fe477c9d3486292a77d2da", "placeholder": "\u200b", "style": "IPY_MODEL_c420cef9f94f454b8698511085cf538e", "value": " 235/235 [00:10<00:00, 22.72it/s, loss=0.31, v_num=7, val_loss=0.275, val_acc=0.921]"}}, "b3c049feedaf4965a787b5c4d545f32e": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "b6d778d7d9b54c099015a006d3385bcb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "bee7eb1c5f004aa09e8c8d480450ddff": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_94477649bcc24394b6766ae40ee7a37b", "placeholder": "\u200b", "style": "IPY_MODEL_e6903d15de3d45f091f541aa054db6db", "value": "Validating: 100%"}}, "c41d39eee0404e809f007049084d30cb": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c420cef9f94f454b8698511085cf538e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "c48ac9d9272f48f0a4930157bbbc4544": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "c4b7018fa4ef4a62ad60e470cfee1c9f": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_09b7fc22ad90482a84100bbd3d5b1c53", "placeholder": "\u200b", "style": "IPY_MODEL_e4c04bc4371d4ca4b06392616ee7ccd7", "value": "Epoch 2: 100%"}}, "c717831da0c7486782afa3bd59a5867a": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d55566f7956945348c51ae8cec3095d2": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "d70e90c6cb0646769c81d4754c39c29b": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "d7ea8fa06e4549c58feb8fbf60d54f12": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "db908903174440b59181bb496ad0bd7d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f79e96bcb58a4eb4bcbacb7174bc2ba2", "placeholder": "\u200b", "style": "IPY_MODEL_e91201e5eeb14339b07597e7a1d71aad", "value": "Validating: 100%"}}, "dd0e944c91f0494e9de3e89daa1d4c3d": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_f4700930b6674d4fba7ef5015f0bd1fc", "max": 235.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_d55566f7956945348c51ae8cec3095d2", "value": 235.0}}, "de4a8179670a4f57b219144d967a13c5": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "df7beb8dbb0446d9a206713382500de7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_de4a8179670a4f57b219144d967a13c5", "placeholder": "\u200b", "style": "IPY_MODEL_79eb4cc1040e4a9a8b971876af6fd9bf", "value": "Testing: 100%"}}, "e38e27d7dd8a435792803c03f6af9982": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "e4c04bc4371d4ca4b06392616ee7ccd7": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e6903d15de3d45f091f541aa054db6db": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "e91201e5eeb14339b07597e7a1d71aad": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "f1ca115efd3241449f819b4377f98007": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "f42bc9c824594b859e33d07c4246c0b5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_5b5fd440314f4e08b05560ea5411d6a7", "placeholder": "\u200b", "style": "IPY_MODEL_89f6945a57fa4bed9a22d6d3f329c59b", "value": " 40/40 [00:01<00:00, 23.36it/s]"}}, "f4700930b6674d4fba7ef5015f0bd1fc": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f68fa8b22e4e4c0a8ba07448efaac3d5": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}, "f79e96bcb58a4eb4bcbacb7174bc2ba2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "f7c86a8dd9c04460b3a3cf2b3056dad0": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_8ceaaab585ca48ac83ad55bb94e639d2", "IPY_MODEL_fc20401683aa4661a2ab78db340ccbd4", "IPY_MODEL_9071e7327b5744a1b17e70e9608e9662"], "layout": "IPY_MODEL_2432b8e2d34a4213a0ac1bdc1e25ff98"}}, "fbc62c1a049e480799d3bdb649afc778": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "fc20401683aa4661a2ab78db340ccbd4": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_b294ce2c629b4c8ca6e53502f288a603", "max": 20.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_19d6db216fc94fd7a8b903efbcaf8d7e", "value": 20.0}}, "fee96cc5fbdc402aa2943e79a4f2adad": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "ff65026a9670453ebcecb7e57edd3cec": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/mnist-tpu-training.ipynb b/source/notebooks/lightning_examples/mnist-tpu-training.ipynb deleted file mode 100644 index d718144..0000000 --- a/source/notebooks/lightning_examples/mnist-tpu-training.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "e49ff764", "metadata": {}, "source": ["\n", "# TPU training with PyTorch Lightning\n", "\n", "* **Author:** PL team\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:53:07.915320\n", "\n", "In this notebook, we'll train a model on TPUs. Updating one Trainer flag is all you need for that. The most up to documentation related to TPU training can be found [here](https://pytorch-lightning.readthedocs.io/en/latest/advanced/tpu.html).\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/mnist-tpu-training.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "f024b9fe", "metadata": {}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": null, "id": "26f8e4d5", "metadata": {"colab": {}, "colab_type": "code", "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0}, "outputs": [], "source": ["! pip install --quiet \"torch>=1.6, <1.9\" \"pytorch-lightning>=1.3\" \"torchvision\" \"torchmetrics>=0.3\""]}, {"cell_type": "markdown", "id": "a1a49780", "metadata": {}, "source": ["### Install Colab TPU compatible PyTorch/TPU wheels and dependencies"]}, {"cell_type": "code", "execution_count": null, "id": "4ed2602f", "metadata": {}, "outputs": [], "source": ["! pip install cloud-tpu-client==0.10 https://storage.googleapis.com/tpu-pytorch/wheels/torch_xla-1.8-cp37-cp37m-linux_x86_64.whl"]}, {"cell_type": "code", "execution_count": null, "id": "5d907ddd", "metadata": {}, "outputs": [], "source": ["import torch\n", "import torch.nn.functional as F\n", "from pytorch_lightning import LightningDataModule, LightningModule, Trainer\n", "from torch import nn\n", "from torch.utils.data import DataLoader, random_split\n", "from torchmetrics.functional import accuracy\n", "from torchvision import transforms\n", "\n", "# Note - you must have torchvision installed for this example\n", "from torchvision.datasets import MNIST\n", "\n", "BATCH_SIZE = 1024"]}, {"cell_type": "markdown", "id": "5f438e89", "metadata": {"lines_to_next_cell": 2}, "source": ["### Defining The `MNISTDataModule`\n", "\n", "Below we define `MNISTDataModule`. You can learn more about datamodules\n", "in [docs](https://pytorch-lightning.readthedocs.io/en/latest/extensions/datamodules.html)."]}, {"cell_type": "code", "execution_count": null, "id": "5e591ce5", "metadata": {"lines_to_next_cell": 2}, "outputs": [], "source": ["class MNISTDataModule(LightningDataModule):\n", " def __init__(self, data_dir: str = \"./\"):\n", " super().__init__()\n", " self.data_dir = data_dir\n", " self.transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])\n", "\n", " # self.dims is returned when you call dm.size()\n", " # Setting default dims here because we know them.\n", " # Could optionally be assigned dynamically in dm.setup()\n", " self.dims = (1, 28, 28)\n", " self.num_classes = 10\n", "\n", " def prepare_data(self):\n", " # download\n", " MNIST(self.data_dir, train=True, download=True)\n", " MNIST(self.data_dir, train=False, download=True)\n", "\n", " def setup(self, stage=None):\n", "\n", " # Assign train/val datasets for use in dataloaders\n", " if stage == \"fit\" or stage is None:\n", " mnist_full = MNIST(self.data_dir, train=True, transform=self.transform)\n", " self.mnist_train, self.mnist_val = random_split(mnist_full, [55000, 5000])\n", "\n", " # Assign test dataset for use in dataloader(s)\n", " if stage == \"test\" or stage is None:\n", " self.mnist_test = MNIST(self.data_dir, train=False, transform=self.transform)\n", "\n", " def train_dataloader(self):\n", " return DataLoader(self.mnist_train, batch_size=BATCH_SIZE)\n", "\n", " def val_dataloader(self):\n", " return DataLoader(self.mnist_val, batch_size=BATCH_SIZE)\n", "\n", " def test_dataloader(self):\n", " return DataLoader(self.mnist_test, batch_size=BATCH_SIZE)"]}, {"cell_type": "markdown", "id": "fa53ebb7", "metadata": {"lines_to_next_cell": 2}, "source": ["### Defining the `LitModel`\n", "\n", "Below, we define the model `LitMNIST`."]}, {"cell_type": "code", "execution_count": null, "id": "a5f891e3", "metadata": {}, "outputs": [], "source": ["class LitModel(LightningModule):\n", " def __init__(self, channels, width, height, num_classes, hidden_size=64, learning_rate=2e-4):\n", "\n", " super().__init__()\n", "\n", " self.save_hyperparameters()\n", "\n", " self.model = nn.Sequential(\n", " nn.Flatten(),\n", " nn.Linear(channels * width * height, hidden_size),\n", " nn.ReLU(),\n", " nn.Dropout(0.1),\n", " nn.Linear(hidden_size, hidden_size),\n", " nn.ReLU(),\n", " nn.Dropout(0.1),\n", " nn.Linear(hidden_size, num_classes),\n", " )\n", "\n", " def forward(self, x):\n", " x = self.model(x)\n", " return F.log_softmax(x, dim=1)\n", "\n", " def training_step(self, batch, batch_idx):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " self.log(\"train_loss\", loss)\n", " return loss\n", "\n", " def validation_step(self, batch, batch_idx):\n", " x, y = batch\n", " logits = self(x)\n", " loss = F.nll_loss(logits, y)\n", " preds = torch.argmax(logits, dim=1)\n", " acc = accuracy(preds, y)\n", " self.log(\"val_loss\", loss, prog_bar=True)\n", " self.log(\"val_acc\", acc, prog_bar=True)\n", " return loss\n", "\n", " def configure_optimizers(self):\n", " optimizer = torch.optim.Adam(self.parameters(), lr=self.hparams.learning_rate)\n", " return optimizer"]}, {"cell_type": "markdown", "id": "1009ada4", "metadata": {}, "source": ["### TPU Training\n", "\n", "Lightning supports training on a single TPU core or 8 TPU cores.\n", "\n", "The Trainer parameters `tpu_cores` defines how many TPU cores to train on (1 or 8) / Single TPU core to train on [1].\n", "\n", "For Single TPU training, Just pass the TPU core ID [1-8] in a list.\n", "Setting `tpu_cores=[5]` will train on TPU core ID 5."]}, {"cell_type": "markdown", "id": "55c40381", "metadata": {}, "source": ["Train on TPU core ID 5 with `tpu_cores=[5]`."]}, {"cell_type": "code", "execution_count": null, "id": "0eb329b1", "metadata": {}, "outputs": [], "source": ["# Init DataModule\n", "dm = MNISTDataModule()\n", "# Init model from datamodule's attributes\n", "model = LitModel(*dm.size(), dm.num_classes)\n", "# Init trainer\n", "trainer = Trainer(max_epochs=3, progress_bar_refresh_rate=20, tpu_cores=[5])\n", "# Train\n", "trainer.fit(model, dm)"]}, {"cell_type": "markdown", "id": "e4b81515", "metadata": {}, "source": ["Train on single TPU core with `tpu_cores=1`."]}, {"cell_type": "code", "execution_count": null, "id": "83272dbd", "metadata": {}, "outputs": [], "source": ["# Init DataModule\n", "dm = MNISTDataModule()\n", "# Init model from datamodule's attributes\n", "model = LitModel(*dm.size(), dm.num_classes)\n", "# Init trainer\n", "trainer = Trainer(max_epochs=3, progress_bar_refresh_rate=20, tpu_cores=1)\n", "# Train\n", "trainer.fit(model, dm)"]}, {"cell_type": "markdown", "id": "3d2ce0a8", "metadata": {}, "source": ["Train on 8 TPU cores with `tpu_cores=8`.\n", "You might have to restart the notebook to run it on 8 TPU cores after training on single TPU core."]}, {"cell_type": "code", "execution_count": null, "id": "4d07c972", "metadata": {}, "outputs": [], "source": ["# Init DataModule\n", "dm = MNISTDataModule()\n", "# Init model from datamodule's attributes\n", "model = LitModel(*dm.size(), dm.num_classes)\n", "# Init trainer\n", "trainer = Trainer(max_epochs=3, progress_bar_refresh_rate=20, tpu_cores=8)\n", "# Train\n", "trainer.fit(model, dm)"]}, {"cell_type": "markdown", "id": "6ebe791c", "metadata": {}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: TPU training with PyTorch Lightning\n", " :card_description: In this notebook, we'll train a model on TPUs. Updating one Trainer flag is all you need for that. The most up to documentation related to TPU training can be found...\n", " :tags: Image,GPU/TPU,Lightning-Examples"]}], "metadata": {"jupytext": {"cell_metadata_filter": "colab_type,colab,id,-all", "formats": "ipynb,py:percent", "main_language": "python"}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/reinforce-learning-DQN.ipynb b/source/notebooks/lightning_examples/reinforce-learning-DQN.ipynb deleted file mode 100644 index c69b474..0000000 --- a/source/notebooks/lightning_examples/reinforce-learning-DQN.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "4efbbac7", "metadata": {"papermill": {"duration": 0.032188, "end_time": "2021-12-04T16:49:24.620327", "exception": false, "start_time": "2021-12-04T16:49:24.588139", "status": "completed"}, "tags": []}, "source": ["\n", "# How to train a Deep Q Network\n", "\n", "* **Author:** PL team\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:53:09.587502\n", "\n", "Main takeaways:\n", "\n", "1. RL has the same flow as previous models we have seen, with a few additions\n", "2. Handle unsupervised learning by using an IterableDataset where the dataset itself is constantly updated during training\n", "3. Each training step carries has the agent taking an action in the environment and storing the experience in the IterableDataset\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/reinforce-learning-DQN.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "efc6c370", "metadata": {"papermill": {"duration": 0.02782, "end_time": "2021-12-04T16:49:24.679755", "exception": false, "start_time": "2021-12-04T16:49:24.651935", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "5894fd9f", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T16:49:24.744158Z", "iopub.status.busy": "2021-12-04T16:49:24.743680Z", "iopub.status.idle": "2021-12-04T16:49:27.687873Z", "shell.execute_reply": "2021-12-04T16:49:27.687317Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 2.979438, "end_time": "2021-12-04T16:49:27.688021", "exception": false, "start_time": "2021-12-04T16:49:24.708583", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"torch>=1.6, <1.9\" \"pytorch-lightning>=1.3\" \"gym\" \"torchmetrics>=0.3\""]}, {"cell_type": "code", "execution_count": 2, "id": "ad4092f4", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:27.750875Z", "iopub.status.busy": "2021-12-04T16:49:27.750375Z", "iopub.status.idle": "2021-12-04T16:49:31.622878Z", "shell.execute_reply": "2021-12-04T16:49:31.623262Z"}, "papermill": {"duration": 3.906565, "end_time": "2021-12-04T16:49:31.623443", "exception": false, "start_time": "2021-12-04T16:49:27.716878", "status": "completed"}, "tags": []}, "outputs": [], "source": ["import os\n", "from collections import OrderedDict, deque, namedtuple\n", "from typing import List, Tuple\n", "\n", "import gym\n", "import numpy as np\n", "import torch\n", "from pytorch_lightning import LightningModule, Trainer\n", "from pytorch_lightning.utilities import DistributedType\n", "from torch import Tensor, nn\n", "from torch.optim import Adam, Optimizer\n", "from torch.utils.data import DataLoader\n", "from torch.utils.data.dataset import IterableDataset\n", "\n", "PATH_DATASETS = os.environ.get(\"PATH_DATASETS\", \".\")\n", "AVAIL_GPUS = min(1, torch.cuda.device_count())"]}, {"cell_type": "code", "execution_count": 3, "id": "4bd3cb4e", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:31.685019Z", "iopub.status.busy": "2021-12-04T16:49:31.684540Z", "iopub.status.idle": "2021-12-04T16:49:31.686662Z", "shell.execute_reply": "2021-12-04T16:49:31.686183Z"}, "papermill": {"duration": 0.035013, "end_time": "2021-12-04T16:49:31.686760", "exception": false, "start_time": "2021-12-04T16:49:31.651747", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class DQN(nn.Module):\n", " \"\"\"Simple MLP network.\"\"\"\n", "\n", " def __init__(self, obs_size: int, n_actions: int, hidden_size: int = 128):\n", " \"\"\"\n", " Args:\n", " obs_size: observation/state size of the environment\n", " n_actions: number of discrete actions available in the environment\n", " hidden_size: size of hidden layers\n", " \"\"\"\n", " super().__init__()\n", " self.net = nn.Sequential(\n", " nn.Linear(obs_size, hidden_size),\n", " nn.ReLU(),\n", " nn.Linear(hidden_size, n_actions),\n", " )\n", "\n", " def forward(self, x):\n", " return self.net(x.float())"]}, {"cell_type": "markdown", "id": "012cc8f7", "metadata": {"papermill": {"duration": 0.032792, "end_time": "2021-12-04T16:49:31.746872", "exception": false, "start_time": "2021-12-04T16:49:31.714080", "status": "completed"}, "tags": []}, "source": ["### Memory"]}, {"cell_type": "code", "execution_count": 4, "id": "77510037", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:31.806464Z", "iopub.status.busy": "2021-12-04T16:49:31.805941Z", "iopub.status.idle": "2021-12-04T16:49:31.808050Z", "shell.execute_reply": "2021-12-04T16:49:31.807649Z"}, "papermill": {"duration": 0.033474, "end_time": "2021-12-04T16:49:31.808151", "exception": false, "start_time": "2021-12-04T16:49:31.774677", "status": "completed"}, "tags": []}, "outputs": [], "source": ["\n", "# Named tuple for storing experience steps gathered in training\n", "Experience = namedtuple(\n", " \"Experience\",\n", " field_names=[\"state\", \"action\", \"reward\", \"done\", \"new_state\"],\n", ")"]}, {"cell_type": "code", "execution_count": 5, "id": "8ae0c219", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:31.869846Z", "iopub.status.busy": "2021-12-04T16:49:31.869370Z", "iopub.status.idle": "2021-12-04T16:49:31.871439Z", "shell.execute_reply": "2021-12-04T16:49:31.870977Z"}, "papermill": {"duration": 0.035508, "end_time": "2021-12-04T16:49:31.871534", "exception": false, "start_time": "2021-12-04T16:49:31.836026", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class ReplayBuffer:\n", " \"\"\"Replay Buffer for storing past experiences allowing the agent to learn from them.\n", "\n", " Args:\n", " capacity: size of the buffer\n", " \"\"\"\n", "\n", " def __init__(self, capacity: int) -> None:\n", " self.buffer = deque(maxlen=capacity)\n", "\n", " def __len__(self) -> None:\n", " return len(self.buffer)\n", "\n", " def append(self, experience: Experience) -> None:\n", " \"\"\"Add experience to the buffer.\n", "\n", " Args:\n", " experience: tuple (state, action, reward, done, new_state)\n", " \"\"\"\n", " self.buffer.append(experience)\n", "\n", " def sample(self, batch_size: int) -> Tuple:\n", " indices = np.random.choice(len(self.buffer), batch_size, replace=False)\n", " states, actions, rewards, dones, next_states = zip(*(self.buffer[idx] for idx in indices))\n", "\n", " return (\n", " np.array(states),\n", " np.array(actions),\n", " np.array(rewards, dtype=np.float32),\n", " np.array(dones, dtype=np.bool),\n", " np.array(next_states),\n", " )"]}, {"cell_type": "code", "execution_count": 6, "id": "d1052c84", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:31.931299Z", "iopub.status.busy": "2021-12-04T16:49:31.930829Z", "iopub.status.idle": "2021-12-04T16:49:31.932898Z", "shell.execute_reply": "2021-12-04T16:49:31.932435Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.033795, "end_time": "2021-12-04T16:49:31.932991", "exception": false, "start_time": "2021-12-04T16:49:31.899196", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class RLDataset(IterableDataset):\n", " \"\"\"Iterable Dataset containing the ExperienceBuffer which will be updated with new experiences during training.\n", "\n", " Args:\n", " buffer: replay buffer\n", " sample_size: number of experiences to sample at a time\n", " \"\"\"\n", "\n", " def __init__(self, buffer: ReplayBuffer, sample_size: int = 200) -> None:\n", " self.buffer = buffer\n", " self.sample_size = sample_size\n", "\n", " def __iter__(self) -> Tuple:\n", " states, actions, rewards, dones, new_states = self.buffer.sample(self.sample_size)\n", " for i in range(len(dones)):\n", " yield states[i], actions[i], rewards[i], dones[i], new_states[i]"]}, {"cell_type": "markdown", "id": "fbe4a973", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.027797, "end_time": "2021-12-04T16:49:31.988622", "exception": false, "start_time": "2021-12-04T16:49:31.960825", "status": "completed"}, "tags": []}, "source": ["### Agent"]}, {"cell_type": "code", "execution_count": 7, "id": "42dd9534", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:32.054816Z", "iopub.status.busy": "2021-12-04T16:49:32.054310Z", "iopub.status.idle": "2021-12-04T16:49:32.056372Z", "shell.execute_reply": "2021-12-04T16:49:32.055906Z"}, "lines_to_next_cell": 2, "papermill": {"duration": 0.039725, "end_time": "2021-12-04T16:49:32.056473", "exception": false, "start_time": "2021-12-04T16:49:32.016748", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class Agent:\n", " \"\"\"Base Agent class handeling the interaction with the environment.\"\"\"\n", "\n", " def __init__(self, env: gym.Env, replay_buffer: ReplayBuffer) -> None:\n", " \"\"\"\n", " Args:\n", " env: training environment\n", " replay_buffer: replay buffer storing experiences\n", " \"\"\"\n", " self.env = env\n", " self.replay_buffer = replay_buffer\n", " self.reset()\n", " self.state = self.env.reset()\n", "\n", " def reset(self) -> None:\n", " \"\"\"Resents the environment and updates the state.\"\"\"\n", " self.state = self.env.reset()\n", "\n", " def get_action(self, net: nn.Module, epsilon: float, device: str) -> int:\n", " \"\"\"Using the given network, decide what action to carry out using an epsilon-greedy policy.\n", "\n", " Args:\n", " net: DQN network\n", " epsilon: value to determine likelihood of taking a random action\n", " device: current device\n", "\n", " Returns:\n", " action\n", " \"\"\"\n", " if np.random.random() < epsilon:\n", " action = self.env.action_space.sample()\n", " else:\n", " state = torch.tensor([self.state])\n", "\n", " if device not in [\"cpu\"]:\n", " state = state.cuda(device)\n", "\n", " q_values = net(state)\n", " _, action = torch.max(q_values, dim=1)\n", " action = int(action.item())\n", "\n", " return action\n", "\n", " @torch.no_grad()\n", " def play_step(\n", " self,\n", " net: nn.Module,\n", " epsilon: float = 0.0,\n", " device: str = \"cpu\",\n", " ) -> Tuple[float, bool]:\n", " \"\"\"Carries out a single interaction step between the agent and the environment.\n", "\n", " Args:\n", " net: DQN network\n", " epsilon: value to determine likelihood of taking a random action\n", " device: current device\n", "\n", " Returns:\n", " reward, done\n", " \"\"\"\n", "\n", " action = self.get_action(net, epsilon, device)\n", "\n", " # do step in the environment\n", " new_state, reward, done, _ = self.env.step(action)\n", "\n", " exp = Experience(self.state, action, reward, done, new_state)\n", "\n", " self.replay_buffer.append(exp)\n", "\n", " self.state = new_state\n", " if done:\n", " self.reset()\n", " return reward, done"]}, {"cell_type": "markdown", "id": "17cb3c7e", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.03211, "end_time": "2021-12-04T16:49:32.116593", "exception": false, "start_time": "2021-12-04T16:49:32.084483", "status": "completed"}, "tags": []}, "source": ["### DQN Lightning Module"]}, {"cell_type": "code", "execution_count": 8, "id": "ed5b8877", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:32.185758Z", "iopub.status.busy": "2021-12-04T16:49:32.175032Z", "iopub.status.idle": "2021-12-04T16:49:32.190998Z", "shell.execute_reply": "2021-12-04T16:49:32.190598Z"}, "papermill": {"duration": 0.045916, "end_time": "2021-12-04T16:49:32.191102", "exception": false, "start_time": "2021-12-04T16:49:32.145186", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class DQNLightning(LightningModule):\n", " \"\"\"Basic DQN Model.\"\"\"\n", "\n", " def __init__(\n", " self,\n", " batch_size: int = 16,\n", " lr: float = 1e-2,\n", " env: str = \"CartPole-v0\",\n", " gamma: float = 0.99,\n", " sync_rate: int = 10,\n", " replay_size: int = 1000,\n", " warm_start_size: int = 1000,\n", " eps_last_frame: int = 1000,\n", " eps_start: float = 1.0,\n", " eps_end: float = 0.01,\n", " episode_length: int = 200,\n", " warm_start_steps: int = 1000,\n", " ) -> None:\n", " \"\"\"\n", " Args:\n", " batch_size: size of the batches\")\n", " lr: learning rate\n", " env: gym environment tag\n", " gamma: discount factor\n", " sync_rate: how many frames do we update the target network\n", " replay_size: capacity of the replay buffer\n", " warm_start_size: how many samples do we use to fill our buffer at the start of training\n", " eps_last_frame: what frame should epsilon stop decaying\n", " eps_start: starting value of epsilon\n", " eps_end: final value of epsilon\n", " episode_length: max length of an episode\n", " warm_start_steps: max episode reward in the environment\n", " \"\"\"\n", " super().__init__()\n", " self.save_hyperparameters()\n", "\n", " self.env = gym.make(self.hparams.env)\n", " obs_size = self.env.observation_space.shape[0]\n", " n_actions = self.env.action_space.n\n", "\n", " self.net = DQN(obs_size, n_actions)\n", " self.target_net = DQN(obs_size, n_actions)\n", "\n", " self.buffer = ReplayBuffer(self.hparams.replay_size)\n", " self.agent = Agent(self.env, self.buffer)\n", " self.total_reward = 0\n", " self.episode_reward = 0\n", " self.populate(self.hparams.warm_start_steps)\n", "\n", " def populate(self, steps: int = 1000) -> None:\n", " \"\"\"Carries out several random steps through the environment to initially fill up the replay buffer with\n", " experiences.\n", "\n", " Args:\n", " steps: number of random steps to populate the buffer with\n", " \"\"\"\n", " for i in range(steps):\n", " self.agent.play_step(self.net, epsilon=1.0)\n", "\n", " def forward(self, x: Tensor) -> Tensor:\n", " \"\"\"Passes in a state x through the network and gets the q_values of each action as an output.\n", "\n", " Args:\n", " x: environment state\n", "\n", " Returns:\n", " q values\n", " \"\"\"\n", " output = self.net(x)\n", " return output\n", "\n", " def dqn_mse_loss(self, batch: Tuple[Tensor, Tensor]) -> Tensor:\n", " \"\"\"Calculates the mse loss using a mini batch from the replay buffer.\n", "\n", " Args:\n", " batch: current mini batch of replay data\n", "\n", " Returns:\n", " loss\n", " \"\"\"\n", " states, actions, rewards, dones, next_states = batch\n", "\n", " state_action_values = self.net(states).gather(1, actions.unsqueeze(-1)).squeeze(-1)\n", "\n", " with torch.no_grad():\n", " next_state_values = self.target_net(next_states).max(1)[0]\n", " next_state_values[dones] = 0.0\n", " next_state_values = next_state_values.detach()\n", "\n", " expected_state_action_values = next_state_values * self.hparams.gamma + rewards\n", "\n", " return nn.MSELoss()(state_action_values, expected_state_action_values)\n", "\n", " def training_step(self, batch: Tuple[Tensor, Tensor], nb_batch) -> OrderedDict:\n", " \"\"\"Carries out a single step through the environment to update the replay buffer. Then calculates loss\n", " based on the minibatch recieved.\n", "\n", " Args:\n", " batch: current mini batch of replay data\n", " nb_batch: batch number\n", "\n", " Returns:\n", " Training loss and log metrics\n", " \"\"\"\n", " device = self.get_device(batch)\n", " epsilon = max(\n", " self.hparams.eps_end,\n", " self.hparams.eps_start - self.global_step + 1 / self.hparams.eps_last_frame,\n", " )\n", "\n", " # step through environment with agent\n", " reward, done = self.agent.play_step(self.net, epsilon, device)\n", " self.episode_reward += reward\n", "\n", " # calculates training loss\n", " loss = self.dqn_mse_loss(batch)\n", "\n", " if self.trainer._distrib_type in {DistributedType.DP, DistributedType.DDP2}:\n", " loss = loss.unsqueeze(0)\n", "\n", " if done:\n", " self.total_reward = self.episode_reward\n", " self.episode_reward = 0\n", "\n", " # Soft update of target network\n", " if self.global_step % self.hparams.sync_rate == 0:\n", " self.target_net.load_state_dict(self.net.state_dict())\n", "\n", " log = {\n", " \"total_reward\": torch.tensor(self.total_reward).to(device),\n", " \"reward\": torch.tensor(reward).to(device),\n", " \"train_loss\": loss,\n", " }\n", " status = {\n", " \"steps\": torch.tensor(self.global_step).to(device),\n", " \"total_reward\": torch.tensor(self.total_reward).to(device),\n", " }\n", "\n", " return OrderedDict({\"loss\": loss, \"log\": log, \"progress_bar\": status})\n", "\n", " def configure_optimizers(self) -> List[Optimizer]:\n", " \"\"\"Initialize Adam optimizer.\"\"\"\n", " optimizer = Adam(self.net.parameters(), lr=self.hparams.lr)\n", " return [optimizer]\n", "\n", " def __dataloader(self) -> DataLoader:\n", " \"\"\"Initialize the Replay Buffer dataset used for retrieving experiences.\"\"\"\n", " dataset = RLDataset(self.buffer, self.hparams.episode_length)\n", " dataloader = DataLoader(\n", " dataset=dataset,\n", " batch_size=self.hparams.batch_size,\n", " )\n", " return dataloader\n", "\n", " def train_dataloader(self) -> DataLoader:\n", " \"\"\"Get train loader.\"\"\"\n", " return self.__dataloader()\n", "\n", " def get_device(self, batch) -> str:\n", " \"\"\"Retrieve device currently being used by minibatch.\"\"\"\n", " return batch[0].device.index if self.on_gpu else \"cpu\""]}, {"cell_type": "markdown", "id": "6068b50b", "metadata": {"papermill": {"duration": 0.029024, "end_time": "2021-12-04T16:49:32.248526", "exception": false, "start_time": "2021-12-04T16:49:32.219502", "status": "completed"}, "tags": []}, "source": ["### Trainer"]}, {"cell_type": "code", "execution_count": 9, "id": "223c17c5", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:32.308551Z", "iopub.status.busy": "2021-12-04T16:49:32.308085Z", "iopub.status.idle": "2021-12-04T16:49:48.709258Z", "shell.execute_reply": "2021-12-04T16:49:48.709649Z"}, "papermill": {"duration": 16.433171, "end_time": "2021-12-04T16:49:48.709816", "exception": false, "start_time": "2021-12-04T16:49:32.276645", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["GPU available: True, used: True\n"]}, {"name": "stderr", "output_type": "stream", "text": ["TPU available: False, using: 0 TPU cores\n"]}, {"name": "stderr", "output_type": "stream", "text": ["IPU available: False, using: 0 IPUs\n"]}, {"name": "stderr", "output_type": "stream", "text": ["LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0,1]\n"]}, {"name": "stderr", "output_type": "stream", "text": ["\n", " | Name | Type | Params\n", "------------------------------------\n", "0 | net | DQN | 898 \n", "1 | target_net | DQN | 898 \n", "------------------------------------\n", "1.8 K Trainable params\n", "0 Non-trainable params\n", "1.8 K Total params\n", "0.007 Total estimated model params size (MB)\n"]}, {"name": "stderr", "output_type": "stream", "text": ["/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/trainer/data_loading.py:111: UserWarning: The dataloader, train_dataloader, does not have many workers which may be a bottleneck. Consider increasing the value of the `num_workers` argument` (try 12 which is the number of cpus on this machine) in the `DataLoader` init to improve performance.\n", " rank_zero_warn(\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "7809a9f0b92743a3bdfcc9d4e6096fec", "version_major": 2, "version_minor": 0}, "text/plain": ["Training: 0it [00:00, ?it/s]"]}, "metadata": {}, "output_type": "display_data"}, {"name": "stderr", "output_type": "stream", "text": ["/tmp/ipykernel_6545/3638216480.py:30: DeprecationWarning: `np.bool` is a deprecated alias for the builtin `bool`. To silence this warning, use `bool` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.bool_` here.\n", "Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\n", " np.array(dones, dtype=np.bool),\n", "/home/AzDevOps_azpcontainer/.local/lib/python3.9/site-packages/pytorch_lightning/loops/optimization/closure.py:35: LightningDeprecationWarning: One of the returned values {'progress_bar', 'log'} has a `grad_fn`. We will detach it automatically but this behaviour will change in v1.6. Please detach it manually: `return {'loss': ..., 'something': something.detach()}`\n", " rank_zero_deprecation(\n"]}], "source": ["\n", "model = DQNLightning()\n", "\n", "trainer = Trainer(\n", " gpus=AVAIL_GPUS,\n", " max_epochs=200,\n", " val_check_interval=100,\n", ")\n", "\n", "trainer.fit(model)"]}, {"cell_type": "code", "execution_count": 10, "id": "b0dc7a72", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:49:48.784401Z", "iopub.status.busy": "2021-12-04T16:49:48.783938Z", "iopub.status.idle": "2021-12-04T16:49:50.361154Z", "shell.execute_reply": "2021-12-04T16:49:50.361542Z"}, "papermill": {"duration": 1.615711, "end_time": "2021-12-04T16:49:50.361705", "exception": false, "start_time": "2021-12-04T16:49:48.745994", "status": "completed"}, "tags": []}, "outputs": [{"data": {"text/html": ["\n", " \n", " \n", " "], "text/plain": [""]}, "metadata": {}, "output_type": "display_data"}], "source": ["# Start tensorboard.\n", "%load_ext tensorboard\n", "%tensorboard --logdir lightning_logs/"]}, {"cell_type": "markdown", "id": "46d9a7e6", "metadata": {"papermill": {"duration": 0.037856, "end_time": "2021-12-04T16:49:50.435994", "exception": false, "start_time": "2021-12-04T16:49:50.398138", "status": "completed"}, "tags": []}, "source": ["## Congratulations - Time to Join the Community!\n", "\n", "Congratulations on completing this notebook tutorial! If you enjoyed this and would like to join the Lightning\n", "movement, you can do so in the following ways!\n", "\n", "### Star [Lightning](https://github.com/PyTorchLightning/pytorch-lightning) on GitHub\n", "The easiest way to help our community is just by starring the GitHub repos! This helps raise awareness of the cool\n", "tools we're building.\n", "\n", "### Join our [Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)!\n", "The best way to keep up to date on the latest advancements is to join our community! Make sure to introduce yourself\n", "and share your interests in `#general` channel\n", "\n", "\n", "### Contributions !\n", "The best way to contribute to our community is to become a code contributor! At any time you can go to\n", "[Lightning](https://github.com/PyTorchLightning/pytorch-lightning) or [Bolt](https://github.com/PyTorchLightning/lightning-bolts)\n", "GitHub Issues page and filter for \"good first issue\".\n", "\n", "* [Lightning good first issue](https://github.com/PyTorchLightning/pytorch-lightning/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* [Bolt good first issue](https://github.com/PyTorchLightning/lightning-bolts/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n", "* You can also contribute your own notebooks with useful examples !\n", "\n", "### Great thanks from the entire Pytorch Lightning Team for your interest !\n", "\n", "[![Pytorch Lightning](data:image/png;base64,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){height=\"60px\" width=\"240px\"}](https://pytorchlightning.ai)"]}, {"cell_type": "raw", "metadata": {"raw_mimetype": "text/restructuredtext"}, "source": [".. customcarditem::\n", " :header: How to train a Deep Q Network\n", " :card_description: Main takeaways: 1. RL has the same flow as previous models we have seen, with a few additions 2. Handle unsupervised learning by using an IterableDataset where the dataset...\n", " :tags: RL,GPU/TPU,Lightning-Examples"]}], "metadata": {"jupytext": {"cell_metadata_filter": "id,colab_type,colab,-all", "formats": "ipynb,py:percent", "main_language": "python"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7"}, "papermill": {"default_parameters": {}, "duration": 29.486464, "end_time": "2021-12-04T16:49:52.782807", "environment_variables": {}, "exception": null, "input_path": "lightning_examples/reinforce-learning-DQN/dqn.ipynb", "output_path": ".notebooks/lightning_examples/reinforce-learning-DQN.ipynb", "parameters": {}, "start_time": "2021-12-04T16:49:23.296343", "version": "2.3.3"}, "widgets": {"application/vnd.jupyter.widget-state+json": {"state": {"0885ff08e5e847eebdcfdc546cf79123": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "142276d4fabb4037a6c15952c792292b": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": "2", "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "1f8780cfa7d5451fbfa8deb76ebec06a": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_c47ec2cbd7bc4b288dae843a48dca3c7", "placeholder": "\u200b", "style": "IPY_MODEL_0885ff08e5e847eebdcfdc546cf79123", "value": "Epoch 199: "}}, "36eace64700644bb8e54ccc45212596c": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "DescriptionStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "description_width": ""}}, "5bdaf8d89f2f4abd9c821c3f7eac63d7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": "inline-flex", "flex": null, "flex_flow": "row wrap", "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": "100%"}}, "7809a9f0b92743a3bdfcc9d4e6096fec": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HBoxView", "box_style": "", "children": ["IPY_MODEL_1f8780cfa7d5451fbfa8deb76ebec06a", "IPY_MODEL_94af77267feb429a90ec99375045262e", "IPY_MODEL_d1478e69106c4687a0b853cbc557271e"], "layout": "IPY_MODEL_5bdaf8d89f2f4abd9c821c3f7eac63d7"}}, "94af77267feb429a90ec99375045262e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatProgressModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "FloatProgressModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "ProgressView", "bar_style": "success", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_142276d4fabb4037a6c15952c792292b", "max": 1.0, "min": 0.0, "orientation": "horizontal", "style": "IPY_MODEL_e0376cde46734387ac89cb86a858bf70", "value": 1.0}}, "9c315c37e7f947e7ba7d32663c2399c2": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "c47ec2cbd7bc4b288dae843a48dca3c7": {"model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {"_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "LayoutView", "align_content": null, "align_items": null, "align_self": null, "border": null, "bottom": null, "display": null, "flex": null, "flex_flow": null, "grid_area": null, "grid_auto_columns": null, "grid_auto_flow": null, "grid_auto_rows": null, "grid_column": null, "grid_gap": null, "grid_row": null, "grid_template_areas": null, "grid_template_columns": null, "grid_template_rows": null, "height": null, "justify_content": null, "justify_items": null, "left": null, "margin": null, "max_height": null, "max_width": null, "min_height": null, "min_width": null, "object_fit": null, "object_position": null, "order": null, "overflow": null, "overflow_x": null, "overflow_y": null, "padding": null, "right": null, "top": null, "visibility": null, "width": null}}, "d1478e69106c4687a0b853cbc557271e": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HTMLModel", "state": {"_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "HTMLModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "1.5.0", "_view_name": "HTMLView", "description": "", "description_tooltip": null, "layout": "IPY_MODEL_9c315c37e7f947e7ba7d32663c2399c2", "placeholder": "\u200b", "style": "IPY_MODEL_36eace64700644bb8e54ccc45212596c", "value": " 13/? [00:00<00:00, 205.93it/s, loss=45.6, v_num=8]"}}, "e0376cde46734387ac89cb86a858bf70": {"model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "ProgressStyleModel", "state": {"_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", "_model_name": "ProgressStyleModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", "_view_name": "StyleView", "bar_color": null, "description_width": ""}}}, "version_major": 2, "version_minor": 0}}}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/source/notebooks/lightning_examples/text-transformers.ipynb b/source/notebooks/lightning_examples/text-transformers.ipynb deleted file mode 100644 index 6dc58ca..0000000 --- a/source/notebooks/lightning_examples/text-transformers.ipynb +++ /dev/null @@ -1 +0,0 @@ -{"cells": [{"cell_type": "markdown", "id": "3c99ec5d", "metadata": {"papermill": {"duration": 0.033967, "end_time": "2021-12-04T16:50:09.385288", "exception": false, "start_time": "2021-12-04T16:50:09.351321", "status": "completed"}, "tags": []}, "source": ["\n", "# Finetune Transformers Models with PyTorch Lightning\n", "\n", "* **Author:** PL team\n", "* **License:** CC BY-SA\n", "* **Generated:** 2021-12-04T16:53:11.286202\n", "\n", "This notebook will use HuggingFace's `datasets` library to get data, which will be wrapped in a `LightningDataModule`.\n", "Then, we write a class to perform text classification on any dataset from the [GLUE Benchmark](https://gluebenchmark.com/).\n", "(We just show CoLA and MRPC due to constraint on compute/disk)\n", "\n", "\n", "---\n", "Open in [![Open In Colab](data:image/png;base64,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){height=\"20px\" width=\"117px\"}](https://colab.research.google.com/github/PytorchLightning/lightning-tutorials/blob/publication/.notebooks/lightning_examples/text-transformers.ipynb)\n", "\n", "Give us a \u2b50 [on Github](https://www.github.com/PytorchLightning/pytorch-lightning/)\n", "| Check out [the documentation](https://pytorch-lightning.readthedocs.io/en/latest/)\n", "| Join us [on Slack](https://join.slack.com/t/pytorch-lightning/shared_invite/zt-pw5v393p-qRaDgEk24~EjiZNBpSQFgQ)"]}, {"cell_type": "markdown", "id": "0e4d7209", "metadata": {"papermill": {"duration": 0.028692, "end_time": "2021-12-04T16:50:09.445776", "exception": false, "start_time": "2021-12-04T16:50:09.417084", "status": "completed"}, "tags": []}, "source": ["## Setup\n", "This notebook requires some packages besides pytorch-lightning."]}, {"cell_type": "code", "execution_count": 1, "id": "49b84d9c", "metadata": {"colab": {}, "colab_type": "code", "execution": {"iopub.execute_input": "2021-12-04T16:50:09.510470Z", "iopub.status.busy": "2021-12-04T16:50:09.505774Z", "iopub.status.idle": "2021-12-04T16:50:12.925383Z", "shell.execute_reply": "2021-12-04T16:50:12.924802Z"}, "id": "LfrJLKPFyhsK", "lines_to_next_cell": 0, "papermill": {"duration": 3.45077, "end_time": "2021-12-04T16:50:12.925533", "exception": false, "start_time": "2021-12-04T16:50:09.474763", "status": "completed"}, "tags": []}, "outputs": [], "source": ["! pip install --quiet \"datasets\" \"pytorch-lightning>=1.3\" \"scipy\" \"transformers\" \"torchmetrics>=0.3\" \"scikit-learn\" \"torchtext>=0.9\" \"torch>=1.6, <1.9\""]}, {"cell_type": "code", "execution_count": 2, "id": "8c24af2f", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:50:12.990546Z", "iopub.status.busy": "2021-12-04T16:50:12.990070Z", "iopub.status.idle": "2021-12-04T16:50:17.320862Z", "shell.execute_reply": "2021-12-04T16:50:17.320412Z"}, "papermill": {"duration": 4.364813, "end_time": "2021-12-04T16:50:17.320999", "exception": false, "start_time": "2021-12-04T16:50:12.956186", "status": "completed"}, "tags": []}, "outputs": [], "source": ["from datetime import datetime\n", "from typing import Optional\n", "\n", "import datasets\n", "import torch\n", "from pytorch_lightning import LightningDataModule, LightningModule, Trainer, seed_everything\n", "from torch.utils.data import DataLoader\n", "from transformers import (\n", " AdamW,\n", " AutoConfig,\n", " AutoModelForSequenceClassification,\n", " AutoTokenizer,\n", " get_linear_schedule_with_warmup,\n", ")\n", "\n", "AVAIL_GPUS = min(1, torch.cuda.device_count())"]}, {"cell_type": "markdown", "id": "2134c12c", "metadata": {"papermill": {"duration": 0.029134, "end_time": "2021-12-04T16:50:17.381739", "exception": false, "start_time": "2021-12-04T16:50:17.352605", "status": "completed"}, "tags": []}, "source": ["## Training BERT with Lightning"]}, {"cell_type": "markdown", "id": "c827ca58", "metadata": {"lines_to_next_cell": 2, "papermill": {"duration": 0.029129, "end_time": "2021-12-04T16:50:17.441085", "exception": false, "start_time": "2021-12-04T16:50:17.411956", "status": "completed"}, "tags": []}, "source": ["### Lightning DataModule for GLUE"]}, {"cell_type": "code", "execution_count": 3, "id": "5a856234", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:50:17.515779Z", "iopub.status.busy": "2021-12-04T16:50:17.512843Z", "iopub.status.idle": "2021-12-04T16:50:17.517417Z", "shell.execute_reply": "2021-12-04T16:50:17.517796Z"}, "papermill": {"duration": 0.047785, "end_time": "2021-12-04T16:50:17.517928", "exception": false, "start_time": "2021-12-04T16:50:17.470143", "status": "completed"}, "tags": []}, "outputs": [], "source": ["class GLUEDataModule(LightningDataModule):\n", "\n", " task_text_field_map = {\n", " \"cola\": [\"sentence\"],\n", " \"sst2\": [\"sentence\"],\n", " \"mrpc\": [\"sentence1\", \"sentence2\"],\n", " \"qqp\": [\"question1\", \"question2\"],\n", " \"stsb\": [\"sentence1\", \"sentence2\"],\n", " \"mnli\": [\"premise\", \"hypothesis\"],\n", " \"qnli\": [\"question\", \"sentence\"],\n", " \"rte\": [\"sentence1\", \"sentence2\"],\n", " \"wnli\": [\"sentence1\", \"sentence2\"],\n", " \"ax\": [\"premise\", \"hypothesis\"],\n", " }\n", "\n", " glue_task_num_labels = {\n", " \"cola\": 2,\n", " \"sst2\": 2,\n", " \"mrpc\": 2,\n", " \"qqp\": 2,\n", " \"stsb\": 1,\n", " \"mnli\": 3,\n", " \"qnli\": 2,\n", " \"rte\": 2,\n", " \"wnli\": 2,\n", " \"ax\": 3,\n", " }\n", "\n", " loader_columns = [\n", " \"datasets_idx\",\n", " \"input_ids\",\n", " \"token_type_ids\",\n", " \"attention_mask\",\n", " \"start_positions\",\n", " \"end_positions\",\n", " \"labels\",\n", " ]\n", "\n", " def __init__(\n", " self,\n", " model_name_or_path: str,\n", " task_name: str = \"mrpc\",\n", " max_seq_length: int = 128,\n", " train_batch_size: int = 32,\n", " eval_batch_size: int = 32,\n", " **kwargs,\n", " ):\n", " super().__init__()\n", " self.model_name_or_path = model_name_or_path\n", " self.task_name = task_name\n", " self.max_seq_length = max_seq_length\n", " self.train_batch_size = train_batch_size\n", " self.eval_batch_size = eval_batch_size\n", "\n", " self.text_fields = self.task_text_field_map[task_name]\n", " self.num_labels = self.glue_task_num_labels[task_name]\n", " self.tokenizer = AutoTokenizer.from_pretrained(self.model_name_or_path, use_fast=True)\n", "\n", " def setup(self, stage: str):\n", " self.dataset = datasets.load_dataset(\"glue\", self.task_name)\n", "\n", " for split in self.dataset.keys():\n", " self.dataset[split] = self.dataset[split].map(\n", " self.convert_to_features,\n", " batched=True,\n", " remove_columns=[\"label\"],\n", " )\n", " self.columns = [c for c in self.dataset[split].column_names if c in self.loader_columns]\n", " self.dataset[split].set_format(type=\"torch\", columns=self.columns)\n", "\n", " self.eval_splits = [x for x in self.dataset.keys() if \"validation\" in x]\n", "\n", " def prepare_data(self):\n", " datasets.load_dataset(\"glue\", self.task_name)\n", " AutoTokenizer.from_pretrained(self.model_name_or_path, use_fast=True)\n", "\n", " def train_dataloader(self):\n", " return DataLoader(self.dataset[\"train\"], batch_size=self.train_batch_size)\n", "\n", " def val_dataloader(self):\n", " if len(self.eval_splits) == 1:\n", " return DataLoader(self.dataset[\"validation\"], batch_size=self.eval_batch_size)\n", " elif len(self.eval_splits) > 1:\n", " return [DataLoader(self.dataset[x], batch_size=self.eval_batch_size) for x in self.eval_splits]\n", "\n", " def test_dataloader(self):\n", " if len(self.eval_splits) == 1:\n", " return DataLoader(self.dataset[\"test\"], batch_size=self.eval_batch_size)\n", " elif len(self.eval_splits) > 1:\n", " return [DataLoader(self.dataset[x], batch_size=self.eval_batch_size) for x in self.eval_splits]\n", "\n", " def convert_to_features(self, example_batch, indices=None):\n", "\n", " # Either encode single sentence or sentence pairs\n", " if len(self.text_fields) > 1:\n", " texts_or_text_pairs = list(zip(example_batch[self.text_fields[0]], example_batch[self.text_fields[1]]))\n", " else:\n", " texts_or_text_pairs = example_batch[self.text_fields[0]]\n", "\n", " # Tokenize the text/text pairs\n", " features = self.tokenizer.batch_encode_plus(\n", " texts_or_text_pairs, max_length=self.max_seq_length, pad_to_max_length=True, truncation=True\n", " )\n", "\n", " # Rename label to labels to make it easier to pass to model forward\n", " features[\"labels\"] = example_batch[\"label\"]\n", "\n", " return features"]}, {"cell_type": "markdown", "id": "532d63d6", "metadata": {"papermill": {"duration": 0.029073, "end_time": "2021-12-04T16:50:17.576485", "exception": false, "start_time": "2021-12-04T16:50:17.547412", "status": "completed"}, "tags": []}, "source": ["**You could use this datamodule with standalone PyTorch if you wanted...**"]}, {"cell_type": "code", "execution_count": 4, "id": "f3f27b26", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:50:17.637888Z", "iopub.status.busy": "2021-12-04T16:50:17.637424Z", "iopub.status.idle": "2021-12-04T16:50:24.396830Z", "shell.execute_reply": "2021-12-04T16:50:24.396334Z"}, "papermill": {"duration": 6.791378, "end_time": "2021-12-04T16:50:24.396956", "exception": false, "start_time": "2021-12-04T16:50:17.605578", "status": "completed"}, "tags": []}, "outputs": [{"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "232400085c33454e97f802179183d1ed", "version_major": 2, "version_minor": 0}, "text/plain": ["Downloading: 0%| | 0.00/28.0 [00:00= 1:\n", " preds = torch.argmax(logits, axis=1)\n", " elif self.hparams.num_labels == 1:\n", " preds = logits.squeeze()\n", "\n", " labels = batch[\"labels\"]\n", "\n", " return {\"loss\": val_loss, \"preds\": preds, \"labels\": labels}\n", "\n", " def validation_epoch_end(self, outputs):\n", " if self.hparams.task_name == \"mnli\":\n", " for i, output in enumerate(outputs):\n", " # matched or mismatched\n", " split = self.hparams.eval_splits[i].split(\"_\")[-1]\n", " preds = torch.cat([x[\"preds\"] for x in output]).detach().cpu().numpy()\n", " labels = torch.cat([x[\"labels\"] for x in output]).detach().cpu().numpy()\n", " loss = torch.stack([x[\"loss\"] for x in output]).mean()\n", " self.log(f\"val_loss_{split}\", loss, prog_bar=True)\n", " split_metrics = {\n", " f\"{k}_{split}\": v for k, v in self.metric.compute(predictions=preds, references=labels).items()\n", " }\n", " self.log_dict(split_metrics, prog_bar=True)\n", " return loss\n", "\n", " preds = torch.cat([x[\"preds\"] for x in outputs]).detach().cpu().numpy()\n", " labels = torch.cat([x[\"labels\"] for x in outputs]).detach().cpu().numpy()\n", " loss = torch.stack([x[\"loss\"] for x in outputs]).mean()\n", " self.log(\"val_loss\", loss, prog_bar=True)\n", " self.log_dict(self.metric.compute(predictions=preds, references=labels), prog_bar=True)\n", " return loss\n", "\n", " def setup(self, stage=None) -> None:\n", " if stage != \"fit\":\n", " return\n", " # Get dataloader by calling it - train_dataloader() is called after setup() by default\n", " train_loader = self.trainer.datamodule.train_dataloader()\n", "\n", " # Calculate total steps\n", " tb_size = self.hparams.train_batch_size * max(1, self.trainer.gpus)\n", " ab_size = self.trainer.accumulate_grad_batches * float(self.trainer.max_epochs)\n", " self.total_steps = (len(train_loader.dataset) // tb_size) // ab_size\n", "\n", " def configure_optimizers(self):\n", " \"\"\"Prepare optimizer and schedule (linear warmup and decay)\"\"\"\n", " model = self.model\n", " no_decay = [\"bias\", \"LayerNorm.weight\"]\n", " optimizer_grouped_parameters = [\n", " {\n", " \"params\": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)],\n", " \"weight_decay\": self.hparams.weight_decay,\n", " },\n", " {\n", " \"params\": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)],\n", " \"weight_decay\": 0.0,\n", " },\n", " ]\n", " optimizer = AdamW(optimizer_grouped_parameters, lr=self.hparams.learning_rate, eps=self.hparams.adam_epsilon)\n", "\n", " scheduler = get_linear_schedule_with_warmup(\n", " optimizer,\n", " num_warmup_steps=self.hparams.warmup_steps,\n", " num_training_steps=self.total_steps,\n", " )\n", " scheduler = {\"scheduler\": scheduler, \"interval\": \"step\", \"frequency\": 1}\n", " return [optimizer], [scheduler]"]}, {"cell_type": "markdown", "id": "be59b890", "metadata": {"papermill": {"duration": 0.055801, "end_time": "2021-12-04T16:50:24.863114", "exception": false, "start_time": "2021-12-04T16:50:24.807313", "status": "completed"}, "tags": []}, "source": ["## Training"]}, {"cell_type": "markdown", "id": "c1a5d61a", "metadata": {"papermill": {"duration": 0.054576, "end_time": "2021-12-04T16:50:24.972560", "exception": false, "start_time": "2021-12-04T16:50:24.917984", "status": "completed"}, "tags": []}, "source": ["### CoLA\n", "\n", "See an interactive view of the\n", "CoLA dataset in [NLP Viewer](https://huggingface.co/nlp/viewer/?dataset=glue&config=cola)"]}, {"cell_type": "code", "execution_count": 6, "id": "774115a0", "metadata": {"execution": {"iopub.execute_input": "2021-12-04T16:50:25.086490Z", "iopub.status.busy": "2021-12-04T16:50:25.086020Z", "iopub.status.idle": "2021-12-04T16:51:46.067948Z", "shell.execute_reply": "2021-12-04T16:51:46.068322Z"}, "papermill": {"duration": 81.04138, "end_time": "2021-12-04T16:51:46.068501", "exception": false, "start_time": "2021-12-04T16:50:25.027121", "status": "completed"}, "tags": []}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Global seed set to 42\n"]}, {"data": {"application/vnd.jupyter.widget-view+json": {"model_id": "0071178f4db54b2a9c7a0c792adbfde9", "version_major": 2, "version_minor": 0}, "text/plain": ["Downloading: 0%| | 0.00/684 [00:00` _ 에서 PyTorch를 설치한 뒤, -아래 명령어로 `pip `_ 를 사용하여 설치할 수 있습니다: - -.. code-block:: bash - - pip install pytorch-lightning - --------------- - -*********************** -Conda를 사용하여 설치 -*********************** - -만약 conda를 아직 설치하지 않았다면, `Conda 설치 가이드 `_ 를 참고하세요. -Lightning은 아래 명령어로 `conda `_ 를 사용하여 설치할 수 있습니다: - -.. code-block:: bash - - conda install pytorch-lightning -c conda-forge - -`Conda 가상환경(Environments) `_ 을 사용할 수도 있습니다: - -.. code-block:: bash - - conda activate my_env - conda install pytorch-lightning -c conda-forge - --------------- - -************************ -소스 코드로 설치 -************************ - -소스 코드로 최신 버전(nightly)을 설치합니다. 아직 배포되지 않은 버그 수정(bug fix)과 새롭게 출시할 기능들이 -포함되어 있습니다. 미검증·불안정 최신 기능(bleeding edge)이므로, 신중하게 사용하세요. - -.. code-block:: bash - - pip install https://github.com/PyTorchLightning/pytorch-lightning/archive/master.zip - -향후 공개될 개선 버전(patch release)를 소스 코드로부터 설치합니다. 개선 버전은 가장 최근의 주요 버전(major release)에 대한 버그 수정만 -포함되어 있습니다. - -.. code-block:: bash - - pip install https://github.com/PyTorchLightning/pytorch-lightning/archive/refs/heads/release/1.5.x.zip - --------------- - -************************************ -Lightning 커버리지(Coverage) -************************************ - -파이토치 라이트닝(PyTorch Lightning)은 다양한 Python과 PyTorch 버전에서 유지 보수 및 테스트되고 있습니다. - -더 자세한 정보는 `CI Coverage `_ 를 참고하세요. - -다양한 GPU와 TPU, CPU, IPU에서 엄격하게 테스트되었습니다. GPU 테스트는 2개의 NVIDIA P100에서 실행됩니다. TPU 테스트는 Google GKE TPUv2/3에서 -실행됩니다. TPU py3.7은 Colab 및 Kaggle 환경을 지원함을 뜻합니다. IPU 테스트는 MK1 IPU 장비에서 실행됩니다. diff --git a/source/starter/introduction.rst b/source/starter/introduction.rst deleted file mode 100644 index a5e92d2..0000000 --- a/source/starter/introduction.rst +++ /dev/null @@ -1,406 +0,0 @@ -:orphan: - -############################### -Lightning 15분 만에 배워보기 -############################### - -**필요한 배경지식:** 없음 - -**목표:** 이 문서에서는 일반적인 Lightning 워크플로우의 주요한 7단계를 안내합니다. - -PyTorch Lightning(파이토치 라이트닝)은 대규모로 엄청 빠른 성능을 요구하면서 최대한의 유연성을 필요로 하는 -전문적인 AI 연구자들과 머신러닝 엔지니어들을 위한 "배터리가 포함된(batteries included)" 딥러닝 프레임워크입니다. - -.. join_slack:: - :align: left - :margin: 20 - -Lightning(라이트닝)은 반복적으로 사용하는 코드(boilerplate)를 제거하고 확장성(scalability)을 확보하도록 PyTorch 코드를 재구성합니다. - -.. raw:: html - - - -| - -PyTorch 코드를 재구성함으로써, Lightning에서는 이런 것들이 가능해집니다: - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 완전한 유연성 - :description: 반복되는 코드 없이 PyTorch를 그대로 사용하여 아이디어를 구현합니다. - :col_css: col-md-3 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/card_full_control.png - :height: 290 - -.. displayitem:: - :header: 재현성 + 가독성 - :description: 연구용 코드와 엔지니어링 코드를 분리하여 재현성을 갖추고 더 나은 가독성을 제공합니다. - :col_css: col-md-3 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/card_no_boilerplate.png - :height: 290 - -.. displayitem:: - :header: 간단한 다중 GPU 학습 - :description: 코드 변경 없이 여러개의 GPU/TPU/HPU 등을 사용합니다. - :col_css: col-md-3 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/card_hardware.png - :height: 290 - -.. displayitem:: - :header: 테스트 완료 - :description: 이미 모든 테스트를 완료하여 직접 테스트 할 필요없습니다. - :col_css: col-md-3 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/card_testing.png - :height: 290 - -.. raw:: html - -
-
- -.. End of callout item section - ----- - -****************************** -1: PyTorch Lightning 설치하기 -****************************** -.. raw:: html - -
-
- -`pip `_ 사용자라면, - -.. code-block:: bash - - pip install pytorch-lightning - -.. raw:: html - -
-
- -`conda `_ 사용자라면, - -.. code-block:: bash - - conda install pytorch-lightning -c conda-forge - -.. raw:: html - -
-
- -또는 `advanced install guide `_ 를 참조하세요. - ----- - -.. _new_project: - -***************************** -2: LightningModule 정의하기 -***************************** - -LightningModule을 사용하여 PyTorch nn.Module이 training_step (뿐만 아니라 validation_step이나 test_step) 내에서 복잡한 방식으로 함께 동작할 수 있도록 합니다. - -.. testcode:: - - import os - from torch import optim, nn, utils, Tensor - from tests.helpers.datasets import MNIST - import pytorch_lightning as pl - - # 원하는만큼의 nn.Module (또는 기존 모델)을 정의합니다. - encoder = nn.Sequential(nn.Linear(28 * 28, 64), nn.ReLU(), nn.Linear(64, 3)) - decoder = nn.Sequential(nn.Linear(3, 64), nn.ReLU(), nn.Linear(64, 28 * 28)) - - # LightningModule을 정의합니다. - class LitAutoEncoder(pl.LightningModule): - def __init__(self, encoder, decoder): - super().__init__() - self.encoder = encoder - self.decoder = decoder - - def training_step(self, batch, batch_idx): - # training_step defines the train loop. - # it is independent of forward - x, y = batch - x = x.view(x.size(0), -1) - z = self.encoder(x) - x_hat = self.decoder(z) - loss = nn.functional.mse_loss(x_hat, x) - # Logging to TensorBoard by default - self.log("train_loss", loss) - return loss - - def configure_optimizers(self): - optimizer = optim.Adam(self.parameters(), lr=1e-3) - return optimizer - - - # 오토인코더(autoencoder)를 초기화합니다. - autoencoder = LitAutoEncoder(encoder, decoder) - ----- - -********************** -3: 데이터셋 정의하기 -********************** - -Lightning은 *어떠한* 순회 가능한 객체(iterable; :class:`~torch.utils.data.DataLoader`, numpy 등...)도 학습/검증/테스트/예측용으로 나누어 사용할 수 있습니다. - -.. code-block:: python - - # 데이터를 설정합니다. - dataset = MNIST(os.getcwd(), download=True) - train_loader = utils.data.DataLoader(dataset) - ----- - -****************** -4: 모델 학습하기 -****************** - -Lightning :doc:`Trainer <../common/trainer>` 는 모든 :doc:`LightningModule <../common/lightning_module>` 과 데이터셋을 "함께(mix)" 학습할 수 있으며, -확장에 필요한 모든 엔지니어링적 복잡성들을 추상화(abstract)합니다. - -.. code-block:: python - - # 모델을 학습합니다 (힌트: 빠른 아이디어 반복에 도움이 되는 Trainer의 인자들을 참고하세요) - trainer = pl.Trainer(limit_train_batches=100, max_epochs=1) - trainer.fit(model=autoencoder, train_dataloaders=train_loader) - -Lightning :doc:`Trainer <../common/trainer>` 는 아래 예시들을 포함하여 `40종류 이상의 기법들 <../common/trainer.html#trainer-flags>`_ 을 자동화합니다: - -* 에폭(epoch) 및 배치(batch) 반복 -* ``optimizer.step()``, ``loss.backward()``, ``optimizer.zero_grad()`` 호출 -* 평가(evaluation) 도중 경사도(grads) 활성화/비활성화를 위한 ``model.eval()`` 호출 -* :doc:`체크포인트(checkpoint) 저장하기 및 불러오기 <../common/checkpointing>` -* 텐서보드(tensorboard) (:doc:`loggers <../visualize/loggers>` 옵션 참조) -* :doc:`Multi-GPU <../accelerators/gpu>` 지원 -* :doc:`TPU <../accelerators/tpu>` -* :ref:`16비트 정밀도(precision) AMP ` 지원 - ----- - - -****************** -5: 모델 사용하기 -****************** - -모델을 학습한 뒤에는 ONNX, TorchScript로 내보내기(export)하여 상용 환경에 포함하거나 단순히 가중치를 불러오고 예측을 실행할 수 있습니다. - -.. code:: python - - # 체크포인트(checkpoint)를 불러옵니다. - checkpoint = "./lightning_logs/version_0/checkpoints/epoch=0-step=100.ckpt" - autoencoder = LitAutoEncoder.load_from_checkpoint(checkpoint, encoder=encoder, decoder=decoder) - - # 학습한 nn.Module을 선택합니다. - encoder = autoencoder.encoder - encoder.eval() - - # 4개의 가짜 이미지로 예측(embed)합니다! - fake_image_batch = Tensor(4, 28 * 28) - embeddings = encoder(fake_image_batch) - print("⚡" * 20, "\nPredictions (4 image embeddings):\n", embeddings, "\n", "⚡" * 20) - ----- - -********************* -6: 학습 시각화하기 -********************* - -Lightning에는 *많은* 배터리가 포함되어 있습니다. 실험을 시각화하는데 사용하는 텐서보드(Tensorboard)도 유용한 도구 중 하나입니다. - -명령줄(commandline)에서 아래를 실행하고 브라우저에서 **http://localhost:6006/** 을 열어보세요. - -.. code:: bash - - tensorboard --logdir . - ----- - -************************* -7: 엄청 빠르게 학습하기 -************************* - -Trainer에 인자(argument)를 사용하여 고급 학습 기능을 사용할 수 있습니다. 이는 다른 코드를 변경하지 않으면서 학습 단계(train loop)에 자동으로 통합할 수 있도록 하는 최신(state-of-the-art)의 기술입니다. - -.. code:: - - # 4개의 GPU에서 학습 - trainer = Trainer( - devices=4, - accelerator="gpu", - ) - - # Deepspeed/FSDP를 사용하여 1TB 이상의 매개변수를 갖는 모델 학습 - trainer = Trainer( - devices=4, - accelerator="gpu", - strategy="deepspeed_stage_2", - precision=16 - ) - - # 빠른 아이디어 반복을 위한 20개 이상의 유용한 플래그(flag) - trainer = Trainer( - max_epochs=10, - min_epochs=5, - overfit_batches=1 - ) - - # 최신 기술을 사용 - trainer = Trainer(callbacks=[StochasticWeightAveraging(...)]) - ----- - -******************** -유연성 극대화하기 -******************** - -Lightning의 핵심 원칙은 **PyTorch의 어떠한 부분도 숨기지 않으면서** 언제나 최대한의 유연성을 제공하는 것입니다. - -Lightning은 프로젝트의 복잡도에 따라 *추가적인* 5단계의 유연성을 제공합니다. - ----- - -학습 단계(loop) 사용자 정의하기 -================================== - -.. image:: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/custom_loop.png - :width: 600 - :alt: Injecting custom code in a training loop - -LightningModule에서 사용할 수 있는 20개 이상의 메소드 (:ref:`lightning_hooks`) 중 일부를 사용하여 훈련 단계 어디에든 사용자 정의 코드를 삽입할 수 있습니다. - -.. testcode:: - - class LitAutoEncoder(pl.LightningModule): - def backward(self, loss, optimizer, optimizer_idx): - loss.backward() - ----- - -Trainer 확장하기 -================== - -.. raw:: html - - - -유사한 기능을 하는 여러줄의 코드가 있는 경우, 콜백(callback)을 사용하여 손쉽게 그룹으로 묶어서 해당하는 코드들을 동시에 켜거나 끌 수 있습니다. - -.. code:: - - trainer = Trainer(callbacks=[AWSCheckpoints()]) - ----- - -PyTorch 자체의 반복(loop) 사용하기 -=================================== - -최첨단 연구 시 특정 유형의 작업들을 위해, Lightning은 전문가들이 다양한 방식으로 학습 단계를 완전히 제어할 수 있는 기능을 제공합니다. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: 직접 최적화(manual optimization) - :description: 자동화된 학습 단계에서 최적화 단계는 사용자가 직접 관여합니다. - :col_css: col-md-4 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/manual_opt.png - :button_link: ../model/build_model_advanced.html#manual-optimization - :image_height: 220px - :height: 320 - -.. displayitem:: - :header: Lightning Lite(라이트닝 라이트) - :description: 복잡한 PyTorch 프로젝트를 이관하기 위한 반복 단계를 완벽히 제어합니다. - :col_css: col-md-4 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/lite.png - :button_link: ../model/build_model_expert.html - :image_height: 220px - :height: 320 - -.. displayitem:: - :header: 반복(Loop) - :description: 메타학습(meta-learning), 강화학습(reinforcement learning), GAN을 완벽히 제어합니다. - :col_css: col-md-4 - :image_center: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/loops.png - :button_link: ../extensions/loops.html - :image_height: 220px - :height: 320 - -.. raw:: html - -
-
- -.. End of callout item section - ----- - -********** -다음 단계 -********** - -사용 사례에 따라, 아래 내용들 중 하나를 다음 단계로 살펴보세요. - -.. raw:: html - -
-
- -.. Add callout items below this line - -.. displayitem:: - :header: Level 2: Add a validation and test set - :description: Add validation and test sets to avoid over/underfitting. - :button_link: ../levels/basic_level_2.html - :col_css: col-md-3 - :height: 180 - :tag: basic - -.. displayitem:: - :header: See more examples - :description: See examples across computer vision, NLP, RL, etc... - :col_css: col-md-3 - :button_link: ../tutorials.html - :height: 180 - :tag: basic - -.. displayitem:: - :header: I need my raw PyTorch Loop - :description: Expert-level control for researchers working on the bleeding-edge - :col_css: col-md-3 - :button_link: ../model/build_model_expert.html - :height: 180 - :tag: expert - -.. displayitem:: - :header: Deploy your model - :description: Learn how to predict or put your model into production - :col_css: col-md-3 - :button_link: ../deploy/production.html - :height: 180 - :tag: basic - -.. raw:: html - -
-
diff --git a/source/starter/lightning_lite.rst b/source/starter/lightning_lite.rst deleted file mode 100644 index 79081bb..0000000 --- a/source/starter/lightning_lite.rst +++ /dev/null @@ -1,718 +0,0 @@ -########################################### -LightningLite (Lightning을 위한 디딤돌) -########################################### - - -:class:`~pytorch_lightning.lite.LightningLite` 는 PyTorch 사용자들이 기존 반복(loop) / 최적화 로직을 -완벽하게 제어하면서 기존 코드를 모든 종류의 장치에서 사용 가능하도록 확장할 수 있도록 합니다. - -.. image:: https://pl-public-data.s3.amazonaws.com/docs/static/images/lite/lightning_lite.gif - :alt: PyTorch 코드를 LightningLite로 변환하는 방법을 보여주는 애니메이션. - :width: 500 - :align: center - -| - -아래 설명들 중 하나에 해당한다면 :class:`~pytorch_lightning.lite.LightningLite` 가 바로 적합한 도구입니다: - -- 기존 코드에 최소한의 변경만으로 여러 장치로 빠르게 확장하고 싶습니다. -- 기존 코드를 Lightning API로 변환하고 싶지만, Lightning으로의 완벽한 전환 과정(full path)이 다소 복잡할 것 같습니다. - 전환하는 동안 재현성(reproducibility)을 보장하기 위한 디딤돌(stepping stone)을 찾고 있습니다. - - -.. warning:: :class:`~pytorch_lightning.lite.LightningLite` 은 현재 beta 기능입니다. 사용자 피드백에 따라 API가 변경될 수 있습니다. - - ----------- - -**************** -예제로 배우기 -**************** - - -기존 PyTorch 코드 -======================== - -``run`` 함수는 ``MyModel`` 학습을 위해 ``MyDataset`` 을 ``num_epochs`` 에폭(epoch)만큼 반복하는 사용자 정의 학습 루프(loop)를 포함하고 있습니다. - -.. code-block:: python - - import torch - from torch import nn - from torch.utils.data import DataLoader, Dataset - - - class MyModel(nn.Module): - ... - - - class MyDataset(Dataset): - ... - - - def run(args): - device = "cuda" if torch.cuda.is_available() else "cpu" - - model = MyModel(...).to(device) - optimizer = torch.optim.SGD(model.parameters(), ...) - - dataloader = DataLoader(MyDataset(...), ...) - - model.train() - for epoch in range(args.num_epochs): - for batch in dataloader: - batch = batch.to(device) - optimizer.zero_grad() - loss = model(batch) - loss.backward() - optimizer.step() - - - run(args) - ----------- - - -LightningLite로 변환하기 -========================== - -:class:`~pytorch_lightning.lite.LightningLite` 로 변환하기 위해 필요한 다섯 단계는 다음과 같습니다. - -1. :class:`~pytorch_lightning.lite.LightningLite` 를 상속(subclass)받아 :meth:`~pytorch_lightning.lite.LightningLite.run` 메소드를 재정의합니다. -2. 기존 ``run`` 함수의 내용을 :class:`~pytorch_lightning.lite.LightningLite` 의 ``run`` 메소드로 이동합니다. -3. ``.to(...)``, ``.cuda()`` 등과 같은 모든 호출을 제거합니다. :class:`~pytorch_lightning.lite.LightningLite` 가 자동으로 이를 처리할 것입니다. -4. 각 모델과 옵티마이저(optimizer) 쌍에는 :meth:`~pytorch_lightning.lite.LightningLite.setup` 을, 모든 데이터로더(dataloader)에는 :meth:`~pytorch_lightning.lite.LightningLite.setup_dataloaders` 을 적용하고, ``loss.backward()`` 를 ``self.backward(loss)`` 로 변경합니다. -5. :class:`~pytorch_lightning.lite.LightningLite` 를 상속받은 서브클래스를 객체화(instantiate)한 뒤 :meth:`~pytorch_lightning.lite.LightningLite.run` 메소드를 호출합니다. - -| - -.. code-block:: python - - import torch - from torch import nn - from torch.utils.data import DataLoader, Dataset - from pytorch_lightning.lite import LightningLite - - - class MyModel(nn.Module): - ... - - - class MyDataset(Dataset): - ... - - - class Lite(LightningLite): - def run(self, args): - - model = MyModel(...) - optimizer = torch.optim.SGD(model.parameters(), ...) - model, optimizer = self.setup(model, optimizer) # 모델 / 옵티마이저(optimizer) 확장 - - dataloader = DataLoader(MyDataset(...), ...) - dataloader = self.setup_dataloaders(dataloader) # 데이터로더(dataloader) 확장 - - model.train() - for epoch in range(args.num_epochs): - for batch in dataloader: - optimizer.zero_grad() - loss = model(batch) - self.backward(loss) # loss.backward() 대체 - optimizer.step() - - - Lite(...).run(args) - - -이게 전부입니다. 이제 모든 종류의 장치에서 학습하고 확장할 수 있습니다. LightningLite를 사용한 전체 MNIST 학습 예제는 `여기 `_ 에서 확인할 수 있습니다. - -:class:`~pytorch_lightning.lite.LightningLite` 가 장치를 관리하므로, 사용자가 관리하지 않아도 됩니다. -코드 내에 특정 장치용 로직이 있다면 삭제해야 합니다. - -다음은 8개의 GPU에서 `torch.bfloat16 `_ 정밀도(precision)로 학습을 하는 방법을 보여줍니다: - -.. code-block:: python - - Lite(strategy="ddp", devices=8, accelerator="gpu", precision="bf16").run(10) - -`DeepSpeed Zero3 `_ 를 사용하여 8개의 GPU와 정밀도 16으로 학습하는 방법은 다음과 같습니다: - -.. code-block:: python - - Lite(strategy="deepspeed", devices=8, accelerator="gpu", precision=16).run(10) - -나아가 :class:`~pytorch_lightning.lite.LightningLite` 가 알아서 해주기도 합니다! - -.. code-block:: python - - Lite(devices="auto", accelerator="auto", precision=16).run(10) - -필요한 경우 분산-집합(distributed collectives)을 사용할 수도 있습니다. -다음은 (8개의 GPU x 32개 노드의) GPU 256개에서 실행하는 예제입니다. - -.. code-block:: python - - class Lite(LightningLite): - def run(self): - - # Transfer and concatenate tensors across processes - self.all_gather(...) - - # Transfer an object from one process to all the others - self.broadcast(..., src=...) - - # The total number of processes running across all devices and nodes. - self.world_size - - # The global index of the current process across all devices and nodes. - self.global_rank - - # The index of the current process among the processes running on the local node. - self.local_rank - - # The index of the current node. - self.node_rank - - # Wether this global rank is rank zero. - if self.is_global_zero: - # do something on rank 0 - ... - - # Wait for all processes to enter this call. - self.barrier() - - - Lite(strategy="ddp", devices=8, num_nodes=32, accelerator="gpu").run() - - -사용자 지정 데이터 또는 모델에 장치 할당이 필요한 경우, 데이터에는 ``self.setup_dataloaders(..., move_to_device=False)`` 를 하고 -모델에는 ``self.setup(..., move_to_device=False)`` 를 함으로써 :class:`~pytorch_lightning.lite.LightningLite` 의 자동 배치를 -비활성화할 수 있습니다. -뿐만 아니라, ``self.device`` 로 현재 장치에 접근하거나 :meth:`~pytorch_lightning.lite.LightningLite.to_device` 를 사용하여 -객체를 현재 장치로 이동할 수 있습니다. - - -.. note:: 큰 모델들은 out-of-memory(메모리 부족) 에러가 발생하므로 :meth:`~pytorch_lightning.lite.LightningLite.run` 에서 모델을 생성(instantiate)하는 것을 권장합니다. - -.. tip:: - - :meth:`~pytorch_lightning.lite.LightningLite.run` 함수 내에 수백에서 수천 라인의 코드가 있고 이에 대해 확신이 서지 않는다면, - 적절한 느낌입니다. 2019년에 :class:`~pytorch_lightning.core.lightning.LightningModule` 이 점점 커지면서 개발자들 또한 같은 느낌을 받았고, - 이에 따라 단순성(simplicity)과 상호운용성(interoperability), 표준화(standardization)를 위해 코드를 구성하기 시작했습니다. - 이러한 느낌은 코드 리팩토링(refactoring)과 함께 / 또는 :class:`~pytorch_lightning.core.lightning.LightningModule` 으로 완전히 전환하는 것을 - 고려해봐야 한다는 좋은 신호입니다. - - ----------- - - -분산 학습 시의 함정(pitfall) -============================= - -:class:`~pytorch_lightning.lite.LightningLite` 는 학습을 확장할 수 있는 도구들을 제공하지만, 직면해야 할 몇 가지 주요한 과제들도 있습니다: - - -.. list-table:: - :widths: 50 50 - :header-rows: 0 - - * - 프로세스 발산(Processes divergence) - - 이전 파일 또는 다른 이유에서 서로 다른 if/else 조건, 경쟁 조건(race condition)으로 프로세스가 코드의 다른 부분(section) 실행하여 멈출(hanging) 때 발생합니다. - * - 프로세스 간 리듀스(Cross processes reduction) - - 리듀스 과정(reduction)에서의 오류로 메트릭(metric) 또는 변화도(gradient)가 잘못 계산되었습니다. - * - 대규모의 샤딩된 모델(Large sharded models) - - 대규모 모델의 생성(instantiation)과 구현(materialization), 상태 관리(state management). - * - 순서가 0뿐인 작업(Rank 0 only actions) - - 로깅(logging), 프로파일링(profiling) 등. - * - 체크포인팅 / 조기 중단 / 콜백 / 로깅 (Checkpointing / Early stopping / Callbacks / Logging) - - 학습 과정을 쉽게 사용자 정의하고 상태를 관리할 수 있는 기능. - * - 결함-감내 학습(Fault-tolerant training) - - 오류 발생 시에 마치 오류가 없었던 것처럼 재개(resume)하는 기능. - - -위와 같은 과제들 중 하나를 맞이했다면, 이제 :class:`~pytorch_lightning.lite.LightningLite` 의 한계를 마주한 것입니다. -이러한 걱정을 할 필요가 없는 :doc:`Lightning <../starter/introduction>` 으로 변환하는 것을 추천합니다. - ----------- - -Lightning으로의 변환 -====================== - -:class:`~pytorch_lightning.lite.LightningLite` 은 수백가지 기능을 갖는 Lightning API로의 완전한 전환을 위한 디딤돌입니다. - -:class:`~pytorch_lightning.lite.LightningLite` 클래스 자체를 :class:`~pytorch_lightning.core.lightning.LightningModule` 의 개선된 버전(future)로 볼 수도 있으므로, -해당 API로 코드를 천천히 재구성(refactor)해보겠습니다. -아래에는 :meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` 와 :meth:`~pytorch_lightning.core.lightning.LightningModule.forward`, -:meth:`~pytorch_lightning.core.lightning.LightningModule.configure_optimizers`, :meth:`~pytorch_lightning.core.lightning.LightningModule.train_dataloader` 메서드들이 -구현되어 있습니다. - - -.. code-block:: python - - class Lite(LightningLite): - - # 1. 이 부분은 LightningModule의 `__init__` 함수가 됩니다. - def run(self, args): - self.args = args - - self.model = MyModel(...) - - self.fit() # 이는 Lightning Trainer에 의해 자동화됩니다. - - # 2. Lightning이 자체적인 학습 루프(fitting loop)를 생성하고, - # 모델, 옵티마이저, 데이터로더 등을 설정하므로 이 코드는 완전히 제거해도 됩니다. - def fit(self): - # 필요한 것들을 설정 - optimizer = self.configure_optimizers() - self.model, optimizer = self.setup(self.model, optimizer) - dataloader = self.setup_dataloaders(self.train_dataloader()) - - # 학습(fitting) 시작 - self.model.train() - for epoch in range(num_epochs): - for batch in enumerate(dataloader): - optimizer.zero_grad() - loss = self.training_step(batch, batch_idx) - self.backward(loss) - optimizer.step() - - # 3. 이는 LightningModule에 속하므로 그대로 둡니다. - def forward(self, x): - return self.model(x) - - def training_step(self, batch, batch_idx): - return self.forward(batch) - - def configure_optimizers(self): - return torch.optim.SGD(self.model.parameters(), ...) - - # 4. [선택사항] 이는 그대로 두거나, LightningDataModule이 더 높은 결합성(composability)을 갖도록 따로 분리(extract)할 수도 있습니다. - def train_dataloader(self): - return DataLoader(MyDataset(...), ...) - - - Lite(...).run(args) - - -마지막으로, :meth:`~pytorch_lightning.lite.LightningLite.run` 을 :meth:`~pytorch_lightning.core.lightning.LightningModule.__init__` 으로 -바꾸고, 내부의 ``fit`` 호출 부분을 삭제합니다. - -.. code-block:: python - - from pytorch_lightning import LightningDataModule, LightningModule, Trainer - - - class LightningModel(LightningModule): - def __init__(self, args): - super().__init__() - self.model = MyModel(...) - - def forward(self, x): - return self.model(x) - - def training_step(self, batch, batch_idx): - loss = self(batch) - self.log("train_loss", loss) - return loss - - def configure_optimizers(self): - return torch.optim.SGD(self.model.parameters(), lr=0.001) - - - class BoringDataModule(LightningDataModule): - def train_dataloader(self): - return DataLoader(MyDataset(...), ...) - - - trainer = Trainer(max_epochs=10) - trainer.fit(LightningModel(), datamodule=BoringDataModule()) - - -이제 수백가지 기능들의 이점을 누릴 수 있는 PyTorch Lightning으로의 변환을 성공적으로 완료하였습니다! - ----------- - -******************************** -Lightning Lite 매개변수(flag) -******************************** - -Lite는 가속화된 분산 학습 및 추론(inference)에 특화되어 있습니다. 이는 장치 및 통신 전략을 손쉽게 구성하고, -다른 방식으로의 원활하게 전환할 수 있는 편리한 방법을 제공합니다. 용어(terminology) 및 사용법이 Lightning과 -동일하므로, 변환을 결심했을 때 변환에 드는 노력을 최소화할 수 있습니다. - - -accelerator (가속기 종류) -========================== - -``"cpu"``, ``"gpu"``, ``"tpu"``, ``"auto"`` 중 하나를 선택합니다 (IPU는 곧 제공 예정입니다). - -.. code-block:: python - - # CPU 가속기 - lite = Lite(accelerator="cpu") - - # 2개의 GPU 가속기에서 실행 - lite = Lite(devices=2, accelerator="gpu") - - # 8개의 TPU 가속기에서 실행 - lite = Lite(devices=8, accelerator="tpu") - - # DistributedDataParallel(ddp) 전략으로 GPU 가속기에서 실행 - lite = Lite(devices=4, accelerator="gpu", strategy="ddp") - -``"auto"`` 옵션은 사용 중인 기기를 인식하고 사용 가능한 가속기를 선택합니다. - -.. code-block:: python - - # 기기에 GPU가 있으면, GPU 가속기를 사용합니다. - lite = Lite(devices=2, accelerator="auto") - - -strategy (학습 전략) -====================== - -학습 전략을 선택합니다: ``"dp"``, ``"ddp"``, ``"ddp_spawn"``, ``"tpu_spawn"``, ``"deepspeed"``, ``"ddp_sharded"``, 또는 ``"ddp_sharded_spawn"`` - -.. code-block:: python - - # 4개의 GPU에서 DistributedDataParallel 전략 사용 - lite = Lite(strategy="ddp", accelerator="gpu", devices=4) - - # 4개의 CPU에서 DDP Spawn 전략 사용 - lite = Lite(strategy="ddp_spawn", accelerator="cpu", devices=4) - - -또한, 몇몇 매개변수를 추가로 설정해서 사용자 지정 전략을 사용할 수 있습니다. - -.. code-block:: python - - from pytorch_lightning.strategies import DeepSpeedStrategy - - lite = Lite(strategy=DeepSpeedStrategy(stage=2), accelerator="gpu", devices=2) - - -Horovoard 및 Full Sharded 학습 전략은 곧 지원될 예정입니다. - - -device (장치) -============== - -실행할 장치를 설정합니다. 아래와 같은 자료형일 수 있습니다: - -- int: 학습할 장치(예. GPU)의 개수 -- list of int: 학습할 장치의 인덱스(예. GPU ID, 0-indexed) -- str: 위 중 하나의 문자열 표현 - -.. code-block:: python - - # Lite에서 사용하는 기본 값, CPU에서 실행 - lite = Lite(devices=None) - - # 위와 동일 - lite = Lite(devices=0) - - # int: 2개의 GPU에서 실행 - lite = Lite(devices=2, accelerator="gpu") - - # list: GPU 1, 4에서 실행 (버스 순서에 따름) - lite = Lite(devices=[1, 4], accelerator="gpu") - lite = Lite(devices="1, 4", accelerator="gpu") # 위와 동일 - - # -1: 모든 GPU에서 실행 - lite = Lite(devices=-1, accelerator="gpu") - lite = Lite(devices="-1", accelerator="gpu") # 위와 동일 - - - -gpus (사용하지 않음) -======================= - -.. warning:: ``gpus=x`` 는 v1.7에서 더 이상 사용하지 않으며(deprecated), v2.0에서 제거될 예정입니다. - 대신에 ``accelerator='gpu'`` 및 ``devices=x`` 을 사용하십시오. - -``devices=X`` 및 ``accelerator="gpu"`` 의 약어(shorthand). - -.. code-block:: python - - # 2개의 GPU에서 실행 - lite = Lite(accelerator="gpu", devices=2) - - # 위와 동일 - lite = Lite(devices=2, accelerator="gpu") - - -tpu_cores (사용하지 않음) -============================ - -.. warning:: ``tpu_cores=x`` 는 v1.7에서 더 이상 사용하지 않으며(deprecated), v2.0에서 제거될 예정입니다. - 대신에 ``accelerator='tpu'`` 및 ``devices=x`` 을 사용하십시오. - -``devices=X`` 및 ``accelerator="tpu"`` 의 약어. - -.. code-block:: python - - # 8개의 TPU에서 실행 - lite = Lite(accelerator="tpu", devices=8) - - # 위와 동일 - lite = Lite(devices=8, accelerator="tpu") - - -num_nodes (노드의 수) -==================================== - -분산 작업 시의 클러스터 노드의 수. - -.. code-block:: python - - # Lite에서 사용하는 기본값 - lite = Lite(num_nodes=1) - - # 8개의 노드에서 실행 - lite = Lite(num_nodes=8) - - -클러스터에서의 분산 다중 노드 학습에 대해서는 :doc:`이 문서 <../clouds/cluster>` 에서 자세히 알아볼 수 있습니다. - - -precision (정밀도) -===================== - -Lightning Lite는 배정밀도(double precision; 64), 단정밀도(full precision; 32), 또는 반정밀도(half precision; 16) 연산(`bfloat16 `_ 포함)을 지원합니다. -반정밀도 또는 혼합 정밀도(mixed precision)는 32비트 정밀도와 16비트 정밀도를 합쳐서 사용하여 모델 학습 시의 메모리 공간(footprint)을 줄입니다. -그 결과 성능이 향상되어 최신 GPU에서 눈에 띄게 성능이 향상됩니다. - -.. code-block:: python - - # Lite에서 사용하는 기본값 - lite = Lite(precision=32, devices=1) - - # 16-비트 (혼합) 정밀도 - lite = Lite(precision=16, devices=1) - - # 16-비트 bfloat 정밀도 - lite = Lite(precision="bf16", devices=1) - - # 64-비트 (배(double)) 정밀도 - lite = Lite(precision=64, devices=1) - - -plugins (플러그인) -===================== - -:ref:`Plugins` 을 사용하여 임의의 백엔드(backend), 정밀도 라이브러리, 클러스터 등을 연결할 수 있습니다. -예: 임의의 동작을 정의하고 싶으면 관련 클래스를 상속받아 전달하면 됩니다. 다음은 직접 만든 -:class:`~pytorch_lightning.plugins.environments.ClusterEnvironment` 를 연결하는 예시입니다. - -.. code-block:: python - - from pytorch_lightning.plugins.environments import ClusterEnvironment - - - class MyCluster(ClusterEnvironment): - @property - def main_address(self): - return your_main_address - - @property - def main_port(self): - return your_main_port - - def world_size(self): - return the_world_size - - - lite = Lite(plugins=[MyCluster()], ...) - - ----------- - - -********************** -Lightning Lite 메소드 -********************** - - -run -==== - -run 메소드는 2가지 용도로 사용합니다: - -1. :class:`~pytorch_lightning.lite.lite.LightningLite` 클래스에서 이 메시드를 재정의(override)하고 - 학습(또는 추론) 코드를 내부에 넣습니다. -2. run 메소드를 호출하여 학습 절차를 시작합니다. Lite는 분산 백엔드 설정을 처리합니다. - -선택적으로 run 메소드에 인자(예를 들어 모델의 하이퍼파라매터나 백엔드)를 전달할 수 있습니다. - -.. code-block:: python - - from pytorch_lightning.lite import LightningLite - - - class Lite(LightningLite): - - # 입력 인자는 선택 사항입니다; 필요 시에 넣으세요. - def run(self, learning_rate, num_layers): - """여기에 학습 과정이 들어갑니다.""" - - - lite = Lite(accelerator="gpu", devices=2) - lite.run(learning_rate=0.01, num_layers=12) - - -setup -====== - -모델 및 해당하는 옵티마이저(들)을 설정합니다. 여러 모델을 설정해야 하는 경우, 각각에 대해서 ``setup()`` 을 호출하십시오. -모델과 옵티마이저는 적절한 장치로 자동으로 이동합니다. - -.. code-block:: python - - model = nn.Linear(32, 64) - optimizer = torch.optim.SGD(model.parameters(), lr=0.001) - - # 가속화된 학습을 위해 모델 및 옵티마이저 설정 - model, optimizer = self.setup(model, optimizer) - - # Lite가 장치를 설정하는 것을 원치 않는 경우 - model, optimizer = self.setup(model, optimizer, move_to_device=False) - - -setup 메소드는 선택한 정밀도로 모델을 준비하여 ``forward()`` 중 연산들이 자동으로 변환(cast)되도록 합니다. - -setup_dataloaders -================= - -가속화된 연산을 위해 하나 이상의 데이터로더를 설정합니다. 분산 전략(예. DDP)을 사용하는 경우, Lite는 자동으로 샘플러(sampler)를 -대체합니다. 또한, 데이터로더는 반환된 데이터 텐서를 적절한 장치로 자동으로 이동하도록 설정됩니다. - -.. code-block:: python - - train_data = torch.utils.DataLoader(train_dataset, ...) - test_data = torch.utils.DataLoader(test_dataset, ...) - - train_data, test_data = self.setup_dataloaders(train_data, test_data) - - # Lite가 데이터를 자동으로 장치로 이동시키는 것을 원치 않는 경우 - train_data, test_data = self.setup_dataloaders(train_data, test_data, move_to_device=False) - - # Lite가 분산 학습 도중 샘플러를 대체하기를 원치 않는 경우 - train_data, test_data = self.setup_dataloaders(train_data, test_data, replace_sampler=False) - - -backward -=========== - -``loss.backward()`` 을 대체하여 정밀도와 가속기 코드를 신경쓰지 않도록(agnostic) 합니다. - -.. code-block:: python - - output = model(input) - loss = loss_fn(output, target) - - # loss.backward() - self.backward(loss) - - -to_device -========= - -:meth:`~pytorch_lightning.lite.lite.LightningLite.to_device` 를 사용하여 모델 또는 텐서, 텐서 컬렉션을 현재 장치로 이동합니다. -기본적으로 :meth:`~pytorch_lightning.lite.lite.LightningLite.setup` 및 :meth:`~pytorch_lightning.lite.lite.LightningLite.setup_dataloaders` 가 -모델과 데이터를 적절한 장치로 이동했으므로, 이 메소드는 수동 작업이 필요할 때만 사용합니다. - -.. code-block:: python - - data = torch.load("dataset.pt") - data = self.to_device(data) - - -seed_everything -=============== - -run의 시작 부분에 이 메소드를 호출하여 코드를 재현 가능하도록 합니다. - -.. code-block:: python - - # `torch.manual_seed(...)` 대신 다음을 호출: - self.seed_everything(1234) - - -이는 PyTorch 및 NumPy, Python 난수 생성기를 포괄합니다. 또한, Lite는 데이터로더 워커(worker) 프로세서의 시드(seed)를 적절히 초기화합니다. -(``workers=False`` 를 전달하여 이 기능을 끌 수 있습니다.) - - -autocast -======== - -정밀도 백엔드가 autocast 컨텍스트 매니저 내부의 코드 블록을 자동으로 캐스팅하도록 합니다. 이는 선택사항이며, Lite가 -(모델이 :meth:`~pytorch_lightning.lite.lite.LightningLite.setup` 될 때) 이미 모델의 forward 메소드에 적용하였습니다 -모델 forward 메소드 외부의 추가 연산들에 대해 자동으로 캐스팅하려는 경우에만 사용합니다: - -.. code-block:: python - - model, optimizer = self.setup(model, optimizer) - - # Lite가 모델의 정밀도를 자동으로 처리합니다 - output = model(inputs) - - with self.autocast(): # 선택 사항 - loss = loss_function(output, target) - - self.backward(loss) - ... - - -print -===== - -내장 print 함수를 통해 콘솔에 출력하지만, 메인 프로세스(main process)에서만 가능합니다. -이는 여러 장치/노드에서 실행할 때 과도한 출력 및 로그를 방지합니다. - - -.. code-block:: python - - # 메인 프로세스에서만 출력 - self.print(f"{epoch}/{num_epochs}| Train Epoch Loss: {loss}") - - -save -==== - -체크포인트(checkpoint)에 내용을 저장합니다. 기존의 ``torch.save(...)`` 를 모두 대체합니다. Lite는 단일 장치나 다중 장치, -다중 노드 중 어디에서 실행하던지 잘 저장될 수 있도록 처리합니다. - -.. code-block:: python - - # `torch.save(...)` 대신 다음을 호출: - self.save(model.state_dict(), "path/to/checkpoint.ckpt") - - -load -==== - -파일로부터 체크포인트 내용을 불러옵니다. 기존의 ``torch.load(...)`` 를 모두 대체합니다. Lite는 단일 장치나 다중 장치, -다중 노드 중 어디에서 실행하던지 잘 불러올 수 있도록 처리합니다. - -.. code-block:: python - - # `torch.load(...)` 대신 다음을 호출: - self.load("path/to/checkpoint.ckpt") - - -barrier -======= - -모든 프로세스들이 대기해였다가 동기화되길 원할 때 사용합니다. 모든 프로세스가 barrier 호출에 진입하면, 그 때 계속 실행합니다. -예를 들어 한 프로세스가 데이터를 다운로드해서 디스크에 쓰는 동안 다른 모든 프로세스들이 대기하도록 할 때 유용합니다. - -.. code-block:: python - - # 한 프로세스에서만 데이터 다운로드 - if self.global_rank == 0: - download_data("http://...") - - # 모든 프로세스가 여기서 만날 때까지 대기 - self.barrier() - - # 이제 모든 프로세스가 데이터를 읽을 수 있음 diff --git a/source/starter/style_guide.rst b/source/starter/style_guide.rst deleted file mode 100644 index 7a00b58..0000000 --- a/source/starter/style_guide.rst +++ /dev/null @@ -1,232 +0,0 @@ -################ -스타일 가이드 -################ -PyTorch Lightning의 주요한 목표는 가독성(readability)과 재현성(reproducibility)을 향상시키는 것입니다. GitHub 저장소나 리서치 프로젝트에서 -:class:`~pytorch_lightning.core.lightning.LightningModule` 을 발견하고, 필요한 부분이 어디에 있는지 찾아보기 위해 정확히 어디를 봐야 하는지 -알 수 있다고 생각해보세요. - -이 스타일 가이드의 목표는 Lightning의 코드가 유사하게 구성되도록 권장하기 위함입니다. - --------------- - -***************** -LightningModule -***************** - -:class:`~pytorch_lightning.core.lightning.LightningModule` 클래스를 구성하는 모범 사례를 살펴보겠습니다: - -시스템(System)과 모델(Model) -============================== - -.. figure:: https://pl-bolts-doc-images.s3.us-east-2.amazonaws.com/pl_docs/model_system.png - :width: 400 - -LightningModule의 주요한 원칙은 전체 시스템이 반드시 독립적(self-contained)이어야 한다는 것입니다. -Lightning에서는 시스템(system)과 모델(model)을 구분합니다. - -모델은 ResNet18, RNN 등과 같은 것입니다. - -시스템은 모델들(a collection of models)이 사용자가 정의한 학습/검증 로직을 사용하여 어떻게 상호작용하는지에 대해 정의합니다. -이에 대한 예시는 다음과 같습니다: - -* GAN -* Seq2Seq -* BERT -* 그 외 - -LightningModule은 시스템과 모델 모두를 정의할 수 있습니다: - -다음은 시스템을 정의하는 LightningModule입니다. 아래와 같은 구조로 코드를 작성하는 것을 권장합니다. 모델을 시스템으로부터 분리하면 -모듈성(modularity)이 향상되어, 더 나은 테스팅에 도움이 되고 시스템에 의존성이 줄어들어 리팩토링이 더 쉬워집니다. - -.. testcode:: - - class Encoder(nn.Module): - ... - - - class Decoder(nn.Module): - ... - - - class AutoEncoder(nn.Module): - def __init__(self): - super().__init__() - self.encoder = Encoder() - self.decoder = Decoder() - - def forward(self, x): - return self.encoder(x) - - - class AutoEncoderSystem(LightningModule): - def __init__(self): - super().__init__() - self.auto_encoder = AutoEncoder() - - -빠른 프로토타이핑을 위해서는 모든 연산을 LightningModule 내에 정의하는 것이 유용합니다. 재현성과 확장성을 -위해서는 관련된 백본(backbone)에 전달하는 것이 더 나을 수 있습니다. - -다음은 LightningModule로 모델을 정의하는 것이지만, 아래 예시처럼 모델을 정의하는 것은 권장하지 않습니다. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self): - super().__init__() - self.layer_1 = nn.Linear() - self.layer_2 = nn.Linear() - self.layer_3 = nn.Linear() - - -독립성(self-contained) -=========================== - -LightningModule은 반드시 독립적(self-contained)이어야 합니다. 모델이 독립적인지를 확인해보는 좋은 방법 중에 하나는, -다음과 같이 스스로에게 물어보는 것입니다: - -"누군가 모델의 내부에 대해서 전혀 모르는 상태에서 이 파일을 Trainer에 사용(drop)할 수 있을까?" - -예를 들어, 주요한 모델들은 특정 옵티마이저(optimizer)와 학습률 스케쥴러(learning rate scheduler)에서 잘 동작하기 때문에 -옵티마이저는 모델과 결합(couple)합니다. - -초기화(init) -================= - -LightningModule이 독립성을 띄지 못하게 되는 첫번째 위치는 초기화(init) 부분입니다. 사용자가 추측할 필요가 없도록 초기화 부분에 -모든 관련된 적절한 기본값(sensible defaults)을 정의해주세요. - -다음은 이 LightningModule이 어떻게 초기화되었는지 알아보기 위해 사용자가 파일을 찾아봐야만 하는 예시입니다. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self, params): - self.lr = params.lr - self.coef_x = params.coef_x - -이렇게 정의된 모델은 많은 궁금증들, 예를 들면 ``coef_x`` 는 무엇인지? 문자열인지? 실수(float)인지? 범위는 어떻게 되는지? 를 -갖게 합니다. 이렇게 하는 대신, 명시적으로 초기화를 하는 것이 좋습니다. - -.. testcode:: - - class LitModel(LightningModule): - def __init__(self, encoder: nn.Module, coef_x: float = 0.2, lr: float = 1e-3): - ... - -이제 사용자는 추측할 필요가 없습니다. 값의 타입(type) 뿐만 아니라, 모델에는 사용자가 즉시 확인할 수 있는 적절한 기본값도 -존재합니다. - - -메소드 순서 -============ -LightningModule에서 필요로 하는 메소드들은 다음의 것들 뿐입니다: - -* init -* training_step -* configure_optimizers - -하지만, 다른 선택적인 메소드들을 구현하기로 마음먹었다면, 권장하는 순서는 다음과 같습니다: - -* 모델/시스템 정의 (초기화) -* 추론(inference)을 한다면, forward 정의 -* 학습용 훅(training hooks) -* 검증용 훅(validation hooks) -* 테스트용 훅(test hooks) -* 예측용 훅(predict hooks) -* 옵티마이저 설정(configure_optimizers) -* 다른 훅(hooks) - -실제 코드는 다음과 같습니다: - -.. code-block:: - - class LitModel(pl.LightningModule): - - def __init__(...): - - def forward(...): - - def training_step(...): - - def training_step_end(...): - - def training_epoch_end(...): - - def validation_step(...): - - def validation_step_end(...): - - def validation_epoch_end(...): - - def test_step(...): - - def test_step_end(...): - - def test_epoch_end(...): - - def configure_optimizers(...): - - def any_extra_hook(...): - - -forward와 training_step -======================== - -:meth:`~pytorch_lightning.core.lightning.LightningModule.forward` 는 추론/예측을 위해 사용하고, -:meth:`~pytorch_lightning.core.lightning.LightningModule.training_step` 를 독립적으로 유지하는 것을 추천합니다. - -.. code-block:: python - - def forward(self, x): - embeddings = self.encoder(x) - return embeddings - - - def training_step(self, batch, batch_idx): - x, _ = batch - z = self.encoder(x) - pred = self.decoder(z) - ... - - --------------- - -************ -데이터 -************ - -데이터를 다루는 모범 사례입니다. - -DataLoader -============== - -Lightning은 :class:`~torch.utils.data.DataLoader` 를 사용해서 시스템 전반의 모든 데이터 흐름을 다룹니다. DataLoader를 구성할 때는 -최대의 효율을 위해 워커(worker)의 수를 반드시 적절하게 조절해야 합니다. - -.. warning:: 코드가 병목을 일으킬 수 있으므로 DataLoader에서 ``Trainer(strategy="ddp_spawn")`` 를 ``num_workers>0`` 로 사용하지 않도록 주의하세요. - -DataModule -============== - -:class:`~pytorch_lightning.core.datamodule.LightningDataModule` 은 데이터와 관련된 훅들을 :class:`~pytorch_lightning.core.lightning.LightningModule` 로부터 -분리하도록 설계되어 데이터셋 종류에 구애받지 않는 모델을 만들 수 있습니다. 이렇게 하면 모델이 서로 다른 데이터셋을 사용하도록 언제든지 교체(hot swap)할 수 있어, -여러 분야(domain)에서 테스트와 벤치마킹을 할 수 있습니다. 또한 프로젝트들 간에 정확한 데이터 분할(split)과 변환(transform)을 공유하고 재사용 할 수 있게 합니다. - -Lightning에서의 데이터 관리 방법과 모범 사례는 :ref:`data` 문서를 참고하세요. - -* 어떠한 데이터 분할(split) 방법이 사용되었나요? -* 전체와 분할된 데이터셋 각각에는 몇 개의 샘플이 있나요? -* 어떠한 변환(transform) 방법이 사용되었나요? - -이러한 이유들 때문에 DataModule 사용하기를 권하고 있습니다. 이는 협업할 때 팀의 시간을 많이 절약할 수 있기에 특히 중요합니다. - -사용자들은 DataModule을 Trainer에 던져놓기만 하고, 데이터에 어떠한 작업이 수행되는지는 신경쓰지 않아도 됩니다. - -이는 데이터의 정제(cleaning)와 특정 목적의 작업(ad-hoc instruction) 때문에 아이디어를 반복하는 과정이 느려지는 학계(academic)나 기업(corporate) 모두에 -해당됩니다. - -- 직접 손으로 따라해볼 수 있는 예제들입니다: -- `Introduction to PyTorch Lightning `_ -- `Introduction to DataModules `_ diff --git a/source/visualize/supported_exp_managers.rst b/source/visualize/supported_exp_managers.rst deleted file mode 100644 index 1a15ee2..0000000 --- a/source/visualize/supported_exp_managers.rst +++ /dev/null @@ -1,198 +0,0 @@ -Comet.ml -======== -To use `Comet.ml `_ first install the comet package: - -.. code-block:: bash - - pip install comet-ml - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import CometLogger - - comet_logger = CometLogger(api_key="YOUR_COMET_API_KEY") - trainer = Trainer(logger=comet_logger) - -Access the comet logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class LitModel(LightningModule): - def any_lightning_module_function_or_hook(self): - comet = self.logger.experiment - fake_images = torch.Tensor(32, 3, 28, 28) - comet.add_image("generated_images", fake_images, 0) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.CometLogger`. - ----- - -MLflow -====== -To use `MLflow `_ first install the MLflow package: - -.. code-block:: bash - - pip install mlflow - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import MLFlowLogger - - mlf_logger = MLFlowLogger(experiment_name="lightning_logs", tracking_uri="file:./ml-runs") - trainer = Trainer(logger=mlf_logger) - -Access the comet logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class LitModel(LightningModule): - def any_lightning_module_function_or_hook(self): - mlf_logger = self.logger.experiment - fake_images = torch.Tensor(32, 3, 28, 28) - mlf_logger.add_image("generated_images", fake_images, 0) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.MLFlowLogger`. - ----- - -Neptune.ai -========== -To use `Neptune.ai `_ first install the neptune package: - -.. code-block:: bash - - pip install neptune-client - -or with conda: - -.. code-block:: bash - - conda install -c conda-forge neptune-client - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import NeptuneLogger - - neptune_logger = NeptuneLogger( - api_key="ANONYMOUS", # replace with your own - project="common/pytorch-lightning-integration", # format "" - ) - trainer = Trainer(logger=neptune_logger) - -Access the neptune logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class LitModel(LightningModule): - def any_lightning_module_function_or_hook(self): - neptune_logger = self.logger.experiment["your/metadata/structure"] - neptune_logger.log(metadata) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.NeptuneLogger`. - ----- - -Tensorboard -=========== -`TensorBoard `_ already comes installed with Lightning. If you removed the install install the following package. - -.. code-block:: bash - - pip install tensorboard - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import TensorBoardLogger - - logger = TensorBoardLogger() - trainer = Trainer(logger=logger) - -Access the tensorboard logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class LitModel(LightningModule): - def any_lightning_module_function_or_hook(self): - tensorboard_logger = self.logger.experiment - fake_images = torch.Tensor(32, 3, 28, 28) - tensorboard_logger.add_image("generated_images", fake_images, 0) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.TensorBoardLogger`. - ----- - -Weights and Biases -================== -To use `Weights and Biases `_ (wandb) first install the wandb package: - -.. code-block:: bash - - pip install wandb - -Configure the logger and pass it to the :class:`~pytorch_lightning.trainer.trainer.Trainer`: - -.. code-block:: python - - from pytorch_lightning.loggers import WandbLogger - - wandb_logger = WandbLogger(project="MNIST", log_model="all") - trainer = Trainer(logger=wandb_logger) - - # log gradients and model topology - wandb_logger.watch(model) - -Access the wandb logger from any function (except the LightningModule *init*) to use its API for tracking advanced artifacts - -.. code-block:: python - - class MyModule(LightningModule): - def any_lightning_module_function_or_hook(self): - wandb_logger = self.logger.experiment - fake_images = torch.Tensor(32, 3, 28, 28) - - # Option 1 - wandb_logger.log({"generated_images": [wandb.Image(fake_images, caption="...")]}) - - # Option 2 for specifically logging images - wandb_logger.log_image(key="generated_images", images=[fake_images]) - -Here's the full documentation for the :class:`~pytorch_lightning.loggers.WandbLogger`. -`Demo in Google Colab `__ with hyperparameter search and model logging. - ----- - -Use multiple exp managers -========================= -To use multiple experiment managers at the same time, pass a list to the *logger* :class:`~pytorch_lightning.trainer.trainer.Trainer` argument. - -.. code-block:: python - - from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger - - logger1 = TensorBoardLogger() - logger2 = WandbLogger() - trainer = Trainer(logger=[logger1, logger2]) - - -Access all loggers from any function (except the LightningModule *init*) to use their APIs for tracking advanced artifacts - -.. code-block:: python - - class MyModule(LightningModule): - def any_lightning_module_function_or_hook(self): - tensorboard_logger = self.logger.experiment[0] - wandb_logger = self.logger.experiment[1] - - fake_images = torch.Tensor(32, 3, 28, 28) - - tensorboard_logger.add_image("generated_images", fake_images, 0) - wandb_logger.add_image("generated_images", fake_images, 0)